diff --git a/package-lock.json b/package-lock.json index 20631de..f6d2527 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,24 +12,24 @@ "@commitlint/cli": "^21.0.2", "@commitlint/config-conventional": "^21.0.2", "@commitlint/types": "^21.0.1", - "@obsidian-typings/obsidian-public-latest": "^6.14.0", + "@obsidian-typings/obsidian-public-latest": "^6.15.0", "@total-typescript/ts-reset": "^0.6.1", "@tsconfig/strictest": "^2.0.8", "@types/node": "^25.9.3", - "@vitest/coverage-v8": "^4.1.8", + "@vitest/coverage-v8": "^4.1.9", "better-typescript-lib": "^2.12.0", "czg": "^1.13.1", - "eslint": "^10.4.1", + "eslint": "^10.5.0", "husky": "^9.1.7", "jiti": "^2.7.0", "jsdom": "^29.1.1", "nano-staged": "^1.0.2", "obsidian": "^1.13.1", - "obsidian-dev-utils": "^70.2.0", - "obsidian-integration-testing": "^4.2.3", - "obsidian-test-mocks": "^3.1.0", + "obsidian-dev-utils": "^72.0.0", + "obsidian-integration-testing": "^4.2.6", + "obsidian-test-mocks": "^3.1.1", "sass-embedded": "^1.100.0", - "vitest": "^4.1.8" + "vitest": "^4.1.9" } }, "node_modules/@asamuzakjp/css-color": { @@ -1494,9 +1494,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -1511,9 +1511,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -1528,9 +1528,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -1545,9 +1545,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -1562,9 +1562,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", - "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -1579,9 +1579,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -1596,9 +1596,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -1613,9 +1613,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -1630,9 +1630,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -1647,9 +1647,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -1664,9 +1664,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -1681,9 +1681,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -1698,9 +1698,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -1715,9 +1715,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -1732,9 +1732,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -1749,9 +1749,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -1766,9 +1766,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -1783,9 +1783,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -1800,9 +1800,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -1817,9 +1817,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -1834,9 +1834,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -1851,9 +1851,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -1868,9 +1868,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -1885,9 +1885,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -1902,9 +1902,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -1919,9 +1919,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -2517,9 +2517,9 @@ } }, "node_modules/@obsidian-typings/obsidian-public-1.12.7": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-1.12.7/-/obsidian-public-1.12.7-6.16.0.tgz", - "integrity": "sha512-mR0rFTCjFxVn7UJq95ZmqA57D0D4UixMsMzy/F/RsCa9sgvljBOr7vPXwb/DKIiPbU0/FKm9SB8SmeuuM7CGWQ==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-1.12.7/-/obsidian-public-1.12.7-6.17.0.tgz", + "integrity": "sha512-wsiIEk3vVBmtLC5kl1WneyuYM6TMDnPMjgdxYMAN5lkDItbxgOAcPQJWdJFyBxXFXk4mYwBU04+7jir/YVZYIQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2527,13 +2527,13 @@ } }, "node_modules/@obsidian-typings/obsidian-public-latest": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-latest/-/obsidian-public-latest-6.14.0.tgz", - "integrity": "sha512-rH6eqOg2mAJ0Lpx7ZuTN8LkQBF5L/Ry5HeZo4i53Jved7Ay7KhROpkz0vafNSmR0DH9JKNaYbC6fW9WiDHo6Ng==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@obsidian-typings/obsidian-public-latest/-/obsidian-public-latest-6.15.0.tgz", + "integrity": "sha512-/35F3w0nvVE0hJpadrNaVUPYKvNSjNsigZV+A842PBm0cdi62CfemG8ClwvrvDlH1wokrG3dvio8mQX9WfAiKA==", "dev": true, "license": "MIT", "dependencies": { - "@obsidian-typings/obsidian-public-1.12.7": "^6.16.0" + "@obsidian-typings/obsidian-public-1.12.7": "^6.17.0" } }, "node_modules/@oxc-project/types": { @@ -3812,17 +3812,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz", - "integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", + "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.61.0", - "@typescript-eslint/type-utils": "8.61.0", - "@typescript-eslint/utils": "8.61.0", - "@typescript-eslint/visitor-keys": "8.61.0", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/type-utils": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -3835,22 +3835,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.61.0", + "@typescript-eslint/parser": "^8.61.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz", - "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.61.0", - "@typescript-eslint/types": "8.61.0", - "@typescript-eslint/typescript-estree": "8.61.0", - "@typescript-eslint/visitor-keys": "8.61.0", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "debug": "^4.4.3" }, "engines": { @@ -3866,14 +3866,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz", - "integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.61.0", - "@typescript-eslint/types": "^8.61.0", + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", "debug": "^4.4.3" }, "engines": { @@ -3888,14 +3888,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz", - "integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.0", - "@typescript-eslint/visitor-keys": "8.61.0" + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3906,9 +3906,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz", - "integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", "dev": true, "license": "MIT", "engines": { @@ -3923,15 +3923,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz", - "integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", + "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.0", - "@typescript-eslint/typescript-estree": "8.61.0", - "@typescript-eslint/utils": "8.61.0", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -3948,9 +3948,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz", - "integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", "dev": true, "license": "MIT", "engines": { @@ -3962,16 +3962,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz", - "integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.61.0", - "@typescript-eslint/tsconfig-utils": "8.61.0", - "@typescript-eslint/types": "8.61.0", - "@typescript-eslint/visitor-keys": "8.61.0", + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3990,16 +3990,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz", - "integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", + "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.61.0", - "@typescript-eslint/types": "8.61.0", - "@typescript-eslint/typescript-estree": "8.61.0" + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4014,13 +4014,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz", - "integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/types": "8.61.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4562,14 +4562,14 @@ ] }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", - "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", + "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.9", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -4583,8 +4583,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.8", - "vitest": "4.1.8" + "@vitest/browser": "4.1.9", + "vitest": "4.1.9" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -4593,16 +4593,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", - "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -4611,13 +4611,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", - "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.8", + "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -4638,9 +4638,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { @@ -4651,13 +4651,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", - "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { @@ -4665,14 +4665,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", - "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -4681,9 +4681,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", - "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { @@ -4691,13 +4691,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", - "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -7258,9 +7258,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", - "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", + "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7500,9 +7500,9 @@ ] }, "node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7513,32 +7513,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/esbuild-sass-plugin": { @@ -7626,11 +7626,14 @@ } }, "node_modules/eslint": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", - "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", + "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -7989,9 +7992,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "63.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.2.tgz", - "integrity": "sha512-0TchoK1uS4VxHSo3P4CyWQ31Lm+6zsT+xkHMC5KbFKwgOf8YrXPf1Bl8EP7kpgw1wfe/Ui5jz5mSX7ou8WAVuw==", + "version": "63.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.5.tgz", + "integrity": "sha512-AzI9bgKhV9li049/mIblX0c41DeWMMfH9qNsRasc+fAxwURRKChIp03Pk57M7UTf+Y6hifTJ89kQyCOoOLtEDw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -8297,13 +8300,13 @@ "license": "MIT" }, "node_modules/eslint-plugin-perfectionist": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.9.0.tgz", - "integrity": "sha512-8TWzg02zmnBdZwCkWLi8jhzqXI+fE7Z/RwV8SL6xD45tJ8Bp3wGuYL2XtQgfe/Wd0eBqOUX+s6ey73IyszvKTA==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.9.1.tgz", + "integrity": "sha512-30mHLNfEhzwaq5cquyWgnzrNXvT8AzwIwyeH5aj4U5ajhHSF2uiO6i09xpMDLv7koaZVTjLsvYF4m3gK/15tyA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.58.2", + "@typescript-eslint/utils": "^8.61.0", "natural-orderby": "^5.0.0" }, "engines": { @@ -10526,10 +10529,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -11194,10 +11207,20 @@ "license": "MIT" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -11429,15 +11452,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -12867,9 +12900,9 @@ } }, "node_modules/obsidian-dev-utils": { - "version": "70.2.0", - "resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-70.2.0.tgz", - "integrity": "sha512-3xDwwrk/LGMrnAnS9hQgWDgCRhsF8iA9WI298NfJXHl1D47Nx0CSqQ+6SZ1IOqgG9VSIs/vMDITJHAv/wcG2fA==", + "version": "72.0.0", + "resolved": "https://registry.npmjs.org/obsidian-dev-utils/-/obsidian-dev-utils-72.0.0.tgz", + "integrity": "sha512-2MljsawOp7o8FF8aVf7yKnJusjm6ho9eFvChaIPEIpTp/cHnbLHHrxv5Ymlwjs/nh1rtTV+j4oLnHoqAjI6ZpA==", "dev": true, "license": "MIT", "dependencies": { @@ -12880,7 +12913,7 @@ "@eslint/config-helpers": "^0.6.0", "@eslint/js": "^10.0.1", "@lezer/common": "1.2.3", - "@obsidian-typings/obsidian-public-latest": "^6.14.0", + "@obsidian-typings/obsidian-public-latest": "^6.15.0", "@stylistic/eslint-plugin": "^5.10.0", "@tsconfig/strictest": "^2.0.8", "@types/babel__core": "^7.20.5", @@ -12895,24 +12928,24 @@ "@types/react": "^19.2.17", "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", - "@typescript-eslint/eslint-plugin": "^8.61.0", - "@typescript-eslint/parser": "^8.61.0", + "@typescript-eslint/eslint-plugin": "^8.61.1", + "@typescript-eslint/parser": "^8.61.1", "commander": "^15.0.0", "compare-versions": "^6.1.1", "cspell": "^10.0.1", "debug": "^4.4.3", "dprint": "^0.54.0", - "enhanced-resolve": "^5.23.0", - "esbuild": "^0.28.0", + "enhanced-resolve": "^5.24.0", + "esbuild": "^0.28.1", "esbuild-sass-plugin": "^3.7.0", "esbuild-svelte": "^0.9.5", - "eslint": "^10.4.1", + "eslint": "^10.5.0", "eslint-import-resolver-typescript": "^4.4.5", "eslint-plugin-import-x": "^4.16.2", - "eslint-plugin-jsdoc": "^63.0.2", + "eslint-plugin-jsdoc": "^63.0.5", "eslint-plugin-no-unsanitized": "^4.1.5", "eslint-plugin-obsidianmd": "^0.3.0", - "eslint-plugin-perfectionist": "^5.9.0", + "eslint-plugin-perfectionist": "^5.9.1", "eslint-plugin-tsdoc": "^0.5.2", "globals": "^17.6.0", "i18next": "^26.3.1", @@ -12933,20 +12966,36 @@ "remark": "^15.0.1", "remark-parse": "^11.0.0", "remark-wiki-link": "^2.0.1", - "sass": "^1.100.0", + "sass": "^1.101.0", "semver": "^7.8.4", "shell-quote": "^1.8.4", "svelte-check": "^4.6.0", "svelte-preprocess": "^6.0.5", "type-fest": "^5.7.0", "typescript": "^6.0.3", - "typescript-eslint": "^8.61.0", + "typescript-eslint": "^8.61.1", "unist-util-visit": "^5.1.0" }, "engines": { "node": ">=22.0.0" } }, + "node_modules/obsidian-dev-utils/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/obsidian-dev-utils/node_modules/markdownlint": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.41.0.tgz", @@ -12971,6 +13020,41 @@ "url": "https://github.com/sponsors/DavidAnson" } }, + "node_modules/obsidian-dev-utils/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/obsidian-dev-utils/node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/obsidian-dev-utils/node_modules/string-width": { "version": "8.2.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", @@ -12989,11 +13073,10 @@ } }, "node_modules/obsidian-integration-testing": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/obsidian-integration-testing/-/obsidian-integration-testing-4.2.3.tgz", - "integrity": "sha512-TsKjz+YfRjh5QH8G8ymPnt8dlH/UlvAq0uW69U6VvUIf9jZaLjHY2aVsh5gi33I5BJMLDxkC+hKB6cYQXeWNag==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/obsidian-integration-testing/-/obsidian-integration-testing-4.2.6.tgz", + "integrity": "sha512-8kjouwiDT1EryrQ5/hTcJlaj3AsB8il3z22UMS5d0gG2k6DVYdMBnD2gweK7fwdH7YZE1bA3+a1KpNnq+VdeLw==", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { "puppeteer-core": "^25.1.0", @@ -13005,11 +13088,10 @@ } }, "node_modules/obsidian-test-mocks": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/obsidian-test-mocks/-/obsidian-test-mocks-3.1.0.tgz", - "integrity": "sha512-svWbUYnm/IPc3hECd/Qe9S+86JWJZnMEKnquN+p744r8DWIxjI2DcuYCkFwSgJzaaQn4s55jEm2jxXEc91NWGw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/obsidian-test-mocks/-/obsidian-test-mocks-3.1.1.tgz", + "integrity": "sha512-pM/nszodPN+94ktMo3yflyZVu+EOZn0qYWjatJeqWi44iQCTjlsoTcbJdV3k4PxmcZO+ApgrNyhywcTR6/RBoQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { "@codemirror/state": "6.5.0", @@ -15876,16 +15958,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.61.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.0.tgz", - "integrity": "sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", + "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.61.0", - "@typescript-eslint/parser": "8.61.0", - "@typescript-eslint/typescript-estree": "8.61.0", - "@typescript-eslint/utils": "8.61.0" + "@typescript-eslint/eslint-plugin": "8.61.1", + "@typescript-eslint/parser": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -15926,9 +16008,9 @@ } }, "node_modules/undici": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", - "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "dev": true, "license": "MIT", "engines": { @@ -16281,19 +16363,19 @@ } }, "node_modules/vitest": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", - "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.8", - "@vitest/mocker": "4.1.8", - "@vitest/pretty-format": "4.1.8", - "@vitest/runner": "4.1.8", - "@vitest/snapshot": "4.1.8", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -16321,12 +16403,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.8", - "@vitest/browser-preview": "4.1.8", - "@vitest/browser-webdriverio": "4.1.8", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/package.json b/package.json index 73d00ad..12748dc 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,8 @@ "overrides": { "@vitest/coverage-v8": "$@vitest/coverage-v8", "eslint": "$eslint", + "js-yaml": "^4.2.0", + "markdown-it": "^14.2.0", "type-fest": "^5.6.0", "typescript": "^6.0.3" }, @@ -44,23 +46,23 @@ "@commitlint/cli": "^21.0.2", "@commitlint/config-conventional": "^21.0.2", "@commitlint/types": "^21.0.1", - "@obsidian-typings/obsidian-public-latest": "^6.14.0", + "@obsidian-typings/obsidian-public-latest": "^6.15.0", "@total-typescript/ts-reset": "^0.6.1", "@tsconfig/strictest": "^2.0.8", "@types/node": "^25.9.3", - "@vitest/coverage-v8": "^4.1.8", + "@vitest/coverage-v8": "^4.1.9", "better-typescript-lib": "^2.12.0", "czg": "^1.13.1", - "eslint": "^10.4.1", + "eslint": "^10.5.0", "husky": "^9.1.7", "jiti": "^2.7.0", "jsdom": "^29.1.1", "nano-staged": "^1.0.2", "obsidian": "^1.13.1", - "obsidian-dev-utils": "^70.2.0", - "obsidian-integration-testing": "^4.2.3", - "obsidian-test-mocks": "^3.1.0", + "obsidian-dev-utils": "^72.0.0", + "obsidian-integration-testing": "^4.2.6", + "obsidian-test-mocks": "^3.1.1", "sass-embedded": "^1.100.0", - "vitest": "^4.1.8" + "vitest": "^4.1.9" } } diff --git a/src/backlink-full-path-component.ts b/src/backlink-full-path-component.ts new file mode 100644 index 0000000..db84c62 --- /dev/null +++ b/src/backlink-full-path-component.ts @@ -0,0 +1,201 @@ +import type { BacklinkView } from '@obsidian-typings/obsidian-public-latest'; +import type { + App, + TFile +} from 'obsidian'; + +import { + InternalPluginName, + ViewType +} from '@obsidian-typings/obsidian-public-latest/implementations'; +import { + MarkdownView, + setTooltip +} from 'obsidian'; +import { invokeAsyncSafely } from 'obsidian-dev-utils/async'; +import { getPrototypeOf } from 'obsidian-dev-utils/object-utils'; +import { LayoutReadyComponent } from 'obsidian-dev-utils/obsidian/components/layout-ready-component'; +import { MonkeyAroundComponent } from 'obsidian-dev-utils/obsidian/components/monkey-around-component'; + +import { PluginSettingsComponent } from './plugin-settings-component.ts'; + +interface BacklinkFullPathComponentConstructorParams { + readonly app: App; + readonly pluginSettingsComponent: PluginSettingsComponent; +} + +export class BacklinkFullPathComponent extends LayoutReadyComponent { + private readonly pluginSettingsComponent: PluginSettingsComponent; + + public constructor(params: BacklinkFullPathComponentConstructorParams) { + super(params.app); + + this.pluginSettingsComponent = params.pluginSettingsComponent; + } + + protected override async onLayoutReady(): Promise { + this.pluginSettingsComponent.on('saveSettings', async () => { + await this.refreshBacklinkPanels(); + }); + + const backlinksCorePlugin = this.app.internalPlugins.getPluginById(InternalPluginName.Backlink); + if (!backlinksCorePlugin) { + return; + } + + const patch = this.addChild(new MonkeyAroundComponent()); + + patch.registerMethodPatch({ + methodName: 'onUserEnable', + obj: getPrototypeOf(backlinksCorePlugin.instance), + patchHandler: ({ + fallback + }) => { + fallback(); + this.onBacklinksCorePluginEnable(); + } + }); + + if (backlinksCorePlugin.enabled) { + await this.patchBacklinksPane(); + await this.refreshBacklinkPanels(); + } + + this.register(() => { + invokeAsyncSafely(async () => { + await this.refreshBacklinkPanels(); + }); + }); + } + + private generateBacklinkTitle(file: TFile): HTMLDivElement { + const fileNamePart = this.pluginSettingsComponent.settings.shouldIncludeExtension ? file.name : file.basename; + + let parentPathParts = file.path.split('/').slice(0, -1); + + for (let length = parentPathParts.length; length >= 1; length--) { + const rootPath = parentPathParts.slice(0, length).join('/'); + if (this.pluginSettingsComponent.settings.rootPaths.includes(rootPath)) { + parentPathParts = parentPathParts.slice(length); + break; + } + } + + if (this.pluginSettingsComponent.settings.pathDepth > 0) { + const partsToSkipCount = Math.max(0, parentPathParts.length - this.pluginSettingsComponent.settings.pathDepth + 1); + if (partsToSkipCount > 0) { + parentPathParts.splice(0, partsToSkipCount); + if (this.pluginSettingsComponent.settings.shouldShowEllipsisForSkippedPathParts) { + parentPathParts.unshift('...'); + } + } + } + + if (this.pluginSettingsComponent.settings.shouldReversePathParts) { + parentPathParts.reverse(); + } + + const pathSeparator = this.pluginSettingsComponent.settings.shouldReversePathParts ? ' \u2190 ' : '/'; + const parentStr = parentPathParts.join(pathSeparator); + + const container = createDiv({ + cls: ['backlink-full-path', 'backlink-control'] + }); + container.dataset['shouldHighlightFileName'] = this.pluginSettingsComponent.settings.shouldHighlightFileName.toString(); + container.dataset['shouldDisplayParentPathOnSeparateLine'] = this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine.toString(); + container.createSpan({ + cls: 'full-path', + text: file.path + }); + const shadowRoot = container.attachShadow({ mode: 'open' }); + setTooltip(container, file.path); + shadowRoot.createSpan({ + attr: { + part: 'file-name' + }, + text: fileNamePart + }); + + if (parentStr) { + let text = parentStr; + if (!this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine) { + text = this.pluginSettingsComponent.settings.shouldReversePathParts ? pathSeparator + text : text + pathSeparator; + } + shadowRoot.createSpan({ + attr: { + part: 'parent-path' + }, + prepend: !this.pluginSettingsComponent.settings.shouldReversePathParts && !this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine, + text + }); + } + + return container; + } + + private async getBacklinkView(): Promise { + const backlinksLeaf = this.app.workspace.getLeavesOfType(ViewType.Backlink)[0]; + if (!backlinksLeaf) { + return null; + } + + await backlinksLeaf.loadIfDeferred(); + return backlinksLeaf.view as BacklinkView; + } + + private onBacklinksCorePluginEnable(): void { + invokeAsyncSafely(() => this.patchBacklinksPane()); + } + + private async patchBacklinksPane(): Promise { + const backlinkView = await this.getBacklinkView(); + if (!backlinkView) { + return; + } + + const patch = this.addChild(new MonkeyAroundComponent()); + + patch.registerMethodPatch({ + methodName: 'addResult', + obj: getPrototypeOf(backlinkView.backlink.backlinkDom), + patchHandler: ({ + fallback, + originalArgs: [file] + }) => { + const resultDomItem = fallback(); + const fileNameCaptionEl = resultDomItem.el.querySelector('.tree-item-inner'); + if (fileNameCaptionEl) { + fileNameCaptionEl.empty(); + fileNameCaptionEl.appendChild(this.generateBacklinkTitle(file)); + } + return resultDomItem; + } + }); + } + + private async refreshBacklinkPanels(): Promise { + await this.reloadBacklinksView(); + + for (const leaf of this.app.workspace.getLeavesOfType(ViewType.Markdown)) { + if (!(leaf.view instanceof MarkdownView)) { + continue; + } + + if (!leaf.view.backlinks) { + continue; + } + + leaf.view.backlinks.recomputeBacklink(leaf.view.backlinks.file); + } + } + + private async reloadBacklinksView(): Promise { + const backlinkView = await this.getBacklinkView(); + if (!backlinkView) { + return; + } + if (backlinkView.file) { + backlinkView.backlink.recomputeBacklink(backlinkView.file); + } + } +} diff --git a/src/main.test.ts b/src/main.test.ts index dfdbbc5..a49ec36 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -1,9 +1,3 @@ -/** - * @file - * - * Tests for the main module default export. - */ - import { describe, expect, diff --git a/src/plugin-settings-component.ts b/src/plugin-settings-component.ts index b79badd..b23427b 100644 --- a/src/plugin-settings-component.ts +++ b/src/plugin-settings-component.ts @@ -1,9 +1,3 @@ -/** - * @file - * - * Settings component that manages plugin settings persistence. - */ - import type { DataHandler } from 'obsidian-dev-utils/obsidian/data-handler'; import type { PluginEventSource } from 'obsidian-dev-utils/obsidian/plugin/plugin-event-source'; diff --git a/src/plugin-settings-tab.test.ts b/src/plugin-settings-tab.test.ts index a42d274..a06c3ac 100644 --- a/src/plugin-settings-tab.test.ts +++ b/src/plugin-settings-tab.test.ts @@ -1,9 +1,3 @@ -/** - * @file - * - * Tests for the PluginSettingsTab class. - */ - import type { PluginManifest } from 'obsidian'; import { @@ -21,15 +15,10 @@ import { PluginSettingsTab } from './plugin-settings-tab.ts'; import { Plugin } from './plugin.ts'; interface DisplayedTabResult { - names: string[]; - tab: PluginSettingsTab; + readonly names: string[]; + readonly tab: PluginSettingsTab; } -/** - * Creates a PluginSettingsTab attached to a freshly created Plugin and calls display(). - * - * @returns An object with the tab and the extracted setting names. - */ function createDisplayedTab(): DisplayedTabResult { const plugin = createPlugin(); const pluginSettingsComponent = Reflect.get(plugin, 'pluginSettingsComponent') as InstanceType< @@ -41,17 +30,11 @@ function createDisplayedTab(): DisplayedTabResult { pluginSettingsComponent }); - // eslint-disable-next-line @typescript-eslint/no-deprecated -- Not ready to migrate `display()`. - tab.display(); + tab.displayLegacy(); const names = getSettingNames(tab.containerEl); return { names, tab }; } -/** - * Creates a Plugin instance with mocked dependencies. - * - * @returns The created Plugin. - */ function createPlugin(): Plugin { const app = App.createConfigured__(); const manifest: PluginManifest = { @@ -65,15 +48,6 @@ function createPlugin(): Plugin { return new Plugin(app.asOriginalType__(), manifest); } -/** - * Extracts setting names from the tab's containerEl after calling display(). - * - * Each Setting mock creates a settingEl > [controlEl, infoEl > [nameEl, descEl]]. - * The nameEl is the second-level div that contains the setting name text. - * - * @param containerEl - The container element. - * @returns The array of setting name strings. - */ function getSettingNames(containerEl: HTMLElement): string[] { const names: string[] = []; for (const settingEl of containerEl.children) { @@ -90,10 +64,6 @@ function getSettingNames(containerEl: HTMLElement): string[] { return names; } -/** - * Patches missing mock properties on obsidian-test-mocks components - * that obsidian-dev-utils's `bind()` method probes via strict proxy. - */ function patchMissingMockProperties(): void { // Obsidian-dev-utils checks for setPlaceholderValue to detect text-based components. if (!('setPlaceholderValue' in ToggleComponent.prototype)) { diff --git a/src/plugin-settings-tab.ts b/src/plugin-settings-tab.ts index 37a19ac..defd4ab 100644 --- a/src/plugin-settings-tab.ts +++ b/src/plugin-settings-tab.ts @@ -6,9 +6,8 @@ import { SettingEx } from 'obsidian-dev-utils/obsidian/setting-ex'; import type { PluginSettings } from './plugin-settings.ts'; export class PluginSettingsTab extends PluginSettingsTabBase { - public override display(): void { - // eslint-disable-next-line @typescript-eslint/no-deprecated -- Not ready to migrate `display()`. - super.display(); + public override displayLegacy(): void { + super.displayLegacy(); new Setting(this.containerEl) .setName('Include extension') diff --git a/src/plugin-settings.test.ts b/src/plugin-settings.test.ts index bbb9cfa..12fdba9 100644 --- a/src/plugin-settings.test.ts +++ b/src/plugin-settings.test.ts @@ -1,9 +1,3 @@ -/** - * @file - * - * Tests for PluginSettings default values. - */ - import { describe, expect, diff --git a/src/plugin-settings.ts b/src/plugin-settings.ts index 057e5a5..a19b27c 100644 --- a/src/plugin-settings.ts +++ b/src/plugin-settings.ts @@ -1,45 +1,15 @@ -/** - * @file - * - * Plugin settings data class with default values. - */ - -/** - * Settings for the Backlink Full Path plugin. - */ export class PluginSettings { - /** - * The depth of the path to include in backlinks (0 for unlimited). - */ public pathDepth = 0; - /** - * The paths to be treated as root paths. - */ public rootPaths: string[] = []; - /** - * Whether to display the parent path on a separate line. - */ public shouldDisplayParentPathOnSeparateLine = false; - /** - * Whether to highlight the file name. - */ public shouldHighlightFileName = true; - /** - * Whether to include the file extension in backlinks. - */ public shouldIncludeExtension = true; - /** - * Whether to reverse the path parts. - */ public shouldReversePathParts = false; - /** - * Whether to show ellipsis for skipped path parts. - */ public shouldShowEllipsisForSkippedPathParts = true; } diff --git a/src/plugin.test.ts b/src/plugin.test.ts index 104d9dc..01f0d0a 100644 --- a/src/plugin.test.ts +++ b/src/plugin.test.ts @@ -1,9 +1,3 @@ -/** - * @file - * - * Tests for the Plugin class. - */ - import type { BacklinkView, ResultDom, @@ -48,16 +42,10 @@ interface SettingsComponentProxy { } interface TestPluginResult { - app: ReturnType; - plugin: Plugin; + readonly app: ReturnType; + readonly plugin: Plugin; } -/** - * Creates a mock TFile for testing. - * - * @param path - The file path. - * @returns A mock TFile. - */ function createMockFile(path: string): TFile { const parts = path.split('/'); const name = parts.at(-1) ?? ''; @@ -72,12 +60,6 @@ function createMockFile(path: string): TFile { }); } -/** - * Creates a mock ResultDomItem with an element that has .tree-item-inner. - * - * @param hasTreeItemInner - Whether the result element should have a .tree-item-inner child. - * @returns The mock ResultDomItem. - */ function createMockResultDomItem(hasTreeItemInner: boolean): ResultDomItem { const el = activeDocument.createElement('div'); if (hasTreeItemInner) { @@ -89,11 +71,6 @@ function createMockResultDomItem(hasTreeItemInner: boolean): ResultDomItem { return mockProxy({ el }); } -/** - * Creates a minimal PluginManifest for testing. - * - * @returns A test PluginManifest. - */ function createTestManifest(): PluginManifest { return { author: 'test', @@ -105,11 +82,6 @@ function createTestManifest(): PluginManifest { }; } -/** - * Creates a test Plugin instance with a configured App. - * - * @returns The plugin and app. - */ function createTestPlugin(): TestPluginResult { const app = App.createConfigured__(); const manifest = createTestManifest(); @@ -117,49 +89,22 @@ function createTestPlugin(): TestPluginResult { return { app, plugin }; } -/** - * Gets the plugin's private pluginSettingsComponent via Reflect. - * - * @param plugin - The plugin instance. - * @returns The pluginSettingsComponent. - */ function getSettingsComponent(plugin: Plugin): SettingsComponentProxy { return Reflect.get(plugin, 'pluginSettingsComponent') as SettingsComponentProxy; } -/** - * Marks the plugin and its internal wrapperComponent as loaded - * without going through the full load lifecycle. - * - * @param plugin - The plugin to mark as loaded. - */ function markPluginLoaded(plugin: Plugin): void { Reflect.set(bypassStrictProxy(plugin), '_loaded', true); const wrapperComponent = Reflect.get(bypassStrictProxy(plugin), 'wrapperComponent') as object; Reflect.set(bypassStrictProxy(wrapperComponent), '_loaded', true); } -/** - * Sets up internalPlugins mock on the plugin's app. - * - * @param plugin - The plugin. - * @param returnValue - The value to return from getPluginById. - * @returns The mocked getPluginById function. - */ function mockInternalPlugins(plugin: Plugin, returnValue: unknown): ReturnType { const getPluginById = vi.fn().mockReturnValue(returnValue); Reflect.set(bypassStrictProxy(plugin.app), 'internalPlugins', strictProxy({ getPluginById })); return getPluginById; } -/** - * Mocks `obsidianDevUtilsState` on both the plugin's app and the - * global `app` so that `super.onload()` can complete without errors. - * The library accesses state via the passed app instance and via the - * global `getApp()` fallback (`globalThis.app`). - * - * @param plugin - The plugin whose app needs the mock. - */ function mockObsidianDevUtilsState(plugin: Plugin): void { Reflect.set(bypassStrictProxy(plugin.app), 'obsidianDevUtilsState', {}); const globalApp = Reflect.get(window, 'app') as unknown; @@ -168,25 +113,11 @@ function mockObsidianDevUtilsState(plugin: Plugin): void { } } -/** - * Creates a strict proxy, working around `PartialDeep` incompatibility - * with complex DOM types by casting the partial before passing to strictProxy. - * - * @param partial - A partial object with the mocked members. - * @returns A strict proxy typed as T. - */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used in the body for strictProxy. function mockProxy(partial: object): T { return strictProxy(partial as StrictProxyPartial); } -/** - * Calls a private/protected method on an object via Reflect and binds it. - * - * @param obj - The object. - * @param name - The method name. - * @returns The bound method. - */ function privateMethod(obj: object, name: string): (...args: never[]) => unknown { const fn = Reflect.get(obj, name) as (...args: never[]) => unknown; return fn.bind(obj); @@ -404,12 +335,6 @@ describe('Plugin', () => { settings = getSettingsComponent(plugin).settings; }); - /** - * Calls the private generateBacklinkTitle method. - * - * @param file - The TFile to generate a title for. - * @returns The generated HTMLDivElement. - */ function callGenerateBacklinkTitle(file: TFile): HTMLDivElement { const fn = privateMethod(plugin, 'generateBacklinkTitle'); return (fn as (...args: unknown[]) => unknown)(file) as HTMLDivElement; diff --git a/src/plugin.ts b/src/plugin.ts index 5b3fa07..18fec5b 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,62 +1,15 @@ -/** - * @file - * - * Main plugin class for the Backlink Full Path plugin. - */ - -import type { - BacklinkPlugin, - BacklinkView, - ResultDom, - ResultDomItem, - ResultDomResult -} from '@obsidian-typings/obsidian-public-latest'; -import type { - App, - PluginManifest, - TFile -} from 'obsidian'; - -import { - InternalPluginName, - ViewType -} from '@obsidian-typings/obsidian-public-latest/implementations'; -import { - MarkdownView, - setTooltip -} from 'obsidian'; -import { invokeAsyncSafely } from 'obsidian-dev-utils/async'; -import { getPrototypeOf } from 'obsidian-dev-utils/object-utils'; -import { MonkeyAroundComponent } from 'obsidian-dev-utils/obsidian/components/monkey-around-component'; import { PluginSettingsTabComponent } from 'obsidian-dev-utils/obsidian/components/plugin-settings-tab-component'; import { PluginDataHandler } from 'obsidian-dev-utils/obsidian/data-handler'; import { PluginBase } from 'obsidian-dev-utils/obsidian/plugin/plugin'; import { PluginEventSourceImpl } from 'obsidian-dev-utils/obsidian/plugin/plugin-event-source'; +import { BacklinkFullPathComponent } from './backlink-full-path-component.ts'; import { PluginSettingsComponent } from './plugin-settings-component.ts'; import { PluginSettingsTab } from './plugin-settings-tab.ts'; -/** - * Type alias for the `addResult` method on `ResultDom`. - */ -type AddResultFn = ResultDom['addResult']; - -/** - * The Backlink Full Path plugin. Replaces backlink titles with full paths. - */ export class Plugin extends PluginBase { - private readonly pluginSettingsComponent: PluginSettingsComponent; - - /** - * Creates a new Backlink Full Path plugin. - * - * @param app - The Obsidian app instance. - * @param manifest - The plugin manifest. - */ - public constructor(app: App, manifest: PluginManifest) { - super(app, manifest); - - this.pluginSettingsComponent = this.addChild( + protected override onloadImpl(): void { + const pluginSettingsComponent = this.addChild( new PluginSettingsComponent({ dataHandler: new PluginDataHandler(this), pluginEventSource: new PluginEventSourceImpl(this) @@ -68,183 +21,16 @@ export class Plugin extends PluginBase { plugin: this, pluginSettingsTab: new PluginSettingsTab({ plugin: this, - pluginSettingsComponent: this.pluginSettingsComponent + pluginSettingsComponent }) }) ); - } - /** - * Called after pre-loaded components are initialized. - */ - public override async onload(): Promise { - await super.onload(); - this.pluginSettingsComponent.on('saveSettings', async () => { - await this.refreshBacklinkPanels(); - }); - } - - /** - * Called when the workspace layout is ready. - */ - protected async onLayoutReady(): Promise { - const backlinksCorePlugin = this.app.internalPlugins.getPluginById(InternalPluginName.Backlink); - if (!backlinksCorePlugin) { - return; - } - - const that = this; - const patch = this.addChild(new MonkeyAroundComponent()); - patch.registerPatch(getPrototypeOf(backlinksCorePlugin.instance), { - onUserEnable: (next: () => void) => { - return function onUserEnablePatched(this: BacklinkPlugin): void { - next.call(this); - that.onBacklinksCorePluginEnable(); - }; - } - }); - - if (backlinksCorePlugin.enabled) { - await this.patchBacklinksPane(); - await this.refreshBacklinkPanels(); - } - - this.register(() => { - invokeAsyncSafely(async () => { - await this.refreshBacklinkPanels(); - }); - }); - } - - private addResult(next: AddResultFn, resultDom: ResultDom, file: TFile, result: ResultDomResult, content: string, shouldShowTitle?: boolean): ResultDomItem { - const resultDomItem = next.call(resultDom, file, result, content, shouldShowTitle); - const fileNameCaptionEl = resultDomItem.el.querySelector('.tree-item-inner'); - if (fileNameCaptionEl) { - fileNameCaptionEl.empty(); - fileNameCaptionEl.appendChild(this.generateBacklinkTitle(file)); - } - return resultDomItem; - } - - private generateBacklinkTitle(file: TFile): HTMLDivElement { - const fileNamePart = this.pluginSettingsComponent.settings.shouldIncludeExtension ? file.name : file.basename; - - let parentPathParts = file.path.split('/').slice(0, -1); - - for (let length = parentPathParts.length; length >= 1; length--) { - const rootPath = parentPathParts.slice(0, length).join('/'); - if (this.pluginSettingsComponent.settings.rootPaths.includes(rootPath)) { - parentPathParts = parentPathParts.slice(length); - break; - } - } - - if (this.pluginSettingsComponent.settings.pathDepth > 0) { - const partsToSkipCount = Math.max(0, parentPathParts.length - this.pluginSettingsComponent.settings.pathDepth + 1); - if (partsToSkipCount > 0) { - parentPathParts.splice(0, partsToSkipCount); - if (this.pluginSettingsComponent.settings.shouldShowEllipsisForSkippedPathParts) { - parentPathParts.unshift('...'); - } - } - } - - if (this.pluginSettingsComponent.settings.shouldReversePathParts) { - parentPathParts.reverse(); - } - - const pathSeparator = this.pluginSettingsComponent.settings.shouldReversePathParts ? ' \u2190 ' : '/'; - const parentStr = parentPathParts.join(pathSeparator); - - const container = createDiv({ - cls: ['backlink-full-path', 'backlink-control'] - }); - container.dataset['shouldHighlightFileName'] = this.pluginSettingsComponent.settings.shouldHighlightFileName.toString(); - container.dataset['shouldDisplayParentPathOnSeparateLine'] = this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine.toString(); - container.createSpan({ - cls: 'full-path', - text: file.path - }); - const shadowRoot = container.attachShadow({ mode: 'open' }); - setTooltip(container, file.path); - shadowRoot.createSpan({ - attr: { - part: 'file-name' - }, - text: fileNamePart - }); - - if (parentStr) { - let text = parentStr; - if (!this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine) { - text = this.pluginSettingsComponent.settings.shouldReversePathParts ? pathSeparator + text : text + pathSeparator; - } - shadowRoot.createSpan({ - attr: { - part: 'parent-path' - }, - prepend: !this.pluginSettingsComponent.settings.shouldReversePathParts && !this.pluginSettingsComponent.settings.shouldDisplayParentPathOnSeparateLine, - text - }); - } - - return container; - } - - private async getBacklinkView(): Promise { - const backlinksLeaf = this.app.workspace.getLeavesOfType(ViewType.Backlink)[0]; - if (!backlinksLeaf) { - return null; - } - - await backlinksLeaf.loadIfDeferred(); - return backlinksLeaf.view as BacklinkView; - } - - private onBacklinksCorePluginEnable(): void { - invokeAsyncSafely(() => this.patchBacklinksPane()); - } - - private async patchBacklinksPane(): Promise { - const backlinkView = await this.getBacklinkView(); - if (!backlinkView) { - return; - } - - const that = this; - const patch = this.addChild(new MonkeyAroundComponent()); - patch.registerPatch(getPrototypeOf(backlinkView.backlink.backlinkDom), { - addResult: (next: AddResultFn): AddResultFn => { - return function addResultPatched(this: ResultDom, file: TFile, result: ResultDomResult, content: string, shouldShowTitle?: boolean): ResultDomItem { - return that.addResult(next, this, file, result, content, shouldShowTitle); - }; - } - }); - } - - private async refreshBacklinkPanels(): Promise { - await this.reloadBacklinksView(); - - for (const leaf of this.app.workspace.getLeavesOfType(ViewType.Markdown)) { - if (!(leaf.view instanceof MarkdownView)) { - continue; - } - - if (!leaf.view.backlinks) { - continue; - } - - leaf.view.backlinks.recomputeBacklink(leaf.view.backlinks.file); - } - } - - private async reloadBacklinksView(): Promise { - const backlinkView = await this.getBacklinkView(); - if (!backlinkView) { - return; - } - if (backlinkView.file) { - backlinkView.backlink.recomputeBacklink(backlinkView.file); - } + this.addChild( + new BacklinkFullPathComponent({ + app: this.app, + pluginSettingsComponent + }) + ); } }