@gfdelarue/testkit 0.5.0 → 0.6.1

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 (215) hide show
  1. package/README.md +590 -165
  2. package/bin/testkit-windows-owner-arm64.exe +0 -0
  3. package/bin/testkit-windows-owner-x64.exe +0 -0
  4. package/dist/app/bugs/batch.js +1 -0
  5. package/dist/app/bugs/ci-report.js +2 -2
  6. package/dist/app/bugs/ci-summary.js +3 -3
  7. package/dist/app/bugs/draft.js +2 -2
  8. package/dist/app/bugs/file.js +1 -1
  9. package/dist/app/bugs/guide.js +1 -1
  10. package/dist/app/bugs/record.js +2 -2
  11. package/dist/app/bugs/run-reading.js +1 -1
  12. package/dist/app/bugs/store.js +1 -1
  13. package/dist/app/campaigns/index.js +2 -2
  14. package/dist/app/campaigns/summary.js +1 -1
  15. package/dist/app/cleanup/index.js +1 -1
  16. package/dist/app/db-schema/prepare.js +1 -1
  17. package/dist/app/db-schema/refresh.js +1 -1
  18. package/dist/app/db-schema/verify.js +1 -1
  19. package/dist/app/destroy/index.js +1 -1
  20. package/dist/app/discover/index.js +2 -2
  21. package/dist/app/environments/config-driven.js +1 -1
  22. package/dist/app/environments/file-setup.js +1 -1
  23. package/dist/app/environments/routing.js +1 -1
  24. package/dist/app/environments/static-values.js +1 -1
  25. package/dist/app/environments/supplied.js +1 -1
  26. package/dist/app/inspect/index.js +1 -1
  27. package/dist/app/lint/native.js +1 -1
  28. package/dist/app/lint/options.js +1 -1
  29. package/dist/app/lint/shared.js +1 -1
  30. package/dist/app/run/engine-orchestration.js +1 -1
  31. package/dist/app/run/environment.js +1 -1
  32. package/dist/app/run/live-run.js +1 -1
  33. package/dist/app/run/orchestrator.js +1 -1
  34. package/dist/app/run/planning.js +1 -1
  35. package/dist/app/run/request.js +1 -1
  36. package/dist/app/run/results.js +1 -1
  37. package/dist/app/run/run-finalization.js +1 -1
  38. package/dist/app/run/run-finisher.js +1 -0
  39. package/dist/app/run/setup/runtime-node-setup-protocol.js +1 -1
  40. package/dist/app/run/setup/runtime-node-setup-worker.js +1 -1
  41. package/dist/app/run/setup/runtime-node-setup.js +2 -2
  42. package/dist/app/run/task-batches.js +1 -1
  43. package/dist/app/run/worker-loop.js +1 -1
  44. package/dist/app/status/model.js +1 -1
  45. package/dist/app/status/runs.js +1 -1
  46. package/dist/app/stop/index.js +1 -1
  47. package/dist/app/tracker/index.js +1 -1
  48. package/dist/app/tracker/token.js +2 -2
  49. package/dist/app/typecheck/index.js +4 -4
  50. package/dist/cli/command-catalog.js +1 -1
  51. package/dist/cli/command-flags.js +1 -1
  52. package/dist/cli/commands/artifacts.js +2 -2
  53. package/dist/cli/commands/bug/file.js +3 -2
  54. package/dist/cli/commands/discover.js +1 -1
  55. package/dist/cli/commands/guard/unsafe-entrypoint.js +1 -1
  56. package/dist/cli/commands/run.js +2 -2
  57. package/dist/cli/commands/typecheck.js +1 -1
  58. package/dist/cli/commands/workspace.js +1 -1
  59. package/dist/cli/framework/help.js +5 -5
  60. package/dist/cli/framework/parser.js +1 -1
  61. package/dist/cli/json-errors.js +1 -1
  62. package/dist/cli/renderers/bug/text.js +1 -1
  63. package/dist/cli/renderers/run/formatting.js +1 -1
  64. package/dist/config/authoring/database-fixture-discovery.js +1 -0
  65. package/dist/config/authoring/index.js +1 -1
  66. package/dist/config/loader/config-loader.js +1 -1
  67. package/dist/config/loader/decode-discovery.js +1 -1
  68. package/dist/config/loader/discover-tests.js +1 -1
  69. package/dist/config/loader/discovery-result.js +1 -0
  70. package/dist/config/loader/discovery.js +2 -2
  71. package/dist/config/loader/file-metadata.js +1 -1
  72. package/dist/config/loader/groups.js +2 -1
  73. package/dist/config/loader/index.js +1 -1
  74. package/dist/config/loader/load.js +1 -1
  75. package/dist/config/loader/managed-configs.js +1 -1
  76. package/dist/config/loader/owned-discovery.js +1 -1
  77. package/dist/config/loader/runtime-isolation.js +1 -1
  78. package/dist/config/loader/skip-config.js +1 -1
  79. package/dist/config/loader/test-selection.js +1 -1
  80. package/dist/database/admin.js +5 -2
  81. package/dist/database/call-caps.js +1 -0
  82. package/dist/database/cleanup.js +1 -1
  83. package/dist/database/configured-steps.js +1 -1
  84. package/dist/database/constants.js +1 -1
  85. package/dist/database/docker-endpoint.js +1 -0
  86. package/dist/database/docker-metadata.js +2 -2
  87. package/dist/database/docker.js +5 -5
  88. package/dist/database/index.js +2 -1
  89. package/dist/database/lifecycle-cleanup.js +1 -1
  90. package/dist/database/local-postgres.js +4 -1
  91. package/dist/database/naming.js +1 -1
  92. package/dist/database/operation-deadline.js +1 -0
  93. package/dist/database/postgres/connection-check.js +1 -1
  94. package/dist/database/postgres/index.js +2 -2
  95. package/dist/database/postgres/owned-exec.js +27 -0
  96. package/dist/database/postgres/protocol.js +2 -2
  97. package/dist/database/provider.js +1 -0
  98. package/dist/database/roles.js +2 -2
  99. package/dist/database/schema-operations.js +5 -1
  100. package/dist/database/schema-source.js +3 -3
  101. package/dist/database/steps.js +1 -1
  102. package/dist/database/template-steps.js +4 -4
  103. package/dist/domain/identifiers.js +1 -1
  104. package/dist/domain/retired-test-types.js +1 -1
  105. package/dist/domain/suite-selection.js +1 -1
  106. package/dist/domain/test-types.js +1 -1
  107. package/dist/engines/adapters/evidence-files.js +1 -1
  108. package/dist/engines/adapters/execution.js +1 -1
  109. package/dist/engines/catalog.js +1 -1
  110. package/dist/engines/definitions.js +1 -1
  111. package/dist/engines/environment/index.js +1 -1
  112. package/dist/engines/installations/node-vitest.js +1 -1
  113. package/dist/engines/installations/playwright.js +1 -1
  114. package/dist/engines/managed-module-resolution.js +6 -2
  115. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  116. package/dist/engines/node-vitest/adapter/driver.js +2 -2
  117. package/dist/engines/node-vitest/adapter/evidence.js +2 -2
  118. package/dist/engines/node-vitest/adapter/workspace.js +8 -24
  119. package/dist/engines/node-vitest/inventory/index.js +2 -2
  120. package/dist/engines/node-vitest/inventory/list-files.js +3 -3
  121. package/dist/engines/node-vitest/runtime/data/index.js +1 -1
  122. package/dist/engines/node-vitest/runtime/data/report.js +1 -1
  123. package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
  124. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  125. package/dist/engines/playwright/adapter/adapter.js +1 -1
  126. package/dist/engines/playwright/adapter/browser-network-evidence.js +4 -0
  127. package/dist/engines/playwright/adapter/config.js +16 -6
  128. package/dist/engines/playwright/adapter/evidence.js +2 -1
  129. package/dist/engines/playwright/adapter/failure-details.js +2 -2
  130. package/dist/engines/playwright/adapter/reporter.js +1 -1
  131. package/dist/engines/playwright/runtime/browser-network.js +1 -0
  132. package/dist/engines/playwright/runtime/browser-traffic.js +1 -0
  133. package/dist/engines/playwright/runtime/chromium-environment.js +1 -0
  134. package/dist/engines/playwright/runtime/emulation.js +1 -0
  135. package/dist/engines/playwright/runtime/index.js +1 -1
  136. package/dist/engines/playwright/runtime/managed-test.js +1 -0
  137. package/dist/engines/playwright/runtime/runtime-input.js +2 -2
  138. package/dist/environment/lifecycle-controller.js +1 -1
  139. package/dist/environment/lifecycle.js +1 -1
  140. package/dist/environment/maintenance.js +1 -1
  141. package/dist/environment/run-environment.js +1 -1
  142. package/dist/environment/run-stopping.js +1 -1
  143. package/dist/environment/runtime-manager.js +1 -1
  144. package/dist/evidence/artifact-config.js +1 -1
  145. package/dist/evidence/artifacts.js +5 -2
  146. package/dist/evidence/bundles.js +2 -1
  147. package/dist/evidence/compressed-artifacts.js +1 -0
  148. package/dist/evidence/environment-record.js +1 -1
  149. package/dist/evidence/index.js +1 -1
  150. package/dist/evidence/live-run-summary.js +1 -0
  151. package/dist/evidence/local-paths.js +1 -0
  152. package/dist/evidence/maintenance.js +1 -0
  153. package/dist/evidence/operation-timings.js +1 -1
  154. package/dist/evidence/read-run.js +1 -1
  155. package/dist/evidence/redaction.js +4 -4
  156. package/dist/evidence/run-evidence.js +1 -0
  157. package/dist/evidence/setup-progress.js +1 -1
  158. package/dist/evidence/storage-inventory.js +1 -1
  159. package/dist/evidence/storage.js +2 -2
  160. package/dist/kernel/known-keys.js +1 -0
  161. package/dist/kernel/redaction-targets.js +1 -1
  162. package/dist/kernel/redaction.js +7 -4
  163. package/dist/process/engine-environment-keys.js +1 -0
  164. package/dist/process/environment-overlay.js +1 -1
  165. package/dist/process/environment.js +1 -1
  166. package/dist/process/execa-bundle.json +3 -3
  167. package/dist/process/files/evidence-writes.js +1 -0
  168. package/dist/process/files/product-dir.js +1 -1
  169. package/dist/process/host-process.js +1 -1
  170. package/dist/process/index.js +1 -1
  171. package/dist/process/lifecycle-fence.js +1 -1
  172. package/dist/process/lock-progress.js +2 -0
  173. package/dist/process/locks.js +4 -4
  174. package/dist/process/process-identity.js +1 -1
  175. package/dist/process/process.js +35 -35
  176. package/dist/process/recovery.js +1 -1
  177. package/dist/process/runs/manifest.js +1 -1
  178. package/dist/process/runs/ownership.js +1 -1
  179. package/dist/process/runs/stop.js +1 -1
  180. package/dist/process/shutdown.js +1 -1
  181. package/dist/process/windows-native.js +1 -1
  182. package/dist/repo/fingerprint-policy.js +1 -1
  183. package/dist/repo/history.js +7 -4
  184. package/dist/repo/managed-git.js +1 -1
  185. package/dist/repo/state.js +4 -4
  186. package/dist/steps/configured-step-testkit-modules.js +1 -1
  187. package/dist/steps/environment.js +1 -1
  188. package/dist/toolchains/index.js +2 -2
  189. package/dist/toolchains/node-provisioning.js +2 -2
  190. package/dist/toolchains/vitest/index.js +1 -1
  191. package/dist/tracker/gitea.js +1 -1
  192. package/dist/tracker/github.js +1 -1
  193. package/dist/tracker/http.js +1 -1
  194. package/dist/tracker/ownership.js +1 -0
  195. package/dist/tracker/redaction.js +1 -1
  196. package/dist/tracker/responses.js +1 -1
  197. package/dist/types/config.d.ts +7 -86
  198. package/dist/types/environment.d.ts +6 -0
  199. package/dist/types/node-vitest-data.d.ts +2 -218
  200. package/dist/types/playwright.d.ts +21 -0
  201. package/dist/types/workspace.d.ts +30 -14
  202. package/docs/bug-records.md +19 -18
  203. package/docs/engines.md +115 -21
  204. package/docs/release-notes.md +2846 -0
  205. package/docs/test-types.md +19 -69
  206. package/package.json +15 -6
  207. package/testkit-config-schema.md +101 -170
  208. package/dist/app/lint/rules/data.js +0 -1
  209. package/dist/config/authoring/runtime.js +0 -1
  210. package/dist/config/loader/task-policy.js +0 -1
  211. package/dist/engines/node-vitest/runtime/contract/index.js +0 -1
  212. package/dist/engines/node-vitest/runtime/data/http.js +0 -1
  213. package/dist/engines/node-vitest/runtime/data/runner.js +0 -1
  214. package/dist/environment/lifecycle-artifacts.js +0 -1
  215. package/dist/types/node-vitest-contract.d.ts +0 -75
@@ -1 +1 @@
1
- import{isDeepStrictEqual as c}from"node:util";import{isUnknownRecord as f}from"../../kernel/unknown-values.js";export function createEnvironment({group:n,setupData:t={},...e}){return{...e,databases:p(e.databases,n,e.databaseUrl,"database"),endpoints:p(e.endpoints,n,e.baseUrl,"endpoint"),env:{...e.env},setupData:t}}export class GroupDefaultConflictError extends Error{}function p(n,t,e,r){if(e===null)return{...n};if(Object.hasOwn(n,t)&&n[t]!==e)throw new GroupDefaultConflictError(`Group "${t}" has a default ${r} and a different ${r} named "${t}"; a group's default ${r} is also named after the group`);return{...n,[t]:e}}export class TaskPreparationError extends Error{cleanupFailures;kind;cause;constructor(t,e,r){super(e.message,{cause:e}),this.name="TaskPreparationError",this.cause=e,this.cleanupFailures=r,this.kind=t}}export function environmentTaskIdentities(n){return("batchTasks"in n?n.batchTasks:[n]).map(environmentTaskIdentity)}const b={baseUrl:"base URL",databaseUrl:"database URL",databases:"database",endpoints:"endpoint",env:"environment value",handles:"handle",setupData:"setup data",ui:"UI values"};export function renderSharedEnvironment(n,t){const[e,...r]=[...new Set(environmentTaskIdentities(n).map(({group:a})=>a))],s=t(e);for(const a of r){const i=t(a),o=Object.keys(b).find(u=>!c(s[u],i[u]));if(o===void 0)continue;const d=h(s[o],i[o]);throw new Error(`Plain Vitest files run in one Vitest invocation, but groups ${e} and ${a} give them a different ${b[o]}${d===void 0?"":` ${d}`}; select one group with --group`)}return s}function h(n,t){if(!(!f(n)||!f(t)))return Object.keys({...n,...t}).find(e=>!c(n[e],t[e]))}export function environmentTaskIdentity({file:n,id:t,targetName:e}){return{id:t,file:n,group:e}}
1
+ import{randomUUID as c}from"node:crypto";import u from"node:path";import{isDeepStrictEqual as f}from"node:util";import{isUnknownRecord as m}from"../../kernel/unknown-values.js";export function createEnvironment({group:n,setupData:t={},...e}){return{...e,databases:b(e.databases,n,e.databaseUrl,"database"),endpoints:b(e.endpoints,n,e.baseUrl,"endpoint"),env:{...e.env},setupData:t}}export class GroupDefaultConflictError extends Error{}function b(n,t,e,r){if(e===null)return{...n};if(Object.hasOwn(n,t)&&n[t]!==e)throw new GroupDefaultConflictError(`Group "${t}" has a default ${r} and a different ${r} named "${t}"; a group's default ${r} is also named after the group`);return{...n,[t]:e}}export class TaskPreparationError extends Error{cleanupFailures;kind;cause;constructor(t,e,r){super(e.message,{cause:e}),this.name="TaskPreparationError",this.cause=e,this.cleanupFailures=r,this.kind=t}}export const PREVIEW_LEASE=Object.freeze({directory:u.join(u.sep,`testkit-preview-${c()}`),id:`testkit-preview-${c()}`});export function environmentTaskIdentities(n){return("batchTasks"in n?n.batchTasks:[n]).map(environmentTaskIdentity)}const h={baseUrl:"base URL",databaseUrl:"database URL",databases:"database",endpoints:"endpoint",env:"environment value",handles:"handle",setupData:"setup data",ui:"UI values"};export function renderSharedEnvironment(n,t){const[e,...r]=[...new Set(environmentTaskIdentities(n).map(({group:o})=>o))],i=t(e);for(const o of r){const s=t(o),a=Object.keys(h).find(p=>!f(i[p],s[p]));if(a===void 0)continue;const d=l(i[a],s[a]);throw new Error(`Plain Vitest files run in one Vitest invocation, but groups ${e} and ${o} give them a different ${h[a]}${d===void 0?"":` ${d}`}; select one group with --group`)}return i}function l(n,t){if(!(!m(n)||!m(t)))return Object.keys({...n,...t}).find(e=>!f(n[e],t[e]))}export function environmentTaskIdentity({file:n,id:t,targetName:e}){return{id:t,file:n,group:e}}
@@ -1 +1 @@
1
- import{finalizeServiceResult as v}from"./results.js";import{buildLiveRunArtifact as S}from"../../evidence/reporting.js";import{writeLiveRunArtifact as R}from"../../evidence/index.js";export function createLiveSnapshotWriter({productDir:e,runId:i,configs:o,trackers:u,startedAt:r,execution:l,workerState:s,selection:t,archiveConfig:p=null,declaredEnvironmentKeys:f=[],metadata:m,environmentRecord:g,operationTimingRegistry:c=null,reporter:y=null}){return()=>{const a=Date.now(),w=o.map(n=>v(u.get(n.name),r,a));try{R(e,S({productDir:e,runId:i,results:w,startedAt:r,updatedAt:a,execution:l,workerCount:s.workerCount,typeValues:t.typeValues,suiteSelectors:t.suiteSelectors,fileNames:t.fileNames,groupFilter:t.groupFilter,planning:t.planning||null,archiveConfig:p,declaredEnvironmentKeys:f,metadata:m,environment:g.snapshot(),operationTimings:c?.listOperations()||[]}))}catch(n){y?.warning?.(`Live run artifact update skipped: ${h(b(n))}`)}}}function b(e){return(typeof e=="object"&&e!==null||typeof e=="function")&&Reflect.get(e,"message")||e}function h(e){if(typeof e=="symbol")throw new TypeError("Cannot convert a Symbol value to a string");return String(e)}
1
+ import{finalizeServiceResult as R}from"./results.js";import{buildLiveRunArtifact as b}from"../../evidence/reporting.js";import{writeLiveRunArtifact as x}from"../../evidence/artifacts.js";export function createLiveSnapshotWriter({productDir:e,runId:l,configs:p,trackers:m,startedAt:s,execution:c,workerState:g,selection:t,archiveConfig:y=null,declaredEnvironmentKeys:d=[],metadata:w,environmentRecord:h,operationTimingRegistry:v=null,reporter:S=null}){let n=null,u=!1;const a=()=>{const f=Date.now(),L=p.map(r=>R(m.get(r.name),s,f));try{x(e,b({productDir:e,runId:l,results:L,startedAt:s,updatedAt:f,execution:c,workerCount:g.workerCount,typeValues:t.typeValues,suiteSelectors:t.suiteSelectors,fileNames:t.fileNames,groupFilter:t.groupFilter,planning:t.planning||null,archiveConfig:y,declaredEnvironmentKeys:d,metadata:w,environment:h.snapshot(),operationTimings:v?.listOperations()||[]}))}catch(r){S?.warning?.(`Live run artifact update skipped: ${k(V(r))}`)}},i=()=>{n!==null&&(clearImmediate(n),n=null,process.off("exit",o),a())},o=()=>{try{i()}catch{}};return{update(){if(u){a();return}n===null&&(n=setImmediate(i),process.prependListener("exit",o))},flush:i,finish(){u=!0,i()}}}function V(e){return(typeof e=="object"&&e!==null||typeof e=="function")&&Reflect.get(e,"message")||e}function k(e){if(typeof e=="symbol")throw new TypeError("Cannot convert a Symbol value to a string");return String(e)}
@@ -1 +1 @@
1
- import{resolveRuntimeClosure as he}from"../../config/loader/dependencies.js";import{claimNextTask as ve,collectSuites as ye}from"./planning.js";import{buildRunPlanningMetadata as ke,buildScheduledQueue as Re}from"./scheduler/index.js";import{requireCompatibleSelection as Se}from"./task-batches.js";import{addTrackerError as we,buildServiceTrackers as Ce,finalizeServiceResult as ie,recordGraphError as Fe,recordTaskOutcome as Ee}from"./results.js";import{loadTimings as Ae,saveTimings as Te,writeRunArtifact as be}from"../../evidence/index.js";import{createRunEnvironmentRecord as Ke}from"../../evidence/environment-record.js";import{createRunLogRegistry as xe}from"../../evidence/logs.js";import{createOperationTimingRegistry as Me,timeOperation as B}from"../../evidence/operation-timings.js";import{createRunReporterFromListener as qe}from"./run-events.js";import{createCallerRedaction as Ie}from"./caller-redaction.js";import{createSetupOperationRegistry as ze}from"../../evidence/setup-progress.js";import{createLocalRunEnvironment as Oe}from"../../environment/run-environment.js";import{createConfigDrivenEnvironments as Pe,planConfiguredCapacity as Ne}from"../environments/config-driven.js";import{declaresProvisioning as je,declaresValues as ae,holdBeforeRelease as De,routeEnvironments as Le}from"../environments/routing.js";import{createStaticValueEnvironments as Ge}from"../environments/static-values.js";import{findRuntimeNodeSetupFile as Ve}from"./setup/runtime-node-setup.js";import{createSuppliedEnvironments as Be}from"../environments/supplied.js";import{environmentTaskIdentity as $e}from"./environment.js";import{createTaskLeases as We}from"./task-leases.js";import{createTaskLocks as Ue}from"./scheduler/locks.js";import{readPackageMetadata as He,safeHostname as Qe,safeUsername as _e}from"../../process/host/metadata.js";import{collectGitMetadata as Je}from"../../repo/metadata.js";import{resolveExecutionConfig as Xe}from"../../config/loader/decode-execution.js";import{resolveTaskPolicy as Ye}from"../../config/loader/task-policy.js";import"../../environment/run-environment.js";import{createEngineManager as Ze}from"../../engines/factory.js";import{createWorker as et,runWorker as tt}from"./worker-loop.js";import{ensureRequestedFilesMatch as rt}from"./run-guards.js";import{createLiveSnapshotWriter as nt}from"./live-run.js";import{finalizeRunArtifacts as it}from"./run-finalization.js";import{buildRunArtifact as at}from"../../evidence/reporting.js";import{createOperationReporterBridge as ot}from"./operation-progress.js";import{formatError as $,serializeError as oe}from"../../kernel/error-serialization.js";export async function runAll(e,n,a,r,c=[]){const h=r.fileNames||[];rt(e,n,a,h);const{productDir:s}=r,p=Oe(s),R=Ze(s),v=new Map(c.map(i=>[i.name,i])),t=Date.now(),C=e[0]?.artifacts??null,q=[...new Set(e.flatMap(i=>i.uses.flatMap(m=>Object.keys(v.get(m)?.testkit.serviceEnv??{}))))].sort(),f={git:await Je(s),host:{hostname:Qe(),username:_e()},testkitVersion:He().version},S=e[0]?.redact??[],b=Ie(process.env,S),Y=r.onEvent,y=Y?qe(i=>{Y(b.redact(i))},{debug:r.debug}):null,K=xe(s),j={workerCount:0},I=resolveRunExecutionConfig(e[0],r),D=collectServicePlans(e,v,n,a,r,I,y);y?.setServicePlans?.(D);const z=Ce(D,t),L=i=>{for(const m of z.values())m.skipped||we(m,i);y?.runError?.(i)},ce=a.map(i=>({...i})),W={typeValues:n,suiteSelectors:ce,fileNames:h,groupFilter:r.serviceFilter||null,planning:null};let k=()=>{};const ue=ot(y),T=Me({onChange:i=>{ue(i),k()}}),Z=ze({logRegistry:K,onChange:()=>{k()}}),d=Ke({logRegistry:K,redactKeys:S,setupRegistry:Z,onPublishedChange:()=>{k()}});b.useSourceTargets(()=>d.redactionTargets()),d.recordGroups(D.map(i=>({dbBackend:i.dbBackend,name:i.config.name})));const U=D.filter(i=>!i.skipped),H=r.environments?.supplied??{},{plans:O,provisionedGraphKeys:G,staticGraphKeys:Q}=assignRuntimeGraphs(U,new Set(Object.keys(H)));let g;const o=await p.start({keepAlive:!0,localInfrastructure:G.size>0,redactKeys:S,redactionTargets:()=>d.redactionTargets(),resetResultsBeforePublish:!0});k=nt({productDir:s,runId:o.runId,configs:e,trackers:z,startedAt:t,execution:I,workerState:j,selection:W,archiveConfig:C,declaredEnvironmentKeys:q,metadata:f,environmentRecord:d,operationTimingRegistry:T,reporter:y});const P={artifacts:null},le=async(i,m)=>(o.beginFinalization(),await o.finish(i,A=>{const w=m(A);return P.artifacts={runArtifact:w.runArtifact},w},"normal"));let F,x;const ee=(i,m,u)=>({productDir:s,runId:o.runId,results:i,startedAt:t,finishedAt:m,execution:I,workerCount:j.workerCount,selection:W,archiveConfig:C,declaredEnvironmentKeys:q,metadata:f,environmentRecord:d,operationTimingRegistry:T,reporter:y,runInterruptReason:o.getInterruptReason(),runStatus:u,publishRunFinalization:le});k();let E=null;try{try{if(U.length>0){const u=Ae(s),{capacity:A,queue:w}=await B(T,{name:"testkit.plan",kind:"planning",summary:"plan test execution",scope:{serviceCount:U.length}},()=>({capacity:Ne(O.filter(l=>G.has(l.runtimeKey))),queue:Re(O.map(l=>prepareSchedulerPlan(l,Object.hasOwn(H,l.config.name)?()=>!1:M=>Ve(s,M)!==null)),{timings:u})}));Se(w,{provisioned:G,static:Q}),W.planning=ke(w),d.recordCapacity(A),y?.setTotalFileCount?.(w.length),j.workerCount=Math.max(1,Math.min(I.taskConcurrency,w.length));const te=Array.from({length:j.workerCount},(l,M)=>et(M+1,s));A.graphs.length>0&&o.provision(A.graphs,{reporter:y,environmentRecord:d,logRegistry:K,setupRegistry:Z,operationTimingRegistry:T,skipSchemaSourceVerify:r.skipSchemaSourceVerify});const me=Pe(o,new Map(O.map(l=>[l.config.name,l.config.uses])),s,d),fe=Ge(new Map(O.filter(l=>Q.has(l.runtimeKey)).map(l=>[l.config.name,l.uses])),s,d),de=Be(H,{productDir:s,record:d,signal:o.signal}),re=We(s),pe={recordRunError:L,runId:o.runId,signal:o.signal,isStopRequested:()=>o.isStopRequested(),environments:mt(Le(new Map(O.map(({runtimeKey:l})=>[l,G.has(l)?me:Q.has(l)?fe:de]))),r.environments?.hold),leases:ut(re,d),locks:Ue(),request:ft(s,e,I,r.vitest)},ne=[];try{const M=(await B(T,{name:"testkit.execute",kind:"execution",summary:"execute local worker queue",scope:{workerCount:te.length,taskCount:w.length}},async()=>Promise.allSettled(te.map(_=>tt(_,w,pe,R,z,ne,ve,(J,V,N,X)=>{Ee(J,V,N,X),N.environmentValuesMissing!==void 0&&d.recordMissingValues($e(V),N.environmentValuesMissing),k()},(J,V,N,X,ge)=>{Fe(J,V,N,X,ge),k()},y))))).flatMap(workerResultErrorsForOrchestrator);for(const _ of M)L(_);M.length>0&&k(),d.recordRuntimeStats(o.getStats())}finally{A.graphs.length>0&&await B(T,{name:"testkit.cleanup",kind:"cleanup",summary:"stop managed runtimes and services"},()=>lt(d,()=>o.cleanupAll())),re.close()}Te(s,u,ne)}}catch(u){E=oe(u),L(E.message||$(u)),k()}g=E===null?0:o.isStopRequested()?130:1,x=Date.now(),F=e.map(u=>ie(z.get(u.name),t,x));const i=resolveRunGateFailure(F,r.allowEmpty===!0);await B(T,{name:"testkit.collect",kind:"collection",summary:"close and collect run logs"},()=>K.closeAll());const m=await it(ee(F,x,o.isStopRequested()?"interrupted":E===null&&i===null?null:"failed"));return E!==null&&(g=1),F.some(u=>u.failed||u.infraFailed)&&(g=1),i!==null&&(g=1),m.shouldFailArchivePublication&&(g=1),m.shouldFailArchiveCleanup&&(g=1),(m.runArtifact.run.status==="interrupted"||o.isStopRequested()||o.committedFinalization?.artifactStatus==="interrupted")&&(g=Math.max(g,130)),{runArtifact:m.runArtifact,results:b.redact(F),exitCode:g,...E===null?{}:{failure:b.redact(E)}}}catch(i){const m=oe(i);L(m.message||$(i)),k(),o.beginFinalization(),g=o.isStopRequested()||o.committedFinalization?.artifactStatus==="interrupted"?130:1,x=Date.now(),F=e.map(A=>ie(z.get(A.name),t,x)),await se(K);let u=P.artifacts;if(u===null)try{u=await st(ee(F,x,o.isStopRequested()?"interrupted":"failed"),o,P,m)}catch{if(P.artifacts===null)throw i;u=P.artifacts}return(u.runArtifact.run.status==="interrupted"||o.isStopRequested()||o.committedFinalization?.artifactStatus==="interrupted")&&(g=Math.max(g,130)),{runArtifact:u.runArtifact,results:b.redact(F),exitCode:g,failure:b.redact(E??m)}}finally{o.dispose(),await se(K),ct(d,r.environments?.observeRecord,y)}}async function st(e,n,a,r){return n.finish("failed",c=>{const h=c.artifactStatus==="interrupted"?c:{artifactStatus:"failed",interruptReason:null},s=e.declaredEnvironmentKeys??[],p=at({productDir:e.productDir,runId:e.runId,results:e.results,startedAt:e.startedAt,finishedAt:e.finishedAt,execution:e.execution,workerCount:e.workerCount,typeValues:e.selection.typeValues,suiteSelectors:e.selection.suiteSelectors,fileNames:e.selection.fileNames,groupFilter:e.selection.groupFilter,planning:e.selection.planning??null,archiveConfig:e.archiveConfig,declaredEnvironmentKeys:s,metadata:e.metadata,environment:e.environmentRecord.snapshot(),operationTimings:e.operationTimingRegistry?.listOperations()??[],runInterruptReason:h.interruptReason,runStatus:h.artifactStatus});p.finalizationFailure={...r};const v={runArtifact:be(e.productDir,p,{archiveConfig:e.archiveConfig,declaredEnvironmentKeys:s,redactKeys:e.environmentRecord.redactKeys,redactionTargets:e.environmentRecord.redactionTargets()})};return a.artifacts=v,v},"recovery")}function ct(e,n,a){if(n!==void 0)try{n(e.snapshot())}catch(r){try{a?.warning?.(`Environment record observer failed: ${$(r)}`)}catch{}}}function ut(e,n){return{...e,remove(a){const r=n.releaseFor(a.id);try{e.remove(a)}catch(c){throw r?.releaseFailed(c),c}r?.released()}}}async function lt(e,n){try{await n()}catch(a){throw e.recordCleanup(a),a}e.recordCleanup(null)}async function se(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,n=!1){return e.some(r=>r.failed)?"test failure":e.some(r=>r.infraFailed)?"test infrastructure failure":e.some(r=>r.notRunFileCount>0)?"one or more required test files did not run":e.some(r=>r.skippedFileCount>0)?"one or more selected test files were skipped":e.reduce((r,c)=>r+c.completedFileCount,0)===0&&!n?"no runnable test files executed":null}function mt(e,n){return n===void 0?e:De(e,n)}function ft(e,n,a,r){return{vitest:r,productDir:e,fileTimeoutSeconds:a.fileTimeoutSeconds,ownerRoots:Object.fromEntries(n.map(c=>[c.name,c.roots]))}}export function resolveRunExecutionConfig(e,n){return Xe({cli:{fileTimeoutSeconds:n.fileTimeoutSeconds,taskConcurrency:n.taskConcurrency},repo:e?{fileTimeoutSeconds:e.testkit.execution.fileTimeoutSeconds,taskConcurrency:e.testkit.execution.taskConcurrency}:void 0})}export function assignRuntimeGraphs(e,n){const a=e.filter(t=>t.suites.some(C=>C.files.length>0)),r=new Set(a.filter(t=>!n.has(t.config.name)&&je(t.runtimeConfigs))),c=new Set([...r].map(t=>t.runtimeKey)),h=t=>!r.has(t)&&!n.has(t.config.name)&&ae(t.runtimeConfigs),s=new Set(a.filter(t=>h(t)&&t.runtimeKey!=="").map(t=>t.runtimeKey)),p=new Set(a.map(t=>t.runtimeKey)),R=t=>t.runtimeKey!==""&&!c.has(t.runtimeKey)&&(h(t)||!s.has(t.runtimeKey))?t.runtimeKey:p.has(t.config.name)?`group:${t.config.name}`:t.config.name,v=a.map(t=>r.has(t)?t:{...t,runtimeKey:R(t)});return{plans:v,provisionedGraphKeys:c,staticGraphKeys:new Set(v.filter(t=>!c.has(t.runtimeKey)&&!n.has(t.config.name)&&ae(t.runtimeConfigs)).map(t=>t.runtimeKey))}}export function prepareSchedulerPlan(e,n){return{graphKey:e.runtimeKey,owner:e.config.name,suites:e.suites.map(a=>({...a,tasks:a.files.map(r=>({file:r,hasSetup:n(r),...Ye(e.config.testkit,a,r)}))}))}}export function collectServicePlans(e,n,a,r,c,h,s=null){return e.map(p=>{const R=p.uses.map(f=>{const S=n.get(f);if(S===void 0)throw new Error(`Group "${p.name}" uses unknown service "${f}"`);return S}),v=R.some(f=>f.testkit.database!==void 0)?"local":null,t=ye(p,a,r,c.fileNames||[],c);if(t.length===0)return s?.serviceSkipped?.(p,`no matching files (types=${a.join(",")||"all"} suites=${r.map(f=>f.raw).join(",")||"all"} files=${(c.fileNames||[]).join(",")||"all"})`),{config:p,dbBackend:v,execution:h,skipped:!0,suites:[],runtimeConfigs:[],runtimeNames:[],runtimeKey:null,uses:R};for(const f of t)for(const S of f.skippedFiles)s?.plannedSkip?.({serviceName:p.name,testType:f.testType,engineId:f.engineId,profileId:f.profileId,file:S.path,reason:S.reason});const C=he(R,n),q=C.map(f=>f.name).sort();return{config:p,dbBackend:v,execution:h,skipped:!1,suites:t,runtimeConfigs:C,runtimeNames:q,runtimeKey:q.join("|"),uses:R}})}
1
+ import{resolveRuntimeClosure as Ee}from"../../config/loader/dependencies.js";import{claimNextTask as Ke,collectSuites as Te}from"./planning.js";import{buildRunPlanningMetadata as Ae,buildScheduledQueue as be}from"./scheduler/index.js";import{addTrackerError as Me,buildServiceTrackers as xe,finalizeServiceResult as ge,recordGraphError as Ie,recordTaskOutcome as Pe,resolveRunGateFailure as qe}from"./results.js";import{loadTimings as ze,saveTimings as Oe}from"../../evidence/index.js";import{writeRunArtifact as Ne}from"../../evidence/artifacts.js";import{stableJson as $e}from"../../evidence/storage.js";import{createRunEnvironmentRecord as Le}from"../../evidence/environment-record.js";import{createRunLogRegistry as je}from"../../evidence/logs.js";import{createOperationTimingRegistry as De,timeOperation as J}from"../../evidence/operation-timings.js";import{createRunReporterFromListener as Ge}from"./run-events.js";import{createCallerRedaction as Ve}from"./caller-redaction.js";import{createSetupOperationRegistry as Be}from"../../evidence/setup-progress.js";import{createLocalRunEnvironment as We}from"../../environment/run-environment.js";import{writeRunMaintenance as Ue}from"../../evidence/maintenance.js";import{createConfigDrivenEnvironments as _e,planConfiguredCapacity as He}from"../environments/config-driven.js";import{declaresProvisioning as Je,declaresValues as he,holdBeforeRelease as Qe,routeEnvironments as Xe}from"../environments/routing.js";import{createStaticValueEnvironments as Ye}from"../environments/static-values.js";import{createSuppliedEnvironments as Ze}from"../environments/supplied.js";import{environmentTaskIdentity as et}from"./environment.js";import{createTaskLeases as tt}from"./task-leases.js";import{createTaskLocks as rt}from"./scheduler/locks.js";import{readPackageMetadata as nt,safeHostname as it,safeUsername as at}from"../../process/host/metadata.js";import{collectGitMetadata as st}from"../../repo/metadata.js";import{resolveExecutionConfig as ot}from"../../config/loader/decode-execution.js";import{projectGroupTestPolicy as ct}from"../../config/loader/discover-tests.js";import{resolveTaskPolicy as ut}from"../../config/loader/test-selection.js";import"../../environment/run-environment.js";import{createEngineManager as lt}from"../../engines/factory.js";import{createWorker as mt,runWorker as ft}from"./worker-loop.js";import{ensureRequestedFilesMatch as dt}from"./run-guards.js";import{createLiveSnapshotWriter as pt}from"./live-run.js";import{finalizeRunArtifacts as gt}from"./run-finalization.js";import{createRunFinisher as ht}from"./run-finisher.js";import{createRunEvidencePublisher as yt}from"../../evidence/run-evidence.js";import{buildRunArtifact as vt}from"../../evidence/reporting.js";import{createOperationReporterBridge as kt}from"./operation-progress.js";import{formatError as z,serializeError as Z}from"../../kernel/error-serialization.js";export async function runAll(e,a,o,n,m=[]){const k=n.fileNames||[];dt(e,a,o,k);const{productDir:c}=n,p=We(c),h=lt(c),y=new Map(m.map(r=>[r.name,r])),t=Date.now(),S=e[0]?.artifacts??null,E=[...new Set(e.flatMap(r=>r.uses.flatMap(l=>Object.keys(y.get(l)?.testkit.serviceEnv??{}))))].sort(),f={git:await st(c),host:{hostname:it(),username:at()},testkitVersion:nt().version},C=e[0]?.redact??[],R=Ve(process.env,C),ee=n.onEvent;let te=()=>{};const v=ee?Ge(r=>{r.type==="run.task_started"&&te(),ee(R.redact(r))},{debug:n.debug}):null,M=je(c),B={workerCount:0},O=resolveRunExecutionConfig(e[0],n),W=collectServicePlans(e,y,a,o,n,O,v);v?.setServicePlans?.(W);const N=xe(W,t),$=(r,l="product")=>{for(const s of N.values())s.skipped||Me(s,r,{classification:l});v?.runError?.(r)},ve=o.map(r=>({...r})),Q={typeValues:a,suiteSelectors:ve,fileNames:k,groupFilter:n.serviceFilter||null,planning:null};let w=()=>{};const ke=kt(v),b=De({onChange:r=>{ke(r),w()}}),re=Be({logRegistry:M,onChange:()=>{w()}}),d=Le({logRegistry:M,redactKeys:C,setupRegistry:re,onPublishedChange:()=>{w()}});R.useSourceTargets(()=>d.redactionTargets()),d.recordGroups(W.map(r=>({dbBackend:r.dbBackend,name:r.config.name})));const X=W.filter(r=>!r.skipped),ne=n.environments?.supplied??{},{plans:x,provisionedGraphKeys:ie,staticGraphKeys:ae}=assignRuntimeGraphs(X,new Set(Object.keys(ne)));let g;const u=await p.start({evidence:yt({redactKeys:C,redactionTargets:()=>d.redactionTargets(),resetResults:!0}),keepAlive:!0}),L=ht(u),U=pt({productDir:c,runId:u.runId,configs:e,trackers:N,startedAt:t,execution:O,workerState:B,selection:Q,archiveConfig:S,declaredEnvironmentKeys:E,metadata:f,environmentRecord:d,operationTimingRegistry:b,reporter:v});w=()=>{U.update()},te=()=>{U.flush()};const j={artifacts:null},D={result:null},se=r=>{const l=R.redact(r);try{return Ue(c,u.runId,l),l}catch(s){const F=`Could not write the maintenance record: ${R.redact(z(s))}`;return{...l,status:"failed",error:l.error===null?F:`${l.error}; ${F}`}}},oe=r=>(D.result=r.maintenance,r.maintenance.status==="failed"&&v?.warning?.(`Post-run maintenance failed: ${r.maintenance.error??"unknown error"}`),r.sealed),Re=async(r,l)=>(u.beginFinalization(),oe(await L.finish(r,s=>{const F=l(s);return j.artifacts={runArtifact:F.runArtifact},F},"normal",se)));let K,I;const ce=(r,l,s)=>({productDir:c,runId:u.runId,results:r,startedAt:t,finishedAt:l,execution:O,workerCount:B.workerCount,selection:Q,archiveConfig:S,declaredEnvironmentKeys:E,metadata:f,environmentRecord:d,operationTimingRegistry:b,reporter:v,runInterruptReason:u.getInterruptReason(),runStatus:s,publishRunFinalization:Re});w();let T=null,ue=Symbol("no release failure");try{try{if(X.length>0){const s=ze(c),F=_e(u,new Map(x.map(i=>[i.config.name,i.config.uses])),c,d),we=Ye(new Map(x.filter(i=>ae.has(i.runtimeKey)).map(i=>[i.config.name,i.uses])),c,d),Se=Ze(ne,{productDir:c,record:d,signal:u.signal}),le=i=>ie.has(i)?F:ae.has(i)?we:Se,me=Xe(new Map(x.map(({runtimeKey:i})=>[i,le(i)]))),{capacity:_,queue:G}=await J(b,{name:"testkit.plan",kind:"planning",summary:"plan test execution",scope:{serviceCount:X.length}},()=>({capacity:He(x.filter(i=>ie.has(i.runtimeKey))),queue:be(x.map(i=>prepareSchedulerPlan(i,A=>me.hasSetup({file:A,graphKey:i.runtimeKey,targetName:i.config.name}))),{timings:s})}));Q.planning=Ae(G),d.recordCapacity(_),v?.setTotalFileCount?.(G.length),B.workerCount=Math.max(1,Math.min(O.taskConcurrency,G.length));const fe=Array.from({length:B.workerCount},(i,A)=>mt(A+1,c));_.graphs.length>0&&u.provision(_.graphs,{reporter:v,environmentRecord:d,logRegistry:M,setupRegistry:re,operationTimingRegistry:b,skipSchemaSourceVerify:n.skipSchemaSourceVerify});const de=tt(c),Ce={recordRunError:$,runId:u.runId,signal:u.signal,isStopRequested:()=>u.isStopRequested(),environments:Ft(me,n.environments?.hold),leases:St(de,d),locks:rt(),request:Et(c,e,O,n.vitest),environmentKeys:new Map(x.flatMap(({config:i,runtimeKey:A})=>{const P=le(A),q=P.preview?.(i.name)??null;return q===null?[]:[[i.name,$e([q,P.checksServices?.(i.name)??!1])]]}))},pe=[];try{const A=(await J(b,{name:"testkit.execute",kind:"execution",summary:"execute local worker queue",scope:{workerCount:fe.length,taskCount:G.length}},async()=>Promise.allSettled(fe.map(P=>ft(P,G,Ce,h,N,pe,Ke,(q,H,V,Y)=>{Pe(q,H,V,Y),V.environmentValuesMissing!==void 0&&d.recordMissingValues(et(H),V.environmentValuesMissing),w()},(q,H,V,Y,Fe)=>{Ie(q,H,V,Y,Fe),w()},v))))).flatMap(workerResultErrorsForOrchestrator);for(const P of A)$(P);A.length>0&&w(),d.recordRuntimeStats(u.getStats())}finally{_.graphs.length>0&&await J(b,{name:"testkit.cleanup",kind:"cleanup",summary:"stop managed runtimes and services"},()=>Ct(d,()=>u.cleanupAll()).catch(i=>{throw ue=i,$(Z(i).message||z(i),"infra"),i})),de.close()}Oe(c,s,pe)}}catch(s){T=Z(s),s!==ue&&$(T.message||z(s)),w()}U.finish(),g=T===null?0:u.isStopRequested()?130:1,I=Date.now(),K=e.map(s=>ge(N.get(s.name),t,I));const r=qe(K,n.allowEmpty===!0);await J(b,{name:"testkit.collect",kind:"collection",summary:"close and collect run logs"},()=>M.closeAll());const l=await gt(ce(K,I,u.isStopRequested()?"interrupted":T===null&&r===null?null:"failed"));return T!==null&&(g=1),K.some(s=>s.failed||s.infraFailed)&&(g=1),r!==null&&(g=1),l.shouldFailArchivePublication&&(g=1),l.shouldFailArchiveCleanup&&(g=1),(l.runArtifact.run.status==="interrupted"||u.isStopRequested()||L.committedFinalization?.artifactStatus==="interrupted")&&(g=Math.max(g,130)),{runArtifact:l.runArtifact,results:R.redact(K),exitCode:g,...T===null?{}:{failure:R.redact(T)},...D.result===null?{}:{maintenance:R.redact(D.result)}}}catch(r){const l=Z(r);$(l.message||z(r),"infra"),w(),U.finish(),u.beginFinalization(),g=u.isStopRequested()||L.committedFinalization?.artifactStatus==="interrupted"?130:1,I=Date.now(),K=e.map(F=>ge(N.get(F.name),t,I)),await ye(M);let s=j.artifacts;if(s===null)try{s=await Rt(ce(K,I,u.isStopRequested()?"interrupted":"failed"),L,j,l,se,oe)}catch{if(j.artifacts===null)throw r;s=j.artifacts}return(s.runArtifact.run.status==="interrupted"||u.isStopRequested()||L.committedFinalization?.artifactStatus==="interrupted")&&(g=Math.max(g,130)),{runArtifact:s.runArtifact,results:R.redact(K),exitCode:g,failure:R.redact(T??l),...D.result===null?{}:{maintenance:R.redact(D.result)}}}finally{u.dispose(),await ye(M),wt(d,n.environments?.observeRecord,v)}}async function Rt(e,a,o,n,m,k){const c=await a.finish("failed",p=>{const h=p.artifactStatus==="interrupted"?p:{artifactStatus:"failed",interruptReason:null},y=e.declaredEnvironmentKeys??[],t=vt({productDir:e.productDir,runId:e.runId,results:e.results,startedAt:e.startedAt,finishedAt:e.finishedAt,execution:e.execution,workerCount:e.workerCount,typeValues:e.selection.typeValues,suiteSelectors:e.selection.suiteSelectors,fileNames:e.selection.fileNames,groupFilter:e.selection.groupFilter,planning:e.selection.planning??null,archiveConfig:e.archiveConfig,declaredEnvironmentKeys:y,metadata:e.metadata,environment:e.environmentRecord.snapshot(),operationTimings:e.operationTimingRegistry?.listOperations()??[],runInterruptReason:h.interruptReason,runStatus:h.artifactStatus});t.finalizationFailure={...n};const E={runArtifact:Ne(e.productDir,t,{archiveConfig:e.archiveConfig,declaredEnvironmentKeys:y,redactKeys:e.environmentRecord.redactKeys,redactionTargets:e.environmentRecord.redactionTargets()})};return o.artifacts=E,E},"recovery",m);return k(c)}function wt(e,a,o){if(a!==void 0)try{a(e.snapshot())}catch(n){try{o?.warning?.(`Environment record observer failed: ${z(n)}`)}catch{}}}function St(e,a){return{...e,remove(o){const n=a.releaseFor(o.id);try{e.remove(o)}catch(m){throw n?.releaseFailed(m),m}n?.released()}}}async function Ct(e,a){try{await a()}catch(o){throw e.recordCleanup(o),o}e.recordCleanup(null)}async function ye(e){try{await e.closeAll()}catch{}}export function workerResultErrorsForOrchestrator(e){return e.status==="rejected"?[z(e.reason)]:[...new Set(e.value.unpersistedErrors)]}function Ft(e,a){return a===void 0?e:Qe(e,a)}function Et(e,a,o,n){return{vitest:n,productDir:e,fileTimeoutSeconds:o.fileTimeoutSeconds,ownerRoots:Object.fromEntries(a.map(m=>[m.name,m.roots]))}}export function resolveRunExecutionConfig(e,a){return ot({cli:{fileTimeoutSeconds:a.fileTimeoutSeconds,taskConcurrency:a.taskConcurrency},repo:e?{fileTimeoutSeconds:e.testkit.execution.fileTimeoutSeconds,taskConcurrency:e.testkit.execution.taskConcurrency}:void 0})}export function assignRuntimeGraphs(e,a){const o=e.filter(t=>t.suites.some(S=>S.files.length>0)),n=new Set(o.filter(t=>!a.has(t.config.name)&&Je(t.runtimeConfigs))),m=new Set([...n].map(t=>t.runtimeKey)),k=t=>!n.has(t)&&!a.has(t.config.name)&&he(t.runtimeConfigs),c=new Set(o.filter(t=>k(t)&&t.runtimeKey!=="").map(t=>t.runtimeKey)),p=new Set(o.map(t=>t.runtimeKey)),h=t=>t.runtimeKey!==""&&!m.has(t.runtimeKey)&&(k(t)||!c.has(t.runtimeKey))?t.runtimeKey:p.has(t.config.name)?`group:${t.config.name}`:t.config.name,y=o.map(t=>n.has(t)?t:{...t,runtimeKey:h(t)});return{plans:y,provisionedGraphKeys:m,staticGraphKeys:new Set(y.filter(t=>!m.has(t.runtimeKey)&&!a.has(t.config.name)&&he(t.runtimeConfigs)).map(t=>t.runtimeKey))}}export function prepareSchedulerPlan(e,a){const o=ct(e.config.testkit);return{graphKey:e.runtimeKey,owner:e.config.name,suites:e.suites.map(n=>({...n,tasks:n.files.map(m=>({file:m,hasSetup:a(m),...ut(o,n,m)}))}))}}export function collectServicePlans(e,a,o,n,m,k,c=null){return e.map(p=>{const h=p.uses.map(f=>{const C=a.get(f);if(C===void 0)throw new Error(`Group "${p.name}" uses unknown service "${f}"`);return C}),y=h.some(f=>f.testkit.database!==void 0)?"local":null,t=Te(p,o,n,m.fileNames||[],m);if(t.length===0)return c?.serviceSkipped?.(p,`no matching files (types=${o.join(",")||"all"} suites=${n.map(f=>f.raw).join(",")||"all"} files=${(m.fileNames||[]).join(",")||"all"})`),{config:p,dbBackend:y,execution:k,skipped:!0,suites:[],runtimeConfigs:[],runtimeNames:[],runtimeKey:null,uses:h};for(const f of t)for(const C of f.skippedFiles)c?.plannedSkip?.({serviceName:p.name,testType:f.testType,engineId:f.engineId,profileId:f.profileId,file:C.path,reason:C.reason});const S=Ee(h,a),E=S.map(f=>f.name).sort();return{config:p,dbBackend:y,execution:k,skipped:!1,suites:t,runtimeConfigs:S,runtimeNames:E,runtimeKey:E.join("|"),uses:h}})}
@@ -1 +1 @@
1
- import{matchesSelectedTypes as x,matchesSuiteSelectors as d}from"../../domain/suite-selection.js";import{PUBLIC_TEST_TYPES as m}from"../../domain/identifiers.js";import{resolveExecutionProfile as S}from"../../engines/catalog.js";export function collectSuites(e,l,a,o=[],r={}){const t=new Set(o.map(h)),f=[];let c=0;for(const n of[...e.suites].sort((i,s)=>k(i.testType)-k(s.testType))){const i=S(n),s=t.size===0?n.files:n.files.filter(g=>t.has(h(g)));if(!x(n.testType,l)||!d(n.testType,n.name,a)||s.length===0)continue;const{files:p,skippedFiles:u}=y(e,n.testType,n.name,s,r);f.push({...n,files:p,skippedFiles:u,orderIndex:c,weight:n.testkit?.weight||(i.taskBatching==="compatible"?Math.max(2,Math.max(1,p.length)):Math.max(1,p.length)),totalFileCount:s.length}),c+=1}return f}function k(e){return e===void 0?m.length:m.indexOf(e)}export function claimNextTask(e,l,a=()=>!0){if(e.length===0)return null;const o=[],r=[];for(let t=0;t<e.length;t+=1)l&&e[t].graphKey===l?o.push(t):r.push(t);for(const t of[...o,...r])if(a(e[t]))return e.splice(t,1)[0];return null}function y(e,l,a,o,r={}){if(r.ignoreSkipRules)return{files:o,skippedFiles:[]};const t=e.testkit.skip,f=(t?.suites||[]).find(i=>d(l,a,[i.selector]))?.reason||null,c=[],n=[];for(const i of o){const s=h(i),u=(e.testkit.fileMetadataByPath?.get(s)||null)?.skipReason||t?.fileReasonByPath?.get(s)||f;if(u){n.push({path:s,reason:u});continue}c.push(i)}return{files:c,skippedFiles:n}}function h(e){return String(e).split("\\").join("/")}
1
+ import{matchesSelectedTypes as x,matchesSuiteSelectors as d}from"../../config/loader/test-selection.js";import{PUBLIC_TEST_TYPES as m}from"../../domain/identifiers.js";import{resolveExecutionProfile as S}from"../../engines/catalog.js";export function collectSuites(e,l,a,o=[],r={}){const t=new Set(o.map(h)),f=[];let c=0;for(const n of[...e.suites].sort((i,s)=>k(i.testType)-k(s.testType))){const i=S(n),s=t.size===0?n.files:n.files.filter(g=>t.has(h(g)));if(!x(n.testType,l)||!d(n.testType,n.name,a)||s.length===0)continue;const{files:p,skippedFiles:u}=y(e,n.testType,n.name,s,r);f.push({...n,files:p,skippedFiles:u,orderIndex:c,weight:n.testkit?.weight||(i.taskBatching==="compatible"?Math.max(2,Math.max(1,p.length)):Math.max(1,p.length)),totalFileCount:s.length}),c+=1}return f}function k(e){return e===void 0?m.length:m.indexOf(e)}export function claimNextTask(e,l,a=()=>!0){if(e.length===0)return null;const o=[],r=[];for(let t=0;t<e.length;t+=1)l&&e[t].graphKey===l?o.push(t):r.push(t);for(const t of[...o,...r])if(a(e[t]))return e.splice(t,1)[0];return null}function y(e,l,a,o,r={}){if(r.ignoreSkipRules)return{files:o,skippedFiles:[]};const t=e.testkit.skip,f=(t?.suites||[]).find(i=>d(l,a,[i.selector]))?.reason||null,c=[],n=[];for(const i of o){const s=h(i),u=(e.testkit.fileMetadataByPath?.get(s)||null)?.skipReason||t?.fileReasonByPath?.get(s)||f;if(u){n.push({path:s,reason:u});continue}c.push(i)}return{files:c,skippedFiles:n}}function h(e){return String(e).split("\\").join("/")}
@@ -1 +1 @@
1
- import{resolveProductDir as g}from"../../process/files/product-dir.js";import{loadRunConfigs as k}from"../../config/loader/managed-configs.js";import{parseFileTimeoutOption as F}from"../../domain/file-timeout.js";import{parseSuiteOption as w,parseTypeOption as O}from"../../domain/suite-selection.js";import{resolveRequestedFiles as R}from"../file-selection.js";import{parseTaskConcurrencyOptions as T}from"../../config/loader/decode-execution.js";import{readEnvironmentFile as h}from"../environments/environment-file.js";import{hasSuppliedInput as E,projectSuppliedEnvironment as V,resolveSuppliedInput as j,suppliedBindings as C}from"../environments/supplied-input.js";import{resolveVitestSelection as I}from"../../engines/catalog.js";export async function buildRunRequest(e,l=null,o=process.cwd(),n=process.cwd(),p=process.env){const a=T(e.taskConcurrency,e.workers),f=e.fileTimeoutSeconds==null?null:F(e.fileTimeoutSeconds),c=O(e.type,l),u=w(e.suite),S=e.file??[],r=g(o,e.dir),m=R(S,r,n),s=e.supplied??{},i=E(s)?j(s,s.environmentFile===void 0?null:h(s.environmentFile,n),p):null,t=I(e.vitest??{},r,p),{groups:d,ownershipNotice:v,services:y}=await k({dir:r,group:e.group,testSelection:{group:e.group,fileNames:m,typeValues:c,suiteSelectors:u},vitest:i===null?t:{...t,host:{...t.host,...C(i)}}});return{groups:d,ownershipNotice:v,services:y,productDir:r,supplied:i===null?null:N(i,d),typeValues:c,suiteSelectors:u,runOptions:{vitest:t,allowEmpty:e.allowEmpty,fileNames:m,taskConcurrency:a,fileTimeoutSeconds:f,serviceFilter:e.group||null,ignoreSkipRules:e.ignoreSkipRules,skipSchemaSourceVerify:e.skipSchemaSourceVerify}}}function N(e,l){return Object.fromEntries(l.map(o=>[o.name,V(e,o.uses)]))}
1
+ import{resolveProductDir as y}from"../../process/files/product-dir.js";import{loadRunConfigs as g}from"../../config/loader/managed-configs.js";import{parseFileTimeoutOption as k}from"../../domain/file-timeout.js";import{parseSuiteOption as F,parseTypeOption as w}from"../../domain/suite-selection.js";import{resolveRequestedFiles as O}from"../file-selection.js";import{parseTaskConcurrencyOptions as R}from"../../config/loader/decode-execution.js";import{readEnvironmentFile as T}from"../environments/environment-file.js";import{hasSuppliedInput as h,projectSuppliedEnvironment as E,resolveSuppliedInput as V,suppliedBindings as j}from"../environments/supplied-input.js";import{resolveVitestSelection as C}from"../../engines/catalog.js";export async function buildRunRequest(e,l=null,o=process.cwd(),n=process.cwd(),p=process.env){const a=R(e.taskConcurrency,e.workers),f=e.fileTimeoutSeconds==null?null:k(e.fileTimeoutSeconds),c=w(e.type,l),u=F(e.suite),S=e.file??[],r=y(o,e.dir),m=O(S,r,n),s=e.supplied??{},i=h(s)?V(s,s.environmentFile===void 0?null:T(s.environmentFile,n),p):null,t=C(e.vitest??{},r,p),{groups:d,services:v}=await g({dir:r,group:e.group,testSelection:{group:e.group,fileNames:m,typeValues:c,suiteSelectors:u},vitest:i===null?t:{...t,host:{...t.host,...j(i)}}});return{groups:d,services:v,productDir:r,supplied:i===null?null:I(i,d),typeValues:c,suiteSelectors:u,runOptions:{vitest:t,allowEmpty:e.allowEmpty,fileNames:m,taskConcurrency:a,fileTimeoutSeconds:f,serviceFilter:e.group||null,ignoreSkipRules:e.ignoreSkipRules,skipSchemaSourceVerify:e.skipSchemaSourceVerify}}}function I(e,l){return Object.fromEntries(l.map(o=>[o.name,E(e,o.uses)]))}
@@ -1 +1 @@
1
- import{suiteKey as x}from"../../domain/suite-selection.js";import{mergeFailureDetails as b}from"./failure-details.js";import{posixPath as g}from"../../process/files/paths.js";export function buildServiceTrackers(e,r){const o=new Map;for(const t of e){const a=t.skipped?[]:t.suites.map(w),l={name:t.config.name,skipped:t.skipped===!0,suiteCount:a.length,suites:a,suitesByKey:new Map(a.map(n=>[n.key,n])),errors:[],errorSet:new Set,errorDetails:[],errorDetailSet:new Set,startedAt:r,firstTaskAt:null,lastTaskAt:null,totalTaskDurationMs:0};o.set(t.config.name,l)}return o}function w(e){const r=new Map;for(const o of e.files){const t=g(o);r.set(t,F(t,"not_run",null))}for(const o of e.skippedFiles??[])r.set(o.path,F(o.path,"skipped",o.reason));return{key:x(e),name:e.name,testType:e.testType,engineId:e.engineId,profileId:e.profileId,orderIndex:e.orderIndex,failedFiles:[],failedFileSet:new Set,fileResultsByPath:r,durationMs:0,error:null}}function F(e,r,o){return{path:e,failed:r==="failed",durationMs:0,error:null,reason:o,status:r,artifacts:[],infraFailed:!1,blockedByRuntime:!1,failureDetails:[],runtimeIsolation:null,runtimeBatch:null,runtimeResets:[]}}export function recordTaskOutcome(e,r,o,t=Date.now()){const a=e.get(r.serviceName);if(!a||a.skipped)return;const l=a.suitesByKey.get(r.suiteKey);if(!l)return;const n=P(o,r,t);a.firstTaskAt=a.firstTaskAt===null?n.startedAt:Math.min(a.firstTaskAt,n.startedAt),a.lastTaskAt=a.lastTaskAt===null?n.finishedAt:Math.max(a.lastTaskAt,n.finishedAt),a.totalTaskDurationMs+=n.durationMs,l.durationMs+=n.durationMs;const f=g(r.file),s=l.fileResultsByPath.get(f),d=s??F(f,n.status,n.reason);d.failed=n.status==="failed",d.durationMs=n.durationMs,d.error=n.error,d.reason=n.reason,d.status=n.status,d.artifacts=n.artifacts,d.infraFailed=n.infraFailed,d.blockedByRuntime=n.blockedByRuntime,d.failureDetails=n.failureDetails,d.runtimeIsolation=n.runtimeIsolation,d.runtimeBatch=n.runtimeBatch,d.runtimeResets=n.runtimeResets,s||l.fileResultsByPath.set(f,{...d}),n.status==="failed"&&!l.failedFileSet.has(r.file)&&(l.failedFileSet.add(r.file),l.failedFiles.push(r.file)),n.error&&!l.error&&(l.error=n.error)}function P(e,r,o){const t=e.durationMs??0,a=e.finishedAt??o;return{artifacts:e.artifacts??[],blockedByRuntime:e.blockedByRuntime??!1,durationMs:t,error:e.error??null,failureDetails:b(e.failureDetails??[]),finishedAt:a,infraFailed:e.infraFailed??!1,reason:e.reason??null,runtimeBatch:e.runtimeBatch??null,runtimeIsolation:e.runtimeIsolation??r.runtimeIsolation??null,runtimeResets:e.runtimeResets??[],startedAt:e.startedAt??Math.max(0,a-t),status:e.status}}export function recordGraphError(e,r,o,t=Date.now(),a={}){const l=r?.targetNames||[];for(const n of l){const f=e.get(n);f&&!f.skipped&&(addTrackerError(f,o,a),f.lastTaskAt=t)}}export function addTrackerError(e,r,o={}){const t=r.trim();if(!t)return;const a=o.classification??"product";e.errorSet.has(t)||(e.errorSet.add(t),e.errors.push(t));const l=`${a}\0${t}`;e.errorDetailSet.has(l)||(e.errorDetailSet.add(l),e.errorDetails.push({message:t,classification:a}))}export function finalizeServiceResult(e,r,o){if(!e||e.skipped)return{name:e?.name||"unknown",failed:!1,skipped:!0,suiteCount:0,completedSuiteCount:0,skippedSuiteCount:0,failedSuiteCount:0,totalFileCount:0,completedFileCount:0,passedFileCount:0,failedFileCount:0,skippedFileCount:0,notRunFileCount:0,durationMs:0,totalTaskDurationMs:0,suites:[],errors:[],errorDetails:[],infraFailed:!1,blockedByRuntime:!1};const t=[...e.suites].sort((i,u)=>i.orderIndex-u.orderIndex||i.name.localeCompare(u.name)).map(i=>z(i)),a=t.filter(i=>i.completedFileCount+i.skippedFileCount===i.fileCount).length,l=t.filter(i=>i.skippedFileCount===i.fileCount&&i.fileCount>0).length,n=t.filter(i=>i.failedFileCount>0).length,f=t.reduce((i,u)=>i+u.fileCount,0),s=t.reduce((i,u)=>i+u.completedFileCount,0),d=t.reduce((i,u)=>i+u.failedFileCount,0),y=t.reduce((i,u)=>i+u.passedFileCount,0),C=t.reduce((i,u)=>i+u.skippedFileCount,0),p=f-s-C,m=t.reduce((i,u)=>i+u.files.filter(c=>c.infraFailed===!0).length,0),k=t.reduce((i,u)=>i+u.files.filter(c=>c.blockedByRuntime===!0).length,0),R=t.reduce((i,u)=>i+u.files.filter(c=>c.status==="cancelled"||c.status==="interrupted").length,0),T=p>0&&R===p,M=p>0&&m===p,h=e.errorDetails,B=h.filter(i=>i.classification!=="infra").length,D=h.filter(i=>i.classification==="infra").length,I=m>0||D>0,S=k>0,A=e.firstTaskAt!==null&&e.lastTaskAt!==null?Math.max(0,e.lastTaskAt-e.firstTaskAt):Math.max(0,o-r);return{name:e.name,failed:n>0||B>0||a<e.suiteCount&&!T&&!M,infraFailed:I,blockedByRuntime:S,infraFailedFileCount:m,blockedByRuntimeFileCount:k,skipped:!1,suiteCount:e.suiteCount,completedSuiteCount:a,skippedSuiteCount:l,failedSuiteCount:n,totalFileCount:f,completedFileCount:s,passedFileCount:y,failedFileCount:d,skippedFileCount:C,notRunFileCount:p,durationMs:A,totalTaskDurationMs:e.totalTaskDurationMs,suites:t,errors:e.errors,errorDetails:h}}function z(e){const r=[...e.fileResultsByPath.values()].sort((s,d)=>s.path.localeCompare(d.path)).map(s=>({path:s.path,failed:s.failed,status:s.status,durationMs:s.durationMs,error:s.error,reason:s.reason,...s.infraFailed?{infraFailed:!0}:{},...s.blockedByRuntime?{blockedByRuntime:!0}:{},...s.failureDetails.length>0?{failureDetails:s.failureDetails}:{},...s.artifacts.length>0?{artifacts:s.artifacts}:{},...s.runtimeIsolation?{runtimeIsolation:s.runtimeIsolation}:{},...s.runtimeBatch?{runtimeBatch:s.runtimeBatch}:{},...s.runtimeResets.length>0?{runtimeResets:s.runtimeResets}:{}})),o=r.length,t=r.filter(s=>s.status==="passed").length,a=r.filter(s=>s.status==="failed").length,l=r.filter(s=>s.status==="skipped").length,n=t+a,f=o-n-l;return{name:e.name,...e.testType===void 0?{}:{testType:e.testType},engineId:e.engineId,profileId:e.profileId,failed:a>0,fileCount:o,completedFileCount:n,passedFileCount:t,failedFileCount:a,skippedFileCount:l,notRunFileCount:f,failedFiles:e.failedFiles,durationMs:e.durationMs,error:e.error,files:r}}
1
+ import{suiteKey as x}from"../../domain/suite-selection.js";import{mergeFailureDetails as b}from"./failure-details.js";import{posixPath as g}from"../../process/files/paths.js";export function buildServiceTrackers(e,r){const o=new Map;for(const t of e){const a=t.skipped?[]:t.suites.map(w),l={name:t.config.name,skipped:t.skipped===!0,suiteCount:a.length,suites:a,suitesByKey:new Map(a.map(n=>[n.key,n])),errors:[],errorSet:new Set,errorDetails:[],errorDetailSet:new Set,startedAt:r,firstTaskAt:null,lastTaskAt:null,totalTaskDurationMs:0};o.set(t.config.name,l)}return o}function w(e){const r=new Map;for(const o of e.files){const t=g(o);r.set(t,h(t,"not_run",null))}for(const o of e.skippedFiles??[])r.set(o.path,h(o.path,"skipped",o.reason));return{key:x(e),name:e.name,testType:e.testType,engineId:e.engineId,profileId:e.profileId,orderIndex:e.orderIndex,failedFiles:[],failedFileSet:new Set,fileResultsByPath:r,durationMs:0,error:null}}function h(e,r,o){return{path:e,failed:r==="failed",durationMs:0,error:null,reason:o,status:r,artifacts:[],infraFailed:!1,blockedByRuntime:!1,failureDetails:[],runtimeIsolation:null,runtimeBatch:null,runtimeResets:[]}}export function recordTaskOutcome(e,r,o,t=Date.now()){const a=e.get(r.serviceName);if(!a||a.skipped)return;const l=a.suitesByKey.get(r.suiteKey);if(!l)return;const n=P(o,r,t);a.firstTaskAt=a.firstTaskAt===null?n.startedAt:Math.min(a.firstTaskAt,n.startedAt),a.lastTaskAt=a.lastTaskAt===null?n.finishedAt:Math.max(a.lastTaskAt,n.finishedAt),a.totalTaskDurationMs+=n.durationMs,l.durationMs+=n.durationMs;const f=g(r.file),s=l.fileResultsByPath.get(f),d=s??h(f,n.status,n.reason);d.failed=n.status==="failed",d.durationMs=n.durationMs,d.error=n.error,d.reason=n.reason,d.status=n.status,d.artifacts=n.artifacts,d.infraFailed=n.infraFailed,d.blockedByRuntime=n.blockedByRuntime,d.failureDetails=n.failureDetails,d.runtimeIsolation=n.runtimeIsolation,d.runtimeBatch=n.runtimeBatch,d.runtimeResets=n.runtimeResets,s||l.fileResultsByPath.set(f,{...d}),n.status==="failed"&&!l.failedFileSet.has(r.file)&&(l.failedFileSet.add(r.file),l.failedFiles.push(r.file)),n.error&&!l.error&&(l.error=n.error)}function P(e,r,o){const t=e.durationMs??0,a=e.finishedAt??o;return{artifacts:e.artifacts??[],blockedByRuntime:e.blockedByRuntime??!1,durationMs:t,error:e.error??null,failureDetails:b(e.failureDetails??[]),finishedAt:a,infraFailed:e.infraFailed??!1,reason:e.reason??null,runtimeBatch:e.runtimeBatch??null,runtimeIsolation:e.runtimeIsolation??r.runtimeIsolation??null,runtimeResets:e.runtimeResets??[],startedAt:e.startedAt??Math.max(0,a-t),status:e.status}}export function recordGraphError(e,r,o,t=Date.now(),a={}){const l=r?.targetNames||[];for(const n of l){const f=e.get(n);f&&!f.skipped&&(addTrackerError(f,o,a),f.lastTaskAt=t)}}export function addTrackerError(e,r,o={}){const t=r.trim();if(!t)return;const a=o.classification??"product";e.errorSet.has(t)||(e.errorSet.add(t),e.errors.push(t));const l=`${a}\0${t}`;e.errorDetailSet.has(l)||(e.errorDetailSet.add(l),e.errorDetails.push({message:t,classification:a}))}export function finalizeServiceResult(e,r,o){if(!e||e.skipped)return{name:e?.name||"unknown",failed:!1,skipped:!0,suiteCount:0,completedSuiteCount:0,skippedSuiteCount:0,failedSuiteCount:0,totalFileCount:0,completedFileCount:0,passedFileCount:0,failedFileCount:0,skippedFileCount:0,notRunFileCount:0,durationMs:0,totalTaskDurationMs:0,suites:[],errors:[],errorDetails:[],infraFailed:!1,blockedByRuntime:!1};const t=[...e.suites].sort((i,u)=>i.orderIndex-u.orderIndex||i.name.localeCompare(u.name)).map(i=>z(i)),a=t.filter(i=>i.completedFileCount+i.skippedFileCount===i.fileCount).length,l=t.filter(i=>i.skippedFileCount===i.fileCount&&i.fileCount>0).length,n=t.filter(i=>i.failedFileCount>0).length,f=t.reduce((i,u)=>i+u.fileCount,0),s=t.reduce((i,u)=>i+u.completedFileCount,0),d=t.reduce((i,u)=>i+u.failedFileCount,0),R=t.reduce((i,u)=>i+u.passedFileCount,0),C=t.reduce((i,u)=>i+u.skippedFileCount,0),p=f-s-C,m=t.reduce((i,u)=>i+u.files.filter(c=>c.infraFailed===!0).length,0),k=t.reduce((i,u)=>i+u.files.filter(c=>c.blockedByRuntime===!0).length,0),y=t.reduce((i,u)=>i+u.files.filter(c=>c.status==="cancelled"||c.status==="interrupted").length,0),T=p>0&&y===p,M=p>0&&m===p,F=e.errorDetails,B=F.filter(i=>i.classification!=="infra").length,D=F.filter(i=>i.classification==="infra").length,I=m>0||D>0,S=k>0,A=e.firstTaskAt!==null&&e.lastTaskAt!==null?Math.max(0,e.lastTaskAt-e.firstTaskAt):Math.max(0,o-r);return{name:e.name,failed:n>0||B>0||a<e.suiteCount&&!T&&!M,infraFailed:I,blockedByRuntime:S,infraFailedFileCount:m,blockedByRuntimeFileCount:k,skipped:!1,suiteCount:e.suiteCount,completedSuiteCount:a,skippedSuiteCount:l,failedSuiteCount:n,totalFileCount:f,completedFileCount:s,passedFileCount:R,failedFileCount:d,skippedFileCount:C,notRunFileCount:p,durationMs:A,totalTaskDurationMs:e.totalTaskDurationMs,suites:t,errors:e.errors,errorDetails:F}}function z(e){const r=[...e.fileResultsByPath.values()].sort((s,d)=>s.path.localeCompare(d.path)).map(s=>({path:s.path,failed:s.failed,status:s.status,durationMs:s.durationMs,error:s.error,reason:s.reason,...s.infraFailed?{infraFailed:!0}:{},...s.blockedByRuntime?{blockedByRuntime:!0}:{},...s.failureDetails.length>0?{failureDetails:s.failureDetails}:{},...s.artifacts.length>0?{artifacts:s.artifacts}:{},...s.runtimeIsolation?{runtimeIsolation:s.runtimeIsolation}:{},...s.runtimeBatch?{runtimeBatch:s.runtimeBatch}:{},...s.runtimeResets.length>0?{runtimeResets:s.runtimeResets}:{}})),o=r.length,t=r.filter(s=>s.status==="passed").length,a=r.filter(s=>s.status==="failed").length,l=r.filter(s=>s.status==="skipped").length,n=t+a,f=o-n-l;return{name:e.name,...e.testType===void 0?{}:{testType:e.testType},engineId:e.engineId,profileId:e.profileId,failed:a>0,fileCount:o,completedFileCount:n,passedFileCount:t,failedFileCount:a,skippedFileCount:l,notRunFileCount:f,failedFiles:e.failedFiles,durationMs:e.durationMs,error:e.error,files:r}}export function resolveRunGateFailure(e,r=!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,a)=>t+a.completedFileCount,0)===0&&!r?"no runnable test files executed":null}
@@ -1 +1 @@
1
- import{resolveCommittedRunFinalization as z}from"../../evidence/finalization.js";import{collectTestkitStorageInventory as T,writeRunArtifact as k}from"../../evidence/index.js";import{buildRunArtifact as w}from"../../evidence/reporting.js";import{isUnknownRecord as x}from"../../kernel/unknown-values.js";export async function finalizeRunArtifacts({productDir:t,runId:e,results:i,startedAt:a,finishedAt:u,execution:c,workerCount:r,selection:n,archiveConfig:d=null,declaredEnvironmentKeys:m=[],metadata:F,environmentRecord:p,operationTimingRegistry:b=null,reporter:h,runInterruptReason:g=null,runStatus:l=null,publishRunFinalization:A}){const o=w({productDir:t,runId:e,results:i,startedAt:a,finishedAt:u,execution:c,workerCount:r,typeValues:n.typeValues,suiteSelectors:n.suiteSelectors,fileNames:n.fileNames,groupFilter:n.groupFilter,planning:n.planning??null,archiveConfig:d,declaredEnvironmentKeys:m,metadata:F,environment:p.snapshot(),operationTimings:b?.listOperations()??[],runInterruptReason:g,runStatus:l}),S=O(o.storage),v=T(t);o.storage={...S??{},testkit:v};const R=l==="failed"||i.some(s=>s.failed||s.infraFailed)?"failed":"passed",y=s=>{N(o,s);const f=C({archiveConfig:d,declaredEnvironmentKeys:m,productDir:t,redactKeys:p.redactKeys,redactionTargets:p.redactionTargets(),runArtifact:o});return h?.runSummary?.(i,u-a,f.runArtifact.run.status==="interrupted"?"interrupted":f.runArtifact.run.status==="failed"?"failed":"passed"),f};return A?await A(R,y):y(z(R,{interrupted:l==="interrupted",interruptReason:l==="interrupted"?g:null}))}function C({archiveConfig:t,declaredEnvironmentKeys:e,productDir:i,redactKeys:a,redactionTargets:u,runArtifact:c}){const r=k(i,c,{archiveConfig:t,declaredEnvironmentKeys:e,redactKeys:a,redactionTargets:u});return{runArtifact:r,shouldFailArchiveCleanup:r.archive.cleanup?.status==="failed",shouldFailArchivePublication:r.archive.publication.status==="failed"&&r.archive.publication.required===!0}}function N(t,e){if(t.run.status=e.artifactStatus,e.artifactStatus!=="interrupted"){delete t.run.interruptReason;return}t.run.interruptReason=e.interruptReason??"run interrupted"}function O(t){return x(t)?t:null}
1
+ import{resolveCommittedRunFinalization as k}from"../../evidence/finalization.js";import{writeRunArtifact as z}from"../../evidence/artifacts.js";import{collectTestkitStorageInventory as T}from"../../evidence/index.js";import{buildRunArtifact as w}from"../../evidence/reporting.js";import{isUnknownRecord as x}from"../../kernel/unknown-values.js";import{formatError as C}from"../../kernel/error-serialization.js";export async function finalizeRunArtifacts({productDir:t,runId:r,results:i,startedAt:a,finishedAt:u,execution:c,workerCount:e,selection:n,archiveConfig:d=null,declaredEnvironmentKeys:m=[],metadata:h,environmentRecord:f,operationTimingRegistry:F=null,reporter:b,runInterruptReason:g=null,runStatus:o=null,publishRunFinalization:y}){const l=w({productDir:t,runId:r,results:i,startedAt:a,finishedAt:u,execution:c,workerCount:e,typeValues:n.typeValues,suiteSelectors:n.suiteSelectors,fileNames:n.fileNames,groupFilter:n.groupFilter,planning:n.planning??null,archiveConfig:d,declaredEnvironmentKeys:m,metadata:h,environment:f.snapshot(),operationTimings:F?.listOperations()??[],runInterruptReason:g,runStatus:o}),v=O(l.storage),S=P(t);l.storage={...v??{},testkit:S};const A=o==="failed"||i.some(s=>s.failed||s.infraFailed)?"failed":"passed",R=s=>{N(l,s);const p=I({archiveConfig:d,declaredEnvironmentKeys:m,productDir:t,redactKeys:f.redactKeys,redactionTargets:f.redactionTargets(),runArtifact:l});return b?.runSummary?.(i,u-a,p.runArtifact.run.status==="interrupted"?"interrupted":p.runArtifact.run.status==="failed"?"failed":"passed"),p};return y?await y(A,R):R(k(A,{interrupted:o==="interrupted",interruptReason:o==="interrupted"?g:null}))}function I({archiveConfig:t,declaredEnvironmentKeys:r,productDir:i,redactKeys:a,redactionTargets:u,runArtifact:c}){const e=z(i,c,{archiveConfig:t,declaredEnvironmentKeys:r,redactKeys:a,redactionTargets:u});return{runArtifact:e,shouldFailArchiveCleanup:e.archive.cleanup?.status==="failed",shouldFailArchivePublication:e.archive.publication.status==="failed"&&e.archive.publication.required===!0}}function N(t,r){if(t.run.status=r.artifactStatus,r.artifactStatus!=="interrupted"){delete t.run.interruptReason;return}t.run.interruptReason=r.interruptReason??"run interrupted"}function O(t){return x(t)?t:null}function P(t){try{return T(t,{rootRelativePath:".testkit/results"})}catch(r){return{status:"failed",error:C(r)}}}
@@ -0,0 +1 @@
1
+ import{resolveCommittedRunFinalization as l}from"../../evidence/finalization.js";export function createRunFinisher(e){let i=null;return{get committedFinalization(){return i},finish(n,a,o,r){return e.finalize(o,async t=>{i??=l(n,t.commit(n==="failed"?"failed":"finished"));const m=a(i);return{maintenance:r(await t.maintain()),sealed:m}})}}}
@@ -1 +1 @@
1
- import{isUnknownRecord as u}from"../../../kernel/unknown-values.js";export const MAX_RUNTIME_NODE_SETUP_DATA_BYTES=1024*1024,MAX_RUNTIME_NODE_SETUP_SIDECARS=16;export function isRuntimeNodeSetupWorkerRequest(e){return u(e)&&typeof e.sessionId=="string"&&e.sessionId.length>0&&typeof e.modulePath=="string"}export function isRuntimeNodeSetupParentMessage(e){return _(e)&&(e.type==="cleanup"||e.type==="abort"&&u(e.error)&&S(e.kind))}export function readRuntimeNodeSetupWorkerMessage(e){if(!_(e))return null;switch(e.type){case"abort-acknowledged":break;case"cleanup-started":if(!Array.isArray(e.owners))return null;break;case"cleanup-progress":if(e.owner!=="consumer"&&e.owner!=="testkit"||!Number.isSafeInteger(e.remainingCallbacks))return null;break;case"ready":if(!("setupData"in e)||!Array.isArray(e.sidecars))return null;break;case"sidecar-started":if(!u(e.sidecar))return null;break;case"setup-error":if(!u(e.error)||!S(e.kind)||!Array.isArray(e.cleanupFailures))return null;break;case"aborted":case"cleanup-complete":if(!Array.isArray(e.cleanupFailures))return null;break;default:return null}return e}function S(e){return e==="cancelled"||e==="deadline"||e==="infra"||e==="product"}function _(e){return u(e)&&typeof e.type=="string"&&typeof e.sessionId=="string"&&e.sessionId.length>0}export function serializeRuntimeNodeSetupError(e){const r=g(e,"message"),n=g(e,"name"),i=g(e,"stack");return{message:typeof r=="string"?r:e===null||typeof e!="object"&&typeof e!="function"?String(e):"Runtime node setup failed",name:typeof n=="string"&&n?n:"Error",...typeof i=="string"?{stack:i}:{}}}export function serializeRuntimeNodeSetupCleanupFailures(e){return e.map(({error:r,owner:n})=>({error:serializeRuntimeNodeSetupError(r),owner:n}))}function g(e,r){const n=new Set;try{for(;u(e)&&!n.has(e);){n.add(e);const i=Object.getOwnPropertyDescriptor(e,r);if(i)return"value"in i?i.value:void 0;e=Object.getPrototypeOf(e)}}catch{}}export function normalizeRuntimeNodeSetupData(e){const r={},n=new Set,i=[{value:e===void 0?{}:e,path:"$",target:r,key:"data"}];let h=0;const a=(p,t)=>{throw new TypeError(`Runtime node setup data at ${p} must be plain JSON: ${t}`)},y=(p,t)=>{h+=p,h>MAX_RUNTIME_NODE_SETUP_DATA_BYTES&&a(t,`must encode to no more than ${MAX_RUNTIME_NODE_SETUP_DATA_BYTES.toString()} bytes`)};for(;i.length;){const p=i.pop();if(!p)break;if("leave"in p){n.delete(p.leave);continue}const{value:t,path:s,target:b,key:k}=p;if(t===null||typeof t=="string"||typeof t=="boolean"||typeof t=="number"&&Number.isFinite(t)){y(Buffer.byteLength(JSON.stringify(t)),s),Object.defineProperty(b,k,{value:t,enumerable:!0,writable:!0,configurable:!0});continue}typeof t!="object"&&a(s,`unsupported ${typeof t} value`),n.has(t)&&a(s,"circular reference");const c=Array.isArray(t),w=Object.getPrototypeOf(t);!c&&w!==Object.prototype&&w!==null&&a(s,"expected a plain object");const l=Reflect.ownKeys(t).filter(o=>!c||o!=="length");c&&l.length!==t.length&&a(s,"sparse array or extra array properties");const R=c?[]:{};Object.defineProperty(b,k,{value:R,enumerable:!0,writable:!0,configurable:!0}),y(2+Math.max(0,l.length-1),s),n.add(t),i.push({leave:t});const m=[];for(const o of l){typeof o!="string"&&a(s,"symbol key");const f=c?`${s}[${o}]`:/^[A-Za-z_$][\w$]*$/u.test(o)?`${s}.${o}`:`${s}[${JSON.stringify(o)}]`;c&&(!/^(?:0|[1-9]\d*)$/u.test(o)||Number(o)>=t.length)&&a(f,"extra array property");const d=Object.getOwnPropertyDescriptor(t,o);(!d||!("value"in d)||!d.enumerable)&&a(f,"expected an enumerable data property"),c||y(Buffer.byteLength(JSON.stringify(o))+1,f),m.push({value:d.value,path:f,target:R,key:o})}for(let o=m.length-1;o>=0;o-=1)i.push(m[o])}return r.data}export function normalizeRuntimeNodeSetupSidecarId(e){const r=typeof e=="string"&&e.trim()?e.trim():"http-capture";if(!/^[A-Za-z0-9._-]+$/u.test(r))throw new TypeError("Runtime node setup sidecar id must contain only letters, numbers, dot, underscore, or hyphen");return r}export const HTTP_CAPTURE_OPERATION_PREFIX="/__testkit__/";export function normalizeRuntimeNodeSetupSidecarPath(e){const r=typeof e=="string"&&e.trim()?e.trim():"/capture";if(!r.startsWith("/")||r.startsWith("//")||/[\\?#]/u.test(r))throw new TypeError("Runtime node setup sidecar path must be an absolute URL path");if(r.startsWith(HTTP_CAPTURE_OPERATION_PREFIX))throw new TypeError(`Runtime node setup sidecar path may not start with ${HTTP_CAPTURE_OPERATION_PREFIX}`);return r}export function httpCaptureRedactionTargets({requestsUrl:e,resetUrl:r}){return[e,r].flatMap(n=>typeof n=="string"?[/\/__testkit__\/([A-Za-z0-9_-]+)\/requests$/u.exec(n)?.[1]??n]:[])}
1
+ import{isUnknownRecord as c}from"../../../kernel/unknown-values.js";export const MAX_RUNTIME_NODE_SETUP_DATA_BYTES=1024*1024,MAX_RUNTIME_NODE_SETUP_SIDECARS=16;export function isRuntimeNodeSetupWorkerRequest(e){return c(e)&&typeof e.sessionId=="string"&&e.sessionId.length>0&&typeof e.modulePath=="string"}export function isRuntimeNodeSetupParentMessage(e){return R(e)&&(e.type==="cleanup"||e.type==="abort"&&c(e.error)&&S(e.kind))}export function readRuntimeNodeSetupWorkerMessage(e){if(!R(e))return null;switch(e.type){case"abort-acknowledged":break;case"cleanup-started":if(!Array.isArray(e.owners))return null;break;case"cleanup-progress":if(e.owner!=="consumer"&&e.owner!=="testkit"||!Number.isSafeInteger(e.remainingCallbacks))return null;break;case"ready":if(!("setupData"in e)||!Array.isArray(e.sidecars))return null;break;case"sidecar-started":if(!c(e.sidecar))return null;break;case"setup-error":if(!c(e.error)||!S(e.kind)||!Array.isArray(e.cleanupFailures))return null;break;case"aborted":case"cleanup-complete":if(!Array.isArray(e.cleanupFailures))return null;break;default:return null}return e}function S(e){return e==="cancelled"||e==="deadline"||e==="infra"||e==="product"}function R(e){return c(e)&&typeof e.type=="string"&&typeof e.sessionId=="string"&&e.sessionId.length>0}export function serializeRuntimeNodeSetupError(e){const t=g(e,"message"),n=g(e,"name"),i=g(e,"stack");return{message:typeof t=="string"?t:e===null||typeof e!="object"&&typeof e!="function"?String(e):"Runtime node setup failed",name:typeof n=="string"&&n?n:"Error",...typeof i=="string"?{stack:i}:{}}}export function serializeRuntimeNodeSetupCleanupFailures(e){return e.map(({error:t,owner:n})=>({error:serializeRuntimeNodeSetupError(t),owner:n}))}function g(e,t){const n=new Set;try{for(;c(e)&&!n.has(e);){n.add(e);const i=Object.getOwnPropertyDescriptor(e,t);if(i)return"value"in i?i.value:void 0;e=Object.getPrototypeOf(e)}}catch{}}export function normalizeRuntimeNodeSetupData(e){const t={},n=new Set,i=[{value:e===void 0?{}:e,path:"$",target:t,key:"data"}];let h=0;const a=(p,r)=>{throw new TypeError(`Runtime node setup data at ${p} must be plain JSON: ${r}`)},y=(p,r)=>{h+=p,h>MAX_RUNTIME_NODE_SETUP_DATA_BYTES&&a(r,`must encode to no more than ${MAX_RUNTIME_NODE_SETUP_DATA_BYTES.toString()} bytes`)};for(;i.length;){const p=i.pop();if(!p)break;if("leave"in p){n.delete(p.leave);continue}const{value:r,path:s,target:b,key:_}=p;if(r===null||typeof r=="string"||typeof r=="boolean"||typeof r=="number"&&Number.isFinite(r)){y(Buffer.byteLength(JSON.stringify(r)),s),Object.defineProperty(b,_,{value:r,enumerable:!0,writable:!0,configurable:!0});continue}typeof r!="object"&&a(s,`unsupported ${typeof r} value`),n.has(r)&&a(s,"circular reference");const u=Array.isArray(r),w=Object.getPrototypeOf(r);!u&&w!==Object.prototype&&w!==null&&a(s,"expected a plain object");const l=Reflect.ownKeys(r).filter(o=>!u||o!=="length");u&&l.length!==r.length&&a(s,"sparse array or extra array properties");const k=u?[]:{};Object.defineProperty(b,_,{value:k,enumerable:!0,writable:!0,configurable:!0}),y(2+Math.max(0,l.length-1),s),n.add(r),i.push({leave:r});const m=[];for(const o of l){typeof o!="string"&&a(s,"symbol key");const f=u?`${s}[${o}]`:/^[A-Za-z_$][\w$]*$/u.test(o)?`${s}.${o}`:`${s}[${JSON.stringify(o)}]`;u&&(!/^(?:0|[1-9]\d*)$/u.test(o)||Number(o)>=r.length)&&a(f,"extra array property");const d=Object.getOwnPropertyDescriptor(r,o);(!d||!("value"in d)||!d.enumerable)&&a(f,"expected an enumerable data property"),u||y(Buffer.byteLength(JSON.stringify(o))+1,f),m.push({value:d.value,path:f,target:k,key:o})}for(let o=m.length-1;o>=0;o-=1)i.push(m[o])}return t.data}export function normalizeRuntimeNodeSetupSidecarId(e){const t=typeof e=="string"&&e.trim()?e.trim():"http-capture";if(!/^[A-Za-z0-9._-]+$/u.test(t))throw new TypeError("Runtime node setup sidecar id must contain only letters, numbers, dot, underscore, or hyphen");return t}export const HTTP_CAPTURE_OPERATION_PREFIX="/__testkit__/";export function normalizeRuntimeNodeSetupSidecarPath(e){const t=typeof e=="string"&&e.trim()?e.trim():"/capture";if(!t.startsWith("/")||t.startsWith("//")||/[\\?#]/u.test(t)||Array.from(t).some(n=>n<" "))throw new TypeError("Runtime node setup sidecar path must be an absolute URL path");if(t.startsWith(HTTP_CAPTURE_OPERATION_PREFIX))throw new TypeError(`Runtime node setup sidecar path may not start with ${HTTP_CAPTURE_OPERATION_PREFIX}`);if(t.split("/").some(n=>/^(?:\.|%2e){1,2}$/iu.test(n)))throw new TypeError("Runtime node setup sidecar path may not contain . or .. segments");return t}export function httpCaptureRedactionTargets({requestsUrl:e,resetUrl:t,url:n}){return[...[e,t].flatMap(i=>typeof i=="string"?[/\/__testkit__\/([A-Za-z0-9_-]+)\/requests$/u.exec(i)?.[1]??i]:[]),...typeof n=="string"?A(n):[]]}function A(e){const t=/^https?:\/\/[^/?#]*\/__testkit__\/([A-Za-z0-9_-]+)\//iu.exec(e)?.[1];return[...new Set([t,x(e)])].filter(n=>n!==void 0)}function x(e){try{return/^\/__testkit__\/([A-Za-z0-9_-]+)\//u.exec(new URL(e).pathname)?.[1]}catch{return}}
@@ -1 +1 @@
1
- import{classifyRuntimeNodeSetupFailure as j,createSetupFailure as E}from"./runtime-node-setup-errors.js";import{randomBytes as Y}from"node:crypto";import G from"node:http";import{pathToFileURL as W}from"node:url";import{parentPort as M,workerData as J}from"node:worker_threads";import{HTTP_CAPTURE_OPERATION_PREFIX as $,isRuntimeNodeSetupParentMessage as Q,isRuntimeNodeSetupWorkerRequest as K,MAX_RUNTIME_NODE_SETUP_SIDECARS as F,normalizeRuntimeNodeSetupData as V,normalizeRuntimeNodeSetupSidecarId as Z,normalizeRuntimeNodeSetupSidecarPath as ee,serializeRuntimeNodeSetupError as q,serializeRuntimeNodeSetupCleanupFailures as te}from"./runtime-node-setup-protocol.js";import{isUnknownRecord as B}from"../../../kernel/unknown-values.js";import{toError as re,errorMessage as ne}from"../../../kernel/error-serialization.js";const oe=1024*1024,se=100,ae=8*1024*1024,ie=8*1024*1024,ue=1e3,de=256,s=Re(J),H=be(),p=new AbortController,k=[],C=new Set,A=[];let P=null,D=0,u="starting",g=null;H.on("message",e=>{le(e)}),L(),ce();async function ce(){try{const t=await import(`${W(s.modulePath).href}?testkit=${encodeURIComponent(s.sessionId)}`);I();const r=Te(t);if(!r)throw new TypeError(`Runtime node setup "${s.moduleRelativePath}" must export a setup function`);const n=await r(pe()),o=he(n);o&&k.push({callback:o,owner:"consumer"}),I();const c=V(we(n));S(),u="ready",d({sessionId:s.sessionId,setupData:c,sidecars:A.map(f=>({...f})),type:"ready"})}catch(e){const t=j(e),r=await _();u="cleaned",S(),d({cleanupFailures:r,error:q(e),kind:t,sessionId:s.sessionId,type:"setup-error"})}}async function le(e){if(!Q(e)||e.sessionId!==s.sessionId){await O(E("infra","Runtime node setup worker received an invalid parent message"));return}if(e.type==="abort"){if(p.abort(E(e.kind,e.error.message)),d({sessionId:s.sessionId,type:"abort-acknowledged"}),u==="ready"){u="cleaning";const r=await _();u="cleaned",S(),d({cleanupFailures:r,sessionId:s.sessionId,type:"aborted"})}return}if(u==="cleaned"){d({cleanupFailures:[],sessionId:s.sessionId,type:"cleanup-complete"});return}if(u!=="ready"){await O(E("infra",`Runtime node setup cleanup is invalid during phase "${u}"`));return}u="cleaning";const t=await _();u="cleaned",S(),d({cleanupFailures:t,sessionId:s.sessionId,type:"cleanup-complete"})}async function O(e){p.abort(e);const t=await _();u="cleaned",S(),d({cleanupFailures:t,error:q(e),kind:"infra",sessionId:s.sessionId,type:"setup-error"})}function pe(){return{deadlineMs:s.deadlineMs,env:{...s.runtimeEnv},leaseDir:s.leaseDir,productDir:s.productDir,serviceName:s.serviceName,sidecars:{httpCapture(e={}){if(I(),A.length+D>=F)return Promise.reject(new RangeError(`Runtime node setup may create at most ${F.toString()} sidecars`));D+=1;const t=fe(e).then(r=>(k.push({callback:r.cleanup,owner:"testkit"}),A.push(r.handle),d({sessionId:s.sessionId,sidecar:{...r.handle},type:"sidecar-started"}),I(),r.handle)).finally(()=>{D-=1,C.delete(t)});return C.add(t),t}},signal:p.signal,testFile:s.testFile}}async function fe(e){const t=[],r=new Set;let n=0;const o=Z(e.id),c=ee(e.path),f=z(e.maxRequests,se,ue,"maxRequests"),m=z(e.maxBodyBytes,oe,ae,"maxBodyBytes"),b=Se(e.status,204),l=`${$}${Y(24).toString("base64url")}/requests`,a=G.createServer((i,h)=>{if(i.url===l){i.method==="GET"?R(h,200,{requests:t}):i.method==="DELETE"?(t.splice(0),n=0,R(h,200,{ok:!0})):R(h,405,{error:"HTTP capture requests allow only GET and DELETE"});return}if(i.url?.startsWith($)===!0){R(h,404,{error:"Unknown HTTP capture operation"});return}ye(i,m).then(w=>{const U={bodyBytes:w.bodyBytes,bodyText:w.retainedBody.toString("utf8"),bodyTruncated:w.bodyBytes>w.retainedBody.length,headers:i.headers,method:i.method??null,path:i.url??null,receivedAt:new Date().toISOString()};for(n+=Buffer.byteLength(U.bodyText),t.push(U);t.length>f||n>ie;){const N=t.shift();if(!N)break;n-=Buffer.byteLength(N.bodyText)}R(h,b,{ok:!0})}).catch(w=>{R(h,500,{error:ne(w)})})});a.maxConnections=de,a.requestTimeout=3e4,a.headersTimeout=3e4,a.keepAliveTimeout=1e3,a.on("connection",i=>{r.add(i),i.once("close",()=>r.delete(i))}),await me(a);const y=a.address();if(y===null||typeof y=="string")throw await x(a,r),new Error("Runtime node HTTP capture did not bind a TCP port");const T=`http://127.0.0.1:${y.port.toString()}`,X={id:o,requestsUrl:`${T}${l}`,resetUrl:`${T}${l}`,type:"http-capture",url:`${T}${c}`};return{cleanup:()=>x(a,r),handle:X}}function me(e){return new Promise((t,r)=>{const n=o=>{r(o)};e.once("error",n),e.listen(0,"127.0.0.1",()=>{e.off("error",n),t()})})}function x(e,t){if(!e.listening){for(const r of t)r.destroy();return Promise.resolve()}return new Promise((r,n)=>{e.close(o=>{o?n(o):r()}),e.closeIdleConnections(),e.closeAllConnections();for(const o of t)o.destroy()})}function ye(e,t){return new Promise((r,n)=>{const o=[];let c=0,f=0,m=!1;const b=l=>{m||(m=!0,n(re(l)))};e.on("data",l=>{if(m)return;if(typeof l!="string"&&!(l instanceof Uint8Array)){b(new TypeError("HTTP capture received an unsupported body chunk"));return}const a=Buffer.from(l);c=Math.min(Number.MAX_SAFE_INTEGER,c+a.length);const y=t-f;if(y>0){const T=a.subarray(0,y);o.push(T),f+=T.length}}),e.once("aborted",()=>{b(new Error("HTTP request was aborted"))}),e.once("error",b),e.once("end",()=>{m||(m=!0,r({bodyBytes:c,retainedBody:Buffer.concat(o,f)}))})})}function R(e,t,r){e.destroyed||e.writableEnded||(e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r)))}function Te(e){if(!B(e))return null;const t=e.setup??e.default;return typeof t=="function"?t:null}function he(e){if(!B(e))return null;const t=v(e,"cleanup");return typeof t=="function"?t:null}function we(e){return B(e)&&("data"in e||"cleanup"in e)?Object.hasOwn(e,"data")?v(e,"data"):{}:e??{}}function v(e,t){let r=e;for(;r!==null;){const n=Object.getOwnPropertyDescriptor(r,t);if(n){if(!("value"in n))throw new TypeError(`Runtime node setup data at $.${t} must be an enumerable data property`);return n.value}r=Object.getPrototypeOf(r)}}function _(){return P!==null||(P=(async()=>{await Promise.allSettled([...C]);const e=[],t=k.splice(0).reverse();d({owners:t.map(({owner:r})=>r),sessionId:s.sessionId,type:"cleanup-started"});for(const[r,{callback:n,owner:o}]of t.entries()){d({owner:o,remainingCallbacks:t.length-r,sessionId:s.sessionId,type:"cleanup-progress"});try{await n()}catch(c){e.push({error:c,owner:o})}}return te(e)})()),P}function L(){const e=s.deadlineMs-Date.now();if(e<=0){p.abort(Ee());return}g=setTimeout(L,Math.min(e,2147483647))}function S(){g!==null&&clearTimeout(g),g=null}function Ee(){return E("deadline",`Runtime node setup exceeded its deadline ${s.deadlineMs.toString()}`)}function I(){if(p.signal.aborted)throw p.signal.reason instanceof Error?p.signal.reason:new Error(String(p.signal.reason))}function d(e){H.postMessage(e)}function Re(e){if(!K(e))throw E("infra","Runtime node setup worker received invalid worker data");return e}function be(){if(M===null)throw E("infra","Runtime node setup worker requires a parent port");return M}function z(e,t,r,n){if(e===void 0)return t;const o=Number(e);if(!Number.isInteger(o)||o<=0||o>r)throw new RangeError(`Runtime node HTTP capture ${n} must be an integer between 1 and ${r.toString()}`);return o}function Se(e,t){if(e===void 0)return t;const r=Number(e);return Number.isInteger(r)&&r>=100&&r<=599?r:t}
1
+ import{classifyRuntimeNodeSetupFailure as J,createSetupFailure as R}from"./runtime-node-setup-errors.js";import{randomBytes as H}from"node:crypto";import Q from"node:http";import{pathToFileURL as K}from"node:url";import{parentPort as O,workerData as V}from"node:worker_threads";import{HTTP_CAPTURE_OPERATION_PREFIX as A,isRuntimeNodeSetupParentMessage as Z,isRuntimeNodeSetupWorkerRequest as ee,MAX_RUNTIME_NODE_SETUP_SIDECARS as x,normalizeRuntimeNodeSetupData as te,normalizeRuntimeNodeSetupSidecarId as re,normalizeRuntimeNodeSetupSidecarPath as ne,serializeRuntimeNodeSetupError as v,serializeRuntimeNodeSetupCleanupFailures as oe}from"./runtime-node-setup-protocol.js";import{isUnknownRecord as $}from"../../../kernel/unknown-values.js";import{toError as se,errorMessage as ae}from"../../../kernel/error-serialization.js";const ie=1024*1024,ue=100,ce=8*1024*1024,de=8*1024*1024,le=1e3,pe=256,s=Pe(V),L=ge(),p=new AbortController,D=[],U=new Set,N=[];let g=null,q=0,u="starting",_=null;L.on("message",e=>{me(e)}),Y(),fe();async function fe(){try{const t=await import(`${K(s.modulePath).href}?testkit=${encodeURIComponent(s.sessionId)}`);B();const r=Ee(t);if(!r)throw new TypeError(`Runtime node setup "${s.moduleRelativePath}" must export a setup function`);const n=await r(ye()),o=Re(n);o&&D.push({callback:o,owner:"consumer"}),B();const d=te(be(n));S(),u="ready",c({sessionId:s.sessionId,setupData:d,sidecars:N.map(f=>({...f})),type:"ready"})}catch(e){const t=J(e),r=await I();u="cleaned",S(),c({cleanupFailures:r,error:v(e),kind:t,sessionId:s.sessionId,type:"setup-error"})}}async function me(e){if(!Z(e)||e.sessionId!==s.sessionId){await z(R("infra","Runtime node setup worker received an invalid parent message"));return}if(e.type==="abort"){if(p.abort(R(e.kind,e.error.message)),c({sessionId:s.sessionId,type:"abort-acknowledged"}),u==="ready"){u="cleaning";const r=await I();u="cleaned",S(),c({cleanupFailures:r,sessionId:s.sessionId,type:"aborted"})}return}if(u==="cleaned"){c({cleanupFailures:[],sessionId:s.sessionId,type:"cleanup-complete"});return}if(u!=="ready"){await z(R("infra",`Runtime node setup cleanup is invalid during phase "${u}"`));return}u="cleaning";const t=await I();u="cleaned",S(),c({cleanupFailures:t,sessionId:s.sessionId,type:"cleanup-complete"})}async function z(e){p.abort(e);const t=await I();u="cleaned",S(),c({cleanupFailures:t,error:v(e),kind:"infra",sessionId:s.sessionId,type:"setup-error"})}function ye(){return{deadlineMs:s.deadlineMs,env:{...s.runtimeEnv},leaseDir:s.leaseDir,productDir:s.productDir,serviceName:s.serviceName,sidecars:{httpCapture(e={}){if(B(),N.length+q>=x)return Promise.reject(new RangeError(`Runtime node setup may create at most ${x.toString()} sidecars`));q+=1;const t=Te(e).then(r=>(D.push({callback:r.cleanup,owner:"testkit"}),N.push(r.handle),c({sessionId:s.sessionId,sidecar:{...r.handle},type:"sidecar-started"}),B(),r.handle)).finally(()=>{q-=1,U.delete(t)});return U.add(t),t}},signal:p.signal,testFile:s.testFile}}async function Te(e){const t=[],r=new Set;let n=0;const o=re(e.id),d=ne(e.path),f=W(e.maxRequests,ue,le,"maxRequests"),m=W(e.maxBodyBytes,ie,ce,"maxBodyBytes"),b=_e(e.status,204),l=`${A}${H(24).toString("base64url")}/requests`,w=`${A}${H(24).toString("base64url")}`;let y="";const a=Q.createServer((i,T)=>{if(i.headers.host!==y){h(T,403,{error:`HTTP capture accepts only requests for ${y}`});return}const P=i.url??"";if(P===l){i.method==="GET"?h(T,200,{requests:t}):i.method==="DELETE"?(t.splice(0),n=0,h(T,200,{ok:!0})):h(T,405,{error:"HTTP capture requests allow only GET and DELETE"});return}if(!P.startsWith(`${w}/`)){h(T,404,{error:P.startsWith(A)?"Unknown HTTP capture operation":"HTTP capture records only requests to its url"});return}we(i,m).then(E=>{const M={bodyBytes:E.bodyBytes,bodyText:E.retainedBody.toString("utf8"),bodyTruncated:E.bodyBytes>E.retainedBody.length,headers:i.headers,method:i.method??null,path:P.slice(w.length),receivedAt:new Date().toISOString()};for(n+=Buffer.byteLength(M.bodyText),t.push(M);t.length>f||n>de;){const F=t.shift();if(!F)break;n-=Buffer.byteLength(F.bodyText)}h(T,b,{ok:!0})}).catch(E=>{h(T,500,{error:ae(E)})})});a.maxConnections=pe,a.requestTimeout=3e4,a.headersTimeout=3e4,a.keepAliveTimeout=1e3,a.on("connection",i=>{r.add(i),i.once("close",()=>r.delete(i))}),await he(a);const k=a.address();if(k===null||typeof k=="string")throw await X(a,r),new Error("Runtime node HTTP capture did not bind a TCP port");y=`127.0.0.1:${k.port.toString()}`;const C=`http://${y}`,G={id:o,requestsUrl:`${C}${l}`,resetUrl:`${C}${l}`,type:"http-capture",url:`${C}${w}${d}`};return{cleanup:()=>X(a,r),handle:G}}function he(e){return new Promise((t,r)=>{const n=o=>{r(o)};e.once("error",n),e.listen(0,"127.0.0.1",()=>{e.off("error",n),t()})})}function X(e,t){if(!e.listening){for(const r of t)r.destroy();return Promise.resolve()}return new Promise((r,n)=>{e.close(o=>{o?n(o):r()}),e.closeIdleConnections(),e.closeAllConnections();for(const o of t)o.destroy()})}function we(e,t){return new Promise((r,n)=>{const o=[];let d=0,f=0,m=!1;const b=l=>{m||(m=!0,n(se(l)))};e.on("data",l=>{if(m)return;if(typeof l!="string"&&!(l instanceof Uint8Array)){b(new TypeError("HTTP capture received an unsupported body chunk"));return}const w=Buffer.from(l);d=Math.min(Number.MAX_SAFE_INTEGER,d+w.length);const y=t-f;if(y>0){const a=w.subarray(0,y);o.push(a),f+=a.length}}),e.once("aborted",()=>{b(new Error("HTTP request was aborted"))}),e.once("error",b),e.once("end",()=>{m||(m=!0,r({bodyBytes:d,retainedBody:Buffer.concat(o,f)}))})})}function h(e,t,r){e.destroyed||e.writableEnded||(e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r)))}function Ee(e){if(!$(e))return null;const t=e.setup??e.default;return typeof t=="function"?t:null}function Re(e){if(!$(e))return null;const t=j(e,"cleanup");return typeof t=="function"?t:null}function be(e){return $(e)&&("data"in e||"cleanup"in e)?Object.hasOwn(e,"data")?j(e,"data"):{}:e??{}}function j(e,t){let r=e;for(;r!==null;){const n=Object.getOwnPropertyDescriptor(r,t);if(n){if(!("value"in n))throw new TypeError(`Runtime node setup data at $.${t} must be an enumerable data property`);return n.value}r=Object.getPrototypeOf(r)}}function I(){return g!==null||(g=(async()=>{await Promise.allSettled([...U]);const e=[],t=D.splice(0).reverse();c({owners:t.map(({owner:r})=>r),sessionId:s.sessionId,type:"cleanup-started"});for(const[r,{callback:n,owner:o}]of t.entries()){c({owner:o,remainingCallbacks:t.length-r,sessionId:s.sessionId,type:"cleanup-progress"});try{await n()}catch(d){e.push({error:d,owner:o})}}return oe(e)})()),g}function Y(){const e=s.deadlineMs-Date.now();if(e<=0){p.abort(Se());return}_=setTimeout(Y,Math.min(e,2147483647))}function S(){_!==null&&clearTimeout(_),_=null}function Se(){return R("deadline",`Runtime node setup exceeded its deadline ${s.deadlineMs.toString()}`)}function B(){if(p.signal.aborted)throw p.signal.reason instanceof Error?p.signal.reason:new Error(String(p.signal.reason))}function c(e){L.postMessage(e)}function Pe(e){if(!ee(e))throw R("infra","Runtime node setup worker received invalid worker data");return e}function ge(){if(O===null)throw R("infra","Runtime node setup worker requires a parent port");return O}function W(e,t,r,n){if(e===void 0)return t;const o=Number(e);if(!Number.isInteger(o)||o<=0||o>r)throw new RangeError(`Runtime node HTTP capture ${n} must be an integer between 1 and ${r.toString()}`);return o}function _e(e,t){if(e===void 0)return t;const r=Number(e);return Number.isInteger(r)&&r>=100&&r<=599?r:t}
@@ -1,3 +1,3 @@
1
- import{classifyRuntimeNodeSetupFailure as R,createSetupFailure as o,createSetupCleanupError as _,createSetupCleanupTimeoutError as y,createUnacknowledgedSetupCleanupError as b,withSetupCleanupFailures as h}from"./runtime-node-setup-errors.js";export{classifyRuntimeNodeSetupFailure,getRuntimeNodeSetupCleanupFailures}from"./runtime-node-setup-errors.js";import F from"node:fs";import{randomUUID as j}from"node:crypto";import a from"node:path";import{Worker as x}from"node:worker_threads";import{isEngineTaskDeadlineExceededReason as M}from"../../../engines/api/index.js";import{readRuntimeNodeSetupWorkerMessage as A,serializeRuntimeNodeSetupError as N}from"./runtime-node-setup-protocol.js";const E=100,C=5e3;export const MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS=6e4;class I{#o;#m;#i;#$;#w;#c=$();#d;#a;#R=!1;#k=null;#r=null;#s=null;#u=0;#h=null;#f=null;#v=!1;#t="starting";#S=!1;#e=null;#g=null;constructor(e,t,s,r){this.#$=r,this.#w=e,this.#d=t,this.#m=e.deadlineMs,this.#i=e.moduleRelativePath,this.#o=s,this.#a=new x(new URL("./runtime-node-setup-worker.js",import.meta.url),{name:"testkit-runtime-node-setup",workerData:e}),this.#a.on("message",n=>{this.#x(n).catch(c=>{this.#T(o("infra",`Runtime node setup worker for "${this.#i}" sent an invalid message`,c))})}),this.#a.once("error",n=>{this.#T(o("infra",`Runtime node setup worker failed for "${this.#i}"`,n))}),this.#a.once("exit",n=>{this.#v||this.#T(o("infra",`Runtime node setup worker for "${this.#i}" exited unexpectedly with code ${n.toString()}`))}),this.#d.addEventListener("abort",this.#D,{once:!0}),this.#F()}start(){return this.#d.aborted?this.#p(f(this.#d.reason,this.#i)):Date.now()>=this.#m&&this.#p(d(this.#m,this.#i)),this.#c.promise}cleanup=()=>{if(this.#r!==null)return this.#r.promise;const e=$();if(this.#r=e,this.#e!==null&&this.#t==="cleaning")return e.promise;if(this.#e!==null||this.#t==="terminated")return this.#W(),e.promise;if(this.#t!=="ready")return this.#E(o("infra",`Runtime node setup cleanup is invalid during phase "${this.#t}"`)),e.promise;this.#t="cleaning",this.#h=setTimeout(()=>{this.#E(this.#L())},this.#o);try{this.#a.postMessage({sessionId:this.#w.sessionId,type:"cleanup"})}catch(t){this.#E(o("infra",`Could not request runtime node setup cleanup for "${this.#i}"`,t))}return e.promise};#D=()=>{this.#p(f(this.#d.reason,this.#i))};async#x(e){const t=A(e);if(t===null||t.sessionId!==this.#w.sessionId){this.#n(`Runtime node setup worker for "${this.#i}" sent an invalid message`);return}switch(t.type){case"abort-acknowledged":this.#M();return;case"cleanup-started":this.#A(t.owners);return;case"cleanup-progress":this.#N(t.owner,t.remainingCallbacks);return;case"ready":this.#C(t);return;case"sidecar-started":this.#$(t.sidecar);return;case"setup-error":await this.#I(t);return;case"cleanup-complete":await this.#P(t);return;case"aborted":await this.#U(t.cleanupFailures)}}#M(){if(this.#e===null||this.#t==="terminated"){this.#n(`Runtime node setup worker acknowledged an unexpected abort during phase "${this.#t}"`);return}if(this.#R){this.#n("Runtime node setup worker acknowledged abort more than once");return}this.#R=!0,this.#y(this.#s!==null?this.#o:E)}#A(e){if(this.#s!==null||this.#t!=="starting"&&this.#t!=="cleaning"){this.#n(this.#s!==null?"Runtime node setup worker started cleanup more than once":`Runtime node setup worker started cleanup during phase "${this.#t}"`);return}this.#s=[...e],this.#u=0,this.#e!==null&&this.#y(this.#o)}#N(e,t){const s=this.#s?.[this.#u],r=(this.#s?.length??0)-this.#u;if(this.#s===null||this.#t!=="starting"&&this.#t!=="cleaning"||s!==e||r!==t){this.#n(`Runtime node setup worker reported invalid cleanup progress during phase "${this.#t}"`);return}this.#u+=1}#C(e){if(this.#t!=="starting"||this.#e!==null||this.#s!==null){this.#e!==null?this.#p(this.#e):this.#n(this.#s!==null?"Runtime node setup worker became ready after starting cleanup":`Runtime node setup worker became ready during phase "${this.#t}"`);return}const t=e.setupData,s=e.sidecars.map(r=>({...r}));l(this.#f),this.#f=null,this.#t="ready",this.#c.resolve({artifacts:[{name:"node-setup",kind:"testkit.node-setup",summary:`node setup ${this.#i}`,contentType:"application/json",data:{module:this.#i,sidecars:s,setupData:t},emittedAt:new Date().toISOString()}],cleanup:this.cleanup,setupData:t,sidecars:s})}async#I(e){if(this.#t!=="starting"){this.#n(`Runtime node setup worker reported setup failure during phase "${this.#t}"`);return}if(!this.#b()){this.#n("Runtime node setup worker reported setup failure before acknowledging cleanup completion");return}const t=m(e.cleanupFailures),s=U(e.error,e.kind,this.#i,t),r=this.#e===null?s:h(this.#e,t);this.#e=r,await this.#l(),this.#c.reject(this.#e)}async#P(e){if(this.#t!=="cleaning"||this.#r===null||!this.#b()){this.#n(`Runtime node setup worker reported cleanup during phase "${this.#t}"`);return}const t=m(e.cleanupFailures);l(this.#h),this.#h=null,t.length>0&&(this.#e=this.#e===null?_(t):h(this.#e,t)),await this.#l(),this.#e!==null?this.#r.reject(this.#e):this.#r.resolve(void 0)}async#U(e){if(!this.#b()){this.#n("Runtime node setup worker reported abort completion before acknowledging cleanup completion");return}const t=m(e),s=h(this.#e??o("infra",`Runtime node setup worker for "${this.#i}" aborted unexpectedly`),t);this.#e=s,await this.#l();const r=this.#e;this.#c.reject(r),this.#r?.reject(r)}#p(e,t=!1){if(this.#t==="terminated")return;if(t){if(this.#S)return;this.#S=!0}if(this.#e!==null){t&&(this.#e=h(this.#e,[{error:e,owner:"testkit"}]));return}const s=this.#t==="ready",r=s||this.#t==="cleaning"?this.#o:E;this.#e=e,s&&(this.#t="cleaning"),this.#j(),l(this.#h),this.#h=null;try{this.#a.postMessage({error:N(e),kind:R(e),sessionId:this.#w.sessionId,type:"abort"})}catch{this.#_();return}this.#y(r)}async#_(){const e=this.#e??o("infra",`Runtime node setup worker for "${this.#i}" was terminated`),t=this.#O();this.#e=h(e,[t]),await this.#l();const s=this.#e;this.#c.reject(s),this.#r?.reject(s)}#y(e){l(this.#k),this.#k=setTimeout(()=>{this.#_()},e)}#O(){if(this.#s!==null){const e=this.#s.at(Math.max(0,this.#u-1))??"testkit";return{error:y(this.#o,e),owner:e}}return{error:b(this.#R),owner:"testkit"}}#L(){return this.#s===null?b(!1):y(this.#o,this.#s.at(Math.max(0,this.#u-1))??"testkit")}#b(){return this.#s!==null&&this.#u===this.#s.length}#n(e){this.#p(o("infra",e),!0)}async#W(){await this.#l();const e=this.#e??o("infra",`Runtime node setup worker for "${this.#i}" is unavailable during cleanup`);this.#r?.reject(e)}async#E(e){this.#e??=e,await this.#l(),this.#r?.reject(this.#e)}#T(e){this.#e===null&&(this.#e=e),this.#l().then(()=>{this.#c.reject(this.#e),this.#r?.reject(this.#e)})}#F(){const e=this.#m-Date.now();if(e<=0){this.#p(d(this.#m,this.#i));return}this.#f=setTimeout(()=>{this.#F()},Math.min(e,2147483647))}#j(){l(this.#f),this.#f=null,this.#d.removeEventListener("abort",this.#D)}#l(){return this.#g!==null?this.#g:(this.#v=!0,this.#t="terminated",this.#j(),l(this.#k),l(this.#h),this.#k=null,this.#h=null,this.#g=this.#a.terminate().then(()=>{}).catch(e=>{const t=o("infra",`Failed to terminate runtime node setup worker for "${this.#i}"`,e);this.#e=this.#e===null?t:h(this.#e,[{error:t,owner:"testkit"}])}),this.#g)}}export async function runRuntimeNodeSetup({cleanupTimeoutMs:i,deadlineMs:e,targetConfig:t,task:s,signal:r,lease:n,onSidecar:c=()=>{},runtimeEnv:p={}}){const v=Math.min(i??C,MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS),u=findRuntimeNodeSetupFile(t.productDir,s.file);if(r.aborted)throw f(r.reason,u?w(t.productDir,u):s.file);if(Date.now()>=e)throw d(e,u?w(t.productDir,u):s.file);if(!u)return P();const k=w(t.productDir,u),S={deadlineMs:e,leaseDir:n?.leaseDir??null,modulePath:u,moduleRelativePath:k,productDir:t.productDir,runtimeEnv:p,serviceName:t.name.trim(),sessionId:j(),testFile:s.file.trim()};let g;try{g=new I(S,r,v,c)}catch(D){throw o("infra",`Could not start runtime node setup worker for "${k}"`,D)}return g.start()}export function findRuntimeNodeSetupFile(i,e){const t=a.resolve(i),s=a.resolve(t,e),r=a.relative(t,s);if(r===".."||r.startsWith(`..${a.sep}`)||a.isAbsolute(r))throw new Error("Runtime node setup test file must be inside productDir");const n=a.parse(s);return[a.join(n.dir,`${n.name}.setup.mjs`),a.join(n.dir,`${n.name}.setup.js`),a.join(n.dir,"__setup__",`${n.name}.mjs`),a.join(n.dir,"__setup__",`${n.name}.js`)].find(p=>F.existsSync(p))??null}function P(){const i=Promise.resolve();return{artifacts:[],cleanup:()=>i,setupData:{},sidecars:[]}}function U(i,e,t,s){const r=T(i),n=o(e,`Runtime node setup "${t}" failed: ${r.message}`,r);return r.stack&&(n.stack=`${n.stack??n.message}
1
+ import{classifyRuntimeNodeSetupFailure as k,createSetupFailure as o,createSetupCleanupError as _,createSetupCleanupTimeoutError as g,createUnacknowledgedSetupCleanupError as R,withSetupCleanupFailures as u}from"./runtime-node-setup-errors.js";export{classifyRuntimeNodeSetupFailure,getRuntimeNodeSetupCleanupFailures}from"./runtime-node-setup-errors.js";import D from"node:fs";import{randomUUID as F}from"node:crypto";import a from"node:path";import{Worker as j}from"node:worker_threads";import{readRuntimeNodeSetupWorkerMessage as x,serializeRuntimeNodeSetupError as M}from"./runtime-node-setup-protocol.js";const b=100,N=5e3;export const MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS=6e4;class C{#o;#m;#r;#E;#w;#c=$();#d;#a;#R=!1;#k=null;#s=null;#i=null;#u=0;#h=null;#f=null;#S=!1;#t="starting";#T=!1;#e=null;#g=null;constructor(e,t,s,r){this.#E=r,this.#w=e,this.#d=t,this.#m=e.deadlineMs,this.#r=e.moduleRelativePath,this.#o=s,this.#a=new j(new URL("./runtime-node-setup-worker.js",import.meta.url),{name:"testkit-runtime-node-setup",workerData:e}),this.#a.on("message",n=>{this.#x(n).catch(c=>{this.#$(o("infra",`Runtime node setup worker for "${this.#r}" sent an invalid message`,c))})}),this.#a.once("error",n=>{this.#$(o("infra",`Runtime node setup worker failed for "${this.#r}"`,n))}),this.#a.once("exit",n=>{this.#S||this.#$(o("infra",`Runtime node setup worker for "${this.#r}" exited unexpectedly with code ${n.toString()}`))}),this.#d.addEventListener("abort",this.#_,{once:!0}),this.#F()}start(){return this.#d.aborted?this.#p(m(this.#d.reason)):Date.now()>=this.#m&&this.#p(runtimeNodeSetupDeadlineError(this.#m,this.#r)),this.#c.promise}cleanup=()=>{if(this.#s!==null)return this.#s.promise;const e=$();if(this.#s=e,this.#e!==null&&this.#t==="cleaning")return e.promise;if(this.#e!==null||this.#t==="terminated")return this.#W(),e.promise;if(this.#t!=="ready")return this.#y(o("infra",`Runtime node setup cleanup is invalid during phase "${this.#t}"`)),e.promise;this.#t="cleaning",this.#h=setTimeout(()=>{this.#y(this.#O())},this.#o);try{this.#a.postMessage({sessionId:this.#w.sessionId,type:"cleanup"})}catch(t){this.#y(o("infra",`Could not request runtime node setup cleanup for "${this.#r}"`,t))}return e.promise};#_=()=>{this.#p(m(this.#d.reason))};async#x(e){const t=x(e);if(t===null||t.sessionId!==this.#w.sessionId){this.#n(`Runtime node setup worker for "${this.#r}" sent an invalid message`);return}switch(t.type){case"abort-acknowledged":this.#M();return;case"cleanup-started":this.#N(t.owners);return;case"cleanup-progress":this.#C(t.owner,t.remainingCallbacks);return;case"ready":this.#A(t);return;case"sidecar-started":this.#E(t.sidecar);return;case"setup-error":await this.#I(t);return;case"cleanup-complete":await this.#P(t);return;case"aborted":await this.#U(t.cleanupFailures)}}#M(){if(this.#e===null||this.#t==="terminated"){this.#n(`Runtime node setup worker acknowledged an unexpected abort during phase "${this.#t}"`);return}if(this.#R){this.#n("Runtime node setup worker acknowledged abort more than once");return}this.#R=!0,this.#b(this.#i!==null?this.#o:b)}#N(e){if(this.#i!==null||this.#t!=="starting"&&this.#t!=="cleaning"){this.#n(this.#i!==null?"Runtime node setup worker started cleanup more than once":`Runtime node setup worker started cleanup during phase "${this.#t}"`);return}this.#i=[...e],this.#u=0,this.#e!==null&&this.#b(this.#o)}#C(e,t){const s=this.#i?.[this.#u],r=(this.#i?.length??0)-this.#u;if(this.#i===null||this.#t!=="starting"&&this.#t!=="cleaning"||s!==e||r!==t){this.#n(`Runtime node setup worker reported invalid cleanup progress during phase "${this.#t}"`);return}this.#u+=1}#A(e){if(this.#t!=="starting"||this.#e!==null||this.#i!==null){this.#e!==null?this.#p(this.#e):this.#n(this.#i!==null?"Runtime node setup worker became ready after starting cleanup":`Runtime node setup worker became ready during phase "${this.#t}"`);return}const t=e.setupData,s=e.sidecars.map(r=>({...r}));h(this.#f),this.#f=null,this.#t="ready",this.#c.resolve({cleanup:this.cleanup,setupData:t,sidecars:s})}async#I(e){if(this.#t!=="starting"){this.#n(`Runtime node setup worker reported setup failure during phase "${this.#t}"`);return}if(!this.#v()){this.#n("Runtime node setup worker reported setup failure before acknowledging cleanup completion");return}const t=p(e.cleanupFailures),s=I(e.error,e.kind,this.#r,t),r=this.#e===null?s:u(this.#e,t);this.#e=r,await this.#l(),this.#c.reject(this.#e)}async#P(e){if(this.#t!=="cleaning"||this.#s===null||!this.#v()){this.#n(`Runtime node setup worker reported cleanup during phase "${this.#t}"`);return}const t=p(e.cleanupFailures);h(this.#h),this.#h=null,t.length>0&&(this.#e=this.#e===null?_(t):u(this.#e,t)),await this.#l(),this.#e!==null?this.#s.reject(this.#e):this.#s.resolve(void 0)}async#U(e){if(!this.#v()){this.#n("Runtime node setup worker reported abort completion before acknowledging cleanup completion");return}const t=p(e),s=u(this.#e??o("infra",`Runtime node setup worker for "${this.#r}" aborted unexpectedly`),t);this.#e=s,await this.#l();const r=this.#e;this.#c.reject(r),this.#s?.reject(r)}#p(e,t=!1){if(this.#t==="terminated")return;if(t){if(this.#T)return;this.#T=!0}if(this.#e!==null){t&&(this.#e=u(this.#e,[{error:e,owner:"testkit"}]));return}const s=this.#t==="ready",r=s||this.#t==="cleaning"?this.#o:b;this.#e=e,s&&(this.#t="cleaning"),this.#j(),h(this.#h),this.#h=null;try{this.#a.postMessage({error:M(e),kind:k(e),sessionId:this.#w.sessionId,type:"abort"})}catch{this.#D();return}this.#b(r)}async#D(){const e=this.#e??o("infra",`Runtime node setup worker for "${this.#r}" was terminated`),t=this.#L();this.#e=u(e,[t]),await this.#l();const s=this.#e;this.#c.reject(s),this.#s?.reject(s)}#b(e){h(this.#k),this.#k=setTimeout(()=>{this.#D()},e)}#L(){if(this.#i!==null){const e=this.#i.at(Math.max(0,this.#u-1))??"testkit";return{error:g(this.#o,e),owner:e}}return{error:R(this.#R),owner:"testkit"}}#O(){return this.#i===null?R(!1):g(this.#o,this.#i.at(Math.max(0,this.#u-1))??"testkit")}#v(){return this.#i!==null&&this.#u===this.#i.length}#n(e){this.#p(o("infra",e),!0)}async#W(){await this.#l();const e=this.#e??o("infra",`Runtime node setup worker for "${this.#r}" is unavailable during cleanup`);this.#s?.reject(e)}async#y(e){this.#e??=e,await this.#l(),this.#s?.reject(this.#e)}#$(e){this.#e===null&&(this.#e=e),this.#l().then(()=>{this.#c.reject(this.#e),this.#s?.reject(this.#e)})}#F(){const e=this.#m-Date.now();if(e<=0){this.#p(runtimeNodeSetupDeadlineError(this.#m,this.#r));return}this.#f=setTimeout(()=>{this.#F()},Math.min(e,2147483647))}#j(){h(this.#f),this.#f=null,this.#d.removeEventListener("abort",this.#_)}#l(){return this.#g!==null?this.#g:(this.#S=!0,this.#t="terminated",this.#j(),h(this.#k),h(this.#h),this.#k=null,this.#h=null,this.#g=this.#a.terminate().then(()=>{}).catch(e=>{const t=o("infra",`Failed to terminate runtime node setup worker for "${this.#r}"`,e);this.#e=this.#e===null?t:u(this.#e,[{error:t,owner:"testkit"}])}),this.#g)}}export async function runRuntimeNodeSetup({cleanupTimeoutMs:i,deadlineMs:e,targetConfig:t,task:s,signal:r,lease:n,onSidecar:c=()=>{},runtimeEnv:d={}}){const E=Math.min(i??N,MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS),l=findRuntimeNodeSetupFile(t.productDir,s.file);if(r.aborted)throw m(r.reason);if(Date.now()>=e)throw runtimeNodeSetupDeadlineError(e,l?y(t.productDir,l):s.file);if(!l)return A();const f=y(t.productDir,l),S={deadlineMs:e,leaseDir:n?.leaseDir??null,modulePath:l,moduleRelativePath:f,productDir:t.productDir,runtimeEnv:d,serviceName:t.name.trim(),sessionId:F(),testFile:s.file.trim()};let w;try{w=new C(S,r,E,c)}catch(T){throw o("infra",`Could not start runtime node setup worker for "${f}"`,T)}return w.start()}export function findRuntimeNodeSetupFile(i,e){const t=a.resolve(i),s=a.resolve(t,e),r=a.relative(t,s);if(r===".."||r.startsWith(`..${a.sep}`)||a.isAbsolute(r))throw new Error("Runtime node setup test file must be inside productDir");const n=a.parse(s);return[a.join(n.dir,`${n.name}.setup.mjs`),a.join(n.dir,`${n.name}.setup.js`),a.join(n.dir,"__setup__",`${n.name}.mjs`),a.join(n.dir,"__setup__",`${n.name}.js`)].find(d=>D.existsSync(d))??null}function A(){const i=Promise.resolve();return{cleanup:()=>i,setupData:{},sidecars:[]}}function I(i,e,t,s){const r=v(i),n=o(e,`Runtime node setup "${t}" failed: ${r.message}`,r);return r.stack&&(n.stack=`${n.stack??n.message}
2
2
  Remote stack:
3
- ${r.stack}`),h(n,s)}function T(i){const e=new Error(i.message);return e.name=i.name,i.stack&&(e.stack=i.stack),e}function m(i){return i.map(e=>({error:T(e.error),owner:e.owner}))}function f(i,e){return R(i)==="deadline"&&i instanceof Error?i:M(i)?d(i.deadlineMs,e):o("cancelled","Runtime node setup was cancelled",i)}function d(i,e){return o("deadline",`Runtime node setup exceeded its deadline ${i.toString()}${e?` for "${e}"`:""}`)}function w(i,e){return a.relative(i,e).split(a.sep).join("/")}function $(){let i,e;return{promise:new Promise((s,r)=>{i=s,e=r}),resolve:i,reject:e}}function l(i){i!==null&&clearTimeout(i)}
3
+ ${r.stack}`),u(n,s)}function v(i){const e=new Error(i.message);return e.name=i.name,i.stack&&(e.stack=i.stack),e}function p(i){return i.map(e=>({error:v(e.error),owner:e.owner}))}function m(i){return k(i)==="deadline"&&i instanceof Error?i:o("cancelled","Runtime node setup was cancelled",i)}export function runtimeNodeSetupDeadlineError(i,e){return o("deadline",`Runtime node setup exceeded its deadline ${i.toString()}${e?` for "${e}"`:""}`)}function y(i,e){return a.relative(i,e).split(a.sep).join("/")}function $(){let i,e;return{promise:new Promise((s,r)=>{i=s,e=r}),resolve:i,reject:e}}function h(i){i!==null&&clearTimeout(i)}
@@ -1 +1 @@
1
- import{isDeepStrictEqual as p}from"node:util";import{resolveExecutionProfile as u}from"../../engines/catalog.js";export const DEFAULT_COMPATIBLE_BATCH_MAX_FILES=25;export function claimCompatibleTaskBatch(e,t,o={}){const r=o.maxFiles??DEFAULT_COMPATIBLE_BATCH_MAX_FILES,s=[t],c=f(t);if(!c&&(!isBatchableEngineTask(t)||r<=1))return s;for(let i=0;i<e.length&&(c||s.length<r);){const n=e[i];if(c?n.engineId!==t.engineId||n.profileId!==t.profileId:!isCompatibleEngineBatchTask(t,n)){i+=1;continue}s.push(n),e.splice(i,1)}return s}export function buildTaskBatch(e){if(e.length<=1)return e[0]??null;const t=e[0];return{...t,batchId:a(e),batchTasks:[t,e[1],...e.slice(2)],batchFiles:e.map(o=>o.file)}}export function expandTaskResultOutcomes(e){return e?e.outcomes!==void 0?[...e.outcomes]:[e]:[]}export function isBatchableEngineTask(e){return u(e).taskBatching==="compatible"&&e.runtimeIsolation==="shared"&&e.resourceCost===1&&e.locks.length===0&&!e.hasSetup}export function isCompatibleEngineBatchTask(e,t){return!isBatchableEngineTask(e)||!isBatchableEngineTask(t)?!1:t.graphKey===e.graphKey&&t.targetName===e.targetName&&t.serviceName===e.serviceName&&t.testType===e.testType&&t.engineId===e.engineId&&t.profileId===e.profileId}const m=[["source","services"],["locks","locks"]];export function requireCompatibleSelection(e,t){const o=e.filter(f),[r]=o,s=i=>t.provisioned.has(i)?["configured",i]:[t.static.has(i)?"static":"supplied"],c=({graphKey:i,locks:n})=>({locks:n,source:s(i)});for(const i of o.length>1?o:[]){if(i.hasSetup||i.runtimeIsolation!=="shared"){const l=i.hasSetup?"a setup module":`${i.runtimeIsolation} isolation`;throw new Error(`${i.file} in group ${i.serviceName} needs ${l}, which plain Vitest files run together cannot use`)}const n=m.find(([l])=>!p(c(i)[l],c(r)[l]));if(n!==void 0)throw new Error(`Plain Vitest files run in one Vitest invocation, but ${r.file} in group ${r.serviceName} and ${i.file} in group ${i.serviceName} need different ${n[1]}; select one group with --group`)}}function f(e){return u(e).taskBatching==="selection"}function a(e){return`${e[0].engineId}-batch-${e.map(t=>t.id).join("-")}`}
1
+ import{resolveExecutionProfile as c}from"../../engines/catalog.js";export const DEFAULT_COMPATIBLE_BATCH_MAX_FILES=25;export function claimCompatibleTaskBatch(e,t,n={}){const r=n.maxFiles??DEFAULT_COMPATIBLE_BATCH_MAX_FILES,o=[t],l=p(t);if(l?f(t):!isBatchableEngineTask(t)||r<=1)return o;for(let u=0;u<e.length&&(l||o.length<r);){const i=e[u];if(l?i.engineId!==t.engineId||i.profileId!==t.profileId||f(i)||!m(t,i,n.environmentKeys):!isCompatibleEngineBatchTask(t,i)){u+=1;continue}o.push(i),e.splice(u,1)}return o}export function buildTaskBatch(e){if(e.length<=1)return e[0]??null;const t=e[0];return{...t,batchId:g(e),batchTasks:[t,e[1],...e.slice(2)],batchFiles:e.map(n=>n.file)}}export function expandTaskResultOutcomes(e){return e?e.outcomes!==void 0?[...e.outcomes]:[e]:[]}export function isBatchableEngineTask(e){return c(e).taskBatching==="compatible"&&e.resourceCost===1&&!f(e)}function f(e){return e.hasSetup||e.locks.length>0||e.runtimeIsolation!=="shared"}export function isCompatibleEngineBatchTask(e,t){return!isBatchableEngineTask(e)||!isBatchableEngineTask(t)?!1:t.graphKey===e.graphKey&&t.targetName===e.targetName&&t.serviceName===e.serviceName&&t.testType===e.testType&&t.engineId===e.engineId&&t.profileId===e.profileId}function m(e,t,n=new Map){if(t.targetName===e.targetName)return!0;const r=n.get(e.targetName);return r!==void 0&&r===n.get(t.targetName)}function p(e){return c(e).taskBatching==="selection"}function g(e){return`${e[0].engineId}-batch-${e.map(t=>t.id).join("-")}`}
@@ -1 +1 @@
1
- import{buildTaskDeadline as U,createDeadlineSignal as J,EngineExecutionBridgeError as I,executeEngineTask as H,normalizeTimeoutMs as Q}from"./engine-execution.js";import{preparationFailure as V,settlePreparedTask as X,taskStartedFailure as Y}from"./engine-execution-failures.js";import{GroupDefaultConflictError as N}from"./environment.js";import{formatError as Z,toError as x}from"../../kernel/error-serialization.js";import{formatEngineInvocationStack as ee}from"./engine-failure-stack.js";import{DEFAULT_ENGINE_REPORTER_CALLBACK_TIMEOUT_MS as _,invokeBoundedReporterCallback as te,invokeEngineReporterCallback as L}from"./engine-execution-reporting.js";import{drainRuntimeFailedTasks as ie}from"./runtime-failures.js";import{buildTimingObservation as ae}from"./scheduler/observations.js";import{buildTaskBatch as ne,claimCompatibleTaskBatch as re,expandTaskResultOutcomes as se}from"./task-batches.js";export function createWorker(e,a){return{workerId:e,productDir:a,currentGraphKey:null,graphSwitches:0,taskCount:0}}export async function runWorker(e,a,t,l,n,u,c,k,g,m=null){const v=Date.now(),w=[],q=[];try{for(;!t.isStopRequested();){const o=c(a,e.currentGraphKey,i=>t.locks.available(i.locks)&&t.environments.canAcquire(i));if(!o){const i=ie(a,t.environments);if(i.length>0){const s=Date.now(),r=new Set;for(const p of i)k(n,p.task,{status:"not_run",reason:p.reason,durationMs:0,startedAt:s,finishedAt:s,error:null,infraFailed:p.infraFailed,blockedByRuntime:p.blockedByRuntime,failureDetails:p.failureDetails}),r.has(p.graph.key)||(g(n,p.graph,p.message,s,{classification:"infra"}),r.add(p.graph.key));e.taskCount+=i.length;continue}if(a.length===0)break;await pe(10);continue}const E=t.leases.allocate(o);t.locks.acquire(o.locks,E.id);let M=null,T=[o],D=o,O=!1,R=null;const d={phase:"acquire",prepared:null};let C=[],F=[],B=null;try{T=re(a,o),D=ne(T)??o,e.currentGraphKey!==null&&e.currentGraphKey!==o.graphKey&&(e.graphSwitches+=1),e.currentGraphKey=o.graphKey;const i=await t.environments.acquire(D,E);M=i,t.leases.create(E),d.phase="execute";const s=await l.withEngine(o.engineId,t.signal,async r=>{let p=null;if(T.length===1){d.phase="start",await L("taskStarted",()=>m?.taskStarted?.(o,{name:o.targetName}),{signal:t.signal,timeoutMs:_}),d.phase="prepare";const K=Q(t.request.fileTimeoutSeconds),y=U(Date.now(),K),b=J(t.signal,y);try{d.prepared=await i.prepare(o,{deadlineMs:y,signal:b.signal,timeoutMs:K})}finally{b.dispose()}p={deadlineMs:y,prepared:d.prepared===null?null:{artifacts:d.prepared.artifacts,handles:d.prepared.handles,setupData:d.prepared.setupData},taskId:String(o.id)},d.phase="execute"}return H(r,{environment:d.prepared?.environment??i.environment,lease:E,request:t.request,started:p,task:D},t,m)});d.phase="mapping",C=se(s),F=resolveWorkerOutcomeMappings(T,C),F.some(({outcome:r})=>r.infraFailed&&r.blockedByRuntime)&&(O=!0)}catch(i){const s=d.phase==="start"?Y(i,String(o.id)):d.phase==="prepare"?V(i,String(o.id)):i,r=d.phase==="acquire"?fe(i,t):ue(s);R={disposition:r,error:s},O=M!==null&&r.runtimeImpact!=="none"}finally{if(d.prepared!==null){const i=await d.prepared.release(),s=await X(R===null?{outcomes:C}:{error:R.error},i,String(o.id),r=>L("taskFinished",()=>m?.taskFinished?.(o,r),{signal:t.signal,timeoutMs:_}));s!==null&&(R={disposition:s.disposition,error:s},O||=s.disposition.runtimeImpact!=="none")}B=await le(t.leases,M,E,O),t.locks.release(o.locks,E.id)}const f=[];let S=null;const $=()=>{if(S!==null)return S;try{S=de(t.environments,o)}catch(i){f.push(j("Runtime failure graph resolution failed",i)),S=A(o)}return S};if(R===null){for(const{outcome:s,task:r}of F)await P({operationErrors:f,outcome:s,recordTaskOutcome:k,recordTiming:!0,reporter:m,reportTaskFinished:!1,task:r,timingUpdates:u,trackers:n});e.taskCount+=F.length;const i=F.filter(({outcome:s})=>s.infraFailed&&s.blockedByRuntime);if(i.length>0){const s=i.map(({outcome:r})=>r.reason??r.error??"Runtime readiness failed before engine execution").join("; ");w.push(s),await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(o,s)),await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,s)}),await h(f,"Result graph error recording failed",()=>{g(n,$(),s,Date.now(),{classification:"infra"})})}}else{const{disposition:i,error:s}=R,r=Z(s),p=oe(T,s);if(i.status!=="cancelled"&&w.push(r),i.notifyRuntimeError&&await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(p,r)),i.status!=="cancelled"){i.runtimeImpact==="fail-graph"&&await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,r)});const y=s instanceof N?A(o):$();await h(f,"Result graph error recording failed",()=>{g(n,y,r,Date.now(),{classification:i.classification??"product"})})}const K=Date.now();for(const y of T){const b=s instanceof I&&s.completedOutcomesByTaskId.has(String(y.id)),z=ce(y,D,s,i,r,K);await P({operationErrors:f,outcome:z,recordTaskOutcome:k,recordTiming:b,reporter:m,reportTaskFinished:!b,task:y,timingUpdates:u,trackers:n})}e.taskCount+=T.length}if(B!==null){const i=B.message;w.push(i),await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(o,i)),B.recovered||await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,i)}),await h(f,"Result graph error recording failed",()=>{g(n,$(),i,Date.now(),{classification:"infra"})})}if(f.length>0){const i=[...f];for(const r of i)w.push(r.message);const s=i.map(r=>r.message).join("; ");await h(f,"Result graph error recording failed while reporting a Testkit operation failure",()=>{g(n,$(),s,Date.now(),{classification:"infra"})});for(const r of f.slice(i.length))w.push(r.message);f.length>i.length&&q.push(...i.map(r=>r.message),...f.slice(i.length).map(r=>r.message))}}}finally{e.currentGraphKey=null}return{workerId:e.workerId,failed:w.length>0,durationMs:Date.now()-v,taskCount:e.taskCount,graphSwitches:e.graphSwitches,errors:w,unpersistedErrors:q}}async function P({operationErrors:e,outcome:a,recordTaskOutcome:t,recordTiming:l,reporter:n,reportTaskFinished:u,task:c,timingUpdates:k,trackers:g}){await h(e,`Task outcome recording failed for ${c.file}`,()=>{t(g,c,a)}),u&&await G(e,`Task-finished reporter failed for ${c.file}`,()=>n?.taskFinished?.(c,a)),l&&await h(e,`Task timing recording failed for ${c.file}`,()=>{const m=ae(c,a);m&&k.push(m)})}async function h(e,a,t){try{await t()}catch(l){e.push(j(a,l))}}async function G(e,a,t){await h(e,a,()=>te(a,t,{timeoutMs:_}))}function j(e,a){const t=x(a);return new Error(`${e}: ${t.message}`,{cause:t})}function oe(e,a){const t=e[0];if(!(a instanceof I))return t;if(a.failingTaskId!==null){const l=e.find(n=>String(n.id)===a.failingTaskId);if(l!==void 0)return l}return e.find(l=>!a.completedOutcomesByTaskId.has(String(l.id)))??t}function A(e){return{key:e.graphKey,targetNames:[e.targetName]}}async function le(e,a,t,l){const n=async u=>{await a?.release({invalidate:u}),e.remove(t)};try{return await n(l),null}catch(u){const c=x(u);try{return await n(!0),{message:`Runtime lease release failed but recovered on idempotent retry: ${c.message}`,recovered:!0}}catch(k){const g=x(k);return{message:`Runtime lease release failed: ${c.message}; idempotent retry also failed: ${g.message}`,recovered:!1}}}}function ce(e,a,t,l,n,u){const c=ee(t),k={kind:"engine-invocation",key:"engine_invocation_failed",title:"Engine invocation failed",count:1,...l.classification===void 0?{}:{classification:l.classification},message:n,...c===void 0?{}:{stack:c}},g=t instanceof I?t.completedOutcomesByTaskId.get(String(e.id)):void 0;if(g!==void 0)return g;const m=t instanceof I?t.artifactReferencesByTaskId.get(String(e.id))??[]:[];return{task:e,status:l.status,error:l.status==="failed"?n:null,reason:l.status==="failed"?null:n,infraFailed:l.classification==="infra",blockedByRuntime:l.status==="not_run",durationMs:0,startedAt:u,finishedAt:u,artifacts:[...m],failureDetails:l.status==="cancelled"?[]:[k],..."batchTasks"in a?{runtimeBatch:{id:a.batchId,index:a.batchTasks.findIndex(v=>v.id===e.id&&v.file===e.file),fileCount:a.batchTasks.length,files:a.batchTasks.map(v=>v.file)}}:{}}}function ue(e){return e instanceof I?e.disposition:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:"none",status:"not_run"}}function fe(e,a){return a.signal.aborted&&e===a.signal.reason?{classification:void 0,notifyRuntimeError:!1,runtimeImpact:"none",status:"cancelled"}:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:e instanceof N?"none":"fail-graph",status:"not_run"}}export function resolveWorkerOutcomeMappings(e,a){const t=new Map;for(const n of e){const u=W(n);if(t.has(u))throw new Error(`Worker claimed duplicate task ${n.id.toString()} (${n.file})`);t.set(u,n)}const l=new Map;for(const n of a){const u=W(n.task),c=t.get(u);if(!c)throw new Error(`Task runner returned an outcome for unclaimed task ${n.task.id.toString()} (${n.task.file})`);if(l.has(u))throw new Error(`Task runner returned duplicate outcomes for claimed task ${c.id.toString()} (${c.file})`);l.set(u,{outcome:n,task:c})}for(const n of e)if(!l.has(W(n)))throw new Error(`Task runner did not return an outcome for claimed task ${n.id.toString()} (${n.file})`);return[...l.values()]}function W(e){return JSON.stringify([typeof e.id,e.id,e.file])}function de(e,a){return e.getGraph(a.graphKey)??A(a)}function pe(e){return new Promise(a=>setTimeout(a,e))}
1
+ import{buildTaskDeadline as U,createDeadlineSignal as J,EngineExecutionBridgeError as I,executeEngineTask as H,normalizeTimeoutMs as Q}from"./engine-execution.js";import{preparationFailure as V,settlePreparedTask as X,taskStartedFailure as Y}from"./engine-execution-failures.js";import{GroupDefaultConflictError as N}from"./environment.js";import{formatError as Z,toError as x}from"../../kernel/error-serialization.js";import{formatEngineInvocationStack as ee}from"./engine-failure-stack.js";import{DEFAULT_ENGINE_REPORTER_CALLBACK_TIMEOUT_MS as _,invokeBoundedReporterCallback as te,invokeEngineReporterCallback as L}from"./engine-execution-reporting.js";import{drainRuntimeFailedTasks as ie}from"./runtime-failures.js";import{buildTimingObservation as ae}from"./scheduler/observations.js";import{buildTaskBatch as ne,claimCompatibleTaskBatch as re,expandTaskResultOutcomes as se}from"./task-batches.js";export function createWorker(e,a){return{workerId:e,productDir:a,currentGraphKey:null,graphSwitches:0,taskCount:0}}export async function runWorker(e,a,t,l,n,u,c,k,g,m=null){const v=Date.now(),w=[],q=[];try{for(;!t.isStopRequested();){const o=c(a,e.currentGraphKey,i=>t.locks.available(i.locks)&&t.environments.canAcquire(i));if(!o){const i=ie(a,t.environments);if(i.length>0){const s=Date.now(),r=new Set;for(const p of i)k(n,p.task,{status:"not_run",reason:p.reason,durationMs:0,startedAt:s,finishedAt:s,error:null,infraFailed:p.infraFailed,blockedByRuntime:p.blockedByRuntime,failureDetails:p.failureDetails}),r.has(p.graph.key)||(g(n,p.graph,p.message,s,{classification:"infra"}),r.add(p.graph.key));e.taskCount+=i.length;continue}if(a.length===0)break;await pe(10);continue}const E=t.leases.allocate(o);t.locks.acquire(o.locks,E.id);let M=null,T=[o],D=o,O=!1,R=null;const d={phase:"acquire",prepared:null};let C=[],F=[],B=null;try{T=re(a,o,{environmentKeys:t.environmentKeys}),D=ne(T)??o,e.currentGraphKey!==null&&e.currentGraphKey!==o.graphKey&&(e.graphSwitches+=1),e.currentGraphKey=o.graphKey;const i=await t.environments.acquire(D,E);M=i,t.leases.create(E),d.phase="execute";const s=await l.withEngine(o.engineId,t.signal,async r=>{let p=null;if(T.length===1){d.phase="start",await L("taskStarted",()=>m?.taskStarted?.(o,{name:o.targetName}),{signal:t.signal,timeoutMs:_}),d.phase="prepare";const $=Q(t.request.fileTimeoutSeconds),y=U(Date.now(),$),b=J(t.signal,y);try{d.prepared=await i.prepare(o,{deadlineMs:y,signal:b.signal,timeoutMs:$})}finally{b.dispose()}p={deadlineMs:y,prepared:d.prepared===null?null:{artifacts:d.prepared.artifacts,handles:d.prepared.handles,setupData:d.prepared.setupData},taskId:String(o.id)},d.phase="execute"}return H(r,{environment:d.prepared?.environment??i.environment,lease:E,request:t.request,started:p,task:D},t,m)});d.phase="mapping",C=se(s),F=resolveWorkerOutcomeMappings(T,C),F.some(({outcome:r})=>r.infraFailed&&r.blockedByRuntime)&&(O=!0)}catch(i){const s=d.phase==="start"?Y(i,String(o.id)):d.phase==="prepare"?V(i,String(o.id)):i,r=d.phase==="acquire"?fe(i,t):ue(s);R={disposition:r,error:s},O=M!==null&&r.runtimeImpact!=="none"}finally{if(d.prepared!==null){const i=await d.prepared.release(),s=await X(R===null?{outcomes:C}:{error:R.error},i,String(o.id),r=>L("taskFinished",()=>m?.taskFinished?.(o,r),{signal:t.signal,timeoutMs:_}));s!==null&&(R={disposition:s.disposition,error:s},O||=s.disposition.runtimeImpact!=="none")}B=await le(t.leases,M,E,O),t.locks.release(o.locks,E.id)}const f=[];let S=null;const K=()=>{if(S!==null)return S;try{S=de(t.environments,o)}catch(i){f.push(j("Runtime failure graph resolution failed",i)),S=A(o)}return S};if(R===null){for(const{outcome:s,task:r}of F)await P({operationErrors:f,outcome:s,recordTaskOutcome:k,recordTiming:!0,reporter:m,reportTaskFinished:!1,task:r,timingUpdates:u,trackers:n});e.taskCount+=F.length;const i=F.filter(({outcome:s})=>s.infraFailed&&s.blockedByRuntime);if(i.length>0){const s=i.map(({outcome:r})=>r.reason??r.error??"Runtime readiness failed before engine execution").join("; ");w.push(s),await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(o,s)),await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,s)}),await h(f,"Result graph error recording failed",()=>{g(n,K(),s,Date.now(),{classification:"infra"})})}}else{const{disposition:i,error:s}=R,r=Z(s),p=oe(T,s);if(i.status!=="cancelled"&&w.push(r),i.notifyRuntimeError&&await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(p,r)),i.status!=="cancelled"){i.runtimeImpact==="fail-graph"&&await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,r)});const y=s instanceof N?A(o):K();await h(f,"Result graph error recording failed",()=>{g(n,y,r,Date.now(),{classification:i.classification??"product"})})}const $=Date.now();for(const y of T){const b=s instanceof I&&s.completedOutcomesByTaskId.has(String(y.id)),z=ce(y,D,s,i,r,$);await P({operationErrors:f,outcome:z,recordTaskOutcome:k,recordTiming:b,reporter:m,reportTaskFinished:!b,task:y,timingUpdates:u,trackers:n})}e.taskCount+=T.length}if(B!==null){const i=B.message;w.push(i),await G(f,"Runtime error reporter failed",()=>m?.runtimeError?.(o,i)),B.recovered||await h(f,"Runtime graph failure recording failed",()=>{t.environments.markGraphFailed(o.graphKey,i)}),await h(f,"Result graph error recording failed",()=>{g(n,K(),i,Date.now(),{classification:"infra"})})}if(f.length>0){const i=[...f];for(const r of i)w.push(r.message);const s=i.map(r=>r.message).join("; ");await h(f,"Result graph error recording failed while reporting a Testkit operation failure",()=>{g(n,K(),s,Date.now(),{classification:"infra"})});for(const r of f.slice(i.length))w.push(r.message);f.length>i.length&&q.push(...i.map(r=>r.message),...f.slice(i.length).map(r=>r.message))}}}finally{e.currentGraphKey=null}return{workerId:e.workerId,failed:w.length>0,durationMs:Date.now()-v,taskCount:e.taskCount,graphSwitches:e.graphSwitches,errors:w,unpersistedErrors:q}}async function P({operationErrors:e,outcome:a,recordTaskOutcome:t,recordTiming:l,reporter:n,reportTaskFinished:u,task:c,timingUpdates:k,trackers:g}){await h(e,`Task outcome recording failed for ${c.file}`,()=>{t(g,c,a)}),u&&await G(e,`Task-finished reporter failed for ${c.file}`,()=>n?.taskFinished?.(c,a)),l&&await h(e,`Task timing recording failed for ${c.file}`,()=>{const m=ae(c,a);m&&k.push(m)})}async function h(e,a,t){try{await t()}catch(l){e.push(j(a,l))}}async function G(e,a,t){await h(e,a,()=>te(a,t,{timeoutMs:_}))}function j(e,a){const t=x(a);return new Error(`${e}: ${t.message}`,{cause:t})}function oe(e,a){const t=e[0];if(!(a instanceof I))return t;if(a.failingTaskId!==null){const l=e.find(n=>String(n.id)===a.failingTaskId);if(l!==void 0)return l}return e.find(l=>!a.completedOutcomesByTaskId.has(String(l.id)))??t}function A(e){return{key:e.graphKey,targetNames:[e.targetName]}}async function le(e,a,t,l){const n=async u=>{await a?.release({invalidate:u}),e.remove(t)};try{return await n(l),null}catch(u){const c=x(u);try{return await n(!0),{message:`Runtime lease release failed but recovered on idempotent retry: ${c.message}`,recovered:!0}}catch(k){const g=x(k);return{message:`Runtime lease release failed: ${c.message}; idempotent retry also failed: ${g.message}`,recovered:!1}}}}function ce(e,a,t,l,n,u){const c=ee(t),k={kind:"engine-invocation",key:"engine_invocation_failed",title:"Engine invocation failed",count:1,...l.classification===void 0?{}:{classification:l.classification},message:n,...c===void 0?{}:{stack:c}},g=t instanceof I?t.completedOutcomesByTaskId.get(String(e.id)):void 0;if(g!==void 0)return g;const m=t instanceof I?t.artifactReferencesByTaskId.get(String(e.id))??[]:[];return{task:e,status:l.status,error:l.status==="failed"?n:null,reason:l.status==="failed"?null:n,infraFailed:l.classification==="infra",blockedByRuntime:l.status==="not_run",durationMs:0,startedAt:u,finishedAt:u,artifacts:[...m],failureDetails:l.status==="cancelled"?[]:[k],..."batchTasks"in a?{runtimeBatch:{id:a.batchId,index:a.batchTasks.findIndex(v=>v.id===e.id&&v.file===e.file),fileCount:a.batchTasks.length,files:a.batchTasks.map(v=>v.file)}}:{}}}function ue(e){return e instanceof I?e.disposition:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:"none",status:"not_run"}}function fe(e,a){return a.signal.aborted&&e===a.signal.reason?{classification:void 0,notifyRuntimeError:!1,runtimeImpact:"none",status:"cancelled"}:{classification:"infra",notifyRuntimeError:!0,runtimeImpact:e instanceof N?"none":"fail-graph",status:"not_run"}}export function resolveWorkerOutcomeMappings(e,a){const t=new Map;for(const n of e){const u=W(n);if(t.has(u))throw new Error(`Worker claimed duplicate task ${n.id.toString()} (${n.file})`);t.set(u,n)}const l=new Map;for(const n of a){const u=W(n.task),c=t.get(u);if(!c)throw new Error(`Task runner returned an outcome for unclaimed task ${n.task.id.toString()} (${n.task.file})`);if(l.has(u))throw new Error(`Task runner returned duplicate outcomes for claimed task ${c.id.toString()} (${c.file})`);l.set(u,{outcome:n,task:c})}for(const n of e)if(!l.has(W(n)))throw new Error(`Task runner did not return an outcome for claimed task ${n.id.toString()} (${n.file})`);return[...l.values()]}function W(e){return JSON.stringify([typeof e.id,e.id,e.file])}function de(e,a){return e.getGraph(a.graphKey)??A(a)}function pe(e){return new Promise(a=>setTimeout(a,e))}
@@ -1 +1 @@
1
- import{createEnvironmentMaintenance as l}from"../../environment/maintenance.js";import{runtimePlanning as p}from"../../environment/runtime-planning.js";import{collectBundleCacheStatus as d}from"../../engines/bundle-cache.js";import{collectDirectorySummary as h}from"../../process/files/directory-status.js";export function collectStatusModel(t,{allConfigs:e=[t]}={}){const n=t.productDir,i=p.collectDesiredRuntimeGraphs(e),{runtimeGraphs:r,runs:o}=l(n).inspect(i,t.name),u=h(t.stateDir),s=d(n,t.name),a=S({runtimeGraphs:r,bundles:s}),m=u.exists||r.some(c=>c.exists)||o.total>0||s.exists;return{schemaVersion:1,name:t.name,product:{dir:n,configFile:t.configFile||null,services:e.map(c=>c.name).sort(),selectedService:t.name,dependencies:[...t.testkit.dependsOn].sort()},hasState:m,runs:o,serviceState:u,runtimeGraphs:r,caches:{bundles:s},warnings:a}}function S({runtimeGraphs:t,bundles:e}){const n=[],i=t.reduce((r,o)=>r+o.staleRuntimeDirs.length,0);if(i>0&&n.push(`${i.toString()} stale runtime ${i===1?"directory":"directories"} found outside the configured runtime set.`),e.duplicatedSourceCount>0&&n.push(`${e.duplicatedSourceCount.toString()} source file${e.duplicatedSourceCount===1?"":"s"} have duplicate bundle versions.`),e.unmanagedFileCount>0&&n.push(`${e.unmanagedFileCount.toString()} bundle cache file${e.unmanagedFileCount===1?"":"s"} are unmanaged by the manifest.`),(e.sourcemapFileCount??0)>0||e.sourcemapStatus==="detected"){const r=e.sourcemapFileCount==null?"Bundle files":`${e.sourcemapFileCount.toString()} bundle file${e.sourcemapFileCount===1?"":"s"}`;n.push(`${r} contain inline sourcemaps.`)}return n}
1
+ import{createEnvironmentMaintenance as l}from"../../environment/maintenance.js";import{runtimePlanning as d}from"../../environment/runtime-planning.js";import{collectBundleCacheStatus as p}from"../../engines/bundle-cache.js";import{collectDirectorySummary as h}from"../../process/files/directory-status.js";import{readRunMaintenance as f}from"../../evidence/maintenance.js";import{formatError as S}from"../../kernel/error-serialization.js";export function collectStatusModel(t,{allConfigs:e=[t]}={}){const n=t.productDir,o=d.collectDesiredRuntimeGraphs(e),{runtimeGraphs:r,runs:i}=l(n).inspect(o,t.name),s=h(t.stateDir),a=p(n,t.name),u=[...$({runtimeGraphs:r,bundles:a}),...C(n)],m=s.exists||r.some(c=>c.exists)||i.total>0||a.exists;return{schemaVersion:1,name:t.name,product:{dir:n,configFile:t.configFile||null,services:e.map(c=>c.name).sort(),selectedService:t.name,dependencies:[...t.testkit.dependsOn].sort()},hasState:m,runs:i,serviceState:s,runtimeGraphs:r,caches:{bundles:a},warnings:u}}function $({runtimeGraphs:t,bundles:e}){const n=[],o=t.reduce((r,i)=>r+i.staleRuntimeDirs.length,0);if(o>0&&n.push(`${o.toString()} stale runtime ${o===1?"directory":"directories"} found outside the configured runtime set.`),e.duplicatedSourceCount>0&&n.push(`${e.duplicatedSourceCount.toString()} source file${e.duplicatedSourceCount===1?"":"s"} have duplicate bundle versions.`),e.unmanagedFileCount>0&&n.push(`${e.unmanagedFileCount.toString()} bundle cache file${e.unmanagedFileCount===1?"":"s"} are unmanaged by the manifest.`),(e.sourcemapFileCount??0)>0||e.sourcemapStatus==="detected"){const r=e.sourcemapFileCount==null?"Bundle files":`${e.sourcemapFileCount.toString()} bundle file${e.sourcemapFileCount===1?"":"s"}`;n.push(`${r} contain inline sourcemaps.`)}return n}function C(t){try{const e=f(t);return e?.status!=="failed"?[]:[`Run ${e.runId}'s post-run maintenance failed: ${e.error??"unknown error"}`]}catch(e){return[`The last run's maintenance record could not be read: ${S(e)}`]}}
@@ -1 +1 @@
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,plain:!1,retired:[]});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
+ import c from"node:fs";import{inspectRunManifests as l}from"../../process/runs/inspection.js";import{resolveProductDir as f}from"../../process/files/product-dir.js";import{asUnknownRecord as u,displayString as o,optionalString as s}from"../../kernel/unknown-values.js";import{readLiveRunArtifact as d,summarizeLiveArtifact as p}from"../../evidence/live-run-summary.js";export{latestOperation}from"../../evidence/live-run-summary.js";export async function collectRunStatus(r){if(r.runId){const e=f(process.cwd(),r.dir);if(!c.statSync(e).isDirectory())throw new Error(`Product directory is not a directory: ${e}`);return buildRunStatus({productDir:e},r)}const{loadConfigContext:t}=await import("../../config/loader/index.js"),n=await t({dir:r.dir,allowUnmigratedServices:!0,plain:!1,retired:[]});return buildRunStatus(n,r)}export function buildRunStatus(r,t={}){const n=l(r.productDir,t.runId||void 0),e=d(r.productDir);if(t.full)return Promise.resolve({productDir:r.productDir,mode:"full",runs:n,live:e});const i={productDir:r.productDir,mode:"summary",runs:n.map(w),live:p(e)};return i.live&&(i.live.state=describeLiveArtifactState(i)),Promise.resolve(i)}export function describeLiveArtifactState(r){const t=u(r.live),n=u(t?.run),e=o(n?.id,"unknown"),i=o(n?.status,"unknown"),a=m(r,e);return i==="running"&&!a?{runId:e,status:"stale",reason:"no active run matches this live artifact"}:{runId:e,status:i,reason:s(n?.interruptReason)}}function m(r,t){if(!t||t==="unknown")return!1;for(const n of r.runs??[])if(n.active&&v(t,n.runId))return!0;return!1}function v(r,t){if(!t)return!1;const n=typeof t=="string"?t:typeof t=="number"&&Number.isFinite(t)?t.toString():null;return n?n===r||n.startsWith(`${r}:`):!1}function w(r){const t=y(r.status);return{runId:r.runId,pid:r.pid,status:r.status||"unknown",startedAt:r.startedAt||null,heartbeatAt:r.heartbeatAt||null,interruptReason:r.interruptReason||null,active:r.active,terminal:t,stale:!r.active&&!t,session:S(r.session)}}function y(r){return typeof r=="string"&&["finished","failed","interrupted"].includes(r)}function S(r){const t=u(r);return t?{kind:s(t.kind),status:s(t.status),stage:s(t.stage)}:null}
@@ -1 +1 @@
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
+ import i from"node:fs";import{resolveProductDir as s}from"../../process/files/product-dir.js";import{createEnvironmentMaintenance as a}from"../../environment/maintenance.js";import{normalizeOperationReason as e}from"../../domain/operation-reason.js";import{createRunEvidencePublisher as u}from"../../evidence/run-evidence.js";export async function stopRuns(n,r={}){return a(n).stop(r.runId?{kind:"run",runId:r.runId}:r.all?{kind:"all"}:{kind:"latest-active"},e(r.reason,"run stopped"),u())}export async function stopProductRuns(n,r){const t=s(process.cwd(),n.dir);if(!i.statSync(t).isDirectory())throw new Error(`Product directory is not a directory: ${t}`);const o=e(n.reason,r),c=await stopRuns(t,{...n,reason:o});return{productDir:t,stoppedRuns:c}}
@@ -1 +1 @@
1
- import{runManagedGitCommand as l}from"../../repo/managed-git.js";import{readGiteaVersion as p}from"../../tracker/gitea.js";import{createTracker as u}from"../../tracker/index.js";import{displayRepositoryReference as m,githubActionsApiUrl as h,parseTrackerRemote as g,parseTrackerRepositoryOverride as c,TrackerTargetError as n}from"./remote.js";import{readGitCredentialToken as d,trackerTokenFromEnvironment as f,trackerTokenVariables as w}from"./token.js";export{TrackerTargetError}from"./remote.js";const v="github.com",T="https://api.github.com";export async function resolveTracker(r){return u((await resolveRepositoryConnection(r)).target)}export async function resolveRepositoryConnection(r){const t=await k(r),{actionsRun:o,apiUrl:e,provider:i}=await R(t,r.env),a=w(i,o),s=f(a,r.env)??await d(r.productDir,t,r.env);if(s===null)throw new n(`Testkit found no token for ${t.origin}. Set ${a.join(" or ")}, or give Git a credential helper that holds one for that host.`);return{location:t,target:{apiUrl:e,name:t.name,owner:t.owner,provider:i,token:s}}}async function k(r){const t=r.repository?.trim()??"";if(/^https?:\/\//iu.test(t))return c(t,null);const o=await y(r),e=o===null?null:g(o);if(t.length>0)return c(t,e);if(e!==null)return e;throw new n(o===null?`${r.productDir} has no origin remote, so Testkit cannot tell which repository to report to. Add the remote, or name the repository as a URL such as https://host/owner/name.`:`Testkit cannot read a host, owner, and repository name from the origin remote ${JSON.stringify(m(o))}. Name the repository as a URL such as https://host/owner/name.`)}async function y(r){const t=await l(r.productDir,["config","--get","remote.origin.url"],{env:r.env,repositoryConfigOnly:!0}),o=t.stdout.trim();return t.exitCode===0&&o.length>0?o:null}async function R(r,t){if(new URL(r.origin).host===v)return{actionsRun:!1,apiUrl:T,provider:"github"};const o=h(r.origin,t);if(await p(r.origin)!==null)return{actionsRun:o!==null,apiUrl:o??`${r.origin}/api/v1`,provider:"gitea"};if(o!==null)return{actionsRun:!0,apiUrl:o,provider:"github"};throw new n(`${r.origin} does not answer as GitHub or Gitea. Testkit supports GitHub and Gitea.`)}
1
+ import{runManagedGitCommand as p}from"../../repo/managed-git.js";import{readGiteaVersion as u}from"../../tracker/gitea.js";import{createTracker as m}from"../../tracker/index.js";import{displayRepositoryReference as h,githubActionsApiUrl as g,parseTrackerRemote as d,parseTrackerRepositoryOverride as c,TrackerTargetError as n}from"./remote.js";import{missingTrackerTokenMessage as f,readGitCredentialToken as T,trackerTokenFromEnvironment as w,trackerTokenVariables as y}from"./token.js";export{TrackerTargetError}from"./remote.js";const k="github.com",v=/^(?:localhost|127(?:\.\d{1,3}){3}|\[::1\])$/iu,U="https://api.github.com";export async function resolveTracker(r){return m((await resolveRepositoryConnection(r)).target)}export async function resolveRepositoryConnection(r){const t=await R(r);l(t.origin);const{actionsRun:o,apiUrl:e,provider:i}=await G(t,r.env);l(e);const a=y(i,o,t.origin,r.env),s=w(a,r.env)??await T(r.productDir,t,r.env);if(s===null)throw new n(f(t.origin,a));return{location:t,target:{apiUrl:e,name:t.name,owner:t.owner,provider:i,token:s}}}function l(r){const t=new URL(r);if(!(t.protocol==="https:"||v.test(t.hostname)))throw new n(`Testkit sends a token only over HTTPS, and ${t.origin} uses HTTP. Use an https:// remote or repository URL. Plain HTTP is allowed only for a loopback host.`)}async function R(r){const t=r.repository?.trim()??"";if(/^https?:\/\//iu.test(t))return c(t,null);const o=await b(r),e=o===null?null:d(o);if(t.length>0)return c(t,e);if(e!==null)return e;throw new n(o===null?`${r.productDir} has no origin remote, so Testkit cannot tell which repository to report to. Add the remote, or name the repository as a URL such as https://host/owner/name.`:`Testkit cannot read a host, owner, and repository name from the origin remote ${JSON.stringify(h(o))}. Name the repository as a URL such as https://host/owner/name.`)}async function b(r){const t=await p(r.productDir,["config","--get","remote.origin.url"],{env:r.env,repositoryConfigOnly:!0}),o=t.stdout.trim();return t.exitCode===0&&o.length>0?o:null}async function G(r,t){if(new URL(r.origin).host===k)return{actionsRun:!1,apiUrl:U,provider:"github"};const o=g(r.origin,t);if(await u(r.origin)!==null)return{actionsRun:o!==null,apiUrl:o??`${r.origin}/api/v1`,provider:"gitea"};if(o!==null)return{actionsRun:!0,apiUrl:o,provider:"github"};throw new n(`${r.origin} does not answer as GitHub or Gitea. Testkit supports GitHub and Gitea.`)}
@@ -1,5 +1,5 @@
1
- import{startManagedHostProcess as a}from"../../process/index.js";import{createManagedGitEnvironment as l,DEFAULT_GIT_COMMAND_TIMEOUT_MS as u}from"../../repo/managed-git.js";const d=["GITHUB_TOKEN","GH_TOKEN"],T=["GITEA_TOKEN"],A=["GITEA_TOKEN","GITHUB_TOKEN"],E=new Set(["GIT_ASKPASS","SSH_ASKPASS"]),i="password=";export function trackerTokenVariables(e,t){return e==="github"?d:t?A:T}export function trackerTokenFromEnvironment(e,t){for(const n of e){const r=t[n]?.trim();if(r)return r}return null}export function gitCredentialRequest(e){const t=new URL(e.origin);return`protocol=${t.protocol.slice(0,-1)}
1
+ import{startManagedHostProcess as c}from"../../process/index.js";import{createManagedGitEnvironment as l,DEFAULT_GIT_COMMAND_TIMEOUT_MS as u}from"../../repo/managed-git.js";const d=["GITHUB_TOKEN","GH_TOKEN"],T=["GITEA_TOKEN"],E=["GITEA_TOKEN","GITHUB_TOKEN"],A=new Set(["GIT_ASKPASS","SSH_ASKPASS"]),i="password=";export function trackerTokenVariables(e,t,n,r){return e==="github"?d:t?E:f(r)===n?T:[]}export function missingTrackerTokenMessage(e,t){const n=t.length>0?t.join(" or "):`GITEA_TOKEN and GITEA_SERVER_URL=${e}`;return`Testkit found no token for ${e}. Set ${n}, or give Git a credential helper that holds one for that host.`}function f(e){const t=e.GITEA_SERVER_URL?.trim()??"";if(!URL.canParse(t))return null;const n=new URL(t);return n.username===""&&n.password===""?n.origin:null}export function trackerTokenFromEnvironment(e,t){for(const n of e){const r=t[n]?.trim();if(r)return r}return null}export function gitCredentialRequest(e){const t=new URL(e.origin);return`protocol=${t.protocol.slice(0,-1)}
2
2
  host=${t.host}
3
3
  path=${e.credentialPath}
4
4
 
5
- `}export function passwordFromGitCredential(e){const t=e.split(/\r?\n/u).find(n=>n.startsWith(i));return t===void 0||t.length===i.length?null:t.slice(i.length)}export async function readGitCredentialToken(e,t,n){const r=Object.fromEntries(Object.entries(n).filter(([c])=>!E.has(c.toUpperCase())));let o;try{o=a({args:["-c","core.askPass=","credential","fill"],cwd:e,displayLabel:"Git credential fill",env:{...l(r),GCM_INTERACTIVE:"never",GIT_TERMINAL_PROMPT:"0"},executable:"git",outputMode:"bounded-capture",recoveryIdentity:"none",timeoutMs:u})}catch{return null}o.stdin.on("error",()=>{}),o.stdin.end(gitCredentialRequest(t));const s=await o.completion;return s.exitCode===0?passwordFromGitCredential(s.stdout):null}
5
+ `}export function passwordFromGitCredential(e){const t=e.split(/\r?\n/u).find(n=>n.startsWith(i));return t===void 0||t.length===i.length?null:t.slice(i.length)}export async function readGitCredentialToken(e,t,n){const r=Object.fromEntries(Object.entries(n).filter(([a])=>!A.has(a.toUpperCase())));let o;try{o=c({args:["-c","core.askPass=","credential","fill"],cwd:e,displayLabel:"Git credential fill",env:{...l(r),GCM_INTERACTIVE:"never",GIT_TERMINAL_PROMPT:"0"},executable:"git",outputMode:"bounded-capture",recoveryIdentity:"none",timeoutMs:u})}catch{return null}o.stdin.on("error",()=>{}),o.stdin.end(gitCredentialRequest(t));const s=await o.completion;return s.exitCode===0?passwordFromGitCredential(s.stdout):null}
@@ -1,4 +1,4 @@
1
- import{attachErrorDiagnostics as S}from"../../kernel/error-serialization.js";import{isPathWithin as b}from"../../process/files/paths.js";import{resolveProductDir as $}from"../../process/files/product-dir.js";import l from"fs";import i from"path";import{createHash as C}from"crypto";import{createRequire as O}from"module";import{fileURLToPath as _}from"url";import{resolveVitestSelection as M}from"../../engines/catalog.js";import{NODE_VITEST_PACKAGE_REQUIREMENTS as A}from"../../engines/installations/package-requirements.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as N}from"../../engines/installations/package-requirements.js";import{hasDiscoveryErrors as R,buildDiscoveryErrorFromDiagnostics as I,detectNextApp as F,loadDiscoveredConfigContext as D}from"../../config/loader/index.js";import{managedHostProcessInfrastructureError as G,runManagedHostProcess as U}from"../../process/index.js";import{findNearestPlaywrightConfigFile as J}from"../../engines/playwright/runtime/config-file.js";import{isUnknownRecord as k}from"../../kernel/unknown-values.js";const L=600*1e3,y=i.resolve(i.dirname(_(import.meta.url)),"..","..",".."),x=O(i.join(y,"package.json")),v=()=>x("typescript");export class TestkitTypecheckError extends Error{result;results;constructor(n,t,o){super(`testkit typecheck failed for ${n}`),this.result=t,this.results=o,S(this,{result:t,results:o})}}export async function runTestkitTypecheck(e={}){w(e.timeoutMs);const n=$(process.cwd(),e.dir),t=M(e.vitest??{},n),{context:o,discovery:s}=await D({dir:n,vitest:t});return o.ownershipNotice!==null&&e.notice?.(o.ownershipNotice),runTestkitTypecheckForContext(o,s,e)}export async function runTestkitTypecheckForContext(e,n,t={}){const o=w(t.timeoutMs);if(R(e.discovery.diagnostics))throw I(e.discovery.diagnostics);const s=e.productDir,r=i.join(s,".testkit","_typecheck");l.mkdirSync(r,{recursive:!0});const c=[];if(e.configFile){const p=z({productDir:s,configFile:e.configFile,outputDir:r});c.push({label:"config",cwd:s,tsconfigPath:p})}const a=n.files.filter(p=>/\.[cm]?tsx?$/u.test(p.path)).map(p=>({engineId:p.engineId,path:p.path})),u=new Set(n.files.filter(p=>p.engineId==="playwright").map(p=>p.group));for(const p of e.groups){if(!u.has(p.name))continue;const h=J(s,p.roots);h&&a.push({engineId:"playwright",path:h})}const d=q(s,a);for(const p of d){const h=K({...p,outputDir:r,productDir:s});c.push({label:W(p.engineId,p.scopeName),cwd:p.cwd,tsconfigPath:h})}const g=[];for(const p of c){const h=await Q(p,o,t.signal);if(g.push(h),!h.ok)throw new TestkitTypecheckError(p.label,h,g)}return{ok:!0,productDir:s,results:g}}function z({productDir:e,configFile:n,outputDir:t}){const o=i.join(t,"config.tsconfig.json"),s=E(e),r={...s?{extends:f(o,s)}:{},compilerOptions:buildTypecheckCompilerOptions({tsconfigPath:o,extendsPath:s,libs:["ES2022"],rootDir:f(o,e),types:["node"]}),files:[f(o,n)],include:[],exclude:[m(o,e,"node_modules"),m(o,e,"dist"),m(o,e,".testkit")]};return l.writeFileSync(o,`${JSON.stringify(r,null,2)}
2
- `),o}function K({productDir:e,cwd:n,engineId:t,extendsPath:o,files:s,outputDir:r,scopeName:c}){const a=i.join(r,`${t}.${Y(c)}.tsconfig.json`),u=t==="playwright",d=u&&F(n),g={...o?{extends:f(a,o)}:{},compilerOptions:{...buildTypecheckCompilerOptions({engineId:t,tsconfigPath:a,extendsPath:o,libs:H(t),...d?{extraPaths:nextRuntimeTypePaths({tsconfigPath:a,serviceDir:n})}:{},types:["node"]}),...u?{allowJs:!0,checkJs:!1}:{}},files:s.map(p=>f(a,p)),include:d?[...l.existsSync(i.join(n,"next-env.d.ts"))?[f(a,i.join(n,"next-env.d.ts"))]:[],m(a,n,"tests/playwright-fixtures/**/*.ts"),m(a,n,"tests/playwright-fixtures/**/*.tsx"),m(a,n,"tests/playwright-fixtures/**/*.mts"),m(a,n,".next/types/**/*.ts"),m(a,n,".next/dev/types/**/*.ts"),m(a,e,".next-testkit/**/dist/types/**/*.ts"),m(a,e,".next-testkit/**/dist/dev/types/**/*.ts")]:[],exclude:["node_modules",".next/cache",".next/dev"]};return l.writeFileSync(a,`${JSON.stringify(g,null,2)}
3
- `),a}function q(e,n){const t=new Map,o=new Map;for(const s of n){const r=i.resolve(e,s.path),c=[s.engineId,r].join("\0");o.set(c,{...s,path:r})}for(const s of o.values()){const r=s.path,c=s.engineId==="playwright"?T(i.dirname(r),e,["tsconfig.playwright.json"]):null,a=c??T(i.dirname(r),e),u=a?i.dirname(a):e,d=V(e,u),g=`${s.engineId}\0${a??""}\0${d}`,p=t.get(g);if(p){p.files.push(r);continue}t.set(g,{cwd:u,engineId:s.engineId,extendsPath:a,files:[r,...c?B(c):[]],scopeName:d})}return[...t.values()].map(s=>({...s,files:[...new Set(s.files)].sort((r,c)=>r.localeCompare(c))})).sort((s,r)=>P(s.engineId)-P(r.engineId)||s.scopeName.localeCompare(r.scopeName))}function B(e){const n=v(),t=s=>{throw new Error(`${e}: ${n.flattenDiagnosticMessageText(s.messageText,`
4
- `)}`)},o=n.getParsedCommandLineOfConfigFile(e,{},{...n.sys,onUnRecoverableConfigFileDiagnostic:t});for(const s of o?.errors??[])s.code!==18002&&s.code!==18003&&t(s);return o?.fileNames??[]}function H(e){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:["ES2022"]}function P(e){return["node-vitest","playwright"].indexOf(e)}function W(e,n){return n==="repo"?e:`${e}:${n}`}function V(e,n){const t=i.relative(e,n);return t===""?"repo":t.split(i.sep).join("/")}function Y(e){const n=e.replace(/[^a-zA-Z0-9_-]+/g,"-");if(n===e)return e;const t=C("sha256").update(e).digest("hex").slice(0,12);return`${n}-${t}`}function Q({label:e,cwd:n,tsconfigPath:t},o,s){const r=Z(n);return U({args:[...r.args,"--noEmit","-p",t],cwd:n,displayLabel:`Testkit typecheck ${e}`,env:process.env,executable:r.command,signal:s,timeoutMs:o}).then(c=>{const a=G(`Testkit typecheck ${e}`,c);if(a)throw a;return{label:e,tsconfigPath:t,ok:c.exitCode===0,exitCode:c.exitCode,stdout:c.stdout,stderr:c.stderr}})}function w(e){const n=e??L;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Testkit typecheck timeout must be a positive safe integer in milliseconds");return n}function Z(e){let n=e;for(;;){const o=i.join(n,"node_modules","typescript","bin","tsc");if(l.existsSync(o))return{command:process.execPath,args:[o]};const s=i.dirname(n);if(s===n)break;n=s}const t=i.join(y,"node_modules","typescript","bin","tsc");if(l.existsSync(t))return{command:process.execPath,args:[t]};throw new Error(`TypeScript compiler not found from ${e} upward`)}function E(e,n=["tsconfig.json","tsconfig.build.json"]){for(const t of n){const o=i.join(e,t);if(l.existsSync(o))return o}return null}function T(e,n,t){const o=i.resolve(n);let s=i.resolve(e);for(;;){if(!b(o,s))return null;const r=E(s,t);if(r)return r;if(s===o)return null;const c=i.dirname(s);if(c===s)return null;s=c}}function f(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(f(e,n),t).split(i.sep).join("/")}function X(e){const n={};for(const[t,o]of Object.entries(j("@gfdelarue/testkit",y)))!k(o)||typeof o.types!="string"||(n[`@gfdelarue/testkit${t.slice(1)}`]=[f(e,i.join(y,o.types))]);return n}const ee=Object.freeze({packages:Object.freeze(A.filter(({authoringResolution:e})=>e==="package-exports").map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"}),te=Object.freeze({packages:Object.freeze(N.map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"});function ne(e,n){const t=oe(e);if(t===null)return{};const o={},s=f(n,i.join(y,"dist","app","typecheck","unavailable.d.ts"));for(const r of t.packages){const c=se(r.name),a=j(r.name,c);o[`${r.name}/*`]=[s];for(const u of Object.keys(a).sort()){if(u!=="."&&!u.startsWith("./")||u.includes("*"))continue;const d=u==="."?r.name:`${r.name}${u.slice(1)}`,g=ie(d,r.name,c);o[d]=[g===null?s:f(n,g)]}}return o}function oe(e){switch(e){case"node-vitest":return ee;case"playwright":return te;case void 0:return null}}function se(e){const n=re(e);let t;try{t=x.resolve(`${e}/package.json`)}catch(r){throw new Error(`Testkit typecheck requires managed public package ${e}`,{cause:r})}const o=l.realpathSync.native(i.dirname(t)),s=JSON.parse(l.readFileSync(i.join(o,"package.json"),"utf8"));return requireManagedPublicPackageIdentity(s,e,n),o}export function requireManagedPublicPackageIdentity(e,n,t){if(!k(e)||e.name!==n||e.version!==t)throw new Error(`Testkit managed public package has invalid identity: expected ${n}@${t}`)}function re(e){const n=JSON.parse(l.readFileSync(i.join(y,"package.json"),"utf8"));if(!k(n)||!k(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 j(e,n){const t=JSON.parse(l.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 ie(e,n,t){const o=v(),s=o.resolveModuleName(e,i.join(y,"__testkit_managed_public_types__.ts"),{module:o.ModuleKind.ESNext,moduleResolution:o.ModuleResolutionKind.Bundler,resolveJsonModule:!0,target:o.ScriptTarget.ES2022},o.sys).resolvedModule;if(s===void 0)return null;const r=l.realpathSync.native(s.resolvedFileName);if(!b(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:o,extraPaths:s={},libs:r,types:c}){const u={...t?pe({tsconfigPath:n,extendsPath:t}):{},...s,...X(n),...ne(e,n)};return{baseUrl:null,lib:[...r],module:"ESNext",moduleResolution:"Bundler",noEmit:!0,...o?{rootDir:o}:{},target:"ES2022",typeRoots:ce(c,n),types:[...c],paths:u}}function ce(e,n){const t=new Set;for(const o of e){const s=ae(o);let r;try{r=x.resolve(`${s}/package.json`)}catch(a){throw new Error(`Testkit typecheck requires installed type package ${s}`,{cause:a})}const c=i.dirname(r);t.add(i.dirname(c))}return[...t].map(o=>f(n,o))}function ae(e){return e==="node"?`@types/${e}`:e}function pe({tsconfigPath:e,extendsPath:n}){const t=v(),o=t.getParsedCommandLineOfConfigFile(n,{},{...t.sys,onUnRecoverableConfigFileDiagnostic:()=>{}}),s=o?.options.paths;if(!s||Object.keys(s).length===0)return{};const r=o.options.baseUrl||(typeof o.options.pathsBasePath=="string"?o.options.pathsBasePath:i.dirname(n)),c={};for(const[a,u]of Object.entries(s))c[a]=u.map(d=>f(e,i.resolve(r,d)));return c}export function nextRuntimeTypePaths({tsconfigPath:e,serviceDir:n}){const t=ue(n,[i.join("next","types.d.ts"),i.join("next","types.js")]);if(!t)return{};const o=f(e,t);return{"next/types":[o],"next/types.js":[o]}}function ue(e,n){let t=i.resolve(e);for(;;){for(const s of n){const r=i.join(t,"node_modules",s);if(l.existsSync(r))return r}const o=i.dirname(t);if(o===t)return null;t=o}}
1
+ import{attachErrorDiagnostics as _}from"../../kernel/error-serialization.js";import{isPathWithin as P}from"../../process/files/paths.js";import{resolveProductDir as M}from"../../process/files/product-dir.js";import d from"fs";import i from"path";import{createHash as O}from"crypto";import{createRequire as A}from"module";import{fileURLToPath as R}from"url";import{resolveVitestSelection as I}from"../../engines/catalog.js";import{NODE_VITEST_PACKAGE_REQUIREMENTS as N}from"../../engines/installations/package-requirements.js";import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as D}from"../../engines/installations/package-requirements.js";import{hasDiscoveryErrors as G,buildDiscoveryErrorFromDiagnostics as L,detectNextApp as U,loadDiscoveredConfigContext as J}from"../../config/loader/index.js";import{managedHostProcessInfrastructureError as z,runManagedHostProcess as B}from"../../process/index.js";import{findNearestPlaywrightConfigFile as K}from"../../engines/playwright/runtime/config-file.js";import{isUnknownRecord as x}from"../../kernel/unknown-values.js";const j=/\.[cm]?tsx?$/u,q=600*1e3,k=i.resolve(i.dirname(R(import.meta.url)),"..","..",".."),v=A(i.join(k,"package.json")),b=()=>v("typescript");export class TestkitTypecheckError extends Error{result;results;constructor(n,t,s){super(`testkit typecheck failed for ${n}`),this.result=t,this.results=s,_(this,{result:t,results:s})}}export async function runTestkitTypecheck(e={}){T(e.timeoutMs);const n=M(process.cwd(),e.dir),t=I(e.vitest??{},n),{context:s,discovery:o}=await J({dir:n,group:e.group,vitest:t});return runTestkitTypecheckForContext(s,o,e)}export async function runTestkitTypecheckForContext(e,n,t={}){const s=T(t.timeoutMs);if(G(e.discovery.diagnostics))throw L(e.discovery.diagnostics);const o=e.productDir,r=i.join(o,".testkit","_typecheck");d.mkdirSync(r,{recursive:!0});const c=[];if(e.configFile){const a=H({productDir:o,configFile:e.configFile,outputDir:r});c.push({label:"config",cwd:o,tsconfigPath:a})}const l=V(e),u=n.files.filter(a=>j.test(a.path)).flatMap(a=>(l.get(a.path)??[[]]).map(g=>({engineId:a.engineId,path:a.path,setupFiles:g}))),m=new Set(n.files.filter(a=>a.engineId==="playwright").map(a=>a.group));for(const a of e.groups){if(!m.has(a.name))continue;const g=K(o,a.roots);g&&u.push({engineId:"playwright",path:g,setupFiles:[]})}const p=Y(o,u);for(const a of p){const g=W({...a,outputDir:r,productDir:o});c.push({label:X(a,o),cwd:a.cwd,tsconfigPath:g})}const y=[];for(const a of c){const g=await ne(a,s,t.signal);if(y.push(g),!g.ok)throw new TestkitTypecheckError(a.label,g,y)}return{ok:!0,productDir:o,results:y}}function H({productDir:e,configFile:n,outputDir:t}){const s=i.join(t,"config.tsconfig.json"),o=S(e),r={...o?{extends:f(s,o)}:{},compilerOptions:buildTypecheckCompilerOptions({tsconfigPath:s,extendsPath:o,libs:["ES2022"],rootDir:f(s,e),types:["node"]}),files:[f(s,n)],include:[],exclude:[h(s,e,"node_modules"),h(s,e,"dist"),h(s,e,".testkit")]};return d.writeFileSync(s,`${JSON.stringify(r,null,2)}
2
+ `),s}function W({productDir:e,companion:n,cwd:t,engineId:s,extendsPath:o,files:r,outputDir:c,scopeName:l,setupFiles:u}){const m=u.length===0?"":`.setup-${w(u)}`,p=i.join(c,`${s}.${te(l)}${m}.tsconfig.json`),y=s==="playwright",a=y&&U(t),g={...o?{extends:f(p,o)}:{},compilerOptions:{...buildTypecheckCompilerOptions({engineId:s,tsconfigPath:p,extendsPath:o,libs:Z(s,o),...a?{extraPaths:nextRuntimeTypePaths({tsconfigPath:p,serviceDir:t})}:{},types:["node"]}),...y?{allowJs:!0,checkJs:!1}:{}},files:r.map(C=>f(p,C)),include:a&&!n?[...d.existsSync(i.join(t,"next-env.d.ts"))?[f(p,i.join(t,"next-env.d.ts"))]:[],h(p,t,"tests/playwright-fixtures/**/*.ts"),h(p,t,"tests/playwright-fixtures/**/*.tsx"),h(p,t,"tests/playwright-fixtures/**/*.mts"),h(p,t,".next/types/**/*.ts"),h(p,t,".next/dev/types/**/*.ts"),h(p,e,".next-testkit/**/dist/types/**/*.ts"),h(p,e,".next-testkit/**/dist/dev/types/**/*.ts")]:[],exclude:["node_modules",".next/cache",".next/dev"]};return d.writeFileSync(p,`${JSON.stringify(g,null,2)}
3
+ `),p}function Y(e,n){const t=new Map,s=new Map;for(const o of n){const r=i.resolve(e,o.path),c=[o.engineId,r,...o.setupFiles].join("\0");s.set(c,{...o,path:r})}for(const o of s.values()){const r=o.path,c=o.engineId==="playwright"?F(i.dirname(r),e,["tsconfig.playwright.json"]):null,l=c??F(i.dirname(r),e),u=l?i.dirname(l):e,m=ee(e,u),p=[o.engineId,l??"",m,...o.setupFiles].join("\0"),y=t.get(p);if(y){y.files.push(r);continue}t.set(p,{companion:c!==null,cwd:u,engineId:o.engineId,extendsPath:l,files:[r,...o.setupFiles,...c?Q(c):[]],scopeName:m,setupFiles:o.setupFiles})}return[...t.values()].map(o=>({...o,files:[...new Set(o.files)].sort((r,c)=>r.localeCompare(c))})).sort((o,r)=>E(o.engineId)-E(r.engineId)||o.scopeName.localeCompare(r.scopeName)||o.setupFiles.join("\0").localeCompare(r.setupFiles.join("\0")))}function V(e){const n=new Map;for(const{file:t,setupFiles:s}of e.plainFiles){const o=[...new Set(s.filter(c=>j.test(c)))].sort((c,l)=>c.localeCompare(l)),r=n.get(t)??[];r.some(c=>c.join("\0")===o.join("\0"))||r.push(o),n.set(t,r)}return n}function Q(e){const n=b(),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 Z(e,n){return e==="playwright"?["ES2022","DOM","DOM.Iterable"]:n?void 0:["ES2022"]}function E(e){return["node-vitest","playwright"].indexOf(e)}function X({engineId:e,scopeName:n,setupFiles:t},s){const o=n==="repo"?e:`${e}:${n}`;if(t.length===0)return o;const r=t.map(c=>i.relative(s,c).split(i.sep).join("/"));return`${o} with ${r.join(", ")}`}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,"-");return n===e?e:`${n}-${w([e])}`}function w(e){return O("sha256").update(e.join("\0")).digest("hex").slice(0,12)}function ne({label:e,cwd:n,tsconfigPath:t},s,o){const r=oe(n);return B({args:[...r.args,"--noEmit","-p",t],cwd:n,displayLabel:`Testkit typecheck ${e}`,env:process.env,executable:r.command,signal:o,timeoutMs:s}).then(c=>{const l=z(`Testkit typecheck ${e}`,c);if(l)throw l;return{label:e,tsconfigPath:t,ok:c.exitCode===0,exitCode:c.exitCode,stdout:c.stdout,stderr:c.stderr}})}function T(e){const n=e??q;if(!Number.isSafeInteger(n)||n<=0)throw new Error("Testkit typecheck timeout must be a positive safe integer in milliseconds");return n}function oe(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(k,"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 S(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 F(e,n,t){const s=i.resolve(n);let o=i.resolve(e);for(;;){if(!P(s,o))return null;const r=S(o,t);if(r)return r;if(o===s)return null;const c=i.dirname(o);if(c===o)return null;o=c}}function f(e,n){const t=i.relative(i.dirname(e),n).split(i.sep).join("/");return t.startsWith("../")||i.isAbsolute(t)?t:`./${t}`}function h(e,n,t){return i.join(f(e,n),t).split(i.sep).join("/")}function se(e){const n={};for(const[t,s]of Object.entries($("@gfdelarue/testkit",k)))!x(s)||typeof s.types!="string"||(n[`@gfdelarue/testkit${t.slice(1)}`]=[f(e,i.join(k,s.types))]);return n}const re=Object.freeze({packages:Object.freeze(N.filter(({authoringResolution:e})=>e==="package-exports").map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"}),ie=Object.freeze({packages:Object.freeze(D.map(({packageName:e})=>Object.freeze({name:e}))),supportedExports:"exact-manifest-exports"});function ce(e,n){const t=ae(e);if(t===null)return{};const s={},o=f(n,i.join(k,"dist","app","typecheck","unavailable.d.ts"));for(const r of t.packages){const c=pe(r.name),l=$(r.name,c);s[`${r.name}/*`]=[o];for(const u of Object.keys(l).sort()){if(u!=="."&&!u.startsWith("./")||u.includes("*"))continue;const m=u==="."?r.name:`${r.name}${u.slice(1)}`,p=ue(m,r.name,c);s[m]=[p===null?o:f(n,p)]}}return s}function ae(e){switch(e){case"node-vitest":return re;case"playwright":return ie;case void 0:return null}}function pe(e){const n=le(e);let t;try{t=v.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(!x(e)||e.name!==n||e.version!==t)throw new Error(`Testkit managed public package has invalid identity: expected ${n}@${t}`)}function le(e){const n=JSON.parse(d.readFileSync(i.join(k,"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 $(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 ue(e,n,t){const s=b(),o=s.resolveModuleName(e,i.join(k,"__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(!P(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:c}){const u={...t?ge({tsconfigPath:n,extendsPath:t}):{},...o,...se(n),...ce(e,n)};return{baseUrl:null,declarationDir:null,...r?{lib:[...r]}:{},mapRoot:null,module:"ESNext",moduleResolution:"Bundler",noEmit:!0,outDir:null,outFile:null,rootDir:s??null,sourceRoot:null,target:"ES2022",tsBuildInfoFile:f(n,n.replace(/\.json$/u,".tsbuildinfo")),typeRoots:fe(c,n),types:[...c],paths:u}}function fe(e,n){const t=new Set;for(const s of e){const o=de(s);let r;try{r=v.resolve(`${o}/package.json`)}catch(l){throw new Error(`Testkit typecheck requires installed type package ${o}`,{cause:l})}const c=i.dirname(r);t.add(i.dirname(c))}return[...t].map(s=>f(n,s))}function de(e){return e==="node"?`@types/${e}`:e}function ge({tsconfigPath:e,extendsPath:n}){const t=b(),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)),c={};for(const[l,u]of Object.entries(o))c[l]=u.map(m=>f(e,i.resolve(r,m)));return c}export function nextRuntimeTypePaths({tsconfigPath:e,serviceDir:n}){const t=me(n,[i.join("next","types.d.ts"),i.join("next","types.js")]);if(!t)return{};const s=f(e,t);return{"next/types":[s],"next/types.js":[s]}}function me(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}}