diff --git a/.gitignore b/.gitignore index dffd2ab72..f5cc1bdf2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ packages/cli/skills .next **/*.tgz coverage +.coverage-v8 react-grab-extension.zip apps/website/public/script.js test-results diff --git a/packages/playwright-coverage/package.json b/packages/playwright-coverage/package.json new file mode 100644 index 000000000..1b689e808 --- /dev/null +++ b/packages/playwright-coverage/package.json @@ -0,0 +1,46 @@ +{ + "name": "@react-grab/playwright-coverage", + "version": "0.1.47", + "private": true, + "description": "Collect native V8 JS coverage from a Playwright run and remap it onto original sources", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/aidenybai/react-grab.git" + }, + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "scripts": { + "build": "vp pack", + "typecheck": "tsc --noEmit", + "lint": "vp lint", + "format": "vp fmt", + "check": "vp check" + }, + "dependencies": { + "monocart-coverage-reports": "^2.12.12" + }, + "devDependencies": { + "@playwright/test": "^1.59.1", + "@types/node": "^25.6.2", + "vite-plus": "^0.1.20" + }, + "peerDependencies": { + "@playwright/test": ">=1.40.0" + } +} diff --git a/packages/playwright-coverage/src/fixture.ts b/packages/playwright-coverage/src/fixture.ts new file mode 100644 index 000000000..773b5dd1f --- /dev/null +++ b/packages/playwright-coverage/src/fixture.ts @@ -0,0 +1,62 @@ +import { randomUUID } from "node:crypto"; +import { mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import type { Page } from "@playwright/test"; + +export interface V8CoverageEntry { + url: string; + source?: string; + scriptId?: string; + functions?: unknown[]; +} + +export const cleanRawCoverage = (rawDir: string): void => { + rmSync(rawDir, { recursive: true, force: true }); + mkdirSync(rawDir, { recursive: true }); +}; + +export const writeRawCoverage = (rawDir: string, entries: V8CoverageEntry[]): void => { + if (!Array.isArray(entries) || entries.length === 0) return; + try { + mkdirSync(rawDir, { recursive: true }); + writeFileSync(join(rawDir, `${randomUUID()}.json`), JSON.stringify(entries)); + } catch { + // Best-effort: coverage must never fail a real test. + } +}; + +/** + * Per-test V8 capture primitive: start JS coverage, run the test body (`use`), + * then write the dump to `rawDir`. Compose it into an auto fixture and gate the + * import on your coverage flag so non-coverage runs never load this module. + * `page.coverage` only exists on Chromium, so capture self-guards and is + * entirely best-effort — it must never fail a real test. + */ +export const captureCoverage = async ( + page: Page, + rawDir: string, + use: () => Promise, +): Promise => { + let started = false; + if (page.coverage) { + try { + await page.coverage.startJSCoverage({ resetOnNavigation: false }); + started = true; + } catch { + started = false; + } + } + + try { + await use(); + } finally { + // finally so a failing test still flushes the coverage it did exercise. + if (started) { + try { + writeRawCoverage(rawDir, await page.coverage.stopJSCoverage()); + } catch { + // ignore: coverage is best-effort + } + } + } +}; diff --git a/packages/playwright-coverage/src/index.ts b/packages/playwright-coverage/src/index.ts new file mode 100644 index 000000000..e9db4e96f --- /dev/null +++ b/packages/playwright-coverage/src/index.ts @@ -0,0 +1,4 @@ +export { captureCoverage, cleanRawCoverage, writeRawCoverage } from "./fixture.js"; +export type { V8CoverageEntry } from "./fixture.js"; +export { generateCoverageReport } from "./report.js"; +export type { CoverageSummary, GenerateCoverageOptions } from "./report.js"; diff --git a/packages/playwright-coverage/src/report.ts b/packages/playwright-coverage/src/report.ts new file mode 100644 index 000000000..938709a06 --- /dev/null +++ b/packages/playwright-coverage/src/report.ts @@ -0,0 +1,249 @@ +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { dirname, isAbsolute, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import type { V8CoverageEntry } from "./fixture.js"; + +const SOURCE_MAP_RE = /\/\/[#@]\s*sourceMappingURL=(\S+)/; +const DEFAULT_REPORTS = ["v8", "console-details", "lcovonly"]; + +interface ScriptCoverage { + functions: unknown[]; +} + +// monocart's `Util.mergeV8Coverage` is the same bcoe/v8-coverage range-tree merge +// that `@bcoe/v8-coverage` ships, but it is part of the dependency we already +// pull in for reporting — so we reuse it instead of adding a second copy. It is +// real at runtime but missing from monocart's published `.d.ts`. +type MergeV8Coverage = (scriptCovs: ScriptCoverage[]) => { functions?: unknown[] }; + +interface UrlAccumulator { + source: string; + merged: ScriptCoverage; +} + +interface FileLineSummary { + covered: number; + total: number; +} + +export interface CoverageSummary { + fileCount: number; + coveredLines: number; + totalLines: number; + pctLines: number; +} + +export interface GenerateCoverageOptions { + /** Directory holding the per-test raw V8 dumps written during the run. */ + rawDir: string; + /** Where monocart writes the generated reports. */ + outputDir: string; + /** Base directory monocart resolves report paths against. Defaults to `process.cwd()`. */ + baseDir?: string; + /** Report name shown in the html report. */ + name?: string; + /** monocart report formats. Defaults to `["v8", "console-details", "lcovonly"]`. */ + reports?: string[]; + /** Keep only the source files you care about (receives an absolute path). */ + sourceFilter?: (sourcePath: string) => boolean; + /** Verbose logging + dump the merged script URLs. Defaults to `Boolean(process.env.COVERAGE_DEBUG)`. */ + debug?: boolean; +} + +/** Turn a served script URL into a local filesystem path, if it maps to one. */ +const urlToLocalPath = (url: string): string | null => { + if (url.startsWith("file://")) { + try { + return fileURLToPath(url); + } catch { + return null; + } + } + // Vite serves out-of-root files (e.g. a linked dist) as http://host/@fs/. + try { + const { pathname } = new URL(url); + const fsMarker = "/@fs"; + const fsIndex = pathname.indexOf(`${fsMarker}/`); + // Keep the marker's trailing slash so the result stays an absolute path + // (`/@fs/Users/x` -> `/Users/x`), not a relative one. + if (fsIndex !== -1) return decodeURIComponent(pathname.slice(fsIndex + fsMarker.length)); + } catch { + return null; + } + if (isAbsolute(url) && existsSync(url)) return url; + return null; +}; + +const inlineMapDataUri = (mapJson: string): string => + `data:application/json;base64,${Buffer.from(mapJson).toString("base64")}`; + +const applyInlineMap = (source: string, inlined: string): string => { + if (SOURCE_MAP_RE.test(source)) { + return source.replace(SOURCE_MAP_RE, `//# sourceMappingURL=${inlined}`); + } + return `${source}\n//# sourceMappingURL=${inlined}`; +}; + +/** + * Rewrite a source map's `sources` to absolute paths relative to the map file. + * The remapper otherwise resolves dist's `../src/index.ts` against `baseDir` + * and loses the package segment (yielding `packages/src/...`); absolute sources + * remove that ambiguity so coverage lands on `packages//src/...`. + */ +const absolutizeMapSources = (mapJson: string, mapPath: string): string => { + const map = JSON.parse(mapJson) as { sources?: string[]; sourceRoot?: string }; + const mapDir = dirname(mapPath); + const sourceRoot = map.sourceRoot ?? ""; + map.sources = (map.sources ?? []).map((source) => + isAbsolute(source) ? source : resolve(mapDir, sourceRoot, source), + ); + map.sourceRoot = ""; + return JSON.stringify(map); +}; + +/** + * Read every per-test raw V8 file and collapse them to one entry per script. + * + * Only scripts whose served URL resolves to an on-disk file with a sibling + * `.map` are kept (the bundled dist we can remap); everything else — the app, + * vite client, framework chunks — is dropped up front. Each test repeats the + * full source of every script it loaded, so with hundreds of tests holding all + * dumps would exhaust the heap. We merge each file's ranges into the running + * accumulator immediately (via monocart's V8 range-tree merge) and keep a single + * source per URL, bounding memory to the number of unique remappable scripts. + */ +const mergeRawCoverage = ( + rawDir: string, + mergeV8Coverage: MergeV8Coverage, +): Map => { + const accumulators = new Map(); + + let files: string[]; + try { + files = readdirSync(rawDir); + } catch { + return accumulators; + } + + for (const file of files) { + if (!file.endsWith(".json")) continue; + let entries: V8CoverageEntry[]; + try { + entries = JSON.parse(readFileSync(join(rawDir, file), "utf8")); + } catch { + continue; + } + if (!Array.isArray(entries)) continue; + + for (const entry of entries) { + if (!entry.url || typeof entry.source !== "string") continue; + const localPath = urlToLocalPath(entry.url); + if (!localPath) continue; + if (!existsSync(`${localPath}.map`)) continue; + + const scriptCov: ScriptCoverage = { functions: entry.functions ?? [] }; + const accumulator = accumulators.get(localPath); + if (accumulator) { + accumulator.merged = { + functions: mergeV8Coverage([accumulator.merged, scriptCov]).functions ?? [], + }; + } else { + accumulators.set(localPath, { source: entry.source, merged: scriptCov }); + } + } + } + + return accumulators; +}; + +/** + * Playwright's V8 coverage carries each script's `source` but not its source + * map. A bundler typically serves an *external* `.map` sibling that the remapper + * can't fetch over http, so inline it (with absolutized sources) and rewrite the + * URL to the real path. This is what lets V8 hit counts on bundled dist remap + * onto the original `src/*`. + */ +const toReportEntries = (accumulators: Map): V8CoverageEntry[] => { + const entries: V8CoverageEntry[] = []; + for (const [localPath, accumulator] of accumulators) { + const siblingMapPath = `${localPath}.map`; + try { + const mapJson = absolutizeMapSources(readFileSync(siblingMapPath, "utf8"), siblingMapPath); + entries.push({ + url: localPath, + source: applyInlineMap(accumulator.source, inlineMapDataUri(mapJson)), + scriptId: "0", + functions: accumulator.merged.functions, + }); + } catch { + // skip: a script we can't pair with a readable map can't be remapped + } + } + return entries; +}; + +/** monocart's `data.lines` value is a hit count or a "covered/total" string. */ +const summarizeFileLines = (lines: Record): FileLineSummary => { + let covered = 0; + let total = 0; + for (const value of Object.values(lines)) { + total++; + const hits = typeof value === "number" ? value : Number(value.split("/")[0]); + if (Number.isFinite(hits) && hits > 0) covered++; + } + return { covered, total }; +}; + +/** + * Merge the per-test raw V8 dumps, inline the dist source maps, and remap the + * V8 byte ranges back onto `src/*` using monocart-coverage-reports as the remap + * engine. monocart is loaded lazily so importing the fixture in test workers + * never pulls in the heavy report dependency. + */ +export const generateCoverageReport = async ( + options: GenerateCoverageOptions, +): Promise => { + const debug = options.debug ?? Boolean(process.env.COVERAGE_DEBUG); + + const { default: MCR } = await import("monocart-coverage-reports"); + const mergeV8Coverage = (MCR.Util as unknown as { mergeV8Coverage: MergeV8Coverage }) + .mergeV8Coverage; + + const accumulators = mergeRawCoverage(options.rawDir, mergeV8Coverage); + if (accumulators.size === 0) return null; + + if (debug) { + const paths = [...accumulators.keys()].sort(); + console.log(`[coverage-debug] ${paths.length} merged scripts:`); + for (const scriptPath of paths) console.log(` ${scriptPath}`); + } + + const mcr = MCR({ + name: options.name ?? "playwright coverage", + outputDir: options.outputDir, + baseDir: options.baseDir ?? process.cwd(), + reports: options.reports ?? DEFAULT_REPORTS, + cleanCache: true, + logging: debug ? "debug" : "info", + sourceFilter: options.sourceFilter, + }); + + await mcr.add(toReportEntries(accumulators)); + const results = await mcr.generate(); + const files = results?.files ?? []; + + let coveredLines = 0; + let totalLines = 0; + for (const file of files) { + const summary = summarizeFileLines(file.data?.lines ?? {}); + coveredLines += summary.covered; + totalLines += summary.total; + } + + return { + fileCount: files.length, + coveredLines, + totalLines, + pctLines: totalLines === 0 ? 0 : (coveredLines / totalLines) * 100, + }; +}; diff --git a/packages/playwright-coverage/tsconfig.json b/packages/playwright-coverage/tsconfig.json new file mode 100644 index 000000000..5cb7777ca --- /dev/null +++ b/packages/playwright-coverage/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "noEmit": true, + "types": ["node"] + }, + "include": ["src"] +} diff --git a/packages/playwright-coverage/vite.config.ts b/packages/playwright-coverage/vite.config.ts new file mode 100644 index 000000000..082a6b7df --- /dev/null +++ b/packages/playwright-coverage/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + pack: { + entry: ["src/index.ts"], + format: ["esm", "cjs"], + dts: true, + clean: true, + sourcemap: false, + platform: "node", + fixedExtension: false, + }, +}); diff --git a/packages/react-grab/e2e/coverage-config.ts b/packages/react-grab/e2e/coverage-config.ts new file mode 100644 index 000000000..6f1680171 --- /dev/null +++ b/packages/react-grab/e2e/coverage-config.ts @@ -0,0 +1,18 @@ +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const E2E_DIR = dirname(fileURLToPath(import.meta.url)); +const PACKAGE_DIR = resolve(E2E_DIR, ".."); + +export const REPO_ROOT = resolve(PACKAGE_DIR, "../.."); +export const COVERAGE_OUTPUT_DIR = resolve(PACKAGE_DIR, "coverage"); +// Per-test raw V8 dumps land here; each Playwright worker is its own process, +// so we write one file per test and merge them in the report step. Kept OUTSIDE +// COVERAGE_OUTPUT_DIR so monocart's report `clean` doesn't wipe it mid-run. +export const COVERAGE_RAW_DIR = resolve(PACKAGE_DIR, ".coverage-v8"); + +export const isReactGrabSource = (sourcePath: string): boolean => { + const normalized = sourcePath.split("\\").join("/"); + if (normalized.includes("/node_modules/")) return false; + return normalized.includes("react-grab/src/") || normalized.includes("packages/react-grab/src/"); +}; diff --git a/packages/react-grab/e2e/coverage-setup.ts b/packages/react-grab/e2e/coverage-setup.ts new file mode 100644 index 000000000..40387fe6e --- /dev/null +++ b/packages/react-grab/e2e/coverage-setup.ts @@ -0,0 +1,10 @@ +import { cleanRawCoverage } from "@react-grab/playwright-coverage"; +import { COVERAGE_RAW_DIR } from "./coverage-config.js"; + +// Playwright globalSetup (only wired under COVERAGE): clear stale per-test V8 +// dumps once before the suite so the merged report reflects just this run. +const globalSetup = async (): Promise => { + cleanRawCoverage(COVERAGE_RAW_DIR); +}; + +export default globalSetup; diff --git a/packages/react-grab/e2e/coverage-teardown.ts b/packages/react-grab/e2e/coverage-teardown.ts new file mode 100644 index 000000000..929fb5a22 --- /dev/null +++ b/packages/react-grab/e2e/coverage-teardown.ts @@ -0,0 +1,35 @@ +import { relative } from "node:path"; +import { generateCoverageReport } from "@react-grab/playwright-coverage"; +import { + COVERAGE_OUTPUT_DIR, + COVERAGE_RAW_DIR, + isReactGrabSource, + REPO_ROOT, +} from "./coverage-config.js"; + +// Playwright globalTeardown: runs once after the whole suite (even on failures), +// so the report reflects whatever tests actually drove a page. Only wired in +// when COVERAGE is set, so a normal run never touches it. +const globalTeardown = async (): Promise => { + const summary = await generateCoverageReport({ + rawDir: COVERAGE_RAW_DIR, + outputDir: COVERAGE_OUTPUT_DIR, + baseDir: REPO_ROOT, + name: "react-grab coverage", + sourceFilter: isReactGrabSource, + }); + + if (!summary) { + console.warn("No V8 coverage captured (Chromium-only; check that tests ran and drove a page)."); + return; + } + + console.log( + `\nreact-grab line coverage: ${summary.pctLines.toFixed(2)}% (${summary.coveredLines}/${summary.totalLines} lines across ${summary.fileCount} files)`, + ); + console.log( + `Reports written to ${relative(process.cwd(), COVERAGE_OUTPUT_DIR) || COVERAGE_OUTPUT_DIR}`, + ); +}; + +export default globalTeardown; diff --git a/packages/react-grab/e2e/fixtures.ts b/packages/react-grab/e2e/fixtures.ts index fffd6ffc4..cd3c4372e 100644 --- a/packages/react-grab/e2e/fixtures.ts +++ b/packages/react-grab/e2e/fixtures.ts @@ -1,4 +1,7 @@ import { test as base, expect, Page, Locator } from "@playwright/test"; +import { COVERAGE_RAW_DIR } from "./coverage-config.js"; + +const COVERAGE_ENABLED = Boolean(process.env.COVERAGE); const ATTRIBUTE_NAME = "data-react-grab"; const DEFAULT_KEY_HOLD_DURATION_MS = 200; @@ -1813,8 +1816,25 @@ const createReactGrabPageObject = ( }; }; -export const test = base.extend<{ reactGrab: ReactGrabPageObject }>({ - reactGrab: async ({ page }, use) => { +export const test = base.extend<{ reactGrab: ReactGrabPageObject; coverageCapture: void }>({ + // Captures V8 JS coverage for every test under COVERAGE. `reactGrab` depends + // on it so it starts before the page navigates. The coverage package is + // imported lazily (and built) only on coverage runs, so normal and perf runs + // never need its dist; off-coverage this is a no-op. + coverageCapture: [ + async ({ page }, use) => { + if (!COVERAGE_ENABLED) { + await use(); + return; + } + const { captureCoverage } = await import("@react-grab/playwright-coverage"); + await captureCoverage(page, COVERAGE_RAW_DIR, use); + }, + { auto: true }, + ], + + reactGrab: async ({ page, coverageCapture }, use) => { + void coverageCapture; const waitForApiReady = async () => { await page.waitForFunction( () => { diff --git a/packages/react-grab/package.json b/packages/react-grab/package.json index b3b7c114b..162dba68e 100644 --- a/packages/react-grab/package.json +++ b/packages/react-grab/package.json @@ -84,9 +84,12 @@ "prebuild": "mkdir -p dist && tailwindcss -i ./src/styles.css -o ./dist/styles.css -m && tsx scripts/css-rem-to-px.ts", "build": "NODE_ENV=production vp pack", "build:profiling": "pnpm run prebuild && NODE_ENV=profiling REACT_GRAB_NO_MINIFY=true REACT_GRAB_SOURCEMAP=true vp pack", + "build:coverage": "pnpm run prebuild && NODE_ENV=production REACT_GRAB_NO_MINIFY=true REACT_GRAB_SOURCEMAP=true vp pack", "dev": "concurrently \"pnpm:css:watch\" \"vp pack --watch\"", "test": "vp test run tests && playwright test", "test:unit": "vp test run tests", + "pretest:coverage": "pnpm --filter @react-grab/playwright-coverage build", + "test:coverage": "COVERAGE=1 playwright test", "test:perf": "playwright test --grep @perf --reporter=list", "test:perf:baseline": "PERF_LABEL=baseline playwright test --grep @perf --reporter=list", "test:expect": "bun e2e/react-grab.expect.ts", @@ -103,6 +106,7 @@ "@babel/core": "^7.29.0", "@babel/preset-typescript": "^7.28.5", "@playwright/test": "^1.59.1", + "@react-grab/playwright-coverage": "workspace:*", "@tailwindcss/cli": "^4.3.0", "@types/babel__core": "^7.20.5", "@types/node": "^25.6.2", diff --git a/packages/react-grab/playwright.config.ts b/packages/react-grab/playwright.config.ts index 96392cea9..50a0c7846 100644 --- a/packages/react-grab/playwright.config.ts +++ b/packages/react-grab/playwright.config.ts @@ -4,6 +4,13 @@ import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// COVERAGE wires a sourcemapped/unminified build, the per-test V8 capture +// fixture, and globalSetup/globalTeardown that clear the raw dir and write the +// report. The coverage package (and its build) is only touched on these runs: +// globalSetup/Teardown and the fixture all load it lazily, so normal and perf +// runs never need @react-grab/playwright-coverage to be built. +const isCoverageRun = Boolean(process.env.COVERAGE); + const VITE_URL = "http://localhost:5175"; const NEXT_URL = "http://localhost:5176"; @@ -38,13 +45,22 @@ const nextProject: Project = { testMatch: [NEXT_SPEC_PATTERN, CROSS_FRAMEWORK_PATTERN], }; +// Under COVERAGE the dist must carry source maps (and stay unminified) so V8 +// byte ranges remap cleanly back onto src/*.ts(x). +const reactGrabBuildCommand = isCoverageRun + ? "pnpm --filter react-grab build:coverage" + : "pnpm --filter react-grab build"; + const viteWebServer = { // Builds react-grab so the dev server picks up whichever src is checked out // (the perf workflow swaps it to the base ref). react-grab's build does not // clean dist, so the Next server reading it concurrently is safe. - command: "pnpm --filter react-grab build && pnpm dev", + command: `${reactGrabBuildCommand} && pnpm dev`, url: VITE_URL, - reuseExistingServer: !process.env.CI, + // Under COVERAGE never reuse a running server: it may serve a minified dist + // with no sibling .map, which silently yields empty/misleading coverage. + // Forcing a fresh start guarantees the sourcemapped build:coverage runs. + reuseExistingServer: !process.env.CI && !isCoverageRun, cwd: path.resolve(__dirname, "../../apps/e2e-app-vite"), timeout: 60_000, }; @@ -52,7 +68,7 @@ const viteWebServer = { const nextWebServer = { command: "pnpm dev", url: NEXT_URL, - reuseExistingServer: !process.env.CI, + reuseExistingServer: !process.env.CI && !isCoverageRun, cwd: path.resolve(__dirname, "../../apps/e2e-app-next"), timeout: 120_000, }; @@ -69,6 +85,8 @@ export default defineConfig({ trace: "on-first-retry", permissions: ["clipboard-read", "clipboard-write"], }, + globalSetup: isCoverageRun ? path.resolve(__dirname, "e2e/coverage-setup.ts") : undefined, + globalTeardown: isCoverageRun ? path.resolve(__dirname, "e2e/coverage-teardown.ts") : undefined, projects: isPerfRun ? viteProjects : [...viteProjects, nextProject], webServer: isPerfRun ? [viteWebServer] : [viteWebServer, nextWebServer], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bd00a5af..8339c8637 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,7 +35,7 @@ importers: version: 6.0.3 vite-plus: specifier: ^0.1.20 - version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) + version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) apps/e2e-app-next: dependencies: @@ -169,7 +169,7 @@ importers: version: 1.4.1(@date-fns/tz@1.4.1)(@types/react@19.2.14)(date-fns@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@vercel/analytics': specifier: ^2.0.1 - version: 2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + version: 2.0.1(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) calligraph: specifier: ^1.4.1 version: 1.4.1(motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -190,7 +190,7 @@ importers: version: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) nuqs: specifier: ^2.8.9 - version: 2.8.9(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) + version: 2.8.9(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6) react: specifier: 19.2.6 version: 19.2.6 @@ -355,7 +355,7 @@ importers: version: 10.1.0 vite-plus: specifier: ^0.1.20 - version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) + version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) packages/grab: dependencies: @@ -363,6 +363,22 @@ importers: specifier: workspace:* version: link:../cli + packages/playwright-coverage: + dependencies: + monocart-coverage-reports: + specifier: ^2.12.12 + version: 2.12.12 + devDependencies: + '@playwright/test': + specifier: ^1.59.1 + version: 1.59.1 + '@types/node': + specifier: ^25.6.2 + version: 25.6.2 + vite-plus: + specifier: ^0.1.20 + version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) + packages/react-grab: dependencies: '@react-grab/cli': @@ -384,6 +400,9 @@ importers: '@playwright/test': specifier: ^1.59.1 version: 1.59.1 + '@react-grab/playwright-coverage': + specifier: workspace:* + version: link:../playwright-coverage '@tailwindcss/cli': specifier: ^4.3.0 version: 4.3.0 @@ -416,7 +435,7 @@ importers: version: 4.21.0 vite-plus: specifier: ^0.1.20 - version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) + version: 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) packages: @@ -634,6 +653,10 @@ packages: '@types/react': optional: true + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@bufbuild/protobuf@2.12.0': resolution: {integrity: sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA==} @@ -1398,6 +1421,10 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + '@joshua.litt/get-ripgrep@0.0.3': resolution: {integrity: sha512-rycdieAKKqXi2bsM7G2ayDiNk5CAX8ZOzsTQsirfOqUKPef04Xw40BWGGyimaOOuvPgLWYt3tPnLLG3TvPXi5Q==} @@ -3585,6 +3612,55 @@ packages: babel-plugin-react-compiler: optional: true + '@vitest/coverage-istanbul@4.1.9': + resolution: {integrity: sha512-4a7DsIwycTf4eYwEDtnMfMV8H80KSKH9PuMHhqL5SwPZzDyUKq2X/TPCVZ7NqIuSz7UbZckmEmkip6iZBI/gEA==} + peerDependencies: + vitest: 4.1.9 + + '@vitest/coverage-v8@4.1.5': + resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==} + peerDependencies: + '@vitest/browser': 4.1.5 + vitest: 4.1.5 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + '@voidzero-dev/vite-plus-core@0.1.22': resolution: {integrity: sha512-OC7tChagbJCoY7YKzD5MuyxJO1km5IF42B3ltZoQ9Twc8UuPrMuWZrVoP984tJKYd/gFJuQFM/lrbNtBm9kyDg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3783,6 +3859,14 @@ packages: peerDependencies: acorn: ^8 + acorn-loose@8.5.2: + resolution: {integrity: sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -3887,6 +3971,9 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} @@ -4102,6 +4189,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -4269,6 +4360,9 @@ packages: resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} engines: {node: '>=18'} + console-grid@2.2.4: + resolution: {integrity: sha512-OLjCRTiHhOpTRo9lQp/2FgJDyq5uQHwkEmVJulEnQ6JVf27oKKzXHZnNOv/e72V4++UdMZCrDWtvXW5sx4lyQg==} + content-disposition@1.1.0: resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} @@ -4569,6 +4663,9 @@ packages: effect@4.0.0-beta.35: resolution: {integrity: sha512-64j8dgJmoEMeq6Y3WLYcZIRqPZ5E/lqnULCf6QW5te3hQ/sa13UodWLGwBEviEqBoq72U8lArhVX+T7ntzhJGQ==} + eight-colors@1.3.3: + resolution: {integrity: sha512-4B54S2Qi4pJjeHmCbDIsveQZWQ/TSSQng4ixYJ9/SYHHpeS5nYK0pzcHvWzWUfRsvJQjwoIENhAwqg59thQceg==} + electron-to-chromium@1.5.353: resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} @@ -4636,6 +4733,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4697,6 +4797,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -4747,6 +4850,10 @@ packages: playwright: optional: true + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + express-rate-limit@8.5.1: resolution: {integrity: sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==} engines: {node: '>= 16'} @@ -4852,6 +4959,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + foreground-child@4.0.3: + resolution: {integrity: sha512-yeXZaNbCBGaT9giTpLPBdtedzjwhlJBUoL/R4BVQU5mn0TQXOHwVIl1Q2DMuBIdNno4ktA1abZ7dQFVxD6uHxw==} + engines: {node: '>=16'} + form-data-encoder@4.1.0: resolution: {integrity: sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==} engines: {node: '>= 18'} @@ -5123,6 +5234,9 @@ packages: html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -5448,6 +5562,18 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@4.2.3: resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} engines: {node: 20 || >=22} @@ -5459,6 +5585,9 @@ packages: jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -5698,6 +5827,9 @@ packages: peerDependencies: react: 19.2.6 + lz-utils@2.1.1: + resolution: {integrity: sha512-d3Thjos0PSJQAoyMj6vipSSrtrRHS7DImqUNR8x9NW3+zQIftPIbMJAWhi5nPdg5Q9zHz6lxtN8kp/VdMlhi/Q==} + macos-version@6.0.0: resolution: {integrity: sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5705,6 +5837,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -5840,6 +5979,13 @@ packages: module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + monocart-coverage-reports@2.12.12: + resolution: {integrity: sha512-d9FdUr2dn58Crweon0IE0zVi8r/i4vLvfvb2G7eL5vL5LfrxCB2X6F6qzuiwV6RioA4zbAI//7CYi6LjCvN3zA==} + hasBin: true + + monocart-locator@1.0.3: + resolution: {integrity: sha512-pe29W2XAoA1WQmZZqxXoP7s06ZEXUhcb81086v68cqjk1HnVL7Q/iU/WJnnetxjPcLqwb4qG8vaSGUOMQU602g==} + motion-dom@12.38.0: resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} @@ -6276,6 +6422,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -6822,6 +6971,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -6932,6 +7084,9 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -7151,6 +7306,10 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + tldts-core@7.0.30: resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} @@ -7470,6 +7629,47 @@ packages: vite: optional: true + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + 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 + watchpack@2.4.4: resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} @@ -7542,6 +7742,11 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + widest-line@5.0.0: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} @@ -7914,6 +8119,9 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@bcoe/v8-coverage@1.0.2': + optional: true + '@bufbuild/protobuf@2.12.0': {} '@changesets/apply-release-plan@7.1.1': @@ -8748,6 +8956,9 @@ snapshots: dependencies: minipass: 7.1.3 + '@istanbuljs/schema@0.1.6': + optional: true + '@joshua.litt/get-ripgrep@0.0.3': dependencies: '@lvce-editor/verror': 1.7.0 @@ -10795,7 +11006,7 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@vercel/analytics@2.0.1(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': + '@vercel/analytics@2.0.1(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)': optionalDependencies: next: 16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react: 19.2.6 @@ -10807,6 +11018,99 @@ snapshots: optionalDependencies: babel-plugin-react-compiler: 1.0.0 + '@vitest/coverage-istanbul@4.1.9(vitest@4.1.9)': + dependencies: + '@babel/core': 7.29.0 + '@istanbuljs/schema': 0.1.6 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.1 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4)) + transitivePeerDependencies: + - supports-color + optional: true + + '@vitest/coverage-v8@4.1.5(vitest@4.1.9)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.5 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.1 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4)) + optional: true + + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + optional: true + + '@vitest/mocker@4.1.9(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.14.5(@types/node@25.6.2)(typescript@6.0.3) + vite: 6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4) + optional: true + + '@vitest/pretty-format@4.1.5': + dependencies: + tinyrainbow: 3.1.0 + optional: true + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + optional: true + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + optional: true + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + optional: true + + '@vitest/spy@4.1.9': + optional: true + + '@vitest/utils@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + optional: true + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + optional: true + '@voidzero-dev/vite-plus-core@0.1.22(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(yaml@2.8.4)': dependencies: '@oxc-project/runtime': 0.129.0 @@ -10841,7 +11145,7 @@ snapshots: '@voidzero-dev/vite-plus-linux-x64-musl@0.1.20': optional: true - '@voidzero-dev/vite-plus-test@0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4)': + '@voidzero-dev/vite-plus-test@0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4)': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 @@ -10860,6 +11164,8 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.6.2 + '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-v8': 4.1.5(vitest@4.1.9) transitivePeerDependencies: - '@arethetypeswrong/core' - '@tsdown/css' @@ -10930,6 +11236,14 @@ snapshots: dependencies: acorn: 8.16.0 + acorn-loose@8.5.2: + dependencies: + acorn: 8.16.0 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + acorn@8.16.0: {} adm-zip@0.5.17: {} @@ -11015,6 +11329,13 @@ snapshots: dependencies: tslib: 2.8.1 + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + optional: true + async-lock@1.4.1: {} async@3.2.6: {} @@ -11225,6 +11546,9 @@ snapshots: ccount@2.0.1: {} + chai@6.2.2: + optional: true + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -11392,6 +11716,8 @@ snapshots: graceful-fs: 4.2.11 xdg-basedir: 5.1.0 + console-grid@2.2.4: {} + content-disposition@1.1.0: {} content-type@1.0.5: {} @@ -11652,6 +11978,8 @@ snapshots: uuid: 13.0.2 yaml: 2.8.4 + eight-colors@1.3.3: {} + electron-to-chromium@1.5.353: {} embla-carousel-react@8.6.0(react@19.2.6): @@ -11704,6 +12032,9 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.1.0: + optional: true + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -11801,6 +12132,11 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + optional: true + esutils@2.0.3: {} etag@1.8.1: {} @@ -11884,6 +12220,9 @@ snapshots: - utf-8-validate - zod + expect-type@1.3.0: + optional: true + express-rate-limit@8.5.1(express@5.2.1): dependencies: express: 5.2.1 @@ -12026,6 +12365,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + foreground-child@4.0.3: + dependencies: + signal-exit: 4.1.0 + form-data-encoder@4.1.0: {} form-data@2.5.5: @@ -12379,6 +12722,8 @@ snapshots: html-entities@2.6.0: {} + html-escaper@2.0.2: {} + html-escaper@3.0.3: {} html-to-text@9.0.5: @@ -12644,6 +12989,19 @@ snapshots: isobject@3.0.1: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@4.2.3: dependencies: '@isaacs/cliui': 9.0.0 @@ -12652,6 +13010,9 @@ snapshots: jose@6.2.3: {} + js-tokens@10.0.0: + optional: true + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -12869,6 +13230,8 @@ snapshots: dependencies: react: 19.2.6 + lz-utils@2.1.1: {} + macos-version@6.0.0: dependencies: semver: 7.8.0 @@ -12877,6 +13240,17 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.8.0 + make-error@1.3.6: {} marked@15.0.12: {} @@ -12992,6 +13366,23 @@ snapshots: module-details-from-path@1.0.4: {} + monocart-coverage-reports@2.12.12: + dependencies: + acorn: 8.16.0 + acorn-loose: 8.5.2 + acorn-walk: 8.3.5 + commander: 14.0.3 + console-grid: 2.2.4 + eight-colors: 1.3.3 + foreground-child: 4.0.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + lz-utils: 2.1.1 + monocart-locator: 1.0.3 + + monocart-locator@1.0.3: {} + motion-dom@12.38.0: dependencies: motion-utils: 12.36.0 @@ -13176,7 +13567,7 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.8.9(next@16.2.6(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6): + nuqs@2.8.9(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6): dependencies: '@standard-schema/spec': 1.0.0 react: 19.2.6 @@ -13481,6 +13872,9 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: + optional: true + peberminta@0.9.0: {} pend@1.2.0: {} @@ -14257,6 +14651,9 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: + optional: true + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -14387,6 +14784,9 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + stackback@0.0.2: + optional: true + standard-as-callback@2.1.0: {} statuses@2.0.2: {} @@ -14625,6 +15025,9 @@ snapshots: tinypool@2.1.0: {} + tinyrainbow@3.1.0: + optional: true + tldts-core@7.0.30: {} tldts@7.0.30: @@ -14915,11 +15318,11 @@ snapshots: - unplugin-unused - unrun - vite-plus@0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4): + vite-plus@0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4): dependencies: '@oxc-project/types': 0.127.0 '@voidzero-dev/vite-plus-core': 0.1.22(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(yaml@2.8.4) - '@voidzero-dev/vite-plus-test': 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) + '@voidzero-dev/vite-plus-test': 0.1.20(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4))(yaml@2.8.4) oxfmt: 0.46.0 oxlint: 1.61.0(oxlint-tsgolint@0.22.0) oxlint-tsgolint: 0.22.0 @@ -14984,6 +15387,37 @@ snapshots: optionalDependencies: vite: '@voidzero-dev/vite-plus-core@0.1.22(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.37.0)(tsx@4.21.0)(typescript@6.0.3)(yaml@2.8.4)' + vitest@4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-istanbul@4.1.9)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 6.4.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.37.0)(tsx@4.21.0)(yaml@2.8.4) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@types/node': 25.6.2 + '@vitest/coverage-istanbul': 4.1.9(vitest@4.1.9) + '@vitest/coverage-v8': 4.1.5(vitest@4.1.9) + transitivePeerDependencies: + - msw + optional: true + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 @@ -15053,6 +15487,12 @@ snapshots: dependencies: isexe: 3.1.5 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + optional: true + widest-line@5.0.0: dependencies: string-width: 7.2.0