@gfdelarue/testkit 0.5.0 → 0.6.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 (95) hide show
  1. package/README.md +136 -110
  2. package/dist/app/bugs/draft.js +2 -2
  3. package/dist/app/bugs/guide.js +1 -1
  4. package/dist/app/bugs/record.js +2 -2
  5. package/dist/app/campaigns/index.js +2 -2
  6. package/dist/app/destroy/index.js +1 -1
  7. package/dist/app/discover/index.js +2 -2
  8. package/dist/app/environments/static-values.js +1 -1
  9. package/dist/app/environments/supplied.js +1 -1
  10. package/dist/app/inspect/index.js +1 -1
  11. package/dist/app/lint/native.js +1 -1
  12. package/dist/app/lint/options.js +1 -1
  13. package/dist/app/run/engine-orchestration.js +1 -1
  14. package/dist/app/run/environment.js +1 -1
  15. package/dist/app/run/orchestrator.js +1 -1
  16. package/dist/app/run/request.js +1 -1
  17. package/dist/app/run/run-finalization.js +1 -1
  18. package/dist/app/run/task-batches.js +1 -1
  19. package/dist/app/run/worker-loop.js +1 -1
  20. package/dist/app/status/model.js +1 -1
  21. package/dist/app/status/runs.js +1 -1
  22. package/dist/app/typecheck/index.js +4 -4
  23. package/dist/cli/command-catalog.js +1 -1
  24. package/dist/cli/command-flags.js +1 -1
  25. package/dist/cli/commands/artifacts.js +2 -2
  26. package/dist/cli/commands/discover.js +1 -1
  27. package/dist/cli/commands/guard/unsafe-entrypoint.js +1 -1
  28. package/dist/cli/commands/run.js +2 -2
  29. package/dist/cli/commands/typecheck.js +1 -1
  30. package/dist/cli/commands/workspace.js +1 -1
  31. package/dist/config/authoring/database-fixture-discovery.js +1 -0
  32. package/dist/config/authoring/index.js +1 -1
  33. package/dist/config/loader/config-loader.js +1 -1
  34. package/dist/config/loader/decode-discovery.js +1 -1
  35. package/dist/config/loader/discover-tests.js +1 -1
  36. package/dist/config/loader/discovery.js +2 -2
  37. package/dist/config/loader/file-metadata.js +1 -1
  38. package/dist/config/loader/groups.js +2 -1
  39. package/dist/config/loader/load.js +1 -1
  40. package/dist/config/loader/managed-configs.js +1 -1
  41. package/dist/config/loader/owned-discovery.js +1 -1
  42. package/dist/config/loader/runtime-isolation.js +1 -1
  43. package/dist/config/loader/skip-config.js +1 -1
  44. package/dist/database/admin.js +2 -2
  45. package/dist/database/configured-steps.js +1 -1
  46. package/dist/domain/identifiers.js +1 -1
  47. package/dist/domain/retired-test-types.js +1 -1
  48. package/dist/domain/suite-selection.js +1 -1
  49. package/dist/domain/test-types.js +1 -1
  50. package/dist/engines/catalog.js +1 -1
  51. package/dist/engines/definitions.js +1 -1
  52. package/dist/engines/environment/index.js +1 -1
  53. package/dist/engines/installations/node-vitest.js +1 -1
  54. package/dist/engines/installations/playwright.js +1 -1
  55. package/dist/engines/managed-module-resolution.js +6 -2
  56. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  57. package/dist/engines/node-vitest/adapter/driver.js +2 -2
  58. package/dist/engines/node-vitest/adapter/evidence.js +2 -2
  59. package/dist/engines/node-vitest/adapter/workspace.js +8 -24
  60. package/dist/engines/node-vitest/inventory/index.js +2 -2
  61. package/dist/engines/node-vitest/inventory/list-files.js +3 -3
  62. package/dist/engines/node-vitest/runtime/data/index.js +1 -1
  63. package/dist/engines/node-vitest/runtime/data/report.js +1 -1
  64. package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
  65. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  66. package/dist/engines/playwright/adapter/adapter.js +1 -1
  67. package/dist/engines/playwright/adapter/config.js +2 -2
  68. package/dist/engines/playwright/runtime/managed-test.js +1 -0
  69. package/dist/environment/lifecycle.js +1 -1
  70. package/dist/environment/run-environment.js +1 -1
  71. package/dist/evidence/artifact-config.js +1 -1
  72. package/dist/evidence/maintenance.js +1 -0
  73. package/dist/evidence/read-run.js +1 -1
  74. package/dist/kernel/known-keys.js +1 -0
  75. package/dist/process/execa-bundle.json +1 -1
  76. package/dist/repo/fingerprint-policy.js +1 -1
  77. package/dist/steps/configured-step-testkit-modules.js +1 -1
  78. package/dist/toolchains/index.js +2 -2
  79. package/dist/toolchains/vitest/index.js +1 -1
  80. package/dist/types/config.d.ts +7 -86
  81. package/dist/types/environment.d.ts +6 -0
  82. package/dist/types/node-vitest-data.d.ts +2 -218
  83. package/dist/types/workspace.d.ts +27 -11
  84. package/docs/bug-records.md +3 -8
  85. package/docs/engines.md +63 -17
  86. package/docs/release-notes.md +2116 -0
  87. package/docs/test-types.md +19 -69
  88. package/package.json +2 -5
  89. package/testkit-config-schema.md +65 -128
  90. package/dist/app/lint/rules/data.js +0 -1
  91. package/dist/config/authoring/runtime.js +0 -1
  92. package/dist/engines/node-vitest/runtime/contract/index.js +0 -1
  93. package/dist/engines/node-vitest/runtime/data/http.js +0 -1
  94. package/dist/engines/node-vitest/runtime/data/runner.js +0 -1
  95. package/dist/types/node-vitest-contract.d.ts +0 -75
@@ -1 +1 @@
1
- import{isEngineId as O,isPublicTestType as U,isRunnerProfileId as x}from"../domain/identifiers.js";import{isEngineProfileIdentity as P}from"../engines/definitions.js";import{isUnknownRecord as z}from"../kernel/unknown-values.js";import{requireObjectRecord as u,requireNumber as g,requireStringArray as j,optionalBoolean as I,optionalNonNegativeNumber as y,requireUnknownArray as k,requireString as c,requireResultsReferencePath as R,optionalStringValue as o,optionalNullableStringValue as m,optionalNumberOrStringValue as _,optionalNullableNumberValue as F}from"./read-fields.js";export function readRunDocument(t,e){const n=u(t,e);if(n.schemaVersion===19)return H(n,e);if(n.schemaVersion===20)return G(n,e);throw new Error(`${e}.schemaVersion must be 19 or 20`)}function G(t,e){const n=w(t.run,`${e}.run`),i=a(t.groups,`${e}.groups`,(d,p)=>C(d,p,!0))??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"group"),r=t.environment===void 0?void 0:J(t.environment,`${e}.environment`);return{...t,schemaVersion:20,...s===void 0?{}:{summary:{...s.record,files:s.files,groups:s.owners,suites:s.suites}},run:n,groups:i,...r===void 0?{}:{environment:r}}}function H(t,e){const n=w(t.run,`${e}.run`),i=a(t.services,`${e}.services`,C)??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"service"),r=t.environment===void 0?void 0:K(t.environment,`${e}.environment`),d=t.logs===void 0?void 0:E(t.logs,`${e}.logs`),p=t.setup===void 0?void 0:Q(t.setup,`${e}.setup`);return{...t,schemaVersion:19,...s===void 0?{}:{summary:{...s.record,files:s.files,services:s.owners,suites:s.suites}},run:n,services:i,...r===void 0?{}:{environment:r},...d===void 0?{}:{logs:d},...p===void 0?{}:{setup:p}}}function w(t,e){const n=u(t,e);return F(n.durationMs,`${e}.durationMs`),a(n.types,`${e}.types`,c),{...n,finishedAt:m(n.finishedAt,`${e}.finishedAt`)??null,id:m(n.id,`${e}.id`)??null,startedAt:c(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function N(t,e){t.archive!==void 0&&u(t.archive,`${e}.archive`),t.finalizationFailure!==void 0&&se(t.finalizationFailure,`${e}.finalizationFailure`),o(t.generatedAt,`${e}.generatedAt`)}function J(t,e){const n=u(t,e),i=n.capacity===void 0?void 0:u(n.capacity,`${e}.capacity`),s=i?.runtimeStats===void 0?void 0:k(i.runtimeStats,`${e}.capacity.runtimeStats`),r=n.logs===void 0?void 0:E(n.logs,`${e}.logs`),d=a(n.services,`${e}.services`,A),p=a(n.setupOperations,`${e}.setupOperations`,q);return{...n,...i===void 0?{}:{capacity:{...i,...s===void 0?{}:{runtimeStats:s}}},...r===void 0?{}:{logs:r},...d===void 0?{}:{services:d},...p===void 0?{}:{setupOperations:p}}}function K(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,A);return{...n,...i===void 0?{}:{services:i}}}function A(t,e){const n=u(t,e);return{...n,attempts:a(n.attempts,`${e}.attempts`,(i,s)=>{const r=u(i,s);return{...r,outcome:c(r.outcome,`${s}.outcome`),port:g(r.port,`${s}.port`)}})??[],baseUrl:m(n.baseUrl,`${e}.baseUrl`)??null,port:g(n.port,`${e}.port`),readyUrl:m(n.readyUrl,`${e}.readyUrl`)??null,runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function E(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,ne),s=a(n.setup,`${e}.setup`,B);return{...n,...i===void 0?{}:{services:i},...s===void 0?{}:{setup:s}}}function Q(t,e){const n=u(t,e),i=a(n.operations,`${e}.operations`,q);return{...n,...i===void 0?{}:{operations:i}}}function C(t,e,n=!1){const i=u(t,e),s=a(i.suites,`${e}.suites`,(r,d)=>X(r,d,n))??[];return{...i,name:c(i.name,`${e}.name`),suites:s}}const W=Object.freeze([...["int","e2e","load","scenario"].map(t=>({engineId:"k6",profileId:"k6-http",testType:t})),{engineId:"k6",profileId:"k6-dal",testType:"dal"},{engineId:"node-vitest",profileId:"node-vitest-contract",testType:"contract"},{engineId:"node-vitest",profileId:"node-vitest-data",testType:"data"}]);function X(t,e,n){const i=u(t,e),s=W.find($=>$.testType===i.testType),r=s?.testType??(U(i.testType)?i.testType:void 0);if(r===void 0&&!(n&&i.testType===void 0))throw new Error(`${e}.testType must be a public test type`);const d=s!==void 0&&i.engineId===s.engineId?s.engineId:O(i.engineId)?i.engineId:void 0;if(d===void 0)throw new Error(`${e}.engineId must be a supported engine id`);const p=s!==void 0&&i.profileId===s.profileId?s.profileId:x(i.profileId)?i.profileId:void 0;if(p===void 0)throw new Error(`${e}.profileId must be a supported runner profile id`);if(!(s===void 0?P(i):d===s.engineId&&p===s.profileId))throw new Error(`${e}.profileId does not match ${d}/${r??"untyped"}`);const f=a(i.files,`${e}.files`,Y);return I(i.failed,`${e}.failed`),L(i.diagnostics,`${e}.diagnostics`),y(i.completedFileCount,`${e}.completedFileCount`),y(i.failedFileCount,`${e}.failedFileCount`),y(i.fileCount,`${e}.fileCount`),y(i.passedFileCount,`${e}.passedFileCount`),y(i.skippedFileCount,`${e}.skippedFileCount`),{...i,files:f,name:c(i.name,`${e}.name`),...r===void 0?{}:{testType:r},engineId:d,profileId:p}}function Y(t,e){const n=u(t,e),i=a(n.artifacts,`${e}.artifacts`,Z),s=a(n.failureDetails,`${e}.failureDetails`,l);return I(n.blockedByRuntime,`${e}.blockedByRuntime`),I(n.failed,`${e}.failed`),I(n.infraFailed,`${e}.infraFailed`),m(n.reason,`${e}.reason`),m(n.runtimeIsolation,`${e}.runtimeIsolation`),n.runtimeBatch!=null&&te(n.runtimeBatch,`${e}.runtimeBatch`),a(n.runtimeResets,`${e}.runtimeResets`,ie),{...n,error:m(n.error,`${e}.error`),artifacts:i,durationMs:F(n.durationMs,`${e}.durationMs`),failureDetails:s,path:c(n.path,`${e}.path`),status:c(n.status,`${e}.status`)}}function Z(t,e){const n=u(t,e);return{...n,contentType:m(n.contentType,`${e}.contentType`),kind:m(n.kind,`${e}.kind`),summary:m(n.summary,`${e}.summary`),name:c(n.name,`${e}.name`),path:R(n.path,`${e}.path`)}}function l(t,e){const n=u(t,e),i=n.request===void 0?void 0:b(n.request,`${e}.request`),s=n.response===void 0?void 0:ee(n.response,`${e}.response`),r=n.location===void 0?void 0:u(n.location,`${e}.location`);return r!==void 0&&(o(r.path,`${e}.location.path`),r.line!==void 0&&g(r.line,`${e}.location.line`),r.column!==void 0&&g(r.column,`${e}.location.column`)),{...n,key:o(n.key,`${e}.key`),kind:o(n.kind,`${e}.kind`),message:o(n.message,`${e}.message`),stack:o(n.stack,`${e}.stack`),location:r,request:i,response:s,title:c(n.title,`${e}.title`)}}function b(t,e){const n=u(t,e);return{...n,method:o(n.method,`${e}.method`),path:o(n.path,`${e}.path`),requestId:o(n.requestId,`${e}.requestId`)}}function ee(t,e){const n=u(t,e);return{...n,bodyPreview:o(n.bodyPreview,`${e}.bodyPreview`),contentType:o(n.contentType,`${e}.contentType`),status:_(n.status,`${e}.status`)}}function ne(t,e){const n=u(t,e);return{...n,path:R(n.path,`${e}.path`),runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function q(t,e){const n=u(t,e),i=n.logRef==null?n.logRef:B(n.logRef,`${e}.logRef`);return{...n,durationMs:F(n.durationMs,`${e}.durationMs`),error:m(n.error,`${e}.error`),summary:m(n.summary,`${e}.summary`),logRef:i,serviceName:c(n.serviceName,`${e}.serviceName`),stage:c(n.stage,`${e}.stage`),startedAt:o(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function B(t,e){const n=u(t,e);return o(n.runtimeLabel,`${e}.runtimeLabel`),o(n.serviceName,`${e}.serviceName`),o(n.stage,`${e}.stage`),{...n,path:R(n.path,`${e}.path`)}}function D(t,e,n){const i=u(t,e),s=n==="group"?"groups":"services",r=$=>i[$]===void 0?{}:u(i[$],`${e}.${$}`),[d,p,h]=[r("files"),r(s),r("suites")],f=($,T,M)=>{const v=$[T];if(v===void 0)return 0;if(typeof v!="number"||!Number.isFinite(v)||v<0)throw new Error(`${e}.${M}.${T} must be a non-negative finite number`);return v};return L(i.diagnostics,`${e}.diagnostics`),{record:i,files:{blockedByRuntime:f(d,"blockedByRuntime","files"),cancelled:f(d,"cancelled","files"),failed:f(d,"failed","files"),infraFailed:f(d,"infraFailed","files"),interrupted:f(d,"interrupted","files"),notRun:f(d,"notRun","files"),passed:f(d,"passed","files"),skipped:f(d,"skipped","files"),total:f(d,"total","files")},owners:{blockedByRuntime:f(p,"blockedByRuntime",s),failed:f(p,"failed",s),infraFailed:f(p,"infraFailed",s),passed:f(p,"passed",s),skipped:f(p,"skipped",s),total:f(p,"total",s)},suites:{completed:f(h,"completed","suites"),failed:f(h,"failed","suites"),passed:f(h,"passed","suites"),skipped:f(h,"skipped","suites"),total:f(h,"total","suites")}}}function L(t,e){if(t!==void 0)return k(t,e).map((n,i)=>{const s=`${e}[${String(i)}]`,r=u(n,s);return{...r,code:c(r.code,`${s}.code`),group:o(r.group,`${s}.group`),message:c(r.message,`${s}.message`),service:o(r.service,`${s}.service`),suite:o(r.suite,`${s}.suite`)}})}function te(t,e){const n=u(t,e);return{...n,id:o(n.id,`${e}.id`),fileCount:y(n.fileCount,`${e}.fileCount`),index:y(n.index,`${e}.index`),files:n.files===void 0?void 0:j(n.files,`${e}.files`)}}function ie(t,e){const n=u(t,e);return{...n,position:o(n.position,`${e}.position`),reason:o(n.reason,`${e}.reason`),runtimeIsolation:o(n.runtimeIsolation,`${e}.runtimeIsolation`),trigger:o(n.trigger,`${e}.trigger`)}}function se(t,e){const n=u(t,e),i=n.operation===void 0?void 0:u(n.operation,`${e}.operation`);return{...n,name:c(n.name,`${e}.name`),message:c(n.message,`${e}.message`),code:o(n.code,`${e}.code`),stack:o(n.stack,`${e}.stack`),phase:o(n.phase,`${e}.phase`),exitCode:n.exitCode===void 0?void 0:g(n.exitCode,`${e}.exitCode`),status:n.status===void 0?void 0:g(n.status,`${e}.status`),diagnostics:n.diagnostics===void 0?void 0:S(n.diagnostics,`${e}.diagnostics`),operation:i===void 0?void 0:{...i,id:o(i.id,`${e}.operation.id`),source:c(i.source,`${e}.operation.source`),name:c(i.name,`${e}.operation.name`),kind:c(i.kind,`${e}.operation.kind`),summary:o(i.summary,`${e}.operation.summary`),status:o(i.status,`${e}.operation.status`),error:o(i.error,`${e}.operation.error`),scope:S(i.scope,`${e}.operation.scope`)}}}function S(t,e){return Object.fromEntries(Object.entries(u(t,e)).map(([n,i])=>[n,V(i,`${e}.${n}`)]))}function V(t,e){return t===null||typeof t=="string"||typeof t=="boolean"?t:Array.isArray(t)?k(t,e).map((n,i)=>V(n,`${e}[${String(i)}]`)):z(t)?S(t,e):g(t,e)}function a(t,e,n){return t===void 0?void 0:k(t,e).map((i,s)=>n(i,`${e}[${String(s)}]`))}
1
+ import{isEngineId as M,isPublicTestType as O,isRunnerProfileId as U}from"../domain/identifiers.js";import{isEngineProfileIdentity as P}from"../engines/definitions.js";import{isUnknownRecord as z}from"../kernel/unknown-values.js";import{requireObjectRecord as u,requireNumber as g,requireStringArray as j,optionalBoolean as I,optionalNonNegativeNumber as y,requireUnknownArray as k,requireString as c,requireResultsReferencePath as R,optionalStringValue as o,optionalNullableStringValue as m,optionalNumberOrStringValue as _,optionalNullableNumberValue as F}from"./read-fields.js";export function readRunDocument(t,e){const n=u(t,e);if(n.schemaVersion===19)return H(n,e);if(n.schemaVersion===20)return G(n,e);throw new Error(`${e}.schemaVersion must be 19 or 20`)}function G(t,e){const n=w(t.run,`${e}.run`),i=a(t.groups,`${e}.groups`,(d,p)=>C(d,p,!0))??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"group"),r=t.environment===void 0?void 0:J(t.environment,`${e}.environment`);return{...t,schemaVersion:20,...s===void 0?{}:{summary:{...s.record,files:s.files,groups:s.owners,suites:s.suites}},run:n,groups:i,...r===void 0?{}:{environment:r}}}function H(t,e){const n=w(t.run,`${e}.run`),i=a(t.services,`${e}.services`,C)??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"service"),r=t.environment===void 0?void 0:K(t.environment,`${e}.environment`),d=t.logs===void 0?void 0:E(t.logs,`${e}.logs`),p=t.setup===void 0?void 0:Q(t.setup,`${e}.setup`);return{...t,schemaVersion:19,...s===void 0?{}:{summary:{...s.record,files:s.files,services:s.owners,suites:s.suites}},run:n,services:i,...r===void 0?{}:{environment:r},...d===void 0?{}:{logs:d},...p===void 0?{}:{setup:p}}}function w(t,e){const n=u(t,e);return F(n.durationMs,`${e}.durationMs`),a(n.types,`${e}.types`,c),{...n,finishedAt:m(n.finishedAt,`${e}.finishedAt`)??null,id:m(n.id,`${e}.id`)??null,startedAt:c(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function N(t,e){t.archive!==void 0&&u(t.archive,`${e}.archive`),t.finalizationFailure!==void 0&&ie(t.finalizationFailure,`${e}.finalizationFailure`),o(t.generatedAt,`${e}.generatedAt`)}function J(t,e){const n=u(t,e),i=n.capacity===void 0?void 0:u(n.capacity,`${e}.capacity`),s=i?.runtimeStats===void 0?void 0:k(i.runtimeStats,`${e}.capacity.runtimeStats`),r=n.logs===void 0?void 0:E(n.logs,`${e}.logs`),d=a(n.services,`${e}.services`,A),p=a(n.setupOperations,`${e}.setupOperations`,q);return{...n,...i===void 0?{}:{capacity:{...i,...s===void 0?{}:{runtimeStats:s}}},...r===void 0?{}:{logs:r},...d===void 0?{}:{services:d},...p===void 0?{}:{setupOperations:p}}}function K(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,A);return{...n,...i===void 0?{}:{services:i}}}function A(t,e){const n=u(t,e);return{...n,attempts:a(n.attempts,`${e}.attempts`,(i,s)=>{const r=u(i,s);return{...r,outcome:c(r.outcome,`${s}.outcome`),port:g(r.port,`${s}.port`)}})??[],baseUrl:m(n.baseUrl,`${e}.baseUrl`)??null,port:g(n.port,`${e}.port`),readyUrl:m(n.readyUrl,`${e}.readyUrl`)??null,runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function E(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,ee),s=a(n.setup,`${e}.setup`,B);return{...n,...i===void 0?{}:{services:i},...s===void 0?{}:{setup:s}}}function Q(t,e){const n=u(t,e),i=a(n.operations,`${e}.operations`,q);return{...n,...i===void 0?{}:{operations:i}}}function C(t,e,n=!1){const i=u(t,e),s=a(i.suites,`${e}.suites`,(r,d)=>W(r,d,n))??[];return{...i,name:c(i.name,`${e}.name`),suites:s}}export const RETIRED_SUITE_IDENTITIES=Object.freeze([...["int","e2e","load","scenario"].map(t=>({engineId:"k6",profileId:"k6-http",testType:t})),{engineId:"k6",profileId:"k6-dal",testType:"dal"},{engineId:"node-vitest",profileId:"node-vitest-contract",testType:"contract"},{engineId:"node-vitest",profileId:"node-vitest-data",testType:"data"}]);function W(t,e,n){const i=u(t,e),s=RETIRED_SUITE_IDENTITIES.find($=>$.testType===i.testType),r=s?.testType??(O(i.testType)?i.testType:void 0);if(r===void 0&&!(n&&i.testType===void 0))throw new Error(`${e}.testType must be a public test type`);const d=s!==void 0&&i.engineId===s.engineId?s.engineId:M(i.engineId)?i.engineId:void 0;if(d===void 0)throw new Error(`${e}.engineId must be a supported engine id`);const p=s!==void 0&&i.profileId===s.profileId?s.profileId:U(i.profileId)?i.profileId:void 0;if(p===void 0)throw new Error(`${e}.profileId must be a supported runner profile id`);if(!(s===void 0?P(i):d===s.engineId&&p===s.profileId))throw new Error(`${e}.profileId does not match ${d}/${r??"untyped"}`);const f=a(i.files,`${e}.files`,X);return I(i.failed,`${e}.failed`),L(i.diagnostics,`${e}.diagnostics`),y(i.completedFileCount,`${e}.completedFileCount`),y(i.failedFileCount,`${e}.failedFileCount`),y(i.fileCount,`${e}.fileCount`),y(i.passedFileCount,`${e}.passedFileCount`),y(i.skippedFileCount,`${e}.skippedFileCount`),{...i,files:f,name:c(i.name,`${e}.name`),...r===void 0?{}:{testType:r},engineId:d,profileId:p}}function X(t,e){const n=u(t,e),i=a(n.artifacts,`${e}.artifacts`,Y),s=a(n.failureDetails,`${e}.failureDetails`,Z);return I(n.blockedByRuntime,`${e}.blockedByRuntime`),I(n.failed,`${e}.failed`),I(n.infraFailed,`${e}.infraFailed`),m(n.reason,`${e}.reason`),m(n.runtimeIsolation,`${e}.runtimeIsolation`),n.runtimeBatch!=null&&ne(n.runtimeBatch,`${e}.runtimeBatch`),a(n.runtimeResets,`${e}.runtimeResets`,te),{...n,error:m(n.error,`${e}.error`),artifacts:i,durationMs:F(n.durationMs,`${e}.durationMs`),failureDetails:s,path:c(n.path,`${e}.path`),status:c(n.status,`${e}.status`)}}function Y(t,e){const n=u(t,e);return{...n,contentType:m(n.contentType,`${e}.contentType`),kind:m(n.kind,`${e}.kind`),summary:m(n.summary,`${e}.summary`),name:c(n.name,`${e}.name`),path:R(n.path,`${e}.path`)}}function Z(t,e){const n=u(t,e),i=n.request===void 0?void 0:l(n.request,`${e}.request`),s=n.response===void 0?void 0:b(n.response,`${e}.response`),r=n.location===void 0?void 0:u(n.location,`${e}.location`);return r!==void 0&&(o(r.path,`${e}.location.path`),r.line!==void 0&&g(r.line,`${e}.location.line`),r.column!==void 0&&g(r.column,`${e}.location.column`)),{...n,key:o(n.key,`${e}.key`),kind:o(n.kind,`${e}.kind`),message:o(n.message,`${e}.message`),stack:o(n.stack,`${e}.stack`),location:r,request:i,response:s,title:c(n.title,`${e}.title`)}}function l(t,e){const n=u(t,e);return{...n,method:o(n.method,`${e}.method`),path:o(n.path,`${e}.path`),requestId:o(n.requestId,`${e}.requestId`)}}function b(t,e){const n=u(t,e);return{...n,bodyPreview:o(n.bodyPreview,`${e}.bodyPreview`),contentType:o(n.contentType,`${e}.contentType`),status:_(n.status,`${e}.status`)}}function ee(t,e){const n=u(t,e);return{...n,path:R(n.path,`${e}.path`),runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function q(t,e){const n=u(t,e),i=n.logRef==null?n.logRef:B(n.logRef,`${e}.logRef`);return{...n,durationMs:F(n.durationMs,`${e}.durationMs`),error:m(n.error,`${e}.error`),summary:m(n.summary,`${e}.summary`),logRef:i,serviceName:c(n.serviceName,`${e}.serviceName`),stage:c(n.stage,`${e}.stage`),startedAt:o(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function B(t,e){const n=u(t,e);return o(n.runtimeLabel,`${e}.runtimeLabel`),o(n.serviceName,`${e}.serviceName`),o(n.stage,`${e}.stage`),{...n,path:R(n.path,`${e}.path`)}}function D(t,e,n){const i=u(t,e),s=n==="group"?"groups":"services",r=$=>i[$]===void 0?{}:u(i[$],`${e}.${$}`),[d,p,h]=[r("files"),r(s),r("suites")],f=($,T,x)=>{const v=$[T];if(v===void 0)return 0;if(typeof v!="number"||!Number.isFinite(v)||v<0)throw new Error(`${e}.${x}.${T} must be a non-negative finite number`);return v};return L(i.diagnostics,`${e}.diagnostics`),{record:i,files:{blockedByRuntime:f(d,"blockedByRuntime","files"),cancelled:f(d,"cancelled","files"),failed:f(d,"failed","files"),infraFailed:f(d,"infraFailed","files"),interrupted:f(d,"interrupted","files"),notRun:f(d,"notRun","files"),passed:f(d,"passed","files"),skipped:f(d,"skipped","files"),total:f(d,"total","files")},owners:{blockedByRuntime:f(p,"blockedByRuntime",s),failed:f(p,"failed",s),infraFailed:f(p,"infraFailed",s),passed:f(p,"passed",s),skipped:f(p,"skipped",s),total:f(p,"total",s)},suites:{completed:f(h,"completed","suites"),failed:f(h,"failed","suites"),passed:f(h,"passed","suites"),skipped:f(h,"skipped","suites"),total:f(h,"total","suites")}}}function L(t,e){if(t!==void 0)return k(t,e).map((n,i)=>{const s=`${e}[${String(i)}]`,r=u(n,s);return{...r,code:c(r.code,`${s}.code`),group:o(r.group,`${s}.group`),message:c(r.message,`${s}.message`),service:o(r.service,`${s}.service`),suite:o(r.suite,`${s}.suite`)}})}function ne(t,e){const n=u(t,e);return{...n,id:o(n.id,`${e}.id`),fileCount:y(n.fileCount,`${e}.fileCount`),index:y(n.index,`${e}.index`),files:n.files===void 0?void 0:j(n.files,`${e}.files`)}}function te(t,e){const n=u(t,e);return{...n,position:o(n.position,`${e}.position`),reason:o(n.reason,`${e}.reason`),runtimeIsolation:o(n.runtimeIsolation,`${e}.runtimeIsolation`),trigger:o(n.trigger,`${e}.trigger`)}}function ie(t,e){const n=u(t,e),i=n.operation===void 0?void 0:u(n.operation,`${e}.operation`);return{...n,name:c(n.name,`${e}.name`),message:c(n.message,`${e}.message`),code:o(n.code,`${e}.code`),stack:o(n.stack,`${e}.stack`),phase:o(n.phase,`${e}.phase`),exitCode:n.exitCode===void 0?void 0:g(n.exitCode,`${e}.exitCode`),status:n.status===void 0?void 0:g(n.status,`${e}.status`),diagnostics:n.diagnostics===void 0?void 0:S(n.diagnostics,`${e}.diagnostics`),operation:i===void 0?void 0:{...i,id:o(i.id,`${e}.operation.id`),source:c(i.source,`${e}.operation.source`),name:c(i.name,`${e}.operation.name`),kind:c(i.kind,`${e}.operation.kind`),summary:o(i.summary,`${e}.operation.summary`),status:o(i.status,`${e}.operation.status`),error:o(i.error,`${e}.operation.error`),scope:S(i.scope,`${e}.operation.scope`)}}}function S(t,e){return Object.fromEntries(Object.entries(u(t,e)).map(([n,i])=>[n,V(i,`${e}.${n}`)]))}function V(t,e){return t===null||typeof t=="string"||typeof t=="boolean"?t:Array.isArray(t)?k(t,e).map((n,i)=>V(n,`${e}[${String(i)}]`)):z(t)?S(t,e):g(t,e)}function a(t,e,n){return t===void 0?void 0:k(t,e).map((i,s)=>n(i,`${e}[${String(s)}]`))}
@@ -0,0 +1 @@
1
+ export function unknownKeysMessage(r,e,s){const n=Object.keys(r).filter(t=>!e.includes(t));if(n.length===0)return null;const o=n.map(t=>{const l=c(t,e);return l===null?JSON.stringify(t):`${JSON.stringify(t)} (mispelled ${JSON.stringify(l)}?)`});return`${s} has unknown ${n.length===1?"key":"keys"} ${o.join(", ")}; it accepts ${e.join(", ")}`}export function rejectUnknownKeys(r,e,s){const n=unknownKeysMessage(r,e,s);if(n!==null)throw new Error(n)}function c(r,e){let s=null,n=Math.max(1,Math.floor(r.length/3))+1;for(const o of e){const t=i(r.toLowerCase(),o.toLowerCase());t<n&&(s=o,n=t)}return s}function i(r,e){let s=Array.from({length:e.length+1},(n,o)=>o);for(let n=0;n<r.length;n+=1){const o=[n+1];for(let t=0;t<e.length;t+=1){const l=r.charCodeAt(n)===e.charCodeAt(t)?0:1;o.push(Math.min((s[t+1]??0)+1,(o[t]??0)+1,(s[t]??0)+l))}s=o}return s[e.length]??0}
@@ -3,7 +3,7 @@
3
3
  "entrypoint": "dist/process/process.js",
4
4
  "sourceSha256": "6607c0273f046a00eb72ed4a0cac0f9b1c54d638be535b11188e1c8e20cdea67",
5
5
  "bundleSha256": "6ce5a9db43a95cc11dd3f265a67cbad9bd4c492ea2f33187c6c2474150ce16df",
6
- "packageLockSha256": "53ab4740dd4220dec486ba6f21059b6307964fea2ef9b1635598b131ef0a10de",
6
+ "packageLockSha256": "d04d8e8ba948c50a5890e5ed3a9ac8885c1f197d493bb9e615afd0948c84985e",
7
7
  "esbuildVersion": "0.28.1",
8
8
  "options": {
9
9
  "format": "esm",
@@ -1 +1 @@
1
- import k from"fs";import f from"path";import{physicalNodeMode as u,readPhysicalDirectoryEntries as y,readPhysicalNode as b}from"./physical-tree.js";import{isUnknownArray as $,isUnknownRecord as w}from"../kernel/unknown-values.js";const E=[".testkit",".next-testkit"],l={exclude:[],include:[]};export function normalizeFingerprintPolicy(t={}){if(t==null)return{exclude:[],include:[]};if(!w(t))throw new Error("testkit.config.ts fingerprints must be an object");return{exclude:m(t.exclude,"fingerprints.exclude"),include:m(t.include,"fingerprints.include")}}export function shouldIgnoreCanonicalFingerprintPath(t,n=l){return shouldIgnoreCanonicalFingerprintPathAtPrefix(t,"",n)}export function shouldIgnoreCanonicalFingerprintPathAtPrefix(t,n,e=l){const r=normalizeCanonicalRelativePath(t),i=g(n,r);return!i||d(i,e.include)?!1:z(r)?!0:d(i,e.exclude)}export function hasIncludedFingerprintDescendantAtPrefix(t,n,e){const r=g(n,t);if(!r)return e.include.length>0;const i=`${r}/`;return e.include.some(c=>{const o=normalizeCanonicalRelativePath(c);if(o.includes("*")){const a=L(o);return a.length===0||r.startsWith(a)||a.startsWith(i)}return h(o).startsWith(i)})}export function appendFingerprintPathToHash(t,n,e,r=l){const i=normalizeRelativePath(n,e),c=k.realpathSync.native(f.resolve(n));P(t,c,i,r)}export function normalizeRelativePath(t,n){const e=f.isAbsolute(n)?f.relative(t,n):n;return f.isAbsolute(n)?normalizeCanonicalRelativePath(e.split(f.sep).join("/")):normalizePath(e)}export function normalizePath(t){return t.split(f.sep).join("/").replace(/\\/g,"/").replace(/^\.\/+/,"").replace(/\/+$/,"")}function m(t,n){if(t==null)return[];if(!$(t))throw new Error(`testkit.config.ts ${n} must be an array of paths`);return t.map((e,r)=>{if(typeof e!="string")throw new Error(`testkit.config.ts ${n}[${String(r)}] must be a string path`);const i=normalizeCanonicalRelativePath(e.trim());if(!i)throw new Error(`testkit.config.ts ${n}[${String(r)}] must be a non-empty path`);if(I(i))throw new Error(`testkit.config.ts ${n}[${String(r)}] must use "/" as the path separator; a literal backslash is written doubled`);if(i.startsWith("/")||/^[a-z]:\//iu.test(i)||i.startsWith("../")||i==="..")throw new Error(`testkit.config.ts ${n}[${String(r)}] must be relative to the product directory`);return i})}function d(t,n){return n.some(e=>T(t,e))}function z(t){return d(t,E)}function T(t,n){const e=normalizeCanonicalRelativePath(t),r=normalizeCanonicalRelativePath(n);if(!r)return!1;const i=h(r);if(e===i||e.startsWith(`${i}/`))return!0;if(!r.includes("*"))return!1;const c=R(r);let o=e;for(;o;){if(c.test(o))return!0;const a=o.lastIndexOf("/");if(a<0)break;o=o.slice(0,a)}return!1}export function normalizeCanonicalRelativePath(t){return t.replace(/^\.\/+/,"").replace(/\/+$/,"")}function I(t){for(let n=0;n<t.length;n+=1)if(t[n]==="\\"){if(t[n+1]!=="\\")return!0;n+=1}return!1}function h(t){return t.replace(/\\\\/g,"\\")}function L(t){let n="";for(let e=0;e<t.length;e+=1){const r=t[e];if(r==="*")break;r==="\\"&&t[e+1]==="\\"?(n+="\\",e+=1):n+=r}return n}function P(t,n,e,r){const i=b(n,e),c=shouldIgnoreCanonicalFingerprintPath(e,r);if(!(c&&(i.kind!=="directory"||!hasIncludedFingerprintDescendantAtPrefix(e,"",r)))&&(c||S(t,e,i),i.kind==="directory"))for(const o of y(n,e))P(t,n,g(e,o),r)}function S(t,n,e){switch(t.update("node\0"),s(t,"path",n),s(t,"kind",e.kind),e.kind){case"file":s(t,"mode",u(e.mode)),p(t,"contents",e.contents);break;case"directory":s(t,"mode",u(e.mode));break;case"symlink":case"ancestor-symlink":"obstaclePath"in e&&s(t,"obstacle",e.obstaclePath),p(t,"target",e.target);break;case"ancestor-missing":s(t,"obstacle",e.obstaclePath);break;case"ancestor-non-directory":s(t,"obstacle",e.obstaclePath),s(t,"mode",u(e.mode));break;case"missing":break}}function s(t,n,e){p(t,n,Buffer.from(e))}function p(t,n,e){t.update(`${n}\0${e.byteLength.toString()}\0`),t.update(e),t.update("\0")}function g(t,n){const e=normalizeCanonicalRelativePath(t),r=normalizeCanonicalRelativePath(n);return e?r?`${e}/${r}`:e:r}function R(t){let n="^";for(let e=0;e<t.length;e+=1){const r=t[e],i=t[e+1];r==="\\"&&i==="\\"?(n+=x("\\"),e+=1):r==="*"&&i==="*"?(n+=".*",e+=1):r==="*"?n+="[^/]*":n+=x(r)}return n+="$",new RegExp(n)}function x(t){return t.replace(/[\\^$+?.()|[\]{}]/g,"\\$&")}export function appendFileToHash(t,n,e,r={}){appendFingerprintPathToHash(t,n,e,normalizeFingerprintPolicy(r))}
1
+ import x from"fs";import f from"path";import{physicalNodeMode as u,readPhysicalDirectoryEntries as y,readPhysicalNode as b}from"./physical-tree.js";import{rejectUnknownKeys as w}from"../kernel/known-keys.js";import{isUnknownArray as $,isUnknownRecord as E}from"../kernel/unknown-values.js";const z=[".testkit",".next-testkit"],l={exclude:[],include:[]};export function normalizeFingerprintPolicy(t={}){if(t==null)return{exclude:[],include:[]};if(!E(t))throw new Error("testkit.config.ts fingerprints must be an object");return w(t,["exclude","include"],"testkit.config.ts fingerprints"),{exclude:m(t.exclude,"fingerprints.exclude"),include:m(t.include,"fingerprints.include")}}export function shouldIgnoreCanonicalFingerprintPath(t,n=l){return shouldIgnoreCanonicalFingerprintPathAtPrefix(t,"",n)}export function shouldIgnoreCanonicalFingerprintPathAtPrefix(t,n,e=l){const r=normalizeCanonicalRelativePath(t),i=g(n,r);return!i||d(i,e.include)?!1:T(r)?!0:d(i,e.exclude)}export function hasIncludedFingerprintDescendantAtPrefix(t,n,e){const r=g(n,t);if(!r)return e.include.length>0;const i=`${r}/`;return e.include.some(c=>{const o=normalizeCanonicalRelativePath(c);if(o.includes("*")){const a=S(o);return a.length===0||r.startsWith(a)||a.startsWith(i)}return h(o).startsWith(i)})}export function appendFingerprintPathToHash(t,n,e,r=l){const i=normalizeRelativePath(n,e),c=x.realpathSync.native(f.resolve(n));P(t,c,i,r)}export function normalizeRelativePath(t,n){const e=f.isAbsolute(n)?f.relative(t,n):n;return f.isAbsolute(n)?normalizeCanonicalRelativePath(e.split(f.sep).join("/")):normalizePath(e)}export function normalizePath(t){return t.split(f.sep).join("/").replace(/\\/g,"/").replace(/^\.\/+/,"").replace(/\/+$/,"")}function m(t,n){if(t==null)return[];if(!$(t))throw new Error(`testkit.config.ts ${n} must be an array of paths`);return t.map((e,r)=>{if(typeof e!="string")throw new Error(`testkit.config.ts ${n}[${String(r)}] must be a string path`);const i=normalizeCanonicalRelativePath(e.trim());if(!i)throw new Error(`testkit.config.ts ${n}[${String(r)}] must be a non-empty path`);if(L(i))throw new Error(`testkit.config.ts ${n}[${String(r)}] must use "/" as the path separator; a literal backslash is written doubled`);if(i.startsWith("/")||/^[a-z]:\//iu.test(i)||i.startsWith("../")||i==="..")throw new Error(`testkit.config.ts ${n}[${String(r)}] must be relative to the product directory`);return i})}function d(t,n){return n.some(e=>I(t,e))}function T(t){return d(t,z)}function I(t,n){const e=normalizeCanonicalRelativePath(t),r=normalizeCanonicalRelativePath(n);if(!r)return!1;const i=h(r);if(e===i||e.startsWith(`${i}/`))return!0;if(!r.includes("*"))return!1;const c=W(r);let o=e;for(;o;){if(c.test(o))return!0;const a=o.lastIndexOf("/");if(a<0)break;o=o.slice(0,a)}return!1}export function normalizeCanonicalRelativePath(t){return t.replace(/^\.\/+/,"").replace(/\/+$/,"")}function L(t){for(let n=0;n<t.length;n+=1)if(t[n]==="\\"){if(t[n+1]!=="\\")return!0;n+=1}return!1}function h(t){return t.replace(/\\\\/g,"\\")}function S(t){let n="";for(let e=0;e<t.length;e+=1){const r=t[e];if(r==="*")break;r==="\\"&&t[e+1]==="\\"?(n+="\\",e+=1):n+=r}return n}function P(t,n,e,r){const i=b(n,e),c=shouldIgnoreCanonicalFingerprintPath(e,r);if(!(c&&(i.kind!=="directory"||!hasIncludedFingerprintDescendantAtPrefix(e,"",r)))&&(c||R(t,e,i),i.kind==="directory"))for(const o of y(n,e))P(t,n,g(e,o),r)}function R(t,n,e){switch(t.update("node\0"),s(t,"path",n),s(t,"kind",e.kind),e.kind){case"file":s(t,"mode",u(e.mode)),p(t,"contents",e.contents);break;case"directory":s(t,"mode",u(e.mode));break;case"symlink":case"ancestor-symlink":"obstaclePath"in e&&s(t,"obstacle",e.obstaclePath),p(t,"target",e.target);break;case"ancestor-missing":s(t,"obstacle",e.obstaclePath);break;case"ancestor-non-directory":s(t,"obstacle",e.obstaclePath),s(t,"mode",u(e.mode));break;case"missing":break}}function s(t,n,e){p(t,n,Buffer.from(e))}function p(t,n,e){t.update(`${n}\0${e.byteLength.toString()}\0`),t.update(e),t.update("\0")}function g(t,n){const e=normalizeCanonicalRelativePath(t),r=normalizeCanonicalRelativePath(n);return e?r?`${e}/${r}`:e:r}function W(t){let n="^";for(let e=0;e<t.length;e+=1){const r=t[e],i=t[e+1];r==="\\"&&i==="\\"?(n+=k("\\"),e+=1):r==="*"&&i==="*"?(n+=".*",e+=1):r==="*"?n+="[^/]*":n+=k(r)}return n+="$",new RegExp(n)}function k(t){return t.replace(/[\\^$+?.()|[\]{}]/g,"\\$&")}export function appendFileToHash(t,n,e,r={}){appendFingerprintPathToHash(t,n,e,normalizeFingerprintPolicy(r))}
@@ -1 +1 @@
1
- export const CONFIGURED_STEP_TESTKIT_MODULE_SPECIFIERS=["@gfdelarue/testkit","@gfdelarue/testkit/config","@gfdelarue/testkit/config/build-assets","@gfdelarue/testkit/config/database-steps","@gfdelarue/testkit/config/next-runtime-tsconfig","@gfdelarue/testkit/drizzle","@gfdelarue/testkit/env","@gfdelarue/testkit/tooling/vitest"];const n=new Set(["@gfdelarue/testkit/environment","@gfdelarue/testkit/node-vitest/contract","@gfdelarue/testkit/node-vitest/data","@gfdelarue/testkit/playwright"]);export const CONFIGURED_STEP_TESTKIT_MODULE_DIST_PATHS={"@gfdelarue/testkit":["index"],"@gfdelarue/testkit/config":["config","authoring","index"],"@gfdelarue/testkit/config/build-assets":["config","runtime","build-assets"],"@gfdelarue/testkit/config/database-steps":["database","steps"],"@gfdelarue/testkit/config/next-runtime-tsconfig":["config","runtime","next-runtime-tsconfig"],"@gfdelarue/testkit/drizzle":["database","drizzle","index"],"@gfdelarue/testkit/env":["config","runtime","env","index"],"@gfdelarue/testkit/tooling/vitest":["toolchains","vitest","index"]};export function isTestkitPackageSpecifier(t){return t==="@gfdelarue/testkit"||t.startsWith("@gfdelarue/testkit/")}export function requireConfiguredStepTestkitModuleSpecifier(t){if(n.has(t))throw new Error(`Configured lifecycle modules run in Node and may not import engine authoring module ${JSON.stringify(t)}`);const e=CONFIGURED_STEP_TESTKIT_MODULE_SPECIFIERS.find(i=>i===t);if(!e)throw new Error(`Unsupported configured Testkit module specifier ${JSON.stringify(t)}`);return e}
1
+ export const CONFIGURED_STEP_TESTKIT_MODULE_SPECIFIERS=["@gfdelarue/testkit","@gfdelarue/testkit/config","@gfdelarue/testkit/config/build-assets","@gfdelarue/testkit/config/database-steps","@gfdelarue/testkit/config/next-runtime-tsconfig","@gfdelarue/testkit/drizzle","@gfdelarue/testkit/env","@gfdelarue/testkit/tooling/vitest"];const n=new Set(["@gfdelarue/testkit/environment","@gfdelarue/testkit/node-vitest/data","@gfdelarue/testkit/playwright"]);export const CONFIGURED_STEP_TESTKIT_MODULE_DIST_PATHS={"@gfdelarue/testkit":["index"],"@gfdelarue/testkit/config":["config","authoring","index"],"@gfdelarue/testkit/config/build-assets":["config","runtime","build-assets"],"@gfdelarue/testkit/config/database-steps":["database","steps"],"@gfdelarue/testkit/config/next-runtime-tsconfig":["config","runtime","next-runtime-tsconfig"],"@gfdelarue/testkit/drizzle":["database","drizzle","index"],"@gfdelarue/testkit/env":["config","runtime","env","index"],"@gfdelarue/testkit/tooling/vitest":["toolchains","vitest","index"]};export function isTestkitPackageSpecifier(t){return t==="@gfdelarue/testkit"||t.startsWith("@gfdelarue/testkit/")}export function requireConfiguredStepTestkitModuleSpecifier(t){if(n.has(t))throw new Error(`Configured lifecycle modules run in Node and may not import engine authoring module ${JSON.stringify(t)}`);const e=CONFIGURED_STEP_TESTKIT_MODULE_SPECIFIERS.find(i=>i===t);if(!e)throw new Error(`Unsupported configured Testkit module specifier ${JSON.stringify(t)}`);return e}
@@ -1,4 +1,4 @@
1
- import _ from"crypto";import h from"fs";import m from"path";import{prepareCommandForSpawn as I,resolveCommandForSpawn as A}from"../process/host/platform.js";import{normalizeContainedRelativePath as V,resolveContainedPath as k}from"../process/files/contained-path.js";import{deriveSemverMinimum as D,isExactSemver as L,normalizeSemver as f,satisfiesSemver as g}from"./semver.js";import{managedHostProcessInfrastructureError as M,runManagedHostProcess as H}from"../process/index.js";import{managedNodeRelease as j}from"./managed-node-releases.js";import{createSharedToolchainOperation as P,waitForSharedToolchainOperation as R}from"./shared-operation.js";import{TOOLCHAIN_METADATA_TIMEOUT_MS as O}from"./timeouts.js";import{isUnknownRecord as N,normalizeOptionalString as d}from"../kernel/unknown-values.js";import{readJsonFileOrNull as F}from"../process/files/json-file.js";const W=".",z="auto",q="require-host",U=600*1e3,B=['"use strict";','const { spawn } = require("node:child_process");',"const [command, ...args] = process.argv.slice(1);",'if (typeof command !== "string" || command.length === 0) {',' process.stderr.write("Missing Windows toolchain command\\n");'," process.exitCode = 126;","} else {"," let spawnFailed = false;"," const child = spawn(command, args, {"," shell: false,",' stdio: "inherit",'," windowsHide: true,"," windowsVerbatimArguments: true,"," });",' child.once("error", (error) => {'," spawnFailed = true;"," process.stderr.write(`${error.stack ?? String(error)}\\n`);"," process.exitCode = 126;"," });",' child.once("close", (code, signal) => {'," if (spawnFailed) return;"," if (signal !== null) {"," process.stderr.write(`Windows toolchain command exited on signal ${signal}\\n`);"," process.exitCode = 1;"," return;"," }"," process.exitCode = code ?? 1;"," });","}"].join(`
2
- `),v=new WeakMap,y=new WeakSet;export function normalizeToolchainRegistry(n){if(n==null)return{};if(!N(n))throw new Error("testkit.config.ts toolchains must be an object");const e={};for(const[o,r]of Object.entries(n))e[o]=x(r,`testkit.config.ts toolchains.${o}`);return e}export function normalizeRuntimeToolchain(n,e,o={}){if(n==null)return null;if(typeof n=="string"){const t=n.trim();if(!t)throw new Error(`${e} must reference a non-empty toolchain name`);const i=o[t];if(!i)throw new Error(`${e} references unknown toolchain "${t}"`);return{...i,refName:t,sourceLabel:`${e} -> ${t}`}}return{...x(n,e),refName:null,sourceLabel:e}}export async function resolveConfiguredToolchain(n,e={}){const o=n?.testkit?.runtime?.toolchain;if(!n||!o)return null;let r=v.get(n);if(r===void 0){r=P(i=>J(n,o,{...e,signal:i})),v.set(n,r);const t=r;r.promise.catch(()=>{v.get(n)===t&&v.delete(n)})}return R(r,e.signal)}export function announceResolvedToolchain(n,e,o=null){return!e||y.has(n)||(y.add(n),o?.toolchainResolved&&o.toolchainResolved({serviceName:n.name,runtimeLabel:n.runtimeLabel||n.name},e.summary)),Promise.resolve()}export function applyToolchainEnv(n,e,o=process.env){if(!e)return{...n};const r={...n,PATH:ee(e.binDir,n.PATH||o.PATH||""),TESTKIT_TOOLCHAIN_KIND:"node",TESTKIT_TOOLCHAIN_NODE_VERSION:e.nodeVersion,TESTKIT_TOOLCHAIN_NODE_SOURCE:e.nodeSource,TESTKIT_TOOLCHAIN_INSTALL_MODE:e.install};return e.npmVersion&&(r.TESTKIT_TOOLCHAIN_NPM_VERSION=e.npmVersion,r.TESTKIT_TOOLCHAIN_NPM_SOURCE=e.npmSource),r}function x(n,e){if(!N(n))throw new Error(`${e} must be an object`);const o=V(d(n.cwd)||W,`${e}.cwd`),r=d(n.detect)||z,t=d(n.install)||q;if(r!=="auto"&&r!=="off")throw new Error(`${e}.detect must be "auto" or "off"`);if(t!=="require-host"&&t!=="download")throw new Error(`${e}.install must be "require-host" or "download"`);return{cwd:o,detect:r,install:t,node:d(n.node),npm:d(n.npm)}}async function J(n,e,o){const r=n.productDir,t=k(r,e.cwd,`${e.sourceLabel}.cwd`),i=K(t),s=e.node||(e.detect==="auto"?i.nodeSpec:null),a=e.node?"toolchain.node":e.detect==="auto"?i.nodeSource??"none":"none",l=e.npm||(e.detect==="auto"?i.npmSpec:null),c=e.npm?"toolchain.npm":e.detect==="auto"?i.npmSource??"none":"none";if(!s&&!l)throw new Error(`Service "${n.name}" enables runtime.toolchain but no Node/npm version could be resolved in ${e.cwd}`);return e.install==="require-host"?G({config:n,cwd:t,nodeSpec:s,nodeSource:a,npmSpec:l,npmSource:c,toolchain:e,options:o}):Q({config:n,cwd:t,nodeSpec:s,nodeSource:a,npmSpec:l,npmSource:c,toolchain:e,options:o})}function K(n){const e=m.join(n,"package.json"),o=F(e),r=Z(m.join(n,".tool-versions")),t=b(m.join(n,".nvmrc")),i=b(m.join(n,".node-version")),s=d(p(o,"volta","node")),a=d(p(o,"engines","node")),l=d(r.nodejs),c=s||t||i||l||a;let u=null;s?u="package.json#volta.node":t?u=".nvmrc":i?u=".node-version":l?u=".tool-versions#nodejs":a&&(u="package.json#engines.node");const E=d(p(o,"volta","npm")),$=d(p(o,"engines","npm")),T=Y(p(o,"packageManager")),C=E||(T?.name==="npm"?T.version:null)||$;let w=null;return E?w="package.json#volta.npm":T?.name==="npm"?w="package.json#packageManager":$&&(w="package.json#engines.npm"),{nodeSpec:c,nodeSource:u,npmSpec:C,npmSource:w}}async function G({config:n,cwd:e,nodeSpec:o,nodeSource:r,npmSpec:t,npmSource:i,toolchain:s,options:a}){const l=f(a.hostNodeVersion||process.versions.node);if(o&&!g(l,o))throw new Error(`Service "${n.name}" requires Node ${o} (${r}), but host Node is ${l}`);let c=null;if(t&&(c=f(a.hostNpmVersion||await X({command:"npm",args:["--version"],cwd:e,env:process.env,commandExecutor:a.commandExecutor,signal:a.signal})),!g(c,t)))throw new Error(`Service "${n.name}" requires npm ${t} (${i}), but host npm is ${c}`);return{install:s.install,cwd:s.cwd,nodeVersion:l,nodeSource:r,nodeSpec:o,npmVersion:c,npmSource:i,npmSpec:t,nodeExecutable:process.execPath,binDir:m.dirname(process.execPath),toolchainDir:m.dirname(process.execPath),fingerprint:S({install:s.install,nodeVersion:l,npmVersion:c,nodeSource:r,npmSource:i}),summary:`${s.install} node=${l}${c?` npm=${c}`:""} (${r})`}}async function Q({config:n,nodeSpec:e,nodeSource:o,npmSpec:r,npmSource:t,toolchain:i,options:s}){if(!e)throw new Error(`Service "${n.name}" download toolchain mode requires a resolvable Node version`);const a=L(e)?f(e):D(e);if(!a)throw new Error(`Service "${n.name}" cannot deterministically provision Node from "${e}". Provide an exact version in runtime.toolchain.node, .nvmrc, .node-version, .tool-versions, or package.json#volta.node.`);const l=await provisionNodeToolchain({productDir:n.productDir,version:a,fetchImpl:s.fetchImpl||fetchToolchain,commandExecutor:s.commandExecutor||executeToolchainCommand,signal:s.signal});if(!g(a,e))throw new Error(`Service "${n.name}" resolved Node ${a}, which does not satisfy ${e}`);const c=l.npmVersion?f(l.npmVersion):null;if(r&&(!c||!g(c,r)))throw new Error(`Service "${n.name}" requires npm ${r} (${t}), but Node ${a} bundles npm ${c||"unknown"}`);return{install:i.install,cwd:i.cwd,nodeVersion:a,nodeSource:o,nodeSpec:e,npmVersion:c,npmSource:t,npmSpec:r,nodeExecutable:l.nodeExecutable,binDir:l.binDir,toolchainDir:l.toolchainDir,fingerprint:S({install:i.install,nodeVersion:a,npmVersion:c,nodeSource:o,npmSource:t}),summary:`download node=${a}${c?` npm=${c}`:""} (${o})`}}export async function provisionNodeToolchain({productDir:n,version:e,fetchImpl:o=fetchToolchain,commandExecutor:r=executeToolchainCommand,signal:t}){const i=f(e);if(e!==i)throw new Error(`Downloaded Node toolchain version must use canonical x.y.z form; received ${JSON.stringify(e)}`);const s=j(i),{provisionNodeToolchainWithRelease:a}=await import("./node-provisioning.js");return a({commandExecutor:r,fetchImpl:o,productDir:n,release:s,signal:t})}function S(n){return _.createHash("sha256").update(JSON.stringify({kind:"node",...n})).digest("hex")}async function X({command:n,args:e,cwd:o,env:r,commandExecutor:t=executeToolchainCommand,signal:i}){const s=await t(n,e,{cwd:o,env:r,signal:i,stdio:"pipe",timeoutMs:O,windowsHide:!0});return s.stdout==null?"":typeof s.stdout=="string"?s.stdout.trim():Buffer.from(s.stdout).toString().trim()}function Y(n){const e=d(n);if(!e)return null;const o=e.lastIndexOf("@");return o<=0||o===e.length-1?null:{name:e.slice(0,o),version:e.slice(o+1)}}function b(n){if(!h.existsSync(n))return null;const o=h.readFileSync(n,"utf8").split(/\r?\n/).find(r=>r.trim().length>0);return o?o.trim():null}function Z(n){if(!h.existsSync(n))return{};const e={};for(const o of h.readFileSync(n,"utf8").split(/\r?\n/)){const r=o.trim();if(!r||r.startsWith("#"))continue;const[t,i]=r.split(/\s+/,2);t&&i&&(e[t]=i.trim())}return e}function ee(n,e){const o=d(n);if(!o)return e;const r=(e||"").split(m.delimiter).filter(Boolean).filter(t=>t!==o);return[o,...r].join(m.delimiter)}export async function fetchToolchain(n,e={}){const o=AbortSignal.timeout(U),r=await fetch(n,{signal:e.signal===void 0?o:AbortSignal.any([e.signal,o])});return{arrayBuffer:()=>r.arrayBuffer(),body:r.body===null?null:ne(r.body),ok:r.ok,status:r.status}}async function*ne(n){const e=n.getReader();let o=!1;try{for(;;){const r=await e.read();if(r.done){o=!0;return}yield r.value}}finally{o||await e.cancel(),e.releaseLock()}}export function prepareToolchainCommandForManagedHost(n,e,o={}){const r={env:o.env||process.env,platform:o.platform},t=A(n,r),i=I(t,e,r);return i.windowsVerbatimArguments?(oe(t,e),{executable:o.hostNodeExecutable||process.execPath,args:["--input-type=commonjs","--eval",B,"--",i.command,...i.args]}):{executable:i.command,args:i.args}}function oe(n,e){if(/[%!"\0\r\n]/u.test(n))throw new Error(`Windows batch toolchain commands cannot safely preserve command path ${JSON.stringify(n)}`);const o=e.find(r=>/[%!"\0\r\n]/u.test(r)||r.endsWith("\\"));if(o!==void 0)throw new Error(`Windows batch toolchain commands cannot safely preserve argument ${JSON.stringify(o)}`)}export async function executeToolchainCommand(n,e,o={}){const r=re(o.timeoutMs),t=m.resolve(o.cwd||process.cwd()),i=prepareToolchainCommandForManagedHost(n,e,{env:o.env||process.env}),s=await H({args:i.args,cwd:t,displayLabel:`Toolchain command ${n}`,env:o.env||process.env,executable:i.executable,signal:o.signal,timeoutMs:r}),a=M(`Toolchain command ${n}`,s);if(a)throw a;if(s.exitCode!==0)throw new Error(`Toolchain command ${n} exited with code ${String(s.exitCode)}${te(s.stdout,s.stderr)}`);return{stdout:s.stdout}}function re(n){const e=n??O;if(!Number.isSafeInteger(e)||e<=0)throw new Error("Toolchain command timeout must be a positive safe integer in milliseconds");return e}function te(n,e){const o=[n,e].map(r=>r.trim()).filter(Boolean).join(`
1
+ import _ from"crypto";import h from"fs";import m from"path";import{prepareCommandForSpawn as I,resolveCommandForSpawn as A}from"../process/host/platform.js";import{normalizeContainedRelativePath as k,resolveContainedPath as V}from"../process/files/contained-path.js";import{deriveSemverMinimum as D,isExactSemver as L,normalizeSemver as f,satisfiesSemver as g}from"./semver.js";import{managedHostProcessInfrastructureError as M,runManagedHostProcess as j}from"../process/index.js";import{managedNodeRelease as H}from"./managed-node-releases.js";import{createSharedToolchainOperation as P,waitForSharedToolchainOperation as R}from"./shared-operation.js";import{TOOLCHAIN_METADATA_TIMEOUT_MS as O}from"./timeouts.js";import{rejectUnknownKeys as F}from"../kernel/known-keys.js";import{isUnknownRecord as N,normalizeOptionalString as l}from"../kernel/unknown-values.js";import{readJsonFileOrNull as W}from"../process/files/json-file.js";const z=".",q="auto",U="require-host",B=600*1e3,K=['"use strict";','const { spawn } = require("node:child_process");',"const [command, ...args] = process.argv.slice(1);",'if (typeof command !== "string" || command.length === 0) {',' process.stderr.write("Missing Windows toolchain command\\n");'," process.exitCode = 126;","} else {"," let spawnFailed = false;"," const child = spawn(command, args, {"," shell: false,",' stdio: "inherit",'," windowsHide: true,"," windowsVerbatimArguments: true,"," });",' child.once("error", (error) => {'," spawnFailed = true;"," process.stderr.write(`${error.stack ?? String(error)}\\n`);"," process.exitCode = 126;"," });",' child.once("close", (code, signal) => {'," if (spawnFailed) return;"," if (signal !== null) {"," process.stderr.write(`Windows toolchain command exited on signal ${signal}\\n`);"," process.exitCode = 1;"," return;"," }"," process.exitCode = code ?? 1;"," });","}"].join(`
2
+ `),v=new WeakMap,y=new WeakSet;export function normalizeToolchainRegistry(n){if(n==null)return{};if(!N(n))throw new Error("testkit.config.ts toolchains must be an object");const e={};for(const[o,r]of Object.entries(n))e[o]=x(r,`testkit.config.ts toolchains.${o}`);return e}export function normalizeRuntimeToolchain(n,e,o={}){if(n==null)return null;if(typeof n=="string"){const t=n.trim();if(!t)throw new Error(`${e} must reference a non-empty toolchain name`);const i=o[t];if(!i)throw new Error(`${e} references unknown toolchain "${t}"`);return{...i,refName:t,sourceLabel:`${e} -> ${t}`}}return{...x(n,e),refName:null,sourceLabel:e}}export async function resolveConfiguredToolchain(n,e={}){const o=n?.testkit?.runtime?.toolchain;if(!n||!o)return null;let r=v.get(n);if(r===void 0){r=P(i=>J(n,o,{...e,signal:i})),v.set(n,r);const t=r;r.promise.catch(()=>{v.get(n)===t&&v.delete(n)})}return R(r,e.signal)}export function announceResolvedToolchain(n,e,o=null){return!e||y.has(n)||(y.add(n),o?.toolchainResolved&&o.toolchainResolved({serviceName:n.name,runtimeLabel:n.runtimeLabel||n.name},e.summary)),Promise.resolve()}export function applyToolchainEnv(n,e,o=process.env){if(!e)return{...n};const r={...n,PATH:ne(e.binDir,n.PATH||o.PATH||""),TESTKIT_TOOLCHAIN_KIND:"node",TESTKIT_TOOLCHAIN_NODE_VERSION:e.nodeVersion,TESTKIT_TOOLCHAIN_NODE_SOURCE:e.nodeSource,TESTKIT_TOOLCHAIN_INSTALL_MODE:e.install};return e.npmVersion&&(r.TESTKIT_TOOLCHAIN_NPM_VERSION=e.npmVersion,r.TESTKIT_TOOLCHAIN_NPM_SOURCE=e.npmSource),r}function x(n,e){if(!N(n))throw new Error(`${e} must be an object`);F(n,["cwd","detect","install","node","npm"],e);const o=k(l(n.cwd)||z,`${e}.cwd`),r=l(n.detect)||q,t=l(n.install)||U;if(r!=="auto"&&r!=="off")throw new Error(`${e}.detect must be "auto" or "off"`);if(t!=="require-host"&&t!=="download")throw new Error(`${e}.install must be "require-host" or "download"`);return{cwd:o,detect:r,install:t,node:l(n.node),npm:l(n.npm)}}async function J(n,e,o){const r=n.productDir,t=V(r,e.cwd,`${e.sourceLabel}.cwd`),i=G(t),s=e.node||(e.detect==="auto"?i.nodeSpec:null),a=e.node?"toolchain.node":e.detect==="auto"?i.nodeSource??"none":"none",d=e.npm||(e.detect==="auto"?i.npmSpec:null),c=e.npm?"toolchain.npm":e.detect==="auto"?i.npmSource??"none":"none";if(!s&&!d)throw new Error(`Service "${n.name}" enables runtime.toolchain but no Node/npm version could be resolved in ${e.cwd}`);return e.install==="require-host"?Q({config:n,cwd:t,nodeSpec:s,nodeSource:a,npmSpec:d,npmSource:c,toolchain:e,options:o}):X({config:n,cwd:t,nodeSpec:s,nodeSource:a,npmSpec:d,npmSource:c,toolchain:e,options:o})}function G(n){const e=m.join(n,"package.json"),o=W(e),r=ee(m.join(n,".tool-versions")),t=b(m.join(n,".nvmrc")),i=b(m.join(n,".node-version")),s=l(p(o,"volta","node")),a=l(p(o,"engines","node")),d=l(r.nodejs),c=s||t||i||d||a;let u=null;s?u="package.json#volta.node":t?u=".nvmrc":i?u=".node-version":d?u=".tool-versions#nodejs":a&&(u="package.json#engines.node");const E=l(p(o,"volta","npm")),$=l(p(o,"engines","npm")),T=Z(p(o,"packageManager")),C=E||(T?.name==="npm"?T.version:null)||$;let w=null;return E?w="package.json#volta.npm":T?.name==="npm"?w="package.json#packageManager":$&&(w="package.json#engines.npm"),{nodeSpec:c,nodeSource:u,npmSpec:C,npmSource:w}}async function Q({config:n,cwd:e,nodeSpec:o,nodeSource:r,npmSpec:t,npmSource:i,toolchain:s,options:a}){const d=f(a.hostNodeVersion||process.versions.node);if(o&&!g(d,o))throw new Error(`Service "${n.name}" requires Node ${o} (${r}), but host Node is ${d}`);let c=null;if(t&&(c=f(a.hostNpmVersion||await Y({command:"npm",args:["--version"],cwd:e,env:process.env,commandExecutor:a.commandExecutor,signal:a.signal})),!g(c,t)))throw new Error(`Service "${n.name}" requires npm ${t} (${i}), but host npm is ${c}`);return{install:s.install,cwd:s.cwd,nodeVersion:d,nodeSource:r,nodeSpec:o,npmVersion:c,npmSource:i,npmSpec:t,nodeExecutable:process.execPath,binDir:m.dirname(process.execPath),toolchainDir:m.dirname(process.execPath),fingerprint:S({install:s.install,nodeVersion:d,npmVersion:c,nodeSource:r,npmSource:i}),summary:`${s.install} node=${d}${c?` npm=${c}`:""} (${r})`}}async function X({config:n,nodeSpec:e,nodeSource:o,npmSpec:r,npmSource:t,toolchain:i,options:s}){if(!e)throw new Error(`Service "${n.name}" download toolchain mode requires a resolvable Node version`);const a=L(e)?f(e):D(e);if(!a)throw new Error(`Service "${n.name}" cannot deterministically provision Node from "${e}". Provide an exact version in runtime.toolchain.node, .nvmrc, .node-version, .tool-versions, or package.json#volta.node.`);const d=await provisionNodeToolchain({productDir:n.productDir,version:a,fetchImpl:s.fetchImpl||fetchToolchain,commandExecutor:s.commandExecutor||executeToolchainCommand,signal:s.signal});if(!g(a,e))throw new Error(`Service "${n.name}" resolved Node ${a}, which does not satisfy ${e}`);const c=d.npmVersion?f(d.npmVersion):null;if(r&&(!c||!g(c,r)))throw new Error(`Service "${n.name}" requires npm ${r} (${t}), but Node ${a} bundles npm ${c||"unknown"}`);return{install:i.install,cwd:i.cwd,nodeVersion:a,nodeSource:o,nodeSpec:e,npmVersion:c,npmSource:t,npmSpec:r,nodeExecutable:d.nodeExecutable,binDir:d.binDir,toolchainDir:d.toolchainDir,fingerprint:S({install:i.install,nodeVersion:a,npmVersion:c,nodeSource:o,npmSource:t}),summary:`download node=${a}${c?` npm=${c}`:""} (${o})`}}export async function provisionNodeToolchain({productDir:n,version:e,fetchImpl:o=fetchToolchain,commandExecutor:r=executeToolchainCommand,signal:t}){const i=f(e);if(e!==i)throw new Error(`Downloaded Node toolchain version must use canonical x.y.z form; received ${JSON.stringify(e)}`);const s=H(i),{provisionNodeToolchainWithRelease:a}=await import("./node-provisioning.js");return a({commandExecutor:r,fetchImpl:o,productDir:n,release:s,signal:t})}function S(n){return _.createHash("sha256").update(JSON.stringify({kind:"node",...n})).digest("hex")}async function Y({command:n,args:e,cwd:o,env:r,commandExecutor:t=executeToolchainCommand,signal:i}){const s=await t(n,e,{cwd:o,env:r,signal:i,stdio:"pipe",timeoutMs:O,windowsHide:!0});return s.stdout==null?"":typeof s.stdout=="string"?s.stdout.trim():Buffer.from(s.stdout).toString().trim()}function Z(n){const e=l(n);if(!e)return null;const o=e.lastIndexOf("@");return o<=0||o===e.length-1?null:{name:e.slice(0,o),version:e.slice(o+1)}}function b(n){if(!h.existsSync(n))return null;const o=h.readFileSync(n,"utf8").split(/\r?\n/).find(r=>r.trim().length>0);return o?o.trim():null}function ee(n){if(!h.existsSync(n))return{};const e={};for(const o of h.readFileSync(n,"utf8").split(/\r?\n/)){const r=o.trim();if(!r||r.startsWith("#"))continue;const[t,i]=r.split(/\s+/,2);t&&i&&(e[t]=i.trim())}return e}function ne(n,e){const o=l(n);if(!o)return e;const r=(e||"").split(m.delimiter).filter(Boolean).filter(t=>t!==o);return[o,...r].join(m.delimiter)}export async function fetchToolchain(n,e={}){const o=AbortSignal.timeout(B),r=await fetch(n,{signal:e.signal===void 0?o:AbortSignal.any([e.signal,o])});return{arrayBuffer:()=>r.arrayBuffer(),body:r.body===null?null:oe(r.body),ok:r.ok,status:r.status}}async function*oe(n){const e=n.getReader();let o=!1;try{for(;;){const r=await e.read();if(r.done){o=!0;return}yield r.value}}finally{o||await e.cancel(),e.releaseLock()}}export function prepareToolchainCommandForManagedHost(n,e,o={}){const r={env:o.env||process.env,platform:o.platform},t=A(n,r),i=I(t,e,r);return i.windowsVerbatimArguments?(re(t,e),{executable:o.hostNodeExecutable||process.execPath,args:["--input-type=commonjs","--eval",K,"--",i.command,...i.args]}):{executable:i.command,args:i.args}}function re(n,e){if(/[%!"\0\r\n]/u.test(n))throw new Error(`Windows batch toolchain commands cannot safely preserve command path ${JSON.stringify(n)}`);const o=e.find(r=>/[%!"\0\r\n]/u.test(r)||r.endsWith("\\"));if(o!==void 0)throw new Error(`Windows batch toolchain commands cannot safely preserve argument ${JSON.stringify(o)}`)}export async function executeToolchainCommand(n,e,o={}){const r=te(o.timeoutMs),t=m.resolve(o.cwd||process.cwd()),i=prepareToolchainCommandForManagedHost(n,e,{env:o.env||process.env}),s=await j({args:i.args,cwd:t,displayLabel:`Toolchain command ${n}`,env:o.env||process.env,executable:i.executable,signal:o.signal,timeoutMs:r}),a=M(`Toolchain command ${n}`,s);if(a)throw a;if(s.exitCode!==0)throw new Error(`Toolchain command ${n} exited with code ${String(s.exitCode)}${ie(s.stdout,s.stderr)}`);return{stdout:s.stdout}}function te(n){const e=n??O;if(!Number.isSafeInteger(e)||e<=0)throw new Error("Toolchain command timeout must be a positive safe integer in milliseconds");return e}function ie(n,e){const o=[n,e].map(r=>r.trim()).filter(Boolean).join(`
3
3
  `);return o?`
4
4
  ${o.slice(-4e3)}`:""}function p(n,...e){let o=n;for(const r of e){if(!N(o))return;o=o[r]}return o}
@@ -1 +1 @@
1
- import{isUnknownRecord as o}from"../../kernel/unknown-values.js";const s=["**/*.contract.testkit.ts","**/*.data.testkit.ts","**/*.ui.testkit.ts"];export function defineConfig(t={},r={}){const e=Reflect.get(t,"test"),n={...o(e)?e:{}},i=new Set([...c(n.exclude,"config.test.exclude"),...r.exclude??[],...s]);return{...t,test:{...n,exclude:[...i]}}}export function testkitExcludes(){return[...s]}function c(t,r){if(t===void 0)return[];if(!Array.isArray(t)||!t.every(e=>typeof e=="string"))throw new TypeError(`${r} must be an array of strings`);return t}
1
+ import{isUnknownRecord as i}from"../../kernel/unknown-values.js";const o=["**/*.ui.testkit.ts"];export function defineConfig(e={},r={}){const t=Reflect.get(e,"test"),n={...i(t)?t:{}},s=new Set([...c(n.exclude,"config.test.exclude"),...r.exclude??[],...o]);return{...e,test:{...n,exclude:[...s]}}}export function testkitExcludes(){return[...o]}function c(e,r){if(e===void 0)return[];if(!Array.isArray(e)||!e.every(t=>typeof t=="string"))throw new TypeError(`${r} must be an array of strings`);return e}
@@ -3,79 +3,6 @@ declare const RUNTIME_ISOLATION_POLICIES: readonly [
3
3
  "resetBefore"
4
4
  ];
5
5
  export type RuntimeIsolationPolicy = (typeof RUNTIME_ISOLATION_POLICIES)[number];
6
- export type RuntimeMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
7
- export interface RuntimeHeaders {
8
- [key: string]: string;
9
- }
10
- export interface RuntimeCookie {
11
- value: string;
12
- }
13
- export interface RuntimeEnv {
14
- BASE: string;
15
- rawEnv?: Record<string, string | undefined>;
16
- }
17
- export interface HttpRequestParams {
18
- [key: string]: unknown;
19
- headers?: RuntimeHeaders;
20
- }
21
- export type HttpProfileEnvironment = Readonly<Record<string, string | undefined>>;
22
- export interface HttpProfileEnv {
23
- [key: string]: unknown;
24
- BASE?: string;
25
- rawEnv?: HttpProfileEnvironment;
26
- }
27
- export type HttpProfileResolvedEnv = HttpProfileEnv & RuntimeEnv;
28
- export interface HttpProfileHeaderContext {
29
- actor?: string | null;
30
- env?: HttpProfileEnv;
31
- }
32
- export type HttpProfileHeaderBuilder<TSetup = unknown> = (setupData: TSetup | null, context?: HttpProfileHeaderContext & {
33
- env: HttpProfileResolvedEnv;
34
- }) => RuntimeHeaders | undefined;
35
- /** Response shape the in-process profile setup client resolves to. */
36
- export interface HttpProfileResponse {
37
- body: string;
38
- cookies?: Record<string, RuntimeCookie[] | undefined>;
39
- headers?: Record<string, string>;
40
- status: number;
41
- }
42
- export type HttpProfileRequestMethod = "del" | "get" | "patch" | "post" | "put";
43
- /** HTTP verbs an execution engine hands to a profile's setup step. */
44
- export type HttpProfileRequestClient<TResult = Promise<HttpProfileResponse>> = {
45
- [Method in HttpProfileRequestMethod]: Method extends "get" ? (url: string, params?: HttpRequestParams) => TResult : (url: string, body?: unknown, params?: HttpRequestParams) => TResult;
46
- };
47
- export interface HttpProfileSetupContext {
48
- env: HttpProfileResolvedEnv;
49
- http: HttpProfileRequestClient;
50
- json(response: unknown): unknown;
51
- }
52
- /**
53
- * Explicit, test-owned authentication for a named HTTP profile. The data
54
- * profile runs `setup` or `setupAsync` once and passes the
55
- * result to `headers` for every request.
56
- */
57
- export interface HttpProfileAuth<TSetup = unknown> {
58
- headers?: HttpProfileHeaderBuilder<TSetup>;
59
- setup?(context: HttpProfileSetupContext): TSetup;
60
- setupAsync?(context: HttpProfileSetupContext): Promise<TSetup>;
61
- }
62
- export interface HttpProfileConfig<TSetup = unknown> {
63
- [key: string]: unknown;
64
- auth?: HttpProfileAuth<TSetup> | null;
65
- baseUrl?: string;
66
- env?: HttpProfileEnv;
67
- headers?: HttpProfileHeaderBuilder<TSetup>;
68
- rawHeaders?: HttpProfileHeaderBuilder<TSetup>;
69
- }
70
- export interface RepoConfig {
71
- [key: string]: unknown;
72
- profiles?: {
73
- http?: Record<string, HttpProfileConfig>;
74
- };
75
- }
76
- export declare function registerRepoConfig(config: RepoConfig | null | undefined): void;
77
- export declare function getRepoConfig(): RepoConfig | null;
78
- export declare function clearRepoConfig(): void;
79
6
  export interface DatabaseBindingEnvConfig {
80
7
  prefix: string;
81
8
  service: string;
@@ -128,6 +55,7 @@ export interface StepsBuildConfigInput extends BuildConfigInputBase {
128
55
  steps?: ConfiguredStepInput[];
129
56
  }
130
57
  export type BuildConfigInput = TscBuildConfigInput | ScriptBuildConfigInput | NextBuildConfigInput | StepsBuildConfigInput;
58
+ export type RuntimeMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
131
59
  export type TemplateCommandStepConfig = ConfiguredCommandStepInput;
132
60
  export type TemplateSqlFileStepConfig = ConfiguredSqlFileStepInput;
133
61
  export type TemplateModuleStepConfig = ConfiguredModuleStepInput;
@@ -251,7 +179,7 @@ export interface RuntimeIsolationConfig {
251
179
  export interface TestkitExecutionConfig {
252
180
  fileTimeoutSeconds?: number;
253
181
  taskConcurrency?: number;
254
- /** @deprecated Use taskConcurrency. Removed in 0.3.0. */
182
+ /** @deprecated Use taskConcurrency. */
255
183
  workers?: number;
256
184
  }
257
185
  export interface FingerprintConfig {
@@ -267,7 +195,7 @@ export interface TestkitBoundaryLintRuleConfig extends TestkitLintRuleConfig {
267
195
  allowed?: string[];
268
196
  trackedOnly?: boolean;
269
197
  }
270
- export type TestkitLintRuleName = "engineImports" | "uiSpecShape" | "dataSuiteContract" | "configImports" | "testkitBoundary" | "unitTestBoundaries";
198
+ export type TestkitLintRuleName = "engineImports" | "uiSpecShape" | "configImports" | "testkitBoundary" | "unitTestBoundaries";
271
199
  export interface TestkitLintConfig {
272
200
  rules?: Partial<Record<Exclude<TestkitLintRuleName, "testkitBoundary">, boolean | TestkitLintRuleConfig>> & {
273
201
  testkitBoundary?: boolean | TestkitBoundaryLintRuleConfig;
@@ -313,9 +241,6 @@ export interface ServiceConfig {
313
241
  envFiles?: string[];
314
242
  local?: false | ServiceLocalConfig;
315
243
  runtime?: RuntimeConfig;
316
- runtimeIsolation?: RuntimeIsolationPolicy | RuntimeIsolationConfig;
317
- requirements?: ServiceRequirementConfig;
318
- skip?: SkipConfig;
319
244
  }
320
245
  export interface AppServiceConfig extends ServiceConfig {
321
246
  local: ServiceLocalConfig;
@@ -328,9 +253,6 @@ export interface PresetServiceOptions extends Record<string, unknown> {
328
253
  discovery?: DiscoveryConfig;
329
254
  envFile?: string;
330
255
  envFiles?: string[];
331
- runtimeIsolation?: RuntimeIsolationPolicy | RuntimeIsolationConfig;
332
- requirements?: ServiceRequirementConfig;
333
- skip?: SkipConfig;
334
256
  }
335
257
  export interface AppRuntimeOptions {
336
258
  instances?: number;
@@ -378,7 +300,7 @@ export interface NodeNextPresetOptions {
378
300
  node?: string;
379
301
  npm?: string;
380
302
  taskConcurrency?: number;
381
- /** @deprecated Use taskConcurrency. Removed in 0.3.0. */
303
+ /** @deprecated Use taskConcurrency. */
382
304
  workers?: number;
383
305
  }
384
306
  export interface AreaConfig {
@@ -408,15 +330,14 @@ export interface TestkitConfig {
408
330
  fingerprints?: FingerprintConfig;
409
331
  groups?: Record<string, GroupConfig>;
410
332
  lint?: TestkitLintConfig;
411
- profiles?: {
412
- http?: Record<string, HttpProfileConfig>;
413
- };
414
333
  redact?: string[];
415
334
  services?: Record<string, ServiceConfig>;
416
335
  toolchains?: Record<string, ToolchainConfig>;
417
336
  ui?: UiConfig;
418
337
  }
419
- export declare function defineConfig<const TConfig extends TestkitConfig>(config: TConfig): TConfig;
338
+ export declare function defineConfig<const TConfig extends TestkitConfig>(config: TConfig & {
339
+ readonly [Key in Exclude<keyof TConfig, keyof TestkitConfig>]: never;
340
+ }): TConfig;
420
341
  declare function commandStep(run: string, options?: ConfiguredStepBaseInput): ConfiguredCommandStepInput;
421
342
  declare function sqlFileStep(filePath: string, options?: ConfiguredStepBaseInput): ConfiguredSqlFileStepInput;
422
343
  declare function moduleStep(target: string, options?: ConfiguredStepBaseInput): ConfiguredModuleStepInput;
@@ -1,3 +1,8 @@
1
+ export type JsonPrimitive = boolean | null | number | string;
2
+ export type JsonValue = JsonPrimitive | readonly JsonValue[] | JsonObject;
3
+ export interface JsonObject {
4
+ readonly [key: string]: JsonValue;
5
+ }
1
6
  export interface CapturedRequest {
2
7
  readonly bodyBytes: number;
3
8
  readonly bodyText: string;
@@ -36,5 +41,6 @@ export declare class EnvironmentValueMissingError extends Error {
36
41
  export declare function endpoint(name: string): string;
37
42
  export declare function database(name: string): string;
38
43
  export declare function handle(name: string): EnvironmentHandle;
44
+ export declare function setupData(): JsonValue;
39
45
 
40
46
  export {};
@@ -1,78 +1,10 @@
1
- import { TestContext, expect } from 'vitest';
1
+ import { TestContext } from 'vitest';
2
2
 
3
- export type RuntimeMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
- export interface RuntimeHeaders {
5
- [key: string]: string;
6
- }
7
- export interface RuntimeCookie {
8
- value: string;
9
- }
10
- export interface RuntimeEnv {
11
- BASE: string;
12
- rawEnv?: Record<string, string | undefined>;
13
- }
14
- export interface HttpRequestParams {
15
- [key: string]: unknown;
16
- headers?: RuntimeHeaders;
17
- }
18
- export type HttpProfileEnvironment = Readonly<Record<string, string | undefined>>;
19
- export interface HttpProfileEnv {
20
- [key: string]: unknown;
21
- BASE?: string;
22
- rawEnv?: HttpProfileEnvironment;
23
- }
24
- export type HttpProfileResolvedEnv = HttpProfileEnv & RuntimeEnv;
25
- export interface HttpProfileHeaderContext {
26
- actor?: string | null;
27
- env?: HttpProfileEnv;
28
- }
29
- export type HttpProfileHeaderBuilder<TSetup = unknown> = (setupData: TSetup | null, context?: HttpProfileHeaderContext & {
30
- env: HttpProfileResolvedEnv;
31
- }) => RuntimeHeaders | undefined;
32
- /** Response shape the in-process profile setup client resolves to. */
33
- export interface HttpProfileResponse {
34
- body: string;
35
- cookies?: Record<string, RuntimeCookie[] | undefined>;
36
- headers?: Record<string, string>;
37
- status: number;
38
- }
39
- export type HttpProfileRequestMethod = "del" | "get" | "patch" | "post" | "put";
40
- /** HTTP verbs an execution engine hands to a profile's setup step. */
41
- export type HttpProfileRequestClient<TResult = Promise<HttpProfileResponse>> = {
42
- [Method in HttpProfileRequestMethod]: Method extends "get" ? (url: string, params?: HttpRequestParams) => TResult : (url: string, body?: unknown, params?: HttpRequestParams) => TResult;
43
- };
44
- export interface HttpProfileSetupContext {
45
- env: HttpProfileResolvedEnv;
46
- http: HttpProfileRequestClient;
47
- json(response: unknown): unknown;
48
- }
49
- /**
50
- * Explicit, test-owned authentication for a named HTTP profile. The data
51
- * profile runs `setup` or `setupAsync` once and passes the
52
- * result to `headers` for every request.
53
- */
54
- export interface HttpProfileAuth<TSetup = unknown> {
55
- headers?: HttpProfileHeaderBuilder<TSetup>;
56
- setup?(context: HttpProfileSetupContext): TSetup;
57
- setupAsync?(context: HttpProfileSetupContext): Promise<TSetup>;
58
- }
59
- export interface HttpMethods<Response, Extra extends unknown[]> {
60
- get: (path: string, ...extra: Extra) => Response;
61
- delete: (path: string, body?: unknown, ...extra: Extra) => Response;
62
- post: (path: string, body?: unknown, ...extra: Extra) => Response;
63
- put: (path: string, body?: unknown, ...extra: Extra) => Response;
64
- patch: (path: string, body?: unknown, ...extra: Extra) => Response;
65
- }
66
3
  declare const DATA_ARTIFACT_KIND = "testkit.data-flow";
67
4
  declare const DATA_SCHEMA_VERSION = 1;
68
5
  export type DataFindingSeverity = "error" | "info" | "waste" | "watch";
69
6
  export type DataMetricBudgetOperator = "<" | "<=" | "==" | ">" | ">=" | "exists";
70
7
  export type DataSequenceBudgetOperator = "exists" | "sequence==" | "sequenceOnly";
71
- export interface DataScenarioOptions {
72
- description?: string;
73
- timeout?: number;
74
- timeoutMs?: number;
75
- }
76
8
  export interface DataMetricOptions {
77
9
  description?: string;
78
10
  dimensions?: Record<string, unknown>;
@@ -102,136 +34,21 @@ export interface DataSequenceBudget {
102
34
  includesOnly(values: readonly unknown[], options?: DataBudgetOptions): this;
103
35
  required(options?: DataBudgetOptions): this;
104
36
  }
105
- export interface DataFixture {
106
- buffer(): Buffer;
107
- contentType: string;
108
- filename: string;
109
- json(): unknown;
110
- path: string;
111
- sha256: string;
112
- sizeBytes: number;
113
- text(encoding?: BufferEncoding): string;
114
- }
115
- export interface DataHttpResponse {
116
- body: string;
117
- cookies: Record<string, Array<{
118
- value: string;
119
- }>>;
120
- durationMs: number;
121
- headers: Record<string, string>;
122
- json(): unknown;
123
- method: string;
124
- path: string;
125
- status: number;
126
- url: string;
127
- }
128
- export interface DataHttpRequestOptions {
129
- contentTypeJson?: boolean;
130
- signal?: AbortSignal;
131
- }
132
- export interface DataActorSession {
133
- [key: string]: unknown;
134
- actorIndex?: number;
135
- email?: string | null;
136
- name?: string | null;
137
- organizationKey?: string | null;
138
- organizationName?: string | null;
139
- session?: Record<string, unknown> | null;
140
- }
141
- export interface DataSessionBundle {
142
- actors: Record<string, DataActorSession | undefined>;
143
- primaryActor: string | null;
144
- }
145
- export interface DataHttpHeaderContext {
146
- actor: DataActorSession | null;
147
- actorName: string | null;
148
- sessionBundle: DataSessionBundle | null;
149
- }
150
- export type DataHttpHeaderBuilder = (context: DataHttpHeaderContext) => Record<string, string>;
151
- export interface DataHttpClientOptions {
152
- baseUrl?: string;
153
- defaultActor?: string;
154
- defaultHeaders?: Record<string, string>;
155
- getHeaders?: DataHttpHeaderBuilder;
156
- getRawHeaders?: DataHttpHeaderBuilder;
157
- profile?: string;
158
- recorder?: DataHttpRecorder;
159
- sessionBundle?: DataSessionBundle;
160
- trace?: DataHttpRecorder;
161
- }
162
- export interface DataHttpRequest {
163
- (method: string, path: string, body?: unknown, headers?: Record<string, string>, options?: DataHttpRequestOptions): Promise<DataHttpResponse>;
164
- }
165
- export type DataActorRawRequest = DataHttpRequest & HttpMethods<Promise<DataHttpResponse>, [
166
- headers?: Record<string, string>,
167
- options?: DataHttpRequestOptions
168
- ]>;
169
- export interface DataRootRawRequest extends DataHttpRequest, Pick<HttpMethods<Promise<DataHttpResponse>, [
170
- headers?: Record<string, string>,
171
- options?: DataHttpRequestOptions
172
- ]>, "get" | "post"> {
173
- as(actorName: string): DataActorRawRequest;
174
- }
175
- export interface DataHttpMethods extends HttpMethods<Promise<DataHttpResponse>, [
176
- headers?: Record<string, string>,
177
- options?: DataHttpRequestOptions
178
- ]> {
179
- headers(extraHeaders?: Record<string, string>): Record<string, string>;
180
- rawHeaders(extraHeaders?: Record<string, string>): Record<string, string>;
181
- request(method: string, path: string, body?: unknown, headers?: Record<string, string>, options?: DataHttpRequestOptions): Promise<DataHttpResponse>;
182
- }
183
- export interface DataActorHttpClient extends DataHttpMethods {
184
- raw: DataActorRawRequest;
185
- rawReq: DataActorRawRequest;
186
- }
187
- export interface DataHttpActor {
188
- email: string | null;
189
- headers: Record<string, string>;
190
- index: number;
191
- key: string;
192
- name: string | null;
193
- organizationKey: string | null;
194
- organizationName: string | null;
195
- rawHeaders: Record<string, string>;
196
- rawReq: DataActorRawRequest;
197
- req: DataActorHttpClient;
198
- session: Record<string, unknown> | null;
199
- }
200
- export interface DataHttpActors {
201
- get(name: string): DataHttpActor;
202
- has(name: string): boolean;
203
- list(): DataHttpActor[];
204
- names: string[];
205
- primary: DataHttpActor | null;
206
- }
207
- export interface DataHttpClient extends DataHttpMethods {
208
- actor?: DataHttpActor | null;
209
- actors?: DataHttpActors;
210
- as(actorName: string): DataActorHttpClient;
211
- baseUrl: string;
212
- env?: HttpProfileEnv;
213
- raw: DataRootRawRequest;
214
- rawReq: DataRootRawRequest;
215
- }
216
37
  export interface DataHttpRequestRecord {
217
38
  bodyBytes?: number;
218
39
  durationMs?: number;
219
40
  method: string;
220
41
  path: string;
221
- requestHeaders?: Record<string, string>;
222
42
  responseBytes?: number;
223
43
  status?: number;
224
44
  }
225
- export interface DataHttpRecorder {
226
- recordHttpRequest(entry: DataHttpRequestRecord): unknown;
227
- }
228
45
  export interface DataExternalCallDetails extends DataEvidenceDetails {
229
46
  costMinor?: number;
230
47
  durationMs?: number;
231
48
  requestBytes?: number;
232
49
  responseBytes?: number;
233
50
  }
234
- export interface DataFlow extends DataHttpRecorder {
51
+ export interface DataFlow {
235
52
  addFinding(severity: string, message: string, details?: Record<string, unknown>): this;
236
53
  actor(name: string, details?: DataEvidenceDetails): this;
237
54
  budget(metricName: string): DataMetricBudget;
@@ -268,30 +85,6 @@ export interface DataFlow extends DataHttpRecorder {
268
85
  system(name: string, details?: DataEvidenceDetails): this;
269
86
  transition(name: string, details?: DataEvidenceDetails): this;
270
87
  }
271
- export interface DataScenarioContext extends DataFlow, DataRuntimeContext {
272
- fixture(name: string): DataFixture;
273
- http(options?: DataHttpProfileOptions): Promise<DataHttpClient>;
274
- }
275
- export interface DataRuntimeContext extends Record<string, unknown> {
276
- baseUrl?: string | null;
277
- engineId?: "node-vitest";
278
- readonly env: Readonly<Record<string, string>>;
279
- profileId?: "node-vitest-data";
280
- serviceName?: string;
281
- task?: {
282
- file?: string;
283
- id?: number | string;
284
- suiteName?: string;
285
- testType: "data";
286
- };
287
- }
288
- export type DataHttpProfileAuth = HttpProfileAuth<DataSessionBundle>;
289
- export interface DataHttpProfileOptions extends DataHttpClientOptions {
290
- auth?: DataHttpProfileAuth | null;
291
- env?: HttpProfileEnv;
292
- headers?: HttpProfileHeaderBuilder<DataSessionBundle>;
293
- rawHeaders?: HttpProfileHeaderBuilder<DataSessionBundle>;
294
- }
295
88
  export interface DataMetric {
296
89
  aggregation?: string;
297
90
  description: string | null;
@@ -358,7 +151,6 @@ export type DataBudgetResult = (Omit<DataMetricBudgetInput, "expected" | "messag
358
151
  export interface DataScenarioReport {
359
152
  budgetFailures: DataBudgetResult[];
360
153
  budgets: DataBudgetResult[];
361
- description: string | null;
362
154
  durationMs: number;
363
155
  error: SerializedDataError | null;
364
156
  events: DataEvent[];
@@ -391,17 +183,9 @@ export interface DataFlowReport {
391
183
  summaryText: string;
392
184
  taskId: string | null;
393
185
  }
394
- export declare function createHttpClient(options?: DataHttpClientOptions): DataHttpClient;
395
- export declare function readDataContext(): DataRuntimeContext;
396
186
  export declare function renderDataFlowMarkdown(report: DataFlowReport): string;
397
- export declare function dataScenario(name: string, run: (context: DataScenarioContext) => unknown): void;
398
- export declare function dataScenario(name: string, options: DataScenarioOptions, run: (context: DataScenarioContext) => unknown): void;
399
187
  export declare function dataFlow(context: TestContext): DataFlow;
400
188
  export declare function measureJsonBytes(value: unknown): number;
401
189
  export declare function stableHash(value: unknown): string;
402
190
 
403
- export {
404
- expect,
405
- };
406
-
407
191
  export {};