diff --git a/package-lock.json b/package-lock.json index 909c136..b0c5e89 100755 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,18 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "jest-environment-jsdom": "^30.3.0", + "chromadb": "^1.5.3", "node-fetch": "^3.3.2", "obsidian": "^1.4.11" }, "devDependencies": { "@types/jest": "^29.5.14", "@types/node": "^20.11.19", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", "eslint": "^8.56.0", "jest": "^29.7.0", + "jest-environment-jsdom": "^30.3.0", "prettier": "^3.2.5", "ts-jest": "^29.1.2", "typescript": "^5.3.3" @@ -29,6 +30,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.3", @@ -42,12 +44,14 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -219,6 +223,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -585,6 +590,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, "funding": [ { "type": "github", @@ -604,6 +610,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, "funding": [ { "type": "github", @@ -627,6 +634,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, "funding": [ { "type": "github", @@ -654,6 +662,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, "funding": [ { "type": "github", @@ -676,6 +685,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, "funding": [ { "type": "github", @@ -1043,6 +1053,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.3.0.tgz", "integrity": "sha512-0hNFs5N6We3DMCwobzI0ydhkY10sT1tZSC0AAiy+0g2Dt/qEWgrcV5BrMxPczhe41cxW4qm6X+jqZaUdpZIajA==", + "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "30.3.0", @@ -1070,6 +1081,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", + "dev": true, "license": "MIT", "dependencies": { "@jest/fake-timers": "30.3.0", @@ -1085,6 +1097,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -1102,6 +1115,7 @@ "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.34.0" @@ -1114,6 +1128,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "dev": true, "license": "MIT", "dependencies": { "@jest/pattern": "30.0.1", @@ -1132,12 +1147,14 @@ "version": "0.34.49", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, "license": "MIT" }, "node_modules/@jest/environment-jsdom-abstract/node_modules/@sinonjs/fake-timers": { "version": "15.3.2", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" @@ -1147,6 +1164,7 @@ "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" @@ -1159,6 +1177,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, "funding": [ { "type": "github", @@ -1174,6 +1193,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -1194,6 +1214,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -1208,6 +1229,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -1225,6 +1247,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1237,6 +1260,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "30.0.5", @@ -1312,6 +1336,7 @@ "version": "30.0.1", "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -1325,6 +1350,7 @@ "version": "30.0.1", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, "license": "MIT", "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -1585,6 +1611,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" @@ -1674,12 +1701,14 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" @@ -1689,6 +1718,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" @@ -1709,6 +1739,7 @@ "version": "21.1.7", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -1716,33 +1747,21 @@ "parse5": "^7.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "node_modules/@types/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, "license": "MIT" }, "node_modules/@types/tern": { @@ -1758,12 +1777,14 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -1773,127 +1794,163 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", "dev": true, "license": "MIT", "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1901,76 +1958,86 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@ungap/structured-clone": { @@ -2007,6 +2074,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -2062,7 +2130,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2104,16 +2171,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -2261,13 +2318,26 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -2392,6 +2462,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -2414,6 +2485,43 @@ "node": ">=10" } }, + "node_modules/chromadb": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/chromadb/-/chromadb-1.10.5.tgz", + "integrity": "sha512-+IeTjjf44pKUY3vp1BacwO2tFAPcWCd64zxPZZm98dVj/kbSBeaHKB2D6eX7iRLHS1PTVASuqoR6mAJ+nrsTBg==", + "license": "Apache-2.0", + "dependencies": { + "cliui": "^8.0.1", + "isomorphic-fetch": "^3.0.0" + }, + "engines": { + "node": ">=14.17.0" + }, + "peerDependencies": { + "@google/generative-ai": "^0.1.1", + "cohere-ai": "^5.0.0 || ^6.0.0 || ^7.0.0", + "ollama": "^0.5.0", + "openai": "^3.0.0 || ^4.0.0", + "voyageai": "^0.0.3-1" + }, + "peerDependenciesMeta": { + "@google/generative-ai": { + "optional": true + }, + "cohere-ai": { + "optional": true + }, + "ollama": { + "optional": true + }, + "openai": { + "optional": true + }, + "voyageai": { + "optional": true + } + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2441,7 +2549,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -2550,6 +2657,7 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^3.2.0", @@ -2572,6 +2680,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, "license": "MIT", "dependencies": { "whatwg-mimetype": "^4.0.0", @@ -2585,6 +2694,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2602,6 +2712,7 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, "license": "MIT" }, "node_modules/dedent": { @@ -2656,19 +2767,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2706,13 +2804,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -3010,36 +3108,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3324,31 +3392,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -3384,6 +3432,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3406,6 +3455,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, "license": "MIT", "dependencies": { "whatwg-encoding": "^3.1.1" @@ -3425,6 +3475,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -3438,6 +3489,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -3461,6 +3513,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -3582,7 +3635,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3635,6 +3687,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, "license": "MIT" }, "node_modules/is-stream": { @@ -3657,6 +3710,58 @@ "dev": true, "license": "ISC" }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/isomorphic-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/isomorphic-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/isomorphic-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/isomorphic-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -3932,6 +4037,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.3.0.tgz", "integrity": "sha512-RLEOJy6ip1lpw0yqJ8tB3i88FC7VBz7i00Zvl2qF71IdxjS98gC9/0SPWYIBVXHm5hgCYK0PAlSlnHGGy9RoMg==", + "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "30.3.0", @@ -3954,6 +4060,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", + "dev": true, "license": "MIT", "dependencies": { "@jest/fake-timers": "30.3.0", @@ -3969,6 +4076,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -3986,6 +4094,7 @@ "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.34.0" @@ -3998,6 +4107,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "dev": true, "license": "MIT", "dependencies": { "@jest/pattern": "30.0.1", @@ -4016,12 +4126,14 @@ "version": "0.34.49", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, "license": "MIT" }, "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": { "version": "15.3.2", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" @@ -4031,6 +4143,7 @@ "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" @@ -4043,6 +4156,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, "funding": [ { "type": "github", @@ -4058,6 +4172,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -4078,6 +4193,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -4092,6 +4208,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.3.0", @@ -4109,6 +4226,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4121,6 +4239,7 @@ "version": "30.3.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", + "dev": true, "license": "MIT", "dependencies": { "@jest/schemas": "30.0.5", @@ -4518,6 +4637,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4537,6 +4657,7 @@ "version": "26.1.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, "license": "MIT", "dependencies": { "cssstyle": "^4.2.1", @@ -4757,16 +4878,6 @@ "dev": true, "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -4792,16 +4903,16 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4830,6 +4941,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/natural-compare": { @@ -4925,6 +5037,7 @@ "version": "2.2.23", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, "license": "MIT" }, "node_modules/obsidian": { @@ -5063,6 +5176,7 @@ "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -5108,20 +5222,11 @@ "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "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": { @@ -5288,6 +5393,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5335,6 +5441,7 @@ "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/require-directory": { @@ -5444,6 +5551,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, "license": "MIT" }, "node_modules/run-parallel": { @@ -5474,12 +5582,14 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -5542,6 +5652,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5579,6 +5690,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" @@ -5591,6 +5703,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5614,7 +5727,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -5629,7 +5741,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5682,6 +5793,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -5707,6 +5819,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, "license": "MIT" }, "node_modules/test-exclude": { @@ -5755,10 +5868,59 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, "license": "MIT", "dependencies": { "tldts-core": "^6.1.86" @@ -5771,6 +5933,7 @@ "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, "license": "MIT" }, "node_modules/tmpl": { @@ -5797,6 +5960,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" @@ -5809,6 +5973,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -5818,16 +5983,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-jest": { @@ -5913,6 +6078,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5963,6 +6129,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, "license": "MIT" }, "node_modules/update-browserslist-db": { @@ -6032,6 +6199,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -6063,6 +6231,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6073,6 +6242,7 @@ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" @@ -6081,10 +6251,17 @@ "node": ">=18" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6094,6 +6271,7 @@ "version": "14.2.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, "license": "MIT", "dependencies": { "tr46": "^5.1.0", @@ -6140,7 +6318,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -6179,6 +6356,7 @@ "version": "8.20.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6200,6 +6378,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" @@ -6209,6 +6388,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, "license": "MIT" }, "node_modules/y18n": { diff --git a/package.json b/package.json index c9a4d22..ca12eb3 100755 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "devDependencies": { "@types/jest": "^29.5.14", "@types/node": "^20.11.19", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", "eslint": "^8.56.0", "jest": "^29.7.0", "jest-environment-jsdom": "^30.3.0", @@ -32,6 +32,7 @@ "typescript": "^5.3.3" }, "dependencies": { + "chromadb": "^1.5.3", "node-fetch": "^3.3.2", "obsidian": "^1.4.11" } diff --git a/src/chat-view.ts b/src/chat-view.ts index 0009c02..8a294fd 100755 --- a/src/chat-view.ts +++ b/src/chat-view.ts @@ -54,14 +54,24 @@ export class ChatView extends ItemView { constructor(leaf: WorkspaceLeaf, settings: PluginSettings) { super(leaf); this.settings = settings; - this.ollamaClient = new OllamaClient(settings.ollamaUrl, settings.model); + this.ollamaClient = new OllamaClient( + settings.ollamaUrl, + settings.model, + undefined, + settings.cacheConfig + ); this.vaultIndexer = new VaultIndexer(this.app.vault); this.toolExecutor = new ToolExecutor(this.app.vault, this.app); } public updateSettings(newSettings: PluginSettings): void { this.settings = newSettings; - this.ollamaClient = new OllamaClient(newSettings.ollamaUrl, newSettings.model); + this.ollamaClient = new OllamaClient( + newSettings.ollamaUrl, + newSettings.model, + undefined, + newSettings.cacheConfig + ); } getViewType(): string { @@ -72,18 +82,18 @@ export class ChatView extends ItemView { return 'Ollama Chat'; } - onOpen(): Promise { + async onOpen(): Promise { + await this.ollamaClient.initializeCache(); this.render(); this.removeEventListeners(); // Clean up any existing listeners before reattaching this.setupEventListeners(); - return Promise.resolve(); } public onSettingsChange(newSettings: PluginSettings): void { this.updateSettings(newSettings); } - onClose(): Promise { + async onClose(): Promise { this.ollamaClient.cancelStream(); this.removeEventListeners(); this.cleanupStreamingResources(); diff --git a/src/constants.ts b/src/constants.ts index cf13341..85626bf 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,4 +6,10 @@ export const DEFAULT_SETTINGS = { vaultSearchLimit: 3, maxMessageHistory: 50, lastIndexTime: 0, + cacheConfig: { + enabled: false, + similarityThreshold: 0.85, + collectionName: 'ollama_semantic_cache', + embeddingModel: 'nomic-embed-text', + }, }; diff --git a/src/main.ts b/src/main.ts index f46518c..3b57a6c 100755 --- a/src/main.ts +++ b/src/main.ts @@ -141,6 +141,17 @@ class OllamaSettingTab extends PluginSettingTab { } }) ); + + new Setting(container) + .setName('Enable Semantic Cache') + .setDesc('Cache responses semantically to speed up repeated queries') + .addToggle((toggle) => + toggle.setValue(this.plugin.settings.cacheConfig.enabled).onChange(async (value) => { + this.plugin.settings.cacheConfig.enabled = value; + await this.plugin.saveSettings(); + this.plugin.notifyChatViews(); + }) + ); } hide(): void { diff --git a/src/ollama-client.ts b/src/ollama-client.ts index 3c4dae3..1e0d5fe 100644 --- a/src/ollama-client.ts +++ b/src/ollama-client.ts @@ -3,6 +3,7 @@ import type { OllamaMessage, OllamaTool } from './types'; import { ApiError } from './types'; import { Logger } from './utils'; +import { SemanticCacheService, CacheConfig } from './semantic-cache'; interface OllamaChatResponse { message?: Partial; @@ -14,11 +15,22 @@ export class OllamaClient { private fetchFn: typeof fetch; private readonly maxRetries: number = 3; private currentStreamController: AbortController | null = null; + private cacheService?: SemanticCacheService; - constructor(baseURL: string, model: string, fetchFn?: typeof fetch) { + constructor(baseURL: string, model: string, fetchFn?: typeof fetch, cacheConfig?: CacheConfig) { this.baseURL = baseURL; this.model = model; this.fetchFn = fetchFn ?? fetch; + + if (cacheConfig?.enabled) { + this.cacheService = new SemanticCacheService(baseURL, cacheConfig); + } + } + + async initializeCache(): Promise { + if (this.cacheService) { + await this.cacheService.initialize(); + } } cancelStream(): void { @@ -32,7 +44,32 @@ export class OllamaClient { messages: OllamaMessage[], tools: OllamaTool[] = [] ): AsyncGenerator { - yield* this.streamChatWithRetry(messages, tools, 0); + // Bypass cache if tools are involved to prevent state corruption + if (tools.length > 0) { + yield* this.streamChatWithRetry(messages, tools, 0); + return; + } + + const lastUserMsg = [...messages].reverse().find((m) => m.role === 'user'); + if (lastUserMsg && this.cacheService) { + const cached = await this.cacheService.getCache(lastUserMsg.content); + if (cached) { + yield { role: 'assistant', content: cached, tool_calls: [] }; + return; + } + } + + const chunks: OllamaMessage[] = []; + for await (const chunk of this.streamChatWithRetry(messages, tools, 0)) { + chunks.push(chunk); + yield chunk; + } + + // Populate cache in background after successful stream + if (this.cacheService && lastUserMsg) { + const fullContent = chunks.map((c) => c.content).join(''); + void this.cacheService.setCache(lastUserMsg.content, fullContent); + } } async streamChatAsPromise( @@ -46,6 +83,29 @@ export class OllamaClient { return chunks; } + async chat(messages: OllamaMessage[], tools: OllamaTool[] = []): Promise { + // Bypass cache if tools are involved + if (tools.length > 0) { + return this.chatWithRetry(messages, tools, 0); + } + + const lastUserMsg = [...messages].reverse().find((m) => m.role === 'user'); + if (lastUserMsg && this.cacheService) { + const cached = await this.cacheService.getCache(lastUserMsg.content); + if (cached) { + return { role: 'assistant', content: cached, tool_calls: [] }; + } + } + + const response = await this.chatWithRetry(messages, tools, 0); + + if (this.cacheService && lastUserMsg) { + void this.cacheService.setCache(lastUserMsg.content, response.content); + } + + return response; + } + private async *streamChatWithRetry( messages: OllamaMessage[], tools: OllamaTool[] = [], @@ -200,10 +260,6 @@ export class OllamaClient { } } - async chat(messages: OllamaMessage[], tools: OllamaTool[] = []): Promise { - return this.chatWithRetry(messages, tools, 0); - } - private async chatWithRetry( messages: OllamaMessage[], tools: OllamaTool[] = [], diff --git a/src/semantic-cache.ts b/src/semantic-cache.ts new file mode 100644 index 0000000..b95f286 --- /dev/null +++ b/src/semantic-cache.ts @@ -0,0 +1,107 @@ +// src/semantic-cache.ts + +import { ChromaClient } from 'chromadb'; +import { Logger } from './utils'; +import { CacheConfig } from './types'; + +export class SemanticCacheService { + private client: ChromaClient; + private collection: ReturnType | null = null; + private config: CacheConfig; + private ollamaURL: string; + + constructor(ollamaURL: string, config: CacheConfig) { + this.ollamaURL = ollamaURL.replace(/\/+$/, ''); + this.config = config; + this.client = new ChromaClient({ path: 'http://localhost:8000' }); + } + + async initialize() { + if (!this.config.enabled) return; + + try { + this.collection = await this.client.getOrCreateCollection({ + name: this.config.collectionName, + metadata: { 'hnsw:space': 'cosine' }, + }); + Logger.info(`Semantic cache initialized: ${this.config.collectionName}`, 'semantic-cache'); + } catch (error) { + Logger.error(`Failed to initialize semantic cache: ${String(error)}`, 'semantic-cache'); + } + } + + private async getEmbedding(text: string): Promise { + try { + const response = await fetch(`${this.ollamaURL}/api/embeddings`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: this.config.embeddingModel, + prompt: text, + }), + }); + + if (!response.ok) { + throw new Error(`Embedding failed with status ${response.status}`); + } + + const data = await response.json(); + return data.embedding; + } catch (error) { + Logger.warn(`Failed to generate embedding: ${String(error)}`, 'semantic-cache'); + return []; + } + } + + async getCache(prompt: string): Promise { + if (!this.collection || !this.config.enabled || !prompt.trim()) { + return null; + } + + try { + const embedding = await this.getEmbedding(prompt); + if (!embedding.length) return null; + + const results = await this.collection.query({ + queryEmbeddings: [embedding], + nResults: 1, + include: ['metadatas', 'distances'], + }); + + // Cosine distance = 1 - cosine_similarity + // We want distance < (1 - threshold) + if ( + results.distances && + results.distances[0] && + results.distances[0][0] < 1 - this.config.similarityThreshold + ) { + Logger.debug('Semantic cache hit', 'semantic-cache'); + return results.metadatas?.[0]?.[0]?.fullResponse ?? null; + } + } catch (error) { + Logger.warn(`Cache lookup failed: ${String(error)}`, 'semantic-cache'); + } + + return null; + } + + async setCache(prompt: string, response: string): Promise { + if (!this.collection || !this.config.enabled || !prompt.trim() || !response.trim()) { + return; + } + + try { + const embedding = await this.getEmbedding(prompt); + if (!embedding.length) return; + + await this.collection.add({ + ids: [crypto.randomUUID()], + embeddings: [embedding], + metadatas: [{ fullResponse: response }], + }); + Logger.debug('Cached new response', 'semantic-cache'); + } catch (error) { + Logger.warn(`Cache write failed: ${String(error)}`, 'semantic-cache'); + } + } +} diff --git a/src/types.ts b/src/types.ts index e613a6f..f82099f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -90,12 +90,20 @@ export class PathValidationError extends OllamaError { // Plugin Configuration // ============================================================ +export interface CacheConfig { + enabled: boolean; + similarityThreshold: number; + collectionName: string; + embeddingModel: string; +} + export interface PluginSettings { ollamaUrl: string; model: string; vaultSearchLimit: number; maxMessageHistory: number; lastIndexTime: number; + cacheConfig: CacheConfig; } // ============================================================ diff --git a/tests/ollama-client-cache.test.ts b/tests/ollama-client-cache.test.ts new file mode 100644 index 0000000..efc4107 --- /dev/null +++ b/tests/ollama-client-cache.test.ts @@ -0,0 +1,441 @@ +// tests/ollama-client-cache.test.ts + +import { OllamaMessage, OllamaTool, CacheConfig } from '../src/types'; + +// Create mock functions for the cache service +const mockInitialize = jest.fn().mockResolvedValue(undefined); +const mockGetCache = jest.fn().mockResolvedValue(null); +const mockSetCache = jest.fn().mockResolvedValue(undefined); + +// Mock the semantic cache service BEFORE importing OllamaClient +jest.mock('../src/semantic-cache', () => ({ + SemanticCacheService: jest.fn().mockImplementation(() => ({ + initialize: mockInitialize, + getCache: mockGetCache, + setCache: mockSetCache, + })), +})); + +// Mock fetch globally +jest.spyOn(global, 'fetch').mockImplementation(jest.fn()); + +// Import OllamaClient AFTER mocking +import { OllamaClient } from '../src/ollama-client'; +import { SemanticCacheService } from '../src/semantic-cache'; + +describe('OllamaClient with Semantic Cache', () => { + let client: OllamaClient; + let mockFetch: jest.Mock; + + function createMockReader(data: string) { + const encoder = new TextEncoder(); + const encoded = encoder.encode(data); + let called = false; + return { + read: () => { + if (!called) { + called = true; + return Promise.resolve({ done: false, value: encoded }); + } + return Promise.resolve({ done: true, value: new Uint8Array(0) }); + }, + releaseLock: jest.fn(), + }; + } + + const mockMessages: OllamaMessage[] = [ + { role: 'system', content: 'You are helpful.' }, + { role: 'user', content: 'What is AI?' }, + ]; + + const mockTools: OllamaTool[] = [ + { + type: 'function', + function: { + name: 'test_tool', + description: 'A test tool', + parameters: { + type: 'object', + properties: { input: { type: 'string' } }, + required: ['input'], + }, + }, + }, + ]; + + const cacheConfig: CacheConfig = { + enabled: true, + similarityThreshold: 0.85, + collectionName: 'test_cache', + embeddingModel: 'nomic-embed-text', + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockFetch = global.fetch as jest.Mock; + + client = new OllamaClient('http://localhost:11434', 'llama3', mockFetch, cacheConfig); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('initializeCache', () => { + it('should call initialize on cache service when enabled', async () => { + await client.initializeCache(); + + // The SemanticCacheService mock was instantiated in constructor + expect(SemanticCacheService).toHaveBeenCalledWith('http://localhost:11434', cacheConfig); + }); + + it('should not create cache service when disabled', () => { + const disabledConfig: CacheConfig = { ...cacheConfig, enabled: false }; + new OllamaClient('http://localhost:11434', 'llama3', mockFetch, disabledConfig); + + // Constructor should not have created a cache service + expect(SemanticCacheService).not.toHaveBeenCalledWith( + 'http://localhost:11434', + disabledConfig + ); + }); + + it('should not create cache service when no config provided', () => { + new OllamaClient('http://localhost:11434', 'llama3', mockFetch); + + expect(SemanticCacheService).not.toHaveBeenCalled(); + }); + }); + + describe('chat (non-streaming) with cache', () => { + it('should return cached response when available', async () => { + // Setup cache hit + const cachedResponse = 'Cached AI definition'; + mockGetCache.mockResolvedValueOnce(cachedResponse); + + const result = await client.chat(mockMessages); + + expect(result.content).toBe(cachedResponse); + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('should call LLM and cache response on cache miss', async () => { + // Setup cache miss + mockGetCache.mockResolvedValueOnce(null); + + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'AI is the simulation of intelligence.', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + const result = await client.chat(mockMessages); + + expect(result.content).toBe('AI is the simulation of intelligence.'); + expect(mockFetch).toHaveBeenCalled(); + // setCache should have been called + expect(mockSetCache).toHaveBeenCalled(); + }); + + it('should bypass cache when tools are present', async () => { + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'Tool response', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + const result = await client.chat(mockMessages, mockTools); + + expect(mockFetch).toHaveBeenCalled(); + expect(mockGetCache).not.toHaveBeenCalled(); + expect(mockSetCache).not.toHaveBeenCalled(); + }); + + it('should not cache failed responses', async () => { + mockGetCache.mockResolvedValueOnce(null); + mockFetch.mockResolvedValueOnce({ ok: false, status: 500 }); + + await expect(client.chat(mockMessages)).rejects.toThrow(); + expect(mockSetCache).not.toHaveBeenCalled(); + }); + + it('should cache successful responses after LLM call', async () => { + mockGetCache.mockResolvedValueOnce(null); + + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'Fresh response from LLM', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + await client.chat(mockMessages); + + expect(mockSetCache).toHaveBeenCalledWith('What is AI?', 'Fresh response from LLM'); + }); + }); + + describe('streamChat with cache', () => { + it('should return cached response when available', async () => { + const cachedResponse = 'Cached streaming response'; + mockGetCache.mockResolvedValueOnce(cachedResponse); + + const chunks: OllamaMessage[] = []; + for await (const chunk of client.streamChat(mockMessages)) { + chunks.push(chunk); + } + + expect(chunks.length).toBe(1); + expect(chunks[0].content).toBe(cachedResponse); + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('should stream from LLM and cache on cache miss', async () => { + mockGetCache.mockResolvedValueOnce(null); + + const streamData = [ + JSON.stringify({ message: { content: 'AI' } }), + '\n', + JSON.stringify({ message: { content: ' is' } }), + '\n', + JSON.stringify({ message: { content: ' cool' } }), + '\n', + ].join(''); + + const mockReader = createMockReader(streamData); + + mockFetch.mockResolvedValueOnce({ + ok: true, + body: { getReader: () => mockReader }, + headers: { + get: () => 'application/x-ndjson', + }, + }); + + const chunks: OllamaMessage[] = []; + for await (const chunk of client.streamChat(mockMessages)) { + chunks.push(chunk); + } + + expect(chunks.length).toBe(3); + expect(mockFetch).toHaveBeenCalled(); + expect(mockSetCache).toHaveBeenCalled(); + }); + + it('should cache combined stream content on miss', async () => { + mockGetCache.mockResolvedValueOnce(null); + + const streamData = [ + JSON.stringify({ message: { content: 'Hello' } }), + '\n', + JSON.stringify({ message: { content: ' world' } }), + '\n', + ].join(''); + + const mockReader = createMockReader(streamData); + + mockFetch.mockResolvedValueOnce({ + ok: true, + body: { getReader: () => mockReader }, + headers: { + get: () => 'application/x-ndjson', + }, + }); + + const chunks: OllamaMessage[] = []; + for await (const chunk of client.streamChat(mockMessages)) { + chunks.push(chunk); + } + + expect(mockSetCache).toHaveBeenCalledWith('What is AI?', 'Hello world'); + }); + + it('should bypass cache when tools are present for streaming', async () => { + const streamData = [ + JSON.stringify({ message: { content: 'Tool call response' } }), + '\n', + ].join(''); + + const mockReader = createMockReader(streamData); + + mockFetch.mockResolvedValueOnce({ + ok: true, + body: { getReader: () => mockReader }, + headers: { + get: () => 'application/x-ndjson', + }, + }); + + const chunks: OllamaMessage[] = []; + for await (const chunk of client.streamChat(mockMessages, mockTools)) { + chunks.push(chunk); + } + + expect(mockFetch).toHaveBeenCalled(); + expect(mockGetCache).not.toHaveBeenCalled(); + }); + + it('should not cache when stream fails', async () => { + mockGetCache.mockResolvedValueOnce(null); + mockFetch.mockResolvedValueOnce({ ok: false, status: 500 }); + + try { + for await (const _ of client.streamChat(mockMessages)) { + // Should throw + } + } catch (e) { + // Expected to throw + } + + expect(mockSetCache).not.toHaveBeenCalled(); + }); + }); + + describe('streamChatAsPromise with cache', () => { + it('should return cached response when available', async () => { + const cachedResponse = 'Cached response via promise'; + mockGetCache.mockResolvedValueOnce(cachedResponse); + + const chunks = await client.streamChatAsPromise(mockMessages); + + expect(chunks.length).toBe(1); + expect(chunks[0].content).toBe(cachedResponse); + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('should stream and cache on miss', async () => { + mockGetCache.mockResolvedValueOnce(null); + + const streamData = [ + JSON.stringify({ message: { content: 'Full' } }), + '\n', + JSON.stringify({ message: { content: ' response' } }), + '\n', + ].join(''); + + const mockReader = createMockReader(streamData); + + mockFetch.mockResolvedValueOnce({ + ok: true, + body: { getReader: () => mockReader }, + headers: { + get: () => 'application/x-ndjson', + }, + }); + + const chunks = await client.streamChatAsPromise(mockMessages); + + expect(chunks.length).toBe(2); + expect(mockFetch).toHaveBeenCalled(); + expect(mockSetCache).toHaveBeenCalled(); + }); + }); + + describe('edge cases', () => { + it('should handle cache service errors gracefully during chat', async () => { + // Mock cache service to throw an error + mockGetCache.mockRejectedValueOnce(new Error('Cache error')); + + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'LLM response after cache failure', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + // The chat method does not handle cache errors, so it should propagate + // However, the client should still be usable + await expect(client.chat(mockMessages)).rejects.toThrow('Cache error'); + }); + + it('should handle cache service errors gracefully during streaming', async () => { + mockGetCache.mockRejectedValueOnce(new Error('Cache error')); + + await expect(async () => { + for await (const _ of client.streamChat(mockMessages)) { + // Should throw + } + }).rejects.toThrow('Cache error'); + }); + + it('should work without cache when no config provided', async () => { + const noCacheClient = new OllamaClient('http://localhost:11434', 'llama3', mockFetch); + + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'Response without cache', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + const result = await noCacheClient.chat(mockMessages); + + expect(result.content).toBe('Response without cache'); + expect(mockFetch).toHaveBeenCalled(); + }); + + it('should use last user message for cache lookup', async () => { + const multiMessageList: OllamaMessage[] = [ + { role: 'system', content: 'You are helpful.' }, + { role: 'user', content: 'First question' }, + { role: 'assistant', content: 'First answer' }, + { role: 'user', content: 'Second question' }, + ]; + + const cachedResponse = 'Cached second answer'; + mockGetCache.mockResolvedValueOnce(cachedResponse); + + const result = await client.chat(multiMessageList); + + expect(result.content).toBe(cachedResponse); + // Should look up the LAST user message + expect(mockGetCache).toHaveBeenCalledWith('Second question'); + }); + + it('should skip cache when no user message found', async () => { + const onlyAssistantMessages: OllamaMessage[] = [ + { role: 'system', content: 'You are helpful.' }, + { role: 'assistant', content: 'Hello!' }, + ]; + + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + message: { + content: 'Response for assistant-only messages', + }, + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + const result = await client.chat(onlyAssistantMessages); + + expect(result.content).toBe('Response for assistant-only messages'); + expect(mockGetCache).not.toHaveBeenCalled(); + expect(mockSetCache).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/tests/semantic-cache.test.ts b/tests/semantic-cache.test.ts new file mode 100644 index 0000000..4b00aa1 --- /dev/null +++ b/tests/semantic-cache.test.ts @@ -0,0 +1,267 @@ +// tests/semantic-cache.test.ts + +import { ChromaClient } from 'chromadb'; +import { CacheConfig } from '../src/types'; + +// Mock ChromaDB module +jest.mock('chromadb', () => ({ + ChromaClient: jest.fn().mockImplementation(() => { + return { + getOrCreateCollection: jest.fn().mockResolvedValue({ + query: jest.fn(), + add: jest.fn(), + }), + }; + }), +})); + +// Now import SemanticCacheService after mocking +import { SemanticCacheService } from '../src/semantic-cache'; + +jest.spyOn(global, 'fetch').mockImplementation(jest.fn()); + +const mockChromaClient = { + getOrCreateCollection: jest.fn().mockResolvedValue({ + query: jest.fn(), + add: jest.fn(), + }), +}; + +// Set up mock instance +(ChromaClient as jest.Mock).mockImplementation(() => mockChromaClient as any); + +describe('SemanticCacheService', () => { + let service: SemanticCacheService; + let config: CacheConfig; + let mockFetch: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + mockFetch = global.fetch as jest.Mock; + + config = { + enabled: true, + similarityThreshold: 0.85, + collectionName: 'test_cache', + embeddingModel: 'nomic-embed-text', + }; + + service = new SemanticCacheService('http://localhost:11434', config); + }); + + describe('initialize', () => { + it('should create or get the collection on initialize', async () => { + await service.initialize(); + + expect(mockChromaClient.getOrCreateCollection).toHaveBeenCalledWith({ + name: 'test_cache', + metadata: { 'hnsw:space': 'cosine' }, + }); + }); + + it('should not initialize if cache is disabled', async () => { + const disabledConfig = { ...config, enabled: false }; + service = new SemanticCacheService('http://localhost:11434', disabledConfig); + await service.initialize(); + + expect(mockChromaClient.getOrCreateCollection).not.toHaveBeenCalled(); + }); + }); + + describe('getEmbedding', () => { + it('should call Ollama embeddings API correctly', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => + Promise.resolve({ + embedding: [0.1, 0.2, 0.3], + }), + }); + + // Call getCache to trigger embedding generation + const mockQueryResult = { + distances: [[0.1]], + metadatas: [[{ fullResponse: 'Cached response' }]], + }; + mockChromaClient.getOrCreateCollection().query.mockResolvedValueOnce(mockQueryResult); + + await service.initialize(); + await service.getCache('test prompt'); + + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:11434/api/embeddings', + expect.objectContaining({ + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: 'nomic-embed-text', + prompt: 'test prompt', + }), + }) + ); + }); + + it('should return empty array on embedding failure', async () => { + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 500, + }); + + await service.initialize(); + // We need to test the private method indirectly via getCache + const result = await service.getCache('test prompt'); + // Embedding failed, so getCache should return null + expect(result).toBeNull(); + }); + }); + + describe('getCache', () => { + beforeEach(async () => { + await service.initialize(); + }); + + it('should return cached response when similarity is above threshold', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + const mockQueryResult = { + distances: [[0.1]], // distance < 0.15 means similarity > 0.85 + metadatas: [[{ fullResponse: 'Cached answer' }]], + }; + mockChromaClient.getOrCreateCollection().query.mockResolvedValueOnce(mockQueryResult); + + const result = await service.getCache('test prompt'); + + expect(result).toBe('Cached answer'); + }); + + it('should return null when similarity is below threshold', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + const mockQueryResult = { + distances: [[0.2]], // distance > 0.15 means similarity < 0.85 + metadatas: [[{ fullResponse: 'Cached answer' }]], + }; + mockChromaClient.getOrCreateCollection().query.mockResolvedValueOnce(mockQueryResult); + + const result = await service.getCache('test prompt'); + + expect(result).toBeNull(); + }); + + it('should return null when no results found', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + mockChromaClient.getOrCreateCollection().query.mockResolvedValueOnce({ + distances: [], + metadatas: [], + }); + + const result = await service.getCache('test prompt'); + + expect(result).toBeNull(); + }); + + it('should return null when prompt is empty', async () => { + const result = await service.getCache(' '); + + expect(result).toBeNull(); + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('should return null when cache is not initialized', async () => { + // Don't call initialize + const result = await service.getCache('test prompt'); + + expect(result).toBeNull(); + }); + + it('should handle query errors gracefully', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + mockChromaClient + .getOrCreateCollection() + .query.mockRejectedValueOnce(new Error('Query failed')); + + const result = await service.getCache('test prompt'); + + expect(result).toBeNull(); + }); + }); + + describe('setCache', () => { + beforeEach(async () => { + await service.initialize(); + }); + + it('should add entry to collection', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + // crypto.randomUUID mock + const mockUuid = 'mock-uuid-123' as any; + jest.spyOn(crypto, 'randomUUID').mockReturnValue(mockUuid); + + await service.setCache('test prompt', 'test response'); + + const mockCollection = mockChromaClient.getOrCreateCollection(); + expect(mockCollection.add).toHaveBeenCalledWith({ + ids: [mockUuid], + embeddings: [[0.1, 0.2, 0.3]], + metadatas: [{ fullResponse: 'test response' }], + }); + }); + + it('should not add entry when prompt is empty', async () => { + await service.setCache(' ', 'test response'); + + expect(mockFetch).not.toHaveBeenCalled(); + expect(mockChromaClient.getOrCreateCollection().add).not.toHaveBeenCalled(); + }); + + it('should not add entry when response is empty', async () => { + await service.setCache('test prompt', ' '); + + expect(mockFetch).not.toHaveBeenCalled(); + expect(mockChromaClient.getOrCreateCollection().add).not.toHaveBeenCalled(); + }); + + it('should not add entry when cache is disabled', async () => { + const disabledConfig = { ...config, enabled: false }; + service = new SemanticCacheService('http://localhost:11434', disabledConfig); + await service.initialize(); + + await service.setCache('test prompt', 'test response'); + + expect(mockFetch).not.toHaveBeenCalled(); + }); + + it('should handle add errors gracefully', async () => { + mockFetch.mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }), + }); + + const mockUuid = 'mock-uuid-456' as any; + jest.spyOn(crypto, 'randomUUID').mockReturnValue(mockUuid); + + mockChromaClient.getOrCreateCollection().add.mockRejectedValueOnce(new Error('Add failed')); + + // Should not throw + await expect(service.setCache('test prompt', 'test response')).resolves.toBeUndefined(); + }); + }); +});