supercov 0.0.16 → 0.0.18

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.
@@ -0,0 +1,145 @@
1
+ # Getting started
2
+
3
+ Supercov measures coverage for JavaScript, TypeScript, and Rust test suites.
4
+ Prefix the command you already run; no config file, import, or reporter is
5
+ required.
6
+
7
+ ```sh
8
+ npx supercov -- npm test
9
+ ```
10
+
11
+ Everything after `--` is your command, executed exactly as written.
12
+
13
+ ## Requirements
14
+
15
+ | Requirement | Detail |
16
+ | --- | --- |
17
+ | Node.js | 22 or newer |
18
+ | Project | JavaScript or TypeScript, with a runnable test command |
19
+ | Disk | A `.supercov/` directory in the project root, which Supercov creates |
20
+
21
+ Nothing else is required. Supercov never contacts a network service, and no
22
+ part of your source or evidence leaves the machine.
23
+
24
+ ## Your first run
25
+
26
+ From the project root:
27
+
28
+ ```sh
29
+ npx supercov -- npm test
30
+ ```
31
+
32
+ The run prints its phases as it goes — initialization, workspace preparation,
33
+ adapter setup, the instrumented build, your unchanged test command, and
34
+ evidence publication — and finishes by publishing one immutable run under
35
+ `.supercov/runs/<run-id>/`. The run id is a UTC timestamp, so run ids sort
36
+ chronologically.
37
+
38
+ If the command you normally use is not `npm test`, use that instead:
39
+
40
+ ```sh
41
+ npx supercov -- npx playwright test
42
+ npx supercov -- pnpm test:e2e
43
+ npx supercov -- npm run test:unit && npx supercov -- npm run test:e2e
44
+ ```
45
+
46
+ A single Supercov run can collect several runners. Coverage from a command that
47
+ launches Vitest and Playwright ends up in one run, with each test labelled by
48
+ the runner that executed it.
49
+
50
+ ## Read the result
51
+
52
+ Start with the summary, then narrow. Every query names one run; `latest`
53
+ selects the newest local run.
54
+
55
+ ```sh
56
+ # What runs exist?
57
+ npx supercov runs --limit 5
58
+
59
+ # How complete is the newest one?
60
+ npx supercov runs latest
61
+
62
+ # Which files hold the most open obligations?
63
+ npx supercov runs latest gaps --limit 10
64
+
65
+ # What exactly is open in one file?
66
+ npx supercov runs latest file app/checkout/session.ts
67
+ ```
68
+
69
+ Output is written for an agent reading a terminal: short, paginated, and
70
+ carrying a copyable next-page command. Add `--json` to any query for the stable
71
+ machine format.
72
+
73
+ ## Add a test and prove it landed
74
+
75
+ Write a test the normal way, then re-run and compare:
76
+
77
+ ```sh
78
+ npx supercov -- npm test
79
+ npx supercov diff <previous-run-id> latest
80
+ ```
81
+
82
+ `diff` reports what the newer run covers that the older one did not. To check
83
+ one specific test's contribution rather than the whole run:
84
+
85
+ ```sh
86
+ npx supercov runs latest test "rejects a locked order"
87
+ ```
88
+
89
+ ## What Supercov writes
90
+
91
+ Supercov owns two marker-protected locations inside your project:
92
+
93
+ ```text
94
+ .supercov/
95
+ runs/<run-id>/evidence.raw.gz exact denominator manifest + raw evidence
96
+ runs/<run-id>/run.json fingerprints, phase timings, integrity
97
+ supercov/
98
+ workspace/<project>/ isolated build namespace, reused between runs
99
+ ```
100
+
101
+ Your source files, test files, runner configuration and ordinary build output
102
+ are never modified, overwritten or rebuilt. Both owned locations carry their
103
+ own gitignore; an existing user `supercov/` directory is never adopted.
104
+
105
+ Storage is bounded by you, not by a background process:
106
+
107
+ ```sh
108
+ npx supercov clean # remove every stored run and build cache
109
+ npx supercov clean --keep 20 # retain the 20 newest runs
110
+ npx supercov clean --keep 20 --dry-run # show what would be removed
111
+ ```
112
+
113
+ ## Choosing what counts
114
+
115
+ Two options change the meaning of a number rather than its presentation, so
116
+ they are worth knowing early.
117
+
118
+ `--filter` selects which attempts contribute:
119
+
120
+ - `all` (default) counts every executed attempt, including attempts that later
121
+ failed. This matches what conventional coverage tools report.
122
+ - `passed` counts only successful attempts of tests that ultimately passed —
123
+ verified coverage.
124
+ - `failed` counts only failed attempts, which is useful when diagnosing a flaky
125
+ test's real execution path.
126
+
127
+ `--kind` selects a semantic test level such as `e2e`, `integration`,
128
+ `component` or `unit`. Kind is resolved from an explicit `SUPERCOV_TEST_KIND`,
129
+ then the Playwright project name, then the test path, then the runner default.
130
+ Queries record how the label was established, so an inferred kind is never
131
+ presented as one you declared.
132
+
133
+ Filtered queries recompute every obligation from the selected tests instead of
134
+ filtering an already-computed percentage. This matters most for MC/DC, where a
135
+ witness pair assembled from one unit vector and one end-to-end vector counts for
136
+ the combined suite but not for either level alone.
137
+
138
+ ## Where to go next
139
+
140
+ - [Agent loop](/docs/agent-loop) — the unattended workflow this is designed for.
141
+ - [CLI reference](/docs/cli) — every command and flag.
142
+ - [Coverage model](/docs/coverage-model) — what an obligation is, and why the
143
+ denominator is larger than lines and branches.
144
+ - [Supported suites](/docs/supported-suites) — where attribution is exact and
145
+ where it is aggregate.
@@ -0,0 +1,119 @@
1
+ # Supported suites
2
+
3
+ Supercov wraps a test command and instruments the processes it launches.
4
+ Runner support differs by attribution level: exact per-test attribution or
5
+ aggregate coverage.
6
+
7
+ ## Attribution by runner
8
+
9
+ | Runner | Attribution | Notes |
10
+ | --- | --- | --- |
11
+ | Playwright | Exact per test | Test, worker, retry and outcome scopes. ESM and CommonJS specs in arbitrary directories, plus project-owned fixture packages. |
12
+ | Vitest | Exact per test | Module-import and setup execution is kept as a separate setup scope. |
13
+ | Jest | Exact per test | Including concurrent and parameterized tests. |
14
+ | `node:test` | Exact per test | Through the generated adapter. |
15
+ | AVA, Mocha, other runners | Aggregate only | First-party structural coverage through inherited process instrumentation. Hits are recorded as background rather than guessed onto tests. |
16
+ | Browser component runners without an adapter | Aggregate only | Same boundary, made explicit in the report. |
17
+
18
+ Adapters are generated into the isolated workspace. Your test imports, reporter
19
+ list and runner configuration are not modified.
20
+
21
+ A single command may collect several runners into one run. Each test is then
22
+ labelled with the runner that executed it and the semantic kind it belongs to.
23
+
24
+ ## Builds
25
+
26
+ | Project shape | How instrumentation is applied |
27
+ | --- | --- |
28
+ | Vite or Vitest | Through the existing Vite graph |
29
+ | Next, Turbopack, Webpack, esbuild, SWC, other build commands | Applied to the disposable source copy, then your unchanged build command runs against it |
30
+ | No build step (ESM or CommonJS) | Direct instrumentation of the disposable source copy |
31
+
32
+ The ordinary application build is never read as an input, overwritten, or
33
+ rebuilt afterwards.
34
+
35
+ When the complete source, configuration and toolchain fingerprint is unchanged
36
+ between runs, the previous instrumented output and manifest are carried into the
37
+ refreshed workspace and the build is skipped entirely.
38
+
39
+ ### Build-only environment flags
40
+
41
+ Before the isolated build, Supercov compares the invoked npm, pnpm, yarn or bun
42
+ script with explicit string-valued `process.env` checks in the project's build
43
+ configuration. A semantic match — a `test:preview` script and a
44
+ `process.env.TEST_PREVIEW === "true"` check, for example — activates that
45
+ build-only flag, and the decision is printed before the build. Values are never
46
+ guessed for unrelated environment variables.
47
+
48
+ ## Browsers
49
+
50
+ The compatibility workflow exercises Chromium, Firefox and WebKit, along with
51
+ Node 22, 24 and 25, Playwright 1.55 and current, Vite 5 and current, Vitest 2
52
+ and current, and modern JavaScript, JSX, TypeScript and TSX syntax fixtures.
53
+
54
+ The Playwright adapter covers the `page` and `request` fixtures, API request
55
+ contexts, user-created browser contexts and pages, popups and all of their
56
+ frames, dedicated and service workers, WebSocket handshake headers, and
57
+ test-spawned child processes.
58
+
59
+ For Chromium documents exposed through the page target, a pre-document probe
60
+ installs the action phase before application JavaScript starts. A newly created
61
+ cross-origin iframe may run in a separate target that cannot be safely paused
62
+ during navigation; its earliest probes use a timing fallback until the frame is
63
+ live. This affects action-level causal precision only — never structural
64
+ coverage or test attribution.
65
+
66
+ ## Servers, background work and child processes
67
+
68
+ Server-side coverage is safe when Playwright runs multiple workers against one
69
+ application server. Every routed request carries a run, worker, test and retry
70
+ scope; Node async context retains that scope and its current phase across
71
+ awaited work; and each worker writes to a distinct attempt path that only its
72
+ own collecting fixture will accept.
73
+
74
+ Detached work is never dropped silently or guessed onto whichever test is
75
+ active:
76
+
77
+ - HTTP callbacks inherit the carrier automatically.
78
+ - Child processes inherit it through their environment.
79
+ - Exported queue helpers cover BullMQ, Bee-Queue, pg-boss, Agenda and
80
+ in-process schedulers.
81
+ - Anything that still arrives without a carrier is persisted under the
82
+ background scope.
83
+
84
+ ## Remote and containerised execution
85
+
86
+ Discovery is structural rather than provider-specific. The preload and a
87
+ narrowly gated ESM transform look for a static `build(options)` capability,
88
+ activate only when those options contain a host-to-guest mount that includes the
89
+ isolated project, scope any existing cache or snapshot identity to the run's
90
+ source fingerprint, and follow the returned object graph. A method whose options
91
+ contain `argv`, `cmd` or `command` receives guest-translated Supercov paths and
92
+ a guest-valid Node preload.
93
+
94
+ The execution log records this process and capability graph, but hashes long or
95
+ multiline arguments so embedded shell bodies and credentials are never
96
+ persisted.
97
+
98
+ The boundary is explicit: Supercov follows Node child processes, not arbitrary
99
+ non-Node supervisors, and not a remote control plane that never exposes its
100
+ launches to the local process. CommonJS and pure-ESM executor SDKs,
101
+ object-shaped and positional execution APIs, and opaque returned object graphs
102
+ are all covered when a discoverable build capability exposes the workspace mount
103
+ and an execution capability accepts an environment. Anything that hides all
104
+ launch state behind an out-of-process RPC needs a dedicated adapter, and
105
+ Supercov reports missing evidence rather than claiming those paths are covered.
106
+
107
+ The public regression suite includes provider-neutral CommonJS and pure-ESM
108
+ opaque executors. CI requires Supercov to discover that structure, scope the
109
+ cache identity, translate paths and the Node preload into the guest, run nested
110
+ Vitest and Playwright commands, parse every concurrent trace shard, and produce
111
+ 100% fixture coverage.
112
+
113
+ ## Distributed runs
114
+
115
+ Each shard produces its own immutable run. `supercov merge` combines runs whose
116
+ source, test, dependency, configuration, instrumenter, schema and denominator
117
+ fingerprints match exactly, publishes a new immutable run atomically, and leaves
118
+ every input untouched. Incompatible shards fail with a clear reason instead of
119
+ being averaged together.
@@ -0,0 +1,102 @@
1
+ # Verification
2
+
3
+ Supercov instruments source before the test command runs. Seven release gates
4
+ check that the instrumented program matches the original program and that
5
+ coverage calculations are correct. Any failed gate blocks publication.
6
+
7
+ ## 1. Semantic differential execution
8
+
9
+ Original and instrumented programs are executed in isolated scopes and compared
10
+ on three axes: return values, thrown errors, and the observable order of side
11
+ effects.
12
+
13
+ The fixtures deliberately target the places where a naive transform breaks:
14
+ getters, proxies, optional calls and `this` binding, computed logical
15
+ assignments, parameter defaults, `try`/`catch`/`finally`, iterator closing,
16
+ switch fallthrough, labelled loops, async functions and generators.
17
+
18
+ ## 2. Deterministic generated corpus
19
+
20
+ A generated corpus exercises 160 nested combinations of short-circuiting,
21
+ ternaries, coercion and thrown expressions on every run. It is deterministic, so
22
+ a regression reproduces exactly rather than appearing once in CI and never
23
+ again.
24
+
25
+ ## 3. Property testing
26
+
27
+ Seeded `fast-check` properties generate a further 500 nested expressions and 300
28
+ control-flow executions per run, with shrinking and a reproducible seed printed
29
+ on failure.
30
+
31
+ ## 4. Coverage oracles
32
+
33
+ Behaviour equivalence is not enough — the numbers have to be right too. Separate
34
+ oracles assert exact decision vectors, MC/DC witnesses and branch alternatives
35
+ independently of what the program does.
36
+
37
+ ## 5. An independent MC/DC implementation
38
+
39
+ The same three-condition masking-MC/DC golden cases must produce identical
40
+ verdicts under Supercov and under Clang/LLVM source-based MC/DC: 100% for a
41
+ complete witness set and 33.33% for an incomplete one.
42
+
43
+ This is the gate that matters most. MC/DC has enough subtlety — masking versus
44
+ unique-cause, short-circuit evaluation, compound conditions — that agreement
45
+ with an independently implemented, widely audited toolchain is far stronger
46
+ evidence than any self-consistent test suite.
47
+
48
+ ## 6. TC39 Test262
49
+
50
+ Release CI shards the pinned Test262 corpus across 16 workers, runs the official
51
+ harness against original and instrumented sources, and rejects any scenario that
52
+ passes originally but fails after transformation.
53
+
54
+ Some categories are excluded by construction, with reason counts printed for
55
+ every shard:
56
+
57
+ | Excluded | Why |
58
+ | --- | --- |
59
+ | Module, async and raw tests | Not comparable under the source-rewrite harness |
60
+ | Parse and resolution negatives | The transform never runs on unparseable input |
61
+ | Annex B sloppy-script extensions | Does not apply to the application modules Supercov instruments |
62
+ | `Function.prototype.toString` and function-source coercion | Exact source reflection necessarily observes a source transform |
63
+
64
+ The last category is handled in the product, not hidden: when application code
65
+ directly coerces or observes a function's source, Supercov leaves that body
66
+ uninstrumented and records a visible `semantic-safety` completeness blocker.
67
+ Dedicated differential fixtures cover the async and generator cases that Test262
68
+ cannot compare.
69
+
70
+ ## 7. Performance budgets
71
+
72
+ Transform latency, transactional workspace preparation, output expansion and
73
+ runtime probe overhead are each checked against explicit budgets. A change that
74
+ makes instrumentation correct but unusably slow fails the same way a wrong
75
+ answer does.
76
+
77
+ ## Cross-platform and compatibility gates
78
+
79
+ Alongside the seven correctness gates, the compatibility workflow runs Node 22,
80
+ 24 and 25, Playwright 1.55 and current, Vite 5 and current, Vitest 2 and
81
+ current, Chromium, Firefox and WebKit, and modern JavaScript, JSX, TypeScript
82
+ and TSX syntax fixtures.
83
+
84
+ Filesystem publication, symlink handling, copy fallback, `ENOSPC`, failed
85
+ rename and forced-termination recovery are all exercised on Ubuntu, macOS and
86
+ Windows.
87
+
88
+ A clean-room gate packs the npm tarball, invokes it through `npx` in a project
89
+ with no build step, and asserts that not a single source or configuration file
90
+ changed.
91
+
92
+ ## Running the gates yourself
93
+
94
+ ```sh
95
+ npm test
96
+ npm run test:clang-mcdc
97
+ npm run benchmark:check
98
+ TEST262_DIR=/path/to/test262 npm run test:test262
99
+ ```
100
+
101
+ The Clang/LLVM oracle requires `clang` and `llvm` to be installed. The Test262
102
+ gate requires a checkout of the pinned corpus.
@@ -1,9 +1,8 @@
1
1
  # Workspace isolation
2
2
 
3
- Supercov's primary filesystem guarantee is stronger than “we clean up when the
4
- command exits”: the user's source and ordinary build artifacts are never
5
- Supercov's write targets. Cleanup is useful for space, but correctness cannot
6
- depend on a signal handler running.
3
+ Supercov writes generated and temporary files only under the project's
4
+ `.supercov/` directory. Application source and ordinary build artifacts are
5
+ not write targets, and cleanup does not depend on a signal handler.
7
6
 
8
7
  ## Owned paths
9
8
 
@@ -17,10 +16,10 @@ is below the project's `.supercov/` directory:
17
16
  | `work/<run>/run-publication/` | Incomplete run staging; atomically renamed or removed on recovery. |
18
17
  | `evidence/<run>/` | Loose in-flight evidence; packed and removed after publication. |
19
18
  | `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
- | `cache/instrumented-workspace/<project>/` | Stable physical fallback and provider snapshot cache. |
21
- | `cache/instrumented-workspace/.<project>.staging-*` | Unpublished cache transaction; removed on error or recovery. |
22
- | `cache/instrumented-workspace/.<project>.previous-*` | Last complete cache generation during publication; restored or removed on recovery. |
23
- | `cache/instrumented-workspace/<project>/.supercov/server-evidence/<run>/` | Server/background transport shared with local or mounted guest processes; archived and removed after publication, interruption, refresh, or cleanup. |
19
+ | `supercov/workspace/<project>/` | Stable physical fallback and provider snapshot cache. The non-dotted ancestor keeps Express/`send` and similar static-file stacks semantically unchanged. |
20
+ | `supercov/workspace/.<project>.staging-*` | Unpublished cache transaction; removed on error or recovery. |
21
+ | `supercov/workspace/.<project>.previous-*` | Last complete cache generation during publication; restored or removed on recovery. |
22
+ | `supercov/workspace/<project>/.supercov/server-evidence/<run>/` | Server/background transport shared with local or mounted guest processes; archived and removed after publication, interruption, refresh, or cleanup. |
24
23
 
25
24
  The lower-level runtime retains `/tmp/supercov-server-evidence` only as a
26
25
  fallback when it is embedded without the Supercov CLI and no owned transport
@@ -30,7 +29,10 @@ translation maps that same root into the guest mount.
30
29
  ## Current physical fallback
31
30
 
32
31
  Builds and opaque VM/container mounts currently use a stable physical namespace
33
- at `.supercov/cache/instrumented-workspace/<project>`. Refresh has four states:
32
+ at `supercov/workspace/<project>`. The `supercov/` container is owned only when
33
+ its exact marker is present; if a project already owns that name, Supercov uses
34
+ a deterministic non-dotted fallback and copies the user's directory as ordinary
35
+ source rather than adopting or excluding it. Refresh has four states:
34
36
 
35
37
  1. The last complete source generation remains live while a sibling `staging`
36
38
  tree is prepared.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Zero-edit, runner-aware coverage completeness for JavaScript test suites",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,6 +36,10 @@
36
36
  "build": "cargo build -p supercov",
37
37
  "sync:rust-assets": "node scripts/sync-rust-package-assets.mjs",
38
38
  "test:rust-assets": "node scripts/sync-rust-package-assets.mjs --check",
39
+ "test:rustc-backend-spike": "node scripts/rustc-backend-spike.mjs",
40
+ "test:rust-compiler-spikes": "cargo build -p supercov && node scripts/rust-libtest-companion-spike.mjs && node scripts/rust-async-attribution-spike.mjs && node scripts/rust-subprocess-attribution-spike.mjs && node scripts/rust-custom-harness-spike.mjs && node scripts/rust-libtest-builder-lifecycle-spike.mjs",
41
+ "test:rust-cargo-runner": "cargo build -p supercov && node scripts/rust-cargo-runner-integration.mjs",
42
+ "test:rust-nextest-runner": "cargo build -p supercov && node scripts/rust-nextest-runner-integration.mjs",
39
43
  "test": "cargo test --workspace",
40
44
  "test:runtime": "node --test tests/runtime/*.test.mjs",
41
45
  "test:fixture": "cargo build -p supercov && node scripts/rust-fixture-matrix.mjs",
@@ -63,12 +67,12 @@
63
67
  "prepublishOnly": "npm run release:check"
64
68
  },
65
69
  "optionalDependencies": {
66
- "@supercov/cli-darwin-arm64": "0.0.16",
67
- "@supercov/cli-darwin-x64": "0.0.16",
68
- "@supercov/cli-linux-arm64-gnu": "0.0.16",
69
- "@supercov/cli-linux-arm64-musl": "0.0.16",
70
- "@supercov/cli-linux-x64-gnu": "0.0.16",
71
- "@supercov/cli-linux-x64-musl": "0.0.16"
70
+ "@supercov/cli-darwin-arm64": "0.0.18",
71
+ "@supercov/cli-darwin-x64": "0.0.18",
72
+ "@supercov/cli-linux-arm64-gnu": "0.0.18",
73
+ "@supercov/cli-linux-arm64-musl": "0.0.18",
74
+ "@supercov/cli-linux-x64-gnu": "0.0.18",
75
+ "@supercov/cli-linux-x64-musl": "0.0.18"
72
76
  },
73
77
  "peerDependencies": {
74
78
  "@playwright/test": ">=1.55.0",
@@ -19,6 +19,17 @@ const importedCapabilityProxies = new WeakMap();
19
19
  const importedMemberProxies = new WeakMap();
20
20
  let installed = false;
21
21
  let remoteLaunchSequence = 0;
22
+
23
+ function isClassConstructor(value) {
24
+ if (typeof value !== "function")
25
+ return false;
26
+ try {
27
+ return /^class(?:\s|\{)/u.test(Function.prototype.toString.call(value));
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
22
33
  function executionLogPath(path) {
23
34
  const shard = (process.env["SUPERCOV_EXECUTION_LOG_SHARD"] ?? "host").replace(/[^A-Za-z0-9_.-]/g, "_");
24
35
  const suffix = `.${shard}.${process.pid}.jsonl`;
@@ -275,6 +286,13 @@ function wrapImportedMember(member, receiver, invoke) {
275
286
  */
276
287
  export function wrapCapabilityCallbacks(value, mapping, depth = 0, seen = new WeakMap()) {
277
288
  if (typeof value === "function") {
289
+ // A class is a nominal value as well as a callable capability. Replacing
290
+ // it with a Proxy changes strict constructor identity in registries such
291
+ // as Lexical, ORMs and dependency-injection containers. Static builder
292
+ // methods are supervised at the export boundary; classes passed through
293
+ // ordinary configuration must remain the exact original value.
294
+ if (isClassConstructor(value))
295
+ return value;
278
296
  const cached = seen.get(value);
279
297
  if (cached)
280
298
  return cached;
@@ -320,6 +338,10 @@ export function wrapCapabilityCallbacks(value, mapping, depth = 0, seen = new We
320
338
  export function wrapCapabilityObject(value, mapping) {
321
339
  if ((!value || typeof value !== "object") && typeof value !== "function")
322
340
  return value;
341
+ if (isClassConstructor(value)) {
342
+ patchBuilder(value);
343
+ return value;
344
+ }
323
345
  const object = value;
324
346
  const cached = capabilityProxies.get(object);
325
347
  if (cached)
@@ -379,6 +401,10 @@ export function wrapCapabilityObject(value, mapping) {
379
401
  export function wrapImportedCapability(value) {
380
402
  if ((!value || typeof value !== "object") && typeof value !== "function")
381
403
  return value;
404
+ if (isClassConstructor(value)) {
405
+ patchBuilder(value);
406
+ return value;
407
+ }
382
408
  const object = value;
383
409
  const cached = importedCapabilityProxies.get(object);
384
410
  if (cached)
@@ -408,6 +434,10 @@ export function wrapImportedCapability(value) {
408
434
  if (fixed.fixed)
409
435
  return fixed.value;
410
436
  const member = Reflect.get(target, property, target);
437
+ if (isClassConstructor(member)) {
438
+ patchBuilder(member);
439
+ return member;
440
+ }
411
441
  if (typeof member !== "function")
412
442
  return wrapImportedCapability(member);
413
443
  return wrapImportedMember(member, target, invoke);
@@ -666,7 +666,7 @@ function wrapMatchers(matchers, path = "expect") {
666
666
  }, (error) => {
667
667
  if (phase && controller)
668
668
  controller.finish(phase, error);
669
- throw error;
669
+ throw coverageRuntime.cleanInstrumentationStack(error);
670
670
  });
671
671
  }
672
672
  if (phase && controller)
@@ -676,7 +676,7 @@ function wrapMatchers(matchers, path = "expect") {
676
676
  catch (error) {
677
677
  if (phase && controller)
678
678
  controller.finish(phase, error);
679
- throw error;
679
+ throw coverageRuntime.cleanInstrumentationStack(error);
680
680
  }
681
681
  };
682
682
  },
@@ -149,6 +149,7 @@ function createState() {
149
149
  serverBuffers: /* @__PURE__ */ new Map(),
150
150
  persistedServerRecords: /* @__PURE__ */ new Set(),
151
151
  backgroundBuffers: /* @__PURE__ */ new Map(),
152
+ backgroundWriters: /* @__PURE__ */ new Map(),
152
153
  backgroundSequence: 0,
153
154
  runtimeSnapshots: false,
154
155
  assertionPhases: /* @__PURE__ */ new Map(),
@@ -371,28 +372,12 @@ function flushAllBufferedServerEvidence() {
371
372
  flushBufferedBackgroundEvidence(runId);
372
373
  }
373
374
  function flushBufferedBackgroundEvidence(runId) {
374
- var _a8;
375
375
  if (isBrowser)
376
376
  return void 0;
377
377
  const records = state.backgroundBuffers.get(runId);
378
378
  if (!records || records.size === 0)
379
379
  return void 0;
380
- const fs = getFs();
381
- if (!fs)
382
- return void 0;
383
- const directory = backgroundEvidenceDirectory(runId);
384
- const shard = (_a8 = process.env["SUPERCOV_EXECUTION_LOG_SHARD"]) != null ? _a8 : "process";
385
- const writer = `${shard}-${process.pid}`;
386
- const payload = [...records.values()].map((record) => JSON.stringify(record)).join("\n") + "\n";
387
- try {
388
- fs.mkdirSync(directory, { recursive: true });
389
- const nextSequence = writeExclusiveBackgroundRecord(fs, runId, writer, state.backgroundSequence, payload);
390
- state.backgroundSequence = nextSequence;
391
- state.backgroundBuffers.delete(runId);
392
- return backgroundEvidencePath(runId, `${writer}-${nextSequence - 1}`);
393
- } catch (e) {
394
- return void 0;
395
- }
380
+ return state.backgroundWriters.get(runId);
396
381
  }
397
382
  function writeExclusiveBackgroundRecord(fs, runId, writer, initialSequence, payload) {
398
383
  let sequence = initialSequence;
@@ -409,6 +394,30 @@ function writeExclusiveBackgroundRecord(fs, runId, writer, initialSequence, payl
409
394
  }
410
395
  throw Object.assign(new Error("Could not allocate a collision-free Supercov background evidence record"), { code: "SUPERCOV_BACKGROUND_COLLISION_LIMIT" });
411
396
  }
397
+ function appendDurableBackgroundRecord(fs, runId, record) {
398
+ var _a8;
399
+ const records = state.backgroundBuffers.get(runId) != null ? state.backgroundBuffers.get(runId) : /* @__PURE__ */ new Map();
400
+ const key = serverRecordKey(record);
401
+ if (records.has(key))
402
+ return state.backgroundWriters.get(runId);
403
+ const directory = backgroundEvidenceDirectory(runId);
404
+ const payload = JSON.stringify(record) + "\n";
405
+ fs.mkdirSync(directory, { recursive: true });
406
+ let path = state.backgroundWriters.get(runId);
407
+ if (!path) {
408
+ const shard = (_a8 = process.env["SUPERCOV_EXECUTION_LOG_SHARD"]) != null ? _a8 : "process";
409
+ const writer = `${shard}-${process.pid}`;
410
+ const nextSequence = writeExclusiveBackgroundRecord(fs, runId, writer, state.backgroundSequence, payload);
411
+ state.backgroundSequence = nextSequence;
412
+ path = backgroundEvidencePath(runId, `${writer}-${nextSequence - 1}`);
413
+ state.backgroundWriters.set(runId, path);
414
+ } else {
415
+ fs.appendFileSync(path, payload);
416
+ }
417
+ records.set(key, record);
418
+ state.backgroundBuffers.set(runId, records);
419
+ return path;
420
+ }
412
421
  var _a7;
413
422
  if (!isBrowser) {
414
423
  const flushers = (_a7 = runtimeGlobal.__SUPERCOV_BUFFER_FLUSHERS__) != null ? _a7 : /* @__PURE__ */ new Set();
@@ -475,18 +484,19 @@ function appendServer(record) {
475
484
  if (deduplicationKey && state.persistedServerRecords.has(deduplicationKey))
476
485
  return;
477
486
  if (!path) {
478
- const buffered = (_d = state.backgroundBuffers.get(runId)) != null ? _d : /* @__PURE__ */ new Map();
479
- buffered.set(serverRecordKey(serialized), serialized);
480
- state.backgroundBuffers.set(runId, buffered);
481
- if (buffered.size >= 4096)
482
- flushBufferedBackgroundEvidence(runId);
487
+ appendDurableBackgroundRecord(fs, runId, serialized);
483
488
  return;
484
489
  }
485
490
  fs.mkdirSync(directory, { recursive: true });
486
491
  fs.appendFileSync(path, JSON.stringify(serialized) + "\n");
487
492
  if (deduplicationKey)
488
493
  state.persistedServerRecords.add(deduplicationKey);
489
- } catch (e) {
494
+ } catch (cause) {
495
+ const detail = cause instanceof Error ? cause.message : String(cause);
496
+ const failure = new Error(`Supercov could not persist coverage evidence for run ${runId}: ${detail}`);
497
+ failure.code = "SUPERCOV_EVIDENCE_TRANSPORT_FAILED";
498
+ failure.cause = cause;
499
+ throw failure;
490
500
  }
491
501
  }
492
502
  function environmentRequestContext() {
@@ -542,6 +552,20 @@ function finishAssertionPhase(phase, error) {
542
552
  if (error !== void 0)
543
553
  phase.error = error instanceof Error ? error.message : String(error);
544
554
  }
555
+
556
+ function cleanInstrumentationStack(error) {
557
+ if (!error || typeof error !== "object" || typeof error.stack !== "string")
558
+ return error;
559
+ const lines = error.stack.split("\n");
560
+ const visible = lines.filter((line, index) => index === 0 || !/[\\/]\.supercov[\\/](?:playwright|nodeTest|vitest|runtime|launchSupervisor|nodeAssert|nodeAssertStrict|nodeAssertAdapter|register|resolve-loader)\.(?:js|mjs)(?::|\))/u.test(line));
561
+ if (visible.length !== lines.length) {
562
+ try {
563
+ error.stack = visible.join("\n");
564
+ } catch (e) {
565
+ }
566
+ }
567
+ return error;
568
+ }
545
569
  function withNodeAssertionPhase(operation, source, callback) {
546
570
  var _a8;
547
571
  const context = currentRequestContext();
@@ -571,13 +595,13 @@ function withNodeAssertionPhase(operation, source, callback) {
571
595
  return value;
572
596
  }, (error) => {
573
597
  finishAssertionPhase(phase, error);
574
- throw error;
598
+ throw cleanInstrumentationStack(error);
575
599
  });
576
600
  finishAssertionPhase(phase);
577
601
  return result;
578
602
  } catch (error) {
579
603
  finishAssertionPhase(phase, error);
580
- throw error;
604
+ throw cleanInstrumentationStack(error);
581
605
  }
582
606
  }
583
607
  function takeNodeAssertionPhases(scope) {
@@ -1021,6 +1045,7 @@ const directRuntimeApi = {
1021
1045
  coverageCarrier,
1022
1046
  coverageContextEnvironment,
1023
1047
  coverageContextHeaders,
1048
+ cleanInstrumentationStack,
1024
1049
  coverageHit,
1025
1050
  coverageHitV2,
1026
1051
  coverageSnapshot,
@@ -1067,6 +1092,7 @@ export {
1067
1092
  coverageCarrier,
1068
1093
  coverageContextEnvironment,
1069
1094
  coverageContextHeaders,
1095
+ cleanInstrumentationStack,
1070
1096
  coverageHit,
1071
1097
  coverageHitV2,
1072
1098
  coverageSnapshot,