@gfdelarue/testkit 0.4.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (787) hide show
  1. package/README.md +724 -495
  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/ci-report.js +2 -2
  5. package/dist/app/bugs/ci-summary.js +3 -3
  6. package/dist/app/bugs/draft.js +3 -3
  7. package/dist/app/bugs/file.js +1 -1
  8. package/dist/app/bugs/gate.js +1 -1
  9. package/dist/app/bugs/guide.js +3 -3
  10. package/dist/app/bugs/issue.js +4 -4
  11. package/dist/app/bugs/known-failures.js +1 -1
  12. package/dist/app/bugs/record.js +1 -1
  13. package/dist/app/bugs/run-reading.js +1 -1
  14. package/dist/app/bugs/supplied-reproduce.js +1 -0
  15. package/dist/app/campaigns/campaign.js +1 -1
  16. package/dist/app/campaigns/index.js +2 -2
  17. package/dist/app/cleanup/index.js +1 -1
  18. package/dist/app/db-schema/prepare.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/doctor/index.js +1 -1
  22. package/dist/app/environments/config-driven.js +1 -1
  23. package/dist/app/environments/environment-file.js +1 -0
  24. package/dist/app/environments/file-setup.js +1 -0
  25. package/dist/app/environments/routing.js +1 -1
  26. package/dist/app/environments/static-values.js +1 -0
  27. package/dist/app/environments/supplied-input.js +1 -0
  28. package/dist/app/environments/supplied.js +1 -1
  29. package/dist/app/environments/task-databases.js +2 -0
  30. package/dist/app/inspect/artifacts.js +1 -1
  31. package/dist/app/inspect/index.js +1 -1
  32. package/dist/app/lint/index.js +1 -1
  33. package/dist/app/lint/native.js +1 -1
  34. package/dist/app/lint/options.js +1 -1
  35. package/dist/app/lint/rules/engine-imports.js +1 -1
  36. package/dist/app/lint/rules/syntax.js +1 -1
  37. package/dist/app/lint/rules/unit.js +1 -1
  38. package/dist/app/reports/prepare.js +1 -1
  39. package/dist/app/run/caller-redaction.js +1 -0
  40. package/dist/app/run/engine-execution-failures.js +1 -1
  41. package/dist/app/run/engine-execution-reporting.js +1 -1
  42. package/dist/app/run/engine-execution.js +1 -1
  43. package/dist/app/run/engine-orchestration.js +1 -1
  44. package/dist/app/run/environment.js +1 -1
  45. package/dist/app/run/failure-details.js +1 -1
  46. package/dist/app/run/live-run.js +1 -1
  47. package/dist/app/run/orchestrator.js +1 -1
  48. package/dist/app/run/planning.js +1 -1
  49. package/dist/app/run/request.js +1 -1
  50. package/dist/app/run/results.js +1 -1
  51. package/dist/app/run/run-events.js +1 -1
  52. package/dist/app/run/run-finalization.js +1 -1
  53. package/dist/app/run/scheduler/estimates.js +1 -1
  54. package/dist/app/run/scheduler/identity.js +1 -1
  55. package/dist/app/run/scheduler/index.js +1 -1
  56. package/dist/app/run/setup/runtime-node-setup-protocol.js +1 -1
  57. package/dist/app/run/setup/runtime-node-setup-worker.js +1 -1
  58. package/dist/app/run/setup/runtime-node-setup.js +2 -2
  59. package/dist/app/run/task-batches.js +1 -1
  60. package/dist/app/run/worker-loop.js +1 -1
  61. package/dist/app/status/model.js +1 -1
  62. package/dist/app/status/runs.js +1 -1
  63. package/dist/app/typecheck/index.js +4 -4
  64. package/dist/cli/command-catalog.js +1 -1
  65. package/dist/cli/command-flags.js +1 -1
  66. package/dist/cli/commands/artifacts.js +2 -2
  67. package/dist/cli/commands/bug/file.js +2 -2
  68. package/dist/cli/commands/bug/record.js +2 -2
  69. package/dist/cli/commands/discover.js +1 -1
  70. package/dist/cli/commands/guard/unsafe-entrypoint.js +1 -1
  71. package/dist/cli/commands/run.js +2 -2
  72. package/dist/cli/commands/typecheck.js +1 -1
  73. package/dist/cli/commands/workspace.js +1 -1
  74. package/dist/cli/framework/errors.js +1 -1
  75. package/dist/cli/framework/execute.js +7 -6
  76. package/dist/cli/framework/parser.js +1 -1
  77. package/dist/cli/operations/run/operation.js +2 -2
  78. package/dist/cli/renderers/bug/text.js +1 -1
  79. package/dist/cli/renderers/discover/report.js +1 -1
  80. package/dist/cli/renderers/run/events.js +2 -2
  81. package/dist/cli/renderers/run/formatting.js +1 -1
  82. package/dist/cli/renderers/run/status.js +1 -1
  83. package/dist/cli/renderers/run/text-reporter.js +24 -22
  84. package/dist/cli/terminal/colors.js +1 -1
  85. package/dist/cli/terminal/layout.js +2 -2
  86. package/dist/config/authoring/database-fixture-discovery.js +1 -0
  87. package/dist/config/authoring/http-methods.js +1 -1
  88. package/dist/config/authoring/index.js +1 -1
  89. package/dist/config/loader/config-loader.js +1 -1
  90. package/dist/config/loader/decode-discovery.js +1 -1
  91. package/dist/config/loader/decode-execution.js +1 -1
  92. package/dist/config/loader/dependencies.js +1 -1
  93. package/dist/config/loader/discover-tests.js +1 -1
  94. package/dist/config/loader/discovery-summary.js +1 -1
  95. package/dist/config/loader/discovery.js +2 -2
  96. package/dist/config/loader/env.js +1 -1
  97. package/dist/config/loader/file-metadata.js +1 -1
  98. package/dist/config/loader/groups.js +2 -1
  99. package/dist/config/loader/index.js +1 -1
  100. package/dist/config/loader/load.js +1 -1
  101. package/dist/config/loader/managed-configs.js +1 -1
  102. package/dist/config/loader/owned-discovery.js +1 -0
  103. package/dist/config/loader/path-policy.js +1 -1
  104. package/dist/config/loader/runtime-isolation.js +1 -1
  105. package/dist/config/loader/runtime.js +1 -1
  106. package/dist/config/loader/skip-config.js +1 -1
  107. package/dist/config/loader/task-policy.js +1 -0
  108. package/dist/config/loader/test-selection.js +1 -1
  109. package/dist/config/loader/validation.js +1 -1
  110. package/dist/database/admin.js +2 -2
  111. package/dist/database/bindings.js +1 -0
  112. package/dist/database/cleanup.js +1 -1
  113. package/dist/database/configured-steps.js +1 -1
  114. package/dist/database/docker-metadata.js +3 -3
  115. package/dist/database/docker.js +3 -3
  116. package/dist/database/index.js +1 -1
  117. package/dist/database/local-postgres.js +1 -1
  118. package/dist/database/naming.js +1 -1
  119. package/dist/database/postgres/connection-check.js +1 -0
  120. package/dist/database/postgres/protocol.js +2 -0
  121. package/dist/database/schema-operations.js +1 -1
  122. package/dist/database/schema-source.js +3 -3
  123. package/dist/database/source-schema-env.js +1 -0
  124. package/dist/database/state-files.js +1 -1
  125. package/dist/database/state.js +1 -1
  126. package/dist/database/steps.js +3 -3
  127. package/dist/domain/file-timeout.js +1 -1
  128. package/dist/domain/identifiers.js +1 -1
  129. package/dist/domain/retired-test-types.js +1 -0
  130. package/dist/domain/runtime-isolation-contract.js +1 -1
  131. package/dist/domain/suite-selection.js +1 -1
  132. package/dist/domain/test-types.js +1 -1
  133. package/dist/engines/adapters/evidence-files.js +1 -1
  134. package/dist/engines/adapters/execution.js +1 -1
  135. package/dist/engines/adapters/json-bounds.js +2 -0
  136. package/dist/engines/adapters/native-report.js +1 -1
  137. package/dist/engines/api/execution.js +1 -1
  138. package/dist/engines/api/index.js +1 -1
  139. package/dist/engines/api/runtime-artifact.js +1 -1
  140. package/dist/engines/catalog.js +1 -1
  141. package/dist/engines/contracts/environment-values.js +1 -0
  142. package/dist/engines/definitions.js +1 -1
  143. package/dist/engines/environment/handles.js +1 -0
  144. package/dist/engines/environment/index.js +1 -0
  145. package/dist/engines/factory.js +1 -1
  146. package/dist/engines/installations/node-vitest.js +1 -1
  147. package/dist/engines/installations/package-resolution.js +1 -1
  148. package/dist/engines/installations/playwright.js +1 -1
  149. package/dist/engines/managed-module-resolution.js +8 -4
  150. package/dist/engines/manager.js +1 -1
  151. package/dist/engines/node-vitest/adapter/adapter.js +1 -1
  152. package/dist/engines/node-vitest/adapter/driver.js +2 -0
  153. package/dist/engines/node-vitest/adapter/evidence.js +2 -2
  154. package/dist/engines/node-vitest/adapter/failure-details.js +4 -2
  155. package/dist/engines/node-vitest/adapter/reporter.js +1 -1
  156. package/dist/engines/node-vitest/adapter/workspace.js +17 -195
  157. package/dist/engines/node-vitest/inventory/index.js +2 -0
  158. package/dist/engines/node-vitest/inventory/list-files.js +3 -0
  159. package/dist/engines/node-vitest/runtime/data/index.js +1 -1
  160. package/dist/engines/node-vitest/runtime/data/report.js +5 -6
  161. package/dist/engines/node-vitest/runtime/data/scenario.js +2 -2
  162. package/dist/engines/node-vitest/runtime/runtime-input.js +1 -1
  163. package/dist/engines/node-vitest/selection.js +1 -0
  164. package/dist/engines/playwright/adapter/adapter.js +1 -1
  165. package/dist/engines/playwright/adapter/config.js +3 -3
  166. package/dist/engines/playwright/adapter/evidence-validation.js +1 -1
  167. package/dist/engines/playwright/adapter/evidence.js +1 -1
  168. package/dist/engines/playwright/adapter/reporter.js +1 -1
  169. package/dist/engines/playwright/runtime/config-file.js +1 -1
  170. package/dist/engines/playwright/runtime/config.js +1 -1
  171. package/dist/engines/playwright/runtime/managed-test.js +1 -0
  172. package/dist/engines/playwright/runtime/runtime-input.js +29 -10
  173. package/dist/engines/runtime-input-file.js +1 -1
  174. package/dist/environment/lifecycle-controller.js +1 -1
  175. package/dist/environment/lifecycle.js +1 -1
  176. package/dist/environment/ready-environment.js +1 -1
  177. package/dist/environment/run-environment.js +1 -1
  178. package/dist/environment/run-stopping.js +1 -1
  179. package/dist/environment/runtime-contexts.js +1 -1
  180. package/dist/environment/runtime-manager.js +1 -1
  181. package/dist/environment/runtime-planning.js +1 -1
  182. package/dist/environment/runtime-preparation.js +1 -1
  183. package/dist/environment/services.js +3 -3
  184. package/dist/environment/template.js +1 -1
  185. package/dist/evidence/artifact-config.js +1 -1
  186. package/dist/evidence/artifacts.js +2 -2
  187. package/dist/evidence/bundles.js +1 -1
  188. package/dist/evidence/environment-record.js +1 -1
  189. package/dist/evidence/index.js +1 -1
  190. package/dist/evidence/maintenance.js +1 -0
  191. package/dist/evidence/read-run.js +1 -1
  192. package/dist/evidence/reading.js +1 -1
  193. package/dist/evidence/redaction.js +4 -0
  194. package/dist/evidence/reporting.js +1 -1
  195. package/dist/evidence/run-model.js +1 -0
  196. package/dist/evidence/storage.js +2 -2
  197. package/dist/evidence/timing.js +1 -1
  198. package/dist/kernel/error-serialization.js +1 -1
  199. package/dist/kernel/known-keys.js +1 -0
  200. package/dist/kernel/redaction-targets.js +1 -0
  201. package/dist/kernel/redaction.js +4 -1
  202. package/dist/kernel/unknown-values.js +1 -1
  203. package/dist/process/environment.js +1 -1
  204. package/dist/process/execa-bundle.json +3 -3
  205. package/dist/process/files/json-file.js +1 -1
  206. package/dist/process/host/platform.js +1 -1
  207. package/dist/process/host-process.js +1 -1
  208. package/dist/process/index.js +1 -1
  209. package/dist/process/locks.js +4 -4
  210. package/dist/process/port-listener.js +2 -0
  211. package/dist/process/process-group.js +1 -1
  212. package/dist/process/process.js +32 -32
  213. package/dist/process/readiness.js +1 -1
  214. package/dist/process/ready-deadline.js +1 -0
  215. package/dist/process/runs/ownership.js +1 -1
  216. package/dist/process/runs/stop.js +1 -1
  217. package/dist/process/service-processes.js +1 -1
  218. package/dist/process/windows-cli-owner.js +3 -3
  219. package/dist/process/windows-native.js +1 -1
  220. package/dist/repo/fingerprint-policy.js +1 -1
  221. package/dist/repo/managed-git.js +1 -1
  222. package/dist/repo/physical-tree.js +1 -1
  223. package/dist/repo/state.js +4 -4
  224. package/dist/steps/build-config.js +1 -1
  225. package/dist/steps/configured-step-testkit-modules.js +1 -1
  226. package/dist/steps/configured-steps.js +1 -1
  227. package/dist/steps/environment.js +1 -1
  228. package/dist/steps/index.js +1 -1
  229. package/dist/toolchains/index.js +3 -3
  230. package/dist/toolchains/semver.js +1 -1
  231. package/dist/toolchains/vitest/index.js +1 -1
  232. package/dist/tracker/redaction.js +1 -2
  233. package/dist/{config/runtime/build-assets.d.ts → types/config-build-assets.d.ts} +5 -3
  234. package/dist/{database/steps.d.ts → types/config-database-steps.d.ts} +7 -7
  235. package/dist/{config/runtime/next-runtime-tsconfig.d.ts → types/config-next-runtime-tsconfig.d.ts} +5 -3
  236. package/dist/types/config.d.ts +422 -0
  237. package/dist/types/drizzle.d.ts +13 -0
  238. package/dist/{config/runtime/env/index.d.ts → types/env.d.ts} +22 -20
  239. package/dist/types/environment.d.ts +46 -0
  240. package/dist/types/index.d.ts +21 -0
  241. package/dist/types/node-vitest-data.d.ts +191 -0
  242. package/dist/types/playwright.d.ts +130 -0
  243. package/dist/types/tooling-vitest.d.ts +13 -0
  244. package/dist/types/workspace.d.ts +633 -0
  245. package/docs/bug-records.md +35 -40
  246. package/docs/engines.md +259 -0
  247. package/docs/release-notes.md +2116 -0
  248. package/docs/test-types.md +37 -85
  249. package/package.json +22 -32
  250. package/testkit-config-schema.md +391 -260
  251. package/dist/app/bugs/areas.d.ts +0 -38
  252. package/dist/app/bugs/assessment.d.ts +0 -9
  253. package/dist/app/bugs/categories.d.ts +0 -41
  254. package/dist/app/bugs/ci-report.d.ts +0 -21
  255. package/dist/app/bugs/ci-summary.d.ts +0 -13
  256. package/dist/app/bugs/draft.d.ts +0 -70
  257. package/dist/app/bugs/facts.d.ts +0 -49
  258. package/dist/app/bugs/file.d.ts +0 -45
  259. package/dist/app/bugs/gate.d.ts +0 -22
  260. package/dist/app/bugs/guide.d.ts +0 -20
  261. package/dist/app/bugs/issue.d.ts +0 -18
  262. package/dist/app/bugs/known-failures.d.ts +0 -49
  263. package/dist/app/bugs/record.d.ts +0 -14
  264. package/dist/app/bugs/run-reading.d.ts +0 -51
  265. package/dist/app/bugs/store.d.ts +0 -34
  266. package/dist/app/campaigns/brief.d.ts +0 -7
  267. package/dist/app/campaigns/campaign.d.ts +0 -14
  268. package/dist/app/campaigns/index.d.ts +0 -38
  269. package/dist/app/campaigns/store.d.ts +0 -5
  270. package/dist/app/campaigns/summary.d.ts +0 -13
  271. package/dist/app/cleanup/index.d.ts +0 -52
  272. package/dist/app/db-schema/prepare.d.ts +0 -11
  273. package/dist/app/db-schema/refresh.d.ts +0 -17
  274. package/dist/app/db-schema/verify.d.ts +0 -11
  275. package/dist/app/destroy/index.d.ts +0 -16
  276. package/dist/app/discover/index.d.ts +0 -8
  277. package/dist/app/doctor/index.d.ts +0 -17
  278. package/dist/app/environments/config-driven.d.ts +0 -5
  279. package/dist/app/environments/routing.d.ts +0 -6
  280. package/dist/app/environments/supplied.d.ts +0 -3
  281. package/dist/app/file-selection.d.ts +0 -1
  282. package/dist/app/inspect/artifacts.d.ts +0 -30
  283. package/dist/app/inspect/index.d.ts +0 -112
  284. package/dist/app/lint/index.d.ts +0 -28
  285. package/dist/app/lint/native.d.ts +0 -10
  286. package/dist/app/lint/options.d.ts +0 -66
  287. package/dist/app/lint/policy.d.ts +0 -21
  288. package/dist/app/lint/rules/data.d.ts +0 -3
  289. package/dist/app/lint/rules/data.js +0 -1
  290. package/dist/app/lint/rules/engine-imports.d.ts +0 -3
  291. package/dist/app/lint/rules/syntax.d.ts +0 -4
  292. package/dist/app/lint/rules/unit.d.ts +0 -3
  293. package/dist/app/lint/shared.d.ts +0 -34
  294. package/dist/app/package-inspection.d.ts +0 -20
  295. package/dist/app/reports/files.d.ts +0 -13
  296. package/dist/app/reports/prepare.d.ts +0 -20
  297. package/dist/app/reports/publish.d.ts +0 -21
  298. package/dist/app/run/endpoint-readiness.d.ts +0 -43
  299. package/dist/app/run/endpoint-readiness.js +0 -1
  300. package/dist/app/run/engine-evidence-persistence.d.ts +0 -39
  301. package/dist/app/run/engine-execution-cleanup.d.ts +0 -8
  302. package/dist/app/run/engine-execution-cleanup.js +0 -1
  303. package/dist/app/run/engine-execution-failures.d.ts +0 -41
  304. package/dist/app/run/engine-execution-observer.d.ts +0 -21
  305. package/dist/app/run/engine-execution-reporting.d.ts +0 -17
  306. package/dist/app/run/engine-execution.d.ts +0 -65
  307. package/dist/app/run/engine-failure-stack.d.ts +0 -1
  308. package/dist/app/run/engine-orchestration.d.ts +0 -30
  309. package/dist/app/run/environment.d.ts +0 -33
  310. package/dist/app/run/failure-details.d.ts +0 -37
  311. package/dist/app/run/live-run.d.ts +0 -29
  312. package/dist/app/run/operation-progress.d.ts +0 -13
  313. package/dist/app/run/orchestrator.d.ts +0 -75
  314. package/dist/app/run/planning-types.d.ts +0 -68
  315. package/dist/app/run/planning.d.ts +0 -14
  316. package/dist/app/run/request.d.ts +0 -30
  317. package/dist/app/run/results.d.ts +0 -161
  318. package/dist/app/run/run-events.d.ts +0 -144
  319. package/dist/app/run/run-finalization.d.ts +0 -56
  320. package/dist/app/run/run-guards.d.ts +0 -3
  321. package/dist/app/run/runtime-failures.d.ts +0 -18
  322. package/dist/app/run/runtime-fixtures.d.ts +0 -2
  323. package/dist/app/run/runtime-task-types.d.ts +0 -20
  324. package/dist/app/run/scheduler/estimates.d.ts +0 -32
  325. package/dist/app/run/scheduler/identity.d.ts +0 -9
  326. package/dist/app/run/scheduler/index.d.ts +0 -124
  327. package/dist/app/run/scheduler/locks.d.ts +0 -6
  328. package/dist/app/run/scheduler/observations.d.ts +0 -20
  329. package/dist/app/run/selection.d.ts +0 -9
  330. package/dist/app/run/setup/runtime-node-setup-errors.d.ts +0 -13
  331. package/dist/app/run/setup/runtime-node-setup-protocol.d.ts +0 -85
  332. package/dist/app/run/setup/runtime-node-setup-worker.d.ts +0 -1
  333. package/dist/app/run/setup/runtime-node-setup.d.ts +0 -48
  334. package/dist/app/run/task-batches.d.ts +0 -23
  335. package/dist/app/run/task-leases.d.ts +0 -15
  336. package/dist/app/run/worker-loop.d.ts +0 -66
  337. package/dist/app/setup/playwright.d.ts +0 -22
  338. package/dist/app/status/index.d.ts +0 -9
  339. package/dist/app/status/model.d.ts +0 -29
  340. package/dist/app/status/runs.d.ts +0 -99
  341. package/dist/app/stop/index.d.ts +0 -14
  342. package/dist/app/tracker/index.d.ts +0 -13
  343. package/dist/app/tracker/remote.d.ts +0 -13
  344. package/dist/app/tracker/token.d.ts +0 -7
  345. package/dist/app/typecheck/index.d.ts +0 -63
  346. package/dist/cli/command-catalog.d.ts +0 -156
  347. package/dist/cli/command-flags.d.ts +0 -19
  348. package/dist/cli/command-registry.d.ts +0 -3
  349. package/dist/cli/commands/artifacts.d.ts +0 -48
  350. package/dist/cli/commands/bug/file.d.ts +0 -16
  351. package/dist/cli/commands/bug/guide.d.ts +0 -5
  352. package/dist/cli/commands/bug/record.d.ts +0 -9
  353. package/dist/cli/commands/campaign/finish.d.ts +0 -10
  354. package/dist/cli/commands/campaign/start.d.ts +0 -7
  355. package/dist/cli/commands/campaign/status.d.ts +0 -13
  356. package/dist/cli/commands/ci/report.d.ts +0 -15
  357. package/dist/cli/commands/cleanup.d.ts +0 -13
  358. package/dist/cli/commands/db/schema/refresh.d.ts +0 -2
  359. package/dist/cli/commands/db/schema/verify.d.ts +0 -2
  360. package/dist/cli/commands/destroy.d.ts +0 -9
  361. package/dist/cli/commands/discover.d.ts +0 -2
  362. package/dist/cli/commands/doctor.d.ts +0 -2
  363. package/dist/cli/commands/guard/unsafe-entrypoint.d.ts +0 -2
  364. package/dist/cli/commands/lint.d.ts +0 -2
  365. package/dist/cli/commands/report/prepare.d.ts +0 -12
  366. package/dist/cli/commands/report/publish.d.ts +0 -13
  367. package/dist/cli/commands/run/status.d.ts +0 -5
  368. package/dist/cli/commands/run/stop.d.ts +0 -7
  369. package/dist/cli/commands/run.d.ts +0 -9
  370. package/dist/cli/commands/setup/playwright.d.ts +0 -2
  371. package/dist/cli/commands/status.d.ts +0 -5
  372. package/dist/cli/commands/stop.d.ts +0 -12
  373. package/dist/cli/commands/typecheck.d.ts +0 -2
  374. package/dist/cli/commands/workspace.d.ts +0 -2
  375. package/dist/cli/entrypoint.d.ts +0 -1
  376. package/dist/cli/framework/command.d.ts +0 -21
  377. package/dist/cli/framework/definitions.d.ts +0 -100
  378. package/dist/cli/framework/errors.d.ts +0 -6
  379. package/dist/cli/framework/execute.d.ts +0 -14
  380. package/dist/cli/framework/help.d.ts +0 -11
  381. package/dist/cli/framework/index.d.ts +0 -3
  382. package/dist/cli/framework/parser.d.ts +0 -6
  383. package/dist/cli/index.d.ts +0 -4
  384. package/dist/cli/json-errors.d.ts +0 -3
  385. package/dist/cli/operations/db/schema/refresh/operation.d.ts +0 -7
  386. package/dist/cli/operations/db/schema/verify/operation.d.ts +0 -7
  387. package/dist/cli/operations/run/operation.d.ts +0 -26
  388. package/dist/cli/process-signal.d.ts +0 -1
  389. package/dist/cli/renderers/bug/text.d.ts +0 -8
  390. package/dist/cli/renderers/campaign/text.d.ts +0 -2
  391. package/dist/cli/renderers/ci/text.d.ts +0 -2
  392. package/dist/cli/renderers/cleanup/format.d.ts +0 -14
  393. package/dist/cli/renderers/db-schema/text.d.ts +0 -4
  394. package/dist/cli/renderers/destroy/text.d.ts +0 -4
  395. package/dist/cli/renderers/discover/report.d.ts +0 -7
  396. package/dist/cli/renderers/discover/text.d.ts +0 -3
  397. package/dist/cli/renderers/doctor/text.d.ts +0 -2
  398. package/dist/cli/renderers/lint/text.d.ts +0 -2
  399. package/dist/cli/renderers/run/events.d.ts +0 -8
  400. package/dist/cli/renderers/run/failure.d.ts +0 -8
  401. package/dist/cli/renderers/run/formatting.d.ts +0 -79
  402. package/dist/cli/renderers/run/status.d.ts +0 -6
  403. package/dist/cli/renderers/run/text-reporter.d.ts +0 -19
  404. package/dist/cli/renderers/status/text.d.ts +0 -2
  405. package/dist/cli/renderers/stop/text.d.ts +0 -7
  406. package/dist/cli/renderers/typecheck/text.d.ts +0 -2
  407. package/dist/cli/terminal/colors.d.ts +0 -10
  408. package/dist/cli/terminal/layout.d.ts +0 -11
  409. package/dist/cli/version-drift.d.ts +0 -25
  410. package/dist/config/authoring/build-config-contract.d.ts +0 -27
  411. package/dist/config/authoring/configured-step-contract.d.ts +0 -18
  412. package/dist/config/authoring/http-execution-contract.d.ts +0 -70
  413. package/dist/config/authoring/http-methods.d.ts +0 -18
  414. package/dist/config/authoring/index.d.ts +0 -372
  415. package/dist/config/authoring/preset-env.d.ts +0 -19
  416. package/dist/config/authoring/runtime.d.ts +0 -64
  417. package/dist/config/authoring/runtime.js +0 -1
  418. package/dist/config/loader/bugs.d.ts +0 -15
  419. package/dist/config/loader/config-loader.d.ts +0 -9
  420. package/dist/config/loader/database-materialization.d.ts +0 -19
  421. package/dist/config/loader/database-roles.d.ts +0 -2
  422. package/dist/config/loader/database.d.ts +0 -55
  423. package/dist/config/loader/decode-discovery.d.ts +0 -5
  424. package/dist/config/loader/decode-execution.d.ts +0 -7
  425. package/dist/config/loader/dependencies.d.ts +0 -7
  426. package/dist/config/loader/discover-tests.d.ts +0 -111
  427. package/dist/config/loader/discovery-summary.d.ts +0 -29
  428. package/dist/config/loader/discovery.d.ts +0 -53
  429. package/dist/config/loader/env.d.ts +0 -4
  430. package/dist/config/loader/execution-model.d.ts +0 -15
  431. package/dist/config/loader/file-metadata.d.ts +0 -19
  432. package/dist/config/loader/groups.d.ts +0 -33
  433. package/dist/config/loader/index.d.ts +0 -10
  434. package/dist/config/loader/load.d.ts +0 -89
  435. package/dist/config/loader/managed-configs.d.ts +0 -23
  436. package/dist/config/loader/path-policy.d.ts +0 -9
  437. package/dist/config/loader/paths.d.ts +0 -5
  438. package/dist/config/loader/runtime-isolation.d.ts +0 -44
  439. package/dist/config/loader/runtime.d.ts +0 -16
  440. package/dist/config/loader/skip-config.d.ts +0 -44
  441. package/dist/config/loader/test-selection.d.ts +0 -15
  442. package/dist/config/loader/validation.d.ts +0 -15
  443. package/dist/config/loader/values.d.ts +0 -3
  444. package/dist/config/loader/values.js +0 -1
  445. package/dist/database/activity-gate.d.ts +0 -22
  446. package/dist/database/admin.d.ts +0 -31
  447. package/dist/database/cleanup.d.ts +0 -36
  448. package/dist/database/configured-steps.d.ts +0 -27
  449. package/dist/database/constants.d.ts +0 -11
  450. package/dist/database/docker-metadata.d.ts +0 -3
  451. package/dist/database/docker.d.ts +0 -65
  452. package/dist/database/drizzle/index.d.ts +0 -13
  453. package/dist/database/fingerprint.d.ts +0 -12
  454. package/dist/database/index.d.ts +0 -39
  455. package/dist/database/lifecycle-cleanup.d.ts +0 -8
  456. package/dist/database/local-postgres.d.ts +0 -71
  457. package/dist/database/naming.d.ts +0 -17
  458. package/dist/database/postgres/index.d.ts +0 -71
  459. package/dist/database/progress.d.ts +0 -6
  460. package/dist/database/roles.d.ts +0 -19
  461. package/dist/database/runtime-config.d.ts +0 -16
  462. package/dist/database/runtime-reservations.d.ts +0 -19
  463. package/dist/database/schema-operations.d.ts +0 -18
  464. package/dist/database/schema-source.d.ts +0 -96
  465. package/dist/database/source-refresh-lock.d.ts +0 -8
  466. package/dist/database/source-url.d.ts +0 -21
  467. package/dist/database/state-files.d.ts +0 -13
  468. package/dist/database/state.d.ts +0 -26
  469. package/dist/database/template-steps.d.ts +0 -39
  470. package/dist/domain/file-timeout.d.ts +0 -25
  471. package/dist/domain/identifiers.d.ts +0 -9
  472. package/dist/domain/operation-display.d.ts +0 -10
  473. package/dist/domain/operation-reason.d.ts +0 -5
  474. package/dist/domain/postgres-route.d.ts +0 -4
  475. package/dist/domain/runtime-identity.d.ts +0 -1
  476. package/dist/domain/runtime-isolation-contract.d.ts +0 -5
  477. package/dist/domain/service-name.d.ts +0 -2
  478. package/dist/domain/suite-selection.d.ts +0 -20
  479. package/dist/domain/test-outcomes.d.ts +0 -2
  480. package/dist/domain/test-types.d.ts +0 -16
  481. package/dist/engines/adapters/evidence-files.d.ts +0 -32
  482. package/dist/engines/adapters/evidence-identity.d.ts +0 -12
  483. package/dist/engines/adapters/execution.d.ts +0 -41
  484. package/dist/engines/adapters/native-report.d.ts +0 -25
  485. package/dist/engines/adapters/node-startup-cache.d.ts +0 -8
  486. package/dist/engines/adapters/workspace.d.ts +0 -4
  487. package/dist/engines/api/capabilities.d.ts +0 -21
  488. package/dist/engines/api/capabilities.js +0 -1
  489. package/dist/engines/api/evidence.d.ts +0 -38
  490. package/dist/engines/api/execution-observer.d.ts +0 -8
  491. package/dist/engines/api/execution-scope.d.ts +0 -17
  492. package/dist/engines/api/execution.d.ts +0 -101
  493. package/dist/engines/api/index.d.ts +0 -5
  494. package/dist/engines/api/k6-runtime-input.d.ts +0 -26
  495. package/dist/engines/api/k6-runtime-input.js +0 -1
  496. package/dist/engines/api/runtime-artifact.d.ts +0 -19
  497. package/dist/engines/bundle-cache.d.ts +0 -32
  498. package/dist/engines/catalog.d.ts +0 -27
  499. package/dist/engines/contracts/fixture-manifest.d.ts +0 -34
  500. package/dist/engines/contracts/json-bounds.d.ts +0 -36
  501. package/dist/engines/contracts/json-bounds.js +0 -2
  502. package/dist/engines/contracts/k6-repository-configuration.d.ts +0 -12
  503. package/dist/engines/contracts/k6-repository-configuration.js +0 -1
  504. package/dist/engines/contracts/test-context.d.ts +0 -15
  505. package/dist/engines/contracts/test-context.js +0 -1
  506. package/dist/engines/definitions.d.ts +0 -23
  507. package/dist/engines/factory.d.ts +0 -6
  508. package/dist/engines/generated-performance.d.ts +0 -1
  509. package/dist/engines/installation-catalog.d.ts +0 -27
  510. package/dist/engines/installation-catalog.js +0 -1
  511. package/dist/engines/installations/k6.d.ts +0 -98
  512. package/dist/engines/installations/k6.js +0 -1
  513. package/dist/engines/installations/node-vitest.d.ts +0 -33
  514. package/dist/engines/installations/package-requirements.d.ts +0 -28
  515. package/dist/engines/installations/package-resolution.d.ts +0 -51
  516. package/dist/engines/installations/playwright-resolution-child.d.ts +0 -1
  517. package/dist/engines/installations/playwright.d.ts +0 -75
  518. package/dist/engines/installations/types.d.ts +0 -10
  519. package/dist/engines/k6/adapter/bundle.d.ts +0 -25
  520. package/dist/engines/k6/adapter/bundle.js +0 -112
  521. package/dist/engines/k6/adapter/evidence.d.ts +0 -15
  522. package/dist/engines/k6/adapter/evidence.js +0 -4
  523. package/dist/engines/k6/adapter/executor.d.ts +0 -13
  524. package/dist/engines/k6/adapter/executor.js +0 -1
  525. package/dist/engines/k6/adapter/index.d.ts +0 -1
  526. package/dist/engines/k6/adapter/index.js +0 -1
  527. package/dist/engines/k6/adapter/input.d.ts +0 -21
  528. package/dist/engines/k6/adapter/input.js +0 -1
  529. package/dist/engines/k6/adapter/native-summary.d.ts +0 -11
  530. package/dist/engines/k6/adapter/native-summary.js +0 -1
  531. package/dist/engines/k6/adapter/repository-configuration.d.ts +0 -2
  532. package/dist/engines/k6/adapter/repository-configuration.js +0 -1
  533. package/dist/engines/k6/authoring-type-paths.d.ts +0 -3
  534. package/dist/engines/k6/authoring-type-paths.js +0 -1
  535. package/dist/engines/k6/runtime/ambient-types/bundle-manifest.json +0 -146
  536. package/dist/engines/k6/runtime/ambient-types/k6/LICENSE +0 -21
  537. package/dist/engines/k6/runtime/ambient-types/k6/browser/index.d.ts +0 -6332
  538. package/dist/engines/k6/runtime/ambient-types/k6/crypto/index.d.ts +0 -218
  539. package/dist/engines/k6/runtime/ambient-types/k6/data/index.d.ts +0 -16
  540. package/dist/engines/k6/runtime/ambient-types/k6/encoding/index.d.ts +0 -46
  541. package/dist/engines/k6/runtime/ambient-types/k6/execution/index.d.ts +0 -127
  542. package/dist/engines/k6/runtime/ambient-types/k6/experimental/csv/index.d.ts +0 -96
  543. package/dist/engines/k6/runtime/ambient-types/k6/experimental/fs/index.d.ts +0 -104
  544. package/dist/engines/k6/runtime/ambient-types/k6/experimental/redis/index.d.ts +0 -821
  545. package/dist/engines/k6/runtime/ambient-types/k6/experimental/streams/index.d.ts +0 -198
  546. package/dist/engines/k6/runtime/ambient-types/k6/global.d.ts +0 -83
  547. package/dist/engines/k6/runtime/ambient-types/k6/html/index.d.ts +0 -1325
  548. package/dist/engines/k6/runtime/ambient-types/k6/http/index.d.ts +0 -806
  549. package/dist/engines/k6/runtime/ambient-types/k6/index.d.ts +0 -126
  550. package/dist/engines/k6/runtime/ambient-types/k6/metrics/index.d.ts +0 -66
  551. package/dist/engines/k6/runtime/ambient-types/k6/net/grpc/index.d.ts +0 -247
  552. package/dist/engines/k6/runtime/ambient-types/k6/options/index.d.ts +0 -459
  553. package/dist/engines/k6/runtime/ambient-types/k6/package.json +0 -79
  554. package/dist/engines/k6/runtime/ambient-types/k6/secrets/index.d.ts +0 -36
  555. package/dist/engines/k6/runtime/ambient-types/k6/timers/index.d.ts +0 -47
  556. package/dist/engines/k6/runtime/ambient-types/k6/websockets/index.d.ts +0 -324
  557. package/dist/engines/k6/runtime/ambient-types/k6/ws/index.d.ts +0 -283
  558. package/dist/engines/k6/runtime/ambient-types/xk6-sql/LICENSE +0 -201
  559. package/dist/engines/k6/runtime/ambient-types/xk6-sql/index.d.ts +0 -397
  560. package/dist/engines/k6/runtime/ambient-types/xk6-sql/package.json +0 -15
  561. package/dist/engines/k6/runtime/artifacts.d.ts +0 -2
  562. package/dist/engines/k6/runtime/artifacts.js +0 -1
  563. package/dist/engines/k6/runtime/checks.d.ts +0 -17
  564. package/dist/engines/k6/runtime/checks.js +0 -1
  565. package/dist/engines/k6/runtime/dal-fixtures.d.ts +0 -24
  566. package/dist/engines/k6/runtime/dal-fixtures.js +0 -1
  567. package/dist/engines/k6/runtime/dal-suite.d.ts +0 -24
  568. package/dist/engines/k6/runtime/dal-suite.js +0 -1
  569. package/dist/engines/k6/runtime/dal.d.ts +0 -10
  570. package/dist/engines/k6/runtime/dal.js +0 -1
  571. package/dist/engines/k6/runtime/fixtures.d.ts +0 -3
  572. package/dist/engines/k6/runtime/fixtures.js +0 -1
  573. package/dist/engines/k6/runtime/http-assertions.d.ts +0 -19
  574. package/dist/engines/k6/runtime/http-assertions.js +0 -1
  575. package/dist/engines/k6/runtime/http-checks.d.ts +0 -19
  576. package/dist/engines/k6/runtime/http-checks.js +0 -1
  577. package/dist/engines/k6/runtime/http-suite-runtime.d.ts +0 -103
  578. package/dist/engines/k6/runtime/http-suite-runtime.js +0 -1
  579. package/dist/engines/k6/runtime/http.d.ts +0 -15
  580. package/dist/engines/k6/runtime/http.js +0 -1
  581. package/dist/engines/k6/runtime/index.d.ts +0 -11
  582. package/dist/engines/k6/runtime/index.js +0 -1
  583. package/dist/engines/k6/runtime/input.d.ts +0 -3
  584. package/dist/engines/k6/runtime/input.js +0 -1
  585. package/dist/engines/k6/runtime/k6-repository-configuration.d.ts +0 -9
  586. package/dist/engines/k6/runtime/k6-repository-configuration.js +0 -1
  587. package/dist/engines/k6/runtime/node-setup.d.ts +0 -13
  588. package/dist/engines/k6/runtime/node-setup.js +0 -1
  589. package/dist/engines/k6/runtime/runtime-globals.d.ts +0 -10
  590. package/dist/engines/k6/runtime/runtime.d.ts +0 -54
  591. package/dist/engines/k6/runtime/runtime.js +0 -1
  592. package/dist/engines/k6/runtime/shared/assertion-details.d.ts +0 -19
  593. package/dist/engines/k6/runtime/shared/assertion-details.js +0 -2
  594. package/dist/engines/k6/runtime/shared/db-normalization.d.ts +0 -19
  595. package/dist/engines/k6/runtime/shared/db-normalization.js +0 -1
  596. package/dist/engines/k6/runtime/shared/error-body.d.ts +0 -3
  597. package/dist/engines/k6/runtime/shared/error-body.js +0 -1
  598. package/dist/engines/k6/runtime/shared/file-data.d.ts +0 -1
  599. package/dist/engines/k6/runtime/shared/file-data.js +0 -1
  600. package/dist/engines/k6/runtime/shared/fixture-engine.d.ts +0 -8
  601. package/dist/engines/k6/runtime/shared/fixture-engine.js +0 -2
  602. package/dist/engines/k6/runtime/shared/http-check-plan.d.ts +0 -29
  603. package/dist/engines/k6/runtime/shared/http-check-plan.js +0 -1
  604. package/dist/engines/k6/runtime/shared/http-parsing.d.ts +0 -10
  605. package/dist/engines/k6/runtime/shared/http-parsing.js +0 -3
  606. package/dist/engines/k6/runtime/shared/ordering.d.ts +0 -2
  607. package/dist/engines/k6/runtime/shared/ordering.js +0 -1
  608. package/dist/engines/k6/runtime/shared/properties.d.ts +0 -1
  609. package/dist/engines/k6/runtime/shared/properties.js +0 -1
  610. package/dist/engines/k6/runtime/suite.d.ts +0 -4
  611. package/dist/engines/k6/runtime/suite.js +0 -1
  612. package/dist/engines/k6/runtime/types.d.ts +0 -244
  613. package/dist/engines/k6/runtime/types.js +0 -1
  614. package/dist/engines/k6/runtime/xk6-sql-driver-postgres.d.ts +0 -7
  615. package/dist/engines/k6/runtime/xk6-sql-driver-postgres.js +0 -1
  616. package/dist/engines/managed-module-resolution-policy-input.d.ts +0 -8
  617. package/dist/engines/managed-module-resolution-policy.d.ts +0 -25
  618. package/dist/engines/managed-module-resolution.d.ts +0 -13
  619. package/dist/engines/manager.d.ts +0 -25
  620. package/dist/engines/node-vitest/adapter/adapter.d.ts +0 -16
  621. package/dist/engines/node-vitest/adapter/consumer-typescript-paths.d.ts +0 -17
  622. package/dist/engines/node-vitest/adapter/consumer-typescript-paths.js +0 -3
  623. package/dist/engines/node-vitest/adapter/evidence.d.ts +0 -33
  624. package/dist/engines/node-vitest/adapter/failure-details.d.ts +0 -3
  625. package/dist/engines/node-vitest/adapter/index.d.ts +0 -1
  626. package/dist/engines/node-vitest/adapter/reporter.d.ts +0 -7
  627. package/dist/engines/node-vitest/adapter/resolution.d.ts +0 -12
  628. package/dist/engines/node-vitest/adapter/resolution.js +0 -1
  629. package/dist/engines/node-vitest/adapter/workspace.d.ts +0 -19
  630. package/dist/engines/node-vitest/runtime/contract/index.d.ts +0 -64
  631. package/dist/engines/node-vitest/runtime/contract/index.js +0 -1
  632. package/dist/engines/node-vitest/runtime/data/http.d.ts +0 -4
  633. package/dist/engines/node-vitest/runtime/data/http.js +0 -1
  634. package/dist/engines/node-vitest/runtime/data/index.d.ts +0 -8
  635. package/dist/engines/node-vitest/runtime/data/primitives.d.ts +0 -1
  636. package/dist/engines/node-vitest/runtime/data/report.d.ts +0 -3
  637. package/dist/engines/node-vitest/runtime/data/runner.d.ts +0 -4
  638. package/dist/engines/node-vitest/runtime/data/runner.js +0 -1
  639. package/dist/engines/node-vitest/runtime/data/scenario.d.ts +0 -11
  640. package/dist/engines/node-vitest/runtime/data/types.d.ts +0 -384
  641. package/dist/engines/node-vitest/runtime/runtime-input.d.ts +0 -64
  642. package/dist/engines/playwright/adapter/adapter.d.ts +0 -18
  643. package/dist/engines/playwright/adapter/attachments.d.ts +0 -26
  644. package/dist/engines/playwright/adapter/config.d.ts +0 -45
  645. package/dist/engines/playwright/adapter/evidence-validation.d.ts +0 -9
  646. package/dist/engines/playwright/adapter/evidence.d.ts +0 -32
  647. package/dist/engines/playwright/adapter/failure-details.d.ts +0 -2
  648. package/dist/engines/playwright/adapter/index.d.ts +0 -3
  649. package/dist/engines/playwright/adapter/reporter.d.ts +0 -12
  650. package/dist/engines/playwright/runtime/config-file.d.ts +0 -2
  651. package/dist/engines/playwright/runtime/config.d.ts +0 -24
  652. package/dist/engines/playwright/runtime/index.d.ts +0 -4
  653. package/dist/engines/playwright/runtime/runtime-input.d.ts +0 -36
  654. package/dist/engines/playwright/runtime/sandbox.d.ts +0 -84
  655. package/dist/engines/runtime-input-file.d.ts +0 -27
  656. package/dist/environment/lifecycle-artifacts.d.ts +0 -7
  657. package/dist/environment/lifecycle-controller.d.ts +0 -60
  658. package/dist/environment/lifecycle.d.ts +0 -32
  659. package/dist/environment/maintenance.d.ts +0 -68
  660. package/dist/environment/planning-types.d.ts +0 -33
  661. package/dist/environment/ports.d.ts +0 -10
  662. package/dist/environment/ready-environment.d.ts +0 -16
  663. package/dist/environment/run-environment.d.ts +0 -39
  664. package/dist/environment/run-stopping.d.ts +0 -13
  665. package/dist/environment/runtime-contexts.d.ts +0 -37
  666. package/dist/environment/runtime-manager.d.ts +0 -87
  667. package/dist/environment/runtime-planning.d.ts +0 -16
  668. package/dist/environment/runtime-preparation.d.ts +0 -30
  669. package/dist/environment/runtime-service.d.ts +0 -12
  670. package/dist/environment/services.d.ts +0 -56
  671. package/dist/environment/state.d.ts +0 -15
  672. package/dist/environment/status-runtime.d.ts +0 -53
  673. package/dist/environment/template.d.ts +0 -164
  674. package/dist/evidence/artifact-config.d.ts +0 -14
  675. package/dist/evidence/artifact-validation.d.ts +0 -4
  676. package/dist/evidence/artifacts.d.ts +0 -75
  677. package/dist/evidence/bundles.d.ts +0 -27
  678. package/dist/evidence/ci-provenance.d.ts +0 -9
  679. package/dist/evidence/contracts.d.ts +0 -154
  680. package/dist/evidence/environment-record.d.ts +0 -22
  681. package/dist/evidence/file-evidence.d.ts +0 -23
  682. package/dist/evidence/finalization.d.ts +0 -9
  683. package/dist/evidence/index.d.ts +0 -8
  684. package/dist/evidence/logs.d.ts +0 -31
  685. package/dist/evidence/operation-timings.d.ts +0 -60
  686. package/dist/evidence/read-bundle.d.ts +0 -4
  687. package/dist/evidence/read-fields.d.ts +0 -21
  688. package/dist/evidence/read-provenance.d.ts +0 -2
  689. package/dist/evidence/read-redaction.d.ts +0 -2
  690. package/dist/evidence/read-retention.d.ts +0 -2
  691. package/dist/evidence/read-run.d.ts +0 -2
  692. package/dist/evidence/reading.d.ts +0 -28
  693. package/dist/evidence/reporting-types.d.ts +0 -349
  694. package/dist/evidence/reporting.d.ts +0 -3
  695. package/dist/evidence/secure-file-copy.d.ts +0 -24
  696. package/dist/evidence/setup-progress.d.ts +0 -63
  697. package/dist/evidence/step-output.d.ts +0 -16
  698. package/dist/evidence/storage-inventory.d.ts +0 -27
  699. package/dist/evidence/storage.d.ts +0 -25
  700. package/dist/evidence/timing.d.ts +0 -23
  701. package/dist/file.d.ts +0 -11
  702. package/dist/index.d.ts +0 -4
  703. package/dist/kernel/error-serialization.d.ts +0 -40
  704. package/dist/kernel/redaction.d.ts +0 -2
  705. package/dist/kernel/unknown-values.d.ts +0 -11
  706. package/dist/kernel/work-metrics.d.ts +0 -6
  707. package/dist/process/capture-output.d.ts +0 -7
  708. package/dist/process/command-shell.d.ts +0 -1
  709. package/dist/process/environment-overlay.d.ts +0 -17
  710. package/dist/process/environment.d.ts +0 -33
  711. package/dist/process/files/contained-path.d.ts +0 -8
  712. package/dist/process/files/directory-status.d.ts +0 -14
  713. package/dist/process/files/dotenv.d.ts +0 -2
  714. package/dist/process/files/file-snapshot.d.ts +0 -34
  715. package/dist/process/files/json-file.d.ts +0 -11
  716. package/dist/process/files/paths.d.ts +0 -3
  717. package/dist/process/files/product-dir.d.ts +0 -1
  718. package/dist/process/files/temp-dir.d.ts +0 -11
  719. package/dist/process/host/metadata.d.ts +0 -6
  720. package/dist/process/host/platform.d.ts +0 -57
  721. package/dist/process/host-command.d.ts +0 -4
  722. package/dist/process/host-inspection-command.d.ts +0 -13
  723. package/dist/process/host-process.d.ts +0 -19
  724. package/dist/process/index.d.ts +0 -9
  725. package/dist/process/lifecycle-fence.d.ts +0 -10
  726. package/dist/process/locks.d.ts +0 -28
  727. package/dist/process/process-contracts.d.ts +0 -78
  728. package/dist/process/process-group.d.ts +0 -12
  729. package/dist/process/process-identity.d.ts +0 -27
  730. package/dist/process/process-limits.d.ts +0 -6
  731. package/dist/process/process.d.ts +0 -12
  732. package/dist/process/readiness.d.ts +0 -43
  733. package/dist/process/recovery.d.ts +0 -3
  734. package/dist/process/runs/inspection.d.ts +0 -5
  735. package/dist/process/runs/manifest.d.ts +0 -86
  736. package/dist/process/runs/ownership.d.ts +0 -11
  737. package/dist/process/runs/product-state.d.ts +0 -2
  738. package/dist/process/runs/schema.d.ts +0 -3
  739. package/dist/process/runs/stop.d.ts +0 -19
  740. package/dist/process/runtime-socket.d.ts +0 -4
  741. package/dist/process/service-processes.d.ts +0 -13
  742. package/dist/process/shutdown.d.ts +0 -10
  743. package/dist/process/stoppable-settlement.d.ts +0 -26
  744. package/dist/process/url-redaction.d.ts +0 -1
  745. package/dist/process/windows-cli-owner.d.ts +0 -2
  746. package/dist/process/windows-native.d.ts +0 -12
  747. package/dist/process/windows-process-identity.d.ts +0 -4
  748. package/dist/repo/ci-environment.d.ts +0 -7
  749. package/dist/repo/fingerprint-policy.d.ts +0 -13
  750. package/dist/repo/git-path.d.ts +0 -2
  751. package/dist/repo/history.d.ts +0 -17
  752. package/dist/repo/managed-git.d.ts +0 -10
  753. package/dist/repo/metadata.d.ts +0 -3
  754. package/dist/repo/physical-tree.d.ts +0 -37
  755. package/dist/repo/publish-directory.d.ts +0 -20
  756. package/dist/repo/remote.d.ts +0 -6
  757. package/dist/repo/state.d.ts +0 -26
  758. package/dist/repo/tree-listing.d.ts +0 -10
  759. package/dist/steps/build-config.d.ts +0 -34
  760. package/dist/steps/configured-step-testkit-modules.d.ts +0 -19
  761. package/dist/steps/configured-steps.d.ts +0 -51
  762. package/dist/steps/environment.d.ts +0 -82
  763. package/dist/steps/index.d.ts +0 -2
  764. package/dist/steps/module-runner.d.ts +0 -1
  765. package/dist/steps/run.d.ts +0 -36
  766. package/dist/toolchains/archive.d.ts +0 -53
  767. package/dist/toolchains/esbuild.d.ts +0 -9
  768. package/dist/toolchains/host-node-support.d.ts +0 -3
  769. package/dist/toolchains/index.d.ts +0 -114
  770. package/dist/toolchains/managed-node-releases.d.ts +0 -28
  771. package/dist/toolchains/node-cache.d.ts +0 -20
  772. package/dist/toolchains/node-integrity.d.ts +0 -18
  773. package/dist/toolchains/node-provisioning.d.ts +0 -29
  774. package/dist/toolchains/semver.d.ts +0 -13
  775. package/dist/toolchains/shared-operation.d.ts +0 -11
  776. package/dist/toolchains/timeouts.d.ts +0 -1
  777. package/dist/toolchains/vitest/index.d.ts +0 -13
  778. package/dist/tracker/gitea.d.ts +0 -14
  779. package/dist/tracker/github.d.ts +0 -14
  780. package/dist/tracker/http.d.ts +0 -33
  781. package/dist/tracker/index.d.ts +0 -4
  782. package/dist/tracker/redaction.d.ts +0 -4
  783. package/dist/tracker/requests.d.ts +0 -17
  784. package/dist/tracker/responses.d.ts +0 -24
  785. package/dist/tracker/types.d.ts +0 -51
  786. package/docs/engine-architecture.md +0 -682
  787. /package/dist/engines/{k6/runtime/runtime-globals.js → api/profiles.js} +0 -0
@@ -1 +1 @@
1
- import m from"node:path";import{readVerifiedOwnedFile as a,writeOwnedRuntimeInput as w,RUNTIME_INPUT_BYTE_MEASURE as S}from"../../runtime-input-file.js";import{requireBoundedJsonValue as O,RUNTIME_INPUT_JSON_BOUNDS as h}from"../../contracts/json-bounds.js";import{ENGINE_PROCESS_RESERVED_ENVIRONMENT_KEYS as V}from"../../../process/environment.js";import{installManagedProcessEnvironmentOverlay as R}from"../../../process/environment-overlay.js";import{isUnknownRecord as c}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",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 d={filename:NODE_VITEST_RUNTIME_INPUT_FILENAME,label:"Node/Vitest runtime input",maxSizeBytes:8*1024*1024},f=512,p=4*1024*1024,l=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","UPDATE_SNAPSHOT",...V].map(e=>e.toUpperCase()));export const MAX_NODE_VITEST_SPAWN_ENVIRONMENT_UNITS=24*1024;export async function writeOwnedNodeVitestRuntimeInput(e,t){return w(e,t,d)}export async function verifyOwnedNodeVitestRuntimeInput(e){await a(e,d)}export function selectNodeVitestForwardedEnvironment(e){const t={...e};return _(t),t}export function assertNodeVitestSpawnEnvironmentBudget(e){if(Object.entries(e).reduce((n,[i,o])=>n+i.length+o.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)throw new Error("Node/Vitest runtime input pointer does not match its engine-owned path");if(n!==e.expectedSha256)throw new Error("Node/Vitest runtime input digest does not match its prepared value");const i={directory:m.dirname(e.expectedFile),file:e.expectedFile,sha256:e.expectedSha256,sizeBytes:e.expectedSizeBytes},o=await a(i,d);let s;try{s=JSON.parse(o)}catch(u){throw new Error("Node/Vitest runtime input contains invalid JSON",{cause:u})}const r=E(I(s));if(r.invocationId!==e.expectedInvocationId||r.profileId!==e.expectedProfileId||r.taskId!==e.expectedTaskId)throw new Error("Node/Vitest runtime input identity does not match its task");R({environment:r.environment,label:"Node/Vitest"}),Object.defineProperty(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL),{configurable:!0,enumerable:!1,value:r,writable:!1}),N(r)}export function applyNodeVitestEngineEnvironment(){const e=readNodeVitestRuntimeInput();if(e===null)throw new Error("Node/Vitest runtime input is not installed");N(e)}function N(e){const t=e.context.baseUrl;typeof t=="string"?process.env.BASE_URL=t:Reflect.deleteProperty(process.env,"BASE_URL")}export function readNodeVitestRuntimeInput(){const e=Reflect.get(globalThis,Symbol.for(NODE_VITEST_RUNTIME_INPUT_SYMBOL));return e===void 0?null:E(I(e,"Installed Node/Vitest runtime input"))}function I(e,t="Node/Vitest runtime input"){if(O(e,t,h,S),!c(e))throw new Error(`${t} must contain an object`);const n=new Set(["context","engineId","environment","fixtures","invocationId","profileId","setupData","taskId","version"]);for(const o of Object.keys(e))if(!n.has(o))throw new Error(`${t} contains unknown field ${o}`);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(!D(e.profileId))throw new Error(`${t}.profileId is invalid`);if(!c(e.context)||!c(e.fixtures))throw new Error(`${t} context and fixtures must be objects`);const i=x(e.environment,`${t}.environment`);return _(i),{context:e.context,engineId:"node-vitest",environment:i,fixtures:e.fixtures,invocationId:T(e.invocationId,`${t}.invocationId`),profileId:e.profileId,...Object.hasOwn(e,"setupData")?{setupData:e.setupData}:{},taskId:T(e.taskId,`${t}.taskId`),version:NODE_VITEST_RUNTIME_INPUT_VERSION}}function _(e){const t=Object.entries(e);if(t.length>f)throw new Error(`Node/Vitest forwarded environment exceeds the ${f.toString()} entry limit`);let n=0;const i=new Set;for(const[o,s]of t){y(o,s,"Node/Vitest forwarded environment");const r=o.toUpperCase();if(i.has(r))throw new Error(`Node/Vitest forwarded environment contains case-insensitive duplicate key ${o}`);if(i.add(r),r.startsWith("TESTKIT_")||r.startsWith("VITEST_")||l.has(r))throw new Error(`Node/Vitest forwarded environment contains reserved key ${o}`);n+=Buffer.byteLength(o)+Buffer.byteLength(s)+2}if(n>p)throw new Error(`Node/Vitest forwarded environment exceeds the ${p.toString()} byte limit`)}function x(e,t){if(!c(e))throw new Error(`${t} must be an object`);const n=Object.entries(e);if(!n.every(i=>typeof i[1]=="string"))throw new Error(`${t} values must all be strings`);return Object.fromEntries(n)}function y(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 E(e){if(e!==null&&typeof e=="object"&&!Object.isFrozen(e)){Object.freeze(e);for(const t of Object.values(e))E(t)}return e}function T(e,t){if(typeof e!="string"||e.length===0)throw new Error(`${t} must be a non-empty string`);return e}function D(e){return e==="node-vitest-contract"||e==="node-vitest-data"}
1
+ import{realpathSync as h,writeFileSync as O}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 v}from"../../../process/environment.js";import{installManagedProcessEnvironmentOverlay as A}from"../../../process/environment-overlay.js";import{isUnknownRecord as d}from"../../../kernel/unknown-values.js";import{toError as U}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="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},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 g=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","UPDATE_SNAPSHOT",...v,...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,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){try{await $(e)}catch(t){throw x(e.startupFailure,t),t}}function x(e,t){const n=i=>{try{return h(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{O(e.file,U(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,a);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&&A({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:E(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: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>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),g.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 E(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"}
@@ -0,0 +1 @@
1
+ import s from"node:fs";import{createRequire as v}from"node:module";import c from"node:path";import{isUnknownRecord as u}from"../../kernel/unknown-values.js";import{isPathWithin as S}from"../../process/files/paths.js";import{NODE_VITEST_ENGINE_ENVIRONMENT_KEYS as N}from"./runtime/runtime-input.js";const O=["auto","managed","repo"];export const NODE_VITEST_INVENTORY_MARKER="TESTKIT_VITEST_INVENTORY=";const h=new Set(["BASE_URL","DATABASE_URL","NODE_OPTIONS",...N].map(e=>e.toUpperCase()));export function resolveVitestSelection(e,t,n=process.env){const{"vitest-config":r,"vitest-project":o,"vitest-runner":a}=e;if(a==="managed"&&(r??o)!==void 0)throw new Error("--vitest-config and --vitest-project select the repository's Vitest, not --vitest-runner managed");return{config:r===void 0?null:g(t,r),host:{...Object.fromEntries(Object.entries(n).filter(i=>i[1]!==void 0&&!h.has(i[0].toUpperCase()))),...n.NODE_OPTIONS===void 0?{}:{NODE_OPTIONS:n.NODE_OPTIONS}},project:o??null,runner:a??((r??o)===void 0?"auto":"repo")}}export function decodeVitestSelection(e){if(!u(e)||!O.includes(e.runner)||![e.config,e.project].every(t=>t===null||typeof t=="string")||!u(e.host)||!Object.values(e.host).every(t=>typeof t=="string"))throw new Error("Node/Vitest selection must name a runner, config, project and host environment");return e}function g(e,t){let n;try{n=s.realpathSync(c.resolve(e,t))}catch(r){throw new Error(`--vitest-config ${t} does not exist in ${e}`,{cause:r})}if(!S(s.realpathSync(e),n))throw new Error(`--vitest-config ${t} resolves outside ${e}`);if(!s.statSync(n).isFile())throw new Error(`--vitest-config ${t} is not a file`);return n}export function resolveRepositoryVitest(e,t=!1){const n=c.join(e,"package.json"),r=v(n);let o;try{o=r.resolve("vitest/package.json")}catch(f){const d=!r.resolve.paths("vitest")?.some(m=>s.lstatSync(c.join(m,"vitest"),{throwIfNoEntry:!1}));if(d&&t&&!w(n))return null;const E=d?"is not installed":"is installed but its package metadata is broken";throw new Error(`Vitest ${E} in ${e}; install vitest 4.x, or pass --vitest-runner managed`,{cause:f})}const a=c.dirname(o),i=JSON.parse(s.readFileSync(o,"utf8")),p=`Vitest ${i.version} at ${a}`;if(!/^4\.\d+\.\d+/u.test(i.version))throw new Error(`${p} is not supported; install vitest 4.x, or pass --vitest-runner managed`);const l=i.exports?.["."]?.import?.default;try{if(typeof l!="string")throw new Error("its package exports name no ESM entry point");return{entrypoint:r.resolve("vitest/node"),index:c.join(a,l),name:"vitest",root:a,version:i.version}}catch(f){throw new Error(`${p} is broken; reinstall it`,{cause:f})}}function w(e){if(!s.existsSync(e))return!1;const t=JSON.parse(s.readFileSync(e,"utf8"));return["dependencies","devDependencies","optionalDependencies"].some(n=>Object.hasOwn(t[n]??{},"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 C,processExitFailure as b,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 j}from"../../api/index.js";import{createNativeInvocationProcessEnvironment as V,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 tt}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(t){const e=g(t);super(e.message,{cause:e}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:V,removeOwnedDirectory:it,writeTaskWorkspace:Q};const rt=new Set(["EBUSY","ENOTEMPTY","EPERM"]),et=5;async function it(r){for(let t=0;;t+=1)try{await u.rm(r,{force:!0,recursive:!0});return}catch(e){const i=e instanceof Error&&"code"in e?e.code:void 0;if(t===et||typeof i!="string"||!rt.has(i))throw e;await $((t+1)*100)}}export async function executePlaywrightEngine(r,t,e,i=playwrightAdapterDependencies){const o=await ot(r,t,i,e);return x(r,o.tasks,y,(a,s)=>nt(r,o,a,s),lt(r,o.installation))}async function ot(r,t,e,i){const o=await B(r.workspace.rootDirectory);O(r.workspace.temporaryDirectory,"temporary directory"),O(r.workspace.artifactDirectory,"artifact directory"),await u.mkdir(r.workspace.temporaryDirectory,{recursive:!0}),await u.mkdir(r.workspace.artifactDirectory,{recursive:!0});const a=await u.realpath(r.workspace.temporaryDirectory),s=await u.realpath(r.workspace.artifactDirectory),n=await D(`playwright-${S(r.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 r.tasks.entries()){const I=j(r,c),_=`${h.toString().padStart(3,"0")}-${S(c.id)}`,v=H.join(n,_),f=await D(`${_}-`,{parentDir:s});try{const m=ct(r,c.serviceName),E=await pt(()=>Y(o,c.file,"Playwright task file")),w=await e.writeTaskWorkspace({artifactDirectory:f,baseUrl:m,browserFile:t.browserExecutable,endpoints:r.target.endpoints,inputs:I,invocationId:r.invocationId,managedModuleEntrypoints:{authoring:t.authoringEntrypoints.authoring,cli:t.family.cli,playwright:t.family.playwright.entrypoint,playwrightCore:t.family.playwrightCore.entrypoint,playwrightTest:t.family.playwrightTest.entrypoint,root:t.authoringEntrypoints.root,rootPackageJson:t.authoringEntrypoints.rootPackageJson},moduleFile:t.family.playwrightTest.entrypoint,orchestration:c.orchestration,rootDirectory:o,serviceName:c.serviceName,sourceFile:E,taskDirectory:v,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 ut([v,f],e)).map(w=>w.error);p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:playwrightPreparationFailure(m,E),prepared:null})}}return Object.freeze({installation:t,invocationEnvironment:l.environment,rootDirectory:o,tasks:Object.freeze(p)})}async function nt(r,t,e,i){const a={...await tt(r.workspace.cacheDirectory,e.inputs.environment),...at(r,t,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:[t.installation.family.cli,"test",X(e.prepared.sourceFile),"--config",e.prepared.workspace.configFile],cwd:t.rootDirectory,displayLabel:"Playwright",env:a,executable:process.execPath,signal:r.signal,timeoutMs:s});let l;try{l=await Z({identity:{engineId:"playwright",invocationId:r.invocationId,taskId:e.task.id,sourceFile:e.prepared.sourceFile},artifactDirectory:e.artifactDirectory,outputDirectory:e.prepared.workspace.outputDirectory,reportFile:e.prepared.workspace.reportFile,rootDirectory:t.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(r.signal.reason));return T(e.task.id,p.status,i,l.evidence,p.failure)}export function classifyPlaywrightProcessResult(r,t,e,i){const o=F(r,e,i,y);if(o!==null)return o;if(t.outputLimit!==null){const n=t.outputLimit;return{failure:{code:"PLAYWRIGHT_OUTPUT_ARTIFACT_LIMIT",details:{...n},message:`Playwright output exceeded its ${n.limit.toString()} ${n.kind==="bytes"?"byte aggregate":"entry"} artifact limit`},status:"failed"}}const a=t.attachmentFailures.find(n=>n.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(a!==void 0)return{failure:a,status:"failed"};if(t.report.failure!==null){const n=t.report.failure;return{...t.report,failure:{...n,details:{...n.details,evidenceErrors:t.reportError===null?0:1,...C(r)}}}}const s=t.attachmentFailures.at(0);return s!==void 0?{failure:s,status:"failed"}:r.exitCode!==0?b(r,y,R(r.stderr)??R(r.stdout)):t.reportError!==null?{failure:{...P(t.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:t.report}export function playwrightEvidenceCollectionFailure(r,t){const e=g(t);return M(r,y,e)??{...P(e,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function at(r,t,e){const i={...t.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:t.installation.registry.canonicalValue,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD:"1",TESTKIT_ACTIVE:"1",TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(o.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(o.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:r.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=st(s,a),A(i),i}function st(r,t){return["--experimental-strip-types",`--require=${JSON.stringify(r)}`,`--import=${JSON.stringify(K(t).href)}`].join(" ")}export function playwrightSpawnEnvironmentBytes(r){return Object.entries(r).reduce((t,[e,i])=>t+(process.platform==="win32"?(e.length+i.length+2)*2:Buffer.byteLength(e,"utf8")+Buffer.byteLength(i,"utf8")+2),0)}function A(r){if(Object.keys(r).length>L)throw new Error(`Playwright physical spawn environment exceeds its ${L.toString()} entry limit`);if(playwrightSpawnEnvironmentBytes(r)>PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright physical spawn environment exceeds its ${PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES.toString()} byte limit`)}function ct(r,t){if(!Object.hasOwn(r.target.endpoints,t))return null;const e=r.target.endpoints[t];if(typeof e!="string"||e.trim().length===0)throw new Error(`Playwright task service ${t} endpoint must be a non-empty URL`);let i;try{i=new URL(e)}catch(o){throw new Error(`Playwright task service ${t} endpoint is not a valid URL`,{cause:o})}if(i.protocol!=="http:"&&i.protocol!=="https:")throw new Error(`Playwright task service ${t} endpoint must use http or https`);return i.href}function lt(r,t){return{...q({engineId:"playwright",installationKind:"root-playwright-family",profile:r.profile}),browserName:"chromium",packages:{playwright:t.family.playwright.version,playwrightCore:t.family.playwrightCore.version,playwrightTest:t.family.playwrightTest.version},registryMode:t.registry.mode}}export function playwrightPreparationFailure(r,t){if(t.length===0)return{...P(r,"PLAYWRIGHT_PREPARE_FAILED"),classification:r instanceof k||r instanceof z?"product":"infra"};const e=g(r);return{classification:"infra",code:"PLAYWRIGHT_PREPARE_CLEANUP_FAILED",details:{cleanupFailures:t.map(i=>i.message),preparationFailure:e.message},message:`Playwright task preparation failed and its partial workspace could not be removed: ${t.map(i=>i.message).join("; ")}`,...e.stack===void 0?{}:{stack:e.stack}}}async function pt(r){try{return await r()}catch(t){throw new k(t)}}async function ut(r,t){const e=[];for(const i of r)try{await t.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 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 tt}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(t){const e=g(t);super(e.message,{cause:e}),this.name="PlaywrightConsumerPreparationError"}}export const playwrightAdapterDependencies={createInvocationEnvironment:j,removeOwnedDirectory:it,writeTaskWorkspace:Q};const rt=new Set(["EBUSY","ENOTEMPTY","EPERM"]),et=5;async function it(r){for(let t=0;;t+=1)try{await u.rm(r,{force:!0,recursive:!0});return}catch(e){const i=e instanceof Error&&"code"in e?e.code:void 0;if(t===et||typeof i!="string"||!rt.has(i))throw e;await $((t+1)*100)}}export async function executePlaywrightEngine(r,t,e,i=playwrightAdapterDependencies){const o=await ot(r,t,i,e);return x(r,o.tasks,y,(a,s)=>nt(r,o,a,s),lt(r,o.installation))}async function ot(r,t,e,i){const o=await B(r.workspace.rootDirectory);O(r.workspace.temporaryDirectory,"temporary directory"),O(r.workspace.artifactDirectory,"artifact directory"),await u.mkdir(r.workspace.temporaryDirectory,{recursive:!0}),await u.mkdir(r.workspace.artifactDirectory,{recursive:!0});const a=await u.realpath(r.workspace.temporaryDirectory),s=await u.realpath(r.workspace.artifactDirectory),n=await D(`playwright-${S(r.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 r.tasks.entries()){const I=V(r,c),v=`${h.toString().padStart(3,"0")}-${S(c.id)}`,_=H.join(n,v),f=await D(`${v}-`,{parentDir:s});try{const m=ct(r,c.serviceName),E=await pt(()=>Y(o,c.file,"Playwright task file")),w=await e.writeTaskWorkspace({artifactDirectory:f,baseUrl:m,browserFile:t.browserExecutable,databases:r.target.databases,endpoints:r.target.endpoints,handles:r.target.handles,inputs:I,invocationId:r.invocationId,managedModuleEntrypoints:{authoring:t.authoringEntrypoints.authoring,cli:t.family.cli,environment:t.authoringEntrypoints.environment,managedTest:t.authoringEntrypoints.managedTest,playwright:t.family.playwright.entrypoint,playwrightCore:t.family.playwrightCore.entrypoint,playwrightTest:t.family.playwrightTest.entrypoint,root:t.authoringEntrypoints.root,rootPackageJson:t.authoringEntrypoints.rootPackageJson},moduleFile:t.family.playwrightTest.entrypoint,orchestration:c.orchestration,rootDirectory:o,serviceName:c.serviceName,sourceFile:E,taskDirectory:_,taskId:c.id});i.defer(async()=>{try{await 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 ut([_,f],e)).map(w=>w.error);p.push({artifactDirectory:f,inputs:I,task:c,preparationFailure:playwrightPreparationFailure(m,E),prepared:null})}}return Object.freeze({installation:t,invocationEnvironment:l.environment,rootDirectory:o,tasks:Object.freeze(p)})}async function nt(r,t,e,i){const a={...await tt(r.workspace.cacheDirectory,e.inputs.environment),...at(r,t,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:[t.installation.family.cli,"test",X(e.prepared.sourceFile),"--config",e.prepared.workspace.configFile],cwd:t.rootDirectory,displayLabel:"Playwright",env:a,executable:process.execPath,signal:r.signal,timeoutMs:s});let l;try{l=await Z({identity:{engineId:"playwright",invocationId:r.invocationId,taskId:e.task.id,sourceFile:e.prepared.sourceFile},artifactDirectory:e.artifactDirectory,outputDirectory:e.prepared.workspace.outputDirectory,reportFile:e.prepared.workspace.reportFile,rootDirectory:t.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(r.signal.reason));return T(e.task.id,p.status,i,l.evidence,p.failure)}export function classifyPlaywrightProcessResult(r,t,e,i){const o=F(r,e,i,y);if(o!==null)return o;if(t.outputLimit!==null){const n=t.outputLimit;return{failure:{code:"PLAYWRIGHT_OUTPUT_ARTIFACT_LIMIT",details:{...n},message:`Playwright output exceeded its ${n.limit.toString()} ${n.kind==="bytes"?"byte aggregate":"entry"} artifact limit`},status:"failed"}}const a=t.attachmentFailures.find(n=>n.code==="PLAYWRIGHT_ATTACHMENT_LIMIT");if(a!==void 0)return{failure:a,status:"failed"};if(t.report.failure!==null){const n=t.report.failure;return{...t.report,failure:{...n,details:{...n.details,evidenceErrors:t.reportError===null?0:1,...b(r)}}}}const s=t.attachmentFailures.at(0);return s!==void 0?{failure:s,status:"failed"}:r.exitCode!==0?C(r,y,R(r.stderr)??R(r.stdout)):t.reportError!==null?{failure:{...P(t.reportError,"PLAYWRIGHT_REPORT_INVALID"),classification:"infra"},status:"failed"}:t.report}export function playwrightEvidenceCollectionFailure(r,t){const e=g(t);return M(r,y,e)??{...P(e,"PLAYWRIGHT_EVIDENCE_FAILED"),classification:"infra"}}function at(r,t,e){const i={...t.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:t.installation.registry.canonicalValue,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD:"1",TESTKIT_ACTIVE:"1",TESTKIT_INTERNAL_FILE_DEADLINE_MS:String(o.deadlineMs),TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS:String(Math.max(1,Math.ceil(o.timeoutMs/1e3))),TESTKIT_INVOCATION_ID:r.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=st(s,a),A(i),i}function st(r,t){return["--experimental-strip-types",`--require=${JSON.stringify(r)}`,`--import=${JSON.stringify(K(t).href)}`].join(" ")}export function playwrightSpawnEnvironmentBytes(r){return Object.entries(r).reduce((t,[e,i])=>t+(process.platform==="win32"?(e.length+i.length+2)*2:Buffer.byteLength(e,"utf8")+Buffer.byteLength(i,"utf8")+2),0)}function A(r){if(Object.keys(r).length>L)throw new Error(`Playwright physical spawn environment exceeds its ${L.toString()} entry limit`);if(playwrightSpawnEnvironmentBytes(r)>PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright physical spawn environment exceeds its ${PLAYWRIGHT_SPAWN_ENVIRONMENT_MAX_BYTES.toString()} byte limit`)}function ct(r,t){const e=r.target.baseUrl;if(e===null)return null;if(typeof e!="string"||e.trim().length===0)throw new Error(`Playwright task service ${t} endpoint must be a non-empty URL`);let i;try{i=new URL(e)}catch(o){throw new Error(`Playwright task service ${t} endpoint is not a valid URL`,{cause:o})}if(i.protocol!=="http:"&&i.protocol!=="https:")throw new Error(`Playwright task service ${t} endpoint must use http or https`);return i.href}function lt(r,t){return{...q({engineId:"playwright",installationKind:"root-playwright-family",profile:r.profile}),browserName:"chromium",packages:{playwright:t.family.playwright.version,playwrightCore:t.family.playwrightCore.version,playwrightTest:t.family.playwrightTest.version},registryMode:t.registry.mode}}export function playwrightPreparationFailure(r,t){if(t.length===0)return{...P(r,"PLAYWRIGHT_PREPARE_FAILED"),classification:r instanceof k||r instanceof z?"product":"infra"};const e=g(r);return{classification:"infra",code:"PLAYWRIGHT_PREPARE_CLEANUP_FAILED",details:{cleanupFailures:t.map(i=>i.message),preparationFailure:e.message},message:`Playwright task preparation failed and its partial workspace could not be removed: ${t.map(i=>i.message).join("; ")}`,...e.stack===void 0?{}:{stack:e.stack}}}async function pt(r){try{return await r()}catch(t){throw new k(t)}}async function ut(r,t){const e=[];for(const i of r)try{await t.removeOwnedDirectory(i)}catch(o){e.push({directory:i,error:g(o)})}return e}
@@ -1,8 +1,8 @@
1
- import{ownedRuntimeInputModuleUrl as w}from"../../runtime-input-file.js";import b from"node:crypto";import n from"node:fs/promises";import o from"node:path";import{fileURLToPath as j,pathToFileURL as g}from"node:url";import{managedNodeModuleResolutionBootstrapSource as P}from"../../managed-module-resolution.js";import{materializePlaywrightRuntimeInput as v,playwrightRuntimeInputBootstrapSource as T}from"../runtime/runtime-input.js";import{nativeReporterFile as k}from"../../adapters/native-report.js";import{isPathWithin as O}from"../../../process/files/paths.js";export class PlaywrightConsumerConfigurationError extends Error{constructor(r){const t=r instanceof Error?r:new Error(String(r));super(t.message,{cause:t}),this.name="PlaywrightConsumerConfigurationError"}}export function playwrightExactFileFilter(e){return m(e)}export async function writePlaywrightTaskWorkspace(e){let r;try{x(e.inputs.supportModules),r=await I(e.rootDirectory,e.inputs)}catch(d){throw new PlaywrightConsumerConfigurationError(d)}await n.mkdir(e.taskDirectory,{recursive:!0}),await n.mkdir(e.artifactDirectory,{recursive:!0});const t=b.randomUUID(),i=o.join(e.taskDirectory,`playwright-${t}.config.mjs`),a=o.join(e.taskDirectory,`playwright-${t}.input-bootstrap.mjs`),l=o.join(e.taskDirectory,`playwright-${t}.resolution-bootstrap.cjs`),f=o.join(e.taskDirectory,"testkit-playwright-report.json"),p=o.join(e.artifactDirectory,"playwright-output");await n.mkdir(p);const c=o.join(e.artifactDirectory,"playwright-attachments");await n.mkdir(c,{mode:448,recursive:!1});const u=await v({baseUrl:e.baseUrl,directory:e.taskDirectory,endpoints:e.endpoints,inputs:e.inputs,invocationId:e.invocationId,orchestration:e.orchestration,taskId:e.taskId}),y=k("playwright");return await h(l,playwrightResolutionBootstrapSource(e.managedModuleEntrypoints,{consumerRoots:[e.rootDirectory],trustedImporterFiles:[i,a],trustedInternalEntrypoints:[e.moduleFile,y,j(w)]})),await h(a,T({digest:u.digest,file:u.file,sizeBytes:u.sizeBytes,invocationId:e.invocationId,taskId:e.taskId})),await h(i,F({baseConfigFile:r,baseUrl:e.baseUrl,browserFile:e.browserFile,inputBootstrapFile:a,outputDirectory:p,reporterFile:y,report:{attachmentDirectory:c,identity:{engineId:"playwright",invocationId:e.invocationId,taskId:e.taskId,sourceFile:e.sourceFile},reportFile:f,workspaceDirectory:e.taskDirectory},rootDirectory:e.rootDirectory,sourceFile:e.sourceFile})),{attachmentDirectory:c,configFile:i,inputBootstrapFile:a,outputDirectory:p,reportFile:f,resolutionBootstrapFile:l}}export function playwrightResolutionBootstrapSource(e,r){const t={authoring:s(e.authoring,"authoring"),cli:s(e.cli,"cli"),playwright:s(e.playwright,"playwright"),playwrightCore:s(e.playwrightCore,"playwrightCore"),playwrightTest:s(e.playwrightTest,"playwrightTest"),root:s(e.root,"root"),rootPackageJson:s(e.rootPackageJson,"rootPackageJson")},i={"@playwright/test":t.playwrightTest,playwright:t.playwright,"playwright-core":t.playwrightCore};return P({consumerRoots:r.consumerRoots,direct:{"@gfdelarue/testkit":t.root,"@gfdelarue/testkit/package.json":t.rootPackageJson,"@gfdelarue/testkit/playwright":t.authoring},directBridgeRoots:[o.dirname(t.root)],engineLabel:"Playwright",forbiddenConsumerPackagePrefixes:[],packageAnchors:i,reservedPackageNames:["@gfdelarue/testkit"],stateSymbol:"@gfdelarue/testkit/playwright-resolution-hooks/v1",trustedImporterFiles:r.trustedImporterFiles,trustedInternalEntrypoints:r.trustedInternalEntrypoints,verifiedParentlessPackageFiles:{playwright:["lib/common/process.js"]},verifiedProcessEntrypoints:[t.cli]})}function s(e,r){if(!o.isAbsolute(e))throw new Error(`Resolved Playwright installation ${r} entrypoint must be absolute`);return e}function F(e){return`
1
+ import{ownedRuntimeInputModuleUrl as d}from"../../runtime-input-file.js";import w from"node:crypto";import n from"node:fs/promises";import o from"node:path";import{fileURLToPath as b,pathToFileURL as f}from"node:url";import{createManagedNodeModuleResolution as j}from"../../managed-module-resolution.js";import{materializePlaywrightRuntimeInput as P,playwrightRuntimeInputBootstrapSource as v}from"../runtime/runtime-input.js";import{nativeReporterFile as T}from"../../adapters/native-report.js";import{requireConsumerFile as k}from"../../adapters/workspace.js";export class PlaywrightConsumerConfigurationError extends Error{constructor(r){const t=r instanceof Error?r:new Error(String(r));super(t.message,{cause:t}),this.name="PlaywrightConsumerConfigurationError"}}export async function writePlaywrightTaskWorkspace(e){let r;try{F(e.inputs.supportModules),r=await D(e.rootDirectory,e.inputs)}catch(m){throw new PlaywrightConsumerConfigurationError(m)}await n.mkdir(e.taskDirectory,{recursive:!0}),await n.mkdir(e.artifactDirectory,{recursive:!0});const t=w.randomUUID(),i=o.join(e.taskDirectory,`playwright-${t}.config.mjs`),s=o.join(e.taskDirectory,`playwright-${t}.input-bootstrap.mjs`),h=o.join(e.taskDirectory,`playwright-${t}.resolution-bootstrap.cjs`),g=o.join(e.taskDirectory,"testkit-playwright-report.json"),l=o.join(e.artifactDirectory,"playwright-output");await n.mkdir(l);const p=o.join(e.artifactDirectory,"playwright-attachments");await n.mkdir(p,{mode:448,recursive:!1});const c=await P({baseUrl:e.baseUrl,databases:e.databases,directory:e.taskDirectory,endpoints:e.endpoints,handles:e.handles,inputs:e.inputs,invocationId:e.invocationId,orchestration:e.orchestration,taskId:e.taskId}),y=T("playwright");return await u(h,playwrightResolutionBootstrapSource(e.managedModuleEntrypoints,{consumerRoots:[e.rootDirectory],trustedImporterFiles:[i,s],trustedInternalEntrypoints:[e.moduleFile,y,b(d)]})),await u(s,v({digest:c.digest,file:c.file,sizeBytes:c.sizeBytes,invocationId:e.invocationId,taskId:e.taskId})),await u(i,O({baseConfigFile:r,baseUrl:e.baseUrl,browserFile:e.browserFile,inputBootstrapFile:s,outputDirectory:l,reporterFile:y,report:{attachmentDirectory:p,identity:{engineId:"playwright",invocationId:e.invocationId,taskId:e.taskId,sourceFile:e.sourceFile},reportFile:g,workspaceDirectory:e.taskDirectory},rootDirectory:e.rootDirectory,sourceFile:e.sourceFile})),{attachmentDirectory:p,configFile:i,inputBootstrapFile:s,outputDirectory:l,reportFile:g,resolutionBootstrapFile:h}}export function playwrightResolutionBootstrapSource(e,r){const t={authoring:a(e.authoring,"authoring"),cli:a(e.cli,"cli"),environment:a(e.environment,"environment"),managedTest:a(e.managedTest,"managedTest"),playwright:a(e.playwright,"playwright"),playwrightCore:a(e.playwrightCore,"playwrightCore"),playwrightTest:a(e.playwrightTest,"playwrightTest"),root:a(e.root,"root"),rootPackageJson:a(e.rootPackageJson,"rootPackageJson")},i={"@playwright/test":t.playwrightTest,playwright:t.playwright,"playwright-core":t.playwrightCore};return j({consumerRoots:r.consumerRoots,direct:{"@gfdelarue/testkit":t.root,"@gfdelarue/testkit/environment":t.environment,"@gfdelarue/testkit/package.json":t.rootPackageJson,"@gfdelarue/testkit/playwright":t.authoring,"@playwright/test":t.managedTest,"playwright/test":t.managedTest},directBridgeRoots:[o.dirname(t.root)],engineLabel:"Playwright",forbiddenConsumerPackagePrefixes:[],packageAnchors:i,reservedPackageNames:["@gfdelarue/testkit"],stateSymbol:"@gfdelarue/testkit/playwright-resolution-hooks/v1",trustedImporterFiles:r.trustedImporterFiles,trustedInternalEntrypoints:r.trustedInternalEntrypoints,verifiedParentlessPackageFiles:{playwright:["lib/common/process.js"]},verifiedProcessEntrypoints:[t.cli]}).bootstrapSource}function a(e,r){if(!o.isAbsolute(e))throw new Error(`Resolved Playwright installation ${r} entrypoint must be absolute`);return e}function O(e){return`
2
2
  import path from "node:path";
3
3
  import { createRequire } from "node:module";
4
4
 
5
- const OPTIONS = ${JSON.stringify({baseConfigUrl:e.baseConfigFile===null?null:g(e.baseConfigFile).href,baseUrl:e.baseUrl,browserFile:e.browserFile,exactTaskMatchSource:m(e.sourceFile),originalConfigDirectory:e.baseConfigFile===null?e.rootDirectory:o.dirname(e.baseConfigFile),originalConfigFile:e.baseConfigFile??o.join(e.rootDirectory,"__testkit_playwright_config__.mjs"),inputBootstrapUrl:g(e.inputBootstrapFile).href,outputDirectory:e.outputDirectory,report:e.report,reporterFile:e.reporterFile,rootDirectory:e.rootDirectory,sourceFile:e.sourceFile})};
5
+ const OPTIONS = ${JSON.stringify({baseConfigUrl:e.baseConfigFile===null?null:f(e.baseConfigFile).href,baseUrl:e.baseUrl,browserFile:e.browserFile,exactTaskMatchSource:playwrightExactFileFilter(e.sourceFile),originalConfigDirectory:e.baseConfigFile===null?e.rootDirectory:o.dirname(e.baseConfigFile),originalConfigFile:e.baseConfigFile??o.join(e.rootDirectory,"__testkit_playwright_config__.mjs"),inputBootstrapUrl:f(e.inputBootstrapFile).href,outputDirectory:e.outputDirectory,report:e.report,reporterFile:e.reporterFile,rootDirectory:e.rootDirectory,sourceFile:e.sourceFile})};
6
6
  const inputBootstrap = await import(OPTIONS.inputBootstrapUrl);
7
7
  if (typeof inputBootstrap.installPlaywrightRuntimeInput !== "function")
8
8
  throw new Error("Managed Playwright runtime-input bootstrap is invalid");
@@ -164,4 +164,4 @@ export default {
164
164
  fullyParallel: false,
165
165
  workers: 1,
166
166
  };
167
- `}function m(e){return`^${e.split(/[\\/]+/u).map(r=>r.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")).join("[\\\\/]")}$`}async function I(e,r){if(!Object.hasOwn(r.supportModules,"playwrightConfig"))return null;const t=r.supportModules.playwrightConfig;if(!o.isAbsolute(t))throw new Error("supportModules.playwrightConfig must be an absolute path");return D(e,t,"supportModules.playwrightConfig")}function x(e){const r=Object.keys(e).filter(t=>t!=="playwrightConfig");if(r.length>0)throw new Error(`Playwright engine does not support support module(s): ${r.join(", ")}`)}async function D(e,r,t){const i=o.resolve(e,r),a=await n.realpath(i);if(!O(e,a))throw new Error(`${t} must stay within the consumer root`);if(!(await n.stat(a)).isFile())throw new Error(`${t} must be a regular file`);return a}async function h(e,r){const t=await n.open(e,"wx",384);try{await t.writeFile(r,"utf8")}finally{await t.close()}}
167
+ `}export function playwrightExactFileFilter(e){return`^${e.split(/[\\/]+/u).map(r=>r.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")).join("[\\\\/]")}$`}async function D(e,r){if(!Object.hasOwn(r.supportModules,"playwrightConfig"))return null;const t=r.supportModules.playwrightConfig;if(!o.isAbsolute(t))throw new Error("supportModules.playwrightConfig must be an absolute path");return k(e,t,"supportModules.playwrightConfig")}function F(e){const r=Object.keys(e).filter(t=>t!=="playwrightConfig");if(r.length>0)throw new Error(`Playwright engine does not support support module(s): ${r.join(", ")}`)}async function u(e,r){const t=await n.open(e,"wx",384);try{await t.writeFile(r,"utf8")}finally{await t.close()}}
@@ -1 +1 @@
1
- import e from"node:path";import{isUnknownRecord as o}from"../../../kernel/unknown-values.js";export function nonNegativeInteger(n,t){if(typeof n!="number"||!Number.isSafeInteger(n)||n<0)throw new Error(`${t} must be a non-negative safe integer`);return n}export function optionalNonNegativeInteger(n,t){return n==null?null:nonNegativeInteger(n,t)}export function nonNegativeNumber(n,t){if(typeof n!="number"||!Number.isFinite(n)||n<0)throw new Error(`${t} must be a non-negative finite number`);return n}export function requireNonEmptyString(n,t){const r=optionalNonEmptyString(n);if(r===null)throw new Error(`${t} must be a non-empty string`);return r}export function optionalNonEmptyString(n){return typeof n=="string"&&n.trim().length>0?n.trim():null}export function portableReportPath(n){return n.replaceAll("\\","/")}export function portableRelativePath(n,t){return portableReportPath(e.relative(n,t))}export function requireRecord(n,t){if(!o(n))throw new Error(`${t} must contain an object`);return n}export function isMissingFileError(n){return n instanceof Error&&"code"in n&&n.code==="ENOENT"}
1
+ import e from"node:path";import{isUnknownRecord as o}from"../../../kernel/unknown-values.js";export function nonNegativeInteger(n,r){if(typeof n!="number"||!Number.isSafeInteger(n)||n<0)throw new Error(`${r} must be a non-negative safe integer`);return n}export function optionalNonNegativeInteger(n,r){return n==null?null:nonNegativeInteger(n,r)}export function nonNegativeNumber(n,r){if(typeof n!="number"||!Number.isFinite(n)||n<0)throw new Error(`${r} must be a non-negative finite number`);return n}export function requireNonEmptyString(n,r){const t=optionalNonEmptyString(n);if(t===null)throw new Error(`${r} must be a non-empty string`);return t}export function optionalNonEmptyString(n){return typeof n=="string"&&n.trim().length>0?n.trim():null}export function portableReportPath(n){return n.replaceAll("\\","/")}export function portableRelativePath(n,r){return portableReportPath(e.relative(n,r))}export function requireRecord(n,r){if(!o(n))throw new Error(`${r} must contain an object`);return n}
@@ -1 +1 @@
1
- import{toError as q}from"../../../kernel/error-serialization.js";import{isPathWithin as F}from"../../../process/files/paths.js";import w from"node:fs/promises";import g from"node:path";import{evidenceArtifact as $,evidenceFileAccess as tt,readStableJsonFile as et,snapshotStableEvidenceFile as C,stableEvidenceFromBuffer as it,writeOwnedEvidenceFile as Y}from"../../adapters/evidence-files.js";import{materializePlaywrightFailureDetails as rt}from"./failure-details.js";import{isMissingFileError as nt,nonNegativeInteger as E,nonNegativeNumber as H,optionalNonEmptyString as N,optionalNonNegativeInteger as G,portableRelativePath as U,portableReportPath as W,requireNonEmptyString as b,requireRecord as S}from"./evidence-validation.js";import{readNativeReport as at}from"../../adapters/native-report.js";import{attachmentLimitError as st,attachmentIoError as ot,MAX_ATTACHMENT_BYTES as X,MAX_TOTAL_ATTACHMENT_BYTES as ct}from"./attachments.js";const lt=64*1024*1024,J=1e4,M=512*1024*1024,ut=2*1024*1024,V=128,pt=".testkit-playwright-evidence",dt=/(?:^|\n)(?:Error:\s*)?No tests found(?:\.|\n|$)/u,ht=["failed","interrupted","passed","timedout"];class R extends Error{limit;constructor(e){super(vt(e)),this.limit=e,this.name="PlaywrightOutputArtifactLimitError"}}export async function collectPlaywrightEvidence(t,e=tt){await w.mkdir(t.artifactDirectory,{recursive:!0});const i=await w.realpath(t.artifactDirectory),c=await w.realpath(t.rootDirectory),n=g.join(i,pt);await w.mkdir(n,{mode:448,recursive:!1});const a=[],s=[];for(const r of["stdout","stderr"]){const l=t[r];if(l.length===0)continue;a.push({stream:r,text:l});const m=it(Buffer.from(l,"utf8")),y=await Y(n,`${r}.log`,m.contents);s.push($(y,m,"text/plain; charset=utf-8","engine.process-output",`playwright-${r}`,`${m.sizeBytes.toString()} bytes`))}let p=null,d=null,f=new Set,h=[];try{const r=await et(t.reportFile,lt,"Testkit native Playwright report",e);if(r.file===null)p=new Error("Playwright did not produce its Testkit native report");else{d=gt(at(r.value,t.identity).result,t.sourceFile);const l=await Y(n,"testkit-playwright-report.json",r.file.contents);s.push($(l,r.file,"application/json","engine.testkit-playwright-report","testkit-playwright-report","Testkit native Playwright report")),a.push(...d.logs);const m=await rt(n,d.failureDetails);m!==null&&s.push(m);const y=await wt(i,n,c,d.attachments,e);s.push(...y.artifacts),f=y.sourcePaths,h=y.failures}}catch(r){p=q(r)}let o={artifacts:[],bytes:0,entries:0},u=null;try{o=await Pt(i,n,t.outputDirectory,f,e),s.push(...o.artifacts)}catch(r){if(!(r instanceof R))throw r;u=r.limit,a.push({stream:"engine",text:r.message})}const P=ft(d,c,t.sourceFile,h.length),k=u??o;return{attachmentFailures:h,evidence:{artifacts:s,logs:a,metrics:[...Object.entries(d?.stats??{}).map(([r,l])=>({name:r==="duration"?"playwright.duration":`playwright.tests.${r}`,unit:r==="duration"?"milliseconds":"tests",value:l})),...["bytes","entries"].map(r=>({name:`playwright.output.${r}`,unit:r,value:k[r]}))],...d===null?{}:{native:{playwright:{attachmentFailures:h.map(r=>({...r})),specs:d.specs,stats:d.stats}}}},outputLimit:u,report:P,reportError:p}}function ft(t,e,i,c){if(t===null)return{failure:null,status:"failed"};const n=t.specs.filter((s,p)=>K(i,t.sourceFiles[p])),a=t.failureMessages.length>0&&t.failureMessages.every(s=>dt.test(s));return t.stats.unexpected>0||t.failureMessages.length>0&&(!a||n.length>0)?{failure:{...t.failureMessages.some(s=>/browserType\.launch(?:PersistentContext)?:/u.test(s)&&/Executable doesn't exist|Host system is missing dependencies|error while loading shared libraries|Failed to launch/u.test(s))?{classification:"infra"}:{},code:"PLAYWRIGHT_SPECS_FAILED",details:{attachmentFailures:c,failOnFlakyTests:t.failOnFlakyTests,reportErrors:t.reportErrors,runStatus:t.runStatus,stats:t.stats,specs:t.specs.slice(0,50)},message:t.failureMessages.at(0)??`${t.stats.unexpected.toString()} Playwright test(s) failed`},status:"failed"}:n.length>0?{failure:null,status:n.every(s=>s.status==="skipped")?"skipped":"passed"}:{failure:{code:"PLAYWRIGHT_NO_TESTS",details:{file:U(e,i),reportedSpecs:t.specs.slice(0,50),stats:t.stats},message:"Selected Playwright task did not execute any tests"},status:"failed"}}function K(t,e){return g.relative(g.normalize(t),e)===""}function gt(t,e){const i=S(t,"Playwright native report result"),c=ht.find(r=>r===i.status);if(c===void 0)throw new Error("Playwright native report status is invalid");if(typeof i.failOnFlakyTests!="boolean")throw new Error("Playwright native report failOnFlakyTests must be a boolean");const n=i.failOnFlakyTests,a=bt(i.stats),s=O(i.errors,"errors"),p=s.length,d=[],f=[],h=[],o=new Set,u={expected:0,flaky:0,skipped:0,unexpected:0};for(const r of A(i.specs,"specs")){const l=S(r,"Playwright native report spec"),m=b(l.id,"Playwright native spec.id");if(o.has(m))throw new Error("Playwright native report contains a duplicate test identity");o.add(m);const y=b(l.title,"Playwright native spec.title"),v=O(l.parents,"spec.parents");v.length>0&&(v[0]=W(v[0]));const L=W(b(l.file,"Playwright native spec.file")),x=b(l.sourceFile,"Playwright native spec.sourceFile");if(!g.isAbsolute(x)&&!g.win32.isAbsolute(x))throw new Error("Playwright native spec.sourceFile must be absolute");const Z=_(l.projectName,"spec.projectName"),T=l.status;if(T!=="expected"&&T!=="flaky"&&T!=="skipped"&&T!=="unexpected")throw new Error("Playwright native spec.status is invalid");u[T]+=1;const j=O(l.failureMessages,"spec.failureMessages"),z=l.failureStack===null?null:_(l.failureStack,"spec.failureStack"),D=G(l.line,"Playwright native spec.line"),B=G(l.column,"Playwright native spec.column");T==="unexpected"&&(s.push(...j),h.push({kind:K(e,x)?"playwright-spec":"playwright-other-file-spec",key:[...v.slice(1),y].filter(Boolean).join(" "),title:y,count:1,message:j[0]??`Playwright reported "${y}" as an unexpected failure`,...v.length===0?{}:{suitePath:v},location:{path:L,...D===null?{}:{line:D},...B===null?{}:{column:B}},...z===null?{}:{stack:z}})),f.push(x),d.push({durationMs:H(l.durationMs,"Playwright native spec.durationMs"),retries:E(l.retries,"Playwright native spec.retries"),file:L,projectName:Z,status:T,title:[...v,y].filter(Boolean).join(" > ")})}for(const r of["expected","flaky","skipped","unexpected"])if(a[r]!==u[r])throw new Error("Playwright native report stats do not match its cases");const P=A(i.logs,"logs").map(r=>{const l=S(r,"Playwright native report log");if(l.stream!=="stdout"&&l.stream!=="stderr")throw new Error("Playwright native report log stream is invalid");return{stream:l.stream,text:_(l.text,"log.text")}});return{attachments:A(i.attachments,"attachments").map((r,l)=>yt(r,l,o)),failOnFlakyTests:n,failureDetails:h,failureMessages:s,logs:St(P),reportErrors:p,runStatus:c,sourceFiles:f,specs:d,stats:a}}function A(t,e){if(!Array.isArray(t))throw new Error(`Playwright native report ${e} must be an array`);return t}function O(t,e){return A(t,e).map(i=>_(i,e))}function _(t,e){if(typeof t!="string")throw new Error(`Playwright native report ${e} must be a string`);return t}function yt(t,e,i){const c=S(t,`Playwright attachment[${e.toString()}]`),n=b(c.specId,"Playwright attachment.specId");if(!i.has(n))throw new Error("Playwright native attachment references an unknown test identity");const a=N(c.path),s=c.error===void 0?void 0:mt(c.error);if(c.body!==void 0||a===null&&s===void 0)throw new Error("Playwright attachment must contain a path or an error");return{contentType:N(c.contentType)??"application/octet-stream",name:N(c.name)??`attachment-${e.toString()}`,specId:n,...a===null?{}:{path:a},...s===void 0?{}:{error:s},retry:E(c.retry,"Playwright attachment.retry"),specTitle:b(c.specTitle,"Playwright attachment.specTitle")}}function mt(t){const e=S(t,"Playwright attachment.error");if(e.kind==="io")return{kind:"io",message:b(e.message,"Playwright attachment.error.message")};const i=e.kind;if(i!=="individual"&&i!=="total")throw new Error("Playwright attachment.error has an invalid kind");const c=i==="individual"?X:ct,n=E(e.observed,"Playwright attachment.error.observed");if(e.limit!==c||n<=c)throw new Error("Playwright attachment.error has an invalid limit");return{kind:i,limit:c,observed:n}}function I(t,e){const{name:i,specId:c,specTitle:n,retry:a}=t,s=e.kind==="io"?e.message:`exceeds its ${e.limit.toString()} byte ${e.kind} limit (observed ${e.observed.toString()} bytes)`;return{code:e.kind==="io"?"PLAYWRIGHT_ATTACHMENT_FAILED":"PLAYWRIGHT_ATTACHMENT_LIMIT",...e.kind==="io"?{classification:"infra"}:{},details:{name:i,specId:c,specTitle:n,retry:a,...e},message:`Playwright attachment "${i}" (${n}, retry ${a.toString()}): ${s}`}}async function wt(t,e,i,c,n){const a=[],s=[],p=new Set;let d=0;const f=g.join(e,"attachments");c.length>0&&await w.mkdir(f,{mode:448,recursive:!1});for(const[h,o]of c.entries())try{if(o.error!==void 0){o.path!==void 0&&p.add(g.resolve(i,o.path)),s.push(I(o,o.error));continue}const u=await Tt(o.path,i,t,n);p.add(u.file);const P=E(Number(u.identity.size),"Playwright attachment size"),k=st(P,d+P);if(k!==null){s.push(I(o,k));continue}const r=await C(u.file,X,"Playwright attachment",n,u.identity,f,`${h.toString().padStart(4,"0")}-${Q(o.name)}${kt(o)}`);d+=r.identity.sizeBytes,a.push({path:r.filePath,contentType:o.contentType,kind:"engine.playwright-attachment",name:o.name,summary:`${o.specTitle} retry ${o.retry.toString()}`,metadata:{...r.identity,specId:o.specId,specTitle:o.specTitle,retry:o.retry}})}catch(u){s.push(I(o,ot(u)))}return{artifacts:a,failures:s,sourcePaths:p}}async function Pt(t,e,i,c,n){try{const a=await n.lstat(i);if(!a.isDirectory()||a.isSymbolicLink())throw new Error("Playwright output directory must be a non-symbolic-link directory");const s=await w.realpath(i);if(!F(t,s))throw new Error("Playwright output directory escaped the artifact root");const p=await Et(s,c,n),d=[],f=g.join(e,"output");p.candidates.length>0&&await w.mkdir(f,{mode:448,recursive:!1});for(const[h,o]of p.candidates.entries()){const u=await C(o.file,M,"Playwright output file",n,o.identity,f,`${h.toString().padStart(5,"0")}-${Q(g.basename(o.file))}${At(o.file)}`);d.push($(u.filePath,u.identity,xt(o.file),"engine.playwright-output",o.relativePath,"Playwright output"))}return{artifacts:d,bytes:p.bytes,entries:p.entries}}catch(a){if(nt(a))return{artifacts:[],bytes:0,entries:0};throw a}}async function Et(t,e,i){const c=[];let n=0,a=0;const s=[{depth:0,directory:t}];for(;s.length>0;){const p=s.pop();if(p===void 0)continue;if(p.depth>V)throw new Error(`Playwright output exceeds the ${V.toString()} level directory depth limit`);const d=p.directory,f=await w.readdir(d,{withFileTypes:!0});f.sort((h,o)=>h.name.localeCompare(o.name));for(const h of f){if(a+=1,a>J)throw new R({bytes:n,entries:a,kind:"entries",limit:J,observed:a});const o=g.join(d,h.name),u=await i.lstat(o);if(u.isSymbolicLink())throw new Error("Playwright output must not contain symbolic links");if(u.isDirectory()){s.push({depth:p.depth+1,directory:o});continue}if(!u.isFile())continue;if(u.size<0n||u.size>BigInt(Number.MAX_SAFE_INTEGER))throw new Error("Playwright output file has an invalid size");const P=Number(u.size);if(n+=P,!Number.isSafeInteger(n)||n>M)throw new R({bytes:n,entries:a,kind:"bytes",limit:M,observed:n});e.has(o)||c.push({file:o,identity:u,relativePath:U(t,o)})}}return{bytes:n,candidates:c,entries:a}}function vt(t){return t.kind==="bytes"?`Playwright output exceeds its ${t.limit.toString()} byte aggregate limit (observed ${t.observed.toString()} bytes)`:`Playwright output exceeds its ${t.limit.toString()} entry limit (observed ${t.observed.toString()} entries)`}async function Tt(t,e,i,c){if(t===void 0)throw new Error("Playwright path attachment did not declare a path");const n=g.resolve(e,t),a=await c.lstat(n);if(!a.isFile()||a.isSymbolicLink())throw new Error("Playwright attachment path must be a regular non-symbolic-link file");const s=await w.realpath(n);if(!F(e,s)&&!F(i,s))throw new Error("Playwright attachment path must stay within the consumer or artifact root");return{file:s,identity:a}}function bt(t){const e=S(t,"Playwright report.stats");return{expected:E(e.expected,"Playwright stats.expected"),unexpected:E(e.unexpected,"Playwright stats.unexpected"),flaky:E(e.flaky,"Playwright stats.flaky"),skipped:E(e.skipped,"Playwright stats.skipped"),duration:H(e.duration,"Playwright stats.duration")}}function St(t){let e=ut;const i=[];for(const c of t){if(e<=0)break;const a=Buffer.from(c.text,"utf8").subarray(0,e).toString("utf8");a.length>0&&i.push({...c,text:a}),e-=Buffer.byteLength(a)}return e<=0&&i.push({stream:"engine",text:"Playwright report log evidence was truncated at its bounded limit"}),i}function kt(t){if(t.path!==void 0){const e=g.extname(t.path);if(/^\.[a-z0-9]{1,12}$/iu.test(e))return e}return t.contentType==="application/json"?".json":t.contentType.startsWith("text/")?".txt":t.contentType==="image/png"?".png":t.contentType==="image/jpeg"?".jpg":".bin"}function xt(t){const e=g.extname(t).toLowerCase();return e===".json"?"application/json":e===".zip"?"application/zip":e===".png"?"image/png":e===".jpg"||e===".jpeg"?"image/jpeg":e===".webm"?"video/webm":[".log",".txt",".md"].includes(e)?"text/plain; charset=utf-8":"application/octet-stream"}function At(t){const e=g.extname(t).toLowerCase();return/^\.[a-z0-9]{1,12}$/u.test(e)?e:".bin"}function Q(t){const e=t.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"").slice(0,80);return e.length>0?e:"attachment"}
1
+ import{toError as ot}from"../../../kernel/error-serialization.js";import{isPathWithin as N}from"../../../process/files/paths.js";import v from"node:fs/promises";import g from"node:path";import{evidenceArtifact as O,evidenceFileAccess as lt,readStableJsonFile as ct,snapshotStableEvidenceFile as U,stableEvidenceFromBuffer as X,writeOwnedEvidenceFile as V,isMissingFileError as ut}from"../../adapters/evidence-files.js";import{materializePlaywrightFailureDetails as pt}from"./failure-details.js";import{nonNegativeInteger as T,nonNegativeNumber as J,optionalNonEmptyString as R,optionalNonNegativeInteger as K,portableRelativePath as Q,portableReportPath as Z,requireNonEmptyString as k,requireRecord as P}from"./evidence-validation.js";import{readNativeReport as dt}from"../../adapters/native-report.js";import{environmentValueMissingFailure as ht,parseEnvironmentValueMissingFailure as q,readEnvironmentValueMissingFailure as ft}from"../../contracts/environment-values.js";import{attachmentLimitError as mt,attachmentIoError as gt,MAX_ATTACHMENT_BYTES as tt,MAX_TOTAL_ATTACHMENT_BYTES as yt}from"./attachments.js";const wt=64*1024*1024,et=1e4,I=512*1024*1024,vt=2*1024*1024,it=128,Pt=".testkit-playwright-evidence",Et=/(?:^|\n)(?:Error:\s*)?No tests found(?:\.|\n|$)/u,Tt=["failed","interrupted","passed","timedout"];class L extends Error{limit;constructor(e){super(Mt(e)),this.limit=e,this.name="PlaywrightOutputArtifactLimitError"}}export async function collectPlaywrightEvidence(t,e=lt){await v.mkdir(t.artifactDirectory,{recursive:!0});const i=await v.realpath(t.artifactDirectory),s=await v.realpath(t.rootDirectory),n=g.join(i,Pt);await v.mkdir(n,{mode:448,recursive:!1});const r=[],o=[];for(const l of["stdout","stderr"]){const y=t[l];if(y.length===0)continue;r.push({stream:l,text:y});const F=X(Buffer.from(y,"utf8")),f=await V(n,`${l}.log`,F.contents);o.push(O(f,F,"text/plain; charset=utf-8","engine.process-output",`playwright-${l}`,`${F.sizeBytes.toString()} bytes`))}let c=null,p=null,m=new Set,h=[];try{const l=await ct(t.reportFile,wt,"Testkit native Playwright report",e);if(l.file===null)c=new Error("Playwright did not produce its Testkit native report");else{p=St(dt(l.value,t.identity).result,t.sourceFile);const y=p.carriesEnvironmentFailures?X(Buffer.from(kt(l.value))):l.file,F=await V(n,"testkit-playwright-report.json",y.contents);o.push(O(F,y,"application/json","engine.testkit-playwright-report","testkit-playwright-report","Testkit native Playwright report")),r.push(...p.logs);const f=await pt(n,p.failureDetails);f!==null&&o.push(f);const u=await At(i,n,s,p.attachments,e);o.push(...u.artifacts),m=u.sourcePaths,h=u.failures}}catch(l){c=ot(l)}let a={artifacts:[],bytes:0,entries:0},d=null;try{a=await _t(i,n,t.outputDirectory,m,e),o.push(...a.artifacts)}catch(l){if(!(l instanceof L))throw l;d=l.limit,r.push({stream:"engine",text:l.message})}const w=bt(p,s,t.sourceFile,h.length),E=d??a;return{attachmentFailures:h,evidence:{artifacts:o,logs:r,metrics:[...Object.entries(p?.stats??{}).map(([l,y])=>({name:l==="duration"?"playwright.duration":`playwright.tests.${l}`,unit:l==="duration"?"milliseconds":"tests",value:y})),...["bytes","entries"].map(l=>({name:`playwright.output.${l}`,unit:l,value:E[l]}))],...p===null?{}:{native:{playwright:{attachmentFailures:h.map(l=>({...l})),specs:p.specs,stats:p.stats}}}},outputLimit:d,report:w,reportError:c}}function bt(t,e,i,s){if(t===null)return{failure:null,status:"failed"};const n=t.environmentFailures.at(0);if(n!==void 0)return{failure:ht(t.environmentFailures.flatMap(c=>ft(c)??[]),n.message),status:"failed"};const r=t.specs.filter((c,p)=>rt(i,t.sourceFiles[p])),o=t.failureMessages.length>0&&t.failureMessages.every(c=>Et.test(c));return t.stats.unexpected>0||t.failureMessages.length>0&&(!o||r.length>0)?{failure:{...t.failureMessages.some(c=>/browserType\.launch(?:PersistentContext)?:/u.test(c)&&/Executable doesn't exist|Host system is missing dependencies|error while loading shared libraries|Failed to launch/u.test(c))?{classification:"infra"}:{},code:"PLAYWRIGHT_SPECS_FAILED",details:{attachmentFailures:s,failOnFlakyTests:t.failOnFlakyTests,reportErrors:t.reportErrors,runStatus:t.runStatus,stats:t.stats,specs:t.specs.slice(0,50)},message:t.failureMessages.at(0)??`${t.stats.unexpected.toString()} Playwright test(s) failed`},status:"failed"}:r.length>0?{failure:null,status:r.every(c=>c.status==="skipped")?"skipped":"passed"}:{failure:{code:"PLAYWRIGHT_NO_TESTS",details:{file:Q(e,i),reportedSpecs:t.specs.slice(0,50),stats:t.stats},message:"Selected Playwright task did not execute any tests"},status:"failed"}}function rt(t,e){return g.relative(g.normalize(t),e)===""}function St(t,e){const i=P(t,"Playwright native report result"),s=Tt.find(f=>f===i.status);if(s===void 0)throw new Error("Playwright native report status is invalid");if(typeof i.failOnFlakyTests!="boolean")throw new Error("Playwright native report failOnFlakyTests must be a boolean");const n=i.failOnFlakyTests,r=Ot(i.stats),o=j(i.errors,"errors"),c=o.length,p=q(i.environmentFailure,"Playwright native report environment failure"),m=p===null?[]:[p];let h=p!==null;const a=[],d=[],w=[],E=new Set,l={expected:0,flaky:0,skipped:0,unexpected:0};for(const f of x(i.specs,"specs")){const u=P(f,"Playwright native report spec"),B=k(u.id,"Playwright native spec.id");if(E.has(B))throw new Error("Playwright native report contains a duplicate test identity");E.add(B);const A=k(u.title,"Playwright native spec.title"),b=j(u.parents,"spec.parents");b.length>0&&(b[0]=Z(b[0]));const D=Z(k(u.file,"Playwright native spec.file")),_=k(u.sourceFile,"Playwright native spec.sourceFile");if(!g.isAbsolute(_)&&!g.win32.isAbsolute(_))throw new Error("Playwright native spec.sourceFile must be absolute");const st=M(u.projectName,"spec.projectName"),S=u.status;if(S!=="expected"&&S!=="flaky"&&S!=="skipped"&&S!=="unexpected")throw new Error("Playwright native spec.status is invalid");l[S]+=1;const C=j(u.failureMessages,"spec.failureMessages"),Y=u.failureStack===null?null:M(u.failureStack,"spec.failureStack"),H=K(u.line,"Playwright native spec.line"),W=K(u.column,"Playwright native spec.column"),G=rt(e,_),$=q(u.environmentFailure,"Playwright native spec environment failure");$!==null&&(h=!0),G&&$!==null&&m.push($),S==="unexpected"&&(o.push(...C),w.push({...$===null?{}:{classification:"infra"},kind:G?"playwright-spec":"playwright-other-file-spec",key:[...b.slice(1),A].filter(Boolean).join(" "),title:A,count:1,message:C[0]??`Playwright reported "${A}" as an unexpected failure`,...b.length===0?{}:{suitePath:b},location:{path:D,...H===null?{}:{line:H},...W===null?{}:{column:W}},...Y===null?{}:{stack:Y}})),d.push(_),a.push({durationMs:J(u.durationMs,"Playwright native spec.durationMs"),retries:T(u.retries,"Playwright native spec.retries"),file:D,projectName:st,status:S,title:[...b,A].filter(Boolean).join(" > ")})}for(const f of["expected","flaky","skipped","unexpected"])if(r[f]!==l[f])throw new Error("Playwright native report stats do not match its cases");const y=x(i.logs,"logs").map(f=>{const u=P(f,"Playwright native report log");if(u.stream!=="stdout"&&u.stream!=="stderr")throw new Error("Playwright native report log stream is invalid");return{stream:u.stream,text:M(u.text,"log.text")}});return{attachments:x(i.attachments,"attachments").map((f,u)=>Ft(f,u,E)),carriesEnvironmentFailures:h,environmentFailures:m,failOnFlakyTests:n,failureDetails:w,failureMessages:o,logs:Rt(y),reportErrors:c,runStatus:s,sourceFiles:d,specs:a,stats:r}}function kt(t){const e=P(t,"Testkit native Playwright report"),i=nt(P(e.result,"Playwright native report result")),s=x(i.specs,"specs").map(n=>nt(P(n,"Playwright native report spec")));return JSON.stringify({...e,result:{...i,specs:s}})}function nt(t){return Object.fromEntries(Object.entries(t).filter(([e])=>e!=="environmentFailure"))}function x(t,e){if(!Array.isArray(t))throw new Error(`Playwright native report ${e} must be an array`);return t}function j(t,e){return x(t,e).map(i=>M(i,e))}function M(t,e){if(typeof t!="string")throw new Error(`Playwright native report ${e} must be a string`);return t}function Ft(t,e,i){const s=P(t,`Playwright attachment[${e.toString()}]`),n=k(s.specId,"Playwright attachment.specId");if(!i.has(n))throw new Error("Playwright native attachment references an unknown test identity");const r=R(s.path),o=s.error===void 0?void 0:xt(s.error);if(s.body!==void 0||r===null&&o===void 0)throw new Error("Playwright attachment must contain a path or an error");return{contentType:R(s.contentType)??"application/octet-stream",name:R(s.name)??`attachment-${e.toString()}`,specId:n,...r===null?{}:{path:r},...o===void 0?{}:{error:o},retry:T(s.retry,"Playwright attachment.retry"),specTitle:k(s.specTitle,"Playwright attachment.specTitle")}}function xt(t){const e=P(t,"Playwright attachment.error");if(e.kind==="io")return{kind:"io",message:k(e.message,"Playwright attachment.error.message")};const i=e.kind;if(i!=="individual"&&i!=="total")throw new Error("Playwright attachment.error has an invalid kind");const s=i==="individual"?tt:yt,n=T(e.observed,"Playwright attachment.error.observed");if(e.limit!==s||n<=s)throw new Error("Playwright attachment.error has an invalid limit");return{kind:i,limit:s,observed:n}}function z(t,e){const{name:i,specId:s,specTitle:n,retry:r}=t,o=e.kind==="io"?e.message:`exceeds its ${e.limit.toString()} byte ${e.kind} limit (observed ${e.observed.toString()} bytes)`;return{code:e.kind==="io"?"PLAYWRIGHT_ATTACHMENT_FAILED":"PLAYWRIGHT_ATTACHMENT_LIMIT",...e.kind==="io"?{classification:"infra"}:{},details:{name:i,specId:s,specTitle:n,retry:r,...e},message:`Playwright attachment "${i}" (${n}, retry ${r.toString()}): ${o}`}}async function At(t,e,i,s,n){const r=[],o=[],c=new Set;let p=0;const m=g.join(e,"attachments");s.length>0&&await v.mkdir(m,{mode:448,recursive:!1});for(const[h,a]of s.entries())try{if(a.error!==void 0){a.path!==void 0&&c.add(g.resolve(i,a.path)),o.push(z(a,a.error));continue}const d=await Nt(a.path,i,t,n);c.add(d.file);const w=T(Number(d.identity.size),"Playwright attachment size"),E=mt(w,p+w);if(E!==null){o.push(z(a,E));continue}const l=await U(d.file,tt,"Playwright attachment",n,d.identity,m,`${h.toString().padStart(4,"0")}-${at(a.name)}${It(a)}`);p+=l.identity.sizeBytes,r.push({path:l.filePath,contentType:a.contentType,kind:"engine.playwright-attachment",name:a.name,summary:`${a.specTitle} retry ${a.retry.toString()}`,metadata:{...l.identity,specId:a.specId,specTitle:a.specTitle,retry:a.retry}})}catch(d){o.push(z(a,gt(d)))}return{artifacts:r,failures:o,sourcePaths:c}}async function _t(t,e,i,s,n){try{const r=await n.lstat(i);if(!r.isDirectory()||r.isSymbolicLink())throw new Error("Playwright output directory must be a non-symbolic-link directory");const o=await v.realpath(i);if(!N(t,o))throw new Error("Playwright output directory escaped the artifact root");const c=await $t(o,s,n),p=[],m=g.join(e,"output");c.candidates.length>0&&await v.mkdir(m,{mode:448,recursive:!1});for(const[h,a]of c.candidates.entries()){const d=await U(a.file,I,"Playwright output file",n,a.identity,m,`${h.toString().padStart(5,"0")}-${at(g.basename(a.file))}${jt(a.file)}`);p.push(O(d.filePath,d.identity,Lt(a.file),"engine.playwright-output",a.relativePath,"Playwright output"))}return{artifacts:p,bytes:c.bytes,entries:c.entries}}catch(r){if(ut(r))return{artifacts:[],bytes:0,entries:0};throw r}}async function $t(t,e,i){const s=[];let n=0,r=0;const o=[{depth:0,directory:t}];for(;o.length>0;){const c=o.pop();if(c===void 0)continue;if(c.depth>it)throw new Error(`Playwright output exceeds the ${it.toString()} level directory depth limit`);const p=c.directory,m=await v.readdir(p,{withFileTypes:!0});m.sort((h,a)=>h.name.localeCompare(a.name));for(const h of m){if(r+=1,r>et)throw new L({bytes:n,entries:r,kind:"entries",limit:et,observed:r});const a=g.join(p,h.name),d=await i.lstat(a);if(d.isSymbolicLink())throw new Error("Playwright output must not contain symbolic links");if(d.isDirectory()){o.push({depth:c.depth+1,directory:a});continue}if(!d.isFile())continue;if(d.size<0n||d.size>BigInt(Number.MAX_SAFE_INTEGER))throw new Error("Playwright output file has an invalid size");const w=Number(d.size);if(n+=w,!Number.isSafeInteger(n)||n>I)throw new L({bytes:n,entries:r,kind:"bytes",limit:I,observed:n});e.has(a)||s.push({file:a,identity:d,relativePath:Q(t,a)})}}return{bytes:n,candidates:s,entries:r}}function Mt(t){return t.kind==="bytes"?`Playwright output exceeds its ${t.limit.toString()} byte aggregate limit (observed ${t.observed.toString()} bytes)`:`Playwright output exceeds its ${t.limit.toString()} entry limit (observed ${t.observed.toString()} entries)`}async function Nt(t,e,i,s){if(t===void 0)throw new Error("Playwright path attachment did not declare a path");const n=g.resolve(e,t),r=await s.lstat(n);if(!r.isFile()||r.isSymbolicLink())throw new Error("Playwright attachment path must be a regular non-symbolic-link file");const o=await v.realpath(n);if(!N(e,o)&&!N(i,o))throw new Error("Playwright attachment path must stay within the consumer or artifact root");return{file:o,identity:r}}function Ot(t){const e=P(t,"Playwright report.stats");return{expected:T(e.expected,"Playwright stats.expected"),unexpected:T(e.unexpected,"Playwright stats.unexpected"),flaky:T(e.flaky,"Playwright stats.flaky"),skipped:T(e.skipped,"Playwright stats.skipped"),duration:J(e.duration,"Playwright stats.duration")}}function Rt(t){let e=vt;const i=[];for(const s of t){if(e<=0)break;const r=Buffer.from(s.text,"utf8").subarray(0,e).toString("utf8");r.length>0&&i.push({...s,text:r}),e-=Buffer.byteLength(r)}return e<=0&&i.push({stream:"engine",text:"Playwright report log evidence was truncated at its bounded limit"}),i}function It(t){if(t.path!==void 0){const e=g.extname(t.path);if(/^\.[a-z0-9]{1,12}$/iu.test(e))return e}return t.contentType==="application/json"?".json":t.contentType.startsWith("text/")?".txt":t.contentType==="image/png"?".png":t.contentType==="image/jpeg"?".jpg":".bin"}function Lt(t){const e=g.extname(t).toLowerCase();return e===".json"?"application/json":e===".zip"?"application/zip":e===".png"?"image/png":e===".jpg"||e===".jpeg"?"image/jpeg":e===".webm"?"video/webm":[".log",".txt",".md"].includes(e)?"text/plain; charset=utf-8":"application/octet-stream"}function jt(t){const e=g.extname(t).toLowerCase();return/^\.[a-z0-9]{1,12}$/u.test(e)?e:".bin"}function at(t){const e=t.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"").slice(0,80);return e.length>0?e:"attachment"}
@@ -1 +1 @@
1
- import f from"node:path";import E from"node:fs/promises";import x from"node:crypto";import{writeNativeReport as D}from"../../adapters/native-report.js";import{writeOwnedEvidenceFile as P}from"../../adapters/evidence-files.js";import{attachmentIoError as j,attachmentLimitError as M}from"./attachments.js";export default class A{options;config;suite;errors=[];constructor(e){this.options=e}onBegin(e,r){this.config=e,this.suite=r}onError(e){this.errors.push(F(e))}async onEnd(e){const r=this.suite.allTests(),o={duration:e.duration,expected:0,flaky:0,skipped:0,unexpected:0},u=[],p=[];let m=0;const d=[];for(const i of r){const y=i.outcome();o[y]+=1;let g=0,h=0,w=[],k=null;for(const s of i.results){g+=s.duration,h=Math.max(h,s.retry);for(const t of["stdout","stderr"])for(const a of s[t])u.push({stream:t,text:typeof a=="string"?a:a.toString("utf8")});for(const t of s.attachments){const a={name:t.name,contentType:t.contentType,...t.path===void 0?{}:{path:t.path},specId:i.id,specTitle:i.title,retry:s.retry};try{let c=t.body?.byteLength;if(c===void 0){if(t.path===void 0)throw new Error("Attachment has no body or path");const l=await E.lstat(t.path);if(!l.isFile()||l.isSymbolicLink())throw new Error("Attachment path must be a regular non-symbolic-link file");c=l.size}const b=M(c,m+c);if(b!==null){p.push({...a,error:b});continue}let v=t.path;if(t.body!==void 0){const l=this.options.attachmentDirectory;if(await E.realpath(l)!==f.resolve(l))throw new Error("Attachment directory must not resolve through symbolic links");v=await P(l,x.randomUUID(),t.body)}p.push({...a,path:v}),m+=c}catch(c){p.push({...a,error:j(c)})}}s.errors.length>0&&(w=s.errors.map(F),k=s.errors.find(t=>t.stack)?.stack??null)}d.push({id:i.id,title:i.title,parents:L(i,this.config.rootDir),file:T(this.config.rootDir,i.location.file),sourceFile:I(i,this.config.rootDir),projectName:i.parent.project()?.name??"",line:i.location.line,column:i.location.column,status:y,durationMs:g,retries:h,failureMessages:w,failureStack:k})}await D(this.options,{status:e.status,failOnFlakyTests:this.options.failOnFlakyTests,stats:o,specs:d,logs:u,attachments:p,errors:this.errors},64*1024*1024)}}function I(n,e){for(let r=n.parent;r;r=r.parent)if(r.type==="file")return f.resolve(e,r.title);return n.location.file}function L(n,e){const r=[];for(let o=n.parent;o&&o.type!=="project"&&o.type!=="root";o=o.parent)r.unshift(o.type==="file"&&o.location?T(e,o.location.file):o.title);return r}function T(n,e){return f.relative(n,e).split(f.sep).join("/")}function F(n){return n.message?.trim()||n.stack?.trim()||n.value?.trim()||"Playwright reported an error"}
1
+ import h from"node:path";import M from"node:fs/promises";import U from"node:crypto";import{stripVTControlCharacters as D}from"node:util";import{writeNativeReport as L}from"../../adapters/native-report.js";import{writeOwnedEvidenceFile as P}from"../../adapters/evidence-files.js";import{boundEnvironmentValueMissingText as V,environmentValueMissingFailure as j,readEnvironmentValueMissingText as F}from"../../contracts/environment-values.js";import{attachmentIoError as C,attachmentLimitError as O}from"./attachments.js";export default class S{options;config;suite;errors=[];missing=[];constructor(t){this.options=t}onBegin(t,r){this.config=t,this.suite=r}onError(t){this.missing.push(...x(t,!1)),this.errors.push(d(g(t)))}async onEnd(t){const r=this.suite.allTests(),o={duration:t.duration,expected:0,flaky:0,skipped:0,unexpected:0},p=[],c=[];let f=0;const y=[];for(const i of r){const v=i.outcome();o[v]+=1;let E=0,m=0,T=[],w=null;const k=[],R=i.parent.project()?.use.baseURL;for(const a of i.results){for(const n of a.errors)k.push(...x(n,!R));E+=a.duration,m=Math.max(m,a.retry);for(const n of["stdout","stderr"])for(const s of a[n])p.push({stream:n,text:typeof s=="string"?s:s.toString("utf8")});for(const n of a.attachments){const s={name:n.name,contentType:n.contentType,...n.path===void 0?{}:{path:n.path},specId:i.id,specTitle:i.title,retry:a.retry};try{let l=n.body?.byteLength;if(l===void 0){if(n.path===void 0)throw new Error("Attachment has no body or path");const u=await M.lstat(n.path);if(!u.isFile()||u.isSymbolicLink())throw new Error("Attachment path must be a regular non-symbolic-link file");l=u.size}const b=O(l,f+l);if(b!==null){c.push({...s,error:b});continue}let A=n.path;if(n.body!==void 0){const u=this.options.attachmentDirectory;if(await M.realpath(u)!==h.resolve(u))throw new Error("Attachment directory must not resolve through symbolic links");A=await P(u,U.randomUUID(),n.body)}c.push({...s,path:A}),f+=l}catch(l){c.push({...s,error:C(l)})}}if(a.errors.length>0){T=a.errors.map(s=>d(g(s)));const n=a.errors.find(s=>s.stack)?.stack;w=n===void 0?null:d(n)}}y.push({id:i.id,title:i.title,parents:X(i,this.config.rootDir),file:I(this.config.rootDir,i.location.file),sourceFile:Z(i,this.config.rootDir),projectName:i.parent.project()?.name??"",line:i.location.line,column:i.location.column,status:v,durationMs:E,retries:m,failureMessages:T,failureStack:w,...N(k)})}await L(this.options,{status:t.status,failOnFlakyTests:this.options.failOnFlakyTests,stats:o,specs:y,logs:p,attachments:c,errors:this.errors,...N(this.missing)},64*1024*1024)}}const z=8,_=/^(?:\w*Error: )?page\.goto: .*Cannot navigate to invalid URL/u,G=/^\s*- navigating to "([^"]*)"/mu,B=/^[A-Za-z][A-Za-z\d+.-]*:/u;function x(e,t){const r=[];let o=e;for(let p=0;p<z&&o!==void 0;p+=1){const c=g(o),f=[...F(c),...t?H(c):[]];f.length>0&&r.push({message:c,values:f}),o=o.cause}return r}function H(e){const t=D(e);if(!_.test(t))return[];const r=G.exec(t)?.[1];return r===void 0||B.test(r)?[]:[{kind:"endpoint",valueName:null}]}function N(e){const t=e.at(0);return t===void 0?{}:{environmentFailure:j(e.flatMap(({values:r})=>r),t.message)}}function d(e){return F(e).length===0?e:V(e)}function Z(e,t){for(let r=e.parent;r;r=r.parent)if(r.type==="file")return h.resolve(t,r.title);return e.location.file}function X(e,t){const r=[];for(let o=e.parent;o&&o.type!=="project"&&o.type!=="root";o=o.parent)r.unshift(o.type==="file"&&o.location?I(t,o.location.file):o.title);return r}function I(e,t){return h.relative(e,t).split(h.sep).join("/")}function g(e){return e.message?.trim()||e.stack?.trim()||e.value?.trim()||"Playwright reported an error"}
@@ -1 +1 @@
1
- import n from"node:fs";import c from"node:path";import{hasErrorCode as e}from"../../../kernel/error-serialization.js";export const PLAYWRIGHT_CONFIG_FILENAMES=Object.freeze(["playwright.config.ts","playwright.config.mts","playwright.config.js","playwright.config.mjs","playwright.config.cjs","playwright.config.cts"]);export function findPlaywrightConfigFile(r){for(const i of PLAYWRIGHT_CONFIG_FILENAMES){const o=c.join(r,i);try{if(n.statSync(o).isFile())return o}catch(t){if(e(t,"ENOENT"))continue;throw new Error(`Could not inspect optional support module ${o}`,{cause:t})}}return null}
1
+ import f from"node:fs";import i from"node:path";import{hasErrorCode as c}from"../../../kernel/error-serialization.js";export const PLAYWRIGHT_CONFIG_FILENAMES=Object.freeze(["playwright.config.ts","playwright.config.mts","playwright.config.js","playwright.config.mjs","playwright.config.cjs","playwright.config.cts"]);export function findPlaywrightConfigFile(e){for(const o of PLAYWRIGHT_CONFIG_FILENAMES){const r=i.join(e,o);try{if(f.statSync(r).isFile())return r}catch(t){if(c(t,"ENOENT")||c(t,"ENOTDIR"))continue;throw new Error(`Could not inspect optional support module ${r}`,{cause:t})}}return null}export function findNearestPlaywrightConfigFile(e,o){const r=i.resolve(e);let t=i.resolve(r,s(o));const n=i.relative(r,t);for((n.split(i.sep)[0]===".."||i.isAbsolute(n))&&(t=r);;){const l=findPlaywrightConfigFile(t);if(l!==null||t===r)return l;t=i.dirname(t)}}function s(e){if(e.length===0)return".";const[o,...r]=e.map(n=>i.posix.normalize(n).split("/").filter(l=>l!==""&&l!=="."));let t=0;for(;t<o.length&&r.every(n=>n[t]===o[t]);)t+=1;return o.slice(0,t).join("/")||"."}
@@ -1 +1 @@
1
- import{DEFAULT_FILE_TIMEOUT_SECONDS as w,INTERNAL_FILE_TIMEOUT_SECONDS_ENV as E}from"../../../domain/file-timeout.js";import{readPlaywrightContext as b}from"./runtime-input.js";import{defineConfig as T}from"@playwright/test";import{loadDotenvFiles as S}from"../../../config/runtime/env/index.js";import{isUnknownRecord as h}from"../../../kernel/unknown-values.js";export function defineTestkitConfig(n={},e={}){const t=e.env||process.env;S({cwd:e.cwd,env:t,files:e.dotenvFiles||[".env.local"],onlyWhenAllowed:e.onlyWhenAllowed,override:e.override});const o=U(t,e.defaultFileTimeoutSeconds)*1e3,i=b(),s=i?.managed===!0,c=a(n.use,"Playwright config.use"),m=a(n.expect,"Playwright config.expect"),d=p(c.baseURL,"Playwright config.use.baseURL"),u=i?.baseUrl??t.BASE_URL??d,f={...c,...u?{baseURL:u}:{},actionTimeout:o,navigationTimeout:o},g={...n,...s?{globalTimeout:o}:{},timeout:o,expect:{...m,timeout:o},use:f,...s?{webServer:void 0}:{}};return T(g)}function U(n,e){const t=n[E],r=l(e??w);return t==null?r:l(t)}function l(n){const e=Number(n);if(!Number.isInteger(e)||e<=0)throw new Error("Playwright file timeout seconds must be a positive integer.");return e}function a(n,e){if(n==null)return{};if(!h(n))throw new Error(`${e} must be an object`);return n}function p(n,e){if(n!=null){if(typeof n!="string")throw new Error(`${e} must be a string`);return n}}
1
+ import{DEFAULT_FILE_TIMEOUT_SECONDS as h,INTERNAL_FILE_TIMEOUT_SECONDS_ENV as E}from"../../../domain/file-timeout.js";import{readPlaywrightContext as T}from"./runtime-input.js";import{defineConfig as b}from"@playwright/test";import{isUnknownRecord as p}from"../../../kernel/unknown-values.js";const y=["cwd","dotenvFiles","onlyWhenAllowed","override"];export function defineTestkitConfig(e={},t={}){const n=y.filter(w=>Object.hasOwn(t,w));if(n.length>0)throw new Error(`defineTestkitConfig no longer loads dotenv files, so it does not accept ${n.join(", ")}. In a Testkit run, the services your tests use already provide their values. To load a file when you run Playwright on its own, use a dotenv library of your own in playwright.config.ts.`);const r=t.env||process.env,o=S(r,t.defaultFileTimeoutSeconds)*1e3,i=T(),s=i?.managed===!0,u=l(e.use,"Playwright config.use"),d=l(e.expect,"Playwright config.expect"),f=O(u.baseURL,"Playwright config.use.baseURL"),a=i?.baseUrl??r.BASE_URL??f,m={...u,...a?{baseURL:a}:{},actionTimeout:o,navigationTimeout:o},g={...e,...s?{globalTimeout:o}:{},timeout:o,expect:{...d,timeout:o},use:m,...s?{webServer:void 0}:{}};return b(g)}function S(e,t){const n=e[E],r=c(t??h);return n==null?r:c(n)}function c(e){const t=Number(e);if(!Number.isInteger(t)||t<=0)throw new Error("Playwright file timeout seconds must be a positive integer.");return t}function l(e,t){if(e==null)return{};if(!p(e))throw new Error(`${t} must be an object`);return e}function O(e,t){if(e!=null){if(typeof e!="string")throw new Error(`${t} must be a string`);return e}}
@@ -0,0 +1 @@
1
+ import s,{test as c}from"playwright/test";export*from"playwright/test";const t=c.extend({_browserOptions:[async({_browserOptions:o,playwright:e},n,r)=>{const a={...o,executablePath:u(r)};Reflect.set(e,"_defaultLaunchOptions",a),await n(a)},{auto:!0,box:!0,scope:"worker"}]});for(const[o,e]of Object.entries(s))Object.hasOwn(t,o)||Reflect.set(t,o,e);Reflect.set(t,"test",t);export{t as test,t as"module.exports"};export default t;function u(o){const e=o.project.use.launchOptions?.executablePath;if(typeof e!="string"||e.length===0)throw new Error("Managed Playwright config names no browser executable");return e}
@@ -1,9 +1,9 @@
1
- import l from"node:fs/promises";import c from"node:path";import{writeOwnedRuntimeInput as m,ownedRuntimeInputModuleUrl as f}from"../../runtime-input-file.js";import{requireBoundedJsonValue as y,RUNTIME_INPUT_JSON_BOUNDS as h}from"../../contracts/json-bounds.js";import{ENGINE_PROCESS_RESERVED_ENVIRONMENT_KEYS as w}from"../../../process/environment.js";import{managedProcessEnvironmentOverlayInstallerSource as g}from"../../../process/environment-overlay.js";import{isUnknownRecord as u}from"../../../kernel/unknown-values.js";export const PLAYWRIGHT_RUNTIME_INPUT_MAX_BYTES=4*1024*1024,PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES=2*1024*1024,PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES=512;const d=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","PLAYWRIGHT_BROWSERS_PATH","PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD","TESTKIT_ACTIVE","TESTKIT_INTERNAL_FILE_DEADLINE_MS","TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS","TESTKIT_INVOCATION_ID","TESTKIT_RUNTIME_ID","TESTKIT_TEST_FILE","TESTKIT_TEST_ID",...w].map(e=>e.toUpperCase())),p={filename:"runtime-input.json",label:"Playwright runtime input",maxSizeBytes:PLAYWRIGHT_RUNTIME_INPUT_MAX_BYTES},E=Symbol.for("@gfdelarue/testkit/playwright-runtime-input/v1");export function readPlaywrightContext(){const e=Reflect.get(globalThis,E);return T(e)?e.context:null}export async function materializePlaywrightRuntimeInput(e){if(!c.isAbsolute(e.directory))throw new Error("Playwright runtime-input directory must be absolute");await l.mkdir(e.directory,{recursive:!0,mode:448}),process.platform!=="win32"&&await l.chmod(e.directory,448);const t=I(e.inputs.environment),r={baseUrl:e.baseUrl===null?null:v(e.baseUrl),endpoints:e.endpoints,engineId:"playwright",environment:t,fixtures:e.inputs.fixtures,invocationId:e.invocationId,managed:!0,schemaVersion:1,taskId:e.taskId,uiConfig:e.inputs.configuration??{},...e.inputs.setupData===void 0?{}:{setupData:e.inputs.setupData}},o=await m(e.directory,r,p);return{digest:o.sha256,file:o.file,sizeBytes:o.sizeBytes}}export function playwrightRuntimeInputBootstrapSource(e){const t=JSON.stringify({digest:e.digest,file:e.file,ownedInput:{directory:c.dirname(e.file),file:e.file,sha256:e.digest,sizeBytes:e.sizeBytes},protocol:p,invocationId:e.invocationId,jsonBounds:h,maximumEnvironmentBytes:PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES,maximumEnvironmentEntries:PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES,reservedEnvironmentKeys:[...d],taskId:e.taskId}),n=g(),r=`const requireBoundedJsonValue = (${y.toString()});`;return`
1
+ import l from"node:fs/promises";import c from"node:path";import{writeOwnedRuntimeInput as m,ownedRuntimeInputModuleUrl as f}from"../../runtime-input-file.js";import{requireBoundedJsonValue as y,RUNTIME_INPUT_JSON_BOUNDS as h}from"../../adapters/json-bounds.js";import{ENGINE_PROCESS_RESERVED_ENVIRONMENT_KEYS as w}from"../../../process/environment.js";import{managedProcessEnvironmentOverlayInstallerSource as g}from"../../../process/environment-overlay.js";import{isUnknownRecord as u}from"../../../kernel/unknown-values.js";export const PLAYWRIGHT_RUNTIME_INPUT_MAX_BYTES=4*1024*1024,PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES=2*1024*1024,PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES=512;const d=new Set(["BASE_URL","FORCE_COLOR","NODE_OPTIONS","NO_COLOR","PLAYWRIGHT_BROWSERS_PATH","PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD","TESTKIT_ACTIVE","TESTKIT_INTERNAL_FILE_DEADLINE_MS","TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS","TESTKIT_INVOCATION_ID","TESTKIT_RUNTIME_ID","TESTKIT_TEST_FILE","TESTKIT_TEST_ID",...w].map(e=>e.toUpperCase())),p={filename:"runtime-input.json",label:"Playwright runtime input",maxSizeBytes:PLAYWRIGHT_RUNTIME_INPUT_MAX_BYTES},E=Symbol.for("@gfdelarue/testkit/playwright-runtime-input/v1");export function readPlaywrightContext(){const e=Reflect.get(globalThis,E);return T(e)?e.context:null}export async function materializePlaywrightRuntimeInput(e){if(!c.isAbsolute(e.directory))throw new Error("Playwright runtime-input directory must be absolute");await l.mkdir(e.directory,{recursive:!0,mode:448}),process.platform!=="win32"&&await l.chmod(e.directory,448);const t=I(e.inputs.environment),i={baseUrl:e.baseUrl===null?null:v(e.baseUrl),databases:e.databases,endpoints:e.endpoints,engineId:"playwright",environment:t,fixtures:e.inputs.fixtures,handles:e.handles,invocationId:e.invocationId,managed:!0,schemaVersion:1,taskId:e.taskId,uiConfig:e.inputs.configuration??{},...e.inputs.setupData===void 0?{}:{setupData:e.inputs.setupData}},o=await m(e.directory,i,p);return{digest:o.sha256,file:o.file,sizeBytes:o.sizeBytes}}export function playwrightRuntimeInputBootstrapSource(e){const t=JSON.stringify({digest:e.digest,file:e.file,ownedInput:{directory:c.dirname(e.file),file:e.file,sha256:e.digest,sizeBytes:e.sizeBytes},protocol:p,invocationId:e.invocationId,jsonBounds:h,maximumEnvironmentBytes:PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES,maximumEnvironmentEntries:PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES,reservedEnvironmentKeys:[...d],taskId:e.taskId}),n=g(),i=`const requireBoundedJsonValue = (${y.toString()});`;return`
2
2
  import path from "node:path";
3
3
  import { readVerifiedOwnedFile } from ${JSON.stringify(f)};
4
4
 
5
5
  ${n}
6
- ${r}
6
+ ${i}
7
7
  const OPTIONS = ${t};
8
8
  const STATE = Symbol.for("@gfdelarue/testkit/playwright-runtime-input/v1");
9
9
 
@@ -31,9 +31,11 @@ export async function installPlaywrightRuntimeInput() {
31
31
  });
32
32
  const context = Object.freeze({
33
33
  baseUrl: input.baseUrl,
34
+ databases: input.databases,
34
35
  endpoints: input.endpoints,
35
36
  env: input.environment,
36
37
  fixtures: input.fixtures,
38
+ handles: input.handles,
37
39
  managed: true,
38
40
  ...(input.setupData === undefined ? {} : { setupData: input.setupData }),
39
41
  uiConfig: input.uiConfig,
@@ -68,11 +70,11 @@ function deepFreezeJson(root) {
68
70
 
69
71
  function validateRuntimeInput(value) {
70
72
  const input = requireRecord(value, "Playwright runtime input");
71
- const allowed = new Set(["baseUrl", "endpoints", "engineId", "environment", "fixtures", "invocationId", "managed", "schemaVersion", "setupData", "taskId", "uiConfig"]);
73
+ const allowed = new Set(["baseUrl", "databases", "endpoints", "engineId", "environment", "fixtures", "handles", "invocationId", "managed", "schemaVersion", "setupData", "taskId", "uiConfig"]);
72
74
  for (const key of Object.keys(input)) {
73
75
  if (!allowed.has(key)) throw new Error("Playwright runtime input contains unsupported field " + key);
74
76
  }
75
- for (const key of ["baseUrl", "endpoints", "engineId", "environment", "fixtures", "invocationId", "managed", "schemaVersion", "taskId", "uiConfig"]) {
77
+ for (const key of ["baseUrl", "databases", "endpoints", "engineId", "environment", "fixtures", "handles", "invocationId", "managed", "schemaVersion", "taskId", "uiConfig"]) {
76
78
  if (!Object.prototype.hasOwnProperty.call(input, key))
77
79
  throw new Error("Playwright runtime input is missing field " + key);
78
80
  }
@@ -83,14 +85,13 @@ function validateRuntimeInput(value) {
83
85
  if (input.baseUrl !== null) validateBaseUrl(input.baseUrl);
84
86
  if (input.invocationId !== OPTIONS.invocationId || input.taskId !== OPTIONS.taskId)
85
87
  throw new Error("Playwright runtime input does not match its invocation and task");
86
- requireStringRecord(input.endpoints, "Playwright runtime input.endpoints");
88
+ requireNamedValues(input.databases, "Playwright runtime input.databases");
89
+ requireNamedValues(input.endpoints, "Playwright runtime input.endpoints");
90
+ requireHandles(input.handles, "Playwright runtime input.handles");
87
91
  validateEnvironment(input.environment);
88
92
  requireRecord(input.fixtures, "Playwright runtime input.fixtures");
89
93
  requireRecord(input.uiConfig, "Playwright runtime input.uiConfig");
90
- requireBoundedJsonValue(input, "Playwright runtime input", OPTIONS.jsonBounds, {
91
- unitLabel: "bytes",
92
- unitsOf: (text) => Buffer.byteLength(text, "utf8"),
93
- });
94
+ requireBoundedJsonValue(input, "Playwright runtime input", OPTIONS.jsonBounds);
94
95
  return input;
95
96
  }
96
97
 
@@ -136,6 +137,24 @@ function validateEnvironmentEntry(key, value) {
136
137
  throw new Error("Playwright runtime input.environment contains an invalid value for " + key);
137
138
  }
138
139
 
140
+ function requireNamedValues(value, label) {
141
+ const record = requireRecord(value, label);
142
+ for (const [key, entry] of Object.entries(record)) {
143
+ if (key.length === 0 || typeof entry !== "string" || entry.length === 0 || entry.includes("\\0"))
144
+ throw new Error(label + " must map names to non-empty strings");
145
+ }
146
+ return record;
147
+ }
148
+
149
+ function requireHandles(value, label) {
150
+ const record = requireRecord(value, label);
151
+ for (const [name, descriptor] of Object.entries(record)) {
152
+ if (name.length === 0 || typeof descriptor !== "object" || descriptor === null || Array.isArray(descriptor) || typeof descriptor.kind !== "string" || descriptor.kind.length === 0)
153
+ throw new Error(label + " must map names to handle descriptors with a kind");
154
+ }
155
+ return record;
156
+ }
157
+
139
158
  function requireStringRecord(value, label) {
140
159
  const record = requireRecord(value, label);
141
160
  for (const [key, entry] of Object.entries(record)) {
@@ -152,4 +171,4 @@ function requireRecord(value, label) {
152
171
  }
153
172
 
154
173
  await installPlaywrightRuntimeInput();
155
- `}function T(e){return!u(e)||!u(e.context)||!u(e.input)?!1:typeof e.digest=="string"&&typeof e.file=="string"&&e.input.engineId==="playwright"&&e.input.schemaVersion===1&&Object.isFrozen(e)&&Object.isFrozen(e.context)&&Object.isFrozen(e.input)}function I(e){const t=Object.create(null),n=new Set;let r=0;const o=Object.entries(e);if(o.length>PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES)throw new Error(`Playwright consumer environment exceeds its ${PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES.toString()} entry limit`);for(const[a,s]of o){O(a,s);const i=a.toUpperCase();if(n.has(i))throw new Error(`Playwright consumer environment contains case-insensitive duplicate key ${a}`);if(n.add(i),i.startsWith("TESTKIT_")||i.startsWith("PLAYWRIGHT_")||i.startsWith("PWTEST_")||i.startsWith("PW_TEST_")||d.has(i))throw new Error(`Playwright consumer environment contains reserved key ${a}`);if(r+=Buffer.byteLength(a,"utf8")+Buffer.byteLength(s,"utf8")+2,r>PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright consumer environment exceeds its ${PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES.toString()} byte limit`);t[a]=s}return t}function v(e){if(typeof e!="string"||e.trim().length===0)throw new Error("Playwright runtime input base URL must be non-empty");let t;try{t=new URL(e)}catch(n){throw new Error("Playwright runtime input base URL must be a valid URL",{cause:n})}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new Error("Playwright runtime input base URL must use HTTP or HTTPS");return t.href}function O(e,t){if(e.length===0||e.includes("=")||e.includes("\0")||typeof t!="string")throw new Error("Playwright consumer environment contains an invalid key");if(t.includes("\0"))throw new Error(`Playwright consumer environment contains an invalid value for ${e}`)}
174
+ `}function T(e){return!u(e)||!u(e.context)||!u(e.input)?!1:typeof e.digest=="string"&&typeof e.file=="string"&&e.input.engineId==="playwright"&&e.input.schemaVersion===1&&Object.isFrozen(e)&&Object.isFrozen(e.context)&&Object.isFrozen(e.input)}function I(e){const t=Object.create(null),n=new Set;let i=0;const o=Object.entries(e);if(o.length>PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES)throw new Error(`Playwright consumer environment exceeds its ${PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_ENTRIES.toString()} entry limit`);for(const[a,s]of o){b(a,s);const r=a.toUpperCase();if(n.has(r))throw new Error(`Playwright consumer environment contains case-insensitive duplicate key ${a}`);if(n.add(r),r.startsWith("TESTKIT_")||r.startsWith("PLAYWRIGHT_")||r.startsWith("PWTEST_")||r.startsWith("PW_TEST_")||d.has(r))throw new Error(`Playwright consumer environment contains reserved key ${a}`);if(i+=Buffer.byteLength(a,"utf8")+Buffer.byteLength(s,"utf8")+2,i>PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES)throw new Error(`Playwright consumer environment exceeds its ${PLAYWRIGHT_RUNTIME_ENVIRONMENT_MAX_BYTES.toString()} byte limit`);t[a]=s}return t}function v(e){if(typeof e!="string"||e.trim().length===0)throw new Error("Playwright runtime input base URL must be non-empty");let t;try{t=new URL(e)}catch(n){throw new Error("Playwright runtime input base URL must be a valid URL",{cause:n})}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new Error("Playwright runtime input base URL must use HTTP or HTTPS");return t.href}function b(e,t){if(e.length===0||e.includes("=")||e.includes("\0")||typeof t!="string")throw new Error("Playwright consumer environment contains an invalid key");if(t.includes("\0"))throw new Error(`Playwright consumer environment contains an invalid value for ${e}`)}
@@ -1 +1 @@
1
- import u from"node:crypto";import a from"node:fs/promises";import o from"node:path";import{serializeBoundedJson as c,RUNTIME_INPUT_JSON_BOUNDS as h}from"./contracts/json-bounds.js";import{readStableEvidenceFile as y,evidenceFileAccess as b}from"./adapters/evidence-files.js";export const ownedRuntimeInputModuleUrl=new URL("../../dist/engines/runtime-input-file.js",import.meta.url).href,RUNTIME_INPUT_BYTE_MEASURE={unitLabel:"bytes",unitsOf:t=>Buffer.byteLength(t,"utf8")};export async function writeOwnedRuntimeInput(t,e,i){const r=c(e,i.label,h,RUNTIME_INPUT_BYTE_MEASURE),s=await m(t,i.label),w=Buffer.byteLength(r);if(w>i.maxSizeBytes)throw new Error(`${i.label} exceeds the ${i.maxSizeBytes.toString()} byte limit`);const f=o.join(s,i.filename),l=o.join(s,`.${i.filename}.${process.pid.toString()}.${u.randomUUID()}.tmp`);let n=null;try{n=await a.open(l,"wx",384),await n.writeFile(r,"utf8"),await n.close(),n=null,await a.rename(l,f),await a.chmod(f,384)}catch(d){throw await n?.close().catch(()=>{}),await a.rm(l,{force:!0}).catch(()=>{}),d}return{directory:s,file:f,sha256:u.createHash("sha256").update(r,"utf8").digest("hex"),sizeBytes:w}}export async function readVerifiedOwnedFile(t,e){const i=await m(t.directory,e.label);if(o.dirname(t.file)!==i)throw new Error(`${e.label} escaped its owned task directory`);const r=await a.lstat(t.file,{bigint:!0});if(!r.isFile()||r.isSymbolicLink())throw new Error(`${e.label} must be an owned regular file`);if(r.size!==BigInt(t.sizeBytes)||r.size>BigInt(e.maxSizeBytes))throw new Error(`${e.label} size changed after preparation`);if(process.platform!=="win32"&&(r.mode&63n)!==0n)throw new Error(`${e.label} permissions must be owner-only`);const s=await a.realpath(t.file);if(s!==t.file||o.dirname(s)!==i)throw new Error(`${e.label} resolved outside its owned task directory`);const{contents:w,sha256:f}=await y(s,e.maxSizeBytes,e.label,b,r),l=Buffer.from(t.sha256),n=Buffer.from(f);if(l.length!==n.length||!u.timingSafeEqual(n,l))throw new Error(`${e.label} integrity check failed`);return w.toString("utf8")}async function m(t,e){if(!o.isAbsolute(t))throw new Error(`${e} directory must be absolute`);const i=await a.realpath(t);if(!(await a.stat(i)).isDirectory())throw new Error(`${e} directory must be a directory`);return i}
1
+ import m from"node:crypto";import a from"node:fs/promises";import f from"node:path";import{serializeBoundedJson as c,RUNTIME_INPUT_JSON_BOUNDS as h}from"./adapters/json-bounds.js";import{readStableEvidenceFile as y,evidenceFileAccess as b}from"./adapters/evidence-files.js";export const ownedRuntimeInputModuleUrl=new URL("../../dist/engines/runtime-input-file.js",import.meta.url).href;export async function writeOwnedRuntimeInput(i,e,t){const r=c(e,t.label,h),s=await d(i,t.label),w=Buffer.byteLength(r);if(w>t.maxSizeBytes)throw new Error(`${t.label} exceeds the ${t.maxSizeBytes.toString()} byte limit`);const o=f.join(s,t.filename),l=f.join(s,`.${t.filename}.${process.pid.toString()}.${m.randomUUID()}.tmp`);let n=null;try{n=await a.open(l,"wx",384),await n.writeFile(r,"utf8"),await n.close(),n=null,await a.rename(l,o),await a.chmod(o,384)}catch(u){throw await n?.close().catch(()=>{}),await a.rm(l,{force:!0}).catch(()=>{}),u}return{directory:s,file:o,sha256:m.createHash("sha256").update(r,"utf8").digest("hex"),sizeBytes:w}}export async function readVerifiedOwnedFile(i,e){const t=await d(i.directory,e.label);if(f.dirname(i.file)!==t)throw new Error(`${e.label} escaped its owned task directory`);const r=await a.lstat(i.file,{bigint:!0});if(!r.isFile()||r.isSymbolicLink())throw new Error(`${e.label} must be an owned regular file`);if(r.size!==BigInt(i.sizeBytes)||r.size>BigInt(e.maxSizeBytes))throw new Error(`${e.label} size changed after preparation`);if(process.platform!=="win32"&&(r.mode&63n)!==0n)throw new Error(`${e.label} permissions must be owner-only`);const s=await a.realpath(i.file);if(s!==i.file||f.dirname(s)!==t)throw new Error(`${e.label} resolved outside its owned task directory`);const{contents:w,sha256:o}=await y(s,e.maxSizeBytes,e.label,b,r),l=Buffer.from(i.sha256),n=Buffer.from(o);if(l.length!==n.length||!m.timingSafeEqual(n,l))throw new Error(`${e.label} integrity check failed`);return w.toString("utf8")}async function d(i,e){if(!f.isAbsolute(i))throw new Error(`${e} directory must be absolute`);const t=await a.realpath(i);if(!(await a.stat(t)).isDirectory())throw new Error(`${e} directory must be a directory`);return t}
@@ -1 +1 @@
1
- import H from"crypto";import{isTerminalRunStatus as N}from"../process/runs/schema.js";import{observeProcessCreationIdentity as x}from"../process/process-identity.js";export{isRunManifestActive}from"../process/runs/inspection.js";import{normalizeOperationReason as U}from"../domain/operation-reason.js";import{finalizeLiveRunArtifact as W}from"../evidence/index.js";import{captureProcessCreationIdentity as V}from"../process/service-processes.js";import{requireProductLifecycleFenceProductDir as B,withProductLifecycleFence as Y}from"../process/lifecycle-fence.js";import{createRunManifest as J,listRunManifests as K,readRunMaintenanceStopRequest as Q,removeRunManifestFile as _,requireSafeRunId as X,RUN_MANIFEST_SCHEMA_VERSION as Z,runManifestPath as ee,writeRunManifest as te}from"../process/runs/manifest.js";const ne=1e3,re=String,k=new WeakMap;export function createUnpublishedRunLifecycle(n,r={}){const s=r.runId===void 0?ue():X(r.runId,"Testkit run id"),u=ee(n,s),o=new AbortController,I=new AbortController,d=r.pid===void 0?process.pid:r.pid;if(!Number.isSafeInteger(d)||d<0)throw new Error("Testkit run pid must be a non-negative safe integer");const S=d>0?V(d):null;if(d>0&&S===null)throw new Error(`Could not capture the Testkit run process identity for pid ${d.toString()}`);let f={incarnationId:H.randomUUID(),schemaVersion:Z,runId:s,pid:d,processIdentity:typeof S=="string"?S:null,controller:se(r.controller),status:"starting",startedAt:new Date().toISOString(),heartbeatAt:r.heartbeat===!1?null:new Date().toISOString(),interruptReason:null,services:[],runtimeDirs:[]};const D=[];let w,O=null;const y=new Set,b=new Set,M=r.keepAlive===!0?setInterval(()=>{},ne):null;let R=null,g=null,v=null,L=!1;function $(e){const t=k.get(h);if(!t)throw new Error("Testkit run lifecycle ownership is unavailable");if(!t.published){J(n,e),t.published=!0;return}te(n,e)}function A(){const e=Q(n,s);return e===null||e.targetIncarnationId!==f.incarnationId||e.targetPid!==d||e.targetProcessIdentity!==f.processIdentity||e.coordinatorPid===O?.coordinatorPid&&e.coordinatorProcessIdentity===O.coordinatorProcessIdentity?null:x(e.coordinatorPid,e.coordinatorProcessIdentity)==="different"?(O=e,null):e}function m(e){E("mutate run state");const t=ie(f);e(t),t.heartbeatAt!==null&&(t.heartbeatAt=new Date().toISOString()),$(t),f=t}function E(e){if(g!==null)throw new Error(`Cannot ${e} after Testkit run ${s} finalization is committed`)}function G(){R||(R=setInterval(()=>{},1e3))}function P(){R&&(clearInterval(R),R=null)}function q(){for(const e of b)clearInterval(e);b.clear()}function z(e="controller process exited before Testkit run finalized"){if(L||N(f.status))return!1;const t=U(e,"controller process exited before Testkit run finalized"),i=new Date,a=f.status==="interrupting"||o.signal.aborted?"interrupted":"failed",l=a==="interrupted"?f.interruptReason??v??t:t;return m(p=>{p.status=a,p.finishedAt=i.toISOString(),p.interruptReason=l,p.heartbeatAt!==null&&(p.heartbeatAt=i.toISOString())}),W(n,{runId:s,reason:l,status:a,finishedAt:i}),!0}const F=()=>{z()};process.on("exit",F);const h={runId:s,manifestPath:u,maintenanceStopSignal:I.signal,signal:o.signal,commitFinalization(e="finished"){if(g!==null)return g;const t=A(),i=o.signal.aborted||t!==null||f.status==="interrupting",a=i?f.interruptReason??v??t?.reason??"run interrupted":null,l=Object.freeze({interruptReason:a,interrupted:i});return m(p=>{p.status=i?"interrupted":e,p.finishedAt=new Date().toISOString(),p.interruptReason=a}),g=l,q(),l},getInterruptReason(){return f.interruptReason??v},isMaintenanceStopCoordinated(){return A()!==null},isStopRequested(){return o.signal.aborted||g?.interrupted===!0},markRunning(){m(e=>{e.status="running"})},markFinished(e="finished"){m(t=>{t.status=e,t.finishedAt=new Date().toISOString()})},heartbeat(e={}){m(t=>{t.heartbeatAt=new Date().toISOString(),e&&typeof e=="object"&&!Array.isArray(e)&&(t.session={...t.session||{},...e})})},startHeartbeat(e=5e3,t=null){E("start a run heartbeat");const i=Number.isFinite(Number(e))&&Number(e)>0?Math.floor(Number(e)):5e3,a=setInterval(()=>{let l;try{l=typeof t=="function"?t():{}}catch{l={}}h.heartbeat(l)},i);return a.unref(),b.add(a),()=>{clearInterval(a),b.delete(a)}},requestStop(e="interrupted"){if(g!==null||N(f.status))return;const t=A(),i=t?t.reason:U(e,"interrupted"),a=!o.signal.aborted;a&&(v=i),G();let l=null;if(a)try{m(c=>{c.status="interrupting",c.interruptReason=i})}catch(c){l=c instanceof Error?c:new Error("Failed to persist the Testkit stop request",{cause:c})}t!==null&&!I.signal.aborted&&I.abort(new Error(`Testkit run ${s} is being stopped by external maintenance`));const p=[...y].map(({supervisor:c})=>c.stop(t===null?void 0:Date.parse(t.requestedAt)+c.shutdownTimeoutMs));if(Promise.allSettled(p).then(P),o.signal.aborted||o.abort(new Error(`testkit run interrupted (${i})`)),!!a&&l!==null)throw l},trackGraphContext(e){m(t=>{ae(t.runtimeDirs,e.runtimeDir)})},registerService(e,t,i,a){if(E("register application service ownership"),i.pid!==t.pid||(process.platform==="win32"?i.recovery.processGroupId!==null:i.recovery.processGroupId!==i.pid))throw new Error("Application service supervisor returned an invalid ownership handoff");const l={supervisor:t,unsubscribe:()=>{}};y.add(l);const p=oe(e);m(c=>{c.services=c.services.filter(C=>C.pid!==i.pid),c.services.push({serviceName:e.name,runtimeLabel:e.runtimeLabel,command:e.testkit.local.start||null,cwd:a,pid:i.pid,processGroupId:i.recovery.processGroupId,processIdentity:i.recovery.creationIdentity,ports:p,startedAt:new Date().toISOString(),shutdownTimeoutMs:t.shutdownTimeoutMs,shutdownDeadlineAt:t.shutdownDeadlineAt})}),l.unsubscribe=t.onShutdownStarted(c=>{m(C=>{const T=C.services.find(j=>j.pid===i.pid);T&&(T.shutdownDeadlineAt=T.shutdownDeadlineAt==null?c:Math.min(T.shutdownDeadlineAt,c))})})},unregisterService(e){E("unregister application service ownership");for(const t of y)t.supervisor.pid===e&&(t.unsubscribe(),y.delete(t));m(t=>{t.services=t.services.filter(i=>i.pid!==e)})},installSignalHandlers(){if(!(D.length>0)&&!(d!==process.pid||x(d,f.processIdentity)!=="matches")){for(const e of["SIGINT","SIGTERM","SIGHUP"]){const t=h.requestStop.bind(h,e);process.on(e,t),D.push([e,t])}process.platform==="win32"&&(w=setInterval(()=>{const e=A();e!==null&&(clearInterval(w),h.requestStop(e.reason))},100),w.unref(),b.add(w))}},removeSignalHandlers(){w&&(clearInterval(w),b.delete(w),w=void 0);for(const[e,t]of D.splice(0))process.off(e,t)},async removeManifest(){await Y(n,e=>cleanupOwnedRunLifecycleWithinLifecycleFence(e,h))},finalizeForControllerExit:z,dispose(){L=!0;for(const e of y)e.unsubscribe();process.off("exit",F),h.removeSignalHandlers(),M&&clearInterval(M),P(),q()}};return k.set(h,{incarnationId:f.incarnationId,pid:d,processIdentity:f.processIdentity,productDir:n,published:!1,runId:s}),h}function ie(n){const r={...n,controller:n.controller===null?null:{...n.controller},runtimeDirs:[...n.runtimeDirs],services:n.services.map(s=>({...s,ports:s.ports.map(u=>({...u}))}))};return n.session!==void 0&&(r.session=n.session===null?null:{...n.session}),r}function se(n){return!n||typeof n!="object"||Array.isArray(n)?null:Object.fromEntries(Object.entries(n).filter(([,r])=>r!==void 0).map(([r,s])=>[r,s===null?null:re(s)]))}export function cleanupOwnedRunLifecycleWithinLifecycleFence(n,r){const{manifest:s,ownership:u,productDir:o}=readOwnedRunLifecycleWithinLifecycleFence(n,r);if(!s)return!1;if(s.services.length>0)throw new Error(`Cannot clean Testkit run ${r.runId} service ownership while holding the lifecycle fence`);return _(o,r.runId),u.published=!1,!0}export function readOwnedRunLifecycleWithinLifecycleFence(n,r){const s=B(n),u=k.get(r);if(!u||u.productDir!==s)throw new Error("Cannot clean a Testkit run without matching lifecycle ownership");if(u.runId!==r.runId)throw new Error("Testkit run lifecycle identity has been modified");const o=K(s).find(I=>I.runId===r.runId);if(o!==void 0&&(o.incarnationId!==u.incarnationId||o.pid!==u.pid||o.processIdentity!==u.processIdentity))throw new Error(`Testkit run ${r.runId} ownership no longer matches its manifest`);return{manifest:o??null,ownership:u,productDir:s}}function oe(n){const r=new Set,s=[];for(const u of[n.testkit.local.baseUrl,n.testkit.local.readyUrl])if(u)try{const o=new URL(u),I=le(o.hostname),d=Number(o.port||(o.protocol==="https:"?443:80)),S=`${I}:${d.toString()}`;if(r.has(S))continue;r.add(S),s.push({host:I,port:d})}catch{}return s}function ae(n,r){r&&(n.includes(r)||n.push(r))}function ue(){return`${Date.now().toString()}-${process.pid.toString()}-${H.randomBytes(4).toString("hex")}`}function le(n){return n?n==="::1"?"127.0.0.1":n:"127.0.0.1"}export function disposeFailedRunLifecycle(n){const r=k.get(n);n.dispose(),n.removeSignalHandlers(),r?.published===!0&&_(r.productDir,n.runId)}
1
+ import H from"crypto";import{isTerminalRunStatus as N}from"../process/runs/schema.js";import{observeProcessCreationIdentity as x}from"../process/process-identity.js";export{isRunManifestActive}from"../process/runs/inspection.js";import{normalizeOperationReason as U}from"../domain/operation-reason.js";import{finalizeLiveRunArtifact as W}from"../evidence/index.js";import{captureProcessCreationIdentity as V}from"../process/service-processes.js";import{requireProductLifecycleFenceProductDir as K,withProductLifecycleFence as B}from"../process/lifecycle-fence.js";import{createRunManifest as Y,listRunManifests as J,readRunMaintenanceStopRequest as Q,removeRunManifestFile as _,requireSafeRunId as X,RUN_MANIFEST_SCHEMA_VERSION as Z,runManifestPath as ee,writeRunManifest as te}from"../process/runs/manifest.js";const ne=1e3,re=String,k=new WeakMap;export function createUnpublishedRunLifecycle(n,r={}){const s=r.runId===void 0?ue():X(r.runId,"Testkit run id"),u=ee(n,s),o=new AbortController,I=new AbortController,d=r.pid===void 0?process.pid:r.pid;if(!Number.isSafeInteger(d)||d<0)throw new Error("Testkit run pid must be a non-negative safe integer");const S=d>0?V(d):null;if(d>0&&S===null)throw new Error(`Could not capture the Testkit run process identity for pid ${d.toString()}`);let f={incarnationId:H.randomUUID(),schemaVersion:Z,runId:s,pid:d,processIdentity:typeof S=="string"?S:null,controller:se(r.controller),status:"starting",startedAt:new Date().toISOString(),heartbeatAt:r.heartbeat===!1?null:new Date().toISOString(),interruptReason:null,services:[],runtimeDirs:[]};const D=[];let w,O=null;const y=new Set,b=new Set,M=r.keepAlive===!0?setInterval(()=>{},ne):null;let R=null,g=null,v=null,L=!1;function $(e){const t=k.get(h);if(!t)throw new Error("Testkit run lifecycle ownership is unavailable");if(!t.published){Y(n,e),t.published=!0;return}te(n,e)}function A(){const e=Q(n,s);return e===null||e.targetIncarnationId!==f.incarnationId||e.targetPid!==d||e.targetProcessIdentity!==f.processIdentity||e.coordinatorPid===O?.coordinatorPid&&e.coordinatorProcessIdentity===O.coordinatorProcessIdentity?null:x(e.coordinatorPid,e.coordinatorProcessIdentity)==="different"?(O=e,null):e}function m(e){T("mutate run state");const t=ie(f);e(t),t.heartbeatAt!==null&&(t.heartbeatAt=new Date().toISOString()),$(t),f=t}function T(e){if(g!==null)throw new Error(`Cannot ${e} after Testkit run ${s} finalization is committed`)}function G(){R||(R=setInterval(()=>{},1e3))}function P(){R&&(clearInterval(R),R=null)}function q(){for(const e of b)clearInterval(e);b.clear()}function z(e="controller process exited before Testkit run finalized"){if(L||N(f.status))return!1;const t=U(e,"controller process exited before Testkit run finalized"),i=new Date,a=f.status==="interrupting"||o.signal.aborted?"interrupted":"failed",l=a==="interrupted"?f.interruptReason??v??t:t;return m(p=>{p.status=a,p.finishedAt=i.toISOString(),p.interruptReason=l,p.heartbeatAt!==null&&(p.heartbeatAt=i.toISOString())}),W(n,{runId:s,reason:l,redactKeys:r.redactKeys,redactionTargets:r.redactionTargets?.(),status:a,finishedAt:i}),!0}const F=()=>{z()};process.on("exit",F);const h={runId:s,manifestPath:u,maintenanceStopSignal:I.signal,signal:o.signal,commitFinalization(e="finished"){if(g!==null)return g;const t=A(),i=o.signal.aborted||t!==null||f.status==="interrupting",a=i?f.interruptReason??v??t?.reason??"run interrupted":null,l=Object.freeze({interruptReason:a,interrupted:i});return m(p=>{p.status=i?"interrupted":e,p.finishedAt=new Date().toISOString(),p.interruptReason=a}),g=l,q(),l},getInterruptReason(){return f.interruptReason??v},isMaintenanceStopCoordinated(){return A()!==null},isStopRequested(){return o.signal.aborted||g?.interrupted===!0},markRunning(){m(e=>{e.status="running"})},markFinished(e="finished"){m(t=>{t.status=e,t.finishedAt=new Date().toISOString()})},heartbeat(e={}){m(t=>{t.heartbeatAt=new Date().toISOString(),e&&typeof e=="object"&&!Array.isArray(e)&&(t.session={...t.session||{},...e})})},startHeartbeat(e=5e3,t=null){T("start a run heartbeat");const i=Number.isFinite(Number(e))&&Number(e)>0?Math.floor(Number(e)):5e3,a=setInterval(()=>{let l;try{l=typeof t=="function"?t():{}}catch{l={}}h.heartbeat(l)},i);return a.unref(),b.add(a),()=>{clearInterval(a),b.delete(a)}},requestStop(e="interrupted"){if(g!==null||N(f.status))return;const t=A(),i=t?t.reason:U(e,"interrupted"),a=!o.signal.aborted;a&&(v=i),G();let l=null;if(a)try{m(c=>{c.status="interrupting",c.interruptReason=i})}catch(c){l=c instanceof Error?c:new Error("Failed to persist the Testkit stop request",{cause:c})}t!==null&&!I.signal.aborted&&I.abort(new Error(`Testkit run ${s} is being stopped by external maintenance`));const p=[...y].map(({supervisor:c})=>c.stop(t===null?void 0:Date.parse(t.requestedAt)+c.shutdownTimeoutMs));if(Promise.allSettled(p).then(P),o.signal.aborted||o.abort(new Error(`testkit run interrupted (${i})`)),!!a&&l!==null)throw l},trackGraphContext(e){m(t=>{ae(t.runtimeDirs,e.runtimeDir)})},registerService(e,t,i,a){if(T("register application service ownership"),i.pid!==t.pid||(process.platform==="win32"?i.recovery.processGroupId!==null:i.recovery.processGroupId!==i.pid))throw new Error("Application service supervisor returned an invalid ownership handoff");const l={supervisor:t,unsubscribe:()=>{}};y.add(l);const p=oe(e);m(c=>{c.services=c.services.filter(C=>C.pid!==i.pid),c.services.push({serviceName:e.name,runtimeLabel:e.runtimeLabel,command:e.testkit.local.start||null,cwd:a,pid:i.pid,processGroupId:i.recovery.processGroupId,processIdentity:i.recovery.creationIdentity,ports:p,startedAt:new Date().toISOString(),shutdownTimeoutMs:t.shutdownTimeoutMs,shutdownDeadlineAt:t.shutdownDeadlineAt})}),l.unsubscribe=t.onShutdownStarted(c=>{m(C=>{const E=C.services.find(j=>j.pid===i.pid);E&&(E.shutdownDeadlineAt=E.shutdownDeadlineAt==null?c:Math.min(E.shutdownDeadlineAt,c))})})},unregisterService(e){T("unregister application service ownership");for(const t of y)t.supervisor.pid===e&&(t.unsubscribe(),y.delete(t));m(t=>{t.services=t.services.filter(i=>i.pid!==e)})},installSignalHandlers(){if(!(D.length>0)&&!(d!==process.pid||x(d,f.processIdentity)!=="matches")){for(const e of["SIGINT","SIGTERM","SIGHUP"]){const t=h.requestStop.bind(h,e);process.on(e,t),D.push([e,t])}process.platform==="win32"&&(w=setInterval(()=>{const e=A();e!==null&&(clearInterval(w),h.requestStop(e.reason))},100),w.unref(),b.add(w))}},removeSignalHandlers(){w&&(clearInterval(w),b.delete(w),w=void 0);for(const[e,t]of D.splice(0))process.off(e,t)},async removeManifest(){await B(n,e=>cleanupOwnedRunLifecycleWithinLifecycleFence(e,h))},finalizeForControllerExit:z,dispose(){L=!0;for(const e of y)e.unsubscribe();process.off("exit",F),h.removeSignalHandlers(),M&&clearInterval(M),P(),q()}};return k.set(h,{incarnationId:f.incarnationId,pid:d,processIdentity:f.processIdentity,productDir:n,published:!1,runId:s}),h}function ie(n){const r={...n,controller:n.controller===null?null:{...n.controller},runtimeDirs:[...n.runtimeDirs],services:n.services.map(s=>({...s,ports:s.ports.map(u=>({...u}))}))};return n.session!==void 0&&(r.session=n.session===null?null:{...n.session}),r}function se(n){return!n||typeof n!="object"||Array.isArray(n)?null:Object.fromEntries(Object.entries(n).filter(([,r])=>r!==void 0).map(([r,s])=>[r,s===null?null:re(s)]))}export function cleanupOwnedRunLifecycleWithinLifecycleFence(n,r){const{manifest:s,ownership:u,productDir:o}=readOwnedRunLifecycleWithinLifecycleFence(n,r);if(!s)return!1;if(s.services.length>0)throw new Error(`Cannot clean Testkit run ${r.runId} service ownership while holding the lifecycle fence`);return _(o,r.runId),u.published=!1,!0}export function readOwnedRunLifecycleWithinLifecycleFence(n,r){const s=K(n),u=k.get(r);if(!u||u.productDir!==s)throw new Error("Cannot clean a Testkit run without matching lifecycle ownership");if(u.runId!==r.runId)throw new Error("Testkit run lifecycle identity has been modified");const o=J(s).find(I=>I.runId===r.runId);if(o!==void 0&&(o.incarnationId!==u.incarnationId||o.pid!==u.pid||o.processIdentity!==u.processIdentity))throw new Error(`Testkit run ${r.runId} ownership no longer matches its manifest`);return{manifest:o??null,ownership:u,productDir:s}}function oe(n){const r=new Set,s=[];for(const u of[n.testkit.local.baseUrl,n.testkit.local.readyUrl])if(u)try{const o=new URL(u),I=le(o.hostname),d=Number(o.port||(o.protocol==="https:"?443:80)),S=`${I}:${d.toString()}`;if(r.has(S))continue;r.add(S),s.push({host:I,port:d})}catch{}return s}function ae(n,r){r&&(n.includes(r)||n.push(r))}function ue(){return`${Date.now().toString()}-${process.pid.toString()}-${H.randomBytes(4).toString("hex")}`}function le(n){return n?n==="::1"?"127.0.0.1":n:"127.0.0.1"}export function disposeFailedRunLifecycle(n){const r=k.get(n);n.dispose(),n.removeSignalHandlers(),r?.published===!0&&_(r.productDir,n.runId)}
@@ -1 +1 @@
1
- import{formatRunSummary as R}from"../process/runs/manifest.js";import{cleanupOrphanedLocalInfrastructure as v,cleanupTerminalRunLocalInfrastructure as m}from"../database/lifecycle-cleanup.js";import{resetResultArtifacts as y}from"../evidence/index.js";import{requireProductLifecycleFenceProductDir as p,withProductLifecycleFence as l}from"../process/lifecycle-fence.js";import{listRunManifests as o,removeRunManifestFile as I,requireSafeRunId as A}from"../process/runs/manifest.js";import{stopRunManifest as L}from"./run-stopping.js";import{createUnpublishedRunLifecycle as k,disposeFailedRunLifecycle as g,isRunManifestActive as u,cleanupOwnedRunLifecycleWithinLifecycleFence as S,readOwnedRunLifecycleWithinLifecycleFence as f}from"./lifecycle-controller.js";import{reconcileInactiveRunArtifact as x,reconcileOrphanedLiveRunArtifact as C}from"./lifecycle-artifacts.js";class h extends Error{constructor(n){super(`Stale Testkit run ${n} still owns application services and requires phased cleanup`),this.name="StaleRunServiceCleanupRequiredError"}}export async function startRunLifecycle(e,n={},c={}){for(;;){await cleanupStaleRuns(e,c);try{return await l(e,t=>startRunLifecycleWithinLifecycleFence(t,n,c))}catch(t){if(!(t instanceof h))throw t}}}export async function startRunLifecycleWithinLifecycleFence(e,n={},c={}){const t=p(e);w(t),await cleanupStaleRunsWithinLifecycleFence(e,c);const s=o(t).find(i=>!u(i)&&i.services.length>0);if(s!==void 0)throw new h(s.runId);w(t);const r=k(t,n);try{return r.installSignalHandlers(),n.resetResultsBeforePublish===!0&&y(t),r.markRunning(),r}catch(i){throw g(r),i}}function w(e){const n=o(e).find(u);if(n)throw new Error(`Active Testkit run ${R(n)} already owns this product`)}export async function cleanupRuns(e,{includeActive:n=!1,localInfrastructure:c}={}){const t=[],s=[];for(;;){const i=await l(e,()=>o(e).flatMap(a=>{const d=u(a);return n&&d||!d&&a.services.length>0?[{active:d,manifest:a}]:[]}));if(i.length===0)break;for(const a of i)await L(e,a.manifest,{reason:a.active?"cleanup":"stale run process not active"}),a.active?t.push(a.manifest):s.push(a.manifest)}const r=await l(e,i=>cleanupRunsWithinLifecycleFence(i,{localInfrastructure:c}));return{...r,cleaned:[...t,...s,...r.cleaned],stoppedActive:t}}export async function cleanupRunsWithinLifecycleFence(e,{protectedLiveRunIds:n=new Set,localInfrastructure:c=!0}={}){const t=p(e),s=o(t),r={cleaned:[],skippedActive:[],stoppedActive:[]};for(const i of s){if(u(i)||i.services.length>0){r.skippedActive.push(i);continue}x(t,i),I(t,E(i)),r.cleaned.push(i)}return C(t,{activeRunIds:new Set([...r.skippedActive.map(i=>i.runId).filter(i=>typeof i=="string"),...n])}),r.resourceCleanup=c?await v(t):{kept:[],removed:[],skipped:[],stopped:[],targets:[]},{cleaned:r.cleaned,resourceCleanup:r.resourceCleanup,skippedActive:r.skippedActive,stoppedActive:r.stoppedActive}}export async function cleanupStaleRuns(e,n={}){const c=[];for(;;){const t=await cleanupRuns(e,n);if(c.push(...t.cleaned),!t.skippedActive.some(r=>!u(r)&&r.services.length>0))return c}}export async function cleanupStaleRunsWithinLifecycleFence(e,n={}){return(await cleanupRunsWithinLifecycleFence(e,n)).cleaned}export async function cleanupOwnedRunLifecycleResourcesWithinLifecycleFence(e,n,{localInfrastructure:c=!0}={}){const t=f(e,n);if(t.manifest===null)throw new Error(`Cannot clean terminal Testkit run ${n.runId} resources without its owned manifest`);if(t.manifest.services.length>0)throw new Error(`Cannot clean terminal Testkit run ${n.runId} resources while service ownership remains published`);const s=F(t.manifest);c&&await m(e,s);const r=f(e,n);if(r.manifest===null||!b(r.manifest,s))throw new Error(`Cannot remove terminal Testkit run ${n.runId} because its manifest changed during resource cleanup`);return S(e,n)}function F(e){if(e.status!=="failed"&&e.status!=="finished"&&e.status!=="interrupted")throw new Error(`Cannot clean Testkit run ${e.runId} database resources before terminal finalization`);if(typeof e.finishedAt!="string"||e.finishedAt.length===0)throw new Error(`Cannot clean terminal Testkit run ${e.runId} database resources without a completion time`);return{finishedAt:e.finishedAt,incarnationId:e.incarnationId,pid:e.pid,processIdentity:e.processIdentity,runId:e.runId,status:e.status}}function b(e,n){return e.runId===n.runId&&e.incarnationId===n.incarnationId&&e.pid===n.pid&&e.processIdentity===n.processIdentity&&e.finishedAt===n.finishedAt&&e.status===n.status&&e.services.length===0}function E(e){return A(e.runId,"Testkit run manifest runId")}export{listRunManifests,RunManifestInventoryError}from"../process/runs/manifest.js";export{stopRunById,stopRunManifest}from"./run-stopping.js";export{cleanupOwnedRunLifecycleWithinLifecycleFence,isRunManifestActive}from"./lifecycle-controller.js";export{isPidRunning}from"../process/runs/stop.js";export{formatRunSummary}from"../process/runs/manifest.js";
1
+ import{formatRunSummary as v}from"../process/runs/manifest.js";import{cleanupOrphanedLocalInfrastructure as w,cleanupTerminalRunLocalInfrastructure as R}from"../database/lifecycle-cleanup.js";import{resetResultArtifacts as y}from"../evidence/index.js";import{requireProductLifecycleFenceProductDir as l,withProductLifecycleFence as p}from"../process/lifecycle-fence.js";import{listRunManifests as u,removeRunManifestFile as I,requireSafeRunId as A}from"../process/runs/manifest.js";import{stopRunManifest as k}from"./run-stopping.js";import{createUnpublishedRunLifecycle as L,disposeFailedRunLifecycle as g,isRunManifestActive as o,cleanupOwnedRunLifecycleWithinLifecycleFence as S,readOwnedRunLifecycleWithinLifecycleFence as f}from"./lifecycle-controller.js";import{reconcileInactiveRunArtifact as x,reconcileOrphanedLiveRunArtifact as C}from"./lifecycle-artifacts.js";class h extends Error{constructor(n){super(`Stale Testkit run ${n} still owns application services and requires phased cleanup`),this.name="StaleRunServiceCleanupRequiredError"}}export async function startRunLifecycle(e,n={},s={}){for(;;){await cleanupStaleRuns(e,s);try{return await p(e,t=>startRunLifecycleWithinLifecycleFence(t,n,s))}catch(t){if(!(t instanceof h))throw t}}}export async function startRunLifecycleWithinLifecycleFence(e,n={},s={}){const t=l(e);m(t),await cleanupRunsWithinLifecycleFence(e,s);const c=u(t).find(r=>!o(r)&&r.services.length>0);if(c!==void 0)throw new h(c.runId);m(t);const i=L(t,n);try{return i.installSignalHandlers(),n.resetResultsBeforePublish===!0&&y(t),i.markRunning(),i}catch(r){throw g(i),r}}function m(e){const n=u(e).find(o);if(n)throw new Error(`Active Testkit run ${v(n)} already owns this product`)}export async function cleanupRuns(e,{includeActive:n=!1,localInfrastructure:s}={}){const t=[],c=[];for(;;){const r=await p(e,()=>u(e).flatMap(a=>{const d=o(a);return n&&d||!d&&a.services.length>0?[{active:d,manifest:a}]:[]}));if(r.length===0)break;for(const a of r)await k(e,a.manifest,{reason:a.active?"cleanup":"stale run process not active"}),a.active?t.push(a.manifest):c.push(a.manifest)}const i=await p(e,r=>cleanupRunsWithinLifecycleFence(r,{localInfrastructure:s}));return{...i,cleaned:[...t,...c,...i.cleaned],stoppedActive:t}}export async function cleanupRunsWithinLifecycleFence(e,{protectedLiveRunIds:n=new Set,localInfrastructure:s=!0}={}){const t=l(e),c=u(t),i={cleaned:[],skippedActive:[],stoppedActive:[]};for(const r of c){if(o(r)||r.services.length>0){i.skippedActive.push(r);continue}x(t,r),I(t,A(r.runId,"Testkit run manifest runId")),i.cleaned.push(r)}return C(t,{activeRunIds:new Set([...i.skippedActive.map(r=>r.runId).filter(r=>typeof r=="string"),...n])}),i.resourceCleanup=s?await w(t):{kept:[],removed:[],skipped:[],stopped:[],targets:[]},{cleaned:i.cleaned,resourceCleanup:i.resourceCleanup,skippedActive:i.skippedActive,stoppedActive:i.stoppedActive}}export async function cleanupStaleRuns(e,n={}){const s=[];for(;;){const t=await cleanupRuns(e,n);if(s.push(...t.cleaned),!t.skippedActive.some(i=>!o(i)&&i.services.length>0))return s}}export async function cleanupOwnedRunLifecycleResourcesWithinLifecycleFence(e,n,s={}){return await cleanupTerminalRunResourcesWithinLifecycleFence(e,n,s),S(e,n)}export async function cleanupTerminalRunResourcesWithinLifecycleFence(e,n,{localInfrastructure:s=!0}={}){const t=f(e,n);if(t.manifest===null)throw new Error(`Cannot clean terminal Testkit run ${n.runId} resources without its owned manifest`);if(t.manifest.services.length>0)throw new Error(`Cannot clean terminal Testkit run ${n.runId} resources while service ownership remains published`);const c=F(t.manifest),i=s?await R(e,c):{kept:[],removed:[],skipped:[],stopped:[],targets:[]},r=f(e,n);if(r.manifest===null||!T(r.manifest,c))throw new Error(`Cannot remove terminal Testkit run ${n.runId} because its manifest changed during resource cleanup`);return i}function F(e){if(e.status!=="failed"&&e.status!=="finished"&&e.status!=="interrupted")throw new Error(`Cannot clean Testkit run ${e.runId} database resources before terminal finalization`);if(typeof e.finishedAt!="string"||e.finishedAt.length===0)throw new Error(`Cannot clean terminal Testkit run ${e.runId} database resources without a completion time`);return{finishedAt:e.finishedAt,incarnationId:e.incarnationId,pid:e.pid,processIdentity:e.processIdentity,runId:e.runId,status:e.status}}function T(e,n){return e.runId===n.runId&&e.incarnationId===n.incarnationId&&e.pid===n.pid&&e.processIdentity===n.processIdentity&&e.finishedAt===n.finishedAt&&e.status===n.status&&e.services.length===0}export{listRunManifests,RunManifestInventoryError}from"../process/runs/manifest.js";export{stopRunById,stopRunManifest}from"./run-stopping.js";export{cleanupOwnedRunLifecycleWithinLifecycleFence,isRunManifestActive}from"./lifecycle-controller.js";export{isPidRunning}from"../process/runs/stop.js";export{formatRunSummary}from"../process/runs/manifest.js";
@@ -1 +1 @@
1
- import{readDatabaseInfo as s}from"../database/state.js";export function createReadyEnvironment(t,n){const a=new Map(t.runtimeConfigs.map(e=>[e.name,s(e.stateDir)]));return{leaseId:n,graphKey:t.graphKey,runtimeId:t.runtimeId,targetNames:[...t.targetNames],services:new Map(t.runtimeConfigs.map(e=>{const r={...e.testkit.templateContext};return delete r.databaseBindingsReader,[e.name,{baseUrl:e.testkit.local?.baseUrl||null,database:a.get(e.name)??null,config:{...e,testkit:{...e.testkit,templateContext:{...r,databaseBindingsByService:a}}}}]}))}}
1
+ export function createReadyEnvironment(e,r){return{leaseId:r,graphKey:e.graphKey,runtimeId:e.runtimeId,targetNames:[...e.targetNames],services:new Map(e.runtimeConfigs.map(a=>[a.name,{baseUrl:a.testkit.local?.baseUrl||null,database:e.databaseBindings.get(a.name)??null,config:a}]))}}
@@ -1 +1 @@
1
- import{cleanupOwnedRunLifecycleResourcesWithinLifecycleFence as w,cleanupRunsWithinLifecycleFence as y,listRunManifests as R,startRunLifecycle as v}from"./lifecycle.js";import{createRuntimeManager as F}from"./runtime-manager.js";import{resolveCommittedRunFinalization as I}from"../evidence/finalization.js";import{withProductLifecycleFence as q}from"../process/lifecycle-fence.js";export function createLocalRunEnvironment(s){return{async start(f){const c={localInfrastructure:f.localInfrastructure!==!1},n=await v(s,f,c),p=n.startHeartbeat(2e3,()=>({kind:"controller",status:n.isStopRequested()?"interrupting":"running"}));let a=null;const m=new Map,l=()=>{if(a===null)throw new Error("Run runtimes have not been provisioned");return a};let d=null;return{runId:n.runId,signal:n.signal,isStopRequested:()=>n.isStopRequested(),getInterruptReason:()=>n.getInterruptReason(),requestStop:e=>{n.requestStop(e)},beginFinalization:p,dispose(){p(),n.removeSignalHandlers(),n.dispose()},get committedFinalization(){return d},provision(e,t){if(a!==null)throw new Error("Run runtimes are already provisioned");a=F({productDir:s,graphs:e,lifecycle:n,runtimeOptions:t})},async acquire(e,t){const r=await l().acquire(e,t);m.set(r.leaseId,r);const{leaseId:o,runtimeId:u,graphKey:i,targetNames:h,services:g}=r;return{leaseId:o,runtimeId:u,graphKey:i,targetNames:h,services:g}},canAcquire:e=>l().canAcquire(e),async release(e,t){const r=m.get(e);r!==void 0&&(await l().release(r,t),m.delete(e))},async cleanupAll(){await a?.cleanupAll()},getStats:()=>a?.getStats()??[],getGraph(e){const t=a?.getGraph(e);return t?{key:t.key,targetNames:[...t.targetNames]}:null},getGraphFailure:e=>a?.getGraphFailure(e)??null,markGraphFailed:(e,t)=>{l().markGraphFailed(e,t)},async finish(e,t,r){return p(),q(s,async o=>{const u=R(s).find(i=>i.runId===n.runId);if(u!==void 0&&u.services.length>0)throw new Error(r==="normal"?"Cannot commit a run while application service ownership remains published":"Cannot publish terminal failure while application service ownership remains published");d??=I(e,n.commitFinalization(e==="failed"?"failed":"finished"));try{await y(o,{...c,protectedLiveRunIds:new Set([n.runId])}),await w(o,n,c)}catch(i){if(r==="normal")throw i}return t(d)})}}}}}
1
+ import{cleanupOwnedRunLifecycleWithinLifecycleFence as R,cleanupRunsWithinLifecycleFence as y,cleanupTerminalRunResourcesWithinLifecycleFence as v,listRunManifests as F,startRunLifecycle as I}from"./lifecycle.js";import{createRuntimeManager as q}from"./runtime-manager.js";import{resolveCommittedRunFinalization as S}from"../evidence/finalization.js";import{withProductLifecycleFence as k}from"../process/lifecycle-fence.js";import{formatError as L}from"../kernel/error-serialization.js";export function createLocalRunEnvironment(s){return{async start(o){const l={localInfrastructure:o.localInfrastructure!==!1},e=await I(s,o,l),r=e.startHeartbeat(2e3,()=>({kind:"controller",status:e.isStopRequested()?"interrupting":"running"}));let a=null;const m=new Map,u=()=>{if(a===null)throw new Error("Run runtimes have not been provisioned");return a};let d=null;return{runId:e.runId,signal:e.signal,isStopRequested:()=>e.isStopRequested(),getInterruptReason:()=>e.getInterruptReason(),requestStop:n=>{e.requestStop(n)},beginFinalization:r,dispose(){r(),e.removeSignalHandlers(),e.dispose()},get committedFinalization(){return d},provision(n,t){if(a!==null)throw new Error("Run runtimes are already provisioned");a=q({productDir:s,graphs:n,lifecycle:e,runtimeOptions:t})},async acquire(n,t){const i=await u().acquire(n,t);m.set(i.leaseId,i);const{leaseId:f,runtimeId:c,graphKey:p,targetNames:h,services:g}=i;return{leaseId:f,runtimeId:c,graphKey:p,targetNames:h,services:g}},canAcquire:n=>u().canAcquire(n),async release(n,t){const i=m.get(n);i!==void 0&&(await u().release(i,t),m.delete(n))},async cleanupAll(){await a?.cleanupAll()},getStats:()=>a?.getStats()??[],getGraph(n){const t=a?.getGraph(n);return t?{key:t.key,targetNames:[...t.targetNames]}:null},getGraphFailure:n=>a?.getGraphFailure(n)??null,markGraphFailed:(n,t)=>{u().markGraphFailed(n,t)},async finish(n,t,i,f){return r(),k(s,async c=>{const p=F(s).find(w=>w.runId===e.runId);if(p!==void 0&&p.services.length>0)throw new Error(i==="normal"?"Cannot commit a run while application service ownership remains published":"Cannot publish terminal failure while application service ownership remains published");d??=S(n,e.commitFinalization(n==="failed"?"failed":"finished"));const h=t(d),g=f(await b(c,e,l));return R(c,e),{maintenance:g,sealed:h}})}}}}}async function b(s,o,l){try{await y(s,{...l,protectedLiveRunIds:new Set([o.runId])});const e=await v(s,o,l);return{status:"passed",error:null,removed:e.removed.map(({name:r})=>r),skipped:e.skipped.map(({name:r})=>r),stopped:e.stopped.map(({name:r})=>r)}}catch(e){return{status:"failed",error:L(e),removed:[],skipped:[],stopped:[]}}}
@@ -1 +1 @@
1
- import{waitForPreparedRunStop as g,drainRunServices as M,requestProcessStop as _,runManifestServiceKey as F,sameRunManifestIncarnation as P}from"../process/runs/stop.js";import{observeProcessCreationIdentity as h,captureProcessCreationIdentity as N}from"../process/index.js";import{normalizeOperationReason as S}from"../domain/operation-reason.js";import{RUN_MAINTENANCE_STOP_REQUEST_SCHEMA_VERSION as L}from"../process/runs/schema.js";import{finalizeLiveRunArtifact as v}from"../evidence/index.js";import{requireProductLifecycleFenceProductDir as q,withProductLifecycleFence as m}from"../process/lifecycle-fence.js";import{listRunManifests as E,readRunMaintenanceStopRequest as T,removeRunMaintenanceStopRequest as y,removeRunManifestFile as O,requireSafeRunId as k,writeRunMaintenanceStopRequest as $,prepareRunShutdownDeadlines as D,persistRunShutdownDeadlines as z}from"../process/runs/manifest.js";export async function stopRunById(r,t,i={}){const n=k(t,"Testkit run id"),e=await m(r,o=>A(o,n,i));if(e===null)return!1;const s=await g(e);return await C(r,e,s),!0}export async function stopRunManifest(r,t,i={}){const n=W(t),e=await m(r,o=>A(o,n,i,t));if(e===null)return{runId:n,pid:t.pid,reason:S(i.reason,"stopped"),stopped:!0};const s=await g(e);return C(r,e,s)}function A(r,t,i={},n=null){const e=q(r);let s=S(i.reason,"stopped");const o=E(e).find(a=>a.runId===t);if(o===void 0){if(n!==null&&n.pid>0&&n.processIdentity===null&&h(n.pid,n.processIdentity)==="unknown")throw new Error(`Cannot establish the creation identity of Testkit run process ${n.pid.toString()}; refusing unsafe stop`);return n!==null&&n.pid===0&&(v(e,{runId:t,reason:s,status:"interrupted"}),y(e,t)),null}if(n!==null&&!P(o,n))throw new Error(`Testkit run ${t} ownership changed before the stop request could be published`);const u=o.pid,f=u>0?h(u,o.processIdentity):"different";if(u===process.pid&&f==="matches")throw new Error(`Cannot stop active Testkit run ${t} from inside its own controller process`);if(f==="unknown")throw new Error(`Cannot establish the creation identity of Testkit run process ${u.toString()}; refusing unsafe stop`);let d=null,p=new Date().toISOString(),l=null;const I=T(e,t),c=I!==null&&I.targetIncarnationId===o.incarnationId&&I.targetPid===u&&I.targetProcessIdentity===o.processIdentity?I:null;c!==null&&(s=c.reason,p=c.requestedAt);let R=null;const w=()=>{if(R!==null)return R;const a=D(o,p);return f==="different"&&z(e,a),R=a,a};if(f==="matches"){if(l=o.processIdentity,l===null)throw new Error(`Testkit run process ${u.toString()} has no creation identity; refusing unsafe stop`);if(c!==null&&h(c.coordinatorPid,c.coordinatorProcessIdentity)!=="different")return d=c.coordinatorProcessIdentity,{controllerActive:!0,coordinatorProcessIdentity:d,graceMs:i.graceMs||2e3,manifest:w(),requestedAt:p,pid:u,productDir:e,reason:s,runId:t,targetProcessIdentity:l};if(d=N(process.pid),d===null)throw new Error("Could not capture the maintenance coordinator process identity");$(e,{coordinatorPid:process.pid,coordinatorProcessIdentity:d,reason:s,requestedAt:p,runId:t,schemaVersion:L,targetIncarnationId:o.incarnationId,targetPid:u,targetProcessIdentity:l}),w();try{process.platform!=="win32"&&_(u,l,"SIGINT")}catch(a){throw y(e,t),a}}return{controllerActive:f==="matches",coordinatorProcessIdentity:d,graceMs:i.graceMs||2e3,manifest:w(),requestedAt:p,pid:u,productDir:e,reason:s,runId:t,targetProcessIdentity:l}}async function C(r,t,i){for(;;){const n=await m(r,e=>U(e,t,i));if(n.result!==null)return n.result;await M(n.services,i)}}function U(r,t,i){const n=q(r),e=E(n).find(o=>o.runId===t.runId);if(e===void 0||!P(e,t.manifest))return V(n,t),{result:b(t),services:null};const s=e.services.filter(o=>!i.has(F(o)));return s.length>0?{result:null,services:s}:(v(n,{runId:t.runId,reason:t.reason,status:"interrupted"}),O(n,t.runId),{result:b(t),services:null})}function b(r){return{runId:r.runId,pid:r.pid,stopped:!0,reason:r.reason}}function V(r,t){const i=T(r,t.runId);i===null||i.coordinatorPid!==process.pid||i.coordinatorProcessIdentity!==t.coordinatorProcessIdentity||i.targetIncarnationId!==t.manifest.incarnationId||i.targetPid!==t.pid||i.targetProcessIdentity!==t.targetProcessIdentity||y(r,t.runId)}function W(r){return k(r.runId,"Testkit run manifest runId")}
1
+ import{waitForPreparedRunStop as g,drainRunServices as M,requestProcessStop as _,runManifestServiceKey as F,sameRunManifestIncarnation as P}from"../process/runs/stop.js";import{observeProcessCreationIdentity as h,captureProcessCreationIdentity as N}from"../process/index.js";import{normalizeOperationReason as S}from"../domain/operation-reason.js";import{RUN_MAINTENANCE_STOP_REQUEST_SCHEMA_VERSION as L}from"../process/runs/schema.js";import{finalizeLiveRunArtifact as v}from"../evidence/index.js";import{requireProductLifecycleFenceProductDir as q,withProductLifecycleFence as m}from"../process/lifecycle-fence.js";import{listRunManifests as E,readRunMaintenanceStopRequest as T,removeRunMaintenanceStopRequest as y,removeRunManifestFile as O,requireSafeRunId as k,writeRunMaintenanceStopRequest as $,prepareRunShutdownDeadlines as D,persistRunShutdownDeadlines as z}from"../process/runs/manifest.js";export async function stopRunById(i,t,r={}){const n=k(t,"Testkit run id"),e=await m(i,o=>A(o,n,r));if(e===null)return!1;const s=await g(e);return await C(i,e,s),!0}export async function stopRunManifest(i,t,r={}){const n=k(t.runId,"Testkit run manifest runId"),e=await m(i,o=>A(o,n,r,t));if(e===null)return{runId:n,pid:t.pid,reason:S(r.reason,"stopped"),stopped:!0};const s=await g(e);return C(i,e,s)}function A(i,t,r={},n=null){const e=q(i);let s=S(r.reason,"stopped");const o=E(e).find(a=>a.runId===t);if(o===void 0){if(n!==null&&n.pid>0&&n.processIdentity===null&&h(n.pid,n.processIdentity)==="unknown")throw new Error(`Cannot establish the creation identity of Testkit run process ${n.pid.toString()}; refusing unsafe stop`);return n!==null&&n.pid===0&&(v(e,{runId:t,reason:s,status:"interrupted"}),y(e,t)),null}if(n!==null&&!P(o,n))throw new Error(`Testkit run ${t} ownership changed before the stop request could be published`);const u=o.pid,f=u>0?h(u,o.processIdentity):"different";if(u===process.pid&&f==="matches")throw new Error(`Cannot stop active Testkit run ${t} from inside its own controller process`);if(f==="unknown")throw new Error(`Cannot establish the creation identity of Testkit run process ${u.toString()}; refusing unsafe stop`);let l=null,p=new Date().toISOString(),d=null;const I=T(e,t),c=I!==null&&I.targetIncarnationId===o.incarnationId&&I.targetPid===u&&I.targetProcessIdentity===o.processIdentity?I:null;c!==null&&(s=c.reason,p=c.requestedAt);let w=null;const R=()=>{if(w!==null)return w;const a=D(o,p);return f==="different"&&z(e,a),w=a,a};if(f==="matches"){if(d=o.processIdentity,d===null)throw new Error(`Testkit run process ${u.toString()} has no creation identity; refusing unsafe stop`);if(c!==null&&h(c.coordinatorPid,c.coordinatorProcessIdentity)!=="different")return l=c.coordinatorProcessIdentity,{controllerActive:!0,coordinatorProcessIdentity:l,graceMs:r.graceMs||2e3,manifest:R(),requestedAt:p,pid:u,productDir:e,reason:s,runId:t,targetProcessIdentity:d};if(l=N(process.pid),l===null)throw new Error("Could not capture the maintenance coordinator process identity");$(e,{coordinatorPid:process.pid,coordinatorProcessIdentity:l,reason:s,requestedAt:p,runId:t,schemaVersion:L,targetIncarnationId:o.incarnationId,targetPid:u,targetProcessIdentity:d}),R();try{process.platform!=="win32"&&_(u,d,"SIGINT")}catch(a){throw y(e,t),a}}return{controllerActive:f==="matches",coordinatorProcessIdentity:l,graceMs:r.graceMs||2e3,manifest:R(),requestedAt:p,pid:u,productDir:e,reason:s,runId:t,targetProcessIdentity:d}}async function C(i,t,r){for(;;){const n=await m(i,e=>U(e,t,r));if(n.result!==null)return n.result;await M(n.services,r)}}function U(i,t,r){const n=q(i),e=E(n).find(o=>o.runId===t.runId);if(e===void 0||!P(e,t.manifest))return V(n,t),{result:b(t),services:null};const s=e.services.filter(o=>!r.has(F(o)));return s.length>0?{result:null,services:s}:(v(n,{runId:t.runId,reason:t.reason,status:"interrupted"}),O(n,t.runId),{result:b(t),services:null})}function b(i){return{runId:i.runId,pid:i.pid,stopped:!0,reason:i.reason}}function V(i,t){const r=T(i,t.runId);r===null||r.coordinatorPid!==process.pid||r.coordinatorProcessIdentity!==t.coordinatorProcessIdentity||r.targetIncarnationId!==t.manifest.incarnationId||r.targetPid!==t.pid||r.targetProcessIdentity!==t.targetProcessIdentity||y(i,t.runId)}
@@ -1 +1 @@
1
- import v from"fs";import p from"path";import{prepareRuntimeServices as m}from"./runtime-preparation.js";import{writeGraphMetadata as y}from"./state.js";import{startLocalServices as g,stopLocalServices as c}from"./services.js";import{resolveRuntimeInstanceConfigs as w}from"./template.js";import{portsOf as R,releasePorts as o,reserveExactPort as P,reservePort as C,reservePorts as S}from"./ports.js";import{buildTemplateExecutionEnv as h}from"../steps/index.js";export function createRuntimeInstanceContext(r,e,s){const t=p.join(s,".testkit","_graphs",e.dirName),i=p.join(t,"runtimes",r);return v.mkdirSync(i,{recursive:!0}),y(t,e),{graphKey:e.key,targetNames:[...e.targetNames],graphDir:t,graphDirName:e.dirName,runtimeDir:i,runtimeId:r,sourceConfigs:e.runtimeConfigs,ports:null,portReservations:new Map,triedPortReservations:[],triedPorts:new Map,runtimeConfigs:[],ready:!1,readyPromise:null,startedServices:[]}}export async function ensureRuntimeInstanceReady(r,e,s={}){return r.ready||(r.readyPromise??=k(r,e,s).finally(()=>{r.readyPromise=null}),await r.readyPromise),r}async function k(r,e,s){const t={...s,signal:e.signal};await D(r),await prepareDatabases(r.runtimeConfigs,t),await m(r.runtimeConfigs,t),r.startedServices=await g(I(r,t),e,s),r.ready=!0}async function D(r){if(r.ports)return;const e=r.sourceConfigs.filter(t=>t.testkit.local).map(t=>t.name),s=await S(e);try{f(r,R(s))}catch(t){throw await o(s.values()),t}r.portReservations=s}function f(r,e){r.runtimeConfigs=w(r.sourceConfigs,r.runtimeId,r.runtimeDir,{graphDirName:r.graphDirName,ports:e}),r.ports=e}function I(r,e){return{get configs(){return r.runtimeConfigs},async releasePort(s){const t=r.portReservations.get(s);r.portReservations.delete(s),await t?.release()},async reassignPort(s,t){const i=r.triedPorts.get(s)??new Set,n=r.ports?.get(s);if(n!==void 0){i.add(n);const a=await P(n);a&&r.triedPortReservations.push(a)}r.triedPorts.set(s,i),await r.portReservations.get(s)?.release();const u=await M(i);r.portReservations.set(s,u);const d=new Map(r.runtimeConfigs.map(a=>[a.name,l(a)]));return f(r,new Map([...r.ports??[],[s,u.port]])),r.runtimeConfigs.filter(a=>t.has(a.name)&&l(a)!==d.get(a.name)).map(a=>a.name)},async prepare(s){await m(r.runtimeConfigs.filter(t=>s.has(t.name)),e)}}}async function M(r){const e=[];try{for(;;){const s=await C();if(!r.has(s.port))return s;e.push(s)}}finally{await o(e)}}function l(r){return JSON.stringify({env:h(r,{},{},{includeTestkitControls:!1}),prepare:r.testkit.runtime.prepare,readyUrl:r.testkit.local?.readyUrl??null,start:r.testkit.local?.start??null})}async function b(r,e){r?.ready&&(await c(r.startedServices,e),r.ready=!1,r.startedServices=[])}export async function cleanupRuntimeInstanceContext(r,e){r&&(await b(r,e),await o([...r.portReservations.values(),...r.triedPortReservations]),r.portReservations.clear(),r.triedPortReservations=[])}export async function prepareDatabases(r,e={}){if(!r.some(t=>t.testkit.database))return;const{provisionDatabase:s}=await import("../database/index.js");for(const t of r)await s(t,{progress:{reporter:e.reporter,setupRegistry:e.setupRegistry},signal:e.signal,skipSchemaSourceVerify:e.skipSchemaSourceVerify})}
1
+ import P from"fs";import p from"path";import{bindDatabaseUrl as S,lendDatabases as m}from"../database/bindings.js";import{prepareRuntimeServices as l}from"./runtime-preparation.js";import{writeGraphMetadata as D}from"./state.js";import{startLocalServices as C,stopLocalServices as T}from"./services.js";import{resolveRuntimeInstanceConfigs as k}from"./template.js";import{withEnvFileValues as E}from"../config/loader/env.js";import{portsOf as M,releasePorts as u,reserveExactPort as U,reservePort as I,reservePorts as j}from"./ports.js";import{buildExecutionEnv as B,buildTemplateContext as K,buildTemplateExecutionEnv as c,finalizeString as O,withDatabaseBindings as A}from"../steps/index.js";import{sourceSchemaCredentialEnvNames as N}from"../database/source-schema-env.js";import{envRedactionTargets as $,jsonRedactionTargets as F,sensitiveKeyMatcher as g,urlRedactionTargets as L}from"../kernel/redaction-targets.js";const v=/\{[a-zA-Z]+(?::[^}]+)?\}/u;export function createRuntimeInstanceContext(r,e,t){const a=p.join(t,".testkit","_graphs",e.dirName),s=p.join(a,"runtimes",r);return P.mkdirSync(s,{recursive:!0}),D(a,e),{databaseBindings:new Map,graphKey:e.key,targetNames:[...e.targetNames],graphDir:a,graphDirName:e.dirName,runtimeDir:s,runtimeId:r,sourceConfigs:e.runtimeConfigs.map(E),ports:null,portReservations:new Map,triedPortReservations:[],triedPorts:new Map,runtimeConfigs:[],ready:!1,readyPromise:null,startedServices:[]}}export async function ensureRuntimeInstanceReady(r,e,t={}){return r.ready||(r.readyPromise??=V(r,e,t).finally(()=>{r.readyPromise=null}),await r.readyPromise),r}async function V(r,e,t){const a={...t,signal:e.signal},s=await _(r);z(r,t.environmentRecord),r.databaseBindings=await prepareDatabases(r.runtimeConfigs,a),f(r,s),y(r,t.environmentRecord),await l(r.runtimeConfigs,a),r.startedServices=await C(G(r,a),e,t),r.ready=!0}function z(r,e){if(!e)return;const t=g(e.redactKeys);for(const a of r.runtimeConfigs){const s=c(a,{},null);for(const i of[...w(a,s,null,t),...R(a,t)])v.test(i)||e.addRedactionTarget(i)}}function y(r,e){if(!e)return;const t=g(e.redactKeys);for(const a of r.runtimeConfigs){const s=B(a,{},null),i=r.databaseBindings.get(a.name)?.url??null;for(const n of w(a,s,i,t))e.addRedactionTarget(n);for(const n of R(a,t))v.test(n)||e.addRedactionTarget(n)}}function R(r,e){return F("ui",d(r.testkit.ui??{},K(r,null)),e)}function d(r,e){if(typeof r=="string")try{return O(r,e)}catch{return r}return Array.isArray(r)?r.map(t=>d(t,e)):r!==null&&typeof r=="object"?Object.fromEntries(Object.entries(r).map(([t,a])=>[t,d(a,e)])):r}function w(r,e,t,a){const s=r.testkit.local,i=[s?.baseUrl,typeof s?.readyUrl=="string"?s.readyUrl:null,t,...N(r).map(n=>e[n]??process.env[n])];return[...$(e,a),...i.flatMap(n=>n?L(n):[])]}async function _(r){if(r.ports)return r.ports;const e=r.sourceConfigs.filter(s=>s.testkit.local).map(s=>s.name),t=await j(e),a=M(t);try{f(r,a)}catch(s){throw await u(t.values()),s}return r.portReservations=t,a}function f(r,e){r.runtimeConfigs=k(r.sourceConfigs,r.runtimeId,r.runtimeDir,{databaseBindings:r.databaseBindings,graphDirName:r.graphDirName,ports:e}),r.ports=e}function G(r,e){return{get configs(){return r.runtimeConfigs},async releasePort(t){const a=r.portReservations.get(t);r.portReservations.delete(t),await a?.release()},async reassignPort(t,a){const s=r.triedPorts.get(t)??new Set,i=r.ports?.get(t);if(i!==void 0){s.add(i);const o=await U(i);o&&r.triedPortReservations.push(o)}r.triedPorts.set(t,s),await r.portReservations.get(t)?.release();const n=await J(s);r.portReservations.set(t,n);const h=new Map(r.runtimeConfigs.map(o=>[o.name,b(o)]));return f(r,new Map([...r.ports??[],[t,n.port]])),y(r,e.environmentRecord),r.runtimeConfigs.filter(o=>a.has(o.name)&&b(o)!==h.get(o.name)).map(o=>o.name)},async prepare(t){await l(r.runtimeConfigs.filter(a=>t.has(a.name)),e)}}}async function J(r){const e=[];try{for(;;){const t=await I();if(!r.has(t.port))return t;e.push(t)}}finally{await u(e)}}function b(r){return JSON.stringify({env:c(r,{},{},{includeTestkitControls:!1}),prepare:r.testkit.runtime.prepare,readyUrl:r.testkit.local?.readyUrl??null,start:r.testkit.local?.start??null})}async function Z(r,e){r?.ready&&(await T(r.startedServices,e),r.ready=!1,r.startedServices=[])}export async function cleanupRuntimeInstanceContext(r,e){if(!r)return;await Z(r,e),await u([...r.portReservations.values(),...r.triedPortReservations]),r.portReservations.clear(),r.triedPortReservations=[];const t=r.runtimeConfigs.filter(s=>s.testkit.database);if(t.length===0)return;const{dropRecordedTaskDatabases:a}=await import("../database/index.js");for(const s of t)await a({productDir:s.productDir,stateDir:s.stateDir})}export async function prepareDatabases(r,e={}){const t=new Map;if(r.some(a=>a.testkit.database)){const{provisionDatabase:a}=await import("../database/index.js");for(const s of r){const i=await a(A(s,m(r,t)),{progress:{reporter:e.reporter,setupRegistry:e.setupRegistry},provisionedSharedDatabases:e.provisionedSharedDatabases,signal:e.signal,skipSchemaSourceVerify:e.skipSchemaSourceVerify});i&&t.set(s.name,S(i.url))}}return q(r,t)}function q(r,e){const t=m(r,e);for(const a of r){const s=a.testkit.databaseFrom;if(s&&!t.has(a.name))throw H(a.name,s,r)}return t}function H(r,e,t){const a=t.some(s=>s.name===e)?`"${e}" declares no database`:`"${e}" is not in this runtime; add it to dependsOn`;return new Error(`Service "${r}" databaseFrom "${e}" has no database to use: ${a}`)}