supercov 0.0.43 → 0.0.45

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.
Files changed (42) hide show
  1. package/README.md +23 -14
  2. package/docs/agent-loop.md +128 -39
  3. package/docs/assertion-agent.md +156 -0
  4. package/docs/assertion-evidence.md +9 -135
  5. package/docs/assertion-maps.md +252 -0
  6. package/docs/assertions.md +82 -0
  7. package/docs/cli.md +38 -23
  8. package/docs/coverage-model.md +18 -6
  9. package/docs/evidence.md +6 -6
  10. package/docs/getting-started.md +60 -72
  11. package/docs/performance.md +4 -4
  12. package/docs/troubleshooting.md +8 -8
  13. package/docs/verification.md +2 -2
  14. package/docs/workspace-isolation.md +1 -1
  15. package/package.json +34 -33
  16. package/runtime/javascript/nodeAssertAdapter.mjs +32 -8
  17. package/runtime/javascript/nodeTest.mjs +13 -5
  18. package/runtime/javascript/runnerEvidence.mjs +33 -11
  19. package/runtime/javascript/runtime.mjs +27 -23
  20. package/schemas/assertions.schema.json +276 -0
  21. package/analyzers/typescript/README.md +0 -55
  22. package/analyzers/typescript/bin/compiler-identity.mjs +0 -78
  23. package/analyzers/typescript/bin/identity.mjs +0 -67
  24. package/analyzers/typescript/bin/query.mjs +0 -29
  25. package/analyzers/typescript/dist/analyze.js +0 -3972
  26. package/analyzers/typescript/dist/archive.js +0 -309
  27. package/analyzers/typescript/dist/build-identity.json +0 -1
  28. package/analyzers/typescript/dist/compiler.js +0 -32
  29. package/analyzers/typescript/dist/frontend.js +0 -75
  30. package/analyzers/typescript/dist/native-frontend.js +0 -271
  31. package/analyzers/typescript/dist/pragmas.js +0 -143
  32. package/analyzers/typescript/dist/types.js +0 -1
  33. package/analyzers/typescript/package.json +0 -27
  34. package/analyzers/typescript/src/analyze.ts +0 -4538
  35. package/analyzers/typescript/src/archive.ts +0 -438
  36. package/analyzers/typescript/src/compiler.ts +0 -49
  37. package/analyzers/typescript/src/frontend.ts +0 -136
  38. package/analyzers/typescript/src/native-frontend.ts +0 -315
  39. package/analyzers/typescript/src/pragmas.ts +0 -218
  40. package/analyzers/typescript/src/types.ts +0 -45
  41. package/analyzers/typescript/tsconfig.json +0 -12
  42. package/docs/code-verification.md +0 -182
@@ -1,68 +1,50 @@
1
1
  # Getting started
2
2
 
3
- Supercov turns the test suite you already have into a list of useful tests to
4
- write next. Run the suite once, inspect a coverage gap, add a focused test, and
5
- compare the result.
3
+ Open your project in a coding agent that can run terminal commands, then paste
4
+ this prompt:
6
5
 
7
- ```sh
8
- npx supercov -- npm test
6
+ ```text supercov-prompt
7
+ Measure code coverage with npx supercov and write one missing test.
8
+ Only change tests. Rerun the full test suite and show me the test you
9
+ added and the before-and-after coverage.
9
10
  ```
10
11
 
11
- No account, config file, import, custom reporter, or hosted service is required.
12
- Supercov supports JavaScript, TypeScript, Rust, Python, and Ruby today.
12
+ Your agent can install Supercov if needed. It runs the commands and edits the
13
+ tests; you don't need to do those steps yourself.
13
14
 
14
- ## Before you start
15
+ When it finishes, review the test change and coverage comparison in your
16
+ conversation. Ask separately if you want a commit or pull request.
15
17
 
16
- You need:
18
+ No account, config file, import, custom reporter, or hosted service is required.
19
+ Supercov supports JavaScript, TypeScript, Rust, Python, and Ruby today.
17
20
 
18
- - macOS (arm64 or x64), Linux (arm64 or x64, glibc 2.28 or newer or musl), or
19
- Windows (arm64 or x64);
20
- - Node.js 22 or newer;
21
- - a test command that already works in the repository; and
22
- - for Rust, the Rust 1.95 toolchain;
23
- - for Python, CPython 3.12 or newer with pytest or unittest;
24
- - for Ruby, Ruby 3.4 or newer with RSpec, Minitest, test-unit or Cucumber (3.3 measures lines, methods and simple branches only).
21
+ ## What the agent does
25
22
 
26
- The CLI is a native binary. `npx supercov` picks the build for your operating
27
- system and architecture, and nothing is compiled on install; the same binary is
28
- on PyPI as `supercov-cli` (`uvx --from supercov-cli supercov`) and on RubyGems
29
- as `supercov` (`gem install supercov`), at the same version, and the source is
30
- on crates.io (`cargo install supercov`). The first invocation may download
31
- Supercov from the registry. Supercov itself does not upload your source or
32
- coverage evidence to a Supercov service.
23
+ The commands below show how the agent measures coverage and checks its work.
24
+ You can also run them yourself if you prefer using the terminal.
33
25
 
34
- ## 1. Run your real test command
26
+ ### 1. Run your real test command
35
27
 
36
- Everything after `--` is the command Supercov measures. Start with the same
37
- complete command you trust before merging or deploying:
28
+ The agent runs your repository's complete test suite through Supercov:
38
29
 
39
- ```sh
40
- # JavaScript or TypeScript
30
+ ```sh supercov-example
41
31
  npx supercov -- npm test
42
- npx supercov -- npx playwright test
43
- npx supercov -- pnpm test:e2e
44
-
45
- # Rust
46
- npx supercov -- cargo test
47
- npx supercov -- cargo nextest run
48
-
49
- # Python
50
- npx supercov -- pytest
51
-
52
- # Ruby
53
- npx supercov -- rspec
54
32
  ```
55
33
 
34
+ Everything after `--` is the command Supercov measures. The agent should use
35
+ the same complete command your project uses before merging or deploying.
36
+ If the project has several suites, tell it which one to use.
37
+
56
38
  Supercov runs that command in an isolated, instrumented copy of the project.
57
39
  The command keeps its normal arguments, environment, output, and exit status.
58
40
  If one command launches several supported runners, their evidence lands in one
59
41
  run.
60
42
 
61
- ## 2. Read the first result
43
+ ### 2. Read the first result
62
44
 
63
- Open the newest run:
45
+ The agent reads the newest run:
64
46
 
65
- ```sh
47
+ ```sh supercov-example
66
48
  npx supercov runs latest
67
49
  ```
68
50
 
@@ -76,18 +58,19 @@ An uncovered gap is a candidate for a test. A measurement limit is different:
76
58
  it means Supercov cannot honestly account for that code yet. Do not try to test
77
59
  away a measurement limit.
78
60
 
79
- ## 3. Choose one useful gap
61
+ ### 3. Choose one useful gap
80
62
 
81
- Ask for a short list, then inspect one file:
63
+ The agent asks for a short list of gaps, then inspects one file. It uses your
64
+ project's file paths in place of the examples:
82
65
 
83
- ```sh
66
+ ```sh supercov-example
84
67
  npx supercov runs latest gaps --limit 10
85
68
  npx supercov runs latest file app/checkout/session.ts
86
69
  ```
87
70
 
88
- Use the more specific queries when you need them:
71
+ It can use more specific queries to understand the gap:
89
72
 
90
- ```sh
73
+ ```sh supercov-example
91
74
  npx supercov runs latest decision app/checkout/session.ts:64
92
75
  npx supercov runs latest line app/checkout/session.ts:64
93
76
  ```
@@ -96,40 +79,45 @@ npx supercov runs latest line app/checkout/session.ts:64
96
79
  outcomes and MC/DC witnesses. `line` shows the obligations and tests associated
97
80
  with one source line.
98
81
 
99
- ## 4. Add a test and prove the gain
82
+ ### 4. Add a test and prove the gain
100
83
 
101
- Write one focused test with a meaningful assertion. Then rerun the same complete
102
- command and compare the two runs:
84
+ The agent writes one focused test with a meaningful assertion. It then reruns
85
+ the same complete command and compares the two runs:
103
86
 
104
- ```sh
87
+ ```sh supercov-example
105
88
  npx supercov -- npm test
106
89
  npx supercov diff <previous-run-id> latest
107
90
  ```
108
91
 
109
- For Rust, rerun the same `cargo test` or `cargo nextest run` command used for the
110
- baseline. A useful change leaves the suite passing and shows the expected gain
111
- without an unexplained loss elsewhere.
92
+ A useful change leaves the suite passing and shows the expected gain without
93
+ an unexplained loss elsewhere. Review what the new test actually checks, not
94
+ just the percentage.
112
95
 
113
- ## Give the loop to a coding agent
96
+ For a recorded example and prompts for longer runs, see
97
+ [Agent workflow](agent-loop.md).
114
98
 
115
- Paste this into any coding agent that can run terminal commands:
99
+ ## Environment requirements
116
100
 
117
- ```text
118
- Use `npx supercov` to improve coverage. Only write tests. Keep going while
119
- useful gaps remain.
101
+ These requirements apply wherever the agent runs commands: your machine,
102
+ a container, or a remote workspace. The agent can check them and tell you
103
+ if anything is missing.
120
104
 
121
- Run the repository's complete test command through Supercov. Use
122
- `npx supercov runs latest gaps --limit 5` to choose one useful target. Inspect
123
- the target, write one focused test with meaningful assertions, rerun the same
124
- complete suite, and verify the gain with
125
- `npx supercov diff <previous-run-id> latest`.
126
-
127
- Never weaken assertions or change application code to make coverage easier.
128
- Stop when no useful gap remains or Supercov reports a measurement limit instead
129
- of an ordinary gap.
130
- ```
105
+ - macOS (arm64 or x64), Linux (arm64 or x64, glibc 2.28 or newer or musl), or
106
+ Windows (arm64 or x64);
107
+ - Node.js 22 or newer when using the npm package;
108
+ - a working test command, including its dependencies, environment variables,
109
+ and any local services;
110
+ - for Rust, the Rust 1.95 toolchain;
111
+ - for Python, CPython 3.12 or newer with pytest or unittest;
112
+ - for Ruby, Ruby 3.4 or newer with RSpec, Minitest, test-unit or Cucumber (3.3 measures lines, methods and simple branches only).
131
113
 
132
- Replace `npm test` with the repository's real complete test command when needed.
114
+ The CLI is a native binary. `npx supercov` picks the build for your operating
115
+ system and architecture, and nothing is compiled on install; the same binary is
116
+ on PyPI as `supercov-cli` (`uvx --from supercov-cli supercov`) and on RubyGems
117
+ as `supercov` (`gem install supercov`), at the same version, and the source is
118
+ on crates.io (`cargo install supercov`). The first invocation may download
119
+ Supercov from the registry. Supercov itself does not upload your source or
120
+ coverage evidence to a Supercov service.
133
121
 
134
122
  ## Files and cleanup
135
123
 
@@ -138,7 +126,7 @@ workspace for instrumented builds. These files are local and ignored by Git.
138
126
  Supercov does not rewrite your source, tests, imports, runner configuration,
139
127
  dependencies, or ordinary build output.
140
128
 
141
- ```sh
129
+ ```sh supercov
142
130
  npx supercov clean --dry-run # preview what would be removed
143
131
  npx supercov clean --keep 20 # keep the 20 newest runs
144
132
  npx supercov clean # remove all runs and the build cache
@@ -6,7 +6,7 @@ the isolated build when the relevant inputs have not changed.
6
6
 
7
7
  ## See where the time went
8
8
 
9
- ```sh
9
+ ```sh supercov
10
10
  npx supercov runs latest
11
11
  ```
12
12
 
@@ -42,7 +42,7 @@ possible mismatch triggers a fresh build rather than risking stale coverage.
42
42
 
43
43
  A narrow test command can shorten the inner loop:
44
44
 
45
- ```sh
45
+ ```sh supercov
46
46
  npx supercov -- npx vitest run app/checkout/session.test.ts
47
47
  ```
48
48
 
@@ -54,7 +54,7 @@ baseline.
54
54
 
55
55
  Compare the original and wrapped command under similar cache conditions:
56
56
 
57
- ```sh
57
+ ```sh supercov
58
58
  /usr/bin/time -p npm test
59
59
  /usr/bin/time -p npx supercov -- npm test
60
60
  ```
@@ -73,7 +73,7 @@ than stored as a full report for every filter.
73
73
  The isolated workspace may be larger because it can contain an instrumented
74
74
  build cache. Supercov does not delete history in the background.
75
75
 
76
- ```sh
76
+ ```sh supercov
77
77
  npx supercov clean --dry-run
78
78
  npx supercov clean --keep 20
79
79
  npx supercov clean
@@ -3,7 +3,7 @@
3
3
  Start with the run summary. It usually tells you whether the problem is the test
4
4
  command, source discovery, runner attribution, or a measurement boundary.
5
5
 
6
- ```sh
6
+ ```sh supercov
7
7
  npx supercov runs latest
8
8
  npx supercov runs latest scope
9
9
  npx supercov runs latest runners
@@ -28,13 +28,13 @@ would for another npm package.
28
28
  Run the same command from the repository root. If first-party code lives in an
29
29
  unusual directory, declare the source roots explicitly:
30
30
 
31
- ```sh
31
+ ```sh supercov
32
32
  SUPERCOV_SOURCE_ROOTS=src,app npx supercov -- npm test
33
33
  ```
34
34
 
35
35
  Then inspect what Supercov included and excluded:
36
36
 
37
- ```sh
37
+ ```sh supercov
38
38
  npx supercov runs latest scope
39
39
  ```
40
40
 
@@ -45,7 +45,7 @@ warning. The goal is an honest boundary around code the repository owns.
45
45
 
46
46
  First check runner and source scope:
47
47
 
48
- ```sh
48
+ ```sh supercov
49
49
  npx supercov runs latest runners
50
50
  npx supercov runs latest scope
51
51
  npx supercov runs latest gaps
@@ -67,7 +67,7 @@ file that cannot be compiled with them is measured through Ruby's `Coverage`
67
67
  module alone rather than failing the run. To put a file on that path
68
68
  deliberately, name a fragment of its path:
69
69
 
70
- ```sh
70
+ ```sh supercov
71
71
  SUPERCOV_RUBY_SKIP_PROBES=app/models/order.rb npx supercov -- bundle exec rspec
72
72
  ```
73
73
 
@@ -82,7 +82,7 @@ workspace after relevant source, tests, dependencies, configuration, or
82
82
  toolchain inputs change. Rerun the same complete command to create a current
83
83
  baseline:
84
84
 
85
- ```sh
85
+ ```sh supercov
86
86
  npx supercov -- npm test
87
87
  ```
88
88
 
@@ -133,7 +133,7 @@ mode still match.
133
133
 
134
134
  Inspect the recorded phases with:
135
135
 
136
- ```sh
136
+ ```sh supercov
137
137
  npx supercov runs latest
138
138
  ```
139
139
 
@@ -143,7 +143,7 @@ See [Speed and storage](performance.md) for practical ways to shorten a loop.
143
143
 
144
144
  Preview cleanup, then choose how much history to keep:
145
145
 
146
- ```sh
146
+ ```sh supercov
147
147
  npx supercov clean --dry-run
148
148
  npx supercov clean --keep 20
149
149
  npx supercov clean
@@ -16,7 +16,7 @@ Before merging, check three things:
16
16
  3. **The test protects behavior.** It should make a meaningful assertion without
17
17
  weakening existing assertions or changing application code for the metric.
18
18
 
19
- ```sh
19
+ ```sh supercov
20
20
  npx supercov diff <baseline-run> <new-run>
21
21
  npx supercov runs <new-run> test "new test name"
22
22
  ```
@@ -34,7 +34,7 @@ Supercov separates four states:
34
34
  - **stale** — the run is valid history but no longer describes the current
35
35
  workspace.
36
36
 
37
- ```sh
37
+ ```sh supercov
38
38
  npx supercov runs latest
39
39
  npx supercov runs latest scope
40
40
  npx supercov runs latest gaps
@@ -74,7 +74,7 @@ staging state. Completed runs remain immutable.
74
74
 
75
75
  Preview cleanup before removing anything:
76
76
 
77
- ```sh
77
+ ```sh supercov
78
78
  npx supercov clean --dry-run
79
79
  npx supercov clean --keep 20
80
80
  npx supercov clean
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.43",
4
- "description": "Coverage for coding agents and software factories 🌙",
3
+ "version": "0.0.45",
4
+ "description": "Coverage for coding agents and software factories \ud83c\udf19",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -15,20 +15,21 @@
15
15
  "files": [
16
16
  "bin",
17
17
  "runtime/javascript",
18
- "docs",
19
- "analyzers/typescript/bin",
20
- "analyzers/typescript/dist/analyze.js",
21
- "analyzers/typescript/dist/archive.js",
22
- "analyzers/typescript/dist/compiler.js",
23
- "analyzers/typescript/dist/frontend.js",
24
- "analyzers/typescript/dist/native-frontend.js",
25
- "analyzers/typescript/dist/pragmas.js",
26
- "analyzers/typescript/dist/types.js",
27
- "analyzers/typescript/dist/build-identity.json",
28
- "analyzers/typescript/src",
29
- "analyzers/typescript/package.json",
30
- "analyzers/typescript/tsconfig.json",
31
- "analyzers/typescript/README.md",
18
+ "docs/getting-started.md",
19
+ "docs/agent-loop.md",
20
+ "docs/assertions.md",
21
+ "docs/assertion-evidence.md",
22
+ "docs/assertion-maps.md",
23
+ "docs/assertion-agent.md",
24
+ "docs/troubleshooting.md",
25
+ "docs/cli.md",
26
+ "docs/coverage-model.md",
27
+ "docs/evidence.md",
28
+ "docs/supported-suites.md",
29
+ "docs/verification.md",
30
+ "docs/performance.md",
31
+ "docs/workspace-isolation.md",
32
+ "schemas",
32
33
  "README.md"
33
34
  ],
34
35
  "engines": {
@@ -47,7 +48,6 @@
47
48
  },
48
49
  "scripts": {
49
50
  "build": "cargo build -p supercov",
50
- "build:asserted-typescript": "npm --prefix analyzers/typescript run build",
51
51
  "sync:rust-assets": "node scripts/sync-rust-package-assets.mjs",
52
52
  "test:rust-assets": "node scripts/sync-rust-package-assets.mjs --check",
53
53
  "check:rustc-backend-spike": "(cd spikes/rustc-backend && RUSTC_BOOTSTRAP=1 RUSTUP_TOOLCHAIN=1.95.0 cargo fmt --check && RUSTC_BOOTSTRAP=1 RUSTUP_TOOLCHAIN=1.95.0 cargo clippy --all-targets -- -D warnings)",
@@ -55,11 +55,8 @@
55
55
  "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",
56
56
  "test": "cargo test --workspace",
57
57
  "test:runtime": "node --test tests/runtime/*.test.mjs",
58
- "test:asserted-typescript": "npm --prefix analyzers/typescript test",
59
- "test:asserted-integration": "cargo build -p supercov && npm --prefix analyzers/typescript run build && SUPERCOV_ASSERTED_INTEGRATION=1 node --test analyzers/typescript/tests/archive.integration.test.mjs",
60
- "test:asserted-package": "cargo build -p supercov && npm run build:asserted-typescript && node scripts/asserted-packed-integration.mjs",
61
58
  "test:fixture": "cargo build -p supercov && node scripts/rust-fixture-matrix.mjs",
62
- "test:packed-npx": "cargo build --release -p supercov && npm run build:asserted-typescript && node scripts/packed-npx-integration.mjs",
59
+ "test:packed-npx": "cargo build --release -p supercov && node scripts/packed-npx-integration.mjs",
63
60
  "test:isolation": "cargo build -p supercov && node scripts/isolation-integration.mjs",
64
61
  "test:filesystem": "cargo test --workspace && cargo build -p supercov && node scripts/workspace-crash-integration.mjs",
65
62
  "test:watchdog": "cargo build -p supercov && node scripts/watchdog-integration.mjs",
@@ -67,7 +64,7 @@
67
64
  "test:agent": "cargo build -p supercov && node scripts/agent-query-eval.mjs",
68
65
  "test:engine": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace && cargo build -p supercov && npm run test:runtime && npm run test:rust-assets && 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/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-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",
69
66
  "test:platform": "cargo test --workspace && cargo build -p supercov && node scripts/rust-process-supervision.mjs && node scripts/workspace-crash-integration.mjs",
70
- "test:native-package": "cargo build --release -p supercov && npm run build:asserted-typescript && node scripts/native-package-integration.mjs && node scripts/native-release-set-integration.mjs",
67
+ "test:native-package": "cargo build --release -p supercov && node scripts/native-package-integration.mjs && node scripts/native-release-set-integration.mjs",
71
68
  "test:pypi-wheel": "node scripts/pypi-wheel-integration.mjs",
72
69
  "test:pypi-registry": "node scripts/pypi-registry-integration.mjs",
73
70
  "build:rubygem": "cargo build --release -p supercov && node scripts/build-rubygem.mjs",
@@ -78,28 +75,31 @@
78
75
  "test:test262": "cargo build --release -p supercov && node scripts/test262-equivalence.mjs",
79
76
  "benchmark:check": "cargo build --release -p supercov && node scripts/rust-transform-benchmark.mjs",
80
77
  "benchmark:python-monitoring": "cargo build -p supercov && node scripts/python-monitoring-benchmark.mjs",
81
- "check": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && npm run test && npm run test:runtime && npm run test:rust-assets && npm run test:asserted-typescript && npm run test:asserted-integration && npm run test:asserted-package && node scripts/package-preflight.mjs && node scripts/verify-binstall-metadata.mjs --offline",
78
+ "check": "cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings && npm run test && npm run test:runtime && npm run test:rust-assets && npm run test:assertion-maps && node scripts/package-preflight.mjs && node scripts/verify-binstall-metadata.mjs --offline",
82
79
  "release:check": "cargo clean && node scripts/sweep-target.mjs && 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:rust-public-cargo && npm run test:packed-npx && npm run test:clang-mcdc && npm run benchmark:check",
83
80
  "release:bump": "node scripts/bump-version.mjs",
84
81
  "sweep": "node scripts/sweep-target.mjs",
85
82
  "oracle:rust": "cargo build --release -p supercov && node scripts/rust-coverage-oracle.mjs",
86
- "prepack": "npm run build:asserted-typescript && node scripts/package-preflight.mjs",
83
+ "prepack": "node scripts/package-preflight.mjs",
87
84
  "prepublishOnly": "npm run release:check",
88
85
  "test:child-attribution": "cargo build -p supercov && node scripts/rust-child-attribution-integration.mjs",
89
86
  "test:host-loader": "cargo build -p supercov && node scripts/rust-host-loader-integration.mjs",
90
87
  "test:python-monitoring": "cargo build -p supercov && node scripts/python-monitoring-integration.mjs",
91
88
  "test:ruby-coverage": "cargo build -p supercov && node scripts/ruby-coverage-integration.mjs",
92
- "test:rust-public-cargo": "cargo build -p supercov && node scripts/rust-public-cargo-integration.mjs"
89
+ "test:rust-public-cargo": "cargo build -p supercov && node scripts/rust-public-cargo-integration.mjs",
90
+ "test:assertion-maps": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-integration.mjs && node scripts/assertion-map-js-integration.mjs",
91
+ "test:assertion-maps:js": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-js-integration.mjs",
92
+ "sync:assertion-schema": "cargo build -p supercov && node scripts/assertion-map-schema.mjs"
93
93
  },
94
94
  "optionalDependencies": {
95
- "@supercov/cli-darwin-arm64": "0.0.43",
96
- "@supercov/cli-darwin-x64": "0.0.43",
97
- "@supercov/cli-linux-arm64-gnu": "0.0.43",
98
- "@supercov/cli-linux-arm64-musl": "0.0.43",
99
- "@supercov/cli-linux-x64-gnu": "0.0.43",
100
- "@supercov/cli-linux-x64-musl": "0.0.43",
101
- "@supercov/cli-win32-arm64": "0.0.43",
102
- "@supercov/cli-win32-x64": "0.0.43"
95
+ "@supercov/cli-darwin-arm64": "0.0.45",
96
+ "@supercov/cli-darwin-x64": "0.0.45",
97
+ "@supercov/cli-linux-arm64-gnu": "0.0.45",
98
+ "@supercov/cli-linux-arm64-musl": "0.0.45",
99
+ "@supercov/cli-linux-x64-gnu": "0.0.45",
100
+ "@supercov/cli-linux-x64-musl": "0.0.45",
101
+ "@supercov/cli-win32-arm64": "0.0.45",
102
+ "@supercov/cli-win32-x64": "0.0.45"
103
103
  },
104
104
  "peerDependencies": {
105
105
  "@playwright/test": ">=1.55.0",
@@ -123,6 +123,7 @@
123
123
  "@types/node": "^24.0.0",
124
124
  "esbuild": "^0.28.2",
125
125
  "expect": "30.4.1",
126
+ "jest": "30.5.1",
126
127
  "next": "^16.3.2",
127
128
  "react": "^19.2.4",
128
129
  "react-dom": "^19.2.4",
@@ -1,4 +1,6 @@
1
1
  import { withNodeAssertionPhase } from "./runtime.mjs";
2
+ import { relative, sep } from "node:path";
3
+ import { fileURLToPath } from "node:url";
2
4
  const ASSERTION_METHODS = new Set([
3
5
  "deepEqual",
4
6
  "deepStrictEqual",
@@ -19,18 +21,40 @@ const ASSERTION_METHODS = new Set([
19
21
  "strictEqual",
20
22
  "throws",
21
23
  ]);
22
- function assertionSource() {
23
- const lines = new Error().stack?.split("\n").slice(2) ?? [];
24
- const entry = lines.find((line) => !/(?:nodeAssert|nodeAssertion|runtime)\.[cm]?[jt]s/.test(line) &&
25
- !line.includes("node:internal"));
26
- if (!entry)
24
+ function assertionSource(boundary) {
25
+ // Omit our own frames by function identity, not basename: a user's helper
26
+ // may itself be called runtime.mjs or nodeAssertAdapter.mjs. Never search
27
+ // deeper for a plausible-looking caller when the immediate frame is opaque.
28
+ // A loader may have transformed this file without composing source maps.
29
+ // Qualify stack coordinates so they cannot masquerade as the original
30
+ // source positions emitted by lexical probes and used for oracle matching.
31
+ try {
32
+ const error = {};
33
+ Error.captureStackTrace(error, boundary);
34
+ if (typeof error.stack !== "string")
35
+ return undefined;
36
+ const entry = error.stack.split("\n")[1]?.trim();
37
+ const match = entry && /(?:^at (?:async )?| \()((?:file:\/\/\/|\/|[A-Za-z]:[\\/]).*):(\d+):(\d+)\)?$/.exec(entry);
38
+ if (!match)
39
+ return undefined;
40
+ const file = match[1].startsWith("file:") ? fileURLToPath(match[1]) : match[1];
41
+ const roots = [process.env.SUPERCOV_PROJECT_ROOT, process.env.SUPERCOV_SOURCE_PROJECT_ROOT, process.cwd()].filter(Boolean);
42
+ for (const root of roots) {
43
+ const path = relative(root, file);
44
+ if (path !== ".." && !path.startsWith(`..${sep}`) && !/^(?:[A-Za-z]:|[\\/])/.test(path))
45
+ return `runtime-stack:${path.split(sep).join("/")}:${match[2]}:${match[3]}`;
46
+ }
47
+ return `runtime-stack:${file.split(sep).join("/")}:${match[2]}:${match[3]}`;
48
+ }
49
+ catch {
50
+ // Custom stack formatters must not change the user's assertion outcome.
27
51
  return undefined;
28
- return entry.trim().replace(/^at\s+/, "");
52
+ }
29
53
  }
30
54
  function wrapAssertion(original, operation) {
31
55
  return new Proxy(original, {
32
- apply(target, thisArgument, argumentsList) {
33
- return withNodeAssertionPhase(operation, assertionSource(), () => Reflect.apply(target, thisArgument, argumentsList));
56
+ apply: function assertionApply(target, thisArgument, argumentsList) {
57
+ return withNodeAssertionPhase(operation, () => assertionSource(assertionApply), () => Reflect.apply(target, thisArgument, argumentsList));
34
58
  },
35
59
  });
36
60
  }
@@ -2,7 +2,7 @@ import * as native from "node:test";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import vm from "node:vm";
4
4
  import { beginBufferedServerEvidence, flushBufferedServerEvidence, takeNodeAssertionPhases, withCoverageCarrier, } from "./runtime.mjs";
5
- import { callerLocation, runnerExecutionScope, writeRunnerEvidence, } from "./runnerEvidence.mjs";
5
+ import { callerLocation, runnerExecutionScope, runnerTestId, writeRunnerEvidence, } from "./runnerEvidence.mjs";
6
6
  function callbackIndex(args) {
7
7
  for (let index = args.length - 1; index >= 0; index -= 1)
8
8
  if (typeof args[index] === "function")
@@ -86,16 +86,18 @@ function restoreUserError(error, depth = 0) {
86
86
  }
87
87
  return error;
88
88
  }
89
- function wrappedRegistration(original) {
89
+ const registrationCounts = new Map();
90
+ function wrappedRegistration(original, parentTestId) {
90
91
  const wrapped = function supercovNodeTest(...args) {
91
92
  const index = callbackIndex(args);
92
93
  if (index < 0)
93
94
  return Reflect.apply(original, this, args);
94
95
  const callback = args[index];
95
- const location = callerLocation(/(?:nodeTest|runnerEvidence)\.[cm]?[jt]s/);
96
+ const location = callerLocation(supercovNodeTest);
96
97
  const identity = {
97
98
  runner: "node:test",
98
99
  name: testName(args, callback),
100
+ ...(parentTestId ? { parentTestId } : {}),
99
101
  ...location,
100
102
  // Source-map producers disagree about whether a call expression maps to
101
103
  // its first token or the first token on its source line. The line and
@@ -104,6 +106,12 @@ function wrappedRegistration(original) {
104
106
  // keeps one identity when esbuild, Babel, SWC, or TypeScript rewrites it.
105
107
  ...(location.line === undefined ? {} : { column: 1 }),
106
108
  };
109
+ // Allocate at registration, never callback completion: concurrent tests
110
+ // can finish in any order. Count each source/name within its parent.
111
+ const registrationKey = runnerTestId(identity);
112
+ const registrationOrdinal = registrationCounts.get(registrationKey) ?? 0;
113
+ registrationCounts.set(registrationKey, registrationOrdinal + 1);
114
+ identity.registrationOrdinal = registrationOrdinal;
107
115
  const scope = runnerExecutionScope(identity);
108
116
  const options = testOptions(args, index);
109
117
  const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"];
@@ -129,7 +137,7 @@ function wrappedRegistration(original) {
129
137
  };
130
138
  }
131
139
  if (property === "test" && typeof value === "function")
132
- return wrappedRegistration(value.bind(target));
140
+ return wrappedRegistration(value.bind(target), scope.testId);
133
141
  return typeof value === "function" ? value.bind(target) : value;
134
142
  },
135
143
  });
@@ -192,7 +200,7 @@ function wrappedRegistration(original) {
192
200
  Object.defineProperty(wrapped, property, {
193
201
  configurable: true,
194
202
  enumerable: true,
195
- value: wrappedRegistration(member),
203
+ value: wrappedRegistration(member, parentTestId),
196
204
  });
197
205
  }
198
206
  return wrapped;