supercov 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,39 +100,40 @@ CLI queries instead of loading the complete derived model into context.
100
100
  ```sh
101
101
  # Orient using only a few lines.
102
102
  npx supercov runs --limit 5
103
- npx supercov runs latest coverage
104
- npx supercov runs latest coverage --filter passed
105
- npx supercov runs latest coverage --filter failed
106
- npx supercov runs latest coverage kinds
107
- npx supercov runs latest coverage runners
108
- npx supercov runs latest coverage scope
109
- npx supercov runs latest coverage --kind e2e
110
- npx supercov runs latest coverage files
111
- npx supercov runs latest coverage gaps
112
- npx supercov runs latest coverage gaps --metric mcdc
113
- npx supercov runs latest coverage gaps --kind e2e
103
+ npx supercov runs latest
104
+ npx supercov runs latest --filter passed
105
+ npx supercov runs latest --filter failed
106
+ npx supercov runs latest kinds
107
+ npx supercov runs latest runners
108
+ npx supercov runs latest scope
109
+ npx supercov runs latest --kind e2e
110
+ npx supercov runs latest files
111
+ npx supercov runs latest gaps
112
+ npx supercov runs latest gaps --metric mcdc
113
+ npx supercov runs latest gaps --kind e2e
114
114
 
115
115
  # Drill into one target selected from the gap list.
116
- npx supercov runs latest coverage file app/routes/example.ts
117
- npx supercov runs latest coverage file app/routes/example.ts --metric mcdc
118
- npx supercov runs latest coverage decision app/routes/example.ts:42
119
- npx supercov runs latest coverage covers app/routes/example.ts:57
116
+ npx supercov runs latest file app/routes/example.ts
117
+ npx supercov runs latest file app/routes/example.ts --metric mcdc
118
+ npx supercov runs latest decision app/routes/example.ts:42
119
+ npx supercov runs latest line app/routes/example.ts:57
120
120
 
121
121
  # Understand redundancy/contribution and validate a newly written test. Replace
122
122
  # "latest" with the immutable run ID when an agent continues work later.
123
- npx supercov runs latest coverage test "test title fragment"
124
- npx supercov runs latest coverage minimize --filter passed
125
- npx supercov runs latest coverage minimize --filter passed --metric mcdc --target 80
123
+ npx supercov runs latest test "test title fragment"
124
+ npx supercov runs latest minimize --filter passed
125
+ npx supercov runs latest minimize --filter passed --metric mcdc --target 80
126
126
  npx supercov diff <older-run> <newer-run>
127
127
 
128
128
  # Combine compatible shards without deleting their immutable source runs.
129
129
  npx supercov merge <first-run-id> <second-run-id>
130
130
  ```
131
131
 
132
- `supercov runs` is metadata-only for uncached history and never reconstructs
133
- coverage for twenty runs merely to list them. Runs whose disposable query index
134
- already exists include their metrics; other rows say `coverage not indexed`.
135
- Selecting a run with `runs <run-id> coverage` materializes its index lazily.
132
+ `supercov runs` always includes coverage percentages. Supercov derives a
133
+ missing disposable query view from the run's immutable evidence before printing
134
+ that row; whether the view was already available is never exposed in the CLI.
135
+ `supercov runs <run-id>` prints the same summary as
136
+ `supercov runs <run-id>`.
136
137
 
137
138
  Coverage queries use `--filter all` by default, matching conventional coverage
138
139
  tools: every executed attempt contributes, including attempts that later fail.
@@ -148,14 +149,14 @@ run. `latest` is a convenience selector for interactive use. Every query
148
149
  accepts `--json` and—where the result can be long—`--limit` and `--offset`.
149
150
  Every collection is paginated at 20 items by default and prints its range plus
150
151
  a copyable next-page command; generated commands omit the default limit.
151
- Agents targeting one coverage dimension can pass `--metric` to `coverage
152
- files`, `coverage gaps`, or `coverage file`; this ranks and narrows the existing
152
+ Agents targeting one coverage dimension can pass `--metric` to `files`, `gaps`,
153
+ or `file`; this ranks and narrows the existing
153
154
  resource instead of requiring a separate MC/DC-specific command.
154
155
  Measurement limitations use the same drill-down commands as ordinary gaps.
155
156
  The coverage summary reports whether the measured denominator is complete,
156
- `coverage files` and `coverage gaps` include per-file limitation counts and
157
- kinds, and `coverage file <path>` returns the bounded source locations, reasons,
158
- and denominator effect. `coverage scope` attaches the same counts to included,
157
+ `files` and `gaps` include per-file limitation counts and kinds, and `file
158
+ <path>` returns bounded gap lines with consolidated obligations and exact `line
159
+ <file:line>` drill-down commands. `scope` attaches the same counts to included,
159
160
  excluded, and ambiguous source entries. A 100% metric with a blocking limitation
160
161
  is therefore never reported as structurally complete.
161
162
  The summary also exposes provider-neutral transport counters. If Supercov
@@ -206,7 +207,7 @@ For a JavaScript or TypeScript project, the CLI:
206
207
  2. inventories first-party source from package entry points, workspaces,
207
208
  conventional source directories, and TypeScript roots. Every candidate is
208
209
  retained as included, excluded, or ambiguous; ambiguity blocks a complete
209
- verdict and is inspectable with `coverage scope`. Set
210
+ verdict and is inspectable with `scope`. Set
210
211
  `SUPERCOV_SOURCE_ROOTS` for an explicit authoritative scope;
211
212
  3. instruments through the existing Vite graph when available, or instruments
212
213
  only the disposable source copy before the project's unchanged
@@ -244,17 +245,17 @@ state writes use sibling-temp files, fsync, and atomic rename; lock acquisition
244
245
  uses exclusive creation and fsync. Published `run.json` is the durable terminal
245
246
  record, so terminal work state is not retained.
246
247
 
247
- Retention is deterministic because UTC run IDs sort chronologically:
248
+ Retention is deterministic because run records store their UTC start time:
248
249
 
249
250
  ```sh
250
- npx supercov prune --keep 20
251
- npx supercov prune --keep 20 --dry-run
252
- npx supercov clean --keep 20 # also removes the shared build cache
251
+ npx supercov clean # removes all stored runs and the shared build cache
252
+ npx supercov clean --dry-run # previews the default full cleanup
253
+ npx supercov clean --keep 20 # explicitly retains the 20 newest runs
253
254
  ```
254
255
 
255
- Neither operation runs automatically. `prune` removes explicit history beyond
256
- the requested retention and orphan/terminal transient data while preserving
257
- the shared cache. `clean` also removes that cache. Both acquire the same lock
256
+ Cleanup never runs automatically. `clean` removes explicit history beyond the
257
+ requested retention, orphan/terminal transient data, and the shared build
258
+ cache. It acquires the same lock
258
259
  as a coverage run, refuse to race an active run, and never touch files outside
259
260
  `.supercov/`.
260
261
 
@@ -43,6 +43,20 @@ reported separately from Supercov's recorded phases.
43
43
 
44
44
  ## Reference measurement, not a guarantee
45
45
 
46
+ On 2026-08-26, the 30-test Essential SEO Playwright suite running across seven
47
+ cycle-restored VMs produced a 43.02 s uncovered control and two valid warm
48
+ Supercov measurements of 46.30 s and 40.20 s. The slower covered sample is
49
+ 1.076x the control and therefore remains below the current 1.1x realistic-suite
50
+ target; the faster result illustrates why multiple alternating pairs are still
51
+ required for a statistically stable benchmark. The 40.20 s run spent 38.63 s
52
+ inside the unchanged test command and 1.04 s total on initialization, workspace
53
+ refresh, adapter restoration and evidence publication. It attributed 6,727
54
+ server records across 30 remote launches with zero corrupt evidence records.
55
+
56
+ VM-image creation is not included in these warm comparisons. A changed engine
57
+ fingerprint intentionally caused a one-time 119 s rebake before the first
58
+ post-change run; comparing that cold run with a warm control would be invalid.
59
+
46
60
  On 2026-08-24, the 29-test Essential SEO offline suite on the development Mac
47
61
  produced this warm pair:
48
62
 
@@ -73,8 +87,10 @@ Before raw-evidence-only storage, the reference run retained 4.5 MB of reports
73
87
  and 1.7 MB across 178 loose evidence files. Its canonical compressed JSON was
74
88
  0.9 MB. The execution evidence alone packed to about 121 KiB; current archives
75
89
  also embed the exact denominator manifest and are the sole coverage artifact.
76
- Every CLI query derives its view from the archive on demand without writing a
77
- cache.
90
+ Every CLI query derives its view from the archive. The first query builds a
91
+ disposable integrity-bound binary index; later queries reuse it while the run
92
+ identity remains valid. The index is an implementation detail and can always be
93
+ deleted and reconstructed.
78
94
  These numbers are application- and filesystem-specific optimization baselines.
79
95
  An exact matching Vite build is also reused across runs, removing the measured
80
96
  4.87-second repeated build; any source/configuration/toolchain-key change falls
@@ -88,8 +104,8 @@ phase and 40.49 seconds total: 0.07 seconds initialization, 0.36 seconds
88
104
  workspace refresh, 0.05 seconds adapter setup, 39.84 seconds in the unchanged
89
105
  test command, and 0.12 seconds evidence validation/archive publication. Fresh
90
106
  process queries for summary, files, and gaps each took 0.16–0.20 seconds on
91
- this run while writing no cache. Test execution is still the dominant and
92
- naturally variable part of the total.
107
+ this run before the reusable query index was introduced. Test execution is
108
+ still the dominant and naturally variable part of the total.
93
109
 
94
110
  ## Isolation strategy trade-offs
95
111
 
@@ -16,7 +16,7 @@ is below the project's `.supercov/` directory:
16
16
  | `work/<run>/state.json` | In-flight lifecycle record; removed after atomic run publication. |
17
17
  | `work/<run>/run-publication/` | Incomplete run staging; atomically renamed or removed on recovery. |
18
18
  | `evidence/<run>/` | Loose in-flight evidence; packed and removed after publication. |
19
- | `runs/<run>/` | Immutable `evidence.raw.gz` (manifest plus raw execution evidence) and `run.json`; retained until explicit prune/clean. Derived query views are never cached. |
19
+ | `runs/<run>/` | Immutable `evidence.raw.gz` (manifest plus raw execution evidence) and `run.json`; retained until explicit `clean`. Derived query views are cached only after their first query. |
20
20
  | `cache/instrumented-workspace/<project>/` | Stable physical fallback and provider snapshot cache. |
21
21
  | `cache/instrumented-workspace/.<project>.staging-*` | Unpublished cache transaction; removed on error or recovery. |
22
22
  | `cache/instrumented-workspace/.<project>.previous-*` | Last complete cache generation during publication; restored or removed on recovery. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Zero-edit, runner-aware coverage completeness for JavaScript test suites",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -63,12 +63,12 @@
63
63
  "prepublishOnly": "npm run release:check"
64
64
  },
65
65
  "optionalDependencies": {
66
- "@supercov/cli-darwin-arm64": "0.0.13",
67
- "@supercov/cli-darwin-x64": "0.0.13",
68
- "@supercov/cli-linux-arm64-gnu": "0.0.13",
69
- "@supercov/cli-linux-arm64-musl": "0.0.13",
70
- "@supercov/cli-linux-x64-gnu": "0.0.13",
71
- "@supercov/cli-linux-x64-musl": "0.0.13"
66
+ "@supercov/cli-darwin-arm64": "0.0.15",
67
+ "@supercov/cli-darwin-x64": "0.0.15",
68
+ "@supercov/cli-linux-arm64-gnu": "0.0.15",
69
+ "@supercov/cli-linux-arm64-musl": "0.0.15",
70
+ "@supercov/cli-linux-x64-gnu": "0.0.15",
71
+ "@supercov/cli-linux-x64-musl": "0.0.15"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "@playwright/test": ">=1.55.0",
@@ -1,4 +1,4 @@
1
- import { mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, writeFileSync, } from "node:fs";
1
+ import { appendFileSync, existsSync, mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, writeFileSync, } from "node:fs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { dirname } from "node:path";
4
4
  function fsyncDirectory(path) {
@@ -38,6 +38,26 @@ export function atomicWriteFileSync(path, data, options) {
38
38
  rmSync(temporary, { force: true });
39
39
  }
40
40
  }
41
+ /** Append one recoverable JSONL record and make the completed line durable. */
42
+ export function appendJsonLineDurableSync(path, data) {
43
+ mkdirSync(dirname(path), { recursive: true });
44
+ const existed = existsSync(path);
45
+ const descriptor = openSync(path, "a", 0o600);
46
+ try {
47
+ writeFileSync(descriptor, data.endsWith("\n") ? data : `${data}\n`);
48
+ fsyncSync(descriptor);
49
+ }
50
+ finally {
51
+ closeSync(descriptor);
52
+ }
53
+ if (!existed)
54
+ fsyncDirectory(dirname(path));
55
+ }
56
+ /** Append a complete local record; process exit closes it before publication. */
57
+ export function appendJsonLineSync(path, data) {
58
+ mkdirSync(dirname(path), { recursive: true });
59
+ appendFileSync(path, data.endsWith("\n") ? data : `${data}\n`, { mode: 0o600 });
60
+ }
41
61
  /** Atomically publish a fully prepared file or directory and persist its entry. */
42
62
  export function atomicRenameSync(source, destination) {
43
63
  mkdirSync(dirname(destination), { recursive: true });
@@ -190,6 +190,7 @@ export function guestCoverageEnvironment(mapping, coverageEnvironment = process.
190
190
  ...translated,
191
191
  SUPERCOV_PROJECT_ROOT: mapping.guestRoot,
192
192
  SUPERCOV_CJS_INTERCEPT: "1",
193
+ SUPERCOV_DURABLE_EVIDENCE_EACH_TEST: "1",
193
194
  NODE_OPTIONS: appendNodeImport(existingEnvironment.NODE_OPTIONS, registerUrl),
194
195
  };
195
196
  }
@@ -556,6 +557,11 @@ export function installLaunchSupervisor() {
556
557
  const originalLoad = moduleLoader._load;
557
558
  moduleLoader._load = function supercovCapabilityLoad(request, parent, isMain) {
558
559
  const exports = originalLoad.call(this, request, parent, isMain);
560
+ // A user's test command may itself be implemented by an installed
561
+ // package. That package can import a VM/container/process SDK without
562
+ // any project-owned module ever crossing the SDK boundary. Inspect all
563
+ // newly loaded export graphs (deduplicated by `exportedValues`) so
564
+ // capability discovery remains provider- and runner-neutral.
559
565
  inspectExports(exports);
560
566
  return exports;
561
567
  };
@@ -12,10 +12,11 @@ import { mkdirSync, readFileSync, rmSync } from "node:fs";
12
12
  import http from "node:http";
13
13
  import https from "node:https";
14
14
  import { syncBuiltinESMExports } from "node:module";
15
- import { dirname, relative, resolve, sep } from "node:path";
15
+ import { relative, resolve, sep } from "node:path";
16
16
  import * as standardPlaywright from "@playwright/test";
17
+ import * as coverageRuntime from "./runtime.js";
17
18
  import { inferTestProvenance } from "./provenance.js";
18
- import { atomicWriteFileSync } from "./atomic.js";
19
+ import { appendJsonLineDurableSync, appendJsonLineSync } from "./atomic.js";
19
20
  import { COVERAGE_PHASE_HEADER, COVERAGE_PHASE_COOKIE, COVERAGE_SCOPE_COOKIE, COVERAGE_SCOPE_HEADER, COVERAGE_CARRIER_ENV, encodeCoverageCarrier, encodeCoverageScope, serverEvidenceDirectory, serverEvidencePath, } from "./transport.js";
20
21
  export * from "@playwright/test";
21
22
  const generatedTargetModule = "__SUPERCOV_PLAYWRIGHT_MODULE__";
@@ -32,6 +33,8 @@ const adapter = (targetModule === "@playwright/test"
32
33
  const base = (adapter[targetTestExport] ?? adapter.test);
33
34
  const baseExpect = adapter.expect;
34
35
  const GENERATED_EVIDENCE_DIRECTORY = "__SUPERCOV_EVIDENCE_DIRECTORY__";
36
+ const evidenceWriterIdentity = () => (process.env.SUPERCOV_EXECUTION_LOG_SHARD ?? `pid-${process.pid}`)
37
+ .replace(/[^A-Za-z0-9_-]/g, "_");
35
38
  const GENERATED_RUN_ID = "__SUPERCOV_RUN_ID__";
36
39
  const PHASE_STORAGE_KEY = "__supercov_phase";
37
40
  const ACTION_METHODS = new Set([
@@ -329,6 +332,11 @@ class CoveragePhaseController {
329
332
  },
330
333
  });
331
334
  this.proxyCache.set(target, proxy);
335
+ // Playwright's built-in `page` fixture is created through the wrapped
336
+ // worker-scoped browser. The test-scoped fixture sees that proxy again;
337
+ // treating our own proxy as a fresh target would nest wrappers, emit two
338
+ // phases for one operation, and perform every browser activation twice.
339
+ this.proxyCache.set(proxy, proxy);
332
340
  return proxy;
333
341
  }
334
342
  createPhase(kind, operation, causedByPhaseId, source = callerSource()) {
@@ -467,7 +475,7 @@ class CoveragePhaseController {
467
475
  let activeController;
468
476
  let bridgedAssertionDepth = 0;
469
477
  const controllers = new Map();
470
- const directRuntime = () => globalThis.__SUPERCOV_DIRECT_RUNTIME__;
478
+ const directRuntime = () => globalThis.__SUPERCOV_DIRECT_RUNTIME__ ?? coverageRuntime;
471
479
  globalThis.__SUPERCOV_ASSERTION_PHASE_BRIDGE__ = (operation, source, callback) => {
472
480
  const controller = activeController;
473
481
  const runtime = directRuntime();
@@ -829,8 +837,6 @@ const instrumentedTest = base.extend({
829
837
  // Emit an artifact even when this test touched no application source.
830
838
  // A complete test-to-coverage matrix must also identify tests that are
831
839
  // removable without changing coverage.
832
- const outputPath = testInfo.outputPath("mcdc.json");
833
- mkdirSync(dirname(outputPath), { recursive: true });
834
840
  const testFile = relative(process.cwd(), testInfo.file)
835
841
  .split(sep)
836
842
  .join("/");
@@ -854,7 +860,6 @@ const instrumentedTest = base.extend({
854
860
  server,
855
861
  };
856
862
  const serialized = `${JSON.stringify(payload)}\n`;
857
- atomicWriteFileSync(outputPath, serialized);
858
863
  // Pool runners may cycle-restore a VM immediately after Playwright
859
864
  // exits, which can discard or overwrite the normal artifact copy.
860
865
  // Write one uniquely named, one-shot evidence file to the runner's
@@ -865,10 +870,10 @@ const instrumentedTest = base.extend({
865
870
  : GENERATED_EVIDENCE_DIRECTORY);
866
871
  if (evidenceDirectory) {
867
872
  const resolvedDirectory = resolve(process.cwd(), evidenceDirectory);
868
- const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "_");
869
- const testEvidenceDirectory = resolve(resolvedDirectory, `${safeTestId}-${testInfo.retry}`);
870
- mkdirSync(testEvidenceDirectory, { recursive: true });
871
- atomicWriteFileSync(resolve(testEvidenceDirectory, "mcdc.json"), serialized);
873
+ const append = process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1"
874
+ ? appendJsonLineDurableSync
875
+ : appendJsonLineSync;
876
+ append(resolve(resolvedDirectory, `playwright-worker-${evidenceWriterIdentity()}-${process.pid}.mcdc.jsonl`), serialized);
872
877
  }
873
878
  }
874
879
  finally {
@@ -1,10 +1,12 @@
1
- import { mkdirSync } from "node:fs";
2
1
  import { relative, resolve, sep } from "node:path";
3
2
  import { inferTestProvenance } from "./provenance.js";
4
- import { atomicWriteFileSync } from "./atomic.js";
3
+ import { appendJsonLineDurableSync, appendJsonLineSync } from "./atomic.js";
5
4
  const GENERATED_EVIDENCE_DIRECTORY = "__SUPERCOV_EVIDENCE_DIRECTORY__";
5
+ const evidenceWriterIdentity = () => (process.env.SUPERCOV_EXECUTION_LOG_SHARD ?? `pid-${process.pid}`)
6
+ .replace(/[^A-Za-z0-9_-]/g, "_");
6
7
  /** Records outcomes even when browser or fixture startup fails before coverage. */
7
8
  export default class SupercovPlaywrightReporter {
9
+ records = [];
8
10
  onTestEnd(test, result) {
9
11
  const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
10
12
  (GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
@@ -32,9 +34,18 @@ export default class SupercovPlaywrightReporter {
32
34
  browser: [],
33
35
  server: [],
34
36
  };
35
- const safeId = test.id.replace(/[^a-zA-Z0-9_-]/g, "_");
36
- const directory = resolve(process.cwd(), evidenceDirectory, `playwright-${safeId}-${result.retry}-status`);
37
- mkdirSync(directory, { recursive: true });
38
- atomicWriteFileSync(resolve(directory, "mcdc.json"), `${JSON.stringify(payload)}\n`);
37
+ this.records.push(payload);
38
+ }
39
+ onEnd() {
40
+ const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
41
+ (GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
42
+ ? undefined
43
+ : GENERATED_EVIDENCE_DIRECTORY);
44
+ if (!evidenceDirectory || this.records.length === 0)
45
+ return;
46
+ const append = process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1"
47
+ ? appendJsonLineDurableSync
48
+ : appendJsonLineSync;
49
+ append(resolve(process.cwd(), evidenceDirectory, `playwright-status-${evidenceWriterIdentity()}-${process.pid}.mcdc.jsonl`), `${this.records.map(record => JSON.stringify(record)).join("\n")}\n`);
39
50
  }
40
51
  }
@@ -64,11 +64,18 @@ if (verboseDiagnostics && !process.__SUPERCOV_DIAGNOSTIC_REPORTER__) {
64
64
  });
65
65
  }
66
66
  }
67
- // Assertion-call instrumentation also uses this runtime in test processes
68
- // whose application build is handled by Vite or another compiler. Loading it
69
- // for every isolated test launch keeps node:assert attribution runner-agnostic.
70
- globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.js");
71
- process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
67
+ // Runner adapters and instrumented modules import the runtime when they need
68
+ // it. Keeping the preload itself thin avoids evaluating the full collector in
69
+ // npm launchers, web-server supervisors, and other Node children that never
70
+ // execute measured JavaScript.
71
+ if (process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1") {
72
+ // A translated remote/VM command can execute an ahead-of-run transformed
73
+ // test through an opaque runner that bypasses the ordinary Playwright or
74
+ // node:test import boundary. The remote-launch adapter marks that process;
75
+ // initialize the runtime before its transformed module can evaluate.
76
+ globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.js");
77
+ process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
78
+ }
72
79
  // Workers are independent Node processes and an explicit `execArgv: []`
73
80
  // otherwise strips the preload that supplies the isolated runtime. Preserve
74
81
  // every user option while adding exactly one Supercov import.
@@ -107,6 +114,7 @@ const generatedPlaywrightConfig = process.env.SUPERCOV_GENERATED_PLAYWRIGHT_CONF
107
114
  const generatedJestConfig = process.env.SUPERCOV_GENERATED_JEST_CONFIG;
108
115
  const entrypoint = process.argv[1]?.replaceAll("\\", "/") ?? "";
109
116
  const playwrightTarget = process.env.SUPERCOV_PLAYWRIGHT_MODULE;
117
+ const projectRoot = process.env.SUPERCOV_PROJECT_ROOT?.replaceAll("\\", "/").replace(/\/$/, "");
110
118
  const nodeTestWrapper = new URL("./nodeTest.js", import.meta.url).href;
111
119
  const nodeAssertWrapper = new URL("./nodeAssert.js", import.meta.url).href;
112
120
  const nodeAssertStrictWrapper = new URL("./nodeAssertStrict.js", import.meta.url).href;
@@ -190,6 +198,7 @@ if (generatedPlaywrightConfig &&
190
198
  }
191
199
  if (process.env.SUPERCOV_CJS_INTERCEPT === "1" &&
192
200
  process.env.SUPERCOV_INSIDE_PLAYWRIGHT === "1" &&
201
+ !isPlaywrightEntrypoint &&
193
202
  process.env.SUPERCOV_INSIDE_VITEST !== "1" &&
194
203
  process.env.VITEST !== "true") {
195
204
  const target = playwrightTarget ?? "@playwright/test";
@@ -222,7 +231,18 @@ if (process.env.SUPERCOV_CJS_INTERCEPT === "1" &&
222
231
  // itself imports the native built-in from Supercov's generated directory, so
223
232
  // only first-party callers are redirected and recursion is impossible.
224
233
  if (process.env.SUPERCOV_CJS_INTERCEPT === "1") {
225
- const projectRoot = process.env.SUPERCOV_PROJECT_ROOT?.replaceAll("\\", "/").replace(/\/$/, "");
234
+ const entrypointBelongsToProject = Boolean(projectRoot && entrypoint.startsWith(`${projectRoot}/`));
235
+ const runnerCanLoadProjectCode = entrypointBelongsToProject ||
236
+ entrypoint === "" ||
237
+ process.env.SUPERCOV_INSIDE_VITEST === "1" ||
238
+ (process.env.SUPERCOV_INSIDE_PLAYWRIGHT === "1" && !isPlaywrightEntrypoint);
239
+ if (!runnerCanLoadProjectCode) {
240
+ // Package-manager and Playwright coordinator processes propagate the
241
+ // preload to their children but never evaluate project test modules.
242
+ // Their worker/child entrypoints install these synchronous CJS hooks.
243
+ // Avoid paying to import three runner adapters in every launcher.
244
+ }
245
+ else {
226
246
  const nodeTestAdapter = await import(__rewriteRelativeImportExtension(nodeTestWrapper));
227
247
  const cjsNodeTestAdapter = Object.assign(nodeTestAdapter.test, nodeTestAdapter);
228
248
  const nodeAssertAdapter = await import(__rewriteRelativeImportExtension(nodeAssertWrapper));
@@ -248,4 +268,5 @@ if (process.env.SUPERCOV_CJS_INTERCEPT === "1") {
248
268
  : cjsNodeAssertAdapter;
249
269
  return originalLoad.call(this, request, parent, isMain);
250
270
  };
271
+ }
251
272
  }
@@ -1014,6 +1014,52 @@ function mcdcEnd(frame, value) {
1014
1014
  }
1015
1015
  return value;
1016
1016
  }
1017
+ const directRuntimeApi = {
1018
+ activateCoverageScope,
1019
+ beginBufferedServerEvidence,
1020
+ bindCoverageContext,
1021
+ coverageCarrier,
1022
+ coverageContextEnvironment,
1023
+ coverageContextHeaders,
1024
+ coverageHit,
1025
+ coverageHitV2,
1026
+ coverageSnapshot,
1027
+ decodeProbeV2Vector,
1028
+ defaultEntered,
1029
+ defaultSelected,
1030
+ enableRuntimeSnapshotEvidence,
1031
+ flushBufferedBackgroundEvidence,
1032
+ flushBufferedServerEvidence,
1033
+ loopBegin,
1034
+ loopEnd,
1035
+ loopEntered,
1036
+ mcdcBegin,
1037
+ mcdcCondition,
1038
+ mcdcEnd,
1039
+ mcdcEndV2,
1040
+ optionalCallBegin,
1041
+ optionalCallContinued,
1042
+ optionalCallEnd,
1043
+ optionalCallReached,
1044
+ optionalSelect,
1045
+ parenthesizedAssignmentValue,
1046
+ registerProbeV2,
1047
+ resetCoverage,
1048
+ selectionBegin,
1049
+ selectionEnd,
1050
+ selectionRight,
1051
+ takeNodeAssertionPhases,
1052
+ tryBegin,
1053
+ tryCatch,
1054
+ tryEnd,
1055
+ withCoverageCarrier,
1056
+ withNodeAssertionPhase,
1057
+ withRequestPhase,
1058
+ writeExclusiveBackgroundRecord
1059
+ };
1060
+ globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= directRuntimeApi;
1061
+ if (typeof process !== "undefined")
1062
+ process.__SUPERCOV_DIRECT_RUNTIME__ ??= directRuntimeApi;
1017
1063
  export {
1018
1064
  activateCoverageScope,
1019
1065
  beginBufferedServerEvidence,