diff --git a/CHANGELOG.md b/CHANGELOG.md index 849124ca..af838b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `mcpc completion ` prints a shell completion script; `mcpc completion install [shell]` auto-detects the user's shell and installs the script in the right place. Completion covers top-level commands, session subcommands, `@session` names (from `~/.mcpc/sessions.json`), saved auth servers, log levels, known flags (introspected from Commander at runtime so there is no static drift), and tool names / resource URIs / prompt names for connected sessions. Tool/resource/prompt names are cached on disk in `~/.mcpc/completion/.json` whenever the user runs `tools-list` / `resources-list` / `prompts-list`, so TAB is fast and never triggers network calls or OAuth flows. - `mcpc connect` (with no arguments) now auto-discovers standard MCP config files (`.mcp.json`, `mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, `~/.claude.json`, Claude Desktop, Windsurf, Kiro, etc.) in the current directory and home directory, and connects every server defined across them. Entries with duplicate session names are deduplicated (project-scoped files win over global ones). VS Code's `"servers"` key is also supported. - `mcpc connect` auto-connects to `mcp.apify.com` as `@apify` when the `APIFY_API_TOKEN` environment variable is set, using it as a Bearer token. Existing live sessions are reused without restart. diff --git a/README.md b/README.md index 843eb400..d0f70428 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ npm install -g @apify/mcpc bun install -g @apify/mcpc ``` +Optional: enable [shell tab-completion](#shell-completion) for bash/zsh/fish: + +```bash +mcpc completion install +``` + **Linux:** credentials use the OS keychain via the [Secret Service API](https://specifications.freedesktop.org/secret-service/). GNOME/KDE desktops work out of the box. On headless/CI systems, `mcpc` falls back to a file-based store (`~/.mcpc/credentials`, mode `0600`). @@ -141,6 +147,7 @@ Commands: clean [resources...] Clean up mcpc data (sessions, profiles, logs, all) grep Search tools and instructions across all active sessions x402 [subcommand] [args...] Configure an x402 payment wallet (EXPERIMENTAL) + completion [args...] Print or install a shell completion script (bash, zsh, fish) help [command] [subcommand] Show help for a specific command Options: @@ -260,6 +267,40 @@ mcpc @apify shell Shell commands: `help`, `exit`/`quit`/Ctrl+D, Ctrl+C to cancel. Arrow keys navigate history (saved to `~/.mcpc/history`). +### Shell completion + +`mcpc` ships with tab-completion scripts for **bash**, **zsh**, and **fish**. +Completion covers top-level commands, session subcommands, `@session` names +(from `~/.mcpc/sessions.json`), saved auth servers (`mcpc login`/`logout`), +known flags, plus **tool names, resource URIs, and prompt names** for +connected sessions. Tool/resource/prompt names are cached in +`~/.mcpc/completion/.json` whenever you run `tools-list`, +`resources-list`, or `prompts-list` — run those once to warm the cache. + +```bash +# Auto-detect your shell and install +mcpc completion install + +# Or install for a specific shell +mcpc completion install bash +mcpc completion install zsh +mcpc completion install fish +``` + +Alternatively, print the script and pipe it wherever you want: + +```bash +# Always-fresh: re-evaluates on every shell start +echo 'eval "$(mcpc completion bash)"' >> ~/.bashrc + +# Or write the file yourself +mcpc completion bash > ~/.local/share/bash-completion/completions/mcpc +``` + +Completion is **purely local** — it reads `~/.mcpc/sessions.json` and +`~/.mcpc/profiles.json` and never triggers network calls or OAuth flows on +TAB, so it stays fast even when sessions are offline. + ### Grep (search across sessions) `mcpc grep` searches tools, resources, and prompts across all active sessions or within a single session: diff --git a/bin/mcpc b/bin/mcpc index b2808789..8bad98cb 100755 --- a/bin/mcpc +++ b/bin/mcpc @@ -3,7 +3,9 @@ // Main CLI executable for mcpc // This wrapper imports the compiled TypeScript CLI entry point -import('../dist/cli/index.js').catch((err) => { - console.error('Failed to load mcpc CLI:', err.message); - process.exit(1); -}); +import('../dist/cli/index.js') + .then((mod) => mod.run()) + .catch((err) => { + console.error('Failed to load mcpc CLI:', err.message); + process.exit(1); + }); diff --git a/docs/TODOs.md b/docs/TODOs.md index 08544c43..ec17503f 100644 --- a/docs/TODOs.md +++ b/docs/TODOs.md @@ -80,7 +80,6 @@ Syntax errors: mcpc call linear_list_issues instead of mcpc @linear tools-call l just re-type it dynamically to make it work. - nit: Cooler OAuth flow finish web page with CSS animation, add Apify example there, show mcpc info. E.g. next step - check Apify rather than close - security: For auth profiles, fetch the detailed user info via http, save to profiles.json and show in 'mcpc', ensure the info is up-to-date -- nit: Implement typing tab-completions (e.g. "mcpc @ap...") - not sure if that's even possible - Consider adding `--dry-run` https://justin.poehnelt.com/posts/rewrite-your-cli-for-ai-agents/ For tool call it could return synthetic resutls conforming the schema. - Show protocolVersion also for stdio in "mcpc --json" - but for that we need to update the SDK to save it! See setProtocolVersion diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index f3425280..00000000 --- a/jest.config.js +++ /dev/null @@ -1,33 +0,0 @@ -export default { - preset: 'ts-jest/presets/default-esm', - testEnvironment: 'node', - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - transform: { - '^.+\\.ts$': [ - 'ts-jest', - { - useESM: true, - isolatedModules: true, - tsconfig: 'tsconfig.test.json', - }, - ], - }, - transformIgnorePatterns: [ - 'node_modules/(?!(chalk|cli-table3|#ansi-styles|#supports-color)/)', - ], - testMatch: [ - '**/test/unit/**/*.test.ts', - '**/test/unit/**/*.spec.ts', - ], - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!src/**/index.ts', - ], - coverageDirectory: 'test/coverage/unit', - coverageReporters: ['text', 'lcov', 'html', 'json'], - verbose: true, -}; diff --git a/package.json b/package.json index a873b163..339f7a40 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,10 @@ "build:watch": "tsc --watch", "build:readme": "./scripts/update-readme.sh", "test": "pnpm run build && pnpm run test:unit && ./test/e2e/run.sh --no-build --parallel 8 && ./test/e2e/run.sh --no-build --parallel 8 --runtime bun", - "test:unit": "jest", - "test:watch": "jest --watch", + "test:unit": "vitest run", + "test:watch": "vitest", "test:coverage": "pnpm run test:coverage:unit && pnpm run test:coverage:e2e && pnpm run test:coverage:merge", - "test:coverage:unit": "jest --coverage && find test/coverage/unit -name '*.html' -exec sed -i '' -e 's/Code coverage report for All files/mcpc Coverage (Unit Tests)/g' -e 's/

All files<\\/h1>/

Unit Test Coverage<\\/h1>/g' {} \\;", + "test:coverage:unit": "vitest run --coverage", "test:coverage:e2e": "./test/e2e/run.sh --coverage", "test:coverage:merge": "test/coverage/coverage-merge.sh", "test:e2e": "./test/e2e/run.sh --keep", @@ -69,24 +69,23 @@ "viem": "^2.46.3" }, "devDependencies": { - "@types/jest": "^30.0.0", "@types/node": "^25.0.3", "@types/proper-lockfile": "^4.1.4", "@types/qrcode-terminal": "^0.12.2", "@types/uuid": "^11.0.0", "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", + "@vitest/coverage-v8": "4.1.5", "c8": "^11.0.0", "doctoc": "^2.2.1", "eslint": "^8.57.1", - "jest": "^30.2.0", "markdown-link-check": "^3.14.2", "nyc": "^18.0.0", "prettier": "^3.7.4", "proxy-chain": "^2.7.1", - "ts-jest": "^29.4.6", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "4.1.5" }, "packageManager": "pnpm@10.33.4", "devEngines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9367eca6..e1aff8e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,9 +45,6 @@ importers: specifier: ^2.46.3 version: 2.48.11(typescript@5.9.3)(zod@4.4.3) devDependencies: - '@types/jest': - specifier: ^30.0.0 - version: 30.0.0 '@types/node': specifier: ^25.0.3 version: 25.6.2 @@ -66,6 +63,9 @@ importers: '@typescript-eslint/parser': specifier: 8.57.2 version: 8.57.2(eslint@8.57.1)(typescript@5.9.3) + '@vitest/coverage-v8': + specifier: 4.1.5 + version: 4.1.5(vitest@4.1.5) c8: specifier: ^11.0.0 version: 11.0.0 @@ -75,9 +75,6 @@ importers: eslint: specifier: ^8.57.1 version: 8.57.1 - jest: - specifier: ^30.2.0 - version: 30.4.2(@types/node@25.6.2) markdown-link-check: specifier: ^3.14.2 version: 3.14.2 @@ -90,15 +87,15 @@ importers: proxy-chain: specifier: ^2.7.1 version: 2.7.1 - ts-jest: - specifier: ^29.4.6 - version: 29.4.9(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0))(jest-util@30.4.1)(jest@30.4.2(@types/node@25.6.2))(typescript@5.9.3) tsx: specifier: ^4.21.0 version: 4.21.0 typescript: specifier: ^5.9.3 version: 5.9.3 + vitest: + specifier: 4.1.5 + version: 4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0)) packages: @@ -139,10 +136,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -164,97 +157,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -267,9 +169,6 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -520,10 +419,6 @@ packages: '@types/node': optional: true - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -532,88 +427,6 @@ packages: resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} - '@jest/console@30.4.1': - resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/core@30.4.2': - resolution: {integrity: sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/diff-sequences@30.4.0': - resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/environment@30.4.1': - resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect-utils@30.4.1': - resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect@30.4.1': - resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/fake-timers@30.4.1': - resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/get-type@30.1.0': - resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/globals@30.4.1': - resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/pattern@30.4.0': - resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/reporters@30.4.1': - resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@30.4.1': - resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/snapshot-utils@30.4.1': - resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/source-map@30.0.1': - resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/test-result@30.4.1': - resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/test-sequencer@30.4.1': - resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/transform@30.4.1': - resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/types@30.4.1': - resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -721,8 +534,11 @@ packages: resolution: {integrity: sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} @@ -764,13 +580,106 @@ packages: resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} engines: {node: '>=20.0'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} + '@oxc-project/types@0.129.0': + resolution: {integrity: sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==} + + '@rolldown/binding-android-arm64@1.0.0': + resolution: {integrity: sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0': + resolution: {integrity: sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0': + resolution: {integrity: sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0': + resolution: {integrity: sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + resolution: {integrity: sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0': + resolution: {integrity: sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0': + resolution: {integrity: sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0': + resolution: {integrity: sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0': + resolution: {integrity: sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0': + resolution: {integrity: sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0': + resolution: {integrity: sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0': + resolution: {integrity: sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0': + resolution: {integrity: sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0': + resolution: {integrity: sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0': + resolution: {integrity: sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@rolldown/pluginutils@1.0.0': + resolution: {integrity: sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==} '@scure/base@1.2.6': resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} @@ -781,14 +690,8 @@ packages: '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - '@sinclair/typebox@0.34.49': - resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} - - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@15.4.0': - resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@textlint/ast-node-types@15.6.0': resolution: {integrity: sha512-CxZHFbYAU7J0A4izz31wV2ZZfySR6aVj2OSR6/3tppZm7VV6hM7nA7sutsLwIiBL/v4lsB1RM79l4Dc/VrH4qw==} @@ -802,30 +705,18 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@30.0.0': - resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} - '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} @@ -841,9 +732,6 @@ packages: '@types/retry@0.12.5': resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -851,12 +739,6 @@ packages: resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==} deprecated: This is a stub types definition. uuid provides its own type definitions, so you do not need this installed. - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.57.2': resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -929,108 +811,43 @@ packages: '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} - cpu: [arm] - os: [android] - - '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} - cpu: [arm64] - os: [android] - - '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} - cpu: [arm64] - os: [darwin] - - '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} - cpu: [x64] - os: [darwin] - - '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} - cpu: [x64] - os: [freebsd] - - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} - cpu: [arm] - os: [linux] - - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} - cpu: [arm] - os: [linux] - - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} - cpu: [riscv64] - os: [linux] - libc: [musl] + '@vitest/coverage-v8@4.1.5': + resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==} + peerDependencies: + '@vitest/browser': 4.1.5 + vitest: 4.1.5 + peerDependenciesMeta: + '@vitest/browser': + optional: true - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} - cpu: [s390x] - os: [linux] - libc: [glibc] + '@vitest/expect@4.1.5': + resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} - cpu: [x64] - os: [linux] - libc: [glibc] + '@vitest/mocker@4.1.5': + resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} - cpu: [x64] - os: [linux] - libc: [musl] + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] + '@vitest/runner@4.1.5': + resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} - cpu: [arm64] - os: [win32] + '@vitest/snapshot@4.1.5': + resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} - cpu: [ia32] - os: [win32] + '@vitest/spy@4.1.5': + resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} - cpu: [x64] - os: [win32] + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} abitype@1.2.3: resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} @@ -1082,10 +899,6 @@ packages: anchor-markdown-header@0.8.4: resolution: {integrity: sha512-20eMBMpts7k5rXAAj67geSqc/tsexHZOZJDWQD214YcDuNtyizDa7Q77sYa5rkao2FwsQP1WKRt2X6mphwmhbg==} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1098,18 +911,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - append-transform@2.0.0: resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} engines: {node: '>=8'} @@ -1123,38 +924,20 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - babel-jest@30.4.1: - resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-0 - - babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} - engines: {node: '>=12'} - - babel-plugin-jest-hoist@30.4.0: - resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} - peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 - - babel-preset-jest@30.4.0: - resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-beta.1 - bail@1.0.5: resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} @@ -1187,9 +970,6 @@ packages: brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} - brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} @@ -1199,16 +979,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -1243,16 +1013,16 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} ccount@1.1.0: resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1261,10 +1031,6 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - character-entities-legacy@1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} @@ -1281,13 +1047,6 @@ packages: resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} engines: {node: '>=20.18.1'} - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - - cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} - clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -1311,13 +1070,6 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1389,21 +1141,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - dedent@1.7.2: - resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - default-require-extensions@3.0.1: resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} engines: {node: '>=8'} @@ -1416,8 +1156,8 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} doctoc@2.4.1: @@ -1455,28 +1195,18 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} electron-to-chromium@1.5.353: resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -1503,9 +1233,6 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1514,6 +1241,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1533,10 +1263,6 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1585,6 +1311,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1604,17 +1333,9 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit-x@0.2.2: - resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} - engines: {node: '>= 0.8.0'} - - expect@30.4.1: - resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} express-rate-limit@8.5.1: resolution: {integrity: sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==} @@ -1656,9 +1377,6 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1753,10 +1471,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} @@ -1768,11 +1482,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -1795,11 +1504,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - handlebars@4.7.9: - resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} - engines: {node: '>=0.4.7'} - hasBin: true - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1844,10 +1548,6 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1868,11 +1568,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1906,9 +1601,6 @@ packages: is-alphanumerical@1.0.4: resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-buffer@2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -1924,10 +1616,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -2001,148 +1689,16 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - istanbul-reports@3.2.0: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jest-changed-files@30.4.1: - resolution: {integrity: sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-circus@30.4.2: - resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-cli@30.4.2: - resolution: {integrity: sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@30.4.2: - resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@types/node': '*' - esbuild-register: '>=3.4.0' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - esbuild-register: - optional: true - ts-node: - optional: true - - jest-diff@30.4.1: - resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-docblock@30.4.0: - resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-each@30.4.1: - resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-environment-node@30.4.1: - resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-haste-map@30.4.1: - resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-leak-detector@30.4.1: - resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-matcher-utils@30.4.1: - resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-message-util@30.4.1: - resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-mock@30.4.1: - resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@30.4.0: - resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-resolve-dependencies@30.4.2: - resolution: {integrity: sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-resolve@30.4.1: - resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-runner@30.4.2: - resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-runtime@30.4.2: - resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-snapshot@30.4.1: - resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-util@30.4.1: - resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-validate@30.4.1: - resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-watcher@30.4.1: - resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-worker@30.4.1: - resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest@30.4.2: - resolution: {integrity: sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2162,9 +1718,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2185,16 +1738,83 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} link-check@5.5.1: resolution: {integrity: sha512-GrtE4Zp/FBduvElmad375NrPeMYnKwNt9rH/TDG/rbQbHL0QVC4S/cEPVKZ0CkhXlVuiK+/5flGpRxQzoLbjEA==} @@ -2210,9 +1830,6 @@ packages: lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2227,9 +1844,6 @@ packages: longest-streak@2.0.4: resolution: {integrity: sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.3.6: resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} @@ -2241,6 +1855,12 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.2: + resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -2249,12 +1869,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - markdown-link-check@3.14.2: resolution: {integrity: sha512-DPJ+itd3D5fcfXD5s1i53lugH0Z/h80kkQxlYCBh8tFwEZGhyVgDcLl0rnKlWssAVDAmSmcbePpHpMEY+JcMMQ==} hasBin: true @@ -2315,9 +1929,6 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - micromark-extension-footnote@0.3.2: resolution: {integrity: sha512-gr/BeIxbIWQoUm02cIfK7mdMZ/fbroRpLsck4kvFtjbzP4yi+OPVbnukTc/zy0i7spC2xYE/dbX1Sur8BEDJsQ==} @@ -2353,10 +1964,6 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -2368,10 +1975,6 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2386,9 +1989,9 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - napi-postinstall@0.3.4: - resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true natural-compare@1.4.0: @@ -2403,9 +2006,6 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - neotraverse@0.6.18: resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} engines: {node: '>= 10'} @@ -2418,9 +2018,6 @@ packages: resolution: {integrity: sha512-69JMeWgEUrCji+dOLULirdSoosRxgAq2y+imfmHHBGvgTwyTKqvm65Ls3+W30DCIWMrYj5kKVb/DHTQDK7OVwQ==} engines: {node: '>=18.0.0'} - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-preload@0.2.1: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} @@ -2428,14 +2025,6 @@ packages: node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2452,6 +2041,9 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -2459,10 +2051,6 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -2529,10 +2117,6 @@ packages: parse-entities@2.0.0: resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -2558,10 +2142,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -2569,21 +2149,16 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -2592,6 +2167,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2601,10 +2180,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@30.4.1: - resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - process-on-spawn@1.1.0: resolution: {integrity: sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==} engines: {node: '>=8'} @@ -2635,9 +2210,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@7.0.1: - resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - qrcode-terminal@0.12.0: resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==} hasBin: true @@ -2657,12 +2229,6 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-is@19.2.6: - resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==} - release-zalgo@1.0.0: resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} engines: {node: '>=4'} @@ -2697,10 +2263,6 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2734,6 +2296,11 @@ packages: engines: {node: 20 || >=22} hasBin: true + rolldown@1.0.0: + resolution: {integrity: sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -2795,6 +2362,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -2802,10 +2372,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -2818,8 +2384,9 @@ packages: resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} @@ -2832,30 +2399,24 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + stdin-discarder@0.3.2: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string-width@8.2.1: resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} @@ -2872,10 +2433,6 @@ packages: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2887,18 +2444,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} - engines: {node: ^14.18.0 || >=16.0.0} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - test-exclude@8.0.0: resolution: {integrity: sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==} engines: {node: 20 || >=22} @@ -2906,12 +2451,20 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} + tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} @@ -2926,33 +2479,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-jest@29.4.9: - resolution: {integrity: sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 || ^30.0.0 - '@jest/types': ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - esbuild: '*' - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: '>=4.3 <7' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/transform': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2965,26 +2491,14 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -2997,11 +2511,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - undici-types@7.19.2: resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} @@ -3025,9 +2534,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - unrs-resolver@1.11.1: - resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -3072,8 +2578,89 @@ packages: typescript: optional: true - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + vite@8.0.12: + resolution: {integrity: sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.5: + resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.5 + '@vitest/browser-preview': 4.1.5 + '@vitest/browser-webdriverio': 4.1.5 + '@vitest/coverage-istanbul': 4.1.5 + '@vitest/coverage-v8': 4.1.5 + '@vitest/ui': 4.1.5 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} @@ -3092,13 +2679,15 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -3107,20 +2696,12 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -3248,8 +2829,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} @@ -3265,136 +2844,49 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/template@7.28.6': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/traverse@7.29.0': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/types@7.29.0': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@bcoe/v8-coverage@1.0.2': {} - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@emnapi/core@1.10.0': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@emnapi/runtime@1.10.0': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + tslib: 2.8.1 + optional: true - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@emnapi/wasi-threads@1.2.1': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + tslib: 2.8.1 + optional: true - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 - - '@babel/traverse@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.3 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@bcoe/v8-coverage@0.2.3': {} - - '@bcoe/v8-coverage@1.0.2': {} - - '@emnapi/core@1.10.0': - dependencies: - '@emnapi/wasi-threads': 1.2.1 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.27.7': - optional: true + '@esbuild/aix-ppc64@0.27.7': + optional: true '@esbuild/android-arm64@0.27.7': optional: true @@ -3546,15 +3038,6 @@ snapshots: optionalDependencies: '@types/node': 25.6.2 - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -3565,184 +3048,6 @@ snapshots: '@istanbuljs/schema@0.1.6': {} - '@jest/console@30.4.1': - dependencies: - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - chalk: 4.1.2 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - slash: 3.0.0 - - '@jest/core@30.4.2': - dependencies: - '@jest/console': 30.4.1 - '@jest/pattern': 30.4.0 - '@jest/reporters': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 4.4.0 - exit-x: 0.2.2 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-changed-files: 30.4.1 - jest-config: 30.4.2(@types/node@25.6.2) - jest-haste-map: 30.4.1 - jest-message-util: 30.4.1 - jest-regex-util: 30.4.0 - jest-resolve: 30.4.1 - jest-resolve-dependencies: 30.4.2 - jest-runner: 30.4.2 - jest-runtime: 30.4.2 - jest-snapshot: 30.4.1 - jest-util: 30.4.1 - jest-validate: 30.4.1 - jest-watcher: 30.4.1 - pretty-format: 30.4.1 - slash: 3.0.0 - transitivePeerDependencies: - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - - '@jest/diff-sequences@30.4.0': {} - - '@jest/environment@30.4.1': - dependencies: - '@jest/fake-timers': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - jest-mock: 30.4.1 - - '@jest/expect-utils@30.4.1': - dependencies: - '@jest/get-type': 30.1.0 - - '@jest/expect@30.4.1': - dependencies: - expect: 30.4.1 - jest-snapshot: 30.4.1 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@30.4.1': - dependencies: - '@jest/types': 30.4.1 - '@sinonjs/fake-timers': 15.4.0 - '@types/node': 25.6.2 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-util: 30.4.1 - - '@jest/get-type@30.1.0': {} - - '@jest/globals@30.4.1': - dependencies: - '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 - '@jest/types': 30.4.1 - jest-mock: 30.4.1 - transitivePeerDependencies: - - supports-color - - '@jest/pattern@30.4.0': - dependencies: - '@types/node': 25.6.2 - jest-regex-util: 30.4.0 - - '@jest/reporters@30.4.1': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.6.2 - chalk: 4.1.2 - collect-v8-coverage: 1.0.3 - exit-x: 0.2.2 - glob: 10.5.0 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.2.0 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - jest-worker: 30.4.1 - slash: 3.0.0 - string-length: 4.0.2 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@30.4.1': - dependencies: - '@sinclair/typebox': 0.34.49 - - '@jest/snapshot-utils@30.4.1': - dependencies: - '@jest/types': 30.4.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - natural-compare: 1.4.0 - - '@jest/source-map@30.0.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@30.4.1': - dependencies: - '@jest/console': 30.4.1 - '@jest/types': 30.4.1 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 - - '@jest/test-sequencer@30.4.1': - dependencies: - '@jest/test-result': 30.4.1 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - slash: 3.0.0 - - '@jest/transform@30.4.1': - dependencies: - '@babel/core': 7.29.0 - '@jest/types': 30.4.1 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-regex-util: 30.4.0 - jest-util: 30.4.1 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - - '@jest/types@30.4.1': - dependencies: - '@jest/pattern': 30.4.0 - '@jest/schemas': 30.4.1 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 25.6.2 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3835,7 +3140,7 @@ snapshots: '@napi-rs/keyring-win32-ia32-msvc': 1.3.0 '@napi-rs/keyring-win32-x64-msvc': 1.3.0 - '@napi-rs/wasm-runtime@0.2.12': + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 @@ -3879,10 +3184,58 @@ snapshots: '@oozcitak/util@10.0.0': {} - '@pkgjs/parseargs@0.11.0': + '@oxc-project/types@0.129.0': {} + + '@rolldown/binding-android-arm64@1.0.0': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0': optional: true - '@pkgr/core@0.2.9': {} + '@rolldown/binding-linux-ppc64-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0': + optional: true + + '@rolldown/pluginutils@1.0.0': {} '@scure/base@1.2.6': {} @@ -3897,15 +3250,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 - '@sinclair/typebox@0.34.49': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@15.4.0': - dependencies: - '@sinonjs/commons': 3.0.1 + '@standard-schema/spec@1.1.0': {} '@textlint/ast-node-types@15.6.0': {} @@ -3931,42 +3276,17 @@ snapshots: tslib: 2.8.1 optional: true - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': + '@types/chai@5.2.3': dependencies: - '@babel/types': 7.29.0 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.3 - '@babel/types': 7.29.0 + '@types/deep-eql@4.0.2': {} - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.0 + '@types/estree@1.0.9': {} '@types/istanbul-lib-coverage@2.0.6': {} - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@30.0.0': - dependencies: - expect: 30.4.1 - pretty-format: 30.4.1 - '@types/mdast@3.0.15': dependencies: '@types/unist': 2.0.11 @@ -3983,20 +3303,12 @@ snapshots: '@types/retry@0.12.5': {} - '@types/stack-utils@2.0.3': {} - '@types/unist@2.0.11': {} '@types/uuid@11.0.0': dependencies: uuid: 13.0.2 - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.35': - dependencies: - '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -4096,64 +3408,60 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - optional: true - - '@unrs/resolver-binding-android-arm64@1.11.1': - optional: true - - '@unrs/resolver-binding-darwin-arm64@1.11.1': - optional: true - - '@unrs/resolver-binding-darwin-x64@1.11.1': - optional: true - - '@unrs/resolver-binding-freebsd-x64@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - optional: true + '@vitest/coverage-v8@4.1.5(vitest@4.1.5)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.5 + ast-v8-to-istanbul: 1.0.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0)) + + '@vitest/expect@4.1.5': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.5(vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0))': + dependencies: + '@vitest/spy': 4.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0) - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - optional: true + '@vitest/pretty-format@4.1.5': + dependencies: + tinyrainbow: 3.1.0 - '@unrs/resolver-binding-wasm32-wasi@1.11.1': + '@vitest/runner@4.1.5': dependencies: - '@napi-rs/wasm-runtime': 0.2.12 - optional: true + '@vitest/utils': 4.1.5 + pathe: 2.0.3 - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - optional: true + '@vitest/snapshot@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pathe: 2.0.3 - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - optional: true + '@vitest/spy@4.1.5': {} - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - optional: true + '@vitest/utils@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 abitype@1.2.3(typescript@5.9.3)(zod@4.4.3): optionalDependencies: @@ -4201,10 +3509,6 @@ snapshots: emoji-regex: 10.6.0 remove-markdown: 0.6.4 - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -4213,15 +3517,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - - ansi-styles@6.2.3: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - append-transform@2.0.0: dependencies: default-require-extensions: 3.0.1 @@ -4234,63 +3529,19 @@ snapshots: argparse@2.0.1: {} + assertion-error@2.0.1: {} + ast-types@0.13.4: dependencies: tslib: 2.8.1 - async@3.2.6: {} - - babel-jest@30.4.1(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.4.1 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.4.0(@babel/core@7.29.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@7.0.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.6 - istanbul-lib-instrument: 6.0.3 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@30.4.0: + ast-v8-to-istanbul@1.0.0: dependencies: - '@types/babel__core': 7.20.5 + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-jest@30.4.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - babel-plugin-jest-hoist: 30.4.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + async@3.2.6: {} bail@1.0.5: {} @@ -4325,10 +3576,6 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.0: - dependencies: - balanced-match: 1.0.2 - brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -4341,16 +3588,6 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-from@1.1.2: {} - bytes@3.1.2: {} c8@11.0.0: @@ -4388,12 +3625,12 @@ snapshots: camelcase@5.3.1: {} - camelcase@6.3.0: {} - caniuse-lite@1.0.30001792: {} ccount@1.1.0: {} + chai@6.2.2: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -4401,8 +3638,6 @@ snapshots: chalk@5.6.2: {} - char-regex@1.0.2: {} - character-entities-legacy@1.1.4: {} character-entities@1.2.4: {} @@ -4432,10 +3667,6 @@ snapshots: undici: 7.25.0 whatwg-mimetype: 4.0.0 - ci-info@4.4.0: {} - - cjs-module-lexer@2.2.0: {} - clean-stack@2.2.0: {} cli-cursor@5.0.0: @@ -4458,10 +3689,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - co@4.6.0: {} - - collect-v8-coverage@1.0.3: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -4515,12 +3742,8 @@ snapshots: decamelize@1.2.0: {} - dedent@1.7.2: {} - deep-is@0.1.4: {} - deepmerge@4.3.1: {} - default-require-extensions@3.0.1: dependencies: strip-bom: 4.0.0 @@ -4533,7 +3756,7 @@ snapshots: depd@2.0.0: {} - detect-newline@3.1.0: {} + detect-libc@2.1.2: {} doctoc@2.4.1: dependencies: @@ -4589,20 +3812,14 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} - ee-first@1.1.1: {} electron-to-chromium@1.5.353: {} - emittery@0.13.1: {} - emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - encodeurl@2.0.0: {} encoding-sniffer@0.2.1: @@ -4620,14 +3837,12 @@ snapshots: entities@7.0.1: {} - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - es-define-property@1.0.1: {} es-errors@1.3.0: {} + es-module-lexer@2.1.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -4667,8 +3882,6 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} escodegen@2.1.0: @@ -4749,6 +3962,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + esutils@2.0.3: {} etag@1.8.1: {} @@ -4761,28 +3978,7 @@ snapshots: dependencies: eventsource-parser: 3.0.8 - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit-x@0.2.2: {} - - expect@30.4.1: - dependencies: - '@jest/expect-utils': 30.4.1 - '@jest/get-type': 30.1.0 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-util: 30.4.1 + expect-type@1.3.0: {} express-rate-limit@8.5.1(express@5.2.1): dependencies: @@ -4850,10 +4046,6 @@ snapshots: dependencies: format: 0.2.2 - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -4948,8 +4140,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@6.0.1: {} - get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -4966,15 +4156,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - glob@13.0.6: dependencies: minimatch: 10.2.5 @@ -5000,15 +4181,6 @@ snapshots: graphemer@1.4.0: {} - handlebars@4.7.9: - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -5066,8 +4238,6 @@ snapshots: transitivePeerDependencies: - supports-color - human-signals@2.1.0: {} - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -5085,11 +4255,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -5114,421 +4279,94 @@ snapshots: is-alphabetical: 1.0.4 is-decimal: 1.0.4 - is-arrayish@0.2.1: {} - is-buffer@2.0.5: {} - is-decimal@1.0.4: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-generator-fn@2.1.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hexadecimal@1.0.4: {} - - is-interactive@2.0.0: {} - - is-path-inside@3.0.3: {} - - is-plain-obj@2.1.0: {} - - is-promise@4.0.0: {} - - is-relative-url@4.1.0: - dependencies: - is-absolute-url: 4.0.1 - - is-stream@2.0.1: {} - - is-typedarray@1.0.0: {} - - is-unicode-supported@2.1.0: {} - - is-windows@1.0.2: {} - - isexe@2.0.0: {} - - isows@1.0.7(ws@8.18.3): - dependencies: - ws: 8.18.3 - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-hook@3.0.0: - dependencies: - append-transform: 2.0.0 - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.3 - '@istanbuljs/schema': 0.1.6 - istanbul-lib-coverage: 3.2.2 - semver: 7.8.0 - transitivePeerDependencies: - - supports-color - - istanbul-lib-processinfo@3.0.0: - dependencies: - archy: 1.0.0 - cross-spawn: 7.0.6 - istanbul-lib-coverage: 3.2.2 - p-map: 3.0.0 - rimraf: 6.1.3 - uuid: 8.3.2 - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jest-changed-files@30.4.1: - dependencies: - execa: 5.1.1 - jest-util: 30.4.1 - p-limit: 3.1.0 - - jest-circus@30.4.2: - dependencies: - '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.7.2 - is-generator-fn: 2.1.0 - jest-each: 30.4.1 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-runtime: 30.4.2 - jest-snapshot: 30.4.1 - jest-util: 30.4.1 - p-limit: 3.1.0 - pretty-format: 30.4.1 - pure-rand: 7.0.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@30.4.2(@types/node@25.6.2): - dependencies: - '@jest/core': 30.4.2 - '@jest/test-result': 30.4.1 - '@jest/types': 30.4.1 - chalk: 4.1.2 - exit-x: 0.2.2 - import-local: 3.2.0 - jest-config: 30.4.2(@types/node@25.6.2) - jest-util: 30.4.1 - jest-validate: 30.4.1 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node + is-decimal@1.0.4: {} - jest-config@30.4.2(@types/node@25.6.2): - dependencies: - '@babel/core': 7.29.0 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.4.0 - '@jest/test-sequencer': 30.4.1 - '@jest/types': 30.4.1 - babel-jest: 30.4.1(@babel/core@7.29.0) - chalk: 4.1.2 - ci-info: 4.4.0 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.4.2 - jest-docblock: 30.4.0 - jest-environment-node: 30.4.1 - jest-regex-util: 30.4.0 - jest-resolve: 30.4.1 - jest-runner: 30.4.2 - jest-util: 30.4.1 - jest-validate: 30.4.1 - parse-json: 5.2.0 - pretty-format: 30.4.1 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 25.6.2 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color + is-extglob@2.1.1: {} - jest-diff@30.4.1: - dependencies: - '@jest/diff-sequences': 30.4.0 - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - pretty-format: 30.4.1 + is-fullwidth-code-point@3.0.0: {} - jest-docblock@30.4.0: + is-glob@4.0.3: dependencies: - detect-newline: 3.1.0 + is-extglob: 2.1.1 - jest-each@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.4.1 - chalk: 4.1.2 - jest-util: 30.4.1 - pretty-format: 30.4.1 + is-hexadecimal@1.0.4: {} - jest-environment-node@30.4.1: - dependencies: - '@jest/environment': 30.4.1 - '@jest/fake-timers': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - jest-mock: 30.4.1 - jest-util: 30.4.1 - jest-validate: 30.4.1 + is-interactive@2.0.0: {} - jest-haste-map@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 30.4.0 - jest-util: 30.4.1 - jest-worker: 30.4.1 - picomatch: 4.0.4 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 + is-path-inside@3.0.3: {} - jest-leak-detector@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - pretty-format: 30.4.1 + is-plain-obj@2.1.0: {} - jest-matcher-utils@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - jest-diff: 30.4.1 - pretty-format: 30.4.1 + is-promise@4.0.0: {} - jest-message-util@30.4.1: + is-relative-url@4.1.0: dependencies: - '@babel/code-frame': 7.29.0 - '@jest/types': 30.4.1 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-util: 30.4.1 - picomatch: 4.0.4 - pretty-format: 30.4.1 - slash: 3.0.0 - stack-utils: 2.0.6 + is-absolute-url: 4.0.1 - jest-mock@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - jest-util: 30.4.1 + is-stream@2.0.1: {} - jest-pnp-resolver@1.2.3(jest-resolve@30.4.1): - optionalDependencies: - jest-resolve: 30.4.1 + is-typedarray@1.0.0: {} - jest-regex-util@30.4.0: {} + is-unicode-supported@2.1.0: {} - jest-resolve-dependencies@30.4.2: - dependencies: - jest-regex-util: 30.4.0 - jest-snapshot: 30.4.1 - transitivePeerDependencies: - - supports-color + is-windows@1.0.2: {} + + isexe@2.0.0: {} - jest-resolve@30.4.1: + isows@1.0.7(ws@8.18.3): dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-pnp-resolver: 1.2.3(jest-resolve@30.4.1) - jest-util: 30.4.1 - jest-validate: 30.4.1 - slash: 3.0.0 - unrs-resolver: 1.11.1 - - jest-runner@30.4.2: - dependencies: - '@jest/console': 30.4.1 - '@jest/environment': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - chalk: 4.1.2 - emittery: 0.13.1 - exit-x: 0.2.2 - graceful-fs: 4.2.11 - jest-docblock: 30.4.0 - jest-environment-node: 30.4.1 - jest-haste-map: 30.4.1 - jest-leak-detector: 30.4.1 - jest-message-util: 30.4.1 - jest-resolve: 30.4.1 - jest-runtime: 30.4.2 - jest-util: 30.4.1 - jest-watcher: 30.4.1 - jest-worker: 30.4.1 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color + ws: 8.18.3 + + istanbul-lib-coverage@3.2.2: {} - jest-runtime@30.4.2: + istanbul-lib-hook@3.0.0: dependencies: - '@jest/environment': 30.4.1 - '@jest/fake-timers': 30.4.1 - '@jest/globals': 30.4.1 - '@jest/source-map': 30.0.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - chalk: 4.1.2 - cjs-module-lexer: 2.2.0 - collect-v8-coverage: 1.0.3 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-regex-util: 30.4.0 - jest-resolve: 30.4.1 - jest-snapshot: 30.4.1 - jest-util: 30.4.1 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color + append-transform: 2.0.0 - jest-snapshot@30.4.1: + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 - '@jest/expect-utils': 30.4.1 - '@jest/get-type': 30.1.0 - '@jest/snapshot-utils': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - chalk: 4.1.2 - expect: 30.4.1 - graceful-fs: 4.2.11 - jest-diff: 30.4.1 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - pretty-format: 30.4.1 + '@babel/parser': 7.29.3 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 semver: 7.8.0 - synckit: 0.11.12 transitivePeerDependencies: - supports-color - jest-util@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.4 - - jest-validate@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.4.1 - camelcase: 6.3.0 - chalk: 4.1.2 - leven: 3.1.0 - pretty-format: 30.4.1 - - jest-watcher@30.4.1: + istanbul-lib-processinfo@3.0.0: dependencies: - '@jest/test-result': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 25.6.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 30.4.1 - string-length: 4.0.2 + archy: 1.0.0 + cross-spawn: 7.0.6 + istanbul-lib-coverage: 3.2.2 + p-map: 3.0.0 + rimraf: 6.1.3 + uuid: 8.3.2 - jest-worker@30.4.1: + istanbul-lib-report@3.0.1: dependencies: - '@types/node': 25.6.2 - '@ungap/structured-clone': 1.3.1 - jest-util: 30.4.1 - merge-stream: 2.0.0 - supports-color: 8.1.1 + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 - jest@30.4.2(@types/node@25.6.2): + istanbul-lib-source-maps@4.0.1: dependencies: - '@jest/core': 30.4.2 - '@jest/types': 30.4.1 - import-local: 3.2.0 - jest-cli: 30.4.2(@types/node@25.6.2) + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - supports-color - - ts-node + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 jose@6.2.3: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -5544,8 +4382,6 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -5560,14 +4396,59 @@ snapshots: dependencies: json-buffer: 3.0.1 - leven@3.1.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - lines-and-columns@1.2.4: {} + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 link-check@5.5.1: dependencies: @@ -5589,8 +4470,6 @@ snapshots: lodash.flattendeep@4.4.0: {} - lodash.memoize@4.1.2: {} - lodash.merge@4.6.2: {} log-symbols@7.0.1: @@ -5602,8 +4481,6 @@ snapshots: longest-streak@2.0.4: {} - lru-cache@10.4.3: {} - lru-cache@11.3.6: {} lru-cache@5.1.1: @@ -5612,6 +4489,16 @@ snapshots: lru-cache@7.18.3: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.2: + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + make-dir@3.1.0: dependencies: semver: 6.3.1 @@ -5620,12 +4507,6 @@ snapshots: dependencies: semver: 7.8.0 - make-error@1.3.6: {} - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - markdown-link-check@3.14.2: dependencies: async: 3.2.6 @@ -5726,8 +4607,6 @@ snapshots: merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} - micromark-extension-footnote@0.3.2: dependencies: micromark: 2.11.4 @@ -5788,8 +4667,6 @@ snapshots: dependencies: mime-db: 1.54.0 - mimic-fn@2.1.0: {} - mimic-function@5.0.1: {} minimatch@10.2.5: @@ -5800,10 +4677,6 @@ snapshots: dependencies: brace-expansion: 1.1.14 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.0 - minimist@1.2.8: {} minipass@7.1.3: {} @@ -5812,7 +4685,7 @@ snapshots: mute-stream@3.0.0: {} - napi-postinstall@0.3.4: {} + nanoid@3.3.12: {} natural-compare@1.4.0: {} @@ -5823,8 +4696,6 @@ snapshots: negotiator@1.0.0: {} - neo-async@2.6.2: {} - neotraverse@0.6.18: {} netmask@2.1.1: {} @@ -5834,20 +4705,12 @@ snapshots: ms: 2.1.3 validator: 13.15.35 - node-int64@0.4.0: {} - node-preload@0.2.1: dependencies: process-on-spawn: 1.1.0 node-releases@2.0.38: {} - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -5888,6 +4751,8 @@ snapshots: object-inspect@1.13.4: {} + obug@2.1.1: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -5896,10 +4761,6 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -6001,13 +4862,6 @@ snapshots: is-decimal: 1.0.4 is-hexadecimal: 1.0.4 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -6029,11 +4883,6 @@ snapshots: path-key@3.1.1: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.3 - path-scurry@2.0.2: dependencies: lru-cache: 11.3.6 @@ -6041,31 +4890,28 @@ snapshots: path-to-regexp@8.4.2: {} - picocolors@1.1.1: {} + pathe@2.0.3: {} - picomatch@2.3.2: {} + picocolors@1.1.1: {} picomatch@4.0.4: {} - pirates@4.0.7: {} - pkce-challenge@5.0.1: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier@3.8.3: {} - pretty-format@30.4.1: - dependencies: - '@jest/schemas': 30.4.1 - ansi-styles: 5.2.0 - react-is-18: react-is@18.3.1 - react-is-19: react-is@19.2.6 - process-on-spawn@1.1.0: dependencies: fromentries: 1.3.2 @@ -6108,8 +4954,6 @@ snapshots: punycode@2.3.1: {} - pure-rand@7.0.1: {} - qrcode-terminal@0.12.0: {} qs@6.15.1: @@ -6127,10 +4971,6 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 - react-is@18.3.1: {} - - react-is@19.2.6: {} - release-zalgo@1.0.0: dependencies: es6-error: 4.1.1 @@ -6170,10 +5010,6 @@ snapshots: require-main-filename@2.0.0: {} - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -6198,6 +5034,27 @@ snapshots: glob: 13.0.6 package-json-from-dist: 1.0.1 + rolldown@1.0.0: + dependencies: + '@oxc-project/types': 0.129.0 + '@rolldown/pluginutils': 1.0.0 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0 + '@rolldown/binding-darwin-arm64': 1.0.0 + '@rolldown/binding-darwin-x64': 1.0.0 + '@rolldown/binding-freebsd-x64': 1.0.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0 + '@rolldown/binding-linux-arm64-gnu': 1.0.0 + '@rolldown/binding-linux-arm64-musl': 1.0.0 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0 + '@rolldown/binding-linux-s390x-gnu': 1.0.0 + '@rolldown/binding-linux-x64-gnu': 1.0.0 + '@rolldown/binding-linux-x64-musl': 1.0.0 + '@rolldown/binding-openharmony-arm64': 1.0.0 + '@rolldown/binding-wasm32-wasi': 1.0.0 + '@rolldown/binding-win32-arm64-msvc': 1.0.0 + '@rolldown/binding-win32-x64-msvc': 1.0.0 + router@2.2.0: dependencies: debug: 4.4.3 @@ -6283,12 +5140,12 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} - slash@3.0.0: {} - smart-buffer@4.2.0: {} socks-proxy-agent@8.0.5: @@ -6304,10 +5161,7 @@ snapshots: ip-address: 10.2.0 smart-buffer: 4.2.0 - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + source-map-js@1.2.1: {} source-map@0.6.1: {} @@ -6323,18 +5177,13 @@ snapshots: sprintf-js@1.0.3: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 + stackback@0.0.2: {} statuses@2.0.2: {} - stdin-discarder@0.3.2: {} + std-env@4.1.0: {} - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 + stdin-discarder@0.3.2: {} string-width@4.2.3: dependencies: @@ -6342,12 +5191,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.2.0 - string-width@8.2.1: dependencies: get-east-asian-width: 1.6.0 @@ -6363,8 +5206,6 @@ snapshots: strip-bom@4.0.0: {} - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} structured-source@4.0.0: @@ -6375,20 +5216,6 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - synckit@0.11.12: - dependencies: - '@pkgr/core': 0.2.9 - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 7.2.3 - minimatch: 3.1.5 - test-exclude@8.0.0: dependencies: '@istanbuljs/schema': 0.1.6 @@ -6397,12 +5224,16 @@ snapshots: text-table@0.2.0: {} + tinybench@2.9.0: {} + + tinyexec@1.1.2: {} + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tmpl@1.0.5: {} + tinyrainbow@3.1.0: {} toidentifier@1.0.1: {} @@ -6412,26 +5243,6 @@ snapshots: dependencies: typescript: 5.9.3 - ts-jest@29.4.9(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0))(jest-util@30.4.1)(jest@30.4.2(@types/node@25.6.2))(typescript@5.9.3): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.9 - jest: 30.4.2(@types/node@25.6.2) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.8.0 - type-fest: 4.41.0 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - babel-jest: 30.4.1(@babel/core@7.29.0) - jest-util: 30.4.1 - tslib@2.8.1: {} tsx@4.21.0: @@ -6445,16 +5256,10 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-detect@4.0.8: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} - type-fest@0.8.1: {} - type-fest@4.41.0: {} - type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -6467,9 +5272,6 @@ snapshots: typescript@5.9.3: {} - uglify-js@3.19.3: - optional: true - undici-types@7.19.2: {} undici@7.25.0: {} @@ -6497,30 +5299,6 @@ snapshots: unpipe@1.0.0: {} - unrs-resolver@1.11.1: - dependencies: - napi-postinstall: 0.3.4 - optionalDependencies: - '@unrs/resolver-binding-android-arm-eabi': 1.11.1 - '@unrs/resolver-binding-android-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-x64': 1.11.1 - '@unrs/resolver-binding-freebsd-x64': 1.11.1 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 - '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-musl': 1.11.1 - '@unrs/resolver-binding-wasm32-wasi': 1.11.1 - '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 - '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 - '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 @@ -6574,9 +5352,46 @@ snapshots: - utf-8-validate - zod - walker@1.0.8: + vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0): dependencies: - makeerror: 1.0.12 + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.14 + rolldown: 1.0.0 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 25.6.2 + esbuild: 0.27.7 + fsevents: 2.3.3 + tsx: 4.21.0 + + vitest@4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0)): + dependencies: + '@vitest/expect': 4.1.5 + '@vitest/mocker': 4.1.5(vite@8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0)) + '@vitest/pretty-format': 4.1.5 + '@vitest/runner': 4.1.5 + '@vitest/snapshot': 4.1.5 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.12(@types/node@25.6.2)(esbuild@0.27.7)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.6.2 + '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) + transitivePeerDependencies: + - msw whatwg-encoding@3.1.1: dependencies: @@ -6590,9 +5405,12 @@ snapshots: dependencies: isexe: 2.0.0 - word-wrap@1.2.5: {} + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 - wordwrap@1.0.0: {} + word-wrap@1.2.5: {} wrap-ansi@6.2.0: dependencies: @@ -6606,12 +5424,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.2.0 - wrappy@1.0.2: {} write-file-atomic@3.0.3: @@ -6621,11 +5433,6 @@ snapshots: signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - ws@8.18.3: {} xmlbuilder2@4.0.3: diff --git a/src/cli/commands/clean.ts b/src/cli/commands/clean.ts index a43f52b7..1110779e 100644 --- a/src/cli/commands/clean.ts +++ b/src/cli/commands/clean.ts @@ -16,6 +16,7 @@ import { } from '../../lib/index.js'; import { formatOutput, formatSuccess, formatWarning } from '../output.js'; import { loadSessions, deleteSession, consolidateSessions } from '../../lib/sessions.js'; +import { deleteCompletionCache } from '../../lib/completion-cache.js'; import { stopBridge } from '../../lib/bridge-manager.js'; import { createLogger } from '../../lib/logger.js'; import { deleteAuthProfiles } from '../../lib/auth/profiles.js'; @@ -87,6 +88,8 @@ async function cleanSessions(): Promise { // Delete session data await deleteSession(name); + // Drop the completion cache file so it doesn't linger as dead data. + await deleteCompletionCache(name); count++; logger.debug(`Cleaned session: ${name}`); diff --git a/src/cli/commands/completion.ts b/src/cli/commands/completion.ts new file mode 100644 index 00000000..82f42f87 --- /dev/null +++ b/src/cli/commands/completion.ts @@ -0,0 +1,725 @@ +/** + * Shell completion for mcpc + * + * Implements the two-piece pattern used by kubectl/gh/cobra: + * 1. `mcpc completion ` prints a thin shell script that registers a + * completion function for the user's shell. + * 2. `mcpc __complete -- ` is a hidden subcommand that + * the shell script calls on every TAB. It returns suggestions on stdout, + * one per line, with an optional `:` directive line at the end. + * + * The shell script is thin and stable — all completion logic lives here in + * TypeScript, so upgrading mcpc automatically picks up new commands without + * regenerating any files. + * + * Per-command flag suggestions are extracted at runtime from Commander's own + * tree (`createTopLevelProgram` + `registerSessionCommands` in ../index.ts), + * so there is no static duplication that could drift. The cycle + * `index.ts → commands/completion.ts → index.ts` is safe under ESM because + * the factories are only *called* inside functions, never at module load. + */ + +import type { Command } from 'commander'; +import { mkdir, writeFile } from 'fs/promises'; +import { homedir, platform } from 'os'; +import { dirname, join } from 'path'; +import { theme } from '../output.js'; +import { loadSessions } from '../../lib/sessions.js'; +import { listAuthProfiles } from '../../lib/auth/profiles.js'; +import { readCompletionCache, type CompletionKind } from '../../lib/completion-cache.js'; +import { ClientError } from '../../lib/index.js'; +import { KNOWN_COMMANDS, KNOWN_SESSION_COMMANDS } from '../parser.js'; +import { createTopLevelProgram, createSessionProgram, registerSessionCommands } from '../index.js'; + +/** Supported shells for completion script generation. */ +export const SUPPORTED_SHELLS = ['bash', 'zsh', 'fish'] as const; +export type Shell = (typeof SUPPORTED_SHELLS)[number]; + +/** + * Directive bitflags returned on the last line of `__complete` output. + * Mirrors the cobra/kubectl convention so existing shell snippets can be + * adapted with minimal changes. + */ +export const DIRECTIVE = { + /** Default — add a trailing space after the match, allow file fallback. */ + DEFAULT: 0, + /** Do not add a trailing space (e.g. partial prefix completion). */ + NO_SPACE: 1, + /** Do not fall back to filename completion when there are no candidates. */ + NO_FILES: 2, +} as const; + +/** Snapshot of per-command flag/option metadata derived from Commander's tree. */ +interface CommandFlagMetadata { + /** Flags valid on the top-level program (`mcpc --foo`). */ + global: readonly string[]; + /** Flags per top-level subcommand: `{ connect: ['--header', '--stdio', ...], ... }`. */ + topLevel: ReadonlyMap; + /** Flags per session subcommand. */ + session: ReadonlyMap; + /** Flag tokens that take a value (used for skip-ahead during context analysis). */ + flagsWithValues: ReadonlySet; +} + +let cachedMetadata: CommandFlagMetadata | undefined; + +/** + * Walk a Commander option-flags spec (e.g. `'-H, --header
'`) and + * return just the flag tokens (`['-H', '--header']`). Argument placeholders + * (`
`, `[name]`) are stripped. + */ +function parseFlagTokens(spec: string): string[] { + return spec + .split(',') + .map((part) => part.trim().split(/\s+/)[0] ?? '') + .filter((tok) => tok.startsWith('-')); +} + +/** + * Commander tracks the help option outside its public `options` array and + * provides no public getter. Every command in this codebase keeps the + * default `-h, --help` (or re-registers it via `.helpOption()`), so we + * surface those tokens as a known supplement rather than reaching into + * Commander internals. + */ +const BUILTIN_HELP_FLAGS = ['-h', '--help'] as const; + +/** Extract all flag tokens declared on a Commander command (plus `-h, --help`). */ +function collectFlags(cmd: Command): string[] { + const flags: string[] = []; + for (const opt of cmd.options) flags.push(...parseFlagTokens(opt.flags)); + flags.push(...BUILTIN_HELP_FLAGS); + return flags; +} + +/** Identify whether a Commander option takes a value (`` or `[arg]` in its spec). */ +function optionTakesValue(spec: string): boolean { + return /[<\[]/.test(spec); +} + +/** + * Build the flag metadata once per process by introspecting the actual + * Commander programs used at runtime. The factories are pure constructors + * (no I/O, no auth) so this is cheap and safe. + */ +function getCommandFlagMetadata(): CommandFlagMetadata { + if (cachedMetadata) return cachedMetadata; + + const topLevelProgram = createTopLevelProgram(); + const sessionProgram = createSessionProgram(); + registerSessionCommands(sessionProgram, '<@session>'); + + const flagsWithValues = new Set(); + const collectValueFlags = (cmd: Command): void => { + for (const opt of cmd.options) { + if (optionTakesValue(opt.flags)) { + for (const tok of parseFlagTokens(opt.flags)) flagsWithValues.add(tok); + } + } + }; + + collectValueFlags(topLevelProgram); + collectValueFlags(sessionProgram); + + const global = collectFlags(topLevelProgram); + + const topLevel = new Map(); + for (const sub of topLevelProgram.commands) { + collectValueFlags(sub); + topLevel.set(sub.name(), collectFlags(sub)); + } + + const session = new Map(); + for (const sub of sessionProgram.commands) { + collectValueFlags(sub); + session.set(sub.name(), collectFlags(sub)); + } + + cachedMetadata = { global, topLevel, session, flagsWithValues }; + return cachedMetadata; +} + +/** Reset the cached metadata. Intended for tests only. */ +export function resetCommandFlagMetadataCache(): void { + cachedMetadata = undefined; +} + +/** Allowed values for `logging-set-level`. */ +const LOG_LEVELS = [ + 'debug', + 'info', + 'notice', + 'warning', + 'error', + 'critical', + 'alert', + 'emergency', +]; + +/** Allowed resources for `mcpc clean`. */ +const CLEAN_RESOURCES = ['sessions', 'profiles', 'logs', 'all']; + +/** + * Filter a list of candidates by a prefix (case-sensitive). + * Empty prefix returns all candidates. + */ +function filterByPrefix(candidates: readonly string[], prefix: string): string[] { + if (!prefix) return [...candidates]; + return candidates.filter((c) => c.startsWith(prefix)); +} + +/** + * Load `@session` names from sessions.json. Never throws — returns [] on any error + * so a corrupted file never freezes the shell. + */ +async function loadSessionNames(): Promise { + // Session keys in sessions.json already include the `@` prefix (e.g. `@apify`). + try { + const storage = await loadSessions(); + return Object.keys(storage.sessions); + } catch { + return []; + } +} + +/** + * Load known server hosts from auth profiles (for `login`/`logout` completion). + * Returns [] on any error — same best-effort contract as `loadSessionNames`. + */ +async function loadProfileServers(): Promise { + try { + const profiles = await listAuthProfiles(); + const hosts = new Set(); + for (const p of profiles) { + try { + const url = new URL(p.serverUrl); + hosts.add(url.host); + } catch { + // Ignore malformed URLs. + } + } + return Array.from(hosts); + } catch { + return []; + } +} + +/** + * Load distinct profile names across all servers (for `--profile ` completion). + * Returns [] on any error — same best-effort contract as `loadSessionNames`. + */ +async function loadProfileNames(): Promise { + try { + const profiles = await listAuthProfiles(); + const names = new Set(); + for (const p of profiles) names.add(p.name); + return Array.from(names); + } catch { + return []; + } +} + +/** + * Context describing where we are in the command line. + * Pure data — extracted from the tokenized words for easy unit testing. + */ +export interface CompletionContext { + /** Words already typed (excluding the program name and the partial). */ + words: string[]; + /** The partial token currently being completed (may be empty). */ + partial: string; + /** First non-option token, if any. */ + firstNonOption?: string; + /** Whether the first non-option token starts with '@' (session command). */ + isSessionCommand: boolean; + /** Session subcommand if `isSessionCommand` and a subcommand is present. */ + sessionSubcommand?: string; + /** Top-level command if not a session command (e.g. 'connect', 'login'). */ + topLevelCommand?: string; + /** The token immediately before the partial (used for flag-value completion). */ + previousToken?: string; +} + +/** + * Analyze the tokenized command line and figure out what we're completing. + * Pure function — easy to unit-test without disk or network access. + * + * `words` is the list of complete tokens before the cursor (e.g. from + * COMP_WORDS minus the program name). `partial` is the current incomplete + * token (possibly empty if the cursor is on whitespace). + */ +export function analyzeContext(words: string[], partial: string): CompletionContext { + const ctx: CompletionContext = { + words, + partial, + isSessionCommand: false, + }; + const last = words[words.length - 1]; + if (last !== undefined) { + ctx.previousToken = last; + } + + const { flagsWithValues } = getCommandFlagMetadata(); + + // Locate the first non-option token. Skip values for options that take one + // so we route based on positional intent (`-H foo @apify` should still see + // `@apify` as the command). + for (let i = 0; i < words.length; i++) { + const w = words[i]; + if (w === undefined) continue; + if (w.startsWith('-')) { + if (flagsWithValues.has(w)) i++; + continue; + } + ctx.firstNonOption = w; + if (w.startsWith('@')) { + ctx.isSessionCommand = true; + for (let j = i + 1; j < words.length; j++) { + const sub = words[j]; + if (sub === undefined) continue; + if (sub.startsWith('-')) { + if (flagsWithValues.has(sub)) j++; + continue; + } + ctx.sessionSubcommand = sub; + break; + } + } else { + ctx.topLevelCommand = w; + } + break; + } + + return ctx; +} + +/** + * Result of a completion query. + */ +export interface CompletionResult { + candidates: string[]; + /** Bitmask of DIRECTIVE.* flags. */ + directive: number; +} + +/** + * Compute completion candidates for the given context. + * Async because @session names live in sessions.json and profile names in profiles.json. + * + * Network access is never performed here — only local disk reads via existing + * lib helpers. Hard failures fall through to "no suggestions". + */ +export async function suggestCompletions(ctx: CompletionContext): Promise { + const { partial, previousToken } = ctx; + + const { flagsWithValues } = getCommandFlagMetadata(); + + // 1. Flag-value completion: previous token is a flag that takes a value. + if (previousToken && flagsWithValues.has(previousToken)) { + if (previousToken === '--profile') { + return { + candidates: filterByPrefix(await loadProfileNames(), partial), + directive: DIRECTIVE.DEFAULT, + }; + } + // For unknown flag values, let the shell fall back to file completion. + return { candidates: [], directive: DIRECTIVE.DEFAULT }; + } + + // 2. The partial starts with '-' → flag completion. + if (partial.startsWith('-')) { + return completeFlags(ctx); + } + + // 3. The partial starts with '@' OR we're at the first non-option slot + // → @session names + top-level commands. + if (!ctx.firstNonOption) { + return completeFirstToken(partial); + } + + // 4. Session command: complete subcommand or its args. + if (ctx.isSessionCommand) { + return completeSessionContext(ctx); + } + + // 5. Top-level command: complete its positional args. + return completeTopLevelContext(ctx); +} + +/** Complete the first token: top-level commands and @session names. */ +async function completeFirstToken(partial: string): Promise { + const sessions = await loadSessionNames(); + const all = [...KNOWN_COMMANDS, ...sessions]; + return { candidates: filterByPrefix(all, partial), directive: DIRECTIVE.DEFAULT }; +} + +/** Complete flags for the current command. */ +function completeFlags(ctx: CompletionContext): CompletionResult { + const meta = getCommandFlagMetadata(); + let flags: readonly string[]; + if (ctx.isSessionCommand) { + const sub = ctx.sessionSubcommand; + const subFlags = sub ? (meta.session.get(sub) ?? []) : []; + flags = [...subFlags, ...meta.global]; + } else if (ctx.topLevelCommand) { + flags = [...(meta.topLevel.get(ctx.topLevelCommand) ?? []), ...meta.global]; + } else { + flags = meta.global; + } + const seen = new Set(); + const unique = flags.filter((f) => (seen.has(f) ? false : (seen.add(f), true))); + return { candidates: filterByPrefix(unique, ctx.partial), directive: DIRECTIVE.DEFAULT }; +} + +/** Map a session subcommand to the cache kind it consumes, if any. */ +const SUBCOMMAND_CACHE_KIND: Record = { + 'tools-get': 'tools', + 'tools-call': 'tools', + 'resources-read': 'resources', + 'resources-subscribe': 'resources', + 'resources-unsubscribe': 'resources', + 'prompts-get': 'prompts', +}; + +/** Complete after a `@session` — either the subcommand or the subcommand's args. */ +async function completeSessionContext(ctx: CompletionContext): Promise { + if (!ctx.sessionSubcommand) { + return { + candidates: filterByPrefix(KNOWN_SESSION_COMMANDS, ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + // Subcommand-specific value completion. + if (ctx.sessionSubcommand === 'logging-set-level') { + return { candidates: filterByPrefix(LOG_LEVELS, ctx.partial), directive: DIRECTIVE.DEFAULT }; + } + const cacheKind = SUBCOMMAND_CACHE_KIND[ctx.sessionSubcommand]; + if (cacheKind && ctx.firstNonOption && isFirstPositionalAfterSubcommand(ctx)) { + // Tool names / resource URIs / prompt names come from the per-session + // cache populated by `tools-list` / `resources-list` / `prompts-list`. + // Run those once to warm the cache if completion comes up empty. + const names = await readCompletionCache(ctx.firstNonOption, cacheKind); + return { candidates: filterByPrefix(names, ctx.partial), directive: DIRECTIVE.DEFAULT }; + } + // No suggestions for other free-form args; shell falls back to file completion. + return { candidates: [], directive: DIRECTIVE.DEFAULT }; +} + +/** + * After `@session `, are we still on the first positional arg + * (i.e. the tool name / URI / prompt name slot)? Used to avoid suggesting + * names again once the user has typed past the first positional. + */ +function isFirstPositionalAfterSubcommand(ctx: CompletionContext): boolean { + const { flagsWithValues } = getCommandFlagMetadata(); + let seenSession = false; + let seenSubcommand = false; + let positionalsAfter = 0; + for (let i = 0; i < ctx.words.length; i++) { + const w = ctx.words[i]; + if (w === undefined) continue; + if (w.startsWith('-')) { + if (flagsWithValues.has(w)) i++; + continue; + } + if (!seenSession) { + seenSession = true; + continue; + } + if (!seenSubcommand) { + seenSubcommand = true; + continue; + } + positionalsAfter++; + } + return seenSubcommand && positionalsAfter === 0; +} + +/** Complete after a top-level command (connect, login, clean, etc.). */ +async function completeTopLevelContext(ctx: CompletionContext): Promise { + const cmd = ctx.topLevelCommand; + switch (cmd) { + case 'connect': { + // `connect [@session]` — suggest @session names so the user can + // tab-complete the optional session name. The shell falls back to file + // completion for the arg (config file paths). + return { + candidates: filterByPrefix(await loadSessionNames(), ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + case 'close': + case 'restart': + case 'shell': { + return { + candidates: filterByPrefix(await loadSessionNames(), ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + case 'login': + case 'logout': { + return { + candidates: filterByPrefix(await loadProfileServers(), ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + case 'clean': { + return { + candidates: filterByPrefix(CLEAN_RESOURCES, ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + case 'help': { + const all = [...KNOWN_COMMANDS, ...KNOWN_SESSION_COMMANDS]; + return { candidates: filterByPrefix(all, ctx.partial), directive: DIRECTIVE.DEFAULT }; + } + case 'completion': { + return { + candidates: filterByPrefix([...SUPPORTED_SHELLS, 'install'], ctx.partial), + directive: DIRECTIVE.DEFAULT, + }; + } + default: + return { candidates: [], directive: DIRECTIVE.DEFAULT }; + } +} + +/** + * Entry point for the hidden `__complete` subcommand. + * + * `args` is everything after `--` on the command line, mirroring `COMP_WORDS`: + * args[0] = program name (typically "mcpc") — ignored + * args[1..N-1] = completed tokens + * args[N] = the current partial token (may be empty) + * + * Output is written to stdout: one candidate per line, plus an optional + * `:` directive line. + */ +export async function handleComplete(args: string[]): Promise { + // Drop the program name; tokens after that are user input. + const tokens = args.slice(1); + const partial = tokens[tokens.length - 1] ?? ''; + const words = tokens.slice(0, -1); + + const ctx = analyzeContext(words, partial); + const result = await suggestCompletions(ctx); + + for (const c of result.candidates) { + process.stdout.write(c + '\n'); + } + if (result.directive !== DIRECTIVE.DEFAULT) { + process.stdout.write(`:${result.directive}\n`); + } +} + +/** Generate the bash completion script. */ +export function generateBashScript(): string { + // All intelligence lives in `mcpc __complete`; this script is just a thin + // adapter that translates bash's COMP_WORDS/COMP_CWORD into the protocol. + return `# mcpc bash completion +# Install: source this file, or place it in /etc/bash_completion.d/ or +# ~/.local/share/bash-completion/completions/mcpc +# Generated by 'mcpc completion bash' + +_mcpc() { + COMPREPLY=() + local IFS=$'\\n' + local response directive=0 last + + # Pass the program name, all completed words, and the current partial + # token. mcpc's __complete handler takes care of context analysis. + if ! response=$(mcpc __complete -- "\${COMP_WORDS[@]:0:\$COMP_CWORD}" "\${COMP_WORDS[\$COMP_CWORD]:-}" 2>/dev/null); then + return 0 + fi + + # If the last line is ':', it's a directive bitmask (cobra-style). + last=\${response##*$'\\n'} + if [[ "\$last" == :* ]]; then + directive=\${last#:} + response=\${response%$'\\n'*} + fi + + while IFS= read -r line; do + [[ -z "\$line" ]] && continue + COMPREPLY+=("\$line") + done <<< "\$response" + + # Directive bit 0 (value 1): no trailing space + if (( (directive & 1) != 0 )); then + compopt -o nospace 2>/dev/null + fi + # Directive bit 1 (value 2): no file fallback + if (( (directive & 2) != 0 )); then + compopt +o default 2>/dev/null + fi +} + +complete -o default -F _mcpc mcpc +`; +} + +/** Generate the zsh completion script. */ +export function generateZshScript(): string { + return `#compdef mcpc +# mcpc zsh completion +# Install: place this file in a directory on your $fpath (e.g. +# ~/.zsh/completions) and add 'autoload -U compinit; compinit' to ~/.zshrc. +# Generated by 'mcpc completion zsh' + +_mcpc() { + local -a candidates + local response directive=0 last + + # Build the words array compatible with the __complete protocol: + # program name + completed words + the current partial. + local cur="\${words[CURRENT]:-}" + local before=("\${words[@]:0:$((CURRENT-1))}") + + response=$(mcpc __complete -- "\${before[@]}" "\$cur" 2>/dev/null) || return + + last=\${response##*$'\\n'} + if [[ "\$last" == :* ]]; then + directive=\${last#:} + response=\${response%$'\\n'*} + fi + + local IFS=$'\\n' + candidates=(\${(f)response}) + + local -a opts=() + if (( (directive & 1) != 0 )); then + opts+=(-S '') # no space after match + fi + if (( \${#candidates[@]} > 0 )); then + compadd "\${opts[@]}" -- "\${candidates[@]}" + fi +} + +compdef _mcpc mcpc +`; +} + +/** Generate the fish completion script. */ +export function generateFishScript(): string { + return `# mcpc fish completion +# Install: place this file in ~/.config/fish/completions/mcpc.fish +# Generated by 'mcpc completion fish' + +function __mcpc_complete + set -l tokens (commandline -opc) (commandline -ct) + mcpc __complete -- $tokens 2>/dev/null | grep -v '^:[0-9]*$' +end + +complete -c mcpc -f -a '(__mcpc_complete)' +`; +} + +/** Print the completion script for the given shell to stdout. */ +export function printCompletionScript(shell: Shell): void { + switch (shell) { + case 'bash': + process.stdout.write(generateBashScript()); + return; + case 'zsh': + process.stdout.write(generateZshScript()); + return; + case 'fish': + process.stdout.write(generateFishScript()); + return; + } +} + +/** Compute the recommended install path for a shell. */ +export function getInstallPath(shell: Shell): string { + const home = homedir(); + switch (shell) { + case 'bash': { + // Linux/macOS user-local bash-completion path. + const xdg = process.env.XDG_DATA_HOME ?? join(home, '.local', 'share'); + return join(xdg, 'bash-completion', 'completions', 'mcpc'); + } + case 'zsh': + return join(home, '.zsh', 'completions', '_mcpc'); + case 'fish': + return join(home, '.config', 'fish', 'completions', 'mcpc.fish'); + } +} + +/** + * Detect the user's shell from $SHELL. Returns undefined if it can't be + * determined or isn't supported. + */ +export function detectShell(): Shell | undefined { + const shellPath = process.env.SHELL ?? ''; + if (shellPath.endsWith('/bash') || shellPath.endsWith('\\bash.exe')) return 'bash'; + if (shellPath.endsWith('/zsh')) return 'zsh'; + if (shellPath.endsWith('/fish')) return 'fish'; + return undefined; +} + +/** + * Install the completion script for the given (or detected) shell. + * Writes the file and prints a short instruction block. + */ +export async function installCompletion(shell?: Shell): Promise { + const resolved = shell ?? detectShell(); + if (!resolved) { + throw new ClientError( + `Could not auto-detect your shell from $SHELL.\n` + + `Run one of:\n` + + ` mcpc completion install bash\n` + + ` mcpc completion install zsh\n` + + ` mcpc completion install fish` + ); + } + + if (platform() === 'win32') { + throw new ClientError( + 'Windows shells are not supported yet. ' + + 'Use WSL or run `mcpc completion ` and source the output manually.' + ); + } + + const installPath = getInstallPath(resolved); + const script = + resolved === 'bash' + ? generateBashScript() + : resolved === 'zsh' + ? generateZshScript() + : generateFishScript(); + + await mkdir(dirname(installPath), { recursive: true }); + await writeFile(installPath, script); + + console.log(theme.green(`✓ Installed ${resolved} completion at ${installPath}`)); + printShellSpecificInstructions(resolved, installPath); +} + +/** Print post-install instructions specific to each shell. */ +function printShellSpecificInstructions(shell: Shell, installPath: string): void { + switch (shell) { + case 'bash': + console.log( + `\nTo activate, either start a new shell or run:\n` + + ` source ${installPath}\n\n` + + `If completions don't work, ensure your shell loads files from\n` + + ` ~/.local/share/bash-completion/completions/\n` + + `Most modern bash-completion installations do this automatically.\n` + + `On macOS with Homebrew bash-completion@2 this is the default.` + ); + return; + case 'zsh': + console.log( + `\nAdd these lines to ~/.zshrc (if not already present):\n` + + ` fpath=(${dirname(installPath)} $fpath)\n` + + ` autoload -U compinit && compinit\n\n` + + `Then start a new shell or run 'exec zsh' to activate.` + ); + return; + case 'fish': + console.log( + `\nFish auto-loads completions from this path; ` + `open a new shell to activate.` + ); + return; + } +} diff --git a/src/cli/commands/prompts.ts b/src/cli/commands/prompts.ts index 79c0ba9b..d2e2ed1b 100644 --- a/src/cli/commands/prompts.ts +++ b/src/cli/commands/prompts.ts @@ -5,6 +5,7 @@ import type { CommandOptions } from '../../lib/types.js'; import { formatOutput } from '../output.js'; import { withMcpClient } from '../helpers.js'; +import { writeCompletionCache } from '../../lib/completion-cache.js'; import { parseCommandArgs, hasStdinData, readStdinArgs } from '../parser.js'; /** @@ -28,6 +29,12 @@ export async function listPrompts(target: string, options: CommandOptions): Prom ...(options.maxChars && { maxChars: options.maxChars }), }) ); + // Mirror names to the shell-completion cache so `prompts-get ` works. + await writeCompletionCache( + target, + 'prompts', + allPrompts.map((p) => p.name) + ); }); } diff --git a/src/cli/commands/resources.ts b/src/cli/commands/resources.ts index 3810cc69..59222371 100644 --- a/src/cli/commands/resources.ts +++ b/src/cli/commands/resources.ts @@ -4,6 +4,7 @@ import { formatOutput, formatSuccess } from '../output.js'; import { withMcpClient } from '../helpers.js'; +import { writeCompletionCache } from '../../lib/completion-cache.js'; import type { CommandOptions } from '../../lib/types.js'; /** @@ -27,6 +28,12 @@ export async function listResources(target: string, options: CommandOptions): Pr ...(options.maxChars && { maxChars: options.maxChars }), }) ); + // Mirror URIs to the shell-completion cache so `resources-read ` works. + await writeCompletionCache( + target, + 'resources', + allResources.map((r) => r.uri) + ); }); } diff --git a/src/cli/commands/tools.ts b/src/cli/commands/tools.ts index 1a850aeb..6ff3a469 100644 --- a/src/cli/commands/tools.ts +++ b/src/cli/commands/tools.ts @@ -18,6 +18,7 @@ import { } from '../output.js'; import { ClientError } from '../../lib/errors.js'; import type { CallToolResult, CommandOptions, TaskUpdate } from '../../lib/types.js'; +import { writeCompletionCache } from '../../lib/completion-cache.js'; import { withMcpClient } from '../helpers.js'; import { parseCommandArgs, hasStdinData, readStdinArgs } from '../parser.js'; import { @@ -96,6 +97,12 @@ export async function listTools( sessionName: target, }) ); + // Mirror names to the shell-completion cache so `tools-get ` / `tools-call ` work. + await writeCompletionCache( + target, + 'tools', + result.tools.map((t) => t.name) + ); }); } diff --git a/src/cli/index.ts b/src/cli/index.ts index a32c99d9..f84134e6 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -11,6 +11,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ import { initProxy } from '../lib/proxy.js'; +import { fileURLToPath } from 'url'; import { Command, CommanderError, Help } from 'commander'; import { setVerbose, setJsonMode, closeFileLogger } from '../lib/index.js'; import { isMcpError, formatHumanError, ClientError } from '../lib/index.js'; @@ -27,6 +28,13 @@ import * as tasks from './commands/tasks.js'; import * as grepCmd from './commands/grep.js'; import { handleX402Command } from './commands/x402.js'; import { clean } from './commands/clean.js'; +import { + handleComplete, + installCompletion, + printCompletionScript, + SUPPORTED_SHELLS, + type Shell, +} from './commands/completion.js'; import type { OutputMode } from '../lib/index.js'; import { extractOptions, @@ -41,10 +49,11 @@ import { KNOWN_COMMANDS, KNOWN_SESSION_COMMANDS, } from './parser.js'; -import { createRequire } from 'module'; -const { version: mcpcVersion } = createRequire(import.meta.url)('../../package.json') as { - version: string; -}; +// Direct JSON import keeps this file free of `import.meta`, which would +// otherwise trip Jest's CommonJS transform when `commands/completion.ts` +// loads us transitively for runtime flag introspection. +import pkg from '../../package.json' with { type: 'json' }; +const mcpcVersion = pkg.version; // Set up HTTP proxy from environment variables (HTTPS_PROXY, HTTP_PROXY, NO_PROXY, and lowercase variants) // Also handle --insecure flag to disable TLS certificate verification (for self-signed certs) @@ -148,6 +157,26 @@ const SCHEMA_BASE = 'https://modelcontextprotocol.io/specification/2025-11-25/sc async function main(): Promise { const args = process.argv.slice(2); + // Hidden completion entry point. Handled before *anything* else so the + // shell never sees log lines, banners, or auth side-effects on stdout — + // only completion candidates. Invoked by the shell script generated via + // `mcpc completion ` on every TAB press. + if (args[0] === '__complete') { + // Convention: everything after `--` (or just everything after + // `__complete` if no `--`) is the COMP_WORDS-style payload. + const dashIdx = args.indexOf('--'); + const payload = dashIdx >= 0 ? args.slice(dashIdx + 1) : args.slice(1); + try { + await handleComplete(payload); + } catch { + // Never fail loudly — a broken completion must not freeze the shell. + } + // Hard exit so the rest of main() (signal handlers, logger init, command + // dispatch) never runs. process.stdout.write() above is synchronous, so + // no IO flush is at risk here. + process.exit(0); + } + // Set up cleanup handlers for graceful shutdown const handleExit = (): void => { void closeFileLogger().then(() => { @@ -346,7 +375,7 @@ async function main(): Promise { * Create the top-level Commander program with global commands * (login, logout, connect, clean, help) */ -function createTopLevelProgram(): Command { +export function createTopLevelProgram(): Command { const program = new Command(); // Configure help output width to avoid wrapping (default is 80) @@ -780,6 +809,69 @@ ${jsonHelp('`[{ sessionName, tools?: Tool[], resources?: Resource[], prompts?: P // eslint-disable-next-line @typescript-eslint/no-empty-function .action(() => {}); + // completion command: mcpc completion | install [shell] + program + .command('completion [args...]') + .description('Print or install a shell completion script (bash, zsh, fish)') + .addHelpText( + 'after', + ` +${chalk.bold('Usage:')} + mcpc completion bash Print bash completion script to stdout + mcpc completion zsh Print zsh completion script to stdout + mcpc completion fish Print fish completion script to stdout + mcpc completion install Auto-detect shell and install + mcpc completion install bash Install for a specific shell + +${chalk.bold('One-liners:')} + # Always-fresh (re-evaluates on every shell start): + echo 'eval "$(mcpc completion bash)"' >> ~/.bashrc + + # Or install once: + mcpc completion install + +Tab-completion covers top-level commands, session subcommands, ${theme.cyan('@session')} +names (from sessions.json), saved auth servers, and known flags. It never +triggers network calls or OAuth flows.` + ) + .action(async (args: string[]) => { + const [first, second, ...rest] = args; + if (rest.length > 0) { + throw new ClientError( + `Too many arguments for 'completion'.\n\n` + + `Usage: mcpc completion <${SUPPORTED_SHELLS.join('|')}> | install [shell]` + ); + } + // mcpc completion install [shell] + if (first === 'install') { + if (second && !SUPPORTED_SHELLS.includes(second as Shell)) { + throw new ClientError( + `Unsupported shell: ${second}. Supported: ${SUPPORTED_SHELLS.join(', ')}` + ); + } + await installCompletion(second as Shell | undefined); + return; + } + if (second) { + throw new ClientError( + `Unexpected argument: ${second}\n\n` + + `Usage: mcpc completion <${SUPPORTED_SHELLS.join('|')}> | install [shell]` + ); + } + if (!first) { + throw new ClientError( + `Missing shell argument.\n\n` + + `Usage: mcpc completion <${SUPPORTED_SHELLS.join('|')}> | install [shell]` + ); + } + if (!SUPPORTED_SHELLS.includes(first as Shell)) { + throw new ClientError( + `Unsupported shell: ${first}. Supported: ${SUPPORTED_SHELLS.join(', ')}` + ); + } + printCompletionScript(first as Shell); + }); + // help command: mcpc help [command] (supports "help x402 sign" etc.) program .command('help [command] [subcommand]') @@ -873,7 +965,7 @@ function showSessionCommandHelp(cmdName: string): boolean { * Register all session subcommands on a Commander program * Extracted so it can be reused for both execution and help lookup */ -function registerSessionCommands(program: Command, session: string): void { +export function registerSessionCommands(program: Command, session: string): void { // Help command — show same output as --help (hidden: already shown via --help) program .command('help', { hidden: true }) @@ -1239,7 +1331,7 @@ ${jsonHelp('`GetPromptResult` object', '`{ description?, messages: [{ role, cont * Create a Commander program for session subcommands * Separate from top-level program to avoid command name conflicts */ -function createSessionProgram(): Command { +export function createSessionProgram(): Command { const program = new Command(); program.configureOutput({ @@ -1375,9 +1467,27 @@ async function flushStdout(): Promise { }); } -// Run main function -main().catch(async (error) => { - console.error('Fatal error:', error); - await closeFileLogger(); - process.exit(1); -}); +/** + * Entry point invoked by `bin/mcpc` (compiled) and by `tsx src/cli/index.ts` + * (e.g. the README-help test). Exported rather than purely self-running so + * other modules — notably `commands/completion.ts`, which introspects the + * Commander tree at TAB time — can `import` from this file without triggering + * CLI execution. + */ +export async function run(): Promise { + try { + await main(); + } catch (error) { + console.error('Fatal error:', error); + await closeFileLogger(); + process.exit(1); + } +} + +// Auto-run when invoked directly (`tsx src/cli/index.ts ...` or +// `node dist/cli/index.js ...`). When loaded via `bin/mcpc` or imported by +// another module, `process.argv[1]` differs from this file's URL and we +// leave invocation to the caller. +if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { + void run(); +} diff --git a/src/cli/parser.ts b/src/cli/parser.ts index 09c334ed..79f67a6f 100644 --- a/src/cli/parser.ts +++ b/src/cli/parser.ts @@ -86,6 +86,7 @@ export const KNOWN_COMMANDS = [ 'clean', 'grep', 'x402', + 'completion', ]; /** diff --git a/src/lib/auth/keychain.ts b/src/lib/auth/keychain.ts index e3c757c9..26b4b4a0 100644 --- a/src/lib/auth/keychain.ts +++ b/src/lib/auth/keychain.ts @@ -69,8 +69,8 @@ type EntryConstructor = new (service: string, account: string) => EntryLike; let keychainAvailable: boolean | null = null; // null = untested // Cache the import() result so the native addon is attempted only once per -// module instance. Using a promise (not top-level await) keeps this -// compatible with CJS environments such as Jest/ts-jest. +// module instance. Using a promise (not top-level await) avoids forcing every +// consumer (including test runners) into top-level-await territory. // Rejects if the addon or its shared-library dependency (libsecret) is absent. let _entryPromise: Promise | null = null; diff --git a/src/lib/completion-cache.ts b/src/lib/completion-cache.ts new file mode 100644 index 00000000..92b797da --- /dev/null +++ b/src/lib/completion-cache.ts @@ -0,0 +1,129 @@ +/** + * Per-session completion cache (~/.mcpc/completion/.json). + * + * Populated opportunistically when the user runs `tools-list`, `resources-list`, + * or `prompts-list` — those code paths already talked to the server, so we + * mirror the names to disk for the next TAB. Reads on the completion hot path + * never trigger network calls; if the cache is missing or stale, completion + * silently falls through to no suggestions. + * + * Why on disk rather than from the bridge: shell completion is a separate + * process from the bridge, so the in-memory `cachedTools` map is unreachable. + * A short JSON read (≪ 1 ms) keeps TAB snappy. + */ +import { readFile, writeFile, mkdir, unlink } from 'fs/promises'; +import { join } from 'path'; +import { atomicRename, fileExists, getMcpcHome } from './utils.js'; + +const COMPLETION_CACHE_DIRNAME = 'completion'; + +/** Kinds of items we cache per session. Matches MCP's three primitive lists. */ +export type CompletionKind = 'tools' | 'resources' | 'prompts'; + +/** On-disk cache file shape. Backwards-compatible: missing fields are treated as []. */ +interface CompletionCacheFile { + tools?: string[]; + resources?: string[]; + prompts?: string[]; + /** ISO timestamp of the last write — informational; not used to invalidate. */ + updatedAt?: string; +} + +function getCacheDir(): string { + return join(getMcpcHome(), COMPLETION_CACHE_DIRNAME); +} + +/** Resolve the on-disk path for a session's cache file. */ +function getCachePath(sessionName: string): string { + // Session names are constrained to `@name` shape (alphanumeric + dash, plus + // the leading `@`) by the same rules that gate sessions.json keys, so they + // are safe to use as filename stems without further escaping. + return join(getCacheDir(), `${sessionName}.json`); +} + +/** + * Remove a session's cache file. Best-effort: missing files are ignored. + * Intended for `mcpc clean sessions` so we don't leave dead cache files + * behind after a session is removed. + */ +export async function deleteCompletionCache(sessionName: string): Promise { + try { + await unlink(getCachePath(sessionName)); + } catch { + // Missing file or permission issue — nothing to do. + } +} + +/** + * Read cached names for a kind. Returns [] on any error — completion must + * never freeze the shell because of a malformed or missing cache file. + */ +export async function readCompletionCache( + sessionName: string, + kind: CompletionKind +): Promise { + try { + const content = await readFile(getCachePath(sessionName), 'utf-8'); + const parsed = JSON.parse(content) as CompletionCacheFile; + return parsed[kind] ?? []; + } catch { + return []; + } +} + +/** + * Merge new names into the cache for a kind. Other kinds in the file are + * preserved (read-then-write). Errors are swallowed so a broken cache never + * fails the user-facing list command. + * + * Targets that aren't `@session` names (e.g. raw URLs, `file:entry`) have + * no stable key to cache under and are silently skipped. + * + * Concurrency: the read-then-write window is not atomic, only the final + * file swap is (via `atomicRename`). Two list commands racing in different + * shells can stomp on each other's update for the *other* kind; worst case + * the user runs that list command again. Acceptable for a UX cache the + * user warms by hand. + */ +export async function writeCompletionCache( + sessionName: string, + kind: CompletionKind, + names: readonly string[] +): Promise { + if (!sessionName.startsWith('@')) return; + try { + const cachePath = getCachePath(sessionName); + await mkdir(getCacheDir(), { recursive: true }); + + let existing: CompletionCacheFile = {}; + if (await fileExists(cachePath)) { + try { + existing = JSON.parse(await readFile(cachePath, 'utf-8')) as CompletionCacheFile; + } catch { + // Corrupted cache → overwrite from scratch. + } + } + + const next: CompletionCacheFile = { + ...existing, + [kind]: [...names], + updatedAt: new Date().toISOString(), + }; + + const tempFile = join(getCacheDir(), `.${sessionName}-${Date.now()}-${process.pid}.tmp`); + await writeFile(tempFile, JSON.stringify(next, null, 2), 'utf-8'); + try { + await atomicRename(tempFile, cachePath); + } catch (renameError) { + // Clean up the orphaned temp file before propagating. + try { + await unlink(tempFile); + } catch { + // Temp file already moved or never created. + } + throw renameError; + } + } catch { + // Best-effort write; a missing cache just degrades to no suggestions. + } +} diff --git a/test/README.md b/test/README.md index 80dbfe85..ff11eec2 100644 --- a/test/README.md +++ b/test/README.md @@ -18,7 +18,7 @@ npm run test:coverage:e2e # Run e2e tests with coverage ## Unit tests -Unit tests use [Jest](https://jestjs.io/) with TypeScript and live in `test/unit/`. They test individual modules in isolation with mocked dependencies. +Unit tests use [Vitest](https://vitest.dev/) with TypeScript and live in `test/unit/`. They test individual modules in isolation with mocked dependencies. Vitest's API is Jest-compatible (`describe`/`it`/`expect`/`vi.fn`/`vi.mock`) and runs ESM natively, so there are no `transformIgnorePatterns` for pure-ESM dependencies like `chalk` and `ora`. ### Running unit tests @@ -36,7 +36,7 @@ Coverage reports are generated to `test/coverage/unit/`: Coverage thresholds are enforced at 70% for branches, functions, lines, and statements. -Unit test coverage measures the TypeScript source files directly via Jest's instrumentation. +Unit test coverage measures the TypeScript source files directly via Vitest's v8 coverage provider (`@vitest/coverage-v8`). ### Writing unit tests diff --git a/test/unit/cli/completion.test.ts b/test/unit/cli/completion.test.ts new file mode 100644 index 00000000..e2b4135d --- /dev/null +++ b/test/unit/cli/completion.test.ts @@ -0,0 +1,272 @@ +/** + * Tests for shell completion logic. + * + * The disk-touching helpers (loadSessions/loadAuthProfiles/completion cache) + * are mocked so the tests are pure and fast. + */ + +vi.mock('../../../src/lib/sessions.js', () => ({ + loadSessions: vi.fn(async () => ({ + sessions: { + '@apify': {}, + '@local': {}, + '@notion': {}, + }, + })), +})); + +vi.mock('../../../src/lib/auth/profiles.js', () => ({ + listAuthProfiles: vi.fn(async () => [ + { name: 'default', serverUrl: 'https://mcp.apify.com' }, + { name: 'work', serverUrl: 'https://mcp.apify.com' }, + { name: 'personal', serverUrl: 'https://other.example.com' }, + ]), +})); + +// Cache reads return empty by default so tool-name suggestions don't bleed +// across tests. Individual tests can override per-call via the mock. +vi.mock('../../../src/lib/completion-cache.js', () => ({ + readCompletionCache: vi.fn(async () => []), + writeCompletionCache: vi.fn(async () => undefined), + deleteCompletionCache: vi.fn(async () => undefined), +})); + +import { + analyzeContext, + suggestCompletions, + generateBashScript, + generateZshScript, + generateFishScript, + detectShell, + getInstallPath, + SUPPORTED_SHELLS, +} from '../../../src/cli/commands/completion.js'; + +describe('analyzeContext', () => { + it('returns empty context when no words have been typed', () => { + const ctx = analyzeContext([], ''); + expect(ctx.firstNonOption).toBeUndefined(); + expect(ctx.isSessionCommand).toBe(false); + expect(ctx.previousToken).toBeUndefined(); + }); + + it('detects a top-level command', () => { + const ctx = analyzeContext(['connect'], ''); + expect(ctx.topLevelCommand).toBe('connect'); + expect(ctx.isSessionCommand).toBe(false); + }); + + it('detects a session command and its subcommand', () => { + const ctx = analyzeContext(['@apify', 'tools-list'], ''); + expect(ctx.firstNonOption).toBe('@apify'); + expect(ctx.isSessionCommand).toBe(true); + expect(ctx.sessionSubcommand).toBe('tools-list'); + }); + + it('skips global flags when looking for the first non-option', () => { + const ctx = analyzeContext(['--json', '--verbose', '@apify'], ''); + expect(ctx.firstNonOption).toBe('@apify'); + expect(ctx.isSessionCommand).toBe(true); + }); + + it('skips flag values for known value-taking flags', () => { + const ctx = analyzeContext(['--profile', 'work', 'connect'], ''); + expect(ctx.topLevelCommand).toBe('connect'); + }); + + it('exposes previousToken for flag-value completion', () => { + const ctx = analyzeContext(['--profile'], ''); + expect(ctx.previousToken).toBe('--profile'); + }); +}); + +describe('suggestCompletions — top-level slot', () => { + it('suggests top-level commands and @session names with an empty partial', async () => { + const ctx = analyzeContext([], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['connect', 'login', 'completion'])); + expect(candidates).toEqual(expect.arrayContaining(['@apify', '@local', '@notion'])); + }); + + it('filters by prefix', async () => { + const ctx = analyzeContext([], 'co'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(['connect', 'completion']); + }); + + it('suggests only @session names when partial starts with @', async () => { + const ctx = analyzeContext([], '@'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['@apify', '@local', '@notion'])); + expect(candidates.every((c) => c.startsWith('@'))).toBe(true); + }); +}); + +describe('suggestCompletions — session subcommands', () => { + it('suggests known session subcommands after a @session', async () => { + const ctx = analyzeContext(['@apify'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual( + expect.arrayContaining(['tools-list', 'tools-call', 'resources-list', 'ping']) + ); + }); + + it('filters session subcommands by prefix', async () => { + const ctx = analyzeContext(['@apify'], 'tools'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(['tools-list', 'tools-get', 'tools-call']); + }); + + it('suggests log levels for logging-set-level', async () => { + const ctx = analyzeContext(['@apify', 'logging-set-level'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['debug', 'info', 'warning', 'error'])); + }); +}); + +describe('suggestCompletions — top-level command args', () => { + it('suggests clean resources after `clean`', async () => { + const ctx = analyzeContext(['clean'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(['sessions', 'profiles', 'logs', 'all']); + }); + + it('suggests @sessions after `connect`', async () => { + const ctx = analyzeContext(['connect'], '@'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['@apify', '@local', '@notion'])); + }); + + it('suggests shell names after `completion`', async () => { + const ctx = analyzeContext(['completion'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['bash', 'zsh', 'fish', 'install'])); + }); + + it('suggests known servers after `login`', async () => { + const ctx = analyzeContext(['login'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['mcp.apify.com', 'other.example.com'])); + }); +}); + +describe('suggestCompletions — flags', () => { + it('suggests global + command-specific flags when partial starts with --', async () => { + const ctx = analyzeContext(['connect'], '--'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual( + expect.arrayContaining(['--proxy', '--stdio', '--json', '--verbose']) + ); + }); + + it('suggests only global flags before the first command', async () => { + const ctx = analyzeContext([], '--'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['--json', '--verbose'])); + expect(candidates).not.toEqual(expect.arrayContaining(['--proxy', '--stdio'])); + }); + + it('deduplicates flags shared between global and command lists', async () => { + const ctx = analyzeContext(['login'], '--profile'); + const { candidates } = await suggestCompletions(ctx); + expect(candidates.filter((c) => c === '--profile').length).toBe(1); + }); + + // Drift guard: if `--header` / `--stdio` / `--proxy` get renamed in + // index.ts, this test fails — catches accidental removals. + it('picks up command-specific flags from Commander at runtime (no static list to drift)', async () => { + const connectFlags = (await suggestCompletions(analyzeContext(['connect'], '--'))).candidates; + expect(connectFlags).toEqual(expect.arrayContaining(['--header', '--stdio', '--proxy'])); + + const sessionToolsFlags = ( + await suggestCompletions(analyzeContext(['@apify', 'tools-list'], '--')) + ).candidates; + expect(sessionToolsFlags).toEqual(expect.arrayContaining(['--full', '--help'])); + }); + + it('marks value-taking flags so the previous-token check completes their values', async () => { + // --profile takes a value; partial after it should suggest profile names, + // not be treated as a free-form positional that gets command candidates. + const ctx = analyzeContext(['--profile'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['default', 'work'])); + expect(candidates).not.toEqual(expect.arrayContaining(['connect', 'login'])); + }); +}); + +describe('suggestCompletions — flag values', () => { + it('completes profile names after --profile', async () => { + const ctx = analyzeContext(['--profile'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual(expect.arrayContaining(['default', 'work', 'personal'])); + }); + + it('returns no candidates for unknown flag values (so shell falls back to files)', async () => { + const ctx = analyzeContext(['-H'], ''); + const { candidates } = await suggestCompletions(ctx); + expect(candidates).toEqual([]); + }); +}); + +describe('generated shell scripts', () => { + it('bash script contains the registration line', () => { + const script = generateBashScript(); + expect(script).toMatch(/complete .* -F _mcpc mcpc/); + expect(script).toMatch(/mcpc __complete/); + }); + + it('zsh script declares compdef', () => { + const script = generateZshScript(); + expect(script).toMatch(/^#compdef mcpc/); + expect(script).toMatch(/compdef _mcpc mcpc/); + }); + + it('fish script uses complete -c mcpc', () => { + const script = generateFishScript(); + expect(script).toMatch(/complete -c mcpc/); + expect(script).toMatch(/mcpc __complete/); + }); +}); + +describe('detectShell', () => { + const originalShell = process.env.SHELL; + afterEach(() => { + if (originalShell === undefined) delete process.env.SHELL; + else process.env.SHELL = originalShell; + }); + + it('detects bash', () => { + process.env.SHELL = '/bin/bash'; + expect(detectShell()).toBe('bash'); + }); + + it('detects zsh', () => { + process.env.SHELL = '/usr/local/bin/zsh'; + expect(detectShell()).toBe('zsh'); + }); + + it('detects fish', () => { + process.env.SHELL = '/opt/homebrew/bin/fish'; + expect(detectShell()).toBe('fish'); + }); + + it('returns undefined for unsupported shells', () => { + process.env.SHELL = '/usr/bin/tcsh'; + expect(detectShell()).toBeUndefined(); + }); + + it('returns undefined when $SHELL is unset', () => { + delete process.env.SHELL; + expect(detectShell()).toBeUndefined(); + }); +}); + +describe('getInstallPath', () => { + for (const shell of SUPPORTED_SHELLS) { + it(`returns an absolute path for ${shell}`, () => { + const path = getInstallPath(shell); + expect(path.startsWith('/')).toBe(true); + expect(path.length).toBeGreaterThan(5); + }); + } +}); diff --git a/test/unit/cli/grep.test.ts b/test/unit/cli/grep.test.ts index 61be8856..8c1a09ce 100644 --- a/test/unit/cli/grep.test.ts +++ b/test/unit/cli/grep.test.ts @@ -2,64 +2,60 @@ * Tests for grep command formatting utilities */ -// Mock chalk to return plain strings (required because Jest can't handle chalk's ESM imports) -const chalkIdentity = (s: string) => s; -const chalkBold = Object.assign(chalkIdentity, { underline: chalkIdentity }); -jest.mock('chalk', () => ({ - default: { - cyan: chalkIdentity, - yellow: chalkIdentity, - red: chalkIdentity, - dim: chalkIdentity, - gray: chalkIdentity, - bold: chalkBold, - green: chalkIdentity, - greenBright: chalkIdentity, - blue: chalkIdentity, - magenta: chalkIdentity, - white: chalkIdentity, - }, - cyan: chalkIdentity, - yellow: chalkIdentity, - red: chalkIdentity, - dim: chalkIdentity, - gray: chalkIdentity, - bold: chalkBold, - green: chalkIdentity, - greenBright: chalkIdentity, - blue: chalkIdentity, - magenta: chalkIdentity, - white: chalkIdentity, +// Mock chalk to return plain strings. `vi.mock` is hoisted above local +// `const` declarations, so identity helpers must come from `vi.hoisted`. +const { chalkApi } = vi.hoisted(() => { + const chalkIdentity = (s: string) => s; + const chalkBold = Object.assign(chalkIdentity, { underline: chalkIdentity }); + return { + chalkApi: { + cyan: chalkIdentity, + yellow: chalkIdentity, + red: chalkIdentity, + dim: chalkIdentity, + gray: chalkIdentity, + bold: chalkBold, + green: chalkIdentity, + greenBright: chalkIdentity, + blue: chalkIdentity, + magenta: chalkIdentity, + white: chalkIdentity, + }, + }; +}); +vi.mock('chalk', () => ({ + default: chalkApi, + ...chalkApi, })); // Mock modules that grep.ts imports transitively -jest.mock('../../../src/lib/errors.js', () => ({ +vi.mock('../../../src/lib/errors.js', () => ({ ClientError: class ClientError extends Error {}, })); -jest.mock('../../../src/lib/utils.js', () => ({ - isProcessAlive: jest.fn(), +vi.mock('../../../src/lib/utils.js', () => ({ + isProcessAlive: vi.fn(), })); -jest.mock('../../../src/lib/sessions.js', () => ({ - consolidateSessions: jest.fn(), - getSession: jest.fn(), +vi.mock('../../../src/lib/sessions.js', () => ({ + consolidateSessions: vi.fn(), + getSession: vi.fn(), })); -jest.mock('../../../src/lib/bridge-manager.js', () => ({ - reconnectCrashedSessions: jest.fn(), +vi.mock('../../../src/lib/bridge-manager.js', () => ({ + reconnectCrashedSessions: vi.fn(), })); -jest.mock('../../../src/lib/session-client.js', () => ({ - withSessionClient: jest.fn(), +vi.mock('../../../src/lib/session-client.js', () => ({ + withSessionClient: vi.fn(), })); -jest.mock('../../../src/cli/helpers.js', () => ({ - withMcpClient: jest.fn(), +vi.mock('../../../src/cli/helpers.js', () => ({ + withMcpClient: vi.fn(), })); -jest.mock('../../../src/cli/output.js', () => ({ - formatJson: jest.fn(), - formatToolLine: jest.fn(), +vi.mock('../../../src/cli/output.js', () => ({ + formatJson: vi.fn(), + formatToolLine: vi.fn(), inBackticks: (s: string) => `\`${s}\``, })); -jest.mock('../../../src/cli/commands/sessions.js', () => ({ - getBridgeStatus: jest.fn(), - formatBridgeStatus: jest.fn(), +vi.mock('../../../src/cli/commands/sessions.js', () => ({ + getBridgeStatus: vi.fn(), + formatBridgeStatus: vi.fn(), })); import { extractInstructionsSnippet } from '../../../src/cli/commands/grep.js'; diff --git a/test/unit/cli/output.test.ts b/test/unit/cli/output.test.ts index d8d1863d..85a56dad 100644 --- a/test/unit/cli/output.test.ts +++ b/test/unit/cli/output.test.ts @@ -2,10 +2,11 @@ * Tests for CLI output formatting */ +import type { MockInstance } from 'vitest'; import { extractAllTextContent } from '../../../src/cli/tool-result.js'; // Mock chalk to return plain strings (required because Jest can't handle chalk's ESM imports) -jest.mock('chalk', () => { +vi.mock('chalk', () => { const identity = (s: string): string => s; const hex = (): ((s: string) => string) => identity; const palette = { @@ -26,8 +27,8 @@ jest.mock('chalk', () => { }); // Mock sessions module before importing output -jest.mock('../../../src/lib/sessions.js', () => ({ - getSession: jest.fn().mockResolvedValue(null), +vi.mock('../../../src/lib/sessions.js', () => ({ + getSession: vi.fn().mockResolvedValue(null), })); // Import after mock is set up @@ -1676,10 +1677,10 @@ describe('formatSessionLine', () => { }); describe('logTarget', () => { - let consoleSpy: jest.SpyInstance; + let consoleSpy: MockInstance; beforeEach(() => { - consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); }); afterEach(() => { diff --git a/test/unit/core/factory.test.ts b/test/unit/core/factory.test.ts index 29a8f1d0..47424572 100644 --- a/test/unit/core/factory.test.ts +++ b/test/unit/core/factory.test.ts @@ -6,11 +6,11 @@ import { McpClient } from '../../../src/core/mcp-client.js'; import { createMcpClient } from '../../../src/core/factory.js'; // Mock the transports -jest.mock('../../../src/core/transports', () => ({ - createTransportFromConfig: jest.fn().mockReturnValue({ - start: jest.fn().mockResolvedValue(undefined), - send: jest.fn().mockResolvedValue(undefined), - close: jest.fn().mockResolvedValue(undefined), +vi.mock('../../../src/core/transports', () => ({ + createTransportFromConfig: vi.fn().mockReturnValue({ + start: vi.fn().mockResolvedValue(undefined), + send: vi.fn().mockResolvedValue(undefined), + close: vi.fn().mockResolvedValue(undefined), onclose: undefined, onerror: undefined, onmessage: undefined, @@ -18,16 +18,18 @@ jest.mock('../../../src/core/transports', () => ({ })); // Mock the SDK Client -jest.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ - Client: jest.fn().mockImplementation(() => ({ - connect: jest.fn().mockResolvedValue(undefined), - close: jest.fn().mockResolvedValue(undefined), - getServerVersion: jest.fn().mockReturnValue({ name: 'test-server', version: '1.0.0' }), - getServerCapabilities: jest.fn().mockReturnValue({}), - getInstructions: jest.fn().mockReturnValue(undefined), - ping: jest.fn().mockResolvedValue(undefined), - onerror: undefined, - })), +vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ + Client: vi.fn(function () { + return { + connect: vi.fn().mockResolvedValue(undefined), + close: vi.fn().mockResolvedValue(undefined), + getServerVersion: vi.fn().mockReturnValue({ name: 'test-server', version: '1.0.0' }), + getServerCapabilities: vi.fn().mockReturnValue({}), + getInstructions: vi.fn().mockReturnValue(undefined), + ping: vi.fn().mockResolvedValue(undefined), + onerror: undefined, + }; + }), })); describe('createMcpClient', () => { diff --git a/test/unit/core/transports.test.ts b/test/unit/core/transports.test.ts index c57e3dfd..79adf6ff 100644 --- a/test/unit/core/transports.test.ts +++ b/test/unit/core/transports.test.ts @@ -2,27 +2,32 @@ * Unit tests for MCP transports */ +import type { Mock } from 'vitest'; import { createTransportFromConfig } from '../../../src/core/transports.js'; import { StreamableHTTPClientTransport } from '../../../src/core/transports.js'; import { ClientError } from '../../../src/lib/errors.js'; import { proxyFetch } from '../../../src/lib/proxy.js'; // Mock the SDK transports -jest.mock('@modelcontextprotocol/sdk/client/stdio.js', () => ({ - StdioClientTransport: jest.fn().mockImplementation(() => ({ - start: jest.fn().mockResolvedValue(undefined), - send: jest.fn().mockResolvedValue(undefined), - close: jest.fn().mockResolvedValue(undefined), - })), - getDefaultEnvironment: jest.fn().mockReturnValue({}), +vi.mock('@modelcontextprotocol/sdk/client/stdio.js', () => ({ + StdioClientTransport: vi.fn(function () { + return { + start: vi.fn().mockResolvedValue(undefined), + send: vi.fn().mockResolvedValue(undefined), + close: vi.fn().mockResolvedValue(undefined), + }; + }), + getDefaultEnvironment: vi.fn().mockReturnValue({}), })); -jest.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({ - StreamableHTTPClientTransport: jest.fn().mockImplementation(() => ({ - start: jest.fn().mockResolvedValue(undefined), - send: jest.fn().mockResolvedValue(undefined), - close: jest.fn().mockResolvedValue(undefined), - })), +vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({ + StreamableHTTPClientTransport: vi.fn(function () { + return { + start: vi.fn().mockResolvedValue(undefined), + send: vi.fn().mockResolvedValue(undefined), + close: vi.fn().mockResolvedValue(undefined), + }; + }), StreamableHTTPError: class StreamableHTTPError extends Error {}, })); @@ -72,7 +77,7 @@ describe('createTransportFromConfig', () => { }); it('should inject proxyFetch into HTTP transport when no custom fetch is provided', () => { - const mock = StreamableHTTPClientTransport as jest.Mock; + const mock = StreamableHTTPClientTransport as Mock; mock.mockClear(); createTransportFromConfig({ url: 'https://mcp.example.com', @@ -84,9 +89,9 @@ describe('createTransportFromConfig', () => { }); it('should preserve custom fetch when provided (e.g. x402 middleware)', () => { - const mock = StreamableHTTPClientTransport as jest.Mock; + const mock = StreamableHTTPClientTransport as Mock; mock.mockClear(); - const customFetch = jest.fn(); + const customFetch = vi.fn(); createTransportFromConfig( { url: 'https://mcp.example.com' }, { customFetch: customFetch as any } diff --git a/test/unit/lib/auth/keychain.test.ts b/test/unit/lib/auth/keychain.test.ts index 1a540203..f7b59a36 100644 --- a/test/unit/lib/auth/keychain.test.ts +++ b/test/unit/lib/auth/keychain.test.ts @@ -31,30 +31,32 @@ const keychainStore = new Map(); /** When true, all keychain operations throw to simulate a missing keyring daemon */ let keychainThrows = false; -jest.mock('@napi-rs/keyring', () => ({ - Entry: jest.fn().mockImplementation((_service: string, account: string) => ({ - setPassword(value: string) { - if (keychainThrows) throw new Error('No keyring daemon'); - keychainStore.set(account, value); - }, - getPassword(): string | null { - if (keychainThrows) throw new Error('No keyring daemon'); - return keychainStore.get(account) ?? null; - }, - deletePassword(): boolean { - if (keychainThrows) throw new Error('No keyring daemon'); - const had = keychainStore.has(account); - keychainStore.delete(account); - return had; - }, - })), +vi.mock('@napi-rs/keyring', () => ({ + Entry: vi.fn(function (_service: string, account: string) { + return { + setPassword(value: string) { + if (keychainThrows) throw new Error('No keyring daemon'); + keychainStore.set(account, value); + }, + getPassword(): string | null { + if (keychainThrows) throw new Error('No keyring daemon'); + return keychainStore.get(account) ?? null; + }, + deletePassword(): boolean { + if (keychainThrows) throw new Error('No keyring daemon'); + const had = keychainStore.has(account); + keychainStore.delete(account); + return had; + }, + }; + }), })); // --------------------------------------------------------------------------- -// Mock chalk — its ESM subpath imports (#ansi-styles) break under Jest/ts-jest +// Mock chalk to keep the test runtime untouched by ANSI codes. // --------------------------------------------------------------------------- -jest.mock('chalk', () => ({ +vi.mock('chalk', () => ({ __esModule: true, default: { red: (s: string) => s }, })); @@ -88,7 +90,7 @@ beforeEach(async () => { // --------------------------------------------------------------------------- async function loadKeychain() { - jest.resetModules(); + vi.resetModules(); return import('../../../../src/lib/auth/keychain.js'); } diff --git a/test/unit/lib/auth/oauth-utils.test.ts b/test/unit/lib/auth/oauth-utils.test.ts index ef8fe5d8..5a5b41ba 100644 --- a/test/unit/lib/auth/oauth-utils.test.ts +++ b/test/unit/lib/auth/oauth-utils.test.ts @@ -2,6 +2,7 @@ * Unit tests for OAuth utility functions */ +import type { MockInstance } from 'vitest'; import { readFileSync } from 'fs'; import { resolve } from 'path'; import { @@ -19,10 +20,10 @@ function mockResponse(body: object | null, ok = true): Response { } describe('discoverTokenEndpoint', () => { - let fetchSpy: jest.SpyInstance; + let fetchSpy: MockInstance; beforeEach(() => { - fetchSpy = jest.spyOn(proxyModule, 'proxyFetch'); + fetchSpy = vi.spyOn(proxyModule, 'proxyFetch'); }); afterEach(() => { diff --git a/test/unit/lib/logger.test.ts b/test/unit/lib/logger.test.ts index 7e55f084..f8fbd49b 100644 --- a/test/unit/lib/logger.test.ts +++ b/test/unit/lib/logger.test.ts @@ -2,6 +2,7 @@ * Unit tests for logger */ +import type { MockInstance } from 'vitest'; import { setVerbose, getVerbose, @@ -39,12 +40,12 @@ describe('Log level', () => { }); describe('Logging functions', () => { - let consoleLogSpy: jest.SpyInstance; - let consoleErrorSpy: jest.SpyInstance; + let consoleLogSpy: MockInstance; + let consoleErrorSpy: MockInstance; beforeEach(() => { - consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(); + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(); setVerbose(false); setLogLevel('debug'); }); @@ -99,12 +100,12 @@ describe('Logging functions', () => { }); describe('Logger class', () => { - let consoleLogSpy: jest.SpyInstance; - let consoleErrorSpy: jest.SpyInstance; + let consoleLogSpy: MockInstance; + let consoleErrorSpy: MockInstance; beforeEach(() => { - consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(); + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(); setVerbose(false); setLogLevel('debug'); }); diff --git a/tsconfig.test.json b/tsconfig.test.json index ab5c3166..d4013b99 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "ES2022", - "moduleResolution": "bundler" + "moduleResolution": "bundler", + "types": ["vitest/globals", "node"] }, "include": [ "test/**/*", diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..d860dff2 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vitest/config'; + +// Coverage thresholds match the previous Jest setup (70% across the board). +// See test/README.md for the test layout. +export default defineConfig({ + test: { + include: ['test/unit/**/*.test.ts', 'test/unit/**/*.spec.ts'], + environment: 'node', + // `globals: true` keeps the existing test bodies (`describe`/`it`/`expect`) + // working without changing every file's imports. Jest-compatible API. + globals: true, + coverage: { + provider: 'v8', + reportsDirectory: 'test/coverage/unit', + reporter: ['text', 'lcov', 'html', 'json'], + include: ['src/**/*.ts'], + exclude: ['src/**/*.d.ts', 'src/**/index.ts'], + thresholds: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + }, + }, +});