@gfdelarue/testkit 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/app/run/engine-execution.d.ts +2 -0
  2. package/dist/app/run/engine-execution.js +1 -1
  3. package/dist/cli/command-catalog.d.ts +0 -127
  4. package/dist/cli/command-catalog.js +1 -1
  5. package/dist/cli/command-registry.d.ts +1 -0
  6. package/dist/cli/command-registry.js +1 -1
  7. package/dist/cli/entrypoint.js +1 -1
  8. package/dist/cli/framework/execute.d.ts +1 -0
  9. package/dist/cli/framework/execute.js +6 -6
  10. package/dist/cli/index.js +1 -1
  11. package/dist/cli/version-drift.js +7 -7
  12. package/dist/config/authoring/index.d.ts +0 -5
  13. package/dist/config/loader/index.d.ts +0 -1
  14. package/dist/config/loader/index.js +1 -1
  15. package/dist/engines/adapters/node-startup-cache.d.ts +8 -0
  16. package/dist/engines/adapters/node-startup-cache.js +1 -0
  17. package/dist/engines/api/execution.d.ts +2 -0
  18. package/dist/engines/managed-module-resolution.js +8 -1
  19. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  20. package/dist/engines/playwright/adapter/adapter.js +1 -1
  21. package/dist/environment/ready-environment.d.ts +3 -1
  22. package/dist/environment/ready-environment.js +1 -1
  23. package/dist/environment/run-environment.js +1 -1
  24. package/dist/environment/runtime-contexts.d.ts +1 -0
  25. package/dist/environment/runtime-contexts.js +1 -1
  26. package/dist/environment/runtime-manager.js +1 -1
  27. package/dist/evidence/bundles.d.ts +2 -3
  28. package/dist/evidence/bundles.js +1 -1
  29. package/dist/evidence/contracts.d.ts +0 -8
  30. package/dist/evidence/index.d.ts +1 -1
  31. package/dist/evidence/index.js +1 -1
  32. package/dist/process/execa-bundle.json +1 -1
  33. package/docs/engine-architecture.md +20 -0
  34. package/package.json +3 -10
  35. package/testkit-config-schema.md +0 -23
  36. package/dist/app/cloud/campaigns.d.ts +0 -6
  37. package/dist/app/cloud/campaigns.js +0 -1
  38. package/dist/app/cloud/control-actions.d.ts +0 -12
  39. package/dist/app/cloud/control-actions.js +0 -2
  40. package/dist/app/cloud/control-links.d.ts +0 -5
  41. package/dist/app/cloud/control-links.js +0 -1
  42. package/dist/app/cloud/control-read.d.ts +0 -24
  43. package/dist/app/cloud/control-read.js +0 -1
  44. package/dist/app/cloud/control-resources.d.ts +0 -3
  45. package/dist/app/cloud/control-resources.js +0 -1
  46. package/dist/app/cloud/control.d.ts +0 -34
  47. package/dist/app/cloud/control.js +0 -5
  48. package/dist/app/cloud/credentials.d.ts +0 -11
  49. package/dist/app/cloud/credentials.js +0 -2
  50. package/dist/app/cloud/index.d.ts +0 -22
  51. package/dist/app/cloud/index.js +0 -2
  52. package/dist/app/cloud/login.d.ts +0 -63
  53. package/dist/app/cloud/login.js +0 -2
  54. package/dist/app/cloud/migration/archive.d.ts +0 -13
  55. package/dist/app/cloud/migration/archive.js +0 -1
  56. package/dist/app/cloud/migration/commands.d.ts +0 -8
  57. package/dist/app/cloud/migration/commands.js +0 -1
  58. package/dist/app/cloud/migration/evidence.d.ts +0 -13
  59. package/dist/app/cloud/migration/evidence.js +0 -2
  60. package/dist/app/cloud/migration/files.d.ts +0 -16
  61. package/dist/app/cloud/migration/files.js +0 -2
  62. package/dist/app/cloud/migration/index.d.ts +0 -6
  63. package/dist/app/cloud/migration/index.js +0 -1
  64. package/dist/app/cloud/migration/journal.d.ts +0 -64
  65. package/dist/app/cloud/migration/journal.js +0 -1
  66. package/dist/app/cloud/migration/mapping-dispositions.d.ts +0 -3
  67. package/dist/app/cloud/migration/mapping-dispositions.js +0 -1
  68. package/dist/app/cloud/migration/mapping.d.ts +0 -11
  69. package/dist/app/cloud/migration/mapping.js +0 -2
  70. package/dist/app/cloud/migration/model.d.ts +0 -175
  71. package/dist/app/cloud/migration/model.js +0 -1
  72. package/dist/app/cloud/migration/plan.d.ts +0 -7
  73. package/dist/app/cloud/migration/plan.js +0 -1
  74. package/dist/app/cloud/migration/progress.d.ts +0 -19
  75. package/dist/app/cloud/migration/progress.js +0 -1
  76. package/dist/app/cloud/migration/reconcile.d.ts +0 -22
  77. package/dist/app/cloud/migration/reconcile.js +0 -1
  78. package/dist/app/cloud/migration/reconciliation-spool.d.ts +0 -4
  79. package/dist/app/cloud/migration/reconciliation-spool.js +0 -1
  80. package/dist/app/cloud/migration/state.d.ts +0 -15
  81. package/dist/app/cloud/migration/state.js +0 -1
  82. package/dist/app/cloud/migration/transfer.d.ts +0 -6
  83. package/dist/app/cloud/migration/transfer.js +0 -1
  84. package/dist/cli/cloud-apply-help.d.ts +0 -1
  85. package/dist/cli/cloud-apply-help.js +0 -56
  86. package/dist/cli/cloud-selection.d.ts +0 -2
  87. package/dist/cli/cloud-selection.js +0 -3
  88. package/dist/cli/commands/cloud/apply.d.ts +0 -5
  89. package/dist/cli/commands/cloud/apply.js +0 -1
  90. package/dist/cli/commands/cloud/campaign/create.d.ts +0 -5
  91. package/dist/cli/commands/cloud/campaign/create.js +0 -1
  92. package/dist/cli/commands/cloud/download.d.ts +0 -5
  93. package/dist/cli/commands/cloud/download.js +0 -1
  94. package/dist/cli/commands/cloud/list.d.ts +0 -5
  95. package/dist/cli/commands/cloud/list.js +0 -1
  96. package/dist/cli/commands/cloud/login.d.ts +0 -13
  97. package/dist/cli/commands/cloud/login.js +0 -2
  98. package/dist/cli/commands/cloud/logout.d.ts +0 -7
  99. package/dist/cli/commands/cloud/logout.js +0 -1
  100. package/dist/cli/commands/cloud/migrate/apply.d.ts +0 -2
  101. package/dist/cli/commands/cloud/migrate/apply.js +0 -1
  102. package/dist/cli/commands/cloud/migrate/plan.d.ts +0 -7
  103. package/dist/cli/commands/cloud/migrate/plan.js +0 -1
  104. package/dist/cli/commands/cloud/migrate/progress.d.ts +0 -2
  105. package/dist/cli/commands/cloud/migrate/progress.js +0 -2
  106. package/dist/cli/commands/cloud/migrate/reconcile.d.ts +0 -2
  107. package/dist/cli/commands/cloud/migrate/reconcile.js +0 -1
  108. package/dist/cli/commands/cloud/policy/preview.d.ts +0 -5
  109. package/dist/cli/commands/cloud/policy/preview.js +0 -1
  110. package/dist/cli/commands/cloud/project/create.d.ts +0 -5
  111. package/dist/cli/commands/cloud/project/create.js +0 -1
  112. package/dist/cli/commands/cloud/publish.d.ts +0 -7
  113. package/dist/cli/commands/cloud/publish.js +0 -1
  114. package/dist/cli/commands/cloud/session/revoke.d.ts +0 -8
  115. package/dist/cli/commands/cloud/session/revoke.js +0 -1
  116. package/dist/cli/commands/cloud/show.d.ts +0 -5
  117. package/dist/cli/commands/cloud/show.js +0 -1
  118. package/dist/cli/commands/cloud/snapshot.d.ts +0 -5
  119. package/dist/cli/commands/cloud/snapshot.js +0 -1
  120. package/dist/cli/commands/cloud/status.d.ts +0 -33
  121. package/dist/cli/commands/cloud/status.js +0 -1
  122. package/dist/cloud/account.d.ts +0 -34
  123. package/dist/cloud/account.js +0 -1
  124. package/dist/cloud/archive-contract.d.ts +0 -1
  125. package/dist/cloud/archive-contract.js +0 -1
  126. package/dist/cloud/archive.d.ts +0 -12
  127. package/dist/cloud/archive.js +0 -1
  128. package/dist/cloud/campaigns.d.ts +0 -200
  129. package/dist/cloud/campaigns.js +0 -2
  130. package/dist/cloud/client.d.ts +0 -68
  131. package/dist/cloud/client.js +0 -4
  132. package/dist/cloud/download.d.ts +0 -5
  133. package/dist/cloud/download.js +0 -1
  134. package/dist/cloud/historical-contract.d.ts +0 -224
  135. package/dist/cloud/historical-contract.js +0 -1
  136. package/dist/cloud/historical-json.d.ts +0 -16
  137. package/dist/cloud/historical-json.js +0 -1
  138. package/dist/cloud/historical.d.ts +0 -69
  139. package/dist/cloud/historical.js +0 -1
  140. package/dist/cloud/pagination.d.ts +0 -5
  141. package/dist/cloud/pagination.js +0 -1
  142. package/dist/cloud/transport.d.ts +0 -18
  143. package/dist/cloud/transport.js +0 -1
  144. package/dist/config/loader/cloud.d.ts +0 -2
  145. package/dist/config/loader/cloud.js +0 -1
  146. package/docs/cloud-control.md +0 -108
  147. package/docs/testkit-cloud-boundary.md +0 -628
  148. package/node_modules/buffer-crc32/LICENSE +0 -19
  149. package/node_modules/buffer-crc32/README.md +0 -55
  150. package/node_modules/buffer-crc32/dist/index.cjs +0 -306
  151. package/node_modules/buffer-crc32/dist/index.d.cts +0 -35
  152. package/node_modules/buffer-crc32/dist/index.d.mts +0 -35
  153. package/node_modules/buffer-crc32/dist/index.mjs +0 -304
  154. package/node_modules/buffer-crc32/index.d.ts +0 -35
  155. package/node_modules/buffer-crc32/package.json +0 -54
  156. package/node_modules/yazl/LICENSE +0 -21
  157. package/node_modules/yazl/README.md +0 -457
  158. package/node_modules/yazl/index.js +0 -826
  159. package/node_modules/yazl/package.json +0 -34
@@ -21,6 +21,8 @@ export interface EngineExecutionLifecycle {
21
21
  readonly signal: AbortSignal;
22
22
  }
23
23
  export interface EngineExecutionLease extends ExecutionEnvironmentLease {
24
+ readonly engineCacheDirectory?: string;
25
+ readonly engineWorkspaceDirectory?: string;
24
26
  readonly leaseDir: string;
25
27
  readonly leaseId: string;
26
28
  }
@@ -1 +1 @@
1
- import U from"node:fs";import F from"node:path";import{EngineTaskDeadlineExceededError as Me}from"../../engines/api/index.js";import"../../domain/identifiers.js";import"../../domain/test-outcomes.js";import{resolveEngineProfile as Ie}from"../../engines/catalog.js";import{BUILT_IN_ENGINE_ORCHESTRATION as be}from"./engine-orchestration.js";import{persistTaskArtifacts as Oe}from"../../evidence/index.js";import{createEvidenceLedger as se,persistTrackedEngineEvidence as ae,requireTaskArtifacts as Ne,requireTaskEvidenceLedger as _e}from"./engine-evidence-persistence.js";import{applyConsumerNodeSetupCleanupFailures as ce,cleanupPreparedNodeSetups as ue}from"./engine-execution-cleanup.js";import{buildBridgeError as Re,EngineExecutionBridgeError as Fe,executeManagedEngineRequest as Ae,infraBridgeDisposition as q,managerFailureBridgeDisposition as Be,productBridgeDisposition as z,reporterBridgeDisposition as G,resolveBridgeDisposition as de,runtimeNodeSetupBridgeDisposition as Ce}from"./engine-execution-failures.js";import{captureEngineReporterCallback as fe,DEFAULT_ENGINE_REPORTER_CALLBACK_TIMEOUT_MS as $e,invokeEngineReporterCallback as le}from"./engine-execution-reporting.js";import{createEngineExecutionObserver as xe}from"./engine-execution-observer.js";export{EngineExecutionBridgeError}from"./engine-execution-failures.js";import{waitForEndpointReadinessPreflight as Pe}from"./endpoint-readiness.js";import{collectCheckDetailsFromRuntimeArtifacts as Le,collectFailureDetailsFromRuntimeArtifacts as je}from"./failure-details.js";import{buildRuntimeFixtureManifest as Je}from"./runtime-fixtures.js";import{MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS as Ue,runRuntimeNodeSetup as qe}from"./setup/runtime-node-setup.js";export async function executeEngineTask(e,t,n,r,s=null){const i=t,u={signal:r.signal,timeoutMs:$e},m="batchTasks"in n?n.batchTasks:[n],w=m[0],S=t.services.get(w.targetName);if(!S)throw new Error(`Runtime instance is missing target config "${w.targetName}"`);const d=S.config;we(d.productDir,"product directory"),we(i.leaseDir,"runtime lease directory");const D=Ie(w.testType,w.engineId),M=be.resolve(D),O=Xe(d.testkit.execution.fileTimeoutSeconds),ke=Date.now(),V=pe(ke,O),B=Ge(t),N=Object.hasOwn(B,w.serviceName)?B[w.serviceName]:null;if(M.endpointReadinessOwner==="orchestrator-http"){if(N===null)throw new Error(`Service "${d.name}" requires local.baseUrl for ${D.id} suites`);const C=Date.now(),v=H(r.signal,V);let y;try{y=await Pe({baseUrl:N,serviceName:d.name,lifecycle:r,signal:v.signal,options:Ye(Qe(V))})}finally{v.dispose()}if(!y.ok){const I=r.isStopRequested()||r.signal.aborted,k=Date.now(),c=buildEndpointReadinessOutcomes(m,n,y,I,C,k),a=[];let g=null;for(const E of c){const l=a.length;await fe(a,"taskStarted",()=>s?.taskStarted?.(E.task,d),u),g===null&&a.length>l&&(g=String(E.task.id)),await fe(a,"taskFinished",()=>s?.taskFinished?.(E.task,E),u),g===null&&a.length>l&&(g=String(E.task.id))}if(a.length>0){const E=a.length===1?a[0]:new AggregateError(a,"Engine readiness reporting failed");throw new Fe(`${y.reason}; ${a.map(l=>l.message).join("; ")}`,E,de(a,[],!1,I?{classification:void 0,notifyRuntimeError:!1,runtimeImpact:"none",status:"cancelled"}:G("fail-graph")),new Map(m.map(l=>[String(l.id),[]])),new Map(c.map(l=>[String(l.task.id),l])),g)}return{outcomes:c}}}return e.withEngine(D.engineId,r.signal,async C=>{const v=He(i,d.productDir);U.mkdirSync(v.temporaryDirectory,{recursive:!0}),U.mkdirSync(v.artifactDirectory,{recursive:!0});const y=new Map(m.map(p=>[String(p.id),[]])),I=new Map(m.map(p=>[String(p.id),se()])),k=new Map,c=[];let a=z(),g=!1;const E=[],l=[],W=[];let X=null;const Q=new Set,$=async(p,o)=>{const f=String(p.id);Q.has(f)||(Q.add(f),await le("taskFinished",()=>s?.taskFinished?.(p,o),u))};try{for(const[p,o]of m.entries()){a=G();const f=String(o.id);X=f,await le("taskStarted",()=>s?.taskStarted?.(o,d),u),a=z();const Z=M.buildRuntimeEnvironment({baseUrl:N,database:S.database,targetConfig:d,task:o}),_=pe(Date.now(),O),ee=H(r.signal,_);let b;try{b=await qe({cleanupTimeoutMs:Math.min(O,Ue),deadlineMs:_,targetConfig:d,task:o,lease:i,runtimeEnv:Z,signal:ee.signal})}catch(h){throw a=Ce(h),h}finally{ee.dispose()}E.push({cleanup:b.cleanup,taskId:f}),a=q();for(const h of b.artifacts){const T=Oe(d.productDir,o,[h]);y.get(String(o.id))?.push(...T)}a=z();const R={baseUrl:N,lease:i,profile:D,runtimeEnvironment:Z,targetConfig:d,task:o},te=M.resolveConfiguration(R),ve={...te===void 0?{}:{configuration:te},environment:M.buildEnvironment(R),fixtures:nt(Je(d.productDir,o.file),`fixture manifest for ${o.file}`),setupData:he(b.setupData,`node setup data for ${o.file}`),supportModules:M.resolveSupportModules(R)},ye=ze(R);a=q();const De=c.length,ne=se(),Te=xe({artifactReferencesByTaskId:y,completedOutcomesByTaskId:k,errors:c,executableTask:n,invocationEvidenceLedger:ne,productDir:d.productDir,taskBatchIndex:p,taskEvidenceLedgers:I,tasks:[o],tasksById:new Map([[f,o]]),workspaceArtifactDirectory:v.artifactDirectory},ge),x=H(r.signal,_);try{const h={invocationId:Ve(r,i,o),observer:Te,profile:D,signal:x.signal,target:{endpoints:B,inputs:Ke()},tasks:[{deadlineMs:_,id:f,file:o.file,serviceName:o.serviceName,suiteName:o.suiteName,testType:o.testType,timeoutMs:O,inputs:ve,orchestration:ye}],workspace:v};let T;try{T=await Ae(C,h)}catch(oe){throw a=Be(r.signal,x.signal,oe),oe}const re=mapEngineOutcomes([o],T)[0],j=Ne(y,f),Se=_e(I,f);ae(re.outcome.evidence,{artifactRoot:v.artifactDirectory,errors:c,ledger:Se,mode:"reconcile",productDir:d.productDir,references:j,scope:"task",task:o});const J=ge(re.outcome,o,j,n,p);k.set(f,J),J.infraFailed&&J.blockedByRuntime&&(a=q("fail-graph"));const ie=et(T);tt(ie)&&ae(ie,{artifactRoot:v.artifactDirectory,errors:c,ledger:ne,mode:"reconcile",productDir:d.productDir,references:j,scope:"invocation",task:o})}finally{x.dispose()}c.length>De&&(g=!0),E.pop();const P=await ue([{cleanup:b.cleanup,taskId:f}]);if(P.length>0){l.push(...P),ce(k,P);const h=k.get(f);if(h!==void 0)try{await $(o,h)}catch(T){c.push(A(T))}break}if(c.length>0)break;const L=k.get(f);if(L===void 0){c.push(new Error(`Engine task ${f} completed without a mapped outcome`)),g=!0;break}W.push(L),a=G();try{await $(o,L)}catch(h){c.push(A(h)),g=!0;break}}}catch(p){const o=c.length;c.push(A(p)),(o>0||a.classification==="infra")&&(g=!0)}const Y=await ue(E);l.push(...Y),ce(k,Y);for(const p of m){const o=k.get(String(p.id));if(o!==void 0)try{await $(p,o)}catch(f){c.push(A(f))}}if(c.length>0||l.length>0)throw Re(c,l,de(c,l,g,a),y,k,X);return{outcomes:W}})}function ze(e){const t=e.targetConfig.testkit.mode;if(t!==void 0&&(typeof t!="string"||t.trim().length===0))throw new Error("Testkit execution mode must be a non-empty string");const n=e.targetConfig.runtimeId;return{lease:{directory:e.lease.leaseDir,id:String(e.lease.leaseId)},mode:t??"test",...n==null?{}:{runtimeId:String(n)}}}export function mapEngineOutcomes(e,t){const n=new Map(e.map(i=>[String(i.id),i])),r=new Map;for(const i of t.outcomes){const u=n.get(i.taskId);if(!u)throw new Error(`Engine manager returned an outcome for unknown task ${i.taskId}`);if(r.has(i.taskId))throw new Error(`Engine manager returned duplicate outcomes for task ${i.taskId}`);r.set(i.taskId,{task:u,outcome:i})}const s=e.map(i=>String(i.id)).filter(i=>!r.has(i));if(s.length>0)throw new Error(`Engine manager did not return outcomes for task(s): ${s.join(", ")}`);return e.map(i=>{const u=r.get(String(i.id));if(!u)throw new Error("Engine outcome mapping invariant failed");return u})}export function buildEndpointReadinessOutcomes(e,t,n,r,s,i){const u=Math.max(0,i-s);return e.map((m,w)=>({task:m,status:r?"cancelled":"not_run",error:null,reason:n.reason,infraFailed:!r,blockedByRuntime:!r,durationMs:w===0?u:0,startedAt:s,finishedAt:i,artifacts:[],failureDetails:r?[]:[...n.failureDetails],checkDetails:[],...Ee(t,m)}))}function Ge(e){return Object.freeze(Object.fromEntries([...e.services.values()].flatMap(({config:t,baseUrl:n})=>typeof n=="string"&&n.length>0?[[t.name,n]]:[])))}function He(e,t){const n=F.join(e.leaseDir,"engine-execution");return{rootDirectory:t,temporaryDirectory:F.join(n,"temporary"),artifactDirectory:F.join(n,"artifacts")}}function Ke(){return{environment:{},fixtures:{},supportModules:{}}}function Ve(e,t,n){return[e.runId,t.leaseId,We(n)].join(":")}function We(e){return"batchId"in e?e.batchId:String(e.id)}function Xe(e){const t=e*1e3;if(!Number.isSafeInteger(t)||t<=0)throw new Error("Engine task timeout must be a positive safe integer");return t}function pe(e,t){const n=e+t;if(!Number.isSafeInteger(n)||n<=e)throw new Error("Engine task deadline must be a positive safe integer");return n}function Qe(e){return Math.max(1,e-Date.now())}function Ye(e){const t=me(process.env.TESTKIT_HTTP_ENDPOINT_PREFLIGHT_TIMEOUT_MS,3e4),n=me(process.env.TESTKIT_HTTP_ENDPOINT_PREFLIGHT_ATTEMPT_TIMEOUT_MS,5e3);return{timeoutMs:Math.min(t,e),attemptTimeoutMs:Math.min(n,e)}}function me(e,t){const n=Number(e);return Number.isInteger(n)&&n>0?n:t}function H(e,t){const n=new AbortController;let r=null;const s=()=>{n.abort(e.reason)},i=()=>{const u=t-Date.now();if(u<=0){n.abort(new Me(t));return}r=setTimeout(i,Math.min(u,2147483647)),r.unref()};return e.aborted?s():(e.addEventListener("abort",s,{once:!0}),i()),{signal:n.signal,dispose(){r!==null&&clearTimeout(r),e.removeEventListener("abort",s)}}}function ge(e,t,n,r,s){const i=je(e.evidence.artifacts),u=i.some(D=>t.engineId==="node-vitest"&&D.kind==="vitest-test"),m=[...e.failure&&!u?[Ze(e.failure)]:[],...i],w=Le(e.evidence.artifacts),S=e.status==="not_run",d=S||e.failure?.classification==="infra";return{task:t,status:e.status,error:e.status==="failed"?e.failure?.message??"Engine task failed without a message":null,reason:e.status==="failed"||e.status==="passed"?null:e.failure?.message??`Engine task ${e.status}`,infraFailed:d,blockedByRuntime:S,durationMs:e.durationMs,startedAt:e.startedAtMs,finishedAt:e.finishedAtMs,artifacts:n,failureDetails:m,checkDetails:w,...Ee(r,t,s)}}function Ee(e,t,n){if(!("batchTasks"in e))return{};const r=n??e.batchTasks.findIndex(s=>s.id===t.id&&s.file===t.file);if(r<0)throw new Error(`Task ${String(t.id)} is missing from its engine batch`);return{runtimeBatch:{id:e.batchId,index:r,fileCount:e.batchTasks.length,files:e.batchTasks.map(s=>s.file)}}}function Ze(e){const t=e.code??"ENGINE_TASK_FAILED",n={code:t,details:e.details===void 0?null:rt(e.details)};return{kind:"engine-failure",key:t,title:t,...e.classification===void 0?{}:{classification:e.classification},count:1,message:e.message,actual:n,...e.stack===void 0?{}:{stack:e.stack}}}function et(e){return{artifacts:K(e.evidence.artifacts,e.outcomes.flatMap(t=>t.evidence.artifacts)),logs:K(e.evidence.logs,e.outcomes.flatMap(t=>t.evidence.logs)),metrics:K(e.evidence.metrics,e.outcomes.flatMap(t=>t.evidence.metrics)),...e.evidence.native===void 0?{}:{native:e.evidence.native}}}function K(e,t){const n=new Map;for(const r of t){const s=JSON.stringify(r);n.set(s,(n.get(s)??0)+1)}return e.filter(r=>{const s=JSON.stringify(r),i=n.get(s)??0;return i===0?!0:(n.set(s,i-1),!1)})}function tt(e){return e.artifacts.length>0||e.logs.length>0||e.metrics.length>0||e.native!==void 0}function nt(e,t){const n=he(e,t);if(n===null||Array.isArray(n)||typeof n!="object")throw new TypeError(`${t} must contain a JSON object`);return n}function he(e,t){try{const n=JSON.stringify(e);if(typeof n!="string")throw new TypeError(`${t} must be JSON-serializable`);return JSON.parse(n)}catch(n){throw new TypeError(`${t} must be JSON-serializable`,{cause:n})}}function rt(e){return JSON.parse(JSON.stringify(e))}function we(e,t){if(!F.isAbsolute(e))throw new Error(`Engine execution ${t} must be absolute`);if(!U.statSync(e).isDirectory())throw new Error(`Engine execution ${t} must be a directory`)}function A(e){return e instanceof Error?e:new Error(String(e))}
1
+ import U from"node:fs";import F from"node:path";import{EngineTaskDeadlineExceededError as Me}from"../../engines/api/index.js";import"../../domain/identifiers.js";import"../../domain/test-outcomes.js";import{resolveEngineProfile as Ie}from"../../engines/catalog.js";import{BUILT_IN_ENGINE_ORCHESTRATION as be}from"./engine-orchestration.js";import{persistTaskArtifacts as Oe}from"../../evidence/index.js";import{createEvidenceLedger as se,persistTrackedEngineEvidence as ae,requireTaskArtifacts as Ne,requireTaskEvidenceLedger as _e}from"./engine-evidence-persistence.js";import{applyConsumerNodeSetupCleanupFailures as ce,cleanupPreparedNodeSetups as ue}from"./engine-execution-cleanup.js";import{buildBridgeError as Re,EngineExecutionBridgeError as Fe,executeManagedEngineRequest as Ae,infraBridgeDisposition as q,managerFailureBridgeDisposition as Ce,productBridgeDisposition as z,reporterBridgeDisposition as G,resolveBridgeDisposition as de,runtimeNodeSetupBridgeDisposition as Be}from"./engine-execution-failures.js";import{captureEngineReporterCallback as fe,DEFAULT_ENGINE_REPORTER_CALLBACK_TIMEOUT_MS as $e,invokeEngineReporterCallback as le}from"./engine-execution-reporting.js";import{createEngineExecutionObserver as xe}from"./engine-execution-observer.js";export{EngineExecutionBridgeError}from"./engine-execution-failures.js";import{waitForEndpointReadinessPreflight as Pe}from"./endpoint-readiness.js";import{collectCheckDetailsFromRuntimeArtifacts as Le,collectFailureDetailsFromRuntimeArtifacts as je}from"./failure-details.js";import{buildRuntimeFixtureManifest as Je}from"./runtime-fixtures.js";import{MAX_RUNTIME_NODE_SETUP_CLEANUP_TIMEOUT_MS as Ue,runRuntimeNodeSetup as qe}from"./setup/runtime-node-setup.js";export async function executeEngineTask(e,t,n,r,s=null){const i=t,u={signal:r.signal,timeoutMs:$e},m="batchTasks"in n?n.batchTasks:[n],w=m[0],S=t.services.get(w.targetName);if(!S)throw new Error(`Runtime instance is missing target config "${w.targetName}"`);const d=S.config;we(d.productDir,"product directory"),we(i.leaseDir,"runtime lease directory");const v=Ie(w.testType,w.engineId),M=be.resolve(v),O=Xe(d.testkit.execution.fileTimeoutSeconds),ke=Date.now(),V=pe(ke,O),C=Ge(t),N=Object.hasOwn(C,w.serviceName)?C[w.serviceName]:null;if(M.endpointReadinessOwner==="orchestrator-http"){if(N===null)throw new Error(`Service "${d.name}" requires local.baseUrl for ${v.id} suites`);const B=Date.now(),y=H(r.signal,V);let D;try{D=await Pe({baseUrl:N,serviceName:d.name,lifecycle:r,signal:y.signal,options:Ye(Qe(V))})}finally{y.dispose()}if(!D.ok){const I=r.isStopRequested()||r.signal.aborted,k=Date.now(),c=buildEndpointReadinessOutcomes(m,n,D,I,B,k),a=[];let g=null;for(const E of c){const l=a.length;await fe(a,"taskStarted",()=>s?.taskStarted?.(E.task,d),u),g===null&&a.length>l&&(g=String(E.task.id)),await fe(a,"taskFinished",()=>s?.taskFinished?.(E.task,E),u),g===null&&a.length>l&&(g=String(E.task.id))}if(a.length>0){const E=a.length===1?a[0]:new AggregateError(a,"Engine readiness reporting failed");throw new Fe(`${D.reason}; ${a.map(l=>l.message).join("; ")}`,E,de(a,[],!1,I?{classification:void 0,notifyRuntimeError:!1,runtimeImpact:"none",status:"cancelled"}:G("fail-graph")),new Map(m.map(l=>[String(l.id),[]])),new Map(c.map(l=>[String(l.task.id),l])),g)}return{outcomes:c}}}return e.withEngine(v.engineId,r.signal,async B=>{const y=He(i,d.productDir);U.mkdirSync(y.temporaryDirectory,{recursive:!0}),U.mkdirSync(y.artifactDirectory,{recursive:!0});const D=new Map(m.map(p=>[String(p.id),[]])),I=new Map(m.map(p=>[String(p.id),se()])),k=new Map,c=[];let a=z(),g=!1;const E=[],l=[],W=[];let X=null;const Q=new Set,$=async(p,o)=>{const f=String(p.id);Q.has(f)||(Q.add(f),await le("taskFinished",()=>s?.taskFinished?.(p,o),u))};try{for(const[p,o]of m.entries()){a=G();const f=String(o.id);X=f,await le("taskStarted",()=>s?.taskStarted?.(o,d),u),a=z();const Z=M.buildRuntimeEnvironment({baseUrl:N,database:S.database,targetConfig:d,task:o}),_=pe(Date.now(),O),ee=H(r.signal,_);let b;try{b=await qe({cleanupTimeoutMs:Math.min(O,Ue),deadlineMs:_,targetConfig:d,task:o,lease:i,runtimeEnv:Z,signal:ee.signal})}catch(h){throw a=Be(h),h}finally{ee.dispose()}E.push({cleanup:b.cleanup,taskId:f}),a=q();for(const h of b.artifacts){const T=Oe(d.productDir,o,[h]);D.get(String(o.id))?.push(...T)}a=z();const R={baseUrl:N,lease:i,profile:v,runtimeEnvironment:Z,targetConfig:d,task:o},te=M.resolveConfiguration(R),ye={...te===void 0?{}:{configuration:te},environment:M.buildEnvironment(R),fixtures:nt(Je(d.productDir,o.file),`fixture manifest for ${o.file}`),setupData:he(b.setupData,`node setup data for ${o.file}`),supportModules:M.resolveSupportModules(R)},De=ze(R);a=q();const ve=c.length,ne=se(),Te=xe({artifactReferencesByTaskId:D,completedOutcomesByTaskId:k,errors:c,executableTask:n,invocationEvidenceLedger:ne,productDir:d.productDir,taskBatchIndex:p,taskEvidenceLedgers:I,tasks:[o],tasksById:new Map([[f,o]]),workspaceArtifactDirectory:y.artifactDirectory},ge),x=H(r.signal,_);try{const h={invocationId:Ve(r,i,o),observer:Te,profile:v,signal:x.signal,target:{endpoints:C,inputs:Ke()},tasks:[{deadlineMs:_,id:f,file:o.file,serviceName:o.serviceName,suiteName:o.suiteName,testType:o.testType,timeoutMs:O,inputs:ye,orchestration:De}],workspace:y};let T;try{T=await Ae(B,h)}catch(oe){throw a=Ce(r.signal,x.signal,oe),oe}const re=mapEngineOutcomes([o],T)[0],j=Ne(D,f),Se=_e(I,f);ae(re.outcome.evidence,{artifactRoot:y.artifactDirectory,errors:c,ledger:Se,mode:"reconcile",productDir:d.productDir,references:j,scope:"task",task:o});const J=ge(re.outcome,o,j,n,p);k.set(f,J),J.infraFailed&&J.blockedByRuntime&&(a=q("fail-graph"));const ie=et(T);tt(ie)&&ae(ie,{artifactRoot:y.artifactDirectory,errors:c,ledger:ne,mode:"reconcile",productDir:d.productDir,references:j,scope:"invocation",task:o})}finally{x.dispose()}c.length>ve&&(g=!0),E.pop();const P=await ue([{cleanup:b.cleanup,taskId:f}]);if(P.length>0){l.push(...P),ce(k,P);const h=k.get(f);if(h!==void 0)try{await $(o,h)}catch(T){c.push(A(T))}break}if(c.length>0)break;const L=k.get(f);if(L===void 0){c.push(new Error(`Engine task ${f} completed without a mapped outcome`)),g=!0;break}W.push(L),a=G();try{await $(o,L)}catch(h){c.push(A(h)),g=!0;break}}}catch(p){const o=c.length;c.push(A(p)),(o>0||a.classification==="infra")&&(g=!0)}const Y=await ue(E);l.push(...Y),ce(k,Y);for(const p of m){const o=k.get(String(p.id));if(o!==void 0)try{await $(p,o)}catch(f){c.push(A(f))}}if(c.length>0||l.length>0)throw Re(c,l,de(c,l,g,a),D,k,X);return{outcomes:W}})}function ze(e){const t=e.targetConfig.testkit.mode;if(t!==void 0&&(typeof t!="string"||t.trim().length===0))throw new Error("Testkit execution mode must be a non-empty string");const n=e.targetConfig.runtimeId;return{lease:{directory:e.lease.leaseDir,id:String(e.lease.leaseId)},mode:t??"test",...n==null?{}:{runtimeId:String(n)}}}export function mapEngineOutcomes(e,t){const n=new Map(e.map(i=>[String(i.id),i])),r=new Map;for(const i of t.outcomes){const u=n.get(i.taskId);if(!u)throw new Error(`Engine manager returned an outcome for unknown task ${i.taskId}`);if(r.has(i.taskId))throw new Error(`Engine manager returned duplicate outcomes for task ${i.taskId}`);r.set(i.taskId,{task:u,outcome:i})}const s=e.map(i=>String(i.id)).filter(i=>!r.has(i));if(s.length>0)throw new Error(`Engine manager did not return outcomes for task(s): ${s.join(", ")}`);return e.map(i=>{const u=r.get(String(i.id));if(!u)throw new Error("Engine outcome mapping invariant failed");return u})}export function buildEndpointReadinessOutcomes(e,t,n,r,s,i){const u=Math.max(0,i-s);return e.map((m,w)=>({task:m,status:r?"cancelled":"not_run",error:null,reason:n.reason,infraFailed:!r,blockedByRuntime:!r,durationMs:w===0?u:0,startedAt:s,finishedAt:i,artifacts:[],failureDetails:r?[]:[...n.failureDetails],checkDetails:[],...Ee(t,m)}))}function Ge(e){return Object.freeze(Object.fromEntries([...e.services.values()].flatMap(({config:t,baseUrl:n})=>typeof n=="string"&&n.length>0?[[t.name,n]]:[])))}function He(e,t){const n=e.engineWorkspaceDirectory??F.join(e.leaseDir,"engine-execution");return{rootDirectory:t,...e.engineCacheDirectory===void 0?{}:{cacheDirectory:e.engineCacheDirectory},temporaryDirectory:F.join(n,"temporary"),artifactDirectory:F.join(n,"artifacts")}}function Ke(){return{environment:{},fixtures:{},supportModules:{}}}function Ve(e,t,n){return[e.runId,t.leaseId,We(n)].join(":")}function We(e){return"batchId"in e?e.batchId:String(e.id)}function Xe(e){const t=e*1e3;if(!Number.isSafeInteger(t)||t<=0)throw new Error("Engine task timeout must be a positive safe integer");return t}function pe(e,t){const n=e+t;if(!Number.isSafeInteger(n)||n<=e)throw new Error("Engine task deadline must be a positive safe integer");return n}function Qe(e){return Math.max(1,e-Date.now())}function Ye(e){const t=me(process.env.TESTKIT_HTTP_ENDPOINT_PREFLIGHT_TIMEOUT_MS,3e4),n=me(process.env.TESTKIT_HTTP_ENDPOINT_PREFLIGHT_ATTEMPT_TIMEOUT_MS,5e3);return{timeoutMs:Math.min(t,e),attemptTimeoutMs:Math.min(n,e)}}function me(e,t){const n=Number(e);return Number.isInteger(n)&&n>0?n:t}function H(e,t){const n=new AbortController;let r=null;const s=()=>{n.abort(e.reason)},i=()=>{const u=t-Date.now();if(u<=0){n.abort(new Me(t));return}r=setTimeout(i,Math.min(u,2147483647)),r.unref()};return e.aborted?s():(e.addEventListener("abort",s,{once:!0}),i()),{signal:n.signal,dispose(){r!==null&&clearTimeout(r),e.removeEventListener("abort",s)}}}function ge(e,t,n,r,s){const i=je(e.evidence.artifacts),u=i.some(v=>t.engineId==="node-vitest"&&v.kind==="vitest-test"),m=[...e.failure&&!u?[Ze(e.failure)]:[],...i],w=Le(e.evidence.artifacts),S=e.status==="not_run",d=S||e.failure?.classification==="infra";return{task:t,status:e.status,error:e.status==="failed"?e.failure?.message??"Engine task failed without a message":null,reason:e.status==="failed"||e.status==="passed"?null:e.failure?.message??`Engine task ${e.status}`,infraFailed:d,blockedByRuntime:S,durationMs:e.durationMs,startedAt:e.startedAtMs,finishedAt:e.finishedAtMs,artifacts:n,failureDetails:m,checkDetails:w,...Ee(r,t,s)}}function Ee(e,t,n){if(!("batchTasks"in e))return{};const r=n??e.batchTasks.findIndex(s=>s.id===t.id&&s.file===t.file);if(r<0)throw new Error(`Task ${String(t.id)} is missing from its engine batch`);return{runtimeBatch:{id:e.batchId,index:r,fileCount:e.batchTasks.length,files:e.batchTasks.map(s=>s.file)}}}function Ze(e){const t=e.code??"ENGINE_TASK_FAILED",n={code:t,details:e.details===void 0?null:rt(e.details)};return{kind:"engine-failure",key:t,title:t,...e.classification===void 0?{}:{classification:e.classification},count:1,message:e.message,actual:n,...e.stack===void 0?{}:{stack:e.stack}}}function et(e){return{artifacts:K(e.evidence.artifacts,e.outcomes.flatMap(t=>t.evidence.artifacts)),logs:K(e.evidence.logs,e.outcomes.flatMap(t=>t.evidence.logs)),metrics:K(e.evidence.metrics,e.outcomes.flatMap(t=>t.evidence.metrics)),...e.evidence.native===void 0?{}:{native:e.evidence.native}}}function K(e,t){const n=new Map;for(const r of t){const s=JSON.stringify(r);n.set(s,(n.get(s)??0)+1)}return e.filter(r=>{const s=JSON.stringify(r),i=n.get(s)??0;return i===0?!0:(n.set(s,i-1),!1)})}function tt(e){return e.artifacts.length>0||e.logs.length>0||e.metrics.length>0||e.native!==void 0}function nt(e,t){const n=he(e,t);if(n===null||Array.isArray(n)||typeof n!="object")throw new TypeError(`${t} must contain a JSON object`);return n}function he(e,t){try{const n=JSON.stringify(e);if(typeof n!="string")throw new TypeError(`${t} must be JSON-serializable`);return JSON.parse(n)}catch(n){throw new TypeError(`${t} must be JSON-serializable`,{cause:n})}}function rt(e){return JSON.parse(JSON.stringify(e))}function we(e,t){if(!F.isAbsolute(e))throw new Error(`Engine execution ${t} must be absolute`);if(!U.statSync(e).isDirectory())throw new Error(`Engine execution ${t} must be a directory`)}function A(e){return e instanceof Error?e:new Error(String(e))}
@@ -18,133 +18,6 @@ export declare const commandCatalog: Readonly<{
18
18
  readonly cache: import("./framework/definitions.js").FlagDefinition<("all" | "bundles" | "runtime")[] | undefined>;
19
19
  readonly resources: import("./framework/definitions.js").FlagDefinition<boolean>;
20
20
  }, undefined>;
21
- "cloud campaign create": import("./framework/command.js").CommandDefinition<{
22
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
23
- readonly org: import("./framework/definitions.js").FlagDefinition<string | undefined>;
24
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
25
- readonly "request-key": import("./framework/definitions.js").FlagDefinition<string | undefined>;
26
- readonly focus: import("./framework/definitions.js").FlagDefinition<string | undefined>;
27
- readonly "target-bugs": import("./framework/definitions.js").FlagDefinition<number | undefined>;
28
- }, undefined>;
29
- "cloud policy preview": import("./framework/command.js").CommandDefinition<{
30
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
31
- readonly org: import("./framework/definitions.js").FlagDefinition<string | undefined>;
32
- readonly file: import("./framework/definitions.js").FlagDefinition<string>;
33
- }, {
34
- readonly policy: import("./framework/definitions.js").ArgumentDefinition<string>;
35
- }>;
36
- "cloud download": import("./framework/command.js").CommandDefinition<{
37
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
38
- readonly receipt: import("./framework/definitions.js").FlagDefinition<string>;
39
- readonly path: import("./framework/definitions.js").FlagDefinition<string>;
40
- readonly output: import("./framework/definitions.js").FlagDefinition<string>;
41
- }, undefined>;
42
- "cloud list": import("./framework/command.js").CommandDefinition<{
43
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
44
- readonly org: import("./framework/definitions.js").FlagDefinition<string | undefined>;
45
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
46
- readonly limit: import("./framework/definitions.js").FlagDefinition<number | undefined>;
47
- readonly cursor: import("./framework/definitions.js").FlagDefinition<string | undefined>;
48
- readonly receipt: import("./framework/definitions.js").FlagDefinition<string | undefined>;
49
- readonly case: import("./framework/definitions.js").FlagDefinition<string | undefined>;
50
- readonly path: import("./framework/definitions.js").FlagDefinition<string | undefined>;
51
- readonly decision: import("./framework/definitions.js").FlagDefinition<"hold" | "ineligible" | "ship" | undefined>;
52
- readonly pr: import("./framework/definitions.js").FlagDefinition<number | undefined>;
53
- readonly sort: import("./framework/definitions.js").FlagDefinition<"accepted_at" | undefined>;
54
- readonly direction: import("./framework/definitions.js").FlagDefinition<"asc" | "desc" | undefined>;
55
- readonly flaky: import("./framework/definitions.js").FlagDefinition<boolean | undefined>;
56
- readonly page: import("./framework/definitions.js").FlagDefinition<number | undefined>;
57
- readonly branch: import("./framework/definitions.js").FlagDefinition<string | undefined>;
58
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string | undefined>;
59
- readonly "root-cause-key": import("./framework/definitions.js").FlagDefinition<string | undefined>;
60
- readonly "issue-reference": import("./framework/definitions.js").FlagDefinition<string | undefined>;
61
- readonly view: import("./framework/definitions.js").FlagDefinition<"all" | "counted" | "secondary" | undefined>;
62
- readonly order: import("./framework/definitions.js").FlagDefinition<"created" | "verified" | undefined>;
63
- }, {
64
- readonly resource: import("./framework/definitions.js").ArgumentDefinition<string>;
65
- }>;
66
- "cloud show": import("./framework/command.js").CommandDefinition<{
67
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
68
- readonly org: import("./framework/definitions.js").FlagDefinition<string | undefined>;
69
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
70
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string | undefined>;
71
- }, {
72
- readonly resource: import("./framework/definitions.js").ArgumentDefinition<"account" | "campaign" | "case" | "connection" | "finding" | "organisation" | "performance" | "policy" | "project" | "project-policy" | "receipt" | "run" | "test-file">;
73
- readonly id: import("./framework/definitions.js").ArgumentDefinition<string | undefined>;
74
- }>;
75
- "cloud project create": import("./framework/command.js").CommandDefinition<{
76
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
77
- readonly org: import("./framework/definitions.js").FlagDefinition<string | undefined>;
78
- readonly name: import("./framework/definitions.js").FlagDefinition<string | undefined>;
79
- readonly repository: import("./framework/definitions.js").FlagDefinition<string | undefined>;
80
- readonly provider: import("./framework/definitions.js").FlagDefinition<"gitea" | "github" | undefined>;
81
- readonly host: import("./framework/definitions.js").FlagDefinition<string | undefined>;
82
- readonly "publish-decisions": import("./framework/definitions.js").FlagDefinition<boolean>;
83
- readonly "request-key": import("./framework/definitions.js").FlagDefinition<string | undefined>;
84
- readonly browser: import("./framework/definitions.js").FlagDefinition<boolean>;
85
- }, undefined>;
86
- "cloud login": import("./framework/command.js").CommandDefinition<{
87
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
88
- readonly browser: import("./framework/definitions.js").FlagDefinition<boolean>;
89
- readonly force: import("./framework/definitions.js").FlagDefinition<boolean | undefined>;
90
- }, undefined>;
91
- "cloud session revoke": import("./framework/command.js").CommandDefinition<{
92
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
93
- }, {
94
- readonly id: import("./framework/definitions.js").ArgumentDefinition<string>;
95
- }>;
96
- "cloud status": import("./framework/command.js").CommandDefinition<{
97
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
98
- }, undefined>;
99
- "cloud logout": import("./framework/command.js").CommandDefinition<{
100
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
101
- }, undefined>;
102
- "cloud apply": import("./framework/command.js").CommandDefinition<{
103
- readonly browser: import("./framework/definitions.js").FlagDefinition<boolean>;
104
- readonly "secret-output": import("./framework/definitions.js").FlagDefinition<string | undefined>;
105
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string | undefined>;
106
- readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
107
- readonly file: import("./framework/definitions.js").FlagDefinition<string>;
108
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
109
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
110
- }, undefined>;
111
- "cloud migrate plan": import("./framework/command.js").CommandDefinition<{
112
- readonly source: import("./framework/definitions.js").FlagDefinition<string>;
113
- readonly registry: import("./framework/definitions.js").FlagDefinition<string>;
114
- readonly regressions: import("./framework/definitions.js").FlagDefinition<string>;
115
- readonly mapping: import("./framework/definitions.js").FlagDefinition<string>;
116
- readonly output: import("./framework/definitions.js").FlagDefinition<string>;
117
- readonly url: import("./framework/definitions.js").FlagDefinition<string>;
118
- readonly project: import("./framework/definitions.js").FlagDefinition<string>;
119
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string>;
120
- }, undefined>;
121
- "cloud migrate apply": import("./framework/command.js").CommandDefinition<{
122
- readonly plan: import("./framework/definitions.js").FlagDefinition<string>;
123
- }, undefined>;
124
- "cloud migrate reconcile": import("./framework/command.js").CommandDefinition<{
125
- readonly plan: import("./framework/definitions.js").FlagDefinition<string>;
126
- }, undefined>;
127
- "cloud publish": import("./framework/command.js").CommandDefinition<{
128
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string | undefined>;
129
- readonly dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
130
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
131
- readonly run: import("./framework/definitions.js").FlagDefinition<string | undefined>;
132
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
133
- }, undefined>;
134
- "cloud snapshot": import("./framework/command.js").CommandDefinition<{
135
- readonly campaign: import("./framework/definitions.js").FlagDefinition<string | undefined>;
136
- readonly finding: import("./framework/definitions.js").FlagDefinition<string | undefined>;
137
- readonly findings: import("./framework/definitions.js").FlagDefinition<boolean | undefined>;
138
- readonly receipts: import("./framework/definitions.js").FlagDefinition<boolean | undefined>;
139
- readonly history: import("./framework/definitions.js").FlagDefinition<boolean | undefined>;
140
- readonly "root-cause-key": import("./framework/definitions.js").FlagDefinition<string | undefined>;
141
- readonly "issue-reference": import("./framework/definitions.js").FlagDefinition<string | undefined>;
142
- readonly view: import("./framework/definitions.js").FlagDefinition<"all" | "counted" | "secondary" | undefined>;
143
- readonly limit: import("./framework/definitions.js").FlagDefinition<number | undefined>;
144
- readonly cursor: import("./framework/definitions.js").FlagDefinition<string | undefined>;
145
- readonly project: import("./framework/definitions.js").FlagDefinition<string | undefined>;
146
- readonly url: import("./framework/definitions.js").FlagDefinition<string | undefined>;
147
- }, undefined>;
148
21
  "db schema refresh": import("./framework/command.js").CommandDefinition<{
149
22
  dir: import("./framework/definitions.js").FlagDefinition<string | undefined>;
150
23
  service: import("./framework/definitions.js").FlagDefinition<string | undefined>;
@@ -1 +1 @@
1
- import{cloudListResources as o,cloudShowResources as n}from"../app/cloud/control-resources.js";import{cloudApplyHelp as a}from"./cloud-apply-help.js";import{Args as r,Flags as e}from"./framework/definitions.js";import{defineCommand as t}from"./framework/command.js";import{sharedFlags as i,runFlags as l}from"./command-flags.js";import{publicTestTypeList as c,publicTestTypeListText as s}from"../domain/test-types.js";export const commandCatalog=Object.freeze({artifacts:t({summary:"List, verify, or inspect artifacts from immutable Testkit runs",enableJsonFlag:!0,args:{file:r.string({description:"Optional test file selector",required:!1})},flags:{...i,kind:e.string({description:"Limit artifacts to one kind"}),format:e.string({description:"Output format",options:["list","text"],default:"list"}),run:e.string({description:"Read one immutable run ID instead of latest"}),list:e.boolean({description:"List retained immutable runs",default:!1}),verify:e.boolean({description:"Verify the selected run manifest and checksums",default:!1})}}),cleanup:t({summary:"Clean stale local testkit state",enableJsonFlag:!0,flags:{...i,"dry-run":e.boolean({description:"Show cleanup actions without deleting state",default:!1}),cache:e.string({description:"Clean cache state: runtime, bundles, or all",multiple:!0,options:["runtime","bundles","all"]}),resources:e.boolean({description:"Clean Testkit-owned Docker resources that are stale or orphaned",default:!1})}}),"cloud campaign create":t({summary:"Create a campaign without starting tests or investigators",enableJsonFlag:!0,flags:{url:e.string({description:"Cloud origin"}),org:e.string({description:"Organisation selector"}),project:e.string({description:"Project ID, slug, or name"}),"request-key":e.string({description:"Reuse a key to retry the same creation"}),focus:e.string({description:"Campaign focus"}),"target-bugs":e.integer({description:"Target bug count"})}}),"cloud policy preview":t({summary:"Evaluate draft policy rules against a receipt without saving",enableJsonFlag:!0,args:{policy:r.string({required:!0,description:"Policy ID"})},flags:{url:e.string({description:"Cloud origin"}),org:e.string({description:"Organisation selector"}),file:e.string({required:!0,description:"JSON file with receiptId and rules"})}}),"cloud download":t({summary:"Download and verify a receipt artifact",enableJsonFlag:!0,flags:{url:e.string({description:"Cloud origin"}),receipt:e.string({required:!0,description:"Receipt ID"}),path:e.string({required:!0,description:"Exact manifest artifact path"}),output:e.string({required:!0,description:"New destination file"})}}),"cloud list":t({summary:"List Cloud resources with their identifiers and pagination",enableJsonFlag:!0,args:{resource:r.string({required:!0,options:Object.keys(o)})},flags:{url:e.string({description:"Cloud origin"}),org:e.string({description:"Organisation ID, slug, or unambiguous name"}),project:e.string({description:"Project ID, slug, or unambiguous name"}),limit:e.integer({description:"Page size"}),cursor:e.string({description:"Opaque nextCursor from the previous page"}),receipt:e.string({description:"Receipt for artifact metadata"}),case:e.string({description:"Case ID for history"}),path:e.string({description:"Exact test file path for history"}),decision:e.string({description:"Run decision",options:["ship","hold","ineligible"]}),pr:e.integer({description:"Pull request number"}),sort:e.string({description:"Run sort",options:["accepted_at"]}),direction:e.string({description:"Run order",options:["asc","desc"]}),flaky:e.boolean({description:"Only flaky tests"}),page:e.integer({description:"Run page number"}),branch:e.string({description:"Exact run branch"}),campaign:e.string({description:"Limit findings to a campaign"}),"root-cause-key":e.string({description:"Exact finding root cause key"}),"issue-reference":e.string({description:"Exact finding issue URL"}),view:e.string({description:"Finding view",options:["all","counted","secondary"]}),order:e.string({description:"Finding order",options:["created","verified"]})}}),"cloud show":t({summary:"Inspect a Cloud resource, result, or pending connection",enableJsonFlag:!0,args:{resource:r.string({required:!0,options:n}),id:r.string({description:"Resource ID or test file path"})},flags:{url:e.string({description:"Cloud origin"}),org:e.string({description:"Organisation ID, slug, or name"}),project:e.string({description:"Project ID, slug, or name"}),campaign:e.string({description:"Campaign for a finding"})}}),"cloud project create":t({summary:"Connect a repository to Cloud and wait for any provider consent",enableJsonFlag:!0,flags:{url:e.string({description:"Cloud origin"}),org:e.string({description:"Organisation ID, slug, or name"}),name:e.string({description:"Project name; defaults to repository name"}),repository:e.string({description:"owner/repo or remote URL; defaults to Git origin"}),provider:e.string({description:"Repository provider",options:["github","gitea"]}),host:e.string({description:"Repository host"}),"publish-decisions":e.boolean({description:"Explicitly enable provider decision publishing",default:!1}),"request-key":e.string({description:"Reuse the same key to resume an uncertain creation"}),browser:e.boolean({description:"Open provider consent in the browser",allowNo:!0,default:!0})}}),"cloud login":t({summary:"Connect your Cloud account through browser approval",enableJsonFlag:!0,flags:{url:e.string({description:"Testkit Cloud origin"}),browser:e.boolean({description:"Open the approval link in your browser",allowNo:!0,default:!0}),force:e.boolean({description:"Start a new approval to switch accounts or replace a session"})}}),"cloud session revoke":t({summary:"Revoke one of your Cloud CLI sessions",enableJsonFlag:!0,args:{id:r.string({required:!0,description:"Session ID from cloud list sessions"})},flags:{url:e.string({description:"Testkit Cloud origin"})}}),"cloud status":t({summary:"Show the Cloud origin, credential source, and signed-in account",enableJsonFlag:!0,flags:{url:e.string({description:"Testkit Cloud origin"})}}),"cloud logout":t({summary:"Revoke and remove the saved Cloud account session",enableJsonFlag:!0,flags:{url:e.string({description:"Testkit Cloud origin"})}}),"cloud apply":t({summary:"Apply an idempotent, file-based mutation to Testkit Cloud",description:a,enableJsonFlag:!0,flags:{browser:e.boolean({description:"Open any required provider consent",allowNo:!0,default:!0}),"secret-output":e.string({description:"New private file for an explicitly created credential"}),campaign:e.string({description:"Apply a report to this campaign"}),dir:e.string({description:"Product directory containing optional local verification config"}),file:e.string({description:"JSON command envelope to apply",required:!0}),project:e.string({description:"Testkit Cloud project ID"}),url:e.string({description:"Testkit Cloud origin"})}}),"cloud migrate plan":t({summary:"Plan a legacy campaign migration without remote writes",enableJsonFlag:!0,flags:{source:e.string({description:"Legacy source root",required:!0}),registry:e.string({description:"Campaign registry path relative to source root",required:!0}),regressions:e.string({description:"Regression registry path relative to source root",required:!0}),mapping:e.string({description:"Mapping file, absolute or relative to source root",required:!0}),output:e.string({description:"New private output directory outside source root",required:!0}),url:e.string({description:"Testkit Cloud origin",required:!0}),project:e.string({description:"Testkit Cloud project ID",required:!0}),campaign:e.string({description:"Existing campaign ID",required:!0})}}),"cloud migrate apply":t({summary:"Apply or resume a saved legacy campaign migration",enableJsonFlag:!0,flags:{plan:e.string({description:"Saved migration plan path",required:!0})}}),"cloud migrate reconcile":t({summary:"Read back and reconcile a saved legacy campaign migration",enableJsonFlag:!0,flags:{plan:e.string({description:"Saved migration plan path",required:!0})}}),"cloud publish":t({summary:"Publish one verified immutable run bundle to Testkit Cloud",enableJsonFlag:!0,flags:{campaign:e.string({description:"Associate receipt with this campaign; requires --run"}),dir:e.string({description:"Explicit product directory"}),project:e.string({description:"Testkit Cloud project ID"}),run:e.string({description:"Immutable run ID; defaults to latest"}),url:e.string({description:"Testkit Cloud origin"})}}),"cloud snapshot":t({summary:"Read the current versioned Testkit Cloud project state",enableJsonFlag:!0,flags:{campaign:e.string({description:"Campaign ID"}),finding:e.string({description:"Finding detail within the campaign"}),findings:e.boolean({description:"Read project-wide findings"}),receipts:e.boolean({description:"Read campaign receipt history"}),history:e.boolean({description:"Read accepted campaign changes"}),"root-cause-key":e.string({description:"Exact finding root-cause key"}),"issue-reference":e.string({description:"Exact external issue URL"}),view:e.string({description:"Finding view",options:["all","counted","secondary"]}),limit:e.integer({description:"Page size, 1\u2013100; history maximum 20"}),cursor:e.string({description:"Opaque nextCursor from the preceding page"}),project:e.string({description:"Testkit Cloud project ID"}),url:e.string({description:"Testkit Cloud origin"})}}),"db schema refresh":t({summary:"Refresh a source database schema cache",enableJsonFlag:!0,flags:i}),"db schema verify":t({summary:"Verify local schema replay against the source database schema",enableJsonFlag:!0,flags:{...i,"skip-schema-source-verify":e.boolean({description:"Skip the check that local schema replay matches the source database schema",default:!1})}}),destroy:t({summary:"Destroy local testkit state",enableJsonFlag:!0,flags:i}),discover:t({summary:"Discover managed tests and report their metadata",enableJsonFlag:!0,flags:{...i,type:e.string({char:"t",multiple:!0,description:`Filter by suite type(s): ${s({includeAll:!0})}`}),suite:e.string({char:"s",multiple:!0,description:"Filter by suite selector(s)"}),file:e.string({char:"f",multiple:!0,description:"Filter by exact file path(s)"}),"runnable-only":e.boolean({description:"Show only files not skipped by repo rules",default:!1}),strict:e.boolean({description:"Fail instead of reporting discovery/config diagnostics",default:!1}),output:e.string({description:"Write machine-readable discovery JSON to a file"}),"output-mode":e.string({description:"Human-readable output mode",options:["compact","verbose"],default:"compact"})}}),doctor:t({summary:"Run built-in config, discovery, and hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),typecheck:e.boolean({description:"Include generated TypeScript checks",default:!0,allowNo:!0})}}),"guard unsafe-entrypoint":t({summary:"Fail a local script that must be run through Testkit",flags:{command:e.string({char:"c",description:"Disabled command name shown in the error message",required:!0}),use:e.string({char:"u",description:"Replacement command shown in the error message",required:!0})}}),lint:t({summary:"Run built-in Testkit repository hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),"run status":t({summary:"Show active Testkit run progress",enableJsonFlag:!0,flags:{...i,run:e.string({description:"Specific run id to show"}),full:e.boolean({description:"Include the full live status payload in JSON output"})}}),"run stop":t({summary:"Stop active Testkit run work",enableJsonFlag:!0,flags:{...i,run:e.string({description:"Specific run id to stop; defaults to the newest active run"}),all:e.boolean({description:"Stop all active runs for this product",default:!1}),reason:e.string({description:"Operator-visible reason recorded with cancelled runs and dirty slots"})}}),run:t({summary:"Run test suites",enableJsonFlag:!0,args:{type:r.string({description:`Optional suite type shortcut: ${s({includeAll:!0})}`,required:!1,options:c({includeAll:!0})})},flags:l}),"setup playwright":t({summary:"Install the managed Playwright Chromium runtime",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),force:e.boolean({default:!1,description:"Force Playwright to repair the browser installation"}),"with-deps":e.boolean({default:!1,description:"Install required host dependencies with Chromium"})}}),status:t({summary:"Show local testkit state",enableJsonFlag:!0,flags:i}),stop:t({summary:"Stop all Testkit-owned runs",enableJsonFlag:!0,flags:{...i,reason:e.string({description:"Operator-visible reason recorded with cancelled runs"})}}),typecheck:t({summary:"Typecheck testkit config, helpers, and suites",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),workspace:t({summary:"Inspect Testkit workstation data for a product",enableJsonFlag:!0,flags:{...i,"target-limit":e.integer({description:"Maximum number of quick-run target previews",default:24}),"no-status":e.boolean({description:"Skip runtime status collection",default:!1})}})});
1
+ import{Args as r,Flags as e}from"./framework/definitions.js";import{defineCommand as t}from"./framework/command.js";import{sharedFlags as s,runFlags as i}from"./command-flags.js";import{publicTestTypeList as o,publicTestTypeListText as a}from"../domain/test-types.js";export const commandCatalog=Object.freeze({artifacts:t({summary:"List, verify, or inspect artifacts from immutable Testkit runs",enableJsonFlag:!0,args:{file:r.string({description:"Optional test file selector",required:!1})},flags:{...s,kind:e.string({description:"Limit artifacts to one kind"}),format:e.string({description:"Output format",options:["list","text"],default:"list"}),run:e.string({description:"Read one immutable run ID instead of latest"}),list:e.boolean({description:"List retained immutable runs",default:!1}),verify:e.boolean({description:"Verify the selected run manifest and checksums",default:!1})}}),cleanup:t({summary:"Clean stale local testkit state",enableJsonFlag:!0,flags:{...s,"dry-run":e.boolean({description:"Show cleanup actions without deleting state",default:!1}),cache:e.string({description:"Clean cache state: runtime, bundles, or all",multiple:!0,options:["runtime","bundles","all"]}),resources:e.boolean({description:"Clean Testkit-owned Docker resources that are stale or orphaned",default:!1})}}),"db schema refresh":t({summary:"Refresh a source database schema cache",enableJsonFlag:!0,flags:s}),"db schema verify":t({summary:"Verify local schema replay against the source database schema",enableJsonFlag:!0,flags:{...s,"skip-schema-source-verify":e.boolean({description:"Skip the check that local schema replay matches the source database schema",default:!1})}}),destroy:t({summary:"Destroy local testkit state",enableJsonFlag:!0,flags:s}),discover:t({summary:"Discover managed tests and report their metadata",enableJsonFlag:!0,flags:{...s,type:e.string({char:"t",multiple:!0,description:`Filter by suite type(s): ${a({includeAll:!0})}`}),suite:e.string({char:"s",multiple:!0,description:"Filter by suite selector(s)"}),file:e.string({char:"f",multiple:!0,description:"Filter by exact file path(s)"}),"runnable-only":e.boolean({description:"Show only files not skipped by repo rules",default:!1}),strict:e.boolean({description:"Fail instead of reporting discovery/config diagnostics",default:!1}),output:e.string({description:"Write machine-readable discovery JSON to a file"}),"output-mode":e.string({description:"Human-readable output mode",options:["compact","verbose"],default:"compact"})}}),doctor:t({summary:"Run built-in config, discovery, and hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),typecheck:e.boolean({description:"Include generated TypeScript checks",default:!0,allowNo:!0})}}),"guard unsafe-entrypoint":t({summary:"Fail a local script that must be run through Testkit",flags:{command:e.string({char:"c",description:"Disabled command name shown in the error message",required:!0}),use:e.string({char:"u",description:"Replacement command shown in the error message",required:!0})}}),lint:t({summary:"Run built-in Testkit repository hygiene checks",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),"run status":t({summary:"Show active Testkit run progress",enableJsonFlag:!0,flags:{...s,run:e.string({description:"Specific run id to show"}),full:e.boolean({description:"Include the full live status payload in JSON output"})}}),"run stop":t({summary:"Stop active Testkit run work",enableJsonFlag:!0,flags:{...s,run:e.string({description:"Specific run id to stop; defaults to the newest active run"}),all:e.boolean({description:"Stop all active runs for this product",default:!1}),reason:e.string({description:"Operator-visible reason recorded with cancelled runs and dirty slots"})}}),run:t({summary:"Run test suites",enableJsonFlag:!0,args:{type:r.string({description:`Optional suite type shortcut: ${a({includeAll:!0})}`,required:!1,options:o({includeAll:!0})})},flags:i}),"setup playwright":t({summary:"Install the managed Playwright Chromium runtime",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"}),force:e.boolean({default:!1,description:"Force Playwright to repair the browser installation"}),"with-deps":e.boolean({default:!1,description:"Install required host dependencies with Chromium"})}}),status:t({summary:"Show local testkit state",enableJsonFlag:!0,flags:s}),stop:t({summary:"Stop all Testkit-owned runs",enableJsonFlag:!0,flags:{...s,reason:e.string({description:"Operator-visible reason recorded with cancelled runs"})}}),typecheck:t({summary:"Typecheck testkit config, helpers, and suites",enableJsonFlag:!0,flags:{dir:e.string({description:"Product directory"})}}),workspace:t({summary:"Inspect Testkit workstation data for a product",enableJsonFlag:!0,flags:{...s,"target-limit":e.integer({description:"Maximum number of quick-run target previews",default:24}),"no-status":e.boolean({description:"Skip runtime status collection",default:!1})}})});
@@ -1,2 +1,3 @@
1
1
  import type { CommandRegistration } from "./framework/command.js";
2
2
  export declare const commandRegistry: readonly CommandRegistration[];
3
+ export declare const removedCommands: ReadonlyMap<string, string>;
@@ -1 +1 @@
1
- import{commandCatalog as i}from"./command-catalog.js";export const commandRegistry=Object.freeze([o("artifacts",()=>import("./commands/artifacts.js")),o("cleanup",()=>import("./commands/cleanup.js")),o("cloud apply",()=>import("./commands/cloud/apply.js")),o("cloud campaign create",()=>import("./commands/cloud/campaign/create.js")),o("cloud policy preview",()=>import("./commands/cloud/policy/preview.js")),o("cloud download",()=>import("./commands/cloud/download.js")),o("cloud list",()=>import("./commands/cloud/list.js")),o("cloud show",()=>import("./commands/cloud/show.js")),o("cloud project create",()=>import("./commands/cloud/project/create.js")),o("cloud login",()=>import("./commands/cloud/login.js")),o("cloud logout",()=>import("./commands/cloud/logout.js")),o("cloud session revoke",()=>import("./commands/cloud/session/revoke.js")),o("cloud status",()=>import("./commands/cloud/status.js")),o("cloud migrate plan",()=>import("./commands/cloud/migrate/plan.js")),o("cloud migrate apply",()=>import("./commands/cloud/migrate/apply.js")),o("cloud migrate reconcile",()=>import("./commands/cloud/migrate/reconcile.js")),o("cloud publish",()=>import("./commands/cloud/publish.js")),o("cloud snapshot",()=>import("./commands/cloud/snapshot.js")),o("db schema refresh",()=>import("./commands/db/schema/refresh.js")),o("db schema verify",()=>import("./commands/db/schema/verify.js")),o("destroy",()=>import("./commands/destroy.js")),o("discover",()=>import("./commands/discover.js")),o("doctor",()=>import("./commands/doctor.js")),o("guard unsafe-entrypoint",()=>import("./commands/guard/unsafe-entrypoint.js")),o("lint",()=>import("./commands/lint.js")),o("run status",()=>import("./commands/run/status.js")),o("run stop",()=>import("./commands/run/stop.js")),o("run",()=>import("./commands/run.js")),o("setup playwright",()=>import("./commands/setup/playwright.js")),o("status",()=>import("./commands/status.js")),o("stop",()=>import("./commands/stop.js")),o("typecheck",()=>import("./commands/typecheck.js")),o("workspace",()=>import("./commands/workspace.js"))]);function o(t,r){return Object.freeze({...i[t],id:t.split(" "),load:r})}
1
+ import{commandCatalog as e}from"./command-catalog.js";export const commandRegistry=Object.freeze([t("artifacts",()=>import("./commands/artifacts.js")),t("cleanup",()=>import("./commands/cleanup.js")),t("db schema refresh",()=>import("./commands/db/schema/refresh.js")),t("db schema verify",()=>import("./commands/db/schema/verify.js")),t("destroy",()=>import("./commands/destroy.js")),t("discover",()=>import("./commands/discover.js")),t("doctor",()=>import("./commands/doctor.js")),t("guard unsafe-entrypoint",()=>import("./commands/guard/unsafe-entrypoint.js")),t("lint",()=>import("./commands/lint.js")),t("run status",()=>import("./commands/run/status.js")),t("run stop",()=>import("./commands/run/stop.js")),t("run",()=>import("./commands/run.js")),t("setup playwright",()=>import("./commands/setup/playwright.js")),t("status",()=>import("./commands/status.js")),t("stop",()=>import("./commands/stop.js")),t("typecheck",()=>import("./commands/typecheck.js")),t("workspace",()=>import("./commands/workspace.js"))]),removedCommands=new Map([["cloud","The testkit cloud commands were removed in Testkit 0.4.0. Testkit no longer publishes runs to Testkit Cloud."]]);function t(r,o){return Object.freeze({...e[r],id:r.split(" "),load:o})}
@@ -1 +1 @@
1
- import{commandRegistry as c}from"./command-registry.js";export function normalizeCliArgs(n){if(n[0]==="help")return f(n);if(u(n))return[...n];const t=new Set(c.map(s=>s.id[0])),l=new Set(c.flatMap(s=>Object.entries(s.flags??{}).flatMap(([i,r])=>r.kind==="boolean"?[]:[`--${i}`,...r.char?[`-${r.char}`]:[]]))),o=m(n,l),e=o.at(0)??null;return e!==null&&t.has(e.value)?d(n,o):["run",...n]}function u(n){const t=n[0];return t==="--help"||t==="-h"||t==="--version"||t==="-v"}function f(n){const t=n[1];return t?[t,...n.slice(2),"--help"]:["--help"]}function m(n,t){const l=[];for(let o=0;o<n.length;o+=1){const e=n[o];if(e==="--")break;if(!e.startsWith("-")){l.push({index:o,value:e});continue}const s=e.indexOf("="),i=s<0?e:e.slice(0,s);s<0&&t.has(i)&&(o+=1)}return l}function d(n,t){if(!t.at(0))return[...n];const o=[...c].sort((i,r)=>r.id.length-i.id.length).find(i=>i.id.every((r,a)=>t[a]?.value===r)),e=t.slice(0,o?.id.length??1),s=new Set(e.map(i=>i.index));return[...e.map(i=>i.value),...n.filter((i,r)=>!s.has(r))]}
1
+ import{commandRegistry as c,removedCommands as u}from"./command-registry.js";export function normalizeCliArgs(n){if(n[0]==="help")return m(n);if(f(n))return[...n];const e=new Set([...c.map(s=>s.id[0]),...u.keys()]),l=new Set(c.flatMap(s=>Object.entries(s.flags??{}).flatMap(([i,r])=>r.kind==="boolean"?[]:[`--${i}`,...r.char?[`-${r.char}`]:[]]))),o=d(n,l),t=o.at(0)??null;return t!==null&&e.has(t.value)?h(n,o):["run",...n]}function f(n){const e=n[0];return e==="--help"||e==="-h"||e==="--version"||e==="-v"}function m(n){const e=n[1];return e?[e,...n.slice(2),"--help"]:["--help"]}function d(n,e){const l=[];for(let o=0;o<n.length;o+=1){const t=n[o];if(t==="--")break;if(!t.startsWith("-")){l.push({index:o,value:t});continue}const s=t.indexOf("="),i=s<0?t:t.slice(0,s);s<0&&e.has(i)&&(o+=1)}return l}function h(n,e){if(!e.at(0))return[...n];const o=[...c].sort((i,r)=>r.id.length-i.id.length).find(i=>i.id.every((r,a)=>e[a]?.value===r)),t=e.slice(0,o?.id.length??1),s=new Set(t.map(i=>i.index));return[...t.map(i=>i.value),...n.filter((i,r)=>!s.has(r))]}
@@ -9,5 +9,6 @@ export interface ExecuteCliOptions {
9
9
  readonly beforeAction?: (id: readonly string[]) => Promise<void>;
10
10
  readonly identity: CliIdentity;
11
11
  readonly registrations: readonly CommandRegistration[];
12
+ readonly removedCommands?: ReadonlyMap<string, string>;
12
13
  }
13
14
  export declare function executeCliArguments(argv: readonly string[], options: Readonly<ExecuteCliOptions>): Promise<void>;
@@ -1,6 +1,6 @@
1
- import{serializeCliError as g,writeStandardOutput as m}from"../json-errors.js";import{CliUsageError as l,commandExitCode as p,errorMessage as w}from"./errors.js";import{renderCommandHelp as h,renderRootHelp as C,renderTopicHelp as x,renderVersion as O}from"./help.js";import{parseCommandArguments as S}from"./parser.js";export async function executeCliArguments(t,n){const r=H;if(t.length===1&&o(t[0])){await r.writeStandardOutput(C(n.identity,n.registrations));return}if(t.length===1&&y(t[0])){await r.writeStandardOutput(O(n.identity));return}let e;try{e=E(t,n.registrations)}catch(a){const s=F(t,n.registrations);if(s){await r.writeStandardOutput(x(s,n.registrations));return}await c(a,!1,r);return}if(u(e.argv,o)){await r.writeStandardOutput(h(e.registration,n.registrations));return}const i=e.registration.enableJsonFlag===!0&&u(e.argv,a=>a==="--json");try{const a=S(e.argv,e.registration.flags,e.registration.args,e.registration.enableJsonFlag===!0);await n.beforeAction?.(e.registration.id);const d=await(await e.registration.load()).default({...a,json:i,log(f=""){r.writeStandardOutput(`${f}
2
- `)},setExitCode:r.setExitCode});i&&d!==void 0&&await r.writeStandardOutput(`${JSON.stringify(d,null,2)}
3
- `)}catch(a){await c(a,i,r)}}function E(t,n){const e=n.filter(i=>i.id.every((a,s)=>t[s]===a)).sort((i,a)=>a.id.length-i.id.length).at(0);if(!e){const i=t.filter(a=>!a.startsWith("-")).at(0);throw new l(i?`Unknown command: ${i}`:"A Testkit command is required")}return{registration:e,argv:t.slice(e.id.length)}}async function c(t,n,r){const e=p(t);if(n){await r.writeStandardOutput(`${JSON.stringify({error:g(t)},null,2)}
4
- `),r.setExitCode(e);return}r.setExitCode(e);const i=t instanceof l?`
5
- Run "testkit --help" to see available commands and options.`:"";r.writeStandardError(`Error: ${w(t)}${i}
6
- `)}function o(t){return t==="--help"||t==="-h"}function y(t){return t==="--version"||t==="-v"}function F(t,n){const r=t.findIndex(o);if(r<1)return null;const e=t.slice(0,r);return n.some(i=>i.id.length>e.length&&e.every((a,s)=>i.id[s]===a))?e:null}function u(t,n){const r=t.indexOf("--");return(r<0?t:t.slice(0,r)).some(n)}const H={setExitCode(t){process.exitCode=t},writeStandardError(t){process.stderr.write(t)},writeStandardOutput:m};
1
+ import{serializeCliError as g,writeStandardOutput as m}from"../json-errors.js";import{CliUsageError as d,commandExitCode as w,errorMessage as p}from"./errors.js";import{renderCommandHelp as h,renderRootHelp as C,renderTopicHelp as x,renderVersion as O}from"./help.js";import{parseCommandArguments as S}from"./parser.js";export async function executeCliArguments(t,i){const e=H;if(t.length===1&&l(t[0])){await e.writeStandardOutput(C(i.identity,i.registrations));return}if(t.length===1&&y(t[0])){await e.writeStandardOutput(O(i.identity));return}let r;try{r=E(t,i)}catch(n){const s=F(t,i.registrations);if(s){await e.writeStandardOutput(x(s,i.registrations));return}await c(n,!1,e);return}if(u(r.argv,l)){await e.writeStandardOutput(h(r.registration,i.registrations));return}const a=r.registration.enableJsonFlag===!0&&u(r.argv,n=>n==="--json");try{const n=S(r.argv,r.registration.flags,r.registration.args,r.registration.enableJsonFlag===!0);await i.beforeAction?.(r.registration.id);const o=await(await r.registration.load()).default({...n,json:a,log(f=""){e.writeStandardOutput(`${f}
2
+ `)},setExitCode:e.setExitCode});a&&o!==void 0&&await e.writeStandardOutput(`${JSON.stringify(o,null,2)}
3
+ `)}catch(n){await c(n,a,e)}}function E(t,{registrations:i,removedCommands:e}){const a=i.filter(n=>n.id.every((s,o)=>t[o]===s)).sort((n,s)=>s.id.length-n.id.length).at(0);if(!a){const n=t.filter(s=>!s.startsWith("-")).at(0);throw n?new d(e?.get(n)??`Unknown command: ${n}`):new d("A Testkit command is required")}return{registration:a,argv:t.slice(a.id.length)}}async function c(t,i,e){const r=w(t);if(i){await e.writeStandardOutput(`${JSON.stringify({error:g(t)},null,2)}
4
+ `),e.setExitCode(r);return}e.setExitCode(r);const a=t instanceof d?`
5
+ Run "testkit --help" to see available commands and options.`:"";e.writeStandardError(`Error: ${p(t)}${a}
6
+ `)}function l(t){return t==="--help"||t==="-h"}function y(t){return t==="--version"||t==="-v"}function F(t,i){const e=t.findIndex(l);if(e<1)return null;const r=t.slice(0,e);return i.some(a=>a.id.length>r.length&&r.every((n,s)=>a.id[s]===n))?r:null}function u(t,i){const e=t.indexOf("--");return(e<0?t:t.slice(0,e)).some(i)}const H={setExitCode(t){process.exitCode=t},writeStandardError(t){process.stderr.write(t)},writeStandardOutput:m};
package/dist/cli/index.js CHANGED
@@ -1 +1 @@
1
- import{testkitIdentity as r}from"../app/package-inspection.js";import{commandRegistry as e}from"./command-registry.js";import{normalizeCliArgs as o}from"./entrypoint.js";import{executeCliArguments as n}from"./framework/index.js";export async function executeCli(t,i={}){await n(t,{...i,identity:r(),registrations:e})}export async function run(t=process.argv){await executeCli(o(t.slice(2)))}
1
+ import{testkitIdentity as r}from"../app/package-inspection.js";import{commandRegistry as e,removedCommands as o}from"./command-registry.js";import{normalizeCliArgs as m}from"./entrypoint.js";import{executeCliArguments as n}from"./framework/index.js";export async function executeCli(t,i={}){await n(t,{...i,identity:r(),registrations:e,removedCommands:o})}export async function run(t=process.argv){await executeCli(m(t.slice(2)))}
@@ -1,7 +1,7 @@
1
- import{inspectPackageVersionDrift as a}from"../app/package-inspection.js";export function checkCliVersionDrift(t,e={}){if(S(t))return null;const r=a({...e,binUrl:e.binUrl||import.meta.url,dir:g(t)});return r?p({...r,args:t}):null}export function emitCliVersionDriftAndExit(t,{json:e=!1}={}){if(!t)return!1;const r=f;return e?r.writeStandardOutput(`${JSON.stringify({error:t},null,2)}
2
- `):(r.writeStandardError(`${t.message}
3
- `),r.writeStandardError(`Current binary: @gfdelarue/testkit ${t.current.version} (${t.current.packageRoot})
4
- `),t.product.version&&t.product.packageRoot&&r.writeStandardError(`Product install: @gfdelarue/testkit ${t.product.version} (${t.product.packageRoot})
5
- `),t.product.declaredSpec&&r.writeStandardError(`Product declaration: ${t.product.declaredSpec}
6
- `),r.writeStandardError(`Run: ${t.hint}
7
- `)),r.setExitCode(1),!0}export function isJsonMode(t=[]){return o(t).includes("--json")}const f=Object.freeze({setExitCode(t){process.exitCode=t},writeStandardError(t){process.stderr.write(t)},writeStandardOutput(t){process.stdout.write(t)}});function p({productDir:t,currentPackageRoot:e,currentVersion:r,productPackageRoot:n,productVersion:i,declaredSpec:s,args:d}){const c=n?"./node_modules/.bin/testkit":"npm exec testkit --",l=`cd ${u(t)} && ${c} ${$(k(d))}`.trim();return{code:"testkit_cli_version_mismatch",message:`Testkit CLI version mismatch for ${t}`,current:{version:r,packageRoot:e},product:{dir:t,version:i,packageRoot:n,declaredSpec:s||null},hint:l}}function S(t=[]){const e=t[0];return!!(!e||e==="--help"||e==="-h"||e==="--version"||e==="-v"||e==="help"||e==="cloud"&&["login","status","session","logout","list","show","project","campaign","policy","download","snapshot"].includes(t[1]??""))}function k(t=[]){const e=[];for(let r=0;r<t.length;r+=1){const n=t[r];if(n==="--"){e.push(...t.slice(r));break}if(n==="--dir"){const i=t.at(r+1);i!==void 0&&i!=="--"&&(r+=1);continue}n.startsWith("--dir=")||e.push(n)}return e}function g(t){const e=o(t);for(let r=0;r<e.length;r+=1){const n=e[r];if(n==="--dir"){const i=e.at(r+1);return i&&(i==="-"||!i.startsWith("-"))?i:null}if(n.startsWith("--dir="))return n.slice(6)||null}return null}function o(t){const e=t.indexOf("--");return e<0?t:t.slice(0,e)}function $(t=[]){return t.map(u).join(" ")}function u(t){const e=t;return/^[A-Za-z0-9_./:=@+-]+$/u.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`}
1
+ import{inspectPackageVersionDrift as a}from"../app/package-inspection.js";export function checkCliVersionDrift(r,t={}){if(S(r))return null;const e=a({...t,binUrl:t.binUrl||import.meta.url,dir:$(r)});return e?p({...e,args:r}):null}export function emitCliVersionDriftAndExit(r,{json:t=!1}={}){if(!r)return!1;const e=f;return t?e.writeStandardOutput(`${JSON.stringify({error:r},null,2)}
2
+ `):(e.writeStandardError(`${r.message}
3
+ `),e.writeStandardError(`Current binary: @gfdelarue/testkit ${r.current.version} (${r.current.packageRoot})
4
+ `),r.product.version&&r.product.packageRoot&&e.writeStandardError(`Product install: @gfdelarue/testkit ${r.product.version} (${r.product.packageRoot})
5
+ `),r.product.declaredSpec&&e.writeStandardError(`Product declaration: ${r.product.declaredSpec}
6
+ `),e.writeStandardError(`Run: ${r.hint}
7
+ `)),e.setExitCode(1),!0}export function isJsonMode(r=[]){return o(r).includes("--json")}const f=Object.freeze({setExitCode(r){process.exitCode=r},writeStandardError(r){process.stderr.write(r)},writeStandardOutput(r){process.stdout.write(r)}});function p({productDir:r,currentPackageRoot:t,currentVersion:e,productPackageRoot:n,productVersion:i,declaredSpec:s,args:d}){const c=n?"./node_modules/.bin/testkit":"npm exec testkit --",l=`cd ${u(r)} && ${c} ${x(k(d))}`.trim();return{code:"testkit_cli_version_mismatch",message:`Testkit CLI version mismatch for ${r}`,current:{version:e,packageRoot:t},product:{dir:r,version:i,packageRoot:n,declaredSpec:s||null},hint:l}}function S(r=[]){const t=r[0];return!t||t==="--help"||t==="-h"||t==="--version"||t==="-v"||t==="help"}function k(r=[]){const t=[];for(let e=0;e<r.length;e+=1){const n=r[e];if(n==="--"){t.push(...r.slice(e));break}if(n==="--dir"){const i=r.at(e+1);i!==void 0&&i!=="--"&&(e+=1);continue}n.startsWith("--dir=")||t.push(n)}return t}function $(r){const t=o(r);for(let e=0;e<t.length;e+=1){const n=t[e];if(n==="--dir"){const i=t.at(e+1);return i&&(i==="-"||!i.startsWith("-"))?i:null}if(n.startsWith("--dir="))return n.slice(6)||null}return null}function o(r){const t=r.indexOf("--");return t<0?r:r.slice(0,t)}function x(r=[]){return r.map(u).join(" ")}function u(r){const t=r;return/^[A-Za-z0-9_./:=@+-]+$/u.test(t)?t:`'${t.replace(/'/g,"'\\''")}'`}
@@ -265,11 +265,6 @@ export interface NodeNextPresetOptions {
265
265
  workers?: number;
266
266
  }
267
267
  export interface TestkitConfig {
268
- cloud?: {
269
- verification?: {
270
- minimumConfirmingRuns?: number;
271
- };
272
- };
273
268
  artifacts?: {
274
269
  publication?: {
275
270
  directory: string;
@@ -7,5 +7,4 @@ export { detectNextApp, type NormalizedRuntimeConfig } from "./runtime.js";
7
7
  export { DEFAULT_RUNTIME_ISOLATION_POLICY, resolveRuntimeIsolationPolicy, type RuntimeIsolationFileMetadata, type RuntimeIsolationPolicy, type RuntimeIsolationResolutionSuite, type RuntimeIsolationResolverConfig, type RuntimeIsolationResolverTestkitConfig, } from "./runtime-isolation.js";
8
8
  export { collectModuleSpecifiers, requireMatchingEngineAuthoringImports, type TestFileMetadata, } from "./file-metadata.js";
9
9
  export type { NormalizedSkipConfig } from "./skip-config.js";
10
- export { minimumConfirmingRuns } from "./cloud.js";
11
10
  export { hasDiscoveryErrors, buildDiscoveryErrorFromDiagnostics } from "./discovery.js";
@@ -1 +1 @@
1
- export{loadConfigContext,loadConfigs,parseDotenv,resolveProductDir,resolveServiceCwd}from"./load.js";export{discoverTests,loadDiscoveredConfigContext,loadDiscoveryContext,fileDisplayName,formatDisplayName,testTypeContract}from"./discover-tests.js";export{findConfigFile,loadTestkitConfig}from"./config-loader.js";export{materializeDatabaseConfig}from"./database-materialization.js";export{detectNextApp}from"./runtime.js";export{DEFAULT_RUNTIME_ISOLATION_POLICY,resolveRuntimeIsolationPolicy}from"./runtime-isolation.js";export{collectModuleSpecifiers,requireMatchingEngineAuthoringImports}from"./file-metadata.js";export{minimumConfirmingRuns}from"./cloud.js";export{hasDiscoveryErrors,buildDiscoveryErrorFromDiagnostics}from"./discovery.js";
1
+ export{loadConfigContext,loadConfigs,parseDotenv,resolveProductDir,resolveServiceCwd}from"./load.js";export{discoverTests,loadDiscoveredConfigContext,loadDiscoveryContext,fileDisplayName,formatDisplayName,testTypeContract}from"./discover-tests.js";export{findConfigFile,loadTestkitConfig}from"./config-loader.js";export{materializeDatabaseConfig}from"./database-materialization.js";export{detectNextApp}from"./runtime.js";export{DEFAULT_RUNTIME_ISOLATION_POLICY,resolveRuntimeIsolationPolicy}from"./runtime-isolation.js";export{collectModuleSpecifiers,requireMatchingEngineAuthoringImports}from"./file-metadata.js";export{hasDiscoveryErrors,buildDiscoveryErrorFromDiagnostics}from"./discovery.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shares compiled code, never evaluated modules, between fresh engine processes.
3
+ * The caller owns the cache root and must retain it until its processes exit.
4
+ * Cache failures are optional optimization failures and must not prevent tests.
5
+ */
6
+ export declare function getNodeStartupCacheEnvironment(cacheDirectory: string | undefined, consumerEnvironment: Readonly<Record<string, string>>, options?: Readonly<{
7
+ coverageEnabled?: boolean;
8
+ }>): Promise<Readonly<Record<string, string>>>;
@@ -0,0 +1 @@
1
+ import n from"node:fs/promises";import u from"node:path";import{makeTempDirAsync as m}from"../../process/files/temp-dir.js";const r=new Map,C=128,y=new Set(["NODE_COMPILE_CACHE","NODE_DISABLE_COMPILE_CACHE","NODE_V8_COVERAGE"]);export async function getNodeStartupCacheEnvironment(t,a,s={}){if(t===void 0||!u.isAbsolute(t)||s.coverageEnabled===!0||Object.keys(a).some(o=>y.has(o.toUpperCase())))return{};try{await n.mkdir(t,{mode:448,recursive:!0});const o=await n.lstat(t);if(!o.isDirectory()||o.isSymbolicLink())return{};const p=await n.realpath(t),c=`${p}\0${E(o)}`;let i=r.get(c);if(i!==void 0){const e=await i,d=await n.lstat(e.directory).catch(()=>null);if(d!==null&&f(d)&&E(d)===e.identity&&await n.realpath(e.directory)===e.directory)return{NODE_COMPILE_CACHE:u.toNamespacedPath(e.directory)};r.delete(c)}if(i=r.get(c),i===void 0&&(i=O(p),r.set(c,i),r.size>C)){const e=r.keys().next().value;e!==void 0&&r.delete(e)}const l=await i;return{NODE_COMPILE_CACHE:u.toNamespacedPath(l.directory)}}catch{return{}}}async function O(t){const a=await m("node-compile-",{parentDir:t}),s=await n.lstat(a);if(!f(s))throw new Error("Node startup cache directory is not private");return{directory:a,identity:E(s)}}function E(t){return`${t.dev.toString()}:${t.ino.toString()}:${t.birthtimeMs.toString()}`}function f(t){return t.isDirectory()&&!t.isSymbolicLink()&&(process.platform==="win32"||(t.mode&63)===0&&t.uid===process.getuid?.())}
@@ -57,6 +57,8 @@ export interface EngineExecutionTarget {
57
57
  }
58
58
  export interface EngineExecutionWorkspace {
59
59
  readonly artifactDirectory: string;
60
+ /** Optional caller-owned compilation cache, removed after all executions finish. */
61
+ readonly cacheDirectory?: string;
60
62
  readonly rootDirectory: string;
61
63
  readonly temporaryDirectory: string;
62
64
  }
@@ -168,7 +168,14 @@ function observedRealpath(file) {
168
168
  recordResolutionWork("resolver.realpath");
169
169
  // Match policy inspection on Windows: native realpath expands 8.3 names and
170
170
  // changes their spelling, mixing physical paths with the inspected roots.
171
- return process.platform === "win32" ? fs.realpathSync(file) : fs.realpathSync.native(file);
171
+ if (process.platform === "win32") return fs.realpathSync(file);
172
+ try {
173
+ return fs.realpathSync.native(file);
174
+ } catch {
175
+ // Keep Node's portable implementation for hosts where native realpath
176
+ // is unavailable, including minimal Linux environments without /proc.
177
+ return fs.realpathSync(file);
178
+ }
172
179
  }
173
180
 
174
181
  function observedRead(file, encoding) {
@@ -1 +1 @@
1
- import{makeTempDirAsync as R}from"../../../process/files/temp-dir.js";import{classifyManagedProcessResult as z,terminalOutcome as I,processOutputEvidence as W,emptyEvidence as J,failureFromError as l,firstOutputLine as h,processExitFailure as H,toError as x,executeEngineTasks as Y,managedProcessFailure as G}from"../../adapters/execution.js";import{requireRootDirectory as Q,requireAbsoluteWorkspacePath as M,requireConsumerFile as P,safeSegment as L}from"../../adapters/workspace.js";import p from"node:fs/promises";import c from"node:path";import{pathToFileURL as X}from"node:url";import{isEngineTaskDeadlineExceededReason as Z,resolveEngineTaskInputs as q}from"../../api/index.js";import{createInvocationProcessEnvironment as tt,runManagedProcess as et}from"../../../process/index.js";import{preflightNodeVitestInstallation as it}from"../../installations/node-vitest.js";import{collectNodeVitestEvidence as rt}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as ot,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as nt,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as at,selectNodeVitestForwardedEnvironment as st,verifyOwnedNodeVitestRuntimeInput as ct,writeOwnedNodeVitestRuntimeInput as ut}from"../runtime/runtime-input.js";import{writeNodeVitestWorkspace as dt}from"./workspace.js";import{engineEvidenceIdentity as lt}from"../../adapters/evidence-identity.js";const T={codePrefix:"NODE_VITEST",label:"Node/Vitest"};class A extends Error{constructor(e){const i=x(e);super(i.message,{cause:i}),this.name="NodeVitestConsumerPreparationError"}}export const nodeVitestExecutorDependencies={removeOwnedDirectory:t=>p.rm(t,{force:!0,recursive:!0}),writeRuntimeInput:ut,writeWorkspace:dt};export async function executeNodeVitestEngine(t,e,i,r=nodeVitestExecutorDependencies){const n=await pt(t,e,r,i);return Y(t,n.tasks,T,(o,a)=>ft(t,n,o,a),_t(t,n.installation))}async function pt(t,e,i,r){const n=t.profile.id,o=e.packages.vitest.cli,a=await Q(t.workspace.rootDirectory);M(t.workspace.temporaryDirectory,"temporary directory"),M(t.workspace.artifactDirectory,"artifact directory"),await p.mkdir(t.workspace.temporaryDirectory,{recursive:!0}),await p.mkdir(t.workspace.artifactDirectory,{recursive:!0});const f=await p.realpath(t.workspace.temporaryDirectory),d=await p.realpath(t.workspace.artifactDirectory),v=Object.freeze({...e.authoringEntrypoints,cli:e.packages.vitest.cli,packages:e.packages}),_=await R(`node-vitest-${L(t.invocationId)}-`,{parentDir:f});r.defer(()=>i.removeOwnedDirectory(_));const j=await tt(_),g=[];for(const[K,s]of t.tasks.entries()){const m=q(t,s),S=`${K.toString().padStart(3,"0")}-${L(s.id)}`,u=c.join(_,S),y=await R(`${S}-`,{parentDir:d}),O=c.join(u,"testkit-vitest-report.json"),U=c.join(y,"data","manifest.json"),B=c.join(u,"setup.mjs"),w=c.join(u,"input-bootstrap.mjs"),F=c.join(u,"resolution-bootstrap.cjs"),V=c.join(u,"vitest.config.mjs");try{await p.mkdir(u,{mode:448,recursive:!1});const D=await N(()=>P(a,s.file,"Node/Vitest task file")),k=await N(()=>vt(a,m)),$=await N(()=>It(t,n,s,m)),E=await i.writeRuntimeInput(u,$);await i.writeWorkspace({report:{identity:{engineId:"node-vitest",invocationId:t.invocationId,taskId:s.id,sourceFile:s.file},reportFile:O,workspaceDirectory:u},configFile:k.testkitConfig,entrypoints:v,inputBootstrapFile:w,resolutionBootstrapFile:F,rootDirectory:a,runtimeInput:{expectedFile:E.file,expectedInvocationId:t.invocationId,expectedProfileId:n,expectedSha256:E.sha256,expectedSizeBytes:E.sizeBytes,expectedTaskId:s.id},setupFile:B,sourceFile:D,timeoutMs:Math.max(1,s.deadlineMs-Date.now()),tsconfigFile:k.tsconfig,vitestConfigFile:V}),g.push({artifactDirectory:y,inputs:m,task:s,preparationFailure:null,prepared:{dataManifestFile:U,inputBootstrapFile:w,reportFile:O,resolutionBootstrapFile:F,runtimeInput:E,vitestConfigFile:V}})}catch(D){g.push({artifactDirectory:y,inputs:m,task:s,preparationFailure:gt(D),prepared:null})}}return Object.freeze({cliFile:o,installation:e,invocationEnvironment:j.environment,profileId:n,rootDirectory:a,tasks:Object.freeze(g)})}async function ft(t,e,i,r){const n=Math.max(0,i.task.deadlineMs-r);try{await ct(i.prepared.runtimeInput)}catch(d){return I(i.task.id,"failed",r,J(),{...l(d,"NODE_VITEST_INPUT_INVALID"),classification:"infra"})}const o=await et({args:[e.cliFile,"run","--config",i.prepared.vitestConfigFile,"--configLoader","runner"],cwd:e.rootDirectory,displayLabel:"Node/Vitest",env:Et(t,e,i),executable:process.execPath,signal:t.signal,timeoutMs:n});let a;try{a=await rt({identity:{engineId:"node-vitest",invocationId:t.invocationId,taskId:i.task.id,sourceFile:i.task.file},artifactDirectory:i.artifactDirectory,dataManifestFile:i.prepared.dataManifestFile,profileId:e.profileId,reportFile:i.prepared.reportFile,rootDirectory:e.rootDirectory,stderr:o.stderr,stdout:o.stdout})}catch(d){const v=W(o);return I(i.task.id,"failed",r,v,nodeVitestEvidenceCollectionFailure(o,d))}if(a.reportSummary===null&&o.exitCode!==0&&!t.signal.aborted&&!o.timedOut&&!o.outputLimitExceeded&&o.terminationError===null&&o.error===null)try{await it(e.installation,{signal:t.signal})}catch(d){if(!t.signal.aborted)return I(i.task.id,"failed",r,a.evidence,{...l(d,"NODE_VITEST_INSTALLATION_BROKEN"),classification:"infra"})}const f=classifyNodeVitestProcessResult(o,a,i.task.timeoutMs,Z(t.signal.reason));return I(i.task.id,f.status,r,a.evidence,f.failure)}export function classifyNodeVitestProcessResult(t,e,i,r){const n=z(t,i,r,T);return n!==null?n:e.reportSummary?.numTotalTests===0?{failure:{code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"},status:"failed"}:mt(e.reportSummary)?{failure:null,status:"skipped"}:t.exitCode!==0?H(t,T,e.reportFailureMessage??h(t.stderr)??h(t.stdout),"Vitest"):e.reportError!==null?{failure:{...l(e.reportError,"NODE_VITEST_REPORT_INVALID"),classification:"infra"},status:"failed"}:e.reportFailureMessage!==null?{failure:{code:"NODE_VITEST_REPORT_FAILED",message:e.reportFailureMessage},status:"failed"}:e.dataEvidenceError!==null?{failure:l(e.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID"),status:"failed"}:e.reportSummary===null||!e.reportSummary.success||e.reportSummary.numFailedTests>0||e.reportSummary.numPassedTests===0?{failure:{code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"},status:"failed"}:{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(t,e){const i=x(e);return G(t,T,i)??{...l(i,"NODE_VITEST_EVIDENCE_FAILED"),classification:"infra"}}function mt(t){return t!==null&&t.success&&t.numTotalTests>0&&t.numPassedTests===0&&t.numFailedTests===0&&(t.numPendingTests>0||t.numTodoTests>0)}function Et(t,e,i){const r=i.task,n=b(t,r.serviceName),o={...e.invocationEnvironment};return Object.assign(o,{...n===null?{}:{BASE_URL:n},FORCE_COLOR:"0",NODE_OPTIONS:Tt(i.prepared.resolutionBootstrapFile,i.prepared.inputBootstrapFile),NO_COLOR:"1",TESTKIT_ACTIVE:"1",[nt]:i.prepared.runtimeInput.file,[at]:i.prepared.runtimeInput.sha256,TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(r.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(r.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:t.invocationId,TESTKIT_LEASE_DIR:r.orchestration.lease.directory,TESTKIT_LEASE_ID:r.orchestration.lease.id,TESTKIT_MODE:r.orchestration.mode,...r.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:r.orchestration.runtimeId},TESTKIT_TEST_FILE:r.file,TESTKIT_TEST_ID:r.id,...e.profileId==="node-vitest-data"?{TESTKIT_DATA_ARTIFACT_DIR:c.dirname(i.prepared.dataManifestFile)}:{}}),ot(o),o}function It(t,e,i,r){const n=b(t,i.serviceName),o=st(r.environment);return{context:{baseUrl:n,engineId:"node-vitest",invocationId:t.invocationId,profileId:e,serviceName:i.serviceName,task:{file:i.file,id:i.id,suiteName:i.suiteName,testType:i.testType}},engineId:"node-vitest",environment:o,fixtures:r.fixtures,invocationId:t.invocationId,profileId:e,...r.setupData===void 0?{}:{setupData:r.setupData},taskId:i.id,version:1}}function Tt(t,e){return[`--require=${JSON.stringify(t)}`,`--import=${JSON.stringify(X(e).href)}`].join(" ")}function b(t,e){return Object.hasOwn(t.target.endpoints,e)?t.target.endpoints[e]:null}async function vt(t,e){return{testkitConfig:await C(t,e.supportModules,"testkitConfig"),tsconfig:await C(t,e.supportModules,"tsconfig")}}async function C(t,e,i){if(!Object.hasOwn(e,i))return null;const r=e[i];if(!c.isAbsolute(r))throw new Error(`supportModules.${i} must be an absolute path`);return P(t,r,`supportModules.${i}`)}function _t(t,e){return{...lt({engineId:"node-vitest",installationKind:"root-direct-packages",profile:t.profile}),directPackages:{esbuild:e.packages.esbuild.version,typescript:e.packages.typescript.version,vite:e.packages.vite.version,vitest:e.packages.vitest.version}}}function gt(t){return{...l(t,"NODE_VITEST_PREPARE_FAILED"),classification:t instanceof A?"product":"infra"}}async function N(t){try{return await t()}catch(e){throw new A(e)}}
1
+ import{makeTempDirAsync as h}from"../../../process/files/temp-dir.js";import{classifyManagedProcessResult as H,terminalOutcome as E,processOutputEvidence as J,emptyEvidence as R,failureFromError as p,firstOutputLine as x,processExitFailure as Y,toError as M,executeEngineTasks as G,managedProcessFailure as Q,taskTimeoutFailure as X}from"../../adapters/execution.js";import{requireRootDirectory as Z,requireAbsoluteWorkspacePath as P,requireConsumerFile as L,safeSegment as A}from"../../adapters/workspace.js";import f from"node:fs/promises";import c from"node:path";import{pathToFileURL as q}from"node:url";import{isEngineTaskDeadlineExceededReason as tt,resolveEngineTaskInputs as et}from"../../api/index.js";import{createInvocationProcessEnvironment as it,runManagedProcess as rt}from"../../../process/index.js";import{preflightNodeVitestInstallation as nt}from"../../installations/node-vitest.js";import{collectNodeVitestEvidence as ot}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as C,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as at,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as st,selectNodeVitestForwardedEnvironment as ct,verifyOwnedNodeVitestRuntimeInput as ut,writeOwnedNodeVitestRuntimeInput as dt}from"../runtime/runtime-input.js";import{writeNodeVitestWorkspace as lt}from"./workspace.js";import{engineEvidenceIdentity as pt}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as ft}from"../../adapters/node-startup-cache.js";const I={codePrefix:"NODE_VITEST",label:"Node/Vitest"};class b extends Error{constructor(e){const i=M(e);super(i.message,{cause:i}),this.name="NodeVitestConsumerPreparationError"}}export const nodeVitestExecutorDependencies={removeOwnedDirectory:t=>f.rm(t,{force:!0,recursive:!0}),writeRuntimeInput:dt,writeWorkspace:lt};export async function executeNodeVitestEngine(t,e,i,r=nodeVitestExecutorDependencies){const n=await mt(t,e,r,i);return G(t,n.tasks,I,(a,o)=>Et(t,n,a,o),yt(t,n.installation))}async function mt(t,e,i,r){const n=t.profile.id,a=e.packages.vitest.cli,o=await Z(t.workspace.rootDirectory);P(t.workspace.temporaryDirectory,"temporary directory"),P(t.workspace.artifactDirectory,"artifact directory"),await f.mkdir(t.workspace.temporaryDirectory,{recursive:!0}),await f.mkdir(t.workspace.artifactDirectory,{recursive:!0});const d=await f.realpath(t.workspace.temporaryDirectory),T=await f.realpath(t.workspace.artifactDirectory),l=Object.freeze({...e.authoringEntrypoints,cli:e.packages.vitest.cli,packages:e.packages}),m=await h(`node-vitest-${A(t.invocationId)}-`,{parentDir:d});r.defer(()=>i.removeOwnedDirectory(m));const U=await it(m),g=[];for(const[B,s]of t.tasks.entries()){const v=et(t,s),S=`${B.toString().padStart(3,"0")}-${A(s.id)}`,u=c.join(m,S),y=await h(`${S}-`,{parentDir:T}),w=c.join(u,"testkit-vitest-report.json"),$=c.join(y,"data","manifest.json"),z=c.join(u,"setup.mjs"),O=c.join(u,"input-bootstrap.mjs"),F=c.join(u,"resolution-bootstrap.cjs"),V=c.join(u,"vitest.config.mjs");try{await f.mkdir(u,{mode:448,recursive:!1});const D=await N(()=>L(o,s.file,"Node/Vitest task file")),k=await N(()=>gt(o,v)),W=await N(()=>vt(t,n,s,v)),_=await i.writeRuntimeInput(u,W);await i.writeWorkspace({report:{identity:{engineId:"node-vitest",invocationId:t.invocationId,taskId:s.id,sourceFile:s.file},reportFile:w,workspaceDirectory:u},configFile:k.testkitConfig,entrypoints:l,inputBootstrapFile:O,resolutionBootstrapFile:F,rootDirectory:o,runtimeInput:{expectedFile:_.file,expectedInvocationId:t.invocationId,expectedProfileId:n,expectedSha256:_.sha256,expectedSizeBytes:_.sizeBytes,expectedTaskId:s.id},setupFile:z,sourceFile:D,timeoutMs:Math.max(1,s.deadlineMs-Date.now()),tsconfigFile:k.tsconfig,vitestConfigFile:V}),g.push({artifactDirectory:y,inputs:v,task:s,preparationFailure:null,prepared:{dataManifestFile:$,inputBootstrapFile:O,reportFile:w,resolutionBootstrapFile:F,runtimeInput:_,vitestConfigFile:V}})}catch(D){g.push({artifactDirectory:y,inputs:v,task:s,preparationFailure:Dt(D),prepared:null})}}return Object.freeze({cliFile:a,installation:e,invocationEnvironment:U.environment,profileId:n,rootDirectory:o,tasks:Object.freeze(g)})}async function Et(t,e,i,r){try{await ut(i.prepared.runtimeInput)}catch(l){return E(i.task.id,"failed",r,R(),{...p(l,"NODE_VITEST_INPUT_INVALID"),classification:"infra"})}const n=await Tt(t,e,i),a=i.task.deadlineMs-Date.now();if(a<=0)return E(i.task.id,"failed",r,R(),X(i.task.timeoutMs,I));const o=await rt({args:[e.cliFile,"run","--config",i.prepared.vitestConfigFile,"--configLoader","runner"],cwd:e.rootDirectory,displayLabel:"Node/Vitest",env:n,executable:process.execPath,signal:t.signal,timeoutMs:a});let d;try{d=await ot({identity:{engineId:"node-vitest",invocationId:t.invocationId,taskId:i.task.id,sourceFile:i.task.file},artifactDirectory:i.artifactDirectory,dataManifestFile:i.prepared.dataManifestFile,profileId:e.profileId,reportFile:i.prepared.reportFile,rootDirectory:e.rootDirectory,stderr:o.stderr,stdout:o.stdout})}catch(l){const m=J(o);return E(i.task.id,"failed",r,m,nodeVitestEvidenceCollectionFailure(o,l))}if(d.reportSummary===null&&o.exitCode!==0&&!t.signal.aborted&&!o.timedOut&&!o.outputLimitExceeded&&o.terminationError===null&&o.error===null)try{await nt(e.installation,{signal:t.signal})}catch(l){if(!t.signal.aborted)return E(i.task.id,"failed",r,d.evidence,{...p(l,"NODE_VITEST_INSTALLATION_BROKEN"),classification:"infra"})}const T=classifyNodeVitestProcessResult(o,d,i.task.timeoutMs,tt(t.signal.reason));return E(i.task.id,T.status,r,d.evidence,T.failure)}export function classifyNodeVitestProcessResult(t,e,i,r){const n=H(t,i,r,I);return n!==null?n:e.reportSummary?.numTotalTests===0?{failure:{code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"},status:"failed"}:It(e.reportSummary)?{failure:null,status:"skipped"}:t.exitCode!==0?Y(t,I,e.reportFailureMessage??x(t.stderr)??x(t.stdout),"Vitest"):e.reportError!==null?{failure:{...p(e.reportError,"NODE_VITEST_REPORT_INVALID"),classification:"infra"},status:"failed"}:e.reportFailureMessage!==null?{failure:{code:"NODE_VITEST_REPORT_FAILED",message:e.reportFailureMessage},status:"failed"}:e.dataEvidenceError!==null?{failure:p(e.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID"),status:"failed"}:e.reportSummary===null||!e.reportSummary.success||e.reportSummary.numFailedTests>0||e.reportSummary.numPassedTests===0?{failure:{code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"},status:"failed"}:{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(t,e){const i=M(e);return Q(t,I,i)??{...p(i,"NODE_VITEST_EVIDENCE_FAILED"),classification:"infra"}}function It(t){return t!==null&&t.success&&t.numTotalTests>0&&t.numPassedTests===0&&t.numFailedTests===0&&(t.numPendingTests>0||t.numTodoTests>0)}async function Tt(t,e,i){const r=i.task,n=j(t,r.serviceName),a={...e.invocationEnvironment};Object.assign(a,{...n===null?{}:{BASE_URL:n},FORCE_COLOR:"0",NODE_OPTIONS:_t(i.prepared.resolutionBootstrapFile,i.prepared.inputBootstrapFile),NO_COLOR:"1",TESTKIT_ACTIVE:"1",[at]:i.prepared.runtimeInput.file,[st]:i.prepared.runtimeInput.sha256,TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(r.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(r.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:t.invocationId,TESTKIT_LEASE_DIR:r.orchestration.lease.directory,TESTKIT_LEASE_ID:r.orchestration.lease.id,TESTKIT_MODE:r.orchestration.mode,...r.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:r.orchestration.runtimeId},TESTKIT_TEST_FILE:r.file,TESTKIT_TEST_ID:r.id,...e.profileId==="node-vitest-data"?{TESTKIT_DATA_ARTIFACT_DIR:c.dirname(i.prepared.dataManifestFile)}:{}}),C(a),Object.assign(a,await ft(t.workspace.cacheDirectory,i.inputs.environment));try{C(a)}catch{delete a.NODE_COMPILE_CACHE}return a}function vt(t,e,i,r){const n=j(t,i.serviceName),a=ct(r.environment);return{context:{baseUrl:n,engineId:"node-vitest",invocationId:t.invocationId,profileId:e,serviceName:i.serviceName,task:{file:i.file,id:i.id,suiteName:i.suiteName,testType:i.testType}},engineId:"node-vitest",environment:a,fixtures:r.fixtures,invocationId:t.invocationId,profileId:e,...r.setupData===void 0?{}:{setupData:r.setupData},taskId:i.id,version:1}}function _t(t,e){return[`--require=${JSON.stringify(t)}`,`--import=${JSON.stringify(q(e).href)}`].join(" ")}function j(t,e){return Object.hasOwn(t.target.endpoints,e)?t.target.endpoints[e]:null}async function gt(t,e){return{testkitConfig:await K(t,e.supportModules,"testkitConfig"),tsconfig:await K(t,e.supportModules,"tsconfig")}}async function K(t,e,i){if(!Object.hasOwn(e,i))return null;const r=e[i];if(!c.isAbsolute(r))throw new Error(`supportModules.${i} must be an absolute path`);return L(t,r,`supportModules.${i}`)}function yt(t,e){return{...pt({engineId:"node-vitest",installationKind:"root-direct-packages",profile:t.profile}),directPackages:{esbuild:e.packages.esbuild.version,typescript:e.packages.typescript.version,vite:e.packages.vite.version,vitest:e.packages.vitest.version}}}function Dt(t){return{...p(t,"NODE_VITEST_PREPARE_FAILED"),classification:t instanceof b?"product":"infra"}}async function N(t){try{return await t()}catch(e){throw new b(e)}}
@@ -1 +1 @@
1
- import{makeTempDirAsync as _}from"../../../process/files/temp-dir.js";import{classifyManagedProcessResult as A,terminalOutcome as v,processOutputEvidence as F,failureFromError as T,firstOutputLine as D,processExitFailure as N,toError as E,executeEngineTasks as x,managedProcessFailure as M}from"../../adapters/execution.js";import{requireRootDirectory as b,requireAbsoluteWorkspacePath as R,requireConsumerFile as C,safeSegment as O}from"../../adapters/workspace.js";import l from"node:fs/promises";import W from"node:path";import{setTimeout as B}from"node:timers/promises";import{pathToFileURL as Y}from"node:url";import{isEngineTaskDeadlineExceededReason as U,resolveEngineTaskInputs as $}from"../../api/index.js";import{createNativeInvocationProcessEnvironment as H,runManagedProcess as K}from"../../../process/index.js";import{PlaywrightConsumerConfigurationError as G,playwrightExactFileFilter as V,writePlaywrightTaskWorkspace as j}from"./config.js";import{collectPlaywrightEvidence as J}from"./evidence.js";import{engineEvidenceIdentity as z}from"../../adapters/evidence-identity.js";const d={codePrefix:"PLAYWRIGHT",label:"Playwright"};export const PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES=24*1024;const S=64;class k extends Error{constructor(r){const e=E(r);super(e.message,{cause:e}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:H,removeOwnedDirectory:Z,writeTaskWorkspace:j};const X=new Set(["EBUSY","ENOTEMPTY","EPERM"]),Q=5;async function Z(t){for(let r=0;;r+=1)try{await l.rm(t,{force:!0,recursive:!0});return}catch(e){const i=e instanceof Error&&"code"in e?e.code:void 0;if(r===Q||typeof i!="string"||!X.has(i))throw e;await B((r+1)*100)}}export async function executePlaywrightEngine(t,r,e,i=playwrightAdapterDependencies){const o=await q(t,r,i,e);return x(t,o.tasks,d,(n,a)=>rr(t,o,n,a),nr(t,o.installation))}async function q(t,r,e,i){const o=await b(t.workspace.rootDirectory);R(t.workspace.temporaryDirectory,"temporary directory"),R(t.workspace.artifactDirectory,"artifact directory"),await l.mkdir(t.workspace.temporaryDirectory,{recursive:!0}),await l.mkdir(t.workspace.artifactDirectory,{recursive:!0});const n=await l.realpath(t.workspace.temporaryDirectory),a=await l.realpath(t.workspace.artifactDirectory),s=await _(`playwright-${O(t.invocationId)}-`,{parentDir:n});i.defer(()=>e.removeOwnedDirectory(s));const p=await e.createInvocationEnvironment(s);i.defer(()=>e.removeOwnedDirectory(p.temporaryDirectory));const h=[];for(const[L,c]of t.tasks.entries()){const g=$(t,c),I=`${L.toString().padStart(3,"0")}-${O(c.id)}`,P=W.join(s,I),u=await _(`${I}-`,{parentDir:a});try{const y=or(t,c.serviceName),f=await ar(()=>C(o,c.file,"Playwright task file")),m=await e.writeTaskWorkspace({artifactDirectory:u,baseUrl:y,browserFile:r.browserExecutable,endpoints:t.target.endpoints,inputs:g,invocationId:t.invocationId,managedModuleEntrypoints:{authoring:r.authoringEntrypoints.authoring,cli:r.family.cli,playwright:r.family.playwright.entrypoint,playwrightCore:r.family.playwrightCore.entrypoint,playwrightTest:r.family.playwrightTest.entrypoint,root:r.authoringEntrypoints.root,rootPackageJson:r.authoringEntrypoints.rootPackageJson},moduleFile:r.family.playwrightTest.entrypoint,orchestration:c.orchestration,rootDirectory:o,serviceName:c.serviceName,sourceFile:f,taskDirectory:P,taskId:c.id});i.defer(async()=>{try{await e.removeOwnedDirectory(m.attachmentDirectory)}catch(w){if(!(w instanceof Error&&"code"in w&&w.code==="ENOTDIR"))throw w}}),h.push({artifactDirectory:u,inputs:g,task:c,preparationFailure:null,prepared:{baseUrl:y,sourceFile:f,workspace:m}})}catch(y){const f=(await sr([P,u],e)).map(m=>m.error);h.push({artifactDirectory:u,inputs:g,task:c,preparationFailure:playwrightPreparationFailure(y,f),prepared:null})}}return Object.freeze({installation:r,invocationEnvironment:p.environment,rootDirectory:o,tasks:Object.freeze(h)})}async function rr(t,r,e,i){const o=Math.max(0,e.task.deadlineMs-i),n=await K({args:[r.installation.family.cli,"test",V(e.prepared.sourceFile),"--config",e.prepared.workspace.configFile],cwd:r.rootDirectory,displayLabel:"Playwright",env:tr(t,r,e),executable:process.execPath,signal:t.signal,timeoutMs:o});let a;try{a=await J({identity:{engineId:"playwright",invocationId:t.invocationId,taskId:e.task.id,sourceFile:e.prepared.sourceFile},artifactDirectory:e.artifactDirectory,outputDirectory:e.prepared.workspace.outputDirectory,reportFile:e.prepared.workspace.reportFile,rootDirectory:r.rootDirectory,sourceFile:e.prepared.sourceFile,stderr:n.stderr,stdout:n.stdout})}catch(p){return v(e.task.id,"failed",i,F(n),playwrightEvidenceCollectionFailure(n,p))}const s=classifyPlaywrightProcessResult(n,a,e.task.timeoutMs,U(t.signal.reason));return v(e.task.id,s.status,i,a.evidence,s.failure)}export function classifyPlaywrightProcessResult(t,r,e,i){const o=A(t,e,i,d);if(o!==null)return o;if(r.outputLimit!==null){const s=r.outputLimit;return{failure:{code:"PLAYWRIGHT_OUTPUT_ARTIFACT_LIMIT",details:{...s},message:`Playwright output exceeded its ${s.limit.toString()} ${s.kind==="bytes"?"byte aggregate":"entry"} artifact limit`},status:"failed"}}const n=r.attachmentFailures.find(s=>s.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(n!==void 0)return{failure:n,status:"failed"};if(r.report.failure!==null)return r.report;const a=r.attachmentFailures.at(0);return a!==void 0?{failure:a,status:"failed"}:t.exitCode!==0?N(t,d,D(t.stderr)??D(t.stdout)):r.reportError!==null?{failure:{...T(r.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:r.report}export function playwrightEvidenceCollectionFailure(t,r){const e=E(r);return M(t,d,e)??{...T(e,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function tr(t,r,e){const i={...r.invocationEnvironment},o=e.task,n=e.prepared.workspace.inputBootstrapFile,a=e.prepared.workspace.resolutionBootstrapFile;return Object.assign(i,{BASE_URL:e.prepared.baseUrl,FORCE_COLOR:"0",NO_COLOR:"1",PLAYWRIGHT_BROWSERS_PATH:r.installation.registry.canonicalValue,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD:"1",TESTKIT_ACTIVE:"1",TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(o.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(o.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:t.invocationId,TESTKIT_LEASE_DIR:o.orchestration.lease.directory,TESTKIT_LEASE_ID:o.orchestration.lease.id,TESTKIT_MODE:o.orchestration.mode,...o.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:o.orchestration.runtimeId},TESTKIT_TEST_FILE:o.file,TESTKIT_TEST_ID:o.id}),i.NODE_OPTIONS=er(a,n),ir(i),i}function er(t,r){return["--experimental-strip-types",`--require=${JSON.stringify(t)}`,`--import=${JSON.stringify(Y(r).href)}`].join(" ")}export function playwrightSpawnEnvironmentBytes(t){return Object.entries(t).reduce((r,[e,i])=>r+(process.platform==="win32"?(e.length+i.length+2)*2:Buffer.byteLength(e,"utf8")+Buffer.byteLength(i,"utf8")+2),0)}function ir(t){if(Object.keys(t).length>S)throw new Error(`Playwright physical spawn environment exceeds its ${S.toString()} entry limit`);if(playwrightSpawnEnvironmentBytes(t)>PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright physical spawn environment exceeds its ${PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES.toString()} byte limit`)}function or(t,r){const e=t.target.endpoints[r];if(typeof e!="string"||e.trim().length===0)throw new Error(`Playwright task service ${r} does not have a published endpoint`);let i;try{i=new URL(e)}catch(o){throw new Error(`Playwright task service ${r} endpoint is not a valid URL`,{cause:o})}if(i.protocol!=="http:"&&i.protocol!=="https:")throw new Error(`Playwright task service ${r} endpoint must use http or https`);return i.href}function nr(t,r){return{...z({engineId:"playwright",installationKind:"root-playwright-family",profile:t.profile}),browserName:"chromium",packages:{playwright:r.family.playwright.version,playwrightCore:r.family.playwrightCore.version,playwrightTest:r.family.playwrightTest.version},registryMode:r.registry.mode}}export function playwrightPreparationFailure(t,r){if(r.length===0)return{...T(t,"PLAYWRIGHT_PREPARE_FAILED"),classification:t instanceof k||t instanceof G?"product":"infra"};const e=E(t);return{classification:"infra",code:"PLAYWRIGHT_PREPARE_CLEANUP_FAILED",details:{cleanupFailures:r.map(i=>i.message),preparationFailure:e.message},message:`Playwright task preparation failed and its partial workspace could not be removed: ${r.map(i=>i.message).join("; ")}`,...e.stack===void 0?{}:{stack:e.stack}}}async function ar(t){try{return await t()}catch(r){throw new k(r)}}async function sr(t,r){const e=[];for(const i of t)try{await r.removeOwnedDirectory(i)}catch(o){e.push({directory:i,error:E(o)})}return e}
1
+ import{makeTempDirAsync as D}from"../../../process/files/temp-dir.js";import{classifyManagedProcessResult as F,terminalOutcome as T,processOutputEvidence as N,failureFromError as P,firstOutputLine as R,processExitFailure as C,toError as h,executeEngineTasks as M,managedProcessFailure as x,emptyEvidence as b,taskTimeoutFailure as W}from"../../adapters/execution.js";import{requireRootDirectory as B,requireAbsoluteWorkspacePath as O,requireConsumerFile as Y,safeSegment as S}from"../../adapters/workspace.js";import u from"node:fs/promises";import H from"node:path";import{setTimeout as U}from"node:timers/promises";import{pathToFileURL as $}from"node:url";import{isEngineTaskDeadlineExceededReason as K,resolveEngineTaskInputs as G}from"../../api/index.js";import{createNativeInvocationProcessEnvironment as V,runManagedProcess as j}from"../../../process/index.js";import{PlaywrightConsumerConfigurationError as J,playwrightExactFileFilter as z,writePlaywrightTaskWorkspace as X}from"./config.js";import{collectPlaywrightEvidence as Q}from"./evidence.js";import{engineEvidenceIdentity as Z}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as q}from"../../adapters/node-startup-cache.js";const y={codePrefix:"PLAYWRIGHT",label:"Playwright"};export const PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES=24*1024;const k=64;class L extends Error{constructor(t){const r=h(t);super(r.message,{cause:r}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:V,removeOwnedDirectory:et,writeTaskWorkspace:X};const tt=new Set(["EBUSY","ENOTEMPTY","EPERM"]),rt=5;async function et(e){for(let t=0;;t+=1)try{await u.rm(e,{force:!0,recursive:!0});return}catch(r){const i=r instanceof Error&&"code"in r?r.code:void 0;if(t===rt||typeof i!="string"||!tt.has(i))throw r;await U((t+1)*100)}}export async function executePlaywrightEngine(e,t,r,i=playwrightAdapterDependencies){const o=await it(e,t,i,r);return M(e,o.tasks,y,(a,s)=>ot(e,o,a,s),ct(e,o.installation))}async function it(e,t,r,i){const o=await B(e.workspace.rootDirectory);O(e.workspace.temporaryDirectory,"temporary directory"),O(e.workspace.artifactDirectory,"artifact directory"),await u.mkdir(e.workspace.temporaryDirectory,{recursive:!0}),await u.mkdir(e.workspace.artifactDirectory,{recursive:!0});const a=await u.realpath(e.workspace.temporaryDirectory),s=await u.realpath(e.workspace.artifactDirectory),n=await D(`playwright-${S(e.invocationId)}-`,{parentDir:a});i.defer(()=>r.removeOwnedDirectory(n));const l=await r.createInvocationEnvironment(n);i.defer(()=>r.removeOwnedDirectory(l.temporaryDirectory));const p=[];for(const[g,c]of e.tasks.entries()){const I=G(e,c),v=`${g.toString().padStart(3,"0")}-${S(c.id)}`,_=H.join(n,v),f=await D(`${v}-`,{parentDir:s});try{const m=st(e,c.serviceName),E=await lt(()=>Y(o,c.file,"Playwright task file")),w=await r.writeTaskWorkspace({artifactDirectory:f,baseUrl:m,browserFile:t.browserExecutable,endpoints:e.target.endpoints,inputs:I,invocationId:e.invocationId,managedModuleEntrypoints:{authoring:t.authoringEntrypoints.authoring,cli:t.family.cli,playwright:t.family.playwright.entrypoint,playwrightCore:t.family.playwrightCore.entrypoint,playwrightTest:t.family.playwrightTest.entrypoint,root:t.authoringEntrypoints.root,rootPackageJson:t.authoringEntrypoints.rootPackageJson},moduleFile:t.family.playwrightTest.entrypoint,orchestration:c.orchestration,rootDirectory:o,serviceName:c.serviceName,sourceFile:E,taskDirectory:_,taskId:c.id});i.defer(async()=>{try{await r.removeOwnedDirectory(w.attachmentDirectory)}catch(d){if(!(d instanceof Error&&"code"in d&&d.code==="ENOTDIR"))throw d}}),p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:null,prepared:{baseUrl:m,sourceFile:E,workspace:w}})}catch(m){const E=(await pt([_,f],r)).map(w=>w.error);p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:playwrightPreparationFailure(m,E),prepared:null})}}return Object.freeze({installation:t,invocationEnvironment:l.environment,rootDirectory:o,tasks:Object.freeze(p)})}async function ot(e,t,r,i){const a={...await q(e.workspace.cacheDirectory,r.inputs.environment),...nt(e,t,r)};try{A(a)}catch{delete a.NODE_COMPILE_CACHE}const s=r.task.deadlineMs-Date.now();if(s<=0)return T(r.task.id,"failed",i,b(),W(r.task.timeoutMs,y));const n=await j({args:[t.installation.family.cli,"test",z(r.prepared.sourceFile),"--config",r.prepared.workspace.configFile],cwd:t.rootDirectory,displayLabel:"Playwright",env:a,executable:process.execPath,signal:e.signal,timeoutMs:s});let l;try{l=await Q({identity:{engineId:"playwright",invocationId:e.invocationId,taskId:r.task.id,sourceFile:r.prepared.sourceFile},artifactDirectory:r.artifactDirectory,outputDirectory:r.prepared.workspace.outputDirectory,reportFile:r.prepared.workspace.reportFile,rootDirectory:t.rootDirectory,sourceFile:r.prepared.sourceFile,stderr:n.stderr,stdout:n.stdout})}catch(g){return T(r.task.id,"failed",i,N(n),playwrightEvidenceCollectionFailure(n,g))}const p=classifyPlaywrightProcessResult(n,l,r.task.timeoutMs,K(e.signal.reason));return T(r.task.id,p.status,i,l.evidence,p.failure)}export function classifyPlaywrightProcessResult(e,t,r,i){const o=F(e,r,i,y);if(o!==null)return o;if(t.outputLimit!==null){const n=t.outputLimit;return{failure:{code:"PLAYWRIGHT_OUTPUT_ARTIFACT_LIMIT",details:{...n},message:`Playwright output exceeded its ${n.limit.toString()} ${n.kind==="bytes"?"byte aggregate":"entry"} artifact limit`},status:"failed"}}const a=t.attachmentFailures.find(n=>n.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(a!==void 0)return{failure:a,status:"failed"};if(t.report.failure!==null)return t.report;const s=t.attachmentFailures.at(0);return s!==void 0?{failure:s,status:"failed"}:e.exitCode!==0?C(e,y,R(e.stderr)??R(e.stdout)):t.reportError!==null?{failure:{...P(t.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:t.report}export function playwrightEvidenceCollectionFailure(e,t){const r=h(t);return x(e,y,r)??{...P(r,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function nt(e,t,r){const i={...t.invocationEnvironment},o=r.task,a=r.prepared.workspace.inputBootstrapFile,s=r.prepared.workspace.resolutionBootstrapFile;return Object.assign(i,{BASE_URL:r.prepared.baseUrl,FORCE_COLOR:"0",NO_COLOR:"1",PLAYWRIGHT_BROWSERS_PATH:t.installation.registry.canonicalValue,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD:"1",TESTKIT_ACTIVE:"1",TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(o.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(o.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:e.invocationId,TESTKIT_LEASE_DIR:o.orchestration.lease.directory,TESTKIT_LEASE_ID:o.orchestration.lease.id,TESTKIT_MODE:o.orchestration.mode,...o.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:o.orchestration.runtimeId},TESTKIT_TEST_FILE:o.file,TESTKIT_TEST_ID:o.id}),i.NODE_OPTIONS=at(s,a),A(i),i}function at(e,t){return["--experimental-strip-types",`--require=${JSON.stringify(e)}`,`--import=${JSON.stringify($(t).href)}`].join(" ")}export function playwrightSpawnEnvironmentBytes(e){return Object.entries(e).reduce((t,[r,i])=>t+(process.platform==="win32"?(r.length+i.length+2)*2:Buffer.byteLength(r,"utf8")+Buffer.byteLength(i,"utf8")+2),0)}function A(e){if(Object.keys(e).length>k)throw new Error(`Playwright physical spawn environment exceeds its ${k.toString()} entry limit`);if(playwrightSpawnEnvironmentBytes(e)>PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright physical spawn environment exceeds its ${PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES.toString()} byte limit`)}function st(e,t){const r=e.target.endpoints[t];if(typeof r!="string"||r.trim().length===0)throw new Error(`Playwright task service ${t} does not have a published endpoint`);let i;try{i=new URL(r)}catch(o){throw new Error(`Playwright task service ${t} endpoint is not a valid URL`,{cause:o})}if(i.protocol!=="http:"&&i.protocol!=="https:")throw new Error(`Playwright task service ${t} endpoint must use http or https`);return i.href}function ct(e,t){return{...Z({engineId:"playwright",installationKind:"root-playwright-family",profile:e.profile}),browserName:"chromium",packages:{playwright:t.family.playwright.version,playwrightCore:t.family.playwrightCore.version,playwrightTest:t.family.playwrightTest.version},registryMode:t.registry.mode}}export function playwrightPreparationFailure(e,t){if(t.length===0)return{...P(e,"PLAYWRIGHT_PREPARE_FAILED"),classification:e instanceof L||e instanceof J?"product":"infra"};const r=h(e);return{classification:"infra",code:"PLAYWRIGHT_PREPARE_CLEANUP_FAILED",details:{cleanupFailures:t.map(i=>i.message),preparationFailure:r.message},message:`Playwright task preparation failed and its partial workspace could not be removed: ${t.map(i=>i.message).join("; ")}`,...r.stack===void 0?{}:{stack:r.stack}}}async function lt(e){try{return await e()}catch(t){throw new L(t)}}async function pt(e,t){const r=[];for(const i of e)try{await t.removeOwnedDirectory(i)}catch(o){r.push({directory:i,error:h(o)})}return r}
@@ -1,5 +1,5 @@
1
1
  import { type DatabaseInfo } from "../database/state.js";
2
- import type { RuntimeInstanceContext } from "./runtime-contexts.js";
2
+ import { type RuntimeInstanceContext } from "./runtime-contexts.js";
3
3
  import type { ReadyServiceConfig } from "./runtime-service.js";
4
4
  export interface ReadyService {
5
5
  readonly baseUrl: string | null;
@@ -7,6 +7,8 @@ export interface ReadyService {
7
7
  readonly database: Readonly<DatabaseInfo> | null;
8
8
  }
9
9
  export interface ReadyEnvironment {
10
+ readonly engineCacheDirectory?: string;
11
+ readonly engineWorkspaceDirectory?: string;
10
12
  readonly graphKey: string;
11
13
  readonly leaseDir: string;
12
14
  readonly leaseId: string;
@@ -1 +1 @@
1
- import{readDatabaseInfo as s}from"../database/state.js";export function createReadyEnvironment(t,n){const a=new Map(t.runtimeConfigs.map(e=>[e.name,s(e.stateDir)]));return{...n,graphKey:t.graphKey,runtimeId:t.runtimeId,targetNames:[...t.targetNames],services:new Map(t.runtimeConfigs.map(e=>{const r={...e.testkit.templateContext};return delete r.databaseBindingsReader,[e.name,{baseUrl:e.testkit.local?.baseUrl||null,database:a.get(e.name)??null,config:{...e,testkit:{...e.testkit,templateContext:{...r,databaseBindingsByService:a}}}}]}))}}
1
+ import i from"node:path";import{readDatabaseInfo as m}from"../database/state.js";import{runtimeEngineDirectory as s}from"./runtime-contexts.js";export function createReadyEnvironment(t,a){const r=new Map(t.runtimeConfigs.map(e=>[e.name,m(e.stateDir)]));return{...a,engineCacheDirectory:i.join(s(t),"cache"),engineWorkspaceDirectory:i.join(s(t),"leases",a.leaseId),graphKey:t.graphKey,runtimeId:t.runtimeId,targetNames:[...t.targetNames],services:new Map(t.runtimeConfigs.map(e=>{const n={...e.testkit.templateContext};return delete n.databaseBindingsReader,[e.name,{baseUrl:e.testkit.local?.baseUrl||null,database:r.get(e.name)??null,config:{...e,testkit:{...e.testkit,templateContext:{...n,databaseBindingsByService:r}}}}]}))}}