@gfdelarue/testkit 0.3.3 → 0.3.5

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 (127) hide show
  1. package/README.md +11 -0
  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/config/loader/validation.js +1 -1
  50. package/dist/database/index.d.ts +1 -3
  51. package/dist/database/index.js +1 -1
  52. package/dist/database/lifecycle-cleanup.d.ts +7 -0
  53. package/dist/database/lifecycle-cleanup.js +1 -0
  54. package/dist/database/local-postgres.js +1 -1
  55. package/dist/database/schema-source.js +3 -3
  56. package/dist/engines/adapters/evidence-files.js +1 -1
  57. package/dist/engines/catalog.d.ts +3 -8
  58. package/dist/engines/catalog.js +1 -1
  59. package/dist/engines/definitions.d.ts +8 -1
  60. package/dist/engines/definitions.js +1 -1
  61. package/dist/engines/generated-performance.d.ts +1 -0
  62. package/dist/engines/generated-performance.js +12 -0
  63. package/dist/engines/installations/node-vitest.d.ts +8 -22
  64. package/dist/engines/installations/node-vitest.js +1 -1
  65. package/dist/engines/installations/package-requirements.d.ts +28 -0
  66. package/dist/engines/installations/package-requirements.js +1 -0
  67. package/dist/engines/installations/playwright.d.ts +1 -11
  68. package/dist/engines/installations/playwright.js +1 -1
  69. package/dist/engines/k6/adapter/repository-configuration.d.ts +1 -1
  70. package/dist/engines/k6/adapter/repository-configuration.js +1 -1
  71. package/dist/engines/managed-module-resolution.js +111 -21
  72. package/dist/engines/manager.d.ts +1 -0
  73. package/dist/engines/manager.js +1 -1
  74. package/dist/engines/node-vitest/adapter/adapter.d.ts +1 -1
  75. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  76. package/dist/engines/node-vitest/adapter/consumer-typescript-paths.js +1 -1
  77. package/dist/engines/node-vitest/adapter/resolution.d.ts +1 -1
  78. package/dist/engines/node-vitest/adapter/resolution.js +1 -1
  79. package/dist/engines/node-vitest/adapter/workspace.js +33 -9
  80. package/dist/engines/node-vitest/runtime/runtime-input.d.ts +1 -0
  81. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  82. package/dist/engines/runtime-input-file.d.ts +3 -1
  83. package/dist/engines/runtime-input-file.js +1 -1
  84. package/dist/environment/lifecycle-controller.d.ts +1 -2
  85. package/dist/environment/lifecycle-controller.js +1 -1
  86. package/dist/environment/lifecycle.js +1 -1
  87. package/dist/environment/maintenance.d.ts +1 -1
  88. package/dist/environment/maintenance.js +1 -1
  89. package/dist/environment/ready-environment.d.ts +1 -1
  90. package/dist/environment/ready-environment.js +1 -1
  91. package/dist/environment/runtime-contexts.d.ts +1 -1
  92. package/dist/environment/runtime-contexts.js +1 -1
  93. package/dist/environment/runtime-preparation.js +1 -1
  94. package/dist/environment/services.js +1 -1
  95. package/dist/environment/template.d.ts +1 -1
  96. package/dist/environment/template.js +1 -1
  97. package/dist/evidence/bundles.js +1 -1
  98. package/dist/evidence/read-run.js +1 -1
  99. package/dist/evidence/reading.d.ts +1 -1
  100. package/dist/evidence/reading.js +1 -1
  101. package/dist/evidence/storage.js +2 -2
  102. package/dist/kernel/error-serialization.js +1 -1
  103. package/dist/kernel/work-metrics.d.ts +6 -0
  104. package/dist/kernel/work-metrics.js +1 -0
  105. package/dist/process/capture-output.js +1 -1
  106. package/dist/process/execa-bundle.json +1 -1
  107. package/dist/process/files/product-dir.d.ts +1 -0
  108. package/dist/process/files/product-dir.js +1 -0
  109. package/dist/process/locks.js +4 -4
  110. package/dist/process/readiness.js +1 -1
  111. package/dist/process/runs/inspection.d.ts +5 -0
  112. package/dist/process/runs/inspection.js +1 -0
  113. package/dist/process/runs/manifest.d.ts +1 -0
  114. package/dist/process/runs/manifest.js +1 -1
  115. package/dist/process/service-processes.d.ts +1 -1
  116. package/dist/process/service-processes.js +1 -1
  117. package/dist/steps/environment.d.ts +6 -0
  118. package/dist/steps/environment.js +1 -1
  119. package/dist/steps/index.d.ts +1 -1
  120. package/dist/steps/index.js +1 -1
  121. package/dist/toolchains/index.js +2 -2
  122. package/docs/engine-architecture.md +4 -1
  123. package/docs/test-types.md +9 -0
  124. package/package.json +2 -1
  125. package/testkit-config-schema.md +52 -0
  126. package/dist/cli/renderers/artifact-preview.d.ts +0 -4
  127. package/dist/cli/renderers/artifact-preview.js +0 -1
package/README.md CHANGED
@@ -645,6 +645,10 @@ testkit artifacts --run <run-id> --verify --json
645
645
  testkit artifacts --run <run-id> path/to/test.testkit.ts --json
646
646
  ```
647
647
 
648
+ The default artifact list shows names, kinds, summaries, and paths without
649
+ reading payload files. Select a file with `--format text` to read its contents,
650
+ or use `--json` for structured output including the selected payloads.
651
+
648
652
  Programmatic consumers can import `listRunBundles`, `loadRunBundleArtifact`,
649
653
  `loadRunBundleManifest`, and `verifyRunBundle` from
650
654
  `@gfdelarue/testkit/workspace`.
@@ -1068,6 +1072,13 @@ platform's host address.
1068
1072
  - template fingerprints are derived automatically from env files, source schema
1069
1073
  cache, migrate/seed config, and repo contents
1070
1074
 
1075
+ The service process receives its provisioned database as `DATABASE_URL`. Tasks
1076
+ get it only through declared `env` values: use the `{dbUrl}`, `{dbHost}`,
1077
+ `{dbPort}`, `{dbName}`, `{dbUser}`, `{dbPassword}`, and `{dbSsl}` tokens, with
1078
+ `:serviceName` to read the database of a service listed in `dependsOn`. Testkit
1079
+ resolves them from the database provisioned for the current run. They are
1080
+ env-only; see [`env`](testkit-config-schema.md#env).
1081
+
1071
1082
  `db schema refresh` forces a source database dump into the `.testkit` source
1072
1083
  schema cache. `db schema verify` prepares local templates and verifies local
1073
1084
  replay against the cached/refreshed source schema. `--skip-schema-source-verify`
@@ -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}}
@@ -1,3 +1,3 @@
1
- import{asUnknownRecord as c}from"../../kernel/unknown-values.js";import s from"node:fs";import{inspectArtifacts as d}from"../../app/inspect/artifacts.js";import{formatArtifactPreview as p}from"../renderers/artifact-preview.js";export default function l({args:i,flags:e,json:n,log:r}){const o=d({dir:e.dir,mode:e.list?"runs":e.verify?"verify":"artifacts",file:i.file||null,runId:e.run||null,service:e.service||null,kind:e.kind});if(o.mode==="runs"){const{runs:t}=o;if(n)return{ok:!0,runs:t};t.length===0&&r("No immutable Testkit runs found.");for(const u of t)r(`${u.runId} ${u.status||"unknown"} ${u.finishedAt||u.startedAt||"unknown-time"}`);return{ok:!0,count:t.length}}const f=o.runId;if(o.mode==="verify"){const{verification:t}=o;return t.ok||(process.exitCode=1),n?{ok:t.ok,verification:t}:(r(t.ok?`Verified ${f??"unknown"}: ${String(t.checkedFiles)} files (${t.manifestDigest??"unknown-digest"})`:`Integrity verification failed for ${f??"unknown"}: ${t.errors.join("; ")}`),{ok:t.ok,verification:t})}const{entries:a}=o;if(n)return{ok:!0,runId:f,artifacts:a.map(t=>({file:t.file.path,service:t.service.name,suite:{engineId:t.suite.engineId,name:t.suite.name,profileId:t.suite.profileId,testType:t.suite.testType},ref:t.artifactRef,payload:t.payload}))};if(e.format==="text")r(k(a));else for(const t of m(a))r(t);return{ok:!0,runId:f,count:a.length}}function m(i){if(i.length===0)return["No artifacts matched."];const e=[];for(const n of i){e.push(`${n.artifactRef.name}${n.artifactRef.kind?` [${n.artifactRef.kind}]`:""}`),n.artifactRef.summary&&e.push(` ${n.artifactRef.summary}`);for(const r of p(n.payload,8))e.push(` ${r}`);e.push(` ${n.artifactRef.path}`)}return e}function k(i){return i.length===0?"No artifacts matched.":i.map(e=>h(e)).join(`
1
+ import{asUnknownRecord as c}from"../../kernel/unknown-values.js";import s from"node:fs";import{inspectArtifacts as d}from"../../app/inspect/artifacts.js";export default function l({args:i,flags:e,json:n,log:o}){const r=d({dir:e.dir,mode:e.list?"runs":e.verify?"verify":"artifacts",file:i.file||null,runId:e.run||null,service:e.service||null,kind:e.kind,includePayload:n||e.format==="text"});if(r.mode==="runs"){const{runs:t}=r;if(n)return{ok:!0,runs:t};t.length===0&&o("No immutable Testkit runs found.");for(const u of t)o(`${u.runId} ${u.status||"unknown"} ${u.finishedAt||u.startedAt||"unknown-time"}`);return{ok:!0,count:t.length}}const a=r.runId;if(r.mode==="verify"){const{verification:t}=r;return t.ok||(process.exitCode=1),n?{ok:t.ok,verification:t}:(o(t.ok?`Verified ${a??"unknown"}: ${String(t.checkedFiles)} files (${t.manifestDigest??"unknown-digest"})`:`Integrity verification failed for ${a??"unknown"}: ${t.errors.join("; ")}`),{ok:t.ok,verification:t})}const{entries:f}=r;if(n)return{ok:!0,runId:a,artifacts:f.map(t=>({file:t.file.path,service:t.service.name,suite:{engineId:t.suite.engineId,name:t.suite.name,profileId:t.suite.profileId,testType:t.suite.testType},ref:t.artifactRef,payload:t.payload}))};if(e.format==="text")o(p(f));else for(const t of m(f))o(t);return{ok:!0,runId:a,count:f.length}}function m(i){if(i.length===0)return["No artifacts matched."];const e=[];for(const n of i)e.push(`${n.artifactRef.name}${n.artifactRef.kind?` [${n.artifactRef.kind}]`:""}`),n.artifactRef.summary&&e.push(` ${n.artifactRef.summary}`),e.push(` ${n.artifactRef.path}`);return e}function p(i){return i.length===0?"No artifacts matched.":i.map(e=>k(e)).join(`
2
2
 
3
- `)}function h(i){if(typeof i.payload?.text=="string")return i.payload.text.trimEnd();const e=c(i.payload?.data);return typeof e?.text=="string"?e.text.trimEnd():i.artifactRef.contentType==="application/json"&&s.existsSync(i.absolutePath)?s.readFileSync(i.absolutePath,"utf8").trimEnd():JSON.stringify(i.payload?.data??i.payload??null,null,2)}
3
+ `)}function k(i){if(typeof i.payload?.text=="string")return i.payload.text.trimEnd();const e=c(i.payload?.data);return typeof e?.text=="string"?e.text.trimEnd():i.artifactRef.contentType==="application/json"&&s.existsSync(i.absolutePath)?s.readFileSync(i.absolutePath,"utf8").trimEnd():JSON.stringify(i.payload?.data??i.payload??null,null,2)}
@@ -1 +1 @@
1
- import{executeDatabaseSchemaRefreshOperation as n}from"../../../operations/db/schema/refresh/operation.js";import{renderDatabaseSchemaRefreshResult as f}from"../../../renderers/db-schema/text.js";export default async function s({flags:r,json:t,log:a}){const e=await n(r);if(!t)for(const o of f(e))a(o);return e}
1
+ import{executeDatabaseSchemaRefreshOperation as f}from"../../../operations/db/schema/refresh/operation.js";import{renderDatabaseSchemaRefreshResult as n}from"../../../renderers/db-schema/text.js";export default async function s({flags:t,json:e,log:a}){const r=await f({...t,json:e});if(!e)for(const o of n(r))a(o);return r}
@@ -1 +1 @@
1
- import{executeDatabaseSchemaVerifyOperation as n}from"../../../operations/db/schema/verify/operation.js";import{renderDatabaseSchemaVerifyResult as f}from"../../../renderers/db-schema/text.js";export default async function i({flags:r,json:t,log:a}){const e=await n(r);if(!t)for(const o of f(e))a(o);return e}
1
+ import{executeDatabaseSchemaVerifyOperation as f}from"../../../operations/db/schema/verify/operation.js";import{renderDatabaseSchemaVerifyResult as i}from"../../../renderers/db-schema/text.js";export default async function n({flags:t,json:e,log:a}){const r=await f({...t,json:e});if(!e)for(const o of i(r))a(o);return r}
@@ -1 +1 @@
1
- import{runDoctor as n}from"../../app/doctor/index.js";import{renderDoctorResult as i}from"../renderers/doctor/text.js";export default async function f({flags:o,json:e,log:c}){const r=await n({dir:o.dir,typecheck:o.typecheck});if(!e)for(const t of i(r))c(t);if(!r.ok)throw Object.assign(new Error("testkit doctor failed"),{result:r});return r}
1
+ import{attachErrorDiagnostics as i}from"../../kernel/error-serialization.js";import{runDoctor as n}from"../../app/doctor/index.js";import{renderDoctorResult as s}from"../renderers/doctor/text.js";export default async function f({flags:o,json:c,log:e}){const r=await n({dir:o.dir,typecheck:o.typecheck});if(!c)for(const t of s(r))e(t);if(!r.ok){const t=Object.assign(new Error("testkit doctor failed"),{result:r});throw i(t,{checks:r.checks})}return r}
@@ -2,5 +2,6 @@ import { type DatabaseSchemaRefreshOptions as ApplicationOptions, type DatabaseS
2
2
  export type { DatabaseSchemaRefreshResult } from "../../../../../app/db-schema/refresh.js";
3
3
  export interface DatabaseSchemaRefreshOptions extends ApplicationOptions {
4
4
  debug?: boolean;
5
+ json?: boolean;
5
6
  }
6
7
  export declare function executeDatabaseSchemaRefreshOperation(options?: Readonly<DatabaseSchemaRefreshOptions>): Promise<DatabaseSchemaRefreshResult>;
@@ -1 +1 @@
1
- import{executeDatabaseSchemaRefresh as r}from"../../../../../app/db-schema/refresh.js";import{createTextStepProgressReporter as t}from"../../../../renderers/run/text-reporter.js";export function executeDatabaseSchemaRefreshOperation(e={}){return r(e,t({debug:e.debug}))}
1
+ import{executeDatabaseSchemaRefresh as t}from"../../../../../app/db-schema/refresh.js";export async function executeDatabaseSchemaRefreshOperation(e={}){return t(e,e.json?{outputMode:"json"}:(await import("../../../../renderers/run/text-reporter.js")).createTextStepProgressReporter({debug:e.debug}))}
@@ -2,5 +2,6 @@ import { type DatabaseSchemaVerifyOptions as ApplicationOptions, type DatabaseSc
2
2
  export type { DatabaseSchemaVerifyResult } from "../../../../../app/db-schema/verify.js";
3
3
  export interface DatabaseSchemaVerifyOptions extends ApplicationOptions {
4
4
  debug?: boolean;
5
+ json?: boolean;
5
6
  }
6
7
  export declare function executeDatabaseSchemaVerifyOperation(options?: Readonly<DatabaseSchemaVerifyOptions>): Promise<DatabaseSchemaVerifyResult>;
@@ -1 +1 @@
1
- import{executeDatabaseSchemaVerify as r}from"../../../../../app/db-schema/verify.js";import{createTextStepProgressReporter as t}from"../../../../renderers/run/text-reporter.js";export function executeDatabaseSchemaVerifyOperation(e={}){return r(e,t({debug:e.debug}))}
1
+ import{executeDatabaseSchemaVerify as t}from"../../../../../app/db-schema/verify.js";export async function executeDatabaseSchemaVerifyOperation(e={}){return t(e,e.json?{outputMode:"json"}:(await import("../../../../renderers/run/text-reporter.js")).createTextStepProgressReporter({debug:e.debug}))}
@@ -1,2 +1,2 @@
1
- import{runAll as a}from"../../../app/run/orchestrator.js";import{createRunTextListener as p}from"../../renderers/run/text-reporter.js";import{createRunEventsListener as l}from"../../renderers/run/events.js";export async function executeRunRequest(t,{outputMode:o="compact",json:e=!1,debug:c=!1,terminal:i={}}={}){const u=i.stdout||process.stdout,n=resolveRunOutputMode(o,e,c),f=n==="json"?()=>{}:n==="events"?l({stdout:u}):p({outputMode:n,stdout:u}),r=await a(t.configs,t.typeValues,t.suiteSelectors,{debug:n==="debug",onEvent:f,...t.runOptions},t.allConfigs),s=r.runArtifact.archive.runId;return(n==="compact"||n==="debug")&&u.write(`Run ID: ${s}
1
+ import{runAll as p}from"../../../app/run/orchestrator.js";export async function executeRunRequest(t,{outputMode:o="compact",json:e=!1,debug:c=!1,terminal:i={}}={}){const u=i.stdout||process.stdout,n=resolveRunOutputMode(o,e,c),a=n==="json"?()=>{}:n==="events"?(await import("../../renderers/run/events.js")).createRunEventsListener({stdout:u}):(await import("../../renderers/run/text-reporter.js")).createRunTextListener({outputMode:n,stdout:u}),r=await p(t.configs,t.typeValues,t.suiteSelectors,{debug:n==="debug",onEvent:a,...t.runOptions},t.allConfigs),s=r.runArtifact.archive.runId;return(n==="compact"||n==="debug")&&u.write(`Run ID: ${s}
2
2
  `),{outputMode:n,runId:s,...r}}export function resolveRunOutputMode(t,o,e){return o?"json":e?"debug":t==="debug"||t==="events"||t==="json"?t:"compact"}