diff --git a/manifest.json b/manifest.json index dfa940e..c080331 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "sample-plugin", - "name": "Sample Plugin", + "id": "mindnet-causal-assistant", + "name": "Mindnet Causal Assistant", "version": "1.0.0", "minAppVersion": "0.15.0", - "description": "Demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", + "description": "Linting, chain explorer and guided authoring for Mindnet causal graphs.", + "author": "Lars Stommer", "authorUrl": "https://obsidian.md", "fundingUrl": "https://obsidian.md/pricing", - "isDesktopOnly": false + "isDesktopOnly": true } diff --git a/package-lock.json b/package-lock.json index d0dac39..5ac9ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,6 +3,11 @@ "version": "1.0.0", "lockfileVersion": 3, "requires": true, + "description": "Mindnet Causal Assistant (Obsidian Plugin): linting, chain explorer, guided authoring", + "keywords": [], + "license": "0-BSD", + "main": "main.js", + "type": "module", "packages": { "": { "name": "obsidian-sample-plugin", @@ -20,30 +25,8 @@ "jiti": "2.6.1", "tslib": "2.4.0", "typescript": "^5.8.3", - "typescript-eslint": "8.35.1" - } - }, - "node_modules/@codemirror/state": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", - "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.38.6", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", - "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@codemirror/state": "^6.5.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" + "typescript-eslint": "8.35.1", + "vitest": "^1.6.0" } }, "node_modules/@esbuild/aix-ppc64": { @@ -472,9 +455,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -555,9 +538,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -567,7 +550,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -591,23 +574,6 @@ "url": "https://eslint.org/donate" } }, - "node_modules/@eslint/json": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.14.0.tgz", - "integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@eslint/core": "^0.17.0", - "@eslint/plugin-kit": "^0.4.1", - "@humanwhocodes/momoa": "^3.3.10", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", @@ -670,17 +636,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/momoa": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.10.tgz", - "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", @@ -695,12 +650,25 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", - "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@microsoft/eslint-plugin-sdl": { "version": "1.1.0", @@ -791,6 +759,356 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -798,6 +1116,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/codemirror": { "version": "5.60.8", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", @@ -1069,19 +1394,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.35.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", @@ -1124,6 +1436,109 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1147,6 +1562,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1347,6 +1775,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1404,6 +1842,16 @@ "node": ">=8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1464,6 +1912,25 @@ "node": ">=6" } }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1481,6 +1948,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1508,12 +1988,12 @@ "dev": true, "license": "MIT" }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "license": "MIT", - "peer": true + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -1602,6 +2082,19 @@ } } }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1645,6 +2138,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -1684,9 +2187,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1698,9 +2201,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", "dependencies": { @@ -1787,27 +2290,27 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.1", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", + "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" }, "engines": { @@ -1929,9 +2432,9 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { @@ -1941,7 +2444,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2004,19 +2507,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/eslint-compat-utils/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -2079,19 +2569,6 @@ "semver": "^7.6.3" } }, - "node_modules/eslint-plugin-depend/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-es-x": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", @@ -2158,6 +2635,16 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-json-schema-validator": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-json-schema-validator/-/eslint-plugin-json-schema-validator-5.1.0.tgz", @@ -2302,19 +2789,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-obsidianmd": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.1.9.tgz", @@ -2438,6 +2912,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-security": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz", @@ -2479,9 +2963,9 @@ } }, "node_modules/eslint/node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -2510,9 +2994,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2545,6 +3029,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2555,6 +3049,30 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2624,9 +3142,9 @@ "license": "BSD-3-Clause" }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -2713,6 +3231,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2764,6 +3297,16 @@ "node": ">= 0.4" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2803,6 +3346,19 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -2998,6 +3554,16 @@ "node": ">= 0.4" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3345,6 +3911,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -3573,9 +4152,9 @@ } }, "node_modules/jsonc-eslint-parser": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.1.tgz", - "integrity": "sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", "dev": true, "license": "MIT", "dependencies": { @@ -3622,19 +4201,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/jsonc-eslint-parser/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -3675,6 +4241,23 @@ "node": ">= 0.8.0" } }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3711,6 +4294,26 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3721,6 +4324,13 @@ "node": ">= 0.4" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3745,6 +4355,19 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3768,6 +4391,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/module-replacements": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.10.1.tgz", @@ -3791,6 +4434,25 @@ "dev": true, "license": "MIT" }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3798,6 +4460,35 @@ "dev": true, "license": "MIT" }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3922,9 +4613,9 @@ } }, "node_modules/obsidian": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz", - "integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.11.4.tgz", + "integrity": "sha512-n0KD3S+VndgaByrEtEe8NELy0ya6/s+KZ7OcxA6xOm5NN4thxKpQjo6eqEudHEvfGCeT/TYToAKJzitQ1I3XTg==", "license": "MIT", "dependencies": { "@types/codemirror": "5.60.8", @@ -3935,6 +4626,22 @@ "@codemirror/view": "6.38.6" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4043,6 +4750,30 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -4056,6 +4787,25 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -4066,6 +4816,35 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4076,6 +4855,41 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4252,6 +5066,51 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4363,13 +5222,16 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/set-function-length": { @@ -4520,6 +5382,50 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -4642,6 +5548,19 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4655,12 +5574,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", @@ -4726,6 +5658,33 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4769,9 +5728,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -4827,6 +5786,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -4942,6 +5911,13 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -4978,12 +5954,584 @@ "punycode": "^2.1.0" } }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } }, "node_modules/which": { "version": "2.0.2", @@ -5069,9 +6617,9 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -5090,6 +6638,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -5101,9 +6666,9 @@ } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", "bin": { @@ -5111,12 +6676,15 @@ }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yaml-eslint-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz", - "integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz", + "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==", "dev": true, "license": "MIT", "dependencies": { @@ -5156,5 +6724,24 @@ "url": "https://github.com/sponsors/sindresorhus" } } + }, + "devDependencies": { + "@eslint/js": "9.30.1", + "@types/node": "^16.11.6", + "esbuild": "0.25.5", + "eslint-plugin-obsidianmd": "0.1.9", + "globals": "14.0.0", + "jiti": "2.6.1", + "tslib": "2.4.0", + "typescript": "^5.8.3", + "typescript-eslint": "8.35.1" + }, + "scripts": { + "version": "node version-bump.mjs && git add manifest.json versions.json", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "build:deploy": "npm run build && npm run deploy:local", + "deploy:local": "powershell -ExecutionPolicy Bypass -File scripts/deploy-local.ps1", + "dev": "node esbuild.config.mjs", + "lint": "eslint ." } } diff --git a/package.json b/package.json index 17268d7..c6e943c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "dev": "node esbuild.config.mjs", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "version": "node version-bump.mjs && git add manifest.json versions.json", - "lint": "eslint ." + "deploy:local": "powershell -ExecutionPolicy Bypass -File scripts/deploy-local.ps1", + "build:deploy": "npm run build && npm run deploy:local", + "lint": "eslint .", + "test": "vitest run" }, "keywords": [], "license": "0-BSD", @@ -21,7 +24,8 @@ "typescript": "^5.8.3", "typescript-eslint": "8.35.1", "@eslint/js": "9.30.1", - "jiti": "2.6.1" + "jiti": "2.6.1", + "vitest": "^1.6.0" }, "dependencies": { "obsidian": "latest" diff --git a/scripts/deploy-local.ps1 b/scripts/deploy-local.ps1 new file mode 100644 index 0000000..e23f85d --- /dev/null +++ b/scripts/deploy-local.ps1 @@ -0,0 +1,16 @@ +param( + [string]$VaultPath = "\\nashome\mindnet\vault\mindnet_dev", + [string]$PluginId = "mindnet-causal-assistant" +) + +$pluginDir = Join-Path $VaultPath ".obsidian\plugins\$PluginId" +New-Item -ItemType Directory -Force -Path $pluginDir | Out-Null + +Copy-Item -Path ".\manifest.json" -Destination $pluginDir -Force +Copy-Item -Path ".\main.js" -Destination $pluginDir -Force + +if (Test-Path ".\styles.css") { + Copy-Item -Path ".\styles.css" -Destination $pluginDir -Force +} + +Write-Host "Deployed to $pluginDir" diff --git a/src/__mocks__/obsidian.ts b/src/__mocks__/obsidian.ts new file mode 100644 index 0000000..5f988ac --- /dev/null +++ b/src/__mocks__/obsidian.ts @@ -0,0 +1,25 @@ +// Mock for obsidian API in tests +export class Notice { + constructor(public message: string) {} +} + +export interface App { + vault: Vault; + workspace: Workspace; +} + +export interface Vault { + read(file: TFile): Promise; + modify(file: TFile, content: string): Promise; + getMarkdownFiles(): TFile[]; +} + +export interface Workspace { + getActiveFile(): TFile | null; +} + +export interface TFile { + path: string; + name: string; + extension: string; +} diff --git a/src/graph/GraphIndex.ts b/src/graph/GraphIndex.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/graph/traverse.ts b/src/graph/traverse.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/LintEngine.ts b/src/lint/LintEngine.ts new file mode 100644 index 0000000..f364eda --- /dev/null +++ b/src/lint/LintEngine.ts @@ -0,0 +1,229 @@ +import type { App, TFile } from "obsidian"; +import type { ParsedEdge } from "../parser/types"; +import type { Vocabulary } from "../vocab/Vocabulary"; +import type { Finding, QuickFix } from "./types"; +import { parseEdgesFromCallouts } from "../parser/parseEdgesFromCallouts"; + +const EDGE_HEADER_RE = /^\s*(>+)\s*\[!edge\]\s*(.+?)\s*$/i; + +/** + * Pure function to lint parsed edges against vocabulary and file existence. + * This can be tested independently. + */ +export function lintEdges( + parsedEdges: ParsedEdge[], + vocabulary: Vocabulary, + existingFilesSet: Set +): Finding[] { + const findings: Finding[] = []; + + for (const edge of parsedEdges) { + const normalized = vocabulary.normalize(edge.rawType); + + // Check for unknown edge type + if (normalized.canonical === null) { + findings.push({ + ruleId: "unknown_edge_type", + severity: "ERROR", + message: `Unknown edge type: "${edge.rawType}"`, + filePath: "", // Will be set by caller + lineStart: edge.lineStart, + lineEnd: edge.lineEnd, + evidence: edge.rawType, + }); + continue; + } + + // Check for alias not normalized + const rawLower = edge.rawType.trim().toLowerCase(); + const canonicalLower = normalized.canonical.toLowerCase(); + if (rawLower !== canonicalLower) { + findings.push({ + ruleId: "alias_not_normalized", + severity: "WARN", + message: `Edge type "${edge.rawType}" should be normalized to "${normalized.canonical}"`, + filePath: "", // Will be set by caller + lineStart: edge.lineStart, + lineEnd: edge.lineStart, + evidence: edge.rawType, + quickFixes: [], // Will be populated by caller with file context + }); + } + + // Check for missing target notes + for (const target of edge.targets) { + if (!target) continue; + + // Check if file exists (exact match to markdown file name) + // Target might be "Note Name" or "Note Name#heading" - we check the base name + const parts = target.split("#"); + const firstPart = parts[0]; + if (!firstPart) continue; + + const baseName = firstPart.trim(); + if (!baseName) continue; + + const markdownFileName = baseName.endsWith(".md") ? baseName : `${baseName}.md`; + + if (!existingFilesSet.has(markdownFileName) && !existingFilesSet.has(baseName)) { + findings.push({ + ruleId: "missing_target_note", + severity: "WARN", + message: `Target note "${target}" not found in vault`, + filePath: "", // Will be set by caller + lineStart: edge.lineStart, + lineEnd: edge.lineEnd, + evidence: target, + }); + } + } + } + + return findings; +} + +/** + * LintEngine for validating markdown notes containing edge definitions. + */ +export class LintEngine { + /** + * Lint the currently active markdown note. + */ + static async lintCurrentNote( + app: App, + vocabulary: Vocabulary + ): Promise { + const activeFile = app.workspace.getActiveFile(); + + if (!activeFile) { + throw new Error("No active file"); + } + + if (activeFile.extension !== "md") { + throw new Error("Active file is not a markdown file"); + } + + // Read file content + const content = await app.vault.read(activeFile); + + // Parse edges + const parsedEdges = parseEdgesFromCallouts(content); + + // Build set of existing markdown files in vault + const existingFilesSet = new Set(); + const markdownFiles = app.vault.getMarkdownFiles(); + for (const file of markdownFiles) { + existingFilesSet.add(file.name); + // Also add without .md extension for matching + if (file.name.endsWith(".md")) { + const baseName = file.name.slice(0, -3); + existingFilesSet.add(baseName); + } + } + + // Run pure linting logic + const findings = lintEdges(parsedEdges, vocabulary, existingFilesSet); + + // Set filePath and add quickfixes + const filePath = activeFile.path; + const lines = content.split(/\r?\n/); + + for (const finding of findings) { + finding.filePath = filePath; + + // Add quickfix for alias_not_normalized + if (finding.ruleId === "alias_not_normalized" && finding.lineStart !== undefined) { + const lineIndex = finding.lineStart; + const line = lines[lineIndex]; + + if (line) { + const normalized = vocabulary.normalize(finding.evidence || ""); + if (normalized.canonical) { + finding.quickFixes = [ + createNormalizeQuickFix( + app, + activeFile, + content, + lineIndex, + finding.evidence || "", + normalized.canonical + ), + ]; + } + } + } + } + + return findings; + } +} + +/** + * Create a quickfix that normalizes an edge type in the file. + */ +function createNormalizeQuickFix( + app: App, + file: TFile, + currentContent: string, + lineIndex: number, + rawType: string, + canonical: string +): QuickFix { + return { + id: "normalize_edge_type", + title: `Normalize to "${canonical}"`, + apply: async () => { + const { Notice } = await import("obsidian"); + const lines = currentContent.split(/\r?\n/); + const line = lines[lineIndex]; + + if (!line) { + new Notice("Line not found"); + return; + } + + // Match the edge header pattern + const match = line.match(EDGE_HEADER_RE); + if (!match || !match[2]) { + new Notice("Edge header pattern not found on line"); + return; + } + + // Find the position of the raw type in the line + // match[2] is the captured type, but we need to find where it appears in the original line + const edgeMarker = "[!edge]"; + const edgeIndex = line.indexOf(edgeMarker); + if (edgeIndex === -1) { + new Notice("Edge marker not found on line"); + return; + } + + // Find the type after [!edge] + const afterEdge = line.substring(edgeIndex + edgeMarker.length); + const typeMatch = afterEdge.match(/^\s+(\S+)/); + if (!typeMatch || typeMatch[1] !== rawType.trim()) { + new Notice("Type token not found at expected position"); + return; + } + + // Replace the raw type with canonical + const beforeType = line.substring(0, edgeIndex + edgeMarker.length + typeMatch[0].indexOf(typeMatch[1])); + const afterType = line.substring(beforeType.length + typeMatch[1].length); + const newLine = beforeType + canonical + afterType; + + // Safety check: verify the new line still matches the pattern + const verifyMatch = newLine.match(EDGE_HEADER_RE); + if (!verifyMatch) { + new Notice("Quickfix would produce invalid line - skipping"); + return; + } + + // Update the line + lines[lineIndex] = newLine; + const newContent = lines.join("\n"); + + // Write back to file + await app.vault.modify(file, newContent); + }, + }; +} diff --git a/src/lint/index.ts b/src/lint/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/rules/index.ts b/src/lint/rules/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/rules/rule_hub_has_causality.ts b/src/lint/rules/rule_hub_has_causality.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/rules/rule_missing_target.ts b/src/lint/rules/rule_missing_target.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/rules/rule_unkown_edge.ts b/src/lint/rules/rule_unkown_edge.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lint/types.ts b/src/lint/types.ts new file mode 100644 index 0000000..a80f731 --- /dev/null +++ b/src/lint/types.ts @@ -0,0 +1,18 @@ +export type Severity = "ERROR" | "WARN" | "INFO"; + +export interface QuickFix { + id: string; + title: string; + apply: () => Promise; +} + +export interface Finding { + ruleId: string; + severity: Severity; + message: string; + filePath: string; + lineStart?: number; + lineEnd?: number; + evidence?: string; + quickFixes?: QuickFix[]; +} diff --git a/src/main.ts b/src/main.ts index 6fe0c83..835e566 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,99 +1,176 @@ -import {App, Editor, MarkdownView, Modal, Notice, Plugin} from 'obsidian'; -import {DEFAULT_SETTINGS, MyPluginSettings, SampleSettingTab} from "./settings"; +import { Notice, Plugin, TFile } from "obsidian"; +import { DEFAULT_SETTINGS, type MindnetSettings, normalizeVaultPath } from "./settings"; +import { VocabularyLoader } from "./vocab/VocabularyLoader"; +import { parseEdgeVocabulary } from "./vocab/parseEdgeVocabulary"; +import { Vocabulary } from "./vocab/Vocabulary"; +import { LintEngine } from "./lint/LintEngine"; +import { MindnetSettingTab } from "./ui/MindnetSettingTab"; -// Remember to rename these classes and interfaces! +export default class MindnetCausalAssistantPlugin extends Plugin { + settings: MindnetSettings; + private vocabulary: Vocabulary | null = null; + private reloadDebounceTimer: number | null = null; -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; + async onload(): Promise { + await this.loadSettings(); - async onload() { - await this.loadSettings(); + // Add settings tab + this.addSettingTab(new MindnetSettingTab(this.app, this)); - // This creates an icon in the left ribbon. - this.addRibbonIcon('dice', 'Sample', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); + // Register live reload for edge vocabulary file + this.registerEvent( + this.app.vault.on("modify", async (file: TFile) => { + const normalizedFilePath = normalizeVaultPath(file.path); + const normalizedVocabPath = normalizeVaultPath(this.settings.edgeVocabularyPath); + + // Check if modified file matches vocabulary path (exact match or ends with) + if (normalizedFilePath === normalizedVocabPath || + normalizedFilePath === `/${normalizedVocabPath}` || + normalizedFilePath.endsWith(`/${normalizedVocabPath}`)) { + // Debounce reload to avoid multiple rapid reloads + if (this.reloadDebounceTimer !== null) { + window.clearTimeout(this.reloadDebounceTimer); + } + + this.reloadDebounceTimer = window.setTimeout(async () => { + await this.reloadVocabulary(); + this.reloadDebounceTimer = null; + }, 200); + } + }) + ); - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status bar text'); + this.addCommand({ + id: "mindnet-reload-edge-vocabulary", + name: "Mindnet: Reload edge vocabulary", + callback: async () => { + await this.reloadVocabulary(); + }, + }); - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-modal-simple', - name: 'Open modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'replace-selected', - name: 'Replace selected content', - editorCallback: (editor: Editor, view: MarkdownView) => { - editor.replaceSelection('Sample editor command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-modal-complex', - name: 'Open modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } + this.addCommand({ + id: "mindnet-validate-current-note", + name: "Mindnet: Validate current note", + callback: async () => { + try { + const vocabulary = await this.ensureVocabularyLoaded(); + if (!vocabulary) { + return; + } - // This command will only show up in Command Palette when the check function returns true - return true; - } - return false; - } - }); + const findings = await LintEngine.lintCurrentNote(this.app, vocabulary); - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); + // Count findings by severity + const errorCount = findings.filter(f => f.severity === "ERROR").length; + const warnCount = findings.filter(f => f.severity === "WARN").length; + const infoCount = findings.filter(f => f.severity === "INFO").length; - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - new Notice("Click"); - }); + // Show summary notice + new Notice(`Lint: ${errorCount} errors, ${warnCount} warnings, ${infoCount} info`); - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); + // Log findings to console + console.log("=== Lint Findings ==="); + for (const finding of findings) { + const quickfixInfo = finding.quickFixes && finding.quickFixes.length > 0 + ? ` [QuickFix: ${finding.quickFixes.map(qf => qf.title).join(", ")}]` + : ""; + console.log(`[${finding.severity}] ${finding.ruleId}: ${finding.message} (${finding.filePath}:${finding.lineStart}${quickfixInfo})`); + } + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + new Notice(`Failed to validate note: ${msg}`); + console.error(e); + } + }, + }); + } - } + onunload(): void { + // nothing yet + } - onunload() { - } + private async loadSettings(): Promise { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial); - } + async saveSettings(): Promise { + await this.saveData(this.settings); + } - async saveSettings() { - await this.saveData(this.settings); - } -} - -class SampleModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - let {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } + /** + * Ensure vocabulary is loaded. Auto-loads if not present. + * Returns Vocabulary instance or null on failure. + */ + private async ensureVocabularyLoaded(): Promise { + if (this.vocabulary) { + return this.vocabulary; + } + + try { + const text = await VocabularyLoader.loadText( + this.app, + this.settings.edgeVocabularyPath + ); + + const parsed = parseEdgeVocabulary(text); + this.vocabulary = new Vocabulary(parsed); + + const stats = this.vocabulary.getStats(); + console.log("Vocabulary auto-loaded", stats); + return this.vocabulary; + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + if (msg.includes("not found") || msg.includes("Vocabulary file not found")) { + new Notice("edge_vocabulary.md not found. Check the path in plugin settings."); + } else { + new Notice(`Failed to load vocabulary: ${msg}. Check plugin settings.`); + } + console.error("Failed to load vocabulary:", e); + return null; + } + } + + /** + * Reload vocabulary from file. Used by manual command and live reload. + */ + private async reloadVocabulary(): Promise { + try { + const text = await VocabularyLoader.loadText( + this.app, + this.settings.edgeVocabularyPath + ); + + const parsed = parseEdgeVocabulary(text); + this.vocabulary = new Vocabulary(parsed); + + const stats = this.vocabulary.getStats(); + console.log("Vocabulary loaded", stats); + new Notice(`Edge vocabulary reloaded: ${stats.canonicalCount} canonical, ${stats.aliasCount} aliases`); + + // Log normalization examples + if (stats.canonicalCount > 0) { + const firstCanonical = Array.from(parsed.byCanonical.keys())[0]; + if (firstCanonical) { + const canonicalNorm = this.vocabulary.normalize(firstCanonical); + console.log(`Normalization example (canonical): "${firstCanonical}" -> canonical: ${canonicalNorm.canonical}, inverse: ${canonicalNorm.inverse}`); + } + + if (stats.aliasCount > 0) { + const firstAlias = Array.from(parsed.aliasToCanonical.keys())[0]; + if (firstAlias) { + const aliasNorm = this.vocabulary.normalize(firstAlias); + console.log(`Normalization example (alias): "${firstAlias}" -> canonical: ${aliasNorm.canonical}, inverse: ${aliasNorm.inverse}`); + } + } + } + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + if (msg.includes("not found") || msg.includes("Vocabulary file not found")) { + new Notice("edge_vocabulary.md not found. Configure path in plugin settings."); + } else { + new Notice(`Failed to reload vocabulary: ${msg}`); + } + console.error(e); + } + } } diff --git a/src/parser/index.ts b/src/parser/index.ts new file mode 100644 index 0000000..0636308 --- /dev/null +++ b/src/parser/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./parseEdgesFromCallouts"; diff --git a/src/parser/parseEdgesFromCallouts.ts b/src/parser/parseEdgesFromCallouts.ts new file mode 100644 index 0000000..02589ba --- /dev/null +++ b/src/parser/parseEdgesFromCallouts.ts @@ -0,0 +1,78 @@ +import type { ParsedEdge } from "./types"; + +const EDGE_HEADER_RE = /^\s*(>+)\s*\[!edge\]\s*(.+?)\s*$/i; +const TARGET_LINK_RE = /\[\[([^\]]+?)\]\]/g; + +/** + * Extract edges from any callout nesting: + * - Edge starts with: > [!edge] (any number of '>' allowed) + * - Collect targets from subsequent lines while quoteLevel >= edgeLevel + * - Stop when: + * a) next [!edge] header appears, OR + * b) quoteLevel drops below edgeLevel (block ends), ignoring blank lines + */ +export function parseEdgesFromCallouts(markdown: string): ParsedEdge[] { + const lines = markdown.split(/\r?\n/); + const edges: ParsedEdge[] = []; + + let current: ParsedEdge | null = null; + let currentEdgeLevel = 0; + + const getQuoteLevel = (line: string): number => { + const m = line.match(/^\s*(>+)/); + return m && m[1] ? m[1].length : 0; + }; + + const flush = (endLine: number) => { + if (!current) return; + current.lineEnd = endLine; + if (current.targets.length > 0) edges.push(current); + current = null; + currentEdgeLevel = 0; + }; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line === undefined) continue; + + // Start of a new edge block + const edgeMatch = line.match(EDGE_HEADER_RE); + if (edgeMatch && edgeMatch[1] && edgeMatch[2]) { + flush(i - 1); + + currentEdgeLevel = edgeMatch[1].length; + current = { + rawType: edgeMatch[2].trim(), + targets: [], + lineStart: i, + lineEnd: i, + }; + continue; + } + + if (!current) continue; + + const trimmed = line.trim(); + const ql = getQuoteLevel(line); + + // End of the current edge block if quote level drops below the edge header level + // (ignore blank lines) + if (trimmed !== "" && ql < currentEdgeLevel) { + flush(i - 1); + continue; + } + + // Collect targets (multiple per line allowed) + TARGET_LINK_RE.lastIndex = 0; + let m: RegExpExecArray | null; + while ((m = TARGET_LINK_RE.exec(line)) !== null) { + if (m[1]) { + const t = m[1].trim(); + if (t) current.targets.push(t); + } + } + } + + flush(lines.length - 1); + return edges; +} diff --git a/src/parser/parseFrontmatter.ts b/src/parser/parseFrontmatter.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/parser/types.ts b/src/parser/types.ts new file mode 100644 index 0000000..e94cb91 --- /dev/null +++ b/src/parser/types.ts @@ -0,0 +1,15 @@ +export interface NodeMeta { + id?: string; + title?: string; + type?: string; + status?: string; + tags?: string[]; + date?: string; +} + +export interface ParsedEdge { + rawType: string; + targets: string[]; // link texts without [[ ]] + lineStart: number; // 0-based + lineEnd: number; // 0-based +} diff --git a/src/settings.ts b/src/settings.ts index 352121e..a4220ec 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,36 +1,21 @@ -import {App, PluginSettingTab, Setting} from "obsidian"; -import MyPlugin from "./main"; - -export interface MyPluginSettings { - mySetting: string; -} - -export const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - new Setting(containerEl) - .setName('Settings #1') - .setDesc('It\'s a secret') - .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) - .onChange(async (value) => { - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } -} +export interface MindnetSettings { + edgeVocabularyPath: string; // vault-relativ + graphSchemaPath: string; // vault-relativ (später) + maxHops: number; + strictMode: boolean; + } + + export const DEFAULT_SETTINGS: MindnetSettings = { + edgeVocabularyPath: "_system/dictionary/edge_vocabulary.md", + graphSchemaPath: "_system/dictionary/graph_schema.md", + maxHops: 3, + strictMode: false, + }; + + /** + * Optional helper: normalize to Obsidian vault paths (forward slashes). + */ + export function normalizeVaultPath(p: string): string { + return (p || "").trim().replace(/\\/g, "/"); + } + \ No newline at end of file diff --git a/src/tests/lint/LintEngine.test.ts b/src/tests/lint/LintEngine.test.ts new file mode 100644 index 0000000..486acd3 --- /dev/null +++ b/src/tests/lint/LintEngine.test.ts @@ -0,0 +1,207 @@ +import { describe, it, expect } from "vitest"; +import type { ParsedEdge } from "../../parser/types"; +import { lintEdges } from "../../lint/LintEngine"; +import { Vocabulary } from "../../vocab/Vocabulary"; +import { parseEdgeVocabulary } from "../../vocab/parseEdgeVocabulary"; + +describe("lintEdges", () => { + // Create a minimal vocabulary for testing + const vocabMd = ` +| System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung | +| :--- | :--- | :--- | :--- | +| \`caused_by\` | \`resulted_in\` | \`wegen\`, \`ausgelöst_durch\` | Test | +| \`impacts\` | \`impacted_by\` | *(Kein Alias)* | Test | +`; + const vocabulary = new Vocabulary(parseEdgeVocabulary(vocabMd)); + + it("reports unknown edge type as ERROR", () => { + const edges: ParsedEdge[] = [ + { + rawType: "unknown_type", + targets: [], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(1); + const finding = findings[0]; + if (!finding) throw new Error("Expected finding"); + expect(finding.ruleId).toBe("unknown_edge_type"); + expect(finding.severity).toBe("ERROR"); + expect(finding.message).toContain("unknown_type"); + }); + + it("reports alias not normalized as WARN", () => { + const edges: ParsedEdge[] = [ + { + rawType: "wegen", + targets: [], + lineStart: 5, + lineEnd: 5, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(1); + const finding = findings[0]; + if (!finding) throw new Error("Expected finding"); + expect(finding.ruleId).toBe("alias_not_normalized"); + expect(finding.severity).toBe("WARN"); + expect(finding.message).toContain("wegen"); + expect(finding.message).toContain("caused_by"); + expect(finding.lineStart).toBe(5); + }); + + it("does not report normalized canonical types", () => { + const edges: ParsedEdge[] = [ + { + rawType: "caused_by", + targets: [], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(0); + }); + + it("reports missing target notes as WARN", () => { + const edges: ParsedEdge[] = [ + { + rawType: "caused_by", + targets: ["MissingNote", "ExistingNote"], + lineStart: 0, + lineEnd: 2, + }, + ]; + + const existingFiles = new Set(["ExistingNote.md", "ExistingNote"]); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(1); + const finding = findings[0]; + if (!finding) throw new Error("Expected finding"); + expect(finding.ruleId).toBe("missing_target_note"); + expect(finding.severity).toBe("WARN"); + expect(finding.message).toContain("MissingNote"); + expect(finding.evidence).toBe("MissingNote"); + }); + + it("handles target notes with headings", () => { + const edges: ParsedEdge[] = [ + { + rawType: "caused_by", + targets: ["Note#heading"], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(["Note.md"]); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(0); + }); + + it("handles target notes without .md extension", () => { + const edges: ParsedEdge[] = [ + { + rawType: "caused_by", + targets: ["NoteName"], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(["NoteName.md"]); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(0); + }); + + it("handles multiple issues in one edge", () => { + const edges: ParsedEdge[] = [ + { + rawType: "wegen", // alias not normalized + targets: ["MissingNote"], // missing target + lineStart: 10, + lineEnd: 12, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(2); + const finding0 = findings[0]; + const finding1 = findings[1]; + if (!finding0 || !finding1) throw new Error("Expected findings"); + expect(finding0.ruleId).toBe("alias_not_normalized"); + expect(finding1.ruleId).toBe("missing_target_note"); + }); + + it("handles case-insensitive alias normalization", () => { + const edges: ParsedEdge[] = [ + { + rawType: "WEGEN", // uppercase alias + targets: [], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(1); + const finding = findings[0]; + if (!finding) throw new Error("Expected finding"); + expect(finding.ruleId).toBe("alias_not_normalized"); + expect(finding.message).toContain("WEGEN"); + expect(finding.message).toContain("caused_by"); + }); + + it("handles empty targets array", () => { + const edges: ParsedEdge[] = [ + { + rawType: "caused_by", + targets: [], + lineStart: 0, + lineEnd: 0, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + expect(findings.length).toBe(0); + }); + + it("preserves line numbers in findings", () => { + const edges: ParsedEdge[] = [ + { + rawType: "unknown", + targets: [], + lineStart: 42, + lineEnd: 45, + }, + ]; + + const existingFiles = new Set(); + const findings = lintEdges(edges, vocabulary, existingFiles); + + const finding = findings[0]; + if (!finding) throw new Error("Expected finding"); + expect(finding.lineStart).toBe(42); + expect(finding.lineEnd).toBe(45); + }); +}); diff --git a/src/tests/parser/parseEdgesFromCallouts.test.ts b/src/tests/parser/parseEdgesFromCallouts.test.ts new file mode 100644 index 0000000..9f97ca9 --- /dev/null +++ b/src/tests/parser/parseEdgesFromCallouts.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect } from "vitest"; +import { parseEdgesFromCallouts } from "../../parser/parseEdgesFromCallouts"; + +describe("parseEdgesFromCallouts", () => { + it("parses edge blocks embedded inside other callouts", () => { + const md = ` +> [!abstract]- 🧩 Some Block +>> Some text +>> [!edge] caused_by +>> [[A]] +>> [[B]] +>> More text + +Outside. +`.trim(); + + const edges = parseEdgesFromCallouts(md); + expect(edges.length).toBe(1); + const edge = edges[0]; + if (!edge) throw new Error("Expected edge to exist"); + expect(edge.rawType).toBe("caused_by"); + expect(edge.targets).toEqual(["A", "B"]); + }); + + it("stops when quote level drops below edge level", () => { + const md = ` +> [!abstract] +>> [!edge] resulted_in +>> [[X]] +Normal text ends quote. +`.trim(); + + const edges = parseEdgesFromCallouts(md); + expect(edges.length).toBe(1); + const edge = edges[0]; + if (!edge) throw new Error("Expected edge to exist"); + expect(edge.rawType).toBe("resulted_in"); + expect(edge.targets).toEqual(["X"]); + }); + + it("starts a new edge when another edge header appears", () => { + const md = ` +> [!edge] caused_by +> [[A]] +> [!edge] resulted_in +> [[B]] +`.trim(); + + const edges = parseEdgesFromCallouts(md); + expect(edges.length).toBe(2); + const edge0 = edges[0]; + const edge1 = edges[1]; + if (!edge0 || !edge1) throw new Error("Expected edges to exist"); + expect(edge0.rawType).toBe("caused_by"); + expect(edge0.targets).toEqual(["A"]); + expect(edge1.rawType).toBe("resulted_in"); + expect(edge1.targets).toEqual(["B"]); + }); + + it("allows multiple links in one line", () => { + const md = ` +>> [!edge] impacts +>> [[Y]] [[Z]] +`.trim(); + + const edges = parseEdgesFromCallouts(md); + expect(edges.length).toBe(1); + const edge = edges[0]; + if (!edge) throw new Error("Expected edge to exist"); + expect(edge.rawType).toBe("impacts"); + expect(edge.targets).toEqual(["Y", "Z"]); + }); +}); diff --git a/src/tests/vocab/parseEdgeVocabulary.test.ts b/src/tests/vocab/parseEdgeVocabulary.test.ts new file mode 100644 index 0000000..536ed5d --- /dev/null +++ b/src/tests/vocab/parseEdgeVocabulary.test.ts @@ -0,0 +1,169 @@ +import { describe, it, expect } from "vitest"; +import { parseEdgeVocabulary } from "../../vocab/parseEdgeVocabulary"; +import { Vocabulary } from "../../vocab/Vocabulary"; + +describe("parseEdgeVocabulary", () => { + const fixtureMd = ` +# Edge Vocabulary + +## Causal Relations + +| System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung | +| :--- | :--- | :--- | :--- | +| **\`caused_by\`** | \`resulted_in\` | \`ausgelöst_durch\`, \`wegen\`, \`verursacht_durch\` | Describes causation | +| \`impacts\` | \`impacted_by\` | *(Kein Alias)* | Impact relationship | +| \`influences\` | \`influenced_by\` | \`beeinflusst\`, \`wirkt_auf\` | Influence relation | + +## Other Relations + +| System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung | +| :--- | :--- | :--- | :--- | +| \`relates_to\` | \`related_from\` | \`verwandt_mit\`, \`bezogen_auf\` | General relation | +`; + + it("parses canonical types correctly", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + expect(vocab.byCanonical.has("caused_by")).toBe(true); + expect(vocab.byCanonical.has("impacts")).toBe(true); + expect(vocab.byCanonical.has("influences")).toBe(true); + expect(vocab.byCanonical.has("relates_to")).toBe(true); + }); + + it("parses inverse types correctly", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + const causedBy = vocab.byCanonical.get("caused_by"); + if (!causedBy) throw new Error("Expected caused_by entry"); + expect(causedBy.inverse).toBe("resulted_in"); + + const impacts = vocab.byCanonical.get("impacts"); + if (!impacts) throw new Error("Expected impacts entry"); + expect(impacts.inverse).toBe("impacted_by"); + }); + + it("handles (Kein Alias) correctly", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + const impacts = vocab.byCanonical.get("impacts"); + if (!impacts) throw new Error("Expected impacts entry"); + expect(impacts.aliases).toEqual([]); + }); + + it("parses multiple aliases correctly", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + const causedBy = vocab.byCanonical.get("caused_by"); + if (!causedBy) throw new Error("Expected caused_by entry"); + expect(causedBy.aliases).toEqual(["ausgelöst_durch", "wegen", "verursacht_durch"]); + + const influences = vocab.byCanonical.get("influences"); + if (!influences) throw new Error("Expected influences entry"); + expect(influences.aliases).toEqual(["beeinflusst", "wirkt_auf"]); + }); + + it("builds alias-to-canonical mapping", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + expect(vocab.aliasToCanonical.get("ausgelöst_durch")).toBe("caused_by"); + expect(vocab.aliasToCanonical.get("wegen")).toBe("caused_by"); + expect(vocab.aliasToCanonical.get("verursacht_durch")).toBe("caused_by"); + expect(vocab.aliasToCanonical.get("beeinflusst")).toBe("influences"); + expect(vocab.aliasToCanonical.get("wirkt_auf")).toBe("influences"); + }); + + it("parses multiple tables correctly", () => { + const vocab = parseEdgeVocabulary(fixtureMd); + + expect(vocab.byCanonical.size).toBe(4); + expect(vocab.byCanonical.has("caused_by")).toBe(true); + expect(vocab.byCanonical.has("impacts")).toBe(true); + expect(vocab.byCanonical.has("influences")).toBe(true); + expect(vocab.byCanonical.has("relates_to")).toBe(true); + }); +}); + +describe("Vocabulary wrapper", () => { + const fixtureMd = ` +| System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung | +| :--- | :--- | :--- | :--- | +| **\`caused_by\`** | \`resulted_in\` | \`ausgelöst_durch\`, \`wegen\` | Test | +| \`impacts\` | \`impacted_by\` | *(Kein Alias)* | Test | +`; + + it("getCanonical returns canonical for canonical input", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + expect(vocab.getCanonical("caused_by")).toBe("caused_by"); + expect(vocab.getCanonical("impacts")).toBe("impacts"); + }); + + it("getCanonical returns canonical for alias input (case-insensitive)", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + expect(vocab.getCanonical("wegen")).toBe("caused_by"); + expect(vocab.getCanonical("WEGEN")).toBe("caused_by"); + expect(vocab.getCanonical("ausgelöst_durch")).toBe("caused_by"); + expect(vocab.getCanonical("AUSGELÖST_DURCH")).toBe("caused_by"); + }); + + it("getCanonical returns null for unknown input", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + expect(vocab.getCanonical("unknown")).toBe(null); + expect(vocab.getCanonical("")).toBe(null); + }); + + it("getInverse returns inverse for canonical", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + expect(vocab.getInverse("caused_by")).toBe("resulted_in"); + expect(vocab.getInverse("impacts")).toBe("impacted_by"); + }); + + it("getInverse returns null for unknown canonical", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + expect(vocab.getInverse("unknown")).toBe(null); + }); + + it("normalize returns correct result for canonical", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + const result = vocab.normalize("caused_by"); + expect(result.raw).toBe("caused_by"); + expect(result.canonical).toBe("caused_by"); + expect(result.inverse).toBe("resulted_in"); + }); + + it("normalize returns correct result for alias (case-insensitive)", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + const result = vocab.normalize("wegen"); + expect(result.raw).toBe("wegen"); + expect(result.canonical).toBe("caused_by"); + expect(result.inverse).toBe("resulted_in"); + + const resultUpper = vocab.normalize("WEGEN"); + expect(resultUpper.raw).toBe("WEGEN"); + expect(resultUpper.canonical).toBe("caused_by"); + expect(resultUpper.inverse).toBe("resulted_in"); + }); + + it("normalize returns null canonical for unknown input", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + const result = vocab.normalize("unknown"); + expect(result.raw).toBe("unknown"); + expect(result.canonical).toBe(null); + expect(result.inverse).toBe(null); + }); + + it("getStats returns correct counts", () => { + const vocab = new Vocabulary(parseEdgeVocabulary(fixtureMd)); + + const stats = vocab.getStats(); + expect(stats.canonicalCount).toBe(2); + expect(stats.aliasCount).toBe(2); // ausgelöst_durch, wegen + }); +}); diff --git a/src/ui/MindnetSettingTab.ts b/src/ui/MindnetSettingTab.ts new file mode 100644 index 0000000..4236730 --- /dev/null +++ b/src/ui/MindnetSettingTab.ts @@ -0,0 +1,99 @@ +import { App, Notice, PluginSettingTab, Setting } from "obsidian"; +import type MindnetCausalAssistantPlugin from "../main"; +import { VocabularyLoader } from "../vocab/VocabularyLoader"; + +export class MindnetSettingTab extends PluginSettingTab { + plugin: MindnetCausalAssistantPlugin; + + constructor(app: App, plugin: MindnetCausalAssistantPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + containerEl.createEl("h2", { text: "Mindnet Settings" }); + + // Edge vocabulary path + new Setting(containerEl) + .setName("Edge vocabulary path") + .setDesc("Vault-relative path to the edge vocabulary markdown file") + .addText((text) => + text + .setPlaceholder("_system/dictionary/edge_vocabulary.md") + .setValue(this.plugin.settings.edgeVocabularyPath) + .onChange(async (value) => { + this.plugin.settings.edgeVocabularyPath = value; + await this.plugin.saveSettings(); + }) + ) + .addButton((button) => + button + .setButtonText("Validate") + .setCta() + .onClick(async () => { + try { + const text = await VocabularyLoader.loadText( + this.app, + this.plugin.settings.edgeVocabularyPath + ); + new Notice( + `Edge vocabulary file found (${text.length} characters)` + ); + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + new Notice( + `Failed to load edge vocabulary: ${msg}` + ); + } + }) + ); + + // Graph schema path + new Setting(containerEl) + .setName("Graph schema path") + .setDesc("Vault-relative path to the graph schema markdown file") + .addText((text) => + text + .setPlaceholder("_system/dictionary/graph_schema.md") + .setValue(this.plugin.settings.graphSchemaPath) + .onChange(async (value) => { + this.plugin.settings.graphSchemaPath = value; + await this.plugin.saveSettings(); + }) + ); + + // Strict mode toggle + new Setting(containerEl) + .setName("Strict mode") + .setDesc("Enable strict validation mode") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.strictMode) + .onChange(async (value) => { + this.plugin.settings.strictMode = value; + await this.plugin.saveSettings(); + }) + ); + + // Max hops number input + new Setting(containerEl) + .setName("Max hops") + .setDesc("Maximum number of hops for graph traversal") + .addText((text) => + text + .setPlaceholder("3") + .setValue(String(this.plugin.settings.maxHops)) + .onChange(async (value) => { + const numValue = parseInt(value, 10); + if (!isNaN(numValue) && numValue > 0) { + this.plugin.settings.maxHops = numValue; + await this.plugin.saveSettings(); + } + }) + ); + } +} diff --git a/src/ui/MindnetView.ts b/src/ui/MindnetView.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/vocab/Vocabulary.ts b/src/vocab/Vocabulary.ts new file mode 100644 index 0000000..6ef6e00 --- /dev/null +++ b/src/vocab/Vocabulary.ts @@ -0,0 +1,64 @@ +import type { EdgeVocabulary, NormalizeResult } from "./types"; + +/** + * Wrapper class for edge vocabulary that provides convenient lookup methods. + */ +export class Vocabulary { + private vocab: EdgeVocabulary; + + constructor(vocab: EdgeVocabulary) { + this.vocab = vocab; + } + + /** + * Get canonical edge type from raw input (case-insensitive). + * Returns null if not found. + */ + getCanonical(raw: string): string | null { + const lowerRaw = raw.toLowerCase(); + + // Check if it's already a canonical + for (const [canonical] of this.vocab.byCanonical) { + if (canonical.toLowerCase() === lowerRaw) { + return canonical; + } + } + + // Check aliases + const canonical = this.vocab.aliasToCanonical.get(lowerRaw); + return canonical ?? null; + } + + /** + * Get inverse edge type for a canonical type. + * Returns null if not found or no inverse defined. + */ + getInverse(canonical: string): string | null { + const entry = this.vocab.byCanonical.get(canonical); + return entry?.inverse ?? null; + } + + /** + * Normalize a raw edge type string to canonical and inverse. + */ + normalize(raw: string): NormalizeResult { + const canonical = this.getCanonical(raw); + const inverse = canonical ? this.getInverse(canonical) : null; + + return { + raw, + canonical, + inverse, + }; + } + + /** + * Get statistics about the vocabulary. + */ + getStats(): { canonicalCount: number; aliasCount: number } { + return { + canonicalCount: this.vocab.byCanonical.size, + aliasCount: this.vocab.aliasToCanonical.size, + }; + } +} diff --git a/src/vocab/VocabularyLoader.ts b/src/vocab/VocabularyLoader.ts new file mode 100644 index 0000000..ca5834c --- /dev/null +++ b/src/vocab/VocabularyLoader.ts @@ -0,0 +1,27 @@ +import type { App, TFile } from "obsidian"; +import { normalizeVaultPath } from "../settings"; + +export class VocabularyLoader { + /** + * Loads a text file from the currently opened Obsidian vault. + * @param app Obsidian App + * @param vaultRelativePath e.g. "_system/dictionary/edge_vocabulary.md" + */ + static async loadText(app: App, vaultRelativePath: string): Promise { + const p = normalizeVaultPath(vaultRelativePath); + const abstract = app.vault.getAbstractFileByPath(p); + + if (!abstract) { + throw new Error(`Vocabulary file not found in vault: "${p}"`); + } + + // Guard: Only files can be read + const file = abstract as TFile; + // TFile has 'extension' and 'path' properties; if it isn't a file this will usually fail at runtime. + if (!(file && typeof file.path === "string")) { + throw new Error(`Path is not a file: "${p}"`); + } + + return await app.vault.read(file); + } +} diff --git a/src/vocab/index.ts b/src/vocab/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/vocab/parseEdgeVocabulary.ts b/src/vocab/parseEdgeVocabulary.ts new file mode 100644 index 0000000..c13232a --- /dev/null +++ b/src/vocab/parseEdgeVocabulary.ts @@ -0,0 +1,105 @@ +import type { EdgeVocabulary } from "./types"; + +const BACKTICK_RE = /`([^`]+)`/g; + +/** + * Parses markdown tables containing edge vocabulary definitions. + * + * Expected format: + * | System-Typ (Canonical) | Inverser Typ | Erlaubte Aliasse (User) | Beschreibung ... | + * | **`caused_by`** | `resulted_in` | `ausgelöst_durch`, `wegen`, ... | ... | + * + * Rules: + * - Extract all backticked tokens from each row + * - First token = canonical (may be wrapped in ** **) + * - Second token = inverse (optional) + * - Remaining tokens = aliases (skip if cell contains "(Kein Alias)") + * - Skip rows with less than 1 token (with warning counter) + * - Store canonical as-is, but use lowercase for lookup keys + */ +export function parseEdgeVocabulary(md: string): EdgeVocabulary { + const lines = md.split(/\r?\n/); + const byCanonical = new Map(); + const aliasToCanonical = new Map(); + + let skippedRows = 0; + + for (const line of lines) { + // Skip header separator rows (e.g., "| :--- | :--- |") + if (/^\s*\|[\s:|-]+\|\s*$/.test(line)) { + continue; + } + + // Only process table rows + if (!line.trim().startsWith("|")) { + continue; + } + + // Extract all backticked tokens + const tokens: string[] = []; + let match: RegExpExecArray | null; + BACKTICK_RE.lastIndex = 0; + while ((match = BACKTICK_RE.exec(line)) !== null) { + if (match[1]) { + const token = match[1].trim(); + if (token) { + tokens.push(token); + } + } + } + + // Skip rows with no tokens or only one token (need at least canonical) + if (tokens.length < 1) { + skippedRows++; + continue; + } + + // Check if aliases cell contains "(Kein Alias)" + const hasNoAliases = /\(Kein Alias\)/i.test(line); + + const canonical = tokens[0]; + if (!canonical) { + skippedRows++; + continue; + } + + const inverse = tokens.length >= 2 && tokens[1] ? tokens[1] : undefined; + + // Extract aliases: all tokens after the first two, but only if not "(Kein Alias)" + const aliases: string[] = []; + if (!hasNoAliases && tokens.length > 2) { + for (let i = 2; i < tokens.length; i++) { + const alias = tokens[i]; + if (alias) { + aliases.push(alias); + } + } + } + + // Store canonical entry + byCanonical.set(canonical, { + canonical, + inverse, + aliases, + }); + + // Build alias-to-canonical mapping (case-insensitive keys) + for (const alias of aliases) { + if (alias) { + const lowerAlias = alias.toLowerCase(); + if (!aliasToCanonical.has(lowerAlias)) { + aliasToCanonical.set(lowerAlias, canonical); + } + } + } + } + + if (skippedRows > 0) { + console.warn(`parseEdgeVocabulary: Skipped ${skippedRows} rows with insufficient tokens`); + } + + return { + byCanonical, + aliasToCanonical, + }; +} diff --git a/src/vocab/types.ts b/src/vocab/types.ts new file mode 100644 index 0000000..80fff3d --- /dev/null +++ b/src/vocab/types.ts @@ -0,0 +1,20 @@ +export type CanonicalEdgeType = string; + +export interface EdgeTypeEntry { + canonical: CanonicalEdgeType; + inverse?: CanonicalEdgeType; + aliases: string[]; +} + +export interface EdgeVocabulary { + // key: canonical edge type + byCanonical: Map; + // key: alias (lowercased) -> canonical + aliasToCanonical: Map; +} + +export interface NormalizeResult { + raw: string; + canonical: CanonicalEdgeType | null; + inverse: CanonicalEdgeType | null; +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..4f63184 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["src/**/*.test.ts"], + globals: true, + }, + resolve: { + alias: { + obsidian: new URL("./src/__mocks__/obsidian.ts", import.meta.url).pathname, + }, + }, +});