@gfdelarue/testkit 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +590 -165
  2. package/bin/testkit-windows-owner-arm64.exe +0 -0
  3. package/bin/testkit-windows-owner-x64.exe +0 -0
  4. package/dist/app/bugs/batch.js +1 -0
  5. package/dist/app/bugs/ci-report.js +2 -2
  6. package/dist/app/bugs/ci-summary.js +3 -3
  7. package/dist/app/bugs/draft.js +2 -2
  8. package/dist/app/bugs/file.js +1 -1
  9. package/dist/app/bugs/guide.js +1 -1
  10. package/dist/app/bugs/record.js +2 -2
  11. package/dist/app/bugs/run-reading.js +1 -1
  12. package/dist/app/bugs/store.js +1 -1
  13. package/dist/app/campaigns/index.js +2 -2
  14. package/dist/app/campaigns/summary.js +1 -1
  15. package/dist/app/cleanup/index.js +1 -1
  16. package/dist/app/db-schema/prepare.js +1 -1
  17. package/dist/app/db-schema/refresh.js +1 -1
  18. package/dist/app/db-schema/verify.js +1 -1
  19. package/dist/app/destroy/index.js +1 -1
  20. package/dist/app/discover/index.js +2 -2
  21. package/dist/app/environments/config-driven.js +1 -1
  22. package/dist/app/environments/file-setup.js +1 -1
  23. package/dist/app/environments/routing.js +1 -1
  24. package/dist/app/environments/static-values.js +1 -1
  25. package/dist/app/environments/supplied.js +1 -1
  26. package/dist/app/inspect/index.js +1 -1
  27. package/dist/app/lint/native.js +1 -1
  28. package/dist/app/lint/options.js +1 -1
  29. package/dist/app/lint/shared.js +1 -1
  30. package/dist/app/run/engine-orchestration.js +1 -1
  31. package/dist/app/run/environment.js +1 -1
  32. package/dist/app/run/live-run.js +1 -1
  33. package/dist/app/run/orchestrator.js +1 -1
  34. package/dist/app/run/planning.js +1 -1
  35. package/dist/app/run/request.js +1 -1
  36. package/dist/app/run/results.js +1 -1
  37. package/dist/app/run/run-finalization.js +1 -1
  38. package/dist/app/run/run-finisher.js +1 -0
  39. package/dist/app/run/setup/runtime-node-setup-protocol.js +1 -1
  40. package/dist/app/run/setup/runtime-node-setup-worker.js +1 -1
  41. package/dist/app/run/setup/runtime-node-setup.js +2 -2
  42. package/dist/app/run/task-batches.js +1 -1
  43. package/dist/app/run/worker-loop.js +1 -1
  44. package/dist/app/status/model.js +1 -1
  45. package/dist/app/status/runs.js +1 -1
  46. package/dist/app/stop/index.js +1 -1
  47. package/dist/app/tracker/index.js +1 -1
  48. package/dist/app/tracker/token.js +2 -2
  49. package/dist/app/typecheck/index.js +4 -4
  50. package/dist/cli/command-catalog.js +1 -1
  51. package/dist/cli/command-flags.js +1 -1
  52. package/dist/cli/commands/artifacts.js +2 -2
  53. package/dist/cli/commands/bug/file.js +3 -2
  54. package/dist/cli/commands/discover.js +1 -1
  55. package/dist/cli/commands/guard/unsafe-entrypoint.js +1 -1
  56. package/dist/cli/commands/run.js +2 -2
  57. package/dist/cli/commands/typecheck.js +1 -1
  58. package/dist/cli/commands/workspace.js +1 -1
  59. package/dist/cli/framework/help.js +5 -5
  60. package/dist/cli/framework/parser.js +1 -1
  61. package/dist/cli/json-errors.js +1 -1
  62. package/dist/cli/renderers/bug/text.js +1 -1
  63. package/dist/cli/renderers/run/formatting.js +1 -1
  64. package/dist/config/authoring/database-fixture-discovery.js +1 -0
  65. package/dist/config/authoring/index.js +1 -1
  66. package/dist/config/loader/config-loader.js +1 -1
  67. package/dist/config/loader/decode-discovery.js +1 -1
  68. package/dist/config/loader/discover-tests.js +1 -1
  69. package/dist/config/loader/discovery-result.js +1 -0
  70. package/dist/config/loader/discovery.js +2 -2
  71. package/dist/config/loader/file-metadata.js +1 -1
  72. package/dist/config/loader/groups.js +2 -1
  73. package/dist/config/loader/index.js +1 -1
  74. package/dist/config/loader/load.js +1 -1
  75. package/dist/config/loader/managed-configs.js +1 -1
  76. package/dist/config/loader/owned-discovery.js +1 -1
  77. package/dist/config/loader/runtime-isolation.js +1 -1
  78. package/dist/config/loader/skip-config.js +1 -1
  79. package/dist/config/loader/test-selection.js +1 -1
  80. package/dist/database/admin.js +5 -2
  81. package/dist/database/call-caps.js +1 -0
  82. package/dist/database/cleanup.js +1 -1
  83. package/dist/database/configured-steps.js +1 -1
  84. package/dist/database/constants.js +1 -1
  85. package/dist/database/docker-endpoint.js +1 -0
  86. package/dist/database/docker-metadata.js +2 -2
  87. package/dist/database/docker.js +5 -5
  88. package/dist/database/index.js +2 -1
  89. package/dist/database/lifecycle-cleanup.js +1 -1
  90. package/dist/database/local-postgres.js +4 -1
  91. package/dist/database/naming.js +1 -1
  92. package/dist/database/operation-deadline.js +1 -0
  93. package/dist/database/postgres/connection-check.js +1 -1
  94. package/dist/database/postgres/index.js +2 -2
  95. package/dist/database/postgres/owned-exec.js +27 -0
  96. package/dist/database/postgres/protocol.js +2 -2
  97. package/dist/database/provider.js +1 -0
  98. package/dist/database/roles.js +2 -2
  99. package/dist/database/schema-operations.js +5 -1
  100. package/dist/database/schema-source.js +3 -3
  101. package/dist/database/steps.js +1 -1
  102. package/dist/database/template-steps.js +4 -4
  103. package/dist/domain/identifiers.js +1 -1
  104. package/dist/domain/retired-test-types.js +1 -1
  105. package/dist/domain/suite-selection.js +1 -1
  106. package/dist/domain/test-types.js +1 -1
  107. package/dist/engines/adapters/evidence-files.js +1 -1
  108. package/dist/engines/adapters/execution.js +1 -1
  109. package/dist/engines/catalog.js +1 -1
  110. package/dist/engines/definitions.js +1 -1
  111. package/dist/engines/environment/index.js +1 -1
  112. package/dist/engines/installations/node-vitest.js +1 -1
  113. package/dist/engines/installations/playwright.js +1 -1
  114. package/dist/engines/managed-module-resolution.js +6 -2
  115. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  116. package/dist/engines/node-vitest/adapter/driver.js +2 -2
  117. package/dist/engines/node-vitest/adapter/evidence.js +2 -2
  118. package/dist/engines/node-vitest/adapter/workspace.js +8 -24
  119. package/dist/engines/node-vitest/inventory/index.js +2 -2
  120. package/dist/engines/node-vitest/inventory/list-files.js +3 -3
  121. package/dist/engines/node-vitest/runtime/data/index.js +1 -1
  122. package/dist/engines/node-vitest/runtime/data/report.js +1 -1
  123. package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
  124. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  125. package/dist/engines/playwright/adapter/adapter.js +1 -1
  126. package/dist/engines/playwright/adapter/browser-network-evidence.js +4 -0
  127. package/dist/engines/playwright/adapter/config.js +16 -6
  128. package/dist/engines/playwright/adapter/evidence.js +2 -1
  129. package/dist/engines/playwright/adapter/failure-details.js +2 -2
  130. package/dist/engines/playwright/adapter/reporter.js +1 -1
  131. package/dist/engines/playwright/runtime/browser-network.js +1 -0
  132. package/dist/engines/playwright/runtime/browser-traffic.js +1 -0
  133. package/dist/engines/playwright/runtime/chromium-environment.js +1 -0
  134. package/dist/engines/playwright/runtime/emulation.js +1 -0
  135. package/dist/engines/playwright/runtime/index.js +1 -1
  136. package/dist/engines/playwright/runtime/managed-test.js +1 -0
  137. package/dist/engines/playwright/runtime/runtime-input.js +2 -2
  138. package/dist/environment/lifecycle-controller.js +1 -1
  139. package/dist/environment/lifecycle.js +1 -1
  140. package/dist/environment/maintenance.js +1 -1
  141. package/dist/environment/run-environment.js +1 -1
  142. package/dist/environment/run-stopping.js +1 -1
  143. package/dist/environment/runtime-manager.js +1 -1
  144. package/dist/evidence/artifact-config.js +1 -1
  145. package/dist/evidence/artifacts.js +5 -2
  146. package/dist/evidence/bundles.js +2 -1
  147. package/dist/evidence/compressed-artifacts.js +1 -0
  148. package/dist/evidence/environment-record.js +1 -1
  149. package/dist/evidence/index.js +1 -1
  150. package/dist/evidence/live-run-summary.js +1 -0
  151. package/dist/evidence/local-paths.js +1 -0
  152. package/dist/evidence/maintenance.js +1 -0
  153. package/dist/evidence/operation-timings.js +1 -1
  154. package/dist/evidence/read-run.js +1 -1
  155. package/dist/evidence/redaction.js +4 -4
  156. package/dist/evidence/run-evidence.js +1 -0
  157. package/dist/evidence/setup-progress.js +1 -1
  158. package/dist/evidence/storage-inventory.js +1 -1
  159. package/dist/evidence/storage.js +2 -2
  160. package/dist/kernel/known-keys.js +1 -0
  161. package/dist/kernel/redaction-targets.js +1 -1
  162. package/dist/kernel/redaction.js +7 -4
  163. package/dist/process/engine-environment-keys.js +1 -0
  164. package/dist/process/environment-overlay.js +1 -1
  165. package/dist/process/environment.js +1 -1
  166. package/dist/process/execa-bundle.json +3 -3
  167. package/dist/process/files/evidence-writes.js +1 -0
  168. package/dist/process/files/product-dir.js +1 -1
  169. package/dist/process/host-process.js +1 -1
  170. package/dist/process/index.js +1 -1
  171. package/dist/process/lifecycle-fence.js +1 -1
  172. package/dist/process/lock-progress.js +2 -0
  173. package/dist/process/locks.js +4 -4
  174. package/dist/process/process-identity.js +1 -1
  175. package/dist/process/process.js +35 -35
  176. package/dist/process/recovery.js +1 -1
  177. package/dist/process/runs/manifest.js +1 -1
  178. package/dist/process/runs/ownership.js +1 -1
  179. package/dist/process/runs/stop.js +1 -1
  180. package/dist/process/shutdown.js +1 -1
  181. package/dist/process/windows-native.js +1 -1
  182. package/dist/repo/fingerprint-policy.js +1 -1
  183. package/dist/repo/history.js +7 -4
  184. package/dist/repo/managed-git.js +1 -1
  185. package/dist/repo/state.js +4 -4
  186. package/dist/steps/configured-step-testkit-modules.js +1 -1
  187. package/dist/steps/environment.js +1 -1
  188. package/dist/toolchains/index.js +2 -2
  189. package/dist/toolchains/node-provisioning.js +2 -2
  190. package/dist/toolchains/vitest/index.js +1 -1
  191. package/dist/tracker/gitea.js +1 -1
  192. package/dist/tracker/github.js +1 -1
  193. package/dist/tracker/http.js +1 -1
  194. package/dist/tracker/ownership.js +1 -0
  195. package/dist/tracker/redaction.js +1 -1
  196. package/dist/tracker/responses.js +1 -1
  197. package/dist/types/config.d.ts +7 -86
  198. package/dist/types/environment.d.ts +6 -0
  199. package/dist/types/node-vitest-data.d.ts +2 -218
  200. package/dist/types/playwright.d.ts +21 -0
  201. package/dist/types/workspace.d.ts +30 -14
  202. package/docs/bug-records.md +19 -18
  203. package/docs/engines.md +115 -21
  204. package/docs/release-notes.md +2846 -0
  205. package/docs/test-types.md +19 -69
  206. package/package.json +15 -6
  207. package/testkit-config-schema.md +101 -170
  208. package/dist/app/lint/rules/data.js +0 -1
  209. package/dist/config/authoring/runtime.js +0 -1
  210. package/dist/config/loader/task-policy.js +0 -1
  211. package/dist/engines/node-vitest/runtime/contract/index.js +0 -1
  212. package/dist/engines/node-vitest/runtime/data/http.js +0 -1
  213. package/dist/engines/node-vitest/runtime/data/runner.js +0 -1
  214. package/dist/environment/lifecycle-artifacts.js +0 -1
  215. package/dist/types/node-vitest-contract.d.ts +0 -75
@@ -1 +1 @@
1
- import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as R}from"./package-requirements.js";export{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS}from"./package-requirements.js";import c from"node:fs";import y from"node:os";import f from"node:path";import{fileURLToPath as m}from"node:url";import{runManagedProcess as x}from"../../process/index.js";import{currentLinuxLibc as T,currentPlatformTarget as _,platformTargetKey as d,SUPPORTED_PLATFORM_TARGETS as I}from"../../process/host/platform.js";import{RootPackageResolver as h}from"./package-resolution.js";import{isUnknownRecord as l}from"../../kernel/unknown-values.js";const O=64*1024,S=6e4,b="PLAYWRIGHT_BROWSERS_PATH",A=new Set(["npm_config_playwright_browsers_path","npm_package_config_playwright_browsers_path"]);export class PlaywrightInstallationError extends Error{code="TESTKIT_PLAYWRIGHT_INSTALLATION_INVALID";constructor(r,t){super(`Playwright installation is invalid: ${r}. Run testkit setup playwright --force.`,t),this.name="PlaywrightInstallationError"}}export class UnsupportedPlaywrightPlatformError extends Error{code="TESTKIT_PLAYWRIGHT_PLATFORM_UNSUPPORTED";constructor(r,t){super(`Playwright does not support ${d(r)}${r.platform==="linux"?` with ${t??"unknown"} libc`:""}`),this.name="UnsupportedPlaywrightPlatformError"}}export function resolvePlaywrightPackageFamily(e=new h){const[r,t,o]=R,i=e.resolveRequireFromRoot(u(r)),n=e.resolveImportFromRoot(u(r));L(e,i,n);const a=e.resolveRequireFromPackage(i,u(t)),s=e.resolveRequireFromPackage(a,u(o)),P=e.resolveRequirePublicEntrypoint(i,r.publicEntrypoints[1]);return Object.freeze({cli:P,playwright:p(a,"playwright"),playwrightCore:p(s,"playwright-core"),playwrightTest:p(n,"@playwright/test")})}function L(e,r,t){if(!(r.name===t.name&&r.version===t.version&&r.root===t.root))throw e.brokenInstallation(r,"the import and require public entrypoints select different physical package roots",void 0)}export function resolvePlaywrightRegistry(e){const r=e.platform==="win32"?f.win32:f.posix;if(!r.isAbsolute(e.productRoot))throw new TypeError("Playwright product root must be absolute");const t=e.configuredValue;if(t==="0")return Object.freeze({canonicalValue:"0",mode:"hermetic"});if(t!==void 0&&t.length>0){const o=r.isAbsolute(t);return Object.freeze({canonicalValue:o?r.normalize(t):r.resolve(e.productRoot,t),mode:o?"configured-absolute":"configured-relative"})}return Object.freeze({canonicalValue:r.resolve(e.productRoot,r.join(C(e.platform,e.ambientEnvironment,e.homeDirectory??y.homedir(),r),"ms-playwright")),mode:"default"})}export function playwrightRegistryEnvironment(e,r){const t={};for(const[o,i]of Object.entries(e))typeof i=="string"&&!A.has(o.toLowerCase())&&o.toUpperCase()!==b&&(t[o]=i);return t[b]=r.canonicalValue,Object.freeze(t)}export async function resolvePlaywrightInstallation(e,r={}){e.signal.throwIfAborted();const t={linuxLibc:T(),target:_()};requireSupportedPlaywrightHost(t);const o=new h,i=r.ambientEnvironment??process.env,n=resolvePlaywrightPackageFamily(o),a=resolvePlaywrightRegistry({ambientEnvironment:i,configuredValue:r.configuredRegistry??i.PLAYWRIGHT_BROWSERS_PATH,homeDirectory:y.homedir(),platform:t.target.platform,productRoot:e.productRoot}),s=await v(e,{family:n,registry:a},i);return Object.freeze({authoringEntrypoints:Object.freeze({authoring:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/playwright"),environment:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/environment"),root:o.resolveRootPublicEntrypoint("@gfdelarue/testkit"),rootPackageJson:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/package.json")}),browserExecutable:requireBrowserExecutable(s.browserExecutable,t.target),engineId:"playwright",family:n,registry:a})}async function v(e,r,t){const o=Buffer.from(JSON.stringify(r),"utf8").toString("base64url"),i=await x({args:[N(),o],cwd:e.productRoot,displayLabel:"Playwright installation resolver",env:playwrightRegistryEnvironment(t,r.registry),executable:process.execPath,signal:e.signal,timeoutMs:S}),n=j(i);throwIfPlaywrightProcessAborted(e.signal,i,n),requireBoundedPlaywrightChildOutput(i.stdout);let a;try{a=parsePlaywrightChildOutput(i.stdout)}catch(s){throw n!==null?new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n}):s}if(n!==null)throw new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n});if(!a.ok){if(i.exitCode!==1||i.exitSignal!==null){const s=E(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}throw new PlaywrightInstallationError(`isolated resolver failed: ${a.error.message}`,{cause:M(a.error)})}if(i.exitCode!==0||i.exitSignal!==null){const s=E(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}return requireMatchingPlaywrightChildRequest(a,r),a}export function throwIfPlaywrightProcessAborted(e,r,t){if(e.aborted){if(!k(r)||t===null){e.throwIfAborted();return}throw t===e.reason?t:new AggregateError([e.reason,t],"Playwright process was cancelled and process cleanup also failed",{cause:e.reason})}}function k(e){return e.terminationError!==null||e.error!==null||e.timedOut||e.outputLimitExceeded}export function requireBoundedPlaywrightChildOutput(e){if(Buffer.byteLength(e,"utf8")>O)throw new PlaywrightInstallationError("isolated resolver returned oversized output")}export function parsePlaywrightChildOutput(e){let r;try{r=JSON.parse(e)}catch(t){throw new PlaywrightInstallationError("isolated resolver did not return bounded JSON",{cause:t})}if(!l(r)||typeof r.ok!="boolean")throw new PlaywrightInstallationError("isolated resolver returned an invalid response");if(!r.ok){if(!l(r.error)||typeof r.error.name!="string"||typeof r.error.message!="string"||r.error.code!==void 0&&typeof r.error.code!="string")throw new PlaywrightInstallationError("isolated resolver returned an invalid error");return{error:{...r.error.code===void 0?{}:{code:r.error.code},message:r.error.message,name:r.error.name},ok:!1}}if(typeof r.browserExecutable!="string"||!$(r.family)||!U(r.registry))throw new PlaywrightInstallationError("isolated resolver returned an invalid installation");return{browserExecutable:r.browserExecutable,family:r.family,ok:!0,registry:r.registry}}export function requireMatchingPlaywrightChildRequest(e,r){if(JSON.stringify(e.family)!==JSON.stringify(r.family))throw new PlaywrightInstallationError("isolated resolver selected a different physical package family");if(e.registry.canonicalValue!==r.registry.canonicalValue||e.registry.mode!==r.registry.mode)throw new PlaywrightInstallationError("isolated resolver selected a different browser registry")}export function requireBrowserExecutable(e,r){if(!f.isAbsolute(e))throw new PlaywrightInstallationError(`public chromium.executablePath() returned a non-absolute path ${JSON.stringify(e)}`);let t;try{t=c.statSync(e)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() returned missing path ${JSON.stringify(e)}`,{cause:o})}if(!t.isFile())throw new PlaywrightInstallationError(`public chromium.executablePath() must identify a regular file; received ${JSON.stringify(e)}`);if(r.platform!=="win32")try{c.accessSync(e,c.constants.X_OK)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() is not executable: ${JSON.stringify(e)}`,{cause:o})}return e}export function requireSupportedPlaywrightHost(e){const r=d(e.target);if(!I.some(t=>d(t)===r)||e.target.platform==="linux"&&e.linuxLibc!=="glibc")throw new UnsupportedPlaywrightPlatformError(e.target,e.linuxLibc)}function C(e,r,t,o){return e==="linux"?r.XDG_CACHE_HOME||o.join(g(t),".cache"):e==="darwin"?o.join(g(t),"Library","Caches"):r.LOCALAPPDATA||o.join(g(t),"AppData","Local")}function g(e){if(e.length===0)throw new TypeError("Playwright default registry requires a home directory");return e}function u(e){return{dependencyGroup:"dependencies",packageName:e.packageName,publicEntrypoint:e.publicEntrypoints[0]}}function p(e,r){if(e.name!==r)throw new TypeError(`Playwright package family expected ${r}, received ${e.name}`);return Object.freeze({entrypoint:e.entrypoint,name:r,root:e.root,version:e.version})}function N(){const e=m(new URL("./playwright-resolution-child.js",import.meta.url));return c.existsSync(e)?e:m(new URL("../../../dist/engines/installations/playwright-resolution-child.js",import.meta.url))}function j(e){return e.terminationError!==null?e.terminationError:e.error!==null?e.error:e.timedOut?new Error("isolated resolver timed out"):e.cancelled?new Error("isolated resolver was cancelled"):e.outputLimitExceeded?new Error("isolated resolver output exceeded the bound"):null}function E(e){return new Error(`isolated resolver exited with ${e.exitCode===null?String(e.exitSignal):e.exitCode.toString()}`)}function M(e){const r=new Error(e.message);return r.name=e.name,e.code!==void 0&&Object.defineProperty(r,"code",{enumerable:!0,value:e.code}),r}function $(e){return l(e)&&typeof e.cli=="string"&&w(e.playwrightTest,"@playwright/test")&&w(e.playwright,"playwright")&&w(e.playwrightCore,"playwright-core")}function w(e,r){return l(e)&&e.name===r&&typeof e.version=="string"&&typeof e.root=="string"&&typeof e.entrypoint=="string"}function U(e){return l(e)&&typeof e.canonicalValue=="string"&&(e.mode==="configured-absolute"||e.mode==="configured-relative"||e.mode==="default"||e.mode==="hermetic")}
1
+ import{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS as R}from"./package-requirements.js";export{PLAYWRIGHT_PACKAGE_FAMILY_REQUIREMENTS}from"./package-requirements.js";import f from"node:fs";import y from"node:os";import l from"node:path";import{fileURLToPath as h}from"node:url";import{runManagedProcess as x}from"../../process/index.js";import{currentLinuxLibc as T,currentPlatformTarget as _,platformTargetKey as g,SUPPORTED_PLATFORM_TARGETS as I}from"../../process/host/platform.js";import{RootPackageResolver as b}from"./package-resolution.js";import{isUnknownRecord as c}from"../../kernel/unknown-values.js";const O=64*1024,S=6e4,E="PLAYWRIGHT_BROWSERS_PATH",A=new Set(["npm_config_playwright_browsers_path","npm_package_config_playwright_browsers_path"]);export class PlaywrightInstallationError extends Error{code="TESTKIT_PLAYWRIGHT_INSTALLATION_INVALID";constructor(r,t){super(`Playwright installation is invalid: ${r}. Run testkit setup playwright --force.`,t),this.name="PlaywrightInstallationError"}}export class UnsupportedPlaywrightPlatformError extends Error{code="TESTKIT_PLAYWRIGHT_PLATFORM_UNSUPPORTED";constructor(r,t){super(`Playwright does not support ${g(r)}${r.platform==="linux"?` with ${t??"unknown"} libc`:""}`),this.name="UnsupportedPlaywrightPlatformError"}}export function resolvePlaywrightPackageFamily(e=new b){const[r,t,o]=R,i=e.resolveRequireFromRoot(d(r)),n=e.resolveImportFromRoot(d(r));L(e,i,n);const a=e.resolveRequireFromPackage(i,d(t)),s=e.resolveRequireFromPackage(a,d(o)),u=e.resolveRequirePublicEntrypoint(i,r.publicEntrypoints[1]);return Object.freeze({cli:u,playwright:m(a,"playwright"),playwrightCore:m(s,"playwright-core"),playwrightTest:m(n,"@playwright/test")})}function L(e,r,t){if(!(r.name===t.name&&r.version===t.version&&r.root===t.root))throw e.brokenInstallation(r,"the import and require public entrypoints select different physical package roots",void 0)}export function resolvePlaywrightRegistry(e){const r=e.platform==="win32"?l.win32:l.posix;if(!r.isAbsolute(e.productRoot))throw new TypeError("Playwright product root must be absolute");const t=e.configuredValue;if(t==="0")return Object.freeze({canonicalValue:"0",mode:"hermetic"});if(t!==void 0&&t.length>0){const o=r.isAbsolute(t);return Object.freeze({canonicalValue:o?r.normalize(t):r.resolve(e.productRoot,t),mode:o?"configured-absolute":"configured-relative"})}return Object.freeze({canonicalValue:r.resolve(e.productRoot,r.join(C(e.platform,e.ambientEnvironment,e.homeDirectory??y.homedir(),r),"ms-playwright")),mode:"default"})}export function playwrightRegistryEnvironment(e,r){const t={};for(const[o,i]of Object.entries(e))typeof i=="string"&&!A.has(o.toLowerCase())&&o.toUpperCase()!==E&&(t[o]=i);return t[E]=r.canonicalValue,Object.freeze(t)}export async function resolvePlaywrightInstallation(e,r={}){e.signal.throwIfAborted();const t={linuxLibc:T(),target:_()};requireSupportedPlaywrightHost(t);const o=new b,i=r.ambientEnvironment??process.env,n=resolvePlaywrightPackageFamily(o),a=resolvePlaywrightRegistry({ambientEnvironment:i,configuredValue:r.configuredRegistry??i.PLAYWRIGHT_BROWSERS_PATH,homeDirectory:y.homedir(),platform:t.target.platform,productRoot:e.productRoot}),s=await v(e,{family:n,registry:a},i),u=o.resolveRootPublicEntrypoint("@gfdelarue/testkit/playwright");return Object.freeze({authoringEntrypoints:Object.freeze({authoring:u,environment:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/environment"),managedTest:l.join(l.dirname(u),"managed-test.js"),root:o.resolveRootPublicEntrypoint("@gfdelarue/testkit"),rootPackageJson:o.resolveRootPublicEntrypoint("@gfdelarue/testkit/package.json")}),browserExecutable:requireBrowserExecutable(s.browserExecutable,t.target),engineId:"playwright",family:n,registry:a})}async function v(e,r,t){const o=Buffer.from(JSON.stringify(r),"utf8").toString("base64url"),i=await x({args:[j(),o],cwd:e.productRoot,displayLabel:"Playwright installation resolver",env:playwrightRegistryEnvironment(t,r.registry),executable:process.execPath,signal:e.signal,timeoutMs:S}),n=N(i);throwIfPlaywrightProcessAborted(e.signal,i,n),requireBoundedPlaywrightChildOutput(i.stdout);let a;try{a=parsePlaywrightChildOutput(i.stdout)}catch(s){throw n!==null?new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n}):s}if(n!==null)throw new PlaywrightInstallationError(`isolated resolver failed: ${n.message}`,{cause:n});if(!a.ok){if(i.exitCode!==1||i.exitSignal!==null){const s=P(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}throw new PlaywrightInstallationError(`isolated resolver failed: ${a.error.message}`,{cause:M(a.error)})}if(i.exitCode!==0||i.exitSignal!==null){const s=P(i);throw new PlaywrightInstallationError(`isolated resolver failed: ${s.message}`,{cause:s})}return requireMatchingPlaywrightChildRequest(a,r),a}export function throwIfPlaywrightProcessAborted(e,r,t){if(e.aborted){if(!k(r)||t===null){e.throwIfAborted();return}throw t===e.reason?t:new AggregateError([e.reason,t],"Playwright process was cancelled and process cleanup also failed",{cause:e.reason})}}function k(e){return e.terminationError!==null||e.error!==null||e.timedOut||e.outputLimitExceeded}export function requireBoundedPlaywrightChildOutput(e){if(Buffer.byteLength(e,"utf8")>O)throw new PlaywrightInstallationError("isolated resolver returned oversized output")}export function parsePlaywrightChildOutput(e){let r;try{r=JSON.parse(e)}catch(t){throw new PlaywrightInstallationError("isolated resolver did not return bounded JSON",{cause:t})}if(!c(r)||typeof r.ok!="boolean")throw new PlaywrightInstallationError("isolated resolver returned an invalid response");if(!r.ok){if(!c(r.error)||typeof r.error.name!="string"||typeof r.error.message!="string"||r.error.code!==void 0&&typeof r.error.code!="string")throw new PlaywrightInstallationError("isolated resolver returned an invalid error");return{error:{...r.error.code===void 0?{}:{code:r.error.code},message:r.error.message,name:r.error.name},ok:!1}}if(typeof r.browserExecutable!="string"||!$(r.family)||!U(r.registry))throw new PlaywrightInstallationError("isolated resolver returned an invalid installation");return{browserExecutable:r.browserExecutable,family:r.family,ok:!0,registry:r.registry}}export function requireMatchingPlaywrightChildRequest(e,r){if(JSON.stringify(e.family)!==JSON.stringify(r.family))throw new PlaywrightInstallationError("isolated resolver selected a different physical package family");if(e.registry.canonicalValue!==r.registry.canonicalValue||e.registry.mode!==r.registry.mode)throw new PlaywrightInstallationError("isolated resolver selected a different browser registry")}export function requireBrowserExecutable(e,r){if(!l.isAbsolute(e))throw new PlaywrightInstallationError(`public chromium.executablePath() returned a non-absolute path ${JSON.stringify(e)}`);let t;try{t=f.statSync(e)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() returned missing path ${JSON.stringify(e)}`,{cause:o})}if(!t.isFile())throw new PlaywrightInstallationError(`public chromium.executablePath() must identify a regular file; received ${JSON.stringify(e)}`);if(r.platform!=="win32")try{f.accessSync(e,f.constants.X_OK)}catch(o){throw new PlaywrightInstallationError(`public chromium.executablePath() is not executable: ${JSON.stringify(e)}`,{cause:o})}return e}export function requireSupportedPlaywrightHost(e){const r=g(e.target);if(!I.some(t=>g(t)===r)||e.target.platform==="linux"&&e.linuxLibc!=="glibc")throw new UnsupportedPlaywrightPlatformError(e.target,e.linuxLibc)}function C(e,r,t,o){return e==="linux"?r.XDG_CACHE_HOME||o.join(p(t),".cache"):e==="darwin"?o.join(p(t),"Library","Caches"):r.LOCALAPPDATA||o.join(p(t),"AppData","Local")}function p(e){if(e.length===0)throw new TypeError("Playwright default registry requires a home directory");return e}function d(e){return{dependencyGroup:"dependencies",packageName:e.packageName,publicEntrypoint:e.publicEntrypoints[0]}}function m(e,r){if(e.name!==r)throw new TypeError(`Playwright package family expected ${r}, received ${e.name}`);return Object.freeze({entrypoint:e.entrypoint,name:r,root:e.root,version:e.version})}function j(){const e=h(new URL("./playwright-resolution-child.js",import.meta.url));return f.existsSync(e)?e:h(new URL("../../../dist/engines/installations/playwright-resolution-child.js",import.meta.url))}function N(e){return e.terminationError!==null?e.terminationError:e.error!==null?e.error:e.timedOut?new Error("isolated resolver timed out"):e.cancelled?new Error("isolated resolver was cancelled"):e.outputLimitExceeded?new Error("isolated resolver output exceeded the bound"):null}function P(e){return new Error(`isolated resolver exited with ${e.exitCode===null?String(e.exitSignal):e.exitCode.toString()}`)}function M(e){const r=new Error(e.message);return r.name=e.name,e.code!==void 0&&Object.defineProperty(r,"code",{enumerable:!0,value:e.code}),r}function $(e){return c(e)&&typeof e.cli=="string"&&w(e.playwrightTest,"@playwright/test")&&w(e.playwright,"playwright")&&w(e.playwrightCore,"playwright-core")}function w(e,r){return c(e)&&e.name===r&&typeof e.version=="string"&&typeof e.root=="string"&&typeof e.entrypoint=="string"}function U(e){return c(e)&&typeof e.canonicalValue=="string"&&(e.mode==="configured-absolute"||e.mode==="configured-relative"||e.mode==="default"||e.mode==="hermetic")}
@@ -229,8 +229,9 @@ function sameNativePackageEdge(origin, parentURL, targetURL) {
229
229
 
230
230
  function resolveManagedSpecifier(specifier, conditions, parentURL) {
231
231
  const direct = DIRECT.get(specifier);
232
- if (direct !== undefined)
233
- return { shortCircuit: true, url: pathToFileURL(direct).href };
232
+ const directUrl = direct === undefined ? null : pathToFileURL(direct).href;
233
+ if (directUrl !== null && parentURL !== directUrl)
234
+ return { shortCircuit: true, url: directUrl };
234
235
 
235
236
  const packageName = matchingPackageName(specifier, PACKAGE_ROOTS);
236
237
  if (packageName !== null) {
@@ -674,6 +675,8 @@ function exactExportedSubpathForFile(exportsDeclaration, packageRoot, file) {
674
675
  if (exportsDeclaration === null || typeof exportsDeclaration !== "object")
675
676
  return null;
676
677
  const canonicalFile = canonicalPath(file);
678
+ const namedSubpath =
679
+ "./" + path.relative(packageRoot, file).split(path.sep).join("/");
677
680
  for (const [subpath, declaration] of Object.entries(exportsDeclaration)) {
678
681
  if (
679
682
  (subpath !== "." && !subpath.startsWith("./")) ||
@@ -681,6 +684,7 @@ function exactExportedSubpathForFile(exportsDeclaration, packageRoot, file) {
681
684
  ) {
682
685
  continue;
683
686
  }
687
+ if (subpath === namedSubpath) return subpath;
684
688
  for (const target of allExportTargets(declaration)) {
685
689
  if (
686
690
  target.startsWith("./") &&
@@ -1 +1 @@
1
- import{makeTempDirAsync as B}from"../../../process/files/temp-dir.js";import{toError as K}from"../../../kernel/error-serialization.js";import{isDeepStrictEqual as ie}from"node:util";import{classifyManagedProcessResult as oe,engineExecutionResult as se,terminalOutcome as j,emptyEvidence as L,failureFromError as x,firstOutputLine as W,processExitDetails as ae,processExitFailure as z,taskBeforeStartOutcome as ce,managedProcessFailure as H,taskTimeoutFailure as de}from"../../adapters/execution.js";import{requireRootDirectory as le,requireAbsoluteWorkspacePath as J,requireConsumerFile as X,safeSegment as Y}from"../../adapters/workspace.js";import h from"node:fs/promises";import I from"node:path";import{pathToFileURL as ue}from"node:url";import{isEngineTaskDeadlineExceededReason as pe,resolveEngineTaskInputs as fe}from"../../api/index.js";import{createInvocationProcessEnvironment as me,runManagedProcess as Ee}from"../../../process/index.js";import{managedVitestAliases as G}from"../../installations/node-vitest.js";import{decodeVitestSelection as Te,resolveRepositoryVitest as ve}from"../selection.js";import{collectNodeVitestEvidence as Ie,readNodeVitestRunReport as ye}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as Q,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as De,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as ge,selectNodeVitestForwardedEnvironment as he,verifyOwnedNodeVitestRuntimeInput as we,writeOwnedNodeVitestRuntimeInput as Ne}from"../runtime/runtime-input.js";import{NODE_VITEST_DATA_RUNNER_FILE as Z,NODE_VITEST_DRIVER_FILE as _e,writeNodeVitestWorkspace as Oe}from"./workspace.js";import{nodeVitestReportOptions as Re}from"./reporter.js";import{engineEvidenceIdentity as Ve}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as Se}from"../../adapters/node-startup-cache.js";const w={codePrefix:"NODE_VITEST",label:"Node/Vitest"},q="Unhandled Reporter Error";class ee extends Error{constructor(t){const r=K(t);super(r.message,{cause:r}),this.name="NodeVitestConsumerPreparationError"}}export const nodeVitestExecutorDependencies={removeOwnedDirectory:e=>h.rm(e,{force:!0,recursive:!0}),writeRuntimeInput:Ne,writeWorkspace:Oe};export async function executeNodeVitestEngine(e,t,r,f=nodeVitestExecutorDependencies){const c=await xe(e,t,f,r),o=Date.now(),s=c.tasks.map(d=>{e.observer?.taskStarted(d.task);const a=d.preparationFailure;return ce(d.task,e.signal,o,w)??(a===null?null:j(d.task.id,"failed",o,L(),a))}),p=c.tasks.filter((d,a)=>s[a]===null),i=[],v=p.length===0?[]:await Fe(e,c,p,o,i);let m=0;return se(e,s.map(d=>d??v[m++]),je(e,c),i)}async function xe(e,t,r,f){const c=e.profile.id,o=await le(e.workspace.rootDirectory);J(e.workspace.temporaryDirectory,"temporary directory"),J(e.workspace.artifactDirectory,"artifact directory"),await h.mkdir(e.workspace.temporaryDirectory,{recursive:!0}),await h.mkdir(e.workspace.artifactDirectory,{recursive:!0});const s=await h.realpath(e.workspace.temporaryDirectory),p=await h.realpath(e.workspace.artifactDirectory),i=await B(`node-vitest-${Y(e.invocationId)}-`,{parentDir:s});f.defer(()=>r.removeOwnedDirectory(i));const v=I.join(i,"reports");await h.mkdir(v,{mode:448});const m=await me(i);let d=[];for(const[n,u]of e.tasks.entries()){const l=fe(e,u),N=`${n.toString().padStart(3,"0")}-${Y(u.id)}`,y=await B(`${N}-`,{parentDir:p});try{const V=await M(()=>X(o,u.file,"Node/Vitest task file")),S=I.join(v,`${N}.json`),A={file:V,reportFile:S,sourceFile:u.file,taskId:u.id},k=I.join(y,"data");d.push({artifactDirectory:y,inputs:l,task:u,preparationFailure:null,prepared:{dataDirectory:k,module:A}})}catch(V){const S=te(V);d.push({artifactDirectory:y,inputs:l,task:u,preparationFailure:S,prepared:null})}}const a=d.filter(n=>n.prepared!==null),E=I.join(i,"setup.mjs"),T=I.join(i,"input-bootstrap.mjs"),D=I.join(i,"driver-request.json"),_=I.join(v,"run.json");let F=null,O=null,g=t.vitest;const R=`--import=${JSON.stringify(ue(T).href)}`;let C={...m.environment,FORCE_COLOR:"0",NODE_OPTIONS:R,NO_COLOR:"1"};if(a.length>0)try{ke(a);const{configuration:n}=a[0].inputs,u=c==="node-vitest"&&n!==void 0?Te(n):null,l=u!==null&&u.runner!=="managed"?u:null;l!==null&&(g=ve(o));const N=await M(()=>be(o,a[0].inputs)),y=await M(()=>Pe(e,c,a,l?.host??null));if(l!==null){const P=b=>process.platform==="win32"?b.toUpperCase():b,ne=new Set(Object.keys(y.environment).map(P));C={...Object.fromEntries(Object.entries(l.host).filter(([b])=>!ne.has(P(b)))),...y.environment,NODE_OPTIONS:[R,l.host.NODE_OPTIONS].join(" ").trim()}}F=await r.writeRuntimeInput(i,y);const{file:V,sha256:S,sizeBytes:A}=F,k=t.authoringEntrypoints;await r.writeWorkspace({configFile:N,inputBootstrapFile:T,modules:{...G(t),vitest:g.index},runtimeInput:{expectedFile:V,expectedInvocationId:e.invocationId,expectedProfileId:c,expectedSha256:S,expectedSizeBytes:A},setupFile:E});const U=c==="node-vitest-data",re=[k.config,k.data,...U?[Z]:[]],$={cacheDirectory:I.join(i,"cache"),invocationId:e.invocationId,runReportFile:_,setupFile:E,vitest:{entrypoint:g.entrypoint,name:"vitest",version:g.version}};O=l===null?{...$,aliases:G(t),config:!1,managed:{externalModules:re.map(P=>P.replaceAll(I.sep,"/"))},project:null,test:{...U?{runner:Z}:{},disableConsoleIntercept:!0,environment:"node",fileParallelism:!1,isolate:!0,maxWorkers:1,pool:"forks"}}:{...$,aliases:{},config:l.config,managed:null,project:l.project,test:{}}}catch(n){const u=te(n);d=d.map(l=>l.prepared===null?l:{...l,preparationFailure:u,prepared:null})}return Object.freeze({driverRequest:O,driverRequestFile:D,installation:t,processEnvironment:C,profileId:c,rootDirectory:o,runReportFile:_,runtimeInput:F,tasks:Object.freeze(d),vitest:g})}async function Fe(e,t,r,f,c){const o=n=>r.map(({task:u})=>j(u.id,"failed",f,L(),n(u.timeoutMs)));try{if(t.runtimeInput===null||t.driverRequest===null)throw new Error("Node/Vitest run was not prepared");await we(t.runtimeInput)}catch(n){return o(()=>({...x(n,"NODE_VITEST_INPUT_INVALID"),classification:"infra"}))}const s=await Ce(e,t,t.runtimeInput,r[0]),p=Math.min(...r.map(({task:n})=>n.deadlineMs))-Date.now();if(p<=0)return o(n=>de(n,w));const i=t.driverRequest.managed===null?{}:{hookTimeout:p,teardownTimeout:p,testTimeout:p},v=r.map(({prepared:n})=>n.module),m={...t.driverRequest,modules:v,test:{...t.driverRequest.test,...i}};await h.writeFile(t.driverRequestFile,JSON.stringify(m),{flag:"wx",mode:384});const d=await Ee({args:[_e,t.driverRequestFile],cwd:t.rootDirectory,displayLabel:"Node/Vitest",env:s,executable:process.execPath,signal:e.signal,timeoutMs:p}),a=await ye(e.invocationId,t.runReportFile),E=a instanceof Error?[]:a.unhandledErrors.filter(({type:n})=>n!==q),T=[];for(const{artifactDirectory:n,prepared:u}of r)T.push(await Ie({identity:Re(e.invocationId,u.module).identity,artifactDirectory:n,dataDirectory:u.dataDirectory,reportFile:u.module.reportFile,rootDirectory:t.rootDirectory,stderr:d.stderr,stdout:d.stdout,unhandledErrors:E.filter(({testPath:l})=>l===void 0||I.resolve(l)===I.resolve(u.module.file))}).catch(l=>nodeVitestEvidenceCollectionFailure(d,l)));const D=T.map(n=>"reportSummary"in n?n.reportSummary:null),_=pe(e.signal.reason),O=(d.cancelled||d.timedOut||_)&&a instanceof Error&&D.includes(null),g=E.length>0||D.some(n=>n?.success===!1||n?.numTotalTests===0),R=a instanceof Error?0:d.exitCode??a.exitCode,C=a instanceof Error?O?{exitCode:0,unhandledErrors:[]}:a:{...a,exitCode:R===1&&(g||a.afterTests!==void 0)?0:R};return!(a instanceof Error)&&a.afterTests!==void 0&&c.push(`Vitest failed the run after its tests passed: ${a.afterTests.join("; ")}`),r.map(({task:n},u)=>{const l=T[u];if(!("reportSummary"in l))return j(n.id,"failed",f,L(),l);const{failure:N,status:y}=classifyNodeVitestTaskResult(d,C,l,n.timeoutMs,_,O);return j(n.id,y,f,l.evidence,N)})}export function classifyNodeVitestTaskResult(e,t,r,f,c,o=!1){const s=E=>({failure:E,status:"failed"}),p=H(e,w);if(p!==null)return s(p);const i=r.reportSummary,v=i===null||!o?oe(e,f,c,w):null;if(v!==null)return v;const m=t instanceof Error?void 0:t.unhandledErrors.find(({type:E})=>E===q),d=t instanceof Error?void 0:t.failure??(m&&{...m,phase:"reporter"});if(d!==void 0){const{message:E,phase:T,stack:D}=d;return{failure:{classification:"infra",code:"NODE_VITEST_RUN_FAILED",details:{phase:T},message:`Vitest ${T} failed: ${E}`,...D===void 0?{}:{stack:D}},status:T==="config"?"not_run":"failed"}}if(r.environmentFailure!==null)return s(r.environmentFailure);if(i===null||t instanceof Error){const E=W(e.stderr)??W(e.stdout),T=e.exitCode===0?null:z(e,w,E,"Vitest").failure;return s({...x(r.reportError??t,"NODE_VITEST_REPORT_INVALID"),...T===null?{}:{details:T.details,message:T.message},classification:"infra"})}const a=o?null:e.exitSignal;if(t.exitCode!==0||a!==null){const E=a===null?`exited with code ${t.exitCode.toString()}`:`was terminated by ${a}`;return s({classification:"infra",code:"NODE_VITEST_EXIT_CODE",details:ae(e),message:`Vitest ${E}, which its test results do not explain`})}return i.numTotalTests===0&&r.reportErrorCount===0?s({code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"}):!i.success||i.numFailedTests>0||r.reportErrorCount>0?z(e,w,r.reportFailureMessage,"Vitest"):i.numPassedTests===0&&(i.numPendingTests>0||i.numTodoTests>0)?{failure:null,status:"skipped"}:r.dataEvidenceError!==null?s(x(r.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID")):i.numPassedTests===0?s({code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"}):{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(e,t){const r=K(t);return H(e,w,r)??{...x(r,"NODE_VITEST_EVIDENCE_FAILED"),classification:"infra"}}async function Ce(e,t,r,{inputs:f,task:c}){const o=e.target.baseUrl,s={...t.processEnvironment,...o===null?{}:{BASE_URL:o},TESTKIT_ACTIVE:"1",[De]:r.file,[ge]:r.sha256,TESTKIT_INVOCATION_ID:e.invocationId,TESTKIT_LEASE_DIR:c.orchestration.lease.directory,TESTKIT_LEASE_ID:c.orchestration.lease.id,TESTKIT_MODE:c.orchestration.mode,...c.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:c.orchestration.runtimeId}},p=t.driverRequest?.managed!==null;p&&Q(s);const i={...t.processEnvironment,...f.environment};Object.assign(s,await Se(e.workspace.cacheDirectory,i));try{p&&Q(s)}catch{delete s.NODE_COMPILE_CACHE}return s}function ke(e){const t=({prepared:o})=>o.module.file,r=e.find((o,s)=>e.findIndex(p=>t(p)===t(o))!==s);if(r!==void 0)throw new Error(`Node/Vitest task ${r.task.id} selects a file that another task in the request selects`);const f=({inputs:o,task:s})=>[o.environment,o.supportModules,s.orchestration,s.timeoutMs],c=e.find(o=>!ie(f(o),f(e[0])));if(c!==void 0)throw new Error(`Node/Vitest task ${c.task.id} needs a different environment, support modules, orchestration or timeout than ${e[0].task.id}`)}function Pe(e,t,r,f){const{baseUrl:c,databases:o,endpoints:s,handles:p}=e.target;return{...f===null?{}:{host:f},baseUrl:c,databases:o,endpoints:s,engineId:"node-vitest",environment:he(r[0].inputs.environment),handles:p,invocationId:e.invocationId,profileId:t,tasks:r.map(({inputs:i,prepared:v,task:m})=>({context:{baseUrl:c,engineId:"node-vitest",invocationId:e.invocationId,profileId:t,serviceName:m.serviceName,task:{file:m.file,id:m.id,suiteName:m.suiteName,...m.testType===void 0?{}:{testType:m.testType}}},dataDirectory:v.dataDirectory,file:v.module.file,fixtures:i.fixtures,...i.setupData===void 0?{}:{setupData:i.setupData},taskId:m.id})),version:1}}async function be(e,{supportModules:t}){if(!Object.hasOwn(t,"testkitConfig"))return null;if(!I.isAbsolute(t.testkitConfig))throw new Error("supportModules.testkitConfig must be an absolute path");return X(e,t.testkitConfig,"supportModules.testkitConfig")}function je(e,t){return{...Ve({engineId:"node-vitest",installationKind:t.driverRequest?.managed===null?"repository-package":"root-direct-packages",profile:e.profile}),directPackages:{vitest:t.vitest.version}}}function te(e){return{...x(e,"NODE_VITEST_PREPARE_FAILED"),classification:e instanceof ee?"product":"infra"}}async function M(e){try{return await e()}catch(t){throw new ee(t)}}
1
+ import{makeTempDirAsync as U}from"../../../process/files/temp-dir.js";import{toError as B}from"../../../kernel/error-serialization.js";import{isDeepStrictEqual as ee}from"node:util";import{classifyManagedProcessResult as te,engineExecutionResult as re,terminalOutcome as P,emptyEvidence as L,failureFromError as j,firstOutputLine as K,processExitDetails as ne,processExitFailure as W,taskBeforeStartOutcome as ie,managedProcessFailure as z,taskTimeoutFailure as oe}from"../../adapters/execution.js";import{requireRootDirectory as se,requireAbsoluteWorkspacePath as H,requireConsumerFile as ae,safeSegment as J}from"../../adapters/workspace.js";import w from"node:fs/promises";import I from"node:path";import{pathToFileURL as ce}from"node:url";import{isEngineTaskDeadlineExceededReason as de,resolveEngineTaskInputs as le}from"../../api/index.js";import{createInvocationProcessEnvironment as ue,runManagedProcess as pe}from"../../../process/index.js";import{managedVitestAliases as me}from"../../installations/node-vitest.js";import{decodeVitestSelection as fe,resolveRepositoryVitest as Ee}from"../selection.js";import{collectNodeVitestEvidence as ve,readNodeVitestRunReport as Te}from"./evidence.js";import{assertNodeVitestSpawnEnvironmentBudget as X,NODE_VITEST_RUNTIME_INPUT_FILE_ENV as Ie,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV as ye,selectNodeVitestForwardedEnvironment as De,splitManagedNodeVitestEnvironment as he,verifyOwnedNodeVitestRuntimeInput as we,writeOwnedNodeVitestRuntimeInput as Ne}from"../runtime/runtime-input.js";import{NODE_VITEST_DRIVER_FILE as Y,writeNodeVitestWorkspace as ge}from"./workspace.js";import{nodeVitestReportOptions as _e}from"./reporter.js";import{engineEvidenceIdentity as Ve}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as Oe}from"../../adapters/node-startup-cache.js";const g={codePrefix:"NODE_VITEST",label:"Node/Vitest"},G="Unhandled Reporter Error";class $ extends Error{constructor(t){const r=B(t);super(r.message,{cause:r}),this.name="NodeVitestConsumerPreparationError"}}export const nodeVitestExecutorDependencies={removeOwnedDirectory:e=>w.rm(e,{force:!0,recursive:!0}),writeRuntimeInput:Ne,writeWorkspace:ge};export async function executeNodeVitestEngine(e,t,r,m=nodeVitestExecutorDependencies){const l=await Re(e,t,m,r),o=Date.now(),s=l.tasks.map(c=>{e.observer?.taskStarted(c.task);const f=c.preparationFailure;return ie(c.task,e.signal,o,g)??(f===null?null:P(c.task.id,"failed",o,L(),f))}),p=l.tasks.filter((c,f)=>s[f]===null),i=[],v=p.length===0?[]:await Se(e,l,p,o,i);let E=0;return re(e,s.map(c=>c??v[E++]),ke(e,l),i)}async function Re(e,t,r,m){const l=e.profile.id,o=await se(e.workspace.rootDirectory);H(e.workspace.temporaryDirectory,"temporary directory"),H(e.workspace.artifactDirectory,"artifact directory"),await w.mkdir(e.workspace.temporaryDirectory,{recursive:!0}),await w.mkdir(e.workspace.artifactDirectory,{recursive:!0});const s=await w.realpath(e.workspace.temporaryDirectory),p=await w.realpath(e.workspace.artifactDirectory),i=await U(`node-vitest-${J(e.invocationId)}-`,{parentDir:s});m.defer(()=>r.removeOwnedDirectory(i));const v=I.join(i,"reports");await w.mkdir(v,{mode:448});const E=await ue(i);let c=[];for(const[n,u]of e.tasks.entries()){const a=le(e,u),N=`${n.toString().padStart(3,"0")}-${J(u.id)}`,y=await U(`${N}-`,{parentDir:p});try{const x=await Z(()=>ae(o,u.file,"Node/Vitest task file")),C=I.join(v,`${N}.json`),M={file:x,reportFile:C,sourceFile:u.file,taskId:u.id},A=I.join(y,"data");c.push({artifactDirectory:y,inputs:a,task:u,preparationFailure:null,prepared:{dataDirectory:A,module:M}})}catch(x){const C=Q(x);c.push({artifactDirectory:y,inputs:a,task:u,preparationFailure:C,prepared:null})}}const f=c.filter(n=>n.prepared!==null),d=I.join(i,"setup.mjs"),T=I.join(i,"input-bootstrap.mjs"),h=I.join(i,"driver-request.json"),R=I.join(v,"run.json"),_=I.join(i,"startup-failure.txt");let V=null,S=null,D=t.vitest;const F=`--import=${JSON.stringify(ce(T).href)}`;let O={...E.environment,FORCE_COLOR:"0",NODE_OPTIONS:F,NO_COLOR:"1"};if(f.length>0)try{xe(f);const{configuration:n}=f[0].inputs,u=n===void 0?null:fe(n),a=u!==null&&u.runner!=="managed"?u:null;a!==null&&(D=Ee(o));const N=Object.keys(f[0].inputs.supportModules);if(N.length>0)throw new $(new Error(`Node/Vitest engine does not support support module(s): ${N.join(", ")}`));let y=await Z(()=>Ce(e,l,f,a?.host??null));if(a===null){const k=he(y.environment);y={...y,environment:k.overlay},O={...O,...k.process}}else{const k=b=>process.platform==="win32"?b.toUpperCase():b,q=new Set(Object.keys(y.environment).map(k));O={...Object.fromEntries(Object.entries(a.host).filter(([b])=>!q.has(k(b)))),...y.environment,NODE_OPTIONS:[F,a.host.NODE_OPTIONS].join(" ").trim()}}V=await r.writeRuntimeInput(i,y);const{file:x,sha256:C,sizeBytes:M}=V;await r.writeWorkspace({inputBootstrapFile:T,runtimeInput:{expectedFile:x,expectedInvocationId:e.invocationId,expectedSha256:C,expectedSizeBytes:M,startupFailure:{driverFile:Y,file:_,vitestWorkersDirectory:I.join(D.root,"dist","workers")}},setupFile:d,vitestModule:D.index});const A={cacheDirectory:I.join(i,"cache"),invocationId:e.invocationId,runReportFile:R,setupFile:d,startupFailureFile:_,vitest:{entrypoint:D.entrypoint,name:"vitest",version:D.version}};S=a===null?{...A,aliases:me(t),config:!1,managed:{externalModules:[t.authoringEntrypoints.data.replaceAll(I.sep,"/")]},project:null,test:{disableConsoleIntercept:!0,environment:"node",fileParallelism:!1,isolate:!0,maxWorkers:1,pool:"forks"}}:{...A,aliases:{},config:a.config,managed:null,project:a.project,test:{}}}catch(n){const u=Q(n);c=c.map(a=>a.prepared===null?a:{...a,preparationFailure:u,prepared:null})}return Object.freeze({driverRequest:S,driverRequestFile:h,installation:t,processEnvironment:O,profileId:l,rootDirectory:o,runReportFile:R,runtimeInput:V,startupFailureFile:_,tasks:Object.freeze(c),vitest:D})}async function Se(e,t,r,m,l){const o=n=>r.map(({task:u})=>P(u.id,"failed",m,L(),n(u.timeoutMs)));try{if(t.runtimeInput===null||t.driverRequest===null)throw new Error("Node/Vitest run was not prepared");await we(t.runtimeInput)}catch(n){return o(()=>({...j(n,"NODE_VITEST_INPUT_INVALID"),classification:"infra"}))}const s=await Fe(e,t,t.runtimeInput,r[0]),p=Math.min(...r.map(({task:n})=>n.deadlineMs))-Date.now();if(p<=0)return o(n=>oe(n,g));const i=t.driverRequest.managed===null?{}:{hookTimeout:p,teardownTimeout:p,testTimeout:p},v=r.map(({prepared:n})=>n.module),E={...t.driverRequest,modules:v,test:{...t.driverRequest.test,...i}};await w.writeFile(t.driverRequestFile,JSON.stringify(E),{flag:"wx",mode:384});const c=await pe({args:[Y,t.driverRequestFile],cwd:t.rootDirectory,displayLabel:"Node/Vitest",env:s,executable:process.execPath,signal:e.signal,timeoutMs:p}),f=await w.readFile(t.startupFailureFile,"utf8").catch(()=>null),d=await Te(e.invocationId,t.runReportFile),T=d instanceof Error?[]:d.unhandledErrors.filter(({type:n})=>n!==G),h=[];for(const{artifactDirectory:n,prepared:u}of r)h.push(await ve({identity:_e(e.invocationId,u.module).identity,artifactDirectory:n,dataDirectory:u.dataDirectory,reportFile:u.module.reportFile,rootDirectory:t.rootDirectory,stderr:c.stderr,stdout:c.stdout,unhandledErrors:T.filter(({testPath:a})=>a===void 0||I.resolve(a)===I.resolve(u.module.file))}).catch(a=>nodeVitestEvidenceCollectionFailure(c,a)));const R=h.map(n=>"reportSummary"in n?n.reportSummary:null),_=de(e.signal.reason),V=c.cancelled||c.timedOut||_,S=V&&d instanceof Error&&R.includes(null),D=T.length>0||R.some(n=>n?.success===!1||n?.numTotalTests===0),F=d instanceof Error?0:c.exitCode??d.exitCode,O=d instanceof Error?S?{exitCode:0,unhandledErrors:[]}:d:{...d,exitCode:F===1&&(D||d.afterTests!==void 0)?0:F};return!(d instanceof Error)&&d.afterTests!==void 0&&l.push(`Vitest failed the run after its tests ${D?"finished":"passed"}: ${d.afterTests.join("; ")}`),r.map(({task:n},u)=>{const a=h[u];if(f!==null&&!V)return P(n.id,"failed",m,"reportSummary"in a?a.evidence:L(),{classification:"infra",code:"NODE_VITEST_START_FAILED",message:`Vitest could not start: ${f}`});if(!("reportSummary"in a))return P(n.id,"failed",m,L(),a);const{failure:N,status:y}=classifyNodeVitestTaskResult(c,O,a,n.timeoutMs,_,S);return P(n.id,y,m,a.evidence,N)})}export function classifyNodeVitestTaskResult(e,t,r,m,l,o=!1){const s=d=>({failure:d,status:"failed"}),p=z(e,g);if(p!==null)return s(p);const i=r.reportSummary,v=i===null||!o?te(e,m,l,g):null;if(v!==null)return v;const E=t instanceof Error?void 0:t.unhandledErrors.find(({type:d})=>d===G),c=t instanceof Error?void 0:t.failure??(E&&{...E,phase:"reporter"});if(c!==void 0){const{message:d,phase:T,stack:h}=c;return{failure:{classification:"infra",code:"NODE_VITEST_RUN_FAILED",details:{phase:T},message:`Vitest ${T} failed: ${d}`,...h===void 0?{}:{stack:h}},status:T==="config"?"not_run":"failed"}}if(r.environmentFailure!==null)return s(r.environmentFailure);if(i===null||t instanceof Error){const d=K(e.stderr)??K(e.stdout),T=e.exitCode===0?null:W(e,g,d,"Vitest").failure;return s({...j(r.reportError??t,"NODE_VITEST_REPORT_INVALID"),...T===null?{}:{details:T.details,message:T.message},classification:"infra"})}const f=o?null:e.exitSignal;if(t.exitCode!==0||f!==null){const d=f===null?`exited with code ${t.exitCode.toString()}`:`was terminated by ${f}`;return s({classification:"infra",code:"NODE_VITEST_EXIT_CODE",details:ne(e),message:`Vitest ${d}, which its test results do not explain`})}return i.numTotalTests===0&&r.reportErrorCount===0?s({code:"NODE_VITEST_NO_TESTS",message:"Vitest did not discover any tests for the task file"}):r.dataEvidenceError!==null?s(j(r.dataEvidenceError,"NODE_VITEST_DATA_EVIDENCE_INVALID")):!i.success||i.numFailedTests>0||r.reportErrorCount>0?W(e,g,r.reportFailureMessage,"Vitest"):i.numPassedTests===0&&(i.numPendingTests>0||i.numTodoTests>0)?{failure:null,status:"skipped"}:i.numPassedTests===0?s({code:"NODE_VITEST_REPORT_FAILED",message:"Vitest report did not contain a completed passing test"}):{failure:null,status:"passed"}}export function nodeVitestEvidenceCollectionFailure(e,t){const r=B(t);return z(e,g,r)??{...j(r,"NODE_VITEST_EVIDENCE_FAILED"),classification:"infra"}}async function Fe(e,t,r,{inputs:m,task:l}){const o=e.target.baseUrl,s={...t.processEnvironment,...o===null?{}:{BASE_URL:o},TESTKIT_ACTIVE:"1",[Ie]:r.file,[ye]:r.sha256,TESTKIT_INVOCATION_ID:e.invocationId,TESTKIT_LEASE_DIR:l.orchestration.lease.directory,TESTKIT_LEASE_ID:l.orchestration.lease.id,TESTKIT_MODE:l.orchestration.mode,...l.orchestration.runtimeId===void 0?{}:{TESTKIT_RUNTIME_ID:l.orchestration.runtimeId}},p=t.driverRequest?.managed!==null;p&&X(s);const i={...t.processEnvironment,...m.environment};Object.assign(s,await Oe(e.workspace.cacheDirectory,i));try{p&&X(s)}catch{delete s.NODE_COMPILE_CACHE}return s}function xe(e){const t=({prepared:o})=>o.module.file,r=e.find((o,s)=>e.findIndex(p=>t(p)===t(o))!==s);if(r!==void 0)throw new Error(`Node/Vitest task ${r.task.id} selects a file that another task in the request selects`);const m=({inputs:o,task:s})=>[o.environment,o.supportModules,s.orchestration,s.timeoutMs],l=e.find(o=>!ee(m(o),m(e[0])));if(l!==void 0)throw new Error(`Node/Vitest task ${l.task.id} needs a different environment, support modules, orchestration or timeout than ${e[0].task.id}`)}function Ce(e,t,r,m){const{baseUrl:l,databases:o,endpoints:s,handles:p}=e.target;return{...m===null?{}:{host:m},baseUrl:l,databases:o,endpoints:s,engineId:"node-vitest",environment:De(r[0].inputs.environment),handles:p,invocationId:e.invocationId,profileId:t,tasks:r.map(({inputs:i,prepared:v,task:E})=>({context:{baseUrl:l,engineId:"node-vitest",invocationId:e.invocationId,profileId:t,serviceName:E.serviceName,task:{file:E.file,id:E.id,suiteName:E.suiteName,...E.testType===void 0?{}:{testType:E.testType}}},dataDirectory:v.dataDirectory,file:v.module.file,fixtures:i.fixtures,...i.setupData===void 0?{}:{setupData:i.setupData},taskId:E.id})),version:1}}function ke(e,t){return{...Ve({engineId:"node-vitest",installationKind:t.driverRequest?.managed===null?"repository-package":"root-direct-packages",profile:e.profile}),directPackages:{vitest:t.vitest.version}}}function Q(e){return{...j(e,"NODE_VITEST_PREPARE_FAILED"),classification:e instanceof $?"product":"infra"}}async function Z(e){try{return await e()}catch(t){throw new $(t)}}
@@ -1,2 +1,2 @@
1
- import{realpathSync as d}from"node:fs";import S from"node:fs/promises";import w from"node:path";import{inspect as R}from"node:util";import{toError as m}from"../../../kernel/error-serialization.js";import{isUnknownRecord as j}from"../../../kernel/unknown-values.js";import{writeNativeReport as x}from"../../adapters/native-report.js";import{importResolvedPackageEntrypoint as k}from"../../installations/package-resolution.js";import{nodeVitestReportOptions as V,reportedError as p,TestkitVitestReporter as F}from"./reporter.js";const o=JSON.parse(await S.readFile(process.argv[2]??"","utf8")),E=new Map(o.modules.map(e=>[d(e.file),e])),N=V(o.invocationId,{reportFile:o.runReportFile,sourceFile:".",taskId:"run"}),a={unhandledErrors:[]},O=16*1024*1024;let g;process.env.TEST="true",process.env.VITEST="true",process.env.NODE_ENV??="test";try{const{createVitest:e}=await k(o.vitest),s={cacheDir:o.cacheDirectory,resolve:{alias:Object.entries(o.aliases).map(([i,u])=>({find:y(i),replacement:u})),...o.managed===null?{}:{tsconfigPaths:!0}},test:{...o.test,...o.managed===null?{}:{server:{deps:{external:o.managed.externalModules.map(y)}}}}},t=await e("test",{...{allowOnly:!1,includeTaskLocation:!0,passWithNoTests:!1,update:"none"},config:o.config??void 0,watch:!1},s);g=t,t.config.snapshotOptions.updateSnapshot="none",t.snapshot.options.updateSnapshot="none";for(const i of t.projects)i.config.setupFiles.push(o.setupFile);const n=await P(t),c=n.map(({moduleId:i,project:u})=>({...T(i),project:u.name})),{reporters:l}=t;o.managed!==null&&(l.length=0),l.unshift(new F(o.invocationId,c));const v=$(t,l),f=t;await(f.standalone===void 0?f.init():f.standalone()),a.unhandledErrors=(await t.runTestSpecifications(n,!1)).unhandledErrors.map(C);const h=v();h.length>0&&(a.afterTests=h)}catch(e){a.failure={phase:g===void 0?"config":"run",...p(m(e))},process.stderr.write(`${R(e)}
2
- `),process.exitCode=1}finally{await g?.close().catch(e=>{a.failure??={phase:"close",...p(m(e))}})}await x(N,{...a,exitCode:process.exitCode??0},O);async function P(e){if(o.config===!1)return o.modules.map(({file:t})=>{const n=t.split(w.sep).join("/");return e.getProjectByName("").createSpecification(n)});const s=(await e.globTestSpecifications()).filter(({moduleId:t,project:n})=>E.has(d(t))&&(o.project===null||n.name===o.project)),r=o.modules.find(({file:t})=>!s.some(({moduleId:n})=>T(n).file===t));if(r!==void 0){const t=r.sourceFile.split(w.sep).join("/");throw new Error(`Vitest's configuration no longer selects ${t}, which Testkit listed earlier`)}return s}function $(e,s){let r;const t=[],n=e.logger.error.bind(e.logger);return e.logger.error=(...c)=>{r!==void 0&&t.push(c.map(String).join(" ")),n(...c)},s.push({onTestRunEnd:()=>{r=process.exitCode??0}}),()=>r===0&&process.exitCode===1?t:[]}function T(e){const s=E.get(d(e));if(s===void 0)throw new Error(`Vitest selected ${e}, which Testkit did not request`);return s}function C(e){if(!j(e)||typeof e.message!="string")return p(m(e));const{name:s,stack:r,type:t,VITEST_TEST_PATH:n}=e;return{...p({message:e.message,...typeof s=="string"?{name:s}:{},...typeof r=="string"?{stack:r}:{}}),...typeof t=="string"?{type:t}:{},...typeof n=="string"?{testPath:n}:{}}}function y(e){return new RegExp(`^${e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}$`,"u")}
1
+ import{existsSync as w,realpathSync as d}from"node:fs";import R from"node:fs/promises";import T from"node:path";import{inspect as j}from"node:util";import{toError as g}from"../../../kernel/error-serialization.js";import{isUnknownRecord as $}from"../../../kernel/unknown-values.js";import{writeNativeReport as k}from"../../adapters/native-report.js";import{importResolvedPackageEntrypoint as V}from"../../installations/package-resolution.js";import{nodeVitestReportOptions as N,reportedError as u,TestkitVitestReporter as C}from"./reporter.js";const o=JSON.parse(await R.readFile(process.argv[2]??"","utf8")),v=new Map(o.modules.map(e=>[d(e.file),e])),O=N(o.invocationId,{reportFile:o.runReportFile,sourceFile:".",taskId:"run"}),p={unhandledErrors:[]},P=16*1024*1024;let h;const b=setInterval(()=>{w(o.startupFailureFile)&&process.exit(1)},100);b.unref();let m;process.env.TEST="true",process.env.VITEST="true",process.env.NODE_ENV??="test";try{const{createVitest:e}=await V(o.vitest),t={cacheDir:o.cacheDirectory,resolve:{alias:Object.entries(o.aliases).map(([r,i])=>({find:S(r),replacement:i})),...o.managed===null?{}:{tsconfigPaths:!0}},test:{...o.test,...o.managed===null?{}:{server:{deps:{external:o.managed.externalModules.map(S)}}}}},s=await e("test",{...{allowOnly:!1,includeTaskLocation:!0,passWithNoTests:!1,update:"none"},config:o.config??void 0,watch:!1},t);h=s,s.config.snapshotOptions.updateSnapshot="none",s.snapshot.options.updateSnapshot="none";for(const r of s.projects)r.config.setupFiles.push(o.setupFile);const n=await I(s),a=n.map(({moduleId:r,project:i})=>({...y(r),project:i.name})),{reporters:f}=s;o.managed!==null&&(f.length=0),f.unshift(new C(o.invocationId,a)),m=_(s);const l=s;await(l.standalone===void 0?l.init():l.standalone()),p.unhandledErrors=(await s.runTestSpecifications(n,!1)).unhandledErrors.map(M),m.testsFinished()}catch(e){p.failure={phase:h===void 0?"config":"run",...u(g(e))},process.stderr.write(`${j(e)}
2
+ `),process.exitCode=1}finally{w(o.startupFailureFile)&&process.exit(1),await h?.close().catch(e=>{p.failure??={phase:"close",...u(g(e))}})}const E=m?.finish(p.failure===void 0)??[];E.length>0&&(p.afterTests=E),await k(O,{...p,exitCode:process.exitCode??0},P);async function I(e){if(o.config===!1)return o.modules.map(({file:s})=>{const n=s.split(T.sep).join("/");return e.getProjectByName("").createSpecification(n)});const t=(await e.globTestSpecifications()).filter(({moduleId:s,project:n})=>v.has(d(s))&&(o.project===null||n.name===o.project)),c=o.modules.find(({file:s})=>!t.some(({moduleId:n})=>y(n).file===s));if(c!==void 0){const s=c.sourceFile.split(T.sep).join("/");throw new Error(`Vitest's configuration no longer selects ${s}, which Testkit listed earlier`)}return t}function _(e){const t={close:0,coverage:0,reporters:0,tests:0},c={close:[],coverage:[]};let s=null;const n=e.logger.error.bind(e.logger);e.logger.error=(...r)=>{s!==null&&c[s].push(r.map(String).join(" ")),n(...r)};const a=()=>{const r=Number(process.exitCode??0);return process.exitCode=void 0,r},f=e,l=f.report.bind(e);return f.report=async(r,...i)=>{if(r!=="onTestRunEnd")return l(r,...i);t.tests=a(),await l(r,...i),t.reporters=a(),s="coverage"},{testsFinished(){t.coverage=a(),s="close"},finish(r){if(t.close=a(),s=null,process.exitCode=[t.close,t.coverage,t.reporters,t.tests].find(F=>F!==0)??0,!r)return[];const i=[];return t.reporters!==0&&i.push(`a reporter set exit code ${t.reporters.toString()}`),t.tests===0&&t.coverage!==0&&i.push(...x(c.coverage,`exit code ${t.coverage.toString()}`)),t.close!==0&&i.push(...x(c.close,`exit code ${t.close.toString()} while Vitest closed`)),i}}}function x(e,t){return e.length>0?e:[t]}function y(e){const t=v.get(d(e));if(t===void 0)throw new Error(`Vitest selected ${e}, which Testkit did not request`);return t}function M(e){if(!$(e)||typeof e.message!="string")return u(g(e));const{name:t,stack:c,type:s,VITEST_TEST_PATH:n}=e;return{...u({message:e.message,...typeof t=="string"?{name:t}:{},...typeof c=="string"?{stack:c}:{}}),...typeof s=="string"?{type:s}:{},...typeof n=="string"?{testPath:n}:{}}}function S(e){return new RegExp(`^${e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}$`,"u")}
@@ -1,2 +1,2 @@
1
- import y from"node:fs/promises";import w from"node:path";import{evidenceFileAccess as _,readStableEvidenceFile as O,writeOwnedEvidenceFile as $}from"../../adapters/evidence-files.js";import{NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as W,nodeVitestDataScenarioFile as X}from"../runtime/runtime-input.js";import{dataFlowArtifacts as Y}from"../runtime/data/report.js";import{readVitestReportCases as z,sharedVitestTitles as U,vitestErrorDetails as K,vitestReportErrors as x,vitestTestFailureDetails as q}from"./failure-details.js";import{parseEnvironmentValueMissingFailure as G}from"../../contracts/environment-values.js";import{readNativeReport as D,requireNativeEvidenceJson as H}from"../../adapters/native-report.js";import{isUnknownRecord as g}from"../../../kernel/unknown-values.js";import{toError as N}from"../../../kernel/error-serialization.js";import{isPathWithin as Q}from"../../../process/files/paths.js";import{firstOutputLine as Z}from"../../adapters/execution.js";import{nodeVitestReportOptions as tt}from"./reporter.js";const et=16*1024*1024,k=1024;export const NODE_VITEST_DATA_ARTIFACT_MAX_BYTES=16*1024*1024,NODE_VITEST_DATA_EVIDENCE_MAX_BYTES=64*1024*1024;const rt=".testkit-owned-evidence";export async function collectNodeVitestEvidence(t,e=_){await y.mkdir(t.artifactDirectory,{recursive:!0});const r=await y.realpath(t.artifactDirectory),s=w.join(r,rt);await y.mkdir(s,{mode:448,recursive:!1});const n=[],a=[];t.stdout.length>0&&(a.push({stream:"stdout",text:t.stdout}),n.push(await I(r,"stdout.log","node-vitest-stdout",t.stdout))),t.stderr.length>0&&(a.push({stream:"stderr",text:t.stderr}),n.push(await I(r,"stderr.log","node-vitest-stderr",t.stderr)));const o=await nt(t,s,e);n.push(...o.artifacts);const u=await ot(t,r,s,o.cases,e);return n.push(...u.artifacts),{dataEvidenceError:u.error,environmentFailure:o.environmentFailure,evidence:{artifacts:n,logs:a,metrics:[],...o.summary===null?{}:{native:{vitest:o.summary}}},reportError:o.error,reportErrorCount:o.errorCount,reportFailureMessage:o.failureMessage,reportSummary:o.summary}}async function nt({identity:t,reportFile:e,rootDirectory:r,unhandledErrors:s=[]},n,a){try{const o=await R(e,a);if(!o.exists||o.contents===null)throw new Error("Vitest did not produce its Testkit native report");const u=D(o.value,t).result;if(!g(u)||!g(u.summary))throw new Error("Vitest native report must contain result and summary objects");const f=ut(u.summary),c=z(u.cases,f),l=q(u.failures,r,U(c));if(f.numFailedTests!==l.length)throw new Error("Vitest native report failure count does not match its summary");const h=x(u.errors);if(f.success&&h.length>0)throw new Error("Vitest native report success conflicts with runtime errors");const V=[...h,...s],E=G(u.environmentFailure,"Vitest native report environment failure"),i=await $(n,"testkit-vitest-report.json",E===null?o.contents:Buffer.from(st(o.value))),m=await A(i,"application/json","engine.testkit-vitest-report","testkit-vitest-report","Testkit native Vitest report"),T=await it(n,l,K(V)),p=[m,...T===null?[]:[T]];if(u.pending!==void 0){const v=new Error("Vitest did not finish the file in every project that selects it");return{artifacts:p,cases:c,environmentFailure:null,error:v,errorCount:0,failureMessage:null,summary:null}}return{artifacts:p,cases:c,environmentFailure:E,error:null,errorCount:V.length,failureMessage:typeof l[0]?.message=="string"?l[0].message:V.find(v=>v.message.trim().length>0)?.message??null,summary:f}}catch(o){return{artifacts:[],cases:[],environmentFailure:null,error:N(o),errorCount:0,failureMessage:null,summary:null}}}function st(t){if(!g(t)||!g(t.result))throw new Error("Vitest native report must contain result and summary objects");const e=Object.fromEntries(Object.entries(t.result).filter(([r])=>r!=="environmentFailure"));return JSON.stringify({...t,result:e})}async function it(t,e,r){if(e.length+r.length===0)return null;const s={failures:[...e,...r],phase:"exec"},n=await $(t,"vitest-failure-details.json",Buffer.from(`${JSON.stringify(s,null,2)}
2
- `,"utf8"));return{...await A(n,"application/json","testkit.failure-details","vitest-failure-details",[...e.length===0?[]:[j(e.length,"failed Vitest test")],...r.length===0?[]:[`${j(r.length,"Vitest error")} outside a test`]].join(" and ")),data:s}}function j(t,e){return`${t.toString()} ${e}${t===1?"":"s"}`}async function ot({dataDirectory:t,identity:e},r,s,n,a){const o=w.dirname(X(t,"")),u=[];try{const c=(await y.readdir(o).catch(i=>{if(M(i)&&i.code==="ENOENT")return[];throw i})).filter(i=>i.endsWith(".json")).sort();if(c.length>k)throw new Error(`Data evidence exceeds its ${k.toString()} test limit`);let l=0;const h=[];for(const i of c){const m=`Data scenario ${i}`,T=await ct(r,w.join(o,i),m),{contents:p}=await O(T,NODE_VITEST_DATA_ARTIFACT_MAX_BYTES,m,a);if(l+=p.byteLength,l>NODE_VITEST_DATA_EVIDENCE_MAX_BYTES)throw new Error(`Data evidence exceeds its ${NODE_VITEST_DATA_EVIDENCE_MAX_BYTES.toString()} byte aggregate limit`);h.push({contents:p,label:m,name:i})}const E=h.flatMap(({contents:i,label:m,name:T})=>{const p=F(b(i,m),m),v=d(p.attempt,`${m}.attempt`),J=d(p.repeat,`${m}.repeat`),B=at(p.scenario,`${m}.scenario`),S=n.find(({id:L})=>L===decodeURIComponent(T.slice(0,-5)));return S!==void 0&&S.state!=="skipped"&&S.attempts===v&&S.repeat===J?[{index:n.indexOf(S),scenario:B}]:[]}).sort((i,m)=>i.index-m.index).map(({scenario:i})=>i);for(const i of E.length===0?[]:Y(E,e.sourceFile,e.taskId)){const m=i.contentType==="text/markdown"?".md":".json",T=await $(s,`${i.name}${m}`,Buffer.from(i.contents,"utf8"));u.push({...await A(T,i.contentType,i.kind,i.name,i.summary),...i.kind===W?{data:F(JSON.parse(i.contents),i.name)}:{}})}}catch(f){return{artifacts:u,error:N(f)}}return{artifacts:u,error:null}}function at(t,e){const r=F(t,e);if(P(r.name,`${e}.name`),P(F(r.location,`${e}.location`).path,`${e}.location.path`),r.status!=="failed"&&r.status!=="passed")throw new Error(`${e}.status must be failed or passed`);const n=["budgetFailures","budgets","events","evidence","findings","metrics","sequences"].find(a=>!Array.isArray(r[a]));if(n!==void 0)throw new Error(`${e}.${n} must be an array`);return r}async function I(t,e,r,s){const n=w.join(t,e);return await y.writeFile(n,s,"utf8"),A(n,"text/plain; charset=utf-8","engine.output",r,Z(s)??"captured output")}async function A(t,e,r,s,n){const a=await y.stat(t);if(!a.isFile())throw new Error(`${s} artifact is not a regular file`);return{contentType:e,kind:r,metadata:{sizeBytes:a.size},name:s,path:t,summary:n}}async function R(t,e){let r;try{({contents:r}=await O(t,et,t,e))}catch(n){if(M(n)&&n.code==="ENOENT")return{contents:null,exists:!1,value:null};throw n}const s=b(r,t);return{contents:r,exists:!0,value:s}}function b(t,e){const r=JSON.parse(t.toString("utf8"));return H(r,e),r}function ut(t){const e={numFailedTests:d(t.numFailedTests,"Vitest native report.summary.numFailedTests"),numPassedTests:d(t.numPassedTests,"Vitest native report.summary.numPassedTests"),numPendingTests:d(t.numPendingTests,"Vitest native report.summary.numPendingTests"),numTodoTests:d(t.numTodoTests,"Vitest native report.summary.numTodoTests"),numTotalTests:d(t.numTotalTests,"Vitest native report.summary.numTotalTests"),numFailedTestSuites:d(t.numFailedTestSuites,"Vitest native report.summary.numFailedTestSuites"),numPassedTestSuites:d(t.numPassedTestSuites,"Vitest native report.summary.numPassedTestSuites"),numPendingTestSuites:d(t.numPendingTestSuites,"Vitest native report.summary.numPendingTestSuites"),numTotalTestSuites:d(t.numTotalTestSuites,"Vitest native report.summary.numTotalTestSuites"),success:mt(t.success,"Vitest native report.summary.success")};if(e.numTotalTests!==e.numFailedTests+e.numPassedTests+e.numPendingTests+e.numTodoTests||e.numTotalTestSuites!==e.numFailedTestSuites+e.numPassedTestSuites+e.numPendingTestSuites||e.success&&(e.numFailedTests>0||e.numFailedTestSuites>0||e.numTotalTestSuites===0))throw new Error("Vitest native report summary contains inconsistent counts or success");return e}async function ct(t,e,r){const s=w.isAbsolute(e)?e:w.resolve(t,e),n=await y.lstat(s);if(!n.isFile()||n.isSymbolicLink())throw new Error(`${r} must be a regular non-symbolic-link file`);const a=await y.realpath(s);if(!Q(t,a))throw new Error(`${r} must stay within the task artifact directory`);return a}function F(t,e){if(!g(t))throw new Error(`${e} must contain an object`);const r={};for(const[s,n]of Object.entries(t))r[s]=C(n,`${e}.${s}`);return r}function C(t,e){if(t===null||typeof t=="boolean"||typeof t=="string")return t;if(typeof t=="number"){if(!Number.isFinite(t))throw new Error(`${e} must be finite`);return t}return Array.isArray(t)?t.map((r,s)=>C(r,`${e}[${s.toString()}]`)):F(t,e)}function P(t,e){if(typeof t!="string"||t.trim().length===0)throw new Error(`${e} must be a non-empty string`);return t}function d(t,e){if(typeof t!="number"||!Number.isSafeInteger(t)||t<0)throw new Error(`${e} must be a non-negative safe integer`);return t}function mt(t,e){if(typeof t!="boolean")throw new Error(`${e} must be boolean`);return t}function M(t){return t instanceof Error}export async function readNodeVitestRunReport(t,e,r=_){try{const{identity:s}=tt(t,{reportFile:e,sourceFile:".",taskId:"run"}),{result:n}=D((await R(e,r)).value,s);if(!g(n)||!Number.isSafeInteger(n.exitCode)||!Array.isArray(n.unhandledErrors))throw new Error("Vitest run report must contain an exit code and unhandled errors");const a=n.unhandledErrors,o=n.failure===void 0?[]:[n.failure];x([...a,...o],"Vitest run report errors");const u=c=>g(c)&&[c.testPath,c.type].every(l=>l===void 0||typeof l=="string"),f=n.afterTests===void 0?[]:n.afterTests;if(!Array.isArray(f)||!f.every(c=>typeof c=="string")||!a.every(u)||!o.every(c=>g(c)&&["close","config","run"].includes(c.phase)))throw new Error("Vitest run report errors are malformed");return n}catch(s){return N(s)}}
1
+ import y from"node:fs/promises";import w from"node:path";import{evidenceFileAccess as O,readStableEvidenceFile as x,writeOwnedEvidenceFile as $}from"../../adapters/evidence-files.js";import{NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as X,nodeVitestDataScenarioFile as Y}from"../runtime/runtime-input.js";import{dataFlowArtifacts as z}from"../runtime/data/report.js";import{readVitestReportCases as U,sharedVitestTitles as K,vitestErrorDetails as q,vitestReportErrors as D,vitestTestFailureDetails as G}from"./failure-details.js";import{parseEnvironmentValueMissingFailure as H}from"../../contracts/environment-values.js";import{readNativeReport as k,requireNativeEvidenceJson as Q}from"../../adapters/native-report.js";import{isUnknownRecord as g}from"../../../kernel/unknown-values.js";import{toError as N}from"../../../kernel/error-serialization.js";import{isPathWithin as Z}from"../../../process/files/paths.js";import{firstOutputLine as tt}from"../../adapters/execution.js";import{nodeVitestReportOptions as et}from"./reporter.js";const rt=16*1024*1024,j=1024;export const NODE_VITEST_DATA_ARTIFACT_MAX_BYTES=16*1024*1024,NODE_VITEST_DATA_EVIDENCE_MAX_BYTES=64*1024*1024;const nt=".testkit-owned-evidence";export async function collectNodeVitestEvidence(t,e=O){await y.mkdir(t.artifactDirectory,{recursive:!0});const r=await y.realpath(t.artifactDirectory),s=w.join(r,nt);await y.mkdir(s,{mode:448,recursive:!1});const n=[],a=[];t.stdout.length>0&&(a.push({stream:"stdout",text:t.stdout}),n.push(await R(r,"stdout.log","node-vitest-stdout",t.stdout))),t.stderr.length>0&&(a.push({stream:"stderr",text:t.stderr}),n.push(await R(r,"stderr.log","node-vitest-stderr",t.stderr)));const o=await st(t,s,e);n.push(...o.artifacts);const u=await at(t,r,s,o.cases,e);return n.push(...u.artifacts),{dataEvidenceError:u.error,environmentFailure:o.environmentFailure,evidence:{artifacts:n,logs:a,metrics:[],...o.summary===null?{}:{native:{vitest:o.summary}}},reportError:o.error,reportErrorCount:o.errorCount,reportFailureMessage:o.failureMessage,reportSummary:o.summary}}async function st({identity:t,reportFile:e,rootDirectory:r,unhandledErrors:s=[]},n,a){try{const o=await b(e,a);if(!o.exists||o.contents===null)throw new Error("Vitest did not produce its Testkit native report");const u=k(o.value,t).result;if(!g(u)||!g(u.summary))throw new Error("Vitest native report must contain result and summary objects");const f=ct(u.summary),m=U(u.cases,f),l=G(u.failures,r,K(m));if(f.numFailedTests!==l.length)throw new Error("Vitest native report failure count does not match its summary");const h=D(u.errors);if(f.success&&h.length>0)throw new Error("Vitest native report success conflicts with runtime errors");const V=[...h,...s],E=H(u.environmentFailure,"Vitest native report environment failure"),i=await $(n,"testkit-vitest-report.json",E===null?o.contents:Buffer.from(it(o.value))),c=await A(i,"application/json","engine.testkit-vitest-report","testkit-vitest-report","Testkit native Vitest report"),T=await ot(n,l,q(V)),p=[c,...T===null?[]:[T]];if(u.pending!==void 0){const v=new Error("Vitest did not finish the file in every project that selects it");return{artifacts:p,cases:m,environmentFailure:null,error:v,errorCount:0,failureMessage:null,summary:null}}return{artifacts:p,cases:m,environmentFailure:E,error:null,errorCount:V.length,failureMessage:typeof l[0]?.message=="string"?l[0].message:V.find(v=>v.message.trim().length>0)?.message??null,summary:f}}catch(o){return{artifacts:[],cases:[],environmentFailure:null,error:N(o),errorCount:0,failureMessage:null,summary:null}}}function it(t){if(!g(t)||!g(t.result))throw new Error("Vitest native report must contain result and summary objects");const e=Object.fromEntries(Object.entries(t.result).filter(([r])=>r!=="environmentFailure"));return JSON.stringify({...t,result:e})}async function ot(t,e,r){if(e.length+r.length===0)return null;const s={failures:[...e,...r],phase:"exec"},n=await $(t,"vitest-failure-details.json",Buffer.from(`${JSON.stringify(s,null,2)}
2
+ `,"utf8"));return{...await A(n,"application/json","testkit.failure-details","vitest-failure-details",[...e.length===0?[]:[I(e.length,"failed Vitest test")],...r.length===0?[]:[`${I(r.length,"Vitest error")} outside a test`]].join(" and ")),data:s}}function I(t,e){return`${t.toString()} ${e}${t===1?"":"s"}`}async function at({dataDirectory:t,identity:e},r,s,n,a){const o=w.dirname(Y(t,"")),u=[];try{const m=(await y.readdir(o).catch(i=>{if(J(i)&&i.code==="ENOENT")return[];throw i})).filter(i=>i.endsWith(".json")).sort();if(m.length>j)throw new Error(`Data evidence exceeds its ${j.toString()} test limit`);let l=0;const h=[];for(const i of m){const c=`Data scenario ${i}`,T=await mt(r,w.join(o,i),c),{contents:p}=await x(T,NODE_VITEST_DATA_ARTIFACT_MAX_BYTES,c,a);if(l+=p.byteLength,l>NODE_VITEST_DATA_EVIDENCE_MAX_BYTES)throw new Error(`Data evidence exceeds its ${NODE_VITEST_DATA_EVIDENCE_MAX_BYTES.toString()} byte aggregate limit`);h.push({contents:p,label:c,name:i})}const E=h.flatMap(({contents:i,label:c,name:T})=>{const p=F(C(i,c),c),v=d(p.attempt,`${c}.attempt`),_=d(p.repeat,`${c}.repeat`),B=d(p.repeats,`${c}.repeats`),L=ut(p.scenario,`${c}.scenario`),S=n.find(({id:W})=>W===decodeURIComponent(T.slice(0,-5)));return S!==void 0&&S.state!=="skipped"&&S.attempts===v&&S.repeat===_&&_===B?[{index:n.indexOf(S),scenario:L}]:[]}).sort((i,c)=>i.index-c.index).map(({scenario:i})=>i);for(const i of E.length===0?[]:z(E,e.sourceFile,e.taskId)){const c=i.contentType==="text/markdown"?".md":".json",T=await $(s,`${i.name}${c}`,Buffer.from(i.contents,"utf8"));u.push({...await A(T,i.contentType,i.kind,i.name,i.summary),...i.kind===X?{data:F(JSON.parse(i.contents),i.name)}:{}})}}catch(f){return{artifacts:u,error:N(f)}}return{artifacts:u,error:null}}function ut(t,e){const r=F(t,e);if(M(r.name,`${e}.name`),M(F(r.location,`${e}.location`).path,`${e}.location.path`),r.status!=="failed"&&r.status!=="passed")throw new Error(`${e}.status must be failed or passed`);const n=["budgetFailures","budgets","events","evidence","findings","metrics","sequences"].find(a=>!Array.isArray(r[a]));if(n!==void 0)throw new Error(`${e}.${n} must be an array`);return r}async function R(t,e,r,s){const n=w.join(t,e);return await y.writeFile(n,s,"utf8"),A(n,"text/plain; charset=utf-8","engine.output",r,tt(s)??"captured output")}async function A(t,e,r,s,n){const a=await y.stat(t);if(!a.isFile())throw new Error(`${s} artifact is not a regular file`);return{contentType:e,kind:r,metadata:{sizeBytes:a.size},name:s,path:t,summary:n}}async function b(t,e){let r;try{({contents:r}=await x(t,rt,t,e))}catch(n){if(J(n)&&n.code==="ENOENT")return{contents:null,exists:!1,value:null};throw n}const s=C(r,t);return{contents:r,exists:!0,value:s}}function C(t,e){const r=JSON.parse(t.toString("utf8"));return Q(r,e),r}function ct(t){const e={numFailedTests:d(t.numFailedTests,"Vitest native report.summary.numFailedTests"),numPassedTests:d(t.numPassedTests,"Vitest native report.summary.numPassedTests"),numPendingTests:d(t.numPendingTests,"Vitest native report.summary.numPendingTests"),numTodoTests:d(t.numTodoTests,"Vitest native report.summary.numTodoTests"),numTotalTests:d(t.numTotalTests,"Vitest native report.summary.numTotalTests"),numFailedTestSuites:d(t.numFailedTestSuites,"Vitest native report.summary.numFailedTestSuites"),numPassedTestSuites:d(t.numPassedTestSuites,"Vitest native report.summary.numPassedTestSuites"),numPendingTestSuites:d(t.numPendingTestSuites,"Vitest native report.summary.numPendingTestSuites"),numTotalTestSuites:d(t.numTotalTestSuites,"Vitest native report.summary.numTotalTestSuites"),success:dt(t.success,"Vitest native report.summary.success")};if(e.numTotalTests!==e.numFailedTests+e.numPassedTests+e.numPendingTests+e.numTodoTests||e.numTotalTestSuites!==e.numFailedTestSuites+e.numPassedTestSuites+e.numPendingTestSuites||e.success&&(e.numFailedTests>0||e.numFailedTestSuites>0||e.numTotalTestSuites===0))throw new Error("Vitest native report summary contains inconsistent counts or success");return e}async function mt(t,e,r){const s=w.isAbsolute(e)?e:w.resolve(t,e),n=await y.lstat(s);if(!n.isFile()||n.isSymbolicLink())throw new Error(`${r} must be a regular non-symbolic-link file`);const a=await y.realpath(s);if(!Z(t,a))throw new Error(`${r} must stay within the task artifact directory`);return a}function F(t,e){if(!g(t))throw new Error(`${e} must contain an object`);const r={};for(const[s,n]of Object.entries(t))r[s]=P(n,`${e}.${s}`);return r}function P(t,e){if(t===null||typeof t=="boolean"||typeof t=="string")return t;if(typeof t=="number"){if(!Number.isFinite(t))throw new Error(`${e} must be finite`);return t}return Array.isArray(t)?t.map((r,s)=>P(r,`${e}[${s.toString()}]`)):F(t,e)}function M(t,e){if(typeof t!="string"||t.trim().length===0)throw new Error(`${e} must be a non-empty string`);return t}function d(t,e){if(typeof t!="number"||!Number.isSafeInteger(t)||t<0)throw new Error(`${e} must be a non-negative safe integer`);return t}function dt(t,e){if(typeof t!="boolean")throw new Error(`${e} must be boolean`);return t}function J(t){return t instanceof Error}export async function readNodeVitestRunReport(t,e,r=O){try{const{identity:s}=et(t,{reportFile:e,sourceFile:".",taskId:"run"}),{result:n}=k((await b(e,r)).value,s);if(!g(n)||!Number.isSafeInteger(n.exitCode)||!Array.isArray(n.unhandledErrors))throw new Error("Vitest run report must contain an exit code and unhandled errors");const a=n.unhandledErrors,o=n.failure===void 0?[]:[n.failure];D([...a,...o],"Vitest run report errors");const u=m=>g(m)&&[m.testPath,m.type].every(l=>l===void 0||typeof l=="string"),f=n.afterTests===void 0?[]:n.afterTests;if(!Array.isArray(f)||!f.every(m=>typeof m=="string")||!a.every(u)||!o.every(m=>g(m)&&["close","config","run"].includes(m.phase)))throw new Error("Vitest run report errors are malformed");return n}catch(s){return N(s)}}
@@ -1,40 +1,24 @@
1
- import s from"node:fs/promises";import e from"node:path";import{fileURLToPath as m,pathToFileURL as i}from"node:url";import{NODE_VITEST_RUNTIME_INPUT_SYMBOL as g}from"../runtime/runtime-input.js";import{ATTEMPT_ERROR_STARTS_META_KEY as p}from"./reporter.js";const r=m(new URL("../../../../dist/engines/node-vitest/",import.meta.url));export const NODE_VITEST_DRIVER_FILE=e.join(r,"adapter/driver.js"),NODE_VITEST_DATA_RUNNER_FILE=e.join(r,"runtime/data/runner.js");const u=e.join(r,"runtime/runtime-input.js");export async function writeNodeVitestWorkspace(t){await s.mkdir(e.dirname(t.setupFile),{recursive:!0}),await Promise.all([s.writeFile(t.setupFile,T(t.configFile,t.runtimeInput.expectedProfileId,t.modules),"utf8"),s.writeFile(t.inputBootstrapFile,`const { installNodeVitestRuntimeInput } = await import(${JSON.stringify(i(u).href)});
1
+ import e from"node:fs/promises";import i from"node:path";import{fileURLToPath as a,pathToFileURL as s}from"node:url";import{NODE_VITEST_RUNTIME_INPUT_SYMBOL as l}from"../runtime/runtime-input.js";import{ATTEMPT_ERROR_STARTS_META_KEY as r}from"./reporter.js";const n=a(new URL("../../../../dist/engines/node-vitest/",import.meta.url));export const NODE_VITEST_DRIVER_FILE=i.join(n,"adapter/driver.js");const o=i.join(n,"runtime/runtime-input.js");export async function writeNodeVitestWorkspace(t){await e.mkdir(i.dirname(t.setupFile),{recursive:!0}),await Promise.all([e.writeFile(t.setupFile,p(t.vitestModule),"utf8"),e.writeFile(t.inputBootstrapFile,`const { installNodeVitestRuntimeInput } = await import(${JSON.stringify(s(o).href)});
2
2
  await installNodeVitestRuntimeInput(${JSON.stringify(t.runtimeInput)});
3
- `,"utf8")])}function T(t,a,o){const n=d=>JSON.stringify(i(d).href),f=`
3
+ `,"utf8")])}function p(t){return`
4
4
  const installedRuntimeInput = globalThis[
5
- Symbol.for(${JSON.stringify(g)})
5
+ Symbol.for(${JSON.stringify(l)})
6
6
  ];
7
7
  if (installedRuntimeInput === undefined)
8
8
  throw new Error("Node/Vitest runtime input is not installed");
9
9
  const baseUrl = installedRuntimeInput.baseUrl;
10
10
  if (typeof baseUrl === "string") process.env.BASE_URL = baseUrl;
11
11
  else delete process.env.BASE_URL;
12
- const { beforeEach, expect } = await import(${n(o.vitest)});
13
- const { nodeVitestDataScenarioFile, selectNodeVitestRuntimeTask } = await import(${JSON.stringify(i(u).href)});
12
+ const { beforeEach, expect } = await import(${JSON.stringify(s(t).href)});
13
+ const { nodeVitestDataScenarioFile, selectNodeVitestRuntimeTask } = await import(${JSON.stringify(s(o).href)});
14
14
  const { rmSync } = await import("node:fs");
15
15
  const task = selectNodeVitestRuntimeTask(expect.getState().testPath);
16
16
  process.env.TESTKIT_TEST_FILE = task.context.task.file;
17
17
  process.env.TESTKIT_TEST_ID = task.taskId;
18
18
  process.env.TESTKIT_DATA_ARTIFACT_DIR = task.dataDirectory;
19
- `,l=`
20
19
  beforeEach(({ task: test }) => {
21
20
  rmSync(nodeVitestDataScenarioFile(task.dataDirectory, test.id), { force: true });
22
- const starts = test.meta[${JSON.stringify(p)}] ?? [];
23
- test.meta[${JSON.stringify(p)}] = [...starts, test.result?.errors?.length ?? 0];
21
+ const starts = test.meta[${JSON.stringify(r)}] ?? [];
22
+ test.meta[${JSON.stringify(r)}] = [...starts, test.result?.errors?.length ?? 0];
24
23
  });
25
- `;if(a==="node-vitest")return`${f}${l}`;const c=a==="node-vitest-data"?`
26
- ${R(t)}
27
- registerRepoConfig(testkitRepositoryConfiguration);
28
- const { __testkitRegisterNodeVitestRepositoryConfiguration } =
29
- await import(${n(o["@gfdelarue/testkit/node-vitest/data"])});
30
- __testkitRegisterNodeVitestRepositoryConfiguration(
31
- testkitRepositoryConfiguration,
32
- );`:"registerRepoConfig(null);";return`
33
- ${f}${l}
34
- const { registerRepoConfig } = await import(${n(o["@gfdelarue/testkit/config"])});
35
- ${c}
36
- `}function R(t){return t===null?"const testkitRepositoryConfiguration = null;":`const testkitConfigModule = await import(${JSON.stringify(i(t).href)});
37
- const testkitRepositoryConfiguration =
38
- testkitConfigModule.default ??
39
- testkitConfigModule.config ??
40
- testkitConfigModule;`}
24
+ `}
@@ -1,2 +1,2 @@
1
- import T from"node:path";import{fileURLToPath as m}from"node:url";import{isTypedTestFile as g}from"../../../domain/identifiers.js";import{isUnknownRecord as O}from"../../../kernel/unknown-values.js";import{managedHostProcessInfrastructureError as E,runManagedHostProcess as h}from"../../../process/index.js";import{NODE_VITEST_INVENTORY_MARKER as u,resolveRepositoryVitest as d}from"../selection.js";const R=m(new URL("../../../../dist/engines/node-vitest/inventory/list-files.js",import.meta.url)),j=6e4,I=m(new URL("../../../../",import.meta.url));export async function listPlainVitestFiles(n,e){const o=e.runner==="managed",s=o?d(I):d(n,e.runner==="auto");if(s===null)return[];const a=o?!1:e.config,l=`Vitest ${s.version} at ${s.root} listing test files${a?` for ${a}`:""}`,r=await h({args:[R,JSON.stringify({config:a,project:e.project,root:n,vitest:s})],cwd:n,displayLabel:l,env:o?Object.fromEntries(Object.entries(e.host).filter(([t])=>t!=="NODE_OPTIONS")):e.host,executable:process.execPath,timeoutMs:j}),c=E(l,r);if(c)throw c;const p=(r.exitCode===0?r.stdout.split(/\r?\n/u):[]).filter(t=>t.startsWith(u)).at(-1),f=p===void 0?null:JSON.parse(p.slice(u.length));if(!Array.isArray(f)||!f.every(t=>O(t)&&typeof t.file=="string"&&typeof t.project=="string")){const t=o||e.config!==null||e.project!==null?"":`
2
- Fix the error above, or pass --vitest-runner managed.`;throw new Error(`${l} failed with exit code ${String(r.exitCode)}: ${r.stderr.trim()}${t}`)}return f.map(({file:t,project:i})=>({file:t.split(T.sep).join("/"),project:i})).filter(({file:t})=>!g(t)).sort((t,i)=>t.file.localeCompare(i.file)||t.project.localeCompare(i.project))}
1
+ import T from"node:path";import{fileURLToPath as m}from"node:url";import{isTypedTestFile as O}from"../../../domain/identifiers.js";import{isUnknownRecord as E}from"../../../kernel/unknown-values.js";import{managedHostProcessInfrastructureError as h,runManagedHostProcess as y}from"../../../process/index.js";import{NODE_VITEST_INVENTORY_MARKER as u,resolveRepositoryVitest as d}from"../selection.js";const R=m(new URL("../../../../dist/engines/node-vitest/inventory/list-files.js",import.meta.url)),j=6e4,v=m(new URL("../../../../",import.meta.url));export async function listPlainVitestFiles(n,e){const s=e.runner==="managed",i=s?d(v):d(n,e.runner==="auto");if(i===null)return[];const a=s?!1:e.config,l=`Vitest ${i.version} at ${i.root} listing test files${a?` for ${a}`:""}`,o=await y({args:[R,JSON.stringify({config:a,project:e.project,root:n,vitest:i})],cwd:n,displayLabel:l,env:s?Object.fromEntries(Object.entries(e.host).filter(([t])=>t!=="NODE_OPTIONS")):e.host,executable:process.execPath,timeoutMs:j}),p=h(l,o);if(p)throw p;const c=(o.exitCode===0?o.stdout.split(/\r?\n/u):[]).filter(t=>t.startsWith(u)).at(-1),f=c===void 0?null:JSON.parse(c.slice(u.length));if(!Array.isArray(f)||!f.every(t=>E(t)&&typeof t.file=="string"&&typeof t.project=="string"&&Array.isArray(t.setupFiles)&&t.setupFiles.every(r=>typeof r=="string"))){const t=s||e.config!==null||e.project!==null?"":`
2
+ Fix the error above, or pass --vitest-runner managed.`;throw new Error(`${l} failed with exit code ${String(o.exitCode)}: ${o.stderr.trim()}${t}`)}return f.map(({file:t,project:r,setupFiles:g})=>({file:t.split(T.sep).join("/"),project:r,setupFiles:g})).filter(({file:t})=>!O(t)).sort((t,r)=>t.file.localeCompare(r.file)||t.project.localeCompare(r.project))}
@@ -1,3 +1,3 @@
1
- import c from"node:fs";import a from"node:path";import{importResolvedPackageEntrypoint as p}from"../../installations/package-resolution.js";import{NODE_VITEST_INVENTORY_MARKER as f}from"../selection.js";const t=JSON.parse(process.argv[2]??"");process.env.TEST="true",process.env.VITEST="true",process.env.NODE_ENV??="test";const{createVitest:l}=await p(t.vitest),o=await l("test",{config:t.config??void 0,watch:!1});try{const s=o.projects.map(({name:e})=>JSON.stringify(e));if(t.project!==null&&!s.includes(JSON.stringify(t.project)))throw new Error(`Vitest has no project named ${JSON.stringify(t.project)}; its projects are ${s.join(", ")}`);const i=c.realpathSync(t.root),r=(await o.globTestSpecifications()).filter(({project:e})=>t.project===null||e.name===t.project).map(({moduleId:e,project:n})=>({file:a.relative(i,e),project:n.name}));process.stdout.write(`
2
- ${f}${JSON.stringify(r)}
3
- `)}finally{await o.close()}
1
+ import p from"node:fs";import r from"node:path";import{importResolvedPackageEntrypoint as f}from"../../installations/package-resolution.js";import{NODE_VITEST_INVENTORY_MARKER as l}from"../selection.js";const t=JSON.parse(process.argv[2]??"");process.env.TEST="true",process.env.VITEST="true",process.env.NODE_ENV??="test";const{createVitest:m}=await f(t.vitest),s=await m("test",{config:t.config??void 0,watch:!1});try{const i=s.projects.map(({name:e})=>JSON.stringify(e));if(t.project!==null&&!i.includes(JSON.stringify(t.project)))throw new Error(`Vitest has no project named ${JSON.stringify(t.project)}; its projects are ${i.join(", ")}`);const n=p.realpathSync(t.root),c=(await s.globTestSpecifications()).filter(({project:e})=>t.project===null||e.name===t.project).map(({moduleId:e,project:o})=>({file:r.relative(n,e),project:o.name,setupFiles:o.config.setupFiles.map(a=>r.resolve(o.config.root,a))}));process.stdout.write(`
2
+ ${l}${JSON.stringify(c)}
3
+ `)}finally{await s.close()}
@@ -1 +1 @@
1
- import{expect as e}from"vitest";import{registerRepoConfig as o}from"../../../../config/authoring/runtime.js";export function __testkitRegisterNodeVitestRepositoryConfiguration(t){o(t)}export{e as expect};export{createHttpClient,readDataContext}from"./http.js";export{renderDataFlowMarkdown}from"./report.js";export{dataFlow,dataScenario,measureJsonBytes,stableHash}from"./scenario.js";
1
+ export{renderDataFlowMarkdown}from"./report.js";export{dataFlow,measureJsonBytes,stableHash}from"./scenario.js";
@@ -1,5 +1,5 @@
1
1
  import{stringFromUnknown as m}from"./primitives.js";import i from"node:fs";import h from"node:path";import{NODE_VITEST_DATA_BUDGET_FAILURE_DETAIL_KIND as g,NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND as l,NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_NAME as $,nodeVitestDataScenarioFile as S}from"../runtime-input.js";import{DATA_ARTIFACT_KIND as p,DATA_REPORT_KIND as T,DATA_SCHEMA_VERSION as y}from"./types.js";import{isUnknownArray as A,isUnknownRecord as D}from"../../../../kernel/unknown-values.js";export function writeDataFlowScenario(t,n,e){const r=process.env.TESTKIT_DATA_ARTIFACT_DIR;if(!r)return;const u=S(r,t);i.mkdirSync(h.dirname(u),{recursive:!0}),i.writeFileSync(`${u}.tmp`,JSON.stringify({...n,scenario:e})),i.renameSync(`${u}.tmp`,u)}export function dataFlowArtifacts(t,n,e){const r=_(t,n,e),u=E(r),a=u?.failures.length??0;return[{contentType:"application/json",contents:`${JSON.stringify(r,null,2)}
2
2
  `,kind:p,name:"data-flow",summary:r.summaryText},{contentType:"text/markdown",contents:`${renderDataFlowMarkdown(r)}
3
3
  `,kind:T,name:"data-flow-report",summary:r.summaryText},...u===null?[]:[{contentType:"application/json",contents:`${JSON.stringify(u,null,2)}
4
- `,kind:l,name:$,summary:`${String(a)} failed data-flow budget${a===1?"":"s"}`}]]}function E(t){const n=t.scenarios.flatMap(e=>e.budgetFailures.map(r=>{const u=`${r.target} budget "${r.name}" (${r.operator})`;return{actual:c(r.actual,`${e.name} ${r.name} actual value`),checkName:r.name,count:1,expected:c(r.expected,`${e.name} ${r.name} expected value`),groupPath:[e.name],key:`${e.name} > ${u}`,kind:g,location:e.location,message:r.message,title:u}}));return n.length===0?null:{failures:n,phase:"exec"}}function c(t,n){let e;try{e=JSON.stringify(t)}catch(u){throw new TypeError(`${n} must be JSON-serializable`,{cause:u})}const r=JSON.parse(e);if(!N(r))throw new TypeError(`${n} must be a finite JSON value`);return r}function N(t){const n=[t];for(;n.length>0;){const e=n.pop();if(!(e===null||typeof e=="boolean"||typeof e=="string")){if(typeof e=="number"){if(!Number.isFinite(e))return!1;continue}if(A(e)){n.push(...e);continue}if(!D(e))return!1;n.push(...Object.values(e))}}return!0}function _(t,n,e){const r=t.filter(o=>o.status==="failed").length,u=t.reduce((o,d)=>o+d.budgetFailures.length,0),a=x(t.flatMap(o=>o.findings)),f=`${String(t.length-r)}/${String(t.length)} data scenario(s) passed, ${String(u)} budget failure(s)`;return{schemaVersion:y,kind:p,generatedAt:new Date().toISOString(),file:n,taskId:e,summary:{budgetFailures:u,failed:r,findingCounts:a,passed:t.length-r,scenarios:t.length,status:r>0?"failed":"passed"},summaryText:f,scenarios:t}}export function renderDataFlowMarkdown(t){const n=[];n.push("# Data Flow Report"),n.push(""),n.push(`Status: ${t.summary.status}`),n.push(`Scenarios: ${String(t.summary.passed)}/${String(t.summary.scenarios)} passed`),n.push(`Budget failures: ${String(t.summary.budgetFailures)}`),n.push("");for(const e of t.scenarios)n.push(`## ${e.name}`),n.push(""),n.push(`Status: ${e.status}`),n.push(`Duration: ${String(e.durationMs)}ms`),e.description&&n.push(`Description: ${e.description}`),e.error?.message&&n.push(`Error: ${e.error.message}`),n.push(""),w(n,e.metrics),F(n,e.sequences),b(n,e.evidence),I(n,e.budgets),O(n,e.findings);return n.join(`
4
+ `,kind:l,name:$,summary:`${String(a)} failed data-flow budget${a===1?"":"s"}`}]]}function E(t){const n=t.scenarios.flatMap(e=>e.budgetFailures.map(r=>{const u=`${r.target} budget "${r.name}" (${r.operator})`;return{actual:c(r.actual,`${e.name} ${r.name} actual value`),checkName:r.name,count:1,expected:c(r.expected,`${e.name} ${r.name} expected value`),groupPath:[e.name],key:`${e.name} > ${u}`,kind:g,location:e.location,message:r.message,title:u}}));return n.length===0?null:{failures:n,phase:"exec"}}function c(t,n){let e;try{e=JSON.stringify(t)}catch(u){throw new TypeError(`${n} must be JSON-serializable`,{cause:u})}const r=JSON.parse(e);if(!N(r))throw new TypeError(`${n} must be a finite JSON value`);return r}function N(t){const n=[t];for(;n.length>0;){const e=n.pop();if(!(e===null||typeof e=="boolean"||typeof e=="string")){if(typeof e=="number"){if(!Number.isFinite(e))return!1;continue}if(A(e)){n.push(...e);continue}if(!D(e))return!1;n.push(...Object.values(e))}}return!0}function _(t,n,e){const r=t.filter(o=>o.status==="failed").length,u=t.reduce((o,d)=>o+d.budgetFailures.length,0),a=x(t.flatMap(o=>o.findings)),f=`${String(t.length-r)}/${String(t.length)} data scenario(s) passed, ${String(u)} budget failure(s)`;return{schemaVersion:y,kind:p,generatedAt:new Date().toISOString(),file:n,taskId:e,summary:{budgetFailures:u,failed:r,findingCounts:a,passed:t.length-r,scenarios:t.length,status:r>0?"failed":"passed"},summaryText:f,scenarios:t}}export function renderDataFlowMarkdown(t){const n=[];n.push("# Data Flow Report"),n.push(""),n.push(`Status: ${t.summary.status}`),n.push(`Scenarios: ${String(t.summary.passed)}/${String(t.summary.scenarios)} passed`),n.push(`Budget failures: ${String(t.summary.budgetFailures)}`),n.push("");for(const e of t.scenarios)n.push(`## ${e.name}`),n.push(""),n.push(`Status: ${e.status}`),n.push(`Duration: ${String(e.durationMs)}ms`),e.error?.message&&n.push(`Error: ${e.error.message}`),n.push(""),w(n,e.metrics),F(n,e.sequences),b(n,e.evidence),I(n,e.budgets),O(n,e.findings);return n.join(`
5
5
  `).trimEnd()}function b(t,n){if(t.push("### Evidence"),n.length===0){t.push(""),t.push("No structured data-flow evidence recorded."),t.push("");return}t.push(""),t.push("| Kind | Name | Status | Details |"),t.push("| --- | --- | --- | --- |");for(const e of n)t.push(`| ${s(e.kind)} | ${s(e.name)} | ${s(e.status||"")} | ${s(k(e.details))} |`);t.push("")}function w(t,n){if(t.push("### Metrics"),n.length===0){t.push(""),t.push("No metrics recorded."),t.push("");return}t.push(""),t.push("| Metric | Value | Unit | Kind |"),t.push("| --- | ---: | --- | --- |");for(const e of n)t.push(`| ${s(e.name)} | ${V(e.value)} | ${s(e.unit||"")} | ${s(e.kind||"")} |`);t.push("")}function F(t,n){if(t.push("### Sequences"),n.length===0){t.push(""),t.push("No sequences recorded."),t.push("");return}t.push(""),t.push("| Sequence | Values |"),t.push("| --- | --- |");for(const e of n)t.push(`| ${s(e.name)} | ${s(e.values.join(" -> "))} |`);t.push("")}function I(t,n){if(t.push("### Budgets"),n.length===0){t.push(""),t.push("No budgets declared."),t.push("");return}t.push("");for(const e of n)t.push(`- ${e.status.toUpperCase()} ${e.name}: ${e.message}`);t.push("")}function O(t,n){if(t.push("### Findings"),n.length===0){t.push(""),t.push("No findings recorded."),t.push("");return}t.push("");for(const e of n)t.push(`- ${e.severity.toUpperCase()}: ${e.message}`);t.push("")}function k(t){const n=Object.entries(t||{}).filter(([e])=>e!=="status").slice(0,6);return n.length===0?"":n.map(([e,r])=>`${e}: ${R(r)}`).join(", ")}function R(t){return t==null?"":typeof t=="string"||typeof t=="number"||typeof t=="boolean"?String(t):JSON.stringify(t)}function x(t){const n={info:0,watch:0,waste:0,error:0};for(const e of t)n[e.severity]+=1;return n}function s(t){return m(t??"").replaceAll("|","\\|").replace(/\s+/gu," ").trim()}function V(t){return Number.isInteger(t)?String(t):String(Number(t.toFixed(4)))}
@@ -1,2 +1,2 @@
1
- import{stringFromUnknown as y}from"./primitives.js";import z from"node:crypto";import A from"node:fs";import{parseRuntimeFixtureManifestEntries as D,resolveRuntimeFixtureEntry as T}from"../../../contracts/fixture-manifest.js";import{readNodeVitestRuntimeInput as N}from"../runtime-input.js";import*as $ from"vitest";import{createDataHttpClient as v,readDataContext as C}from"./http.js";import{writeDataFlowScenario as O}from"./report.js";import{isUnknownArray as V,isUnknownRecord as l}from"../../../../kernel/unknown-values.js";const j=/(authorization|cookie|password|secret|token|api[_-]?key|private[_-]?key)/iu,b=String,k=new WeakSet,h=new WeakMap;export function isDataScenarioCase(e){return k.has(e)}export function dataScenario(e,n,r){const{options:t,run:s}=ee(n,r),i=a=>({...C(),fixture:o=>R(o,a),http:async(o={})=>v({...o,recorder:o.recorder||a})});$.test(e,a=>s(B(a,t.description,i)),t.timeoutMs||t.timeout);const u=$.TestRunner.getCurrentSuite().tasks.at(-1);if(u?.type!=="test")throw new Error("dataScenario did not register a native test case");k.add(u)}export function dataFlow(e){return B(e)}function B(e,n,r=()=>({})){const t=e?.task;if(t?.result===void 0||t.result.duration!==void 0||e?.onTestFinished===void 0)throw new Error("dataFlow(context) needs the context of a running Vitest test: test(name, (context) => { dataFlow(context); })");const s=h.get(e);if(s!==void 0)return s;const i=I(_(t),n,{path:process.env.TESTKIT_TEST_FILE??t.file.name,...t.location}),u=L(i,r);return h.set(e,u),e.onTestFinished(()=>{if(h.delete(e),t.result?.state==="skip")return;const a=U(i,t.result),o={attempt:(t.result?.retryCount??0)+1,repeat:t.result?.repeatCount??0};if(O(t.id,o,a),a.budgetFailures.length>0)throw new Error(X(a))}),u}function _(e){const n=[e.name];for(let r=e.suite;r!==void 0&&r!==e.file;r=r.suite)n.unshift(r.name);return n.join(" ")}export function measureJsonBytes(e){return Buffer.byteLength(JSON.stringify(e??null))}export function stableHash(e){const n=w(e);if(n===void 0)throw new TypeError("stableHash(...) requires a serializable value");return z.createHash("sha256").update(n).digest("hex")}function L(e,n){const r={addFinding(t,s,i={}){return e.findings.push({severity:te(t),message:c(s,"finding message"),details:p(i)}),r},actor(t,s={}){return m(e,"actor",t,s),r},budget(t){return F(e,t)},bytes(t,s,i={}){return r.metric(t,s,{...i,kind:"bytes",unit:i.unit||"bytes"})},count(t,s=1,i={}){return r.metric(t,s,{...i,kind:"count",unit:i.unit||"count"})},duration(t,s,i={}){return r.metric(t,s,{...i,kind:"duration",unit:i.unit||"ms"})},event(t,s={}){return e.events.push({atMs:Date.now()-e.startedAtMs,name:c(t,"event name"),details:p(s)}),r},externalCall(t,s={}){const i=g(s);return m(e,"external-call",t,i),typeof i.durationMs=="number"&&r.duration(`${t}.external.duration`,i.durationMs),typeof i.requestBytes=="number"&&r.bytes(`${t}.external.request.bytes`,i.requestBytes),typeof i.responseBytes=="number"&&r.bytes(`${t}.external.response.bytes`,i.responseBytes),typeof i.costMinor=="number"&&r.money(`${t}.external.cost`,i.costMinor),r},expectMetric(t){return F(e,t)},expectSequence(t){return E(e,t)},finding(t,s,i={}){return r.addFinding(t,s,i)},metric(t,s,i={}){return P(e,t,s,i),r},money(t,s,i={}){return r.metric(t,s,{...i,kind:"money",unit:i.unit||"minor"})},ratio(t,s,i={}){return r.metric(t,s,{...i,kind:"ratio",unit:i.unit||"ratio"})},reconcile(t,s={}){return m(e,"reconciliation",t,s),r},record(t,s={}){const i=g(s);return m(e,"record",t,i),typeof i.count=="number"&&r.count(`${t}.record.count`,i.count),r},recordCache(t,{hits:s=0,misses:i=0,reads:u=0,writes:a=0}={}){const o=c(t,"cache metric name");r.count(`${o}.cache.hits`,s),r.count(`${o}.cache.misses`,i),r.count(`${o}.cache.reads`,u),r.count(`${o}.cache.writes`,a);const S=s+i;return S>0&&r.ratio(`${o}.cache.hit_rate`,s/S),r},recordHttpRequest(t){return r.count("http.request.count",1),r.bytes("http.request.bytes",t.bodyBytes||0),r.bytes("http.response.bytes",t.responseBytes||0),r.duration("http.request.duration",t.durationMs||0),r.sequence("http.methods",[t.method]),r.sequence("http.paths",[t.path]),r.event("http.request",{durationMs:t.durationMs||0,method:t.method,path:t.path,requestBytes:t.bodyBytes||0,responseBytes:t.responseBytes||0,status:t.status}),r},recordLlmUsage(t,s={}){const i=c(t,"llm usage name");for(const[u,a]of Object.entries(s))typeof a=="number"&&Number.isFinite(a)&&r.count(`${i}.${se(u)}`,a,{unit:u.toLowerCase().includes("cost")?"minor":"tokens"});return r},recordRpc(t,{method:s,requestBytes:i=0,responseBytes:u=0}={}){const a=c(t,"rpc metric name");return r.count(`${a}.rpc.count`,1),s&&r.sequence(`${a}.rpc.methods`,[s]),r.bytes(`${a}.rpc.request.bytes`,i),r.bytes(`${a}.rpc.response.bytes`,u),r},sequence(t,s,i={}){return K(e,t,s,i),r},sequenceBudget(t){return E(e,t)},system(t,s={}){return m(e,"system",t,s),r},transition(t,s={}){return m(e,"transition",t,s),r}};return Object.freeze(Object.assign(r,n(r)))}function R(e,n=null){const r=J(e),t=A.readFileSync(r.absolutePath);return n?.count("fixture.count",1,{dimensions:{path:r.path}}),n?.bytes("fixture.bytes",t.byteLength,{dimensions:{path:r.path}}),{buffer(){return Buffer.from(t)},contentType:r.contentType,filename:r.filename,json(){return JSON.parse(t.toString("utf8"))},path:r.path,sha256:r.sha256,sizeBytes:r.sizeBytes,text(s="utf8"){return t.toString(s)}}}function J(e){const n=c(e,"fixture name"),r=H();return T(r.entries,n)}function H(){const e=N();return e===null?{entries:[]}:{entries:D(e.fixtures,"Node/Vitest data fixture manifest")}}function F(e,n){const r=c(n,"metric budget name");return{atLeast(t,s={}){return f(e,{name:r,target:"metric",operator:">=",expected:t,message:s.message}),this},atMost(t,s={}){return f(e,{name:r,target:"metric",operator:"<=",expected:t,message:s.message}),this},equals(t,s={}){return f(e,{name:r,target:"metric",operator:"==",expected:t,message:s.message}),this},greaterThan(t,s={}){return f(e,{name:r,target:"metric",operator:">",expected:t,message:s.message}),this},lessThan(t,s={}){return f(e,{name:r,target:"metric",operator:"<",expected:t,message:s.message}),this},required(t={}){return f(e,{name:r,target:"metric",operator:"exists",message:t.message}),this}}}function E(e,n){const r=c(n,"sequence budget name");return{equals(t,s={}){return f(e,{name:r,target:"sequence",operator:"sequence==",expected:x(t),message:s.message}),this},includesOnly(t,s={}){return f(e,{name:r,target:"sequence",operator:"sequenceOnly",expected:x(t),message:s.message}),this},required(t={}){return f(e,{name:r,target:"sequence",operator:"exists",message:t.message}),this}}}function I(e,n,r){return{budgets:[],description:n||null,events:[],evidence:[],findings:[],location:r,metrics:[],name:c(e,"data scenario name"),sequences:[],startedAtMs:Date.now()}}function U(e,n){const{budgets:r,startedAtMs:t,...s}=e,i=r.map(o=>W(e,o)),u=i.filter(o=>o.status==="failed"),a=n?.state==="fail"?re(n.errors?.at(-1)):null;return{...s,budgetFailures:u,budgets:i,durationMs:Date.now()-t,error:a,status:a||u.length>0?"failed":"passed"}}function P(e,n,r,t={}){const s=r;if(!Number.isFinite(s))throw new Error(`Data metric "${n}" must be a finite number`);e.metrics.push({description:d(t.description),dimensions:M(t.dimensions??{}),kind:d(t.kind)||"metric",name:c(n,"metric name"),unit:d(t.unit),value:s})}function K(e,n,r,t={}){const s=x(r).map(i=>b(i));e.sequences.push({description:d(t.description),dimensions:M(t.dimensions??{}),name:c(n,"sequence name"),values:s})}function f(e,n){e.budgets.push({...n,message:d(n.message)})}function m(e,n,r,t={}){const s=g(t);e.evidence.push({atMs:Date.now()-e.startedAtMs,details:p(s),kind:n,name:c(r,`${n} evidence name`),status:d(s.status)||null})}function g(e){return l(e)?e:{value:e}}function W(e,n){const r=n.target==="sequence"?G(e,n.name):Y(e,n.name),t=Z(n,r),s=n.message||Q(n,r,t);return n.target==="metric"?{...n,actual:r,expected:n.expected??null,message:s,status:t?"passed":"failed"}:{...n,actual:r,expected:n.expected??null,message:s,status:t?"passed":"failed"}}function Z(e,n){if(e.target==="metric")return e.operator==="exists"?n!==null:typeof n!="number"?!1:e.operator==="<="?n<=Number(e.expected):e.operator===">="?n>=Number(e.expected):e.operator==="<"?n<Number(e.expected):e.operator===">"?n>Number(e.expected):Object.is(n,Number(e.expected));if(e.operator==="exists")return n!==null;if(!Array.isArray(n))return!1;if(e.operator==="sequence==")return ne(n,e.expected);const r=new Set((e.expected??[]).map(t=>y(t)));return n.every(t=>r.has(t))}function Y(e,n){for(let r=e.metrics.length-1;r>=0;r-=1)if(e.metrics[r].name===n)return e.metrics[r].value;return null}function G(e,n){const r=[];for(const t of e.sequences)t.name===n&&r.push(...t.values);return r.length>0?r:null}function Q(e,n,r){return e.operator==="exists"?r?`${e.target} "${e.name}" was recorded`:`${e.target} "${e.name}" was not recorded`:`${e.target} "${e.name}" expected ${e.operator} ${JSON.stringify(e.expected)}, got ${JSON.stringify(n)}`}function X(e){return[`Data-flow budgets failed in "${e.name}":`,...e.budgetFailures.map(n=>`- ${n.message}`)].join(`
2
- `)}function ee(e,n){if(typeof e=="function")return{options:{},run:e};if(typeof n!="function")throw new Error("dataScenario(name, options?, run) requires a run callback");return{options:e,run:n}}function x(e){return V(e)?[...e]:[e]}function c(e,n){const r=y(e).trim();if(!r)throw new Error(`${n} must be a non-empty string`);return r}function d(e){if(e==null)return null;const n=y(e).trim();return n||null}function te(e){const n=y(e||"info").toLowerCase();return ae(n)?n:"info"}function p(e){if(Array.isArray(e))return e.map(r=>p(r));if(!l(e))return e;const n={};for(const[r,t]of Object.entries(e))n[r]=j.test(r)?"[redacted]":p(t);return n}function re(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack??null}:{message:q(e,"message")??ie(e),name:q(e,"name")??"Error",stack:q(e,"stack")}}function w(e){return Array.isArray(e)?`[${e.map(n=>w(n)).join(",")}]`:l(e)?`{${Object.keys(e).sort().map(n=>{const r=w(e[n]);return`${JSON.stringify(n)}:${r??"undefined"}`}).join(",")}}`:JSON.stringify(e)}function ne(e,n){return!Array.isArray(e)||!Array.isArray(n)||e.length!==n.length?!1:e.every((r,t)=>String(r)===String(n[t]))}function se(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/[^a-zA-Z0-9]+/g,"_").replace(/^_+|_+$/g,"").toLowerCase()}function M(e){const n=p(e);return l(n)?n:{}}function q(e,n){if(!l(e))return null;try{const r=Reflect.get(e,n);return typeof r=="string"?r:null}catch{return null}}function ie(e){try{return b(e)}catch{return"Unknown thrown value"}}function ae(e){return e==="error"||e==="info"||e==="waste"||e==="watch"}
1
+ import{stringFromUnknown as g}from"./primitives.js";import B from"node:crypto";import{writeDataFlowScenario as M}from"./report.js";import{isUnknownArray as z,isUnknownRecord as p}from"../../../../kernel/unknown-values.js";const A=/(authorization|cookie|password|secret|token|api[_-]?key|private[_-]?key)/iu,$=String,y=new WeakMap;export function dataFlow(e){return E(e)}function E(e){const t=e?.task;if(t?.result===void 0||t.result.duration!==void 0||e?.onTestFinished===void 0)throw new Error("dataFlow(context) needs the context of a running Vitest test: test(name, (context) => { dataFlow(context); })");const r=y.get(e);if(r!==void 0)return r;const n=D(F(t),{path:process.env.TESTKIT_TEST_FILE??t.file.name,...t.location}),s=T(n);return y.set(e,s),e.onTestFinished(()=>{if(y.delete(e),t.result?.state==="skip")return;const i=N(n,t.result),a={attempt:(t.result?.retryCount??0)+1,repeat:t.result?.repeatCount??0,repeats:t.repeats??0};if(M(t.id,a,i),i.budgetFailures.length>0)throw new Error(R(i))}),s}function F(e){const t=[e.name];for(let r=e.suite;r!==void 0&&r!==e.file;r=r.suite)t.unshift(r.name);return t.join(" ")}export function measureJsonBytes(e){return Buffer.byteLength(JSON.stringify(e??null))}export function stableHash(e){const t=x(e);if(t===void 0)throw new TypeError("stableHash(...) requires a serializable value");return B.createHash("sha256").update(t).digest("hex")}function T(e){const t={addFinding(r,n,s={}){return e.findings.push({severity:J(r),message:o(n,"finding message"),details:l(s)}),t},actor(r,n={}){return d(e,"actor",r,n),t},budget(r){return S(e,r)},bytes(r,n,s={}){return t.metric(r,n,{...s,kind:"bytes",unit:s.unit||"bytes"})},count(r,n=1,s={}){return t.metric(r,n,{...s,kind:"count",unit:s.unit||"count"})},duration(r,n,s={}){return t.metric(r,n,{...s,kind:"duration",unit:s.unit||"ms"})},event(r,n={}){return e.events.push({atMs:Date.now()-e.startedAtMs,name:o(r,"event name"),details:l(n)}),t},externalCall(r,n={}){const s=h(n);return d(e,"external-call",r,s),typeof s.durationMs=="number"&&t.duration(`${r}.external.duration`,s.durationMs),typeof s.requestBytes=="number"&&t.bytes(`${r}.external.request.bytes`,s.requestBytes),typeof s.responseBytes=="number"&&t.bytes(`${r}.external.response.bytes`,s.responseBytes),typeof s.costMinor=="number"&&t.money(`${r}.external.cost`,s.costMinor),t},expectMetric(r){return S(e,r)},expectSequence(r){return k(e,r)},finding(r,n,s={}){return t.addFinding(r,n,s)},metric(r,n,s={}){return O(e,r,n,s),t},money(r,n,s={}){return t.metric(r,n,{...s,kind:"money",unit:s.unit||"minor"})},ratio(r,n,s={}){return t.metric(r,n,{...s,kind:"ratio",unit:s.unit||"ratio"})},reconcile(r,n={}){return d(e,"reconciliation",r,n),t},record(r,n={}){const s=h(n);return d(e,"record",r,s),typeof s.count=="number"&&t.count(`${r}.record.count`,s.count),t},recordCache(r,{hits:n=0,misses:s=0,reads:i=0,writes:a=0}={}){const c=o(r,"cache metric name");t.count(`${c}.cache.hits`,n),t.count(`${c}.cache.misses`,s),t.count(`${c}.cache.reads`,i),t.count(`${c}.cache.writes`,a);const m=n+s;return m>0&&t.ratio(`${c}.cache.hit_rate`,n/m),t},recordHttpRequest(r){return t.count("http.request.count",1),t.bytes("http.request.bytes",r.bodyBytes||0),t.bytes("http.response.bytes",r.responseBytes||0),t.duration("http.request.duration",r.durationMs||0),t.sequence("http.methods",[r.method]),t.sequence("http.paths",[r.path]),t.event("http.request",{durationMs:r.durationMs||0,method:r.method,path:r.path,requestBytes:r.bodyBytes||0,responseBytes:r.responseBytes||0,status:r.status}),t},recordLlmUsage(r,n={}){const s=o(r,"llm usage name");for(const[i,a]of Object.entries(n))typeof a=="number"&&Number.isFinite(a)&&t.count(`${s}.${I(i)}`,a,{unit:i.toLowerCase().includes("cost")?"minor":"tokens"});return t},recordRpc(r,{method:n,requestBytes:s=0,responseBytes:i=0}={}){const a=o(r,"rpc metric name");return t.count(`${a}.rpc.count`,1),n&&t.sequence(`${a}.rpc.methods`,[n]),t.bytes(`${a}.rpc.request.bytes`,s),t.bytes(`${a}.rpc.response.bytes`,i),t},sequence(r,n,s={}){return v(e,r,n,s),t},sequenceBudget(r){return k(e,r)},system(r,n={}){return d(e,"system",r,n),t},transition(r,n={}){return d(e,"transition",r,n),t}};return Object.freeze(t)}function S(e,t){const r=o(t,"metric budget name");return{atLeast(n,s={}){return u(e,{name:r,target:"metric",operator:">=",expected:n,message:s.message}),this},atMost(n,s={}){return u(e,{name:r,target:"metric",operator:"<=",expected:n,message:s.message}),this},equals(n,s={}){return u(e,{name:r,target:"metric",operator:"==",expected:n,message:s.message}),this},greaterThan(n,s={}){return u(e,{name:r,target:"metric",operator:">",expected:n,message:s.message}),this},lessThan(n,s={}){return u(e,{name:r,target:"metric",operator:"<",expected:n,message:s.message}),this},required(n={}){return u(e,{name:r,target:"metric",operator:"exists",message:n.message}),this}}}function k(e,t){const r=o(t,"sequence budget name");return{equals(n,s={}){return u(e,{name:r,target:"sequence",operator:"sequence==",expected:w(n),message:s.message}),this},includesOnly(n,s={}){return u(e,{name:r,target:"sequence",operator:"sequenceOnly",expected:w(n),message:s.message}),this},required(n={}){return u(e,{name:r,target:"sequence",operator:"exists",message:n.message}),this}}}function D(e,t){return{budgets:[],events:[],evidence:[],findings:[],location:t,metrics:[],name:o(e,"data scenario name"),sequences:[],startedAtMs:Date.now()}}function N(e,t){const{budgets:r,startedAtMs:n,...s}=e,i=r.map(m=>V(e,m)),a=i.filter(m=>m.status==="failed"),c=t?.state==="fail"?U(t.errors?.at(-1)):null;return{...s,budgetFailures:a,budgets:i,durationMs:Date.now()-n,error:c,status:c||a.length>0?"failed":"passed"}}function O(e,t,r,n={}){const s=r;if(!Number.isFinite(s))throw new Error(`Data metric "${t}" must be a finite number`);e.metrics.push({description:f(n.description),dimensions:b(n.dimensions??{}),kind:f(n.kind)||"metric",name:o(t,"metric name"),unit:f(n.unit),value:s})}function v(e,t,r,n={}){const s=w(r).map(i=>$(i));e.sequences.push({description:f(n.description),dimensions:b(n.dimensions??{}),name:o(t,"sequence name"),values:s})}function u(e,t){e.budgets.push({...t,message:f(t.message)})}function d(e,t,r,n={}){const s=h(n);e.evidence.push({atMs:Date.now()-e.startedAtMs,details:l(s),kind:t,name:o(r,`${t} evidence name`),status:f(s.status)||null})}function h(e){return p(e)?e:{value:e}}function V(e,t){const r=t.target==="sequence"?C(e,t.name):j(e,t.name),n=_(t,r),s=t.message||L(t,r,n);return t.target==="metric"?{...t,actual:r,expected:t.expected??null,message:s,status:n?"passed":"failed"}:{...t,actual:r,expected:t.expected??null,message:s,status:n?"passed":"failed"}}function _(e,t){if(e.target==="metric")return e.operator==="exists"?t!==null:typeof t!="number"?!1:e.operator==="<="?t<=Number(e.expected):e.operator===">="?t>=Number(e.expected):e.operator==="<"?t<Number(e.expected):e.operator===">"?t>Number(e.expected):Object.is(t,Number(e.expected));if(e.operator==="exists")return t!==null;if(!Array.isArray(t))return!1;if(e.operator==="sequence==")return H(t,e.expected);const r=new Set((e.expected??[]).map(n=>g(n)));return t.every(n=>r.has(n))}function j(e,t){for(let r=e.metrics.length-1;r>=0;r-=1)if(e.metrics[r].name===t)return e.metrics[r].value;return null}function C(e,t){const r=[];for(const n of e.sequences)n.name===t&&r.push(...n.values);return r.length>0?r:null}function L(e,t,r){return e.operator==="exists"?r?`${e.target} "${e.name}" was recorded`:`${e.target} "${e.name}" was not recorded`:`${e.target} "${e.name}" expected ${e.operator} ${JSON.stringify(e.expected)}, got ${JSON.stringify(t)}`}function R(e){return[`Data-flow budgets failed in "${e.name}":`,...e.budgetFailures.map(t=>`- ${t.message}`)].join(`
2
+ `)}function w(e){return z(e)?[...e]:[e]}function o(e,t){const r=g(e).trim();if(!r)throw new Error(`${t} must be a non-empty string`);return r}function f(e){if(e==null)return null;const t=g(e).trim();return t||null}function J(e){const t=g(e||"info").toLowerCase();return K(t)?t:"info"}function l(e){if(Array.isArray(e))return e.map(r=>l(r));if(!p(e))return e;const t={};for(const[r,n]of Object.entries(e))t[r]=A.test(r)?"[redacted]":l(n);return t}function U(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack??null}:{message:q(e,"message")??P(e),name:q(e,"name")??"Error",stack:q(e,"stack")}}function x(e){return Array.isArray(e)?`[${e.map(t=>x(t)).join(",")}]`:p(e)?`{${Object.keys(e).sort().map(t=>{const r=x(e[t]);return`${JSON.stringify(t)}:${r??"undefined"}`}).join(",")}}`:JSON.stringify(e)}function H(e,t){return!Array.isArray(e)||!Array.isArray(t)||e.length!==t.length?!1:e.every((r,n)=>String(r)===String(t[n]))}function I(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/[^a-zA-Z0-9]+/g,"_").replace(/^_+|_+$/g,"").toLowerCase()}function b(e){const t=l(e);return p(t)?t:{}}function q(e,t){if(!p(e))return null;try{const r=Reflect.get(e,t);return typeof r=="string"?r:null}catch{return null}}function P(e){try{return $(e)}catch{return"Unknown thrown value"}}function K(e){return e==="error"||e==="info"||e==="waste"||e==="watch"}
@@ -1 +1 @@
1
- import c from"node:path";import{readVerifiedOwnedFile as f,writeOwnedRuntimeInput as O}from"../../runtime-input-file.js";import{requireBoundedJsonValue as l,RUNTIME_INPUT_JSON_BOUNDS as R}from"../../adapters/json-bounds.js";import{parseEnvironmentValueRecord as I}from"../../contracts/environment-values.js";import{parseEnvironmentHandleRecord as V}from"../../environment/handles.js";import{ENGINE_PROCESS_RESERVED_ENVIRONMENT_KEYS as D}from"../../../process/environment.js";import{installManagedProcessEnvironmentOverlay as y}from"../../../process/environment-overlay.js";import{isUnknownRecord as d}from"../../../kernel/unknown-values.js";export const NODE_VITEST_RUNTIME_INPUT_VERSION=1,NODE_VITEST_RUNTIME_INPUT_FILENAME="runtime-input.json",NODE_VITEST_RUNTIME_INPUT_FILE_ENV="TESTKIT_NODE_VITEST_INPUT_FILE",NODE_VITEST_RUNTIME_INPUT_SHA256_ENV="TESTKIT_NODE_VITEST_INPUT_SHA256",NODE_VITEST_RUNTIME_INPUT_SYMBOL="elench.testkit.node-vitest.runtime-input.v1";const _="elench.testkit.node-vitest.runtime-task.v1";export const NODE_VITEST_DATA_BUDGET_FAILURE_DETAIL_KIND="data-flow-budget",NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND="testkit.failure-details",NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_NAME="data-flow-failure-details";const a={filename:NODE_VITEST_RUNTIME_INPUT_FILENAME,label:"Node/Vitest runtime input",maxSizeBytes:8*1024*1024},N=512,p=4*1024*1024;export const NODE_VITEST_ENGINE_ENVIRONMENT_KEYS=Object.freeze([NODE_VITEST_RUNTIME_INPUT_FILE_ENV,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV,"TESTKIT_ACTIVE","TESTKIT_DATA_ARTIFACT_DIR","TESTKIT_INVOCATION_ID","TESTKIT_LEASE_DIR","TESTKIT_LEASE_ID","TESTKIT_MODE","TESTKIT_RUNTIME_ID","TESTKIT_TEST_FILE","TESTKIT_TEST_ID","VITEST_POOL_ID","VITEST_WORKER_ID"]);const A=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","UPDATE_SNAPSHOT",...D,...NODE_VITEST_ENGINE_ENVIRONMENT_KEYS].map(e=>e.toUpperCase()));export const MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS=24*1024;export async function writeOwnedNodeVitestRuntimeInput(e,t){return O(e,t,a)}export async function verifyOwnedNodeVitestRuntimeInput(e){await f(e,a)}export function selectNodeVitestForwardedEnvironment(e){const t={...e};return S(t),t}export function assertNodeVitestSpawnEnvironmentBudget(e){if(Object.entries(e).reduce((n,[o,r])=>n+o.length+r.length+2,1)>MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS)throw new Error(`Node/Vitest process environment exceeds the ${MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS.toString()} character environment-block budget`)}export async function installNodeVitestRuntimeInput(e){const t=process.env[NODE_VITEST_RUNTIME_INPUT_FILE_ENV],n=process.env[NODE_VITEST_RUNTIME_INPUT_SHA256_ENV];if(t!==e.expectedFile)return;if(n!==e.expectedSha256)throw new Error("Node/Vitest runtime input digest does not match its prepared value");const o={directory:c.dirname(e.expectedFile),file:e.expectedFile,sha256:e.expectedSha256,sizeBytes:e.expectedSizeBytes},r=await f(o,a);let s;try{s=JSON.parse(r)}catch(h){throw new Error("Node/Vitest runtime input contains invalid JSON",{cause:h})}const i=T(m(s));if(i.invocationId!==e.expectedInvocationId||i.profileId!==e.expectedProfileId)throw new Error("Node/Vitest runtime input identity does not match its invocation");i.host===void 0&&y({environment:i.environment,label:"Node/Vitest"}),Object.defineProperty(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL),{configurable:!0,enumerable:!1,value:i,writable:!1}),i.baseUrl===null?Reflect.deleteProperty(process.env,"BASE_URL"):process.env.BASE_URL=i.baseUrl}function u(){const e=Reflect.get(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL));return e===void 0?null:T(m(e,"Installed Node/Vitest runtime input"))}export function nodeVitestDataScenarioFile(e,t){return c.join(e,"scenarios",`${encodeURIComponent(t)}.json`)}export function selectNodeVitestRuntimeTask(e){const t=u()?.tasks.find(n=>c.resolve(n.file)===c.resolve(e));if(t===void 0)throw new Error(`Node/Vitest runtime input has no task for ${e}`);return Reflect.set(globalThis,Symbol.for(_),t.taskId),t}export function readNodeVitestRuntimeInput(){const e=u();if(e===null)return null;const t=Reflect.get(globalThis,Symbol.for(_)),{tasks:n,...o}=e,r=n.length===1?n[0]:n.find(({taskId:s})=>s===t);if(r===void 0)throw new Error("Node/Vitest runtime input has no selected task");return{...o,...r}}function m(e,t="Node/Vitest runtime input"){if(l(e,t,R),!d(e))throw new Error(`${t} must contain an object`);const n=new Set(["baseUrl","databases","endpoints","engineId","environment","handles","host","invocationId","profileId","tasks","version"]);for(const r of Object.keys(e))if(!n.has(r))throw new Error(`${t} contains unknown field ${r}`);if(e.version!==NODE_VITEST_RUNTIME_INPUT_VERSION)throw new Error(`${t}.version must be 1`);if(e.engineId!=="node-vitest")throw new Error(`${t}.engineId must be "node-vitest"`);if(!g(e.profileId))throw new Error(`${t}.profileId is invalid`);if(e.baseUrl!==null&&typeof e.baseUrl!="string")throw new Error(`${t}.baseUrl must be a string or null`);if(!Array.isArray(e.tasks)||e.tasks.length===0)throw new Error(`${t}.tasks must be a non-empty array`);const o=w(e.environment,`${t}.environment`);return S(o),{baseUrl:e.baseUrl,databases:I(e.databases,`${t}.databases`),endpoints:I(e.endpoints,`${t}.endpoints`),engineId:"node-vitest",environment:o,handles:V(e.handles,`${t}.handles`),...e.host===void 0?{}:{host:w(e.host,`${t}.host`)},invocationId:E(e.invocationId,`${t}.invocationId`),profileId:e.profileId,tasks:e.tasks.map((r,s)=>U(r,`${t}.tasks[${s.toString()}]`)),version:NODE_VITEST_RUNTIME_INPUT_VERSION}}function U(e,t){if(!d(e)||!d(e.context)||!d(e.fixtures))throw new Error(`${t} must be an object with context and fixtures objects`);return{context:e.context,dataDirectory:E(e.dataDirectory,`${t}.dataDirectory`),file:E(e.file,`${t}.file`),fixtures:e.fixtures,...Object.hasOwn(e,"setupData")?{setupData:e.setupData}:{},taskId:E(e.taskId,`${t}.taskId`)}}function S(e){const t=Object.entries(e);if(t.length>N)throw new Error(`Node/Vitest forwarded environment exceeds the ${N.toString()} entry limit`);let n=0;const o=new Set;for(const[r,s]of t){b(r,s,"Node/Vitest forwarded environment");const i=r.toUpperCase();if(o.has(i))throw new Error(`Node/Vitest forwarded environment contains case-insensitive duplicate key ${r}`);if(o.add(i),A.has(i))throw new Error(`Node/Vitest forwarded environment contains reserved key ${r}`);n+=Buffer.byteLength(r)+Buffer.byteLength(s)+2}if(n>p)throw new Error(`Node/Vitest forwarded environment exceeds the ${p.toString()} byte limit`)}function w(e,t){if(!d(e))throw new Error(`${t} must be an object`);const n=Object.entries(e);if(!n.every(o=>typeof o[1]=="string"))throw new Error(`${t} values must all be strings`);return Object.fromEntries(n)}function b(e,t,n){if(e.length===0||e.includes("=")||e.includes("\0"))throw new Error(`${n} contains an invalid variable name`);if(t.includes("\0"))throw new Error(`${n}.${e} contains a NUL character`)}function T(e){if(e!==null&&typeof e=="object"&&!Object.isFrozen(e)){Object.freeze(e);for(const t of Object.values(e))T(t)}return e}function E(e,t){if(typeof e!="string"||e.length===0)throw new Error(`${t} must be a non-empty string`);return e}function g(e){return e==="node-vitest"||e==="node-vitest-contract"||e==="node-vitest-data"}
1
+ import{realpathSync as O,writeFileSync as h}from"node:fs";import c from"node:path";import{readVerifiedOwnedFile as f,writeOwnedRuntimeInput as V}from"../../runtime-input-file.js";import{requireBoundedJsonValue as R,RUNTIME_INPUT_JSON_BOUNDS as D}from"../../adapters/json-bounds.js";import{parseEnvironmentValueRecord as I}from"../../contracts/environment-values.js";import{parseEnvironmentHandleRecord as y}from"../../environment/handles.js";import{ENGINE_PROCESS_RESERVED_ENVIRONMENT_KEYS as g}from"../../../process/engine-environment-keys.js";import{installManagedProcessEnvironmentOverlay as v}from"../../../process/environment-overlay.js";import{isUnknownRecord as d}from"../../../kernel/unknown-values.js";import{toError as A}from"../../../kernel/error-serialization.js";export const NODE_VITEST_RUNTIME_INPUT_VERSION=1,NODE_VITEST_RUNTIME_INPUT_FILENAME="runtime-input.json",NODE_VITEST_RUNTIME_INPUT_FILE_ENV="TESTKIT_NODE_VITEST_INPUT_FILE",NODE_VITEST_RUNTIME_INPUT_SHA256_ENV="TESTKIT_NODE_VITEST_INPUT_SHA256",NODE_VITEST_RUNTIME_INPUT_SYMBOL="@gfdelarue/testkit/node-vitest-runtime-input/v1";const _="@gfdelarue/testkit/node-vitest-runtime-task/v1";export const NODE_VITEST_DATA_BUDGET_FAILURE_DETAIL_KIND="data-flow-budget",NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_KIND="testkit.failure-details",NODE_VITEST_DATA_FAILURE_DETAILS_ARTIFACT_NAME="data-flow-failure-details";const E={filename:NODE_VITEST_RUNTIME_INPUT_FILENAME,label:"Node/Vitest runtime input",maxSizeBytes:8*1024*1024},u=512,p=4*1024*1024;export const NODE_VITEST_ENGINE_ENVIRONMENT_KEYS=Object.freeze([NODE_VITEST_RUNTIME_INPUT_FILE_ENV,NODE_VITEST_RUNTIME_INPUT_SHA256_ENV,"TESTKIT_ACTIVE","TESTKIT_DATA_ARTIFACT_DIR","TESTKIT_INVOCATION_ID","TESTKIT_LEASE_DIR","TESTKIT_LEASE_ID","TESTKIT_MODE","TESTKIT_RUNTIME_ID","TESTKIT_TEST_FILE","TESTKIT_TEST_ID","VITEST_POOL_ID","VITEST_WORKER_ID"]);const U=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","UPDATE_SNAPSHOT",...g,...NODE_VITEST_ENGINE_ENVIRONMENT_KEYS].map(e=>e.toUpperCase()));export const MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS=24*1024;const b=new Set(["DEV","FORCE_TTY","MODE","PROD","SSR","TEST","VITEST","VITEST_MODE"]);export function splitManagedNodeVitestEnvironment(e){const t={},n={};for(const[o,r]of Object.entries(e)){const i=process.platform==="win32"?o.toUpperCase():o;i==="NODE_ENV"||i.startsWith("VITE_")?n[o]=r:b.has(i)||(t[o]=r)}return{overlay:t,process:n}}export async function writeOwnedNodeVitestRuntimeInput(e,t){return V(e,t,E)}export async function verifyOwnedNodeVitestRuntimeInput(e){await f(e,E)}export function selectNodeVitestForwardedEnvironment(e){const t={...e};return S(t),t}export function assertNodeVitestSpawnEnvironmentBudget(e){if(Object.entries(e).reduce((n,[o,r])=>n+o.length+r.length+2,1)>MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS)throw new Error(`Node/Vitest process environment exceeds the ${MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS.toString()} character environment-block budget`)}export async function installNodeVitestRuntimeInput(e){try{await $(e)}catch(t){throw x(e.startupFailure,t),t}}function x(e,t){const n=i=>{try{return O(i)}catch{return c.resolve(i)}},o=process.argv.at(1);if(o===void 0)return;const r=n(o);if(!(r!==n(e.driverFile)&&c.dirname(r)!==n(e.vitestWorkersDirectory)))try{h(e.file,A(t).message,{flag:"wx",mode:384})}catch{}}async function $(e){const t=process.env[NODE_VITEST_RUNTIME_INPUT_FILE_ENV],n=process.env[NODE_VITEST_RUNTIME_INPUT_SHA256_ENV];if(t!==e.expectedFile)return;if(n!==e.expectedSha256)throw new Error("Node/Vitest runtime input digest does not match its prepared value");const o={directory:c.dirname(e.expectedFile),file:e.expectedFile,sha256:e.expectedSha256,sizeBytes:e.expectedSizeBytes},r=await f(o,E);let i;try{i=JSON.parse(r)}catch(l){throw new Error("Node/Vitest runtime input contains invalid JSON",{cause:l})}const s=T(m(i));if(s.invocationId!==e.expectedInvocationId)throw new Error("Node/Vitest runtime input identity does not match its invocation");s.host===void 0&&v({environment:s.environment,label:"Node/Vitest"}),Object.defineProperty(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL),{configurable:!0,enumerable:!1,value:s,writable:!1}),s.baseUrl===null?Reflect.deleteProperty(process.env,"BASE_URL"):process.env.BASE_URL=s.baseUrl}function N(){const e=Reflect.get(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL));return e===void 0?null:T(m(e,"Installed Node/Vitest runtime input"))}export function nodeVitestDataScenarioFile(e,t){return c.join(e,"scenarios",`${encodeURIComponent(t)}.json`)}export function selectNodeVitestRuntimeTask(e){const t=N()?.tasks.find(n=>c.resolve(n.file)===c.resolve(e));if(t===void 0)throw new Error(`Node/Vitest runtime input has no task for ${e}`);return Reflect.set(globalThis,Symbol.for(_),t.taskId),t}export function readNodeVitestRuntimeInput(){const e=N();if(e===null)return null;const t=Reflect.get(globalThis,Symbol.for(_)),{tasks:n,...o}=e,r=n.length===1?n[0]:n.find(({taskId:i})=>i===t);if(r===void 0)throw new Error("Node/Vitest runtime input has no selected task");return{...o,...r}}function m(e,t="Node/Vitest runtime input"){if(R(e,t,D),!d(e))throw new Error(`${t} must contain an object`);const n=new Set(["baseUrl","databases","endpoints","engineId","environment","handles","host","invocationId","profileId","tasks","version"]);for(const r of Object.keys(e))if(!n.has(r))throw new Error(`${t} contains unknown field ${r}`);if(e.version!==NODE_VITEST_RUNTIME_INPUT_VERSION)throw new Error(`${t}.version must be 1`);if(e.engineId!=="node-vitest")throw new Error(`${t}.engineId must be "node-vitest"`);if(!L(e.profileId))throw new Error(`${t}.profileId is invalid`);if(e.baseUrl!==null&&typeof e.baseUrl!="string")throw new Error(`${t}.baseUrl must be a string or null`);if(!Array.isArray(e.tasks)||e.tasks.length===0)throw new Error(`${t}.tasks must be a non-empty array`);const o=w(e.environment,`${t}.environment`);return S(o),{baseUrl:e.baseUrl,databases:I(e.databases,`${t}.databases`),endpoints:I(e.endpoints,`${t}.endpoints`),engineId:"node-vitest",environment:o,handles:y(e.handles,`${t}.handles`),...e.host===void 0?{}:{host:w(e.host,`${t}.host`)},invocationId:a(e.invocationId,`${t}.invocationId`),profileId:e.profileId,tasks:e.tasks.map((r,i)=>M(r,`${t}.tasks[${i.toString()}]`)),version:NODE_VITEST_RUNTIME_INPUT_VERSION}}function M(e,t){if(!d(e)||!d(e.context)||!d(e.fixtures))throw new Error(`${t} must be an object with context and fixtures objects`);return{context:e.context,dataDirectory:a(e.dataDirectory,`${t}.dataDirectory`),file:a(e.file,`${t}.file`),fixtures:e.fixtures,...Object.hasOwn(e,"setupData")?{setupData:e.setupData}:{},taskId:a(e.taskId,`${t}.taskId`)}}function S(e){const t=Object.entries(e);if(t.length>u)throw new Error(`Node/Vitest forwarded environment exceeds the ${u.toString()} entry limit`);let n=0;const o=new Set;for(const[r,i]of t){F(r,i,"Node/Vitest forwarded environment");const s=r.toUpperCase();if(o.has(s))throw new Error(`Node/Vitest forwarded environment contains case-insensitive duplicate key ${r}`);if(o.add(s),U.has(s))throw new Error(`Node/Vitest forwarded environment contains reserved key ${r}`);n+=Buffer.byteLength(r)+Buffer.byteLength(i)+2}if(n>p)throw new Error(`Node/Vitest forwarded environment exceeds the ${p.toString()} byte limit`)}function w(e,t){if(!d(e))throw new Error(`${t} must be an object`);const n=Object.entries(e);if(!n.every(o=>typeof o[1]=="string"))throw new Error(`${t} values must all be strings`);return Object.fromEntries(n)}function F(e,t,n){if(e.length===0||e.includes("=")||e.includes("\0"))throw new Error(`${n} contains an invalid variable name`);if(t.includes("\0"))throw new Error(`${n}.${e} contains a NUL character`)}function T(e){if(e!==null&&typeof e=="object"&&!Object.isFrozen(e)){Object.freeze(e);for(const t of Object.values(e))T(t)}return e}function a(e,t){if(typeof e!="string"||e.length===0)throw new Error(`${t} must be a non-empty string`);return e}function L(e){return e==="node-vitest"}
@@ -1 +1 @@
1
- import{makeTempDirAsync as D}from"../../../process/files/temp-dir.js";import{toError as g}from"../../../kernel/error-serialization.js";import{classifyManagedProcessResult as F,terminalOutcome as T,processOutputEvidence as N,failureFromError as P,firstOutputLine as R,processExitDetails as b,processExitFailure as C,executeEngineTasks as x,managedProcessFailure as M,emptyEvidence as W,taskTimeoutFailure as U}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 $}from"node:timers/promises";import{pathToFileURL as K}from"node:url";import{isEngineTaskDeadlineExceededReason as G,resolveEngineTaskInputs as V}from"../../api/index.js";import{createNativeInvocationProcessEnvironment as j,runManagedProcess as J}from"../../../process/index.js";import{PlaywrightConsumerConfigurationError as z,playwrightExactFileFilter as X,writePlaywrightTaskWorkspace as Q}from"./config.js";import{collectPlaywrightEvidence as Z}from"./evidence.js";import{engineEvidenceIdentity as q}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as rr}from"../../adapters/node-startup-cache.js";const y={codePrefix:"PLAYWRIGHT",label:"Playwright"};export const PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES=24*1024;const L=64;class k extends Error{constructor(r){const e=g(r);super(e.message,{cause:e}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:j,removeOwnedDirectory:ir,writeTaskWorkspace:Q};const tr=new Set(["EBUSY","ENOTEMPTY","EPERM"]),er=5;async function ir(t){for(let r=0;;r+=1)try{await u.rm(t,{force:!0,recursive:!0});return}catch(e){const i=e instanceof Error&&"code"in e?e.code:void 0;if(r===er||typeof i!="string"||!tr.has(i))throw e;await $((r+1)*100)}}export async function executePlaywrightEngine(t,r,e,i=playwrightAdapterDependencies){const o=await or(t,r,i,e);return x(t,o.tasks,y,(a,s)=>nr(t,o,a,s),lr(t,o.installation))}async function or(t,r,e,i){const o=await B(t.workspace.rootDirectory);O(t.workspace.temporaryDirectory,"temporary directory"),O(t.workspace.artifactDirectory,"artifact directory"),await u.mkdir(t.workspace.temporaryDirectory,{recursive:!0}),await u.mkdir(t.workspace.artifactDirectory,{recursive:!0});const a=await u.realpath(t.workspace.temporaryDirectory),s=await u.realpath(t.workspace.artifactDirectory),n=await D(`playwright-${S(t.invocationId)}-`,{parentDir:a});i.defer(()=>e.removeOwnedDirectory(n));const l=await e.createInvocationEnvironment(n);i.defer(()=>e.removeOwnedDirectory(l.temporaryDirectory));const p=[];for(const[h,c]of t.tasks.entries()){const I=V(t,c),v=`${h.toString().padStart(3,"0")}-${S(c.id)}`,_=H.join(n,v),f=await D(`${v}-`,{parentDir:s});try{const m=cr(t,c.serviceName),E=await pr(()=>Y(o,c.file,"Playwright task file")),w=await e.writeTaskWorkspace({artifactDirectory:f,baseUrl:m,browserFile:r.browserExecutable,databases:t.target.databases,endpoints:t.target.endpoints,handles:t.target.handles,inputs:I,invocationId:t.invocationId,managedModuleEntrypoints:{authoring:r.authoringEntrypoints.authoring,cli:r.family.cli,environment:r.authoringEntrypoints.environment,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:E,taskDirectory:_,taskId:c.id});i.defer(async()=>{try{await e.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 ur([_,f],e)).map(w=>w.error);p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:playwrightPreparationFailure(m,E),prepared:null})}}return Object.freeze({installation:r,invocationEnvironment:l.environment,rootDirectory:o,tasks:Object.freeze(p)})}async function nr(t,r,e,i){const a={...await rr(t.workspace.cacheDirectory,e.inputs.environment),...ar(t,r,e)};try{A(a)}catch{delete a.NODE_COMPILE_CACHE}const s=e.task.deadlineMs-Date.now();if(s<=0)return T(e.task.id,"failed",i,W(),U(e.task.timeoutMs,y));const n=await J({args:[r.installation.family.cli,"test",X(e.prepared.sourceFile),"--config",e.prepared.workspace.configFile],cwd:r.rootDirectory,displayLabel:"Playwright",env:a,executable:process.execPath,signal:t.signal,timeoutMs:s});let l;try{l=await Z({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(h){return T(e.task.id,"failed",i,N(n),playwrightEvidenceCollectionFailure(n,h))}const p=classifyPlaywrightProcessResult(n,l,e.task.timeoutMs,G(t.signal.reason));return T(e.task.id,p.status,i,l.evidence,p.failure)}export function classifyPlaywrightProcessResult(t,r,e,i){const o=F(t,e,i,y);if(o!==null)return o;if(r.outputLimit!==null){const n=r.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=r.attachmentFailures.find(n=>n.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(a!==void 0)return{failure:a,status:"failed"};if(r.report.failure!==null){const n=r.report.failure;return{...r.report,failure:{...n,details:{...n.details,evidenceErrors:r.reportError===null?0:1,...b(t)}}}}const s=r.attachmentFailures.at(0);return s!==void 0?{failure:s,status:"failed"}:t.exitCode!==0?C(t,y,R(t.stderr)??R(t.stdout)):r.reportError!==null?{failure:{...P(r.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:r.report}export function playwrightEvidenceCollectionFailure(t,r){const e=g(r);return M(t,y,e)??{...P(e,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function ar(t,r,e){const i={...r.invocationEnvironment},o=e.task,a=e.prepared.workspace.inputBootstrapFile,s=e.prepared.workspace.resolutionBootstrapFile;return Object.assign(i,{...e.prepared.baseUrl===null?{}:{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=sr(s,a),A(i),i}function sr(t,r){return["--experimental-strip-types",`--require=${JSON.stringify(t)}`,`--import=${JSON.stringify(K(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 A(t){if(Object.keys(t).length>L)throw new Error(`Playwright physical spawn environment exceeds its ${L.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 cr(t,r){const e=t.target.baseUrl;if(e===null)return null;if(typeof e!="string"||e.trim().length===0)throw new Error(`Playwright task service ${r} endpoint must be a non-empty URL`);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 lr(t,r){return{...q({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{...P(t,"PLAYWRIGHT_PREPARE_FAILED"),classification:t instanceof k||t instanceof z?"product":"infra"};const e=g(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 pr(t){try{return await t()}catch(r){throw new k(r)}}async function ur(t,r){const e=[];for(const i of t)try{await r.removeOwnedDirectory(i)}catch(o){e.push({directory:i,error:g(o)})}return e}
1
+ import{makeTempDirAsync as D}from"../../../process/files/temp-dir.js";import{toError as g}from"../../../kernel/error-serialization.js";import{classifyManagedProcessResult as A,terminalOutcome as T,processOutputEvidence as N,failureFromError as P,firstOutputLine as R,processExitDetails as M,processExitFailure as b,executeEngineTasks as C,managedProcessFailure as x,emptyEvidence as W,taskTimeoutFailure as U}from"../../adapters/execution.js";import{requireRootDirectory as B,requireAbsoluteWorkspacePath as O,requireConsumerFile as H,safeSegment as S}from"../../adapters/workspace.js";import u from"node:fs/promises";import Y from"node:path";import{setTimeout as $}from"node:timers/promises";import{pathToFileURL as G}from"node:url";import{isEngineTaskDeadlineExceededReason as K,resolveEngineTaskInputs as V}from"../../api/index.js";import{createNativeInvocationProcessEnvironment as j,runManagedProcess as J}from"../../../process/index.js";import{PlaywrightConsumerConfigurationError as z,playwrightFileSuiteGrep as X,writePlaywrightTaskWorkspace as Q}from"./config.js";import{collectPlaywrightEvidence as Z}from"./evidence.js";import{engineEvidenceIdentity as q}from"../../adapters/evidence-identity.js";import{getNodeStartupCacheEnvironment as rr}from"../../adapters/node-startup-cache.js";import{CHROMIUM_STARTUP_ENVIRONMENT as tr}from"../runtime/chromium-environment.js";const y={codePrefix:"PLAYWRIGHT",label:"Playwright"};export const PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES=24*1024;const F=64;class L extends Error{constructor(r){const t=g(r);super(t.message,{cause:t}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:j,removeOwnedDirectory:or,writeTaskWorkspace:Q};const er=new Set(["EBUSY","ENOTEMPTY","EPERM"]),ir=5;async function or(e){for(let r=0;;r+=1)try{await u.rm(e,{force:!0,recursive:!0});return}catch(t){const i=t instanceof Error&&"code"in t?t.code:void 0;if(r===ir||typeof i!="string"||!er.has(i))throw t;await $((r+1)*100)}}export async function executePlaywrightEngine(e,r,t,i=playwrightAdapterDependencies){const o=await nr(e,r,i,t);return C(e,o.tasks,y,(a,s)=>ar(e,o,a,s),pr(e,o.installation))}async function nr(e,r,t,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(()=>t.removeOwnedDirectory(n));const l=await t.createInvocationEnvironment(n);i.defer(()=>t.removeOwnedDirectory(l.temporaryDirectory));const p=[];for(const[h,c]of e.tasks.entries()){const I=V(e,c),v=`${h.toString().padStart(3,"0")}-${S(c.id)}`,_=Y.join(n,v),f=await D(`${v}-`,{parentDir:s});try{const m=lr(e,c.serviceName),E=await ur(()=>H(o,c.file,"Playwright task file")),d=await t.writeTaskWorkspace({artifactDirectory:f,baseUrl:m,browserFile:r.browserExecutable,databases:e.target.databases,endpoints:e.target.endpoints,handles:e.target.handles,inputs:I,invocationId:e.invocationId,managedModuleEntrypoints:{authoring:r.authoringEntrypoints.authoring,cli:r.family.cli,environment:r.authoringEntrypoints.environment,managedTest:r.authoringEntrypoints.managedTest,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:E,taskDirectory:_,taskId:c.id});i.defer(async()=>{try{await t.removeOwnedDirectory(d.attachmentDirectory)}catch(w){if(!(w instanceof Error&&"code"in w&&w.code==="ENOTDIR"))throw w}}),p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:null,prepared:{baseUrl:m,sourceFile:E,workspace:d}})}catch(m){const E=(await yr([_,f],t)).map(d=>d.error);p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:playwrightPreparationFailure(m,E),prepared:null})}}return Object.freeze({installation:r,invocationEnvironment:l.environment,rootDirectory:o,tasks:Object.freeze(p)})}async function ar(e,r,t,i){const a={...await rr(e.workspace.cacheDirectory,t.inputs.environment),...sr(e,r,t)};try{k(a)}catch{delete a.NODE_COMPILE_CACHE}const s=t.task.deadlineMs-Date.now();if(s<=0)return T(t.task.id,"failed",i,W(),U(t.task.timeoutMs,y));const n=await J({args:[r.installation.family.cli,"test","--grep",X(t.prepared.sourceFile),"--config",t.prepared.workspace.configFile],cwd:r.rootDirectory,displayLabel:"Playwright",env:a,executable:process.execPath,signal:e.signal,timeoutMs:s});let l;try{l=await Z({identity:{engineId:"playwright",invocationId:e.invocationId,taskId:t.task.id,sourceFile:t.prepared.sourceFile},artifactDirectory:t.artifactDirectory,outputDirectory:t.prepared.workspace.outputDirectory,reportFile:t.prepared.workspace.reportFile,reportersFinishedFile:t.prepared.workspace.reportersFinishedFile,rootDirectory:r.rootDirectory,sourceFile:t.prepared.sourceFile,stderr:n.stderr,stdout:n.stdout})}catch(h){return T(t.task.id,"failed",i,N(n),playwrightEvidenceCollectionFailure(n,h))}const p=classifyPlaywrightProcessResult(n,l,t.task.timeoutMs,K(e.signal.reason));return T(t.task.id,p.status,i,l.evidence,p.failure)}export function classifyPlaywrightProcessResult(e,r,t,i){const o=A(e,t,i,y);if(o!==null)return o;if(r.outputLimit!==null){const n=r.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=r.attachmentFailures.find(n=>n.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(a!==void 0)return{failure:a,status:"failed"};if(r.report.failure!==null){const n=r.report.failure;return{...r.report,failure:{...n,details:{...n.details,evidenceErrors:r.reportError===null?0:1,...M(e)}}}}const s=r.attachmentFailures.at(0);return s!==void 0?{failure:s,status:"failed"}:e.exitCode!==0?b(e,y,R(e.stderr)??R(e.stdout)):r.reportError!==null?{failure:{...P(r.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:r.report}export function playwrightEvidenceCollectionFailure(e,r){const t=g(r);return x(e,y,t)??{...P(t,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function sr(e,r,t){const i={...r.invocationEnvironment},o=t.task,a=t.prepared.workspace.inputBootstrapFile,s=t.prepared.workspace.resolutionBootstrapFile;return Object.assign(i,{...t.prepared.baseUrl===null?{}:{BASE_URL:t.prepared.baseUrl},...tr,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: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=cr(s,a),k(i),i}function cr(e,r){return["--experimental-strip-types",`--require=${JSON.stringify(e)}`,`--import=${JSON.stringify(G(r).href)}`].join(" ")}export function playwrightSpawnEnvironmentBytes(e){return Object.entries(e).reduce((r,[t,i])=>r+(process.platform==="win32"?(t.length+i.length+2)*2:Buffer.byteLength(t,"utf8")+Buffer.byteLength(i,"utf8")+2),0)}function k(e){if(Object.keys(e).length>F)throw new Error(`Playwright physical spawn environment exceeds its ${F.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 lr(e,r){const t=e.target.baseUrl;if(t===null)return null;if(typeof t!="string"||t.trim().length===0)throw new Error(`Playwright task service ${r} endpoint must be a non-empty URL`);let i;try{i=new URL(t)}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 pr(e,r){return{...q({engineId:"playwright",installationKind:"root-playwright-family",profile:e.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(e,r){if(r.length===0)return{...P(e,"PLAYWRIGHT_PREPARE_FAILED"),classification:e instanceof L||e instanceof z?"product":"infra"};const t=g(e);return{classification:"infra",code:"PLAYWRIGHT_PREPARE_CLEANUP_FAILED",details:{cleanupFailures:r.map(i=>i.message),preparationFailure:t.message},message:`Playwright task preparation failed and its partial workspace could not be removed: ${r.map(i=>i.message).join("; ")}`,...t.stack===void 0?{}:{stack:t.stack}}}async function ur(e){try{return await e()}catch(r){throw new L(r)}}async function yr(e,r){const t=[];for(const i of e)try{await r.removeOwnedDirectory(i)}catch(o){t.push({directory:i,error:g(o)})}return t}
@@ -0,0 +1,4 @@
1
+ import{MAX_DETAILED_REQUESTS as B,MAX_OTHER_ORIGINS as q,MAX_ROWS as h,MAX_STATUSES as g}from"../runtime/browser-network.js";import{nonNegativeInteger as R,requireNonEmptyString as S,requireRecord as O}from"./evidence-validation.js";export const BROWSER_NETWORK_ARTIFACT_KIND="testkit.browser-network",BROWSER_NETWORK_REPORT_KIND="testkit.browser-network.report",MAX_BROWSER_NETWORK_RECORD_BYTES=1024*1024;export function readBrowserNetworkRecord(t){const e=$(JSON.parse(t.toString("utf8")),"record"),s=$(e.totals,"totals");return{totals:{requests:n(s.requests,"totals.requests"),requestBytes:n(s.requestBytes,"totals.requestBytes"),responseBytes:n(s.responseBytes,"totals.responseBytes")},resourceTypes:p(e.resourceTypes,"resourceTypes",Number.MAX_SAFE_INTEGER,T),rows:p(e.rows,"rows",h,E),omittedRows:n(e.omittedRows,"omittedRows"),otherOrigins:p(e.otherOrigins,"otherOrigins",q,v),omittedOrigins:n(e.omittedOrigins,"omittedOrigins"),repeatedWrites:p(e.repeatedWrites,"repeatedWrites",h,_),requests:p(e.requests,"requests",B,M),omittedRequests:n(e.omittedRequests,"omittedRequests")}}export function renderBrowserNetwork(t,e){const s=e.reduce((o,i)=>o+i.totals.requests,0),r=e.reduce((o,i)=>o+m(i).length,0);return{json:`${JSON.stringify({schemaVersion:1,file:t,tests:e},null,2)}
2
+ `,markdown:`${["# Browser network","",`\`${t}\``,...e.flatMap(w)].join(`
3
+ `)}
4
+ `,summary:[a(e.length,"test"),a(s,"request"),...r>0?[a(r,"lead")]:[]].join(", ")}}function w(t){const e=t.resourceTypes.map(o=>`${o.type} ${c(o.responseBytes)}`).join(", "),s=["",`## ${t.title} (${t.status})`,"",`${a(t.totals.requests,"request")}, ${c(t.totals.responseBytes)} received${e===""?"":`: ${e}`}`];if(t.rows.length>0&&s.push("","| Service | Request | Count | Sent | Received | Status |","| ------- | ------- | ----- | ---- | -------- | ------ |",...t.rows.map(o=>`| ${f(o.service)} | ${f(`${o.method} ${o.path}`)} | ${o.requests.toString()} | ${c(o.requestBytes)} | ${c(o.responseBytes)} | ${y(o)} |`)),t.omittedRows>0&&s.push("",`${a(t.omittedRows,"more request pattern")} not listed.`),t.otherOrigins.length>0){const o=t.otherOrigins.map(d=>`${d.host}, ${a(d.requests,"request")}, ${c(d.responseBytes)}`),i=t.omittedOrigins>0?`; ${a(t.omittedOrigins,"more origin")}`:"";s.push("",`Other origins: ${o.join("; ")}${i}`)}const r=m(t);return r.length>0&&s.push("","Leads:","",...r.map(o=>`- ${o}`)),s}function m(t){const e=t.repeatedWrites.map(r=>`\`${r.method} ${r.path}\` to ${r.service} went out ${r.requests.toString()} times with the same body.`);if(t.status!=="passed"&&t.status!=="flaky")return e;const s=t.rows.filter(r=>r.failures>0||r.statuses.some(o=>o>=400)).map(r=>`\`${r.method} ${r.path}\` to ${r.service} got ${y(r)} in a test that passed.`);return[...e,...s]}function y(t){const e=t.failures===0?[]:[t.failures===1?"failed":`failed \xD7${t.failures.toString()}`];return[...t.statuses.map(String),...e].join(", ")||"none"}function f(t){return t.replaceAll("|","\\|")}function a(t,e){return`${t.toString()} ${e}${t===1?"":"s"}`}function c(t){if(t<1e3)return`${t.toString()} B`;const e=["KB","MB","GB"];let s=t/1e3,r=0;for(;s>=1e3&&r<e.length-1;)s/=1e3,r+=1;return`${s<10?s.toFixed(1):Math.round(s).toString()} ${e[r]}`}function T(t,e){const s=$(t,e);return{type:u(s.type,`${e}.type`),requests:n(s.requests,`${e}.requests`),responseBytes:n(s.responseBytes,`${e}.responseBytes`)}}function E(t,e){const s=$(t,e);return{service:u(s.service,`${e}.service`),method:u(s.method,`${e}.method`),path:u(s.path,`${e}.path`),requests:n(s.requests,`${e}.requests`),requestBytes:n(s.requestBytes,`${e}.requestBytes`),responseBytes:n(s.responseBytes,`${e}.responseBytes`),statuses:p(s.statuses,`${e}.statuses`,g,n),failures:n(s.failures,`${e}.failures`)}}function v(t,e){const s=$(t,e);return{host:u(s.host,`${e}.host`),requests:n(s.requests,`${e}.requests`),responseBytes:n(s.responseBytes,`${e}.responseBytes`)}}function _(t,e){const s=$(t,e);return{service:u(s.service,`${e}.service`),method:u(s.method,`${e}.method`),path:u(s.path,`${e}.path`),requests:n(s.requests,`${e}.requests`)}}function M(t,e){const s=$(t,e);return{service:u(s.service,`${e}.service`),method:u(s.method,`${e}.method`),path:u(s.path,`${e}.path`),resourceType:u(s.resourceType,`${e}.resourceType`),status:s.status===null?null:n(s.status,`${e}.status`),failure:s.failure===null?null:u(s.failure,`${e}.failure`),requestBytes:n(s.requestBytes,`${e}.requestBytes`),responseBytes:n(s.responseBytes,`${e}.responseBytes`),startMs:n(s.startMs,`${e}.startMs`),durationMs:s.durationMs===null?null:n(s.durationMs,`${e}.durationMs`)}}function p(t,e,s,r){if(!Array.isArray(t))throw new Error(`Browser network ${e} must be an array`);if(t.length>s)throw new Error(`Browser network ${e} exceeds its ${s.toString()} entry limit`);return t.map((o,i)=>r(o,`${e}[${i.toString()}]`))}function $(t,e){return O(t,`Browser network ${e}`)}function u(t,e){return S(t,`Browser network ${e}`)}function n(t,e){return R(t,`Browser network ${e}`)}