diff --git a/docs/CLI.md b/docs/CLI.md index 59fcd897806e..0e067d68f701 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -170,7 +170,7 @@ The directory where Jest should output its coverage files. ### `--coverageProvider=` -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. +Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default), `v8` or `odz`. ### `--debug` diff --git a/docs/Configuration.md b/docs/Configuration.md index 1ca9a15e1341..988ff873b914 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -224,11 +224,36 @@ Default: `false` Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. -Jest ships with two coverage providers: `babel` (default) and `v8`. See the [`coverageProvider`](#coverageprovider-string) option for more details. +Jest ships with three coverage providers: `babel` (default), `v8` and `odz`. See the [`coverageProvider`](#coverageprovider-string) option for more details. :::info -The `babel` and `v8` coverage providers use `/* istanbul ignore next */` and `/* c8 ignore next */` comments to exclude lines from coverage reports, respectively. For more information, you can view the [`istanbuljs` documentation](https://github.com/istanbuljs/nyc#parsing-hints-ignoring-lines) and the [`c8` documentation](https://github.com/bcoe/c8#ignoring-uncovered-lines-functions-and-blocks). +The `babel` and `v8` coverage providers use `/* istanbul ignore next */` and `/* c8 ignore next */` comments to exclude lines from coverage reports, respectively. For more information, you can view the [`istanbuljs` documentation](https://github.com/istanbuljs/nyc#parsing-hints-ignoring-lines) and the [`c8` documentation](https://github.com/bcoe/c8#ignoring-uncovered-lines-functions-and-blocks). The `odz` coverage provider doesn't exclusion comment. + +The `v8` coverage provider comes with the following tradeoffs: + +* It is not a 1:1 replacement for Babel/Istanbul coverage + * Switching between the two will usually change the reported coverage statistics, which can change whether coverage thresholds are reached or not + * Switching between the two can cause regions of uncovered code to be discovered or ignored + (This is mostly just an overall summary of the other points) + +* It works by taking a coverage report for output/transpiled code, and then using `v8-to-istanbul` and source maps to convert that report into an Istanbul-compatible format + * This is an inherently imprecise and heuristic-driven process, though in many cases it works well enough for practical purposes + * In some cases this can give confusing or misleading results, or fail to distinguish between user code and generated code (e.g. uncovered branches introduced by `__esModule` detection shims) + * Babel/Istanbul is able to be more precise because it usually operates directly on the user's original source code + +* It tracks “blocks”, not individual statements + * In particular, if you have a sequence of statements, and the middle statements always throw an exception, V8 coverage will mark the later statements as “covered” even though they never ran + * This is a deliberate tradeoff made by the V8 developers who implemented coverage + * Babel/Istanbul is able to be more precise because it explicitly instruments every source statement + +* It does not track the else branch of an if-statement without an explicit else + * So if a one-sided if-statement's condition is always true, V8 will not warn about an uncovered branch + * Babel/Istanbul is able to track these by artificially inserting an else with a branch counter + +* It does not respect the `collectCoverageFrom` Jest configuration: regardless of the value of `collectCoverageFrom`, it emits coverage report for whatever file that was encountered during the execution of the tests. + +The `odz` coverage provider also makes use of V8 coverage data, but doesn't come any of the `v8` provider tradeoffs because it operates at the AST level. ::: @@ -314,7 +339,7 @@ These pattern strings match against the full path. Use the `` string to ### `coverageProvider` \[string] -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. +Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default), `v8` and `odz`. ### `coverageReporters` \[array<string | \[string, options]>] diff --git a/e2e/__tests__/__snapshots__/coverageProviderODZ.test.ts.snap b/e2e/__tests__/__snapshots__/coverageProviderODZ.test.ts.snap new file mode 100644 index 000000000000..82462c8681b5 --- /dev/null +++ b/e2e/__tests__/__snapshots__/coverageProviderODZ.test.ts.snap @@ -0,0 +1,116 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`prints correct coverage report, if a CJS module is put under test without transformation 1`] = ` +" console.log + this will print + + at covered (module.js:11:11) + +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 60 | 50 | 50 | 60 | + module.js | 66.66 | 50 | 50 | 66.66 | 14-15,19 + uncovered.js | 0 | 100 | 100 | 0 | 8 +--------------|---------|----------|---------|---------|-------------------" +`; + +exports[`prints correct coverage report, if a TS module is transpiled by Babel to CJS and put under test 1`] = ` +" console.log + this will print + + at log (module.ts:13:11) + +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 62.5 | 50 | 50 | 62.5 | + module.ts | 62.5 | 50 | 50 | 62.5 | 16-17,21 + types.ts | 0 | 0 | 0 | 0 | + uncovered.ts | 0 | 0 | 0 | 0 | +--------------|---------|----------|---------|---------|-------------------" +`; + +exports[`prints correct coverage report, if a TS module is transpiled by custom transformer to ESM put under test 1`] = ` +" console.log + this will print + + at covered (module.ts:13:11) + +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 62.5 | 50 | 50 | 62.5 | + module.ts | 62.5 | 50 | 50 | 62.5 | 16-17,21 + types.ts | 0 | 0 | 0 | 0 | + uncovered.ts | 0 | 0 | 0 | 0 | +--------------|---------|----------|---------|---------|-------------------" +`; + +exports[`prints correct coverage report, if an ESM module is put under test without transformation 1`] = ` +" console.log + this will print + + at covered (module.js:11:11) + +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 62.5 | 50 | 50 | 62.5 | + module.js | 62.5 | 50 | 50 | 62.5 | 14-15,19 + uncovered.js | 0 | 0 | 0 | 0 | +--------------|---------|----------|---------|---------|-------------------" +`; + +exports[`prints coverage with empty sourcemaps 1`] = ` +"----------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +----------|---------|----------|---------|---------|------------------- +All files | 0 | 0 | 0 | 0 | + types.ts | 0 | 0 | 0 | 0 | +----------|---------|----------|---------|---------|-------------------" +`; + +exports[`prints coverage with missing sourcemaps 1`] = ` +" console.log + 42 + + at Object.log (__tests__/Thing.test.js:10:9) + +----------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +----------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 100 | 100 | + Thing.js | 100 | 100 | 100 | 100 | + x.css | 0 | 0 | 0 | 0 | +----------|---------|----------|---------|---------|-------------------" +`; + +exports[`reports coverage with \`resetModules\` 1`] = ` +" console.log + this will print + + at log (module.js:11:11) + + console.log + this will print + + at log (module.js:11:11) + +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 60 | 50 | 50 | 60 | + module.js | 66.66 | 50 | 50 | 66.66 | 14-15,19 + uncovered.js | 0 | 100 | 100 | 0 | 8 +--------------|---------|----------|---------|---------|-------------------" +`; + +exports[`vm script coverage generator 1`] = ` +"-------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +-------------|---------|----------|---------|---------|------------------- +All files | 80 | 75 | 66.66 | 80 | + vmscript.js | 80 | 75 | 66.66 | 80 | 20-21 +-------------|---------|----------|---------|---------|-------------------" +`; diff --git a/e2e/__tests__/coverageProviderODZ.test.ts b/e2e/__tests__/coverageProviderODZ.test.ts new file mode 100644 index 000000000000..48734078be2c --- /dev/null +++ b/e2e/__tests__/coverageProviderODZ.test.ts @@ -0,0 +1,120 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import * as path from 'path'; +import runJest from '../runJest'; + +const DIR = path.resolve(__dirname, '../coverage-provider-v8'); + +test('prints coverage with missing sourcemaps', () => { + const sourcemapDir = path.join(DIR, 'no-sourcemap'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('prints coverage with empty sourcemaps', () => { + const sourcemapDir = path.join(DIR, 'empty-sourcemap'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('reports coverage with `resetModules`', () => { + const sourcemapDir = path.join(DIR, 'with-resetModules'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('prints correct coverage report, if a CJS module is put under test without transformation', () => { + const sourcemapDir = path.join(DIR, 'cjs-native-without-sourcemap'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz', '--no-cache'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('prints correct coverage report, if a TS module is transpiled by Babel to CJS and put under test', () => { + const sourcemapDir = path.join(DIR, 'cjs-with-babel-transformer'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz', '--no-cache'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('prints correct coverage report, if an ESM module is put under test without transformation', () => { + const sourcemapDir = path.join(DIR, 'esm-native-without-sourcemap'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz', '--no-cache'], + { + nodeOptions: '--experimental-vm-modules --no-warnings', + stripAnsi: true, + }, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('prints correct coverage report, if a TS module is transpiled by custom transformer to ESM put under test', () => { + const sourcemapDir = path.join(DIR, 'esm-with-custom-transformer'); + + const {stdout, exitCode} = runJest( + sourcemapDir, + ['--coverage', '--coverage-provider', 'odz', '--no-cache'], + { + nodeOptions: '--experimental-vm-modules --no-warnings', + stripAnsi: true, + }, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); + +test('vm script coverage generator', () => { + const dir = path.resolve(__dirname, '../vmscript-coverage'); + const {stdout, exitCode} = runJest( + dir, + ['--coverage', '--coverage-provider', 'odz'], + {stripAnsi: true}, + ); + + expect(exitCode).toBe(0); + expect(stdout).toMatchSnapshot(); +}); diff --git a/e2e/coverage-provider-v8/empty-sourcemap/package.json b/e2e/coverage-provider-v8/empty-sourcemap/package.json index d3ae283aee02..a64db340cb93 100644 --- a/e2e/coverage-provider-v8/empty-sourcemap/package.json +++ b/e2e/coverage-provider-v8/empty-sourcemap/package.json @@ -2,6 +2,9 @@ "name": "empty-sourcemap", "version": "1.0.0", "jest": { - "testEnvironment": "node" + "testEnvironment": "node", + "collectCoverageFrom": [ + "/types.js" + ] } } diff --git a/e2e/coverage-provider-v8/no-sourcemap/package.json b/e2e/coverage-provider-v8/no-sourcemap/package.json index e63e40a94614..37d8e5adb023 100644 --- a/e2e/coverage-provider-v8/no-sourcemap/package.json +++ b/e2e/coverage-provider-v8/no-sourcemap/package.json @@ -6,6 +6,10 @@ "transform": { "\\.[jt]sx?$": "babel-jest", "\\.css$": "/cssTransform.js" - } + }, + "collectCoverageFrom": [ + "/Thing.js", + "/x.css" + ] } } diff --git a/package.json b/package.json index 9c809d4412bb..72e06c13d4fc 100644 --- a/package.json +++ b/package.json @@ -204,5 +204,8 @@ "psl": "patch:psl@npm:^1.9.0#./.yarn/patches/psl-npm-1.9.0-a546edad1a.patch", "ts-node@^10.5.0": "patch:ts-node@npm:^10.5.0#./.yarn/patches/ts-node-npm-10.9.1-6c268be7f4.patch" }, - "packageManager": "yarn@3.8.5" + "packageManager": "yarn@3.8.5", + "dependencies": { + "one-double-zero": "1.0.0-beta.12" + } } diff --git a/packages/jest-cli/src/args.ts b/packages/jest-cli/src/args.ts index f6993afcd784..4b6dd2e055b8 100644 --- a/packages/jest-cli/src/args.ts +++ b/packages/jest-cli/src/args.ts @@ -213,8 +213,8 @@ export const options: {[key: string]: Options} = { type: 'array', }, coverageProvider: { - choices: ['babel', 'v8'], - description: 'Select between Babel and V8 to collect coverage', + choices: ['babel', 'v8', 'odz'], + description: 'Select between Babel, V8 and One Double Zero to collect coverage', requiresArg: true, }, coverageReporters: { diff --git a/packages/jest-reporters/src/CoverageReporter.ts b/packages/jest-reporters/src/CoverageReporter.ts index 39b8636e39fa..c6b385764867 100644 --- a/packages/jest-reporters/src/CoverageReporter.ts +++ b/packages/jest-reporters/src/CoverageReporter.ts @@ -15,6 +15,7 @@ import istanbulCoverage = require('istanbul-lib-coverage'); import istanbulReport = require('istanbul-lib-report'); import libSourceMaps = require('istanbul-lib-source-maps'); import istanbulReports = require('istanbul-reports'); +import {createOneDoubleZero, type ProcessCoverage, type SourceMap} from 'one-double-zero'; import v8toIstanbul = require('v8-to-istanbul'); import type { AggregatedResult, @@ -30,6 +31,7 @@ import {type JestWorkerFarm, Worker} from 'jest-worker'; import BaseReporter from './BaseReporter'; import getWatermarks from './getWatermarks'; import type {ReporterContext} from './types'; +import {pathToFileURL} from 'url'; type CoverageWorker = typeof import('./CoverageWorker'); @@ -70,7 +72,21 @@ export default class CoverageReporter extends BaseReporter { aggregatedResults: AggregatedResult, ): Promise { await this._addUntestedFiles(testContexts); - const {map, reportContext} = await this._getCoverageResult(); + + const sourceFiles: Array = []; + + for (const testContext of testContexts) { + for (const filePath of testContext.hasteFS.matchFilesWithGlob( + this._globalConfig.collectCoverageFrom, + testContext.config.rootDir, + )) { + if (!sourceFiles.includes(filePath)) { + sourceFiles.push(filePath); + } + } + } + + const {map, reportContext} = await this._getCoverageResult(sourceFiles); try { const coverageReporters = this._globalConfig.coverageReporters || []; @@ -432,7 +448,7 @@ export default class CoverageReporter extends BaseReporter { } } - private async _getCoverageResult(): Promise<{ + private async _getCoverageResult(sourceFiles: Array): Promise<{ map: istanbulCoverage.CoverageMap; reportContext: istanbulReport.Context; }> { @@ -502,6 +518,98 @@ export default class CoverageReporter extends BaseReporter { return {map, reportContext}; } + if (this._globalConfig.coverageProvider === 'odz') { + const mergedCoverages = mergeProcessCovs( + this._v8CoverageResults.map(coverageResult => { + return { + result: coverageResult.map(result => { + const wrapperLength = result.codeTransformResult?.wrapperLength || 0; + + return { + scriptId: result.result.scriptId, + url: result.result.url, + functions: result.result.functions.map((functionCoverage) => { + return { + isBlockCoverage: functionCoverage.isBlockCoverage, + functionName: functionCoverage.functionName, + ranges: functionCoverage.ranges.map((rangeCoverage) => { + const startOffset = Math.max(0, rangeCoverage.startOffset - wrapperLength); + const endOffset = Math.max(0, rangeCoverage.endOffset - wrapperLength); + + return { + startOffset, + endOffset, + count: rangeCoverage.count + }; + }) + }; + }) + }; + }) + }; + }) + ); + + const fileTransforms = new Map(); + + for (const v8CoverageResult of this._v8CoverageResults) { + for (const entry of v8CoverageResult) { + if (entry.codeTransformResult && !fileTransforms.has(entry.result.url)) { + fileTransforms.set(entry.result.url, entry.codeTransformResult); + } + } + } + + const loadProcessCoverage = (): ProcessCoverage => { + const sourceMaps = new Map(); + + for (const scriptCoverage of mergedCoverages.result) { + const fileTransform = fileTransforms.get(scriptCoverage.url); + + // the v8 coverage collector is replacing the URLS with paths; we need to restore the original URLs. + scriptCoverage.url = pathToFileURL(scriptCoverage.url).href; + + if ( + fileTransform?.sourceMapPath && + fs.existsSync(fileTransform.sourceMapPath) + ) { + const sourceMapContent = JSON.parse( + fs.readFileSync(fileTransform.sourceMapPath, 'utf8'), + ) as SourceMap; + + sourceMapContent.sources = sourceMapContent.sources.map((source) => { + return pathToFileURL(source).href; + }); + + sourceMaps.set(scriptCoverage.url, { + ...sourceMapContent, + scriptContent: fileTransform.code + }); + } + } + + return { + scriptCoverages: mergedCoverages.result, + sourceMaps, + }; + }; + + const oneDoubleZero = createOneDoubleZero(console.info, fs.readFileSync); + const processCoverage = loadProcessCoverage(); + + return oneDoubleZero.getCoverageMap(sourceFiles, processCoverage) + .then(coverageMap => { + return { + map: coverageMap, + reportContext: istanbulReport.createContext({ + coverageMap, + dir: this._globalConfig.coverageDirectory, + watermarks: getWatermarks(this._globalConfig), + }), + } + }); + } + const map = await this._sourceMapStore.transformCoverage(this._coverageMap); const reportContext = istanbulReport.createContext({ coverageMap: map, diff --git a/packages/jest-reporters/src/generateEmptyCoverage.ts b/packages/jest-reporters/src/generateEmptyCoverage.ts index 2c96be14c2bf..12c21a4fb97f 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.ts +++ b/packages/jest-reporters/src/generateEmptyCoverage.ts @@ -22,7 +22,8 @@ export type CoverageWorkerResult = | { kind: 'V8Coverage'; result: SingleV8Coverage; - }; + } +; export default async function generateEmptyCoverage( source: string, @@ -41,7 +42,10 @@ export default async function generateEmptyCoverage( }; let coverageWorkerResult: CoverageWorkerResult | null = null; if (shouldInstrument(filename, coverageOptions, config)) { - if (coverageOptions.coverageProvider === 'v8') { + if ( + coverageOptions.coverageProvider === 'v8' || + coverageOptions.coverageProvider === 'odz' + ) { const stat = fs.statSync(filename); return { kind: 'V8Coverage', diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 5a20296dd21e..724a5042b83e 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -297,7 +297,7 @@ async function runTestInternal( // if we don't have `getVmContext` on the env skip coverage const collectV8Coverage = globalConfig.collectCoverage && - globalConfig.coverageProvider === 'v8' && + (globalConfig.coverageProvider === 'v8' || globalConfig.coverageProvider === 'odz') && typeof environment.getVmContext === 'function'; // Node's error-message stack size is limited at 10, but it's pretty useful diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index debea999ed27..8d45f34d5e56 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -13,7 +13,7 @@ import type {InitialOptions, SnapshotFormat} from '@jest/schemas'; export type {InitialOptions} from '@jest/schemas'; -type CoverageProvider = 'babel' | 'v8'; +type CoverageProvider = 'babel' | 'v8' | 'odz'; export type FakeableAPI = | 'Date' diff --git a/yarn.lock b/yarn.lock index 33fedbb4ecb9..37dc5403d022 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3309,6 +3309,7 @@ __metadata: mock-fs: ^5.1.2 netlify-plugin-cache: ^1.0.3 node-notifier: ^10.0.0 + one-double-zero: 1.0.0-beta.12 p-limit: ^3.1.0 pkg-dir: ^5.0.0 prettier: ^3.0.3 @@ -8078,6 +8079,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 68e9818b00fc1ed9cdab9eb16905551c2b768a317ae69a5e3c43924c2b20ac9bb65b27e1cab36aeda7b6496376d4da908996ba2c0b5d79463e0fb1e77935d514 + languageName: node + linkType: hard + "commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -11350,7 +11358,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.4.5": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.4.2, glob@npm:^10.4.5": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -13028,7 +13036,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" checksum: 2367407a8d13982d8f7a859a35e7f8dd5d8f75aae4bb5484ede3a9ea1b426dc245aff28b976a2af48ee759fdd9be374ce2bd2669b644f31e76c5f46a2e29a831 @@ -13048,7 +13056,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-report@npm:^3.0.0": +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" dependencies: @@ -13070,7 +13078,7 @@ __metadata: languageName: node linkType: hard -"istanbul-reports@npm:^3.1.3": +"istanbul-reports@npm:^3.1.3, istanbul-reports@npm:^3.1.7": version: 3.1.7 resolution: "istanbul-reports@npm:3.1.7" dependencies: @@ -16536,6 +16544,26 @@ __metadata: languageName: node linkType: hard +"one-double-zero@npm:1.0.0-beta.12": + version: 1.0.0-beta.12 + resolution: "one-double-zero@npm:1.0.0-beta.12" + dependencies: + "@bcoe/v8-coverage": ^0.2.3 + "@jridgewell/trace-mapping": ^0.3.25 + commander: ^12.1.0 + find-up: ^5.0.0 + glob: ^10.4.2 + istanbul-lib-coverage: ^3.2.2 + istanbul-lib-report: ^3.0.1 + istanbul-reports: ^3.1.7 + toml: ^3.0.0 + typescript: ^5.6.2 + bin: + odz: odz.mjs + checksum: e80f7bb60920a75b140d121547f775f7a80d7109dfbe9743a06f6cc0993f22bd8cf082b8c635988ff57dd8c7fcee5963c94736ff612589afcd3941ddf85f57dd + languageName: node + linkType: hard + "onetime@npm:^5.1.0, onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -20432,6 +20460,13 @@ __metadata: languageName: node linkType: hard +"toml@npm:^3.0.0": + version: 3.0.0 + resolution: "toml@npm:3.0.0" + checksum: 5d7f1d8413ad7780e9bdecce8ea4c3f5130dd53b0a4f2e90b93340979a137739879d7b9ce2ce05c938b8cc828897fe9e95085197342a1377dd8850bf5125f15f + languageName: node + linkType: hard + "totalist@npm:^3.0.0": version: 3.0.1 resolution: "totalist@npm:3.0.1" @@ -20796,6 +20831,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.6.2": + version: 5.6.3 + resolution: "typescript@npm:5.6.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: ba302f8822777ebefb28b554105f3e074466b671e7444ec6b75dadc008a62f46f373d9e57ceced1c433756d06c8b7dc569a7eefdf3a9573122a49205ff99021a + languageName: node + linkType: hard + "typescript@patch:typescript@5.4.2#~builtin": version: 5.4.2 resolution: "typescript@patch:typescript@npm%3A5.4.2#~builtin::version=5.4.2&hash=5adc0c" @@ -20816,6 +20861,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@^5.6.2#~builtin": + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#~builtin::version=5.6.3&hash=8c6c40" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: f6947c263dc67ea8429eefc3921683320f901a07d9ef29a1101a34f5b30108343b5a29aa77a581217a17f2616091ae7bd3043b304cf37f37d5e128e19296c08b + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.19.2 resolution: "uglify-js@npm:3.19.2"