From 4b9dcdb5d2de8d23e0e985b609dc9eaf78b60d57 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 09:48:40 +0000 Subject: [PATCH] Security hardening and dead-code pruning for public release Security fixes: - hygiene.cleanup now defaults to dry-run; deletion requires explicit dryRun=false (only the modal-confirmed Delete File flow opts in), and the command is no longer exposed in the palette or tree context menu - deleteFile operates on the directory entry itself (lstat + parent guard), so deleting a symlink no longer removes its target; directory deletion is refused - scan/analytics handlers fail with WORKSPACE_NOT_FOUND instead of falling back to process.cwd(); background scan timer no longer binds to cwd when no workspace folder is open - removed unguarded resolveWorkspacePath duplicate (infrastructure/ workspace.ts, dead module shadowing the security path-guard) - security.logging.sensitive is now enforced: run-log error messages pass through the redaction sanitizer (injected into CommandRouter) - LM egress policy check now runs before payload serialization - webview CSPs tightened to nonce-only script-src, img-src https: removed, explicit connect-src 'none' everywhere; session-briefing webview added to the pinned security test - escaped remaining payload-derived webview sinks (risk/category enums, legend labels) and coerced numeric fields; esc() helpers aligned Correctness/efficiency: - large-file scan uses fs.stat metadata instead of reading every file into memory (also fixes >512MB files being silently skipped) - ignore patterns now match workspace-relative paths, fixing silently ignored root-anchored .gitignore/.meridianignore entries - workspace walker prunes .git/node_modules during traversal and pre-compiles glob regexes; Set-based dedupe in scan buckets - impact-analysis cache key includes functionName (stale-result bug when analyzing two functions in the same file) - dead-code analyzer workspace containment uses a separator-terminated prefix; fixed duplicate condition in impact-visitor path heuristic - prune categories are validated against the known enum - fixed pruneCandiates -> pruneCandidates typo in the report schema Dead code removed (~1,200 lines): - unused GitProvider methods (diff/getCommitRange/getMergeBase/stage/ reset/getChanges/getDiff/getUncommittedDiff) - also closes a latent git argument-injection surface on unvalidated refs - speculative telemetry (workflow/agent/cache events, InMemoryTelemetry sink), permission/rate-limit middleware, streamProse, CHAT/WORKFLOW/ AGENT/SKILL error-code namespaces, GIT_DEFAULTS/CHAT_SETTINGS/ LOG_SETTINGS constants, retry-config scaffolding Tooling: - vitest 1.x -> 4.x (clears 3 critical / 2 high dev-dependency audit findings; prod deps were already clean) - added SECURITY.md documenting network posture, telemetry, and filesystem boundaries https://claude.ai/code/session_01NTdZdJhedr99fwxZrpdGka --- SECURITY.md | 55 + docs/ARCHITECTURE.md | 2 +- docs/FEATURES.md | 2 +- package-lock.json | 2351 ++++++++--------- package.json | 27 +- src/constants.ts | 92 - src/cross-cutting/middleware.ts | 70 - src/domains/git/analytics-ui/index.html | 2 +- src/domains/git/analytics-ui/script.js | 23 +- .../git/session-briefing-ui/index.html | 2 +- src/domains/git/session-briefing-ui/script.js | 10 +- src/domains/hygiene/analytics-handler.ts | 11 +- src/domains/hygiene/analytics-service.ts | 4 +- src/domains/hygiene/analytics-types.ts | 2 +- src/domains/hygiene/analytics-ui/index.html | 2 +- src/domains/hygiene/analytics-ui/script.js | 12 +- src/domains/hygiene/cleanup-handler.ts | 6 +- src/domains/hygiene/dead-code-analyzer.ts | 9 +- .../hygiene/impact-analysis-handler.ts | 2 +- src/domains/hygiene/impact-visitor.ts | 9 +- src/domains/hygiene/prune-config.ts | 9 +- src/domains/hygiene/scan-handler.ts | 48 +- src/infrastructure/error-codes.ts | 376 +-- src/infrastructure/git-provider.ts | 85 - src/infrastructure/prose-generator.ts | 52 +- src/infrastructure/telemetry.ts | 444 +--- src/infrastructure/workspace-provider.ts | 74 +- src/infrastructure/workspace.ts | 71 - src/main.ts | 12 +- src/presentation/command-registry.ts | 4 +- src/presentation/specialized-commands.ts | 8 +- src/router.ts | 16 +- src/types.ts | 17 +- tests/cleanupHandler.test.ts | 23 +- tests/commandRouter.test.ts | 22 + tests/fixtures.ts | 62 +- tests/hygieneAnalyticsHandler.test.ts | 2 +- tests/impactAnalysisHandler.test.ts | 32 +- tests/webview-security.test.ts | 31 +- tests/workspace.test.ts | 51 - 40 files changed, 1497 insertions(+), 2635 deletions(-) create mode 100644 SECURITY.md delete mode 100644 src/infrastructure/workspace.ts delete mode 100644 tests/workspace.test.ts diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e5fa08f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,55 @@ +# Security + +## Posture + +Meridian is a local-first VS Code extension. It computes everything from the +workspace on disk and the local git history. There is no Meridian backend, no +account, and no analytics service. + +### Network access + +The extension itself opens no network connections. The only operations that can +reach the network are: + +- **Git network operations** (`fetch` / `pull`) — gated by + `meridian.security.gitNetwork.mode` (`allow` / `prompt` / `deny`, default + `prompt`) and an optional remote-host allowlist + (`meridian.security.gitNetwork.allowedHosts`). Both run through the single + policy chokepoint in `src/security/operation-policy.ts`. +- **Optional language-model prose** — sent through the VS Code Language Model + API (e.g. GitHub Copilot) only when a model is installed and enabled. Sends + are gated by `meridian.security.lmEgress.mode` (default `prompt`) and every + payload passes through `sanitizeLmPayload()` (token/secret redaction + + truncation) in `src/security/lm-policy.ts`. All reports degrade to their + deterministic content when no model is available or egress is denied. + +Webviews enforce a `default-src 'none'` Content-Security-Policy with +nonce-only scripts and `connect-src 'none'`; chart.js is vendored at build +time, never loaded from a CDN. + +### Telemetry + +None leaves the machine. The telemetry tracker writes command lifecycle events +to an in-memory/console sink only (`src/infrastructure/telemetry.ts`). The +run-log is an append-only JSONL file inside the workspace. + +### Filesystem boundaries + +- Every read/delete and every webview-originated path is resolved through + `resolveWorkspacePath()` (`src/security/path-guard.ts`), which realpaths and + rejects anything outside the workspace root (including symlink escapes and + `../` traversal). +- File deletion is only reachable through a modal confirmation + (`Hygiene: Delete File`); the underlying `hygiene.cleanup` command defaults + to dry-run and requires an explicit opt-in to remove files. Deletion + operates on the directory entry itself, so deleting a symlink never removes + its target, and directories are refused. +- Logging redacts token-shaped content by default + (`meridian.security.logging.sensitive`, default `redact`). + +## Reporting a vulnerability + +Please open a [GitHub security advisory](https://github.com/scscodes/meridian/security/advisories/new) +or a private report rather than a public issue. Include reproduction steps and +the extension/VS Code versions. Reports are typically acknowledged within a +week. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c4f2f23..977c14c 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -39,7 +39,7 @@ VS Code (commands · views · webviews · chat · LM tools) cleanup, collections). Each exposes handlers (thin) over services (logic) and its own `types.ts`. Webview assets live beside their domain (`analytics-ui/`, `session-briefing-ui/`). - **`src/infrastructure/`** — capabilities domains depend on: `git-provider.ts` (the only place that - shells git), `workspace.ts` / `workspace-provider.ts`, `settings.ts` (the single `readSetting` + shells git), `workspace-provider.ts`, `settings.ts` (the single `readSetting` chokepoint, ADR 013), `run-log.ts` (append-only JSONL event log, ADR 009), `telemetry.ts`, `cache.ts`, `prose-generator.ts` (injected LM prose, ADR 004), `webview-provider.ts`. - **`src/security/`** — boundary enforcement: `path-guard.ts` (no escaping the workspace), diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 04a43bb..7e46829 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -44,7 +44,7 @@ Scan the workspace for cleanup candidates: Respects `.gitignore` and `.meridian/.meridianignore` patterns. Returns a categorized list with file paths, sizes, ages, and reasons. ### **hygiene.cleanup** -Delete specified files with optional dry-run mode. Batch removal of candidates surfaced by `hygiene.scan`. Requires user confirmation before deletion. +Delete specified files. Batch removal of candidates surfaced by `hygiene.scan`. Defaults to dry-run: deletion happens only with an explicit `dryRun: false`, and the user-facing path (**Delete File**) requires a modal confirmation first. Not exposed in the command palette. ### **hygiene.impactAnalysis** Trace the blast radius of a file or function change by analyzing imports, call sites, and test coverage via the TypeScript Compiler API. Returns importers, call sites, dependent file count, test coverage, and an optional prose summary (when a language model is available). Helps assess refactor/removal risk. diff --git a/package-lock.json b/package-lock.json index 99bed3b..fbd0a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "meridian", - "version": "2.4.0", + "version": "2.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "meridian", - "version": "2.4.0", + "version": "2.12.0", "license": "MIT", "dependencies": { "chart.js": "^4.5.1", @@ -22,12 +22,12 @@ "@types/micromatch": "^4.0.10", "@types/node": "^22.19.0", "@types/vscode": "^1.80.0", - "@vitest/coverage-v8": "^1.0.4", - "@vitest/ui": "^1.0.4", + "@vitest/coverage-v8": "^4.1.8", + "@vitest/ui": "^4.1.8", "esbuild": "^0.25.12", "semantic-release": "^25.0.3", "semantic-release-vsce": "^6.1.3", - "vitest": "^1.0.4" + "vitest": "^4.1.8" }, "engines": { "node": ">=22.22.0", @@ -83,20 +83,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@azu/format-text": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", @@ -269,20 +255,29 @@ } }, "node_modules/@azure/msal-node": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.3.tgz", - "integrity": "sha512-LqT8mRZpEils9zGR9eW+Ljqifh2aMA99UF/X0jxIKDYZeHr6onlHwhVP4xHCeLhh55BI63JCbdf1iWJbMh1mPw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.2.3.tgz", + "integrity": "sha512-YYX4TchEVddVBiybKvKhV9QO/q22jgewP+BVxKG7Uh115voPcviGlypbKERDsqQdAiSTJrwi80gcWFjYKdo8+Q==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "16.5.0", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" + "@azure/msal-common": "16.7.0", + "jsonwebtoken": "^9.0.0" }, "engines": { "node": ">=20" } }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.7.0.tgz", + "integrity": "sha512-Jb8Y7pX6KM42SIT7KWP6YbY3+vLbwB5b5m+tpiiOzMU1QeyelQzs9lO8jv1e7/Uj9r7tg7VjPvW4T0KB1jF3UQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -299,9 +294,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -309,9 +304,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -319,13 +314,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -335,25 +330,28 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/@colors/colors": { "version": "1.5.0", @@ -852,40 +850,6 @@ "node": ">=18" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1396,6 +1360,16 @@ "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -1448,24 +1422,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -1474,12 +1434,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -1488,12 +1451,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -1502,26 +1468,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -1530,26 +1485,15 @@ "optional": true, "os": [ "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -1558,26 +1502,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], @@ -1586,54 +1519,32 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", - "cpu": [ - "loong64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ - "loong64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", - "cpu": [ - "ppc64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], @@ -1642,40 +1553,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", - "cpu": [ - "riscv64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], @@ -1684,12 +1570,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -1698,12 +1587,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], @@ -1712,26 +1604,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -1740,54 +1621,70 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ - "arm64" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", - "cpu": [ - "ia32" - ], + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -1796,7 +1693,17 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", @@ -2489,13 +2396,6 @@ "url": "https://ko-fi.com/dangreen" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -2522,6 +2422,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@textlint/ast-node-types": { "version": "15.5.4", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.4.tgz", @@ -2657,10 +2564,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -2721,140 +2646,139 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.1.tgz", - "integrity": "sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", + "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.8", + "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0" + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.1" + "@vitest/browser": "4.1.8", + "vitest": "4.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, "node_modules/@vitest/expect": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", - "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", - "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "node_modules/@vitest/pretty-format": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "node_modules/@vitest/runner": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", - "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", - "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^2.2.0" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/ui": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.1.tgz", - "integrity": "sha512-xa57bCPGuzEFqGjPs3vVLyqareG8DX0uMkr5U/v5vLv5/ZUrBrPL7gzxzTJedEyZxFMfsozwTIbbYfEQVo3kgg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.8.tgz", + "integrity": "sha512-RUS2ZU2TsduVrI+9c12uTNaKrNUTsm6yFt3fueEUB9iKvyC2UP83F+sqIz00HQIah4UOL1TMoDAki8K0NjGvsA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.1", - "fast-glob": "^3.3.2", - "fflate": "^0.8.1", - "flatted": "^3.2.9", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "sirv": "^2.0.4" + "@vitest/utils": "4.1.8", + "fflate": "^0.8.2", + "flatted": "^3.4.2", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.1" + "vitest": "4.1.8" } }, "node_modules/@vitest/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -3079,9 +3003,9 @@ } }, "node_modules/@vscode/vsce/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -3195,32 +3119,6 @@ "dev": true, "license": "MIT" }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", - "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -3291,19 +3189,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -3333,15 +3218,34 @@ "license": "MIT" }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.3.tgz", + "integrity": "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" } }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3554,16 +3458,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -3606,22 +3500,13 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/chalk": { @@ -3659,19 +3544,6 @@ "pnpm": ">=8" } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, "node_modules/cheerio": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", @@ -4058,13 +3930,6 @@ "dev": true, "license": "MIT" }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -4149,6 +4014,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4311,19 +4183,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -4429,21 +4288,10 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4844,6 +4692,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -4996,6 +4851,16 @@ "node": ">=6" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-content-type-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", @@ -5038,9 +4903,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -5065,9 +4930,9 @@ } }, "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "dev": true, "license": "MIT" }, @@ -5251,13 +5116,6 @@ "node": ">=14.14" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -5319,16 +5177,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -5446,9 +5294,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -5879,18 +5727,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6153,21 +5989,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", @@ -6376,90 +6197,334 @@ "node": ">=6" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "uc.micro": "^2.0.0" + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=14" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/antfu" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" }, "node_modules/lodash-es": { "version": "4.18.1", @@ -6545,16 +6610,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -6573,15 +6628,15 @@ } }, "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" } }, "node_modules/make-asynchronous": { @@ -6835,26 +6890,6 @@ "license": "MIT", "optional": true }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" - } - }, - "node_modules/mlly/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -6892,9 +6927,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -8978,12 +9013,27 @@ "node": ">= 0.4" } }, + "node_modules/obug": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz", + "integrity": "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", + "optional": true, "dependencies": { "wrappy": "1" } @@ -9327,16 +9377,6 @@ "node": ">=4" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -9385,22 +9425,12 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -9451,25 +9481,6 @@ "node": ">=4" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/pkg-types/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -9481,9 +9492,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -9501,7 +9512,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -9538,21 +9549,6 @@ "node": ">=10" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/pretty-ms": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", @@ -9606,9 +9602,9 @@ } }, "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9671,13 +9667,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -9799,49 +9788,38 @@ "node": ">=0.10.0" } }, - "node_modules/rollup": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", - "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.2", - "@rollup/rollup-android-arm64": "4.60.2", - "@rollup/rollup-darwin-arm64": "4.60.2", - "@rollup/rollup-darwin-x64": "4.60.2", - "@rollup/rollup-freebsd-arm64": "4.60.2", - "@rollup/rollup-freebsd-x64": "4.60.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", - "@rollup/rollup-linux-arm-musleabihf": "4.60.2", - "@rollup/rollup-linux-arm64-gnu": "4.60.2", - "@rollup/rollup-linux-arm64-musl": "4.60.2", - "@rollup/rollup-linux-loong64-gnu": "4.60.2", - "@rollup/rollup-linux-loong64-musl": "4.60.2", - "@rollup/rollup-linux-ppc64-gnu": "4.60.2", - "@rollup/rollup-linux-ppc64-musl": "4.60.2", - "@rollup/rollup-linux-riscv64-gnu": "4.60.2", - "@rollup/rollup-linux-riscv64-musl": "4.60.2", - "@rollup/rollup-linux-s390x-gnu": "4.60.2", - "@rollup/rollup-linux-x64-gnu": "4.60.2", - "@rollup/rollup-linux-x64-musl": "4.60.2", - "@rollup/rollup-openbsd-x64": "4.60.2", - "@rollup/rollup-openharmony-arm64": "4.60.2", - "@rollup/rollup-win32-arm64-msvc": "4.60.2", - "@rollup/rollup-win32-ia32-msvc": "4.60.2", - "@rollup/rollup-win32-x64-gnu": "4.60.2", - "@rollup/rollup-win32-x64-msvc": "4.60.2", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/run-applescript": { @@ -10641,9 +10619,9 @@ } }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, "license": "MIT", "dependencies": { @@ -10652,7 +10630,7 @@ "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/skin-tone": { @@ -10796,9 +10774,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -10907,26 +10885,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/structured-source": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", @@ -11158,43 +11116,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "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", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11275,10 +11196,20 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -11323,20 +11254,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -11344,9 +11265,9 @@ } }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { @@ -11419,16 +11340,6 @@ "node": "*" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -11474,13 +11385,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "dev": true, - "license": "MIT" - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -11592,16 +11496,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -11626,93 +11520,100 @@ "url": "https://bevry.me/fund" } }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "node_modules/vitest": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" }, "bin": { - "vite": "bin/vite.js" + "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "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 }, - "less": { + "@vitest/browser-playwright": { "optional": true }, - "lightningcss": { + "@vitest/browser-preview": { "optional": true }, - "sass": { + "@vitest/browser-webdriverio": { "optional": true }, - "sass-embedded": { + "@vitest/coverage-istanbul": { "optional": true }, - "stylus": { + "@vitest/coverage-v8": { "optional": true }, - "sugarss": { + "@vitest/ui": { "optional": true }, - "terser": { + "happy-dom": { + "optional": true + }, + "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, - "node_modules/vite-node": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", - "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], @@ -11722,14 +11623,15 @@ "os": [ "aix" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], @@ -11739,14 +11641,15 @@ "os": [ "android" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], @@ -11756,14 +11659,15 @@ "os": [ "android" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], @@ -11773,14 +11677,15 @@ "os": [ "android" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], @@ -11790,14 +11695,15 @@ "os": [ "darwin" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], @@ -11807,14 +11713,15 @@ "os": [ "darwin" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], @@ -11824,14 +11731,15 @@ "os": [ "freebsd" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], @@ -11841,14 +11749,15 @@ "os": [ "freebsd" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], @@ -11858,14 +11767,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], @@ -11875,14 +11785,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], @@ -11892,14 +11803,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], @@ -11909,14 +11821,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], @@ -11926,14 +11839,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], @@ -11943,14 +11857,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], @@ -11960,14 +11875,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], @@ -11977,14 +11893,15 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], @@ -11994,16 +11911,17 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", @@ -12011,31 +11929,51 @@ "os": [ "netbsd" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, "os": [ "openbsd" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], @@ -12043,16 +11981,17 @@ "license": "MIT", "optional": true, "os": [ - "sunos" + "openbsd" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "node_modules/vitest/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], @@ -12060,18 +11999,37 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "openharmony" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ - "ia32" + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" ], "dev": true, "license": "MIT", @@ -12079,16 +12037,17 @@ "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ - "x64" + "ia32" ], "dev": true, "license": "MIT", @@ -12096,257 +12055,189 @@ "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "node": ">=18" } }, - "node_modules/vitest": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", - "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" + "@vitest/spy": "4.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", - "happy-dom": "*", - "jsdom": "*" + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { + "msw": { "optional": true }, - "jsdom": { + "vite": { "optional": true } } }, - "node_modules/vitest/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/vitest/node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" + "optional": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/vitest/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vitest/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "node_modules/vitest/node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^4.0.0" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "url": "https://github.com/vitejs/vite?sponsor=1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" + "optionalDependencies": { + "fsevents": "~2.3.3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/vitest/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "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" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "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 + } } }, "node_modules/web-worker": { @@ -12482,7 +12373,8 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/wsl-utils": { "version": "0.1.0", @@ -12643,19 +12535,6 @@ "buffer-crc32": "~0.2.3" } }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yoctocolors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", diff --git a/package.json b/package.json index 3d33225..5ba0a1f 100644 --- a/package.json +++ b/package.json @@ -318,7 +318,7 @@ "explorer/context": [ { "command": "meridian.hygiene.deleteFile", - "when": "workspaceFolderCount > 0", + "when": "workspaceFolderCount > 0 && !explorerResourceIsFolder", "group": "meridian@1" }, { @@ -359,15 +359,15 @@ }, { "command": "meridian.hygiene.cleanup", - "when": "workspaceFolderCount > 0" + "when": "false" }, { "command": "meridian.hygiene.deleteFile", - "when": "workspaceFolderCount > 0" + "when": "false" }, { "command": "meridian.hygiene.ignoreFile", - "when": "workspaceFolderCount > 0" + "when": "false" }, { "command": "meridian.hygiene.showAnalytics", @@ -411,20 +411,15 @@ "when": "view == meridian.git.view && viewItem == branch", "group": "git@2" }, - { - "command": "meridian.hygiene.cleanup", - "when": "view == meridian.hygiene.view && viewItem =~ /file/", - "group": "hygiene@1" - }, { "command": "meridian.hygiene.deleteFile", - "when": "view == meridian.hygiene.view && viewItem =~ /file/", - "group": "hygiene@2" + "when": "view == meridian.hygiene.view && viewItem == file", + "group": "hygiene@1" }, { "command": "meridian.hygiene.ignoreFile", - "when": "view == meridian.hygiene.view && viewItem =~ /file/", - "group": "hygiene@3" + "when": "view == meridian.hygiene.view && viewItem == file", + "group": "hygiene@2" } ], "view/item/inline": [ @@ -469,11 +464,11 @@ "@types/micromatch": "^4.0.10", "@types/node": "^22.19.0", "@types/vscode": "^1.80.0", - "@vitest/coverage-v8": "^1.0.4", - "@vitest/ui": "^1.0.4", + "@vitest/coverage-v8": "^4.1.8", + "@vitest/ui": "^4.1.8", "esbuild": "^0.25.12", "semantic-release": "^25.0.3", "semantic-release-vsce": "^6.1.3", - "vitest": "^1.0.4" + "vitest": "^4.1.8" } } diff --git a/src/constants.ts b/src/constants.ts index 0016285..dbe3ca2 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -27,45 +27,6 @@ export const MERIDIAN_DIR = ".meridian"; /** Generated-report / artifact subdir under the dotdir (self-ignored). */ export const MERIDIAN_ARTIFACTS_DIR = "artifacts"; -// ============================================================================ -// Git Configuration Defaults -// ============================================================================ - -export const GIT_DEFAULTS = { - /** Default remote name */ - DEFAULT_REMOTE: "origin" as const, - - /** Default main branch */ - DEFAULT_BRANCH: "main" as const, - - /** Fallback branch if main doesn't exist */ - FALLBACK_BRANCH: "master" as const, - - /** Default depth for shallow clones (0 = full clone) */ - CLONE_DEPTH: 0, - - /** Whether to auto-fetch before operations */ - AUTO_FETCH: false, - - /** Whether to clean branches after merge */ - AUTO_BRANCH_CLEAN: true, - - /** Commit message minimum length (characters) */ - MIN_MESSAGE_LENGTH: 5, - - /** Commit message maximum length (characters) */ - MAX_MESSAGE_LENGTH: 72, - - /** Maximum number of inbound changes to process */ - MAX_INBOUND_CHANGES: 100, - - /** Git operation timeout in milliseconds */ - OPERATION_TIMEOUT_MS: 30 * 1000, - - /** Maximum diff size in bytes before truncation (for LLM token safety) */ - MAX_DIFF_BYTES: 50_000, -} as const; - // ============================================================================ // Workspace Exclusion Base — shared across git and hygiene analytics. // Domain-specific lists extend via spread. @@ -171,51 +132,6 @@ export const HYGIENE_ANALYTICS_EXCLUDE_PATTERNS = [ "**/.cpcache", ] as const; -// ============================================================================ -// Chat Configuration -// ============================================================================ - -export const CHAT_SETTINGS = { - /** Default LLM model for chat operations */ - DEFAULT_MODEL: "gpt-4" as const, - - /** Alternative models */ - AVAILABLE_MODELS: ["gpt-4", "gpt-3.5-turbo", "gpt-4-turbo"] as const, - - /** Lines of context to include from active file */ - CONTEXT_LINES: 50, - - /** Maximum context size in characters */ - MAX_CONTEXT_CHARS: 4000, - - /** Chat message timeout in milliseconds */ - RESPONSE_TIMEOUT_MS: 30 * 1000, - - /** Maximum number of messages to keep in conversation */ - MAX_CONVERSATION_DEPTH: 10, -} as const; - -// ============================================================================ -// Logging Configuration -// ============================================================================ - -export const LOG_SETTINGS = { - /** Default log level */ - DEFAULT_LEVEL: "info" as const, - - /** Available log levels */ - LEVELS: ["debug", "info", "warn", "error"] as const, - - /** Maximum entries per log level */ - MAX_ENTRIES_PER_LEVEL: 250, - - /** Whether to include timestamps in logs */ - INCLUDE_TIMESTAMPS: true, - - /** Whether to include context in logs */ - INCLUDE_CONTEXT: true, -} as const; - // ============================================================================ // Telemetry Event Types // ============================================================================ @@ -224,14 +140,6 @@ export const TELEMETRY_EVENT_KINDS = { COMMAND_STARTED: "COMMAND_STARTED", COMMAND_COMPLETED: "COMMAND_COMPLETED", COMMAND_FAILED: "COMMAND_FAILED", - CACHE_HIT: "CACHE_HIT", - CACHE_MISS: "CACHE_MISS", - ERROR_OCCURRED: "ERROR_OCCURRED", - WORKFLOW_STARTED: "WORKFLOW_STARTED", - WORKFLOW_COMPLETED: "WORKFLOW_COMPLETED", - WORKFLOW_FAILED: "WORKFLOW_FAILED", - AGENT_INVOKED: "AGENT_INVOKED", - USER_ACTION: "USER_ACTION", } as const; // ============================================================================ diff --git a/src/cross-cutting/middleware.ts b/src/cross-cutting/middleware.ts index 46e365f..817dad2 100644 --- a/src/cross-cutting/middleware.ts +++ b/src/cross-cutting/middleware.ts @@ -51,75 +51,6 @@ export function createObservabilityMiddleware( }; } -/** - * Permission middleware — checks command-level access control. - */ -export function createPermissionMiddleware( - logger: Logger, - permissionChecker: (commandName: string) => boolean -): Middleware { - return async (ctx: MiddlewareContext, next: () => Promise) => { - const allowed = permissionChecker(ctx.commandName); - - if (!allowed) { - const err: AppError = { - code: "PERMISSION_DENIED", - message: `User lacks permission to execute '${ctx.commandName}'`, - context: ctx.commandName, - }; - logger.warn( - `Permission denied: ${ctx.commandName}`, - "PermissionMiddleware", - err - ); - throw err; - } - - logger.debug( - `[${ctx.commandName}] Permission granted`, - "PermissionMiddleware" - ); - await next(); - }; -} - -/** - * Rate-limiting middleware — prevents command spam. - */ -export function createRateLimitMiddleware( - logger: Logger, - maxPerSecond: number = 10 -): Middleware { - const callTimes: Map = new Map(); - - return async (ctx: MiddlewareContext, next: () => Promise) => { - const now = Date.now(); - const key = ctx.commandName; - const times = callTimes.get(key) || []; - - // Remove calls older than 1 second - const recentCalls = times.filter((t) => now - t < 1000); - - if (recentCalls.length >= maxPerSecond) { - const err: AppError = { - code: "RATE_LIMIT_EXCEEDED", - message: `Rate limit exceeded for '${ctx.commandName}'`, - context: ctx.commandName, - }; - logger.warn( - `Rate limit: ${ctx.commandName}`, - "RateLimitMiddleware", - err - ); - throw err; - } - - recentCalls.push(now); - callTimes.set(key, recentCalls); - await next(); - }; -} - /** * Audit middleware — logs significant state changes for compliance. */ @@ -130,7 +61,6 @@ export function createAuditMiddleware(logger: Logger): Middleware { "git.commit", "git.pull", "hygiene.cleanup", - "chat.delegate", ]; if (auditCommands.includes(ctx.commandName)) { diff --git a/src/domains/git/analytics-ui/index.html b/src/domains/git/analytics-ui/index.html index b6b0b0a..8d306b1 100644 --- a/src/domains/git/analytics-ui/index.html +++ b/src/domains/git/analytics-ui/index.html @@ -1,7 +1,7 @@ - + Git Analytics diff --git a/src/domains/git/analytics-ui/script.js b/src/domains/git/analytics-ui/script.js index 1612a04..9080c00 100644 --- a/src/domains/git/analytics-ui/script.js +++ b/src/domains/git/analytics-ui/script.js @@ -340,8 +340,8 @@ function renderCommitsTable() { ${escapeHtml(c.hash.slice(0, 7))} ${escapeHtml(c.author)} ${escapeHtml(c.message.slice(0, 70))} - +${c.insertions} - −${c.deletions} + +${Number(c.insertions) || 0} + −${Number(c.deletions) || 0}
@@ -367,11 +367,11 @@ function renderFilesTable() { const row = tbody.insertRow(); row.innerHTML = ` ${escapeHtml(file.path)} - ${file.commitCount} - +${file.insertions} - -${file.deletions} + ${Number(file.commitCount) || 0} + +${Number(file.insertions) || 0} + -${Number(file.deletions) || 0} ${file.volatility.toFixed(1)} - ${file.risk} + ${escapeHtml(file.risk)} `; } } @@ -411,7 +411,7 @@ function renderCoChange() { row.innerHTML = '' + escapeHtml(p.a) + '' + '' + escapeHtml(p.b) + '' + - '' + p.count + '' + + '' + (Number(p.count) || 0) + '' + '' + Math.round((p.coChangeRate || 0) * 100) + '%'; } } @@ -548,12 +548,3 @@ function installIgnoreContextMenu() { window.addEventListener("blur", dismiss); window.addEventListener("scroll", dismiss, true); } - -// Initialize on document load -if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", () => { - if (analyticsData) renderUI(); - }); -} else { - if (analyticsData) renderUI(); -} diff --git a/src/domains/git/session-briefing-ui/index.html b/src/domains/git/session-briefing-ui/index.html index 2631f13..a92e027 100644 --- a/src/domains/git/session-briefing-ui/index.html +++ b/src/domains/git/session-briefing-ui/index.html @@ -1,7 +1,7 @@ - + Session Briefing diff --git a/src/domains/git/session-briefing-ui/script.js b/src/domains/git/session-briefing-ui/script.js index 054d2a9..9c5ed02 100644 --- a/src/domains/git/session-briefing-ui/script.js +++ b/src/domains/git/session-briefing-ui/script.js @@ -112,7 +112,7 @@ // All three summary cards open the Source Control view (single VS Code // built-in command, no payload; handled host-side as type:"openScm"). return '
' + - '

' + esc(label) + '

' + value + '

'; + '

' + esc(label) + '

' + (Number(value) || 0) + '

'; } // Map known flag prefixes to a section anchor for scroll-to. Flag strings @@ -214,7 +214,7 @@ return; } section.style.display = ""; - document.getElementById("activityPeriod").textContent = "(" + esc(w.period) + ")"; + document.getElementById("activityPeriod").textContent = "(" + (w.period == null ? "" : String(w.period)) + ")"; var cards = [ metricCard("Commits", w.commitsInWindow), @@ -451,8 +451,8 @@ '' + esc(c.shortHash) + '' + '' + esc(c.author) + '' + '' + esc(c.message) + '' + - '+' + c.insertions + '' + - '-' + c.deletions + '' + + '+' + (Number(c.insertions) || 0) + '' + + '-' + (Number(c.deletions) || 0) + '' + ''; }).join(""); updateSortIndicators(); @@ -656,6 +656,6 @@ // ── Util ─────────────────────────────────────────────────────────── function esc(str) { if (str == null) return ""; - return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); + return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } })(); diff --git a/src/domains/hygiene/analytics-handler.ts b/src/domains/hygiene/analytics-handler.ts index 3a046f4..dc00194 100644 --- a/src/domains/hygiene/analytics-handler.ts +++ b/src/domains/hygiene/analytics-handler.ts @@ -3,7 +3,7 @@ */ import { Handler, CommandContext, success, failure, Logger } from "../../types"; -import { HYGIENE_ERROR_CODES } from "../../infrastructure/error-codes"; +import { HYGIENE_ERROR_CODES, INFRASTRUCTURE_ERROR_CODES } from "../../infrastructure/error-codes"; import { HygieneAnalyticsReport, PruneConfig, PRUNE_DEFAULTS } from "./analytics-types"; import { HygieneAnalyzer } from "./analytics-service"; import { DeadCodeAnalyzer } from "./dead-code-analyzer"; @@ -15,7 +15,14 @@ export function createShowHygieneAnalyticsHandler( ): Handler, HygieneAnalyticsReport> { return async (ctx: CommandContext, params: Partial = {}) => { try { - const workspaceRoot = ctx.workspaceFolders?.[0] ?? process.cwd(); + const workspaceRoot = ctx.workspaceFolders?.[0]; + if (!workspaceRoot) { + return failure({ + code: INFRASTRUCTURE_ERROR_CODES.WORKSPACE_NOT_FOUND, + message: "No workspace folder found", + context: "hygiene.showAnalytics", + }); + } // Merge caller-supplied config with defaults const config: PruneConfig = { diff --git a/src/domains/hygiene/analytics-service.ts b/src/domains/hygiene/analytics-service.ts index 411ecf7..f9c21ec 100644 --- a/src/domains/hygiene/analytics-service.ts +++ b/src/domains/hygiene/analytics-service.ts @@ -141,7 +141,7 @@ export class HygieneAnalyzer { : undefined; const summary = this.buildSummary(files); - const pruneCandiates = files.filter((f) => f.isPruneCandidate); + const pruneCandidates = files.filter((f) => f.isPruneCandidate); const largestFiles = [...files].sort((a, b) => b.sizeBytes - a.sizeBytes).slice(0, 20); const oldestFiles = [...files].sort((a, b) => b.ageDays - a.ageDays).slice(0, 20); const temporalData = buildTemporalData(files, deadCodeByRelPath); @@ -154,7 +154,7 @@ export class HygieneAnalyzer { workspaceRoot, summary, files, - pruneCandiates, + pruneCandidates, largestFiles, oldestFiles, temporalData, diff --git a/src/domains/hygiene/analytics-types.ts b/src/domains/hygiene/analytics-types.ts index 98e11de..b8e9d23 100644 --- a/src/domains/hygiene/analytics-types.ts +++ b/src/domains/hygiene/analytics-types.ts @@ -117,7 +117,7 @@ export interface HygieneAnalyticsReport { /** All scanned non-excluded files */ files: HygieneFileEntry[]; /** Files satisfying active prune criteria */ - pruneCandiates: HygieneFileEntry[]; + pruneCandidates: HygieneFileEntry[]; /** Top 20 by sizeBytes */ largestFiles: HygieneFileEntry[]; /** Top 20 by ageDays */ diff --git a/src/domains/hygiene/analytics-ui/index.html b/src/domains/hygiene/analytics-ui/index.html index ae7705c..88455dc 100644 --- a/src/domains/hygiene/analytics-ui/index.html +++ b/src/domains/hygiene/analytics-ui/index.html @@ -1,7 +1,7 @@ - + Hygiene Analytics diff --git a/src/domains/hygiene/analytics-ui/script.js b/src/domains/hygiene/analytics-ui/script.js index 6cf1c37..dc0797f 100644 --- a/src/domains/hygiene/analytics-ui/script.js +++ b/src/domains/hygiene/analytics-ui/script.js @@ -159,7 +159,7 @@ function renderCategoryBar() { legendEl.innerHTML = categories.map((cat) => ` - ${cat} (${byCategory[cat].count}) + ${esc(cat)} (${Number(byCategory[cat].count) || 0}) `).join(""); } @@ -269,7 +269,7 @@ function renderPruneTable() { if (!tbody) return; tbody.innerHTML = ""; - const candidates = report.pruneCandiates || []; + const candidates = report.pruneCandidates || []; if (candidates.length === 0) { tbody.innerHTML = `No prune candidates with active criteria`; return; @@ -282,8 +282,8 @@ function renderPruneTable() { ${esc(f.path)} ${fmtBytes(f.sizeBytes)} ${f.lineCount >= 0 ? f.lineCount.toLocaleString() : "—"} - ${f.ageDays} - ${f.category} + ${Number(f.ageDays) || 0} + ${esc(f.category)} `; } } @@ -361,8 +361,8 @@ function renderTopTable(bodyId, rows) { ${esc(f.path)} ${fmtBytes(f.sizeBytes)} ${f.lineCount >= 0 ? f.lineCount.toLocaleString() : "—"} - ${f.ageDays} - ${esc(f.category)} + ${Number(f.ageDays) || 0} + ${esc(f.category)} `).join(""); } diff --git a/src/domains/hygiene/cleanup-handler.ts b/src/domains/hygiene/cleanup-handler.ts index 33b83d4..897f72f 100644 --- a/src/domains/hygiene/cleanup-handler.ts +++ b/src/domains/hygiene/cleanup-handler.ts @@ -27,7 +27,9 @@ export interface CleanupResult { /** * hygiene.cleanup — Remove specified files from the workspace. * Safety: requires an explicit file list; never deletes without one. - * If dryRun=true, returns the list of files that WOULD be deleted without touching the FS. + * Defaults to dryRun=true: deletion requires an explicit dryRun=false, so a + * bare programmatic invocation (command palette, another extension) can never + * remove files. The confirmed UI flow (hygiene.deleteFile) opts in explicitly. */ export function createCleanupHandler( workspaceProvider: WorkspaceProvider, @@ -35,7 +37,7 @@ export function createCleanupHandler( ): Handler { return async (_ctx: CommandContext, params: CleanupParams = {}) => { try { - const { dryRun = false, files } = params; + const { dryRun = true, files } = params; if (!files || files.length === 0) { return failure({ diff --git a/src/domains/hygiene/dead-code-analyzer.ts b/src/domains/hygiene/dead-code-analyzer.ts index 14309f4..405b87c 100644 --- a/src/domains/hygiene/dead-code-analyzer.ts +++ b/src/domains/hygiene/dead-code-analyzer.ts @@ -98,9 +98,12 @@ export class DeadCodeAnalyzer { }; } - // 2. Only files inside workspaceRoot; skip declaration files + // 2. Only files inside workspaceRoot; skip declaration files. + // Trailing separator so a sibling like "/repo-other" never matches "/repo". + const rootPrefix = workspaceRoot.endsWith(path.sep) ? workspaceRoot : workspaceRoot + path.sep; + const insideRoot = (f: string): boolean => f.startsWith(rootPrefix) || f.startsWith(rootPrefix.replace(/\\/g, "/")); const relevantFiles = fileNames.filter( - (f) => f.startsWith(workspaceRoot) && !f.endsWith(".d.ts") + (f) => insideRoot(f) && !f.endsWith(".d.ts") ); // 3. Create program and collect diagnostics @@ -109,7 +112,7 @@ export class DeadCodeAnalyzer { for (const sourceFile of program.getSourceFiles()) { const filePath = sourceFile.fileName; - if (!filePath.startsWith(workspaceRoot) || filePath.endsWith(".d.ts")) { + if (!insideRoot(filePath) || filePath.endsWith(".d.ts")) { continue; } diff --git a/src/domains/hygiene/impact-analysis-handler.ts b/src/domains/hygiene/impact-analysis-handler.ts index cae1a9b..4f3c121 100644 --- a/src/domains/hygiene/impact-analysis-handler.ts +++ b/src/domains/hygiene/impact-analysis-handler.ts @@ -81,7 +81,7 @@ class ImpactAnalyzer { filePath?: string, functionName?: string ): ImpactContext | null { - const cacheKey = `${workspaceRoot}|${filePath || functionName}`; + const cacheKey = `${workspaceRoot}|${filePath ?? ""}|${functionName ?? ""}`; const cached = this.cache.get(cacheKey); if (cached) { return cached; diff --git a/src/domains/hygiene/impact-visitor.ts b/src/domains/hygiene/impact-visitor.ts index 0e49921..f7dbde8 100644 --- a/src/domains/hygiene/impact-visitor.ts +++ b/src/domains/hygiene/impact-visitor.ts @@ -83,11 +83,14 @@ export class ImpactAnalysisVisitor { return null; } - // Simple heuristic: check if path contains target filename stem. + // Simple heuristic: check if path ends with the target filename stem. private pathsResolveToTarget(importPath: string): boolean { + if (!this.targetFile) return false; const targetStem = path.parse(this.targetFile).name; - // Match ".../name" or "./name" or "../name" - return importPath.endsWith(`/${targetStem}`) || importPath.endsWith(`\/${targetStem}`); + // Match ".../name", "./name", or "../name" (with or without extension) + return importPath.endsWith(`/${targetStem}`) || + importPath.endsWith(`/${targetStem}.js`) || + importPath.endsWith(`/${targetStem}.ts`); } private extractCallName(node: ts.CallExpression): string | null { diff --git a/src/domains/hygiene/prune-config.ts b/src/domains/hygiene/prune-config.ts index c4f62af..710661d 100644 --- a/src/domains/hygiene/prune-config.ts +++ b/src/domains/hygiene/prune-config.ts @@ -7,11 +7,18 @@ import { readSetting } from "../../infrastructure/settings"; import { PruneConfig, FileCategory } from "./analytics-types"; +const VALID_CATEGORIES: ReadonlySet = new Set([ + "markdown", "log", "config", "backup", "temp", "source", "artifact", "other", +]); + export function getPruneConfig(): PruneConfig { return { minAgeDays: readSetting("hygiene.prune.minAgeDays"), maxSizeMB: readSetting("hygiene.prune.maxSizeMB"), minLineCount: readSetting("hygiene.prune.minLineCount"), - categories: [...readSetting("hygiene.prune.categories")] as FileCategory[], + // VS Code does not enforce enum membership on user-supplied values; + // narrow here so unknown strings never enter the typed config. + categories: readSetting("hygiene.prune.categories") + .filter((c): c is FileCategory => VALID_CATEGORIES.has(c)), }; } diff --git a/src/domains/hygiene/scan-handler.ts b/src/domains/hygiene/scan-handler.ts index 881a609..d82896d 100644 --- a/src/domains/hygiene/scan-handler.ts +++ b/src/domains/hygiene/scan-handler.ts @@ -2,6 +2,7 @@ * Hygiene Domain Scan Handler — workspace analysis for dead files, large files, and stale logs. */ +import * as path from "path"; import { Handler, CommandContext, @@ -14,7 +15,7 @@ import { Logger, } from "../../types"; import { HYGIENE_SETTINGS } from "../../constants"; -import { HYGIENE_ERROR_CODES } from "../../infrastructure/error-codes"; +import { HYGIENE_ERROR_CODES, INFRASTRUCTURE_ERROR_CODES } from "../../infrastructure/error-codes"; import { pathMatchesAny } from "../../infrastructure/glob-match"; import { readGitignorePatterns, readMeridianIgnorePatterns } from "../../security/ignore-store"; import { DeadCodeAnalyzer } from "./dead-code-analyzer"; @@ -23,7 +24,7 @@ import { detectCollections } from "./collection-detector"; /** * hygiene.scan — Analyze workspace for dead files, large files, and stale logs. * Uses WorkspaceProvider.findFiles() with patterns from HYGIENE_SETTINGS. - * Large-file detection reads file content to measure byte length (no stat API available). + * Large-file detection uses file metadata (statFile) — content is never read. */ export function createScanHandler( workspaceProvider: WorkspaceProvider, @@ -35,7 +36,14 @@ export function createScanHandler( try { logger.info("Scanning workspace for hygiene issues", "HygieneScanHandler"); - const workspaceRoot = ctx.workspaceFolders?.[0] ?? process.cwd(); + const workspaceRoot = ctx.workspaceFolders?.[0]; + if (!workspaceRoot) { + return failure({ + code: INFRASTRUCTURE_ERROR_CODES.WORKSPACE_NOT_FOUND, + message: "No workspace folder found", + context: "hygiene.scan", + }); + } const gitignorePatterns = readGitignorePatterns(workspaceRoot); const meridianIgnorePatterns = readMeridianIgnorePatterns(workspaceRoot); const excludePatterns = [ @@ -44,51 +52,51 @@ export function createScanHandler( ...meridianIgnorePatterns, ]; + // findFiles may return absolute paths; ignore patterns (including + // root-anchored .gitignore entries) match workspace-relative paths. + const isExcluded = (filePath: string): boolean => + pathMatchesAny(path.isAbsolute(filePath) ? path.relative(workspaceRoot, filePath) : filePath, excludePatterns); + // --- Dead files: temp/backup patterns (sourced from HYGIENE_SETTINGS.TEMP_FILE_PATTERNS) --- - const deadFiles: string[] = []; + const deadFileSet = new Set(); const deadPatterns = HYGIENE_SETTINGS.TEMP_FILE_PATTERNS.map((p) => `**/${p}`); for (const pattern of deadPatterns) { const result = await workspaceProvider.findFiles(pattern); if (result.kind === "ok") { for (const f of result.value) { - if (!deadFiles.includes(f) && !pathMatchesAny(f, excludePatterns)) { - deadFiles.push(f); - } + if (!isExcluded(f)) deadFileSet.add(f); } } } + const deadFiles = Array.from(deadFileSet); // --- Log files --- - const logFiles: string[] = []; + const logFileSet = new Set(); const logPatterns = HYGIENE_SETTINGS.LOG_FILE_PATTERNS.map((p) => `**/${p}`); for (const pattern of logPatterns) { const result = await workspaceProvider.findFiles(pattern); if (result.kind === "ok") { for (const f of result.value) { - if (!logFiles.includes(f) && !pathMatchesAny(f, excludePatterns)) { - logFiles.push(f); - } + if (!isExcluded(f)) logFileSet.add(f); } } } + const logFiles = Array.from(logFileSet); - // --- Large files: read content to measure size, skip excluded paths --- + // --- Large files: metadata size only, skip excluded paths --- const largeFiles: Array<{ path: string; sizeBytes: number }> = []; const allFilesResult = await workspaceProvider.findFiles("**/*"); if (allFilesResult.kind === "ok") { for (const filePath of allFilesResult.value) { - if (pathMatchesAny(filePath, excludePatterns)) { + if (isExcluded(filePath)) { continue; } - const readResult = await workspaceProvider.readFile(filePath); - if (readResult.kind === "ok") { - const sizeBytes = Buffer.byteLength(readResult.value, "utf8"); - if (sizeBytes > HYGIENE_SETTINGS.MAX_FILE_SIZE_BYTES) { - largeFiles.push({ path: filePath, sizeBytes }); - } + const statResult = await workspaceProvider.statFile(filePath); + if (statResult.kind === "ok" && statResult.value.sizeBytes > HYGIENE_SETTINGS.MAX_FILE_SIZE_BYTES) { + largeFiles.push({ path: filePath, sizeBytes: statResult.value.sizeBytes }); } } } @@ -98,7 +106,7 @@ export function createScanHandler( const mdResult = await workspaceProvider.findFiles("**/*.md"); if (mdResult.kind === "ok") { for (const filePath of mdResult.value) { - if (pathMatchesAny(filePath, excludePatterns)) continue; + if (isExcluded(filePath)) continue; const readResult = await workspaceProvider.readFile(filePath); if (readResult.kind === "ok") { const sizeBytes = Buffer.byteLength(readResult.value, "utf8"); diff --git a/src/infrastructure/error-codes.ts b/src/infrastructure/error-codes.ts index e5d8dfe..f4cd2da 100644 --- a/src/infrastructure/error-codes.ts +++ b/src/infrastructure/error-codes.ts @@ -1,6 +1,6 @@ /** - * Error Code Definitions — Centralized error codes and telemetry events - * Every error code must be explicitly defined here + * Error Code Definitions — Centralized error codes. + * Every error code must be explicitly defined here. */ // ============================================================================ @@ -15,38 +15,15 @@ export const GIT_ERROR_CODES = { GIT_PULL_ERROR: "GIT_PULL_ERROR", GIT_COMMIT_ERROR: "GIT_COMMIT_ERROR", GIT_FETCH_ERROR: "GIT_FETCH_ERROR", - GIT_RESET_ERROR: "GIT_RESET_ERROR", + GIT_OPERATION_FAILED: "GIT_OPERATION_FAILED", + GIT_POLICY_DENIED: "GIT_POLICY_DENIED", - // Change Parsing & Staging + // Change Parsing GET_CHANGES_FAILED: "GET_CHANGES_FAILED", - PARSE_CHANGES_FAILED: "PARSE_CHANGES_FAILED", - STAGE_FAILED: "STAGE_FAILED", - - // Batch Commit Operations - COMMIT_FAILED: "COMMIT_FAILED", - BATCH_COMMIT_ERROR: "BATCH_COMMIT_ERROR", - ROLLBACK_FAILED: "ROLLBACK_FAILED", - - // Inbound Analysis - INBOUND_ANALYSIS_ERROR: "INBOUND_ANALYSIS_ERROR", - INBOUND_DIFF_PARSE_ERROR: "INBOUND_DIFF_PARSE_ERROR", - CONFLICT_DETECTION_ERROR: "CONFLICT_DETECTION_ERROR", // Analytics ANALYTICS_ERROR: "ANALYTICS_ERROR", INVALID_PERIOD: "INVALID_PERIOD", - - // Smart Commit - SMART_COMMIT_ERROR: "SMART_COMMIT_ERROR", - - // Validation - NO_CHANGES: "NO_CHANGES", - NO_GROUPS_APPROVED: "NO_GROUPS_APPROVED", - COMMIT_CANCELLED: "COMMIT_CANCELLED", - PR_GENERATION_ERROR: "PR_GENERATION_ERROR", - PR_REVIEW_ERROR: "PR_REVIEW_ERROR", - PR_COMMENT_ERROR: "PR_COMMENT_ERROR", - CONFLICT_RESOLUTION_ERROR: "CONFLICT_RESOLUTION_ERROR", } as const; // ============================================================================ @@ -65,58 +42,6 @@ export const HYGIENE_ERROR_CODES = { DEAD_CODE_SCAN_ERROR: "DEAD_CODE_SCAN_ERROR", } as const; -// ============================================================================ -// Chat Domain Error Codes -// ============================================================================ - -export const CHAT_ERROR_CODES = { - CHAT_INIT_ERROR: "CHAT_INIT_ERROR", - CHAT_CONTEXT_ERROR: "CHAT_CONTEXT_ERROR", - CHAT_DELEGATE_ERROR: "CHAT_DELEGATE_ERROR", - CHAT_DELEGATE_NO_GENERATE_FN: "CHAT_DELEGATE_NO_GENERATE_FN", -} as const; - -// ============================================================================ -// Workflow Engine Error Codes -// ============================================================================ - -export const WORKFLOW_ERROR_CODES = { - WORKFLOW_INIT_ERROR: "WORKFLOW_INIT_ERROR", - STEP_RUNNER_NOT_AVAILABLE: "STEP_RUNNER_NOT_AVAILABLE", - WORKFLOW_EXECUTION_ERROR: "WORKFLOW_EXECUTION_ERROR", - INVALID_NEXT_STEP: "INVALID_NEXT_STEP", - STEP_EXECUTION_ERROR: "STEP_EXECUTION_ERROR", - STEP_TIMEOUT: "STEP_TIMEOUT", - INTERPOLATION_ERROR: "INTERPOLATION_ERROR", - INVALID_WORKFLOW: "INVALID_WORKFLOW", - WORKFLOW_LIST_ERROR: "WORKFLOW_LIST_ERROR", - WORKFLOW_NOT_FOUND: "WORKFLOW_NOT_FOUND", - WORKFLOW_EXECUTION_FAILED: "WORKFLOW_EXECUTION_FAILED", - WORKFLOW_RUN_ERROR: "WORKFLOW_RUN_ERROR", -} as const; - -// ============================================================================ -// Agent Domain Error Codes -// ============================================================================ - -export const AGENT_ERROR_CODES = { - AGENT_INIT_ERROR: "AGENT_INIT_ERROR", - AGENT_LIST_ERROR: "AGENT_LIST_ERROR", - AGENT_NOT_FOUND: "AGENT_NOT_FOUND", - MISSING_CAPABILITY: "MISSING_CAPABILITY", - EXECUTION_FAILED: "EXECUTION_FAILED", - INVALID_WORKFLOW_REFERENCE: "INVALID_WORKFLOW_REFERENCE", -} as const; - -// ============================================================================ -// Skill Domain Error Codes -// ============================================================================ - -export const SKILL_ERROR_CODES = { - SKILL_EXECUTION_ERROR: "SKILL_EXECUTION_ERROR", - SKILL_STEP_FAILED: "SKILL_STEP_FAILED", -} as const; - // ============================================================================ // Router Error Codes // ============================================================================ @@ -164,300 +89,9 @@ export const GENERIC_ERROR_CODES = { export const ERROR_CODES = { ...GIT_ERROR_CODES, ...HYGIENE_ERROR_CODES, - ...CHAT_ERROR_CODES, - ...WORKFLOW_ERROR_CODES, - ...AGENT_ERROR_CODES, - ...SKILL_ERROR_CODES, ...ROUTER_ERROR_CODES, ...INFRASTRUCTURE_ERROR_CODES, ...GENERIC_ERROR_CODES, } as const; export type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES]; - -// ============================================================================ -// Telemetry Event Definitions -// ============================================================================ - -export enum TelemetryEvent { - // Command execution - COMMAND_STARTED = "COMMAND_STARTED", - COMMAND_COMPLETED = "COMMAND_COMPLETED", - COMMAND_FAILED = "COMMAND_FAILED", - - // Git operations - GIT_INIT = "GIT_INIT", - GIT_STATUS_CHECK = "GIT_STATUS_CHECK", - GIT_PULL_EXECUTED = "GIT_PULL_EXECUTED", - GIT_COMMIT_EXECUTED = "GIT_COMMIT_EXECUTED", - GIT_SMART_COMMIT = "GIT_SMART_COMMIT", - GIT_BATCH_COMMIT = "GIT_BATCH_COMMIT", - GIT_BATCH_ROLLBACK = "GIT_BATCH_ROLLBACK", - GIT_INBOUND_ANALYSIS = "GIT_INBOUND_ANALYSIS", - - // Workflow execution - WORKFLOW_STARTED = "WORKFLOW_STARTED", - WORKFLOW_COMPLETED = "WORKFLOW_COMPLETED", - WORKFLOW_STEP_EXECUTED = "WORKFLOW_STEP_EXECUTED", - - // Hygiene operations - HYGIENE_SCAN = "HYGIENE_SCAN", - HYGIENE_CLEANUP = "HYGIENE_CLEANUP", - - // Error events - ERROR_OCCURRED = "ERROR_OCCURRED", - RETRY_ATTEMPTED = "RETRY_ATTEMPTED", - - // Analytics - ANALYTICS_GENERATED = "ANALYTICS_GENERATED", - ANALYTICS_EXPORTED = "ANALYTICS_EXPORTED", -} - -/** - * Telemetry event metadata - */ -export interface TelemetryEventMetadata { - eventName: TelemetryEvent; - isCritical: boolean; - description: string; - payloadExample: Record; -} - -export const TELEMETRY_EVENTS: Record = { - [TelemetryEvent.COMMAND_STARTED]: { - eventName: TelemetryEvent.COMMAND_STARTED, - isCritical: false, - description: "Fired when a command begins execution", - payloadExample: { - commandName: "git.smartCommit", - timestamp: Date.now(), - }, - }, - [TelemetryEvent.COMMAND_COMPLETED]: { - eventName: TelemetryEvent.COMMAND_COMPLETED, - isCritical: true, - description: "Fired when a command completes successfully", - payloadExample: { - commandName: "git.smartCommit", - durationMs: 1234, - timestamp: Date.now(), - }, - }, - [TelemetryEvent.COMMAND_FAILED]: { - eventName: TelemetryEvent.COMMAND_FAILED, - isCritical: true, - description: "Fired when a command fails", - payloadExample: { - commandName: "git.smartCommit", - errorCode: "BATCH_COMMIT_ERROR", - durationMs: 234, - }, - }, - [TelemetryEvent.GIT_INIT]: { - eventName: TelemetryEvent.GIT_INIT, - isCritical: true, - description: "Git domain initialized successfully", - payloadExample: { - branch: "main", - timestamp: Date.now(), - }, - }, - [TelemetryEvent.GIT_STATUS_CHECK]: { - eventName: TelemetryEvent.GIT_STATUS_CHECK, - isCritical: false, - description: "Git status checked", - payloadExample: { - branch: "main", - isDirty: true, - stagedCount: 3, - }, - }, - [TelemetryEvent.GIT_PULL_EXECUTED]: { - eventName: TelemetryEvent.GIT_PULL_EXECUTED, - isCritical: true, - description: "Git pull completed", - payloadExample: { - branch: "main", - success: true, - }, - }, - [TelemetryEvent.GIT_COMMIT_EXECUTED]: { - eventName: TelemetryEvent.GIT_COMMIT_EXECUTED, - isCritical: true, - description: "Git commit completed", - payloadExample: { - fileCount: 5, - hash: "abc123def456", - }, - }, - [TelemetryEvent.GIT_SMART_COMMIT]: { - eventName: TelemetryEvent.GIT_SMART_COMMIT, - isCritical: true, - description: "Smart commit executed with change grouping", - payloadExample: { - filesAnalyzed: 10, - groupsCreated: 3, - commitsCreated: 3, - durationMs: 2000, - }, - }, - [TelemetryEvent.GIT_BATCH_COMMIT]: { - eventName: TelemetryEvent.GIT_BATCH_COMMIT, - isCritical: true, - description: "Batch commit executed", - payloadExample: { - groupCount: 3, - totalFiles: 10, - commitCount: 3, - }, - }, - [TelemetryEvent.GIT_BATCH_ROLLBACK]: { - eventName: TelemetryEvent.GIT_BATCH_ROLLBACK, - isCritical: true, - description: "Batch commit rolled back due to error", - payloadExample: { - commitCount: 2, - reason: "COMMIT_FAILED", - }, - }, - [TelemetryEvent.GIT_INBOUND_ANALYSIS]: { - eventName: TelemetryEvent.GIT_INBOUND_ANALYSIS, - isCritical: false, - description: "Inbound changes analyzed", - payloadExample: { - remoteChanges: 5, - conflicts: 2, - highSeverity: 1, - }, - }, - [TelemetryEvent.WORKFLOW_STARTED]: { - eventName: TelemetryEvent.WORKFLOW_STARTED, - isCritical: true, - description: "Workflow execution started", - payloadExample: { - workflowName: "deploy", - stepCount: 5, - }, - }, - [TelemetryEvent.WORKFLOW_COMPLETED]: { - eventName: TelemetryEvent.WORKFLOW_COMPLETED, - isCritical: true, - description: "Workflow completed successfully", - payloadExample: { - workflowName: "deploy", - durationMs: 5000, - stepsExecuted: 5, - }, - }, - [TelemetryEvent.WORKFLOW_STEP_EXECUTED]: { - eventName: TelemetryEvent.WORKFLOW_STEP_EXECUTED, - isCritical: false, - description: "Single workflow step executed", - payloadExample: { - workflowName: "deploy", - stepId: "checkout", - success: true, - }, - }, - [TelemetryEvent.HYGIENE_SCAN]: { - eventName: TelemetryEvent.HYGIENE_SCAN, - isCritical: false, - description: "Workspace hygiene scan completed", - payloadExample: { - deadFiles: 3, - largeFiles: 2, - logFiles: 5, - }, - }, - [TelemetryEvent.HYGIENE_CLEANUP]: { - eventName: TelemetryEvent.HYGIENE_CLEANUP, - isCritical: true, - description: "Workspace cleanup completed", - payloadExample: { - filesDeleted: 10, - bytesFreed: 1048576, - dryRun: false, - }, - }, - [TelemetryEvent.ERROR_OCCURRED]: { - eventName: TelemetryEvent.ERROR_OCCURRED, - isCritical: true, - description: "Error occurred during execution", - payloadExample: { - errorCode: "GIT_UNAVAILABLE", - context: "GitDomainService.initialize", - timestamp: Date.now(), - }, - }, - [TelemetryEvent.RETRY_ATTEMPTED]: { - eventName: TelemetryEvent.RETRY_ATTEMPTED, - isCritical: false, - description: "Operation retry attempted", - payloadExample: { - attemptNumber: 2, - operation: "git.fetch", - backoffMs: 1000, - }, - }, - [TelemetryEvent.ANALYTICS_GENERATED]: { - eventName: TelemetryEvent.ANALYTICS_GENERATED, - isCritical: false, - description: "Git analytics report generated", - payloadExample: { - commitCount: 150, - authorCount: 5, - fileCount: 50, - }, - }, - [TelemetryEvent.ANALYTICS_EXPORTED]: { - eventName: TelemetryEvent.ANALYTICS_EXPORTED, - isCritical: false, - description: "Analytics exported to file", - payloadExample: { - format: "json", - filePath: "/workspace/analytics.json", - }, - }, -}; - -// ============================================================================ -// Timeout Constants -// ============================================================================ - -export const TIMEOUTS = { - GIT_OPERATION: 30_000, // 30 seconds - GIT_CLONE: 120_000, // 2 minutes - WORKFLOW_STEP: 60_000, // 1 minute - NETWORK_REQUEST: 10_000, // 10 seconds -} as const; - -// ============================================================================ -// Retry Configuration -// ============================================================================ - -export interface RetryConfig { - maxAttempts: number; - initialBackoffMs: number; - maxBackoffMs: number; - backoffMultiplier: number; -} - -export const DEFAULT_RETRY_CONFIG: RetryConfig = { - maxAttempts: 3, - initialBackoffMs: 100, - maxBackoffMs: 5000, - backoffMultiplier: 2, -}; - -/** - * Get retry config for specific error code - */ -export function getRetryConfig(errorCode: ErrorCode): RetryConfig | null { - // Network errors and timeouts are retryable - const retryableErrors: ErrorCode[] = [ - GIT_ERROR_CODES.GIT_FETCH_ERROR, - GENERIC_ERROR_CODES.TIMEOUT, - ]; - - return retryableErrors.includes(errorCode) ? DEFAULT_RETRY_CONFIG : null; -} diff --git a/src/infrastructure/git-provider.ts b/src/infrastructure/git-provider.ts index b347f3a..e0ffa5b 100644 --- a/src/infrastructure/git-provider.ts +++ b/src/infrastructure/git-provider.ts @@ -10,7 +10,6 @@ import { GitProvider, GitStatus, GitPullResult, - GitStageChange, GitFileChange, RecentCommit, Result, @@ -235,67 +234,6 @@ class RealGitProvider implements GitProvider { return success(hash); } - async getChanges(): Promise> { - const result = await git( - ["diff", "--cached", "--name-status"], - this.workspaceRoot - ); - if (result.kind === "err") return result; - - const changes: GitStageChange[] = []; - for (const line of result.value.split("\n").filter(Boolean)) { - const [code, ...rest] = line.split("\t"); - const filePath = rest.join("\t").trim(); - if (!filePath) continue; - - let status: GitStageChange["status"] = "modified"; - if (code === "A") status = "added"; - else if (code === "D") status = "deleted"; - - changes.push({ path: filePath, status }); - } - return success(changes); - } - - async getDiff(paths?: string[]): Promise> { - const args = ["diff", "--cached"]; - if (paths && paths.length > 0) { - args.push("--", ...paths); - } - return git(args, this.workspaceRoot); - } - - async getUncommittedDiff(paths?: string[]): Promise> { - // All changes (staged + unstaged) relative to HEAD - const args = ["diff", "HEAD"]; - if (paths && paths.length > 0) { - args.push("--", ...paths); - } - return git(args, this.workspaceRoot); - } - - async stage(paths: string[]): Promise> { - if (paths.length === 0) return success(undefined); - const result = await git(["add", "--", ...paths], this.workspaceRoot); - if (result.kind === "err") return result; - return success(undefined); - } - - async reset( - paths: string[] | { mode: string; ref: string } - ): Promise> { - let args: string[]; - if (Array.isArray(paths)) { - if (paths.length === 0) return success(undefined); - args = ["reset", "HEAD", "--", ...paths]; - } else { - args = ["reset", `--${paths.mode}`, paths.ref]; - } - const result = await git(args, this.workspaceRoot); - if (result.kind === "err") return result; - return success(undefined); - } - async getAllChanges(): Promise> { // --numstat gives: additionsdeletionspath for each changed file const stagedResult = await git( @@ -392,14 +330,6 @@ class RealGitProvider implements GitProvider { return result; } - async diff( - revision: string, - options?: string[] - ): Promise> { - const args = ["diff", ...(options ?? []), revision]; - return git(args, this.workspaceRoot); - } - async getRecentCommits(count: number): Promise> { const logResult = await git( ["log", `-${count}`, "--pretty=format:%h|%s|%an", "--numstat"], @@ -409,21 +339,6 @@ class RealGitProvider implements GitProvider { return success(parseCommitLog(logResult.value)); } - async getCommitRange(from: string, to: string = "HEAD"): Promise> { - const logResult = await git( - ["log", `${from}..${to}`, "--pretty=format:%h|%s|%an", "--numstat"], - this.workspaceRoot - ); - if (logResult.kind === "err") return logResult; - return success(parseCommitLog(logResult.value)); - } - - async getMergeBase(branch: string, base = "main"): Promise> { - const result = await git(["merge-base", branch, base], this.workspaceRoot); - if (result.kind === "err") return result; - return success(result.value.trim()); - } - async getUntrackedFiles(): Promise> { const result = await git( ["ls-files", "--others", "--exclude-standard"], diff --git a/src/infrastructure/prose-generator.ts b/src/infrastructure/prose-generator.ts index 5f2a438..e58a4bd 100644 --- a/src/infrastructure/prose-generator.ts +++ b/src/infrastructure/prose-generator.ts @@ -27,10 +27,6 @@ export async function generateProse(request: ProseRequest): Promise void -): Promise> { - try { - const model = await selectModel(request.domain); - if (!model) { - return failure({ - code: "MODEL_UNAVAILABLE", - message: "No language model available. Ensure GitHub Copilot is enabled.", - context: "streamProse", - }); - } - const dataStr = request.formatData ? request.formatData(request.data) : JSON.stringify(request.data, null, 2); - const permitted = await enforceLmEgressPolicy(request.domain); - if (!permitted) { - return failure({ - code: "LM_EGRESS_BLOCKED", - message: "LM egress blocked by security policy.", - context: "streamProse", - }); - } - const messages = [ vscode.LanguageModelChatMessage.User(`${request.systemPrompt}\n\n---\n\n${sanitizeLmPayload(dataStr)}`), ]; @@ -99,7 +50,6 @@ export async function streamProse( let text = ""; for await (const fragment of response.text) { text += fragment; - sink(fragment); } return success(text.trim()); @@ -107,7 +57,7 @@ export async function streamProse( return failure({ code: "PROSE_GENERATION_ERROR", message: `Prose generation failed: ${err instanceof Error ? err.message : String(err)}`, - context: "streamProse", + context: "generateProse", }); } } diff --git a/src/infrastructure/telemetry.ts b/src/infrastructure/telemetry.ts index 9e694d7..2e66f03 100644 --- a/src/infrastructure/telemetry.ts +++ b/src/infrastructure/telemetry.ts @@ -1,14 +1,12 @@ /** * Telemetry & Structured Logging Infrastructure - * - * Provides: - * - Type-safe telemetry events (signal events only, not verbose logs) - * - Structured event tracking (commands, cache, errors) - * - Integration with Logger for signal-based reporting - * - No breaking changes to existing Logger interface (additive only) + * + * Tracks signal events for command dispatch (started / completed / failed), + * emitted by the router's observability middleware. Local-only: sinks write + * to memory or the developer console — nothing leaves the machine. */ -import { Logger, AppError, CommandName } from "../types"; +import { AppError, CommandName } from "../types"; import { TELEMETRY_EVENT_KINDS } from "../constants"; import { ErrorCode } from "./error-codes"; @@ -16,23 +14,11 @@ import { ErrorCode } from "./error-codes"; // Telemetry Event Types // ============================================================================ -/** - * Event kind discriminator for type-safe event dispatch. - * Only signal events that represent state changes or important milestones, - * NOT verbose debug logs. - */ +/** Event kind discriminator for type-safe event dispatch. */ export type TelemetryEventKind = | typeof TELEMETRY_EVENT_KINDS.COMMAND_STARTED | typeof TELEMETRY_EVENT_KINDS.COMMAND_COMPLETED - | typeof TELEMETRY_EVENT_KINDS.COMMAND_FAILED - | typeof TELEMETRY_EVENT_KINDS.CACHE_HIT - | typeof TELEMETRY_EVENT_KINDS.CACHE_MISS - | typeof TELEMETRY_EVENT_KINDS.ERROR_OCCURRED - | typeof TELEMETRY_EVENT_KINDS.WORKFLOW_STARTED - | typeof TELEMETRY_EVENT_KINDS.WORKFLOW_COMPLETED - | typeof TELEMETRY_EVENT_KINDS.WORKFLOW_FAILED - | typeof TELEMETRY_EVENT_KINDS.AGENT_INVOKED - | typeof TELEMETRY_EVENT_KINDS.USER_ACTION; + | typeof TELEMETRY_EVENT_KINDS.COMMAND_FAILED; /** * Command execution event payload. @@ -48,80 +34,12 @@ export interface CommandEventPayload { frequency?: number; // how many times this command has been executed in this session } -/** - * Cache operation event payload. - */ -export interface CacheEventPayload { - cacheKey: string; - hitRate?: number; // 0.0 to 1.0 - itemSize?: number; // bytes - evictionReason?: string; -} - -/** - * Error event payload. - */ -export interface ErrorEventPayload { - errorCode: ErrorCode | string; - errorMessage: string; - errorType?: string; - stack?: string; - context?: string; - recoverable: boolean; -} - -/** - * Workflow event payload. - */ -export interface WorkflowEventPayload { - workflowId: string; - stepId?: string; - duration?: number; // milliseconds - outcome?: "success" | "failure"; - error?: { - code: ErrorCode | string; - message: string; - }; -} - -/** - * Agent event payload. - */ -export interface AgentEventPayload { - agentId: string; - capability: CommandName | string; - duration?: number; // milliseconds - outcome?: "success" | "failure"; -} - -/** - * User action event payload (e.g., UI interactions). - */ -export interface UserActionEventPayload { - action: string; - target?: string; - value?: string | number | boolean; -} - -/** - * Discriminated union of all telemetry event payloads. - */ -export type TelemetryEventPayload = - | CommandEventPayload - | CacheEventPayload - | ErrorEventPayload - | WorkflowEventPayload - | AgentEventPayload - | UserActionEventPayload - | Record; - /** * A single telemetry event: kind + structured payload. - * This is what gets emitted and tracked. */ export interface TelemetryEvent { kind: TelemetryEventKind; - payload: TelemetryEventPayload; + payload: CommandEventPayload; timestamp: number; // Unix timestamp in milliseconds sessionId?: string; // Session identifier for correlation } @@ -139,9 +57,8 @@ export interface TelemetrySink { // ============================================================================ /** - * Tracks telemetry events across the application. - * Emits only signal events (state changes, errors, milestones). - * Does NOT emit verbose debug logs. + * Tracks command lifecycle events. Emits only signal events + * (state changes, errors), NOT verbose debug logs. * * Usage: * telemetry.trackCommandStarted('git.status'); @@ -162,15 +79,12 @@ export class TelemetryTracker { * Track command execution start. */ trackCommandStarted(commandName: CommandName | string): void { - const event: TelemetryEvent = { + this.emit({ kind: TELEMETRY_EVENT_KINDS.COMMAND_STARTED, - payload: { - commandName, - }, + payload: { commandName }, timestamp: Date.now(), sessionId: this.sessionId, - }; - this.emit(event); + }); } /** @@ -181,22 +95,13 @@ export class TelemetryTracker { duration: number, outcome: "success" | "failure" | "timeout" | "cancelled" = "success" ): void { - const frequency = - (this.commandFrequency.get(commandName) || 0) + 1; - this.commandFrequency.set(commandName, frequency); - - const event: TelemetryEvent = { + const frequency = this.bumpFrequency(commandName); + this.emit({ kind: TELEMETRY_EVENT_KINDS.COMMAND_COMPLETED, - payload: { - commandName, - duration, - outcome, - frequency, - }, + payload: { commandName, duration, outcome, frequency }, timestamp: Date.now(), sessionId: this.sessionId, - }; - this.emit(event); + }); } /** @@ -207,194 +112,19 @@ export class TelemetryTracker { duration: number, error: AppError ): void { - const frequency = - (this.commandFrequency.get(commandName) || 0) + 1; - this.commandFrequency.set(commandName, frequency); - - const event: TelemetryEvent = { + const frequency = this.bumpFrequency(commandName); + this.emit({ kind: TELEMETRY_EVENT_KINDS.COMMAND_FAILED, payload: { commandName, duration, outcome: "failure", - error: { - code: error.code, - message: error.message, - }, + error: { code: error.code, message: error.message }, frequency, }, timestamp: Date.now(), sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track cache hit. - */ - trackCacheHit(cacheKey: string, itemSize?: number): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.CACHE_HIT, - payload: { - cacheKey, - itemSize, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track cache miss. - */ - trackCacheMiss(cacheKey: string): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.CACHE_MISS, - payload: { - cacheKey, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track error occurrence. - */ - trackError( - error: AppError, - recoverable: boolean = false, - additionalContext?: string - ): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.ERROR_OCCURRED, - payload: { - errorCode: error.code, - errorMessage: error.message, - recoverable, - context: additionalContext || error.context, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track workflow execution start. - */ - trackWorkflowStarted(workflowId: string): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.WORKFLOW_STARTED, - payload: { - workflowId, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track workflow execution completion. - */ - trackWorkflowCompleted( - workflowId: string, - duration: number, - stepId?: string - ): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.WORKFLOW_COMPLETED, - payload: { - workflowId, - stepId, - duration, - outcome: "success", - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track workflow execution failure. - */ - trackWorkflowFailed( - workflowId: string, - duration: number, - error: AppError, - stepId?: string - ): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.WORKFLOW_FAILED, - payload: { - workflowId, - stepId, - duration, - outcome: "failure", - error: { - code: error.code, - message: error.message, - }, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track agent invocation. - */ - trackAgentInvoked( - agentId: string, - capability: CommandName | string, - duration?: number, - outcome?: "success" | "failure" - ): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.AGENT_INVOKED, - payload: { - agentId, - capability, - duration, - outcome, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Track user action (e.g., UI interaction). - */ - trackUserAction( - action: string, - target?: string, - value?: string | number | boolean - ): void { - const event: TelemetryEvent = { - kind: TELEMETRY_EVENT_KINDS.USER_ACTION, - payload: { - action, - target, - value, - }, - timestamp: Date.now(), - sessionId: this.sessionId, - }; - this.emit(event); - } - - /** - * Get command execution frequency for a given command. - */ - getCommandFrequency(commandName: string): number { - return this.commandFrequency.get(commandName) || 0; + }); } /** @@ -406,9 +136,12 @@ export class TelemetryTracker { } } - /** - * Private: emit an event to the sink. - */ + private bumpFrequency(commandName: string): number { + const frequency = (this.commandFrequency.get(commandName) || 0) + 1; + this.commandFrequency.set(commandName, frequency); + return frequency; + } + private emit(event: TelemetryEvent): void { try { this.sink.emit(event); @@ -419,126 +152,11 @@ export class TelemetryTracker { } } - /** - * Generate a unique session ID. - */ private generateSessionId(): string { return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`; } } -// ============================================================================ -// Logger Telemetry Integration (Additive) -// ============================================================================ - -/** - * Extend the Logger interface with telemetry tracking methods. - * This is additive — no breaking changes to existing Logger. - * - * Usage in domain services: - * logger.trackCommand('git.status', startTime); - * logger.trackError(error, true); // recoverable error - */ -export interface LoggerWithTelemetry extends Logger { - /** - * Associate a telemetry tracker with this logger. - * Enables additive telemetry tracking without breaking existing Logger interface. - */ - setTelemetry?(telemetry: TelemetryTracker): void; - - /** - * Track a command execution. - * Helper method for common command tracking pattern. - */ - trackCommand?( - commandName: CommandName | string, - startTime: number, - outcome?: "success" | "failure" - ): void; - - /** - * Track an error with telemetry. - */ - trackError?(error: AppError, recoverable?: boolean): void; - - /** - * Track a cache operation. - */ - trackCache?(key: string, hit: boolean): void; -} - -/** - * In-memory telemetry sink (for testing and development). - * Stores events in a circular buffer. - */ -export class InMemoryTelemetrySink implements TelemetrySink { - private events: TelemetryEvent[] = []; - private maxEvents: number; - - constructor(maxEvents: number = 1000) { - this.maxEvents = maxEvents; - } - - emit(event: TelemetryEvent): void { - this.events.push(event); - - // Prevent unbounded growth - if (this.events.length > this.maxEvents) { - this.events.shift(); - } - } - - /** - * Get all stored events. - */ - getEvents(): TelemetryEvent[] { - return [...this.events]; - } - - /** - * Get events filtered by kind. - */ - getEventsByKind(kind: TelemetryEventKind): TelemetryEvent[] { - return this.events.filter((e) => e.kind === kind); - } - - /** - * Clear all stored events. - */ - clear(): void { - this.events = []; - } - - /** - * Get summary statistics. - */ - getSummary(): { - totalEvents: number; - eventsByKind: Record; - sessionIds: string[]; - } { - const eventsByKind: Record = {} as any; - const sessionIds = new Set(); - - for (const event of this.events) { - eventsByKind[event.kind] = (eventsByKind[event.kind] || 0) + 1; - if (event.sessionId) { - sessionIds.add(event.sessionId); - } - } - - return { - totalEvents: this.events.length, - eventsByKind, - sessionIds: Array.from(sessionIds), - }; - } - - async flush(): Promise { - // No-op for in-memory sink - } -} - /** * Console telemetry sink (for development/debugging). * Logs important events to console. @@ -555,15 +173,13 @@ export class ConsoleTelemetrySink implements TelemetrySink { return; } - // Only log important events, not all noise - const importantKinds = [ + // Only log signal events, not per-command completion noise + const importantKinds: TelemetryEventKind[] = [ TELEMETRY_EVENT_KINDS.COMMAND_STARTED, TELEMETRY_EVENT_KINDS.COMMAND_FAILED, - TELEMETRY_EVENT_KINDS.ERROR_OCCURRED, - TELEMETRY_EVENT_KINDS.WORKFLOW_FAILED, ]; - if (importantKinds.includes(event.kind as any)) { + if (importantKinds.includes(event.kind)) { // Intentional console.log: this sink IS the output destination; // routing through Logger would create a circular dependency. console.log(`[TELEMETRY:${event.kind}]`, event.payload); diff --git a/src/infrastructure/workspace-provider.ts b/src/infrastructure/workspace-provider.ts index af878ec..fdf3d84 100644 --- a/src/infrastructure/workspace-provider.ts +++ b/src/infrastructure/workspace-provider.ts @@ -25,6 +25,13 @@ function fsError(code: string, op: string, filePath: string, err: unknown): AppE }; } +/** + * Directories never worth descending into. Every consumer's exclude list + * already filters these out of results; pruning them during the walk avoids + * traversing the (typically largest) trees in the workspace at all. + */ +const PRUNED_DIRS = new Set([".git", "node_modules"]); + /** * Recursively collect all file paths under a directory, applying a simple * glob-style include pattern. Supports ** prefix and suffix wildcards only. @@ -36,6 +43,7 @@ async function collectFiles( logger?: Logger ): Promise { const results: string[] = []; + const matches = compilePattern(pattern); async function walk(current: string): Promise { let entries: Dirent[]; @@ -52,8 +60,10 @@ async function collectFiles( for (const entry of entries) { const fullPath = path.join(current, entry.name); if (entry.isDirectory()) { - await walk(fullPath); - } else if (entry.isFile() && matchesPattern(entry.name, pattern)) { + if (!PRUNED_DIRS.has(entry.name)) { + await walk(fullPath); + } + } else if (entry.isFile() && matches(entry.name)) { results.push(fullPath); } } @@ -64,18 +74,21 @@ async function collectFiles( } /** - * Minimal glob pattern matching: supports leading **, *, and literal strings. - * Matches against the file name only (not the full path). + * Compile a minimal glob (leading **, *, and literal strings) into a + * file-name predicate. Matches against the file name only (not the full path). */ -function matchesPattern(name: string, pattern: string): boolean { +function compilePattern(pattern: string): (name: string) => boolean { // Strip leading **/ and */ prefixes — matching is against file name const base = pattern.replace(/^\*+\//, ""); - if (base === "*" || base === "**") return true; + if (base === "*" || base === "**") return () => true; - // Convert simple glob to regex: * → .*, . → \. - const escaped = base.replace(/\./g, "\\.").replace(/\*/g, ".*"); - return new RegExp(`^${escaped}$`).test(name); + // Escape regex metacharacters, then expand * → .* + const escaped = base + .replace(/[.+?^${}()|[\]\\]/g, "\\$&") + .replace(/\*/g, ".*"); + const regex = new RegExp(`^${escaped}$`); + return (name: string) => regex.test(name); } class RealWorkspaceProvider implements WorkspaceProvider { @@ -107,18 +120,55 @@ class RealWorkspaceProvider implements WorkspaceProvider { } } - async deleteFile(filePath: string): Promise> { + async statFile(filePath: string): Promise> { let resolved: string; try { resolved = resolveWorkspacePath(this.workspaceRoot, filePath); + } catch (err) { + return failure(fsError("FILE_READ_ERROR", "statFile", filePath, err)); + } + try { + const stat = await fs.stat(resolved); + return success({ sizeBytes: stat.size }); + } catch (err) { + return failure(fsError("FILE_READ_ERROR", "statFile", resolved, err)); + } + } + + async deleteFile(filePath: string): Promise> { + // Guard the parent directory (realpath + workspace containment), then + // unlink the entry itself. Realpathing the full candidate would follow a + // final-component symlink and delete its target instead of the link. + let target: string; + try { + const abs = path.isAbsolute(filePath) + ? path.resolve(filePath) + : path.resolve(this.workspaceRoot, filePath); + const base = path.basename(abs); + if (!base || base === "." || base === "..") { + throw new Error("Invalid delete target"); + } + const parent = resolveWorkspacePath(this.workspaceRoot, path.dirname(abs)); + target = path.join(parent, base); } catch (err) { return failure(fsError("FILE_DELETE_ERROR", "deleteFile", filePath, err)); } try { - await fs.unlink(resolved); + const stat = await fs.lstat(target); + if (stat.isDirectory()) { + return failure( + fsError( + "FILE_DELETE_ERROR", + "deleteFile", + target, + new Error("Refusing to delete a directory") + ) + ); + } + await fs.unlink(target); return success(undefined); } catch (err) { - return failure(fsError("FILE_DELETE_ERROR", "deleteFile", resolved, err)); + return failure(fsError("FILE_DELETE_ERROR", "deleteFile", target, err)); } } } diff --git a/src/infrastructure/workspace.ts b/src/infrastructure/workspace.ts deleted file mode 100644 index 0dff957..0000000 --- a/src/infrastructure/workspace.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Workspace utilities — detect workspace root, resolve .vscode/ paths. - * All workflow/agent definitions live under .vscode/ - * - * Note: In a real VS Code extension, these would use vscode.workspace APIs. - * This implementation uses Node.js fs/promises for real file I/O. - */ - -import * as fs from "fs"; -import * as path from "path"; - -/** - * Workspace paths and constants. - */ -export const WORKSPACE_PATHS = { - AGENTS_DIR: ".vscode/agents", - WORKFLOWS_DIR: ".vscode/workflows", - AGENT_SCHEMA: ".vscode/agents/.schema.json", - WORKFLOW_SCHEMA: ".vscode/workflows/.schema.json", -} as const; - -/** - * Detect workspace root by searching for .vscode directory. - * Falls back to process.cwd() when not in a VS Code extension context. - */ -export function detectWorkspaceRoot(startPath: string = "."): string { - try { - return process.cwd(); - } catch { - return startPath; - } -} - -/** - * Resolve path relative to workspace root. - */ -export function resolveWorkspacePath( - relativePath: string, - workspaceRoot?: string -): string { - return path.join(workspaceRoot ?? ".", relativePath); -} - -/** - * Get absolute path to agents directory. - */ -export function getAgentsDir(workspaceRoot?: string): string { - return resolveWorkspacePath(WORKSPACE_PATHS.AGENTS_DIR, workspaceRoot); -} - -/** - * Get absolute path to workflows directory. - */ -export function getWorkflowsDir(workspaceRoot?: string): string { - return resolveWorkspacePath(WORKSPACE_PATHS.WORKFLOWS_DIR, workspaceRoot); -} - -/** - * List all JSON files (non-recursively) in a directory. - * Returns an empty array if the directory does not exist or is unreadable. - */ -export function listJsonFiles(dirPath: string): string[] { - try { - const entries = fs.readdirSync(dirPath, { withFileTypes: true }); - return entries - .filter((e) => e.isFile() && e.name.endsWith(".json")) - .map((e) => path.join(dirPath, e.name)); - } catch { - return []; - } -} diff --git a/src/main.ts b/src/main.ts index 9f3bf8d..29956e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,6 +24,7 @@ import { readSetting } from "./infrastructure/settings"; import { getPruneConfig } from "./domains/hygiene/prune-config"; import { createRunLog } from "./infrastructure/run-log"; import { migrateLegacyIgnoreFile } from "./infrastructure/dotdir-migration"; +import { isSensitiveLoggingEnabled, sanitizeForLogs } from "./security/operation-policy"; // Presentation layer import { registerCommands, COMMAND_MAP } from "./presentation/command-registry"; @@ -72,12 +73,19 @@ export async function activate(context: vscode.ExtensionContext): Promise const runLog = createRunLog(workspaceRoot, logger); // ── Router + middleware ───────────────────────────────────────────── - const router = new CommandRouter(logger, runLog); + // security.logging.sensitive (default "redact"): error text is redacted + // before it is persisted to the run-log unless the user opts into raw logs. + const router = new CommandRouter(logger, runLog, (text) => + isSensitiveLoggingEnabled() ? text : sanitizeForLogs(text) + ); router.use(createObservabilityMiddleware(logger, telemetry)); router.use(createAuditMiddleware(logger)); // ── Domain registration ───────────────────────────────────────────── - const hygieneDomain = createHygieneDomain(workspaceProvider, logger, workspaceRoot, generateProse); + // Pass the raw (possibly undefined) folder, not the cwd fallback: the + // hygiene background scan must never crawl an arbitrary cwd when no + // workspace is open. The service skips its timer when root is undefined. + const hygieneDomain = createHygieneDomain(workspaceProvider, logger, workspaceFolder, generateProse); const gitDomain = createGitDomain( gitProvider, logger, workspaceRoot, generateProse, runLog, () => hygieneDomain.getLastScan() diff --git a/src/presentation/command-registry.ts b/src/presentation/command-registry.ts index 7632408..380b456 100644 --- a/src/presentation/command-registry.ts +++ b/src/presentation/command-registry.ts @@ -59,7 +59,9 @@ export const COMMAND_MAP: ReadonlyArray = [ { vsCodeId: "meridian.git.sessionBriefing", commandName: "git.sessionBriefing", title: "Session Briefing", showInPalette: true, requiresGit: true, icon: "$(notebook)" }, // ── Hygiene ────────────────────────────────────────────────────────────────── { vsCodeId: "meridian.hygiene.scan", commandName: "hygiene.scan", title: "Hygiene: Scan Workspace", showInPalette: true }, - { vsCodeId: "meridian.hygiene.cleanup", commandName: "hygiene.cleanup", title: "Hygiene: Cleanup", showInPalette: true }, + // cleanup is not palette-exposed: it deletes files and requires an explicit + // file list + dryRun=false; the user-facing path is hygiene.deleteFile (confirmed). + { vsCodeId: "meridian.hygiene.cleanup", commandName: "hygiene.cleanup", title: "Hygiene: Cleanup" }, { vsCodeId: "meridian.hygiene.showAnalytics", commandName: "hygiene.showAnalytics",title: "Hygiene Analytics", showInPalette: true, icon: "$(graph)" }, // hygiene.impactAnalysis — dedicated registration per ADR 005 (active-file fallback + function name prompt) ]; diff --git a/src/presentation/specialized-commands.ts b/src/presentation/specialized-commands.ts index 041edd7..3aaf858 100644 --- a/src/presentation/specialized-commands.ts +++ b/src/presentation/specialized-commands.ts @@ -45,7 +45,7 @@ export function registerSpecializedCommands( if (confirm !== "Delete") return; const freshCtx = getCommandContext(); const result = await router.dispatch( - { name: "hygiene.cleanup", params: { files: [filePath] } }, freshCtx + { name: "hygiene.cleanup", params: { files: [filePath], dryRun: false } }, freshCtx ); if (result.kind === "ok") { vscode.window.showInformationMessage(`Deleted: ${filename}`); @@ -58,7 +58,11 @@ export function registerSpecializedCommands( vscode.commands.registerCommand("meridian.hygiene.ignoreFile", async (item: FileActionItem) => { const filePath = extractFilePath(item); if (!filePath) return; - const wsRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? process.cwd(); + const wsRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if (!wsRoot) { + vscode.window.showErrorMessage("Open a workspace folder to use Meridian ignore patterns."); + return; + } const result = appendIgnorePattern(wsRoot, filePath, "file"); if (result.kind === "err") { vscode.window.showErrorMessage(result.error.message); diff --git a/src/router.ts b/src/router.ts index c816dea..81c261a 100644 --- a/src/router.ts +++ b/src/router.ts @@ -35,7 +35,17 @@ export class CommandRouter { private afterListeners: Array<(e: DispatchCompleteEvent) => void> = []; private readonly runScope = new AsyncLocalStorage<{ runId: string }>(); - constructor(logger: Logger, private readonly runLog?: RunLog) { + /** + * @param sanitizeLogText Applied to error messages before they are persisted + * to the run-log (the only router output that lands on disk). Injected so + * the router stays free of VS Code / settings dependencies; main.ts wires + * the `security.logging.sensitive` policy here. Defaults to identity. + */ + constructor( + logger: Logger, + private readonly runLog?: RunLog, + private readonly sanitizeLogText: (text: string) => string = (text) => text + ) { this.logger = logger; } @@ -236,7 +246,7 @@ export class CommandRouter { resultKind: "err", durationMs: duration, errorCode: result.error.code, - errorMessage: result.error.message, + errorMessage: this.sanitizeLogText(result.error.message), }); } return result; @@ -266,7 +276,7 @@ export class CommandRouter { resultKind: "err", durationMs: Date.now() - mwCtx.startTime, errorCode: err.code, - errorMessage: err.message, + errorMessage: this.sanitizeLogText(err.message), }); return failResult; } diff --git a/src/types.ts b/src/types.ts index 11df4eb..b988b35 100644 --- a/src/types.ts +++ b/src/types.ts @@ -99,25 +99,19 @@ export interface GitProvider { status(branch?: string): Promise>; pull(branch?: string): Promise>; commit(message: string, branch?: string): Promise>; // Returns commit hash - getChanges(): Promise>; - getDiff(paths?: string[]): Promise>; - stage(paths: string[]): Promise>; - reset(paths: string[] | { mode: string; ref: string }): Promise>; getAllChanges(): Promise>; // Get staged + unstaged changes - fetch(remote?: string): Promise>; // Fetch from remote without pulling + fetch(remote?: string): Promise>; // Fetch from remote without pulling (network-policy gated) getRemoteUrl(remote?: string): Promise>; // Get remote URL for generating diff links getCurrentBranch(): Promise>; // Get current branch name - diff(revision: string, options?: string[]): Promise>; // Advanced diff with options getRecentCommits(count: number): Promise>; - getCommitRange(from: string, to?: string): Promise>; - getMergeBase(branch: string, base?: string): Promise>; getUntrackedFiles(): Promise>; - getUncommittedDiff(paths?: string[]): Promise>; } export interface WorkspaceProvider { findFiles(pattern: string): Promise>; readFile(path: string): Promise>; + /** File size in bytes from metadata — no content read. */ + statFile(path: string): Promise>; deleteFile(path: string): Promise>; } @@ -143,11 +137,6 @@ export interface GitPullResult { message: string; } -export interface GitStageChange { - path: string; - status: "added" | "modified" | "deleted"; -} - export interface GitFileChange { path: string; status: "A" | "M" | "D" | "R"; // Add, Modify, Delete, Rename diff --git a/tests/cleanupHandler.test.ts b/tests/cleanupHandler.test.ts index 4e96f05..7e9bb29 100644 --- a/tests/cleanupHandler.test.ts +++ b/tests/cleanupHandler.test.ts @@ -51,13 +51,28 @@ describe('hygiene.cleanup', () => { } }); - it('deletes all files successfully on execute', async () => { + it('defaults to dry run when dryRun is not specified', async () => { const files = ['src/old.ts', 'src/legacy.ts']; - vi.spyOn(wp, 'deleteFile').mockResolvedValue(success(void 0)); + const deleteSpy = vi.spyOn(wp, 'deleteFile'); const handler = createCleanupHandler(wp, logger); const result = await handler(createMockContext(), { files } as any); + expect(result.kind).toBe('ok'); + if (result.kind === 'ok') { + expect(result.value.dryRun).toBe(true); + expect(result.value.deleted).toEqual([]); + } + expect(deleteSpy).not.toHaveBeenCalled(); + }); + + it('deletes all files successfully on explicit execute', async () => { + const files = ['src/old.ts', 'src/legacy.ts']; + vi.spyOn(wp, 'deleteFile').mockResolvedValue(success(void 0)); + + const handler = createCleanupHandler(wp, logger); + const result = await handler(createMockContext(), { dryRun: false, files } as any); + expect(result.kind).toBe('ok'); if (result.kind === 'ok') { expect(result.value.dryRun).toBe(false); @@ -76,7 +91,7 @@ describe('hygiene.cleanup', () => { }); const handler = createCleanupHandler(wp, logger); - const result = await handler(createMockContext(), { files } as any); + const result = await handler(createMockContext(), { dryRun: false, files } as any); expect(result.kind).toBe('ok'); if (result.kind === 'ok') { @@ -93,7 +108,7 @@ describe('hygiene.cleanup', () => { }); const handler = createCleanupHandler(wp, logger); - const result = await handler(createMockContext(), { files: ['crash.ts'] } as any); + const result = await handler(createMockContext(), { dryRun: false, files: ['crash.ts'] } as any); expect(result.kind).toBe('err'); if (result.kind === 'err') { diff --git a/tests/commandRouter.test.ts b/tests/commandRouter.test.ts index 03903a1..47647e1 100644 --- a/tests/commandRouter.test.ts +++ b/tests/commandRouter.test.ts @@ -400,6 +400,28 @@ describe('CommandRouter — run log correlation', () => { expect(events[1].errorCode).toBe('FAIL'); }); + it('applies the injected sanitizer to persisted error messages', async () => { + const logger = new MockLogger(); + const { runLog, events } = createMemoryRunLog(); + const router = new CommandRouter(logger, runLog, (text) => + text.replace(/secret-token/g, '[REDACTED]') + ); + + router.registerDomain({ + name: 'run-log-sanitize', + handlers: { + 'git.status': async () => ({ + kind: 'err' as const, + error: { code: 'FAIL', message: 'leaked secret-token in output' }, + }), + }, + }); + + await router.dispatch({ name: 'git.status', params: {} }, createMockContext()); + expect(events[1].phase).toBe('fail'); + expect(events[1].errorMessage).toBe('leaked [REDACTED] in output'); + }); + it('propagates parentRunId for nested dispatches', async () => { const logger = new MockLogger(); const { runLog, events } = createMemoryRunLog(); diff --git a/tests/fixtures.ts b/tests/fixtures.ts index 636c759..f2c0208 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -10,7 +10,6 @@ import { CommandContext, GitStatus, GitPullResult, - GitStageChange, GitFileChange, RecentCommit, DeadCodeScan, @@ -67,10 +66,7 @@ export class MockGitProvider implements GitProvider { untracked: 0, }; - private changes: GitStageChange[] = []; private allChanges: GitFileChange[] = []; - private diffOutput = ''; - private stagedPaths: string[] = []; private currentBranch = 'main'; private remoteUrl = 'https://github.com/test/repo.git'; @@ -79,18 +75,10 @@ export class MockGitProvider implements GitProvider { this.statusValue = status; } - setChanges(changes: GitStageChange[]): void { - this.changes = changes; - } - setAllChanges(changes: GitFileChange[]): void { this.allChanges = changes; } - setDiff(output: string): void { - this.diffOutput = output; - } - setCurrentBranch(branch: string): void { this.currentBranch = branch; } @@ -99,10 +87,6 @@ export class MockGitProvider implements GitProvider { this.remoteUrl = url; } - getStagedPaths(): string[] { - return this.stagedPaths; - } - async status(): Promise> { return success(this.statusValue); } @@ -120,29 +104,6 @@ export class MockGitProvider implements GitProvider { return success(hash); } - async getChanges(): Promise> { - return success(this.changes); - } - - async getDiff(): Promise> { - return success(this.diffOutput); - } - - async getUncommittedDiff(): Promise> { - return success(this.diffOutput); - } - - async stage(paths: string[]): Promise> { - this.stagedPaths = paths; - return success(void 0); - } - - async reset( - paths: string[] | { mode: string; ref: string } - ): Promise> { - return success(void 0); - } - async getAllChanges(): Promise> { return success(this.allChanges); } @@ -159,22 +120,10 @@ export class MockGitProvider implements GitProvider { return success(this.currentBranch); } - async diff(revision: string, options?: string[]): Promise> { - return success(`diff ${revision}\n${this.diffOutput}`); - } - async getRecentCommits(_count: number): Promise> { return success([]); } - async getCommitRange(_from: string, _to?: string): Promise> { - return this.getRecentCommits(20); - } - - async getMergeBase(_branch: string, _base?: string): Promise> { - return success("abc0000"); - } - async getUntrackedFiles(): Promise> { return success([]); } @@ -276,6 +225,17 @@ export class MockWorkspaceProvider implements WorkspaceProvider { return success(content); } + async statFile(path: string): Promise> { + const content = this.files.get(path); + if (content === undefined) { + return failure({ + code: 'FILE_NOT_FOUND', + message: `File not found: ${path}`, + }); + } + return success({ sizeBytes: Buffer.byteLength(content, 'utf8') }); + } + async deleteFile(path: string): Promise> { this.files.delete(path); return success(void 0); diff --git a/tests/hygieneAnalyticsHandler.test.ts b/tests/hygieneAnalyticsHandler.test.ts index 1ab65ac..968c0ea 100644 --- a/tests/hygieneAnalyticsHandler.test.ts +++ b/tests/hygieneAnalyticsHandler.test.ts @@ -38,7 +38,7 @@ describe("hygiene.showAnalytics (createShowHygieneAnalyticsHandler)", () => { byCategory: {}, }, files: [], - pruneCandiates: [], + pruneCandidates: [], largestFiles: [], oldestFiles: [], temporalData: { buckets: [], topExtensions: [] }, diff --git a/tests/impactAnalysisHandler.test.ts b/tests/impactAnalysisHandler.test.ts index a65744e..c45cea9 100644 --- a/tests/impactAnalysisHandler.test.ts +++ b/tests/impactAnalysisHandler.test.ts @@ -10,16 +10,21 @@ import type { GenerateProseFn } from "../src/types"; import { MockLogger } from "./fixtures"; // ── Mock TypeScript compiler API ────────────────────────────────────────────── -vi.mock("typescript", () => ({ - findConfigFile: vi.fn().mockReturnValue("/ws/tsconfig.json"), - readConfigFile: vi.fn().mockReturnValue({ config: {}, error: undefined }), - parseJsonConfigFileContent: vi.fn().mockReturnValue({ - fileNames: ["/ws/src/main.ts"], - options: {}, - }), - createProgram: vi.fn().mockReturnValue({}), - sys: { fileExists: vi.fn().mockReturnValue(true), readFile: vi.fn() }, -})); +// typescript is CJS; expose the mock as both named exports and `default` so +// `import * as ts` resolves the same members under vitest's CJS interop. +vi.mock("typescript", () => { + const tsMock = { + findConfigFile: vi.fn().mockReturnValue("/ws/tsconfig.json"), + readConfigFile: vi.fn().mockReturnValue({ config: {}, error: undefined }), + parseJsonConfigFileContent: vi.fn().mockReturnValue({ + fileNames: ["/ws/src/main.ts"], + options: {}, + }), + createProgram: vi.fn().mockReturnValue({}), + sys: { fileExists: vi.fn().mockReturnValue(true), readFile: vi.fn() }, + }; + return { ...tsMock, default: tsMock }; +}); // ── Mock ImpactAnalysisVisitor ──────────────────────────────────────────────── const mockVisitorAnalyze = vi.fn().mockReturnValue({ @@ -29,9 +34,10 @@ const mockVisitorAnalyze = vi.fn().mockReturnValue({ }); vi.mock("../src/domains/hygiene/impact-visitor", () => ({ - ImpactAnalysisVisitor: vi.fn().mockImplementation(() => ({ - analyze: mockVisitorAnalyze, - })), + // vitest 4: a mock must use `function`/`class` to be constructible with `new`. + ImpactAnalysisVisitor: vi.fn().mockImplementation(function () { + return { analyze: mockVisitorAnalyze }; + }), })); import { createImpactAnalysisHandler } from "../src/domains/hygiene/impact-analysis-handler"; diff --git a/tests/webview-security.test.ts b/tests/webview-security.test.ts index ee206f3..9cc19ca 100644 --- a/tests/webview-security.test.ts +++ b/tests/webview-security.test.ts @@ -2,8 +2,9 @@ import { describe, expect, it } from "vitest"; import * as fs from "fs"; import * as path from "path"; -const analyticsHtmlPaths = [ +const webviewHtmlPaths = [ path.resolve(__dirname, "../src/domains/git/analytics-ui/index.html"), + path.resolve(__dirname, "../src/domains/git/session-briefing-ui/index.html"), path.resolve(__dirname, "../src/domains/hygiene/analytics-ui/index.html"), ]; @@ -17,26 +18,40 @@ function getCspContent(html: string): string { } describe("Webview security policy", () => { - it("does not allow remote script URLs in analytics webviews", () => { - for (const htmlPath of analyticsHtmlPaths) { + it("does not allow remote script URLs in webviews", () => { + for (const htmlPath of webviewHtmlPaths) { const html = readHtml(htmlPath); expect(html).not.toMatch(/]+src="https?:\/\//); } }); - it("uses local-only script sources in analytics webview CSP", () => { - for (const htmlPath of analyticsHtmlPaths) { + it("uses nonce-only script sources in webview CSP", () => { + for (const htmlPath of webviewHtmlPaths) { const csp = getCspContent(readHtml(htmlPath)); - expect(csp).toContain("script-src {{WEBVIEW_CSP_SOURCE}} 'nonce-{{NONCE}}'"); + expect(csp).toContain("default-src 'none'"); + expect(csp).toContain("script-src 'nonce-{{NONCE}}'"); + expect(csp).not.toContain("unsafe-eval"); expect(csp).not.toContain("https://cdn.jsdelivr.net"); } }); - it("does not allow broad outbound connect in analytics webview CSP", () => { - for (const htmlPath of analyticsHtmlPaths) { + it("requires a nonce on every script tag", () => { + for (const htmlPath of webviewHtmlPaths) { + const html = readHtml(htmlPath); + const scriptTags = html.match(/]*>/g) ?? []; + expect(scriptTags.length).toBeGreaterThan(0); + for (const tag of scriptTags) { + expect(tag).toContain('nonce="{{NONCE}}"'); + } + } + }); + + it("does not allow broad outbound connect or remote images in webview CSP", () => { + for (const htmlPath of webviewHtmlPaths) { const csp = getCspContent(readHtml(htmlPath)); expect(csp).toContain("connect-src 'none'"); expect(csp).not.toContain("connect-src https:"); + expect(csp).not.toMatch(/img-src[^;]*https:/); } }); }); diff --git a/tests/workspace.test.ts b/tests/workspace.test.ts deleted file mode 100644 index f9ea016..0000000 --- a/tests/workspace.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import * as fs from "fs"; -import { listJsonFiles } from "../src/infrastructure/workspace"; - -vi.mock("fs"); - -describe("workspace utilities", () => { - beforeEach(() => { - vi.resetAllMocks(); - }); - - // ========================================================================= - // listJsonFiles - // ========================================================================= - describe("listJsonFiles", () => { - // --------------------------------------------------------------------- - // 4. Mixed files — only .json files returned as full paths - // --------------------------------------------------------------------- - it("returns only .json files as full paths", () => { - vi.mocked(fs.readdirSync).mockReturnValue([ - { name: "a.json", isFile: () => true } as any, - { name: "b.txt", isFile: () => true } as any, - { name: "c.json", isFile: () => true } as any, - { name: "dir", isFile: () => false } as any, - ]); - - const result = listJsonFiles("/workspace/.vscode/agents"); - - expect(result).toEqual([ - "/workspace/.vscode/agents/a.json", - "/workspace/.vscode/agents/c.json", - ]); - expect(fs.readdirSync).toHaveBeenCalledWith("/workspace/.vscode/agents", { - withFileTypes: true, - }); - }); - - // --------------------------------------------------------------------- - // 5. Missing directory (readdirSync throws) — returns empty array - // --------------------------------------------------------------------- - it("returns empty array when directory does not exist", () => { - vi.mocked(fs.readdirSync).mockImplementation(() => { - throw new Error("ENOENT: no such file or directory"); - }); - - const result = listJsonFiles("/nonexistent/path"); - - expect(result).toEqual([]); - }); - }); -});