supercov 0.0.9 → 0.0.12

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 (156) hide show
  1. package/README.md +28 -23
  2. package/bin/native.js +87 -0
  3. package/bin/supercov.js +10 -4
  4. package/package.json +39 -25
  5. package/{dist → runtime/javascript}/atomic.js +0 -1
  6. package/{dist → runtime/javascript}/launchSupervisor.js +3 -1
  7. package/{dist → runtime/javascript}/nodeAssert.js +0 -1
  8. package/{dist → runtime/javascript}/nodeAssertAdapter.js +0 -1
  9. package/{dist → runtime/javascript}/nodeAssertStrict.js +0 -1
  10. package/{dist → runtime/javascript}/nodeTest.js +13 -5
  11. package/{dist → runtime/javascript}/playwright.js +177 -90
  12. package/{dist → runtime/javascript}/playwrightReporter.js +0 -1
  13. package/{dist → runtime/javascript}/provenance.js +0 -1
  14. package/{dist → runtime/javascript}/register.mjs +52 -1
  15. package/{dist → runtime/javascript}/resolve-loader.mjs +0 -36
  16. package/{dist → runtime/javascript}/runnerEvidence.js +6 -5
  17. package/{dist → runtime/javascript}/runtime.js +308 -27
  18. package/{dist → runtime/javascript}/transport.js +2 -3
  19. package/{dist → runtime/javascript}/vitest.js +38 -8
  20. package/{dist → runtime/javascript}/vitestReporter.js +0 -1
  21. package/dist/agentJson.d.ts +0 -44
  22. package/dist/agentJson.d.ts.map +0 -1
  23. package/dist/agentJson.js +0 -84
  24. package/dist/agentJson.js.map +0 -1
  25. package/dist/analyze.d.ts +0 -12
  26. package/dist/analyze.d.ts.map +0 -1
  27. package/dist/analyze.js +0 -699
  28. package/dist/analyze.js.map +0 -1
  29. package/dist/atomic.d.ts +0 -6
  30. package/dist/atomic.d.ts.map +0 -1
  31. package/dist/atomic.js.map +0 -1
  32. package/dist/buildCache.d.ts +0 -14
  33. package/dist/buildCache.d.ts.map +0 -1
  34. package/dist/buildCache.js +0 -84
  35. package/dist/buildCache.js.map +0 -1
  36. package/dist/cli.d.ts +0 -2
  37. package/dist/cli.d.ts.map +0 -1
  38. package/dist/cli.js +0 -706
  39. package/dist/cli.js.map +0 -1
  40. package/dist/directInstrumenter.d.ts +0 -8
  41. package/dist/directInstrumenter.d.ts.map +0 -1
  42. package/dist/directInstrumenter.js +0 -92
  43. package/dist/directInstrumenter.js.map +0 -1
  44. package/dist/esmInterceptor.d.ts +0 -6
  45. package/dist/esmInterceptor.d.ts.map +0 -1
  46. package/dist/esmInterceptor.js +0 -63
  47. package/dist/esmInterceptor.js.map +0 -1
  48. package/dist/evidenceArchive.d.ts +0 -37
  49. package/dist/evidenceArchive.d.ts.map +0 -1
  50. package/dist/evidenceArchive.js +0 -110
  51. package/dist/evidenceArchive.js.map +0 -1
  52. package/dist/instrumenter.d.ts +0 -11
  53. package/dist/instrumenter.d.ts.map +0 -1
  54. package/dist/instrumenter.js +0 -1246
  55. package/dist/instrumenter.js.map +0 -1
  56. package/dist/integrity.d.ts +0 -10
  57. package/dist/integrity.d.ts.map +0 -1
  58. package/dist/integrity.js +0 -205
  59. package/dist/integrity.js.map +0 -1
  60. package/dist/launchSupervisor.d.ts +0 -38
  61. package/dist/launchSupervisor.d.ts.map +0 -1
  62. package/dist/launchSupervisor.js.map +0 -1
  63. package/dist/merge.d.ts +0 -2
  64. package/dist/merge.d.ts.map +0 -1
  65. package/dist/merge.js +0 -116
  66. package/dist/merge.js.map +0 -1
  67. package/dist/nodeAssert.d.ts +0 -31
  68. package/dist/nodeAssert.d.ts.map +0 -1
  69. package/dist/nodeAssert.js.map +0 -1
  70. package/dist/nodeAssertAdapter.d.ts +0 -6
  71. package/dist/nodeAssertAdapter.d.ts.map +0 -1
  72. package/dist/nodeAssertAdapter.js.map +0 -1
  73. package/dist/nodeAssertStrict.d.ts +0 -31
  74. package/dist/nodeAssertStrict.d.ts.map +0 -1
  75. package/dist/nodeAssertStrict.js.map +0 -1
  76. package/dist/nodeAssertionInstrumenter.d.ts +0 -8
  77. package/dist/nodeAssertionInstrumenter.d.ts.map +0 -1
  78. package/dist/nodeAssertionInstrumenter.js +0 -263
  79. package/dist/nodeAssertionInstrumenter.js.map +0 -1
  80. package/dist/nodeTest.d.ts +0 -15
  81. package/dist/nodeTest.d.ts.map +0 -1
  82. package/dist/nodeTest.js.map +0 -1
  83. package/dist/playwright.d.ts +0 -9
  84. package/dist/playwright.d.ts.map +0 -1
  85. package/dist/playwright.js.map +0 -1
  86. package/dist/playwrightReporter.d.ts +0 -6
  87. package/dist/playwrightReporter.d.ts.map +0 -1
  88. package/dist/playwrightReporter.js.map +0 -1
  89. package/dist/project.d.ts +0 -20
  90. package/dist/project.d.ts.map +0 -1
  91. package/dist/project.js +0 -296
  92. package/dist/project.js.map +0 -1
  93. package/dist/provenance.d.ts +0 -9
  94. package/dist/provenance.d.ts.map +0 -1
  95. package/dist/provenance.js.map +0 -1
  96. package/dist/query.d.ts +0 -70
  97. package/dist/query.d.ts.map +0 -1
  98. package/dist/query.js +0 -1443
  99. package/dist/query.js.map +0 -1
  100. package/dist/queryCache.d.ts +0 -15
  101. package/dist/queryCache.d.ts.map +0 -1
  102. package/dist/queryCache.js +0 -95
  103. package/dist/queryCache.js.map +0 -1
  104. package/dist/queueAdapters.d.ts +0 -15
  105. package/dist/queueAdapters.d.ts.map +0 -1
  106. package/dist/queueAdapters.js +0 -55
  107. package/dist/queueAdapters.js.map +0 -1
  108. package/dist/register.d.mts +0 -2
  109. package/dist/register.d.mts.map +0 -1
  110. package/dist/register.mjs.map +0 -1
  111. package/dist/reporter.d.ts +0 -13
  112. package/dist/reporter.d.ts.map +0 -1
  113. package/dist/reporter.js +0 -252
  114. package/dist/reporter.js.map +0 -1
  115. package/dist/resolve-loader.d.mts +0 -3
  116. package/dist/resolve-loader.d.mts.map +0 -1
  117. package/dist/resolve-loader.mjs.map +0 -1
  118. package/dist/runAnalysis.d.ts +0 -19
  119. package/dist/runAnalysis.d.ts.map +0 -1
  120. package/dist/runAnalysis.js +0 -169
  121. package/dist/runAnalysis.js.map +0 -1
  122. package/dist/runnerEvidence.d.ts +0 -19
  123. package/dist/runnerEvidence.d.ts.map +0 -1
  124. package/dist/runnerEvidence.js.map +0 -1
  125. package/dist/runtime.d.ts +0 -88
  126. package/dist/runtime.d.ts.map +0 -1
  127. package/dist/runtime.js.map +0 -1
  128. package/dist/runtimeIsolation.d.ts +0 -3
  129. package/dist/runtimeIsolation.d.ts.map +0 -1
  130. package/dist/runtimeIsolation.js +0 -9
  131. package/dist/runtimeIsolation.js.map +0 -1
  132. package/dist/sourceDiscovery.d.ts +0 -9
  133. package/dist/sourceDiscovery.d.ts.map +0 -1
  134. package/dist/sourceDiscovery.js +0 -238
  135. package/dist/sourceDiscovery.js.map +0 -1
  136. package/dist/transport.d.ts +0 -17
  137. package/dist/transport.d.ts.map +0 -1
  138. package/dist/transport.js.map +0 -1
  139. package/dist/types.d.ts +0 -378
  140. package/dist/types.d.ts.map +0 -1
  141. package/dist/types.js +0 -2
  142. package/dist/types.js.map +0 -1
  143. package/dist/vitePlugin.d.ts +0 -13
  144. package/dist/vitePlugin.d.ts.map +0 -1
  145. package/dist/vitePlugin.js +0 -125
  146. package/dist/vitePlugin.js.map +0 -1
  147. package/dist/vitest.d.ts +0 -2
  148. package/dist/vitest.d.ts.map +0 -1
  149. package/dist/vitest.js.map +0 -1
  150. package/dist/vitestReporter.d.ts +0 -43
  151. package/dist/vitestReporter.d.ts.map +0 -1
  152. package/dist/vitestReporter.js.map +0 -1
  153. package/dist/workspace.d.ts +0 -81
  154. package/dist/workspace.d.ts.map +0 -1
  155. package/dist/workspace.js +0 -504
  156. package/dist/workspace.js.map +0 -1
@@ -105,6 +105,7 @@ class CoveragePhaseController {
105
105
  pendingRegistrations = new Set();
106
106
  scriptUpdate = Promise.resolve();
107
107
  proxyCache = new WeakMap();
108
+ runtimeSnapshots;
108
109
  // Parameter properties are stateful despite the base ESLint rule treating
109
110
  // this as an empty constructor.
110
111
  // eslint-disable-next-line no-useless-constructor
@@ -118,6 +119,41 @@ class CoveragePhaseController {
118
119
  allWorkers() {
119
120
  return [...this.workers];
120
121
  }
122
+ async collectRuntimeSnapshots() {
123
+ if (this.runtimeSnapshots)
124
+ return this.runtimeSnapshots;
125
+ const snapshots = [];
126
+ for (const page of this.allPages()) {
127
+ for (const frame of page.frames()) {
128
+ const snapshot = await frame
129
+ .evaluate(() => {
130
+ const getSnapshot = globalThis.__SUPERCOV_COVERAGE_SNAPSHOT__;
131
+ return getSnapshot?.() ?? { decisions: [], hits: [], events: [] };
132
+ })
133
+ .catch(() => ({
134
+ decisions: [],
135
+ hits: [],
136
+ events: [],
137
+ }));
138
+ snapshots.push(snapshot);
139
+ }
140
+ }
141
+ for (const worker of this.allWorkers()) {
142
+ const snapshot = await worker
143
+ .evaluate(() => {
144
+ const getSnapshot = globalThis.__SUPERCOV_COVERAGE_SNAPSHOT__;
145
+ return getSnapshot?.() ?? { decisions: [], hits: [], events: [] };
146
+ })
147
+ .catch(() => ({
148
+ decisions: [],
149
+ hits: [],
150
+ events: [],
151
+ }));
152
+ snapshots.push(snapshot);
153
+ }
154
+ this.runtimeSnapshots = snapshots;
155
+ return snapshots;
156
+ }
121
157
  async registerPage(page) {
122
158
  if (this.pages.has(page))
123
159
  return;
@@ -190,6 +226,7 @@ class CoveragePhaseController {
190
226
  globalThis.__SUPERCOV_MCDC_TEST_ID__ = attemptId;
191
227
  if (phase)
192
228
  globalThis.__SUPERCOV_PHASE_ID__ = phase;
229
+ globalThis.__SUPERCOV_ACTIVATE_PROBE_CONTEXT__?.(attemptId, phase);
193
230
  const originalFetch = globalThis.fetch?.bind(globalThis);
194
231
  if (!originalFetch)
195
232
  return;
@@ -217,8 +254,8 @@ class CoveragePhaseController {
217
254
  await this.activateInBrowser(phase.id);
218
255
  return phase;
219
256
  }
220
- beginAssertion(operation) {
221
- const phase = this.createPhase("assertion", operation, this.lastActionId);
257
+ beginAssertion(operation, source = callerSource()) {
258
+ const phase = this.createPhase("assertion", operation, this.lastActionId, source);
222
259
  this.activePhaseId = phase.id;
223
260
  // Playwright queues browser protocol commands in order. Starting this
224
261
  // evaluation before an async locator assertion is sufficient to tag its
@@ -294,8 +331,7 @@ class CoveragePhaseController {
294
331
  this.proxyCache.set(target, proxy);
295
332
  return proxy;
296
333
  }
297
- createPhase(kind, operation, causedByPhaseId) {
298
- const source = callerSource();
334
+ createPhase(kind, operation, causedByPhaseId, source = callerSource()) {
299
335
  const phase = {
300
336
  id: `${this.scope.attemptId}:phase:${++this.counter}`,
301
337
  kind,
@@ -369,6 +405,8 @@ class CoveragePhaseController {
369
405
  await Promise.all([...this.pages].flatMap((page) => page.frames()).map((frame) => frame
370
406
  .evaluate(({ id, storageKey, scopeCookie, scopeValue, phaseCookie }) => {
371
407
  globalThis.__SUPERCOV_PHASE_ID__ = id;
408
+ const coverageGlobal = globalThis;
409
+ coverageGlobal.__SUPERCOV_ACTIVATE_PROBE_CONTEXT__?.(coverageGlobal.__SUPERCOV_MCDC_TEST_ID__ ?? "unscoped", id);
372
410
  try {
373
411
  localStorage.setItem(storageKey, id);
374
412
  document.cookie = `${scopeCookie}=${encodeURIComponent(scopeValue)}; Path=/; SameSite=Lax`;
@@ -387,7 +425,9 @@ class CoveragePhaseController {
387
425
  .catch(() => undefined)));
388
426
  await Promise.all([...this.workers].map((worker) => worker
389
427
  .evaluate((id) => {
390
- globalThis.__SUPERCOV_PHASE_ID__ = id;
428
+ const coverageGlobal = globalThis;
429
+ coverageGlobal.__SUPERCOV_PHASE_ID__ = id;
430
+ coverageGlobal.__SUPERCOV_ACTIVATE_PROBE_CONTEXT__?.(coverageGlobal.__SUPERCOV_MCDC_TEST_ID__ ?? "unscoped", id);
391
431
  }, phaseId)
392
432
  .catch(() => undefined)));
393
433
  await Promise.all([...this.pages].map((page) => this.activatePage(page, phaseId)));
@@ -414,7 +454,7 @@ class CoveragePhaseController {
414
454
  }
415
455
  const installed = (await cdp
416
456
  .send("Page.addScriptToEvaluateOnNewDocument", {
417
- source: `globalThis.__SUPERCOV_PHASE_ID__=${JSON.stringify(phaseId)};`,
457
+ source: `globalThis.__SUPERCOV_PHASE_ID__=${JSON.stringify(phaseId)};globalThis.__SUPERCOV_ACTIVATE_PROBE_CONTEXT__?.(globalThis.__SUPERCOV_MCDC_TEST_ID__??"unscoped",${JSON.stringify(phaseId)});`,
418
458
  runImmediately: true,
419
459
  })
420
460
  .catch(() => undefined));
@@ -425,7 +465,40 @@ class CoveragePhaseController {
425
465
  }
426
466
  }
427
467
  let activeController;
468
+ let bridgedAssertionDepth = 0;
428
469
  const controllers = new Map();
470
+ const directRuntime = () => globalThis.__SUPERCOV_DIRECT_RUNTIME__;
471
+ globalThis.__SUPERCOV_ASSERTION_PHASE_BRIDGE__ = (operation, source, callback) => {
472
+ const controller = activeController;
473
+ const runtime = directRuntime();
474
+ if (!controller || !runtime)
475
+ return { handled: false };
476
+ const phase = controller.beginAssertion(operation, source);
477
+ bridgedAssertionDepth += 1;
478
+ try {
479
+ const value = runtime.withCoverageCarrier({ version: 1, scope: controller.scope, phaseId: phase.id }, callback);
480
+ bridgedAssertionDepth -= 1;
481
+ if (value &&
482
+ typeof value.then === "function")
483
+ return {
484
+ handled: true,
485
+ value: Promise.resolve(value).then((resolved) => {
486
+ controller.finish(phase);
487
+ return resolved;
488
+ }, (error) => {
489
+ controller.finish(phase, error);
490
+ throw error;
491
+ }),
492
+ };
493
+ controller.finish(phase);
494
+ return { handled: true, value };
495
+ }
496
+ catch (error) {
497
+ bridgedAssertionDepth -= 1;
498
+ controller.finish(phase, error);
499
+ throw error;
500
+ }
501
+ };
429
502
  function activeCoverageHeaders() {
430
503
  const controller = activeController;
431
504
  if (!controller)
@@ -572,7 +645,9 @@ function wrapMatchers(matchers, path = "expect") {
572
645
  return value;
573
646
  return (...args) => {
574
647
  const controller = activeController;
575
- const phase = controller?.beginAssertion(`${path}.${name}`);
648
+ const phase = bridgedAssertionDepth === 0
649
+ ? controller?.beginAssertion(`${path}.${name}`)
650
+ : undefined;
576
651
  try {
577
652
  const result = Reflect.apply(value, target, args);
578
653
  if (result instanceof Promise) {
@@ -663,22 +738,51 @@ function readServerRecords(scope) {
663
738
  return [];
664
739
  }
665
740
  }
741
+ function phaseSourceLine(source) {
742
+ return source?.replace(/:\d+$/, "");
743
+ }
744
+ /**
745
+ * Playwright's redirected `expect` is the preferred assertion boundary, but
746
+ * its transform loader can bypass Node's resolve hook for project-owned
747
+ * fixture modules. The ahead-of-run frontend therefore keeps a lexical
748
+ * fallback. If both paths observe the same assertion, retain the richer
749
+ * Playwright phase (including action causality) exactly once.
750
+ */
751
+ function mergeCollectedPhases(controllerPhases, fallbackPhases) {
752
+ const observed = new Map();
753
+ for (const phase of controllerPhases) {
754
+ if (phase.kind !== "assertion")
755
+ continue;
756
+ const key = `${phase.operation}\0${phaseSourceLine(phase.source) ?? ""}`;
757
+ observed.set(key, (observed.get(key) ?? 0) + 1);
758
+ }
759
+ const remaining = fallbackPhases.filter((phase) => {
760
+ if (phase.kind !== "assertion")
761
+ return true;
762
+ const key = `${phase.operation}\0${phaseSourceLine(phase.source) ?? ""}`;
763
+ const count = observed.get(key) ?? 0;
764
+ if (count === 0)
765
+ return true;
766
+ observed.set(key, count - 1);
767
+ return false;
768
+ });
769
+ return [...controllerPhases, ...remaining];
770
+ }
666
771
  const instrumentedTest = base.extend({
667
772
  page: async ({ page }, use, testInfo) => {
668
773
  const scope = executionScope(testInfo);
669
- const configuredHeaders = Object.fromEntries(Object.entries(testInfo.project.use.extraHTTPHeaders ?? {}).map(([name, value]) => [name, String(value)]));
670
- const controller = new CoveragePhaseController(scope, configuredHeaders);
671
- controllers.set(scope.attemptId, controller);
672
- activeController = controller;
774
+ const controller = controllers.get(scope.attemptId);
775
+ if (!controller)
776
+ throw new Error(`Supercov's automatic Playwright collector was not initialized for ${scope.attemptId}`);
673
777
  await controller.registerPage(page);
674
778
  try {
675
779
  await use(controller.wrap(page));
676
780
  }
677
781
  finally {
678
- await controller.dispose();
679
- if (activeController === controller)
680
- activeController = undefined;
681
- controllers.delete(scope.attemptId);
782
+ // This fixture still owns a live page. The automatic test fixture tears
783
+ // down after page dependencies and therefore cannot reliably evaluate
784
+ // frames on every Playwright version.
785
+ await controller.collectRuntimeSnapshots();
682
786
  }
683
787
  },
684
788
  browser: [
@@ -705,8 +809,13 @@ const instrumentedTest = base.extend({
705
809
  }));
706
810
  },
707
811
  mcdcAutoCollect: [
708
- async ({ page }, use, testInfo) => {
812
+ async ({}, use, testInfo) => {
709
813
  const scope = executionScope(testInfo);
814
+ const configuredHeaders = Object.fromEntries(Object.entries(testInfo.project.use.extraHTTPHeaders ?? {}).map(([name, value]) => [name, String(value)]));
815
+ const controller = new CoveragePhaseController(scope, configuredHeaders);
816
+ controllers.set(scope.attemptId, controller);
817
+ activeController = controller;
818
+ directRuntime()?.activateCoverageScope(scope);
710
819
  const serverOutput = serverEvidencePath(scope);
711
820
  mkdirSync(serverEvidenceDirectory(scope), { recursive: true });
712
821
  rmSync(serverOutput, { force: true });
@@ -714,81 +823,60 @@ const instrumentedTest = base.extend({
714
823
  await use();
715
824
  }
716
825
  finally {
717
- const controller = controllers.get(scope.attemptId);
718
- const browser = [];
719
- const pages = controller?.allPages() ?? [page];
720
- for (const currentPage of pages) {
721
- for (const frame of currentPage.frames()) {
722
- const frameSnapshot = await frame
723
- .evaluate(() => {
724
- const getSnapshot = globalThis.__SUPERCOV_COVERAGE_SNAPSHOT__;
725
- return getSnapshot?.() ?? { decisions: [], hits: [], events: [] };
726
- })
727
- .catch(() => ({
728
- decisions: [],
729
- hits: [],
730
- events: [],
731
- }));
732
- browser.push(frameSnapshot);
826
+ try {
827
+ const browser = await controller.collectRuntimeSnapshots();
828
+ const server = readServerRecords(scope);
829
+ // Emit an artifact even when this test touched no application source.
830
+ // A complete test-to-coverage matrix must also identify tests that are
831
+ // removable without changing coverage.
832
+ const outputPath = testInfo.outputPath("mcdc.json");
833
+ mkdirSync(dirname(outputPath), { recursive: true });
834
+ const testFile = relative(process.cwd(), testInfo.file)
835
+ .split(sep)
836
+ .join("/");
837
+ const payload = {
838
+ testId: testInfo.testId,
839
+ scope,
840
+ test: testInfo.titlePath.join(" > "),
841
+ testFile,
842
+ title: testInfo.title,
843
+ retry: testInfo.retry,
844
+ status: testInfo.status ?? "unknown",
845
+ expectedStatus: testInfo.expectedStatus,
846
+ provenance: inferTestProvenance({
847
+ runner: "playwright",
848
+ file: testFile,
849
+ project: testInfo.project.name,
850
+ explicitKind: process.env["SUPERCOV_TEST_KIND"],
851
+ }),
852
+ phases: mergeCollectedPhases(controller.phases, directRuntime()?.takeNodeAssertionPhases(scope) ?? []),
853
+ browser,
854
+ server,
855
+ };
856
+ const serialized = `${JSON.stringify(payload)}\n`;
857
+ atomicWriteFileSync(outputPath, serialized);
858
+ // Pool runners may cycle-restore a VM immediately after Playwright
859
+ // exits, which can discard or overwrite the normal artifact copy.
860
+ // Write one uniquely named, one-shot evidence file to the runner's
861
+ // shared directory as well. No test streams into this path.
862
+ const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
863
+ (GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
864
+ ? undefined
865
+ : GENERATED_EVIDENCE_DIRECTORY);
866
+ if (evidenceDirectory) {
867
+ const resolvedDirectory = resolve(process.cwd(), evidenceDirectory);
868
+ const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "_");
869
+ const testEvidenceDirectory = resolve(resolvedDirectory, `${safeTestId}-${testInfo.retry}`);
870
+ mkdirSync(testEvidenceDirectory, { recursive: true });
871
+ atomicWriteFileSync(resolve(testEvidenceDirectory, "mcdc.json"), serialized);
733
872
  }
734
873
  }
735
- for (const worker of controller?.allWorkers() ?? []) {
736
- const workerSnapshot = await worker
737
- .evaluate(() => {
738
- const getSnapshot = globalThis.__SUPERCOV_COVERAGE_SNAPSHOT__;
739
- return getSnapshot?.() ?? { decisions: [], hits: [], events: [] };
740
- })
741
- .catch(() => ({
742
- decisions: [],
743
- hits: [],
744
- events: [],
745
- }));
746
- browser.push(workerSnapshot);
747
- }
748
- const server = readServerRecords(scope);
749
- // Emit an artifact even when this test touched no application source.
750
- // A complete test-to-coverage matrix must also identify tests that are
751
- // removable without changing coverage.
752
- const outputPath = testInfo.outputPath("mcdc.json");
753
- mkdirSync(dirname(outputPath), { recursive: true });
754
- const testFile = relative(process.cwd(), testInfo.file)
755
- .split(sep)
756
- .join("/");
757
- const payload = {
758
- testId: testInfo.testId,
759
- scope,
760
- test: testInfo.titlePath.join(" > "),
761
- testFile,
762
- title: testInfo.title,
763
- retry: testInfo.retry,
764
- status: testInfo.status ?? "unknown",
765
- expectedStatus: testInfo.expectedStatus,
766
- provenance: inferTestProvenance({
767
- runner: "playwright",
768
- file: testFile,
769
- project: testInfo.project.name,
770
- explicitKind: process.env["SUPERCOV_TEST_KIND"],
771
- }),
772
- phases: controller?.phases ?? [],
773
- browser,
774
- server,
775
- };
776
- const serialized = `${JSON.stringify(payload)}\n`;
777
- atomicWriteFileSync(outputPath, serialized);
778
- // Pool runners may cycle-restore a VM immediately after Playwright
779
- // exits, which can discard or overwrite the normal artifact copy.
780
- // Write one uniquely named, one-shot evidence file to the runner's
781
- // shared directory as well. No test streams into this path.
782
- const evidenceDirectory = process.env["SUPERCOV_EVIDENCE_DIR"] ??
783
- (GENERATED_EVIDENCE_DIRECTORY.startsWith("__")
784
- ? undefined
785
- : GENERATED_EVIDENCE_DIRECTORY);
786
- if (evidenceDirectory) {
787
- const resolvedDirectory = resolve(process.cwd(), evidenceDirectory);
788
- const safeTestId = testInfo.testId.replace(/[^a-zA-Z0-9_-]/g, "_");
789
- const testEvidenceDirectory = resolve(resolvedDirectory, `${safeTestId}-${testInfo.retry}`);
790
- mkdirSync(testEvidenceDirectory, { recursive: true });
791
- atomicWriteFileSync(resolve(testEvidenceDirectory, "mcdc.json"), serialized);
874
+ finally {
875
+ await controller.dispose();
876
+ directRuntime()?.activateCoverageScope();
877
+ if (activeController === controller)
878
+ activeController = undefined;
879
+ controllers.delete(scope.attemptId);
792
880
  }
793
881
  }
794
882
  },
@@ -797,4 +885,3 @@ const instrumentedTest = base.extend({
797
885
  });
798
886
  export const test = instrumentedTest;
799
887
  /*__SUPERCOV_ADAPTER_EXPORTS__*/
800
- //# sourceMappingURL=playwright.js.map
@@ -38,4 +38,3 @@ export default class SupercovPlaywrightReporter {
38
38
  atomicWriteFileSync(resolve(directory, "mcdc.json"), `${JSON.stringify(payload)}\n`);
39
39
  }
40
40
  }
41
- //# sourceMappingURL=playwrightReporter.js.map
@@ -48,4 +48,3 @@ export function inferTestProvenance({ runner, file, project, explicitKind, }) {
48
48
  source: defaultKind === "unknown" ? "unknown" : "runner-default",
49
49
  };
50
50
  }
51
- //# sourceMappingURL=provenance.js.map
@@ -7,9 +7,61 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  import Module, { register, syncBuiltinESMExports } from "node:module";
10
+ import { closeSync, openSync, unlinkSync } from "node:fs";
10
11
  import { resolve } from "node:path";
11
12
  import { installLaunchSupervisor } from "./launchSupervisor.js";
12
13
  installLaunchSupervisor();
14
+ // Long-running commands are diagnosed without changing their runner's exit
15
+ // semantics. The first preloaded Node process atomically elects itself for the
16
+ // run and periodically reports public active-resource types. This deliberately
17
+ // uses no Unix signal: a launch tree may contain uninstrumented Node children,
18
+ // and signalling one would terminate a healthy command by default.
19
+ if (!process.__SUPERCOV_DIAGNOSTIC_REPORTER__) {
20
+ process.__SUPERCOV_DIAGNOSTIC_REPORTER__ = true;
21
+ const ownerFile = process.env.SUPERCOV_DIAGNOSTIC_OWNER_FILE;
22
+ let ownerDescriptor;
23
+ try {
24
+ if (ownerFile)
25
+ ownerDescriptor = openSync(ownerFile, "wx");
26
+ }
27
+ catch {
28
+ // Another preloaded process owns diagnostics for this run.
29
+ }
30
+ if (ownerDescriptor !== undefined) {
31
+ const configuredInterval = Number(process.env.SUPERCOV_DIAGNOSTIC_INTERVAL_MS ?? 60_000);
32
+ const intervalMs = Number.isSafeInteger(configuredInterval) && configuredInterval > 0
33
+ ? configuredInterval
34
+ : 60_000;
35
+ const reportResources = () => {
36
+ const resources = typeof process.getActiveResourcesInfo === "function"
37
+ ? process.getActiveResourcesInfo()
38
+ : [];
39
+ const counts = Object.fromEntries([...new Set(resources)].sort().map(resource => [
40
+ resource,
41
+ resources.filter(candidate => candidate === resource).length,
42
+ ]));
43
+ console.error(`[supercov:active-resources] ${JSON.stringify({
44
+ pid: process.pid,
45
+ ppid: process.ppid,
46
+ uptimeMs: Math.round(process.uptime() * 1000),
47
+ resources: counts,
48
+ })}`);
49
+ };
50
+ const diagnosticTimer = setInterval(reportResources, intervalMs);
51
+ diagnosticTimer.unref();
52
+ process.once("exit", () => {
53
+ clearInterval(diagnosticTimer);
54
+ try {
55
+ closeSync(ownerDescriptor);
56
+ }
57
+ catch { }
58
+ try {
59
+ unlinkSync(ownerFile);
60
+ }
61
+ catch { }
62
+ });
63
+ }
64
+ }
13
65
  // Assertion-call instrumentation also uses this runtime in test processes
14
66
  // whose application build is handled by Vite or another compiler. Loading it
15
67
  // for every isolated test launch keeps node:assert attribution runner-agnostic.
@@ -195,4 +247,3 @@ if (process.env.SUPERCOV_CJS_INTERCEPT === "1") {
195
247
  return originalLoad.call(this, request, parent, isMain);
196
248
  };
197
249
  }
198
- //# sourceMappingURL=register.mjs.map
@@ -1,11 +1,3 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
1
  import { resolve as resolvePath } from "node:path";
10
2
  import { pathToFileURL } from "node:url";
11
3
  const GENERATED_TARGET = "__SUPERCOV_PLAYWRIGHT_MODULE__";
@@ -81,31 +73,3 @@ export async function resolve(specifier, context, nextResolve) {
81
73
  }
82
74
  return nextResolve(specifier, context);
83
75
  }
84
- export async function load(url, context, nextLoad) {
85
- const loaded = await nextLoad(url, context);
86
- const transformer = process.env.SUPERCOV_ESM_TRANSFORMER;
87
- const wrapper = process.env.SUPERCOV_ESM_CAPABILITY_WRAPPER;
88
- if (!transformer ||
89
- !wrapper ||
90
- loaded.format !== "module" ||
91
- !belongsToProject(url) ||
92
- loaded.source === undefined ||
93
- loaded.source === null)
94
- return loaded;
95
- const source = typeof loaded.source === "string"
96
- ? loaded.source
97
- : Buffer.from(loaded.source).toString("utf8");
98
- try {
99
- const { transformCapabilityImports } = await import(__rewriteRelativeImportExtension(transformer));
100
- const transformed = transformCapabilityImports(source, url, wrapper);
101
- return transformed.transformed
102
- ? { ...loaded, source: transformed.code }
103
- : loaded;
104
- }
105
- catch (error) {
106
- if (process.env.SUPERCOV_DEBUG === "1")
107
- console.error("[supercov] ESM capability transform skipped", { url, error });
108
- return loaded;
109
- }
110
- }
111
- //# sourceMappingURL=resolve-loader.mjs.map
@@ -52,9 +52,9 @@ export function callerLocation(ignored) {
52
52
  }
53
53
  return {};
54
54
  }
55
- export function readScopedServerEvidence(scope) {
55
+ export function readScopedServerEvidence(scope, evidencePath = serverEvidencePath(scope)) {
56
56
  try {
57
- return readFileSync(serverEvidencePath(scope), "utf8")
57
+ return readFileSync(evidencePath, "utf8")
58
58
  .split("\n")
59
59
  .filter(Boolean)
60
60
  .map((line) => JSON.parse(line))
@@ -64,7 +64,7 @@ export function readScopedServerEvidence(scope) {
64
64
  return [];
65
65
  }
66
66
  }
67
- export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOverride, phases = []) {
67
+ export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOverride, phases = [], serverEvidenceSource) {
68
68
  const evidenceDirectory = evidenceDirectoryOverride ?? process.env["SUPERCOV_EVIDENCE_DIR"];
69
69
  if (!evidenceDirectory)
70
70
  return;
@@ -85,10 +85,11 @@ export function writeRunnerEvidence(identity, status, scope, evidenceDirectoryOv
85
85
  ...(phases.length > 0 ? { phases } : {}),
86
86
  runtime: [],
87
87
  browser: [],
88
- server: readScopedServerEvidence(scope),
88
+ server: serverEvidenceSource
89
+ ? readScopedServerEvidence(scope, serverEvidenceSource)
90
+ : readScopedServerEvidence(scope),
89
91
  };
90
92
  const directory = resolve(process.cwd(), evidenceDirectory, `${identity.runner.replace(/[^A-Za-z0-9_-]/g, "_")}-${scope.attemptId}`);
91
93
  mkdirSync(directory, { recursive: true });
92
94
  atomicWriteFileSync(resolve(directory, "mcdc.json"), `${JSON.stringify(payload)}\n`);
93
95
  }
94
- //# sourceMappingURL=runnerEvidence.js.map