@gfdelarue/testkit 0.3.4 → 0.3.6

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 (124) hide show
  1. package/README.md +11 -73
  2. package/dist/app/doctor/index.js +1 -1
  3. package/dist/app/inspect/artifacts.d.ts +1 -0
  4. package/dist/app/inspect/artifacts.js +1 -1
  5. package/dist/app/inspect/index.js +1 -1
  6. package/dist/app/lint/index.js +1 -1
  7. package/dist/app/lint/native.js +1 -1
  8. package/dist/app/lint/policy.js +1 -1
  9. package/dist/app/lint/rules/data.js +1 -1
  10. package/dist/app/lint/rules/engine-imports.js +1 -1
  11. package/dist/app/lint/rules/syntax.js +1 -1
  12. package/dist/app/lint/rules/unit.js +1 -1
  13. package/dist/app/lint/shared.d.ts +1 -1
  14. package/dist/app/lint/shared.js +1 -1
  15. package/dist/app/run/orchestrator.js +1 -1
  16. package/dist/app/run/request.js +1 -1
  17. package/dist/app/setup/playwright.js +1 -1
  18. package/dist/app/status/index.d.ts +2 -0
  19. package/dist/app/status/index.js +1 -1
  20. package/dist/app/status/runs.d.ts +2 -2
  21. package/dist/app/status/runs.js +1 -1
  22. package/dist/app/stop/index.js +1 -1
  23. package/dist/app/typecheck/index.d.ts +2 -0
  24. package/dist/app/typecheck/index.js +4 -4
  25. package/dist/cli/commands/artifacts.js +2 -2
  26. package/dist/cli/commands/db/schema/refresh.js +1 -1
  27. package/dist/cli/commands/db/schema/verify.js +1 -1
  28. package/dist/cli/commands/doctor.js +1 -1
  29. package/dist/cli/operations/db/schema/refresh/operation.d.ts +1 -0
  30. package/dist/cli/operations/db/schema/refresh/operation.js +1 -1
  31. package/dist/cli/operations/db/schema/verify/operation.d.ts +1 -0
  32. package/dist/cli/operations/db/schema/verify/operation.js +1 -1
  33. package/dist/cli/operations/run/operation.js +1 -1
  34. package/dist/config/loader/discover-tests.d.ts +5 -0
  35. package/dist/config/loader/discover-tests.js +1 -1
  36. package/dist/config/loader/discovery.js +2 -2
  37. package/dist/config/loader/file-metadata.js +1 -1
  38. package/dist/config/loader/index.d.ts +2 -1
  39. package/dist/config/loader/index.js +1 -1
  40. package/dist/config/loader/load.d.ts +2 -0
  41. package/dist/config/loader/load.js +1 -1
  42. package/dist/config/loader/managed-configs.d.ts +2 -0
  43. package/dist/config/loader/managed-configs.js +1 -1
  44. package/dist/config/loader/paths.d.ts +1 -1
  45. package/dist/config/loader/paths.js +1 -1
  46. package/dist/config/loader/skip-config.d.ts +1 -1
  47. package/dist/config/loader/test-selection.d.ts +15 -0
  48. package/dist/config/loader/test-selection.js +1 -0
  49. package/dist/database/index.d.ts +1 -3
  50. package/dist/database/index.js +1 -1
  51. package/dist/database/lifecycle-cleanup.d.ts +7 -0
  52. package/dist/database/lifecycle-cleanup.js +1 -0
  53. package/dist/database/local-postgres.js +1 -1
  54. package/dist/database/schema-source.js +3 -3
  55. package/dist/engines/adapters/evidence-files.js +1 -1
  56. package/dist/engines/catalog.d.ts +3 -8
  57. package/dist/engines/catalog.js +1 -1
  58. package/dist/engines/definitions.d.ts +8 -1
  59. package/dist/engines/definitions.js +1 -1
  60. package/dist/engines/generated-performance.d.ts +1 -0
  61. package/dist/engines/generated-performance.js +12 -0
  62. package/dist/engines/installations/node-vitest.d.ts +8 -22
  63. package/dist/engines/installations/node-vitest.js +1 -1
  64. package/dist/engines/installations/package-requirements.d.ts +28 -0
  65. package/dist/engines/installations/package-requirements.js +1 -0
  66. package/dist/engines/installations/playwright.d.ts +1 -11
  67. package/dist/engines/installations/playwright.js +1 -1
  68. package/dist/engines/k6/adapter/repository-configuration.d.ts +1 -1
  69. package/dist/engines/k6/adapter/repository-configuration.js +1 -1
  70. package/dist/engines/managed-module-resolution.js +111 -21
  71. package/dist/engines/manager.d.ts +1 -0
  72. package/dist/engines/manager.js +1 -1
  73. package/dist/engines/node-vitest/adapter/adapter.d.ts +1 -1
  74. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  75. package/dist/engines/node-vitest/adapter/consumer-typescript-paths.js +1 -1
  76. package/dist/engines/node-vitest/adapter/resolution.d.ts +1 -1
  77. package/dist/engines/node-vitest/adapter/resolution.js +1 -1
  78. package/dist/engines/node-vitest/adapter/workspace.js +33 -9
  79. package/dist/engines/node-vitest/runtime/runtime-input.d.ts +1 -0
  80. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  81. package/dist/engines/runtime-input-file.d.ts +3 -1
  82. package/dist/engines/runtime-input-file.js +1 -1
  83. package/dist/environment/lifecycle-controller.d.ts +1 -2
  84. package/dist/environment/lifecycle-controller.js +1 -1
  85. package/dist/environment/lifecycle.js +1 -1
  86. package/dist/environment/maintenance.d.ts +1 -1
  87. package/dist/environment/maintenance.js +1 -1
  88. package/dist/environment/ready-environment.d.ts +1 -1
  89. package/dist/environment/ready-environment.js +1 -1
  90. package/dist/environment/runtime-contexts.d.ts +1 -1
  91. package/dist/environment/runtime-contexts.js +1 -1
  92. package/dist/environment/runtime-preparation.js +1 -1
  93. package/dist/environment/services.js +1 -1
  94. package/dist/environment/template.d.ts +1 -1
  95. package/dist/environment/template.js +1 -1
  96. package/dist/evidence/bundles.js +1 -1
  97. package/dist/evidence/read-run.js +1 -1
  98. package/dist/evidence/reading.d.ts +1 -1
  99. package/dist/evidence/reading.js +1 -1
  100. package/dist/evidence/storage.js +2 -2
  101. package/dist/kernel/error-serialization.js +1 -1
  102. package/dist/kernel/work-metrics.d.ts +6 -0
  103. package/dist/kernel/work-metrics.js +1 -0
  104. package/dist/process/capture-output.js +1 -1
  105. package/dist/process/execa-bundle.json +1 -1
  106. package/dist/process/files/product-dir.d.ts +1 -0
  107. package/dist/process/files/product-dir.js +1 -0
  108. package/dist/process/locks.js +4 -4
  109. package/dist/process/readiness.js +1 -1
  110. package/dist/process/runs/inspection.d.ts +5 -0
  111. package/dist/process/runs/inspection.js +1 -0
  112. package/dist/process/runs/manifest.d.ts +1 -0
  113. package/dist/process/runs/manifest.js +1 -1
  114. package/dist/process/service-processes.d.ts +1 -1
  115. package/dist/process/service-processes.js +1 -1
  116. package/dist/toolchains/index.js +2 -2
  117. package/docs/cloud-control.md +3 -0
  118. package/docs/engine-architecture.md +4 -1
  119. package/docs/test-types.md +9 -0
  120. package/docs/testkit-cloud-boundary.md +12 -24
  121. package/package.json +2 -1
  122. package/testkit-config-schema.md +2 -0
  123. package/dist/cli/renderers/artifact-preview.d.ts +0 -4
  124. package/dist/cli/renderers/artifact-preview.js +0 -1
package/README.md CHANGED
@@ -7,7 +7,9 @@
7
7
  `@gfdelarue/testkit` is the orchestrator and managed distribution for product test
8
8
  suites. It discovers `*.testkit.ts` files, infers suite ownership from the
9
9
  filesystem, starts local services, provisions Docker-managed local Postgres
10
- databases, schedules suites, and collects results and artifacts.
10
+ databases, schedules suites, and collects results and artifacts. Coding agents
11
+ use it to write tests and hunt bugs, and CI uses it to run those tests. It needs
12
+ no account or server.
11
13
 
12
14
  Testkit exists to replace false confidence with inspectable execution evidence.
13
15
  It cannot prove that an entire application works. It can make narrower claims
@@ -47,33 +49,6 @@ Testkit writes the immutable, verifiable run bundle under
47
49
  `.testkit/results/runs/<run-id>/`. Upload `.testkit/results` with `if: always()`
48
50
  when CI evidence should survive a failed job.
49
51
 
50
- Publishing that evidence to Testkit Cloud is an additional explicit step. Set
51
- `TESTKIT_CLOUD_PROJECT_ID` and the secret `TESTKIT_CLOUD_TOKEN`, then run:
52
-
53
- ```bash
54
- npx testkit cloud publish
55
- ```
56
-
57
- The command verifies the immutable bundle before upload. Ordinary runs never
58
- contact Cloud, and the presence of `CI` does not authorize publication.
59
-
60
- ## Connect to Cloud
61
-
62
- For interactive use, approve your CLI through the browser once:
63
-
64
- ```sh
65
- npx testkit cloud login
66
- npx testkit cloud project create
67
- npx testkit cloud publish --project PROJECT_ID --run RUN_ID
68
- ```
69
-
70
- Login works outside Git. Project creation infers the repository from Git origin
71
- or accepts `--repository owner/repo`. It resumes after any required provider
72
- consent. `cloud list`, `cloud show`, and `cloud apply --file` provide discovery,
73
- settings and product control. See the [Cloud control boundary](docs/testkit-cloud-boundary.md#complete-cloud-control)
74
- and [complete operation reference](https://github.com/gfdelarue/testkit-cloud/blob/master/docs/cloud-control.md).
75
- Explicit `TESTKIT_CLOUD_TOKEN` still takes precedence over the saved account.
76
-
77
52
  ## Execution engines
78
53
 
79
54
  Testkit has three explicit built-in engines rather than one implicit runtime:
@@ -219,40 +194,9 @@ Batch `run` output stays intentionally short: one line per completed file,
219
194
  concise failure blocks, and a final summary. Service logs, captured runtime
220
195
  output, and emitted artifacts are persisted under `.testkit/results/`.
221
196
 
222
- Testkit produces an immutable local run bundle and, when requested, a Cloud
223
- publication receipt. `testkit run` prints `Run ID: <id>` after sealing in text
224
- output and returns `runId` in `--json` output, for passed and failed runs.
225
- Publish that exact evidence with `testkit cloud publish --run <id>`. Omitting
226
- `--run` selects the newest bundle for interactive use. Historical summaries
227
- and trends belong to Testkit Cloud.
228
-
229
- For CI, preserve the run exit status and publish the ID from that invocation:
230
-
231
- ```sh
232
- run_exit=0
233
- testkit run --json > testkit-run.json || run_exit=$?
234
- run_id=$(node -e 'const r = JSON.parse(require("node:fs").readFileSync("testkit-run.json", "utf8")); if (typeof r.runId !== "string" || !r.runId) process.exit(1); process.stdout.write(r.runId)') || {
235
- if [ "$run_exit" -ne 0 ]; then exit "$run_exit"; fi
236
- exit 1
237
- }
238
- publish_exit=0
239
- testkit cloud publish --run "$run_id" || publish_exit=$?
240
- if [ "$publish_exit" -ne 0 ]; then
241
- echo "Cloud publication failed with exit $publish_exit" >&2
242
- fi
243
- exit "$run_exit"
244
- ```
245
-
246
- Set `TESTKIT_CLOUD_PROJECT_ID` and `TESTKIT_CLOUD_TOKEN` in the CI environment.
247
- `TESTKIT_CLOUD_URL`, or the `--url` flag on `cloud publish`, `cloud apply`, and
248
- `cloud snapshot`, changes the Testkit Cloud origin. The bearer token and the
249
- run bundle are sent only to that origin, so protect the variable like the
250
- token itself. Leave it unset to use `https://testkit.cloud`.
251
- A failed run can still publish a valid sealed bundle. Publication reports its
252
- own exit status and does not change the test outcome. This recipe reports a
253
- publication failure and retains the run status as the job status. See the
254
- [Cloud boundary](docs/testkit-cloud-boundary.md) and
255
- [breaking-change migration](https://github.com/gfdelarue/testkit/blob/master/docs/release-notes.md).
197
+ Testkit seals an immutable local run bundle. `testkit run` prints
198
+ `Run ID: <id>` after sealing in text output and returns `runId` in `--json`
199
+ output, for passed and failed runs.
256
200
 
257
201
  Test execution also maintains a scheduler cache at `.testkit/timings.json`.
258
202
  Completed file-level task durations are used to rank future runs with a
@@ -645,20 +589,14 @@ testkit artifacts --run <run-id> --verify --json
645
589
  testkit artifacts --run <run-id> path/to/test.testkit.ts --json
646
590
  ```
647
591
 
592
+ The default artifact list shows names, kinds, summaries, and paths without
593
+ reading payload files. Select a file with `--format text` to read its contents,
594
+ or use `--json` for structured output including the selected payloads.
595
+
648
596
  Programmatic consumers can import `listRunBundles`, `loadRunBundleArtifact`,
649
597
  `loadRunBundleManifest`, and `verifyRunBundle` from
650
598
  `@gfdelarue/testkit/workspace`.
651
599
 
652
- The run bundle is also the boundary for Testkit Cloud. The local runner never
653
- reads regression state or mutates GitHub. See
654
- [`docs/testkit-cloud-boundary.md`](docs/testkit-cloud-boundary.md) for ownership,
655
- ingestion, and migration rules.
656
-
657
- Use `testkit cloud snapshot` to read versioned Cloud regression state. Apply
658
- reviewed mutations from a file with `testkit cloud apply --file commands.json`;
659
- the file must contain a schema version, idempotency key, expected snapshot
660
- version, and commands.
661
-
662
600
  ## Authoring
663
601
 
664
602
  ### Node setup beside a test
@@ -1035,7 +973,7 @@ identifier plus canonical metadata such as:
1035
973
  - `dependsOn`
1036
974
  - `displayName`
1037
975
 
1038
- For cross-run counts, last outcomes, durations, or trends, use Testkit Cloud.
976
+ Discovery output has no cross-run counts, last outcomes, durations, or trends.
1039
977
  See the [status and history migration](https://github.com/gfdelarue/testkit/blob/master/docs/release-notes.md#local-status-and-history-removal)
1040
978
  for the removed discovery fields and consumer migration.
1041
979
 
@@ -1 +1 @@
1
- import p from"path";import{discoverTests as h,loadConfigContext as m}from"../../config/loader/index.js";import{runTestkitTypecheck as k}from"../typecheck/index.js";import{runLintForConfig as v}from"../lint/index.js";export async function runDoctor(e={}){const t=[],r=e.dir?p.resolve(process.cwd(),e.dir):process.cwd(),s=await m({dir:r});t.push({code:"config-load",level:"pass",message:"Loaded testkit config and service config"});const f=await h({dir:r,diagnostics:"report"}),l=b(f,"discovery result"),u=a(l.diagnostics,"discovery result diagnostics"),g=a(l.files,"discovery result files"),n=u.filter(w);t.push({code:"discovery",level:n.length===0?"pass":"fail",message:n.length===0?`Discovered ${String(g.length)} testkit files without diagnostics`:`Discovery reported ${String(n.length)} error diagnostics`,details:n});const i=await v(c(s,"config"),{dir:r});if(t.push({code:"lint",level:i.ok?"pass":"fail",message:i.ok?`No Testkit lint violations across ${String(i.summary.testkitFiles)} suite file(s)`:`Found ${String(i.summary.violations)} Testkit lint violation(s)`,details:i.violations}),e.typecheck!==!1)try{const o=await k({dir:r}),y=a(c(o,"results"),"typecheck results");t.push({code:"typecheck",level:"pass",message:`Typechecked ${String(y.length)} generated program(s)`})}catch(o){t.push({code:"typecheck",level:"fail",message:$(o)})}return{ok:!t.some(o=>o.level==="fail"),productDir:r,checks:t}}function w(e){return d(e)&&Reflect.get(e,"severity")==="error"}function $(e){const t=c(e,"result"),r=c(t,"stderr");if(typeof r=="string"&&r.length>0)return r;const s=c(t,"stdout");return typeof s=="string"&&s.length>0?s:e instanceof Error?e.message:String(e)}function a(e,t){if(!Array.isArray(e))throw new Error(`${t} must be an array`);return e}function b(e,t){if(!d(e))throw new Error(`${t} must be an object`);return e}function c(e,t){return d(e)?Reflect.get(e,t):void 0}function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}
1
+ import m from"path";import{randomUUID as v}from"node:crypto";import{loadDiscoveryContext as k,loadConfigContext as w}from"../../config/loader/index.js";import{runLintForConfig as D}from"../lint/index.js";export async function runDoctor(e={}){const t=[],r=e.dir?m.resolve(process.cwd(),e.dir):process.cwd(),s=await w({dir:r,configImportCacheSalt:v()});t.push({code:"config-load",level:"pass",message:"Loaded testkit config and service config"});const l=await k({dir:r,diagnostics:"report"},s),u=l.discovery,f=b(u,"discovery result"),g=a(f.diagnostics,"discovery result diagnostics"),y=a(f.files,"discovery result files"),c=g.filter(S);t.push({code:"discovery",level:c.length===0?"pass":"fail",message:c.length===0?`Discovered ${String(y.length)} testkit files without diagnostics`:`Discovery reported ${String(c.length)} error diagnostics`,details:c});const o=await D(n(s,"config"),{dir:r});if(t.push({code:"lint",level:o.ok?"pass":"fail",message:o.ok?`No Testkit lint violations across ${String(o.summary.testkitFiles)} suite file(s)`:`Found ${String(o.summary.violations)} Testkit lint violation(s)`,details:o.violations}),e.typecheck!==!1)try{const{runTestkitTypecheckForContext:i}=await import("../typecheck/index.js"),p=await i(s,l.discovery,{dir:r}),h=a(n(p,"results"),"typecheck results");t.push({code:"typecheck",level:"pass",message:`Typechecked ${String(h.length)} generated program(s)`})}catch(i){t.push({code:"typecheck",level:"fail",message:$(i)})}return{ok:!t.some(i=>i.level==="fail"),productDir:r,checks:t}}function S(e){return d(e)&&Reflect.get(e,"severity")==="error"}function $(e){const t=n(e,"result"),r=n(t,"stderr");if(typeof r=="string"&&r.length>0)return r;const s=n(t,"stdout");return typeof s=="string"&&s.length>0?s:e instanceof Error?e.message:String(e)}function a(e,t){if(!Array.isArray(e))throw new Error(`${t} must be an array`);return e}function b(e,t){if(!d(e))throw new Error(`${t} must be an object`);return e}function n(e,t){return d(e)?Reflect.get(e,t):void 0}function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}
@@ -5,6 +5,7 @@ export declare function inspectArtifacts(options: Readonly<{
5
5
  runId: string | null;
6
6
  service: string | null;
7
7
  kind?: string;
8
+ includePayload?: boolean;
8
9
  }>): {
9
10
  mode: "runs";
10
11
  runs: import("../../evidence/contracts.js").RunBundleSummary[];
@@ -1 +1 @@
1
- import c from"node:path";import{collectArtifactEntries as d,listRunBundles as u,loadRunArtifact as l,verifyRunBundle as a}from"../../evidence/index.js";export function inspectArtifacts(r){const e=c.resolve(r.dir||process.cwd());if(r.mode==="runs")return{mode:"runs",runs:u(e,{file:r.file})};const t=l(e,r.runId),i=r.runId||t.run.id||null;if(r.mode==="verify")return{mode:"verify",runId:i,verification:a(e,i)};const f=d(e,t,r.file,r.service).filter(n=>!r.kind||n.artifactRef.kind===r.kind);return{mode:"artifacts",runId:i,entries:f}}
1
+ import d from"node:path";import{collectArtifactEntries as f,listRunBundles as l,loadRunArtifact as c,verifyRunBundle as a}from"../../evidence/index.js";export function inspectArtifacts(r){const e=d.resolve(r.dir||process.cwd());if(r.mode==="runs")return{mode:"runs",runs:l(e,{file:r.file})};const n=r.runId?null:c(e),t=r.runId||n?.run.id||null;if(r.mode==="verify")return{mode:"verify",runId:t,verification:a(e,t)};const i=n??c(e,t),u=f(e,i,r.file,r.service,r.kind,r.includePayload);return{mode:"artifacts",runId:t,entries:u}}
@@ -1 +1 @@
1
- export{formatTestTypeLabel}from"../../domain/test-types.js";import{readRunDocument as h}from"../../evidence/read-run.js";import c from"fs";import i from"path";import{errorMessage as u}from"../../kernel/error-serialization.js";import{isPublicTestType as I}from"../../domain/identifiers.js";import{discoverTests as w,formatDisplayName as k}from"../../config/loader/index.js";import{collectServiceStatus as A}from"../status/index.js";export{discoverTests,fileDisplayName,formatDisplayName,testTypeContract}from"../../config/loader/index.js";export{listRunBundles,loadRunBundleArtifact,loadRunBundleManifest,verifyRunBundle}from"../../evidence/index.js";const b=2,S=24,N=String;export async function inspectWorkspace(e={}){const t=E(e.targetLimit,S),r=new Date().toISOString(),s=await $(e),a=s.data?.product.directory||i.resolve(process.cwd(),e.dir||"."),[n,o,l]=await Promise.all([e.status===!1?M():D(e),d(a,"latest"),d(a,"live")]),y=p(o.data,"latest"),g=p(l.data,"live"),T=x({discovery:s.data,latestRunArtifact:o.data,liveRunArtifact:l.data,limit:t}),R=[...s.warnings,...n.warnings,...o.warnings,...l.warnings];return{schemaVersion:b,source:"testkit-workspace",generatedAt:r,product:s.data?.product||{directory:a,name:i.basename(a)},discovery:s.data?{configFile:s.data.configFile||null,diagnostics:s.data.diagnostics,summary:s.data.summary}:null,status:n.data,runs:{latest:y,live:g},targets:T,activity:B(n.data),warnings:R}}async function $(e){try{const t=await w({diagnostics:"report",dir:e.dir,runnableOnly:!1,service:e.service||null}),r=t.diagnostics.filter(s=>s.severity==="error").map(s=>`Discovery diagnostic: ${s.message}`);return{data:t,warnings:r}}catch(t){return{data:null,warnings:[`Discovery unavailable: ${u(t)}`]}}}async function D(e){try{const t=await A({dir:e.dir,service:e.service||null});return{data:j(t),warnings:[]}}catch(t){return{data:{ok:!1,results:[]},warnings:[`Status unavailable: ${u(t)}`]}}}function M(){return Promise.resolve({data:{ok:!0,results:[]},warnings:[]})}function d(e,t){const r=t==="live"?"live.json":"latest.json",s=i.join(e,".testkit","results",r);if(!c.existsSync(s))return Promise.resolve({data:null,warnings:[]});try{const a=h(JSON.parse(c.readFileSync(s,"utf8")),s);return Promise.resolve({data:a,warnings:[]})}catch(a){return Promise.resolve({data:null,warnings:[`${t} run artifact unavailable: ${u(a)}`]})}}function j(e){return{ok:!0,results:e.map(t=>({caches:t.caches,hasState:t.hasState,name:t.name,product:t.product,runtimeGraphs:t.runtimeGraphs.map(r=>({currentRuntimeDirCount:r.currentRuntimeDirs.length,desired:r.desired,exists:r.exists,name:r.name,orphan:r.orphan,runtimeDirCount:r.runtimeDirCount,staleRuntimeDirCount:r.staleRuntimeDirs.length})),runs:{active:t.runs.active,stale:t.runs.stale,total:t.runs.total},warnings:t.warnings}))}}function p(e,t){if(e===null)return null;const{run:r,summary:s}=e;return{durationMs:r.durationMs??null,generatedAt:e.generatedAt??null,id:r.id,startedAt:r.startedAt,status:r.status,types:r.types??[],source:t,files:{failed:s?.files.failed??0,notRun:s?.files.notRun??0,passed:s?.files.passed??0,skipped:s?.files.skipped??0,total:s?.files.total??0},services:{failed:s?.services.failed??0,passed:s?.services.passed??0,skipped:s?.services.skipped??0,total:s?.services.total??0},suites:{completed:s?.suites.completed??0,failed:s?.suites.failed??0,passed:s?.suites.passed??0,skipped:s?.suites.skipped??0,total:s?.suites.total??0}}}function x({discovery:e,latestRunArtifact:t,liveRunArtifact:r,limit:s}){const a=[F(e),...m(r,"live"),...m(t,"latest"),...P(e).slice(0,Math.ceil(s/2)),...C(e).slice(0,s)].filter(n=>n!==null);return L(a).slice(0,s)}function F(e){return e?{detail:e.product.directory,id:"project:all",kind:"project",label:`All tests in ${e.product.name}`,runnable:!0,source:"discovery"}:null}function P(e){return(e?.suites||[]).map(t=>({detail:[t.service,t.testType,t.engineId,t.profileId].filter(Boolean).join(" - "),id:`suite:${t.service}:${t.testType}:${t.engineId}:${t.profileId}:${t.name}`,engineId:t.engineId,kind:"suite",label:t.displayName||t.name,profileId:t.profileId,runnable:t.activeFileCount>0,service:t.service,source:"discovery",suiteName:t.name,testType:t.testType}))}function C(e){return(e?.files||[]).filter(t=>!t.skipped).map(t=>f(t,"discovery"))}function m(e,t){const r=[];for(const s of e?.services??[])for(const a of s.suites)for(const n of a.files??[])n.status!=="failed"&&!n.error||r.push(f({displayName:v(n.path),engineId:a.engineId,path:n.path,profileId:a.profileId,service:s.name,suiteName:a.name,testType:a.testType},t,"failed"));return r}function f(e,t,r=null){return{detail:[e.service,e.testType,e.engineId,e.profileId,e.suiteName].filter(Boolean).join(" - "),filePath:e.path,engineId:e.engineId,id:`file:${e.service||""}:${e.testType}:${e.engineId}:${e.profileId}:${e.path}`,kind:"file",label:e.displayName||v(e.path),profileId:e.profileId,runnable:I(e.testType),service:e.service||null,source:t,status:r||null,suiteName:e.suiteName||null,testType:e.testType}}function B(e){return{activeRuns:e.results.reduce((t,r)=>t+r.runs.active,0),staleRuns:e.results.reduce((t,r)=>t+r.runs.stale,0)}}function L(e){const t=new Set,r=[];for(const s of e)t.has(s.id)||(t.add(s.id),r.push(s));return r}function E(e,t){const r=Number(e);return Number.isFinite(r)?Math.max(1,Math.min(100,Math.round(r))):t}function v(e){return k(i.basename(N(e||"test")).replace(/\.testkit\.[cm]?[jt]sx?$/,""))}
1
+ export{formatTestTypeLabel}from"../../domain/test-types.js";import{readRunDocument as R}from"../../evidence/read-run.js";import c from"fs";import i from"path";import{errorMessage as u}from"../../kernel/error-serialization.js";import{isPublicTestType as w}from"../../domain/identifiers.js";import{loadDiscoveryContext as I,hasDiscoveryErrors as k,buildDiscoveryErrorFromDiagnostics as D,formatDisplayName as S}from"../../config/loader/index.js";import{collectServiceStatus as b,collectConfiguredServiceStatus as A}from"../status/index.js";export{discoverTests,fileDisplayName,formatDisplayName,testTypeContract}from"../../config/loader/index.js";export{listRunBundles,loadRunBundleArtifact,loadRunBundleManifest,verifyRunBundle}from"../../evidence/index.js";const N=2,$=24,C=String;export async function inspectWorkspace(t={}){const e=_(t.targetLimit,$),s=new Date().toISOString(),r=await F(t),a=r.data?.product.directory||i.resolve(process.cwd(),t.dir||"."),[n,o,l]=await Promise.all([t.status===!1?j():M(t,r.context),d(a,"latest"),d(a,"live")]),y=p(o.data,"latest"),g=p(l.data,"live"),T=P({discovery:r.data,latestRunArtifact:o.data,liveRunArtifact:l.data,limit:e}),h=[...r.warnings,...n.warnings,...o.warnings,...l.warnings];return{schemaVersion:N,source:"testkit-workspace",generatedAt:s,product:r.data?.product||{directory:a,name:i.basename(a)},discovery:r.data?{configFile:r.data.configFile||null,diagnostics:r.data.diagnostics,summary:r.data.summary}:null,status:n.data,runs:{latest:y,live:g},targets:T,activity:O(n.data),warnings:h}}async function F(t){try{const{discovery:e,context:s}=await I({diagnostics:"report",dir:t.dir,runnableOnly:!1,service:t.service||null}),r=e.diagnostics.filter(a=>a.severity==="error").map(a=>`Discovery diagnostic: ${a.message}`);return{data:e,warnings:r,context:s}}catch(e){return{data:null,context:null,warnings:[`Discovery unavailable: ${u(e)}`]}}}async function M(t,e){try{if(e&&k(e.discovery.diagnostics))throw D(e.discovery.diagnostics);const s=e?A(e.configs,t.service||null):await b({dir:t.dir,service:t.service||null});return{data:x(s),warnings:[]}}catch(s){return{data:{ok:!1,results:[]},warnings:[`Status unavailable: ${u(s)}`]}}}function j(){return Promise.resolve({data:{ok:!0,results:[]},warnings:[]})}function d(t,e){const s=e==="live"?"live.json":"latest.json",r=i.join(t,".testkit","results",s);if(!c.existsSync(r))return Promise.resolve({data:null,warnings:[]});try{const a=R(JSON.parse(c.readFileSync(r,"utf8")),r);return Promise.resolve({data:a,warnings:[]})}catch(a){return Promise.resolve({data:null,warnings:[`${e} run artifact unavailable: ${u(a)}`]})}}function x(t){return{ok:!0,results:t.map(e=>({caches:e.caches,hasState:e.hasState,name:e.name,product:e.product,runtimeGraphs:e.runtimeGraphs.map(s=>({currentRuntimeDirCount:s.currentRuntimeDirs.length,desired:s.desired,exists:s.exists,name:s.name,orphan:s.orphan,runtimeDirCount:s.runtimeDirCount,staleRuntimeDirCount:s.staleRuntimeDirs.length})),runs:{active:e.runs.active,stale:e.runs.stale,total:e.runs.total},warnings:e.warnings}))}}function p(t,e){if(t===null)return null;const{run:s,summary:r}=t;return{durationMs:s.durationMs??null,generatedAt:t.generatedAt??null,id:s.id,startedAt:s.startedAt,status:s.status,types:s.types??[],source:e,files:{failed:r?.files.failed??0,notRun:r?.files.notRun??0,passed:r?.files.passed??0,skipped:r?.files.skipped??0,total:r?.files.total??0},services:{failed:r?.services.failed??0,passed:r?.services.passed??0,skipped:r?.services.skipped??0,total:r?.services.total??0},suites:{completed:r?.suites.completed??0,failed:r?.suites.failed??0,passed:r?.suites.passed??0,skipped:r?.suites.skipped??0,total:r?.suites.total??0}}}function P({discovery:t,latestRunArtifact:e,liveRunArtifact:s,limit:r}){const a=[E(t),...m(s,"live"),...m(e,"latest"),...B(t).slice(0,Math.ceil(r/2)),...L(t).slice(0,r)].filter(n=>n!==null);return W(a).slice(0,r)}function E(t){return t?{detail:t.product.directory,id:"project:all",kind:"project",label:`All tests in ${t.product.name}`,runnable:!0,source:"discovery"}:null}function B(t){return(t?.suites||[]).map(e=>({detail:[e.service,e.testType,e.engineId,e.profileId].filter(Boolean).join(" - "),id:`suite:${e.service}:${e.testType}:${e.engineId}:${e.profileId}:${e.name}`,engineId:e.engineId,kind:"suite",label:e.displayName||e.name,profileId:e.profileId,runnable:e.activeFileCount>0,service:e.service,source:"discovery",suiteName:e.name,testType:e.testType}))}function L(t){return(t?.files||[]).filter(e=>!e.skipped).map(e=>f(e,"discovery"))}function m(t,e){const s=[];for(const r of t?.services??[])for(const a of r.suites)for(const n of a.files??[])n.status!=="failed"&&!n.error||s.push(f({displayName:v(n.path),engineId:a.engineId,path:n.path,profileId:a.profileId,service:r.name,suiteName:a.name,testType:a.testType},e,"failed"));return s}function f(t,e,s=null){return{detail:[t.service,t.testType,t.engineId,t.profileId,t.suiteName].filter(Boolean).join(" - "),filePath:t.path,engineId:t.engineId,id:`file:${t.service||""}:${t.testType}:${t.engineId}:${t.profileId}:${t.path}`,kind:"file",label:t.displayName||v(t.path),profileId:t.profileId,runnable:w(t.testType),service:t.service||null,source:e,status:s||null,suiteName:t.suiteName||null,testType:t.testType}}function O(t){return{activeRuns:t.results.reduce((e,s)=>e+s.runs.active,0),staleRuns:t.results.reduce((e,s)=>e+s.runs.stale,0)}}function W(t){const e=new Set,s=[];for(const r of t)e.has(r.id)||(e.add(r.id),s.push(r));return s}function _(t,e){const s=Number(t);return Number.isFinite(s)?Math.max(1,Math.min(100,Math.round(s))):e}function v(t){return S(i.basename(C(t||"test")).replace(/\.testkit\.[cm]?[jt]sx?$/,""))}
@@ -1,2 +1,2 @@
1
- import{loadTestkitConfig as f,resolveProductDir as u}from"../../config/loader/index.js";export async function executeLintOperation(t={}){const n=u(process.cwd(),t.dir),i=await f(n);return runLintForConfig(m(i),{dir:n})}function m(t){if(!(typeof t!="object"||t===null||Array.isArray(t)))return Reflect.get(t,"config")}import c from"node:fs";import r from"node:path";import{normalizeLintOptions as p,normalizeLintConfig as l}from"./options.js";import{collectCurrentFiles as d,collectTrackedFiles as g,isTestkitSuitePath as y}from"./shared.js";import{lintProject as w}from"./native.js";export async function runLint(t={}){const{dir:n,...i}=t,o=r.resolve(process.cwd(),n||".");return a(o,p(i))}export async function runLintForConfig(t,n={}){const i=r.resolve(process.cwd(),n.dir||".");return a(i,l(t))}async function a(t,n){const i=await g(t),o=await d(t),e=await w({productDir:t,files:o,options:n,trackedFiles:i}),s={ok:e.length===0,productDir:t,summary:{files:o.length,testkitFiles:o.filter(y).length,violations:e.length},violations:e};return k(t,s),s}export function normalizeLintConfig(t={}){return l(t)}function k(t,n){try{const i=r.join(t,".testkit","lint");c.mkdirSync(i,{recursive:!0}),c.writeFileSync(r.join(i,"latest.json"),`${JSON.stringify(n,null,2)}
1
+ import{loadTestkitConfig as f,resolveProductDir as u}from"../../config/loader/index.js";export async function executeLintOperation(t={}){const i=u(process.cwd(),t.dir),n=await f(i);return runLintForConfig(m(n),{dir:i})}function m(t){if(!(typeof t!="object"||t===null||Array.isArray(t)))return Reflect.get(t,"config")}import c from"node:fs";import r from"node:path";import{normalizeLintOptions as p,normalizeLintConfig as l}from"./options.js";import{collectCurrentFiles as d,collectTrackedFiles as g,isTestkitSuitePath as y}from"./shared.js";export async function runLint(t={}){const{dir:i,...n}=t,o=r.resolve(process.cwd(),i||".");return a(o,p(n))}export async function runLintForConfig(t,i={}){const n=r.resolve(process.cwd(),i.dir||".");return a(n,l(t))}async function a(t,i){const n=await g(t),o=await d(t,n!==null),e=o.length===0?[]:await(await import("./native.js")).lintProject({productDir:t,files:o,options:i,trackedFiles:n}),s={ok:e.length===0,productDir:t,summary:{files:o.length,testkitFiles:o.filter(y).length,violations:e.length},violations:e};return w(t,s),s}export function normalizeLintConfig(t={}){return l(t)}function w(t,i){try{const n=r.join(t,".testkit","lint");c.mkdirSync(n,{recursive:!0}),c.writeFileSync(r.join(n,"latest.json"),`${JSON.stringify(i,null,2)}
2
2
  `,"utf8")}catch{}}
@@ -1 +1 @@
1
- import y from"node:path";import{ESLint as j}from"eslint";import*as S from"@typescript-eslint/parser";import{LINT_RULE_NAMES as O}from"./options.js";import{compareFindings as _,findingKey as $,isTypeScriptLikePath as B,matchesAnyPathPattern as k,normalizeFinding as c,normalizePath as E,SOURCE_IGNORES as M}from"./shared.js";import{createSyntaxPolicy as F}from"./rules/syntax.js";import{createDataPolicy as U}from"./rules/data.js";import{createUnitPolicy as z}from"./rules/unit.js";import{createEngineImportPolicy as A}from"./rules/engine-imports.js";export async function lintProject({productDir:i,files:n,trackedFiles:o,options:s}){if(j.version!=="10.7.0"||S.version!=="8.65.0")throw new Error("Testkit lint requires its managed eslint@10.7.0 and @typescript-eslint/parser@8.65.0 installation");const h=new Map,I=[],f=O.filter(e=>s.rules[e].enabled),x=Object.fromEntries(f.filter(e=>e!=="testkitBoundary").map(e=>{const t=s.rules[e],r={meta:{type:"problem",schema:[],messages:{violation:"{{message}}"}},defaultOptions:[],create(l){const P=E(y.relative(i,l.filename)),u={context:l,file:P,report:(a,C,L,g)=>{const N=c({file:P,ruleName:e,ruleId:t.ruleId,kind:C,target:L,message:g,line:a.loc.start.line,snippet:l.sourceCode.getText(a).split(/\r?\n/u)[0]});h.set(T(l.filename,a.loc.start.line,a.loc.start.column+1,g),N),l.report({node:a,messageId:"violation",data:{message:g}})},options:s,literals:I};return e==="engineImports"?A(u):e==="dataSuiteContract"?U(u):e==="unitTestBoundaries"?z(u):F(e,u,n)}};return[t.ruleId,r]})),b=new j({cwd:i,overrideConfigFile:!0,allowInlineConfig:!1,errorOnUnmatchedPattern:!1,globInputPaths:!1,warnIgnored:!1,overrideConfig:[{ignores:M},{files:["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],languageOptions:{parser:S,ecmaVersion:"latest",sourceType:"module"},plugins:{testkit:{rules:x}}},...f.filter(e=>e!=="testkitBoundary").map(e=>{const t=s.rules[e];return{files:t.include.length?t.include:["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],ignores:t.exclude,rules:{[`testkit/${t.ruleId}`]:"error"}}})]}),w=[],v=n.filter(B);v.length&&w.push(...await b.lintFiles(v.map(e=>y.join(i,e))));const d=w.flatMap(e=>e.messages.map(t=>{if(t.ruleId===null)return c({file:E(y.relative(i,e.filePath)),line:t.line,ruleName:"syntax",ruleId:"syntax",kind:"parse-error",target:t.message,message:t.message});const r=h.get(T(e.filePath,t.line,t.column,t.message));if(!r)throw new Error(`Missing Testkit finding for ${t.ruleId}: ${t.message}`);return r}));if(f.includes("testkitBoundary")){const e=s.rules.testkitBoundary,t=o&&new Set(o);for(const r of n)e.trackedOnly&&t&&!t.has(r)||!R(r,e)||!r.split("/").some(l=>l.toLowerCase().includes("testkit"))||k(r,e.allowed??[])||d.push(c({file:r,ruleName:"testkitBoundary",ruleId:e.ruleId,kind:"testkit-path",target:r,message:"Tracked Testkit plumbing must live in testkit.config.* or __testkit__/ suites, or move into @gfdelarue/testkit"}))}const m=new Map;for(const e of I){const t=`${e.field}:${e.value}`,r=m.get(t)??[];r.push(e),m.set(t,r)}for(const[e,t]of m)if(!(new Set(t.map(r=>r.file)).size<2))for(const r of t)d.push(c({...r,ruleName:"dalParallelSafety",ruleId:"dal-parallel-safety",kind:"duplicate-literal",target:e,message:`Duplicate ${r.field} literal "${r.value}" appears across DAL specs; use fixture-derived namespaced values`}));const p=new Map;for(const e of d.sort(_)){const t=$(e);p.has(t)||p.set(t,e)}return[...p.values()]}function T(i,n,o,s){return[i,n,o,s].join("\0")}function R(i,n){return(!n.include.length||k(i,n.include))&&!k(i,n.exclude)}
1
+ import y from"node:path";import{LINT_RULE_NAMES as _}from"./options.js";import{compareFindings as $,findingKey as B,isTypeScriptLikePath as M,matchesAnyPathPattern as h,normalizeFinding as c,normalizePath as E,SOURCE_IGNORES as F}from"./shared.js";import{createSyntaxPolicy as R}from"./rules/syntax.js";import{createDataPolicy as U}from"./rules/data.js";import{createUnitPolicy as z}from"./rules/unit.js";import{createEngineImportPolicy as A}from"./rules/engine-imports.js";export async function lintProject({productDir:i,files:n,trackedFiles:o,options:s}){const x=await K(),[{ESLint:k},v]=x;if(k.version!=="10.7.0"||v.version!=="8.65.0")throw new Error("Testkit lint requires its managed eslint@10.7.0 and @typescript-eslint/parser@8.65.0 installation");const I=new Map,w=[],f=_.filter(e=>s.rules[e].enabled),L=Object.fromEntries(f.filter(e=>e!=="testkitBoundary").map(e=>{const t=s.rules[e],r={meta:{type:"problem",schema:[],messages:{violation:"{{message}}"}},defaultOptions:[],create(l){return(()=>{const S=E(y.relative(i,l.filename)),u={context:l,file:S,report:(a,b,C,g)=>{const O=c({file:S,ruleName:e,ruleId:t.ruleId,kind:b,target:C,message:g,line:a.loc.start.line,snippet:l.sourceCode.getText(a).split(/\r?\n/u)[0]});I.set(T(l.filename,a.loc.start.line,a.loc.start.column+1,g),O),l.report({node:a,messageId:"violation",data:{message:g}})},options:s,literals:w};return e==="engineImports"?A(u):e==="dataSuiteContract"?U(u):e==="unitTestBoundaries"?z(u):R(e,u,n)})()}};return[t.ruleId,r]})),N=new k({cwd:i,overrideConfigFile:!0,allowInlineConfig:!1,errorOnUnmatchedPattern:!1,globInputPaths:!1,warnIgnored:!1,overrideConfig:[{ignores:F},{files:["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],languageOptions:{parser:v,ecmaVersion:"latest",sourceType:"module"},plugins:{testkit:{rules:L}}},...f.filter(e=>e!=="testkitBoundary").map(e=>{const t=s.rules[e];return{files:t.include.length?t.include:["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],ignores:t.exclude,rules:{[`testkit/${t.ruleId}`]:"error"}}})]}),P=[],j=n.filter(M);if(j.length){const e=j.map(r=>y.join(i,r)),t=await N.lintFiles(e);P.push(...t)}const d=P.flatMap(e=>e.messages.map(t=>{if(t.ruleId===null)return c({file:E(y.relative(i,e.filePath)),line:t.line,ruleName:"syntax",ruleId:"syntax",kind:"parse-error",target:t.message,message:t.message});const r=I.get(T(e.filePath,t.line,t.column,t.message));if(!r)throw new Error(`Missing Testkit finding for ${t.ruleId}: ${t.message}`);return r}));if(f.includes("testkitBoundary")){const e=s.rules.testkitBoundary,t=o&&new Set(o);for(const r of n)e.trackedOnly&&t&&!t.has(r)||!q(r,e)||!r.split("/").some(l=>l.toLowerCase().includes("testkit"))||h(r,e.allowed??[])||d.push(c({file:r,ruleName:"testkitBoundary",ruleId:e.ruleId,kind:"testkit-path",target:r,message:"Tracked Testkit plumbing must live in testkit.config.* or __testkit__/ suites, or move into @gfdelarue/testkit"}))}const p=new Map;for(const e of w){const t=`${e.field}:${e.value}`,r=p.get(t)??[];r.push(e),p.set(t,r)}for(const[e,t]of p)if(!(new Set(t.map(r=>r.file)).size<2))for(const r of t)d.push(c({...r,ruleName:"dalParallelSafety",ruleId:"dal-parallel-safety",kind:"duplicate-literal",target:e,message:`Duplicate ${r.field} literal "${r.value}" appears across DAL specs; use fixture-derived namespaced values`}));const m=new Map;for(const e of d.sort($)){const t=B(e);m.has(t)||m.set(t,e)}return[...m.values()]}function T(i,n,o,s){return[i,n,o,s].join("\0")}function q(i,n){return(!n.include.length||h(i,n.include))&&!h(i,n.exclude)}function K(){return Promise.all([import("eslint"),import("@typescript-eslint/parser")])}
@@ -1 +1 @@
1
- import{AST_NODE_TYPES as t}from"@typescript-eslint/utils";export function stringValue(e){return e?.type===t.Literal&&typeof e.value=="string"?e.value:e?.type===t.TemplateLiteral&&e.expressions.length===0?e.quasis[0]?.value.cooked??null:null}export function memberName(e){return!e.computed&&e.property.type===t.Identifier?e.property.name:stringValue(e.property)}export function binding(e,p){for(let r=e.sourceCode.getScope(p);r;r=r.upper){const u=r.set.get(p.name);if(u)return u}return null}
1
+ import{AST_NODE_TYPES as t}from"@typescript-eslint/types";export function stringValue(e){return e?.type===t.Literal&&typeof e.value=="string"?e.value:e?.type===t.TemplateLiteral&&e.expressions.length===0?e.quasis[0]?.value.cooked??null:null}export function memberName(e){return!e.computed&&e.property.type===t.Identifier?e.property.name:stringValue(e.property)}export function binding(e,p){for(let r=e.sourceCode.getScope(p);r;r=r.upper){const u=r.set.get(p.name);if(u)return u}return null}
@@ -1 +1 @@
1
- import{AST_NODE_TYPES as r}from"@typescript-eslint/utils";import{binding as p,memberName as w,stringValue as E}from"../policy.js";const T=new Set(["addFinding","actor","budget","bytes","count","duration","event","externalCall","expectMetric","expectSequence","fixture","finding","http","metric","money","ratio","reconcile","record","recordCache","recordHttpRequest","recordLlmUsage","recordRpc","sequence","sequenceBudget","system","transition"]),N=new Set(["actor","bytes","duration","externalCall","http","money","ratio","reconcile","record","recordCache","recordHttpRequest","recordLlmUsage","recordRpc","sequence","system","transition"]),f=new Set(["bench","describe","it","suite","test","BenchmarkRunner","TestRunner","EvaluatedModules"]);export function createDataPolicy({context:o,file:C,report:n}){if(!C.endsWith(".data.testkit.ts"))return{};const d=new Set,h=new Set,v=new Map,g=new Map;let y=!1,b=!1;const u=(e,t,a="vitest-runner-import")=>{n(e,a,t,`Data suites cannot use ${t} for Vitest case registration; declare cases with dataScenario(...)`)},x=e=>{for(const t of o.sourceCode.getAncestors(e).reverse())if(t.type===r.ArrowFunctionExpression||t.type===r.FunctionExpression){const a=g.get(t);if(a)return a}},m=e=>e.startsWith("vitest/")&&e!=="vitest/package.json"||e==="@gfdelarue/testkit/node-vitest/contract",k=e=>{E(e)?.includes("{serviceContextUrl:")&&n(e,"service-namespace-url","serviceContextUrl","Data suites must not use serviceContextUrl placeholders because they run outside the deployed service namespace")};return{ImportDeclaration(e){const t=e.source.value;if(t==="@playwright/test"&&n(e,"browser-runtime-import",t,"Browser/runtime driving belongs in ui or e2e suites, not data suites"),(t.startsWith("@testing-library/")||t==="jsdom"||t==="happy-dom")&&n(e,"dom-runtime-import","dom-runtime","DOM/rendering tests belong in ui suites, not data suites"),e.importKind==="type")return;const a=e.specifiers.filter(s=>s.type!==r.ImportSpecifier||s.importKind!=="type");if(!(e.specifiers.length&&!a.length)){m(t)&&u(e,t),t==="@gfdelarue/testkit/node-vitest/data"&&(y=!0);for(const s of a){const i=o.sourceCode.getDeclaredVariables(e).find(l=>l.name===s.local.name);if(t==="vitest"&&s.type===r.ImportNamespaceSpecifier&&i&&h.add(i),s.type!==r.ImportSpecifier)continue;const c=s.imported.type===r.Identifier?s.imported.name:s.imported.value;t==="vitest"&&f.has(c)&&u(s,c),t==="@gfdelarue/testkit/node-vitest/data"&&c==="dataScenario"&&i&&d.add(i)}}},ExportNamedDeclaration(e){if(!e.source||e.exportKind==="type")return;const t=e.specifiers.filter(a=>a.exportKind!=="type");if(!(e.specifiers.length&&!t.length)&&(m(e.source.value)&&u(e,e.source.value),e.source.value==="vitest"))for(const a of t){const s=a.local.type===r.Identifier?a.local.name:a.local.value;f.has(s)&&u(a,s)}},ExportAllDeclaration(e){e.exportKind!=="type"&&m(e.source.value)&&u(e,e.source.value)},MemberExpression(e){if(e.object.type!==r.Identifier)return;const t=p(o,e.object),a=w(e);t&&h.has(t)&&a&&f.has(a)&&u(e,a,"vitest-runner-call")},CallExpression(e){const t=e.callee.type===r.Identifier?p(o,e.callee):null;if(t&&d.has(t)){b=!0;const i=e.arguments.at(-1);if(!i||i.type!==r.ArrowFunctionExpression&&i.type!==r.FunctionExpression){n(e,"missing-scenario-callback","dataScenario","dataScenario(...) must provide a callback that receives the Testkit data context");return}const c=i.params[0]?.type===r.AssignmentPattern?i.params[0].left:i.params.at(0),l=c?.type===r.Identifier?o.sourceCode.getDeclaredVariables(i).find(D=>D.name===c.name):null;if(!l){n(e,"missing-data-context","dataScenario","dataScenario callbacks must accept the Testkit data context and record data evidence");return}const S={callback:i,context:l,contextName:l.name,methods:new Set,assertion:!1};v.set(e,S),g.set(i,S);return}const a=o.sourceCode.getText(e.callee);e.callee.type===r.MemberExpression&&a.includes(".serviceContextUrl")&&n(e,"service-namespace-url","serviceContextUrl","Data suites run in the managed Node.js runner, not inside a service namespace; use baseUrl/public endpoints or move service-namespace checks to int/e2e");const s=x(e);if(s&&(/^(expect|assert)(?:\(|\.)/u.test(a)&&(s.assertion=!0),e.callee.type===r.MemberExpression&&e.callee.object.type===r.Identifier&&p(o,e.callee.object)===s.context)){const i=w(e.callee);i&&T.has(i)&&s.methods.add(i)}},"CallExpression:exit"(e){const t=v.get(e);if(!t)return;const{callback:a,contextName:s,methods:i,assertion:c}=t;i.size?[...i].some(l=>N.has(l))||(n(a,"weak-data-evidence",s,"Data scenarios must record product data-flow evidence such as http, bytes, sequence, record, transition, system, externalCall, recordCache, recordLlmUsage, or reconcile; ordinary Node/Vitest-style checks belong in *.contract.testkit.ts with @gfdelarue/testkit/node-vitest/contract"),c&&n(a,"vitest-shaped-data-suite",s,"This dataScenario looks like an ordinary assertion test with weak data bookkeeping; move Node-backed contract checks to *.contract.testkit.ts or model the product data flow with structured evidence")):n(a,"missing-data-evidence",s,"Data scenarios must record Testkit-owned data-flow evidence with the data context, such as http, bytes, sequence, record, transition, system, externalCall, recordCache, recordLlmUsage, or reconcile")},ThrowStatement(e){const t=x(e);t&&(t.assertion=!0)},Literal:k,TemplateLiteral:k,"Program:exit"(e){y?d.size?b||n(e,"missing-data-scenario","dataScenario","Data suites must declare at least one dataScenario(...)"):n(e,"missing-data-scenario-import","dataScenario","Data suites must import dataScenario from @gfdelarue/testkit/node-vitest/data"):n(e,"missing-data-import","@gfdelarue/testkit/node-vitest/data","Data suites must import dataScenario from @gfdelarue/testkit/node-vitest/data")}}}
1
+ import{AST_NODE_TYPES as r}from"@typescript-eslint/types";import{binding as p,memberName as w,stringValue as E}from"../policy.js";const T=new Set(["addFinding","actor","budget","bytes","count","duration","event","externalCall","expectMetric","expectSequence","fixture","finding","http","metric","money","ratio","reconcile","record","recordCache","recordHttpRequest","recordLlmUsage","recordRpc","sequence","sequenceBudget","system","transition"]),N=new Set(["actor","bytes","duration","externalCall","http","money","ratio","reconcile","record","recordCache","recordHttpRequest","recordLlmUsage","recordRpc","sequence","system","transition"]),f=new Set(["bench","describe","it","suite","test","BenchmarkRunner","TestRunner","EvaluatedModules"]);export function createDataPolicy({context:o,file:C,report:n}){if(!C.endsWith(".data.testkit.ts"))return{};const d=new Set,h=new Set,v=new Map,g=new Map;let y=!1,b=!1;const u=(e,t,a="vitest-runner-import")=>{n(e,a,t,`Data suites cannot use ${t} for Vitest case registration; declare cases with dataScenario(...)`)},x=e=>{for(const t of o.sourceCode.getAncestors(e).reverse())if(t.type===r.ArrowFunctionExpression||t.type===r.FunctionExpression){const a=g.get(t);if(a)return a}},m=e=>e.startsWith("vitest/")&&e!=="vitest/package.json"||e==="@gfdelarue/testkit/node-vitest/contract",k=e=>{E(e)?.includes("{serviceContextUrl:")&&n(e,"service-namespace-url","serviceContextUrl","Data suites must not use serviceContextUrl placeholders because they run outside the deployed service namespace")};return{ImportDeclaration(e){const t=e.source.value;if(t==="@playwright/test"&&n(e,"browser-runtime-import",t,"Browser/runtime driving belongs in ui or e2e suites, not data suites"),(t.startsWith("@testing-library/")||t==="jsdom"||t==="happy-dom")&&n(e,"dom-runtime-import","dom-runtime","DOM/rendering tests belong in ui suites, not data suites"),e.importKind==="type")return;const a=e.specifiers.filter(s=>s.type!==r.ImportSpecifier||s.importKind!=="type");if(!(e.specifiers.length&&!a.length)){m(t)&&u(e,t),t==="@gfdelarue/testkit/node-vitest/data"&&(y=!0);for(const s of a){const i=o.sourceCode.getDeclaredVariables(e).find(l=>l.name===s.local.name);if(t==="vitest"&&s.type===r.ImportNamespaceSpecifier&&i&&h.add(i),s.type!==r.ImportSpecifier)continue;const c=s.imported.type===r.Identifier?s.imported.name:s.imported.value;t==="vitest"&&f.has(c)&&u(s,c),t==="@gfdelarue/testkit/node-vitest/data"&&c==="dataScenario"&&i&&d.add(i)}}},ExportNamedDeclaration(e){if(!e.source||e.exportKind==="type")return;const t=e.specifiers.filter(a=>a.exportKind!=="type");if(!(e.specifiers.length&&!t.length)&&(m(e.source.value)&&u(e,e.source.value),e.source.value==="vitest"))for(const a of t){const s=a.local.type===r.Identifier?a.local.name:a.local.value;f.has(s)&&u(a,s)}},ExportAllDeclaration(e){e.exportKind!=="type"&&m(e.source.value)&&u(e,e.source.value)},MemberExpression(e){if(e.object.type!==r.Identifier)return;const t=p(o,e.object),a=w(e);t&&h.has(t)&&a&&f.has(a)&&u(e,a,"vitest-runner-call")},CallExpression(e){const t=e.callee.type===r.Identifier?p(o,e.callee):null;if(t&&d.has(t)){b=!0;const i=e.arguments.at(-1);if(!i||i.type!==r.ArrowFunctionExpression&&i.type!==r.FunctionExpression){n(e,"missing-scenario-callback","dataScenario","dataScenario(...) must provide a callback that receives the Testkit data context");return}const c=i.params[0]?.type===r.AssignmentPattern?i.params[0].left:i.params.at(0),l=c?.type===r.Identifier?o.sourceCode.getDeclaredVariables(i).find(D=>D.name===c.name):null;if(!l){n(e,"missing-data-context","dataScenario","dataScenario callbacks must accept the Testkit data context and record data evidence");return}const S={callback:i,context:l,contextName:l.name,methods:new Set,assertion:!1};v.set(e,S),g.set(i,S);return}const a=o.sourceCode.getText(e.callee);e.callee.type===r.MemberExpression&&a.includes(".serviceContextUrl")&&n(e,"service-namespace-url","serviceContextUrl","Data suites run in the managed Node.js runner, not inside a service namespace; use baseUrl/public endpoints or move service-namespace checks to int/e2e");const s=x(e);if(s&&(/^(expect|assert)(?:\(|\.)/u.test(a)&&(s.assertion=!0),e.callee.type===r.MemberExpression&&e.callee.object.type===r.Identifier&&p(o,e.callee.object)===s.context)){const i=w(e.callee);i&&T.has(i)&&s.methods.add(i)}},"CallExpression:exit"(e){const t=v.get(e);if(!t)return;const{callback:a,contextName:s,methods:i,assertion:c}=t;i.size?[...i].some(l=>N.has(l))||(n(a,"weak-data-evidence",s,"Data scenarios must record product data-flow evidence such as http, bytes, sequence, record, transition, system, externalCall, recordCache, recordLlmUsage, or reconcile; ordinary Node/Vitest-style checks belong in *.contract.testkit.ts with @gfdelarue/testkit/node-vitest/contract"),c&&n(a,"vitest-shaped-data-suite",s,"This dataScenario looks like an ordinary assertion test with weak data bookkeeping; move Node-backed contract checks to *.contract.testkit.ts or model the product data flow with structured evidence")):n(a,"missing-data-evidence",s,"Data scenarios must record Testkit-owned data-flow evidence with the data context, such as http, bytes, sequence, record, transition, system, externalCall, recordCache, recordLlmUsage, or reconcile")},ThrowStatement(e){const t=x(e);t&&(t.assertion=!0)},Literal:k,TemplateLiteral:k,"Program:exit"(e){y?d.size?b||n(e,"missing-data-scenario","dataScenario","Data suites must declare at least one dataScenario(...)"):n(e,"missing-data-scenario-import","dataScenario","Data suites must import dataScenario from @gfdelarue/testkit/node-vitest/data"):n(e,"missing-data-import","@gfdelarue/testkit/node-vitest/data","Data suites must import dataScenario from @gfdelarue/testkit/node-vitest/data")}}}
@@ -1 +1 @@
1
- import{AST_NODE_TYPES as l}from"@typescript-eslint/utils";import{isPublicTestType as g}from"../../../domain/identifiers.js";import{BUILT_IN_ENGINE_CATALOG as I,expectedEngineAuthoringModule as h,invalidEngineAuthoringImport as E,requireMatchingHostImport as x}from"../../../engines/catalog.js";import{binding as y,stringValue as c}from"../policy.js";export function createEngineImportPolicy({context:u,file:i,report:m}){const n=/\.([^.]+)\.testkit\.ts$/u.exec(i)?.[1];if(!g(n))return{};const p=I.profilesForTestType(n),o=p[0];if(p.length!==1)return{};const a={engineId:o.engineId,profileId:o.id},f=h(o.id),r=(t,e)=>{if(e!==null)try{if(x(i,a,e),e!==f&&(e.startsWith("@gfdelarue/testkit/")||e.startsWith("@gfdelarue/testkit-engine-")))throw E(i,a,e)}catch(s){if(!(s instanceof Error))throw s;m(t,"engine-import",e,s.message)}};return{ImportDeclaration(t){t.importKind!=="type"&&(t.specifiers.length===0||t.specifiers.some(e=>e.type!==l.ImportSpecifier||e.importKind!=="type"))&&r(t,t.source.value)},ExportNamedDeclaration(t){t.source&&t.exportKind!=="type"&&t.specifiers.some(e=>e.exportKind!=="type")&&r(t,t.source.value)},ExportAllDeclaration(t){t.exportKind!=="type"&&r(t,t.source.value)},ImportExpression(t){r(t,c(t.source))},CallExpression(t){t.callee.type===l.Identifier&&t.callee.name==="require"&&!y(u,t.callee)?.defs.length&&r(t,c(t.arguments[0]))}}}
1
+ import{AST_NODE_TYPES as l}from"@typescript-eslint/types";import{isPublicTestType as g}from"../../../domain/identifiers.js";import{BUILT_IN_ENGINE_CATALOG as I,expectedEngineAuthoringModule as h,invalidEngineAuthoringImport as E,requireMatchingHostImport as x}from"../../../engines/catalog.js";import{binding as y,stringValue as c}from"../policy.js";export function createEngineImportPolicy({context:u,file:i,report:m}){const n=/\.([^.]+)\.testkit\.ts$/u.exec(i)?.[1];if(!g(n))return{};const p=I.profilesForTestType(n),o=p[0];if(p.length!==1)return{};const a={engineId:o.engineId,profileId:o.id},f=h(o.id),r=(t,e)=>{if(e!==null)try{if(x(i,a,e),e!==f&&(e.startsWith("@gfdelarue/testkit/")||e.startsWith("@gfdelarue/testkit-engine-")))throw E(i,a,e)}catch(s){if(!(s instanceof Error))throw s;m(t,"engine-import",e,s.message)}};return{ImportDeclaration(t){t.importKind!=="type"&&(t.specifiers.length===0||t.specifiers.some(e=>e.type!==l.ImportSpecifier||e.importKind!=="type"))&&r(t,t.source.value)},ExportNamedDeclaration(t){t.source&&t.exportKind!=="type"&&t.specifiers.some(e=>e.exportKind!=="type")&&r(t,t.source.value)},ExportAllDeclaration(t){t.exportKind!=="type"&&r(t,t.source.value)},ImportExpression(t){r(t,c(t.source))},CallExpression(t){t.callee.type===l.Identifier&&t.callee.name==="require"&&!y(u,t.callee)?.defs.length&&r(t,c(t.arguments[0]))}}}
@@ -1 +1 @@
1
- import{AST_NODE_TYPES as a}from"@typescript-eslint/utils";import{memberName as O,stringValue as u}from"../policy.js";export function createSyntaxPolicy(n,{file:s,context:c,report:l,options:r,literals:m},N){if(n==="uiSpecShape"&&s.endsWith(".ui.testkit.ts")){let t=0;return{CallExpression(e){e.callee.type===a.Identifier&&e.callee.name==="test"&&u(e.arguments[0])!==null&&(t+=1)},"Program:exit"(e){const i=c.sourceCode.text.split(/\r?\n/u).length;(i>r.ui.maxLines||t>r.ui.maxTests)&&l(e,"shape-limit",`${String(i)}:${String(t)}`,`UI spec exceeds shape limit (${String(i)}/${String(r.ui.maxLines)} lines, ${String(t)}/${String(r.ui.maxTests)} tests)`)}}}if(n==="configImports"&&s===N.find(t=>/^testkit\.config\.[cm]?[jt]s$/u.test(t)))return{ImportDeclaration(t){const e=t.source.value;(e.startsWith(".")||e.startsWith("/"))&&e.includes("__testkit__")&&l(t,"config-helper-import",e,"testkit.config.ts must not import repo-local __testkit__ helper modules")}};if(n!=="dalParallelSafety")return{};const o=s.endsWith(".dal.testkit.ts"),p=s.split("/").includes("__testkit__")&&s.split("/").includes("helpers")&&s.endsWith(".ts");return{CallExpression(t){if(!o)return;const e=t.callee;(e.type===a.Identifier&&e.name==="truncate"||e.type===a.MemberExpression&&O(e)==="truncate")&&l(t,"truncate","truncate","DAL specs must not truncate shared tables; seed namespaced rows and scope queries instead")},Property(t){if(!o)return;const e=!t.computed&&t.key.type===a.Identifier?t.key.name:u(t.key),i=u(t.value);e&&["slug","email","authProviderId"].includes(e)&&i&&m.push({field:e,value:i,file:s,line:t.loc.start.line,snippet:c.sourceCode.getText(t).split(/\r?\n/u)[0]})},Literal(t){p&&typeof t.value=="string"&&/\bON\s+CONFLICT(?:\s*\([^)]*\))?\s+DO\s+NOTHING\b/iu.test(t.value)&&l(t,"silent-conflict","ON CONFLICT DO NOTHING","Testkit helper seed code must not use ON CONFLICT DO NOTHING; collisions should fail loudly")},TemplateLiteral(t){p&&/\bON\s+CONFLICT(?:\s*\([^)]*\))?\s+DO\s+NOTHING\b/iu.test(t.quasis.map(e=>e.value.cooked??"").join("\0"))&&l(t,"silent-conflict","ON CONFLICT DO NOTHING","Testkit helper seed code must not use ON CONFLICT DO NOTHING; collisions should fail loudly")}}}
1
+ import{AST_NODE_TYPES as a}from"@typescript-eslint/types";import{memberName as O,stringValue as u}from"../policy.js";export function createSyntaxPolicy(n,{file:s,context:c,report:l,options:r,literals:m},N){if(n==="uiSpecShape"&&s.endsWith(".ui.testkit.ts")){let t=0;return{CallExpression(e){e.callee.type===a.Identifier&&e.callee.name==="test"&&u(e.arguments[0])!==null&&(t+=1)},"Program:exit"(e){const i=c.sourceCode.text.split(/\r?\n/u).length;(i>r.ui.maxLines||t>r.ui.maxTests)&&l(e,"shape-limit",`${String(i)}:${String(t)}`,`UI spec exceeds shape limit (${String(i)}/${String(r.ui.maxLines)} lines, ${String(t)}/${String(r.ui.maxTests)} tests)`)}}}if(n==="configImports"&&s===N.find(t=>/^testkit\.config\.[cm]?[jt]s$/u.test(t)))return{ImportDeclaration(t){const e=t.source.value;(e.startsWith(".")||e.startsWith("/"))&&e.includes("__testkit__")&&l(t,"config-helper-import",e,"testkit.config.ts must not import repo-local __testkit__ helper modules")}};if(n!=="dalParallelSafety")return{};const o=s.endsWith(".dal.testkit.ts"),p=s.split("/").includes("__testkit__")&&s.split("/").includes("helpers")&&s.endsWith(".ts");return{CallExpression(t){if(!o)return;const e=t.callee;(e.type===a.Identifier&&e.name==="truncate"||e.type===a.MemberExpression&&O(e)==="truncate")&&l(t,"truncate","truncate","DAL specs must not truncate shared tables; seed namespaced rows and scope queries instead")},Property(t){if(!o)return;const e=!t.computed&&t.key.type===a.Identifier?t.key.name:u(t.key),i=u(t.value);e&&["slug","email","authProviderId"].includes(e)&&i&&m.push({field:e,value:i,file:s,line:t.loc.start.line,snippet:c.sourceCode.getText(t).split(/\r?\n/u)[0]})},Literal(t){p&&typeof t.value=="string"&&/\bON\s+CONFLICT(?:\s*\([^)]*\))?\s+DO\s+NOTHING\b/iu.test(t.value)&&l(t,"silent-conflict","ON CONFLICT DO NOTHING","Testkit helper seed code must not use ON CONFLICT DO NOTHING; collisions should fail loudly")},TemplateLiteral(t){p&&/\bON\s+CONFLICT(?:\s*\([^)]*\))?\s+DO\s+NOTHING\b/iu.test(t.quasis.map(e=>e.value.cooked??"").join("\0"))&&l(t,"silent-conflict","ON CONFLICT DO NOTHING","Testkit helper seed code must not use ON CONFLICT DO NOTHING; collisions should fail loudly")}}}
@@ -1 +1 @@
1
- import{AST_NODE_TYPES as o}from"@typescript-eslint/utils";import{memberName as a,stringValue as m}from"../policy.js";const u=new Set(["vi.mock","jest.mock","mock.method"]),p=new Set(["vi.fn","vi.spyOn","vi.stubGlobal","vi.useFakeTimers","jest.fn","jest.spyOn","jest.useFakeTimers"]),b=new Set(["sinon","nock","msw","jest-mock"]),f=new Set(["fetch","http.createServer","net.createServer"]),l=new Set(["fs","node:fs","child_process","node:child_process","execa"]);export function createUnitPolicy({file:r,report:s}){if(!/\.(test|spec)\.[cm]?[jt]sx?$/u.test(r)||r.split("/").includes("__testkit__"))return{};const i=(e,t)=>{if(!t)return;const c=t.split("/"),n=t.startsWith("@")?c.slice(0,2).join("/"):c[0];b.has(n)&&s(e,"test-double-library",n,"Unit tests must not use test-double libraries"),(l.has(t)||l.has(n))&&s(e,"io-boundary-import",t,"Unit tests must not import process, network, filesystem, or runtime boundary modules")};return{ImportDeclaration(e){i(e,e.source.value)},ImportExpression(e){i(e,m(e.source))},CallExpression(e){const t=h(e.callee);u.has(t)&&s(e,"module-replacement",t,"Unit tests must not replace modules; move boundary behavior to a Testkit suite"),p.has(t)&&s(e,"function-replacement",t,"Unit tests must not replace collaborators; extract pure logic or move behavior to a Testkit suite"),f.has(t)&&s(e,"io-boundary-call",t,"Unit tests must not cross process, network, filesystem, or runtime boundaries"),t==="require"&&e.arguments.length===1&&i(e,m(e.arguments[0]))},MemberExpression(e){e.object.type===o.Identifier&&e.object.name==="process"&&a(e)==="env"&&s(e,"io-boundary-global","process.env","Unit tests must not read process-global environment state")}}}function h(r){if(r.type===o.Identifier)return r.name;if(r.type!==o.MemberExpression)return"";let s=r.object;for(;s.type===o.MemberExpression;)s=s.object;const i=a(r);return s.type===o.Identifier&&i?`${s.name}.${i}`:""}
1
+ import{AST_NODE_TYPES as o}from"@typescript-eslint/types";import{memberName as a,stringValue as m}from"../policy.js";const u=new Set(["vi.mock","jest.mock","mock.method"]),p=new Set(["vi.fn","vi.spyOn","vi.stubGlobal","vi.useFakeTimers","jest.fn","jest.spyOn","jest.useFakeTimers"]),b=new Set(["sinon","nock","msw","jest-mock"]),f=new Set(["fetch","http.createServer","net.createServer"]),l=new Set(["fs","node:fs","child_process","node:child_process","execa"]);export function createUnitPolicy({file:r,report:s}){if(!/\.(test|spec)\.[cm]?[jt]sx?$/u.test(r)||r.split("/").includes("__testkit__"))return{};const i=(e,t)=>{if(!t)return;const c=t.split("/"),n=t.startsWith("@")?c.slice(0,2).join("/"):c[0];b.has(n)&&s(e,"test-double-library",n,"Unit tests must not use test-double libraries"),(l.has(t)||l.has(n))&&s(e,"io-boundary-import",t,"Unit tests must not import process, network, filesystem, or runtime boundary modules")};return{ImportDeclaration(e){i(e,e.source.value)},ImportExpression(e){i(e,m(e.source))},CallExpression(e){const t=h(e.callee);u.has(t)&&s(e,"module-replacement",t,"Unit tests must not replace modules; move boundary behavior to a Testkit suite"),p.has(t)&&s(e,"function-replacement",t,"Unit tests must not replace collaborators; extract pure logic or move behavior to a Testkit suite"),f.has(t)&&s(e,"io-boundary-call",t,"Unit tests must not cross process, network, filesystem, or runtime boundaries"),t==="require"&&e.arguments.length===1&&i(e,m(e.arguments[0]))},MemberExpression(e){e.object.type===o.Identifier&&e.object.name==="process"&&a(e)==="env"&&s(e,"io-boundary-global","process.env","Unit tests must not read process-global environment state")}}}function h(r){if(r.type===o.Identifier)return r.name;if(r.type!==o.MemberExpression)return"";let s=r.object;for(;s.type===o.MemberExpression;)s=s.object;const i=a(r);return s.type===o.Identifier&&i?`${s.name}.${i}`:""}
@@ -22,7 +22,7 @@ export declare const DEFAULT_EXCLUDED_DIRS: string[];
22
22
  export declare const SOURCE_PATTERNS: string[];
23
23
  export declare const SOURCE_IGNORES: string[];
24
24
  export declare const DEFAULT_TESTKIT_BOUNDARY_ALLOWED: string[];
25
- export declare function collectCurrentFiles(productDir: string): Promise<string[]>;
25
+ export declare function collectCurrentFiles(productDir: string, gitAvailable: boolean): Promise<string[]>;
26
26
  export declare function collectTrackedFiles(productDir: string): Promise<string[] | null>;
27
27
  export declare function normalizePath(value: string): string;
28
28
  export declare function isObjectRecord(value: unknown): value is Record<string, unknown>;
@@ -1 +1 @@
1
- import n from"node:fs";import s from"node:path";import{minimatch as i}from"minimatch";import{glob as c}from"tinyglobby";import{TEST_TYPE_ORDER as p}from"../../domain/test-types.js";import{runManagedGitCommand as l}from"../../repo/managed-git.js";export const DEFAULT_EXCLUDED_DIRS=[".git",".next",".next-testkit",".testkit","coverage","dist","node_modules","playwright-report"],SOURCE_PATTERNS=["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}"],SOURCE_IGNORES=DEFAULT_EXCLUDED_DIRS.map(t=>`**/${t}/**`),DEFAULT_TESTKIT_BOUNDARY_ALLOWED=["testkit.config.ts","testkit.config.mts","testkit.config.mjs","testkit.config.js","**/__testkit__/**"];export async function collectCurrentFiles(t){const e=await l(t,["ls-files","--cached","--others","--exclude-standard","-z"]),o=e.exitCode===0?e.stdout.split("\0").filter(r=>r&&n.existsSync(s.join(t,r))):await c(SOURCE_PATTERNS,{cwd:t,ignore:SOURCE_IGNORES,dot:!0,followSymbolicLinks:!1});return[...new Set(o.map(normalizePath))].filter(r=>isSourceLikePath(r)&&!isExcludedRelativePath(r)).sort((r,a)=>r.localeCompare(a))}export async function collectTrackedFiles(t){const e=await l(t,["ls-files","-z"]);return e.exitCode!==0?null:e.stdout.split("\0").map(o=>normalizePath(o)).filter(Boolean).filter(o=>n.existsSync(s.join(t,o))).sort((o,r)=>o.localeCompare(r))}export function normalizePath(t){return t.split(s.sep).join("/")}export function isObjectRecord(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}export function isSourceLikePath(t){return/\.(?:[cm]?[jt]sx?|json)$/u.test(t)}export function isTypeScriptLikePath(t){return/\.[cm]?[jt]sx?$/u.test(t)}export function isTestkitSuitePath(t){return p.some(e=>t.endsWith(`.${e}.testkit.ts`))}export function isExcludedRelativePath(t){return SOURCE_IGNORES.some(e=>i(t,e,{dot:!0}))}export function matchesAnyPathPattern(t,e){return e.some(o=>i(t,o,{dot:!0}))}export function compareFindings(t,e){return t.file.localeCompare(e.file)||t.line-e.line||t.ruleId.localeCompare(e.ruleId)||t.kind.localeCompare(e.kind)||t.target.localeCompare(e.target)}export function normalizeFinding(t){return{ruleName:t.ruleName,ruleId:t.ruleId,kind:t.kind||"violation",target:t.target||"",file:normalizePath(t.file),line:t.line||1,message:t.message,...t.snippet?{snippet:t.snippet}:{}}}export function findingKey(t){return[t.file,String(t.line),t.ruleId,t.kind,t.target].join("\0")}
1
+ import i from"node:fs";import n from"node:path";import{minimatch as l}from"minimatch";import{glob as p}from"tinyglobby";import{TEST_TYPE_ORDER as u}from"../../domain/test-types.js";import{runManagedGitCommand as a}from"../../repo/managed-git.js";export const DEFAULT_EXCLUDED_DIRS=[".git",".next",".next-testkit",".testkit","coverage","dist","node_modules","playwright-report"],SOURCE_PATTERNS=["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json}"],SOURCE_IGNORES=DEFAULT_EXCLUDED_DIRS.map(t=>`**/${t}/**`),DEFAULT_TESTKIT_BOUNDARY_ALLOWED=["testkit.config.ts","testkit.config.mts","testkit.config.mjs","testkit.config.js","**/__testkit__/**"];export async function collectCurrentFiles(t,e){const o=e?await a(t,["ls-files","--cached","--others","--exclude-standard","-z"]):void 0,s=o?.exitCode===0?o.stdout.split("\0").filter(r=>r&&i.existsSync(n.join(t,r))):await p(SOURCE_PATTERNS,{cwd:t,ignore:SOURCE_IGNORES,dot:!0,followSymbolicLinks:!1});return[...new Set(s.map(normalizePath))].filter(r=>isSourceLikePath(r)&&!isExcludedRelativePath(r)).sort((r,c)=>r.localeCompare(c))}export async function collectTrackedFiles(t){const e=await a(t,["ls-files","-z"]);return e.exitCode!==0?null:e.stdout.split("\0").map(o=>normalizePath(o)).filter(Boolean).filter(o=>i.existsSync(n.join(t,o))).sort((o,s)=>o.localeCompare(s))}export function normalizePath(t){return t.split(n.sep).join("/")}export function isObjectRecord(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}export function isSourceLikePath(t){return/\.(?:[cm]?[jt]sx?|json)$/u.test(t)}export function isTypeScriptLikePath(t){return/\.[cm]?[jt]sx?$/u.test(t)}export function isTestkitSuitePath(t){return u.some(e=>t.endsWith(`.${e}.testkit.ts`))}export function isExcludedRelativePath(t){return SOURCE_IGNORES.some(e=>l(t,e,{dot:!0}))}export function matchesAnyPathPattern(t,e){return e.some(o=>l(t,o,{dot:!0}))}export function compareFindings(t,e){return t.file.localeCompare(e.file)||t.line-e.line||t.ruleId.localeCompare(e.ruleId)||t.kind.localeCompare(e.kind)||t.target.localeCompare(e.target)}export function normalizeFinding(t){return{ruleName:t.ruleName,ruleId:t.ruleId,kind:t.kind||"violation",target:t.target||"",file:normalizePath(t.file),line:t.line||1,message:t.message,...t.snippet?{snippet:t.snippet}:{}}}export function findingKey(t){return[t.file,String(t.line),t.ruleId,t.kind,t.target].join("\0")}
@@ -1 +1 @@
1
- import{resolveRuntimeConfigs as re}from"../../config/loader/dependencies.js";import{buildRuntimeGraphs as ne,claimNextTask as ie,collectSuites as ae}from"./planning.js";import{buildRunPlanningMetadata as se,buildScheduledQueue as oe}from"./scheduler/index.js";import{addTrackerError as B,buildServiceTrackers as ue,finalizeServiceResult as _,recordGraphError as le,recordTaskOutcome as ce,summarizeDbBackend as me}from"./results.js";import{loadTimings as fe,saveTimings as de,writeRunArtifact as pe}from"../../evidence/index.js";import{createRunLogRegistry as ge}from"../../evidence/logs.js";import{createOperationTimingRegistry as ke,timeOperation as L}from"../../evidence/operation-timings.js";import{createRunReporterFromListener as he}from"./run-events.js";import{createSetupOperationRegistry as Se}from"../../evidence/setup-progress.js";import{createLocalRunEnvironment as ve}from"../../environment/run-environment.js";import{readPackageMetadata as Re,safeHostname as ye,safeUsername as Ce}from"../../process/host/metadata.js";import{collectGitMetadata as we}from"../../repo/metadata.js";import{resolveExecutionConfig as Fe}from"../../config/loader/decode-execution.js";import"../../environment/run-environment.js";import{createEngineManager as Ae}from"../../engines/factory.js";import{createWorker as xe,runWorker as Ie}from"./worker-loop.js";import{ensureRequestedFilesMatch as Ee}from"./run-guards.js";import{createLiveSnapshotWriter as Te}from"./live-run.js";import{finalizeRunArtifacts as be}from"./run-finalization.js";import{buildRunArtifact as ze}from"../../evidence/reporting.js";import{createOperationReporterBridge as Me}from"./operation-progress.js";import{formatError as $,serializeError as J}from"../../kernel/error-serialization.js";export async function runAll(e,l,k,t,g=e){const a=e[0]?.productDir||process.cwd(),C=ve(a),m=Ae(a),x=new Map(g.map(n=>[n.name,n])),f=Date.now(),b=e[0]?.artifacts??null,d=[...new Set(e.flatMap(n=>Object.keys(n.testkit.serviceEnv)))].sort(),z={git:await we(a),host:{hostname:ye(),username:Ce()},testkitVersion:Re().version},h=t.onEvent?he(t.onEvent,{debug:t.debug}):null,w=ge(a),S={workerCount:0,runtimeInstanceCount:0,runtimeStats:[]},Q=t.fileNames||[];Ee(e,l,k,Q);const q=resolveRunExecutionConfig(e[0],t),N=collectServicePlans(e,x,l,k,t,q,h);h?.setServicePlans?.(N);const F=ue(N,f),Y=k.map(n=>({...n})),D={typeValues:l,suiteSelectors:Y,fileNames:Q,serviceFilter:t.serviceFilter||null,planning:null};let p=()=>{};const Z=Me(h),A=ke({onChange:n=>{Z(n),p()}}),P=Se({logRegistry:w,onChange:()=>{p()}}),O=N.filter(n=>!n.skipped);let o;const r=await C.start({keepAlive:!0,resetResultsBeforePublish:!0});p=Te({productDir:a,runId:r.runId,configs:e,trackers:F,startedAt:f,execution:q,workerState:S,selection:D,archiveConfig:b,declaredEnvironmentKeys:d,metadata:z,logRegistry:w,setupRegistry:P,operationTimingRegistry:A,reporter:h});const M={artifacts:null},ee=async(n,c)=>(r.beginFinalization(),await r.finish(n,u=>{const i=c(u);return M.artifacts={runArtifact:i.runArtifact},i},"normal"));let v,I;const U=(n,c,s)=>({productDir:a,runId:r.runId,results:n,startedAt:f,finishedAt:c,execution:q,workerCount:S.workerCount,runtimeInstanceCount:S.runtimeInstanceCount,runtimeStats:S.runtimeStats,selection:D,archiveConfig:b,declaredEnvironmentKeys:d,metadata:z,logRegistry:w,setupRegistry:P,operationTimingRegistry:A,reporter:h,runInterruptReason:r.getInterruptReason(),runStatus:s,publishRunFinalization:ee});p();let R=null;try{try{if(O.length>0){const s=fe(a),{graphs:u,queue:i}=await L(A,{name:"testkit.plan",kind:"planning",summary:"plan test execution",scope:{serviceCount:O.length}},()=>{const E=ne(O),y=oe(O,E,{timings:s});return{graphs:E,queue:y}});D.planning=se(i,u),p(),h?.setTotalFileCount?.(i.length),S.workerCount=Math.max(1,Math.min(q.taskConcurrency,i.length)),S.runtimeInstanceCount=u.reduce((E,y)=>E+y.instanceCount,0);const V=Array.from({length:S.workerCount},(E,y)=>xe(y+1,a));r.provision(u,{reporter:h,logRegistry:w,setupRegistry:P,operationTimingRegistry:A,skipSchemaSourceVerify:t.skipSchemaSourceVerify});const H=[];try{const y=(await L(A,{name:"testkit.execute",kind:"execution",summary:"execute local worker queue",scope:{workerCount:V.length,taskCount:i.length}},async()=>Promise.allSettled(V.map(j=>Ie(j,i,r,m,F,H,ie,(T,G,K,W)=>{ce(T,G,K,W),p()},(T,G,K,W,te)=>{le(T,G,K,W,te),p()},h))))).flatMap(workerResultErrorsForOrchestrator);for(const j of y)for(const T of F.values())T.skipped||B(T,j);y.length>0&&p(),S.runtimeStats=r.getStats(),p()}finally{await L(A,{name:"testkit.cleanup",kind:"cleanup",summary:"stop managed runtimes and services"},()=>r.cleanupAll())}de(a,s,H)}}catch(s){R=J(s);const u=R.message||$(s);for(const i of F.values())i.skipped||B(i,u);p()}o=R===null?0:r.isStopRequested()?130:1,I=Date.now(),v=e.map(s=>_(F.get(s.name),f,I));const n=resolveRunGateFailure(v,t.allowEmpty===!0);await L(A,{name:"testkit.collect",kind:"collection",summary:"close and collect run logs"},()=>w.closeAll());const c=await be(U(v,I,r.isStopRequested()?"interrupted":R===null&&n===null?null:"failed"));return R!==null&&(o=1),v.some(s=>s.failed||s.infraFailed)&&(o=1),n!==null&&(o=1),c.shouldFailArchivePublication&&(o=1),c.shouldFailArchiveCleanup&&(o=1),(c.runArtifact.run.status==="interrupted"||r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted")&&(o=Math.max(o,130)),{runArtifact:c.runArtifact,results:v,exitCode:o,...R===null?{}:{failure:R}}}catch(n){const c=J(n),s=c.message||$(n);for(const i of F.values())i.skipped||B(i,s);p(),r.beginFinalization(),o=r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted"?130:1,I=Date.now(),v=e.map(i=>_(F.get(i.name),f,I)),await X(w);let u=M.artifacts;if(u===null)try{u=await qe(U(v,I,r.isStopRequested()?"interrupted":"failed"),r,M,c)}catch{if(M.artifacts===null)throw n;u=M.artifacts}return(u.runArtifact.run.status==="interrupted"||r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted")&&(o=Math.max(o,130)),{runArtifact:u.runArtifact,results:v,exitCode:o,failure:R??c}}finally{r.dispose(),await X(w)}}async function qe(e,l,k,t){return l.finish("failed",g=>{const a=g.artifactStatus==="interrupted"?g:{artifactStatus:"failed",interruptReason:null},C=e.declaredEnvironmentKeys??[],m=ze({productDir:e.productDir,runId:e.runId,results:e.results,startedAt:e.startedAt,finishedAt:e.finishedAt,execution:e.execution,workerCount:e.workerCount,runtimeInstanceCount:e.runtimeInstanceCount,runtimeStats:e.runtimeStats,typeValues:e.selection.typeValues,suiteSelectors:e.selection.suiteSelectors,fileNames:e.selection.fileNames,serviceFilter:e.selection.serviceFilter,planning:e.selection.planning??null,archiveConfig:e.archiveConfig,declaredEnvironmentKeys:C,metadata:e.metadata,summarizeDbBackend:me,serviceLogs:e.logRegistry.listServiceLogs(),setupLogs:e.logRegistry.listSetupLogs(),setupOperations:e.setupRegistry.listOperations(),operationTimings:e.operationTimingRegistry?.listOperations()??[],runInterruptReason:a.interruptReason,runStatus:a.artifactStatus});m.finalizationFailure={...t};const f={runArtifact:pe(e.productDir,m,{archiveConfig:e.archiveConfig,declaredEnvironmentKeys:C})};return k.artifacts=f,f},"recovery")}async function X(e){try{await e.closeAll()}catch{}}export function workerResultErrorsForOrchestrator(e){return e.status==="rejected"?[$(e.reason)]:[...new Set(e.value.unpersistedErrors)]}export function resolveRunGateFailure(e,l=!1){return e.some(t=>t.failed)?"test failure":e.some(t=>t.infraFailed)?"test infrastructure failure":e.some(t=>t.notRunFileCount>0)?"one or more required test files did not run":e.some(t=>t.skippedFileCount>0)?"one or more selected test files were skipped":e.reduce((t,g)=>t+g.completedFileCount,0)===0&&!l?"no runnable test files executed":null}export function resolveRunExecutionConfig(e,l){return Fe({cli:{fileTimeoutSeconds:l.fileTimeoutSeconds,taskConcurrency:l.taskConcurrency},repo:e?{fileTimeoutSeconds:e.testkit.execution.fileTimeoutSeconds,taskConcurrency:e.testkit.execution.taskConcurrency}:void 0})}export function collectServicePlans(e,l,k,t,g,a,C=null){return e.map(m=>{const x=ae(m,k,t,g.fileNames||[],g);if(x.length===0)return C?.serviceSkipped?.(m,`no matching files (types=${k.join(",")||"all"} suites=${t.map(d=>d.raw).join(",")||"all"} files=${(g.fileNames||[]).join(",")||"all"})`),{config:m,execution:a,skipped:!0,suites:[],runtimeConfigs:[],runtimeNames:[],runtimeKey:null};for(const d of x)for(const z of d.skippedFiles)C?.plannedSkip?.({serviceName:m.name,testType:d.testType,engineId:d.engineId,profileId:d.profileId,file:z.path,reason:z.reason});const f=re(m,l),b=f.map(d=>d.name).sort();return{config:m,execution:a,skipped:!1,suites:x,runtimeConfigs:f,runtimeNames:b,runtimeKey:b.join("|")}})}
1
+ import{resolveRuntimeConfigs as re}from"../../config/loader/dependencies.js";import{buildRuntimeGraphs as ne,claimNextTask as ie,collectSuites as ae}from"./planning.js";import{buildRunPlanningMetadata as se,buildScheduledQueue as oe}from"./scheduler/index.js";import{addTrackerError as B,buildServiceTrackers as ue,finalizeServiceResult as _,recordGraphError as le,recordTaskOutcome as ce,summarizeDbBackend as me}from"./results.js";import{loadTimings as fe,saveTimings as de,writeRunArtifact as pe}from"../../evidence/index.js";import{createRunLogRegistry as ge}from"../../evidence/logs.js";import{createOperationTimingRegistry as ke,timeOperation as L}from"../../evidence/operation-timings.js";import{createRunReporterFromListener as he}from"./run-events.js";import{createSetupOperationRegistry as Se}from"../../evidence/setup-progress.js";import{createLocalRunEnvironment as ve}from"../../environment/run-environment.js";import{readPackageMetadata as Re,safeHostname as ye,safeUsername as Ce}from"../../process/host/metadata.js";import{collectGitMetadata as we}from"../../repo/metadata.js";import{resolveExecutionConfig as Fe}from"../../config/loader/decode-execution.js";import"../../environment/run-environment.js";import{createEngineManager as Ae}from"../../engines/factory.js";import{createWorker as xe,runWorker as Ie}from"./worker-loop.js";import{ensureRequestedFilesMatch as Ee}from"./run-guards.js";import{createLiveSnapshotWriter as Te}from"./live-run.js";import{finalizeRunArtifacts as be}from"./run-finalization.js";import{buildRunArtifact as ze}from"../../evidence/reporting.js";import{createOperationReporterBridge as Me}from"./operation-progress.js";import{formatError as $,serializeError as J}from"../../kernel/error-serialization.js";export async function runAll(e,l,g,t,p=e){const k=t.fileNames||[];Ee(e,l,g,k);const a=e[0]?.productDir||process.cwd(),m=ve(a),I=Ae(a),w=new Map(p.map(n=>[n.name,n])),h=Date.now(),f=e[0]?.artifacts??null,z=[...new Set(e.flatMap(n=>Object.keys(n.testkit.serviceEnv)))].sort(),Q={git:await we(a),host:{hostname:ye(),username:Ce()},testkitVersion:Re().version},S=t.onEvent?he(t.onEvent,{debug:t.debug}):null,F=ge(a),v={workerCount:0,runtimeInstanceCount:0,runtimeStats:[]},q=resolveRunExecutionConfig(e[0],t),N=collectServicePlans(e,w,l,g,t,q,S);S?.setServicePlans?.(N);const A=ue(N,h),Y=g.map(n=>({...n})),D={typeValues:l,suiteSelectors:Y,fileNames:k,serviceFilter:t.serviceFilter||null,planning:null};let d=()=>{};const Z=Me(S),x=ke({onChange:n=>{Z(n),d()}}),P=Se({logRegistry:F,onChange:()=>{d()}}),O=N.filter(n=>!n.skipped);let o;const r=await m.start({keepAlive:!0,resetResultsBeforePublish:!0});d=Te({productDir:a,runId:r.runId,configs:e,trackers:A,startedAt:h,execution:q,workerState:v,selection:D,archiveConfig:f,declaredEnvironmentKeys:z,metadata:Q,logRegistry:F,setupRegistry:P,operationTimingRegistry:x,reporter:S});const M={artifacts:null},ee=async(n,c)=>(r.beginFinalization(),await r.finish(n,u=>{const i=c(u);return M.artifacts={runArtifact:i.runArtifact},i},"normal"));let R,E;const U=(n,c,s)=>({productDir:a,runId:r.runId,results:n,startedAt:h,finishedAt:c,execution:q,workerCount:v.workerCount,runtimeInstanceCount:v.runtimeInstanceCount,runtimeStats:v.runtimeStats,selection:D,archiveConfig:f,declaredEnvironmentKeys:z,metadata:Q,logRegistry:F,setupRegistry:P,operationTimingRegistry:x,reporter:S,runInterruptReason:r.getInterruptReason(),runStatus:s,publishRunFinalization:ee});d();let y=null;try{try{if(O.length>0){const s=fe(a),{graphs:u,queue:i}=await L(x,{name:"testkit.plan",kind:"planning",summary:"plan test execution",scope:{serviceCount:O.length}},()=>{const T=ne(O),C=oe(O,T,{timings:s});return{graphs:T,queue:C}});D.planning=se(i,u),d(),S?.setTotalFileCount?.(i.length),v.workerCount=Math.max(1,Math.min(q.taskConcurrency,i.length)),v.runtimeInstanceCount=u.reduce((T,C)=>T+C.instanceCount,0);const V=Array.from({length:v.workerCount},(T,C)=>xe(C+1,a));r.provision(u,{reporter:S,logRegistry:F,setupRegistry:P,operationTimingRegistry:x,skipSchemaSourceVerify:t.skipSchemaSourceVerify});const H=[];try{const C=(await L(x,{name:"testkit.execute",kind:"execution",summary:"execute local worker queue",scope:{workerCount:V.length,taskCount:i.length}},async()=>Promise.allSettled(V.map(j=>Ie(j,i,r,I,A,H,ie,(b,G,K,W)=>{ce(b,G,K,W),d()},(b,G,K,W,te)=>{le(b,G,K,W,te),d()},S))))).flatMap(workerResultErrorsForOrchestrator);for(const j of C)for(const b of A.values())b.skipped||B(b,j);C.length>0&&d(),v.runtimeStats=r.getStats(),d()}finally{await L(x,{name:"testkit.cleanup",kind:"cleanup",summary:"stop managed runtimes and services"},()=>r.cleanupAll())}de(a,s,H)}}catch(s){y=J(s);const u=y.message||$(s);for(const i of A.values())i.skipped||B(i,u);d()}o=y===null?0:r.isStopRequested()?130:1,E=Date.now(),R=e.map(s=>_(A.get(s.name),h,E));const n=resolveRunGateFailure(R,t.allowEmpty===!0);await L(x,{name:"testkit.collect",kind:"collection",summary:"close and collect run logs"},()=>F.closeAll());const c=await be(U(R,E,r.isStopRequested()?"interrupted":y===null&&n===null?null:"failed"));return y!==null&&(o=1),R.some(s=>s.failed||s.infraFailed)&&(o=1),n!==null&&(o=1),c.shouldFailArchivePublication&&(o=1),c.shouldFailArchiveCleanup&&(o=1),(c.runArtifact.run.status==="interrupted"||r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted")&&(o=Math.max(o,130)),{runArtifact:c.runArtifact,results:R,exitCode:o,...y===null?{}:{failure:y}}}catch(n){const c=J(n),s=c.message||$(n);for(const i of A.values())i.skipped||B(i,s);d(),r.beginFinalization(),o=r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted"?130:1,E=Date.now(),R=e.map(i=>_(A.get(i.name),h,E)),await X(F);let u=M.artifacts;if(u===null)try{u=await qe(U(R,E,r.isStopRequested()?"interrupted":"failed"),r,M,c)}catch{if(M.artifacts===null)throw n;u=M.artifacts}return(u.runArtifact.run.status==="interrupted"||r.isStopRequested()||r.committedFinalization?.artifactStatus==="interrupted")&&(o=Math.max(o,130)),{runArtifact:u.runArtifact,results:R,exitCode:o,failure:y??c}}finally{r.dispose(),await X(F)}}async function qe(e,l,g,t){return l.finish("failed",p=>{const k=p.artifactStatus==="interrupted"?p:{artifactStatus:"failed",interruptReason:null},a=e.declaredEnvironmentKeys??[],m=ze({productDir:e.productDir,runId:e.runId,results:e.results,startedAt:e.startedAt,finishedAt:e.finishedAt,execution:e.execution,workerCount:e.workerCount,runtimeInstanceCount:e.runtimeInstanceCount,runtimeStats:e.runtimeStats,typeValues:e.selection.typeValues,suiteSelectors:e.selection.suiteSelectors,fileNames:e.selection.fileNames,serviceFilter:e.selection.serviceFilter,planning:e.selection.planning??null,archiveConfig:e.archiveConfig,declaredEnvironmentKeys:a,metadata:e.metadata,summarizeDbBackend:me,serviceLogs:e.logRegistry.listServiceLogs(),setupLogs:e.logRegistry.listSetupLogs(),setupOperations:e.setupRegistry.listOperations(),operationTimings:e.operationTimingRegistry?.listOperations()??[],runInterruptReason:k.interruptReason,runStatus:k.artifactStatus});m.finalizationFailure={...t};const w={runArtifact:pe(e.productDir,m,{archiveConfig:e.archiveConfig,declaredEnvironmentKeys:a})};return g.artifacts=w,w},"recovery")}async function X(e){try{await e.closeAll()}catch{}}export function workerResultErrorsForOrchestrator(e){return e.status==="rejected"?[$(e.reason)]:[...new Set(e.value.unpersistedErrors)]}export function resolveRunGateFailure(e,l=!1){return e.some(t=>t.failed)?"test failure":e.some(t=>t.infraFailed)?"test infrastructure failure":e.some(t=>t.notRunFileCount>0)?"one or more required test files did not run":e.some(t=>t.skippedFileCount>0)?"one or more selected test files were skipped":e.reduce((t,p)=>t+p.completedFileCount,0)===0&&!l?"no runnable test files executed":null}export function resolveRunExecutionConfig(e,l){return Fe({cli:{fileTimeoutSeconds:l.fileTimeoutSeconds,taskConcurrency:l.taskConcurrency},repo:e?{fileTimeoutSeconds:e.testkit.execution.fileTimeoutSeconds,taskConcurrency:e.testkit.execution.taskConcurrency}:void 0})}export function collectServicePlans(e,l,g,t,p,k,a=null){return e.map(m=>{const I=ae(m,g,t,p.fileNames||[],p);if(I.length===0)return a?.serviceSkipped?.(m,`no matching files (types=${g.join(",")||"all"} suites=${t.map(f=>f.raw).join(",")||"all"} files=${(p.fileNames||[]).join(",")||"all"})`),{config:m,execution:k,skipped:!0,suites:[],runtimeConfigs:[],runtimeNames:[],runtimeKey:null};for(const f of I)for(const z of f.skippedFiles)a?.plannedSkip?.({serviceName:m.name,testType:f.testType,engineId:f.engineId,profileId:f.profileId,file:z.path,reason:z.reason});const w=re(m,l),h=w.map(f=>f.name).sort();return{config:m,execution:k,skipped:!1,suites:I,runtimeConfigs:w,runtimeNames:h,runtimeKey:h.join("|")}})}
@@ -1 +1 @@
1
- import{loadManagedConfigs as u}from"../../config/loader/managed-configs.js";import{parseFileTimeoutOption as d}from"../../domain/file-timeout.js";import{parseSuiteOption as y,parseTypeOption as S}from"../../domain/suite-selection.js";import{resolveRequestedFiles as k}from"../file-selection.js";import{parseTaskConcurrencyOptions as w}from"../../config/loader/decode-execution.js";export async function buildRunRequest(e,s=null,i=process.cwd(),c=process.cwd()){const{allConfigs:o,configs:l}=await u({dir:e.dir,service:e.service}),t=w(e.taskConcurrency,e.workers),n=e.fileTimeoutSeconds==null?null:d(e.fileTimeoutSeconds),a=S(e.type,s),m=y(e.suite),f=e.file??[],r=o[0]?.productDir||i,p=k(f,r,c);return{allConfigs:o,configs:l,productDir:r,typeValues:a,suiteSelectors:m,runOptions:{allowEmpty:e.allowEmpty,fileNames:p,taskConcurrency:t,fileTimeoutSeconds:n,serviceFilter:e.service||null,ignoreSkipRules:e.ignoreSkipRules,skipSchemaSourceVerify:e.skipSchemaSourceVerify}}}
1
+ import{resolveProductDir as u}from"../../process/files/product-dir.js";import{loadManagedConfigs as d}from"../../config/loader/managed-configs.js";import{parseFileTimeoutOption as S}from"../../domain/file-timeout.js";import{parseSuiteOption as y,parseTypeOption as k}from"../../domain/suite-selection.js";import{resolveRequestedFiles as v}from"../file-selection.js";import{parseTaskConcurrencyOptions as w}from"../../config/loader/decode-execution.js";export async function buildRunRequest(e,c=null,l=process.cwd(),t=process.cwd()){const n=w(e.taskConcurrency,e.workers),a=e.fileTimeoutSeconds==null?null:S(e.fileTimeoutSeconds),r=k(e.type,c),s=y(e.suite),m=e.file??[],o=u(l,e.dir),i=v(m,o,t),{allConfigs:f,configs:p}=await d({dir:o,service:e.service,testSelection:{service:e.service,fileNames:i,typeValues:r,suiteSelectors:s}});return{allConfigs:f,configs:p,productDir:o,typeValues:r,suiteSelectors:s,runOptions:{allowEmpty:e.allowEmpty,fileNames:i,taskConcurrency:n,fileTimeoutSeconds:a,serviceFilter:e.service||null,ignoreSkipRules:e.ignoreSkipRules,skipSchemaSourceVerify:e.skipSchemaSourceVerify}}}
@@ -1 +1 @@
1
- import{resolveProductDir as c}from"../../config/loader/paths.js";import u from"node:os";import{runManagedProcess as m}from"../../process/index.js";import{currentLinuxLibc as s,currentPlatformTarget as n}from"../../process/host/platform.js";import{RootPackageResolver as d}from"../../engines/installations/package-resolution.js";import{playwrightRegistryEnvironment as g,requireSupportedPlaywrightHost as f,resolvePlaywrightInstallation as h,resolvePlaywrightPackageFamily as p,resolvePlaywrightRegistry as w,throwIfPlaywrightProcessAborted as P}from"../../engines/installations/playwright.js";export const PLAYWRIGHT_SETUP_TIMEOUT_MS=1200*1e3;export class PlaywrightSetupError extends Error{code="TESTKIT_PLAYWRIGHT_SETUP_FAILED";constructor(e,t){super(`Playwright setup failed: ${e}`,t),this.name="PlaywrightSetupError"}}export function playwrightSetupArguments(r,e){return Object.freeze([r,"install","chromium","--no-shell",...e.withDeps?["--with-deps"]:[],...e.force?["--force"]:[]])}export async function executePlaywrightSetup(r){r.signal.throwIfAborted(),f({linuxLibc:s(),target:n()});const e=Object.freeze({...r.ambientEnvironment??process.env}),t=r.configuredRegistry??e.PLAYWRIGHT_BROWSERS_PATH,a=p(new d),l=w({ambientEnvironment:e,configuredValue:t,homeDirectory:u.homedir(),platform:n().platform,productRoot:r.productRoot}),o=await m({args:playwrightSetupArguments(a.cli,r),cwd:r.productRoot,displayLabel:"Playwright Chromium setup",env:g(e,l),executable:process.execPath,signal:r.signal,timeoutMs:PLAYWRIGHT_SETUP_TIMEOUT_MS}),i=y(o);if(P(r.signal,o,i),i!==null)throw new PlaywrightSetupError(i.message,{cause:i});return h({productRoot:r.productRoot,signal:r.signal},{ambientEnvironment:e,configuredRegistry:t})}function y(r){if(r.terminationError!==null)return r.terminationError;if(r.error!==null)return r.error;if(r.timedOut)return new Error(`Chromium installer exceeded ${PLAYWRIGHT_SETUP_TIMEOUT_MS.toString()} milliseconds`);if(r.cancelled)return new Error("Chromium installer was cancelled");if(r.outputLimitExceeded)return new Error("Chromium installer exceeded the managed output limit");if(r.exitCode!==0){const e=r.stderr.trim();return new Error(`Chromium installer ${r.exitCode===null?`terminated by ${String(r.exitSignal)}`:`exited with code ${r.exitCode.toString()}`}${e.length===0?"":`: ${e}`}`)}return null}export function setupPlaywright(r){return executePlaywrightSetup({...r,productRoot:c(process.cwd(),r.dir)})}
1
+ import{resolveProductDir as m}from"../../config/loader/paths.js";import g from"node:os";import{recordWork as l}from"../../kernel/work-metrics.js";import{runManagedProcess as d}from"../../process/index.js";import{currentLinuxLibc as h,currentPlatformTarget as c}from"../../process/host/platform.js";import{RootPackageResolver as f}from"../../engines/installations/package-resolution.js";import{playwrightRegistryEnvironment as w,PlaywrightInstallationError as p,requireSupportedPlaywrightHost as y,resolvePlaywrightInstallation as P,resolvePlaywrightPackageFamily as E,resolvePlaywrightRegistry as x,throwIfPlaywrightProcessAborted as R}from"../../engines/installations/playwright.js";export const PLAYWRIGHT_SETUP_TIMEOUT_MS=1200*1e3;export class PlaywrightSetupError extends Error{code="TESTKIT_PLAYWRIGHT_SETUP_FAILED";constructor(e,t){super(`Playwright setup failed: ${e}`,t),this.name="PlaywrightSetupError"}}export function playwrightSetupArguments(r,e){return Object.freeze([r,"install","chromium","--no-shell",...e.withDeps?["--with-deps"]:[],...e.force?["--force"]:[]])}export async function executePlaywrightSetup(r){r.signal.throwIfAborted(),y({linuxLibc:h(),target:c()});const e=Object.freeze({...r.ambientEnvironment??process.env}),t=r.configuredRegistry??e.PLAYWRIGHT_BROWSERS_PATH,u=E(new f),s=x({ambientEnvironment:e,configuredValue:t,homeDirectory:g.homedir(),platform:c().platform,productRoot:r.productRoot}),o=()=>(l("playwright.setup.probes"),P({productRoot:r.productRoot,signal:r.signal},{ambientEnvironment:e,configuredRegistry:t}));if(!r.force&&!r.withDeps)try{return await o()}catch(a){if(r.signal.throwIfAborted(),!(a instanceof p))throw a}l("playwright.setup.installations");const n=await d({args:playwrightSetupArguments(u.cli,r),cwd:r.productRoot,displayLabel:"Playwright Chromium setup",env:w(e,s),executable:process.execPath,signal:r.signal,timeoutMs:PLAYWRIGHT_SETUP_TIMEOUT_MS}),i=S(n);if(R(r.signal,n,i),i!==null)throw new PlaywrightSetupError(i.message,{cause:i});return o()}function S(r){if(r.terminationError!==null)return r.terminationError;if(r.error!==null)return r.error;if(r.timedOut)return new Error(`Chromium installer exceeded ${PLAYWRIGHT_SETUP_TIMEOUT_MS.toString()} milliseconds`);if(r.cancelled)return new Error("Chromium installer was cancelled");if(r.outputLimitExceeded)return new Error("Chromium installer exceeded the managed output limit");if(r.exitCode!==0){const e=r.stderr.trim();return new Error(`Chromium installer ${r.exitCode===null?`terminated by ${String(r.exitSignal)}`:`exited with code ${r.exitCode.toString()}`}${e.length===0?"":`: ${e}`}`)}return null}export function setupPlaywright(r){return executePlaywrightSetup({...r,productRoot:m(process.cwd(),r.dir)})}
@@ -1,3 +1,4 @@
1
+ import type { NormalizedServiceConfig } from "../../config/loader/index.js";
1
2
  import { type StatusModel } from "./model.js";
2
3
  export type { StatusModel } from "./model.js";
3
4
  export interface CollectServiceStatusOptions {
@@ -5,3 +6,4 @@ export interface CollectServiceStatusOptions {
5
6
  service?: null | string;
6
7
  }
7
8
  export declare function collectServiceStatus(options?: Readonly<CollectServiceStatusOptions>): Promise<StatusModel[]>;
9
+ export declare function collectConfiguredServiceStatus(allConfigs: NormalizedServiceConfig[], service?: null | string): StatusModel[];
@@ -1 +1 @@
1
- import{loadManagedConfigs as n}from"../../config/loader/managed-configs.js";import{collectStatusModel as e}from"./model.js";export async function collectServiceStatus(o={}){const{allConfigs:t,configs:a}=await n(o);return a.map(c=>e(c,{allConfigs:t}))}
1
+ import{filterConfigsByService as n,loadManagedConfigs as i}from"../../config/loader/managed-configs.js";import{collectStatusModel as r}from"./model.js";export async function collectServiceStatus(t={}){const{allConfigs:o,configs:e}=await i(t);return e.map(c=>r(c,{allConfigs:o}))}export function collectConfiguredServiceStatus(t,o=null){return n(t,o).map(e=>r(e,{allConfigs:t}))}
@@ -1,5 +1,5 @@
1
- import { type NormalizedConfigContext } from "../../config/loader/index.js";
2
- import { type RunManifest } from "../../environment/maintenance.js";
1
+ import type { NormalizedConfigContext } from "../../config/loader/index.js";
2
+ import type { RunManifest } from "../../process/runs/manifest.js";
3
3
  import { type UnknownRecord } from "../../kernel/unknown-values.js";
4
4
  type RunStatusContext = Pick<NormalizedConfigContext, "productDir">;
5
5
  export interface BuildRunStatusOptions {
@@ -1 +1 @@
1
- import c from"node:fs";import f from"node:path";import{loadConfigContext as m}from"../../config/loader/index.js";import{createEnvironmentMaintenance as d}from"../../environment/maintenance.js";import{asUnknownRecord as a,displayString as l,isUnknownRecord as p,optionalString as e}from"../../kernel/unknown-values.js";export async function collectRunStatus(t){const r=await m({dir:t.dir});return buildRunStatus(r,t)}export function buildRunStatus(t,r={}){const n=d(t.productDir).inspectRuns().filter(o=>!r.runId||o.runId===r.runId),s=A(f.join(t.productDir,".testkit","results","live.json"));if(r.full)return Promise.resolve({productDir:t.productDir,mode:"full",runs:n,live:s});const i={productDir:t.productDir,mode:"summary",runs:n.map(g),live:k(s)};return i.live&&(i.live.state=describeLiveArtifactState(i)),Promise.resolve(i)}export function latestOperation(t){if(!Array.isArray(t))return null;const r=t.filter(p);return[...r].reverse().find(n=>n.status==="running")??r.at(-1)??null}export function describeLiveArtifactState(t){const r=a(t.live),n=a(r?.run),s=l(n?.id,"unknown"),i=l(n?.status,"unknown"),o=v(t,s);return i==="running"&&!o?{runId:s,status:"stale",reason:"no active run matches this live artifact"}:{runId:s,status:i,reason:e(n?.interruptReason)}}function v(t,r){if(!r||r==="unknown")return!1;for(const n of t.runs??[])if(n.active&&y(r,n.runId))return!0;return!1}function y(t,r){if(!r)return!1;const n=typeof r=="string"?r:typeof r=="number"&&Number.isFinite(r)?r.toString():null;return n?n===t||n.startsWith(`${t}:`):!1}function A(t){try{return JSON.parse(c.readFileSync(t,"utf8"))}catch{return null}}function g(t){const r=h(t.status);return{runId:t.runId,pid:t.pid,status:t.status||"unknown",startedAt:t.startedAt||null,heartbeatAt:t.heartbeatAt||null,interruptReason:t.interruptReason||null,active:t.active,terminal:r,stale:!t.active&&!r,session:b(t.session)}}function h(t){return typeof t=="string"&&["finished","failed","interrupted"].includes(t)}function b(t){const r=a(t);return r?{kind:e(r.kind),status:e(r.status),stage:e(r.stage)}:null}function k(t){const r=a(t);if(!r)return null;const s=a(r.timings)?.operations??[],i=Array.isArray(s)?s:[],o=a(r.product),u=a(r.run);return{present:!0,schemaVersion:w(r.schemaVersion),generatedAt:e(r.generatedAt),product:o?{name:e(o.name),directory:e(o.directory)}:null,run:u?{id:e(u.id),status:e(u.status),startedAt:e(u.startedAt),finishedAt:e(u.finishedAt),durationMs:S(u.durationMs),interruptReason:e(u.interruptReason),testkitVersion:e(u.testkitVersion)}:null,summary:r.summary??null,operation:latestOperation(i),operationCount:i.length,artifactRef:".testkit/results/live.json",truncated:!0}}function S(t){const r=Number(t);return Number.isFinite(r)?r:null}function w(t){return typeof t=="number"&&Number.isFinite(t)?t:null}
1
+ import c from"node:fs";import f from"node:path";import{inspectRunManifests as d}from"../../process/runs/inspection.js";import{resolveProductDir as m}from"../../process/files/product-dir.js";import{asUnknownRecord as o,displayString as l,isUnknownRecord as p,optionalString as i}from"../../kernel/unknown-values.js";export async function collectRunStatus(t){if(t.runId){const e=m(process.cwd(),t.dir);if(!c.statSync(e).isDirectory())throw new Error(`Product directory is not a directory: ${e}`);return buildRunStatus({productDir:e},t)}const{loadConfigContext:r}=await import("../../config/loader/index.js"),n=await r({dir:t.dir});return buildRunStatus(n,t)}export function buildRunStatus(t,r={}){const n=d(t.productDir,r.runId||void 0),e=S(f.join(t.productDir,".testkit","results","live.json"));if(r.full)return Promise.resolve({productDir:t.productDir,mode:"full",runs:n,live:e});const s={productDir:t.productDir,mode:"summary",runs:n.map(A),live:b(e)};return s.live&&(s.live.state=describeLiveArtifactState(s)),Promise.resolve(s)}export function latestOperation(t){if(!Array.isArray(t))return null;const r=t.filter(p);return[...r].reverse().find(n=>n.status==="running")??r.at(-1)??null}export function describeLiveArtifactState(t){const r=o(t.live),n=o(r?.run),e=l(n?.id,"unknown"),s=l(n?.status,"unknown"),a=y(t,e);return s==="running"&&!a?{runId:e,status:"stale",reason:"no active run matches this live artifact"}:{runId:e,status:s,reason:i(n?.interruptReason)}}function y(t,r){if(!r||r==="unknown")return!1;for(const n of t.runs??[])if(n.active&&v(r,n.runId))return!0;return!1}function v(t,r){if(!r)return!1;const n=typeof r=="string"?r:typeof r=="number"&&Number.isFinite(r)?r.toString():null;return n?n===t||n.startsWith(`${t}:`):!1}function A(t){const r=g(t.status);return{runId:t.runId,pid:t.pid,status:t.status||"unknown",startedAt:t.startedAt||null,heartbeatAt:t.heartbeatAt||null,interruptReason:t.interruptReason||null,active:t.active,terminal:r,stale:!t.active&&!r,session:h(t.session)}}function g(t){return typeof t=="string"&&["finished","failed","interrupted"].includes(t)}function h(t){const r=o(t);return r?{kind:i(r.kind),status:i(r.status),stage:i(r.stage)}:null}function b(t){const r=o(t);if(!r)return null;const e=o(r.timings)?.operations??[],s=Array.isArray(e)?e:[],a=o(r.product),u=o(r.run);return{present:!0,schemaVersion:k(r.schemaVersion),generatedAt:i(r.generatedAt),product:a?{name:i(a.name),directory:i(a.directory)}:null,run:u?{id:i(u.id),status:i(u.status),startedAt:i(u.startedAt),finishedAt:i(u.finishedAt),durationMs:w(u.durationMs),interruptReason:i(u.interruptReason),testkitVersion:i(u.testkitVersion)}:null,summary:r.summary??null,operation:latestOperation(s),operationCount:s.length,artifactRef:".testkit/results/live.json",truncated:!0}}function w(t){const r=Number(t);return Number.isFinite(r)?r:null}function k(t){return typeof t=="number"&&Number.isFinite(t)?t:null}function S(t){try{return JSON.parse(c.readFileSync(t,"utf8"))}catch{return null}}
@@ -1 +1 @@
1
- import{loadConfigContext as i}from"../../config/loader/index.js";import{createEnvironmentMaintenance as u}from"../../environment/maintenance.js";import{normalizeOperationReason as a}from"../../domain/operation-reason.js";export async function stopRuns(r,n={}){return u(r).stop(n.runId?{kind:"run",runId:n.runId}:n.all?{kind:"all"}:{kind:"latest-active"},a(n.reason,"run stopped"))}export async function stopProductRuns(r,n){const{productDir:t}=await i({dir:r.dir}),e=a(r.reason,n),o=await stopRuns(t,{...r,reason:e});return{productDir:t,stoppedRuns:o}}
1
+ import s from"node:fs";import{resolveProductDir as a}from"../../process/files/product-dir.js";import{createEnvironmentMaintenance as i}from"../../environment/maintenance.js";import{normalizeOperationReason as o}from"../../domain/operation-reason.js";export async function stopRuns(n,r={}){return i(n).stop(r.runId?{kind:"run",runId:r.runId}:r.all?{kind:"all"}:{kind:"latest-active"},o(r.reason,"run stopped"))}export async function stopProductRuns(n,r){const t=a(process.cwd(),n.dir);if(!s.statSync(t).isDirectory())throw new Error(`Product directory is not a directory: ${t}`);const e=o(n.reason,r),c=await stopRuns(t,{...n,reason:e});return{productDir:t,stoppedRuns:c}}
@@ -1,4 +1,5 @@
1
1
  import type { EngineId } from "../../domain/identifiers.js";
2
+ import { type NormalizedConfigContext, type DiscoveryResult } from "../../config/loader/index.js";
2
3
  export interface TestkitTypecheckOptions {
3
4
  dir?: null | string;
4
5
  signal?: AbortSignal;
@@ -56,6 +57,7 @@ export interface NextRuntimeTypePathsInput {
56
57
  tsconfigPath: string;
57
58
  }
58
59
  export declare function runTestkitTypecheck(options?: Readonly<TestkitTypecheckOptions>): Promise<TestkitTypecheckResult>;
60
+ export declare function runTestkitTypecheckForContext(context: NormalizedConfigContext, discovery: DiscoveryResult, options?: Readonly<TestkitTypecheckOptions>): Promise<TestkitTypecheckResult>;
59
61
  export declare function requireManagedPublicPackageIdentity(manifest: unknown, packageName: string, expectedVersion: string): void;
60
62
  export declare function buildTypecheckCompilerOptions({ engineId, tsconfigPath, extendsPath, rootDir, extraPaths, libs, types, }: Readonly<BuildTypecheckCompilerOptionsInput>): TypecheckCompilerOptions;
61
63
  export declare function nextRuntimeTypePaths({ tsconfigPath, serviceDir, }: Readonly<NextRuntimeTypePathsInput>): TypeScriptPathMappings;
@@ -1,4 +1,4 @@
1
- import{attachErrorDiagnostics as S}from"../../kernel/error-serialization.js";import{isPathWithin as E}from"../../process/files/paths.js";import d from"fs";import r from"path";import{createHash as w}from"crypto";import{createRequire as j}from"module";import{fileURLToPath as _}from"url";import m from"typescript";import{NODE_VITEST_PACKAGE_REQUIREMENTS as O}from"../../engines/installations/node-vitest.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as C}from"../../engines/installations/playwright.js";import{detectNextApp as A,loadDiscoveredConfigContext as $,resolveServiceCwd as R}from"../../config/loader/index.js";import{K6_POSTGRES_DRIVER_SPECIFIER as M,K6_POSTGRES_DRIVER_TYPE_FILE as I,K6_RUNTIME_GLOBAL_TYPE_FILE as F}from"../../engines/k6/authoring-type-paths.js";import{managedHostProcessInfrastructureError as N,runManagedHostProcess as D}from"../../process/index.js";import{CONFIGURED_STEP_TESTKIT_MODULE_DIST_PATHS as L}from"../../steps/configured-step-testkit-modules.js";import{findPlaywrightConfigFile as G}from"../../engines/playwright/runtime/config-file.js";const U=600*1e3,y=r.resolve(r.dirname(_(import.meta.url)),"..","..",".."),b=j(r.join(y,"package.json")),K={...z(["@gfdelarue/testkit","@gfdelarue/testkit/config","@gfdelarue/testkit/config/build-assets","@gfdelarue/testkit/config/next-runtime-tsconfig","@gfdelarue/testkit/config/database-steps","@gfdelarue/testkit/drizzle","@gfdelarue/testkit/env","@gfdelarue/testkit/tooling/vitest"]),"@gfdelarue/testkit/k6":["engines","k6","runtime","index.d.ts"],"@gfdelarue/testkit/playwright":["engines","playwright","runtime","index.d.ts"],"@gfdelarue/testkit/node-vitest/data":["engines","node-vitest","runtime","data","index.d.ts"],"@gfdelarue/testkit/node-vitest/contract":["engines","node-vitest","runtime","contract","index.d.ts"],"@gfdelarue/testkit/workspace":["app","inspect","index.d.ts"]};function z(e){return Object.fromEntries(e.map(n=>{const t=L[n],s=t[t.length-1];return[n,[...t.slice(0,-1),`${s}.d.ts`]]}))}export class TestkitTypecheckError extends Error{result;results;constructor(n,t,s){super(`testkit typecheck failed for ${n}`),this.result=t,this.results=s,S(this,{result:t,results:s})}}export async function runTestkitTypecheck(e={}){const n=ee(e.timeoutMs),{context:t,discovery:s}=await $({dir:e.dir}),o=t.productDir,i=r.join(o,".testkit","_typecheck");d.mkdirSync(i,{recursive:!0});const a=[];if(t.configFile){const p=J({productDir:o,configFile:t.configFile,outputDir:i});a.push({label:"config",cwd:o,tsconfigPath:p})}const c=s.files.map(p=>({engineId:p.engineId,path:p.path})),u=new Set(s.files.filter(p=>p.engineId==="playwright").map(p=>p.service));for(const p of t.configs){if(!u.has(p.name))continue;const k=G(R(o,p.testkit.local?.cwd));k&&c.push({engineId:"playwright",path:k})}const g=B(o,c);for(const p of g){const k=q({...p,outputDir:i,productDir:o});a.push({label:V(p.engineId,p.scopeName),cwd:p.cwd,tsconfigPath:k})}const f=[];for(const p of a){const k=await X(p,n,e.signal);if(f.push(k),!k.ok)throw new TestkitTypecheckError(p.label,k,f)}return{ok:!0,productDir:o,results:f}}function J({productDir:e,configFile:n,outputDir:t}){const s=r.join(t,"config.tsconfig.json"),o=P(e),i={...o?{extends:l(s,o)}:{},compilerOptions:buildTypecheckCompilerOptions({tsconfigPath:s,extendsPath:o,libs:["ES2022"],rootDir:l(s,e),types:["node"]}),files:[l(s,n)],include:[],exclude:[h(s,e,"node_modules"),h(s,e,"dist"),h(s,e,".testkit")]};return d.writeFileSync(s,`${JSON.stringify(i,null,2)}
2
- `),s}function q({productDir:e,cwd:n,engineId:t,extendsPath:s,files:o,outputDir:i,scopeName:a}){const c=r.join(i,`${t}.${Z(a)}.tsconfig.json`),u=t==="playwright",g=u&&A(n),f={...s?{extends:l(c,s)}:{},compilerOptions:{...buildTypecheckCompilerOptions({engineId:t,tsconfigPath:c,extendsPath:s,libs:W(t),...g?{extraPaths:nextRuntimeTypePaths({tsconfigPath:c,serviceDir:n})}:{},types:Y(t)}),...u?{allowJs:!0,checkJs:!1}:{}},files:[...ie(t,c),...o.map(p=>l(c,p))],include:g?[...d.existsSync(r.join(n,"next-env.d.ts"))?[l(c,r.join(n,"next-env.d.ts"))]:[],h(c,n,"tests/playwright-fixtures/**/*.ts"),h(c,n,"tests/playwright-fixtures/**/*.tsx"),h(c,n,"tests/playwright-fixtures/**/*.mts"),h(c,n,".next/types/**/*.ts"),h(c,n,".next/dev/types/**/*.ts"),h(c,e,".next-testkit/**/dist/types/**/*.ts"),h(c,e,".next-testkit/**/dist/dev/types/**/*.ts")]:[],exclude:["node_modules",".next/cache",".next/dev"]};return d.writeFileSync(c,`${JSON.stringify(f,null,2)}
3
- `),c}function B(e,n){const t=new Map,s=new Map;for(const o of n){const i=r.resolve(e,o.path),a=[o.engineId,i].join("\0");s.set(a,{...o,path:i})}for(const o of s.values()){const i=o.path,a=o.engineId==="playwright"?v(r.dirname(i),e,["tsconfig.playwright.json"]):null,c=a??v(r.dirname(i),e),u=c?r.dirname(c):e,g=Q(e,u),f=`${o.engineId}\0${c??""}\0${g}`,p=t.get(f);if(p){p.files.push(i);continue}t.set(f,{cwd:u,engineId:o.engineId,extendsPath:c,files:[i,...a?H(a):[]],scopeName:g})}return[...t.values()].map(o=>({...o,files:[...new Set(o.files)].sort((i,a)=>i.localeCompare(a))})).sort((o,i)=>T(o.engineId)-T(i.engineId)||o.scopeName.localeCompare(i.scopeName))}function H(e){const n=s=>{throw new Error(`${e}: ${m.flattenDiagnosticMessageText(s.messageText,`
4
- `)}`)},t=m.getParsedCommandLineOfConfigFile(e,{},{...m.sys,onUnRecoverableConfigFileDiagnostic:n});for(const s of t?.errors??[])s.code!==18002&&s.code!==18003&&n(s);return t?.fileNames??[]}function Y(e){switch(e){case"k6":return["k6","xk6-sql"];case"node-vitest":case"playwright":return["node"]}}function W(e){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:["ES2022"]}function T(e){return["k6","node-vitest","playwright"].indexOf(e)}function V(e,n){return n==="repo"?e:`${e}:${n}`}function Q(e,n){const t=r.relative(e,n);return t===""?"repo":t.split(r.sep).join("/")}function Z(e){const n=e.replace(/[^a-zA-Z0-9_-]+/g,"-");if(n===e)return e;const t=w("sha256").update(e).digest("hex").slice(0,12);return`${n}-${t}`}function X({label:e,cwd:n,tsconfigPath:t},s,o){const i=te(n);return D({args:[...i.args,"--noEmit","-p",t],cwd:n,displayLabel:`Testkit typecheck ${e}`,env:process.env,executable:i.command,signal:o,timeoutMs:s}).then(a=>{const c=N(`Testkit typecheck ${e}`,a);if(c)throw c;return{label:e,tsconfigPath:t,ok:a.exitCode===0,exitCode:a.exitCode,stdout:a.stdout,stderr:a.stderr}})}function ee(e){const n=e??U;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Testkit typecheck timeout must be a positive safe integer in milliseconds");return n}function te(e){let n=e;for(;;){const s=r.join(n,"node_modules","typescript","bin","tsc");if(d.existsSync(s))return{command:process.execPath,args:[s]};const o=r.dirname(n);if(o===n)break;n=o}const t=r.join(y,"node_modules","typescript","bin","tsc");if(d.existsSync(t))return{command:process.execPath,args:[t]};throw new Error(`TypeScript compiler not found from ${e} upward`)}function P(e,n=["tsconfig.json","tsconfig.build.json"]){for(const t of n){const s=r.join(e,t);if(d.existsSync(s))return s}return null}function v(e,n,t){const s=r.resolve(n);let o=r.resolve(e);for(;;){if(!E(s,o))return null;const i=P(o,t);if(i)return i;if(o===s)return null;const a=r.dirname(o);if(a===o)return null;o=a}}function l(e,n){const t=r.relative(r.dirname(e),n).split(r.sep).join("/");return t.startsWith("../")||r.isAbsolute(t)?t:`./${t}`}function h(e,n,t){return r.join(l(e,n),t).split(r.sep).join("/")}function ne(e){const n={};for(const[t,s]of Object.entries(K))n[t]=[l(e,r.join(y,"dist",...s))];return n}const se=Object.freeze({packages:Object.freeze(O.filter(({authoringResolution:e})=>e==="package-exports").map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"}),oe=Object.freeze({packages:Object.freeze(C.map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"});function re(e,n){const t=ae(e);if(t===null)return{};const s={},o=l(n,r.join(y,"dist","app","typecheck","unavailable.d.ts"));for(const i of t.packages){const a=pe(i.name),c=le(i.name,a);s[`${i.name}/*`]=[o];for(const u of Object.keys(c).sort()){if(u!=="."&&!u.startsWith("./")||u.includes("*"))continue;const g=u==="."?i.name:`${i.name}${u.slice(1)}`,f=de(g,i.name,a);s[g]=[f===null?o:l(n,f)]}}return s}function ie(e,n){return e==="k6"?[l(n,r.join(y,"dist",...F))]:[]}function ce(e,n){return e==="k6"?{[M]:[l(n,r.join(y,"dist",...I))]}:{}}function ae(e){switch(e){case"node-vitest":return se;case"playwright":return oe;case"k6":case void 0:return null}}function pe(e){const n=ue(e);let t;try{t=b.resolve(`${e}/package.json`)}catch(i){throw new Error(`Testkit typecheck requires managed public package ${e}`,{cause:i})}const s=d.realpathSync.native(r.dirname(t)),o=JSON.parse(d.readFileSync(r.join(s,"package.json"),"utf8"));return requireManagedPublicPackageIdentity(o,e,n),s}export function requireManagedPublicPackageIdentity(e,n,t){if(!x(e)||e.name!==n||e.version!==t)throw new Error(`Testkit managed public package has invalid identity: expected ${n}@${t}`)}function ue(e){const n=JSON.parse(d.readFileSync(r.join(y,"package.json"),"utf8"));if(!x(n)||!x(n.dependencies))throw new Error("Testkit package has invalid dependency metadata");const t=n.dependencies[e];if(typeof t!="string"||!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(t))throw new Error(`Testkit managed public package ${e} must be an exact root dependency`);return t}function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function le(e,n){const t=JSON.parse(d.readFileSync(r.join(n,"package.json"),"utf8"));if(typeof t!="object"||t===null||Array.isArray(t)||!("name"in t)||t.name!==e||!("exports"in t)||typeof t.exports!="object"||t.exports===null||Array.isArray(t.exports))throw new Error(`Testkit managed public package has invalid exports: ${e}`);return t.exports}function de(e,n,t){const s=m.resolveModuleName(e,r.join(y,"__testkit_managed_public_types__.ts"),{module:m.ModuleKind.ESNext,moduleResolution:m.ModuleResolutionKind.Bundler,resolveJsonModule:!0,target:m.ScriptTarget.ES2022},m.sys).resolvedModule;if(s===void 0)return null;const o=d.realpathSync.native(s.resolvedFileName);if(!E(t,o))throw new Error(`Managed public package type ${e} resolved outside ${n}`);return o}export function buildTypecheckCompilerOptions({engineId:e,tsconfigPath:n,extendsPath:t,rootDir:s,extraPaths:o={},libs:i,types:a}){const u={...t?me({tsconfigPath:n,extendsPath:t}):{},...o,...ne(n),...re(e,n),...ce(e,n)};return{baseUrl:null,lib:[...i],module:"ESNext",moduleResolution:"Bundler",noEmit:!0,...s?{rootDir:s}:{},target:"ES2022",typeRoots:fe(a,n),types:[...a],paths:u}}function fe(e,n){const t=new Set;for(const s of e){if(s==="k6"||s==="xk6-sql"){const c=r.join(y,"dist","engines","k6","runtime","ambient-types");if(!d.existsSync(r.join(c,s,"index.d.ts")))throw new Error(`Testkit typecheck requires bundled ${s} authoring types; rebuild the installed Testkit package`);t.add(c);continue}const o=ge(s);let i;try{i=b.resolve(`${o}/package.json`)}catch(c){throw new Error(`Testkit typecheck requires installed type package ${o}`,{cause:c})}const a=r.dirname(i);t.add(r.dirname(a))}return[...t].map(s=>l(n,s))}function ge(e){return e==="node"||e==="k6"?`@types/${e}`:e}function me({tsconfigPath:e,extendsPath:n}){const t=m.getParsedCommandLineOfConfigFile(n,{},{...m.sys,onUnRecoverableConfigFileDiagnostic:()=>{}}),s=t?.options.paths;if(!s||Object.keys(s).length===0)return{};const o=t.options.baseUrl||(typeof t.options.pathsBasePath=="string"?t.options.pathsBasePath:r.dirname(n)),i={};for(const[a,c]of Object.entries(s))i[a]=c.map(u=>l(e,r.resolve(o,u)));return i}export function nextRuntimeTypePaths({tsconfigPath:e,serviceDir:n}){const t=he(n,[r.join("next","types.d.ts"),r.join("next","types.js")]);if(!t)return{};const s=l(e,t);return{"next/types":[s],"next/types.js":[s]}}function he(e,n){let t=r.resolve(e);for(;;){for(const o of n){const i=r.join(t,"node_modules",o);if(d.existsSync(i))return i}const s=r.dirname(t);if(s===t)return null;t=s}}
1
+ import{attachErrorDiagnostics as w}from"../../kernel/error-serialization.js";import{isPathWithin as T}from"../../process/files/paths.js";import d from"fs";import i from"path";import{createHash as j}from"crypto";import{createRequire as _}from"module";import{fileURLToPath as O}from"url";import{NODE_VITEST_PACKAGE_REQUIREMENTS as C}from"../../engines/installations/package-requirements.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as A}from"../../engines/installations/package-requirements.js";import{hasDiscoveryErrors as $,buildDiscoveryErrorFromDiagnostics as R,detectNextApp as M,loadDiscoveredConfigContext as I,resolveServiceCwd as F}from"../../config/loader/index.js";import{K6_POSTGRES_DRIVER_SPECIFIER as N,K6_POSTGRES_DRIVER_TYPE_FILE as D,K6_RUNTIME_GLOBAL_TYPE_FILE as L}from"../../engines/k6/authoring-type-paths.js";import{managedHostProcessInfrastructureError as G,runManagedHostProcess as U}from"../../process/index.js";import{CONFIGURED_STEP_TESTKIT_MODULE_DIST_PATHS as K}from"../../steps/configured-step-testkit-modules.js";import{findPlaywrightConfigFile as z}from"../../engines/playwright/runtime/config-file.js";const J=600*1e3,h=i.resolve(i.dirname(O(import.meta.url)),"..","..",".."),k=_(i.join(h,"package.json")),x=()=>k("typescript"),q={...B(["@gfdelarue/testkit","@gfdelarue/testkit/config","@gfdelarue/testkit/config/build-assets","@gfdelarue/testkit/config/next-runtime-tsconfig","@gfdelarue/testkit/config/database-steps","@gfdelarue/testkit/drizzle","@gfdelarue/testkit/env","@gfdelarue/testkit/tooling/vitest"]),"@gfdelarue/testkit/k6":["engines","k6","runtime","index.d.ts"],"@gfdelarue/testkit/playwright":["engines","playwright","runtime","index.d.ts"],"@gfdelarue/testkit/node-vitest/data":["engines","node-vitest","runtime","data","index.d.ts"],"@gfdelarue/testkit/node-vitest/contract":["engines","node-vitest","runtime","contract","index.d.ts"],"@gfdelarue/testkit/workspace":["app","inspect","index.d.ts"]};function B(e){return Object.fromEntries(e.map(n=>{const t=K[n],s=t[t.length-1];return[n,[...t.slice(0,-1),`${s}.d.ts`]]}))}export class TestkitTypecheckError extends Error{result;results;constructor(n,t,s){super(`testkit typecheck failed for ${n}`),this.result=t,this.results=s,w(this,{result:t,results:s})}}export async function runTestkitTypecheck(e={}){v(e.timeoutMs);const{context:n,discovery:t}=await I({dir:e.dir});return runTestkitTypecheckForContext(n,t,e)}export async function runTestkitTypecheckForContext(e,n,t={}){const s=v(t.timeoutMs);if($(e.discovery.diagnostics))throw R(e.discovery.diagnostics);const o=e.productDir,r=i.join(o,".testkit","_typecheck");d.mkdirSync(r,{recursive:!0});const a=[];if(e.configFile){const u=H({productDir:o,configFile:e.configFile,outputDir:r});a.push({label:"config",cwd:o,tsconfigPath:u})}const c=n.files.map(u=>({engineId:u.engineId,path:u.path})),p=new Set(n.files.filter(u=>u.engineId==="playwright").map(u=>u.service));for(const u of e.configs){if(!p.has(u.name))continue;const y=z(F(o,u.testkit.local?.cwd));y&&c.push({engineId:"playwright",path:y})}const f=W(o,c);for(const u of f){const y=Y({...u,outputDir:r,productDir:o});a.push({label:X(u.engineId,u.scopeName),cwd:u.cwd,tsconfigPath:y})}const g=[];for(const u of a){const y=await ne(u,s,t.signal);if(g.push(y),!y.ok)throw new TestkitTypecheckError(u.label,y,g)}return{ok:!0,productDir:o,results:g}}function H({productDir:e,configFile:n,outputDir:t}){const s=i.join(t,"config.tsconfig.json"),o=P(e),r={...o?{extends:l(s,o)}:{},compilerOptions:buildTypecheckCompilerOptions({tsconfigPath:s,extendsPath:o,libs:["ES2022"],rootDir:l(s,e),types:["node"]}),files:[l(s,n)],include:[],exclude:[m(s,e,"node_modules"),m(s,e,"dist"),m(s,e,".testkit")]};return d.writeFileSync(s,`${JSON.stringify(r,null,2)}
2
+ `),s}function Y({productDir:e,cwd:n,engineId:t,extendsPath:s,files:o,outputDir:r,scopeName:a}){const c=i.join(r,`${t}.${te(a)}.tsconfig.json`),p=t==="playwright",f=p&&M(n),g={...s?{extends:l(c,s)}:{},compilerOptions:{...buildTypecheckCompilerOptions({engineId:t,tsconfigPath:c,extendsPath:s,libs:Z(t),...f?{extraPaths:nextRuntimeTypePaths({tsconfigPath:c,serviceDir:n})}:{},types:Q(t)}),...p?{allowJs:!0,checkJs:!1}:{}},files:[...ae(t,c),...o.map(u=>l(c,u))],include:f?[...d.existsSync(i.join(n,"next-env.d.ts"))?[l(c,i.join(n,"next-env.d.ts"))]:[],m(c,n,"tests/playwright-fixtures/**/*.ts"),m(c,n,"tests/playwright-fixtures/**/*.tsx"),m(c,n,"tests/playwright-fixtures/**/*.mts"),m(c,n,".next/types/**/*.ts"),m(c,n,".next/dev/types/**/*.ts"),m(c,e,".next-testkit/**/dist/types/**/*.ts"),m(c,e,".next-testkit/**/dist/dev/types/**/*.ts")]:[],exclude:["node_modules",".next/cache",".next/dev"]};return d.writeFileSync(c,`${JSON.stringify(g,null,2)}
3
+ `),c}function W(e,n){const t=new Map,s=new Map;for(const o of n){const r=i.resolve(e,o.path),a=[o.engineId,r].join("\0");s.set(a,{...o,path:r})}for(const o of s.values()){const r=o.path,a=o.engineId==="playwright"?S(i.dirname(r),e,["tsconfig.playwright.json"]):null,c=a??S(i.dirname(r),e),p=c?i.dirname(c):e,f=ee(e,p),g=`${o.engineId}\0${c??""}\0${f}`,u=t.get(g);if(u){u.files.push(r);continue}t.set(g,{cwd:p,engineId:o.engineId,extendsPath:c,files:[r,...a?V(a):[]],scopeName:f})}return[...t.values()].map(o=>({...o,files:[...new Set(o.files)].sort((r,a)=>r.localeCompare(a))})).sort((o,r)=>b(o.engineId)-b(r.engineId)||o.scopeName.localeCompare(r.scopeName))}function V(e){const n=x(),t=o=>{throw new Error(`${e}: ${n.flattenDiagnosticMessageText(o.messageText,`
4
+ `)}`)},s=n.getParsedCommandLineOfConfigFile(e,{},{...n.sys,onUnRecoverableConfigFileDiagnostic:t});for(const o of s?.errors??[])o.code!==18002&&o.code!==18003&&t(o);return s?.fileNames??[]}function Q(e){switch(e){case"k6":return["k6","xk6-sql"];case"node-vitest":case"playwright":return["node"]}}function Z(e){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:["ES2022"]}function b(e){return["k6","node-vitest","playwright"].indexOf(e)}function X(e,n){return n==="repo"?e:`${e}:${n}`}function ee(e,n){const t=i.relative(e,n);return t===""?"repo":t.split(i.sep).join("/")}function te(e){const n=e.replace(/[^a-zA-Z0-9_-]+/g,"-");if(n===e)return e;const t=j("sha256").update(e).digest("hex").slice(0,12);return`${n}-${t}`}function ne({label:e,cwd:n,tsconfigPath:t},s,o){const r=se(n);return U({args:[...r.args,"--noEmit","-p",t],cwd:n,displayLabel:`Testkit typecheck ${e}`,env:process.env,executable:r.command,signal:o,timeoutMs:s}).then(a=>{const c=G(`Testkit typecheck ${e}`,a);if(c)throw c;return{label:e,tsconfigPath:t,ok:a.exitCode===0,exitCode:a.exitCode,stdout:a.stdout,stderr:a.stderr}})}function v(e){const n=e??J;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Testkit typecheck timeout must be a positive safe integer in milliseconds");return n}function se(e){let n=e;for(;;){const s=i.join(n,"node_modules","typescript","bin","tsc");if(d.existsSync(s))return{command:process.execPath,args:[s]};const o=i.dirname(n);if(o===n)break;n=o}const t=i.join(h,"node_modules","typescript","bin","tsc");if(d.existsSync(t))return{command:process.execPath,args:[t]};throw new Error(`TypeScript compiler not found from ${e} upward`)}function P(e,n=["tsconfig.json","tsconfig.build.json"]){for(const t of n){const s=i.join(e,t);if(d.existsSync(s))return s}return null}function S(e,n,t){const s=i.resolve(n);let o=i.resolve(e);for(;;){if(!T(s,o))return null;const r=P(o,t);if(r)return r;if(o===s)return null;const a=i.dirname(o);if(a===o)return null;o=a}}function l(e,n){const t=i.relative(i.dirname(e),n).split(i.sep).join("/");return t.startsWith("../")||i.isAbsolute(t)?t:`./${t}`}function m(e,n,t){return i.join(l(e,n),t).split(i.sep).join("/")}function oe(e){const n={};for(const[t,s]of Object.entries(q))n[t]=[l(e,i.join(h,"dist",...s))];return n}const re=Object.freeze({packages:Object.freeze(C.filter(({authoringResolution:e})=>e==="package-exports").map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"}),ie=Object.freeze({packages:Object.freeze(A.map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"});function ce(e,n){const t=pe(e);if(t===null)return{};const s={},o=l(n,i.join(h,"dist","app","typecheck","unavailable.d.ts"));for(const r of t.packages){const a=le(r.name),c=fe(r.name,a);s[`${r.name}/*`]=[o];for(const p of Object.keys(c).sort()){if(p!=="."&&!p.startsWith("./")||p.includes("*"))continue;const f=p==="."?r.name:`${r.name}${p.slice(1)}`,g=ge(f,r.name,a);s[f]=[g===null?o:l(n,g)]}}return s}function ae(e,n){return e==="k6"?[l(n,i.join(h,"dist",...L))]:[]}function ue(e,n){return e==="k6"?{[N]:[l(n,i.join(h,"dist",...D))]}:{}}function pe(e){switch(e){case"node-vitest":return re;case"playwright":return ie;case"k6":case void 0:return null}}function le(e){const n=de(e);let t;try{t=k.resolve(`${e}/package.json`)}catch(r){throw new Error(`Testkit typecheck requires managed public package ${e}`,{cause:r})}const s=d.realpathSync.native(i.dirname(t)),o=JSON.parse(d.readFileSync(i.join(s,"package.json"),"utf8"));return requireManagedPublicPackageIdentity(o,e,n),s}export function requireManagedPublicPackageIdentity(e,n,t){if(!E(e)||e.name!==n||e.version!==t)throw new Error(`Testkit managed public package has invalid identity: expected ${n}@${t}`)}function de(e){const n=JSON.parse(d.readFileSync(i.join(h,"package.json"),"utf8"));if(!E(n)||!E(n.dependencies))throw new Error("Testkit package has invalid dependency metadata");const t=n.dependencies[e];if(typeof t!="string"||!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(t))throw new Error(`Testkit managed public package ${e} must be an exact root dependency`);return t}function E(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function fe(e,n){const t=JSON.parse(d.readFileSync(i.join(n,"package.json"),"utf8"));if(typeof t!="object"||t===null||Array.isArray(t)||!("name"in t)||t.name!==e||!("exports"in t)||typeof t.exports!="object"||t.exports===null||Array.isArray(t.exports))throw new Error(`Testkit managed public package has invalid exports: ${e}`);return t.exports}function ge(e,n,t){const s=x(),o=s.resolveModuleName(e,i.join(h,"__testkit_managed_public_types__.ts"),{module:s.ModuleKind.ESNext,moduleResolution:s.ModuleResolutionKind.Bundler,resolveJsonModule:!0,target:s.ScriptTarget.ES2022},s.sys).resolvedModule;if(o===void 0)return null;const r=d.realpathSync.native(o.resolvedFileName);if(!T(t,r))throw new Error(`Managed public package type ${e} resolved outside ${n}`);return r}export function buildTypecheckCompilerOptions({engineId:e,tsconfigPath:n,extendsPath:t,rootDir:s,extraPaths:o={},libs:r,types:a}){const p={...t?ye({tsconfigPath:n,extendsPath:t}):{},...o,...oe(n),...ce(e,n),...ue(e,n)};return{baseUrl:null,lib:[...r],module:"ESNext",moduleResolution:"Bundler",noEmit:!0,...s?{rootDir:s}:{},target:"ES2022",typeRoots:me(a,n),types:[...a],paths:p}}function me(e,n){const t=new Set;for(const s of e){if(s==="k6"||s==="xk6-sql"){const c=i.join(h,"dist","engines","k6","runtime","ambient-types");if(!d.existsSync(i.join(c,s,"index.d.ts")))throw new Error(`Testkit typecheck requires bundled ${s} authoring types; rebuild the installed Testkit package`);t.add(c);continue}const o=he(s);let r;try{r=k.resolve(`${o}/package.json`)}catch(c){throw new Error(`Testkit typecheck requires installed type package ${o}`,{cause:c})}const a=i.dirname(r);t.add(i.dirname(a))}return[...t].map(s=>l(n,s))}function he(e){return e==="node"||e==="k6"?`@types/${e}`:e}function ye({tsconfigPath:e,extendsPath:n}){const t=x(),s=t.getParsedCommandLineOfConfigFile(n,{},{...t.sys,onUnRecoverableConfigFileDiagnostic:()=>{}}),o=s?.options.paths;if(!o||Object.keys(o).length===0)return{};const r=s.options.baseUrl||(typeof s.options.pathsBasePath=="string"?s.options.pathsBasePath:i.dirname(n)),a={};for(const[c,p]of Object.entries(o))a[c]=p.map(f=>l(e,i.resolve(r,f)));return a}export function nextRuntimeTypePaths({tsconfigPath:e,serviceDir:n}){const t=ke(n,[i.join("next","types.d.ts"),i.join("next","types.js")]);if(!t)return{};const s=l(e,t);return{"next/types":[s],"next/types.js":[s]}}function ke(e,n){let t=i.resolve(e);for(;;){for(const o of n){const r=i.join(t,"node_modules",o);if(d.existsSync(r))return r}const s=i.dirname(t);if(s===t)return null;t=s}}