diff --git a/.github/workflows/ci_osx.yml b/.github/workflows/ci_osx.yml index 2ce1eb3..eda0ac7 100644 --- a/.github/workflows/ci_osx.yml +++ b/.github/workflows/ci_osx.yml @@ -156,7 +156,11 @@ jobs: ! command -v node - name: 🧪 Testing - run: set -o pipefail && deno task test:deno 2>&1 | tee test-output.log + uses: nick-fields/retry@v3 + with: + timeout_minutes: 5 + max_attempts: 2 + command: set -o pipefail && deno task test:deno 2>&1 | tee test-output.log - name: 📤 Upload test output if: always() diff --git a/.prettierrc b/.prettierrc index cba9b4d..47f2fd0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -29,6 +29,12 @@ "options": { "trailingComma": "none" } + }, + { + "files": "*.coveragerc", + "options": { + "trailingComma": "none" + } } ] } diff --git a/CLAUDE.md b/CLAUDE.md index 4b86551..4e33473 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # @pokujs/coverage -The first code coverage package that targets Node.js (V8), Bun (JSC), and Deno (V8) simultaneously. Exclusive plugin for Poku, the only test runner that executes the same test suite natively on the three runtimes. +The first code coverage package that targets Node.js (V8), Bun (JSC), and Deno (V8) simultaneously. > Notes to the agent: > diff --git a/README.md b/README.md index eccbc2c..0d5a3c6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Enjoying **Poku**? [Give him a star to show your support](https://github.com/wel --- -☔️ **@pokujs/coverage** is a **Poku** plugin that unifies coverage collection across **Node.js**, **Deno**, and **Bun**. +☔️ **@pokujs/coverage** unifies coverage collection across **Node.js**, **Deno**, and **Bun**. > [!TIP] > @@ -29,7 +29,6 @@ Enjoying **Poku**? [Give him a star to show your support](https://github.com/wel ## Why -- Move and combine entire test suites between **Node.js**, **Deno**, and **Bun** with zero configuration changes. - Know exactly what is and isn't tested across each runtime using the same test suite. - Whether it's **CommonJS**, **ES Modules**, **TypeScript**, or both, just install and use it. @@ -43,7 +42,9 @@ Enjoying **Poku**? [Give him a star to show your support](https://github.com/wel npm i -D @pokujs/coverage ``` -### 🤹 Usage +### Usage + +#### 🐷 Poku ```json { @@ -59,6 +60,51 @@ npm i -D @pokujs/coverage --- +#### ⚡️ Vitest + +```ts +// vitest.config.ts +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + provider: 'custom', + customProviderModule: '@pokujs/coverage/vitest', + }, + }, +}); +``` + +```json +{ + "scripts": { + "test": "vitest run --coverage" + } +} +``` + +- **Node.js** only. + +--- + +#### ⌨️ CLI + +```json +{ + "scripts": { + "test:node": "coverage node --test", + "test:ava": "coverage ava", + "test:mocha": "coverage mocha", + "test:deno": "coverage deno test" + } +} +``` + +- **Bun** _(built-in)_ isn't supported in **CLI** mode yet. + +--- + ## Options | Option | Type | Default | Node.js | Deno | Bun | diff --git a/package-lock.json b/package-lock.json index 29fc969..cd50d8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,8 @@ "poku": "^4.3.0", "prettier": "^3.8.3", "tsx": "^4.21.0", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "vitest": "^4.1.5" }, "engines": { "node": ">=18.x.x" @@ -45,7 +46,8 @@ }, "peerDependencies": { "poku": "^4.3.0", - "typescript": "^5.9.3 || ^6.0.3" + "typescript": "^5.9.3 || ^6.0.3", + "vitest": "^4.0.0" }, "peerDependenciesMeta": { "poku": { @@ -53,6 +55,9 @@ }, "typescript": { "optional": true + }, + "vitest": { + "optional": true } } }, @@ -182,6 +187,40 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@epic-web/invariant": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", @@ -705,6 +744,25 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "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, + "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/@nodable/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", @@ -718,196 +776,681 @@ ], "license": "MIT" }, - "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", - "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "node_modules/@oxc-project/types": { + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^8.9.0" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@types/lcov-parse": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/lcov-parse/-/lcov-parse-1.0.2.tgz", - "integrity": "sha512-tdoxiYm04XdDEdR7UMwkWj78UAVo9U2IOcxI6tmX2/s9TK/ue/9T8gbpS/07yeWyVkVO0UumFQ5EUIBQbVejzQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "undici-types": "~7.19.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "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==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.4.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=7.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-env": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", - "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@epic-web/invariant": "^1.0.0", - "cross-spawn": "^7.0.6" - }, - "bin": { - "cross-env": "dist/bin/cross-env.js", - "cross-env-shell": "dist/bin/cross-env-shell.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "cpu": [ + "s390x" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/dom-serializer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.0.0.tgz", - "integrity": "sha512-x+9D6nkC8tdXOQUS32egtZpZFLP90+HBZmWjuT920srbJvD/zPgFB9t4k3pEhlw5BQrXStQtRc1Y1zuriXk+Nw==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "domelementtype": "^3.0.0", - "domhandler": "^6.0.0", - "entities": "^8.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20.19.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/domelementtype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz", - "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" ], - "license": "BSD-2-Clause", "engines": { - "node": ">=20.19.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/domhandler": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz", - "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "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/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", + "dev": true, + "license": "MIT" + }, + "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/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lcov-parse": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/lcov-parse/-/lcov-parse-1.0.2.tgz", + "integrity": "sha512-tdoxiYm04XdDEdR7UMwkWj78UAVo9U2IOcxI6tmX2/s9TK/ue/9T8gbpS/07yeWyVkVO0UumFQ5EUIBQbVejzQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "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==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "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": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "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/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.0.0.tgz", + "integrity": "sha512-x+9D6nkC8tdXOQUS32egtZpZFLP90+HBZmWjuT920srbJvD/zPgFB9t4k3pEhlw5BQrXStQtRc1Y1zuriXk+Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz", + "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/domhandler": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz", + "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -977,6 +1520,13 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "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/esbuild": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", @@ -1029,6 +1579,26 @@ "node": ">=6" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/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-xml-builder": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", @@ -1067,6 +1637,24 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1117,84 +1705,397 @@ "url": "https://github.com/sponsors/fb55" } ], - "license": "MIT", - "dependencies": { - "domelementtype": "^3.0.0", - "domhandler": "^6.0.0", - "domutils": "^4.0.2", - "entities": "^8.0.0" - }, + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "domutils": "^4.0.2", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jsonc.min/-/jsonc.min-1.1.2.tgz", + "integrity": "sha512-8DzkaJnjZF8Tm/pozI77pH2Gd5JaKmk/JaO/cGXxQMtzzxG3DBg7SLEoHPrtU1KY386o5jOwrdgsGHVAR05kCA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/lcov-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "lcov-parse": "bin/cli.js" + } + }, + "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": "MPL-2.0", + "dependencies": { + "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/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": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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": "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-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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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": ">=20.19.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "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": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/nanoid": { + "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": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "bin": { - "jsesc": "bin/jsesc" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc.min": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/jsonc.min/-/jsonc.min-1.1.2.tgz", - "integrity": "sha512-8DzkaJnjZF8Tm/pozI77pH2Gd5JaKmk/JaO/cGXxQMtzzxG3DBg7SLEoHPrtU1KY386o5jOwrdgsGHVAR05kCA==", - "license": "MIT", - "engines": { - "bun": ">=1.0.0", - "deno": ">=1.30.0", - "node": ">=4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wellwelwel" - } - }, - "node_modules/lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "lcov-parse": "bin/cli.js" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], "license": "MIT" }, "node_modules/packages-update": { @@ -1237,6 +2138,13 @@ "node": ">=8" } }, + "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/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1244,6 +2152,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/poku": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/poku/-/poku-4.3.0.tgz", @@ -1264,6 +2185,35 @@ "url": "https://github.com/sponsors/wellwelwel" } }, + "node_modules/postcss": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prettier": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", @@ -1300,6 +2250,40 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" + } + }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -1336,6 +2320,37 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "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" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1377,6 +2392,50 @@ ], "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/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": { + "node": ">=14.0.0" + } + }, "node_modules/toml.min": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toml.min/-/toml.min-1.0.0.tgz", @@ -1390,6 +2449,14 @@ "url": "https://github.com/sponsors/wellwelwel" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", @@ -1915,6 +2982,174 @@ "dev": true, "license": "MIT" }, + "node_modules/vite": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.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": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1931,6 +3166,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index a16eb98..a416d72 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "import": "./lib/index.js", "require": "./lib/index.cjs", "default": "./lib/index.js" + }, + "./vitest": { + "default": "./lib/integrations/vitest.js" } }, "bin": { @@ -56,7 +59,8 @@ }, "peerDependencies": { "poku": "^4.3.0", - "typescript": "^5.9.3 || ^6.0.3" + "typescript": "^5.9.3 || ^6.0.3", + "vitest": "^4.0.0" }, "peerDependenciesMeta": { "poku": { @@ -64,8 +68,17 @@ }, "typescript": { "optional": true + }, + "vitest": { + "optional": true } }, + "dependencies": { + "acorn": "^8.16.0", + "jsonc.min": "^1.1.2", + "toml.min": "^1.0.0", + "yaml.min": "^1.0.0" + }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.7.1", "@jridgewell/trace-mapping": "^0.3.31", @@ -83,7 +96,8 @@ "poku": "^4.3.0", "prettier": "^3.8.3", "tsx": "^4.21.0", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "vitest": "^4.1.5" }, "publishConfig": { "access": "public" @@ -104,6 +118,9 @@ "node", "bun", "deno", + "typescript", + "tsx", + "jsx", "cross-runtime", "cross-platform", "interoperability", @@ -128,12 +145,12 @@ "c8", "monocart-coverage-reports", "monocart", - "one-double-zero" - ], - "dependencies": { - "acorn": "^8.16.0", - "jsonc.min": "^1.1.2", - "toml.min": "^1.0.0", - "yaml.min": "^1.0.0" - } + "one-double-zero", + "vitest", + "jest", + "mocha", + "ava", + "type", + "types" + ] } diff --git a/src/@types/remap.ts b/src/@types/remap.ts index 6d3bf88..fa50200 100644 --- a/src/@types/remap.ts +++ b/src/@types/remap.ts @@ -20,3 +20,10 @@ export type OriginalFileState = { lineStartTable: number[]; functions: V8Function[]; }; + +export type GeneratedNameHelper = { + state: OriginalFileState; + startOffset: number; + endOffset: number; + count: number; +}; diff --git a/src/@types/vitest.ts b/src/@types/vitest.ts new file mode 100644 index 0000000..148a6c2 --- /dev/null +++ b/src/@types/vitest.ts @@ -0,0 +1,60 @@ +import type { + BaseCoverageProvider, + CoverageProvider, + CoverageProviderModule, + ReportContext, + ResolvedCoverageOptions, + Vite, + Vitest, +} from 'vitest/node'; +import type { V8ScriptCoverage } from './v8.js'; + +export type { + BaseCoverageProvider, + CoverageProvider, + CoverageProviderModule, + ReportContext, + ResolvedCoverageOptions, + Vite, + Vitest, +}; + +export type IstanbulCoverageMap = ReturnType< + BaseCoverageProvider['createCoverageMap'] +>; + +export type VitestThresholds = { + 100?: boolean; + perFile?: boolean; + statements?: number; + functions?: number; + branches?: number; + lines?: number; +}; + +export type VitestRuntimeOptions = { + isolate: boolean; +}; + +export type VitestWorkerState = { + session: import('node:inspector/promises').Session | null; + enabled: boolean; +}; + +export type VitestModuleExecutionEntry = { + startOffset: number; +}; + +export type VitestModuleExecutionInfo = Map; + +export type VitestTakeCoverageOptions = { + moduleExecutionInfo?: VitestModuleExecutionInfo; +}; + +export type VitestScriptCoverageWithOffset = V8ScriptCoverage & { + startOffset: number; +}; + +export type VitestRawCoverageWithOffsets = { + result: VitestScriptCoverageWithOffset[]; +}; diff --git a/src/configs/vitestrc.ts b/src/configs/vitestrc.ts new file mode 100644 index 0000000..18ece19 --- /dev/null +++ b/src/configs/vitestrc.ts @@ -0,0 +1,75 @@ +import type { CoverageThresholds } from '../@types/check-coverage.js'; +import type { CoverageOptions } from '../@types/coverage.js'; +import type { + ResolvedCoverageOptions, + VitestThresholds, +} from '../@types/vitest.js'; + +const mapThresholds = ( + thresholds: ResolvedCoverageOptions['thresholds'] +): CoverageThresholds | number | undefined => { + if (thresholds === undefined) return undefined; + + const known: VitestThresholds = thresholds; + if (known[100] === true) return 100; + + const mapped: CoverageThresholds = Object.create(null); + let hasAny = false; + + if (typeof known.statements === 'number') { + mapped.statements = known.statements; + hasAny = true; + } + + if (typeof known.functions === 'number') { + mapped.functions = known.functions; + hasAny = true; + } + + if (typeof known.branches === 'number') { + mapped.branches = known.branches; + hasAny = true; + } + + if (typeof known.lines === 'number') { + mapped.lines = known.lines; + hasAny = true; + } + + if (typeof known.perFile === 'boolean') { + mapped.perFile = known.perFile; + hasAny = true; + } + + return hasAny ? mapped : undefined; +}; + +const extract = (source: ResolvedCoverageOptions): CoverageOptions => { + const mapped: CoverageOptions = Object.create(null); + + if (source.include !== undefined) mapped.include = source.include; + if (source.exclude !== undefined) mapped.exclude = source.exclude; + if (source.reportsDirectory !== undefined) + mapped.reportsDirectory = source.reportsDirectory; + if (source.skipFull !== undefined) mapped.skipFull = source.skipFull; + if (source.watermarks !== undefined) mapped.watermarks = source.watermarks; + if (source.excludeAfterRemap !== undefined) + mapped.excludeAfterRemap = source.excludeAfterRemap; + if (source.clean !== undefined) mapped.clean = source.clean; + + if (Array.isArray(source.reporter)) { + mapped.reporter = source.reporter.map((entry) => + typeof entry === 'string' ? entry : entry[0] + ); + } else if (typeof source.reporter === 'string') { + mapped.reporter = source.reporter; + } + + const thresholds = mapThresholds(source.thresholds); + + if (thresholds !== undefined) mapped.checkCoverage = thresholds; + + return mapped; +}; + +export const vitestrc = { extract } as const; diff --git a/src/converters/shared/function-names.ts b/src/converters/shared/function-names.ts index e75f023..be6a68c 100644 --- a/src/converters/shared/function-names.ts +++ b/src/converters/shared/function-names.ts @@ -2,6 +2,7 @@ import type { Node } from 'acorn'; import type { TypedNode } from '../../@types/acorn-nodes.js'; import type { FunctionLocation } from '../../@types/function-names.js'; import type { FileAggregation } from '../../@types/v8.js'; +import { offsets } from '../../utils/offsets.js'; import { astCache } from './ast-cache.js'; const SKIP_KEYS: ReadonlySet = new Set([ @@ -120,6 +121,7 @@ const collectFunctionLocations = (program: Node): FunctionLocation[] => { const resolve = (aggregation: FileAggregation, source: string): void => { const program = astCache.parse(source); const locations = program === null ? null : collectFunctionLocations(program); + const lineStartTable = offsets.lineStarts(source); for (const functionEntry of aggregation.functions.values()) { if (functionEntry.isModuleFunction) continue; @@ -136,6 +138,28 @@ const resolve = (aggregation: FileAggregation, source: string): void => { functionEntry.name = match.inferredName; } + for (const functionEntry of aggregation.functions.values()) { + if (functionEntry.isModuleFunction) continue; + if (functionEntry.name !== '') continue; + if (locations === null) continue; + + const match = locations.find( + (location) => + location.startOffset >= functionEntry.startOffset && + location.endOffset <= functionEntry.endOffset + ); + if (match === undefined) continue; + + const location = offsets.toLocation(match.startOffset, lineStartTable); + + functionEntry.line = location.line; + functionEntry.column = location.column; + functionEntry.startOffset = match.startOffset; + functionEntry.endOffset = match.endOffset; + + if (match.inferredName !== '') functionEntry.name = match.inferredName; + } + const anonymousEntries = Array.from(aggregation.functions.values()) .filter( (functionEntry) => diff --git a/src/converters/shared/remap.ts b/src/converters/shared/remap.ts index efbd219..6e7af93 100644 --- a/src/converters/shared/remap.ts +++ b/src/converters/shared/remap.ts @@ -1,4 +1,6 @@ +import type { LineColumn } from '../../@types/offsets.js'; import type { + GeneratedNameHelper, OriginalFileState, RemapInputs, RemappedScriptEntry, @@ -6,18 +8,67 @@ import type { import type { InvalidOriginalMapping, OriginalMapping, + TraceMap, } from '../../@types/source-map.js'; import type { V8Range, V8ScriptCoverage } from '../../@types/v8.js'; import { isAbsolute, sep } from 'node:path'; import { pathToFileURL } from 'node:url'; import { offsets } from '../../utils/offsets.js'; import { paths } from '../../utils/paths.js'; +import { traceMap } from '../../utils/source-map/index.js'; const isValidMapping = ( mapping: OriginalMapping | InvalidOriginalMapping ): mapping is OriginalMapping => mapping.source !== null && mapping.line !== null && mapping.column !== null; +const lookupWithFallback = ( + traceMapInstance: TraceMap, + location: LineColumn, + totalLines: number, + direction: 'forward' | 'backward' +): OriginalMapping | InvalidOriginalMapping => { + const primary = traceMapInstance.originalPositionFor(location); + + if (isValidMapping(primary)) return primary; + + const sameLine = traceMapInstance.originalPositionFor({ + ...location, + bias: traceMap.LEAST_UPPER_BOUND, + }); + + if (isValidMapping(sameLine)) return sameLine; + + if (direction === 'forward') { + for ( + let probeLine = location.line + 1; + probeLine <= totalLines; + probeLine++ + ) { + const probe = traceMapInstance.originalPositionFor({ + line: probeLine, + column: 0, + bias: traceMap.LEAST_UPPER_BOUND, + }); + + if (isValidMapping(probe)) return probe; + } + + return sameLine; + } + + for (let probeLine = location.line - 1; probeLine >= 1; probeLine--) { + const probe = traceMapInstance.originalPositionFor({ + line: probeLine, + column: 0, + }); + + if (isValidMapping(probe)) return probe; + } + + return sameLine; +}; + const pathFromResolvedSource = ( resolvedSource: string, cwd: string @@ -72,7 +123,8 @@ const remapRange = ( range: V8Range, transpiledLineStarts: number[], inputs: RemapInputs, - stateMap: Map + stateMap: Map, + allowEndFallback: boolean ): { state: OriginalFileState; range: V8Range } | undefined => { const inclusiveEndOffset = Math.max(range.startOffset, range.endOffset - 1); @@ -86,14 +138,21 @@ const remapRange = ( transpiledLineStarts ); - const startMapping = - inputs.traceMapInstance.originalPositionFor(startLocation); - - const endMapping = inputs.traceMapInstance.originalPositionFor(endLocation); + const totalLines = Math.max(0, transpiledLineStarts.length - 1); + const startMapping = lookupWithFallback( + inputs.traceMapInstance, + startLocation, + totalLines, + 'backward' + ); + const endMapping = lookupWithFallback( + inputs.traceMapInstance, + endLocation, + totalLines, + 'forward' + ); if (!isValidMapping(startMapping)) return undefined; - if (!isValidMapping(endMapping)) return undefined; - if (startMapping.source !== endMapping.source) return undefined; const originalPath = pathFromResolvedSource(startMapping.source, inputs.cwd); if (originalPath === undefined) return undefined; @@ -106,15 +165,21 @@ const remapRange = ( state = ensureFileState(stateMap, originalPath, originalSource); } + if (isValidMapping(endMapping) && startMapping.source !== endMapping.source) + return undefined; + if (!isValidMapping(endMapping) && !allowEndFallback) return undefined; + const originalStart = offsets.toOffset( { line: startMapping.line, column: startMapping.column }, state.lineStartTable ); - const originalEnd = offsets.toOffset( - { line: endMapping.line, column: endMapping.column + 1 }, - state.lineStartTable - ); + const originalEnd = isValidMapping(endMapping) + ? offsets.toOffset( + { line: endMapping.line, column: endMapping.column + 1 }, + state.lineStartTable + ) + : state.originalSource.length; if (originalEnd <= originalStart) return undefined; @@ -128,6 +193,82 @@ const remapRange = ( }; }; +const collectGeneratedNameHelper = ( + scriptFunction: V8ScriptCoverage['functions'][number], + transpiledLineStarts: number[], + inputs: RemapInputs, + stateMap: Map, + generatedNameHelpers: Map +): void => { + if (scriptFunction.functionName !== '') return; + + const outerRange = scriptFunction.ranges[0]; + + if (outerRange === undefined) return; + if (outerRange.startOffset === 0) return; + + const startLocation = offsets.toLocation( + outerRange.startOffset, + transpiledLineStarts + ); + const startMapping = lookupWithFallback( + inputs.traceMapInstance, + startLocation, + Math.max(0, transpiledLineStarts.length - 1), + 'backward' + ); + + if (isValidMapping(startMapping)) return; + + const inclusiveEndOffset = Math.max( + outerRange.startOffset, + outerRange.endOffset - 1 + ); + const endLocation = offsets.toLocation( + inclusiveEndOffset, + transpiledLineStarts + ); + const endMapping = lookupWithFallback( + inputs.traceMapInstance, + endLocation, + Math.max(0, transpiledLineStarts.length - 1), + 'forward' + ); + + if (!isValidMapping(endMapping)) return; + + const originalPath = pathFromResolvedSource(endMapping.source, inputs.cwd); + if (originalPath === undefined) return; + + let state = stateMap.get(originalPath); + if (state === undefined) { + const originalSource = lookupOriginalSource(inputs, endMapping.source); + if (originalSource === undefined) return; + + state = ensureFileState(stateMap, originalPath, originalSource); + } + + const originalStart = offsets.toOffset( + { line: endMapping.line, column: endMapping.column }, + state.lineStartTable + ); + const originalEnd = Math.min(originalStart + 1, state.originalSource.length); + if (originalEnd <= originalStart) return; + + const existing = generatedNameHelpers.get(originalPath); + if (existing !== undefined) { + existing.count++; + return; + } + + generatedNameHelpers.set(originalPath, { + state, + startOffset: originalStart, + endOffset: originalEnd, + count: 1, + }); +}; + const buildSyntheticScript = ( sourceScript: V8ScriptCoverage, state: OriginalFileState @@ -171,7 +312,11 @@ const injectBaselineRanges = ( const project = (inputs: RemapInputs): RemappedScriptEntry[] => { const transpiledLineStarts = inputs.transpiledLineStarts; const stateMap = new Map(); + const generatedNameHelpers = new Map(); const baselineCount = baselineCountForScript(inputs.script); + const hasNameHelper = inputs.script.functions.some( + (scriptFunction) => scriptFunction.functionName === '__name' + ); for (const scriptFunction of inputs.script.functions) { const remappedRanges: V8Range[] = []; @@ -185,9 +330,30 @@ const project = (inputs: RemapInputs): RemappedScriptEntry[] => { rangeIndex++ ) { const range = scriptFunction.ranges[rangeIndex]; - - const outcome = remapRange(range, transpiledLineStarts, inputs, stateMap); - if (outcome === undefined) continue; + const allowEndFallback = !( + scriptFunction.functionName === '' && + rangeIndex === 0 && + range.startOffset === 0 + ); + + const outcome = remapRange( + range, + transpiledLineStarts, + inputs, + stateMap, + allowEndFallback + ); + if (outcome === undefined) { + if (!hasNameHelper && rangeIndex === 0) + collectGeneratedNameHelper( + scriptFunction, + transpiledLineStarts, + inputs, + stateMap, + generatedNameHelpers + ); + continue; + } if (targetState === undefined) { targetState = outcome.state; @@ -227,6 +393,20 @@ const project = (inputs: RemapInputs): RemappedScriptEntry[] => { }); } + for (const generatedNameHelper of generatedNameHelpers.values()) { + generatedNameHelper.state.functions.push({ + functionName: '__name', + isBlockCoverage: false, + ranges: [ + { + startOffset: generatedNameHelper.startOffset, + endOffset: generatedNameHelper.endOffset, + count: generatedNameHelper.count, + }, + ], + }); + } + injectBaselineRanges(stateMap, baselineCount); const entries: RemappedScriptEntry[] = []; diff --git a/src/core.ts b/src/core.ts index 03c6882..a258a57 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,5 +1,7 @@ export { config } from './configs/index.js'; +export { vitestrc } from './configs/vitestrc.js'; export { bun } from './runtimes/bun.js'; export { deno } from './runtimes/deno.js'; export { node } from './runtimes/node.js'; +export { lifecycle } from './runtimes/lifecycle/index.js'; export { state } from './runtimes/lifecycle/state.js'; diff --git a/src/integrations/vitest/index.ts b/src/integrations/vitest/index.ts new file mode 100644 index 0000000..432c397 --- /dev/null +++ b/src/integrations/vitest/index.ts @@ -0,0 +1,396 @@ +import type { + CoverageContext, + CoverageOptions, +} from '../../@types/coverage.js'; +import type { Reporter } from '../../@types/reporters.js'; +import type { EncodedSourceMap } from '../../@types/source-map.js'; +import type { V8Function, V8Range, V8ScriptCoverage } from '../../@types/v8.js'; +import type { + CoverageProvider, + CoverageProviderModule, + IstanbulCoverageMap, + ReportContext, + ResolvedCoverageOptions, + Vite, + Vitest, + VitestRawCoverageWithOffsets, + VitestRuntimeOptions, + VitestScriptCoverageWithOffset, + VitestTakeCoverageOptions, + VitestWorkerState, +} from '../../@types/vitest.js'; +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import inspector from 'node:inspector/promises'; +import { join } from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; +import { BaseCoverageProvider as BaseProvider } from 'vitest/node'; +import { config, lifecycle, state, vitestrc } from '../../core.js'; +import { traceMap } from '../../utils/source-map/index.js'; +import { sourceMapTrim } from '../../utils/source-map/trim.js'; + +const WORKER_STATE_KEY = '__pokujsCoverageVitestWorker__'; +const NO_OP_DEBUG: ((..._logs: unknown[]) => void) & { enabled: false } = + Object.assign((..._logs: unknown[]) => undefined, { + enabled: false as const, + }); + +const getWorkerState = (): VitestWorkerState => { + const root = globalThis as Record; + let container = root[WORKER_STATE_KEY] as VitestWorkerState | undefined; + + if (!container) { + container = { session: null, enabled: false }; + root[WORKER_STATE_KEY] = container; + } + + return container; +}; + +const workerOnly = (entry: V8ScriptCoverage): boolean => { + if (!entry.url.startsWith('file://')) return false; + if (entry.url.includes('/node_modules/')) return false; + if (entry.url.includes('/@id/@vitest/')) return false; + if (entry.url.includes('/@vite/client')) return false; + if (entry.url.includes('__vitest__')) return false; + return true; +}; + +const stripQuery = (url: string): string => { + const queryIndex = url.indexOf('?'); + return queryIndex === -1 ? url : url.slice(0, queryIndex); +}; + +const computeLineLengths = (code: string): number[] => { + const lengths: number[] = []; + let lineStart = 0; + + for (let charIndex = 0; charIndex < code.length; charIndex++) { + if (code.charCodeAt(charIndex) === 10) { + lengths.push(charIndex - lineStart); + + lineStart = charIndex + 1; + } + } + + lengths.push(code.length - lineStart); + return lengths; +}; + +const shiftFunctions = ( + functions: V8Function[], + wrapperLength: number +): V8Function[] => { + const adjustedFunctions: V8Function[] = []; + + for (const scriptFunction of functions) { + const adjustedRanges: V8Range[] = []; + + for (const functionRange of scriptFunction.ranges) { + const adjustedStart = Math.max( + 0, + functionRange.startOffset - wrapperLength + ); + const adjustedEnd = Math.max(0, functionRange.endOffset - wrapperLength); + + if (adjustedEnd <= adjustedStart) continue; + + adjustedRanges.push({ + startOffset: adjustedStart, + endOffset: adjustedEnd, + count: functionRange.count, + }); + } + + if (adjustedRanges.length === 0) continue; + + adjustedFunctions.push({ + functionName: scriptFunction.functionName, + isBlockCoverage: scriptFunction.isBlockCoverage, + ranges: adjustedRanges, + }); + } + + return adjustedFunctions; +}; + +const findTargetSourceIndex = ( + map: EncodedSourceMap, + filePath: string, + mapUrl: string +): number => { + const resolver = traceMap.create(map, mapUrl); + const fileUrl = mapUrl.startsWith('file://') ? fileURLToPath(mapUrl) : mapUrl; + + for ( + let sourceIndex = 0; + sourceIndex < resolver.resolvedSources.length; + sourceIndex++ + ) { + const resolvedSource = resolver.resolvedSources[sourceIndex]; + + if (resolvedSource === filePath) return sourceIndex; + if (resolvedSource === fileUrl) return sourceIndex; + } + + return -1; +}; + +class PokuCoverageProvider extends BaseProvider implements CoverageProvider { + readonly name = 'v8' as const; + private coverageState = state.create(); + private envelopeCounter = 0; + private resolvedOptions: CoverageOptions = Object.create(null); + + version = ''; + + initialize(ctx: Vitest): void { + this.version = ctx.version; + + // Overrides `'v8' | 'istanbul'`; + Object.defineProperty(this, 'name', { + value: '@pokujs/coverage', + enumerable: true, + configurable: true, + writable: false, + }); + + this._initialize(ctx); + + this.coverageState.cwd = ctx.config.root; + this.coverageState.enabled = true; + + const cliConfig = process.argv + .find((arg) => arg.startsWith('--coverageConfig')) + ?.split('=')[1]; + const vitestOptions = vitestrc.extract(this.options); + const fileConfig = config.load(ctx.config.root, cliConfig); + const merged: CoverageOptions = { ...vitestOptions, ...fileConfig }; + const reporterList: Reporter[] = Array.isArray(merged.reporter) + ? merged.reporter + : merged.reporter !== undefined + ? [merged.reporter] + : []; + const keptReporters = reporterList.filter( + (reporter) => reporter !== 'v8' && reporter !== 'jsc' + ); + + this.resolvedOptions = { ...merged, reporter: keptReporters }; + } + + resolveOptions(): ResolvedCoverageOptions { + return { ...this.options, provider: 'custom' }; + } + + createCoverageMap(): IstanbulCoverageMap { + return Object.create(null); + } + + async generateCoverage(_reportContext: ReportContext): Promise { + const envelopeDir = join(this.coverageFilesDirectory, 'pokujs'); + const collected: VitestScriptCoverageWithOffset[] = []; + let resolvedProject: BaseProvider['ctx']['projects'][number] | undefined; + + mkdirSync(envelopeDir, { recursive: true }); + + this.coverageState.tempDir = envelopeDir; + this.coverageState.userProvidedTempDir = false; + + await this.readCoverageFiles({ + onFileRead: (rawCoverage) => { + if (!Array.isArray(rawCoverage?.result)) return; + + for (const entry of rawCoverage.result) collected.push(entry); + }, + onFinished: async (project, environment) => { + if (resolvedProject === undefined || environment === 'ssr') + resolvedProject = project; + }, + onDebug: NO_OP_DEBUG, + }); + + const project = resolvedProject ?? this.ctx.getRootProject(); + const viteEnvironment = project.vite.environments.ssr; + if (!viteEnvironment) + throw new Error( + '@pokujs/coverage: SSR environment not available on the resolved Vite project' + ); + + for (const entry of collected) { + const cleanUrl = stripQuery(entry.url); + if (!cleanUrl.startsWith('file://')) continue; + + const filePath = fileURLToPath(cleanUrl); + let transform: Vite.TransformResult | null = null; + + try { + transform = await viteEnvironment.transformRequest(filePath); + } catch { + transform = null; + } + + if (!transform?.code) continue; + if (!transform.map || typeof transform.map !== 'object') continue; + + const rawMap = transform.map as Partial; + if (typeof rawMap.mappings !== 'string') continue; + if (!Array.isArray(rawMap.sources)) continue; + + const fullMap: EncodedSourceMap = { + version: 3, + names: rawMap.names ?? [], + sources: rawMap.sources, + sourcesContent: rawMap.sourcesContent, + mappings: rawMap.mappings, + ...(rawMap.file !== undefined + ? { file: rawMap.file } + : Object.create(null)), + ...(rawMap.sourceRoot !== undefined + ? { sourceRoot: rawMap.sourceRoot } + : Object.create(null)), + ...(rawMap.ignoreList !== undefined + ? { ignoreList: rawMap.ignoreList } + : Object.create(null)), + }; + + const targetIndex = findTargetSourceIndex(fullMap, filePath, entry.url); + if (targetIndex === -1) continue; + + const trimmedMap = sourceMapTrim.keepOnly(fullMap, targetIndex); + + trimmedMap.sources = [filePath]; + + if ( + !Array.isArray(trimmedMap.sourcesContent) || + typeof trimmedMap.sourcesContent[0] !== 'string' + ) { + try { + trimmedMap.sourcesContent = [readFileSync(filePath, 'utf8')]; + } catch { + continue; + } + } + + const lineLengths = computeLineLengths(transform.code); + + const adjustedScript: V8ScriptCoverage = { + scriptId: entry.scriptId, + url: entry.url, + functions: + entry.startOffset > 0 + ? shiftFunctions(entry.functions, entry.startOffset) + : entry.functions, + }; + + const envelope = { + result: [adjustedScript], + 'source-map-cache': { + [entry.url]: { data: trimmedMap, lineLengths }, + }, + }; + + const envelopePath = join( + envelopeDir, + `pokujs-envelope-${this.envelopeCounter++}.json` + ); + + writeFileSync(envelopePath, JSON.stringify(envelope)); + } + + return Object.create(null); + } + + async generateReports(_coverageMap: unknown): Promise { + const optionsForTeardown: CoverageOptions = { + ...this.resolvedOptions, + clean: false, + tempDirectory: undefined, + }; + + const context: CoverageContext = { + cwd: this.ctx.config.root, + runtime: 'node', + }; + + lifecycle.teardown(context, optionsForTeardown, this.coverageState, 'node'); + } +} + +const startCoverage = async ( + runtimeOptions: VitestRuntimeOptions +): Promise => { + const workerState = getWorkerState(); + + if (runtimeOptions.isolate === false && workerState.enabled) return; + + workerState.enabled = true; + workerState.session ||= new inspector.Session(); + + workerState.session.connect(); + await workerState.session.post('Profiler.enable'); + await workerState.session.post('Profiler.startPreciseCoverage', { + callCount: true, + detailed: true, + }); +}; + +const takeCoverage = async ( + options?: VitestTakeCoverageOptions +): Promise => { + const workerState = getWorkerState(); + if (!workerState.session) return { result: [] }; + + const response = await workerState.session.post( + 'Profiler.takePreciseCoverage' + ); + const scripts = response.result as V8ScriptCoverage[]; + const filtered: VitestScriptCoverageWithOffset[] = []; + + for (const entry of scripts) { + if (!workerOnly(entry)) continue; + + let startOffset = 0; + + if (options?.moduleExecutionInfo && entry.url.startsWith('file://')) { + try { + const filePath = fileURLToPath(stripQuery(entry.url)); + const executionEntry = options.moduleExecutionInfo.get(filePath); + + if (executionEntry) startOffset = executionEntry.startOffset; + } catch { + startOffset = 0; + } + } + + filtered.push({ ...entry, startOffset }); + } + + return { result: filtered }; +}; + +const stopCoverage = async ( + runtimeOptions: VitestRuntimeOptions +): Promise => { + if (runtimeOptions.isolate === false) return; + + const workerState = getWorkerState(); + if (!workerState.session) return; + + await workerState.session.post('Profiler.stopPreciseCoverage'); + await workerState.session.post('Profiler.disable'); + workerState.session.disconnect(); + + workerState.session = null; + workerState.enabled = false; +}; + +const getProvider = (): CoverageProvider => new PokuCoverageProvider(); + +const vitestModule: CoverageProviderModule = { + startCoverage, + takeCoverage, + stopCoverage, + getProvider, +}; + +export default vitestModule; diff --git a/src/runtimes/lifecycle/index.ts b/src/runtimes/lifecycle/index.ts index 26f4072..8bffa02 100644 --- a/src/runtimes/lifecycle/index.ts +++ b/src/runtimes/lifecycle/index.ts @@ -36,14 +36,14 @@ const setup = ( state.userProvidedTempDir = false; } + state.enabled = true; + if (envVar) { state.originalEnv = process.env[envVar]; process.env[envVar] = state.tempDir; } if (runtime === 'node') sourceMaps.enable(state); - - state.enabled = true; }; const teardown = ( diff --git a/src/utils/source-map/trim.ts b/src/utils/source-map/trim.ts new file mode 100644 index 0000000..9cfe6d0 --- /dev/null +++ b/src/utils/source-map/trim.ts @@ -0,0 +1,67 @@ +import type { + EncodedSourceMap, + SourceMapSegment, +} from '../../@types/source-map.js'; +import { SOURCES_INDEX } from './segment.js'; +import { encodeMappings } from './vlq-encode.js'; +import { decodeMappings } from './vlq.js'; + +const keepOnly = ( + map: EncodedSourceMap, + targetIndex: number +): EncodedSourceMap => { + if (targetIndex < 0 || targetIndex >= map.sources.length) + throw new Error( + `sourceMapTrim.keepOnly: targetIndex ${targetIndex} out of range for sources.length ${map.sources.length}` + ); + + const decoded = decodeMappings(map.mappings); + const trimmedLines: SourceMapSegment[][] = new Array(decoded.length); + + for (let lineIndex = 0; lineIndex < decoded.length; lineIndex++) { + const lineSegments = decoded[lineIndex]; + const keptSegments: SourceMapSegment[] = []; + + for ( + let segmentIndex = 0; + segmentIndex < lineSegments.length; + segmentIndex++ + ) { + const segment = lineSegments[segmentIndex]; + + if (segment.length === 1) continue; + if (segment[SOURCES_INDEX] !== targetIndex) continue; + if (segment.length === 4) { + keptSegments.push([segment[0], 0, segment[2], segment[3]]); + continue; + } + + keptSegments.push([segment[0], 0, segment[2], segment[3], segment[4]]); + } + + trimmedLines[lineIndex] = keptSegments; + } + + const trimmedSourcesContent = + map.sourcesContent === undefined + ? undefined + : [map.sourcesContent[targetIndex] ?? null]; + + const trimmed: EncodedSourceMap = { + version: 3, + names: map.names, + sources: [map.sources[targetIndex]], + mappings: encodeMappings(trimmedLines), + }; + + if (map.file !== undefined) trimmed.file = map.file; + if (map.sourceRoot !== undefined) trimmed.sourceRoot = map.sourceRoot; + if (trimmedSourcesContent !== undefined) + trimmed.sourcesContent = trimmedSourcesContent; + if (map.ignoreList !== undefined) + trimmed.ignoreList = map.ignoreList.includes(targetIndex) ? [0] : []; + + return trimmed; +}; + +export const sourceMapTrim = { keepOnly } as const; diff --git a/src/utils/source-map/vlq-encode.ts b/src/utils/source-map/vlq-encode.ts new file mode 100644 index 0000000..929bde1 --- /dev/null +++ b/src/utils/source-map/vlq-encode.ts @@ -0,0 +1,107 @@ +/* + * Adapted from @jridgewell/sourcemap-codec's `vlq.ts` and `sourcemap-codec.ts`. + * Original: https://github.com/jridgewell/sourcemaps + * Copyright 2024 Justin Ridgewell + * MIT License + */ + +import type { SourceMapSegment } from '../../@types/source-map.js'; + +const BASE64_CHARSET = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +const INT_TO_CHAR = new Uint8Array(64); + +for (let charsetIndex = 0; charsetIndex < BASE64_CHARSET.length; charsetIndex++) + INT_TO_CHAR[charsetIndex] = BASE64_CHARSET.charCodeAt(charsetIndex); + +const COMMA_CODE = 44; +const SEMICOLON_CODE = 59; + +const encodeSignedNumber = (byteBuffer: number[], value: number): void => { + let payload = value < 0 ? (-value << 1) | 1 : value << 1; + + do { + let vlqUnit = payload & 0b011111; + payload >>>= 5; + + if (payload > 0) vlqUnit |= 0b100000; + + byteBuffer.push(INT_TO_CHAR[vlqUnit]); + } while (payload > 0); +}; + +const FLUSH_CHUNK_SIZE = 16 * 1024; + +const flushChunk = (chunk: number[]): string => { + const text = String.fromCharCode.apply(null, chunk); + + chunk.length = 0; + + return text; +}; + +export const encodeMappings = ( + decodedMappings: readonly (readonly SourceMapSegment[])[] +): string => { + const chunk: number[] = []; + const segments: string[] = []; + + let previousSourcesIndex = 0; + let previousSourceLine = 0; + let previousSourceColumn = 0; + let previousNamesIndex = 0; + + for (let lineIndex = 0; lineIndex < decodedMappings.length; lineIndex++) { + if (lineIndex > 0) chunk.push(SEMICOLON_CODE); + + const lineSegments = decodedMappings[lineIndex]; + let previousGeneratedColumn = 0; + + for ( + let segmentIndex = 0; + segmentIndex < lineSegments.length; + segmentIndex++ + ) { + const segment = lineSegments[segmentIndex]; + + if (segmentIndex > 0) chunk.push(COMMA_CODE); + + const generatedColumn = segment[0]; + + encodeSignedNumber(chunk, generatedColumn - previousGeneratedColumn); + + previousGeneratedColumn = generatedColumn; + + if (segment.length === 1) { + if (chunk.length >= FLUSH_CHUNK_SIZE) segments.push(flushChunk(chunk)); + continue; + } + + const sourcesIndex = segment[1]; + const sourceLine = segment[2]; + const sourceColumn = segment[3]; + + encodeSignedNumber(chunk, sourcesIndex - previousSourcesIndex); + encodeSignedNumber(chunk, sourceLine - previousSourceLine); + encodeSignedNumber(chunk, sourceColumn - previousSourceColumn); + + previousSourcesIndex = sourcesIndex; + previousSourceLine = sourceLine; + previousSourceColumn = sourceColumn; + + if (segment.length === 5) { + const namesIndex = segment[4]; + + encodeSignedNumber(chunk, namesIndex - previousNamesIndex); + + previousNamesIndex = namesIndex; + } + + if (chunk.length >= FLUSH_CHUNK_SIZE) segments.push(flushChunk(chunk)); + } + } + + if (chunk.length > 0) segments.push(flushChunk(chunk)); + return segments.join(''); +}; diff --git a/tools/build.mts b/tools/build.mts index fafe514..92353ec 100644 --- a/tools/build.mts +++ b/tools/build.mts @@ -39,11 +39,14 @@ const buildOptions: BuildOptions = { 'poku/plugin', 'toml.min', 'yaml.min', + 'vitest', + 'vitest/node', ], }; await rm('lib', { recursive: true, force: true }); await mkdir('lib', { recursive: true }); +await mkdir('lib/integrations', { recursive: true }); await Promise.all([ // Index build({ @@ -96,6 +99,15 @@ await Promise.all([ external: ['../core.js'], }), + // Vitest + build({ + ...buildOptions, + format: 'esm', + entryPoints: ['src/integrations/vitest/index.ts'], + outfile: 'lib/integrations/vitest.js', + plugins: [externalizeCore('../core.js')], + }), + // Declarations writeFile('lib/index.d.ts', dtsBundle, 'utf-8'), ]);