From d6255374cbd8bb3d169ad21229a1d99becd886f2 Mon Sep 17 00:00:00 2001 From: Zero Liu Date: Thu, 14 May 2026 01:12:52 -0700 Subject: [PATCH] chore(deps): swap unmaintained/legacy deps per e18e module-replacements (#2447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - npm-run-all → npm-run-all2 (dev script uses run-p) - lint-staged → nano-staged (config block + husky hook) - lodash.debounce → local src/utils/debounce.ts with cancel/flush/leading/trailing - dotenv → process.loadEnvFile in integration test bootstrap - eslint-plugin-react → @eslint-react/eslint-plugin (flat-config migration) - js-yaml → yaml (mock + jest CJS moduleNameMapper for jsdom) - crypto-js → pure-JS md5/sha256 in src/utils/hash.ts producing byte-identical digests so all on-disk cache keys (PDF, file, project, search index) remain valid across the upgrade — no migration or re-parse cost Co-authored-by: Claude Opus 4.7 (1M context) --- .husky/pre-commit | 2 +- __mocks__/obsidian.js | 4 +- eslint.config.mjs | 21 +- jest.config.js | 4 + package-lock.json | 1596 ++++++----------- package.json | 19 +- src/cache/fileCache.ts | 4 +- src/cache/pdfCache.ts | 4 +- src/cache/projectContextCache.ts | 6 +- src/commands/customCommandRegister.ts | 2 +- .../chat-components/ChatSettingsPopover.tsx | 6 +- src/context/PromptContextEngine.ts | 8 +- src/integration_tests/composerPrompt.test.ts | 10 +- src/projects/projectRegister.ts | 2 +- src/search/dbOperations.ts | 4 +- src/search/indexOperations.ts | 4 +- src/state/vaultDataAtoms.ts | 10 +- src/system-prompts/systemPromptRegister.ts | 2 +- src/tests/projectContextCache.test.ts | 8 +- src/utils.ts | 11 +- src/utils/debounce.test.ts | 86 + src/utils/debounce.ts | 89 + src/utils/hash.test.ts | 80 + src/utils/hash.ts | 214 +++ 24 files changed, 1108 insertions(+), 1088 deletions(-) create mode 100644 src/utils/debounce.test.ts create mode 100644 src/utils/debounce.ts create mode 100644 src/utils/hash.test.ts create mode 100644 src/utils/hash.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index 2312dc58..87119bc1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -npx lint-staged +npx nano-staged diff --git a/__mocks__/obsidian.js b/__mocks__/obsidian.js index 319f4b79..f683a649 100644 --- a/__mocks__/obsidian.js +++ b/__mocks__/obsidian.js @@ -1,5 +1,5 @@ // __mocks__/obsidian.js -import yaml from "js-yaml"; +import { parse as parseYamlString } from "yaml"; module.exports = { // Reason: normalizePath is used by projectPaths.ts; identity function is sufficient for tests @@ -32,7 +32,7 @@ module.exports = { isDesktop: true, }, parseYaml: jest.fn().mockImplementation((content) => { - return yaml.load(content); + return parseYamlString(content); }), Modal: class Modal { constructor() { diff --git a/eslint.config.mjs b/eslint.config.mjs index 39b1a2f2..41b74c8c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,5 @@ import obsidianmd from "eslint-plugin-obsidianmd"; -import reactPlugin from "eslint-plugin-react"; +import eslintReact from "@eslint-react/eslint-plugin"; import reactHooks from "eslint-plugin-react-hooks"; import tailwind from "eslint-plugin-tailwindcss"; import globals from "globals"; @@ -26,10 +26,10 @@ export default [ // React + tailwind plugins ship flat configs with no `files` filter, so // they'd cascade onto package.json (which uses the JSON parser) and crash. - // Constrain them to JS/TS sources. + // Constrain them to JSX/TSX sources where React/JSX rules actually apply. { - files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"], - ...reactPlugin.configs.flat.recommended, + files: ["**/*.{jsx,tsx}"], + ...eslintReact.configs.recommended, }, { files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"], @@ -53,7 +53,7 @@ export default [ }, }, settings: { - react: { version: "detect" }, + "react-x": { version: "detect" }, tailwindcss: { callees: ["classnames", "clsx", "ctl", "cn", "cva"], config: "./tailwind.config.js", @@ -63,7 +63,6 @@ export default [ rules: { // Carry-over from legacy .eslintrc "no-prototype-builtins": "off", - "react/prop-types": "off", "tailwindcss/classnames-order": "error", "tailwindcss/enforces-negative-arbitrary-values": "error", "tailwindcss/enforces-shorthand": "error", @@ -268,15 +267,7 @@ export default [ "error", { presets: ["native", "microutilities", "preferred"], - allowed: [ - "crypto-js", - "dotenv", - "js-yaml", - "lodash.debounce", - "eslint-plugin-react", - "lint-staged", - "npm-run-all", - ], + allowed: [], }, ], }, diff --git a/jest.config.js b/jest.config.js index 015e9365..4dcc8f2c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,6 +9,10 @@ module.exports = { "\\.(css|less|scss|sass)$": "identity-obj-proxy", "^@/(.*)$": "/src/$1", "^obsidian$": "/__mocks__/obsidian.js", + // The yaml package's "exports" field defaults to a browser ESM entry under + // jsdom; Jest can't parse ESM without extra config, so point at the CJS + // build it ships under dist/. + "^yaml$": "/node_modules/yaml/dist/index.js", }, testRegex: ".*\\.test\\.(jsx?|tsx?)$", moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], diff --git a/package-lock.json b/package-lock.json index ea137d76..3a575002 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,6 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "codemirror-companion-extension": "^0.0.11", - "crypto-js": "^4.1.1", "diff": "^7.0.0", "esbuild-plugin-svg": "^0.1.0", "eventsource-parser": "^1.0.0", @@ -64,7 +63,6 @@ "koa-proxies": "^0.12.3", "langchain": "^1.2.28", "lexical": "^0.34.0", - "lodash.debounce": "^4.0.8", "lucide-react": "^0.462.0", "luxon": "^3.5.0", "minisearch": "^7.2.0", @@ -87,18 +85,17 @@ "zod": "^3.25.76" }, "devDependencies": { + "@eslint-react/eslint-plugin": "^1.38.4", "@jest/globals": "^29.7.0", "@langchain/ollama": "^1.2.2", "@tailwindcss/container-queries": "^0.1.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", - "@types/crypto-js": "^4.1.1", "@types/diff": "^7.0.1", "@types/events": "^3.0.0", "@types/jest": "^29.5.11", "@types/koa": "^2.13.7", "@types/koa__cors": "^4.0.0", - "@types/lodash.debounce": "^4.0.9", "@types/luxon": "^3.4.2", "@types/node": "^16.11.6", "@types/react": "^18.0.33", @@ -108,27 +105,25 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.19.1", "@typescript-eslint/parser": "^8.19.1", - "dotenv": "^16.4.5", "electron": "^27.3.2", "esbuild": "^0.25.0", "eslint": "^9.18.0", "eslint-plugin-obsidianmd": "^0.3.0", - "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-tailwindcss": "^3.18.0", "husky": "^9.1.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.5.0", - "js-yaml": "^4.1.0", - "lint-staged": "^15.2.9", - "npm-run-all": "^4.1.5", + "nano-staged": "^0.8.0", + "npm-run-all2": "^7.0.2", "obsidian": "^1.2.5", "prettier": "^3.3.3", "tailwindcss": "^3.4.15", "ts-jest": "^29.1.0", "tslib": "2.4.0", "typescript": "^5.7.2", - "web-streams-polyfill": "^3.3.2" + "web-streams-polyfill": "^3.3.2", + "yaml": "^2.6.1" } }, "node_modules/@adobe/css-tools": { @@ -1378,6 +1373,166 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint-react/ast": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/ast/-/ast-1.53.1.tgz", + "integrity": "sha512-qvUC99ewtriJp9quVEOvZ6+RHcsMLfVQ0OhZ4/LupZUDhjW7GiX1dxJsFaxHdJ9rLNLhQyLSPmbAToeqUrSruQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/typescript-estree": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/core": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/core/-/core-1.53.1.tgz", + "integrity": "sha512-8prroos5/Uvvh8Tjl1HHCpq4HWD3hV9tYkm7uXgKA6kqj0jHlgRcQzuO6ZPP7feBcK3uOeug7xrq03BuG8QKCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "birecord": "^0.1.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/eff": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/eff/-/eff-1.53.1.tgz", + "integrity": "sha512-uq20lPRAmsWRjIZm+mAV/2kZsU2nDqn5IJslxGWe3Vfdw23hoyhEw3S1KKlxbftwbTvsZjKvVP0iw3bZo/NUpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/eslint-plugin": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/eslint-plugin/-/eslint-plugin-1.53.1.tgz", + "integrity": "sha512-JZ2ciXNCC9CtBBAqYtwWH+Jy/7ZzLw+whei8atP4Fxsbh+Scs30MfEwBzuiEbNw6uF9eZFfPidchpr5RaEhqxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "eslint-plugin-react-debug": "1.53.1", + "eslint-plugin-react-dom": "1.53.1", + "eslint-plugin-react-hooks-extra": "1.53.1", + "eslint-plugin-react-naming-convention": "1.53.1", + "eslint-plugin-react-web-api": "1.53.1", + "eslint-plugin-react-x": "1.53.1" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@eslint-react/kit": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/kit/-/kit-1.53.1.tgz", + "integrity": "sha512-zOi2le9V4rMrJvQV4OeedGvMGvDT46OyFPOwXKs7m0tQu5vXVJ8qwIPaVQT1n/WIuvOg49OfmAVaHpGxK++xLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/utils": "^8.43.0", + "ts-pattern": "^5.8.0", + "zod": "^4.1.5" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/kit/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@eslint-react/shared": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/shared/-/shared-1.53.1.tgz", + "integrity": "sha512-gomJQmFqQgQVI3Ra4vTMG/s6a4bx3JqeNiTBjxBJt4C9iGaBj458GkP4LJHX7TM6xUzX+fMSKOPX7eV3C/+UCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@typescript-eslint/utils": "^8.43.0", + "ts-pattern": "^5.8.0", + "zod": "^4.1.5" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@eslint-react/shared/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@eslint-react/var": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@eslint-react/var/-/var-1.53.1.tgz", + "integrity": "sha512-yzwopvPntcHU7mmDvWzRo1fb8QhjD8eDRRohD11rTV1u7nWO4QbJi0pOyugQakvte1/W11Y0Vr8Of0Ojk/A6zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@eslint/config-array": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", @@ -8056,12 +8211,6 @@ "@types/node": "*" } }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", - "dev": true - }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -8315,21 +8464,6 @@ "@types/koa": "*" } }, - "node_modules/@types/lodash": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz", - "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", - "dev": true - }, - "node_modules/@types/lodash.debounce": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", - "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/luxon": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", @@ -9483,6 +9617,13 @@ "node": ">=8" } }, + "node_modules/birecord": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/birecord/-/birecord-0.1.1.tgz", + "integrity": "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==", + "dev": true, + "license": "(MIT OR Apache-2.0)" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -9913,87 +10054,6 @@ "url": "https://polar.sh/cva" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -10116,12 +10176,6 @@ "simple-swizzle": "^0.2.2" } }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -10150,6 +10204,13 @@ "node": ">= 10" } }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10278,11 +10339,6 @@ "node": "*" } }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, "node_modules/css-select": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", @@ -10819,6 +10875,7 @@ "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "peer": true, "engines": { "node": ">=12" }, @@ -10973,18 +11030,6 @@ "node": ">=6" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -11930,6 +11975,78 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-debug": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-debug/-/eslint-plugin-react-debug-1.53.1.tgz", + "integrity": "sha512-WNOiQ6jhodJE88VjBU/IVDM+2Zr9gKHlBFDUSA3fQ0dMB5RiBVj5wMtxbxRuipK/GqNJbteqHcZoYEod7nfddg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-dom": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-dom/-/eslint-plugin-react-dom-1.53.1.tgz", + "integrity": "sha512-UYrWJ2cS4HpJ1A5XBuf1HfMpPoLdfGil+27g/ldXfGemb4IXqlxHt4ANLyC8l2CWcE3SXGJW7mTslL34MG0qTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "compare-versions": "^6.1.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", @@ -11943,6 +12060,155 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, + "node_modules/eslint-plugin-react-hooks-extra": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks-extra/-/eslint-plugin-react-hooks-extra-1.53.1.tgz", + "integrity": "sha512-fshTnMWNn9NjFLIuy7HzkRgGK29vKv4ZBO9UMr+kltVAfKLMeXXP6021qVKk66i/XhQjbktiS+vQsu1Rd3ZKvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-naming-convention": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-naming-convention/-/eslint-plugin-react-naming-convention-1.53.1.tgz", + "integrity": "sha512-rvZ/B/CSVF8d34HQ4qIt90LRuxotVx+KUf3i1OMXAyhsagEFMRe4gAlPJiRufZ+h9lnuu279bEdd+NINsXOteA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-web-api": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-web-api/-/eslint-plugin-react-web-api-1.53.1.tgz", + "integrity": "sha512-INVZ3Cbl9/b+sizyb43ChzEPXXYuDsBGU9BIg7OVTNPyDPloCXdI+dQFAcSlDocZhPrLxhPV3eT6+gXbygzYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-x": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-x/-/eslint-plugin-react-x-1.53.1.tgz", + "integrity": "sha512-MwMNnVwiPem0U6SlejDF/ddA4h/lmP6imL1RDZ2m3pUBrcdcOwOx0gyiRVTA3ENnhRlWfHljHf5y7m8qDSxMEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-react/ast": "1.53.1", + "@eslint-react/core": "1.53.1", + "@eslint-react/eff": "1.53.1", + "@eslint-react/kit": "1.53.1", + "@eslint-react/shared": "1.53.1", + "@eslint-react/var": "1.53.1", + "@typescript-eslint/scope-manager": "^8.43.0", + "@typescript-eslint/type-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "@typescript-eslint/utils": "^8.43.0", + "compare-versions": "^6.1.1", + "is-immutable-type": "^5.0.1", + "string-ts": "^2.2.1", + "ts-pattern": "^5.8.0" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "ts-api-utils": "^2.1.0", + "typescript": "^4.9.5 || ^5.3.3" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": false + }, + "ts-api-utils": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -12702,18 +12968,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -13200,12 +13454,6 @@ "react-is": "^16.7.0" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -13857,6 +14105,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-immutable-type": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/is-immutable-type/-/is-immutable-type-5.0.1.tgz", + "integrity": "sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@typescript-eslint/type-utils": "^8.0.0", + "ts-api-utils": "^2.0.0", + "ts-declaration-location": "^1.0.4" + }, + "peerDependencies": { + "eslint": "*", + "typescript": ">=4.7.4" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -15443,12 +15707,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -15907,336 +16165,6 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "node_modules/lint-staged": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.9.tgz", - "integrity": "sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==", - "dev": true, - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.7", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/lint-staged/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, - "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/lint-staged/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, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/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, - "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/lint-staged/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, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lint-staged/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", - "dev": true, - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, - "node_modules/listr2/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -16258,11 +16186,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "devOptional": true }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -16317,150 +16240,6 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "peer": true }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dev": true, - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dev": true, - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -17372,18 +17151,6 @@ "node": ">=6" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -17477,6 +17244,22 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nano-staged": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/nano-staged/-/nano-staged-0.8.0.tgz", + "integrity": "sha512-QSEqPGTCJbkHU2yLvfY6huqYPjdBrOaTMKatO1F8nCSrkQGXeKwtCiCnsdxnuMhbg3DTVywKaeWLGCE5oJpq0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0" + }, + "bin": { + "nano-staged": "lib/bin.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -18027,12 +17810,6 @@ "@img/sharp-win32-x64": "0.34.4" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -18131,27 +17908,6 @@ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -18172,179 +17928,106 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-7.0.2.tgz", + "integrity": "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" }, "bin": { "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">= 4" + "node": "^18.17.0 || >=20.5.0", + "npm": ">= 9" } }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm-run-all2/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node": ">=16 || 14 >=14.17" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, + "license": "ISC", "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "which": "bin/which" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-run-path": { @@ -19655,39 +19338,28 @@ "pify": "^2.3.0" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", "dev": true, + "license": "ISC", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/readable-web-to-node-stream": { @@ -19972,49 +19644,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/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, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -20046,12 +19675,6 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -20497,46 +20120,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20572,38 +20155,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", - "dev": true - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -20674,15 +20225,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -20696,6 +20238,13 @@ "node": ">=10" } }, + "node_modules/string-ts": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/string-ts/-/string-ts-2.3.1.tgz", + "integrity": "sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -20751,24 +20300,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/string.prototype.repeat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", @@ -21454,6 +20985,42 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-declaration-location": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz", + "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==", + "dev": true, + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/ts-declaration-location" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/ts-declaration-location/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/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -21502,6 +21069,13 @@ } } }, + "node_modules/ts-pattern": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.9.0.tgz", + "integrity": "sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==", + "dev": true, + "license": "MIT" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -21984,16 +21558,6 @@ "node": ">=10.12.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -22352,14 +21916,18 @@ "dev": true }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yaml-eslint-parser": { diff --git a/package.json b/package.json index 1e10f12b..a397e3c7 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Your AI Copilot: Chat with Your Second Brain, Learn Faster, Work Smarter.", "main": "main.js", "scripts": { - "dev": "npm-run-all --parallel dev:*", + "dev": "run-p dev:*", "dev:tailwind": "npx tailwindcss -i src/styles/tailwind.css -o styles.css --watch", "dev:esbuild": "node esbuild.config.mjs", "build": "npm run build:tailwind && npm run build:esbuild || exit 1", @@ -22,7 +22,7 @@ }, "keywords": [], "author": "Logan Yang", - "lint-staged": { + "nano-staged": { "*.{js,jsx,ts,tsx,json,css,md}": [ "prettier --write" ], @@ -32,18 +32,17 @@ }, "license": "AGPL-3.0", "devDependencies": { + "@eslint-react/eslint-plugin": "^1.38.4", "@jest/globals": "^29.7.0", "@langchain/ollama": "^1.2.2", "@tailwindcss/container-queries": "^0.1.1", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", - "@types/crypto-js": "^4.1.1", "@types/diff": "^7.0.1", "@types/events": "^3.0.0", "@types/jest": "^29.5.11", "@types/koa": "^2.13.7", "@types/koa__cors": "^4.0.0", - "@types/lodash.debounce": "^4.0.9", "@types/luxon": "^3.4.2", "@types/node": "^16.11.6", "@types/react": "^18.0.33", @@ -53,27 +52,25 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.19.1", "@typescript-eslint/parser": "^8.19.1", - "dotenv": "^16.4.5", "electron": "^27.3.2", "esbuild": "^0.25.0", "eslint": "^9.18.0", "eslint-plugin-obsidianmd": "^0.3.0", - "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-tailwindcss": "^3.18.0", "husky": "^9.1.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.5.0", - "js-yaml": "^4.1.0", - "lint-staged": "^15.2.9", - "npm-run-all": "^4.1.5", + "nano-staged": "^0.8.0", + "npm-run-all2": "^7.0.2", "obsidian": "^1.2.5", "prettier": "^3.3.3", "tailwindcss": "^3.4.15", "ts-jest": "^29.1.0", "tslib": "2.4.0", "typescript": "^5.7.2", - "web-streams-polyfill": "^3.3.2" + "web-streams-polyfill": "^3.3.2", + "yaml": "^2.6.1" }, "dependencies": { "@codemirror/state": "^6.5.2", @@ -121,7 +118,6 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "codemirror-companion-extension": "^0.0.11", - "crypto-js": "^4.1.1", "diff": "^7.0.0", "esbuild-plugin-svg": "^0.1.0", "eventsource-parser": "^1.0.0", @@ -131,7 +127,6 @@ "koa-proxies": "^0.12.3", "langchain": "^1.2.28", "lexical": "^0.34.0", - "lodash.debounce": "^4.0.8", "lucide-react": "^0.462.0", "luxon": "^3.5.0", "minisearch": "^7.2.0", diff --git a/src/cache/fileCache.ts b/src/cache/fileCache.ts index 4d58bbe4..b89ef8c1 100644 --- a/src/cache/fileCache.ts +++ b/src/cache/fileCache.ts @@ -1,5 +1,5 @@ import { logError, logInfo } from "@/logger"; -import { MD5 } from "crypto-js"; +import { md5 } from "@/utils/hash"; import { TFile } from "obsidian"; export interface FileCacheEntry { @@ -33,7 +33,7 @@ export class FileCache { getCacheKey(file: TFile, additionalContext?: string): string { // Use file path, size and mtime for a unique but efficient cache key const metadata = `${file.path}:${file.stat.size}:${file.stat.mtime}${additionalContext ? `:${additionalContext}` : ""}`; - return MD5(metadata).toString(); + return md5(metadata); } private getCachePath(cacheKey: string): string { diff --git a/src/cache/pdfCache.ts b/src/cache/pdfCache.ts index 6eb48558..e6ca01b3 100644 --- a/src/cache/pdfCache.ts +++ b/src/cache/pdfCache.ts @@ -1,6 +1,6 @@ import { Pdf4llmResponse } from "@/LLMProviders/brevilabsClient"; import { logError, logInfo } from "@/logger"; -import { MD5 } from "crypto-js"; +import { md5 } from "@/utils/hash"; import { TFile } from "obsidian"; export class PDFCache { @@ -26,7 +26,7 @@ export class PDFCache { private getCacheKey(file: TFile): string { // Use file path, size and mtime for a unique but efficient cache key const metadata = `${file.path}:${file.stat.size}:${file.stat.mtime}`; - const key = MD5(metadata).toString(); + const key = md5(metadata); logInfo("Generated cache key for PDF:", { path: file.path, key }); return key; } diff --git a/src/cache/projectContextCache.ts b/src/cache/projectContextCache.ts index 6b2f6500..e7976536 100644 --- a/src/cache/projectContextCache.ts +++ b/src/cache/projectContextCache.ts @@ -3,9 +3,9 @@ import { FileCache } from "@/cache/fileCache"; import { logError, logInfo, logWarn } from "@/logger"; import { getMatchingPatterns, shouldIndexFile } from "@/search/searchUtils"; import { getCachedProjects } from "@/projects/state"; -import { MD5 } from "crypto-js"; +import { md5 } from "@/utils/hash"; import { TAbstractFile, TFile, Vault } from "obsidian"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { Mutex } from "async-mutex"; export interface ContextCache { @@ -173,7 +173,7 @@ export class ProjectContextCache { private getCacheKey(project: ProjectConfig): string { // Use project ID as cache key - return MD5(project.id).toString(); + return md5(project.id); } private getCachePath(cacheKey: string): string { diff --git a/src/commands/customCommandRegister.ts b/src/commands/customCommandRegister.ts index 1915cfa3..a8111e14 100644 --- a/src/commands/customCommandRegister.ts +++ b/src/commands/customCommandRegister.ts @@ -9,7 +9,7 @@ import { } from "@/commands/customCommandUtils"; import { Editor, Plugin, TFile, Vault } from "obsidian"; import { CustomCommandChatModal } from "@/commands/CustomCommandChatModal"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { CustomCommand } from "@/commands/type"; import { deleteCachedCommand, diff --git a/src/components/chat-components/ChatSettingsPopover.tsx b/src/components/chat-components/ChatSettingsPopover.tsx index 5a2bf48e..f1110cfe 100644 --- a/src/components/chat-components/ChatSettingsPopover.tsx +++ b/src/components/chat-components/ChatSettingsPopover.tsx @@ -12,7 +12,7 @@ import { SettingSwitch } from "@/components/ui/setting-switch"; import { ModelParametersEditor } from "@/components/ui/ModelParametersEditor"; import { CustomModel, getModelKey } from "@/aiParams"; import { getSettings, updateSetting } from "@/settings/model"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { getDefaultSystemPromptTitle, getDisableBuiltinSystemPrompt, @@ -83,8 +83,8 @@ export function ChatSettingsPopover() { } }, [showConfirmation]); - // Debounced save function - must be defined before handleOpenChange - // Reference: Command module uses lodash.debounce which has cancel() method + // Debounced save function - must be defined before handleOpenChange. + // Uses debounce.cancel() / debounce.flush() to manage pending writes on unmount. const debouncedSave = useMemo( () => debounce((updatedModel: CustomModel) => { diff --git a/src/context/PromptContextEngine.ts b/src/context/PromptContextEngine.ts index fd2317fa..94675e95 100644 --- a/src/context/PromptContextEngine.ts +++ b/src/context/PromptContextEngine.ts @@ -1,4 +1,4 @@ -import { SHA256 } from "crypto-js"; +import { sha256 } from "@/utils/hash"; import { logInfo } from "@/logger"; import { PROMPT_LAYER_LABELS, @@ -125,11 +125,11 @@ export class PromptContextEngine { } /** - * Compute a SHA-256 hash for the supplied value. - * Uses crypto-js for mobile compatibility (no Node.js crypto). + * Compute a SHA-256 hash for the supplied value. Pure-JS so it works on + * Obsidian mobile (no node:crypto, no sync Web Crypto). */ private hash(value: string): string { - return SHA256(value || "").toString(); + return sha256(value || ""); } /** diff --git a/src/integration_tests/composerPrompt.test.ts b/src/integration_tests/composerPrompt.test.ts index 1da4ee12..4367b24a 100644 --- a/src/integration_tests/composerPrompt.test.ts +++ b/src/integration_tests/composerPrompt.test.ts @@ -1,5 +1,4 @@ import { DEFAULT_SYSTEM_PROMPT, COMPOSER_OUTPUT_INSTRUCTIONS } from "../constants"; -import * as dotenv from "dotenv"; import { jest } from "@jest/globals"; import { GoogleGenerativeAI, @@ -8,8 +7,13 @@ import { GenerativeModel, } from "@google/generative-ai"; -// Load environment variables from .env.test -dotenv.config({ path: ".env.test" }); +// Load environment variables from .env.test using Node's native env-file loader. +// `loadEnvFile` is available on Node >= 20.12 / 21.7 but @types/node here predates the typing. +try { + (process as unknown as { loadEnvFile: (path: string) => void }).loadEnvFile(".env.test"); +} catch { + // .env.test is optional — skip silently when absent +} // Test data const atom_note = `Atoms are the basic particles of the chemical elements. An atom consists of a nucleus of protons and generally neutrons, surrounded by an electromagnetically bound swarm of electrons. The chemical elements are distinguished from each other by the number of protons that are in their atoms. For example, any atom that contains 11 protons is sodium, and any atom that contains 29 protons is copper. Atoms with the same number of protons but a different number of neutrons are called isotopes of the same element. diff --git a/src/projects/projectRegister.ts b/src/projects/projectRegister.ts index 55178f9d..ff4a8065 100644 --- a/src/projects/projectRegister.ts +++ b/src/projects/projectRegister.ts @@ -21,7 +21,7 @@ import { import { loadAllProjects } from "@/projects/projectUtils"; import { PROJECT_CONFIG_FILE_NAME, PROJECTS_UNSUPPORTED_FOLDER_NAME } from "@/projects/constants"; import { getSettings, subscribeToSettingsChange } from "@/settings/model"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { App, Notice, TAbstractFile, Vault } from "obsidian"; /** diff --git a/src/search/dbOperations.ts b/src/search/dbOperations.ts index 484dbc22..6caec67e 100644 --- a/src/search/dbOperations.ts +++ b/src/search/dbOperations.ts @@ -8,7 +8,7 @@ import { areEmbeddingModelsSame } from "@/utils"; import { Embeddings } from "@langchain/core/embeddings"; import { create, insert, Orama, remove, removeMultiple, search } from "@orama/orama"; import { Mutex } from "async-mutex"; -import { MD5 } from "crypto-js"; +import { md5 } from "@/utils/hash"; import { App, Notice, Platform } from "obsidian"; import { ChunkedStorage } from "./chunkedStorage"; import { getMatchingPatterns, getVectorLength, shouldIndexFile } from "./searchUtils"; @@ -260,7 +260,7 @@ export class DBOperations { private getVaultIdentifier(): string { const vaultName = this.app.vault.getName(); - return MD5(vaultName).toString(); + return md5(vaultName); } public markUnsavedChanges() { diff --git a/src/search/indexOperations.ts b/src/search/indexOperations.ts index 01a07cf0..ddae0996 100644 --- a/src/search/indexOperations.ts +++ b/src/search/indexOperations.ts @@ -12,7 +12,7 @@ import { RateLimiter } from "@/rateLimiter"; import { ChunkManager, getSharedChunkManager } from "@/search/v3/chunks"; import { getSettings, subscribeToSettingsChange } from "@/settings/model"; import { formatDateTime } from "@/utils"; -import { MD5 } from "crypto-js"; +import { md5 } from "@/utils/hash"; import { App, Notice, TFile } from "obsidian"; import type { SemanticIndexBackend, @@ -379,7 +379,7 @@ export class IndexOperations { } private getDocHash(sourceDocument: string): string { - return MD5(sourceDocument).toString(); + return md5(sourceDocument); } private async getFilesToIndex(overwrite?: boolean): Promise { diff --git a/src/state/vaultDataAtoms.ts b/src/state/vaultDataAtoms.ts index 7ea9c82e..499728ce 100644 --- a/src/state/vaultDataAtoms.ts +++ b/src/state/vaultDataAtoms.ts @@ -1,6 +1,6 @@ import { atom } from "jotai"; import { TFile, TFolder, TAbstractFile } from "obsidian"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { settingsStore } from "@/settings/model"; import { getTagsFromNote, isAllowedFileForNoteContext } from "@/utils"; import { logInfo } from "@/logger"; @@ -161,7 +161,7 @@ export class VaultDataManager { }; /** - * Debounced notes refresh - batches rapid file operations using lodash.debounce + * Debounced notes refresh - batches rapid file operations via debounce */ private debouncedRefreshNotes = debounce(() => this.refreshNotes(), VAULT_DEBOUNCE_DELAY, { leading: true, @@ -169,7 +169,7 @@ export class VaultDataManager { }); /** - * Debounced folders refresh - batches rapid file operations using lodash.debounce + * Debounced folders refresh - batches rapid file operations via debounce */ private debouncedRefreshFolders = debounce(() => this.refreshFolders(), VAULT_DEBOUNCE_DELAY, { leading: true, @@ -177,7 +177,7 @@ export class VaultDataManager { }); /** - * Debounced frontmatter tags refresh - batches rapid file operations using lodash.debounce + * Debounced frontmatter tags refresh - batches rapid file operations via debounce */ private debouncedRefreshTagsFrontmatter = debounce( () => this.refreshTagsFrontmatter(), @@ -189,7 +189,7 @@ export class VaultDataManager { ); /** - * Debounced all tags refresh - batches rapid file operations using lodash.debounce + * Debounced all tags refresh - batches rapid file operations via debounce */ private debouncedRefreshTagsAll = debounce(() => this.refreshTagsAll(), VAULT_DEBOUNCE_DELAY, { leading: true, diff --git a/src/system-prompts/systemPromptRegister.ts b/src/system-prompts/systemPromptRegister.ts index c427cdc9..a9f36209 100644 --- a/src/system-prompts/systemPromptRegister.ts +++ b/src/system-prompts/systemPromptRegister.ts @@ -17,7 +17,7 @@ import { } from "@/system-prompts/state"; import { getSettings, subscribeToSettingsChange, updateSetting } from "@/settings/model"; import { SystemPromptManager } from "@/system-prompts/systemPromptManager"; -import debounce from "lodash.debounce"; +import { debounce } from "@/utils/debounce"; import { logError, logInfo } from "@/logger"; /** diff --git a/src/tests/projectContextCache.test.ts b/src/tests/projectContextCache.test.ts index 27e0060f..262b9151 100644 --- a/src/tests/projectContextCache.test.ts +++ b/src/tests/projectContextCache.test.ts @@ -42,11 +42,9 @@ jest.mock("@/search/searchUtils", () => ({ }), })); -// Mock crypto-js -jest.mock("crypto-js", () => ({ - MD5: jest.fn().mockImplementation((str) => ({ - toString: () => `mocked-hash-${str}`, - })), +// Mock md5 so the cache key in tests is predictable +jest.mock("@/utils/hash", () => ({ + md5: jest.fn().mockImplementation((str: string) => `mocked-hash-${str}`), })); // Mock plusUtils diff --git a/src/utils.ts b/src/utils.ts index 2cc2942d..f7bae519 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1112,16 +1112,7 @@ export async function insertIntoEditor(message: string, replace: boolean = false finalizeInsertion(); } -export function debounce void>( - func: T, - wait: number -): (...args: Parameters) => void { - let timeout: number; - return (...args: Parameters) => { - window.clearTimeout(timeout); - timeout = window.setTimeout(() => func(...args), wait); - }; -} +export { debounce } from "@/utils/debounce"; /** * Compare two semantic version strings. diff --git a/src/utils/debounce.test.ts b/src/utils/debounce.test.ts new file mode 100644 index 00000000..9b905271 --- /dev/null +++ b/src/utils/debounce.test.ts @@ -0,0 +1,86 @@ +import { debounce } from "./debounce"; + +jest.useFakeTimers(); + +describe("debounce", () => { + it("invokes once after the wait period with the latest args (trailing default)", () => { + const fn = jest.fn(); + const d = debounce(fn, 100); + + d("a"); + d("b"); + d("c"); + + expect(fn).not.toHaveBeenCalled(); + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith("c"); + }); + + it("supports leading-only mode", () => { + const fn = jest.fn(); + const d = debounce(fn, 100, { leading: true, trailing: false }); + + d("a"); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith("a"); + + d("b"); + d("c"); + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("fires both edges when leading and trailing are true and there are multiple calls", () => { + const fn = jest.fn(); + const d = debounce(fn, 100, { leading: true, trailing: true }); + + d("a"); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenLastCalledWith("a"); + + d("b"); + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(2); + expect(fn).toHaveBeenLastCalledWith("b"); + }); + + it("does not fire trailing if leading already covered the single call", () => { + const fn = jest.fn(); + const d = debounce(fn, 100, { leading: true, trailing: true }); + + d("only"); + expect(fn).toHaveBeenCalledTimes(1); + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("cancel prevents pending trailing invocation", () => { + const fn = jest.fn(); + const d = debounce(fn, 100); + + d("a"); + d.cancel(); + jest.advanceTimersByTime(100); + expect(fn).not.toHaveBeenCalled(); + }); + + it("flush invokes the pending call synchronously", () => { + const fn = jest.fn(); + const d = debounce(fn, 100); + + d("a"); + d.flush(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith("a"); + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it("flush is a no-op when nothing is pending", () => { + const fn = jest.fn(); + const d = debounce(fn, 100); + expect(d.flush()).toBeUndefined(); + expect(fn).not.toHaveBeenCalled(); + }); +}); diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 00000000..a1ec295a --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,89 @@ +/** + * Returned by {@link debounce}. Calling it schedules the underlying function, + * with `cancel` and `flush` controls modeled after `lodash.debounce`. + */ +export interface DebouncedFunction any> { + (...args: Parameters): ReturnType | undefined; + cancel(): void; + flush(): ReturnType | undefined; +} + +export interface DebounceOptions { + leading?: boolean; + trailing?: boolean; +} + +/** + * Delay-invoking `func` until `wait` ms have elapsed since the last call. + * Supports the `leading` / `trailing` options and `cancel` / `flush` methods + * used by the `lodash.debounce` API the codebase relied on previously. + */ +export function debounce any>( + func: T, + wait: number, + options: DebounceOptions = {} +): DebouncedFunction { + const leading = options.leading ?? false; + const trailing = options.trailing ?? true; + + let timeoutId: number | undefined; + let lastArgs: Parameters | undefined; + let lastResult: ReturnType | undefined; + let hasPendingTrailingCall = false; + + const invoke = (): ReturnType | undefined => { + const args = lastArgs; + lastArgs = undefined; + hasPendingTrailingCall = false; + if (!args) return lastResult; + lastResult = func(...args) as ReturnType; + return lastResult; + }; + + const startTimer = () => { + timeoutId = window.setTimeout(() => { + timeoutId = undefined; + if (trailing && hasPendingTrailingCall) { + invoke(); + } + }, wait); + }; + + const debounced = ((...args: Parameters) => { + lastArgs = args; + hasPendingTrailingCall = true; + + const isFirstCallInWindow = timeoutId === undefined; + if (timeoutId !== undefined) { + window.clearTimeout(timeoutId); + } + + if (isFirstCallInWindow && leading) { + invoke(); + } + + startTimer(); + return lastResult; + }) as DebouncedFunction; + + debounced.cancel = () => { + if (timeoutId !== undefined) { + window.clearTimeout(timeoutId); + timeoutId = undefined; + } + lastArgs = undefined; + hasPendingTrailingCall = false; + }; + + debounced.flush = () => { + if (timeoutId === undefined) return lastResult; + window.clearTimeout(timeoutId); + timeoutId = undefined; + if (hasPendingTrailingCall) { + return invoke(); + } + return lastResult; + }; + + return debounced; +} diff --git a/src/utils/hash.test.ts b/src/utils/hash.test.ts new file mode 100644 index 00000000..92e1df76 --- /dev/null +++ b/src/utils/hash.test.ts @@ -0,0 +1,80 @@ +import { md5, sha256 } from "./hash"; + +// Test vectors below are standard RFC 1321 / FIPS 180-4 reference values and +// match what `crypto-js`'s `MD5(str).toString()` / `SHA256(str).toString()` +// produced for the same UTF-8-encoded inputs. Preserving these byte-for-byte +// keeps existing on-disk cache keys (PDF cache, file cache, project cache, +// search index doc hashes) valid across the crypto-js removal. + +describe("md5", () => { + it.each([ + ["", "d41d8cd98f00b204e9800998ecf8427e"], + ["a", "0cc175b9c0f1b6a831c399e269772661"], + ["abc", "900150983cd24fb0d6963f7d28e17f72"], + ["message digest", "f96b697d7cb7938d525a2f31aaf161d0"], + ["abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"], + [ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "d174ab98d277d9f5a5611c2c9f419d9f", + ], + [ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + "57edf4a22be3c955ac49da2e2107b67a", + ], + ["The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"], + ])("md5(%j) = %s", (input, expected) => { + expect(md5(input)).toBe(expected); + }); + + it("handles UTF-8 multibyte input", () => { + // crypto-js encodes JS strings as UTF-8 before hashing; "café" → 63 61 66 c3 a9. + expect(md5("café")).toBe("07117fe4a1ebd544965dc19573183da2"); + }); + + it("handles inputs across block boundaries (55, 56, 64, 119, 120 bytes)", () => { + expect(md5("a".repeat(55))).toBe("ef1772b6dff9a122358552954ad0df65"); + expect(md5("a".repeat(56))).toBe("3b0c8ac703f828b04c6c197006d17218"); + expect(md5("a".repeat(64))).toBe("014842d480b571495a4a0363793f7367"); + expect(md5("a".repeat(119))).toBe("8a7bd0732ed6a28ce75f6dabc90e1613"); + expect(md5("a".repeat(120))).toBe("5f61c0ccad4cac44c75ff505e1f1e537"); + }); +}); + +describe("sha256", () => { + it.each([ + ["", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"], + ["abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"], + [ + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", + ], + [ + "The quick brown fox jumps over the lazy dog", + "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + ], + ])("sha256(%j) = %s", (input, expected) => { + expect(sha256(input)).toBe(expected); + }); + + it("handles UTF-8 multibyte input", () => { + expect(sha256("café")).toBe("850f7dc43910ff890f8879c0ed26fe697c93a067ad93a7d50f466a7028a9bf4e"); + }); + + it("handles inputs across block boundaries (55, 56, 64, 119, 120 bytes)", () => { + expect(sha256("a".repeat(55))).toBe( + "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318" + ); + expect(sha256("a".repeat(56))).toBe( + "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a" + ); + expect(sha256("a".repeat(64))).toBe( + "ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb" + ); + expect(sha256("a".repeat(119))).toBe( + "31eba51c313a5c08226adf18d4a359cfdfd8d2e816b13f4af952f7ea6584dcfb" + ); + expect(sha256("a".repeat(120))).toBe( + "2f3d335432c70b580af0e8e1b3674a7c020d683aa5f73aaaedfdc55af904c21c" + ); + }); +}); diff --git a/src/utils/hash.ts b/src/utils/hash.ts new file mode 100644 index 00000000..b14dc6d1 --- /dev/null +++ b/src/utils/hash.ts @@ -0,0 +1,214 @@ +/** + * Pure-JS MD5 and SHA-256 producing lowercase-hex digests that match what + * `crypto-js`'s `MD5(str).toString()` / `SHA256(str).toString()` produced. + * + * Used for cache keys and content fingerprints — not for security. Pure JS so + * it stays available across Obsidian's desktop (Electron) and mobile (WebView) + * runtimes, where `node:crypto` is unavailable and `crypto.subtle.digest` is + * async-only. + * + * Inputs are interpreted as UTF-8 (matching crypto-js's default `Utf8.parse`). + */ + +const HEX = "0123456789abcdef"; + +function bytesToHex(bytes: Uint8Array): string { + let out = ""; + for (let i = 0; i < bytes.length; i++) { + out += HEX[bytes[i] >>> 4] + HEX[bytes[i] & 0xf]; + } + return out; +} + +// ---------- MD5 (RFC 1321) ---------- + +const MD5_T = new Int32Array([ + -0x28955b88, -0x173848aa, 0x242070db, -0x3e423112, -0x0a83f051, 0x4787c62a, -0x57cfb9ed, + -0x02b96aff, 0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842, 0x6b901122, -0x02678e6d, + -0x5986bc72, 0x49b40821, -0x09e1da9e, -0x3fbf4cc0, 0x265e5a51, -0x16493856, -0x29d0efa3, + 0x02441453, -0x275e197f, -0x182c0438, 0x21e1cde6, -0x3cc8f82a, -0x0b2af279, 0x455a14ed, + -0x561c16fb, -0x03105c08, 0x676f02d9, -0x72d5b376, -0x0005c6be, -0x788e097f, 0x6d9d6122, + -0x021ac7f4, -0x5b4115bc, 0x4bdecfa9, -0x0944b4a0, -0x41404390, 0x289b7ec6, -0x155ed806, + -0x2b10cf7b, 0x04881d05, -0x262b2fc7, -0x1924661b, 0x1fa27cf8, -0x3b53a99b, -0x0bd6ddbc, + 0x432aff97, -0x546bdc59, -0x036c5fc7, 0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f, + 0x6fa87e4f, -0x01d31920, -0x5cfebcec, 0x4e0811a1, -0x08ac817e, -0x42c50dcb, 0x2ad7d2bb, + -0x14792c6f, +]); + +const MD5_S = [ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, + 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, + 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, +]; + +function rotl32(x: number, n: number): number { + return (x << n) | (x >>> (32 - n)); +} + +export function md5(input: string): string { + const msg = new TextEncoder().encode(input); + const len = msg.length; + // Pad to a multiple of 64 with at least 9 trailing bytes (0x80 + 8-byte length). + const paddedLen = (((len + 8) >>> 6) + 1) << 6; + const padded = new Uint8Array(paddedLen); + padded.set(msg); + padded[len] = 0x80; + const dv = new DataView(padded.buffer); + // Length in bits, little-endian, 64-bit. JS numbers handle len * 8 up to ~2^50. + const bitLenLow = (len * 8) >>> 0; + const bitLenHigh = Math.floor(len / 0x20000000); + dv.setUint32(paddedLen - 8, bitLenLow, true); + dv.setUint32(paddedLen - 4, bitLenHigh, true); + + let a0 = 0x67452301 | 0; + let b0 = 0xefcdab89 | 0; + let c0 = 0x98badcfe | 0; + let d0 = 0x10325476 | 0; + + const M = new Int32Array(16); + for (let off = 0; off < paddedLen; off += 64) { + for (let j = 0; j < 16; j++) M[j] = dv.getInt32(off + j * 4, true); + + let a = a0; + let b = b0; + let c = c0; + let d = d0; + + for (let i = 0; i < 64; i++) { + let f: number; + let g: number; + if (i < 16) { + f = (b & c) | (~b & d); + g = i; + } else if (i < 32) { + f = (d & b) | (~d & c); + g = (5 * i + 1) & 0xf; + } else if (i < 48) { + f = b ^ c ^ d; + g = (3 * i + 5) & 0xf; + } else { + f = c ^ (b | ~d); + g = (7 * i) & 0xf; + } + const tmp = d; + d = c; + c = b; + b = (b + rotl32((a + f + MD5_T[i] + M[g]) | 0, MD5_S[i])) | 0; + a = tmp; + } + + a0 = (a0 + a) | 0; + b0 = (b0 + b) | 0; + c0 = (c0 + c) | 0; + d0 = (d0 + d) | 0; + } + + // MD5 digest: state words in little-endian byte order. + const out = new Uint8Array(16); + const outDv = new DataView(out.buffer); + outDv.setInt32(0, a0, true); + outDv.setInt32(4, b0, true); + outDv.setInt32(8, c0, true); + outDv.setInt32(12, d0, true); + return bytesToHex(out); +} + +// ---------- SHA-256 (FIPS 180-4) ---------- + +const SHA256_K = new Uint32Array([ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +]); + +function rotr32(x: number, n: number): number { + return (x >>> n) | (x << (32 - n)); +} + +export function sha256(input: string): string { + const msg = new TextEncoder().encode(input); + const len = msg.length; + const paddedLen = (((len + 8) >>> 6) + 1) << 6; + const padded = new Uint8Array(paddedLen); + padded.set(msg); + padded[len] = 0x80; + const dv = new DataView(padded.buffer); + // Length in bits, big-endian, 64-bit. JS numbers handle len * 8 up to ~2^50. + const bitLenLow = (len * 8) >>> 0; + const bitLenHigh = Math.floor(len / 0x20000000); + dv.setUint32(paddedLen - 8, bitLenHigh, false); + dv.setUint32(paddedLen - 4, bitLenLow, false); + + let h0 = 0x6a09e667 | 0; + let h1 = 0xbb67ae85 | 0; + let h2 = 0x3c6ef372 | 0; + let h3 = 0xa54ff53a | 0; + let h4 = 0x510e527f | 0; + let h5 = 0x9b05688c | 0; + let h6 = 0x1f83d9ab | 0; + let h7 = 0x5be0cd19 | 0; + + const W = new Int32Array(64); + for (let off = 0; off < paddedLen; off += 64) { + for (let i = 0; i < 16; i++) W[i] = dv.getInt32(off + i * 4, false); + for (let i = 16; i < 64; i++) { + const w15 = W[i - 15]; + const w2 = W[i - 2]; + const s0 = rotr32(w15, 7) ^ rotr32(w15, 18) ^ (w15 >>> 3); + const s1 = rotr32(w2, 17) ^ rotr32(w2, 19) ^ (w2 >>> 10); + W[i] = (W[i - 16] + s0 + W[i - 7] + s1) | 0; + } + + let a = h0; + let b = h1; + let c = h2; + let d = h3; + let e = h4; + let f = h5; + let g = h6; + let h = h7; + + for (let i = 0; i < 64; i++) { + const S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25); + const ch = (e & f) ^ (~e & g); + const t1 = (h + S1 + ch + SHA256_K[i] + W[i]) | 0; + const S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22); + const maj = (a & b) ^ (a & c) ^ (b & c); + const t2 = (S0 + maj) | 0; + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + h0 = (h0 + a) | 0; + h1 = (h1 + b) | 0; + h2 = (h2 + c) | 0; + h3 = (h3 + d) | 0; + h4 = (h4 + e) | 0; + h5 = (h5 + f) | 0; + h6 = (h6 + g) | 0; + h7 = (h7 + h) | 0; + } + + const out = new Uint8Array(32); + const outDv = new DataView(out.buffer); + outDv.setInt32(0, h0, false); + outDv.setInt32(4, h1, false); + outDv.setInt32(8, h2, false); + outDv.setInt32(12, h3, false); + outDv.setInt32(16, h4, false); + outDv.setInt32(20, h5, false); + outDv.setInt32(24, h6, false); + outDv.setInt32(28, h7, false); + return bytesToHex(out); +}