supercov 4.7.0 → 4.8.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.
@@ -96,7 +96,16 @@ Both are useful:
96
96
 
97
97
  Code that runs while no test is running counts in the run's total and in no
98
98
  test's: a server starting before the first test, modules loading, work between
99
- tests. When a run has any, the summary's `By test kind` table ends with a
99
+ tests.
100
+
101
+ A request to your app server that names no test -- a webhook from a mock
102
+ Stripe server Supercov does not measure -- is credited to the test running when
103
+ the server handled it, if that test was the only one running then. A mock that
104
+ runs under Supercov passes the test's identity on in its own requests, and its
105
+ webhook is credited exactly. The run's
106
+ `Attribution` line counts those tests as `with server work credited by time`,
107
+ and each one's `runs <id> test` page says so. Work done while tests ran at
108
+ once, or while none ran, stays no test's. When a run has any, the summary's `By test kind` table ends with a
100
109
  `no test` row for what only that covered, which is why a run with one kind of
101
110
  test can show a total above that kind's own:
102
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "4.7.0",
3
+ "version": "4.8.0",
4
4
  "description": "Coverage, security and code quality for coding agents",
5
5
  "keywords": [
6
6
  "coverage",
@@ -83,7 +83,7 @@
83
83
  "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",
84
84
  "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",
85
85
  "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",
86
- "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/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/rust-classic-browser-integration.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",
86
+ "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/rust-direct-playwright-integration.mjs && node scripts/rust-custom-browser-playwright-integration.mjs && node scripts/cross-origin-requests.mjs && node scripts/background-by-time.mjs && node scripts/rust-classic-browser-integration.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",
87
87
  "test:platform": "cargo test --workspace && cargo build -p supercov && node scripts/rust-process-supervision.mjs && node scripts/workspace-crash-integration.mjs",
88
88
  "test:native-package": "cargo build --release -p supercov && node scripts/native-package-integration.mjs && node scripts/native-release-set-integration.mjs",
89
89
  "test:pypi-wheel": "node scripts/pypi-wheel-integration.mjs",
@@ -120,14 +120,14 @@
120
120
  "test:windows-tls": "node scripts/windows-tls-test.mjs"
121
121
  },
122
122
  "optionalDependencies": {
123
- "@supercov/cli-darwin-arm64": "4.7.0",
124
- "@supercov/cli-darwin-x64": "4.7.0",
125
- "@supercov/cli-linux-arm64-gnu": "4.7.0",
126
- "@supercov/cli-linux-arm64-musl": "4.7.0",
127
- "@supercov/cli-linux-x64-gnu": "4.7.0",
128
- "@supercov/cli-linux-x64-musl": "4.7.0",
129
- "@supercov/cli-win32-arm64": "4.7.0",
130
- "@supercov/cli-win32-x64": "4.7.0"
123
+ "@supercov/cli-darwin-arm64": "4.8.0",
124
+ "@supercov/cli-darwin-x64": "4.8.0",
125
+ "@supercov/cli-linux-arm64-gnu": "4.8.0",
126
+ "@supercov/cli-linux-arm64-musl": "4.8.0",
127
+ "@supercov/cli-linux-x64-gnu": "4.8.0",
128
+ "@supercov/cli-linux-x64-musl": "4.8.0",
129
+ "@supercov/cli-win32-arm64": "4.8.0",
130
+ "@supercov/cli-win32-x64": "4.8.0"
131
131
  },
132
132
  "peerDependencies": {
133
133
  "@playwright/test": ">=1.55.0",
@@ -461,7 +461,16 @@ function attemptKey(scope) {
461
461
  function serverRecordKey(record) {
462
462
  var _a8;
463
463
  const suffix = record.type === "decision" ? `${record.meta.id}:${vectorKey(record.vector)}` : record.id;
464
- return `${(_a8 = record.phaseId) != null ? _a8 : "unscoped"}:${record.type}:${suffix}`;
464
+ // Each request that names no test keeps its own first hits: the report
465
+ // credits them to the test running when it came, and a later request's
466
+ // work belongs to whichever test was running then.
467
+ const request = record.request ? `request-${record.request}:` : "";
468
+ return `${request}${(_a8 = record.phaseId) != null ? _a8 : "unscoped"}:${record.type}:${suffix}`;
469
+ }
470
+ // Numbers the requests a server handles that name no test.
471
+ function nextUnscopedRequest() {
472
+ state.unscopedRequests = (state.unscopedRequests || 0) + 1;
473
+ return state.unscopedRequests;
465
474
  }
466
475
  function beginBufferedServerEvidence(scope) {
467
476
  if (isBrowser)
@@ -794,7 +803,11 @@ function appendServer(record) {
794
803
  try {
795
804
  const directory = scope ? serverEvidenceDirectory(scope) : void 0;
796
805
  const path = scope ? serverEvidencePath(scope) : void 0;
797
- const serialized = __spreadValues(__spreadValues({}, record), scope ? { scope } : {});
806
+ // Work done while handling a request that named no test -- a webhook a
807
+ // mock server sends -- says so, with the request's number.
808
+ const stored = serverPhaseStorage == null ? void 0 : serverPhaseStorage.getStore();
809
+ const unscopedRequest = !scope && stored && stored.request ? { request: stored.request } : {};
810
+ const serialized = __spreadValues(__spreadValues(__spreadValues({}, record), scope ? { scope } : {}), unscopedRequest);
798
811
  if (scope && state.bufferedAttempts.has(attemptKey(scope))) {
799
812
  const key = attemptKey(scope);
800
813
  const buffered = (_c = state.serverBuffers.get(key)) != null ? _c : {
@@ -1387,9 +1400,24 @@ function installNodeRequestPropagation() {
1387
1400
  // the server process were created by a shared before hook.
1388
1401
  const incoming = requestCoverageContext(args[0]);
1389
1402
  if (incoming) context = incoming.scope ? incoming : context != null ? context : currentRequestContext();
1403
+ // A request that names no test, to a server no test owns, is numbered:
1404
+ // the report credits its work to the test running when it came.
1405
+ if (incoming && serverPhaseStorage && !(context && (context.scope || context.phaseId)))
1406
+ context = { request: nextUnscopedRequest() };
1407
+ // Node emits a request's body and its response's events outside the
1408
+ // handler's context, so a handler that reads the body -- `req.on('end')`
1409
+ // -- ran as no test's. The request and response carry it with them.
1410
+ if (incoming && context) {
1411
+ for (const emitter of args.slice(0, 2)) {
1412
+ if (emitter && typeof emitter === "object" && typeof emitter.emit === "function")
1413
+ emitterCoverageContexts.set(emitter, context);
1414
+ }
1415
+ }
1390
1416
  }
1391
1417
  const invoke = () => Reflect.apply(original, this, [event, ...args]);
1392
1418
  try {
1419
+ if (context && context.request && !context.scope)
1420
+ return serverPhaseStorage.run(context, () => withProbeV2Context(context, invoke));
1393
1421
  return context ? withCoverageCarrier({ version: 1, ...context }, invoke) : invoke();
1394
1422
  } finally {
1395
1423
  if (event === "close") emitterCoverageContexts.delete(this);
@@ -1410,6 +1438,8 @@ function withRequestPhase(handler, event) {
1410
1438
  // owner. Shared servers have no captured test scope and stay unattributed.
1411
1439
  const inheritedContext = requestContext && requestContext.scope ? {} : registeredContext.scope ? registeredContext : requestContext === void 0 ? currentRequestContext() : {};
1412
1440
  const context = __spreadValues(__spreadValues({}, ((_a8 = requestContext == null ? void 0 : requestContext.scope) != null ? _a8 : inheritedContext.scope) ? { scope: (_b = requestContext == null ? void 0 : requestContext.scope) != null ? _b : inheritedContext.scope } : {}), ((_c = requestContext == null ? void 0 : requestContext.phaseId) != null ? _c : inheritedContext.phaseId) ? { phaseId: (_d = requestContext == null ? void 0 : requestContext.phaseId) != null ? _d : inheritedContext.phaseId } : {});
1441
+ if (requestContext !== void 0 && !context.scope)
1442
+ context.request = nextUnscopedRequest();
1413
1443
  const invoke = () => {
1414
1444
  if (event === "connection" && context.scope && args[0] &&
1415
1445
  typeof args[0] === "object" && typeof args[0].emit === "function")