supercov 4.24.5 → 4.24.7

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.
@@ -77,6 +77,13 @@ Bun keeps its own formatted reporter titles. Async test bodies retain their
77
77
  identity across `await`, including concurrent execution. Lifecycle hooks have
78
78
  separate setup identities and do not credit their coverage to the test body.
79
79
 
80
+ Bun can also run another test runner by its entry point, such as
81
+ `bun node_modules/vitest/vitest.mjs run` or `bun node_modules/mocha/bin/mocha.js`.
82
+ The Bun adapter sets that runner up as Node's preload would: Vitest gets
83
+ Supercov's generated config, which merges the project's own (including one given
84
+ by `--config`), and Mocha's runner is patched before it loads. Their tests are
85
+ attributed as they are under Node.
86
+
80
87
  Callback completion supplies the recorded outcome. If Bun interrupts a callback
81
88
  before it completes, Supercov preserves its named coverage with an **unknown**
82
89
  outcome. Bun's final runner verdict can additionally reflect a timeout, hook
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "4.24.5",
3
+ "version": "4.24.7",
4
4
  "description": "Coverage, security and code quality for coding agents",
5
5
  "keywords": [
6
6
  "coverage",
@@ -82,7 +82,7 @@
82
82
  "eval:skill": "claude plugin eval plugins/supercov --scaffold --trust-plugin --no-publish --model claude-sonnet-5 --judge-model claude-haiku-4-5 --threshold 0.66 --tag behavior trigger --allow-tools Bash Edit Write --max-cost-usd 15",
83
83
  "eval:skill-triggers": "claude plugin eval plugins/supercov --scaffold --trust-plugin --no-publish --model claude-sonnet-5 --judge-model claude-haiku-4-5 --threshold 0.66 --tag trigger --ablation none --max-cost-usd 5",
84
84
  "test:engine": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace && npm run test:runtime && npm run test:rust-assets && npm run test:engine-integrations",
85
- "test:engine-integrations": "cargo build -p supercov && node scripts/html-report-integration.mjs && node scripts/rust-process-supervision.mjs && node scripts/rust-direct-node-integration.mjs && node scripts/rust-public-run-integration.mjs && node scripts/rust-embedded-runtime-integration.mjs && node scripts/rust-direct-vitest-integration.mjs && node scripts/rust-direct-jest-integration.mjs && node scripts/mocha-failure-output.mjs && node scripts/failure-output.mjs node ava tap && node scripts/side-by-side.mjs node && node scripts/merge-across.mjs && node scripts/import-line-evidence.mjs && node scripts/mock-reach.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/leaving-page.mjs && node scripts/shared-page.mjs && node scripts/context-headers.mjs && node scripts/context-cookies.mjs && node scripts/hidden-request-emit.mjs && node scripts/js-syntax-zoo.mjs && node scripts/ruby-syntax-zoo.mjs && node scripts/rust-syntax-zoo.mjs && node scripts/python-syntax-zoo.mjs && node scripts/go-syntax-zoo.mjs && node scripts/java-syntax-zoo.mjs && node scripts/background-by-time.mjs && node scripts/python-server-playwright.mjs && node scripts/ruby-server-playwright.mjs && node scripts/go-server-playwright.mjs && node scripts/jvm-server-playwright.mjs && node scripts/docker-server-playwright.mjs && node scripts/docker-clients-playwright.mjs && node scripts/docker-exec-playwright.mjs && node scripts/rust-classic-browser-integration.mjs && node scripts/exec-handover.mjs && node scripts/rust-region-container.mjs && node scripts/rust-top-packages-integration.mjs && node scripts/rust-generic-esbuild-integration.mjs && node scripts/rust-generic-tsc-integration.mjs && node scripts/rust-generic-build-matrix.mjs && node scripts/rust-vite-playwright-integration.mjs && node scripts/workspace-crash-integration.mjs",
85
+ "test:engine-integrations": "cargo build -p supercov && node scripts/html-report-integration.mjs && node scripts/rust-process-supervision.mjs && node scripts/rust-direct-node-integration.mjs && node scripts/rust-public-run-integration.mjs && node scripts/rust-embedded-runtime-integration.mjs && node scripts/rust-direct-vitest-integration.mjs && node scripts/rust-nested-vitest-integration.mjs && node scripts/rust-direct-jest-integration.mjs && node scripts/mocha-failure-output.mjs && node scripts/failure-output.mjs node ava tap && node scripts/side-by-side.mjs node && node scripts/merge-across.mjs && node scripts/import-line-evidence.mjs && node scripts/mock-reach.mjs && node scripts/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/leaving-page.mjs && node scripts/shared-page.mjs && node scripts/context-headers.mjs && node scripts/context-cookies.mjs && node scripts/hidden-request-emit.mjs && node scripts/js-syntax-zoo.mjs && node scripts/ruby-syntax-zoo.mjs && node scripts/rust-syntax-zoo.mjs && node scripts/python-syntax-zoo.mjs && node scripts/go-syntax-zoo.mjs && node scripts/java-syntax-zoo.mjs && node scripts/background-by-time.mjs && node scripts/python-server-playwright.mjs && node scripts/ruby-server-playwright.mjs && node scripts/go-server-playwright.mjs && node scripts/jvm-server-playwright.mjs && node scripts/docker-server-playwright.mjs && node scripts/docker-clients-playwright.mjs && node scripts/docker-exec-playwright.mjs && node scripts/rust-classic-browser-integration.mjs && node scripts/exec-handover.mjs && node scripts/rust-region-container.mjs && node scripts/rust-top-packages-integration.mjs && node scripts/rust-generic-esbuild-integration.mjs && node scripts/rust-generic-tsc-integration.mjs && node scripts/rust-generic-build-matrix.mjs && node scripts/rust-vite-playwright-integration.mjs && node scripts/workspace-crash-integration.mjs",
86
86
  "test:platform": "cargo test --workspace && cargo build -p supercov && node scripts/rust-process-supervision.mjs && node scripts/workspace-crash-integration.mjs",
87
87
  "test:native-package": "cargo build --release -p supercov && node scripts/native-package-integration.mjs && node scripts/native-release-set-integration.mjs",
88
88
  "test:pypi-wheel": "node scripts/pypi-wheel-integration.mjs",
@@ -123,14 +123,14 @@
123
123
  "test:windows-tls": "node scripts/windows-tls-test.mjs"
124
124
  },
125
125
  "optionalDependencies": {
126
- "@supercov/cli-darwin-arm64": "4.24.5",
127
- "@supercov/cli-darwin-x64": "4.24.5",
128
- "@supercov/cli-linux-arm64-gnu": "4.24.5",
129
- "@supercov/cli-linux-arm64-musl": "4.24.5",
130
- "@supercov/cli-linux-x64-gnu": "4.24.5",
131
- "@supercov/cli-linux-x64-musl": "4.24.5",
132
- "@supercov/cli-win32-arm64": "4.24.5",
133
- "@supercov/cli-win32-x64": "4.24.5"
126
+ "@supercov/cli-darwin-arm64": "4.24.7",
127
+ "@supercov/cli-darwin-x64": "4.24.7",
128
+ "@supercov/cli-linux-arm64-gnu": "4.24.7",
129
+ "@supercov/cli-linux-arm64-musl": "4.24.7",
130
+ "@supercov/cli-linux-x64-gnu": "4.24.7",
131
+ "@supercov/cli-linux-x64-musl": "4.24.7",
132
+ "@supercov/cli-win32-arm64": "4.24.7",
133
+ "@supercov/cli-win32-x64": "4.24.7"
134
134
  },
135
135
  "peerDependencies": {
136
136
  "@playwright/test": ">=1.55.0",
@@ -5,6 +5,10 @@
5
5
  // existing API so both keep native test registration and execution semantics.
6
6
  import * as runtime from "./runtime.mjs";
7
7
  import { callerLocation, runnerExecutionScope, runnerTestId, writeRunnerEvidence } from "./runnerEvidence.mjs";
8
+ import { adaptRunnerEntry } from "./runnerEntry.mjs";
9
+
10
+ // Bun runs no Node preload: a runner it starts is adapted here.
11
+ await adaptRunnerEntry(process.argv[1]?.replaceAll("\\", "/") ?? "");
8
12
 
9
13
  const adapted = new WeakSet();
10
14
  const nativeJest = Bun.jest;
@@ -39,7 +43,9 @@ function adapt(api) {
39
43
  const index = callbackIndex(args);
40
44
  const callback = index < 0 ? undefined : args[index];
41
45
  const name = row.name ?? (typeof args[0] === "string" ? args[0] : callback?.name || "anonymous test");
42
- const location = row.location ?? callerLocation(supercovBunRegistration);
46
+ // Bun gives a Proxy call an `unknown` frame when the target is the
47
+ // boundary. Pass the invoked Proxy itself to omit that frame.
48
+ const location = row.location ?? callerLocation(registration);
43
49
  if (kind === "suite") {
44
50
  if (!callback) return Reflect.apply(original, this, args);
45
51
  const next = [...args];
@@ -116,7 +122,7 @@ function adapt(api) {
116
122
  }
117
123
  return result;
118
124
  }
119
- return new Proxy(supercovBunRegistration, {
125
+ const registration = new Proxy(supercovBunRegistration, {
120
126
  get(target, property) {
121
127
  if (["call", "apply", "bind"].includes(property)) return Reflect.get(target, property);
122
128
 
@@ -166,6 +172,7 @@ function adapt(api) {
166
172
  return member.bind(original);
167
173
  },
168
174
  });
175
+ return registration;
169
176
  }
170
177
 
171
178
  api.test = api.it = wrap(originals.test, "test");
@@ -12,6 +12,7 @@ import path, { basename, dirname, isAbsolute, relative, resolve, sep } from "nod
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { installLaunchSupervisor, wrapImportedCapability } from "./launchSupervisor.mjs";
14
14
  import { __supercovBindCapabilityWrapper } from "./capability.mjs";
15
+ import { adaptRunnerEntry, noteSkippedCoverageThresholds } from "./runnerEntry.mjs";
15
16
  installLaunchSupervisor();
16
17
  // Playwright 1.59 writes each file it compiles into its cache in place and
17
18
  // reads it back unchecked: a process that read one while another wrote it
@@ -167,7 +168,6 @@ syncBuiltinESMExports();
167
168
  // is Vitest, replace its config with our generated merging config before the
168
169
  // CLI parses argv. This is what makes `supercov -- npm test` work
169
170
  // without editing package scripts, Vitest configs, setup files, or test imports.
170
- const generatedVitestConfig = process.env.SUPERCOV_GENERATED_VITEST_CONFIG;
171
171
  const generatedPlaywrightConfig = process.env.SUPERCOV_GENERATED_PLAYWRIGHT_CONFIG;
172
172
  const entrypoint = process.argv[1]?.replaceAll("\\", "/") ?? "";
173
173
  // Node's own test coverage filters files by `--test-coverage-include` and
@@ -789,19 +789,9 @@ function installAuthoredSourceView() {
789
789
  };
790
790
  syncBuiltinESMExports();
791
791
  }
792
- // A project's own coverage tool measures what runs, and under Supercov that is
793
- // the instrumented copy, whose probes are branches no test was written to
794
- // cover: c8, Jest and Vitest each failed a 100% gate on a suite that covers
795
- // every branch (80%, 83.33%, 87.5%). The tool still runs and reports; its
796
- // thresholds are not checked, and the run says so once. Jest's and Vitest's
797
- // thresholds are dropped from the configurations Supercov hands them, which
798
- // call this too.
799
- process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS__ ??= (tool) => {
800
- if (process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS_NOTED__)
801
- return;
802
- process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS_NOTED__ = true;
803
- console.error(`[supercov] ${tool}'s coverage thresholds were not checked: under Supercov, ${tool} measures the instrumented copy, probes included. Supercov's own report has this run's coverage.`);
804
- };
792
+ // Jest's and Vitest's thresholds are dropped from the configurations Supercov
793
+ // hands them, which call this too.
794
+ process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS__ ??= noteSkippedCoverageThresholds;
805
795
  const coverageTool = /\/node_modules\/(?:\.bin\/(c8|nyc)|(c8|nyc)\/bin\/(?:c8|nyc)\.js)$/.exec(entrypoint);
806
796
  if (coverageTool)
807
797
  skipCoverageThresholds(coverageTool[1] ?? coverageTool[2]);
@@ -1004,59 +994,7 @@ register(new URL("./resolve-loader.mjs", import.meta.url), {
1004
994
  if (process.env.SUPERCOV_DEBUG === "1") {
1005
995
  console.error("[supercov] preload", { entrypoint });
1006
996
  }
1007
- if (generatedVitestConfig && /\/vitest(?:\.m?js)?$/.test(entrypoint)) {
1008
- // Worker processes inherit this marker. In particular, do not eagerly load
1009
- // Playwright's expect implementation in a Vitest worker: both runners use
1010
- // the Jest matcher registry symbol and intentionally cannot coexist there.
1011
- process.env.SUPERCOV_INSIDE_VITEST = "1";
1012
- let originalConfig;
1013
- for (let index = 2; index < process.argv.length; index += 1) {
1014
- const argument = process.argv[index];
1015
- if (argument === "--config" || argument === "-c") {
1016
- const configured = process.argv[index + 1];
1017
- const resolvedConfig = configured
1018
- ? resolve(process.cwd(), configured)
1019
- : undefined;
1020
- if (resolvedConfig && resolvedConfig !== resolve(generatedVitestConfig)) {
1021
- originalConfig = resolvedConfig;
1022
- }
1023
- process.argv.splice(index, configured ? 2 : 1);
1024
- index -= 1;
1025
- }
1026
- else if (argument?.startsWith("--config=")) {
1027
- const resolvedConfig = resolve(process.cwd(), argument.slice("--config=".length));
1028
- if (resolvedConfig !== resolve(generatedVitestConfig)) {
1029
- originalConfig = resolvedConfig;
1030
- }
1031
- process.argv.splice(index, 1);
1032
- index -= 1;
1033
- }
1034
- }
1035
- // Thresholds on the command line override the configuration's, which the
1036
- // generated config leaves out (see the coverage tools above). Vitest 0.x
1037
- // took them directly under --coverage.
1038
- for (let index = 2; index < process.argv.length; index += 1) {
1039
- const argument = process.argv[index];
1040
- if (!/^--coverage\.(?:thresholds(?:\.[^=]+)?|lines|functions|branches|statements|perFile|100|thresholdAutoUpdate)(?:=|$)/.test(argument))
1041
- continue;
1042
- const valued = !argument.includes("=") && /^(?:\d+(?:\.\d+)?|true|false)$/.test(process.argv[index + 1] ?? "");
1043
- process.argv.splice(index, valued ? 2 : 1);
1044
- index -= 1;
1045
- process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS__("Vitest");
1046
- }
1047
- if (originalConfig) {
1048
- process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG = originalConfig;
1049
- }
1050
- process.argv.push("--config", generatedVitestConfig);
1051
- if (process.env.SUPERCOV_DEBUG === "1") {
1052
- console.error("[supercov] Vitest argv configured", {
1053
- entrypoint,
1054
- originalConfig,
1055
- generatedVitestConfig,
1056
- argv: process.argv.slice(2),
1057
- });
1058
- }
1059
- }
997
+ await adaptRunnerEntry(entrypoint);
1060
998
  if ((isJestEntrypoint || process.env.JEST_WORKER_ID) && process.env.SUPERCOV_EVIDENCE_DIR) {
1061
999
  // Jest evaluates instrumented modules in its own module system, past the
1062
1000
  // loader hook that imports the runtime on first use elsewhere, and
@@ -1068,32 +1006,6 @@ if ((isJestEntrypoint || process.env.JEST_WORKER_ID) && process.env.SUPERCOV_EVI
1068
1006
  globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
1069
1007
  process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
1070
1008
  }
1071
- // The Mocha CLI, the child it forks for Node options, and each --parallel
1072
- // worker run tests through Mocha's Runner, which the adapter patches before
1073
- // Mocha loads it.
1074
- const isMochaEntrypoint = /\/node_modules\/(?:\.bin\/_?mocha|mocha\/(?:bin\/_?mocha(?:\.js)?|lib\/cli\/cli\.js|lib\/nodejs\/worker\.c?js))$/.test(entrypoint);
1075
- if (isMochaEntrypoint && process.env.SUPERCOV_EVIDENCE_DIR) {
1076
- globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
1077
- process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
1078
- const { adaptMocha } = await import(new URL("./mocha.mjs", import.meta.url).href);
1079
- await adaptMocha(entrypoint);
1080
- }
1081
- // An AVA worker, a thread or a child process, runs one test file through
1082
- // AVA's Runner, which the adapter patches before the worker loads it.
1083
- if (/\/node_modules\/ava\/lib\/worker\/base\.js$/.test(entrypoint) && process.env.SUPERCOV_EVIDENCE_DIR) {
1084
- globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
1085
- process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
1086
- const { adaptAva } = await import(new URL("./ava.mjs", import.meta.url).href);
1087
- await adaptAva(entrypoint);
1088
- }
1089
- // The tap CLI runs each test file as a process of its own, marked with
1090
- // TAP_CHILD_ID; the adapter patches tap's TestBase before the file loads it.
1091
- if (process.env.TAP_CHILD_ID && process.env.SUPERCOV_EVIDENCE_DIR && entrypoint) {
1092
- globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
1093
- process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
1094
- const { adaptTap } = await import(new URL("./tap.mjs", import.meta.url).href);
1095
- await adaptTap(entrypoint);
1096
- }
1097
1009
  if (isJestEntrypoint && process.env.SUPERCOV_EVIDENCE_DIR) {
1098
1010
  // Jest reads one configuration. Ours (jest.config.mjs) reads the user's
1099
1011
  // the way Jest would and adds the adapter and reporter; an explicit
@@ -0,0 +1,108 @@
1
+ // A runner started by its own entry point -- Vitest, Mocha, an AVA worker, a
2
+ // tap child -- is adapted as it starts: Vitest takes the run's generated
3
+ // config, the others' runners are patched before they load. Without it every
4
+ // probe a test reaches is counted as background. Node runs this from
5
+ // register.mjs before the CLI parses argv; Bun loads no Node preload, so its
6
+ // adapter does instead.
7
+ import { resolve } from "node:path";
8
+
9
+ // A project's own coverage tool measures what runs, and under Supercov that is
10
+ // the instrumented copy, whose probes are branches no test was written to
11
+ // cover: c8, Jest and Vitest each failed a 100% gate on a suite that covers
12
+ // every branch (80%, 83.33%, 87.5%). The tool still runs and reports; its
13
+ // thresholds are not checked, and the run says so once.
14
+ export function noteSkippedCoverageThresholds(tool) {
15
+ if (process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS_NOTED__)
16
+ return;
17
+ process.__SUPERCOV_SKIPPED_COVERAGE_THRESHOLDS_NOTED__ = true;
18
+ console.error(`[supercov] ${tool}'s coverage thresholds were not checked: under Supercov, ${tool} measures the instrumented copy, probes included. Supercov's own report has this run's coverage.`);
19
+ }
20
+
21
+ /** Point a Vitest process at the run's generated config. */
22
+ function useGeneratedVitestConfig(entrypoint) {
23
+ const generatedVitestConfig = process.env.SUPERCOV_GENERATED_VITEST_CONFIG;
24
+ if (!generatedVitestConfig || !/\/vitest(?:\.m?js)?$/.test(entrypoint))
25
+ return;
26
+ // Worker processes inherit this marker. In particular, do not eagerly load
27
+ // Playwright's expect implementation in a Vitest worker: both runners use
28
+ // the Jest matcher registry symbol and intentionally cannot coexist there.
29
+ process.env.SUPERCOV_INSIDE_VITEST = "1";
30
+ let originalConfig;
31
+ for (let index = 2; index < process.argv.length; index += 1) {
32
+ const argument = process.argv[index];
33
+ if (argument === "--config" || argument === "-c") {
34
+ const configured = process.argv[index + 1];
35
+ const resolvedConfig = configured
36
+ ? resolve(process.cwd(), configured)
37
+ : undefined;
38
+ if (resolvedConfig && resolvedConfig !== resolve(generatedVitestConfig)) {
39
+ originalConfig = resolvedConfig;
40
+ }
41
+ process.argv.splice(index, configured ? 2 : 1);
42
+ index -= 1;
43
+ }
44
+ else if (argument?.startsWith("--config=")) {
45
+ const resolvedConfig = resolve(process.cwd(), argument.slice("--config=".length));
46
+ if (resolvedConfig !== resolve(generatedVitestConfig)) {
47
+ originalConfig = resolvedConfig;
48
+ }
49
+ process.argv.splice(index, 1);
50
+ index -= 1;
51
+ }
52
+ }
53
+ // Thresholds on the command line override the configuration's, which the
54
+ // generated config leaves out (see the coverage tools above). Vitest 0.x
55
+ // took them directly under --coverage.
56
+ for (let index = 2; index < process.argv.length; index += 1) {
57
+ const argument = process.argv[index];
58
+ if (!/^--coverage\.(?:thresholds(?:\.[^=]+)?|lines|functions|branches|statements|perFile|100|thresholdAutoUpdate)(?:=|$)/.test(argument))
59
+ continue;
60
+ const valued = !argument.includes("=") && /^(?:\d+(?:\.\d+)?|true|false)$/.test(process.argv[index + 1] ?? "");
61
+ process.argv.splice(index, valued ? 2 : 1);
62
+ index -= 1;
63
+ noteSkippedCoverageThresholds("Vitest");
64
+ }
65
+ if (originalConfig) {
66
+ process.env.SUPERCOV_ORIGINAL_VITEST_CONFIG = originalConfig;
67
+ }
68
+ process.argv.push("--config", generatedVitestConfig);
69
+ if (process.env.SUPERCOV_DEBUG === "1") {
70
+ console.error("[supercov] Vitest argv configured", {
71
+ entrypoint,
72
+ originalConfig,
73
+ generatedVitestConfig,
74
+ argv: process.argv.slice(2),
75
+ });
76
+ }
77
+ }
78
+
79
+ /** Adapt the runner whose entry point `entrypoint` is, if any. */
80
+ export async function adaptRunnerEntry(entrypoint) {
81
+ useGeneratedVitestConfig(entrypoint);
82
+ // The Mocha CLI, the child it forks for Node options, and each --parallel
83
+ // worker run tests through Mocha's Runner, which the adapter patches before
84
+ // Mocha loads it.
85
+ const isMochaEntrypoint = /\/node_modules\/(?:\.bin\/_?mocha|mocha\/(?:bin\/_?mocha(?:\.js)?|lib\/cli\/cli\.js|lib\/nodejs\/worker\.c?js))$/.test(entrypoint);
86
+ if (isMochaEntrypoint && process.env.SUPERCOV_EVIDENCE_DIR) {
87
+ globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
88
+ process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
89
+ const { adaptMocha } = await import(new URL("./mocha.mjs", import.meta.url).href);
90
+ await adaptMocha(entrypoint);
91
+ }
92
+ // An AVA worker, a thread or a child process, runs one test file through
93
+ // AVA's Runner, which the adapter patches before the worker loads it.
94
+ if (/\/node_modules\/ava\/lib\/worker\/base\.js$/.test(entrypoint) && process.env.SUPERCOV_EVIDENCE_DIR) {
95
+ globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
96
+ process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
97
+ const { adaptAva } = await import(new URL("./ava.mjs", import.meta.url).href);
98
+ await adaptAva(entrypoint);
99
+ }
100
+ // The tap CLI runs each test file as a process of its own, marked with
101
+ // TAP_CHILD_ID; the adapter patches tap's TestBase before the file loads it.
102
+ if (process.env.TAP_CHILD_ID && process.env.SUPERCOV_EVIDENCE_DIR && entrypoint) {
103
+ globalThis.__SUPERCOV_DIRECT_RUNTIME__ ??= await import("./runtime.mjs");
104
+ process.__SUPERCOV_DIRECT_RUNTIME__ ??= globalThis.__SUPERCOV_DIRECT_RUNTIME__;
105
+ const { adaptTap } = await import(new URL("./tap.mjs", import.meta.url).href);
106
+ await adaptTap(entrypoint);
107
+ }
108
+ }
@@ -68,6 +68,11 @@ export function runnerExecutionScope(identity) {
68
68
  attemptId: `${testKey}-${retry}-${processInstanceToken()}`,
69
69
  };
70
70
  }
71
+ // A frame's location: a path, then `:line:column`. Bun 1.2 writes a module's
72
+ // top-level frame with a line and no column; the column is tried first, as a
73
+ // greedy path would otherwise take `file.ts:2:5` for the file `file.ts:2`.
74
+ const FRAME_WITH_COLUMN = /(?:^at (?:async )?| \()((?:file:\/\/|\/|[A-Za-z]:[\\/]|\\\\).*):(\d+):(\d+)\)?$/;
75
+ const FRAME_WITH_LINE = /(?:^at (?:async )?| \()((?:file:\/\/|\/|[A-Za-z]:[\\/]|\\\\).*):(\d+)\)?$/;
71
76
  export function callerLocation(boundary = callerLocation) {
72
77
  // Omit the registration wrapper by identity. A user file may have the same
73
78
  // basename as a runtime adapter. Parse the location suffix, not individual
@@ -78,13 +83,13 @@ export function callerLocation(boundary = callerLocation) {
78
83
  if (typeof error.stack !== "string")
79
84
  return {};
80
85
  const entry = error.stack.split("\n")[1]?.trim();
81
- const match = entry && /(?:^at (?:async )?| \()((?:file:\/\/|\/|[A-Za-z]:[\\/]|\\\\).*):(\d+):(\d+)\)?$/.exec(entry);
86
+ const match = entry && (FRAME_WITH_COLUMN.exec(entry) ?? FRAME_WITH_LINE.exec(entry));
82
87
  if (!match)
83
88
  return {};
84
89
  return {
85
90
  file: match[1],
86
91
  line: Number(match[2]),
87
- column: Number(match[3]),
92
+ ...(match[3] === undefined ? {} : { column: Number(match[3]) }),
88
93
  };
89
94
  }
90
95
  catch {