supercov 4.23.0 → 4.24.0

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/docs/cli.md CHANGED
@@ -742,15 +742,18 @@ Merge creates a new run. The inputs must describe the same source,
742
742
  configuration, toolchain, schema, and coverage denominator. Supercov rejects an
743
743
  incompatible merge rather than publishing a misleading aggregate.
744
744
 
745
- With no run named, `merge` takes the runs recorded on the source as it is now:
746
- the newest of each command, leaving out runs that are themselves a merge. It
747
- prints the runs it took. This is what a script uses after measuring its
748
- suites, one after another or side by side, without knowing what each run was
749
- called. Asked again with nothing new, it names the merge already made.
745
+ With no run named, `merge` takes the runs recorded on the source as it is now,
746
+ leaving out runs that are themselves a merge. It prints the runs it took. This
747
+ is what a script uses after measuring its suites, one after another or side by
748
+ side, without knowing what each run was called. Asked again with nothing new,
749
+ it names the merge already made.
750
750
 
751
751
  A test that is in two of the runs is one test in the merge. If it failed in
752
- either, it is failed, which is why a command run again replaces its earlier
753
- run and is not merged with it.
752
+ either, it is failed, which is why a command measured again in the same
753
+ project replaces its earlier runs there and is not merged with them. Runs of
754
+ one command measured on different machines and copied into one
755
+ `.supercov/runs` replace none of each other: each is a share of the suite, and
756
+ all are merged.
754
757
 
755
758
  The runs may differ in their tests, the helpers beside them, and the test
756
759
  runners' settings, as long as nothing that changed reaches what an earlier
@@ -710,6 +710,37 @@ copy before its container starts, so an image left over from an earlier run
710
710
  is never used. An image built from an earlier copy, by a command outside the
711
711
  run, is left as it is, and the run says to build it in the test command.
712
712
 
713
+ A container the project is mounted into runs the copy's code whatever its
714
+ image, as `.:/workspace` in a compose file does: Supercov runs the command
715
+ in the copy, so the mount is of the copy. It gets the run's settings with
716
+ each path into the copy moved to where the container sees it, and the
717
+ project's `node_modules`, which the copy's packages link to, mounted
718
+ read-only at its own path. A mount of one directory of the project
719
+ (`./src:/app/src`) is not the copy, and is left alone.
720
+
721
+ The tests can run in such a container too. A command `docker exec` (or
722
+ `docker compose exec`) starts in it, `npx playwright test` say, gets the
723
+ test command's settings, moved the same way, with `NODE_OPTIONS` added to
724
+ the container's own. Playwright then runs under Supercov's preload and
725
+ generated configuration, as it does beside Supercov, and each test is
726
+ credited with what it ran:
727
+
728
+ ```yaml
729
+ services:
730
+ tests:
731
+ image: mcr.microsoft.com/playwright:v1.56.0-noble
732
+ volumes: [".:/workspace"]
733
+ working_dir: /workspace
734
+ command: sleep infinity
735
+ ```
736
+
737
+ ```sh
738
+ npx supercov -- sh -c "docker compose up -d && docker compose exec tests npx playwright test"
739
+ ```
740
+
741
+ When containers were given the run's settings and no test or coverage came
742
+ back from them, the run says so: what ran there is missing, not zero.
743
+
713
744
  - Images pulled from a registry (`image:` with no `build:`, `postgres:16`)
714
745
  run code nobody instrumented, and are left as they are.
715
746
  - testcontainers' reaper, Ryuk, is pointed at the daemon's own socket
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "4.23.0",
3
+ "version": "4.24.0",
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/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-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",
@@ -122,14 +122,14 @@
122
122
  "test:windows-tls": "node scripts/windows-tls-test.mjs"
123
123
  },
124
124
  "optionalDependencies": {
125
- "@supercov/cli-darwin-arm64": "4.23.0",
126
- "@supercov/cli-darwin-x64": "4.23.0",
127
- "@supercov/cli-linux-arm64-gnu": "4.23.0",
128
- "@supercov/cli-linux-arm64-musl": "4.23.0",
129
- "@supercov/cli-linux-x64-gnu": "4.23.0",
130
- "@supercov/cli-linux-x64-musl": "4.23.0",
131
- "@supercov/cli-win32-arm64": "4.23.0",
132
- "@supercov/cli-win32-x64": "4.23.0"
125
+ "@supercov/cli-darwin-arm64": "4.24.0",
126
+ "@supercov/cli-darwin-x64": "4.24.0",
127
+ "@supercov/cli-linux-arm64-gnu": "4.24.0",
128
+ "@supercov/cli-linux-arm64-musl": "4.24.0",
129
+ "@supercov/cli-linux-x64-gnu": "4.24.0",
130
+ "@supercov/cli-linux-x64-musl": "4.24.0",
131
+ "@supercov/cli-win32-arm64": "4.24.0",
132
+ "@supercov/cli-win32-x64": "4.24.0"
133
133
  },
134
134
  "peerDependencies": {
135
135
  "@playwright/test": ">=1.55.0",
@@ -973,7 +973,7 @@ function mergeSnapshots(snapshots) {
973
973
  for (const event of snapshot?.events ?? [])
974
974
  events.set(JSON.stringify(event), event);
975
975
  for (const decision of snapshot?.decisions ?? []) {
976
- const entry = vectorsOf(decisions, decision.meta?.id, { meta: decision.meta });
976
+ const entry = vectorsOf(decisions, decision.id, { id: decision.id });
977
977
  for (const vector of decision.vectors ?? [])
978
978
  entry.seen.set(JSON.stringify(vector), vector);
979
979
  }
@@ -984,7 +984,7 @@ function mergeSnapshots(snapshots) {
984
984
  }
985
985
  }
986
986
  return {
987
- decisions: [...decisions.values()].map(({ seen, meta }) => ({ meta, vectors: [...seen.values()] })),
987
+ decisions: [...decisions.values()].map(({ seen, id }) => ({ id, vectors: [...seen.values()] })),
988
988
  hits: [...hits],
989
989
  events: [...events.values()],
990
990
  logicals: [...logicals.values()].map(({ seen, id }) => ({ id, vectors: [...seen.values()] })),
@@ -185,8 +185,8 @@ function createState() {
185
185
  try {
186
186
  const stored = JSON.parse((_a8 = localStorage.getItem(storageKey)) != null ? _a8 : "{}");
187
187
  for (const snapshot of (_b = stored.decisions) != null ? _b : []) {
188
- state2.decisions.set(snapshot.meta.id, {
189
- meta: snapshot.meta,
188
+ state2.decisions.set(snapshot.id, {
189
+ id: snapshot.id,
190
190
  vectors: new Map(snapshot.vectors.map((vector) => [vectorKey(vector), vector]))
191
191
  });
192
192
  }
@@ -304,7 +304,7 @@ runtimeGlobal.__SUPERCOV_ACTIVATE_PROBE_CONTEXT__ = (browserTestId, browserPhase
304
304
  };
305
305
  function decisionSnapshot() {
306
306
  return [...state.decisions.values()].map((decision) => ({
307
- meta: decision.meta,
307
+ id: decision.id,
308
308
  vectors: [...decision.vectors.values()]
309
309
  }));
310
310
  }
@@ -470,7 +470,7 @@ function attemptKey(scope) {
470
470
  }
471
471
  function serverRecordKey(record) {
472
472
  var _a8;
473
- const suffix = record.type === "decision" ? `${record.meta.id}:${vectorKey(record.vector)}` : record.id;
473
+ const suffix = record.type === "decision" ? `${record.id}:${vectorKey(record.vector)}` : record.id;
474
474
  // Each request that names no test keeps its own first hits: the report
475
475
  // credits them to the test running when it came, and a later request's
476
476
  // work belongs to whichever test was running then.
@@ -982,7 +982,7 @@ function appendServer(record) {
982
982
  if (record.type === "decision") {
983
983
  recordBrowserEvent(__spreadProps(__spreadValues({
984
984
  type: "decision",
985
- id: record.meta.id,
985
+ id: record.id,
986
986
  vector: record.vector,
987
987
  timestampMs
988
988
  }, record.phaseId ? { phaseId: record.phaseId } : {}), {
@@ -1801,11 +1801,14 @@ function recordCoverageHit(id) {
1801
1801
  function registerProbeV2(definition) {
1802
1802
  var _a8, _b;
1803
1803
  const file = {
1804
+ // Each decision's id and its number of conditions: the module carries
1805
+ // no source text, which the manifest holds.
1804
1806
  decisions: definition.decisions,
1807
+ decisionWidths: definition.decisionWidths,
1805
1808
  pointIds: definition.pointIds,
1806
1809
  clock: state.probeV2Clock,
1807
1810
  hitEpochs: new Uint32Array(definition.pointIds.length),
1808
- decisionEpochs: definition.decisions.map((meta) => meta.conditions.length <= 6 ? new Uint32Array(2 * 3 ** meta.conditions.length) : /* @__PURE__ */ new Map()),
1811
+ decisionEpochs: definition.decisionWidths.map((width) => width <= 6 ? new Uint32Array(2 * 3 ** width) : /* @__PURE__ */ new Map()),
1809
1812
  decisionVectorCounts: definition.decisions.map((_, index) => {
1810
1813
  var _a9, _b2;
1811
1814
  return (_b2 = (_a9 = definition.decisionVectorCounts) == null ? void 0 : _a9[index]) != null ? _b2 : 0;
@@ -1898,8 +1901,8 @@ function mcdcEndV2(file, decisionIndex, encoded, value) {
1898
1901
  }
1899
1902
  function recordMcdcEndV2(file, decisionIndex, encoded, value) {
1900
1903
  var _a8, _b;
1901
- const meta = file.decisions[decisionIndex];
1902
- if (!meta || !Number.isSafeInteger(encoded) || encoded < 0)
1904
+ const id = file.decisions[decisionIndex];
1905
+ if (!id || !Number.isSafeInteger(encoded) || encoded < 0)
1903
1906
  return value;
1904
1907
  const outcome = Boolean(value);
1905
1908
  const vectorIndex = encoded * 2 + (outcome ? 1 : 0);
@@ -1934,10 +1937,10 @@ function recordMcdcEndV2(file, decisionIndex, encoded, value) {
1934
1937
  if (observedCount >= expectedCount)
1935
1938
  file.decisionCompleteEpochs[decisionIndex] = epoch;
1936
1939
  }
1937
- if (!state.decisions.has(meta.id))
1938
- state.decisions.set(meta.id, { meta, vectors: /* @__PURE__ */ new Map() });
1939
- const vector = decodeProbeV2Vector(meta.conditions.length, encoded, outcome);
1940
- return vector ? mcdcEnd({ meta, values: vector.values }, value) : value;
1940
+ if (!state.decisions.has(id))
1941
+ state.decisions.set(id, { id, vectors: /* @__PURE__ */ new Map() });
1942
+ const vector = decodeProbeV2Vector(file.decisionWidths[decisionIndex], encoded, outcome);
1943
+ return vector ? mcdcEnd({ id, values: vector.values }, value) : value;
1941
1944
  } finally {
1942
1945
  if (fallbackEpoch)
1943
1946
  file.clock.epoch = previousEpoch;
@@ -2224,13 +2227,13 @@ function loopEntered(frame) {
2224
2227
  function loopEnd(frame) {
2225
2228
  if (!frame.entered) coverageHit(frame.zeroId);
2226
2229
  }
2227
- function mcdcBegin(id, meta) {
2230
+ function mcdcBegin(id, width) {
2228
2231
  if (!state.decisions.has(id)) {
2229
- state.decisions.set(id, { meta, vectors: /* @__PURE__ */ new Map() });
2232
+ state.decisions.set(id, { id, vectors: /* @__PURE__ */ new Map() });
2230
2233
  }
2231
2234
  return {
2232
- meta,
2233
- values: Array.from({ length: meta.conditions.length }, () => null)
2235
+ id,
2236
+ values: Array.from({ length: width }, () => null)
2234
2237
  };
2235
2238
  }
2236
2239
  function mcdcCondition(frame, index, value) {
@@ -2238,7 +2241,7 @@ function mcdcCondition(frame, index, value) {
2238
2241
  return value;
2239
2242
  }
2240
2243
  function mcdcEnd(frame, value) {
2241
- const decision = state.decisions.get(frame.meta.id);
2244
+ const decision = state.decisions.get(frame.id);
2242
2245
  if (!decision)
2243
2246
  return value;
2244
2247
  const vector = { values: frame.values, outcome: Boolean(value) };
@@ -2249,7 +2252,7 @@ function mcdcEnd(frame, value) {
2249
2252
  if (isBrowser) {
2250
2253
  if (recordBrowserEvent(__spreadProps(__spreadValues({
2251
2254
  type: "decision",
2252
- id: decision.meta.id,
2255
+ id: decision.id,
2253
2256
  vector,
2254
2257
  timestampMs
2255
2258
  }, phaseId ? { phaseId } : {}), {
@@ -2259,7 +2262,7 @@ function mcdcEnd(frame, value) {
2259
2262
  } else {
2260
2263
  appendServer(__spreadValues({
2261
2264
  type: "decision",
2262
- meta: decision.meta,
2265
+ id: decision.id,
2263
2266
  vector,
2264
2267
  timestampMs
2265
2268
  }, phaseId ? { phaseId } : {}));