@intentius/chant 0.13.0 → 0.15.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 (453) hide show
  1. package/dist/audit/catalog.d.ts +45 -5
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/fetch.d.ts.map +1 -1
  4. package/dist/audit/proof.d.ts +3 -0
  5. package/dist/audit/proof.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +4 -1
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/report.d.ts +3 -0
  9. package/dist/audit/report.d.ts.map +1 -1
  10. package/dist/audit/rules-doc.d.ts +8 -2
  11. package/dist/audit/rules-doc.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts.map +1 -1
  13. package/dist/cli/commands/doctor.d.ts.map +1 -1
  14. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
  15. package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
  16. package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
  17. package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
  20. package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
  21. package/dist/cli/handlers/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/components.d.ts +39 -0
  23. package/dist/cli/handlers/components.d.ts.map +1 -0
  24. package/dist/cli/handlers/dev.d.ts +3 -0
  25. package/dist/cli/handlers/dev.d.ts.map +1 -1
  26. package/dist/cli/handlers/graph.d.ts +4 -2
  27. package/dist/cli/handlers/graph.d.ts.map +1 -1
  28. package/dist/cli/handlers/misc.d.ts.map +1 -1
  29. package/dist/cli/handlers/run.d.ts +49 -0
  30. package/dist/cli/handlers/run.d.ts.map +1 -1
  31. package/dist/cli/main.d.ts.map +1 -1
  32. package/dist/cli/registry.d.ts +35 -0
  33. package/dist/cli/registry.d.ts.map +1 -1
  34. package/dist/codegen/fetch.d.ts +22 -0
  35. package/dist/codegen/fetch.d.ts.map +1 -1
  36. package/dist/codegen/lexicon-regen.d.ts +110 -0
  37. package/dist/codegen/lexicon-regen.d.ts.map +1 -0
  38. package/dist/codegen/pinned-upgrade.d.ts +153 -0
  39. package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
  40. package/dist/codegen/rolling-upgrade.d.ts +138 -0
  41. package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
  42. package/dist/codegen/surface-snapshot.d.ts +173 -0
  43. package/dist/codegen/surface-snapshot.d.ts.map +1 -0
  44. package/dist/codegen/version-bump.d.ts +21 -0
  45. package/dist/codegen/version-bump.d.ts.map +1 -0
  46. package/dist/components/auto-release.d.ts +93 -0
  47. package/dist/components/auto-release.d.ts.map +1 -0
  48. package/dist/components/builders.d.ts +37 -0
  49. package/dist/components/builders.d.ts.map +1 -0
  50. package/dist/components/capability-plugin-loader.d.ts +111 -0
  51. package/dist/components/capability-plugin-loader.d.ts.map +1 -0
  52. package/dist/components/capability-plugin-schema.d.ts +24 -0
  53. package/dist/components/capability-plugin-schema.d.ts.map +1 -0
  54. package/dist/components/capability-plugin.d.ts +85 -0
  55. package/dist/components/capability-plugin.d.ts.map +1 -0
  56. package/dist/components/capability.d.ts +89 -0
  57. package/dist/components/capability.d.ts.map +1 -0
  58. package/dist/components/cli-support.d.ts +204 -0
  59. package/dist/components/cli-support.d.ts.map +1 -0
  60. package/dist/components/component.d.ts +179 -0
  61. package/dist/components/component.d.ts.map +1 -0
  62. package/dist/components/config-defaults.d.ts +41 -0
  63. package/dist/components/config-defaults.d.ts.map +1 -0
  64. package/dist/components/discover.d.ts +60 -0
  65. package/dist/components/discover.d.ts.map +1 -0
  66. package/dist/components/driver-output.d.ts +20 -0
  67. package/dist/components/driver-output.d.ts.map +1 -0
  68. package/dist/components/driver.d.ts +209 -0
  69. package/dist/components/driver.d.ts.map +1 -0
  70. package/dist/components/index.d.ts +22 -0
  71. package/dist/components/index.d.ts.map +1 -0
  72. package/dist/components/manifest-persistence.d.ts +79 -0
  73. package/dist/components/manifest-persistence.d.ts.map +1 -0
  74. package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
  75. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
  76. package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
  77. package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
  78. package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
  79. package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
  80. package/dist/components/pilots/lambda.pilot.d.ts +35 -0
  81. package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
  82. package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
  83. package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
  84. package/dist/components/pilots/project.d.ts +13 -0
  85. package/dist/components/pilots/project.d.ts.map +1 -0
  86. package/dist/components/presets/ecs-fargate.d.ts +59 -0
  87. package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
  88. package/dist/components/presets/index.d.ts +29 -0
  89. package/dist/components/presets/index.d.ts.map +1 -0
  90. package/dist/components/presets/lambda.d.ts +35 -0
  91. package/dist/components/presets/lambda.d.ts.map +1 -0
  92. package/dist/components/presets/single-host-compose.d.ts +43 -0
  93. package/dist/components/presets/single-host-compose.d.ts.map +1 -0
  94. package/dist/components/registry.d.ts +29 -0
  95. package/dist/components/registry.d.ts.map +1 -0
  96. package/dist/components/starter-plugin.d.ts +51 -0
  97. package/dist/components/starter-plugin.d.ts.map +1 -0
  98. package/dist/components/temporal-codegen-loader.d.ts +27 -0
  99. package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
  100. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
  101. package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
  102. package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
  103. package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
  104. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
  105. package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
  106. package/dist/components/verbs/bom-writer.d.ts +155 -0
  107. package/dist/components/verbs/bom-writer.d.ts.map +1 -0
  108. package/dist/components/verbs/build-archive.d.ts +205 -0
  109. package/dist/components/verbs/build-archive.d.ts.map +1 -0
  110. package/dist/components/verbs/build.d.ts +150 -0
  111. package/dist/components/verbs/build.d.ts.map +1 -0
  112. package/dist/components/verbs/cloud-executor.d.ts +88 -0
  113. package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
  114. package/dist/components/verbs/component-bom.d.ts +103 -0
  115. package/dist/components/verbs/component-bom.d.ts.map +1 -0
  116. package/dist/components/verbs/index.d.ts +49 -0
  117. package/dist/components/verbs/index.d.ts.map +1 -0
  118. package/dist/components/verbs/license-policy.d.ts +56 -0
  119. package/dist/components/verbs/license-policy.d.ts.map +1 -0
  120. package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
  121. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
  122. package/dist/components/verbs/process-runner.d.ts +64 -0
  123. package/dist/components/verbs/process-runner.d.ts.map +1 -0
  124. package/dist/components/verbs/reproducibility.d.ts +80 -0
  125. package/dist/components/verbs/reproducibility.d.ts.map +1 -0
  126. package/dist/components/verbs/sbom-generator.d.ts +157 -0
  127. package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
  128. package/dist/components/verbs/sbom.d.ts +79 -0
  129. package/dist/components/verbs/sbom.d.ts.map +1 -0
  130. package/dist/components/verbs/shell.d.ts +35 -0
  131. package/dist/components/verbs/shell.d.ts.map +1 -0
  132. package/dist/components/verbs/sign.d.ts +222 -0
  133. package/dist/components/verbs/sign.d.ts.map +1 -0
  134. package/dist/components/verbs/stub.d.ts +18 -0
  135. package/dist/components/verbs/stub.d.ts.map +1 -0
  136. package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
  137. package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
  138. package/dist/components/verbs/verify.d.ts +106 -0
  139. package/dist/components/verbs/verify.d.ts.map +1 -0
  140. package/dist/components/verbs/vex.d.ts +65 -0
  141. package/dist/components/verbs/vex.d.ts.map +1 -0
  142. package/dist/components/verbs/vuln-gate.d.ts +91 -0
  143. package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
  144. package/dist/components/verbs/vuln-scan.d.ts +116 -0
  145. package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
  146. package/dist/components/verbs/wait-verify.d.ts +85 -0
  147. package/dist/components/verbs/wait-verify.d.ts.map +1 -0
  148. package/dist/config.d.ts +205 -0
  149. package/dist/config.d.ts.map +1 -1
  150. package/dist/index.d.ts +4 -0
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/lexicon.d.ts +78 -0
  153. package/dist/lexicon.d.ts.map +1 -1
  154. package/dist/lifecycle/build-ledger-store.d.ts +121 -0
  155. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
  156. package/dist/lifecycle/build-ledger.d.ts +225 -0
  157. package/dist/lifecycle/build-ledger.d.ts.map +1 -0
  158. package/dist/lifecycle/git.d.ts +84 -0
  159. package/dist/lifecycle/git.d.ts.map +1 -1
  160. package/dist/lifecycle/index.d.ts +5 -0
  161. package/dist/lifecycle/index.d.ts.map +1 -1
  162. package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
  163. package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
  164. package/dist/lifecycle/release-ledger.d.ts +122 -0
  165. package/dist/lifecycle/release-ledger.d.ts.map +1 -0
  166. package/dist/lifecycle/status.d.ts +150 -0
  167. package/dist/lifecycle/status.d.ts.map +1 -0
  168. package/dist/lint/component-checks.d.ts +95 -0
  169. package/dist/lint/component-checks.d.ts.map +1 -0
  170. package/dist/lint/engine.d.ts +23 -0
  171. package/dist/lint/engine.d.ts.map +1 -1
  172. package/dist/lint/presets/strict.json +8 -1
  173. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
  174. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
  175. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
  176. package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
  177. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
  178. package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
  179. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
  180. package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
  181. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
  182. package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
  183. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
  184. package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
  185. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
  186. package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
  187. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
  188. package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
  189. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
  190. package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
  191. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
  192. package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
  193. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
  194. package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
  195. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
  196. package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
  197. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
  198. package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
  199. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
  200. package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
  201. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
  202. package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
  203. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
  204. package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
  205. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
  206. package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
  207. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
  208. package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
  209. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
  210. package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
  211. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
  212. package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
  213. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
  214. package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
  215. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
  216. package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
  217. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
  218. package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
  219. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
  220. package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
  221. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
  222. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
  223. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
  224. package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
  225. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
  226. package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
  227. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
  228. package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
  229. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
  230. package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
  231. package/dist/lint/rules/comp/index.d.ts +18 -0
  232. package/dist/lint/rules/comp/index.d.ts.map +1 -0
  233. package/dist/lint/rules/comp/support.d.ts +71 -0
  234. package/dist/lint/rules/comp/support.d.ts.map +1 -0
  235. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  236. package/dist/op/activity-registry.d.ts +11 -6
  237. package/dist/op/activity-registry.d.ts.map +1 -1
  238. package/dist/op/activity-runtime.d.ts +24 -0
  239. package/dist/op/activity-runtime.d.ts.map +1 -0
  240. package/dist/op/builders.d.ts +56 -0
  241. package/dist/op/builders.d.ts.map +1 -1
  242. package/dist/op/index.d.ts +2 -1
  243. package/dist/op/index.d.ts.map +1 -1
  244. package/dist/ownership.d.ts +20 -14
  245. package/dist/ownership.d.ts.map +1 -1
  246. package/package.json +14 -1
  247. package/src/audit/catalog.test.ts +74 -22
  248. package/src/audit/catalog.ts +74 -471
  249. package/src/audit/fetch.test.ts +112 -8
  250. package/src/audit/fetch.ts +53 -8
  251. package/src/audit/proof.ts +4 -2
  252. package/src/audit/report-html.test.ts +14 -7
  253. package/src/audit/report-model.ts +20 -9
  254. package/src/audit/report.test.ts +36 -13
  255. package/src/audit/report.ts +2 -0
  256. package/src/audit/rules-doc.test.ts +8 -6
  257. package/src/audit/rules-doc.ts +13 -5
  258. package/src/cli/commands/audit.ts +23 -18
  259. package/src/cli/commands/component-lint.test.ts +158 -0
  260. package/src/cli/commands/doctor.test.ts +116 -2
  261. package/src/cli/commands/doctor.ts +46 -0
  262. package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
  263. package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
  264. package/src/cli/commands/lexicon-surface-diff.ts +185 -0
  265. package/src/cli/commands/lint.ts +142 -1
  266. package/src/cli/commands/pinned-upgrade.ts +204 -0
  267. package/src/cli/handlers/build.ts +58 -1
  268. package/src/cli/handlers/components.test.ts +504 -0
  269. package/src/cli/handlers/components.ts +404 -0
  270. package/src/cli/handlers/dev.ts +60 -1
  271. package/src/cli/handlers/graph.ts +47 -2
  272. package/src/cli/handlers/misc.ts +45 -1
  273. package/src/cli/handlers/run.test.ts +967 -3
  274. package/src/cli/handlers/run.ts +618 -11
  275. package/src/cli/main.test.ts +66 -0
  276. package/src/cli/main.ts +105 -2
  277. package/src/cli/registry.ts +35 -0
  278. package/src/codegen/fetch.test.ts +84 -3
  279. package/src/codegen/fetch.ts +76 -2
  280. package/src/codegen/lexicon-regen.test.ts +298 -0
  281. package/src/codegen/lexicon-regen.ts +545 -0
  282. package/src/codegen/pinned-upgrade.test.ts +399 -0
  283. package/src/codegen/pinned-upgrade.ts +423 -0
  284. package/src/codegen/rolling-upgrade.test.ts +333 -0
  285. package/src/codegen/rolling-upgrade.ts +320 -0
  286. package/src/codegen/surface-snapshot.test.ts +461 -0
  287. package/src/codegen/surface-snapshot.ts +546 -0
  288. package/src/codegen/version-bump.test.ts +47 -0
  289. package/src/codegen/version-bump.ts +39 -0
  290. package/src/components/README.md +130 -0
  291. package/src/components/SPRAWL-VALIDATION.md +181 -0
  292. package/src/components/__fixtures__/alb-ecs-service.json +51 -0
  293. package/src/components/__fixtures__/dynamodb-infra.json +32 -0
  294. package/src/components/__fixtures__/emr-job-consumer.json +26 -0
  295. package/src/components/__fixtures__/jar-lib-producer.json +16 -0
  296. package/src/components/__fixtures__/lambda-image-processor.json +28 -0
  297. package/src/components/__fixtures__/neo4j-fanout.json +34 -0
  298. package/src/components/__fixtures__/single-host-compose.json +29 -0
  299. package/src/components/auto-release.test.ts +217 -0
  300. package/src/components/auto-release.ts +216 -0
  301. package/src/components/builders.test.ts +45 -0
  302. package/src/components/builders.ts +71 -0
  303. package/src/components/capability-plugin-loader.ts +218 -0
  304. package/src/components/capability-plugin-schema.ts +56 -0
  305. package/src/components/capability-plugin.test.ts +235 -0
  306. package/src/components/capability-plugin.ts +108 -0
  307. package/src/components/capability.ts +117 -0
  308. package/src/components/cli-support.test.ts +579 -0
  309. package/src/components/cli-support.ts +481 -0
  310. package/src/components/component-schema.test.ts +265 -0
  311. package/src/components/component.schema.json +235 -0
  312. package/src/components/component.test.ts +251 -0
  313. package/src/components/component.ts +255 -0
  314. package/src/components/config-defaults.test.ts +263 -0
  315. package/src/components/config-defaults.ts +145 -0
  316. package/src/components/discover.test.ts +352 -0
  317. package/src/components/discover.ts +167 -0
  318. package/src/components/driver-output.ts +71 -0
  319. package/src/components/driver.test.ts +638 -0
  320. package/src/components/driver.ts +690 -0
  321. package/src/components/index.ts +113 -0
  322. package/src/components/manifest-persistence.test.ts +152 -0
  323. package/src/components/manifest-persistence.ts +164 -0
  324. package/src/components/pilots/README.md +33 -0
  325. package/src/components/pilots/alb-ecs.pilot.ts +63 -0
  326. package/src/components/pilots/dynamodb.pilot.ts +59 -0
  327. package/src/components/pilots/jar-emr.pilot.ts +69 -0
  328. package/src/components/pilots/lambda.pilot.ts +55 -0
  329. package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
  330. package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
  331. package/src/components/pilots/pilots.test.ts +126 -0
  332. package/src/components/pilots/project.ts +13 -0
  333. package/src/components/presets/comp007-reduction.test.ts +122 -0
  334. package/src/components/presets/ecs-fargate.test.ts +84 -0
  335. package/src/components/presets/ecs-fargate.ts +103 -0
  336. package/src/components/presets/index.ts +32 -0
  337. package/src/components/presets/lambda.test.ts +35 -0
  338. package/src/components/presets/lambda.ts +53 -0
  339. package/src/components/presets/single-host-compose.test.ts +65 -0
  340. package/src/components/presets/single-host-compose.ts +64 -0
  341. package/src/components/registry.test.ts +112 -0
  342. package/src/components/registry.ts +37 -0
  343. package/src/components/starter-plugin.ts +105 -0
  344. package/src/components/temporal-codegen-loader.ts +41 -0
  345. package/src/components/verbs/__fixtures__/package-lock.json +45 -0
  346. package/src/components/verbs/__fixtures__/pom.xml +37 -0
  347. package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
  348. package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
  349. package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
  350. package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
  351. package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
  352. package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
  353. package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
  354. package/src/components/verbs/bom-writer.test.ts +331 -0
  355. package/src/components/verbs/bom-writer.ts +386 -0
  356. package/src/components/verbs/build-archive.test.ts +251 -0
  357. package/src/components/verbs/build-archive.ts +296 -0
  358. package/src/components/verbs/build.test.ts +222 -0
  359. package/src/components/verbs/build.ts +252 -0
  360. package/src/components/verbs/cloud-executor.ts +181 -0
  361. package/src/components/verbs/component-bom.test.ts +219 -0
  362. package/src/components/verbs/component-bom.ts +189 -0
  363. package/src/components/verbs/index.ts +49 -0
  364. package/src/components/verbs/license-policy.ts +115 -0
  365. package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
  366. package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
  367. package/src/components/verbs/process-runner.test.ts +79 -0
  368. package/src/components/verbs/process-runner.ts +113 -0
  369. package/src/components/verbs/reproducibility.test.ts +24 -0
  370. package/src/components/verbs/reproducibility.ts +100 -0
  371. package/src/components/verbs/sbom-generator.test.ts +96 -0
  372. package/src/components/verbs/sbom-generator.ts +209 -0
  373. package/src/components/verbs/sbom.test.ts +184 -0
  374. package/src/components/verbs/sbom.ts +152 -0
  375. package/src/components/verbs/shell.test.ts +36 -0
  376. package/src/components/verbs/shell.ts +51 -0
  377. package/src/components/verbs/sign.test.ts +210 -0
  378. package/src/components/verbs/sign.ts +350 -0
  379. package/src/components/verbs/stub.ts +34 -0
  380. package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
  381. package/src/components/verbs/tool-sbom-generator.ts +192 -0
  382. package/src/components/verbs/verify.test.ts +159 -0
  383. package/src/components/verbs/verify.ts +179 -0
  384. package/src/components/verbs/vex.ts +182 -0
  385. package/src/components/verbs/vuln-gate.test.ts +201 -0
  386. package/src/components/verbs/vuln-gate.ts +170 -0
  387. package/src/components/verbs/vuln-scan.test.ts +145 -0
  388. package/src/components/verbs/vuln-scan.ts +266 -0
  389. package/src/components/verbs/wait-verify.test.ts +111 -0
  390. package/src/components/verbs/wait-verify.ts +171 -0
  391. package/src/config.test.ts +74 -1
  392. package/src/config.ts +219 -0
  393. package/src/index.ts +32 -0
  394. package/src/lexicon.ts +88 -0
  395. package/src/lifecycle/build-ledger-store.test.ts +217 -0
  396. package/src/lifecycle/build-ledger-store.ts +165 -0
  397. package/src/lifecycle/build-ledger.test.ts +349 -0
  398. package/src/lifecycle/build-ledger.ts +305 -0
  399. package/src/lifecycle/git.test.ts +118 -0
  400. package/src/lifecycle/git.ts +149 -14
  401. package/src/lifecycle/index.ts +5 -0
  402. package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
  403. package/src/lifecycle/oras-referrer-lookup.ts +110 -0
  404. package/src/lifecycle/release-ledger.test.ts +209 -0
  405. package/src/lifecycle/release-ledger.ts +185 -0
  406. package/src/lifecycle/status.test.ts +330 -0
  407. package/src/lifecycle/status.ts +278 -0
  408. package/src/lint/component-checks.ts +148 -0
  409. package/src/lint/config.test.ts +21 -0
  410. package/src/lint/engine.ts +11 -2
  411. package/src/lint/presets/relaxed.json +8 -1
  412. package/src/lint/presets/strict.json +8 -1
  413. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
  414. package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
  415. package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
  416. package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
  417. package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
  418. package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
  419. package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
  420. package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
  421. package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
  422. package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
  423. package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
  424. package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
  425. package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
  426. package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
  427. package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
  428. package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
  429. package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
  430. package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
  431. package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
  432. package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
  433. package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
  434. package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
  435. package/src/lint/rules/comp/comp.test.ts +378 -0
  436. package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
  437. package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
  438. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
  439. package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
  440. package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
  441. package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
  442. package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
  443. package/src/lint/rules/comp/index.ts +37 -0
  444. package/src/lint/rules/comp/support.ts +173 -0
  445. package/src/lsp/lexicon-providers.test.ts +55 -0
  446. package/src/lsp/lexicon-providers.ts +13 -3
  447. package/src/op/activity-registry.test.ts +38 -0
  448. package/src/op/activity-registry.ts +31 -12
  449. package/src/op/activity-runtime.ts +73 -0
  450. package/src/op/builders.ts +92 -0
  451. package/src/op/index.ts +3 -1
  452. package/src/ownership.test.ts +24 -26
  453. package/src/ownership.ts +26 -38
@@ -0,0 +1,690 @@
1
+ /**
2
+ * Thin interpret driver — Phase 1 (#556), saga rollback hardened in Phase 4
3
+ * (#565, epic #551).
4
+ *
5
+ * One generic orchestrator that runs an arbitrary set of components on the
6
+ * local in-process executor: it orders components by `dependsOn` (reusing the
7
+ * same Kahn-layering approach `computeStackGraph` uses for `chant graph
8
+ * --stacks`, see ../build.ts), then for each component in order runs its
9
+ * `deploy` composition by dispatching every step to the `CapabilityRegistry`
10
+ * by `kind`. It resolves the three wiring reference forms from
11
+ * component.schema.json (`@Phase.field` prior-step references,
12
+ * `@<component>.publish.<field>` cross-component artifact references, and
13
+ * passes through `$env.*`/`stackOutput` values as opaque env config), runs
14
+ * `parallel` phases concurrently, rejects a `gate` locally (matching
15
+ * ../op/local-executor.ts's `LocalGateUnsupportedError`), and on terminal
16
+ * failure unwinds every executed step in reverse via that step's capability
17
+ * `rollback`, then runs `onFailure` phases in reverse order (best-effort),
18
+ * mirroring the Op local executor's saga semantics.
19
+ *
20
+ * A step whose capability declares no `rollback` is never silently passed
21
+ * over during unwind: it gets a `"rollback-opted-out"` record (see
22
+ * `rollbackExecuted`), carrying the step's `noRollback` reason when the
23
+ * composition declared one. This mirrors, at run time, the opt-out the
24
+ * COMP003 lint rule (../lint/rules/comp/comp003-mutating-no-rollback.ts)
25
+ * already requires at author time — the driver reports the same fact the
26
+ * lint rule already made a component author state.
27
+ *
28
+ * This unwind runs entirely in-process: if the process crashes mid-rollback
29
+ * on the local executor, nothing resumes it — there is no persisted run state
30
+ * to resume from. That is the documented Temporal boundary (see
31
+ * docs/components/orchestration.mdx#rollback-comes-free and
32
+ * docs/guide/local-vs-temporal.mdx): the *mechanism* (reverse-order unwind
33
+ * calling each capability's `rollback`) is capability-agnostic and works
34
+ * identically on both executors, but *durable resume of a rollback already in
35
+ * progress* is Temporal-only, the same boundary that already applies to
36
+ * forward `onFailure` compensation on an Op.
37
+ *
38
+ * Contains zero per-component logic: nothing in this module branches on a
39
+ * component or capability `kind`/name. All behavior is either generic
40
+ * (ordering, phase/step dispatch, wiring resolution) or delegated to the
41
+ * `CapabilityRegistry` the caller supplies.
42
+ */
43
+
44
+ import { topoSort } from "../codegen/topo-sort";
45
+ import type { CapabilityRegistry, DeployContext } from "./capability";
46
+
47
+ // ── Component-shaped input (mirrors component.schema.json / ./component.ts) ──
48
+
49
+ /** A wiring reference or literal value, as it appears in a step's fields (schema `WiringValue`). */
50
+ export type WiringValue =
51
+ | string
52
+ | { stackOutput: { stack: string; name: string } }
53
+ | Record<string, unknown>;
54
+
55
+ /** A single capability invocation — the leaf unit of a composition (schema `Step`). */
56
+ export interface DriverStep {
57
+ kind: string;
58
+ [param: string]: unknown;
59
+ }
60
+
61
+ /** A gate step — pauses for an external signal; unsupported on the local executor (schema `Gate`). */
62
+ export interface DriverGate {
63
+ kind: "gate";
64
+ signalName: string;
65
+ timeout?: string;
66
+ description?: string;
67
+ }
68
+
69
+ /** One named phase of a deploy composition (schema `Phase`). A step may itself be a nested `Phase` (fan-out). */
70
+ export interface DriverPhase {
71
+ phase: string;
72
+ steps: Array<DriverStep | DriverGate | DriverPhase>;
73
+ parallel?: boolean;
74
+ onFailure?: DriverPhase[];
75
+ }
76
+
77
+ /** The subset of the Component contract the driver needs to run a deploy (schema-shaped; see component.schema.json). */
78
+ export interface DriverComponent {
79
+ name: string;
80
+ dependsOn?: string[];
81
+ deploy: DriverPhase[];
82
+ /** Component-level saga compensation, run in reverse order on terminal failure (schema `rollback`). */
83
+ rollback?: DriverPhase[];
84
+ }
85
+
86
+ function isGateStep(step: DriverStep | DriverGate | DriverPhase): step is DriverGate {
87
+ return (step as { kind?: unknown }).kind === "gate";
88
+ }
89
+
90
+ function isPhaseStep(step: DriverStep | DriverGate | DriverPhase): step is DriverPhase {
91
+ return typeof (step as { phase?: unknown }).phase === "string" && Array.isArray((step as DriverPhase).steps);
92
+ }
93
+
94
+ // ── Errors ────────────────────────────────────────────────────────────────
95
+
96
+ /** Thrown when a component's composition contains a `gate` — gates need a durable runtime, matching chant's Op local executor. */
97
+ export class DriverGateUnsupportedError extends Error {
98
+ constructor(
99
+ public readonly component: string,
100
+ public readonly signalName: string,
101
+ ) {
102
+ super(
103
+ `component "${component}": gate "${signalName}" is not supported on the local executor — ` +
104
+ `gates need a durable runtime. Re-run with a durable (Temporal) backend.`,
105
+ );
106
+ this.name = "DriverGateUnsupportedError";
107
+ }
108
+ }
109
+
110
+ /** Thrown when a dependency cycle is found among `dependsOn` edges. */
111
+ export class DependencyCycleError extends Error {
112
+ constructor(public readonly cycle: string[]) {
113
+ super(`dependency cycle among components: ${cycle.join(" -> ")}`);
114
+ this.name = "DependencyCycleError";
115
+ }
116
+ }
117
+
118
+ /** Thrown when a component's `dependsOn` names a component not present in the run set. */
119
+ export class UnknownDependencyError extends Error {
120
+ constructor(
121
+ public readonly component: string,
122
+ public readonly dependency: string,
123
+ ) {
124
+ super(`component "${component}" depends on unknown component "${dependency}"`);
125
+ this.name = "UnknownDependencyError";
126
+ }
127
+ }
128
+
129
+ /** Thrown on terminal run failure; carries the partial run result for rendering/inspection. */
130
+ export class DriverRunFailure extends Error {
131
+ constructor(public readonly result: DriverRunResult) {
132
+ super(`interpret run failed at component "${result.failedComponent ?? "unknown"}"`);
133
+ this.name = "DriverRunFailure";
134
+ }
135
+ }
136
+
137
+ // ── Records ───────────────────────────────────────────────────────────────
138
+
139
+ export interface DriverStepRecord {
140
+ component: string;
141
+ phase: string;
142
+ kind: string;
143
+ status: "ok" | "fail" | "skipped" | "rolled-back" | "rollback-opted-out";
144
+ durationMs: number;
145
+ output?: unknown;
146
+ error?: string;
147
+ }
148
+
149
+ export interface DriverComponentResult {
150
+ component: string;
151
+ ok: boolean;
152
+ records: DriverStepRecord[];
153
+ }
154
+
155
+ export interface DriverRunResult {
156
+ /** Component order actually attempted, in run order. */
157
+ order: string[];
158
+ /** Parallel-safe waves the order was derived from (see resolveComponentGraph). */
159
+ waves: string[][];
160
+ results: DriverComponentResult[];
161
+ ok: boolean;
162
+ /** Name of the component that terminated the run, if any. */
163
+ failedComponent?: string;
164
+ /**
165
+ * The accumulated cross-component/cross-stack outputs after the run — each
166
+ * component's `publish` output and, for an applied stack, its `cfn-deploy`
167
+ * outputs, keyed by component name. Seeded from `options.componentOutputs`
168
+ * and grown as components complete. The CLI's `--dump-outputs` serializes
169
+ * this so a later, separate run (a downstream CI job) can `--seed-outputs`
170
+ * it and resolve `stackOutput()`/`@<name>.publish.*` references to a
171
+ * component that ran in an earlier job.
172
+ */
173
+ componentOutputs: Record<string, Record<string, unknown>>;
174
+ }
175
+
176
+ // ── Graph: order + parallel-safe waves from `dependsOn` ──────────────────────
177
+
178
+ export interface ComponentGraph {
179
+ /** Flat topological order — every dependency before its dependents. */
180
+ order: string[];
181
+ /** Waves: components in the same wave share no dependency and may run concurrently. */
182
+ waves: string[][];
183
+ }
184
+
185
+ /**
186
+ * Resolve run order and parallel-safe waves from each component's `dependsOn`.
187
+ * Reuses the generic `topoSort` (../codegen/topo-sort.ts) for the flat order,
188
+ * and the same Kahn-layering approach `computeStackGraph` uses for `chant
189
+ * graph --stacks` (../build.ts) to additionally group independent components
190
+ * into waves. This is a plain string-graph algorithm — no lexicon/AttrRef
191
+ * machinery — since a component's `dependsOn` is already a flat name list.
192
+ */
193
+ export function resolveComponentGraph(components: DriverComponent[]): ComponentGraph {
194
+ const byName = new Map(components.map((c) => [c.name, c]));
195
+ for (const c of components) {
196
+ for (const dep of c.dependsOn ?? []) {
197
+ if (!byName.has(dep)) throw new UnknownDependencyError(c.name, dep);
198
+ }
199
+ }
200
+
201
+ // Kahn layering (mirrors computeStackGraph's wave computation in ../build.ts):
202
+ // a component is ready once every dependency it names has already been placed.
203
+ // Computed first because it doubles as cycle detection: `topoSort` below is a
204
+ // plain DFS with no cycle guard, so it must only run once a cycle is ruled out.
205
+ const deps = new Map<string, Set<string>>();
206
+ for (const c of components) deps.set(c.name, new Set(c.dependsOn ?? []));
207
+
208
+ const remaining = new Set(deps.keys());
209
+ const waves: string[][] = [];
210
+ while (remaining.size > 0) {
211
+ const wave = [...remaining]
212
+ .filter((n) => [...deps.get(n)!].every((d) => !remaining.has(d)))
213
+ .sort();
214
+ if (wave.length === 0) {
215
+ throw new DependencyCycleError([...remaining].sort());
216
+ }
217
+ for (const n of wave) remaining.delete(n);
218
+ waves.push(wave);
219
+ }
220
+
221
+ const order = topoSort(
222
+ components,
223
+ (c) => c.name,
224
+ (c) => c.dependsOn ?? [],
225
+ ).map((c) => c.name);
226
+
227
+ return { order, waves };
228
+ }
229
+
230
+ // ── Wiring resolution ─────────────────────────────────────────────────────
231
+
232
+ const PRIOR_STEP_REF = /^@([A-Za-z0-9_ ]+)\.([A-Za-z0-9_.]+)$/;
233
+ const COMPONENT_ARTIFACT_REF = /^@([a-z0-9]+(?:-[a-z0-9]+)*)\.publish\.(uri|digest|key)$/;
234
+
235
+ /** Dot-path lookup, matching ../op/local-executor.ts's resolvePath. */
236
+ function resolvePath(value: unknown, path: string): unknown {
237
+ return path
238
+ .split(".")
239
+ .reduce<unknown>((acc, key) => (acc == null ? acc : (acc as Record<string, unknown>)[key]), value);
240
+ }
241
+
242
+ /**
243
+ * Resolve one wiring value against already-produced outputs. Three reference
244
+ * forms, all resolved here rather than by any component-specific code:
245
+ * - `@Phase.field` — a prior step's output within the same component, keyed
246
+ * by phase name (`phaseOutputs`).
247
+ * - `@<component>.publish.uri|digest|key` — another component's published
248
+ * artifact output (`componentOutputs`), per composition-and-wiring.mdx.
249
+ * - `{ stackOutput: { stack, name } }` — a cross-stack output; Phase 1 has no
250
+ * cross-stack apply-order integration (out of scope per #556), so this
251
+ * resolves via the same `componentOutputs` map keyed by `stack`/`name`,
252
+ * letting tests and callers seed cross-stack values the same way as
253
+ * artifact outputs. `$env.*` and plain literals pass through unchanged —
254
+ * env resolution is the caller's `DeployContext.vars`, not the driver's.
255
+ */
256
+ export function resolveWiring(
257
+ value: WiringValue,
258
+ phaseOutputs: Record<string, Record<string, unknown>>,
259
+ componentOutputs: Record<string, Record<string, unknown>>,
260
+ ): unknown {
261
+ if (typeof value === "string") {
262
+ const priorStep = value.match(PRIOR_STEP_REF);
263
+ if (priorStep) {
264
+ const [, phaseName, field] = priorStep;
265
+ return resolvePath(phaseOutputs[phaseName], field);
266
+ }
267
+ const artifactRef = value.match(COMPONENT_ARTIFACT_REF);
268
+ if (artifactRef) {
269
+ const [, componentName, field] = artifactRef;
270
+ return resolvePath(componentOutputs[componentName]?.publish, field);
271
+ }
272
+ return value; // literal, or `$env.*` — left for the caller to resolve via DeployContext.
273
+ }
274
+ if (value && typeof value === "object" && "stackOutput" in value) {
275
+ const { stack, name } = (value as { stackOutput: { stack: string; name: string } }).stackOutput;
276
+ return resolvePath(componentOutputs[stack], name);
277
+ }
278
+ return value;
279
+ }
280
+
281
+ /**
282
+ * Deep-walk an object, resolving every string/stackOutput wiring value found.
283
+ * Arrays and nested objects are walked too.
284
+ *
285
+ * Exported (not just used internally) so the durable Temporal path
286
+ * (`@intentius/chant-lexicon-temporal`'s component workflow codegen, see
287
+ * epic #551 #589) can resolve a step's wiring the same way inside a Temporal
288
+ * *activity* — the workflow itself only accumulates `phaseOutputs`/
289
+ * `componentOutputs` and passes them through; resolution logic stays in one
290
+ * place so local and durable execution can never silently diverge.
291
+ */
292
+ export function resolveStepInput(
293
+ input: Record<string, unknown>,
294
+ phaseOutputs: Record<string, Record<string, unknown>>,
295
+ componentOutputs: Record<string, Record<string, unknown>>,
296
+ ): Record<string, unknown> {
297
+ const walk = (value: unknown): unknown => {
298
+ if (typeof value === "string") return resolveWiring(value, phaseOutputs, componentOutputs);
299
+ if (Array.isArray(value)) return value.map(walk);
300
+ if (value && typeof value === "object") {
301
+ if ("stackOutput" in value) return resolveWiring(value as WiringValue, phaseOutputs, componentOutputs);
302
+ const out: Record<string, unknown> = {};
303
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = walk(v);
304
+ return out;
305
+ }
306
+ return value;
307
+ };
308
+ return walk(input) as Record<string, unknown>;
309
+ }
310
+
311
+ // ── Step + phase execution ───────────────────────────────────────────────
312
+
313
+ /** A step executed and its resolved input, kept so saga rollback can call the same capability's `rollback` with the same input. */
314
+ interface ExecutedStep {
315
+ step: DriverStep;
316
+ resolvedInput: Record<string, unknown>;
317
+ phaseName: string;
318
+ }
319
+
320
+ class StepFailure extends Error {
321
+ constructor(
322
+ public readonly records: DriverStepRecord[],
323
+ public readonly executed: ExecutedStep[],
324
+ ) {
325
+ super("step failed");
326
+ this.name = "StepFailure";
327
+ }
328
+ }
329
+
330
+ /** Run a single capability step. Never throws for a capability-run failure; returns a fail record instead. */
331
+ async function runCapabilityStep(
332
+ step: DriverStep,
333
+ phaseName: string,
334
+ ctx: DeployContext,
335
+ registry: CapabilityRegistry,
336
+ phaseOutputs: Record<string, Record<string, unknown>>,
337
+ componentOutputs: Record<string, Record<string, unknown>>,
338
+ ): Promise<{ record: DriverStepRecord; resolvedInput: Record<string, unknown>; output?: unknown }> {
339
+ const { kind, ...rest } = step;
340
+ const resolvedInput = resolveStepInput(rest, phaseOutputs, componentOutputs);
341
+ const start = Date.now();
342
+ try {
343
+ const capability = registry.resolve(kind);
344
+ const output = await capability.run(ctx, resolvedInput as never);
345
+ return {
346
+ record: {
347
+ component: ctx.component,
348
+ phase: phaseName,
349
+ kind,
350
+ status: "ok",
351
+ durationMs: Date.now() - start,
352
+ output,
353
+ },
354
+ resolvedInput,
355
+ output,
356
+ };
357
+ } catch (err) {
358
+ return {
359
+ record: {
360
+ component: ctx.component,
361
+ phase: phaseName,
362
+ kind,
363
+ status: "fail",
364
+ durationMs: Date.now() - start,
365
+ error: errMessage(err),
366
+ },
367
+ resolvedInput,
368
+ };
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Run a phase's steps. A step may be a `Gate` (rejected — local executor has
374
+ * no durable wait) or a nested `Phase` (a fan-out unit; recursed into,
375
+ * inheriting `parallel` from its own definition, not its parent's). Steps run
376
+ * sequentially unless `phase.parallel` is set, in which case they run via
377
+ * `Promise.all`, matching ../op/local-executor.ts's phase semantics.
378
+ */
379
+ async function runPhase(
380
+ phaseDef: DriverPhase,
381
+ ctx: DeployContext,
382
+ registry: CapabilityRegistry,
383
+ phaseOutputs: Record<string, Record<string, unknown>>,
384
+ componentOutputs: Record<string, Record<string, unknown>>,
385
+ ): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> {
386
+ const gate = phaseDef.steps.find(isGateStep);
387
+ if (gate) throw new DriverGateUnsupportedError(ctx.component, gate.signalName);
388
+
389
+ const entries = phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s));
390
+
391
+ const runEntry = async (
392
+ entry: DriverStep | DriverPhase,
393
+ ): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean }> => {
394
+ if (isPhaseStep(entry)) {
395
+ try {
396
+ const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs);
397
+ return { ...nested, failed: false };
398
+ } catch (err) {
399
+ if (err instanceof StepFailure) return { records: err.records, executed: err.executed, failed: true };
400
+ throw err;
401
+ }
402
+ }
403
+ const { record, resolvedInput, output } = await runCapabilityStep(
404
+ entry,
405
+ phaseDef.phase,
406
+ ctx,
407
+ registry,
408
+ phaseOutputs,
409
+ componentOutputs,
410
+ );
411
+ if (record.status === "ok") {
412
+ phaseOutputs[phaseDef.phase] = { ...(phaseOutputs[phaseDef.phase] ?? {}), ...(output as object) };
413
+ }
414
+ return {
415
+ records: [record],
416
+ executed: record.status === "ok" ? [{ step: entry, resolvedInput, phaseName: phaseDef.phase }] : [],
417
+ failed: record.status === "fail",
418
+ };
419
+ };
420
+
421
+ if (phaseDef.parallel) {
422
+ const results = await Promise.all(entries.map(runEntry));
423
+ const records = results.flatMap((r) => r.records);
424
+ const executed = results.flatMap((r) => r.executed);
425
+ if (results.some((r) => r.failed)) throw new StepFailure(records, executed);
426
+ return { records, executed };
427
+ }
428
+
429
+ const records: DriverStepRecord[] = [];
430
+ const executed: ExecutedStep[] = [];
431
+ for (let i = 0; i < entries.length; i++) {
432
+ const result = await runEntry(entries[i]);
433
+ records.push(...result.records);
434
+ executed.push(...result.executed);
435
+ if (result.failed) {
436
+ for (const skipped of entries.slice(i + 1)) {
437
+ const skippedKind = isPhaseStep(skipped) ? skipped.phase : (skipped as DriverStep).kind;
438
+ records.push({
439
+ component: ctx.component,
440
+ phase: phaseDef.phase,
441
+ kind: skippedKind,
442
+ status: "skipped",
443
+ durationMs: 0,
444
+ });
445
+ }
446
+ throw new StepFailure(records, executed);
447
+ }
448
+ }
449
+ return { records, executed };
450
+ }
451
+
452
+ /**
453
+ * Roll back every executed step in reverse order via its capability's
454
+ * `rollback`, if declared. Best-effort: a rollback failure is recorded but
455
+ * does not stop the unwind.
456
+ *
457
+ * A step whose capability declares no `rollback` is not silently passed over:
458
+ * it gets its own `"rollback-opted-out"` record, carrying the step's
459
+ * `noRollback` reason when the composition declared one (the same opt-out
460
+ * property COMP003 lint requires at author time — see
461
+ * ../lint/rules/comp/comp003-mutating-no-rollback.ts) or a generic fallback
462
+ * message otherwise. This is decided purely by whether `capability.rollback`
463
+ * exists — never by the step's `kind` or the component's name — so it stays
464
+ * capability-agnostic like the rest of this module.
465
+ */
466
+ async function rollbackExecuted(
467
+ executed: ExecutedStep[],
468
+ ctx: DeployContext,
469
+ registry: CapabilityRegistry,
470
+ ): Promise<DriverStepRecord[]> {
471
+ const records: DriverStepRecord[] = [];
472
+ for (const { step, resolvedInput, phaseName } of [...executed].reverse()) {
473
+ const start = Date.now();
474
+ try {
475
+ const capability = registry.resolve(step.kind);
476
+ if (!capability.rollback) {
477
+ const reason = typeof step.noRollback === "string" ? step.noRollback : undefined;
478
+ records.push({
479
+ component: ctx.component,
480
+ phase: phaseName,
481
+ kind: step.kind,
482
+ status: "rollback-opted-out",
483
+ durationMs: Date.now() - start,
484
+ error: reason ?? `capability "${step.kind}" declares no rollback and the step has no "noRollback" reason`,
485
+ });
486
+ continue;
487
+ }
488
+ await capability.rollback(ctx, resolvedInput as never);
489
+ records.push({
490
+ component: ctx.component,
491
+ phase: phaseName,
492
+ kind: step.kind,
493
+ status: "rolled-back",
494
+ durationMs: Date.now() - start,
495
+ });
496
+ } catch (err) {
497
+ records.push({
498
+ component: ctx.component,
499
+ phase: phaseName,
500
+ kind: step.kind,
501
+ status: "fail",
502
+ durationMs: Date.now() - start,
503
+ error: errMessage(err),
504
+ });
505
+ }
506
+ }
507
+ return records;
508
+ }
509
+
510
+ // ── Per-component deploy ─────────────────────────────────────────────────
511
+
512
+ /**
513
+ * Run one component's `deploy` composition. On terminal failure: unwind every
514
+ * executed step in reverse via its capability's `rollback` (saga
515
+ * compensation), then run the component's declared `rollback` phases in
516
+ * reverse order (best-effort), matching ../op/local-executor.ts's `onFailure`
517
+ * handling. Cross-component artifact outputs this component published (if
518
+ * any) are recorded into `componentOutputs` under its own name so downstream
519
+ * components can reference `@<name>.publish.*`.
520
+ */
521
+ export async function runComponentDeploy(
522
+ component: DriverComponent,
523
+ ctx: DeployContext,
524
+ registry: CapabilityRegistry,
525
+ componentOutputs: Record<string, Record<string, unknown>>,
526
+ ): Promise<DriverComponentResult> {
527
+ const phaseOutputs: Record<string, Record<string, unknown>> = {};
528
+ const records: DriverStepRecord[] = [];
529
+ const allExecuted: ExecutedStep[] = [];
530
+
531
+ try {
532
+ for (const phaseDef of component.deploy) {
533
+ const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs);
534
+ records.push(...result.records);
535
+ allExecuted.push(...result.executed);
536
+ }
537
+ } catch (err) {
538
+ if (err instanceof StepFailure) {
539
+ records.push(...err.records);
540
+ allExecuted.push(...err.executed);
541
+ } else {
542
+ throw err;
543
+ }
544
+
545
+ records.push(...(await rollbackExecuted(allExecuted, ctx, registry)));
546
+
547
+ for (const phaseDef of [...(component.rollback ?? [])].reverse()) {
548
+ try {
549
+ const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs);
550
+ records.push(...result.records);
551
+ } catch (compErr) {
552
+ if (compErr instanceof StepFailure) records.push(...compErr.records);
553
+ else throw compErr;
554
+ }
555
+ }
556
+
557
+ return { component: component.name, ok: false, records };
558
+ }
559
+
560
+ // Publish-family outputs (publish-image / publish-artifact / load-image-on-host all
561
+ // return at least one of uri/digest/key — see ../verbs/publish.ts) become this
562
+ // component's `@<name>.publish.*` for downstream cross-component references.
563
+ const publishOutput = findPublishOutput(phaseOutputs);
564
+ if (publishOutput) {
565
+ componentOutputs[component.name] = { ...componentOutputs[component.name], publish: publishOutput };
566
+ }
567
+
568
+ // Stack outputs from an apply step (cfn-deploy returns `CfnDeployOutput.outputs`)
569
+ // become resolvable by downstream components' `stackOutput(<name>, ...)`
570
+ // references — the cross-stack apply-order integration deferred in #556.
571
+ // Merged at the top level of this component's entry (peer to `publish`, which
572
+ // is namespaced under its own key), so `resolveWiring`'s stackOutput branch —
573
+ // `resolvePath(componentOutputs[stack], name)` — finds each output by name.
574
+ // Keyed by the component's own name, which by convention is the stack name a
575
+ // `stackOutput` reference targets (see the pilots and composition-and-wiring.mdx).
576
+ const stackOutputs = findStackOutputs(phaseOutputs);
577
+ if (stackOutputs) {
578
+ componentOutputs[component.name] = { ...componentOutputs[component.name], ...stackOutputs };
579
+ }
580
+
581
+ return { component: component.name, ok: true, records };
582
+ }
583
+
584
+ /**
585
+ * Find the output of the last step that looks like a publish result (carries
586
+ * `uri`, `digest`, or `key`) across every phase this component ran, so the
587
+ * driver can populate `@<component>.publish.*` for a downstream consumer.
588
+ * Generic by shape, not by capability `kind` — any capability whose output
589
+ * carries one of these fields is eligible, keeping the driver free of
590
+ * per-capability branching.
591
+ */
592
+ function findPublishOutput(
593
+ phaseOutputs: Record<string, Record<string, unknown>>,
594
+ ): Record<string, unknown> | undefined {
595
+ let found: Record<string, unknown> | undefined;
596
+ for (const output of Object.values(phaseOutputs)) {
597
+ if (output && ("uri" in output || "digest" in output || "key" in output)) {
598
+ found = { ...found, ...output };
599
+ }
600
+ }
601
+ return found;
602
+ }
603
+
604
+ /**
605
+ * Find the stack `outputs` map produced by an apply-style step (cfn-deploy
606
+ * returns `CfnDeployOutput.outputs`) across every phase this component ran, so
607
+ * a deployed stack's outputs can seed downstream `stackOutput()` resolution.
608
+ * Generic by shape — any output carrying an `outputs` record is eligible —
609
+ * keeping the driver free of per-capability branching, the same way
610
+ * `findPublishOutput` is.
611
+ */
612
+ function findStackOutputs(
613
+ phaseOutputs: Record<string, Record<string, unknown>>,
614
+ ): Record<string, unknown> | undefined {
615
+ let found: Record<string, unknown> | undefined;
616
+ for (const output of Object.values(phaseOutputs)) {
617
+ const outputs = output?.outputs;
618
+ if (outputs && typeof outputs === "object" && !Array.isArray(outputs)) {
619
+ found = { ...found, ...(outputs as Record<string, unknown>) };
620
+ }
621
+ }
622
+ return found;
623
+ }
624
+
625
+ function errMessage(err: unknown): string {
626
+ return err instanceof Error ? err.message : String(err);
627
+ }
628
+
629
+ // ── Public API: the interpret driver ─────────────────────────────────────
630
+
631
+ export interface InterpretRunOptions {
632
+ /** Target environment name, threaded into every capability's `DeployContext`. */
633
+ env: string;
634
+ /** Environment config resolved ahead of the run (registry URLs, cluster names, ...) — passed through as `DeployContext.vars`. */
635
+ vars?: Record<string, unknown>;
636
+ /** Pre-seeded cross-component/cross-stack outputs (e.g. from a prior run, or a caller resolving `stackOutput` externally). Merged with outputs this run produces. */
637
+ componentOutputs?: Record<string, Record<string, unknown>>;
638
+ }
639
+
640
+ /**
641
+ * Run a set of components to completion on the local in-process executor:
642
+ * resolve dependency order and parallel-safe waves from `dependsOn`, then run
643
+ * each wave's components (independent components within a wave run
644
+ * concurrently; components across waves run in wave order), dispatching
645
+ * every step to `registry` by `kind`. Stops the whole run at the first failed
646
+ * component (after that component's own saga rollback completes), returning
647
+ * a result with `ok: false`; throws `DriverRunFailure` carrying that result
648
+ * so callers can choose to inspect or propagate it.
649
+ *
650
+ * Zero per-component logic: this function and everything it calls dispatches
651
+ * purely on the generic `Component`/`Phase`/`Step` shapes and the registry —
652
+ * no branch anywhere names a specific component or capability.
653
+ */
654
+ export async function runInterpretDriver(
655
+ components: DriverComponent[],
656
+ registry: CapabilityRegistry,
657
+ options: InterpretRunOptions,
658
+ ): Promise<DriverRunResult> {
659
+ const { order, waves } = resolveComponentGraph(components);
660
+ const byName = new Map(components.map((c) => [c.name, c]));
661
+ const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
662
+
663
+ const results: DriverComponentResult[] = [];
664
+ let failedComponent: string | undefined;
665
+
666
+ waveLoop: for (const wave of waves) {
667
+ const waveComponents = wave.map((name) => byName.get(name)!);
668
+ const waveResults = await Promise.all(
669
+ waveComponents.map((component) =>
670
+ runComponentDeploy(
671
+ component,
672
+ { env: options.env, component: component.name, vars: options.vars },
673
+ registry,
674
+ componentOutputs,
675
+ ),
676
+ ),
677
+ );
678
+ results.push(...waveResults);
679
+ const failed = waveResults.find((r) => !r.ok);
680
+ if (failed) {
681
+ failedComponent = failed.component;
682
+ break waveLoop;
683
+ }
684
+ }
685
+
686
+ const ok = failedComponent === undefined;
687
+ const result: DriverRunResult = { order, waves, results, ok, failedComponent, componentOutputs };
688
+ if (!ok) throw new DriverRunFailure(result);
689
+ return result;
690
+ }