supercov 0.0.34 → 0.0.36

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
@@ -40,6 +40,7 @@ npx supercov -- cargo test
40
40
  npx supercov -- cargo nextest run
41
41
  npx supercov -- pytest
42
42
  npx supercov -- python -m unittest
43
+ npx supercov -- bundle exec rspec
43
44
  ```
44
45
 
45
46
  ## Give Supercov a job
@@ -116,6 +117,18 @@ The denominator comes from source structure before the run, so adding or removin
116
117
 
117
118
  Supercov requires Node.js 22 or newer. Rust support currently uses Rust 1.95; cargo-nextest 0.9.138 and 0.9.140 are supported. Python support requires CPython 3.12 or newer and measures pytest and unittest runs. Ruby support requires Ruby 3.3 or newer (3.4 or newer for full measurement) and measures RSpec, Minitest, test-unit and Cucumber runs.
118
119
 
120
+ ## Supported operating systems and architectures
121
+
122
+ The CLI is a single native binary. `npx supercov` selects the build for the machine it runs on; nothing is compiled during installation.
123
+
124
+ | Operating system | Architectures | Notes |
125
+ | --- | --- | --- |
126
+ | macOS | arm64 (Apple silicon), x64 (Intel) | macOS 11 or newer |
127
+ | Linux | arm64, x64 | glibc and musl (Alpine) builds, chosen automatically |
128
+ | Windows | arm64, x64 | Windows 10 or newer |
129
+
130
+ On Windows, JavaScript and TypeScript suites are verified on every release; Python, Ruby, and Rust suites are not yet verified there. Containers, VMs, and remote executors run the same Linux builds. The npm package is the current release channel for every language, including Rust, Python, and Ruby projects. A PyPI package (`supercov-cli`), a RubyGems gem (`supercov`), and a crate (`supercov`) exist for macOS arm64 at an earlier version and are being brought up to date with each release.
131
+
119
132
  ## Supported test suites
120
133
 
121
134
  Supercov uses exact per-test attribution where an adapter is available. For other supported runners, it reports aggregate structural coverage instead of guessing which test covered a path.
@@ -129,6 +142,9 @@ Supercov uses exact per-test attribution where an adapter is available. For othe
129
142
  | AVA and Mocha | Aggregate structural coverage |
130
143
  | Cargo's standard libtest runner | Exact test and attempt identity |
131
144
  | cargo-nextest | Exact test, attempt, retry, and binary identity |
145
+ | RSpec | Exact example and before/example/after phase identity |
146
+ | Minitest and test-unit | Exact test and setup/test/teardown identity |
147
+ | Cucumber | Exact scenario and hook-phase identity |
132
148
 
133
149
  Supercov works with Vite, Next, Turbopack, Webpack, esbuild, SWC, and projects with no build step. One command can collect evidence from several supported runners into a single run.
134
150
 
@@ -58,7 +58,8 @@ owners are allowed, and everyone else is denied. Branches, decision vectors, and
58
58
  MC/DC expose those missing cases instead of treating one executed line as proof
59
59
  that the decision is safe.
60
60
 
61
- The same principle applies to Rust boolean expressions and control flow.
61
+ The same principle applies to Rust, Python, and Ruby boolean expressions and
62
+ control flow.
62
63
 
63
64
  ## What 100% means
64
65
 
@@ -15,15 +15,19 @@ Supercov supports JavaScript, TypeScript, Rust, Python, and Ruby today.
15
15
 
16
16
  You need:
17
17
 
18
+ - macOS (arm64 or x64), Linux (arm64 or x64, glibc or musl), or Windows (arm64
19
+ or x64; JavaScript and TypeScript suites are verified there today);
18
20
  - Node.js 22 or newer;
19
21
  - a test command that already works in the repository; and
20
22
  - for Rust, the Rust 1.95 toolchain;
21
23
  - for Python, CPython 3.12 or newer with pytest or unittest;
22
24
  - for Ruby, Ruby 3.4 or newer with RSpec, Minitest, test-unit or Cucumber (3.3 measures lines, methods and simple branches only).
23
25
 
24
- The CLI is distributed through npm, even for Rust projects. The first `npx`
25
- invocation may download Supercov from the npm registry. Supercov itself does not
26
- upload your source or coverage evidence to a Supercov service.
26
+ The CLI is a native binary distributed through npm, even for Rust, Python, and
27
+ Ruby projects: `npx supercov` picks the build for your operating system and
28
+ architecture, and nothing is compiled on install. The first `npx` invocation may
29
+ download Supercov from the npm registry. Supercov itself does not upload your
30
+ source or coverage evidence to a Supercov service.
27
31
 
28
32
  ## 1. Run your real test command
29
33
 
@@ -63,6 +63,20 @@ TypeScript, and TSX are supported.
63
63
  Supercov instruments an isolated copy. It does not ask you to add an import,
64
64
  reporter, plugin, or alternate build output.
65
65
 
66
+ A suite that runs its own compiled output is built before the tests start.
67
+ Supercov reads the tests: one that imports from `dist/`, or launches a package
68
+ script that does—`spawn("npm", ["run", "start"])`, `execSync("npm run
69
+ start")`, or any launch handed to the shell as one string—means the build is
70
+ part of the run, and a `build` script is run inside the isolated copy first.
71
+ Without it the server under test would never exist. A launch that only names a
72
+ build subcommand, such as `vite build`, is not taken as consuming a build.
73
+
74
+ Instrumented TypeScript is exempt from the host's type policy whether the
75
+ compile is Supercov's own build step or the test command's own `tsc`.
76
+ Instrumentation necessarily rewrites control-flow expressions in ways a type
77
+ checker cannot narrow through, so a project that compiles inside its test
78
+ command builds under measurement exactly as it does without it.
79
+
66
80
  ### Browsers, servers, and child processes
67
81
 
68
82
  Playwright support includes Chromium, Firefox, and WebKit, along with pages,
@@ -82,6 +96,15 @@ a short drain window after the test command finishes so buffered evidence can
82
96
  arrive. Work without a reliable test identity is kept as background coverage
83
97
  instead of being assigned to an arbitrary test.
84
98
 
99
+ A child a test stops in teardown keeps the coverage it produced. Buffered
100
+ evidence is written when a terminating signal arrives—`SIGTERM`, `SIGINT`,
101
+ `SIGHUP`—not only when the process exits on its own, so killing a gateway
102
+ after the request it served does not lose the request. The program's own
103
+ signal handling is untouched: a process with no handler still dies from the
104
+ signal exactly as it would unmeasured, and one with its own handler keeps
105
+ it. `SIGKILL` cannot be caught by anything and is the one stop that loses
106
+ whatever was still buffered.
107
+
85
108
  ## Rust
86
109
 
87
110
  | Runner | Attribution | Current requirement |
@@ -208,6 +231,16 @@ decisions, `||=`, loops, `rescue` flow, a second statement on a line) is
208
231
  declared unmeasured on that interpreter rather than shown as a gap. Ruby 3.4
209
232
  and newer measure everything.
210
233
 
234
+ Ruby reads its own coverage as the interpreter exits, and that shapes what a
235
+ stopped process keeps. A process ended by a signal it can catch—`SIGTERM`
236
+ from a test's teardown, `SIGINT`—unwinds through that exit and reports
237
+ everything it measured. A process killed with `SIGKILL`, or one that leaves
238
+ through `exit!`, never gets there and takes with it whatever it observed since
239
+ its last test boundary. Supercov cannot recover that or say which lines it
240
+ would have been, so the run declares that a process did not report, which
241
+ blocks completeness, rather than counting those lines against the code. Stop a
242
+ Ruby server with `SIGTERM`, or wait for it to exit, and it reports.
243
+
211
244
  ```sh
212
245
  npx supercov -- rspec
213
246
  npx supercov -- bundle exec rspec
@@ -100,6 +100,14 @@ Read the reason in the summary, `scope`, or `gaps` output. Fix a configuration
100
100
  problem when one is named. Otherwise stop the coverage loop and report the
101
101
  limit; do not change application code or add a meaningless test to chase 100%.
102
102
 
103
+ One limit names a cause you can remove. `ruby-process-did-not-report` means a
104
+ Ruby process the run measured ended without reporting: it was killed with
105
+ `SIGKILL`, or left through `exit!`, before the exit where Ruby hands over its
106
+ coverage. Whatever that process observed since its last test boundary is gone,
107
+ and its lines are not shown as uncovered because nothing can say whether they
108
+ ran. Stop the process with `SIGTERM` instead, or wait for it to exit, and the
109
+ limit does not appear.
110
+
103
111
  ## Coverage is aggregate instead of per test
104
112
 
105
113
  Aggregate coverage still shows which source ran, but Supercov cannot truthfully
@@ -68,12 +68,15 @@ aggregate instead of being assigned to whichever test happened to be nearby.
68
68
 
69
69
  Release checks cover program behavior before and after instrumentation, line and
70
70
  branch results, decision vectors and MC/DC, supported JavaScript, TypeScript,
71
- and Rust runner contracts, Chromium, Firefox, and WebKit execution, clean
72
- installation, interrupted-run recovery, and isolated publication.
71
+ Rust, Python, and Ruby runner contracts, Chromium, Firefox, and WebKit
72
+ execution, clean installation, interrupted-run recovery, and isolated
73
+ publication.
73
74
 
74
75
  JavaScript behavior is exercised against a pinned TC39 Test262 corpus. MC/DC
75
76
  cases are also compared with an independent LLVM implementation so a
76
- self-consistent calculation error does not pass unnoticed.
77
+ self-consistent calculation error does not pass unnoticed. Python and Ruby
78
+ gates run real suites through their supported runners and assert the resulting
79
+ coverage totals, test identity, and measurement limits.
77
80
 
78
81
  These checks reduce risk; they do not replace reviewing the assertions and
79
82
  behavior protected by a new test.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.34",
4
- "description": "Zero-edit, runner-aware coverage completeness for JavaScript, TypeScript, Rust, and Python test suites",
3
+ "version": "0.0.36",
4
+ "description": "Zero-edit, runner-aware coverage completeness for JavaScript, TypeScript, Rust, Python, and Ruby test suites",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/supercorp-ai/supercov.git"
9
9
  },
10
- "homepage": "https://github.com/supercorp-ai/supercov#readme",
10
+ "homepage": "https://supercov.com",
11
11
  "bugs": {
12
12
  "url": "https://github.com/supercorp-ai/supercov/issues"
13
13
  },
@@ -65,6 +65,7 @@
65
65
  "benchmark:python-monitoring": "cargo build -p supercov && node scripts/python-monitoring-benchmark.mjs",
66
66
  "check": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && npm run test && npm run test:runtime && npm run test:rust-assets && node scripts/package-preflight.mjs",
67
67
  "release:check": "npm run check && npm run test:engine && npm run test:fixture && npm run test:watchdog && npm run test:engine-contract && npm run test:agent && npm run test:child-attribution && npm run test:host-loader && npm run test:python-monitoring && npm run test:ruby-coverage && npm run test:packed-npx && npm run test:clang-mcdc && npm run benchmark:check",
68
+ "release:bump": "node scripts/bump-version.mjs",
68
69
  "prepack": "node scripts/package-preflight.mjs",
69
70
  "prepublishOnly": "npm run release:check",
70
71
  "test:child-attribution": "cargo build -p supercov && node scripts/rust-child-attribution-integration.mjs",
@@ -73,12 +74,14 @@
73
74
  "test:ruby-coverage": "cargo build -p supercov && node scripts/ruby-coverage-integration.mjs"
74
75
  },
75
76
  "optionalDependencies": {
76
- "@supercov/cli-darwin-arm64": "0.0.34",
77
- "@supercov/cli-darwin-x64": "0.0.34",
78
- "@supercov/cli-linux-arm64-gnu": "0.0.34",
79
- "@supercov/cli-linux-arm64-musl": "0.0.34",
80
- "@supercov/cli-linux-x64-gnu": "0.0.34",
81
- "@supercov/cli-linux-x64-musl": "0.0.34"
77
+ "@supercov/cli-darwin-arm64": "0.0.36",
78
+ "@supercov/cli-darwin-x64": "0.0.36",
79
+ "@supercov/cli-linux-arm64-gnu": "0.0.36",
80
+ "@supercov/cli-linux-arm64-musl": "0.0.36",
81
+ "@supercov/cli-linux-x64-gnu": "0.0.36",
82
+ "@supercov/cli-linux-x64-musl": "0.0.36",
83
+ "@supercov/cli-win32-arm64": "0.0.36",
84
+ "@supercov/cli-win32-x64": "0.0.36"
82
85
  },
83
86
  "peerDependencies": {
84
87
  "@playwright/test": ">=1.55.0",
@@ -13,6 +13,7 @@ import http from "node:http";
13
13
  import https from "node:https";
14
14
  import { syncBuiltinESMExports } from "node:module";
15
15
  import { relative, resolve, sep } from "node:path";
16
+ import { pathToFileURL } from "node:url";
16
17
  import * as standardPlaywright from "@playwright/test";
17
18
  import * as coverageRuntime from "./runtime.mjs";
18
19
  import { inferTestProvenance } from "./provenance.mjs";
@@ -147,8 +148,11 @@ function callerSource() {
147
148
  .replace(/\/$/, "");
148
149
  if (!projectRoot)
149
150
  return normalized;
151
+ // A stack frame's URL is what Node's pathToFileURL produces -- on Windows
152
+ // `file:///C:/...`, never a hand-built `file://C:/...` -- so strip the
153
+ // prefix derived the same way, then the bare path for frames without one.
150
154
  return normalized
151
- .replace(`file://${projectRoot}/`, "")
155
+ .replace(`${pathToFileURL(projectRoot).href}/`, "")
152
156
  .replace(`${projectRoot}/`, "");
153
157
  }
154
158
  class CoveragePhaseController {
@@ -168,9 +172,11 @@ class CoveragePhaseController {
168
172
  scriptUpdate = Promise.resolve();
169
173
  proxyCache = new WeakMap();
170
174
  runtimeSnapshots;
171
- // Contexts this controller found rather than created: it does not know
175
+ // Contexts whose creation the collector did not observe: it does not know
172
176
  // what `extraHTTPHeaders` their owner configured, so it must not rewrite
173
177
  // them (the scope cookie and the fetch patch still carry attribution).
178
+ // A worker-scoped context launched through a patched BrowserType is found
179
+ // later but is not opaque: its original headers were captured at launch.
174
180
  adoptedContexts = new Set();
175
181
  // Listeners installed on contexts that outlive this test, removed on
176
182
  // dispose so a worker-scoped context does not keep registering pages with
@@ -257,8 +263,10 @@ class CoveragePhaseController {
257
263
  if (this.disposed)
258
264
  return;
259
265
  for (const context of liveTrackedContexts()) {
260
- if (!this.contexts.has(context))
261
- await this.registerContext(context, this.configuredHeaders, { adopted: true }).catch(() => undefined);
266
+ if (!this.contexts.has(context)) {
267
+ const headersKnown = trackedContextConfiguredHeaders.has(context);
268
+ await this.registerContext(context, trackedContextConfiguredHeaders.get(context) ?? this.configuredHeaders, { adopted: !headersKnown }).catch(() => undefined);
269
+ }
262
270
  }
263
271
  }
264
272
  async registerPage(page) {
@@ -266,8 +274,11 @@ class CoveragePhaseController {
266
274
  return;
267
275
  timingCount("registerPage");
268
276
  this.pages.add(page);
269
- await this.registerContext(page.context(), this.configuredHeaders, {
270
- adopted: !this.contexts.has(page.context()) && liveTrackedContexts().has(page.context()),
277
+ const context = page.context();
278
+ const tracked = liveTrackedContexts().has(context);
279
+ const headersKnown = trackedContextConfiguredHeaders.has(context);
280
+ await this.registerContext(context, trackedContextConfiguredHeaders.get(context) ?? this.configuredHeaders, {
281
+ adopted: !this.contexts.has(context) && tracked && !headersKnown,
271
282
  });
272
283
  const cdp = await page.context().newCDPSession(page).catch(() => undefined);
273
284
  if (cdp)
@@ -399,6 +410,14 @@ class CoveragePhaseController {
399
410
  this.disposed = true;
400
411
  await Promise.all([...this.pendingRegistrations]);
401
412
  await this.scriptUpdate;
413
+ // A worker-scoped context survives this test. Remove its test scope
414
+ // while preserving the exact headers supplied by the suite so work
415
+ // between tests cannot be charged to the attempt that just ended.
416
+ await Promise.all([...this.contexts]
417
+ .filter((context) => !this.adoptedContexts.has(context))
418
+ .map((context) => context
419
+ .setExtraHTTPHeaders(this.contextConfiguredHeaders.get(context) ?? {})
420
+ .catch(() => undefined)));
402
421
  for (const [context, listeners] of this.contextListeners) {
403
422
  for (const [event, listener] of listeners)
404
423
  context.off?.(event, listener);
@@ -505,9 +524,10 @@ class CoveragePhaseController {
505
524
  trackBrowser(result);
506
525
  if (typeof candidate["pages"] === "function" &&
507
526
  typeof candidate["route"] === "function") {
508
- trackContext(result);
509
- await this.registerContext(result, (sourceArgs?.[0]
510
- ?.extraHTTPHeaders ?? this.configuredHeaders));
527
+ const configuredHeaders = sourceArgs?.[0]
528
+ ?.extraHTTPHeaders ?? this.configuredHeaders;
529
+ trackContext(result, configuredHeaders);
530
+ await this.registerContext(result, configuredHeaders);
511
531
  }
512
532
  if (typeof candidate["frames"] === "function" &&
513
533
  typeof candidate["context"] === "function")
@@ -630,6 +650,10 @@ const controllers = new Map();
630
650
  // launches are recorded here, and each test's controller adopts what is live.
631
651
  const trackedBrowsers = new Set();
632
652
  const trackedContexts = new Set();
653
+ // Creation-time headers for contexts launched through patched Playwright
654
+ // methods. Capturing an explicit empty object matters: it proves replacing
655
+ // the headers later is safe, unlike a context discovered only by enumeration.
656
+ const trackedContextConfiguredHeaders = new WeakMap();
633
657
  const patchedPrototypes = new WeakSet();
634
658
  function trackBrowser(browser) {
635
659
  if (!browser || typeof browser !== "object" || trackedBrowsers.has(browser))
@@ -638,8 +662,12 @@ function trackBrowser(browser) {
638
662
  browser.once?.("disconnected", () => trackedBrowsers.delete(browser));
639
663
  patchBrowserPrototype(browser);
640
664
  }
641
- function trackContext(context) {
642
- if (!context || typeof context !== "object" || trackedContexts.has(context))
665
+ function trackContext(context, configuredHeaders) {
666
+ if (!context || typeof context !== "object")
667
+ return;
668
+ if (configuredHeaders !== undefined)
669
+ trackedContextConfiguredHeaders.set(context, configuredHeaders);
670
+ if (trackedContexts.has(context))
643
671
  return;
644
672
  trackedContexts.add(context);
645
673
  context.once?.("close", () => trackedContexts.delete(context));
@@ -685,10 +713,11 @@ function patchBrowserPrototype(browser) {
685
713
  patchedPrototypes.add(prototype);
686
714
  patchOnce(browser, "newContext", (original) => async function (...args) {
687
715
  const context = await original.apply(this, args);
688
- trackContext(context);
716
+ const configuredHeaders = args[0]?.extraHTTPHeaders ?? {};
717
+ trackContext(context, configuredHeaders);
689
718
  const controller = activeController;
690
719
  if (controller && !controller.contexts.has(context))
691
- await controller.registerContext(context, args[0]?.extraHTTPHeaders ?? controller.configuredHeaders, { adopted: true }).catch(() => undefined);
720
+ await controller.registerContext(context, configuredHeaders).catch(() => undefined);
692
721
  return context;
693
722
  });
694
723
  }
@@ -734,10 +763,11 @@ function installBrowserLaunchTracking() {
734
763
  }
735
764
  patchOnce(browserType, "launchPersistentContext", (original) => async function (...args) {
736
765
  const context = await original.apply(this, args);
737
- trackContext(context);
766
+ const configuredHeaders = args[1]?.extraHTTPHeaders ?? {};
767
+ trackContext(context, configuredHeaders);
738
768
  const controller = activeController;
739
769
  if (controller)
740
- await controller.registerContext(context, args[1]?.extraHTTPHeaders ?? controller.configuredHeaders, { adopted: true }).catch(() => undefined);
770
+ await controller.registerContext(context, configuredHeaders).catch(() => undefined);
741
771
  return context;
742
772
  });
743
773
  }
@@ -18,8 +18,10 @@ function belongsToProject(parentURL) {
18
18
  return false;
19
19
  if (!PROJECT_ROOT)
20
20
  return parentURL.includes("/tests/");
21
- const normalizedRoot = PROJECT_ROOT.replaceAll("\\", "/").replace(/\/$/, "");
22
- const projectURL = `file://${normalizedRoot}/`;
21
+ // Derive the URL the way Node derives parentURL, or the two never match on
22
+ // Windows: a hand-built `file://C:/...` is not the `file:///C:/...` that
23
+ // pathToFileURL produces, and every project module would read as foreign.
24
+ const projectURL = pathToFileURL(PROJECT_ROOT).href.replace(/\/?$/, "/");
23
25
  const generatedURL = `${projectURL}.supercov/`;
24
26
  return (parentURL.startsWith(projectURL) && !parentURL.startsWith(generatedURL));
25
27
  }
@@ -480,12 +480,59 @@ function appendDurableBackgroundRecord(fs, runId, record) {
480
480
  return path;
481
481
  }
482
482
  var _a7;
483
+ // Evidence buffered for the current turn is lost when a signal ends the
484
+ // process, because "exit" does not run for one -- and killing a child in
485
+ // teardown is exactly how a suite stops a gateway it started. Flush on the
486
+ // terminating signals as well as on exit.
487
+ //
488
+ // Exactly one listener per signal serves every flusher: two of our own would
489
+ // each see a sibling listener, neither would judge itself alone, and a
490
+ // signalled process would stay alive. Having flushed, restore what the signal
491
+ // would have done -- if nothing else is listening the program expected to die,
492
+ // so re-raise with our listener gone; if the program installed its own handler
493
+ // it deliberately suppressed that default and we must not exit on its behalf.
494
+ function flushOnTermination(flush) {
495
+ var _a9;
496
+ if (typeof process === "undefined")
497
+ return;
498
+ const flushers = (_a9 = runtimeGlobal.__SUPERCOV_TERMINATION_FLUSHERS__) != null ? _a9 : /* @__PURE__ */ new Set();
499
+ runtimeGlobal.__SUPERCOV_TERMINATION_FLUSHERS__ = flushers;
500
+ flushers.add(flush);
501
+ if (runtimeGlobal.__SUPERCOV_TERMINATION_INSTALLED__)
502
+ return;
503
+ runtimeGlobal.__SUPERCOV_TERMINATION_INSTALLED__ = true;
504
+ const drain = () => {
505
+ var _a10;
506
+ for (const value of (_a10 = runtimeGlobal.__SUPERCOV_TERMINATION_FLUSHERS__) != null ? _a10 : []) {
507
+ try {
508
+ value();
509
+ } catch (e) {
510
+ }
511
+ }
512
+ };
513
+ try {
514
+ process.on("exit", drain);
515
+ } catch (e) {
516
+ }
517
+ for (const signal of ["SIGTERM", "SIGINT", "SIGHUP", "SIGBREAK"]) {
518
+ try {
519
+ process.on(signal, () => {
520
+ drain();
521
+ if (process.listenerCount(signal) <= 1) {
522
+ process.removeAllListeners(signal);
523
+ process.kill(process.pid, signal);
524
+ }
525
+ });
526
+ } catch (e) {
527
+ }
528
+ }
529
+ }
483
530
  if (!isBrowser) {
484
531
  const flushers = (_a7 = runtimeGlobal.__SUPERCOV_BUFFER_FLUSHERS__) != null ? _a7 : /* @__PURE__ */ new Set();
485
532
  runtimeGlobal.__SUPERCOV_BUFFER_FLUSHERS__ = flushers;
486
533
  flushers.add(flushAllBufferedServerEvidence);
487
534
  if (!runtimeGlobal.__SUPERCOV_BUFFER_EXIT_INSTALLED__) {
488
- process.once("exit", () => {
535
+ flushOnTermination(() => {
489
536
  var _a8;
490
537
  for (const flush of (_a8 = runtimeGlobal.__SUPERCOV_BUFFER_FLUSHERS__) != null ? _a8 : [])
491
538
  flush();
@@ -538,7 +585,7 @@ function enqueueServerAppend(directory, path, line, runId) {
538
585
  if (!state.serverExitHookInstalled && typeof process !== "undefined") {
539
586
  state.serverExitHookInstalled = true;
540
587
  try {
541
- process.on("exit", flushServerAppends);
588
+ flushOnTermination(flushServerAppends);
542
589
  } catch (e) {
543
590
  }
544
591
  }
@@ -1238,6 +1285,7 @@ export {
1238
1285
  enableRuntimeSnapshotEvidence,
1239
1286
  flushBufferedBackgroundEvidence,
1240
1287
  flushBufferedServerEvidence,
1288
+ flushOnTermination,
1241
1289
  loopBegin,
1242
1290
  loopEnd,
1243
1291
  loopEntered,