@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{buildRuntimeIds as w}from"../domain/runtime-identity.js";import{formatError as d}from"../kernel/error-serialization.js";import{cleanupRuntimeInstanceContext as I,createRuntimeInstanceContext as M,ensureRuntimeInstanceReady as L}from"./runtime-contexts.js";import{timeOperation as U}from"../evidence/operation-timings.js";import{createReadyEnvironment as b}from"./ready-environment.js";export function createRuntimeManager({productDir:t,graphs:i,lifecycle:n,runtimeOptions:r={}}){const a=new Map(i.map(e=>[e.key,e])),u=new Map,o=T(i),c=new Map;return{canAcquire(e){if(c.has(e.graphKey))return!1;const s=f(u,a,e);return A(s,e,o)!==null},async acquire(e,s={}){const m=f(u,a,e);if(n.isStopRequested())throw n.signal.reason||new Error("testkit run interrupted");const g=e.leaseId,l=await N(r,s,"testkit.runtime.slot_claim",{summary:"claim runtime slot",scope:{graphKey:e.graphKey,taskId:e.id,serviceName:e.serviceName||null,targetName:e.targetName||null},metadata:{decision:"claim",resourceCost:v(e).toString()}},()=>{const p=S(m,e,o);if(!p)throw new Error(`Task ${e.id.toString()} was claimed before runtime capacity was available`);return p});try{const p=await K(l,t,e,n,r,s);return n.signal.throwIfAborted(),{...b(p,g),resourceCost:v(e),slot:l,context:p}}catch(p){throw R(l,v(e)),l.context||x(l,o),await h(l,n,o),n.isStopRequested()||k(c,e.graphKey,d(p)),p}},async release(e,s={}){e?.slot&&(s.invalidate&&(e.slot.draining=!0),e.released||(R(e.slot,e.resourceCost),e.released=!0),e.slot.draining&&e.slot.activeLeaseCount===0&&await h(e.slot,n,o))},async cleanupAll(){const e=[];for(const s of u.values())for(const m of s.slots){m.draining=!0;try{await h(m,n,o)}catch(g){e.push({slot:m,error:g})}}if(e.length>0)throw new AggregateError(e.map(s=>s.error),`Failed to clean up ${e.length.toString()} runtime ${e.length===1?"slot":"slots"}: ${e.map(s=>`${s.slot.runtimeId}: ${d(s.error)}`).join("; ")}`)},getStats(){return[...u.values()].map(e=>({graphKey:e.slots[0]?.graph.key||null,targetNames:[...e.slots.at(0)?.graph.targetNames??[]],maxConcurrentTasks:Number.isFinite(e.slots[0]?.graph.maxConcurrentTasks)?e.slots[0]?.graph.maxConcurrentTasks??null:null,runtimeCount:e.slots.length,runtimes:e.slots.map(s=>({runtimeId:s.runtimeId,peakLeaseCount:s.peakLeaseCount,peakResourceUnits:s.peakResourceUnits,resourceReservations:s.resourceReservations.map(m=>({...m}))})),resourceBudgets:[...o.values()].filter(s=>e.slots.some(m=>m.graph.resourceReservations.some(g=>g.key===s.key))).map(s=>({key:s.key,capacity:s.capacity,reserved:s.reserved,available:s.available,active:s.active,peakActive:s.peakActive}))})).sort((e,s)=>(e.graphKey||"").localeCompare(s.graphKey||""))},getGraphFailure(e){return c.get(e)??null},getGraph(e){return a.get(e)||null},markGraphFailed(e,s){k(c,e,s)}}}function f(t,i,n){const r=t.get(n.graphKey);if(r)return r;const a=i.get(n.graphKey);if(!a)throw new Error(`Unknown graph "${n.graphKey}"`);const o={slots:w(a.instanceCount).map(c=>({graph:a,runtimeId:c,context:null,activeLeaseCount:0,activeResourceUnits:0,resourceReservations:[],peakLeaseCount:0,peakResourceUnits:0,draining:!1}))};return t.set(n.graphKey,o),o}function S(t,i,n){const r=v(i),a=t.slots.filter(o=>!o.draining);if(a.length===0)return null;const u=[...a].filter(o=>y(o,r)&&C(o,n)).sort((o,c)=>o.activeResourceUnits-c.activeResourceUnits||o.activeLeaseCount-c.activeLeaseCount||o.runtimeId.localeCompare(c.runtimeId)).at(0);return u?($(u,n),u.activeLeaseCount+=1,u.activeResourceUnits+=r,u.peakLeaseCount=Math.max(u.peakLeaseCount,u.activeLeaseCount),u.peakResourceUnits=Math.max(u.peakResourceUnits,u.activeResourceUnits),u):null}function R(t,i){t.activeLeaseCount=Math.max(0,t.activeLeaseCount-1),t.activeResourceUnits=Math.max(0,t.activeResourceUnits-i)}async function K(t,i,n,r,a,u={}){t.context||(t.context=M(t.runtimeId,t.graph,i),r.trackGraphContext(t.context));const o=a.operationTimingRegistry||null,c=o?.start({name:"testkit.runtime.ensure",kind:"runtime",summary:"ensure runtime instance ready",parentId:u.operationTimingParentId||null,scope:{graphKey:t.graph.key,runtimeId:t.runtimeId,serviceName:n.serviceName,targetName:n.targetName}})??null;try{await L(t.context,r,a),o?.finish(c,{status:"passed"})}catch(e){throw o?.finish(c,{status:"failed",error:d(e)}),e}return t.context}function N(t,i,n,r,a){return U(t.operationTimingRegistry||null,{name:n,kind:"runtime",summary:r.summary||null,parentId:i.operationTimingParentId||null,scope:r.scope||null,metadata:r.metadata||null},a)}async function h(t,i,n){!t.context||t.activeLeaseCount>0||(await I(t.context,i),x(t,n),t.context=null,t.draining=!1)}function A(t,i,n){const r=v(i);return t.slots.find(a=>!a.draining&&y(a,r)&&C(a,n))||null}function y(t,i){return t.activeResourceUnits+i<=t.graph.maxConcurrentTasks}function T(t){const i=new Map;for(const n of t)for(const r of n.resourceReservations){const a=i.get(r.key)||{key:r.key,capacity:r.capacity||0,reserved:r.reserved||0,available:r.available||Math.max(0,(r.capacity||0)-(r.reserved||0)),active:0,peakActive:0};a.capacity=Math.max(a.capacity,r.capacity||0),a.reserved=Math.max(a.reserved,r.reserved||0),a.available=Math.max(0,a.capacity-a.reserved),i.set(r.key,a)}return i}function C(t,i){return t.resourceReservations.length>0?!0:t.graph.resourceReservations.every(n=>{const r=i.get(n.key);return r?r.active+n.perRuntime<=r.available:!0})}function $(t,i){if(t.resourceReservations.length>0)return;const n=[];for(const r of t.graph.resourceReservations){const a=i.get(r.key);a&&(a.active+=r.perRuntime,a.peakActive=Math.max(a.peakActive,a.active),n.push({key:r.key,units:r.perRuntime,services:r.services}))}t.resourceReservations=n}function x(t,i){for(const n of t.resourceReservations){const r=i.get(n.key);r&&(r.active=Math.max(0,r.active-n.units))}t.resourceReservations=[]}function k(t,i,n){!i||t.has(i)||t.set(i,n)}function v(t){return t.resourceCost??1}
1
+ import{buildRuntimeIds as L}from"../domain/runtime-identity.js";import{formatError as h}from"../kernel/error-serialization.js";import{cleanupRuntimeInstanceContext as M,createRuntimeInstanceContext as S,ensureRuntimeInstanceReady as U}from"./runtime-contexts.js";import{timeOperation as N}from"../evidence/operation-timings.js";import{createReadyEnvironment as A}from"./ready-environment.js";import{buildRuntimeLabel as K}from"./template.js";export function createRuntimeManager({productDir:t,graphs:i,lifecycle:n,runtimeOptions:r={}}){const a=new Map(i.map(e=>[e.key,e])),u=new Map,o=G(i),c=new Map,d={...r,provisionedSharedDatabases:new Set};return{canAcquire(e){if(c.has(e.graphKey))return!1;const s=y(u,a,e);return F(s,e,o)!==null},async acquire(e,s={}){const p=y(u,a,e);if(n.isStopRequested())throw n.signal.reason||new Error("testkit run interrupted");const g=e.leaseId,m=await E(r,s,"testkit.runtime.slot_claim",{summary:"claim runtime slot",scope:{graphKey:e.graphKey,taskId:e.id,serviceName:e.serviceName||null,targetName:e.targetName||null},metadata:{decision:"claim",resourceCost:v(e).toString()}},()=>{const l=T(p,e,o);if(!l)throw new Error(`Task ${e.id.toString()} was claimed before runtime capacity was available`);return l});try{const l=K(m.graph.dirName,m.runtimeId);s.onRuntimeAssigned?.({runtimeId:m.runtimeId,services:m.graph.runtimeConfigs.map(({name:b})=>({serviceName:b,runtimeLabel:l}))});const R=await $(m,t,e,n,d,s);return n.signal.throwIfAborted(),{...A(R,g),resourceCost:v(e),slot:m,context:R}}catch(l){throw C(m,v(e)),m.context||w(m,o),await f(m,n,o),n.isStopRequested()||I(c,e.graphKey,h(l)),l}},async release(e,s={}){e?.slot&&(s.invalidate&&(e.slot.draining=!0),e.released||(C(e.slot,e.resourceCost),e.released=!0),e.slot.draining&&e.slot.activeLeaseCount===0&&await f(e.slot,n,o))},async cleanupAll(){const e=[];for(const s of u.values())for(const p of s.slots){p.draining=!0;try{await f(p,n,o)}catch(g){e.push({slot:p,error:g})}}if(e.length>0)throw new AggregateError(e.map(s=>s.error),`Failed to clean up ${e.length.toString()} runtime ${e.length===1?"slot":"slots"}: ${e.map(s=>`${s.slot.runtimeId}: ${h(s.error)}`).join("; ")}`)},getStats(){return[...u.values()].map(e=>({graphKey:e.slots[0]?.graph.key||null,targetNames:[...e.slots.at(0)?.graph.targetNames??[]],maxConcurrentTasks:Number.isFinite(e.slots[0]?.graph.maxConcurrentTasks)?e.slots[0]?.graph.maxConcurrentTasks??null:null,runtimeCount:e.slots.length,runtimes:e.slots.map(s=>({runtimeId:s.runtimeId,peakLeaseCount:s.peakLeaseCount,peakResourceUnits:s.peakResourceUnits,resourceReservations:s.resourceReservations.map(p=>({...p}))})),resourceBudgets:[...o.values()].filter(s=>e.slots.some(p=>p.graph.resourceReservations.some(g=>g.key===s.key))).map(s=>({key:s.key,capacity:s.capacity,reserved:s.reserved,available:s.available,active:s.active,peakActive:s.peakActive}))})).sort((e,s)=>(e.graphKey||"").localeCompare(s.graphKey||""))},getGraphFailure(e){return c.get(e)??null},getGraph(e){return a.get(e)||null},markGraphFailed(e,s){I(c,e,s)}}}function y(t,i,n){const r=t.get(n.graphKey);if(r)return r;const a=i.get(n.graphKey);if(!a)throw new Error(`Unknown graph "${n.graphKey}"`);const o={slots:L(a.instanceCount).map(c=>({graph:a,runtimeId:c,context:null,activeLeaseCount:0,activeResourceUnits:0,resourceReservations:[],peakLeaseCount:0,peakResourceUnits:0,draining:!1}))};return t.set(n.graphKey,o),o}function T(t,i,n){const r=v(i),a=t.slots.filter(o=>!o.draining);if(a.length===0)return null;const u=[...a].filter(o=>x(o,r)&&k(o,n)).sort((o,c)=>o.activeResourceUnits-c.activeResourceUnits||o.activeLeaseCount-c.activeLeaseCount||o.runtimeId.localeCompare(c.runtimeId)).at(0);return u?(q(u,n),u.activeLeaseCount+=1,u.activeResourceUnits+=r,u.peakLeaseCount=Math.max(u.peakLeaseCount,u.activeLeaseCount),u.peakResourceUnits=Math.max(u.peakResourceUnits,u.activeResourceUnits),u):null}function C(t,i){t.activeLeaseCount=Math.max(0,t.activeLeaseCount-1),t.activeResourceUnits=Math.max(0,t.activeResourceUnits-i)}async function $(t,i,n,r,a,u={}){t.context||(t.context=S(t.runtimeId,t.graph,i),r.trackGraphContext(t.context));const o=a.operationTimingRegistry||null,c=o?.start({name:"testkit.runtime.ensure",kind:"runtime",summary:"ensure runtime instance ready",parentId:u.operationTimingParentId||null,scope:{graphKey:t.graph.key,runtimeId:t.runtimeId,serviceName:n.serviceName,targetName:n.targetName}})??null;try{await U(t.context,r,a),o?.finish(c,{status:"passed"})}catch(d){throw o?.finish(c,{status:"failed",error:h(d)}),d}return t.context}function E(t,i,n,r,a){return N(t.operationTimingRegistry||null,{name:n,kind:"runtime",summary:r.summary||null,parentId:i.operationTimingParentId||null,scope:r.scope||null,metadata:r.metadata||null},a)}async function f(t,i,n){!t.context||t.activeLeaseCount>0||(await M(t.context,i),w(t,n),t.context=null,t.draining=!1)}function F(t,i,n){const r=v(i);return t.slots.find(a=>!a.draining&&x(a,r)&&k(a,n))||null}function x(t,i){return t.activeResourceUnits+i<=t.graph.maxConcurrentTasks}function G(t){const i=new Map;for(const n of t)for(const r of n.resourceReservations){const a=i.get(r.key)||{key:r.key,capacity:r.capacity||0,reserved:r.reserved||0,available:r.available||Math.max(0,(r.capacity||0)-(r.reserved||0)),active:0,peakActive:0};a.capacity=Math.max(a.capacity,r.capacity||0),a.reserved=Math.max(a.reserved,r.reserved||0),a.available=Math.max(0,a.capacity-a.reserved),i.set(r.key,a)}return i}function k(t,i){return t.resourceReservations.length>0?!0:t.graph.resourceReservations.every(n=>{const r=i.get(n.key);return r?r.active+n.perRuntime<=r.available:!0})}function q(t,i){if(t.resourceReservations.length>0)return;const n=[];for(const r of t.graph.resourceReservations){const a=i.get(r.key);a&&(a.active+=r.perRuntime,a.peakActive=Math.max(a.peakActive,a.active),n.push({key:r.key,units:r.perRuntime,services:r.services}))}t.resourceReservations=n}function w(t,i){for(const n of t.resourceReservations){const r=i.get(n.key);r&&(r.active=Math.max(0,r.active-n.units))}t.resourceReservations=[]}function I(t,i,n){!i||t.has(i)||t.set(i,n)}function v(t){return t.resourceCost??1}
@@ -1 +1 @@
1
- import{resolveRuntimeInstanceConfigs as g}from"./template.js";import{buildGraphResourceReservations as p}from"../database/runtime-reservations.js";import{resolveRuntimeConfigs as m}from"../config/loader/dependencies.js";export const runtimePlanning=Object.freeze({collectDesiredRuntimeGraphs,resolveRuntimeInstanceConfigs:g,buildRuntimeGraphs,resolveRuntimeConfigs:m,buildGraphDirName});export function buildRuntimeGraphs(t){const r=[],s=new Map;for(const e of t){const n=s.get(e.runtimeKey);if(n){n.targetNames.push(e.config.name),n.instanceCount=Math.max(n.instanceCount,e.config.testkit.runtime.instances),n.maxConcurrentTasks=Math.min(n.maxConcurrentTasks,u(e.runtimeConfigs));continue}const i={key:e.runtimeKey,runtimeNames:e.runtimeNames,runtimeConfigs:e.runtimeConfigs,targetNames:[e.config.name],dirName:buildGraphDirName(e.runtimeNames),instanceCount:e.config.testkit.runtime.instances,maxConcurrentTasks:u(e.runtimeConfigs),resourceReservations:p(e.runtimeConfigs)};r.push(i),s.set(e.runtimeKey,i)}return r.sort((e,n)=>e.dirName.localeCompare(n.dirName))}export function buildGraphDirName(t){const r=t.map(f).join("__");return r.length>0?r:"graph"}function f(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"svc"}function u(t){return t.reduce((r,s)=>Math.min(r,s.testkit.runtime.maxConcurrentTasks),Number.POSITIVE_INFINITY)}export function collectDesiredRuntimeGraphs(t=[]){const r=new Map(t.map(e=>[e.name,e])),s=new Map;for(const e of t){const i=m(e,r).map(c=>c.name).sort(),o=i.join("|"),a=s.get(o);if(a){a.targetNames.push(e.name),a.targetNames.sort(),a.instanceCount=Math.max(a.instanceCount,e.testkit.runtime.instances);continue}s.set(o,{key:o,dirName:buildGraphDirName(i),runtimeNames:i,targetNames:[e.name],instanceCount:e.testkit.runtime.instances})}return[...s.values()].sort((e,n)=>e.dirName.localeCompare(n.dirName))}
1
+ import{resolveRuntimeInstanceConfigs as p}from"./template.js";import{buildGraphResourceReservations as f}from"../database/runtime-reservations.js";import{resolveRuntimeConfigs as m}from"../config/loader/dependencies.js";export const runtimePlanning=Object.freeze({collectDesiredRuntimeGraphs,resolveRuntimeInstanceConfigs:p,buildRuntimeGraphs,resolveRuntimeConfigs:m,buildGraphDirName});export function buildRuntimeGraphs(n){const t=[],s=new Map;for(const e of n){const r=s.get(e.runtimeKey);if(r){r.targetNames.push(e.config.name),r.instanceCount=Math.max(r.instanceCount,u(e.uses)),r.maxConcurrentTasks=Math.min(r.maxConcurrentTasks,c(e.runtimeConfigs));continue}const a={key:e.runtimeKey,runtimeNames:e.runtimeNames,runtimeConfigs:e.runtimeConfigs,targetNames:[e.config.name],dirName:buildGraphDirName(e.runtimeNames),instanceCount:u(e.uses),maxConcurrentTasks:c(e.runtimeConfigs),resourceReservations:f(e.runtimeConfigs)};t.push(a),s.set(e.runtimeKey,a)}return t.sort((e,r)=>e.dirName.localeCompare(r.dirName))}export function buildGraphDirName(n){const t=n.map(C).join("__");return t.length>0?t:"graph"}function C(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"svc"}function u(n){return n.reduce((t,s)=>Math.max(t,s.testkit.runtime.instances),1)}function c(n){return n.reduce((t,s)=>Math.min(t,s.testkit.runtime.maxConcurrentTasks),Number.POSITIVE_INFINITY)}export function collectDesiredRuntimeGraphs(n=[]){const t=new Map(n.map(e=>[e.name,e])),s=new Map;for(const e of n){const a=m(e,t).map(g=>g.name).sort(),o=a.join("|"),i=s.get(o);if(i){i.targetNames.push(e.name),i.targetNames.sort(),i.instanceCount=Math.max(i.instanceCount,e.testkit.runtime.instances);continue}s.set(o,{key:o,dirName:buildGraphDirName(a),runtimeNames:a,targetNames:[e.name],instanceCount:e.testkit.runtime.instances})}return[...s.values()].sort((e,r)=>e.dirName.localeCompare(r.dirName))}
@@ -1 +1 @@
1
- import S from"crypto";import p from"fs";import k from"path";import{resolveServiceCwd as w}from"../config/loader/index.js";import{appendFileToHash as v}from"../repo/fingerprint-policy.js";import{announceResolvedToolchain as D,resolveConfiguredToolchain as y}from"../toolchains/index.js";import{readDatabaseUrl as m}from"../database/state.js";import{buildExecutionEnv as x,buildTemplateExecutionEnv as b,collectConfiguredInputs as R}from"../steps/index.js";import{runConfiguredSteps as g}from"../database/configured-steps.js";import"../evidence/setup-progress.js";import{isUnknownRecord as I,readDynamicProperty as N}from"../kernel/unknown-values.js";const F="prepare-manifest.json";export async function prepareRuntimeServices(e,r={}){for(const t of e)await prepareRuntimeService(t,r)}export async function prepareRuntimeService(e,r={}){const t=e.testkit.runtime.prepare;if(!t||t.steps.length===0||!O(e))return;const n=e.testkit.prepareDir,s=await computeRuntimePrepareFingerprint(e,{signal:r.signal}),i=k.join(n,F);if(A(i)?.fingerprint===s){r.setupRegistry?.recordCached({config:e,stage:"runtime:prepare",kind:"runtime-prepare",summary:"runtime prepare cache hit"});return}p.rmSync(n,{recursive:!0,force:!0}),p.mkdirSync(n,{recursive:!0});const o={...x(e,{...e.testkit.local?.env||{},...r.extraEnv||{}},process.env)},d=m(e.stateDir);d&&(o.DATABASE_URL=d);const u=r.setupRegistry?.start({config:e,stage:"runtime:prepare",kind:"runtime-prepare",summary:"runtime prepare",recordLog:!1}),c=r.setupRegistry;try{await D(e,await y(e,{signal:r.signal}),r.reporter),await g({config:e,steps:t.steps,env:o,labelPrefix:"runtime:prepare",reporter:r.reporter,setupRegistry:c||null,parentOperation:u,signal:r.signal});const a=u?c?.finish(u,{status:"passed",summary:"runtime prepare"}):null;a&&r.reporter?.setupOperationFinished?.(a),P(i,{fingerprint:s,preparedAt:new Date().toISOString(),runtimeId:e.runtimeId||null,serviceName:e.name})}catch(a){const f=u?c?.finish(u,{status:"failed",summary:"runtime prepare",error:N(a,"message")||a}):null;throw f&&r.reporter?.setupOperationFinished?.(f),p.rmSync(n,{recursive:!0,force:!0}),a}}function O(e){return e.testkit.runtime.prepare!=null}export async function computeRuntimePrepareFingerprint(e,r={}){const t=await y(e,{signal:r.signal}),n=S.createHash("sha256");n.update(JSON.stringify({prepare:e.testkit.runtime.prepare,toolchain:t?{kind:"node",install:t.install,nodeVersion:t.nodeVersion,npmVersion:t.npmVersion,nodeSource:t.nodeSource,npmSource:t.npmSource,fingerprint:t.fingerprint}:null,env:b(e,{},{},{includeTestkitControls:!1}),envTemplates:{service:e.testkit.serviceEnv||{},local:e.testkit.local?.env||{}},cwd:e.testkit.local?e.testkit.local.cwd:null})),n.update(JSON.stringify(E(e)));const s=e.testkit.fingerprints||{exclude:[],include:[]};for(const i of e.testkit.envFiles||[])v(n,e.productDir,w(e.productDir,i),s);for(const i of R(e.productDir,e.testkit.runtime.prepare))v(n,e.productDir,i,s);return n.digest("hex")}function E(e){const r=[],t=m(e.stateDir);t&&r.push({service:e.name,url:t});const n=new Set;for(const i of Object.values(e.testkit.serviceEnv||{}))h(i,n,e.name);for(const i of Object.values(e.testkit.local?.env||{}))h(i,n,e.name);const s=e.testkit.templateContext?.stateDirByService;for(const i of[...n].sort()){const l=s?.get(i),o=l?m(l):null;r.push({service:i,url:o||null})}return r}function h(e,r,t){if(typeof e!="string")return;const n=/\{db(?:Url|Host|Port|Name|User|Password|Ssl)(?::([a-zA-Z0-9_-]+))?\}/g;let s=n.exec(e);for(;s;)r.add(s[1]||t),s=n.exec(e)}function A(e){if(!p.existsSync(e))return null;try{const r=JSON.parse(p.readFileSync(e,"utf8"));return I(r)?{fingerprint:r.fingerprint,...typeof r.preparedAt=="string"?{preparedAt:r.preparedAt}:{},...typeof r.runtimeId=="string"||typeof r.runtimeId=="number"||r.runtimeId===null?{runtimeId:r.runtimeId}:{},...typeof r.serviceName=="string"?{serviceName:r.serviceName}:{}}:null}catch{return null}}function P(e,r){p.writeFileSync(e,JSON.stringify(r,null,2))}
1
+ import y from"crypto";import p from"fs";import S from"path";import{resolveServiceCwd as k}from"../config/loader/index.js";import{appendFileToHash as f}from"../repo/fingerprint-policy.js";import{announceResolvedToolchain as w,resolveConfiguredToolchain as v}from"../toolchains/index.js";import{buildExecutionEnv as x,buildTemplateExecutionEnv as D,collectConfiguredInputs as R,serviceDatabaseBinding as l}from"../steps/index.js";import{runConfiguredSteps as b}from"../database/configured-steps.js";import"../evidence/setup-progress.js";import{isUnknownRecord as g,readDynamicProperty as I}from"../kernel/unknown-values.js";const N="prepare-manifest.json";export async function prepareRuntimeServices(e,r={}){for(const t of e)await prepareRuntimeService(t,r)}export async function prepareRuntimeService(e,r={}){const t=e.testkit.runtime.prepare;if(!t||t.steps.length===0||!F(e))return;const n=e.testkit.prepareDir,i=await computeRuntimePrepareFingerprint(e,{signal:r.signal}),s=S.join(n,N);if(E(s)?.fingerprint===i){r.setupRegistry?.recordCached({config:e,stage:"runtime:prepare",kind:"runtime-prepare",summary:"runtime prepare cache hit"});return}p.rmSync(n,{recursive:!0,force:!0}),p.mkdirSync(n,{recursive:!0});const c={...x(e,{...e.testkit.local?.env||{},...r.extraEnv||{}},process.env)},m=l(e)?.url;m&&(c.DATABASE_URL=m);const u=r.setupRegistry?.start({config:e,stage:"runtime:prepare",kind:"runtime-prepare",summary:"runtime prepare",recordLog:!1}),o=r.setupRegistry;try{await w(e,await v(e,{signal:r.signal}),r.reporter),await b({config:e,steps:t.steps,env:c,labelPrefix:"runtime:prepare",reporter:r.reporter,setupRegistry:o||null,parentOperation:u,signal:r.signal});const a=u?o?.finish(u,{status:"passed",summary:"runtime prepare"}):null;a&&r.reporter?.setupOperationFinished?.(a),A(s,{fingerprint:i,preparedAt:new Date().toISOString(),runtimeId:e.runtimeId||null,serviceName:e.name})}catch(a){const d=u?o?.finish(u,{status:"failed",summary:"runtime prepare",error:I(a,"message")||a}):null;throw d&&r.reporter?.setupOperationFinished?.(d),p.rmSync(n,{recursive:!0,force:!0}),a}}function F(e){return e.testkit.runtime.prepare!=null}export async function computeRuntimePrepareFingerprint(e,r={}){const t=await v(e,{signal:r.signal}),n=y.createHash("sha256");n.update(JSON.stringify({prepare:e.testkit.runtime.prepare,toolchain:t?{kind:"node",install:t.install,nodeVersion:t.nodeVersion,npmVersion:t.npmVersion,nodeSource:t.nodeSource,npmSource:t.npmSource,fingerprint:t.fingerprint}:null,env:D(e,{},{},{includeTestkitControls:!1}),envTemplates:{service:e.testkit.serviceEnv||{},local:e.testkit.local?.env||{}},cwd:e.testkit.local?e.testkit.local.cwd:null})),n.update(JSON.stringify(O(e)));const i=e.testkit.fingerprints||{exclude:[],include:[]};for(const s of e.testkit.envFiles||[])f(n,e.productDir,k(e.productDir,s),i);for(const s of R(e.productDir,e.testkit.runtime.prepare))f(n,e.productDir,s,i);return n.digest("hex")}function O(e){const r=[],t=l(e)?.url;t&&r.push({service:e.name,url:t});const n=new Set;for(const i of Object.values(e.testkit.serviceEnv||{}))h(i,n,e.name);for(const i of Object.values(e.testkit.local?.env||{}))h(i,n,e.name);for(const i of[...n].sort())r.push({service:i,url:l(e,i)?.url??null});return r}function h(e,r,t){if(typeof e!="string")return;const n=/\{db(?:Url|Host|Port|Name|User|Password|Ssl)(?::([a-zA-Z0-9_-]+))?\}/g;let i=n.exec(e);for(;i;)r.add(i[1]||t),i=n.exec(e)}function E(e){if(!p.existsSync(e))return null;try{const r=JSON.parse(p.readFileSync(e,"utf8"));return g(r)?{fingerprint:r.fingerprint,...typeof r.preparedAt=="string"?{preparedAt:r.preparedAt}:{},...typeof r.runtimeId=="string"||typeof r.runtimeId=="number"||r.runtimeId===null?{runtimeId:r.runtimeId}:{},...typeof r.serviceName=="string"?{serviceName:r.serviceName}:{}}:null}catch{return null}}function A(e,r){p.writeFileSync(e,JSON.stringify(r,null,2))}
@@ -1,3 +1,3 @@
1
- import{DEFAULT_READY_TIMEOUT_MS as P,ServiceExitedBeforeReadyError as D,ServiceReadinessTimeoutError as _,waitForReady as C,serviceCompletionError as x}from"../process/readiness.js";import{resolveServiceCwd as I}from"../config/loader/index.js";import{startSupervisedProcess as j,ProcessExitedBeforeReadyError as B,DEFAULT_SHUTDOWN_TIMEOUT_MS as F}from"../process/index.js";import{announceResolvedToolchain as N,applyToolchainEnv as V,resolveConfiguredToolchain as q}from"../toolchains/index.js";import{buildExecutionEnv as H}from"../steps/index.js";import{SERVICE_START_ATTEMPTS as W}from"./ports.js";import{captureOutput as R}from"../process/index.js";import{prepareServiceStartInvocation as X}from"../process/service-processes.js";import{readDatabaseUrl as Y}from"../database/state.js";import{toError as S}from"../kernel/error-serialization.js";const z=20;class y extends Error{outcome;output;constructor(t,r,n){super(n.message,{cause:n}),this.outcome=t,this.output=r,this.name="LocalServiceExitedBeforeReadyError"}}export async function startLocalServices(e,t,r={}){const n=e.configs.filter(O).map(a=>a.name),o=[],c=new Map,u=new Map,f=new Map,b=(a,s)=>{const m=U(a);c.set(a.name,[...c.get(a.name)??[],{port:m,outcome:s}]);let d=f.get(a.name);!d&&r.environmentRecord&&(d=r.environmentRecord.service({runtimeLabel:a.runtimeLabel,serviceName:a.name}),f.set(a.name,d)),d?.recordAttempt({baseUrl:a.testkit.local.baseUrl,outcome:s,port:m,readyUrl:typeof a.testkit.local.readyUrl=="string"?a.testkit.local.readyUrl:null})},g=(a,s,m)=>{const d=c.get(a.name)?.at(-1);if(!m||!d){b(a,s);return}d.outcome=s,f.get(a.name)?.amendLastOutcome(s)};try{for(;o.length<n.length;){const a=n[o.length],s=Q(e.configs,a);let m=!1;const d=async()=>{await e.releasePort(a),b(s,"starting"),m=!0};try{o.push(await startLocalService(s,t,r,d)),g(s,"ready",m)}catch(l){if(g(s,J(l,t),m),t.signal.aborted)throw l;if(!(l instanceof y))throw M(s,c.get(a)??[],l,2);const L=[...u.get(a)??[],l];if(u.set(a,L),L.length>=W)throw K(s,c.get(a)??[],L);try{const E=await e.reassignPort(a,new Set(o.map(h=>h.name))),v=o.findIndex(h=>E.includes(h.name));if(v>=0){const h=o.splice(v);for(const T of h)f.get(T.name)?.amendLastOutcome(`ready, then restarted after ${a} moved to a fresh port`);await stopLocalServices(h,t)}await e.prepare(new Set(n.slice(o.length)))}catch(E){throw G(t)?E:M(s,c.get(a)??[],E,1)}}}}catch(a){try{await stopLocalServices(o,t)}catch(s){throw new AggregateError([a,s],"Local service startup failed and its supervised cleanup also failed",{cause:s})}throw a}return o}function G(e){return e.signal.aborted}function J(e,t){return t.signal.aborted?"cancelled":e instanceof y?e.outcome:e instanceof _?"readiness timed out":"failed to start"}function K(e,t,r){const n=r.at(-1),o=[`Service "${e.runtimeLabel}:${e.name}" exited before becoming ready on all ${r.length.toString()} attempts, each on a fresh OS-assigned port:`,...A(t),...n&&n.output.length>0?[`Last output from attempt ${t.length.toString()}:`,...n.output.map(c=>` ${c}`)]:[]];return new Error(o.join(`
2
- `),{cause:n})}function M(e,t,r,n){if(t.length<n)return r;const o=S(r);return new Error([`Service "${e.runtimeLabel}:${e.name}" did not start after ${t.length.toString()} attempts, each on a fresh OS-assigned port:`,...A(t),o.message].join(`
3
- `),{cause:o})}function A(e){return e.map((t,r)=>` attempt ${(r+1).toString()} on port ${t.port.toString()}: ${t.outcome}`)}function Q(e,t){const r=e.find(n=>n.name===t);if(!r||!O(r))throw new Error(`Runtime has no local service "${t}"`);return r}function U(e){const t=e.testkit.local.assignedPort;if(t===void 0)throw new Error(`Testkit assigned no port to service "${e.runtimeLabel}:${e.name}"`);return t}export async function startLocalService(e,t,r={},n=()=>Promise.resolve()){const o=I(e.productDir,e.testkit.local.cwd),c=await q(e,{signal:t.signal});await N(e,c,r.reporter);const u=V(H(e,{...e.testkit.local.env||{},...r.extraEnv||{}},process.env),c),f=U(e);u.PORT=String(f);const b=Y(e.stateDir);b&&(u.DATABASE_URL=b),r.reporter?.localServiceStarting?.(e,e.testkit.local.start);const g=X(e.testkit.local.start,u),a=r.logRegistry?.ensureServiceLogRecord(e),s=r.reporter?.outputMode==="debug"?i=>r.reporter?.writeDebugLine?.(i):null,m=[],d=i=>{m.push(i),m.length>z&&m.shift()};await n();const l=j({args:g.args,windowsVerbatimArguments:g.windowsVerbatimArguments,cwd:o,displayLabel:`Testkit local service ${e.runtimeLabel}:${e.name}`,env:ee(u),executable:g.executable,outputMode:"caller-drained",signal:t.signal,shutdownTimeoutMs:e.testkit.local.shutdownTimeoutMs??F,timeoutMs:null}),L=[R(l.stdout,{livePrefix:`[${e.runtimeLabel}:${e.name}]`,liveWriter:s,onLine(i){d(i),a&&r.logRegistry?.append(a,"stdout",i)}}),R(l.stderr,{livePrefix:`[${e.runtimeLabel}:${e.name}]`,liveWriter:s,onLine(i){d(i),a&&r.logRegistry?.append(a,"stderr",i)}})];let E,v=!1,h=!1;try{E=await l.ready,v=!0,t.registerService(e,l,E,o);const i=e.testkit.local.readyTimeoutMs||P;e.testkit.local.readyUrl!==!1&&e.testkit.local.readyUrl!=null&&await C({name:`${e.runtimeLabel}:${e.name}`,url:e.testkit.local.readyUrl,timeoutMs:i,process:l,signal:t.signal})}catch(i){const p=i instanceof B?x(`${e.runtimeLabel}:${e.name}`,i):i;h=!0;const w=[];try{w.push(...await k(l,L,!1))}catch($){w.push(S($))}if(v)try{t.unregisterService(l.pid)}catch($){w.push(S($))}throw w.length>0?new AggregateError([p,...w],`Local service ${e.runtimeLabel}:${e.name} startup and cleanup failed`,{cause:i}):p instanceof D?new y(p.outcome,m,p):p}return{completionMonitor:l.completion.then(i=>{if(h)return null;if(t.signal.aborted){if(v){v=!1;try{t.unregisterService(l.pid)}catch{}}return null}let p=x(`${e.runtimeLabel}:${e.name}`,i,"unexpectedly");if(v){v=!1;try{t.unregisterService(l.pid)}catch(w){p=new AggregateError([p,w],`Local service ${e.runtimeLabel}:${e.name} exited and its manifest entry could not be removed`)}}try{t.requestStop(p.message)}catch(w){p=new AggregateError([p,w],`Local service ${e.runtimeLabel}:${e.name} exited and the active run could not be interrupted`)}return p}),expectCompletion(){h=!0},name:e.name,outputDrains:L,ownership:E,supervisor:l}}export async function stopLocalServices(e,t){const r=[];for(const n of[...e].reverse()){n.expectCompletion();try{r.push(...await k(n.supervisor,n.outputDrains)),await n.completionMonitor}catch(o){r.push(S(o))}finally{try{t?.unregisterService(n.ownership.pid)}catch(o){r.push(S(o))}}}if(r.length>0)throw new AggregateError(r,"Could not stop all local services")}async function k(e,t,r=!0){const o=await e.stop().then(u=>({error:null,result:u}),u=>({error:S(u),result:null})),c=await Promise.allSettled(t);return[...o.result?Z(o.result,r):[o.error],...c.flatMap(u=>u.status==="rejected"?[S(u.reason)]:[])]}function Z(e,t=!0){return[...t?[e.error]:[],e.terminationError].filter(r=>r!==null)}function ee(e){return Object.fromEntries(Object.entries(e).filter(t=>t[1]!==void 0))}function O(e){return e.testkit.local!=null}
1
+ import{DEFAULT_READY_TIMEOUT_MS as I,ServiceExitedBeforeReadyError as x,ServiceReadinessTimeoutError as B,waitForReady as M,serviceCompletionError as A}from"../process/readiness.js";import{resolveServiceCwd as j}from"../config/loader/index.js";import{startSupervisedProcess as N,ProcessExitedBeforeReadyError as F,DEFAULT_SHUTDOWN_TIMEOUT_MS as q}from"../process/index.js";import{announceResolvedToolchain as V,applyToolchainEnv as H,resolveConfiguredToolchain as W}from"../toolchains/index.js";import{buildExecutionEnv as X,serviceDatabaseBinding as Y}from"../steps/index.js";import{SERVICE_START_ATTEMPTS as z}from"./ports.js";import{captureOutput as O}from"../process/index.js";import{prepareServiceStartInvocation as G}from"../process/service-processes.js";import{portListenerOwner as J}from"../process/port-listener.js";import{toError as S}from"../kernel/error-serialization.js";const K=20;class y extends Error{outcome;output;constructor(t,r,o){super(o.message,{cause:o}),this.outcome=t,this.output=r,this.name="LocalServiceExitedBeforeReadyError"}}export async function startLocalServices(e,t,r={}){const o=e.configs.filter(_).map(a=>a.name),n=[],m=new Map,u=new Map,v=new Map,$=(a,s)=>{const d=P(a);m.set(a.name,[...m.get(a.name)??[],{port:d,outcome:s}]);let h=v.get(a.name);!h&&r.environmentRecord&&(h=r.environmentRecord.service({runtimeLabel:a.runtimeLabel,serviceName:a.name}),v.set(a.name,h)),h?.recordAttempt({baseUrl:a.testkit.local.baseUrl??null,outcome:s,port:d,readyUrl:typeof a.testkit.local.readyUrl=="string"?a.testkit.local.readyUrl:null})},L=(a,s,d)=>{const h=m.get(a.name)?.at(-1);if(!d||!h){$(a,s);return}h.outcome=s,v.get(a.name)?.amendLastOutcome(s)};try{for(;n.length<o.length;){const a=o[n.length],s=te(e.configs,a);let d=!1;const h=async()=>{await e.releasePort(a),$(s,"starting"),d=!0};try{n.push(await startLocalService(s,t,r,h)),L(s,"ready",d)}catch(l){if(L(s,Z(l,t),d),t.signal.aborted)throw l;if(!(l instanceof y))throw U(s,m.get(a)??[],l,2);const b=[...u.get(a)??[],l];if(u.set(a,b),b.length>=z)throw ee(s,m.get(a)??[],b);try{const g=await e.reassignPort(a,new Set(n.map(w=>w.name))),f=n.findIndex(w=>g.includes(w.name));if(f>=0){const w=n.splice(f);for(const T of w)v.get(T.name)?.amendLastOutcome(`ready, then restarted after ${a} moved to a fresh port`);await stopLocalServices(w,t)}await e.prepare(new Set(o.slice(n.length)))}catch(g){throw Q(t)?g:U(s,m.get(a)??[],g,1)}}}}catch(a){try{await stopLocalServices(n,t)}catch(s){throw new AggregateError([a,s],"Local service startup failed and its supervised cleanup also failed",{cause:s})}throw a}return n}function Q(e){return e.signal.aborted}function Z(e,t){return t.signal.aborted?"cancelled":e instanceof y?e.outcome:e instanceof B?"readiness timed out":"failed to start"}function ee(e,t,r){const o=r.at(-1),n=[`Service "${e.runtimeLabel}:${e.name}" exited before becoming ready on all ${r.length.toString()} attempts, each on a fresh OS-assigned port:`,...k(t),...o&&o.output.length>0?[`Last output from attempt ${t.length.toString()}:`,...o.output.map(m=>` ${m}`)]:[]];return new Error(n.join(`
2
+ `),{cause:o})}function U(e,t,r,o){if(t.length<o)return r;const n=S(r);return new Error([`Service "${e.runtimeLabel}:${e.name}" did not start after ${t.length.toString()} attempts, each on a fresh OS-assigned port:`,...k(t),n.message].join(`
3
+ `),{cause:n})}function k(e){return e.map((t,r)=>` attempt ${(r+1).toString()} on port ${t.port.toString()}: ${t.outcome}`)}function te(e,t){const r=e.find(o=>o.name===t);if(!r||!_(r))throw new Error(`Runtime has no local service "${t}"`);return r}function P(e){const t=e.testkit.local.assignedPort;if(t===void 0)throw new Error(`Testkit assigned no port to service "${e.runtimeLabel}:${e.name}"`);return t}export async function startLocalService(e,t,r={},o=()=>Promise.resolve()){const n=j(e.productDir,e.testkit.local.cwd),m=await W(e,{signal:t.signal});await V(e,m,r.reporter);const u=H(X(e,{...e.testkit.local.env||{},...r.extraEnv||{}},process.env),m),v=P(e);u.PORT=String(v);const $=Y(e)?.url;$&&(u.DATABASE_URL=$),r.reporter?.localServiceStarting?.({serviceName:e.name,runtimeLabel:e.runtimeLabel||e.name,command:e.testkit.local.start,port:v});const L=G(e.testkit.local.start,u),a=r.logRegistry?.ensureServiceLogRecord(e),s=r.reporter?.outputMode==="debug"?i=>r.reporter?.writeDebugLine?.(i):null,d=[],h=i=>{d.push(i),d.length>K&&d.shift()};await o();const l=N({args:L.args,windowsVerbatimArguments:L.windowsVerbatimArguments,cwd:n,displayLabel:`Testkit local service ${e.runtimeLabel}:${e.name}`,env:ae(u),executable:L.executable,outputMode:"caller-drained",signal:t.signal,shutdownTimeoutMs:e.testkit.local.shutdownTimeoutMs??q,timeoutMs:null}),b=[O(l.stdout,{livePrefix:`[${e.runtimeLabel}:${e.name}]`,liveWriter:s,onLine(i){h(i),a&&r.logRegistry?.append(a,"stdout",i)}}),O(l.stderr,{livePrefix:`[${e.runtimeLabel}:${e.name}]`,liveWriter:s,onLine(i){h(i),a&&r.logRegistry?.append(a,"stderr",i)}})];let g,f=!1,w=!1;try{g=await l.ready,f=!0,t.registerService(e,l,g,n);const i=e.testkit.local.readyTimeoutMs||I,c=Date.now()+i,{baseUrl:p,readyUrl:E}=e.testkit.local,R={name:`${e.runtimeLabel}:${e.name}`,process:l,signal:t.signal};if(typeof E=="string"&&await M({...R,url:E,timeoutMs:i}),p!==void 0&&!(typeof E=="string"&&ne(E,p))&&await M({...R,url:p,requirement:"response",timeoutMs:Math.max(1,c-Date.now())}),await J(v,l.pid)==="foreign"){const C=`Readiness for "${e.runtimeLabel}:${e.name}" came from another process on port ${String(v)}`;throw new x(C,"another process held the port")}}catch(i){const c=i instanceof F?A(`${e.runtimeLabel}:${e.name}`,i):i;w=!0;const p=[];try{p.push(...await D(l,b,!1))}catch(E){p.push(S(E))}if(f)try{t.unregisterService(l.pid)}catch(E){p.push(S(E))}throw p.length>0?new AggregateError([c,...p],`Local service ${e.runtimeLabel}:${e.name} startup and cleanup failed`,{cause:i}):c instanceof x?new y(c.outcome,d,c):c}return{completionMonitor:l.completion.then(i=>{if(w)return null;if(t.signal.aborted){if(f){f=!1;try{t.unregisterService(l.pid)}catch{}}return null}let c=A(`${e.runtimeLabel}:${e.name}`,i,"unexpectedly");if(f){f=!1;try{t.unregisterService(l.pid)}catch(p){c=new AggregateError([c,p],`Local service ${e.runtimeLabel}:${e.name} exited and its manifest entry could not be removed`)}}try{t.requestStop(c.message)}catch(p){c=new AggregateError([c,p],`Local service ${e.runtimeLabel}:${e.name} exited and the active run could not be interrupted`)}return c}),expectCompletion(){w=!0},name:e.name,outputDrains:b,ownership:g,supervisor:l}}export async function stopLocalServices(e,t){const r=[];for(const o of[...e].reverse()){o.expectCompletion();try{r.push(...await D(o.supervisor,o.outputDrains)),await o.completionMonitor}catch(n){r.push(S(n))}finally{try{t?.unregisterService(o.ownership.pid)}catch(n){r.push(S(n))}}}if(r.length>0)throw new AggregateError(r,"Could not stop all local services")}async function D(e,t,r=!0){const n=await e.stop().then(u=>({error:null,result:u}),u=>({error:S(u),result:null})),m=await Promise.allSettled(t);return[...n.result?re(n.result,r):[n.error],...m.flatMap(u=>u.status==="rejected"?[S(u.reason)]:[])]}function re(e,t=!0){return[...t?[e.error]:[],e.terminationError].filter(r=>r!==null)}function ae(e){return Object.fromEntries(Object.entries(e).filter(t=>t[1]!==void 0))}function _(e){return e.testkit.local!=null}function ne(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}
@@ -1 +1 @@
1
- import{finalizeRuntimeDatabaseConfig as w}from"../database/runtime-config.js";import S from"path";import{finalizeConfiguredInputs as E,finalizeConfiguredSteps as U}from"../steps/configured-steps.js";import"../config/loader/index.js";import{finalizeString as k,resolveTemplateString as y}from"../steps/index.js";import{readDatabaseInfo as j}from"../database/state.js";export function resolveRuntimeInstanceConfigs(e,n,s,r={}){const c=B(r.graphDirName,n),p=r.ports??new Map,i=new Map,o=new Map,u=new Map,f=new Map,d=new Map,m=new Map,v=x(r.externalEndpoints),b=x(r.publicEndpoints),l=x(r.privateEndpoints),h=x(r.serviceContextEndpoints);for(const t of e)m.set(t.name,resolveServiceStateDir(s,t));for(const t of e)t.testkit.local&&(i.set(t.name,resolveRuntimeUrl(t.testkit.local.baseUrl,t.name,n,{runtimeId:n,runtimeLabel:c,runtimeDir:s,portMap:p,baseUrlByService:i,readyUrlByService:o,stateDirByService:m})),t.testkit.local.readyUrl!==!1&&t.testkit.local.readyUrl!=null&&o.set(t.name,resolveRuntimeUrl(t.testkit.local.readyUrl,t.name,n,{runtimeId:n,runtimeLabel:c,runtimeDir:s,portMap:p,baseUrlByService:i,readyUrlByService:o,stateDirByService:m})));for(const[t,a]of Object.entries(v))i.has(t)||i.set(t,a),o.has(t)||o.set(t,a);for(const[t,a]of Object.entries(b))u.set(t,a);for(const[t,a]of Object.entries(l))f.set(t,a);for(const[t,a]of Object.entries(h))d.set(t,a);for(const[t,a]of i.entries())u.has(t)||u.set(t,C(a,r.publicHost||null)),d.has(t)||d.set(t,a),f.has(t)||f.set(t,a);return e.map(t=>resolveRuntimeConfig(t,n,c,s,p,i,o,u,f,d,r.publicHost||null,m))}function x(e){return Object.fromEntries(Object.entries(e??{}).map(([n,s])=>[n,(s??"").trim()]).filter(n=>n[1].length>0))}export function resolveRuntimeConfig(e,n,s,r,c,p,i,o,u,f,d,m){const v=resolveServiceStateDir(r,e),b=resolveServicePrepareDir(r,e),l={runtimeId:n,runtimeLabel:s,runtimeDir:r,productDir:e.productDir,serviceName:e.name,serviceCwd:e.testkit.local?.cwd||".",serviceStateDir:v,prepareDir:b,portMap:c,baseUrlByService:p,readyUrlByService:i,publicBaseUrlByService:o,privateBaseUrlByService:u,serviceContextUrlByService:f,publicHost:d,stateDirByService:m,leaseId:null,leaseDir:null,databaseBindingsReader:j},h=w(e.testkit.database,l),t={...e.testkit.runtime,prepare:R(e.testkit.runtime.prepare,l)},a=e.testkit.local?{...e.testkit.local,start:k(e.testkit.local.start,l),cwd:e.testkit.local.cwd!==void 0?k(e.testkit.local.cwd,l):e.testkit.local.cwd,assignedPort:c.get(e.name),baseUrl:p.get(e.name),readyUrl:i.get(e.name),env:{...e.testkit.local.env||{}}}:void 0;return{...e,stateDir:v,runtimeId:n,runtimeLabel:s,testkit:{...e.testkit,database:h,prepareDir:b,templateContext:l,runtime:t,local:a}}}export function resolveServiceStateDir(e,n){return S.join(e,"services",n.name)}export function resolveServicePrepareDir(e,n){return S.join(resolveServiceStateDir(e,n),"prepared")}export function resolveRuntimeUrl(e,n,s,r){return y(e,{...r,runtimeId:s,serviceName:n})}function C(e,n){if(!n)return e;try{const s=new URL(e);s.hostname=n;let r=s.toString();return!e.endsWith("/")&&s.pathname==="/"&&r.endsWith("/")&&(r=r.slice(0,-1)),r}catch{return e}}function R(e,n){return e?{inputs:E(e.inputs,s=>k(s,n)),steps:U(e.steps,s=>k(s,n))}:{inputs:[],steps:[]}}function B(e,n){return e?`${e}/${n}`:n}
1
+ import{finalizeRuntimeDatabaseConfig as w}from"../database/runtime-config.js";import h from"path";import{finalizeConfiguredInputs as E,finalizeConfiguredSteps as U}from"../steps/configured-steps.js";import"../config/loader/index.js";import{finalizeString as b,resolveTemplateString as y}from"../steps/index.js";export function resolveRuntimeInstanceConfigs(e,n,r,s={}){const c=buildRuntimeLabel(s.graphDirName,n),p=s.ports??new Map,a=new Map,l=new Map,u=new Map,d=new Map,f=new Map,x=k(s.externalEndpoints),m=k(s.publicEndpoints),v=k(s.privateEndpoints),o=k(s.serviceContextEndpoints);for(const t of e)t.testkit.local&&(t.testkit.local.baseUrl!==void 0&&a.set(t.name,resolveRuntimeUrl(t.testkit.local.baseUrl,t.name,n,{runtimeId:n,runtimeLabel:c,runtimeDir:r,portMap:p,baseUrlByService:a,readyUrlByService:l})),t.testkit.local.readyUrl!==!1&&t.testkit.local.readyUrl!=null&&l.set(t.name,resolveRuntimeUrl(t.testkit.local.readyUrl,t.name,n,{runtimeId:n,runtimeLabel:c,runtimeDir:r,portMap:p,baseUrlByService:a,readyUrlByService:l})));for(const[t,i]of Object.entries(x))a.has(t)||a.set(t,i),l.has(t)||l.set(t,i);for(const[t,i]of Object.entries(m))u.set(t,i);for(const[t,i]of Object.entries(v))d.set(t,i);for(const[t,i]of Object.entries(o))f.set(t,i);for(const[t,i]of a.entries())u.has(t)||u.set(t,C(i,s.publicHost||null)),f.has(t)||f.set(t,i),d.has(t)||d.set(t,i);return e.map(t=>resolveRuntimeConfig(t,n,c,r,p,a,l,u,d,f,s.publicHost||null,s.databaseBindings??new Map))}function k(e){return Object.fromEntries(Object.entries(e??{}).map(([n,r])=>[n,(r??"").trim()]).filter(n=>n[1].length>0))}export function resolveRuntimeConfig(e,n,r,s,c,p,a,l,u,d,f,x){const m=resolveServiceStateDir(s,e),v=resolveServicePrepareDir(s,e),o={runtimeId:n,runtimeLabel:r,runtimeDir:s,productDir:e.productDir,serviceName:e.name,serviceCwd:e.testkit.local?.cwd||".",serviceStateDir:m,prepareDir:v,portMap:c,baseUrlByService:p,readyUrlByService:a,publicBaseUrlByService:l,privateBaseUrlByService:u,serviceContextUrlByService:d,publicHost:f,leaseId:null,leaseDir:null,databaseBindingsByService:x},t=w(e.testkit.database,o),i={...e.testkit.runtime,prepare:j(e.testkit.runtime.prepare,o)},S=e.testkit.local?{...e.testkit.local,start:b(e.testkit.local.start,o),cwd:e.testkit.local.cwd!==void 0?b(e.testkit.local.cwd,o):e.testkit.local.cwd,assignedPort:c.get(e.name),baseUrl:p.get(e.name),readyUrl:a.get(e.name),env:{...e.testkit.local.env||{}}}:void 0;return{...e,stateDir:m,runtimeId:n,runtimeLabel:r,testkit:{...e.testkit,database:t,prepareDir:v,templateContext:o,runtime:i,local:S}}}export function resolveServiceStateDir(e,n){return h.join(e,"services",n.name)}export function resolveServicePrepareDir(e,n){return h.join(resolveServiceStateDir(e,n),"prepared")}export function resolveRuntimeUrl(e,n,r,s){return y(e,{...s,runtimeId:r,serviceName:n})}function C(e,n){if(!n)return e;try{const r=new URL(e);r.hostname=n;let s=r.toString();return!e.endsWith("/")&&r.pathname==="/"&&s.endsWith("/")&&(s=s.slice(0,-1)),s}catch{return e}}function j(e,n){return e?{inputs:E(e.inputs,r=>b(r,n)),steps:U(e.steps,r=>b(r,n))}:{inputs:[],steps:[]}}export function buildRuntimeLabel(e,n){return e?`${e}/${n}`:n}
@@ -1 +1 @@
1
- import{isUnknownArray as o,isUnknownRecord as s}from"../kernel/unknown-values.js";export function normalizeArtifactArchiveConfig(r){const n=e(r,"artifacts"),i=e(n?.retention,"artifacts.retention"),t=e(n?.publication,"artifacts.publication");return{retention:{maxRuns:a(i?.maxRuns,"artifacts.retention.maxRuns"),maxAgeDays:u(i?.maxAgeDays,"artifacts.retention.maxAgeDays"),preserveSuites:f(i?.preserveSuites,"artifacts.retention.preserveSuites")},publication:t?{directory:m(t.directory,"artifacts.publication.directory"),required:c(t.required,"artifacts.publication.required")}:null}}function a(r,n){if(r==null)return null;if(typeof r!="number"||!Number.isInteger(r)||r<=0)throw new Error(`${n} must be a positive integer`);return r}function u(r,n){if(r==null)return null;if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${n} must be a positive number`);return r}function f(r,n){if(r==null)return[];if(!o(r))throw new Error(`${n} must be an array of non-empty strings`);const i=r.map(t=>{if(typeof t!="string"||t.trim().length===0)throw new Error(`${n} must be an array of non-empty strings`);return t.trim()});return[...new Set(i)].sort()}function m(r,n){if(typeof r!="string"||r.trim().length===0)throw new Error(`${n} is required`);return r.trim()}function c(r,n){if(r===void 0)return!1;if(typeof r!="boolean")throw new Error(`${n} must be a boolean`);return r}function e(r,n){if(r!=null){if(!s(r))throw new Error(`${n} must be an object`);return r}}
1
+ import{rejectUnknownKeys as e}from"../kernel/known-keys.js";import{isUnknownArray as o,isUnknownRecord as s}from"../kernel/unknown-values.js";export function normalizeArtifactArchiveConfig(r){const t=normalizeOptionalRecord(r,"artifacts"),i=normalizeOptionalRecord(t?.retention,"artifacts.retention"),n=normalizeOptionalRecord(t?.publication,"artifacts.publication");return t&&e(t,["publication","retention"],"artifacts"),i&&e(i,["maxAgeDays","maxRuns","preserveSuites"],"artifacts.retention"),n&&e(n,["directory","required"],"artifacts.publication"),{retention:{maxRuns:a(i?.maxRuns,"artifacts.retention.maxRuns"),maxAgeDays:f(i?.maxAgeDays,"artifacts.retention.maxAgeDays"),preserveSuites:u(i?.preserveSuites,"artifacts.retention.preserveSuites")},publication:n?{directory:c(n.directory,"artifacts.publication.directory"),required:m(n.required,"artifacts.publication.required")}:null}}function a(r,t){if(r==null)return null;if(typeof r!="number"||!Number.isInteger(r)||r<=0)throw new Error(`${t} must be a positive integer`);return r}function f(r,t){if(r==null)return null;if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${t} must be a positive number`);return r}function u(r,t){if(r==null)return[];if(!o(r))throw new Error(`${t} must be an array of non-empty strings`);const i=r.map(n=>{if(typeof n!="string"||n.trim().length===0)throw new Error(`${t} must be an array of non-empty strings`);return n.trim()});return[...new Set(i)].sort()}function c(r,t){if(typeof r!="string"||r.trim().length===0)throw new Error(`${t} is required`);return r.trim()}function m(r,t){if(r===void 0)return!1;if(typeof r!="boolean")throw new Error(`${t} must be a boolean`);return r}export function normalizeOptionalRecord(r,t){if(r!=null){if(!s(r))throw new Error(`${t} must be an object`);return r}}
@@ -1,2 +1,2 @@
1
- import{isPathWithin as F}from"../process/files/paths.js";import{isUnknownArray as P,isUnknownRecord as _}from"../kernel/unknown-values.js";import{readRunDocument as C}from"./read-run.js";import l from"node:fs";import o from"node:path";import"../engines/api/index.js";import"../domain/identifiers.js";import{normalizeArtifactArchiveConfig as M}from"./artifact-config.js";import{applyRunBundleRetention as B,publishRunBundle as $,sealRunBundle as U}from"./bundles.js";import{RETENTION_DECISION_SCHEMA_VERSION as J}from"./contracts.js";import{applyTimingUpdates as q,createEmptyTimings as I,normalizeTimings as V}from"./timing.js";import{normalizeOptionalNonEmptyString as E,normalizeStringArray as K,readOptionalProperty as x,requireObjectRecord as X}from"./artifact-validation.js";import{errorMessage as G,serializeJson as k,writeJsonFileAtomicWithRetry as j}from"../process/files/json-file.js";import{copyRegularFileSecurely as W}from"./secure-file-copy.js";import{hasErrorCode as N}from"../kernel/error-serialization.js";import{posixPath as R}from"../process/files/paths.js";const z="timings.json",A="artifacts",H="logs",Q="setup",T="live.json",Y=1e4,O=String;export function writeRunArtifact(t,e,r={},n={}){const i=e,s=o.join(t,".testkit","results");l.mkdirSync(s,{recursive:!0});const c=i.archive,a=r.archiveConfig??M(x(c,"request")||{}),u=r.declaredEnvironmentKeys??K(x(c,"declaredEnvironmentKeys")||[],"run artifact declaredEnvironmentKeys"),m=n.sealRunBundle??U,p=n.publishRunBundle??$,d=n.applyRunBundleRetention??B,y=n.writeLatestProjection??j,f=m(t,i,{env:r.env,archiveConfig:a,declaredEnvironmentKeys:u}),h=Z(t,f.archive.runId,a.publication,p),S=tt(t,f.archive.runId,a.retention,d),v=i.finalizationFailure,b={...f,...v===void 0?{}:{finalizationFailure:v},run:h.status==="failed"&&h.required===!0||f.archive.cleanup?.status==="failed"||v!==void 0&&f.run.status!=="interrupted"?{...f.run,status:"failed"}:f.run,archive:{...f.archive,publication:h,retention:S}};return et(s,b,y)}function Z(t,e,r,n){try{return n(t,e,r)}catch(i){return{bytesUploaded:0,configured:r!==null,error:w(i),recordedAt:new Date().toISOString(),required:r?.required??!1,runId:e,schemaVersion:1,status:"failed",target:r===null?null:o.resolve(t,r.directory)}}}function tt(t,e,r,n){try{return n(t,e,r)}catch(i){return{currentRunId:e,decidedAt:new Date().toISOString(),error:w(i),garbageCollection:{failures:[],reclaimedBytes:0,removedObjects:0,status:"not_started"},policy:{maxAgeDays:r.maxAgeDays,maxRuns:r.maxRuns,preserveSuites:[...r.preserveSuites]},protectedRunIds:[e],removed:[],removalErrors:[],schemaVersion:J,status:"failed"}}}function et(t,e,r){const n=o.join(t,"latest.json"),i={path:R(o.relative(o.dirname(t),n)),status:"complete"},s={...e,archive:{...e.archive,latest:i}};try{return r(n,s),s}catch(c){return{...s,archive:{...s.archive,latest:{...i,error:w(c),status:"failed"}}}}}function w(t){try{return G(t)}catch{return"Unknown post-seal archive operation failure"}}export function writeLiveRunArtifact(t,e){const r=e,n=o.join(t,".testkit","results");l.mkdirSync(n,{recursive:!0}),j(o.join(n,T),r)}export function finalizeLiveRunArtifact(t,e={}){const r=it(e),n=o.join(t,".testkit","results",T),i=lt(n);if(i===null)return!1;const s=rt(i,r);return s?(writeRunArtifact(t,s),!0):!1}function rt(t,e){const r=C(t,"live run artifact"),n=r.run.id||null;if(e.runId&&n&&n!==e.runId)return null;const i=st(e.finishedAt),s=new Date(i).toISOString(),c=ot(r.run.startedAt),a=c===null?r.run.durationMs:Math.max(0,i-c);return{...r,generatedAt:s,run:{...r.run,id:n??e.runId,status:e.status,finishedAt:s,durationMs:a,interruptReason:e.reason},timings:nt(r.timings,s,e.reason)}}function nt(t,e,r){if(t==null)return t;const n=X(t,"live run artifact.timings"),i=n.operations;if(i==null)return n;if(!P(i))throw new Error("live run artifact.timings.operations must be an array");return{...n,operations:i.map(s=>!_(s)||s.status!=="running"?s:{...s,status:"cancelled",finishedAt:E(s.finishedAt,"live run operation.finishedAt")??e,message:E(s.message,"live run operation.message")??r})}}function it(t){return{runId:t.runId??null,reason:t.reason??"run interrupted",status:t.status??"interrupted",finishedAt:t.finishedAt??Date.now()}}function st(t){if(t instanceof Date&&Number.isFinite(t.getTime()))return t.getTime();if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="string"){const e=Date.parse(t);if(Number.isFinite(e))return e}return Date.now()}function ot(t){if(typeof t!="string")return null;const e=Date.parse(t);return Number.isFinite(e)?e:null}export function resetResultArtifacts(t){l.rmSync(o.join(t,".testkit","results",A),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",H),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",Q),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",T),{force:!0})}export function persistTaskArtifacts(t,e,r){if(r.length===0)return[];const n=g(e.serviceName||"service"),i=L(t,n);return r.map((s,c)=>{const a=at(s),u={schemaVersion:2,source:"testkit-runtime-artifact",service:e.serviceName,suite:{engineId:e.engineId,key:e.suiteKey,name:e.suiteName,profileId:e.profileId,testType:e.testType},file:e.file,taskId:e.id,index:c,name:a.name,kind:a.kind,summary:a.summary,contentType:a.contentType,emittedAt:a.emittedAt,data:a.data},m=`${k(u,"runtime artifact payload")}
2
- `,p=D(i,e.id,c,a.name,".json",y=>{l.writeFileSync(y,m,{flag:"wx"})}),d=o.join(".testkit","results",A,n,p);return{name:u.name,kind:u.kind,summary:u.summary,contentType:u.contentType,path:R(d)}})}export function persistTaskFileArtifacts(t,e,r,n={}){if(r.length===0)return[];const i=g(e.serviceName||"service"),s=L(t,i);return r.map((c,a)=>{const u=ut(c),m=o.resolve(u.path),p=ft(m,u.contentType),d=[],y=D(s,e.id,a,u.name,p,S=>{d.push(W(m,S,{...n.maxBytes===void 0?{}:{maxBytes:n.maxBytes},...n.sourceRoot===void 0?{}:{sourceRoot:n.sourceRoot},...n.sizeLimitDescription===void 0?{}:{sizeLimitDescription:n.sizeLimitDescription}}))}),f=d.at(0);if(f===void 0)throw new Error("File artifact copy completed without a result");n.copyResults?.push(f);const h=o.join(".testkit","results",A,i,y);return{name:u.name,kind:u.kind,summary:u.summary,contentType:u.contentType??dt(m),path:R(h)}})}export function persistTaskOutputArtifacts(t,e,r){if(r.length===0)return[];const n=[];for(const i of r){const s=i.text;s.trim().length!==0&&n.push({name:i.name||"task-output",kind:i.kind||"runtime.output",summary:i.summary||mt(s),contentType:i.contentType||"text/plain",emittedAt:i.emittedAt||new Date().toISOString(),data:{stream:i.stream||null,text:s}})}return persistTaskArtifacts(t,e,n)}export function loadTimings(t){const e=o.join(t,".testkit",z);if(!l.existsSync(e))return I();try{const r=JSON.parse(l.readFileSync(e,"utf8"));return V(r)}catch{return I()}}export function saveTimings(t,e,r){if(r.length===0)return;const n=q(e,r),i=o.join(t,".testkit");l.mkdirSync(i,{recursive:!0}),l.writeFileSync(o.join(i,z),k(n,"timings"))}function at(t){return{name:t.name,kind:t.kind||null,summary:t.summary||null,contentType:t.contentType||"application/json",emittedAt:t.emittedAt||null,data:t.data}}function ut(t){return{path:t.path,name:t.name||"artifact",kind:t.kind||null,summary:t.summary||null,contentType:t.contentType||null}}function ct(t,e,r,n,i=0){const s=i===0?"":`-${(i+1).toString().padStart(2,"0")}`;return`task-${g(t)}-${(e+1).toString().padStart(2,"0")}-${g(r)}${s}${n}`}function D(t,e,r,n,i,s){for(let c=0;c<Y;c+=1){const a=ct(e,r,n,i,c),u=o.join(t,a);try{return s(u),a}catch(m){if(N(m,"EEXIST"))continue;throw l.rmSync(u,{force:!0}),m}}throw new Error(`Could not allocate a unique artifact path for task ${O(e)} and artifact ${n}`)}function L(t,e){const r=o.resolve(t);if(!l.statSync(r).isDirectory())throw new Error("Artifact product directory must be a directory");const i=l.realpathSync(r);let s=r;for(const c of[".testkit","results",A,e]){s=o.join(s,c);try{l.mkdirSync(s)}catch(m){if(!N(m,"EEXIST"))throw m}const a=l.lstatSync(s);if(a.isSymbolicLink()||!a.isDirectory())throw new Error(`Artifact output directory ${JSON.stringify(s)} must be a real directory below productDir`);const u=l.realpathSync(s);if(!F(i,u))throw new Error(`Artifact output directory ${JSON.stringify(s)} escapes productDir`)}return s}function g(t){return O(t).trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"artifact"}function lt(t){try{return JSON.parse(l.readFileSync(t,"utf8"))}catch{return null}}function mt(t){const e=t.split(/\r?\n/u).map(r=>r.trim()).find(Boolean);return e?e.slice(0,120):"captured task output"}function ft(t,e){const r=o.extname(t);if(r)return r;const n=(e??"").split(";",1)[0].trim().toLowerCase();return n==="text/markdown"?".md":n==="text/plain"?".txt":n==="application/json"?".json":n==="application/zip"?".zip":n==="image/png"?".png":n==="image/jpeg"?".jpg":n==="text/html"?".html":n==="video/webm"?".webm":".artifact"}function dt(t){const e=o.extname(t).toLowerCase();return e===".md"||e===".markdown"?"text/markdown":e===".txt"||e===".log"?"text/plain":e===".json"?"application/json":"application/octet-stream"}
1
+ import{isPathWithin as _}from"../process/files/paths.js";import{isUnknownArray as C,isUnknownRecord as M}from"../kernel/unknown-values.js";import{readRunDocument as B}from"./read-run.js";import l from"node:fs";import o from"node:path";import"../engines/api/index.js";import"../domain/identifiers.js";import{normalizeArtifactArchiveConfig as $}from"./artifact-config.js";import{applyRunBundleRetention as K,publishRunBundle as U,sealRunBundle as V}from"./bundles.js";import{RETENTION_DECISION_SCHEMA_VERSION as q}from"./contracts.js";import{redactedRunValue as A,runRedaction as J}from"./redaction.js";import{applyTimingUpdates as X,createEmptyTimings as x,normalizeTimings as G}from"./timing.js";import{normalizeOptionalNonEmptyString as k,normalizeStringArray as W,readOptionalProperty as j,requireObjectRecord as H}from"./artifact-validation.js";import{errorMessage as Q,serializeJson as N,writeJsonFileAtomicWithRetry as z,readJsonFileOrNull as Y}from"../process/files/json-file.js";import{copyRegularFileSecurely as Z}from"./secure-file-copy.js";import{hasErrorCode as O}from"../kernel/error-serialization.js";import{posixPath as w}from"../process/files/paths.js";const D="timings.json",S="artifacts",tt="logs",et="setup",I="live.json",rt=1e4,b=String;export function writeRunArtifact(t,e,r={},n={}){const i=e,a=o.join(t,".testkit","results");l.mkdirSync(a,{recursive:!0});const u=i.archive,s=r.archiveConfig??$(j(u,"request")||{}),c=r.declaredEnvironmentKeys??W(j(u,"declaredEnvironmentKeys")||[],"run artifact declaredEnvironmentKeys"),m=n.sealRunBundle??V,p=n.publishRunBundle??U,d=n.applyRunBundleRetention??K,y=n.writeLatestProjection??z,f=m(t,i,{env:r.env,archiveConfig:s,declaredEnvironmentKeys:c,redactKeys:r.redactKeys,redactionTargets:r.redactionTargets}),h=nt(t,f.archive.runId,s.publication,p),v=it(t,f.archive.runId,s.retention,d),{targets:g}=J({env:r.env,redactKeys:r.redactKeys,redactionTargets:r.redactionTargets}),R=A(i.finalizationFailure,g),P={...f,...R===void 0?{}:{finalizationFailure:R},run:h.status==="failed"&&h.required===!0||f.archive.cleanup?.status==="failed"||R!==void 0&&f.run.status!=="interrupted"?{...f.run,status:"failed"}:f.run,archive:{...f.archive,publication:A(h,g),retention:A(v,g)}};return at(a,P,y,g)}function nt(t,e,r,n){try{return n(t,e,r)}catch(i){return{bytesUploaded:0,configured:r!==null,error:E(i),recordedAt:new Date().toISOString(),required:r?.required??!1,runId:e,schemaVersion:1,status:"failed",target:r===null?null:o.resolve(t,r.directory)}}}function it(t,e,r,n){try{return n(t,e,r)}catch(i){return{currentRunId:e,decidedAt:new Date().toISOString(),error:E(i),garbageCollection:{failures:[],reclaimedBytes:0,removedObjects:0,status:"not_started"},policy:{maxAgeDays:r.maxAgeDays,maxRuns:r.maxRuns,preserveSuites:[...r.preserveSuites]},protectedRunIds:[e],removed:[],removalErrors:[],schemaVersion:q,status:"failed"}}}function at(t,e,r,n){const i=o.join(t,"latest.json"),a={path:w(o.relative(o.dirname(t),i)),status:"complete"},u={...e,archive:{...e.archive,latest:a}};try{return r(i,u),u}catch(s){return{...u,archive:{...u.archive,latest:{...a,error:A(E(s),n),status:"failed"}}}}}function E(t){try{return Q(t)}catch{return"Unknown post-seal archive operation failure"}}export function writeLiveRunArtifact(t,e){const r=e,n=o.join(t,".testkit","results");l.mkdirSync(n,{recursive:!0}),z(o.join(n,I),r)}export function finalizeLiveRunArtifact(t,e={}){const r=ut(e),n=o.join(t,".testkit","results",I),i=Y(n);if(i===null)return!1;const a=st(i,r);return a?(writeRunArtifact(t,a,{redactKeys:e.redactKeys,redactionTargets:e.redactionTargets}),!0):!1}function st(t,e){const r=B(t,"live run artifact"),n=r.run.id||null;if(e.runId&&n&&n!==e.runId)return null;const i=ct(e.finishedAt),a=new Date(i).toISOString(),u=lt(r.run.startedAt),s=u===null?r.run.durationMs:Math.max(0,i-u);return{...r,generatedAt:a,run:{...r.run,id:n??e.runId,status:e.status,finishedAt:a,durationMs:s,interruptReason:e.reason},timings:ot(r.timings,a,e.reason)}}function ot(t,e,r){if(t==null)return t;const n=H(t,"live run artifact.timings"),i=n.operations;if(i==null)return n;if(!C(i))throw new Error("live run artifact.timings.operations must be an array");return{...n,operations:i.map(a=>!M(a)||a.status!=="running"?a:{...a,status:"cancelled",finishedAt:k(a.finishedAt,"live run operation.finishedAt")??e,message:k(a.message,"live run operation.message")??r})}}function ut(t){return{runId:t.runId??null,reason:t.reason??"run interrupted",status:t.status??"interrupted",finishedAt:t.finishedAt??Date.now()}}function ct(t){if(t instanceof Date&&Number.isFinite(t.getTime()))return t.getTime();if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="string"){const e=Date.parse(t);if(Number.isFinite(e))return e}return Date.now()}function lt(t){if(typeof t!="string")return null;const e=Date.parse(t);return Number.isFinite(e)?e:null}export function resetResultArtifacts(t){l.rmSync(o.join(t,".testkit","results",S),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",tt),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",et),{recursive:!0,force:!0}),l.rmSync(o.join(t,".testkit","results",I),{force:!0})}export function persistTaskArtifacts(t,e,r){if(r.length===0)return[];const n=T(e.serviceName||"service"),i=F(t,n);return r.map((a,u)=>{const s=mt(a),c={schemaVersion:2,source:"testkit-runtime-artifact",service:e.serviceName,suite:{engineId:e.engineId,key:e.suiteKey,name:e.suiteName,profileId:e.profileId,testType:e.testType},file:e.file,taskId:e.id,index:u,name:s.name,kind:s.kind,summary:s.summary,contentType:s.contentType,emittedAt:s.emittedAt,data:s.data},m=`${N(c,"runtime artifact payload")}
2
+ `,p=L(i,e.id,u,s.name,".json",y=>{l.writeFileSync(y,m,{flag:"wx"})}),d=o.join(".testkit","results",S,n,p);return{name:c.name,kind:c.kind,summary:c.summary,contentType:c.contentType,path:w(d)}})}export function persistTaskFileArtifacts(t,e,r,n={}){if(r.length===0)return[];const i=T(e.serviceName||"service"),a=F(t,i);return r.map((u,s)=>{const c=ft(u),m=o.resolve(c.path),p=yt(m,c.contentType),d=[],y=L(a,e.id,s,c.name,p,v=>{d.push(Z(m,v,{...n.maxBytes===void 0?{}:{maxBytes:n.maxBytes},...n.sourceRoot===void 0?{}:{sourceRoot:n.sourceRoot},...n.sizeLimitDescription===void 0?{}:{sizeLimitDescription:n.sizeLimitDescription}}))}),f=d.at(0);if(f===void 0)throw new Error("File artifact copy completed without a result");n.copyResults?.push(f);const h=o.join(".testkit","results",S,i,y);return{name:c.name,kind:c.kind,summary:c.summary,contentType:c.contentType??ht(m),path:w(h)}})}export function persistTaskOutputArtifacts(t,e,r){if(r.length===0)return[];const n=[];for(const i of r){const a=i.text;a.trim().length!==0&&n.push({name:i.name||"task-output",kind:i.kind||"runtime.output",summary:i.summary||pt(a),contentType:i.contentType||"text/plain",emittedAt:i.emittedAt||new Date().toISOString(),data:{stream:i.stream||null,text:a}})}return persistTaskArtifacts(t,e,n)}export function loadTimings(t){const e=o.join(t,".testkit",D);if(!l.existsSync(e))return x();try{const r=JSON.parse(l.readFileSync(e,"utf8"));return G(r)}catch{return x()}}export function saveTimings(t,e,r){if(r.length===0)return;const n=X(e,r),i=o.join(t,".testkit");l.mkdirSync(i,{recursive:!0}),l.writeFileSync(o.join(i,D),N(n,"timings"))}function mt(t){return{name:t.name,kind:t.kind||null,summary:t.summary||null,contentType:t.contentType||"application/json",emittedAt:t.emittedAt||null,data:t.data}}function ft(t){return{path:t.path,name:t.name||"artifact",kind:t.kind||null,summary:t.summary||null,contentType:t.contentType||null}}function dt(t,e,r,n,i=0){const a=i===0?"":`-${(i+1).toString().padStart(2,"0")}`;return`task-${T(t)}-${(e+1).toString().padStart(2,"0")}-${T(r)}${a}${n}`}function L(t,e,r,n,i,a){for(let u=0;u<rt;u+=1){const s=dt(e,r,n,i,u),c=o.join(t,s);try{return a(c),s}catch(m){if(O(m,"EEXIST"))continue;throw l.rmSync(c,{force:!0}),m}}throw new Error(`Could not allocate a unique artifact path for task ${b(e)} and artifact ${n}`)}function F(t,e){const r=o.resolve(t);if(!l.statSync(r).isDirectory())throw new Error("Artifact product directory must be a directory");const i=l.realpathSync(r);let a=r;for(const u of[".testkit","results",S,e]){a=o.join(a,u);try{l.mkdirSync(a)}catch(m){if(!O(m,"EEXIST"))throw m}const s=l.lstatSync(a);if(s.isSymbolicLink()||!s.isDirectory())throw new Error(`Artifact output directory ${JSON.stringify(a)} must be a real directory below productDir`);const c=l.realpathSync(a);if(!_(i,c))throw new Error(`Artifact output directory ${JSON.stringify(a)} escapes productDir`)}return a}function T(t){return b(t).trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"artifact"}function pt(t){const e=t.split(/\r?\n/u).map(r=>r.trim()).find(Boolean);return e?e.slice(0,120):"captured task output"}function yt(t,e){const r=o.extname(t);if(r)return r;const n=(e??"").split(";",1)[0].trim().toLowerCase();return n==="text/markdown"?".md":n==="text/plain"?".txt":n==="application/json"?".json":n==="application/zip"?".zip":n==="image/png"?".png":n==="image/jpeg"?".jpg":n==="text/html"?".html":n==="video/webm"?".webm":".artifact"}function ht(t){const e=o.extname(t).toLowerCase();return e===".md"||e===".markdown"?"text/markdown":e===".txt"||e===".log"?"text/plain":e===".json"?"application/json":"application/octet-stream"}
@@ -1 +1 @@
1
- import{recordWork as H}from"../kernel/work-metrics.js";import{isUnknownRecord as A,optionalString as k}from"../kernel/unknown-values.js";import{posixPath as J}from"../process/files/paths.js";import{readRunProvenance as ue}from"./read-provenance.js";import{parseRunBundleArtifact as le,parseRunBundleManifest as Y,readRunBundleArtifact as fe}from"./read-bundle.js";import{readRunDocument as de}from"./read-run.js";import me from"crypto";import c from"fs";import u from"path";import{redactSensitiveText as G}from"../kernel/redaction.js";import{BUNDLE_SCHEMA_VERSION as Q,OBJECTS_DIRNAME as X,PROVENANCE_SCHEMA_VERSION as pe,RETENTION_DECISION_SCHEMA_VERSION as he,RUNS_DIRNAME as ge}from"./contracts.js";import{bundleRelativePath as $,errorMessage as x,hashFile as F,normalizeRunId as I,objectRecords as Z,optionalStringArray as z,optionalUnknownArray as ye,readJsonFile as P,requirePublicationPathOutsideResults as M,requireRunId as ve,runIncludesFile as Se,resolveManifestEntryPath as ee,resolveRunBundleDirectory as O,resolveRunsRoot as te,resultsRoot as U,sanitizeSegment as Re,sha256 as C,stableJson as q,treeBytes as ne,walkFiles as re,writeBufferAtomic as je,writeJson as N}from"./storage.js";import{resolveCiProvenance as be}from"./ci-provenance.js";const W=["artifacts","logs","setup"],we=/(?:PASSWORD|TOKEN|SECRET|PRIVATE_KEY|AUTH|COOKIE|SESSION|API_KEY)/iu,Ie=["package.json","package-lock.json","npm-shrinkwrap.json","pnpm-lock.yaml","yarn.lock","testkit.config.ts","testkit.config.mts","testkit.config.js","testkit.config.mjs"];export function sealRunBundle(e,n,t={},s={}){const r=n,o=ve(r),i=te(e,!0),a=u.dirname(i),f=u.join(i,o),m=s.cleanupActiveRunResults??ze,p=loadVerifiedRunBundleArtifactIfExists(e,o);if(p!==null)return{...p,archive:{...p.archive,cleanup:m(a)}};const d=u.join(i,`.${o}.${String(process.pid)}.${String(Date.now())}.${me.randomBytes(4).toString("hex")}.tmp`);c.mkdirSync(d,{recursive:!0});try{const R=Te(t.env??process.env),g=Ee({productDir:e,resultsDir:a,runId:o,tempDir:d,sensitiveValues:R}),h=Be(r,o,R),y=de(h.artifact,"run artifact");g.redaction.structuredReplacements=h.replacements,g.redaction.replacements+=h.replacements;const v=xe(e,y,o,{...t,redaction:g.redaction}),j={schemaVersion:Q,runId:o,status:"complete",canonicalRunPath:$(o,"run.json"),manifestPath:$(o,"manifest.json"),provenancePath:$(o,"provenance.json"),integrity:"sha256",publication:{configured:!!t.archiveConfig?.publication,status:t.archiveConfig?.publication?"pending":"not_configured"},redaction:g.redaction};y.archive=j,N(u.join(d,"run.json"),y),N(u.join(d,"provenance.json"),v);const b=$e(d),D=C(Buffer.from(q(b))),E={schemaVersion:Q,runId:o,createdAt:new Date().toISOString(),algorithm:"sha256",files:b,storage:{fileCount:b.length,logicalBytes:b.reduce((T,_)=>T+_.bytes,0),objectBytesWritten:g.objectBytesWritten,deduplicatedBytes:g.deduplicatedBytes},redaction:g.redaction,digest:D};return N(u.join(d,"manifest.json"),E),c.renameSync(d,f),{...y,archive:{...j,cleanup:m(a),manifestDigest:E.digest,storage:E.storage}}}catch(R){try{c.rmSync(d,{recursive:!0,force:!0})}catch{}try{Ce(e)}catch{}throw R}}export function loadVerifiedRunBundleArtifactIfExists(e,n){const t=I(n),s=O(e,t);if(!c.existsSync(s))return null;const r=verifyRunBundle(e,t);if(!r.ok)throw new Error(`Immutable run bundle ${t} already exists but failed integrity verification: ${r.errors.join("; ")}`);return loadRunBundleArtifact(e,t)}export function loadRunBundleArtifact(e,n){return se(e,n).artifact}function se(e,n){const t=I(n),s=fe(e,t),r=loadRunBundleManifest(e,t);return{manifest:r,artifact:{...s,archive:{...s.archive,manifestDigest:r.digest,storage:r.storage}}}}export function loadRunBundleManifest(e,n){const t=I(n),s=O(e,t),r=u.join(s,"manifest.json");if(!c.existsSync(r))throw new Error(`No immutable Testkit run manifest found for ${t}`);return Y(P(r),r,t)}export function listRunBundles(e,n={}){return ie(e,n).map(({artifact:t,manifest:s,...r})=>r)}function ie(e,n={}){const t=te(e,!1);if(!c.existsSync(t))return[];const s=c.readdirSync(t,{withFileTypes:!0});return H("artifacts.runDirectoriesRead",1),H("artifacts.runEntriesVisited",s.length),s.filter(o=>o.isDirectory()&&!o.name.startsWith(".")).map(o=>{try{const{artifact:i,manifest:a}=se(e,o.name);return n.file&&!Se(i,n.file)?null:{artifact:i,manifest:a,runId:o.name,status:i.run.status,startedAt:i.run.startedAt,finishedAt:i.run.finishedAt??null,manifestDigest:i.archive.manifestDigest??null,canonicalRunPath:i.archive.canonicalRunPath}}catch{return null}}).filter(o=>o!==null).sort((o,i)=>(i.finishedAt??i.startedAt??"").localeCompare(o.finishedAt??o.startedAt??""))}export function verifyRunBundle(e,n){const t=I(n);try{return V(O(e,t),t)}catch(s){return{ok:!1,runId:t,errors:[x(s)],checkedFiles:0}}}export function publishRunBundle(e,n,t,s={}){const r=I(n),o=s.writePublicationReceipt??Pe;if(!t)return o(e,r,{status:"not_configured",required:!1,target:null,bytesUploaded:0});const i=u.resolve(e,t.directory);try{M(e,i)}catch(d){return o(e,r,{status:"failed",required:t.required,target:i,error:x(d),bytesUploaded:0})}const a=Re(u.basename(e)),f=u.join(i,a),m=u.join(f,r),p=u.join(f,`.${r}.${String(process.pid)}.${String(Date.now())}.tmp`);try{const d=O(e,r);if(c.mkdirSync(f,{recursive:!0}),M(e,f),c.existsSync(m)){const h=c.lstatSync(m);if(h.isSymbolicLink()||!h.isDirectory())throw new Error("Publication target must be a regular bundle directory");M(e,m);const y=V(d,r);if(!y.ok)throw new Error(`Source run bundle failed integrity verification: ${y.errors.join("; ")}`);const v=V(m,r);if(!v.ok)throw new Error(`Publication target failed integrity verification: ${v.errors.join("; ")}`);if(y.manifestDigest!==v.manifestDigest)throw new Error("Publication target already exists with a different manifest digest");return o(e,r,{status:"complete",required:t.required,target:m,manifestDigest:y.manifestDigest,reused:!0,bundleBytes:ne(d),bytesUploaded:0})}const R=ne(d);c.cpSync(d,p,{recursive:!0,preserveTimestamps:!0});const g=V(p,r);if(!g.ok)throw new Error(g.errors.join("; "));return c.renameSync(p,m),o(e,r,{status:"complete",required:t.required,target:m,manifestDigest:g.manifestDigest,reused:!1,bundleBytes:R,bytesUploaded:R})}catch(d){return c.rmSync(p,{recursive:!0,force:!0}),o(e,r,{status:"failed",required:t.required,target:m,error:x(d),bytesUploaded:0})}}export function applyRunBundleRetention(e,n,t={},s={}){const r=I(n),o=s.removeObject??ce,i=s.removeRunBundle??Ne,a=t.maxRuns??null,f=t.maxAgeDays??null,m=new Set(t.preserveSuites||[]),p=ie(e),d=new Set([r]);for(const l of p)Oe(l.artifact,m)&&d.add(l.runId);const R=new Set(a?p.slice(0,a).map(l=>l.runId):p.map(l=>l.runId)),g=f==null?null:Date.now()-f*24*60*60*1e3,h=[];for(const l of p){if(d.has(l.runId))continue;const S=g!=null&&Date.parse(l.finishedAt||l.startedAt||"")<g,w=!R.has(l.runId);if(!S&&!w)continue;const B=[];S&&B.push("max_age"),w&&B.push("max_runs"),h.push({path:u.join(U(e),ge,l.runId),reasons:B,runId:l.runId})}const y=new Map(p.map(l=>[l.runId,l.manifest.files.filter(S=>S.path.startsWith("files/")).map(S=>S.sha256)])),v=oe(e),j=[],b=[];for(const l of h){let S=null;try{i(l.path)}catch(B){S=L(B)}const w=!c.existsSync(l.path);!w&&S===null&&(S="Run bundle still exists after removal completed"),w&&j.push({reasons:l.reasons,runId:l.runId}),S!==null&&b.push({...l,error:S,removed:w})}const D=new Set(j.map(l=>l.runId)),E=new Set(p.filter(l=>!D.has(l.runId)).flatMap(l=>y.get(l.runId)??[])),T=ae(v,E,o),_=new Date().toISOString();return Ve({currentRunId:r,decidedAt:_,garbageCollection:T,policy:{maxRuns:a,maxAgeDays:f,preserveSuites:[...m].sort()},protectedRunIds:[...d].sort(),removed:j,removalErrors:b})}function Ee({resultsDir:e,tempDir:n,sensitiveValues:t}){let s=0,r=0,o=0,i=0,a=0;const f=u.join(e,X,"sha256");c.mkdirSync(f,{recursive:!0});for(const m of W){const p=u.join(e,m);if(c.existsSync(p))for(const d of re(p)){const R=u.relative(p,d),g=c.readFileSync(d),h=De(g,d,t);h.scanned&&(o+=1),h.replacements>0&&(i+=1,a+=h.replacements);const y=C(h.buffer),v=u.join(f,y);c.existsSync(v)?r+=h.buffer.length:(je(v,h.buffer),c.chmodSync(v,292),s+=h.buffer.length);const j=u.join(n,"files",m,R);c.mkdirSync(u.dirname(j),{recursive:!0});try{c.linkSync(v,j)}catch{c.copyFileSync(v,j)}}}return{objectBytesWritten:s,deduplicatedBytes:r,redaction:{policyVersion:1,status:"applied",textualFilesScanned:o,filesRedacted:i,replacements:a,sensitiveEnvironmentKeys:t.map(m=>m.key).sort(),valuesStored:!1}}}function Be(e,n,t){const s=new Map(W.map(o=>[`.testkit/results/${o}/`,`${$(n,`files/${o}`)}/`])),r=K(e,s);return ke(Ae(r),t)}function Ae(e){if(!A(e))return e;const n={...e};if(A(n.product)){const t={...n.product};delete t.directory,n.product=t}if(A(n.git)){const t={...n.git};delete t.remoteUrl,delete t.repoRoot,delete t.worktreePath,n.git=t}return delete n.host,n}function K(e,n){if(Array.isArray(e))return e.map(s=>K(s,n));if(A(e))return Object.fromEntries(Object.entries(e).map(([s,r])=>[s,K(r,n)]));if(typeof e!="string")return e;let t=e;for(const[s,r]of n)t.startsWith(s)&&(t=`${r}${t.slice(s.length)}`);return t}function ke(e,n){let t=0;function s(r){if(Array.isArray(r))return r.map(s);if(A(r))return Object.fromEntries(Object.entries(r).map(([i,a])=>[i,s(a)]));if(typeof r!="string")return r;let o=G(r);o!==r&&(t+=1);for(const i of n){if(!o.includes(i.value))continue;const a=o.split(i.value);t+=a.length-1,o=a.join("<redacted>")}return o}return{artifact:s(e),replacements:t}}function xe(e,n,t,s){const r=z(n.run.fileNames),o=[...new Set([...Ie,...r])].map(i=>Fe(e,i)).filter(i=>i!==null);return{schemaVersion:pe,runId:t,ci:be(s.env??process.env),source:{git:n.git??null,files:o},runtime:{controller:{testkitVersion:k(n.run.testkitVersion),node:process.versions.node,platform:process.platform,architecture:process.arch},runtimeStats:ye(n.run.runtimeStats)},scenario:{types:z(n.run.types),suiteSelectors:z(n.run.suiteSelectors),fileNames:r,serviceFilter:k(n.run.serviceFilter),planningDigest:C(Buffer.from(q(n.planning??null))),declaredEnvironmentKeys:[...new Set(s.declaredEnvironmentKeys??[])].sort()},redaction:s.redaction}}function $e(e){return re(e).map(n=>({path:J(u.relative(e,n)),bytes:c.statSync(n).size,sha256:F(n)})).sort((n,t)=>n.path.localeCompare(t.path))}function V(e,n){const t=u.join(e,"manifest.json"),s=Y(P(t),t,n);le(P(u.join(e,"run.json")),u.join(e,"run.json"),n);const r=[];for(const i of s.files){let a;try{a=ee(e,i.path)}catch(m){r.push(x(m));continue}if(!c.existsSync(a)){r.push(`missing ${i.path}`);continue}const f=c.lstatSync(a);if(f.isSymbolicLink()){r.push(`symbolic link not allowed ${i.path}`);continue}if(!f.isFile()){r.push(`not a regular file ${i.path}`);continue}f.size!==i.bytes&&r.push(`size mismatch ${i.path}`),F(a)!==i.sha256&&r.push(`checksum mismatch ${i.path}`)}C(Buffer.from(q(s.files)))!==s.digest&&r.push("manifest digest mismatch"),s.storage.fileCount!==s.files.length&&r.push("manifest file count mismatch");for(const i of["run.json","provenance.json"])s.files.some(a=>a.path===i)||r.push(`manifest must include ${i}`);if(r.length===0){const i=ee(e,"provenance.json");ue(P(i),i,n)}return{runId:n,checkedFiles:s.files.length,storage:s.storage,ok:r.length===0,errors:r,manifestDigest:s.digest}}function Pe(e,n,t){const s={schemaVersion:1,runId:n,recordedAt:new Date().toISOString(),...t};return N(u.join(U(e),"publications",`${n}.json`),s),s}function Oe(e,n){return n.size===0?!1:Z(e.services).some(t=>Z(t.suites).some(s=>{const r=k(t.name)??"",o=k(s.name)??"",i=k(s.testType)??"";return[o,`${i}:${o}`,`${r}/${i}:${o}`].some(f=>n.has(f))}))}function Ce(e){const n=new Set;for(const t of listRunBundles(e)){const s=loadRunBundleManifest(e,t.runId);for(const r of s.files)r.path.startsWith("files/")&&n.add(r.sha256)}return ae(oe(e),n,ce)}function oe(e){const n=u.join(U(e),X,"sha256");return c.existsSync(n)?c.readdirSync(n,{withFileTypes:!0}).filter(t=>t.isFile()).map(t=>{const s=u.join(n,t.name);return{bytes:c.statSync(s).size,digest:t.name,path:s}}):[]}function ae(e,n,t){const s=[];let r=0,o=0;for(const i of e){if(n.has(i.digest))continue;let a=null;try{t(i.path)}catch(m){a=L(m)}!c.existsSync(i.path)?(r+=1,o+=i.bytes):a===null&&(a="Object still exists after removal completed"),a!==null&&s.push({error:a,path:i.path})}return{failures:s,reclaimedBytes:o,removedObjects:r,status:s.length===0?"complete":"failed"}}function Ne(e){c.rmSync(e,{recursive:!0,force:!0})}function ce(e){c.rmSync(e,{force:!0})}function Ve({currentRunId:e,decidedAt:n,garbageCollection:t,policy:s,protectedRunIds:r,removed:o,removalErrors:i}){const a=[...i.map(f=>`remove ${f.runId}: ${f.error}`),...t.failures.map(f=>`garbage collect ${f.path}: ${f.error}`)];return{currentRunId:e,decidedAt:n,garbageCollection:t,policy:s,protectedRunIds:r,removed:o,removalErrors:i,schemaVersion:he,status:a.length===0?"complete":"failed",...a.length===0?{}:{error:a.join("; ")}}}function De(e,n,t){if(!_e(e,n)){for(const i of t)if(e.includes(Buffer.from(i.value)))throw new Error(`Refusing to archive binary file containing sensitive environment value: ${n}`);return{buffer:e,scanned:!1,replacements:0}}const s=e.toString("utf8");let r=G(s),o=r===s?0:1;for(const i of t){if(!r.includes(i.value))continue;const a=r.split(i.value);o+=a.length-1,r=a.join("<redacted>")}return{buffer:Buffer.from(r),scanned:!0,replacements:o}}function Te(e){const n=[];for(const[t,s]of Object.entries(e))we.test(t)&&typeof s=="string"&&s.length>=8&&n.push({key:t,value:s});return n}function _e(e,n){if(e.includes(0))return!1;const t=u.extname(n).toLowerCase();return![".zip",".gz",".png",".jpg",".jpeg",".webm",".pdf"].includes(t)}function Fe(e,n){const t=u.resolve(e,n);return!t.startsWith(`${u.resolve(e)}${u.sep}`)||!c.existsSync(t)||!c.statSync(t).isFile()?null:{path:J(u.relative(e,t)),bytes:c.statSync(t).size,sha256:F(t)}}function ze(e){const n=[];for(const t of[...W.map(s=>u.join(e,s)),u.join(e,"live.json")])try{c.rmSync(t,{recursive:!0,force:!0})}catch(s){n.push(`${t}: ${L(s)}`)}return n.length===0?{status:"complete"}:{error:n.join("; "),status:"failed"}}function L(e){try{return x(e)}catch{return"Unknown post-seal archive operation failure"}}
1
+ import{recordWork as J}from"../kernel/work-metrics.js";import{isUnknownRecord as A,optionalString as H}from"../kernel/unknown-values.js";import{posixPath as K}from"../process/files/paths.js";import{readRunProvenance as oe}from"./read-provenance.js";import{parseRunBundleArtifact as ae,parseRunBundleManifest as Y,readRunBundleArtifact as ce}from"./read-bundle.js";import{readRunDocument as ue}from"./read-run.js";import{runModel as G}from"./run-model.js";import le from"crypto";import a from"fs";import u from"path";import{redactRunText as fe,redactRunValue as me,runRedaction as de}from"./redaction.js";import{BUNDLE_SCHEMA_VERSION as Q,OBJECTS_DIRNAME as X,PROVENANCE_SCHEMA_VERSION as pe,RETENTION_DECISION_SCHEMA_VERSION as he,RUNS_DIRNAME as ye}from"./contracts.js";import{bundleRelativePath as $,hashFile as z,normalizeRunId as I,optionalStringArray as D,optionalUnknownArray as ge,readJsonFile as x,requirePublicationPathOutsideResults as _,requireRunId as ve,runIncludesFile as Se,resolveManifestEntryPath as Z,resolveRunBundleDirectory as P,resolveRunsRoot as N,resultsRoot as T,sanitizeSegment as Re,sha256 as O,stableJson as M,treeBytes as ee,walkFiles as te,writeBufferAtomic as je,writeJson as C}from"./storage.js";import{resolveCiProvenance as be}from"./ci-provenance.js";import{errorMessage as B}from"../kernel/error-serialization.js";const U=["artifacts","logs","setup"],we=["package.json","package-lock.json","npm-shrinkwrap.json","pnpm-lock.yaml","yarn.lock","testkit.config.ts","testkit.config.mts","testkit.config.js","testkit.config.mjs"];export function sealRunBundle(e,n,t={},r={}){const s=n,o=ve(s),i=N(e,!0),c=u.dirname(i),f=u.join(i,o),l=r.cleanupActiveRunResults??_e,p=loadVerifiedRunBundleArtifactIfExists(e,o);if(p!==null)return{...p,archive:{...p.archive,cleanup:l(c)}};const m=u.join(i,`.${o}.${String(process.pid)}.${String(Date.now())}.${le.randomBytes(4).toString("hex")}.tmp`);a.mkdirSync(m,{recursive:!0});try{const S=de(t),y=Ie({productDir:e,resultsDir:c,runId:o,tempDir:m,redaction:S}),h=Be(s,o,S.targets),v=ue(h.artifact,"run artifact");y.redaction.structuredReplacements=h.replacements,y.redaction.replacements+=h.replacements;const g=ke(e,v,o,{...t,redaction:y.redaction}),j={schemaVersion:Q,runId:o,status:"complete",canonicalRunPath:$(o,"run.json"),manifestPath:$(o,"manifest.json"),provenancePath:$(o,"provenance.json"),integrity:"sha256",publication:{configured:!!t.archiveConfig?.publication,status:t.archiveConfig?.publication?"pending":"not_configured"},redaction:y.redaction};v.archive=j,C(u.join(m,"run.json"),v),C(u.join(m,"provenance.json"),g);const b=Ae(m),V=O(Buffer.from(M(b))),E={schemaVersion:Q,runId:o,createdAt:new Date().toISOString(),algorithm:"sha256",files:b,storage:{fileCount:b.length,logicalBytes:b.reduce((d,R)=>d+R.bytes,0),objectBytesWritten:y.objectBytesWritten,deduplicatedBytes:y.deduplicatedBytes},redaction:y.redaction,digest:V};return C(u.join(m,"manifest.json"),E),a.renameSync(m,f),{...v,archive:{...j,cleanup:l(c),manifestDigest:E.digest,storage:E.storage}}}catch(S){try{a.rmSync(m,{recursive:!0,force:!0})}catch{}try{Pe(e)}catch{}throw S}}export function loadVerifiedRunBundleArtifactIfExists(e,n){const t=I(n),r=P(e,t);if(!a.existsSync(r))return null;const s=verifyRunBundle(e,t);if(!s.ok)throw new Error(`Immutable run bundle ${t} already exists but failed integrity verification: ${s.errors.join("; ")}`);return loadRunBundleArtifact(e,t)}export function loadRunBundleArtifact(e,n){return ne(e,n).artifact}function ne(e,n){const t=I(n),r=ce(e,t),s=loadRunBundleManifest(e,t);return{manifest:s,artifact:{...r,archive:{...r.archive,manifestDigest:s.digest,storage:s.storage}}}}export function loadRunBundleManifest(e,n){const t=I(n),r=P(e,t),s=u.join(r,"manifest.json");if(!a.existsSync(s))throw new Error(`No immutable Testkit run manifest found for ${t}`);return Y(x(s),s,t)}export function listRunBundles(e,n={}){return q(e,n).map(({artifact:t,manifest:r,...s})=>s)}function q(e,n={}){const t=N(e,!1);if(!a.existsSync(t))return[];const r=a.readdirSync(t,{withFileTypes:!0});return J("artifacts.runDirectoriesRead",1),J("artifacts.runEntriesVisited",r.length),r.filter(o=>o.isDirectory()&&!o.name.startsWith(".")).map(o=>{try{const{artifact:i,manifest:c}=ne(e,o.name);return n.file&&!Se(G(i),n.file)?null:{artifact:i,manifest:c,runId:o.name,status:i.run.status,startedAt:i.run.startedAt,finishedAt:i.run.finishedAt??null,manifestDigest:i.archive.manifestDigest??null,canonicalRunPath:i.archive.canonicalRunPath}}catch{return null}}).filter(o=>o!==null).sort((o,i)=>(i.finishedAt??i.startedAt??"").localeCompare(o.finishedAt??o.startedAt??""))}export function verifyRunBundle(e,n){const t=I(n);try{return F(P(e,t),t)}catch(r){return{ok:!1,runId:t,errors:[B(r)],checkedFiles:0}}}export function publishRunBundle(e,n,t,r={}){const s=I(n),o=r.writePublicationReceipt??$e;if(!t)return o(e,s,{status:"not_configured",required:!1,target:null,bytesUploaded:0});const i=u.resolve(e,t.directory);try{_(e,i)}catch(m){return o(e,s,{status:"failed",required:t.required,target:i,error:B(m),bytesUploaded:0})}const c=Re(u.basename(e)),f=u.join(i,c),l=u.join(f,s),p=u.join(f,`.${s}.${String(process.pid)}.${String(Date.now())}.tmp`);try{const m=P(e,s);if(a.mkdirSync(f,{recursive:!0}),_(e,f),a.existsSync(l)){const h=a.lstatSync(l);if(h.isSymbolicLink()||!h.isDirectory())throw new Error("Publication target must be a regular bundle directory");_(e,l);const v=F(m,s);if(!v.ok)throw new Error(`Source run bundle failed integrity verification: ${v.errors.join("; ")}`);const g=F(l,s);if(!g.ok)throw new Error(`Publication target failed integrity verification: ${g.errors.join("; ")}`);if(v.manifestDigest!==g.manifestDigest)throw new Error("Publication target already exists with a different manifest digest");return o(e,s,{status:"complete",required:t.required,target:l,manifestDigest:v.manifestDigest,reused:!0,bundleBytes:ee(m),bytesUploaded:0})}const S=ee(m);a.cpSync(m,p,{recursive:!0,preserveTimestamps:!0});const y=F(p,s);if(!y.ok)throw new Error(y.errors.join("; "));return a.renameSync(p,l),o(e,s,{status:"complete",required:t.required,target:l,manifestDigest:y.manifestDigest,reused:!1,bundleBytes:S,bytesUploaded:S})}catch(m){return a.rmSync(p,{recursive:!0,force:!0}),o(e,s,{status:"failed",required:t.required,target:l,error:B(m),bytesUploaded:0})}}export function applyRunBundleRetention(e,n,t={},r={}){const s=I(n),o=r.removeObject??ie,i=r.removeRunBundle??Ce,c=t.maxRuns??null,f=t.maxAgeDays??null,l=new Set(t.preserveSuites||[]),p=q(e),m=new Set([s]);for(const d of p)xe(G(d.artifact),l)&&m.add(d.runId);const S=new Set(c?p.slice(0,c).map(d=>d.runId):p.map(d=>d.runId)),y=f==null?null:Date.now()-f*24*60*60*1e3,h=[];for(const d of p){if(m.has(d.runId))continue;const R=y!=null&&Date.parse(d.finishedAt||d.startedAt||"")<y,w=!S.has(d.runId);if(!R&&!w)continue;const k=[];R&&k.push("max_age"),w&&k.push("max_runs"),h.push({path:u.join(T(e),ye,d.runId),reasons:k,runId:d.runId})}const v=se(e),g=[],j=[];for(const d of h){let R=null;try{i(d.path)}catch(k){R=L(k)}const w=!a.existsSync(d.path);!w&&R===null&&(R="Run bundle still exists after removal completed"),w&&g.push({reasons:d.reasons,runId:d.runId}),R!==null&&j.push({...d,error:R,removed:w})}const b=new Set(g.map(d=>d.runId)),V=re(e,v,o,p.filter(d=>!b.has(d.runId))),E=new Date().toISOString();return Fe({currentRunId:s,decidedAt:E,garbageCollection:V,policy:{maxRuns:c,maxAgeDays:f,preserveSuites:[...l].sort()},protectedRunIds:[...m].sort(),removed:g,removalErrors:j})}function Ie({resultsDir:e,tempDir:n,redaction:t}){let r=0,s=0,o=0,i=0,c=0;const f=u.join(e,X,"sha256");a.mkdirSync(f,{recursive:!0});for(const l of U){const p=u.join(e,l);if(a.existsSync(p))for(const m of te(p)){const S=u.relative(p,m),y=a.readFileSync(m),h=Ve(y,m,t.targets);h.scanned&&(o+=1),h.replacements>0&&(i+=1,c+=h.replacements);const v=O(h.buffer),g=u.join(f,v);a.existsSync(g)?s+=h.buffer.length:(je(g,h.buffer),a.chmodSync(g,292),r+=h.buffer.length);const j=u.join(n,"files",l,S);a.mkdirSync(u.dirname(j),{recursive:!0});try{a.linkSync(g,j)}catch{a.copyFileSync(g,j)}}}return{objectBytesWritten:r,deduplicatedBytes:s,redaction:{policyVersion:1,status:t.status,textualFilesScanned:o,filesRedacted:i,replacements:c,sensitiveEnvironmentKeys:t.keys,valuesStored:!1}}}function Be(e,n,t){const r=new Map(U.map(i=>[`.testkit/results/${i}/`,`${$(n,`files/${i}`)}/`])),s=W(e,r),o=me(Ee(s),t);return{artifact:o.value,replacements:o.replacements}}function Ee(e){if(!A(e))return e;const n={...e};if(A(n.product)){const t={...n.product};delete t.directory,n.product=t}if(A(n.git)){const t={...n.git};delete t.remoteUrl,delete t.repoRoot,delete t.worktreePath,n.git=t}return delete n.host,n}function W(e,n){if(Array.isArray(e))return e.map(r=>W(r,n));if(A(e))return Object.fromEntries(Object.entries(e).map(([r,s])=>[r,W(s,n)]));if(typeof e!="string")return e;let t=e;for(const[r,s]of n)t.startsWith(r)&&(t=`${s}${t.slice(r.length)}`);return t}function ke(e,n,t,r){const s=D(n.run.fileNames),o=[...new Set([...we,...s])].map(i=>De(e,i)).filter(i=>i!==null);return{schemaVersion:pe,runId:t,ci:be(r.env??process.env),source:{git:n.git??null,files:o},runtime:{controller:{testkitVersion:H(n.run.testkitVersion),node:process.versions.node,platform:process.platform,architecture:process.arch},runtimeStats:n.schemaVersion===19?ge(n.run.runtimeStats):[...n.environment?.capacity?.runtimeStats??[]]},scenario:{types:D(n.run.types),suiteSelectors:D(n.run.suiteSelectors),fileNames:s,serviceFilter:H(n.schemaVersion===19?n.run.serviceFilter:n.run.groupFilter),planningDigest:O(Buffer.from(M(n.planning??null))),declaredEnvironmentKeys:[...new Set(r.declaredEnvironmentKeys??[])].sort()},redaction:r.redaction}}function Ae(e){return te(e).map(n=>({path:K(u.relative(e,n)),bytes:a.statSync(n).size,sha256:z(n)})).sort((n,t)=>n.path.localeCompare(t.path))}function F(e,n){const t=u.join(e,"manifest.json"),r=Y(x(t),t,n);ae(x(u.join(e,"run.json")),u.join(e,"run.json"),n);const s=[];for(const i of r.files){let c;try{c=Z(e,i.path)}catch(l){s.push(B(l));continue}if(!a.existsSync(c)){s.push(`missing ${i.path}`);continue}const f=a.lstatSync(c);if(f.isSymbolicLink()){s.push(`symbolic link not allowed ${i.path}`);continue}if(!f.isFile()){s.push(`not a regular file ${i.path}`);continue}f.size!==i.bytes&&s.push(`size mismatch ${i.path}`),z(c)!==i.sha256&&s.push(`checksum mismatch ${i.path}`)}O(Buffer.from(M(r.files)))!==r.digest&&s.push("manifest digest mismatch"),r.storage.fileCount!==r.files.length&&s.push("manifest file count mismatch");for(const i of["run.json","provenance.json"])r.files.some(c=>c.path===i)||s.push(`manifest must include ${i}`);if(s.length===0){const i=Z(e,"provenance.json");oe(x(i),i,n)}return{runId:n,checkedFiles:r.files.length,storage:r.storage,ok:s.length===0,errors:s,manifestDigest:r.digest}}function $e(e,n,t){const r={schemaVersion:1,runId:n,recordedAt:new Date().toISOString(),...t};return C(u.join(T(e),"publications",`${n}.json`),r),r}function xe(e,n){return n.size===0?!1:e.groups.some(t=>t.suites.some(r=>{const s=r.testType??"";return[r.name,`${s}:${r.name}`,`${t.name}/${s}:${r.name}`].some(i=>n.has(i))}))}function Pe(e){return re(e,se(e),ie,q(e))}function re(e,n,t,r){const s=N(e,!1),o=new Map(r.map(l=>[l.runId,l.manifest])),i=new Set,c=[],f=a.existsSync(s)?a.readdirSync(s,{withFileTypes:!0}):[];for(const l of f)if(!(!l.isDirectory()||l.name.startsWith(".")))try{for(const p of(o.get(l.name)??loadRunBundleManifest(e,l.name)).files)p.path.startsWith("files/")&&i.add(p.sha256)}catch(p){c.push({error:`No stored object was removed: ${B(p)}`,path:u.join(s,l.name)})}return c.length>0?{failures:c,reclaimedBytes:0,removedObjects:0,status:"not_started"}:Oe(n,i,t)}function se(e){const n=u.join(T(e),X,"sha256");return a.existsSync(n)?a.readdirSync(n,{withFileTypes:!0}).filter(t=>t.isFile()).map(t=>{const r=u.join(n,t.name);return{bytes:a.statSync(r).size,digest:t.name,path:r}}):[]}function Oe(e,n,t){const r=[];let s=0,o=0;for(const i of e){if(n.has(i.digest))continue;let c=null;try{t(i.path)}catch(l){c=L(l)}!a.existsSync(i.path)?(s+=1,o+=i.bytes):c===null&&(c="Object still exists after removal completed"),c!==null&&r.push({error:c,path:i.path})}return{failures:r,reclaimedBytes:o,removedObjects:s,status:r.length===0?"complete":"failed"}}function Ce(e){a.rmSync(e,{recursive:!0,force:!0})}function ie(e){a.rmSync(e,{force:!0})}function Fe({currentRunId:e,decidedAt:n,garbageCollection:t,policy:r,protectedRunIds:s,removed:o,removalErrors:i}){const c=[...i.map(f=>`remove ${f.runId}: ${f.error}`),...t.failures.map(f=>`garbage collect ${f.path}: ${f.error}`)];return{currentRunId:e,decidedAt:n,garbageCollection:t,policy:r,protectedRunIds:s,removed:o,removalErrors:i,schemaVersion:he,status:c.length===0?"complete":"failed",...c.length===0?{}:{error:c.join("; ")}}}function Ve(e,n,t){if(!ze(e,n)){for(const s of t)if(e.includes(Buffer.from(s)))throw new Error(`Refusing to archive binary file containing sensitive environment value: ${n}`);return{buffer:e,scanned:!1,replacements:0}}const r=fe(e.toString("utf8"),t);return r.replacements===0?{buffer:e,scanned:!0,replacements:0}:{buffer:Buffer.from(r.text),scanned:!0,replacements:r.replacements}}function ze(e,n){if(e.includes(0))return!1;const t=u.extname(n).toLowerCase();return![".zip",".gz",".png",".jpg",".jpeg",".webm",".pdf"].includes(t)}function De(e,n){const t=u.resolve(e,n);return!t.startsWith(`${u.resolve(e)}${u.sep}`)||!a.existsSync(t)||!a.statSync(t).isFile()?null:{path:K(u.relative(e,t)),bytes:a.statSync(t).size,sha256:z(t)}}function _e(e){const n=[];for(const t of[...U.map(r=>u.join(e,r)),u.join(e,"live.json")])try{a.rmSync(t,{recursive:!0,force:!0})}catch(r){n.push(`${t}: ${L(r)}`)}return n.length===0?{status:"complete"}:{error:n.join("; "),status:"failed"}}function L(e){try{return B(e)}catch{return"Unknown post-seal archive operation failure"}}
@@ -1 +1 @@
1
- import{redactUrlSecrets as o}from"../process/url-redaction.js";export function createRunEnvironmentRecord(m={}){const c=[];return{service({runtimeLabel:e,serviceName:t}){let r=null;return{recordAttempt({baseUrl:n,outcome:a,port:s,readyUrl:l}){const u={baseUrl:o(n),readyUrl:l===null?null:o(l)};r===null&&(r={serviceName:t,runtimeLabel:e,...u,port:s,attempts:[]},c.push(r)),Object.assign(r,u,{port:s}),r.attempts.push({port:s,outcome:a}),m.onChange?.()},amendLastOutcome(n){const a=r?.attempts.at(-1);a&&(a.outcome=n,m.onChange?.())}}},listServices(){return c.map(e=>({...e,attempts:e.attempts.map(t=>({...t}))})).sort((e,t)=>e.runtimeLabel.localeCompare(t.runtimeLabel)||e.serviceName.localeCompare(t.serviceName))}}}
1
+ import{formatError as v}from"../kernel/error-serialization.js";import{jsonRedactionTargets as E,orderRedactionTargets as B,sensitiveKeyMatcher as I}from"../kernel/redaction-targets.js";import{redactUrlSecrets as C}from"../process/url-redaction.js";import{redactedRunValue as K}from"./redaction.js";export function publishEnvironmentRecord(a){return{...a,acquisitions:a.acquisitions.map(o=>({...o,settledAt:O(o.settledAt),setups:o.setups.map(p=>({...p,settledAt:O(p.settledAt),startedAt:new Date(p.startedAt).toISOString()})),startedAt:new Date(o.startedAt).toISOString()})),dbBackend:a.groups.some(o=>o.dbBackend==="local")?"local":null}}function O(a){return a===null?null:new Date(a).toISOString()}export function createRunEnvironmentRecord(a={}){const o=[],p=[],h=[],R=new Set,b=I(a.redactKeys);let y=[],m={instanceCount:0,resourceBudgets:[],runtimeStats:[]},k=null;const g=a.now??Date.now,f=()=>{a.onPublishedChange?.()},w=(e,t)=>({releaseFailed(r){e.releaseErrors.push(v(r)),e.failingReleases.add(t),e.releaseReported=!0},released(){e.failingReleases.delete(t),e.releaseReported=!0}});return{redactKeys:a.redactKeys??[],service({runtimeLabel:e,serviceName:t}){let r=null;return{recordAttempt({baseUrl:u,outcome:s,port:n,readyUrl:l}){const c={baseUrl:u===null?null:C(u),readyUrl:l===null?null:C(l)};r===null&&(r={serviceName:t,runtimeLabel:e,...c,port:n,attempts:[]},o.push(r)),Object.assign(r,c,{port:n}),r.attempts.push({port:n,outcome:s}),f()},amendLastOutcome(u){const s=r?.attempts.at(-1);s&&(s.outcome=u,f())}}},acquisition({graphKey:e,leaseId:t,source:r,tasks:u}){const s={source:r,graphKey:e,leaseId:t,runtimeId:null,services:[],status:"pending",startedAt:g(),settledAt:null,error:null,releaseErrors:[],failingReleases:new Set,releaseReported:!1,setups:[],tasks:u.map(l=>({...l})),values:null};p.push(s);const n=({runtimeId:l,services:c})=>{s.runtimeId=l,s.services=c.map(i=>({...i}))};return{...w(s,"source"),assigned:n,acquired(l){n(l),s.status="acquired",s.settledAt=g()},cancelled(l){s.status="cancelled",s.error=v(l),s.settledAt??=g()},failed(l){s.status="failed",s.error=v(l),s.settledAt??=g()},rendered(l){s.values=l,f()},setup({module:l,task:c}){const i={error:null,module:l,releaseErrors:[],releaseReported:!1,settledAt:null,startedAt:g(),status:"pending",task:{...c}};s.setups.push(i);const S=(d,A)=>{i.status=d,i.error=A===null?null:v(A),i.settledAt??=g()};return{prepared(d){S("prepared",null);for(const A of E("setupData",d,b))R.add(A);s.values!==null&&(s.values={...s.values,setupData:d}),f()},failed(d){S("failed",d)},cancelled(d){S("cancelled",d)},releaseFailed(d){i.releaseErrors.push(v(d)),i.releaseReported=!0},released(){i.releaseReported=!0}}}}},releaseFor(e){const t=p.find(r=>r.leaseId===e);return t===void 0?null:w(t,"lease")},recordCapacity({instanceCount:e,resourceBudgets:t}){m={...m,instanceCount:e,resourceBudgets:[...t]},f()},recordRuntimeStats(e){m={...m,runtimeStats:[...e]},f()},recordGroups(e){y=e.map(t=>({...t}))},recordMissingValues(e,t){for(const{kind:r,valueName:u}of t)h.push({kind:r,task:{...e},valueName:u})},recordCleanup(e){k=e==null?{status:"released"}:{status:"failed",errors:[v(e)],recovered:!1}},addRedactionTarget(e){e.length>0&&R.add(e)},redactionTargets(){return[...R]},snapshot(){const e={acquisitions:p.map(({failingReleases:t,releaseErrors:r,releaseReported:u,...s})=>({...s,services:s.services.map(n=>({...n})),setups:s.setups.map(({releaseErrors:n,releaseReported:l,...c})=>({...c,task:{...c.task},release:n.length>0?{status:"failed",errors:[...n],recovered:!1}:l?{status:"released"}:null})),tasks:s.tasks.map(n=>({...n})),values:s.values,release:r.length>0?{status:"failed",errors:[...r],recovered:t.size===0}:u?{status:"released"}:null})),capacity:{instanceCount:m.instanceCount,resourceBudgets:[...m.resourceBudgets],runtimeStats:[...m.runtimeStats]},cleanup:L(k),groups:y.map(t=>({...t})),logs:{services:a.logRegistry?.listServiceLogs()??[],setup:a.logRegistry?.listSetupLogs()??[]},missingValues:h.map(t=>({...t,task:{...t.task}})),services:o.map(t=>({...t,attempts:t.attempts.map(r=>({...r}))})).sort((t,r)=>t.runtimeLabel.localeCompare(r.runtimeLabel)||t.serviceName.localeCompare(r.serviceName)),setupOperations:a.setupRegistry?.listOperations()??[]};return K(e,B(R))}}}function L(a){return a===null||a.status==="released"?a:{status:"failed",errors:[...a.errors],recovered:a.recovered}}
@@ -1 +1 @@
1
- export{normalizeArtifactArchiveConfig}from"./artifact-config.js";export{finalizeLiveRunArtifact,loadTimings,persistTaskArtifacts,persistTaskFileArtifacts,persistTaskOutputArtifacts,resetResultArtifacts,saveTimings,writeLiveRunArtifact,writeRunArtifact}from"./artifacts.js";export{applyRunBundleRetention,listRunBundles,loadRunBundleArtifact,loadRunBundleManifest,loadVerifiedRunBundleArtifactIfExists,publishRunBundle,sealRunBundle,verifyRunBundle}from"./bundles.js";export{collectArtifactEntries,loadCurrentRunArtifact,loadLatestRunArtifact,loadRunArtifact}from"./reading.js";export{DEFAULT_REPORTED_ARCHIVE_CONFIG}from"./reporting-types.js";export{copyRegularFileSecurely,MAX_PERSISTED_FILE_ARTIFACT_BYTES}from"./secure-file-copy.js";export{collectTestkitStorageInventory}from"./storage-inventory.js";export{}from"./timing.js";
1
+ export{normalizeArtifactArchiveConfig}from"./artifact-config.js";export{finalizeLiveRunArtifact,loadTimings,persistTaskArtifacts,persistTaskFileArtifacts,persistTaskOutputArtifacts,resetResultArtifacts,saveTimings,writeLiveRunArtifact,writeRunArtifact}from"./artifacts.js";export{applyRunBundleRetention,listRunBundles,loadRunBundleArtifact,loadRunBundleManifest,loadVerifiedRunBundleArtifactIfExists,publishRunBundle,sealRunBundle,verifyRunBundle}from"./bundles.js";export{collectArtifactEntries,loadCurrentRunArtifact,loadLatestRunArtifact,loadRun,loadRunArtifact}from"./reading.js";export{DEFAULT_REPORTED_ARCHIVE_CONFIG}from"./reporting-types.js";export{runModel}from"./run-model.js";export{copyRegularFileSecurely,MAX_PERSISTED_FILE_ARTIFACT_BYTES}from"./secure-file-copy.js";export{collectTestkitStorageInventory}from"./storage-inventory.js";export{}from"./timing.js";
@@ -0,0 +1 @@
1
+ import i from"node:fs";import s from"node:path";import{readJsonFile as p,resultsRoot as a}from"./storage.js";import{isUnknownRecord as c}from"../kernel/unknown-values.js";import{writeJsonFileAtomicWithRetry as m}from"../process/files/json-file.js";export function runMaintenancePath(e){return s.join(a(e),"maintenance.json")}export function writeRunMaintenance(e,t,r){const d={schemaVersion:1,runId:t,recordedAt:new Date().toISOString(),status:r.status,error:r.error,removed:r.removed,skipped:r.skipped,stopped:r.stopped},o=runMaintenancePath(e);i.mkdirSync(s.dirname(o),{recursive:!0}),m(o,d)}export function readRunMaintenance(e){const t=runMaintenancePath(e);if(!i.existsSync(t))return null;const r=p(t);if(!c(r)||r.schemaVersion!==1||typeof r.runId!="string"||typeof r.recordedAt!="string"||r.status!=="failed"&&r.status!=="passed"||r.error!==null&&typeof r.error!="string"||!n(r.removed)||!n(r.skipped)||!n(r.stopped))throw new Error(`Invalid run maintenance record in ${t}`);return{schemaVersion:1,runId:r.runId,recordedAt:r.recordedAt,status:r.status,error:r.error,removed:r.removed,skipped:r.skipped,stopped:r.stopped}}function n(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}
@@ -1 +1 @@
1
- import{isEngineId as C,isPublicTestType as S,isRunnerProfileId as q}from"../domain/identifiers.js";import{isEngineProfileIdentity as T}from"../engines/definitions.js";import{isUnknownRecord as E}from"../kernel/unknown-values.js";import{requireObjectRecord as s,requireNumber as p,requireStringArray as B,optionalBoolean as h,optionalNonNegativeNumber as m,requireUnknownArray as v,requireString as d,requireResultsReferencePath as k,optionalStringValue as o,optionalNullableStringValue as f,optionalNumberOrStringValue as L,optionalNullableNumberValue as R}from"./read-fields.js";export function readRunDocument(t,n){const e=s(t,n);if(e.schemaVersion!==19)throw new Error(`${n}.schemaVersion must be 19`);const i=M(e.run,`${n}.run`),r=a(e.services,`${n}.services`,D)??[];e.archive!==void 0&&s(e.archive,`${n}.archive`),e.finalizationFailure!==void 0&&Z(e.finalizationFailure,`${n}.finalizationFailure`),o(e.generatedAt,`${n}.generatedAt`);const u=W(e.summary,`${n}.summary`),c=e.environment===void 0?void 0:x(e.environment,`${n}.environment`),y=e.logs===void 0?void 0:V(e.logs,`${n}.logs`),$=e.setup===void 0?void 0:P(e.setup,`${n}.setup`);return{...e,schemaVersion:19,...u===void 0?{}:{summary:u},run:i,services:r,...c===void 0?{}:{environment:c},...y===void 0?{}:{logs:y},...$===void 0?{}:{setup:$}}}function M(t,n){const e=s(t,n);return R(e.durationMs,`${n}.durationMs`),a(e.types,`${n}.types`,d),{...e,finishedAt:f(e.finishedAt,`${n}.finishedAt`)??null,id:f(e.id,`${n}.id`)??null,startedAt:d(e.startedAt,`${n}.startedAt`),status:d(e.status,`${n}.status`)}}function x(t,n){const e=s(t,n),i=a(e.services,`${n}.services`,U);return{...e,...i===void 0?{}:{services:i}}}function U(t,n){const e=s(t,n);return{...e,attempts:a(e.attempts,`${n}.attempts`,(i,r)=>{const u=s(i,r);return{...u,outcome:d(u.outcome,`${r}.outcome`),port:p(u.port,`${r}.port`)}})??[],baseUrl:d(e.baseUrl,`${n}.baseUrl`),port:p(e.port,`${n}.port`),readyUrl:f(e.readyUrl,`${n}.readyUrl`)??null,runtimeLabel:d(e.runtimeLabel,`${n}.runtimeLabel`),serviceName:d(e.serviceName,`${n}.serviceName`)}}function V(t,n){const e=s(t,n),i=a(e.services,`${n}.services`,K),r=a(e.setup,`${n}.setup`,F);return{...e,...i===void 0?{}:{services:i},...r===void 0?{}:{setup:r}}}function P(t,n){const e=s(t,n),i=a(e.operations,`${n}.operations`,Q);return{...e,...i===void 0?{}:{operations:i}}}function D(t,n){const e=s(t,n),i=a(e.suites,`${n}.suites`,O)??[];return{...e,name:d(e.name,`${n}.name`),suites:i}}function O(t,n){const e=s(t,n);if(e.testType!=="scenario"&&!S(e.testType))throw new Error(`${n}.testType must be a public test type`);if(!C(e.engineId))throw new Error(`${n}.engineId must be a supported engine id`);if(!q(e.profileId))throw new Error(`${n}.profileId must be a supported runner profile id`);if(!(e.testType==="scenario"?e.engineId==="k6"&&e.profileId==="k6-http":T(e)))throw new Error(`${n}.profileId does not match ${e.engineId}/${e.testType}`);const r=a(e.files,`${n}.files`,z);return h(e.failed,`${n}.failed`),N(e.diagnostics,`${n}.diagnostics`),m(e.completedFileCount,`${n}.completedFileCount`),m(e.failedFileCount,`${n}.failedFileCount`),m(e.fileCount,`${n}.fileCount`),m(e.passedFileCount,`${n}.passedFileCount`),m(e.skippedFileCount,`${n}.skippedFileCount`),{...e,files:r,name:d(e.name,`${n}.name`),testType:e.testType,engineId:e.engineId,profileId:e.profileId}}function z(t,n){const e=s(t,n),i=a(e.artifacts,`${n}.artifacts`,j),r=a(e.failureDetails,`${n}.failureDetails`,G);return h(e.blockedByRuntime,`${n}.blockedByRuntime`),h(e.failed,`${n}.failed`),h(e.infraFailed,`${n}.infraFailed`),f(e.reason,`${n}.reason`),f(e.runtimeIsolation,`${n}.runtimeIsolation`),e.runtimeBatch!=null&&X(e.runtimeBatch,`${n}.runtimeBatch`),a(e.runtimeResets,`${n}.runtimeResets`,Y),{...e,error:f(e.error,`${n}.error`),artifacts:i,durationMs:R(e.durationMs,`${n}.durationMs`),failureDetails:r,path:d(e.path,`${n}.path`),status:d(e.status,`${n}.status`)}}function j(t,n){const e=s(t,n);return{...e,contentType:f(e.contentType,`${n}.contentType`),kind:f(e.kind,`${n}.kind`),summary:f(e.summary,`${n}.summary`),name:d(e.name,`${n}.name`),path:k(e.path,`${n}.path`)}}function G(t,n){const e=s(t,n),i=e.request===void 0?void 0:H(e.request,`${n}.request`),r=e.response===void 0?void 0:J(e.response,`${n}.response`),u=e.location===void 0?void 0:s(e.location,`${n}.location`);return u!==void 0&&(o(u.path,`${n}.location.path`),u.line!==void 0&&p(u.line,`${n}.location.line`),u.column!==void 0&&p(u.column,`${n}.location.column`)),{...e,key:o(e.key,`${n}.key`),kind:o(e.kind,`${n}.kind`),message:o(e.message,`${n}.message`),stack:o(e.stack,`${n}.stack`),location:u,request:i,response:r,title:d(e.title,`${n}.title`)}}function H(t,n){const e=s(t,n);return{...e,method:o(e.method,`${n}.method`),path:o(e.path,`${n}.path`),requestId:o(e.requestId,`${n}.requestId`)}}function J(t,n){const e=s(t,n);return{...e,bodyPreview:o(e.bodyPreview,`${n}.bodyPreview`),contentType:o(e.contentType,`${n}.contentType`),status:L(e.status,`${n}.status`)}}function K(t,n){const e=s(t,n);return{...e,path:k(e.path,`${n}.path`),runtimeLabel:d(e.runtimeLabel,`${n}.runtimeLabel`),serviceName:d(e.serviceName,`${n}.serviceName`)}}function Q(t,n){const e=s(t,n),i=e.logRef==null?e.logRef:F(e.logRef,`${n}.logRef`);return{...e,durationMs:R(e.durationMs,`${n}.durationMs`),error:f(e.error,`${n}.error`),summary:f(e.summary,`${n}.summary`),logRef:i,serviceName:d(e.serviceName,`${n}.serviceName`),stage:d(e.stage,`${n}.stage`),startedAt:o(e.startedAt,`${n}.startedAt`),status:d(e.status,`${n}.status`)}}function F(t,n){const e=s(t,n);return o(e.runtimeLabel,`${n}.runtimeLabel`),o(e.serviceName,`${n}.serviceName`),o(e.stage,`${n}.stage`),{...e,path:k(e.path,`${n}.path`)}}function W(t,n){if(t===void 0)return;const e=s(t,n),i=e.files===void 0?{}:s(e.files,`${n}.files`),r=e.services===void 0?{}:s(e.services,`${n}.services`),u=e.suites===void 0?{}:s(e.suites,`${n}.suites`),c=(y,$,A)=>{const g=y[$];if(g===void 0)return 0;if(typeof g!="number"||!Number.isFinite(g)||g<0)throw new Error(`${n}.${A}.${$} must be a non-negative finite number`);return g};return N(e.diagnostics,`${n}.diagnostics`),{...e,files:{blockedByRuntime:c(i,"blockedByRuntime","files"),cancelled:c(i,"cancelled","files"),failed:c(i,"failed","files"),infraFailed:c(i,"infraFailed","files"),interrupted:c(i,"interrupted","files"),notRun:c(i,"notRun","files"),passed:c(i,"passed","files"),skipped:c(i,"skipped","files"),total:c(i,"total","files")},services:{blockedByRuntime:c(r,"blockedByRuntime","services"),failed:c(r,"failed","services"),infraFailed:c(r,"infraFailed","services"),passed:c(r,"passed","services"),skipped:c(r,"skipped","services"),total:c(r,"total","services")},suites:{completed:c(u,"completed","suites"),failed:c(u,"failed","suites"),passed:c(u,"passed","suites"),skipped:c(u,"skipped","suites"),total:c(u,"total","suites")}}}function N(t,n){if(t!==void 0)return v(t,n).map((e,i)=>{const r=`${n}[${String(i)}]`,u=s(e,r);return{...u,code:d(u.code,`${r}.code`),message:d(u.message,`${r}.message`),service:o(u.service,`${r}.service`),suite:o(u.suite,`${r}.suite`)}})}function X(t,n){const e=s(t,n);return{...e,id:o(e.id,`${n}.id`),fileCount:m(e.fileCount,`${n}.fileCount`),index:m(e.index,`${n}.index`),files:e.files===void 0?void 0:B(e.files,`${n}.files`)}}function Y(t,n){const e=s(t,n);return{...e,position:o(e.position,`${n}.position`),reason:o(e.reason,`${n}.reason`),runtimeIsolation:o(e.runtimeIsolation,`${n}.runtimeIsolation`),trigger:o(e.trigger,`${n}.trigger`)}}function Z(t,n){const e=s(t,n),i=e.operation===void 0?void 0:s(e.operation,`${n}.operation`);return{...e,name:d(e.name,`${n}.name`),message:d(e.message,`${n}.message`),code:o(e.code,`${n}.code`),stack:o(e.stack,`${n}.stack`),phase:o(e.phase,`${n}.phase`),exitCode:e.exitCode===void 0?void 0:p(e.exitCode,`${n}.exitCode`),status:e.status===void 0?void 0:p(e.status,`${n}.status`),diagnostics:e.diagnostics===void 0?void 0:I(e.diagnostics,`${n}.diagnostics`),operation:i===void 0?void 0:{...i,id:o(i.id,`${n}.operation.id`),source:d(i.source,`${n}.operation.source`),name:d(i.name,`${n}.operation.name`),kind:d(i.kind,`${n}.operation.kind`),summary:o(i.summary,`${n}.operation.summary`),status:o(i.status,`${n}.operation.status`),error:o(i.error,`${n}.operation.error`),scope:I(i.scope,`${n}.operation.scope`)}}}function I(t,n){return Object.fromEntries(Object.entries(s(t,n)).map(([e,i])=>[e,w(i,`${n}.${e}`)]))}function w(t,n){return t===null||typeof t=="string"||typeof t=="boolean"?t:Array.isArray(t)?v(t,n).map((e,i)=>w(e,`${n}[${String(i)}]`)):E(t)?I(t,n):p(t,n)}function a(t,n,e){return t===void 0?void 0:v(t,n).map((i,r)=>e(i,`${n}[${String(r)}]`))}
1
+ import{isEngineId as M,isPublicTestType as O,isRunnerProfileId as U}from"../domain/identifiers.js";import{isEngineProfileIdentity as P}from"../engines/definitions.js";import{isUnknownRecord as z}from"../kernel/unknown-values.js";import{requireObjectRecord as u,requireNumber as g,requireStringArray as j,optionalBoolean as I,optionalNonNegativeNumber as y,requireUnknownArray as k,requireString as c,requireResultsReferencePath as R,optionalStringValue as o,optionalNullableStringValue as m,optionalNumberOrStringValue as _,optionalNullableNumberValue as F}from"./read-fields.js";export function readRunDocument(t,e){const n=u(t,e);if(n.schemaVersion===19)return H(n,e);if(n.schemaVersion===20)return G(n,e);throw new Error(`${e}.schemaVersion must be 19 or 20`)}function G(t,e){const n=w(t.run,`${e}.run`),i=a(t.groups,`${e}.groups`,(d,p)=>C(d,p,!0))??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"group"),r=t.environment===void 0?void 0:J(t.environment,`${e}.environment`);return{...t,schemaVersion:20,...s===void 0?{}:{summary:{...s.record,files:s.files,groups:s.owners,suites:s.suites}},run:n,groups:i,...r===void 0?{}:{environment:r}}}function H(t,e){const n=w(t.run,`${e}.run`),i=a(t.services,`${e}.services`,C)??[];N(t,e);const s=t.summary===void 0?void 0:D(t.summary,`${e}.summary`,"service"),r=t.environment===void 0?void 0:K(t.environment,`${e}.environment`),d=t.logs===void 0?void 0:E(t.logs,`${e}.logs`),p=t.setup===void 0?void 0:Q(t.setup,`${e}.setup`);return{...t,schemaVersion:19,...s===void 0?{}:{summary:{...s.record,files:s.files,services:s.owners,suites:s.suites}},run:n,services:i,...r===void 0?{}:{environment:r},...d===void 0?{}:{logs:d},...p===void 0?{}:{setup:p}}}function w(t,e){const n=u(t,e);return F(n.durationMs,`${e}.durationMs`),a(n.types,`${e}.types`,c),{...n,finishedAt:m(n.finishedAt,`${e}.finishedAt`)??null,id:m(n.id,`${e}.id`)??null,startedAt:c(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function N(t,e){t.archive!==void 0&&u(t.archive,`${e}.archive`),t.finalizationFailure!==void 0&&ie(t.finalizationFailure,`${e}.finalizationFailure`),o(t.generatedAt,`${e}.generatedAt`)}function J(t,e){const n=u(t,e),i=n.capacity===void 0?void 0:u(n.capacity,`${e}.capacity`),s=i?.runtimeStats===void 0?void 0:k(i.runtimeStats,`${e}.capacity.runtimeStats`),r=n.logs===void 0?void 0:E(n.logs,`${e}.logs`),d=a(n.services,`${e}.services`,A),p=a(n.setupOperations,`${e}.setupOperations`,q);return{...n,...i===void 0?{}:{capacity:{...i,...s===void 0?{}:{runtimeStats:s}}},...r===void 0?{}:{logs:r},...d===void 0?{}:{services:d},...p===void 0?{}:{setupOperations:p}}}function K(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,A);return{...n,...i===void 0?{}:{services:i}}}function A(t,e){const n=u(t,e);return{...n,attempts:a(n.attempts,`${e}.attempts`,(i,s)=>{const r=u(i,s);return{...r,outcome:c(r.outcome,`${s}.outcome`),port:g(r.port,`${s}.port`)}})??[],baseUrl:m(n.baseUrl,`${e}.baseUrl`)??null,port:g(n.port,`${e}.port`),readyUrl:m(n.readyUrl,`${e}.readyUrl`)??null,runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function E(t,e){const n=u(t,e),i=a(n.services,`${e}.services`,ee),s=a(n.setup,`${e}.setup`,B);return{...n,...i===void 0?{}:{services:i},...s===void 0?{}:{setup:s}}}function Q(t,e){const n=u(t,e),i=a(n.operations,`${e}.operations`,q);return{...n,...i===void 0?{}:{operations:i}}}function C(t,e,n=!1){const i=u(t,e),s=a(i.suites,`${e}.suites`,(r,d)=>W(r,d,n))??[];return{...i,name:c(i.name,`${e}.name`),suites:s}}export const RETIRED_SUITE_IDENTITIES=Object.freeze([...["int","e2e","load","scenario"].map(t=>({engineId:"k6",profileId:"k6-http",testType:t})),{engineId:"k6",profileId:"k6-dal",testType:"dal"},{engineId:"node-vitest",profileId:"node-vitest-contract",testType:"contract"},{engineId:"node-vitest",profileId:"node-vitest-data",testType:"data"}]);function W(t,e,n){const i=u(t,e),s=RETIRED_SUITE_IDENTITIES.find($=>$.testType===i.testType),r=s?.testType??(O(i.testType)?i.testType:void 0);if(r===void 0&&!(n&&i.testType===void 0))throw new Error(`${e}.testType must be a public test type`);const d=s!==void 0&&i.engineId===s.engineId?s.engineId:M(i.engineId)?i.engineId:void 0;if(d===void 0)throw new Error(`${e}.engineId must be a supported engine id`);const p=s!==void 0&&i.profileId===s.profileId?s.profileId:U(i.profileId)?i.profileId:void 0;if(p===void 0)throw new Error(`${e}.profileId must be a supported runner profile id`);if(!(s===void 0?P(i):d===s.engineId&&p===s.profileId))throw new Error(`${e}.profileId does not match ${d}/${r??"untyped"}`);const f=a(i.files,`${e}.files`,X);return I(i.failed,`${e}.failed`),L(i.diagnostics,`${e}.diagnostics`),y(i.completedFileCount,`${e}.completedFileCount`),y(i.failedFileCount,`${e}.failedFileCount`),y(i.fileCount,`${e}.fileCount`),y(i.passedFileCount,`${e}.passedFileCount`),y(i.skippedFileCount,`${e}.skippedFileCount`),{...i,files:f,name:c(i.name,`${e}.name`),...r===void 0?{}:{testType:r},engineId:d,profileId:p}}function X(t,e){const n=u(t,e),i=a(n.artifacts,`${e}.artifacts`,Y),s=a(n.failureDetails,`${e}.failureDetails`,Z);return I(n.blockedByRuntime,`${e}.blockedByRuntime`),I(n.failed,`${e}.failed`),I(n.infraFailed,`${e}.infraFailed`),m(n.reason,`${e}.reason`),m(n.runtimeIsolation,`${e}.runtimeIsolation`),n.runtimeBatch!=null&&ne(n.runtimeBatch,`${e}.runtimeBatch`),a(n.runtimeResets,`${e}.runtimeResets`,te),{...n,error:m(n.error,`${e}.error`),artifacts:i,durationMs:F(n.durationMs,`${e}.durationMs`),failureDetails:s,path:c(n.path,`${e}.path`),status:c(n.status,`${e}.status`)}}function Y(t,e){const n=u(t,e);return{...n,contentType:m(n.contentType,`${e}.contentType`),kind:m(n.kind,`${e}.kind`),summary:m(n.summary,`${e}.summary`),name:c(n.name,`${e}.name`),path:R(n.path,`${e}.path`)}}function Z(t,e){const n=u(t,e),i=n.request===void 0?void 0:l(n.request,`${e}.request`),s=n.response===void 0?void 0:b(n.response,`${e}.response`),r=n.location===void 0?void 0:u(n.location,`${e}.location`);return r!==void 0&&(o(r.path,`${e}.location.path`),r.line!==void 0&&g(r.line,`${e}.location.line`),r.column!==void 0&&g(r.column,`${e}.location.column`)),{...n,key:o(n.key,`${e}.key`),kind:o(n.kind,`${e}.kind`),message:o(n.message,`${e}.message`),stack:o(n.stack,`${e}.stack`),location:r,request:i,response:s,title:c(n.title,`${e}.title`)}}function l(t,e){const n=u(t,e);return{...n,method:o(n.method,`${e}.method`),path:o(n.path,`${e}.path`),requestId:o(n.requestId,`${e}.requestId`)}}function b(t,e){const n=u(t,e);return{...n,bodyPreview:o(n.bodyPreview,`${e}.bodyPreview`),contentType:o(n.contentType,`${e}.contentType`),status:_(n.status,`${e}.status`)}}function ee(t,e){const n=u(t,e);return{...n,path:R(n.path,`${e}.path`),runtimeLabel:c(n.runtimeLabel,`${e}.runtimeLabel`),serviceName:c(n.serviceName,`${e}.serviceName`)}}function q(t,e){const n=u(t,e),i=n.logRef==null?n.logRef:B(n.logRef,`${e}.logRef`);return{...n,durationMs:F(n.durationMs,`${e}.durationMs`),error:m(n.error,`${e}.error`),summary:m(n.summary,`${e}.summary`),logRef:i,serviceName:c(n.serviceName,`${e}.serviceName`),stage:c(n.stage,`${e}.stage`),startedAt:o(n.startedAt,`${e}.startedAt`),status:c(n.status,`${e}.status`)}}function B(t,e){const n=u(t,e);return o(n.runtimeLabel,`${e}.runtimeLabel`),o(n.serviceName,`${e}.serviceName`),o(n.stage,`${e}.stage`),{...n,path:R(n.path,`${e}.path`)}}function D(t,e,n){const i=u(t,e),s=n==="group"?"groups":"services",r=$=>i[$]===void 0?{}:u(i[$],`${e}.${$}`),[d,p,h]=[r("files"),r(s),r("suites")],f=($,T,x)=>{const v=$[T];if(v===void 0)return 0;if(typeof v!="number"||!Number.isFinite(v)||v<0)throw new Error(`${e}.${x}.${T} must be a non-negative finite number`);return v};return L(i.diagnostics,`${e}.diagnostics`),{record:i,files:{blockedByRuntime:f(d,"blockedByRuntime","files"),cancelled:f(d,"cancelled","files"),failed:f(d,"failed","files"),infraFailed:f(d,"infraFailed","files"),interrupted:f(d,"interrupted","files"),notRun:f(d,"notRun","files"),passed:f(d,"passed","files"),skipped:f(d,"skipped","files"),total:f(d,"total","files")},owners:{blockedByRuntime:f(p,"blockedByRuntime",s),failed:f(p,"failed",s),infraFailed:f(p,"infraFailed",s),passed:f(p,"passed",s),skipped:f(p,"skipped",s),total:f(p,"total",s)},suites:{completed:f(h,"completed","suites"),failed:f(h,"failed","suites"),passed:f(h,"passed","suites"),skipped:f(h,"skipped","suites"),total:f(h,"total","suites")}}}function L(t,e){if(t!==void 0)return k(t,e).map((n,i)=>{const s=`${e}[${String(i)}]`,r=u(n,s);return{...r,code:c(r.code,`${s}.code`),group:o(r.group,`${s}.group`),message:c(r.message,`${s}.message`),service:o(r.service,`${s}.service`),suite:o(r.suite,`${s}.suite`)}})}function ne(t,e){const n=u(t,e);return{...n,id:o(n.id,`${e}.id`),fileCount:y(n.fileCount,`${e}.fileCount`),index:y(n.index,`${e}.index`),files:n.files===void 0?void 0:j(n.files,`${e}.files`)}}function te(t,e){const n=u(t,e);return{...n,position:o(n.position,`${e}.position`),reason:o(n.reason,`${e}.reason`),runtimeIsolation:o(n.runtimeIsolation,`${e}.runtimeIsolation`),trigger:o(n.trigger,`${e}.trigger`)}}function ie(t,e){const n=u(t,e),i=n.operation===void 0?void 0:u(n.operation,`${e}.operation`);return{...n,name:c(n.name,`${e}.name`),message:c(n.message,`${e}.message`),code:o(n.code,`${e}.code`),stack:o(n.stack,`${e}.stack`),phase:o(n.phase,`${e}.phase`),exitCode:n.exitCode===void 0?void 0:g(n.exitCode,`${e}.exitCode`),status:n.status===void 0?void 0:g(n.status,`${e}.status`),diagnostics:n.diagnostics===void 0?void 0:S(n.diagnostics,`${e}.diagnostics`),operation:i===void 0?void 0:{...i,id:o(i.id,`${e}.operation.id`),source:c(i.source,`${e}.operation.source`),name:c(i.name,`${e}.operation.name`),kind:c(i.kind,`${e}.operation.kind`),summary:o(i.summary,`${e}.operation.summary`),status:o(i.status,`${e}.operation.status`),error:o(i.error,`${e}.operation.error`),scope:S(i.scope,`${e}.operation.scope`)}}}function S(t,e){return Object.fromEntries(Object.entries(u(t,e)).map(([n,i])=>[n,V(i,`${e}.${n}`)]))}function V(t,e){return t===null||typeof t=="string"||typeof t=="boolean"?t:Array.isArray(t)?k(t,e).map((n,i)=>V(n,`${e}[${String(i)}]`)):z(t)?S(t,e):g(t,e)}function a(t,e,n){return t===void 0?void 0:k(t,e).map((i,s)=>n(i,`${e}[${String(s)}]`))}
@@ -1 +1 @@
1
- import{isWorkObserved as k,recordWork as h}from"../kernel/work-metrics.js";import a from"fs";import{posixPath as S}from"../process/files/paths.js";import l from"path";import{normalizeEngineJsonValue as g}from"../engines/api/index.js";import{readJsonFile as p}from"./storage.js";import{loadRunBundleArtifact as P}from"./bundles.js";import{readRunDocument as y}from"./read-run.js";import{requireObjectRecord as R,requireResultsReferencePath as A,optionalStringValue as x,optionalNullableStringValue as f}from"./read-fields.js";export function loadLatestRunArtifact(r){const t=l.join(r,".testkit","results","latest.json");if(!a.existsSync(t))throw new Error(`No run artifact found at ${l.relative(r,t)}`);return y(p(t),t)}export function loadCurrentRunArtifact(r){const t=l.join(r,".testkit","results","live.json");return a.existsSync(t)?y(p(t),t):loadLatestRunArtifact(r)}export function loadRunArtifact(r,t=null){return t?P(r,t):loadLatestRunArtifact(r)}export function resolveFileSubject(r,t=null,e=null){const n=w(r,e);if(n.length===0)throw new Error("No file results were found in the latest run artifact.");if(!t)return n.find(s=>s.file.status==="failed")||n[0];const o=S(t),i=n.filter(s=>s.file.path===o);if(i.length===1)return i[0];if(i.length>1)throw new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`);const u=n.filter(s=>s.file.path.endsWith(`/${o}`));if(u.length===1)return u[0];throw u.length>1?new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`):new Error(`No file matched "${t}".`)}export function collectArtifactEntries(r,t,e=null,n=null,o,i=!0){const u=e?[resolveFileSubject(t,e,n)]:w(t,n),s=[];for(const d of u)for(const c of d.file.artifacts||[]){if(o&&c.kind!==o)continue;const m=T(r,c.path,`artifact "${c.name}" path`),$=i&&a.existsSync(m)?E(m,c):null;s.push({...d,artifactRef:c,absolutePath:m,payload:$})}return s}function E(r,t){let e;if(k()){const o=a.readFileSync(r);h("artifacts.payloadFilesRead",1),h("artifacts.payloadBytesRead",o.byteLength),e=o.toString("utf8")}else e=a.readFileSync(r,"utf8");const n=t.contentType||"";if(n==="text/markdown"||n==="text/plain")return{contentType:n,kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:e};try{const o=JSON.parse(e);return j(o,r)}catch{return{contentType:n||"text/plain",kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:e}}}function w(r,t=null){const e=[];for(const n of r.services)if(!(t&&n.name!==t))for(const o of n.suites)for(const i of o.files||[])e.push({service:n,suite:o,file:i});return e}function j(r,t){const e=R(r,t),n=x(e.emittedAt,`${t}.emittedAt`);return{...e,contentType:f(e.contentType,`${t}.contentType`),...e.data===void 0?{}:{data:g(e.data,`${t}.data`)},...n===void 0?{}:{emittedAt:n},kind:f(e.kind,`${t}.kind`),name:f(e.name,`${t}.name`),summary:f(e.summary,`${t}.summary`),text:x(e.text,`${t}.text`)}}function T(r,t,e){const n=A(t,e),o=l.resolve(r,".testkit","results"),i=l.resolve(r,n);if(v(o,i,e),a.existsSync(i)){const u=a.realpathSync(o),s=a.realpathSync(i);v(u,s,e)}return i}function v(r,t,e){const n=l.relative(r,t);if(n.length===0||n===".."||n.startsWith(`..${l.sep}`)||l.isAbsolute(n))throw new Error(`${e} must resolve within .testkit/results`)}
1
+ import{isWorkObserved as S,recordWork as p}from"../kernel/work-metrics.js";import a from"fs";import{posixPath as g}from"../process/files/paths.js";import u from"path";import{normalizeEngineJsonValue as R}from"../engines/api/index.js";import{readJsonFile as h}from"./storage.js";import{loadRunBundleArtifact as A}from"./bundles.js";import{readRunDocument as y}from"./read-run.js";import{runModel as x}from"./run-model.js";import{requireObjectRecord as P,requireResultsReferencePath as E,optionalStringValue as w,optionalNullableStringValue as f}from"./read-fields.js";export function loadLatestRunArtifact(e){const t=u.join(e,".testkit","results","latest.json");if(!a.existsSync(t))throw new Error(`No run artifact found at ${u.relative(e,t)}`);return y(h(t),t)}export function loadLatestRun(e){return x(loadLatestRunArtifact(e))}export function loadCurrentRunArtifact(e){const t=u.join(e,".testkit","results","live.json");return a.existsSync(t)?y(h(t),t):loadLatestRunArtifact(e)}export function loadRunArtifact(e,t=null){return t?A(e,t):loadLatestRunArtifact(e)}export function loadRun(e,t=null){return x(loadRunArtifact(e,t))}export function resolveFileSubject(e,t=null,n=null){const r=v(e,n);if(r.length===0)throw new Error("No file results were found in the latest run artifact.");if(!t)return r.find(s=>s.file.status==="failed")||r[0];const o=g(t),i=r.filter(s=>s.file.path===o);if(i.length===1)return i[0];if(i.length>1)throw new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`);const l=r.filter(s=>s.file.path.endsWith(`/${o}`));if(l.length===1)return l[0];throw l.length>1?new Error(`Multiple files matched "${t}". Re-run with --service to disambiguate.`):new Error(`No file matched "${t}".`)}export function collectArtifactEntries(e,t,n=null,r=null,o,i=!0){const l=n?[resolveFileSubject(t,n,r)]:v(t,r),s=[];for(const d of l)for(const c of d.file.artifacts||[]){if(o&&c.kind!==o)continue;const m=N(e,c.path,`artifact "${c.name}" path`),k=i&&a.existsSync(m)?j(m,c):null;s.push({...d,artifactRef:c,absolutePath:m,payload:k})}return s}function j(e,t){let n;if(S()){const o=a.readFileSync(e);p("artifacts.payloadFilesRead",1),p("artifacts.payloadBytesRead",o.byteLength),n=o.toString("utf8")}else n=a.readFileSync(e,"utf8");const r=t.contentType||"";if(r==="text/markdown"||r==="text/plain")return{contentType:r,kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:n};try{const o=JSON.parse(n);return T(o,e)}catch{return{contentType:r||"text/plain",kind:t.kind||null,name:t.name||null,summary:t.summary||null,text:n}}}function v(e,t=null){const n=[];for(const r of e.groups)if(!(t&&r.name!==t))for(const o of r.suites)for(const i of o.files)n.push({group:r,suite:o,file:i});return n}function T(e,t){const n=P(e,t),r=w(n.emittedAt,`${t}.emittedAt`);return{...n,contentType:f(n.contentType,`${t}.contentType`),...n.data===void 0?{}:{data:R(n.data,`${t}.data`)},...r===void 0?{}:{emittedAt:r},kind:f(n.kind,`${t}.kind`),name:f(n.name,`${t}.name`),summary:f(n.summary,`${t}.summary`),text:w(n.text,`${t}.text`)}}function N(e,t,n){const r=E(t,n),o=u.resolve(e,".testkit","results"),i=u.resolve(e,r);if($(o,i,n),a.existsSync(i)){const l=a.realpathSync(o),s=a.realpathSync(i);$(l,s,n)}return i}function $(e,t,n){const r=u.relative(e,t);if(r.length===0||r===".."||r.startsWith(`..${u.sep}`)||u.isAbsolute(r))throw new Error(`${n} must resolve within .testkit/results`)}
@@ -0,0 +1,4 @@
1
+ import{REDACTED as f,redactSensitiveText as m,SENSITIVE_KEY_PATTERN as E,sensitiveTextRanges as R}from"../kernel/redaction.js";import{isRedactionTarget as A,MIN_REDACTION_TARGET_LENGTH as N,orderRedactionTargets as _,redactionTargetSpans as S,replaceRedactionTargets as T,sensitiveKeyMatcher as I,urlRedactionTargets as L}from"../kernel/redaction-targets.js";import{isUnknownRecord as y}from"../kernel/unknown-values.js";import{recordWork as J}from"../kernel/work-metrics.js";const K=8,k="\uE010",P=new Set(["data","details","dimensions","metadata","native"]);export function runRedaction({env:e=process.env,redactKeys:n,redactionTargets:t}={}){const s=I(n),c=Object.entries(e).filter(r=>s(r[0])&&typeof r[1]=="string"&&A(r[1]));return{keys:c.map(([r])=>r).sort(),status:t===void 0?"source-targets-unavailable":"applied",targets:_([...c.flatMap(([,r])=>[r,...L(r)]),...t??[]])}}export function redactRunText(e,n){return redactJsonText(e,n)??W(e,n)}function W(e,n){const t=D(S(e,n)),s=[];let c=0,r=0;for(let a=0;a<e.length;){const l=e.indexOf(`
2
+ `,a),d=l===-1?e.length:l,p=e[d-1]==="\r"?d-1:d,o=C(e,a,p,n,t);if(o!==null){const u=b(e.slice(r,o.start),n);s.push(u.text,o.text),c+=u.replacements+o.replacements,r=p}a=d+1}const i=b(e.slice(r),n);return s.push(i.text),{replacements:c+i.replacements,text:s.join("")}}function C(e,n,t,s,c){const r=e.slice(n,t).search(/[[{]/u);if(r<0)return null;const i=n+r;if(c(i,t))return null;const a=e.slice(n,i),l=e.slice(i,t);if(m(a+l)!==m(a)+m(l))return null;const d=redactJsonText(l,s);return d===null?null:{...d,start:i}}function D(e){const n=[...e].sort((c,r)=>c.start-r.start);let t=0,s=-1;return(c,r)=>{let i=0;for(;t<n.length&&n[t].start<c;t+=1)s=Math.max(s,n[t].end),i+=1;let a=s>c;for(let l=t;!a&&l<n.length&&n[l].start<r;l+=1)a=n[l].end>r,i+=1;return J("redaction.spanEdgeSteps",i),a}}function b(e,n){let t=e,s=0;for(let c=0;c<K;c+=1){const r=G(t,n);if(r.text===t)return{replacements:s,text:t};s+=Math.max(r.replacements,1),t=r.text}return{replacements:s+1,text:f}}function G(e,n){const t=S(e,n),s=$(e,n);if(t.length===0&&s.length===0){const r=m(e);return{replacements:r===e?0:1,text:r}}const c=T(e,n,r=>k.repeat(r.length)).text;return F(e,[...R(e),...R(c),...t,...s])}const w=new WeakMap;function j(e){let n=w.get(e);return n===void 0&&(n=e.filter(t=>t.includes(k)||V(t)),w.set(e,n)),n}function V(e){if(e.includes(f)||f.includes(e))return!0;for(let n=1;n<f.length;n+=1)if(e.startsWith(f.slice(-n))||e.endsWith(f.slice(0,n)))return!0;return!1}function $(e,n){const t=j(n);if(t.length===0)return[];if(t.some(c=>/[\r\n]/u.test(c)))return g(e,n)?[O(e)]:[];const s=[];for(let c=0;c<=e.length;){const r=e.indexOf(`
3
+ `,c),i=r===-1?e.length:r,a=e[i-1]==="\r"?i-1:i;g(e.slice(c,a),n)&&s.push({end:a,start:c}),c=i+1}return s.length===0&&g(e,n)?[O(e)]:s}function O(e){const n=e.search(/[^\r\n]/u);let t=e.length;for(;t>n&&(e[t-1]===`
4
+ `||e[t-1]==="\r");)t-=1;return n<0?{end:0,start:0}:{end:t,start:n}}function g(e,n){const t=j(n);if(t.length===0||e.length===0)return!1;let s=!1;const c=r=>i=>(t.some(a=>a.startsWith(i))&&(s=!0),r(i));return T(e,n,c(()=>f)),T(m(e),n,c(()=>f)),T(e,n,c(r=>k.repeat(r.length))),s}function F(e,n){const t=[...n].sort((i,a)=>i.start-a.start||a.end-i.end),s=[];let c=0,r=0;for(let i=0;i<t.length;){const a=t[i].start;let l=t[i].end;for(i+=1;i<t.length&&t[i].start<=l;i+=1)l=Math.max(l,t[i].end);s.push(e.slice(c,a),f),e.slice(a,l)!==f&&(r+=1),c=l}return s.push(e.slice(c)),{replacements:r,text:s.join("")}}export function redactRunValue(e,n){let t=0;function s(l){const d=redactRunText(l,n);return t+=d.replacements,d.text}function c(l,d){if(Array.isArray(l))return l.map(p=>c(p,d));if(y(l)){const p=Object.entries(l),o=d?i(p.map(([u])=>u)):p.map(([u])=>u);return Object.fromEntries(p.map(([u,h],v)=>[o[v],d&&typeof h=="string"&&E.test(u)?r(h):c(h,d||P.has(u))]))}return typeof l=="string"?s(l):l}function r(l){return l!==f&&(t+=1),f}function i(l){const d=l.map(o=>redactRunText(o,n)),p=new Set(l.filter((o,u)=>d[u].text===o));return l.map((o,u)=>d[u].text===o?o:(t+=d[u].replacements,M(p,d[u].text)))}const a=c(e,!1);return{replacements:t,value:a}}export function redactedRunValue(e,n){return redactRunValue(e,n).value}export function redactJsonText(e,n){if(!/^\s*[[{"]/u.test(e))return null;try{JSON.parse(e)}catch{return null}const t=Y(e),s=new Set;for(const o of S(e,n)){const u=U(t,o.start,o.end);if(u===void 0)return null;H(e,o,n)||s.add(u)}const c=o=>s.has(o)||g(o.decoded,n)?{replacements:o.decoded===f?0:1,text:f}:redactRunText(o.decoded,n),r=new Map(t.filter(o=>o.key).map(o=>[o,c(o)])),i=new Map;for(const[o,u]of r)o.object===null||u.text!==o.decoded||i.set(o.object,(i.get(o.object)??new Set).add(o.decoded));const a=[];let l=0,d=0;for(const o of t){let u=r.get(o);if(u!==void 0&&u.text!==o.decoded&&o.object!==null){const h=i.get(o.object)??new Set;i.set(o.object,h),u={...u,text:M(h,u.text)}}else u===void 0&&o.member!==null&&E.test(o.member)?u={replacements:o.decoded===f?0:1,text:f}:u===void 0&&(u=c(o));u.text!==o.decoded&&(a.push(e.slice(l,o.start),JSON.stringify(u.text)),l=o.close+1,d+=u.replacements)}a.push(e.slice(l));const p=a.join("");return n.some(o=>p.includes(o))?null:{replacements:d,text:p}}function H(e,n,t){let s=0;for(;e[n.start-1-s]==="\\";)s+=1;if(s%2===1)return!1;const c=e.slice(n.start,n.end);if(!c.includes("\\"))return!0;let r;try{r=JSON.parse(`"${c}"`)}catch{return!1}return n.cut?r.length>=N&&t.some(i=>i.startsWith(r)):t.includes(r)}function U(e,n,t){let s=0,c=e.length-1;for(;s<=c;){const i=s+c>>1;e[i].start<n?s=i+1:c=i-1}const r=e.at(c);return r!==void 0&&r.start<n&&t<=r.close?r:void 0}function Y(e){const n=[],t=[];for(let s=0;s<e.length;s+=1){const c=e[s];if(c==="{"?t.push({id:s,key:null}):c==="["?t.push(null):(c==="}"||c==="]")&&t.pop(),c!=='"')continue;const r=q(e,s),i=JSON.parse(e.slice(s,r+1)),a=t.at(-1)??null,l=a!==null&&x(e,r+1)===":";n.push({close:r,decoded:i,key:l,member:l?null:a?.key??null,object:a?.id??null,start:s}),l&&(a.key=i),s=r}return n}function q(e,n){for(let t=n+1;t<e.length;t+=1)if(e[t]==="\\")t+=1;else if(e[t]==='"')return t;return e.length-1}function x(e,n){for(let t=n;t<e.length;t+=1)if(!/\s/u.test(e[t]))return e[t]}function M(e,n){let t=n;for(let s=2;e.has(t);s++)t=`${n} (${String(s)})`;return e.add(t),t}
@@ -1 +1 @@
1
- import $ from"path";import{DEFAULT_REPORTED_ARCHIVE_CONFIG as ee}from"./reporting-types.js";export function buildRunArtifact({productDir:n,runId:e=null,results:i,startedAt:l,finishedAt:s,execution:o,workerCount:a,runtimeInstanceCount:d,runtimeStats:m,typeValues:g,suiteSelectors:k,fileNames:F,serviceFilter:C,planning:f=null,archiveConfig:S=null,declaredEnvironmentKeys:h=[],metadata:c,summarizeDbBackend:R,environmentServices:y=[],serviceLogs:v=[],setupLogs:b=[],setupOperations:D=[],operationTimings:M=[],runInterruptReason:A=null,runStatus:E=null}){const u=i.filter(t=>!t.skipped),z=u.filter(x),B=u.filter(t=>t.failed),w=u.filter(t=>t.infraFailed),N=u.filter(t=>t.blockedByRuntime),V=u.filter(t=>te(t)),I=oe(u),H=ne(u),_=r(u,"totalFileCount"),L=r(u,"passedFileCount"),P=r(u,"failedFileCount"),j=r(u,"skippedFileCount"),G=r(u,"notRunFileCount"),O=p(u,t=>t.status==="cancelled"),T=p(u,t=>t.status==="interrupted"),J=Math.max(0,G-O-T),K=p(u,t=>t.infraFailed===!0),U=p(u,t=>t.blockedByRuntime===!0),q=R(i),Q=typeof q=="string"?q:null,W=new Date(s??Date.now()).toISOString(),X=s===null?null:new Date(s).toISOString(),Y=s===null?null:s-l,Z=S??ee;return{schemaVersion:19,source:"testkit",generatedAt:W,product:{name:$.basename(n),directory:n},git:{branch:c.git.branch??null,commitSha:c.git.commitSha??null,dirty:c.git.dirty===!0,pullRequest:c.git.pullRequest??null,pullRequestHeadSha:c.git.pullRequestHeadSha??null,repository:c.git.repository??null},host:c.host,run:{id:e,status:E||(B.length>0||w.length>0?"failed":"passed"),startedAt:new Date(l).toISOString(),finishedAt:X,durationMs:Y,workers:o.taskConcurrency,fileTimeoutSeconds:o.fileTimeoutSeconds,workerCount:a,runtimeInstanceCount:d,runtimeStats:[...m??[]],dbBackend:Q,types:[...g],suiteSelectors:k.map(t=>t.raw),fileNames:[...F],serviceFilter:C,testkitVersion:c.testkitVersion,...A===null?{}:{interruptReason:A}},archive:{request:Z,declaredEnvironmentKeys:[...new Set(h)].sort()},...f?{planning:f}:{},summary:{services:{total:u.length,passed:V.length,failed:B.length,infraFailed:w.length,blockedByRuntime:N.length,skipped:z.length},suites:H,files:{total:_,passed:L,failed:P,skipped:j,notRun:J,cancelled:O,interrupted:T,infraFailed:K,blockedByRuntime:U},...I.length>0?{diagnostics:I}:{}},environment:{services:[...y]},logs:{services:[...v],setup:[...b]},setup:{operations:[...D]},timings:{operations:le(M)},services:i.map(t=>({name:t.name,failed:t.failed,infraFailed:t.infraFailed===!0,blockedByRuntime:t.blockedByRuntime===!0,skipped:t.skipped,suiteCount:t.suiteCount,completedSuiteCount:t.completedSuiteCount,skippedSuiteCount:t.skippedSuiteCount,failedSuiteCount:t.failedSuiteCount,totalFileCount:t.totalFileCount,completedFileCount:t.completedFileCount,passedFileCount:t.passedFileCount,failedFileCount:t.failedFileCount,skippedFileCount:t.skippedFileCount,notRunFileCount:t.notRunFileCount,durationMs:t.durationMs,totalTaskDurationMs:t.totalTaskDurationMs,dbBackend:t.dbBackend,suites:t.suites,errors:t.errors,...t.diagnostics&&t.diagnostics.length>0?{diagnostics:t.diagnostics}:{},...t.errorDetails&&t.errorDetails.length>0?{errorDetails:t.errorDetails}:{}}))}}export function buildLiveRunArtifact({productDir:n,runId:e=null,results:i,startedAt:l,updatedAt:s,execution:o,workerCount:a,runtimeInstanceCount:d,runtimeStats:m,typeValues:g,suiteSelectors:k,fileNames:F,serviceFilter:C,planning:f=null,archiveConfig:S=null,declaredEnvironmentKeys:h=[],metadata:c,summarizeDbBackend:R,environmentServices:y=[],serviceLogs:v=[],setupLogs:b=[],setupOperations:D=[],operationTimings:M=[]}){return{...buildRunArtifact({productDir:n,runId:e,results:i,startedAt:l,finishedAt:null,execution:o,workerCount:a,runtimeInstanceCount:d,runtimeStats:m,typeValues:g,suiteSelectors:k,fileNames:F,serviceFilter:C,planning:f,archiveConfig:S,declaredEnvironmentKeys:h,metadata:c,summarizeDbBackend:R,environmentServices:y,serviceLogs:v,setupLogs:b,setupOperations:D,operationTimings:M,runStatus:"running"}),generatedAt:new Date(s).toISOString()}}function x(n){return!n.skipped&&!n.failed&&n.skippedSuiteCount>0&&n.skippedSuiteCount===n.suiteCount&&n.notRunFileCount===0}function te(n){return!n.failed&&!n.infraFailed&&!x(n)&&n.notRunFileCount===0}function p(n,e){let i=0;for(const l of n)for(const s of l.suites)for(const o of s.files??[])e(o)&&(i+=1);return i}function ne(n){const e={total:0,completed:0,passed:0,failed:0,skipped:0};for(const i of n){const l=i.suites,s=i.suiteCount;if(l.length===0){const o=ie(i,s);e.total+=o.total,e.completed+=o.completed,e.passed+=o.passed,e.failed+=o.failed,e.skipped+=o.skipped;continue}e.total+=s;for(const o of l){const a=se(o);a==="passed"?(e.completed+=1,e.passed+=1):a==="failed"?(e.completed+=1,e.failed+=1):a==="skipped"&&(e.completed+=1,e.skipped+=1)}}return e}function ie(n,e){const i=n.failedSuiteCount,l=n.skippedSuiteCount,s=n.completedSuiteCount;return{total:e,completed:s,passed:Math.max(0,s-i-l),failed:i,skipped:l}}function se(n){const e=n.files??[],i=e.length,l=Math.max(n.fileCount??0,i),s=e.length>0?e.filter(d=>d.status==="failed"||d.failed===!0).length:n.failedFileCount??0,o=e.length>0?e.filter(d=>d.status==="skipped").length:n.skippedFileCount??0,a=e.length>0?e.filter(d=>d.status==="passed").length:n.passedFileCount??0;return s>0||n.failed===!0?"failed":l>0&&o>=l?"skipped":l>0&&a+o>=l?"passed":"pending"}function r(n,e){return n.reduce((i,l)=>i+l[e],0)}function oe(n){const e=[],i=new Set,l=s=>{const o=JSON.stringify(s);i.has(o)||(i.add(o),e.push(s))};for(const s of n){const o=s.diagnostics??[];for(const a of o)l({...a,service:a.service||s.name});if(!(o.length>0))for(const a of s.suites)for(const d of a.diagnostics??[])l({...d,service:d.service||s.name,suite:d.suite||a.name})}return e}function le(n){return n.map(e=>({id:e.id||null,source:e.source||"testkit",name:e.name||"operation",kind:e.kind||"operation",summary:e.summary||null,scope:e.scope?{...e.scope}:{},parentId:e.parentId||null,status:e.status||"unknown",startedAt:e.startedAt||null,updatedAt:e.updatedAt||null,finishedAt:e.finishedAt||null,durationMs:e.durationMs!==null&&e.durationMs!==void 0&&Number.isFinite(e.durationMs)?Math.max(0,Math.round(e.durationMs)):null,error:e.error||null,message:e.message||null,logRefs:(e.logRefs??[]).map(i=>ue(i)?{...i}:i),metadata:e.metadata?{...e.metadata}:{}}))}function ue(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}
1
+ import J from"path";import{publishEnvironmentRecord as K}from"./environment-record.js";import{DEFAULT_REPORTED_ARCHIVE_CONFIG as U}from"./reporting-types.js";export function buildRunArtifact({productDir:n,runId:e=null,results:i,startedAt:l,finishedAt:s,execution:o,workerCount:a,typeValues:d,suiteSelectors:R,fileNames:y,groupFilter:b,planning:f=null,archiveConfig:D=null,declaredEnvironmentKeys:M=[],metadata:r,environment:A,operationTimings:w=[],runInterruptReason:m=null,runStatus:B=null}){const u=i.filter(t=>!t.skipped),G=u.filter(S),g=u.filter(t=>t.failed),F=u.filter(t=>t.infraFailed),I=u.filter(t=>t.blockedByRuntime),O=u.filter(t=>Q(t)),C=Z(u),v=W(u),E=c(u,"totalFileCount"),T=c(u,"passedFileCount"),q=c(u,"failedFileCount"),x=c(u,"skippedFileCount"),N=c(u,"notRunFileCount"),k=p(u,t=>t.status==="cancelled"),h=p(u,t=>t.status==="interrupted"),V=Math.max(0,N-k-h),z=p(u,t=>t.infraFailed===!0),H=p(u,t=>t.blockedByRuntime===!0),_=new Date(s??Date.now()).toISOString(),L=s===null?null:new Date(s).toISOString(),P=s===null?null:s-l,j=D??U;return{schemaVersion:20,source:"testkit",generatedAt:_,product:{name:J.basename(n),directory:n},git:{branch:r.git.branch??null,commitSha:r.git.commitSha??null,dirty:r.git.dirty===!0,pullRequest:r.git.pullRequest??null,pullRequestHeadSha:r.git.pullRequestHeadSha??null,repository:r.git.repository??null},host:r.host,run:{id:e,status:B||(g.length>0||F.length>0?"failed":"passed"),startedAt:new Date(l).toISOString(),finishedAt:L,durationMs:P,workers:o.taskConcurrency,fileTimeoutSeconds:o.fileTimeoutSeconds,workerCount:a,types:[...d],suiteSelectors:R.map(t=>t.raw),fileNames:[...y],groupFilter:b,testkitVersion:r.testkitVersion,...m===null?{}:{interruptReason:m}},archive:{request:j,declaredEnvironmentKeys:[...new Set(M)].sort()},...f?{planning:f}:{},summary:{groups:{total:u.length,passed:O.length,failed:g.length,infraFailed:F.length,blockedByRuntime:I.length,skipped:G.length},suites:v,files:{total:E,passed:T,failed:q,skipped:x,notRun:V,cancelled:k,interrupted:h,infraFailed:z,blockedByRuntime:H},...C.length>0?{diagnostics:C}:{}},environment:K(A),timings:{operations:$(w)},groups:i.map(t=>({name:t.name,failed:t.failed,infraFailed:t.infraFailed===!0,blockedByRuntime:t.blockedByRuntime===!0,skipped:t.skipped,suiteCount:t.suiteCount,completedSuiteCount:t.completedSuiteCount,skippedSuiteCount:t.skippedSuiteCount,failedSuiteCount:t.failedSuiteCount,totalFileCount:t.totalFileCount,completedFileCount:t.completedFileCount,passedFileCount:t.passedFileCount,failedFileCount:t.failedFileCount,skippedFileCount:t.skippedFileCount,notRunFileCount:t.notRunFileCount,durationMs:t.durationMs,totalTaskDurationMs:t.totalTaskDurationMs,suites:t.suites,errors:t.errors,...t.diagnostics&&t.diagnostics.length>0?{diagnostics:t.diagnostics}:{},...t.errorDetails&&t.errorDetails.length>0?{errorDetails:t.errorDetails}:{}}))}}export function buildLiveRunArtifact({updatedAt:n,...e}){return{...buildRunArtifact({...e,finishedAt:null,runStatus:"running"}),generatedAt:new Date(n).toISOString()}}function S(n){return!n.skipped&&!n.failed&&n.skippedSuiteCount>0&&n.skippedSuiteCount===n.suiteCount&&n.notRunFileCount===0}function Q(n){return!n.failed&&!n.infraFailed&&!S(n)&&n.notRunFileCount===0}function p(n,e){let i=0;for(const l of n)for(const s of l.suites)for(const o of s.files??[])e(o)&&(i+=1);return i}function W(n){const e={total:0,completed:0,passed:0,failed:0,skipped:0};for(const i of n){const l=i.suites,s=i.suiteCount;if(l.length===0){const o=X(i,s);e.total+=o.total,e.completed+=o.completed,e.passed+=o.passed,e.failed+=o.failed,e.skipped+=o.skipped;continue}e.total+=s;for(const o of l){const a=Y(o);a==="passed"?(e.completed+=1,e.passed+=1):a==="failed"?(e.completed+=1,e.failed+=1):a==="skipped"&&(e.completed+=1,e.skipped+=1)}}return e}function X(n,e){const i=n.failedSuiteCount,l=n.skippedSuiteCount,s=n.completedSuiteCount;return{total:e,completed:s,passed:Math.max(0,s-i-l),failed:i,skipped:l}}function Y(n){const e=n.files??[],i=e.length,l=Math.max(n.fileCount??0,i),s=e.length>0?e.filter(d=>d.status==="failed"||d.failed===!0).length:n.failedFileCount??0,o=e.length>0?e.filter(d=>d.status==="skipped").length:n.skippedFileCount??0,a=e.length>0?e.filter(d=>d.status==="passed").length:n.passedFileCount??0;return s>0||n.failed===!0?"failed":l>0&&o>=l?"skipped":l>0&&a+o>=l?"passed":"pending"}function c(n,e){return n.reduce((i,l)=>i+l[e],0)}function Z(n){const e=[],i=new Set,l=s=>{const o=JSON.stringify(s);i.has(o)||(i.add(o),e.push(s))};for(const s of n){const o=s.diagnostics??[];for(const a of o)l({...a,group:a.group||s.name});if(!(o.length>0))for(const a of s.suites)for(const d of a.diagnostics??[])l({...d,group:d.group||s.name,suite:d.suite||a.name})}return e}function $(n){return n.map(e=>({id:e.id||null,source:e.source||"testkit",name:e.name||"operation",kind:e.kind||"operation",summary:e.summary||null,scope:e.scope?{...e.scope}:{},parentId:e.parentId||null,status:e.status||"unknown",startedAt:e.startedAt||null,updatedAt:e.updatedAt||null,finishedAt:e.finishedAt||null,durationMs:e.durationMs!==null&&e.durationMs!==void 0&&Number.isFinite(e.durationMs)?Math.max(0,Math.round(e.durationMs)):null,error:e.error||null,message:e.message||null,logRefs:(e.logRefs??[]).map(i=>ee(i)?{...i}:i),metadata:e.metadata?{...e.metadata}:{}}))}function ee(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}
@@ -0,0 +1 @@
1
+ import{isUnknownRecord as e}from"../kernel/unknown-values.js";export function runModel(r){return r.schemaVersion===19?u(r):a(r)}function u(r){return{...n(r),groups:r.services.map(t),summary:r.summary===void 0?null:{files:r.summary.files,groups:r.summary.services,suites:r.summary.suites}}}function a(r){return{...n(r),groups:r.groups.map(t),summary:r.summary===void 0?null:{files:r.summary.files,groups:r.summary.groups,suites:r.summary.suites}}}function n(r){return{archive:f(r.archive),finalizationFailure:r.finalizationFailure??null,generatedAt:r.generatedAt??null,git:{commitSha:e(r.git)?p(r.git.commitSha):null},run:{durationMs:r.run.durationMs??null,finishedAt:r.run.finishedAt,id:r.run.id,startedAt:r.run.startedAt,status:r.run.status,types:r.run.types??[]},schemaVersion:r.schemaVersion}}function t(r){return{errors:Array.isArray(r.errors)?r.errors.filter(s=>typeof s=="string"):[],name:r.name,suites:r.suites.map(l)}}function l(r){return{engineId:r.engineId,files:r.files??[],name:r.name,profileId:r.profileId,...r.testType===void 0?{}:{testType:r.testType}}}function f(r){if(!e(r))return{cleanup:null,publication:null};const{cleanup:s,publication:i}=r;return{cleanup:e(s)&&typeof s.status=="string"?{status:s.status}:null,publication:e(i)&&typeof i.status=="string"?{required:i.required===!0,status:i.status}:null}}function p(r){return typeof r=="string"?r:null}
@@ -1,2 +1,2 @@
1
- import{isWorkObserved as f,recordWork as l}from"../kernel/work-metrics.js";import{isPathWithin as p}from"../process/files/paths.js";import{isUnknownRecord as m}from"../kernel/unknown-values.js";import v from"crypto";import i from"fs";import o from"path";import{RUNS_DIRNAME as w}from"./contracts.js";import{requireManifestRelativePath as g}from"./read-fields.js";import{posixPath as S}from"../process/files/paths.js";export function readJsonFile(r){try{let t;if(f()){const n=i.readFileSync(r);l("json.filesRead",1),l("json.bytesRead",n.byteLength),t=n.toString("utf8")}else t=i.readFileSync(r,"utf8");return JSON.parse(t)}catch(t){throw new Error(`Invalid JSON in ${r}: ${errorMessage(t)}`,{cause:t})}}export function runIncludesFile(r,t){const e=S(t);return r.services.some(n=>n.suites.some(u=>u.files?.some(s=>s.path===e||s.path.endsWith(e))))}export function requireRunId(r){const t=r.run.id==null?null:normalizeRunId(r.run.id);if(t===null)throw new Error("Cannot seal a run bundle without a run id");return t}export function normalizeRunId(r){const t=typeof r=="string"?r:"";if(!t||t==="."||t===".."||t.includes("\0")||t.includes("/")||t.includes("\\"))throw new Error(`Invalid Testkit run id: ${t||"<empty>"}`);return t}export function walkFiles(r){if(!i.existsSync(r))return[];const t=[];for(const e of i.readdirSync(r,{withFileTypes:!0})){const n=o.join(r,e.name);e.isDirectory()?t.push(...walkFiles(n)):e.isFile()&&t.push(n)}return t}export function treeBytes(r){return walkFiles(r).reduce((t,e)=>t+i.statSync(e).size,0)}export function writeBufferAtomic(r,t){i.mkdirSync(o.dirname(r),{recursive:!0});const e=`${r}.${String(process.pid)}.${String(Date.now())}.tmp`;i.writeFileSync(e,t);try{i.renameSync(e,r)}catch(n){if(i.rmSync(e,{force:!0}),!i.existsSync(r))throw n}}export function writeJson(r,t){i.mkdirSync(o.dirname(r),{recursive:!0});const e=JSON.stringify(t,null,2);l("json.serializations"),f()&&l("json.bytesSerialized",Buffer.byteLength(e)),i.writeFileSync(r,`${e}
2
- `)}export function hashFile(r){const t=i.readFileSync(r);return l("evidence.hashFilesRead",1),l("evidence.hashBytesRead",t.byteLength),sha256(t)}export function sha256(r){return v.createHash("sha256").update(r).digest("hex")}export function stableJson(r){const t=JSON.stringify(d(r));return l("json.canonicalSerializations"),f()&&l("json.canonicalBytesSerialized",Buffer.byteLength(t)),t}function d(r){return Array.isArray(r)?r.map(d):m(r)?Object.fromEntries(Object.keys(r).sort().map(t=>[t,d(r[t])])):r}export function objectRecords(r){return Array.isArray(r)?r.filter(m):[]}export function resolveRunBundleDirectory(r,t){const e=resolveRunsRoot(r,!1),n=o.join(e,t),u=i.lstatSync(n,{throwIfNoEntry:!1});if(u===void 0)return n;if(u.isSymbolicLink())throw new Error(`Immutable run bundle directory must not be a symbolic link: ${t}`);if(!u.isDirectory())throw new Error(`Immutable run bundle path must be a directory: ${t}`);const s=i.realpathSync(e),c=i.realpathSync(n),a=o.relative(s,c);if(a.length===0||a===".."||a.startsWith(`..${o.sep}`)||o.isAbsolute(a))throw new Error(`Immutable run bundle directory must resolve within the runs root: ${t}`);return n}export function resolveRunsRoot(r,t){const e=o.resolve(r),n=resultsRoot(e),u=o.join(n,w);if(t)E(e,n,"Testkit results root"),i.mkdirSync(n,{recursive:!0});else if(!i.existsSync(n))return u;const s=b(n,"Testkit results root");if(y(i.realpathSync(e),s,"Testkit results root must resolve within the product directory"),t)i.mkdirSync(u,{recursive:!0});else if(!i.existsSync(u))return u;const c=b(u,"Immutable run bundles root");return y(s,c,"Immutable run bundles root must resolve within .testkit/results"),u}function E(r,t,e){const n=o.resolve(r),u=i.realpathSync(n);let s=o.resolve(t),c=i.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const a=o.dirname(s);if(a===s)throw new Error(`${e} has no existing parent directory`);s=a,c=i.lstatSync(s,{throwIfNoEntry:!1})}if(s!==n&&c.isSymbolicLink())throw new Error(`${e} must not traverse a symbolic link`);if(!c.isDirectory())throw new Error(`${e} parent must be a directory`);y(u,i.realpathSync(s),`${e} must resolve within the product directory`,!0)}function b(r,t){const e=i.lstatSync(r,{throwIfNoEntry:!1});if(e===void 0)throw new Error(`${t} does not exist`);if(e.isSymbolicLink())throw new Error(`${t} must not be a symbolic link`);if(!e.isDirectory())throw new Error(`${t} must be a directory`);return i.realpathSync(r)}function y(r,t,e,n=!1){const u=o.relative(r,t);if(!n&&u.length===0||u===".."||u.startsWith(`..${o.sep}`)||o.isAbsolute(u))throw new Error(e)}export function requirePublicationPathOutsideResults(r,t){const e=o.resolve(resultsRoot(r)),n=o.resolve(t);if(p(e,n))throw new Error("Publication directory must be outside .testkit/results");const u=i.realpathSync(e);let s=n,c=i.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const h=o.dirname(s);if(h===s)throw new Error("Publication directory has no existing parent");s=h,c=i.lstatSync(s,{throwIfNoEntry:!1})}const a=i.realpathSync(s);if(p(u,a))throw new Error("Publication directory must be outside .testkit/results")}export function resolveManifestEntryPath(r,t){const e=o.resolve(r),n=o.resolve(e,g(t,"manifest entry path"));return x(e,n,t),i.existsSync(n)&&x(i.realpathSync(e),i.realpathSync(n),t),n}function x(r,t,e){const n=o.relative(r,t);if(n.length===0||n===".."||n.startsWith(`..${o.sep}`)||o.isAbsolute(n))throw new Error(`manifest path escapes bundle directory: ${e}`)}export function optionalStringArray(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}export function optionalUnknownArray(r){return Array.isArray(r)?r:[]}export function errorMessage(r){return r instanceof Error?r.message:String(r)}export function bundleRelativePath(r,t){return S(o.join(".testkit","results",w,r,t))}export function resultsRoot(r){return o.join(r,".testkit","results")}export function sanitizeSegment(r){return r.toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"")||"product"}
1
+ import{isWorkObserved as f,recordWork as l}from"../kernel/work-metrics.js";import{isPathWithin as p}from"../process/files/paths.js";import{isUnknownRecord as x}from"../kernel/unknown-values.js";import v from"crypto";import i from"fs";import o from"path";import{RUNS_DIRNAME as m}from"./contracts.js";import{requireManifestRelativePath as g}from"./read-fields.js";import{posixPath as w}from"../process/files/paths.js";import{errorMessage as E}from"../kernel/error-serialization.js";export function readJsonFile(r){try{let t;if(f()){const n=i.readFileSync(r);l("json.filesRead",1),l("json.bytesRead",n.byteLength),t=n.toString("utf8")}else t=i.readFileSync(r,"utf8");return JSON.parse(t)}catch(t){throw new Error(`Invalid JSON in ${r}: ${E(t)}`,{cause:t})}}export function runIncludesFile(r,t){const e=w(t);return r.groups.some(n=>n.suites.some(u=>u.files.some(s=>s.path===e||s.path.endsWith(e))))}export function requireRunId(r){const t=r.run.id==null?null:normalizeRunId(r.run.id);if(t===null)throw new Error("Cannot seal a run bundle without a run id");return t}export function normalizeRunId(r){const t=typeof r=="string"?r:"";if(!t||t==="."||t===".."||t.includes("\0")||t.includes("/")||t.includes("\\"))throw new Error(`Invalid Testkit run id: ${t||"<empty>"}`);return t}export function walkFiles(r){if(!i.existsSync(r))return[];const t=[];for(const e of i.readdirSync(r,{withFileTypes:!0})){const n=o.join(r,e.name);e.isDirectory()?t.push(...walkFiles(n)):e.isFile()&&t.push(n)}return t}export function treeBytes(r){return walkFiles(r).reduce((t,e)=>t+i.statSync(e).size,0)}export function writeBufferAtomic(r,t){i.mkdirSync(o.dirname(r),{recursive:!0});const e=`${r}.${String(process.pid)}.${String(Date.now())}.tmp`;i.writeFileSync(e,t);try{i.renameSync(e,r)}catch(n){if(i.rmSync(e,{force:!0}),!i.existsSync(r))throw n}}export function writeJson(r,t){i.mkdirSync(o.dirname(r),{recursive:!0});const e=JSON.stringify(t,null,2);l("json.serializations"),f()&&l("json.bytesSerialized",Buffer.byteLength(e)),i.writeFileSync(r,`${e}
2
+ `)}export function hashFile(r){const t=i.readFileSync(r);return l("evidence.hashFilesRead",1),l("evidence.hashBytesRead",t.byteLength),sha256(t)}export function sha256(r){return v.createHash("sha256").update(r).digest("hex")}export function stableJson(r){const t=JSON.stringify(d(r));return l("json.canonicalSerializations"),f()&&l("json.canonicalBytesSerialized",Buffer.byteLength(t)),t}function d(r){return Array.isArray(r)?r.map(d):x(r)?Object.fromEntries(Object.keys(r).sort().map(t=>[t,d(r[t])])):r}export function resolveRunBundleDirectory(r,t){const e=resolveRunsRoot(r,!1),n=o.join(e,t),u=i.lstatSync(n,{throwIfNoEntry:!1});if(u===void 0)return n;if(u.isSymbolicLink())throw new Error(`Immutable run bundle directory must not be a symbolic link: ${t}`);if(!u.isDirectory())throw new Error(`Immutable run bundle path must be a directory: ${t}`);const s=i.realpathSync(e),c=i.realpathSync(n),a=o.relative(s,c);if(a.length===0||a===".."||a.startsWith(`..${o.sep}`)||o.isAbsolute(a))throw new Error(`Immutable run bundle directory must resolve within the runs root: ${t}`);return n}export function resolveRunsRoot(r,t){const e=o.resolve(r),n=resultsRoot(e),u=o.join(n,m);if(t)k(e,n,"Testkit results root"),i.mkdirSync(n,{recursive:!0});else if(!i.existsSync(n))return u;const s=S(n,"Testkit results root");if(y(i.realpathSync(e),s,"Testkit results root must resolve within the product directory"),t)i.mkdirSync(u,{recursive:!0});else if(!i.existsSync(u))return u;const c=S(u,"Immutable run bundles root");return y(s,c,"Immutable run bundles root must resolve within .testkit/results"),u}function k(r,t,e){const n=o.resolve(r),u=i.realpathSync(n);let s=o.resolve(t),c=i.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const a=o.dirname(s);if(a===s)throw new Error(`${e} has no existing parent directory`);s=a,c=i.lstatSync(s,{throwIfNoEntry:!1})}if(s!==n&&c.isSymbolicLink())throw new Error(`${e} must not traverse a symbolic link`);if(!c.isDirectory())throw new Error(`${e} parent must be a directory`);y(u,i.realpathSync(s),`${e} must resolve within the product directory`,!0)}function S(r,t){const e=i.lstatSync(r,{throwIfNoEntry:!1});if(e===void 0)throw new Error(`${t} does not exist`);if(e.isSymbolicLink())throw new Error(`${t} must not be a symbolic link`);if(!e.isDirectory())throw new Error(`${t} must be a directory`);return i.realpathSync(r)}function y(r,t,e,n=!1){const u=o.relative(r,t);if(!n&&u.length===0||u===".."||u.startsWith(`..${o.sep}`)||o.isAbsolute(u))throw new Error(e)}export function requirePublicationPathOutsideResults(r,t){const e=o.resolve(resultsRoot(r)),n=o.resolve(t);if(p(e,n))throw new Error("Publication directory must be outside .testkit/results");const u=i.realpathSync(e);let s=n,c=i.lstatSync(s,{throwIfNoEntry:!1});for(;c===void 0;){const h=o.dirname(s);if(h===s)throw new Error("Publication directory has no existing parent");s=h,c=i.lstatSync(s,{throwIfNoEntry:!1})}const a=i.realpathSync(s);if(p(u,a))throw new Error("Publication directory must be outside .testkit/results")}export function resolveManifestEntryPath(r,t){const e=o.resolve(r),n=o.resolve(e,g(t,"manifest entry path"));return b(e,n,t),i.existsSync(n)&&b(i.realpathSync(e),i.realpathSync(n),t),n}function b(r,t,e){const n=o.relative(r,t);if(n.length===0||n===".."||n.startsWith(`..${o.sep}`)||o.isAbsolute(n))throw new Error(`manifest path escapes bundle directory: ${e}`)}export function optionalStringArray(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}export function optionalUnknownArray(r){return Array.isArray(r)?r:[]}export function bundleRelativePath(r,t){return w(o.join(".testkit","results",m,r,t))}export function resultsRoot(r){return o.join(r,".testkit","results")}export function sanitizeSegment(r){return r.toLowerCase().replace(/[^a-z0-9._-]+/gu,"-").replace(/^-+|-+$/gu,"")||"product"}
@@ -1 +1 @@
1
- import{isUnknownRecord as m}from"../kernel/unknown-values.js";export function createEmptyTimings(){return{version:2,files:{}}}export function normalizeTimings(t){if(!m(t)||t.version!==2||!m(t.files))return createEmptyTimings();const n={};for(const[e,r]of Object.entries(t.files)){if(!m(r))continue;const u=D(r);u&&(n[e]=u)}return{version:2,files:n}}export function applyTimingUpdates(t,n,e=new Date().toISOString()){const r={...t.files},u=e||null;for(const o of n){const i=Math.max(1,Math.round(o.durationMs)),s=r[o.key];if(!s){r[o.key]={durationMs:i,avgDurationMs:i,lastDurationMs:i,minDurationMs:i,maxDurationMs:i,runs:1,lastStatus:o.status||null,updatedAt:u};continue}const M=s.runs,f=M+1,d=s.avgDurationMs||s.durationMs||i,l=Math.max(1,Math.round((d*M+i)/f));r[o.key]={durationMs:l,avgDurationMs:l,lastDurationMs:i,minDurationMs:Math.min(s.minDurationMs||i,i),maxDurationMs:Math.max(s.maxDurationMs||i,i),runs:f,lastStatus:(o.status||null)??s.lastStatus,updatedAt:u}}return{version:2,files:r}}function D(t){if(!p(t.durationMs))return null;const n=a(t.durationMs);return{durationMs:n,avgDurationMs:a(t.avgDurationMs,n),lastDurationMs:a(t.lastDurationMs,n),minDurationMs:a(t.minDurationMs,n),maxDurationMs:a(t.maxDurationMs,n),runs:g(t.runs),lastStatus:c(t.lastStatus),updatedAt:c(t.updatedAt)}}function p(t){return typeof t=="number"?Number.isFinite(t):typeof t!="string"||t.trim().length===0?!1:Number.isFinite(Number(t))}function a(t,n=0){const r=Number(t||n);return Number.isFinite(r)?Math.max(0,Math.round(r)):n}function g(t){const n=Number(t||0);return Number.isFinite(n)?Math.max(0,Math.floor(n)):0}function c(t){return typeof t=="string"&&t.length>0?t:null}
1
+ import{isUnknownRecord as m,optionalString as l}from"../kernel/unknown-values.js";export function createEmptyTimings(){return{version:2,files:{}}}export function normalizeTimings(t){if(!m(t)||t.version!==2||!m(t.files))return createEmptyTimings();const n={};for(const[e,r]of Object.entries(t.files)){if(!m(r))continue;const u=D(r);u&&(n[e]=u)}return{version:2,files:n}}export function applyTimingUpdates(t,n,e=new Date().toISOString()){const r={...t.files},u=e||null;for(const o of n){const i=Math.max(1,Math.round(o.durationMs)),s=r[o.key];if(!s){r[o.key]={durationMs:i,avgDurationMs:i,lastDurationMs:i,minDurationMs:i,maxDurationMs:i,runs:1,lastStatus:o.status||null,updatedAt:u};continue}const M=s.runs,c=M+1,d=s.avgDurationMs||s.durationMs||i,f=Math.max(1,Math.round((d*M+i)/c));r[o.key]={durationMs:f,avgDurationMs:f,lastDurationMs:i,minDurationMs:Math.min(s.minDurationMs||i,i),maxDurationMs:Math.max(s.maxDurationMs||i,i),runs:c,lastStatus:(o.status||null)??s.lastStatus,updatedAt:u}}return{version:2,files:r}}function D(t){if(!p(t.durationMs))return null;const n=a(t.durationMs);return{durationMs:n,avgDurationMs:a(t.avgDurationMs,n),lastDurationMs:a(t.lastDurationMs,n),minDurationMs:a(t.minDurationMs,n),maxDurationMs:a(t.maxDurationMs,n),runs:g(t.runs),lastStatus:l(t.lastStatus),updatedAt:l(t.updatedAt)}}function p(t){return typeof t=="number"?Number.isFinite(t):typeof t!="string"||t.trim().length===0?!1:Number.isFinite(Number(t))}function a(t,n=0){const r=Number(t||n);return Number.isFinite(r)?Math.max(0,Math.round(r)):n}function g(t){const n=Number(t||0);return Number.isFinite(n)?Math.max(0,Math.floor(n)):0}
@@ -1 +1 @@
1
- import{isPropertyContainer as O}from"./unknown-values.js";const g="testkitOperation",m="testkitDiagnostics",o=String;export function attachErrorOperation(t,n){if(!i(t)||!i(n)||s(t,g))return t;const e=y(n);return e!==null&&d(t,g,e),t}export function attachErrorDiagnostics(t,n){if(!i(t)||!i(n))return t;const e=getErrorDiagnostics(t),r={...a(e),...a(n)};return d(t,m,r),t}export function getErrorOperation(t){return i(t)?y(s(t,g)||s(t,"operation")):null}export function getErrorDiagnostics(t){return i(t)?a(s(t,m)||s(t,"diagnostics")):{}}export function errorMessage(t){return t instanceof Error?t.message:o(t)}export function formatError(t){return sanitizeErrorMessage(errorMessage(t))}export function sanitizeErrorMessage(t){return t.replace(/(postgres(?:ql)?:\/\/[^:\s"'@]+:)[^@\s"']+(@)/gi,"$1<redacted>$2").replace(/("password"\s*:\s*")[^"]*(")/gi,"$1<redacted>$2").replace(/('password'\s*:\s*')[^']*(')/gi,"$1<redacted>$2").replace(/(PGPASSWORD=)[^\s'"\\]+/g,"$1<redacted>")}export function errorDetail(t){if(i(t)){const n=s(t,"stack");if(typeof n=="string"&&n.length>0)return n;const e=s(t,"message");if(typeof e=="string"&&e.length>0)return e}return t instanceof Error?t.stack||t.message:o(t)}export function serializeError(t,{includeStack:n=!1}={}){if(t instanceof Error){const e=getErrorOperation(t),r=getErrorDiagnostics(t),c=s(t,"code"),u=s(t,"exitCode"),f=s(t,"status");return{name:t.name||"Error",message:t.message||o(t),...typeof c=="string"?{code:c}:{},...typeof u=="number"&&Number.isInteger(u)?{exitCode:u}:{},...typeof f=="number"&&Number.isInteger(f)?{status:f}:{},...e?{phase:e.name,operation:e}:{},...Object.keys(r).length>0?{diagnostics:r}:{},...n&&typeof t.stack=="string"?{stack:t.stack}:{}}}return{name:"Error",message:o(t)}}function d(t,n,e){Object.defineProperty(t,n,{value:e,configurable:!0,enumerable:!1,writable:!0})}function y(t){if(!i(t))return null;const n=s(t,"id"),e=s(t,"source"),r=s(t,"name"),c=s(t,"kind"),u=s(t,"summary"),f=s(t,"status"),p=s(t,"error");return{...n?{id:o(n)}:{},source:typeof e=="string"?e:"testkit",name:typeof r=="string"&&r?r:"operation",kind:typeof c=="string"&&c?c:"operation",...u?{summary:o(u)}:{},scope:a(s(t,"scope")),...f?{status:o(f)}:{},...p?{error:o(p)}:{}}}function a(t){if(!i(t)||E(t))return{};const n=[];for(const e of Object.keys(t)){const r=s(t,e);r!==void 0&&n.push([e,l(r)])}return Object.fromEntries(n)}function l(t){return t===null?null:typeof t=="string"||typeof t=="number"||typeof t=="boolean"?t:E(t)?t.map(n=>l(n)):i(t)?a(t):o(t)}function s(t,n){return Reflect.get(t,n)}function E(t){return Array.isArray(t)}function i(t){return t!==null&&typeof t=="object"}export function toError(t){return t instanceof Error?t:new Error(String(t))}export function hasErrorCode(t,n){return O(t)&&Reflect.get(t,"code")===n}
1
+ import{redactSensitiveText as O}from"./redaction.js";import{isPropertyContainer as k,isNonNullObject as r,isUnknownArray as p}from"./unknown-values.js";const m="testkitOperation",y="testkitDiagnostics",o=String;export function attachErrorOperation(t,n){if(!r(t)||!r(n)||s(t,m))return t;const e=E(n);return e!==null&&d(t,m,e),t}export function attachErrorDiagnostics(t,n){if(!r(t)||!r(n))return t;const e=getErrorDiagnostics(t),i={...a(e),...a(n)};return d(t,y,i),t}export function getErrorOperation(t){return r(t)?E(s(t,m)||s(t,"operation")):null}export function getErrorDiagnostics(t){return r(t)?a(s(t,y)||s(t,"diagnostics")):{}}export function errorMessage(t){return t instanceof Error?t.message:o(t)}export function formatError(t){return sanitizeErrorMessage(errorMessage(t))}export function sanitizeErrorMessage(t){return O(t)}export function errorDetail(t){if(r(t)){const n=s(t,"stack");if(typeof n=="string"&&n.length>0)return n;const e=s(t,"message");if(typeof e=="string"&&e.length>0)return e}return t instanceof Error?t.stack||t.message:o(t)}export function serializeError(t,{includeStack:n=!1}={}){if(t instanceof Error){const e=getErrorOperation(t),i=getErrorDiagnostics(t),c=s(t,"code"),u=s(t,"exitCode"),f=s(t,"status");return{name:t.name||"Error",message:t.message||o(t),...typeof c=="string"?{code:c}:{},...typeof u=="number"&&Number.isInteger(u)?{exitCode:u}:{},...typeof f=="number"&&Number.isInteger(f)?{status:f}:{},...e?{phase:e.name,operation:e}:{},...Object.keys(i).length>0?{diagnostics:i}:{},...n&&typeof t.stack=="string"?{stack:t.stack}:{}}}return{name:"Error",message:o(t)}}function d(t,n,e){Object.defineProperty(t,n,{value:e,configurable:!0,enumerable:!1,writable:!0})}function E(t){if(!r(t))return null;const n=s(t,"id"),e=s(t,"source"),i=s(t,"name"),c=s(t,"kind"),u=s(t,"summary"),f=s(t,"status"),g=s(t,"error");return{...n?{id:o(n)}:{},source:typeof e=="string"?e:"testkit",name:typeof i=="string"&&i?i:"operation",kind:typeof c=="string"&&c?c:"operation",...u?{summary:o(u)}:{},scope:a(s(t,"scope")),...f?{status:o(f)}:{},...g?{error:o(g)}:{}}}function a(t){if(!r(t)||p(t))return{};const n=[];for(const e of Object.keys(t)){const i=s(t,e);i!==void 0&&n.push([e,l(i)])}return Object.fromEntries(n)}function l(t){return t===null?null:typeof t=="string"||typeof t=="number"||typeof t=="boolean"?t:p(t)?t.map(n=>l(n)):r(t)?a(t):o(t)}function s(t,n){return Reflect.get(t,n)}export function toError(t){return t instanceof Error?t:new Error(String(t))}export function hasErrorCode(t,n){return k(t)&&Reflect.get(t,"code")===n}
@@ -0,0 +1 @@
1
+ export function unknownKeysMessage(r,e,s){const n=Object.keys(r).filter(t=>!e.includes(t));if(n.length===0)return null;const o=n.map(t=>{const l=c(t,e);return l===null?JSON.stringify(t):`${JSON.stringify(t)} (mispelled ${JSON.stringify(l)}?)`});return`${s} has unknown ${n.length===1?"key":"keys"} ${o.join(", ")}; it accepts ${e.join(", ")}`}export function rejectUnknownKeys(r,e,s){const n=unknownKeysMessage(r,e,s);if(n!==null)throw new Error(n)}function c(r,e){let s=null,n=Math.max(1,Math.floor(r.length/3))+1;for(const o of e){const t=i(r.toLowerCase(),o.toLowerCase());t<n&&(s=o,n=t)}return s}function i(r,e){let s=Array.from({length:e.length+1},(n,o)=>o);for(let n=0;n<r.length;n+=1){const o=[n+1];for(let t=0;t<e.length;t+=1){const l=r.charCodeAt(n)===e.charCodeAt(t)?0:1;o.push(Math.min((s[t+1]??0)+1,(o[t]??0)+1,(s[t]??0)+l))}s=o}return s[e.length]??0}
@@ -0,0 +1 @@
1
+ import{REDACTED as m,SENSITIVE_KEY_PATTERN as E}from"./redaction.js";import{recordWork as A}from"./work-metrics.js";export const MIN_REDACTION_TARGET_LENGTH=8;const l=/^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/iu,M=/^postgres(?:ql)?:\/\//iu,u=/["'`]?(?:…|\.\.\.)/gu;export function sensitiveKeyMatcher(t=[]){const r=new Set(t.map(n=>n.toUpperCase()));return n=>E.test(n)||r.has(n.toUpperCase())}export function isRedactionTarget(t){return t.length>=MIN_REDACTION_TARGET_LENGTH}export function envRedactionTargets(t,r=sensitiveKeyMatcher()){return Object.entries(t).flatMap(([n,e])=>e===void 0?[]:valueRedactionTargets(n,e,r))}export function valueRedactionTargets(t,r,n=sensitiveKeyMatcher()){return l.test(r)?urlRedactionTargets(r):n(t)&&isRedactionTarget(r)?[r]:[]}export function jsonRedactionTargets(t,r,n=sensitiveKeyMatcher()){return typeof r=="string"?valueRedactionTargets(t,r,n):Array.isArray(r)?r.flatMap(e=>jsonRedactionTargets(t,e,n)):r!==null&&typeof r=="object"?Object.entries(r).flatMap(([e,o])=>jsonRedactionTargets(e,o,n)):[]}export function urlRedactionTargets(t){const r=l.exec(t);if(r===null)return[];const n=r[1].slice(0,Math.max(r[1].lastIndexOf("@"),0)),e=n.indexOf(":"),o=[...e<0?[]:[n.slice(e+1)],...M.test(t)?O(t.slice(r[0].length)):[]];return[...new Set(o.flatMap(s=>[s,x(s)]))].filter(isRedactionTarget)}function O(t){return(/\?([^#]*)/u.exec(t)?.[1]??"").split("&").flatMap(n=>{const e=n.indexOf("=");return e>0&&x(n.slice(0,e))==="password"?[n.slice(e+1)]:[]})}export function orderRedactionTargets(t){const r=[...t].filter(isRedactionTarget).flatMap(n=>[n,...n.split(/\r?\n/u)]).flatMap(n=>{const e=T(n);return[n,e,T(e)]});return[...new Set(r)].filter(isRedactionTarget).sort((n,e)=>e.length-n.length)}export function replaceRedactionTargets(t,r,n=()=>m){let e=t,o=0;for(const c of r){if(!e.includes(c))continue;const i=e.split(c);o+=i.length-1,e=i.join(n(c))}const s=N(e,r,n);return{replacements:o+s.replacements,text:s.text}}export function redactionTargetSpans(t,r){const n=[];for(const e of r)for(let o=t.indexOf(e);o!==-1;o=t.indexOf(e,o+1))n.push({cut:!1,end:o+e.length,start:o});for(const e of t.matchAll(u)){const o=h(t,e.index,0,r);o>0&&n.push({cut:!0,end:e.index,start:e.index-o})}return n}function N(t,r,n){const e=[];let o=0,s=0;for(const c of t.matchAll(u)){const i=h(t,c.index,o,r);i!==0&&(e.push(t.slice(o,c.index-i),n(t.slice(c.index-i,c.index))),o=c.index,s+=1)}return e.push(t.slice(o)),{replacements:s,text:e.join("")}}const d=new WeakMap;function h(t,r,n,e){let o=d.get(e);o===void 0&&(o=e.map(_),d.set(e,o));let s=0,c=0;return e.forEach((i,R)=>{const p=Math.max(n,r-(i.length-1));if(r-p<=s||r-p<MIN_REDACTION_TARGET_LENGTH)return;const g=o[R];let f=0;for(let a=p;a<r;a++){for(c+=1;f>0&&i[f]!==t[a];)f=g[f-1],c+=1;i[f]===t[a]&&(f+=1)}f>=MIN_REDACTION_TARGET_LENGTH&&f>s&&(s=f)}),A("redaction.truncationSteps",c),s}function _(t){const r=new Int32Array(t.length);let n=0;for(let e=1;e<t.length;e++){for(;n>0&&t[n]!==t[e];)n=r[n-1];t[n]===t[e]&&(n+=1),r[e]=n}return r}function T(t){return JSON.stringify(t).slice(1,-1)}function x(t){try{return decodeURIComponent(t)}catch{return t}}