@intentius/chant 0.124.32 → 0.124.34

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 (320) hide show
  1. package/dist/ci-jobs.d.ts +4 -0
  2. package/dist/ci-jobs.d.ts.map +1 -1
  3. package/dist/ci-jobs.js +9 -2
  4. package/dist/cli/handlers/build.d.ts.map +1 -1
  5. package/dist/cli/handlers/build.js +18 -3
  6. package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
  7. package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
  8. package/dist/cli/handlers/lifecycle-health.js +150 -0
  9. package/dist/cli/handlers/lifecycle.d.ts +26 -0
  10. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  11. package/dist/cli/handlers/lifecycle.js +1 -1
  12. package/dist/cli/handlers/pr.d.ts +2 -0
  13. package/dist/cli/handlers/pr.d.ts.map +1 -1
  14. package/dist/cli/handlers/pr.js +124 -2
  15. package/dist/cli/handlers/promote.d.ts.map +1 -1
  16. package/dist/cli/handlers/promote.js +277 -74
  17. package/dist/cli/handlers/release.d.ts +16 -1
  18. package/dist/cli/handlers/release.d.ts.map +1 -1
  19. package/dist/cli/handlers/release.js +83 -11
  20. package/dist/cli/main.d.ts.map +1 -1
  21. package/dist/cli/main.js +99 -22
  22. package/dist/cli/parse-args.d.ts.map +1 -1
  23. package/dist/cli/parse-args.js +30 -1
  24. package/dist/cli/registry.d.ts +21 -5
  25. package/dist/cli/registry.d.ts.map +1 -1
  26. package/dist/components/deploy-inputs.d.ts +91 -0
  27. package/dist/components/deploy-inputs.d.ts.map +1 -0
  28. package/dist/components/deploy-inputs.js +184 -0
  29. package/dist/components/driver.d.ts.map +1 -1
  30. package/dist/components/driver.js +9 -0
  31. package/dist/components/gate-plan.d.ts +8 -0
  32. package/dist/components/gate-plan.d.ts.map +1 -1
  33. package/dist/components/gate-plan.js +6 -0
  34. package/dist/components/pr-pipeline.d.ts +33 -10
  35. package/dist/components/pr-pipeline.d.ts.map +1 -1
  36. package/dist/components/pr-pipeline.js +42 -11
  37. package/dist/components/pr-run.d.ts +1 -1
  38. package/dist/components/pr-run.d.ts.map +1 -1
  39. package/dist/components/promote-rounds.d.ts +102 -0
  40. package/dist/components/promote-rounds.d.ts.map +1 -0
  41. package/dist/components/promote-rounds.js +174 -0
  42. package/dist/components/release-compile.d.ts +37 -2
  43. package/dist/components/release-compile.d.ts.map +1 -1
  44. package/dist/components/release-compile.js +64 -8
  45. package/dist/components/release-gate.d.ts +30 -0
  46. package/dist/components/release-gate.d.ts.map +1 -1
  47. package/dist/components/release-gate.js +92 -42
  48. package/dist/components/release-promote.d.ts +17 -3
  49. package/dist/components/release-promote.d.ts.map +1 -1
  50. package/dist/components/release-promote.js +79 -18
  51. package/dist/components/verbs/bom-writer.d.ts +1 -1
  52. package/dist/components/verbs/bom-writer.d.ts.map +1 -1
  53. package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
  54. package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
  55. package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
  56. package/dist/components/wave-plan.d.ts +10 -0
  57. package/dist/components/wave-plan.d.ts.map +1 -1
  58. package/dist/components/wave-plan.js +32 -4
  59. package/dist/graph-declared.d.ts +4 -0
  60. package/dist/graph-declared.d.ts.map +1 -1
  61. package/dist/graph-declared.js +11 -1
  62. package/dist/graph-detail.d.ts.map +1 -1
  63. package/dist/graph-detail.js +32 -7
  64. package/dist/health.d.ts +110 -0
  65. package/dist/health.d.ts.map +1 -0
  66. package/dist/health.js +105 -0
  67. package/dist/health.schema.json +191 -0
  68. package/dist/lexicon.d.ts +27 -0
  69. package/dist/lexicon.d.ts.map +1 -1
  70. package/dist/lifecycle/merge-record.d.ts +104 -0
  71. package/dist/lifecycle/merge-record.d.ts.map +1 -0
  72. package/dist/lifecycle/merge-record.js +102 -0
  73. package/dist/lifecycle/publish-attachments.d.ts +8 -5
  74. package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
  75. package/dist/lifecycle/publish-attachments.js +27 -13
  76. package/dist/lifecycle/publish-npm.d.ts +3 -2
  77. package/dist/lifecycle/publish-npm.d.ts.map +1 -1
  78. package/dist/lifecycle/publish-npm.js +4 -3
  79. package/dist/lifecycle/release-at.d.ts +40 -0
  80. package/dist/lifecycle/release-at.d.ts.map +1 -0
  81. package/dist/lifecycle/release-at.js +83 -0
  82. package/dist/lifecycle/release-cut.d.ts.map +1 -1
  83. package/dist/lifecycle/release-cut.js +23 -12
  84. package/dist/lifecycle/release-publish.d.ts +28 -12
  85. package/dist/lifecycle/release-publish.d.ts.map +1 -1
  86. package/dist/lifecycle/release-publish.js +29 -17
  87. package/dist/lifecycle/release-store.d.ts +25 -2
  88. package/dist/lifecycle/release-store.d.ts.map +1 -1
  89. package/dist/lifecycle/release-store.js +40 -13
  90. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  91. package/dist/lifecycle/run-ledger.js +1 -0
  92. package/dist/lint/rules/op/index.d.ts +2 -1
  93. package/dist/lint/rules/op/index.d.ts.map +1 -1
  94. package/dist/lint/rules/op/index.js +4 -2
  95. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
  96. package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
  97. package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
  98. package/dist/op/activities/activity-contracts.d.ts +5 -0
  99. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  100. package/dist/op/activities/activity-contracts.js +3 -0
  101. package/dist/op/activities/shell.d.ts +38 -0
  102. package/dist/op/activities/shell.d.ts.map +1 -1
  103. package/dist/op/activities/shell.js +89 -3
  104. package/dist/op/builders.d.ts +7 -0
  105. package/dist/op/builders.d.ts.map +1 -1
  106. package/dist/op/builders.js +7 -0
  107. package/dist/op/runtime.d.ts +2 -0
  108. package/dist/op/runtime.d.ts.map +1 -1
  109. package/dist/pr-loop.d.ts +14 -3
  110. package/dist/pr-loop.d.ts.map +1 -1
  111. package/dist/pr-loop.js +15 -5
  112. package/dist/pr-report-url.d.ts +2 -2
  113. package/dist/pr-report-url.d.ts.map +1 -1
  114. package/dist/runner-start.d.ts +179 -0
  115. package/dist/runner-start.d.ts.map +1 -0
  116. package/dist/runner-start.js +277 -0
  117. package/dist/wave-runtime.mjs +236 -229
  118. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  119. package/dist/workspace/__fixtures__/contract-repo.js +13 -2
  120. package/dist/workspace/check.schema.json +88 -2
  121. package/dist/workspace/checks/ci-workflow.d.ts +17 -0
  122. package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
  123. package/dist/workspace/checks/ci-workflow.js +123 -0
  124. package/dist/workspace/checks/security.d.ts +50 -0
  125. package/dist/workspace/checks/security.d.ts.map +1 -0
  126. package/dist/workspace/checks/security.js +305 -0
  127. package/dist/workspace/checks.d.ts +2 -0
  128. package/dist/workspace/checks.d.ts.map +1 -1
  129. package/dist/workspace/checks.js +8 -0
  130. package/dist/workspace/ci-builds.d.ts +163 -0
  131. package/dist/workspace/ci-builds.d.ts.map +1 -0
  132. package/dist/workspace/ci-builds.js +315 -0
  133. package/dist/workspace/ci-green-cli.d.ts +2 -1
  134. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  135. package/dist/workspace/ci-green-cli.js +25 -5
  136. package/dist/workspace/ci-green-forge.d.ts +12 -0
  137. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  138. package/dist/workspace/ci-green-forge.js +12 -0
  139. package/dist/workspace/ci-green-workflow.d.ts +55 -1
  140. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  141. package/dist/workspace/ci-green-workflow.js +128 -1
  142. package/dist/workspace/ci-green.d.ts +89 -4
  143. package/dist/workspace/ci-green.d.ts.map +1 -1
  144. package/dist/workspace/ci-green.js +310 -23
  145. package/dist/workspace/ci-init.d.ts +100 -0
  146. package/dist/workspace/ci-init.d.ts.map +1 -0
  147. package/dist/workspace/ci-init.js +434 -0
  148. package/dist/workspace/ci-last-green.schema.json +49 -0
  149. package/dist/workspace/ci-list.schema.json +35 -1
  150. package/dist/workspace/ci-run-index.d.ts +1 -1
  151. package/dist/workspace/ci-run-index.d.ts.map +1 -1
  152. package/dist/workspace/ci-run-index.schema.json +1 -1
  153. package/dist/workspace/ci-run.d.ts +12 -6
  154. package/dist/workspace/ci-run.d.ts.map +1 -1
  155. package/dist/workspace/ci-run.js +72 -16
  156. package/dist/workspace/ci-runner-start.d.ts +77 -0
  157. package/dist/workspace/ci-runner-start.d.ts.map +1 -0
  158. package/dist/workspace/ci-runner-start.js +185 -0
  159. package/dist/workspace/ci-serve.d.ts +76 -7
  160. package/dist/workspace/ci-serve.d.ts.map +1 -1
  161. package/dist/workspace/ci-serve.js +153 -19
  162. package/dist/workspace/ci-workflow.d.ts +91 -0
  163. package/dist/workspace/ci-workflow.d.ts.map +1 -0
  164. package/dist/workspace/ci-workflow.js +33 -0
  165. package/dist/workspace/declaration.d.ts +35 -0
  166. package/dist/workspace/declaration.d.ts.map +1 -1
  167. package/dist/workspace/declaration.js +12 -2
  168. package/dist/workspace/declaration.schema.json +51 -0
  169. package/dist/workspace/init.js +2 -2
  170. package/dist/workspace/ls.d.ts +2 -0
  171. package/dist/workspace/ls.d.ts.map +1 -1
  172. package/dist/workspace/ls.js +1 -1
  173. package/dist/workspace/ls.schema.json +2 -1
  174. package/dist/workspace/member-pipeline.d.ts +1 -0
  175. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  176. package/dist/workspace/member-pipeline.js +2 -0
  177. package/dist/workspace/merge-record.schema.json +49 -0
  178. package/dist/workspace/pr-report.schema.json +3 -3
  179. package/dist/workspace/reason-codes.d.ts +26 -0
  180. package/dist/workspace/reason-codes.d.ts.map +1 -1
  181. package/dist/workspace/reason-codes.js +28 -0
  182. package/dist/workspace/release-candidates.d.ts +129 -0
  183. package/dist/workspace/release-candidates.d.ts.map +1 -0
  184. package/dist/workspace/release-candidates.js +233 -0
  185. package/dist/workspace/release-candidates.schema.json +353 -0
  186. package/dist/workspace/release-workflow.d.ts +31 -0
  187. package/dist/workspace/release-workflow.d.ts.map +1 -0
  188. package/dist/workspace/release-workflow.js +150 -0
  189. package/dist/workspace/security.d.ts +40 -0
  190. package/dist/workspace/security.d.ts.map +1 -0
  191. package/dist/workspace/security.js +99 -0
  192. package/package.json +8 -1
  193. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
  194. package/src/ci-jobs.ts +11 -2
  195. package/src/cli/handlers/build.test.ts +36 -0
  196. package/src/cli/handlers/build.ts +18 -3
  197. package/src/cli/handlers/lifecycle-health.test.ts +123 -0
  198. package/src/cli/handlers/lifecycle-health.ts +157 -0
  199. package/src/cli/handlers/lifecycle.ts +1 -1
  200. package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
  201. package/src/cli/handlers/pr-record.test.ts +201 -0
  202. package/src/cli/handlers/pr.test.ts +22 -0
  203. package/src/cli/handlers/pr.ts +127 -4
  204. package/src/cli/handlers/promote.test.ts +17 -0
  205. package/src/cli/handlers/promote.ts +296 -84
  206. package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
  207. package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
  208. package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
  209. package/src/cli/handlers/release.e2e.test.ts +19 -28
  210. package/src/cli/handlers/release.ts +81 -11
  211. package/src/cli/main.ts +99 -22
  212. package/src/cli/parse-args.ts +22 -1
  213. package/src/cli/registry.ts +21 -5
  214. package/src/components/deploy-inputs.test.ts +108 -0
  215. package/src/components/deploy-inputs.ts +225 -0
  216. package/src/components/driver.ts +11 -0
  217. package/src/components/gate-plan.ts +9 -0
  218. package/src/components/pr-pipeline.ts +56 -15
  219. package/src/components/pr-run.ts +1 -1
  220. package/src/components/promote-rounds.test.ts +220 -0
  221. package/src/components/promote-rounds.ts +232 -0
  222. package/src/components/release-compile.test.ts +34 -0
  223. package/src/components/release-compile.ts +89 -8
  224. package/src/components/release-gate.test.ts +47 -0
  225. package/src/components/release-gate.ts +111 -46
  226. package/src/components/release-promote.test.ts +55 -2
  227. package/src/components/release-promote.ts +93 -23
  228. package/src/components/verbs/bom-writer.ts +1 -1
  229. package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
  230. package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
  231. package/src/components/wave-plan.ts +32 -4
  232. package/src/graph-declared-exports.test.ts +40 -0
  233. package/src/graph-declared.ts +9 -1
  234. package/src/graph-detail.test.ts +57 -0
  235. package/src/graph-detail.ts +33 -7
  236. package/src/health.schema.json +191 -0
  237. package/src/health.test.ts +62 -0
  238. package/src/health.ts +178 -0
  239. package/src/lexicon.ts +41 -1
  240. package/src/lifecycle/merge-record.test.ts +111 -0
  241. package/src/lifecycle/merge-record.ts +154 -0
  242. package/src/lifecycle/publish-attachments.ts +25 -13
  243. package/src/lifecycle/publish-npm.ts +4 -3
  244. package/src/lifecycle/publish-targets.test.ts +34 -4
  245. package/src/lifecycle/release-at.test.ts +48 -0
  246. package/src/lifecycle/release-at.ts +100 -0
  247. package/src/lifecycle/release-cut.test.ts +41 -0
  248. package/src/lifecycle/release-cut.ts +22 -11
  249. package/src/lifecycle/release-publish.test.ts +43 -13
  250. package/src/lifecycle/release-publish.ts +38 -18
  251. package/src/lifecycle/release-store.ts +57 -13
  252. package/src/lifecycle/run-ledger.test.ts +13 -0
  253. package/src/lifecycle/run-ledger.ts +1 -0
  254. package/src/lint/rules/op/index.ts +4 -2
  255. package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
  256. package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
  257. package/src/op/activities/activity-contracts.ts +3 -0
  258. package/src/op/activities/shell-container.e2e.test.ts +67 -0
  259. package/src/op/activities/shell.test.ts +44 -1
  260. package/src/op/activities/shell.ts +110 -3
  261. package/src/op/builders.ts +7 -0
  262. package/src/op/runtime.ts +2 -0
  263. package/src/op/step-console.test.ts +4 -2
  264. package/src/pr-loop.test.ts +29 -0
  265. package/src/pr-loop.ts +26 -7
  266. package/src/pr-report-url.ts +2 -2
  267. package/src/runner-start-bundle.test.ts +29 -0
  268. package/src/runner-start.test.ts +179 -0
  269. package/src/runner-start.ts +329 -0
  270. package/src/workspace/__fixtures__/contract-repo.ts +11 -2
  271. package/src/workspace/check-contract.test.ts +4 -2
  272. package/src/workspace/check.schema.json +88 -2
  273. package/src/workspace/checks/ci-workflow.test.ts +150 -0
  274. package/src/workspace/checks/ci-workflow.ts +129 -0
  275. package/src/workspace/checks/security.test.ts +138 -0
  276. package/src/workspace/checks/security.ts +305 -0
  277. package/src/workspace/checks.ts +8 -0
  278. package/src/workspace/ci-builds.e2e.test.ts +208 -0
  279. package/src/workspace/ci-builds.test.ts +174 -0
  280. package/src/workspace/ci-builds.ts +385 -0
  281. package/src/workspace/ci-green-builds.test.ts +180 -0
  282. package/src/workspace/ci-green-cli.test.ts +54 -0
  283. package/src/workspace/ci-green-cli.ts +26 -5
  284. package/src/workspace/ci-green-forge.ts +24 -0
  285. package/src/workspace/ci-green-workflow.test.ts +84 -1
  286. package/src/workspace/ci-green-workflow.ts +139 -2
  287. package/src/workspace/ci-green.test.ts +5 -2
  288. package/src/workspace/ci-green.ts +359 -27
  289. package/src/workspace/ci-init.test.ts +159 -0
  290. package/src/workspace/ci-init.ts +445 -0
  291. package/src/workspace/ci-last-green.schema.json +49 -0
  292. package/src/workspace/ci-list.schema.json +35 -1
  293. package/src/workspace/ci-report-url.test.ts +6 -1
  294. package/src/workspace/ci-run-index.schema.json +1 -1
  295. package/src/workspace/ci-run-index.ts +1 -1
  296. package/src/workspace/ci-run.test.ts +24 -7
  297. package/src/workspace/ci-run.ts +78 -16
  298. package/src/workspace/ci-runner-start.test.ts +151 -0
  299. package/src/workspace/ci-runner-start.ts +193 -0
  300. package/src/workspace/ci-serve-builds.test.ts +86 -0
  301. package/src/workspace/ci-serve.test.ts +59 -0
  302. package/src/workspace/ci-serve.ts +177 -22
  303. package/src/workspace/ci-workflow.ts +71 -0
  304. package/src/workspace/declaration.schema.json +51 -0
  305. package/src/workspace/declaration.test.ts +17 -0
  306. package/src/workspace/declaration.ts +53 -3
  307. package/src/workspace/init.ts +2 -2
  308. package/src/workspace/ls.schema.json +2 -1
  309. package/src/workspace/ls.ts +3 -2
  310. package/src/workspace/member-pipeline.ts +2 -1
  311. package/src/workspace/merge-record.schema.json +49 -0
  312. package/src/workspace/pr-report.schema.json +3 -3
  313. package/src/workspace/reason-codes.test.ts +4 -0
  314. package/src/workspace/reason-codes.ts +28 -0
  315. package/src/workspace/release-candidates.schema.json +353 -0
  316. package/src/workspace/release-candidates.test.ts +106 -0
  317. package/src/workspace/release-candidates.ts +315 -0
  318. package/src/workspace/release-workflow.ts +167 -0
  319. package/src/workspace/security.test.ts +70 -0
  320. package/src/workspace/security.ts +118 -0
@@ -39,6 +39,14 @@
39
39
  * planned by that capability, as a pull request's plan is. A plan that
40
40
  * returns no change-set part is a hole, since nothing says what it changes.
41
41
  *
42
+ * A dependency the promote also deploys is read from its plan, not from the
43
+ * estate (#4168 step 4, the rule #3999 gave a pull request): a dependent is
44
+ * planned against the outputs its upstream's plan says it will expose, and a
45
+ * reference the plan can't say is listed in `unknownInputs`. Any other
46
+ * dependency is read as it stands. What each component was planned against is
47
+ * kept in `reads`, so the promote can hold it back when an upstream applies
48
+ * as another value (`./promote-rounds.ts`).
49
+ *
42
50
  * Any other component is a `chant` member compared with what the
43
51
  * environment runs now: its latest deploy record and the release that record
44
52
  * names. The artifact digest, each template the release's target records for
@@ -51,7 +59,7 @@
51
59
  * component a promote, rollback or redeploy left failed or gated with nothing
52
60
  * recorded since (#4035): its record names what ran before that run.
53
61
  */
54
- import { deployContext, resolveComponentGraph, resolveStepInput } from "./driver.js";
62
+ import { deployContext, resolveComponentGraph, resolveStepInput, unresolvedWiringReferences } from "./driver.js";
55
63
  import { componentPlanDigest } from "./gate-plan.js";
56
64
  import { gateName, RELEASE_GATE_OP } from "../op/gate-name.js";
57
65
  import { capabilitySteps, planContext, readComponentOutputs } from "./wave-plan.js";
@@ -187,66 +195,108 @@ export function chantComponentPart(input) {
187
195
  * that fails to plan: it becomes a failed member.
188
196
  */
189
197
  export async function planReleaseChangeSet(input) {
198
+ return (await planReleaseSet(input)).doc;
199
+ }
200
+ /**
201
+ * {@link planReleaseChangeSet}, with what each component read. Components are
202
+ * planned a wave at a time, so a dependency the promote deploys has planned
203
+ * before its dependents read its outputs.
204
+ */
205
+ export async function planReleaseSet(input) {
190
206
  const { release, env, registry } = input;
191
207
  const components = [...input.components];
192
208
  const byName = new Map(components.map((c) => [c.name, c]));
193
209
  const outputs = { ...(input.seededOutputs ?? {}) };
194
210
  const read = new Set(Object.keys(outputs));
195
211
  const parts = [];
196
- const { order } = resolveComponentGraph(components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) })));
197
- for (const name of order) {
198
- const component = byName.get(name);
199
- const digest = input.digests.get(name) ?? "";
200
- const plannable = capabilitySteps(component.deploy).filter((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
201
- if (plannable.length === 0) {
202
- parts.push(chantComponentPart({ component, release, env, digest, current: input.current.get(name), unsettled: input.unsettled?.get(name) }));
203
- continue;
204
- }
205
- const ctx = deployContext({ env }, name);
206
- try {
207
- for (const dep of component.dependsOn ?? []) {
208
- if (read.has(dep) || !byName.has(dep))
209
- continue;
210
- read.add(dep);
211
- const found = await readComponentOutputs(byName.get(dep), registry, deployContext({ env }, dep), outputs);
212
- if (found)
213
- outputs[dep] = found;
212
+ const plannedBy = new Map();
213
+ const planned = {};
214
+ const unknownInputs = {};
215
+ const reads = {};
216
+ const inPlan = (name) => input.only === undefined || input.only.has(name);
217
+ const isPlannable = (c) => capabilitySteps(c.deploy).some((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
218
+ const { waves } = resolveComponentGraph(components.map((c) => ({ ...c, dependsOn: (c.dependsOn ?? []).filter((d) => byName.has(d)) })));
219
+ for (const wave of waves) {
220
+ const planning = wave.filter(inPlan);
221
+ for (const name of planning) {
222
+ const component = byName.get(name);
223
+ const digest = input.digests.get(name) ?? "";
224
+ const plannable = capabilitySteps(component.deploy).filter((s) => registry.has(s.kind) && registry.resolve(s.kind).plan !== undefined);
225
+ if (plannable.length === 0) {
226
+ parts.push(chantComponentPart({ component, release, env, digest, current: input.current.get(name), unsettled: input.unsettled?.get(name) }));
227
+ continue;
228
+ }
229
+ const ctx = deployContext({ env }, name);
230
+ try {
231
+ for (const dep of component.dependsOn ?? []) {
232
+ if (read.has(dep) || !byName.has(dep))
233
+ continue;
234
+ read.add(dep);
235
+ // A dependency this promote plans exposes what its plan says (#3999).
236
+ if (plannedBy.has(dep) && isPlannable(byName.get(dep))) {
237
+ const known = plannedBy.get(dep)?.known ?? {};
238
+ outputs[dep] = { ...known };
239
+ planned[dep] = Object.keys(known).sort();
240
+ continue;
241
+ }
242
+ const found = await readComponentOutputs(byName.get(dep), registry, deployContext({ env }, dep), outputs);
243
+ if (found)
244
+ outputs[dep] = found;
245
+ }
246
+ reads[name] = Object.fromEntries((component.dependsOn ?? []).filter((d) => d in outputs).map((d) => [d, structuredClone(outputs[d])]));
247
+ const unknown = new Set();
248
+ let exposes;
249
+ for (const step of plannable) {
250
+ const { kind, ...rest } = step;
251
+ for (const r of unresolvedWiringReferences(rest, outputs))
252
+ if (plannedBy.has(r.source) && !r.publish)
253
+ unknown.add(r.ref);
254
+ const result = await registry.resolve(kind).plan(planContext(ctx, rest, outputs), resolveStepInput(rest, {}, outputs));
255
+ if (result.outputs)
256
+ exposes = { known: { ...(exposes?.known ?? {}), ...result.outputs.known }, unknown: [...new Set([...(exposes?.unknown ?? []), ...result.outputs.unknown])].sort() };
257
+ parts.push(result.changeSet ?? {
258
+ member: {
259
+ member: result.member,
260
+ lexicon: "chant",
261
+ planner: "chant",
262
+ status: "planned",
263
+ planDigest: result.planDigest,
264
+ holes: [{ address: result.member, reason: `its ${kind} plan describes no changes, so what it changes is unknown` }],
265
+ },
266
+ entries: [],
267
+ });
268
+ }
269
+ plannedBy.set(name, exposes);
270
+ if (unknown.size > 0)
271
+ unknownInputs[name] = [...unknown].sort();
214
272
  }
215
- for (const step of plannable) {
216
- const { kind, ...rest } = step;
217
- const planned = await registry.resolve(kind).plan(planContext(ctx, rest, outputs), resolveStepInput(rest, {}, outputs));
218
- parts.push(planned.changeSet ?? {
273
+ catch (err) {
274
+ plannedBy.set(name, undefined);
275
+ parts.push({
219
276
  member: {
220
- member: planned.member,
277
+ member: name,
221
278
  lexicon: "chant",
222
279
  planner: "chant",
223
- status: "planned",
224
- planDigest: planned.planDigest,
225
- holes: [{ address: planned.member, reason: `its ${kind} plan describes no changes, so what it changes is unknown` }],
280
+ status: "failed",
281
+ error: err instanceof Error ? err.message : String(err),
282
+ planDigest: null,
283
+ holes: [],
226
284
  },
227
285
  entries: [],
228
286
  });
229
287
  }
230
288
  }
231
- catch (err) {
232
- parts.push({
233
- member: {
234
- member: name,
235
- lexicon: "chant",
236
- planner: "chant",
237
- status: "failed",
238
- error: err instanceof Error ? err.message : String(err),
239
- planDigest: null,
240
- holes: [],
241
- },
242
- entries: [],
243
- });
244
- }
245
289
  }
246
- return composeChangeSet(parts, {
290
+ const doc = composeChangeSet(parts, {
247
291
  ...(input.chant !== undefined ? { chant: input.chant } : {}),
248
292
  planned: { releaseDigest: release.releaseDigest, environment: env },
249
293
  });
294
+ return {
295
+ doc,
296
+ reads,
297
+ ...(Object.keys(planned).length > 0 ? { plannedOutputs: planned } : {}),
298
+ ...(Object.keys(unknownInputs).length > 0 ? { unknownInputs } : {}),
299
+ };
250
300
  }
251
301
  /** The members a release gate refuses: provisional ones, which no approval of the document covers (`change-set.mdx`). */
252
302
  export function provisionalMembers(doc) {
@@ -11,9 +11,19 @@
11
11
  * under `.chant/release-templates/<digest>/` in the member's directory and the
12
12
  * step reads them from there. The path is relative and fixed by the digest,
13
13
  * so a gate approval bound to the promote's plan still matches on a re-run.
14
+ *
15
+ * A release cut since #4155 (a target with `trees`) records every local path
16
+ * a deploy step names (`recordedInputRefs`), directories file by file. A
17
+ * promote points each of them at the recorded copy: a directory is written
18
+ * under `.chant/release-templates/<digest>/<name>/`, its digest taken over its
19
+ * files the way a gate's directory digest is (`./deploy-inputs.ts`). A path
20
+ * outside the template fields that the release does not record, such as a
21
+ * chart named from a repository, is left as written. A target cut before
22
+ * #4155 is pinned as it was then: template fields only, other paths read from
23
+ * the checkout.
14
24
  */
15
25
  import type { DriverComponent } from "./driver.js";
16
- import type { Release, ReleaseTarget } from "../lifecycle/release-store.js";
26
+ import { type Release, type ReleaseTarget } from "../lifecycle/release-store.js";
17
27
  import type { ReleaseRecord } from "../lifecycle/release-ledger.js";
18
28
  import type { BuildArchiveManifest } from "./verbs/build-archive.js";
19
29
  /** Where a promote under a release writes the recorded templates, relative to the member's directory. */
@@ -22,8 +32,12 @@ export declare const RELEASE_TEMPLATES_DIR = ".chant/release-templates";
22
32
  export interface PinnedTemplate {
23
33
  /** Relative to the member's directory. */
24
34
  path: string;
35
+ /** The text, or the base64 of the bytes under `encoding: "base64"`. */
25
36
  content: string;
37
+ encoding?: "base64";
26
38
  }
39
+ /** Reads a recorded template's stored copy by digest: its text, or its base64 under `base64`. */
40
+ export type TemplateReader = (digest: string, encoding?: "base64") => Promise<string | null>;
27
41
  /** The release's target for `env`, or an error naming the environments it has. */
28
42
  export declare function targetFor(release: Release, env: string): {
29
43
  target: ReleaseTarget;
@@ -38,7 +52,7 @@ export declare function isImplicitRelease(release: Release): boolean;
38
52
  * the promote is refused: a template the target does not record, one whose
39
53
  * bytes are missing, or one whose bytes do not hash to the target's digest.
40
54
  */
41
- export declare function pinReleaseTemplates(components: readonly DriverComponent[], release: Release, target: ReleaseTarget, readTemplate: (digest: string) => Promise<string | null>): Promise<{
55
+ export declare function pinReleaseTemplates(components: readonly DriverComponent[], release: Release, target: ReleaseTarget, readTemplate: TemplateReader): Promise<{
42
56
  components: DriverComponent[];
43
57
  files: PinnedTemplate[];
44
58
  } | {
@@ -53,7 +67,7 @@ export declare function pinReleaseTemplates(components: readonly DriverComponent
53
67
  * member's directory; a template whose bytes are missing or don't hash to the
54
68
  * target's digest is refused, as {@link pinReleaseTemplates} refuses one.
55
69
  */
56
- export declare function targetFiles(release: Release, target: ReleaseTarget, readTemplate: (digest: string) => Promise<string | null>): Promise<{
70
+ export declare function targetFiles(release: Release, target: ReleaseTarget, readTemplate: TemplateReader): Promise<{
57
71
  files: PinnedTemplate[];
58
72
  } | {
59
73
  errors: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"release-promote.d.ts","sourceRoot":"","sources":["../../src/components/release-promote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAuC,MAAM,UAAU,CAAC;AAErF,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,yGAAyG;AACzG,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAMtG;AAED,sIAAsI;AACtI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAG3D;AAUD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACvD,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IAAC,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAiD5F;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACvD,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA2B7D;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAMxF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAQzF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC,EACpE,YAAY,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAC7E,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB"}
1
+ {"version":3,"file":"release-promote.d.ts","sourceRoot":"","sources":["../../src/components/release-promote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAuC,MAAM,UAAU,CAAC;AAErF,OAAO,EAAuB,KAAK,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEnG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,yGAAyG;AACzG,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,iGAAiG;AACjG,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7F,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAMtG;AAED,sIAAsI;AACtI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAG3D;AAUD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,cAAc,GAC3B,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IAAC,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAyD5F;AA+CD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,cAAc,GAC3B,OAAO,CAAC;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA2B7D;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAMxF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI,CAUzF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC,EACpE,YAAY,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAC7E,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB"}
@@ -11,11 +11,22 @@
11
11
  * under `.chant/release-templates/<digest>/` in the member's directory and the
12
12
  * step reads them from there. The path is relative and fixed by the digest,
13
13
  * so a gate approval bound to the promote's plan still matches on a re-run.
14
+ *
15
+ * A release cut since #4155 (a target with `trees`) records every local path
16
+ * a deploy step names (`recordedInputRefs`), directories file by file. A
17
+ * promote points each of them at the recorded copy: a directory is written
18
+ * under `.chant/release-templates/<digest>/<name>/`, its digest taken over its
19
+ * files the way a gate's directory digest is (`./deploy-inputs.ts`). A path
20
+ * outside the template fields that the release does not record, such as a
21
+ * chart named from a repository, is left as written. A target cut before
22
+ * #4155 is pinned as it was then: template fields only, other paths read from
23
+ * the checkout.
14
24
  */
15
25
  import { mkdirSync, readdirSync, rmSync, rmdirSync, writeFileSync } from "node:fs";
16
26
  import { basename, dirname, join } from "node:path";
17
- import { contentDigest } from "../content-digest.js";
18
- import { TEMPLATE_FIELDS, templateRefs } from "./release-compile.js";
27
+ import { recordedFields, recordedInputRefs, TEMPLATE_FIELDS, templateRefs } from "./release-compile.js";
28
+ import { templateBytesDigest } from "../lifecycle/release-store.js";
29
+ import { computePlanDigest } from "../lifecycle/plan-digest.js";
19
30
  /** Where a promote under a release writes the recorded templates, relative to the member's directory. */
20
31
  export const RELEASE_TEMPLATES_DIR = ".chant/release-templates";
21
32
  /** The release's target for `env`, or an error naming the environments it has. */
@@ -48,29 +59,41 @@ export async function pinReleaseTemplates(components, release, target, readTempl
48
59
  const errors = [];
49
60
  const files = [];
50
61
  const out = [];
62
+ // #4155: a target with `trees` records every deploy input; one without was cut before and records template fields only.
63
+ const full = target.trees !== undefined;
51
64
  for (const component of components) {
52
65
  const replace = new Map();
53
- for (const ref of templateRefs(component)) {
66
+ const refs = full ? recordedInputRefs(component) : templateRefs(component).map((r) => ({ ...r, required: true }));
67
+ for (const ref of refs) {
54
68
  if (replace.has(ref.value))
55
69
  continue;
70
+ const dir = ref.path.replace(/\/+$/, "");
71
+ const tree = full ? target.trees.find((t) => t.component === component.name && t.path === dir) : undefined;
72
+ if (tree) {
73
+ const pinned = await pinTree(component.name, tree.path, target, readTemplate);
74
+ if ("errors" in pinned)
75
+ errors.push(...pinned.errors);
76
+ else {
77
+ files.push(...pinned.files);
78
+ replace.set(ref.value, pinned.path);
79
+ }
80
+ continue;
81
+ }
56
82
  const recorded = target.templates.find((t) => t.component === component.name && t.path === ref.path);
57
83
  if (!recorded) {
84
+ if (!ref.required)
85
+ continue;
58
86
  errors.push(`component "${component.name}" reads ${ref.field} "${ref.value}", which release ${release.releaseDigest} does not record for "${target.env}"; ` +
59
87
  `this checkout's deploy is not the one the release was compiled from (commit ${release.commit.slice(0, 12)})`);
60
88
  continue;
61
89
  }
62
- const content = await readTemplate(recorded.digest);
63
- if (content === null) {
64
- errors.push(`component "${component.name}": the bytes of template "${ref.path}" (${recorded.digest}) are not on the chant/lifecycle branch; fetch it and try again`);
65
- continue;
66
- }
67
- const got = contentDigest(content);
68
- if (got !== recorded.digest) {
69
- errors.push(`component "${component.name}": template "${ref.path}" hashes to ${got}, but the release's target for "${target.env}" records ${recorded.digest}; refusing to deploy it`);
90
+ const read = await readChecked(component.name, ref.path, recorded, target, readTemplate);
91
+ if ("error" in read) {
92
+ errors.push(read.error);
70
93
  continue;
71
94
  }
72
95
  const path = join(RELEASE_TEMPLATES_DIR, recorded.digest.replace(/:/g, "_"), basename(ref.path));
73
- files.push({ path, content });
96
+ files.push({ path, content: read.content, ...(recorded.encoding ? { encoding: recorded.encoding } : {}) });
74
97
  replace.set(ref.value, path);
75
98
  }
76
99
  const rewrite = (phase) => ({
@@ -81,7 +104,7 @@ export async function pinReleaseTemplates(components, release, target, readTempl
81
104
  if (isGate(step))
82
105
  return step;
83
106
  const next = { ...step };
84
- for (const field of TEMPLATE_FIELDS) {
107
+ for (const field of full ? recordedFields(step.kind) : TEMPLATE_FIELDS) {
85
108
  const v = next[field];
86
109
  if (typeof v === "string" && replace.has(v))
87
110
  next[field] = replace.get(v);
@@ -95,6 +118,42 @@ export async function pinReleaseTemplates(components, release, target, readTempl
95
118
  }
96
119
  return errors.length > 0 ? { errors } : { components: out, files };
97
120
  }
121
+ /** One recorded template's stored copy, checked against its digest. */
122
+ async function readChecked(component, path, recorded, target, readTemplate) {
123
+ const who = component === null ? "" : `component "${component}": `;
124
+ const content = await readTemplate(recorded.digest, recorded.encoding);
125
+ if (content === null)
126
+ return { error: `${who}the bytes of template "${path}" (${recorded.digest}) are not on the chant/lifecycle branch; fetch it and try again` };
127
+ const got = templateBytesDigest(content, recorded.encoding);
128
+ if (got !== recorded.digest) {
129
+ return { error: `${who}template "${path}" hashes to ${got}, but the release's target for "${target.env}" records ${recorded.digest}; refusing to deploy it` };
130
+ }
131
+ return { content };
132
+ }
133
+ /**
134
+ * A recorded directory's files, under `.chant/release-templates/<digest>/<name>/`
135
+ * (#4155). The digest is taken over the files' paths from the directory and
136
+ * their bytes, as a gate's directory digest is, so the path names the tree.
137
+ */
138
+ async function pinTree(component, dir, target, readTemplate) {
139
+ const prefix = `${dir}/`;
140
+ const entries = target.templates.filter((t) => t.component === component && t.path.startsWith(prefix));
141
+ const errors = [];
142
+ const read = [];
143
+ for (const e of entries) {
144
+ const got = await readChecked(component, e.path, e, target, readTemplate);
145
+ if ("error" in got)
146
+ errors.push(got.error);
147
+ else
148
+ read.push({ rel: e.path.slice(prefix.length), content: got.content, digest: e.digest, ...(e.encoding ? { encoding: e.encoding } : {}) });
149
+ }
150
+ if (errors.length > 0)
151
+ return { errors };
152
+ read.sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));
153
+ const digest = computePlanDigest("directory", read.map(({ rel, digest }) => ({ path: rel, digest })));
154
+ const path = join(RELEASE_TEMPLATES_DIR, digest.replace(/:/g, "_"), basename(dir));
155
+ return { path, files: read.map((r) => ({ path: join(path, r.rel), content: r.content, ...(r.encoding ? { encoding: r.encoding } : {}) })) };
156
+ }
98
157
  /**
99
158
  * The files a release's target records, read back by digest and checked, for
100
159
  * `chant release export` (#4046). A project with no components has no deploy
@@ -113,12 +172,12 @@ export async function targetFiles(release, target, readTemplate) {
113
172
  errors.push(`${who}template "${t.path}" is not a path inside the member's directory; refusing to write it`);
114
173
  continue;
115
174
  }
116
- const content = await readTemplate(t.digest);
175
+ const content = await readTemplate(t.digest, t.encoding);
117
176
  if (content === null) {
118
177
  errors.push(`${who}the bytes of template "${t.path}" (${t.digest}) are not on the chant/lifecycle branch; fetch it and try again`);
119
178
  continue;
120
179
  }
121
- const got = contentDigest(content);
180
+ const got = templateBytesDigest(content, t.encoding);
122
181
  if (got !== t.digest) {
123
182
  errors.push(`${who}template "${t.path}" hashes to ${got}, but release ${release.releaseDigest} records ${t.digest} for "${target.env}"; refusing to write it`);
124
183
  continue;
@@ -129,7 +188,7 @@ export async function targetFiles(release, target, readTemplate) {
129
188
  errors.push(`two components record different bytes at "${t.path}" for "${target.env}", so they can't both be written there`);
130
189
  continue;
131
190
  }
132
- files.push({ path: t.path, content });
191
+ files.push({ path: t.path, content, ...(t.encoding ? { encoding: t.encoding } : {}) });
133
192
  }
134
193
  return errors.length > 0 ? { errors } : { files };
135
194
  }
@@ -138,7 +197,7 @@ export function writePinnedTemplates(dir, files) {
138
197
  for (const f of files) {
139
198
  const full = join(dir, f.path);
140
199
  mkdirSync(dirname(full), { recursive: true });
141
- writeFileSync(full, f.content);
200
+ writeFileSync(full, f.encoding === "base64" ? Buffer.from(f.content, "base64") : f.content);
142
201
  }
143
202
  }
144
203
  /**
@@ -148,8 +207,10 @@ export function writePinnedTemplates(dir, files) {
148
207
  * again from the release.
149
208
  */
150
209
  export function removePinnedTemplates(dir, files) {
210
+ // Each file sits under `.chant/release-templates/<digest>/`, a recorded tree's deeper; that digest directory goes.
211
+ const prefix = RELEASE_TEMPLATES_DIR.split("/").length;
151
212
  for (const f of files)
152
- rmSync(dirname(join(dir, f.path)), { recursive: true, force: true });
213
+ rmSync(join(dir, ...f.path.split(/[\\/]/).slice(0, prefix + 1)), { recursive: true, force: true });
153
214
  const root = join(dir, RELEASE_TEMPLATES_DIR);
154
215
  try {
155
216
  if (readdirSync(root).length === 0)
@@ -68,7 +68,7 @@ export interface BomPackage {
68
68
  /** Version string, when known. Omitted for resources with no version concept (e.g. a CFN resource). */
69
69
  version?: string;
70
70
  /** Package manager / ecosystem this entry came from (e.g. "npm", "maven"), surfaced in SPDX's `externalRefs`/CycloneDX's `purl` when derivable. */
71
- type?: "npm" | "maven" | "config" | "external-reference";
71
+ type?: "npm" | "maven" | "cargo" | "config" | "external-reference";
72
72
  /** Package URL (purl), when derivable — e.g. `pkg:npm/left-pad@1.3.0`. */
73
73
  purl?: string;
74
74
  /** SPDX download location, when known. Defaults to `"NOASSERTION"` per the SPDX spec's requirement that every package declare one. */
@@ -1 +1 @@
1
- {"version":3,"file":"bom-writer.d.ts","sourceRoot":"","sources":["../../../src/components/verbs/bom-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAIrE;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,2HAA2H;IAC3H,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mJAAmJ;IACnJ,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,oBAAoB,CAAC;IACzD,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sIAAsI;IACtI,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0JAA0J;IAC1J,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+MAA+M;IAC/M,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,oGAAoG;AACpG,MAAM,WAAW,QAAQ;IACvB,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2IAA2I;IAC3I,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,gIAAgI;IAChI,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;CACjC;AAyDD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAE,MAAM,IAAuB,GAAG,MAAM,CAyErF;AAsBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAE,MAAM,IAAuB,GAAG,MAAM,CAgE1F;AAUD,+NAA+N;AAC/N,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,QAAQ,EACf,GAAG,CAAC,EAAE,MAAM,IAAI,GACf;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAG1D"}
1
+ {"version":3,"file":"bom-writer.d.ts","sourceRoot":"","sources":["../../../src/components/verbs/bom-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAIrE;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,2HAA2H;IAC3H,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mJAAmJ;IACnJ,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,oBAAoB,CAAC;IACnE,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sIAAsI;IACtI,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0JAA0J;IAC1J,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+MAA+M;IAC/M,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,oGAAoG;AACpG,MAAM,WAAW,QAAQ;IACvB,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2IAA2I;IAC3I,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,gIAAgI;IAChI,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;CACjC;AAyDD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAE,MAAM,IAAuB,GAAG,MAAM,CAyErF;AAsBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAE,MAAM,IAAuB,GAAG,MAAM,CAgE1F;AAUD,+NAA+N;AAC/N,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,QAAQ,EACf,GAAG,CAAC,EAAE,MAAM,IAAI,GACf;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAG1D"}
@@ -55,6 +55,26 @@ export declare function parseNpmPackageLock(content: string): BomPackage[];
55
55
  * inheritance chain.
56
56
  */
57
57
  export declare function parsePomXml(content: string): BomPackage[];
58
+ /**
59
+ * Parse a `Cargo.lock` (version 3 or 4) into a flat `BomPackage[]`: one
60
+ * entry per `[[package]]` with a `source`, so the workspace's own crates,
61
+ * which have none, are left out as the npm parser leaves out the root.
62
+ * A dependency is named `name` or `name version` in the lock; only the name
63
+ * is kept. A small line reader, not a TOML parser: the lock is written by
64
+ * cargo in one fixed shape.
65
+ */
66
+ export declare function parseCargoLock(content: string): BomPackage[];
67
+ /** Each lockfile kind's parser and file name. */
68
+ export declare const LOCKFILE_PARSERS: {
69
+ readonly npm: typeof parseNpmPackageLock;
70
+ readonly maven: typeof parsePomXml;
71
+ readonly cargo: typeof parseCargoLock;
72
+ };
73
+ export declare const LOCKFILE_NAMES: {
74
+ readonly npm: "package-lock.json";
75
+ readonly maven: "pom.xml";
76
+ readonly cargo: "Cargo.lock";
77
+ };
58
78
  export interface LockfileSbomGeneratorOptions {
59
79
  /** Where generated SBOM documents are written on disk, as a sibling of the scanned path (see `sbomOutputPath`). Default: alongside the scanned manifest, named `sbom.<format>.json`. */
60
80
  outDir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"lockfile-sbom-generator.d.ts","sourceRoot":"","sources":["../../../src/components/verbs/lockfile-sbom-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAMV,aAAa,EACd,MAAM,kBAAkB,CAAC;AAe1B;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAkCjE;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CA8BzD;AAID,MAAM,WAAW,4BAA4B;IAC3C,wLAAwL;IACxL,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAgBD,qIAAqI;AACrI,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,aAAa,CA0CrG;AAED,gMAAgM;AAChM,eAAO,MAAM,qBAAqB,EAAE,aAA6C,CAAC"}
1
+ {"version":3,"file":"lockfile-sbom-generator.d.ts","sourceRoot":"","sources":["../../../src/components/verbs/lockfile-sbom-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAMV,aAAa,EACd,MAAM,kBAAkB,CAAC;AAe1B;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAkCjE;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CA8BzD;AAID;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAsB5D;AAED,iDAAiD;AACjD,eAAO,MAAM,gBAAgB;aAAK,GAAG;aAAuB,KAAK;aAAe,KAAK;CAA2B,CAAC;AACjH,eAAO,MAAM,cAAc;aAAK,GAAG,EAAE,mBAAmB;aAAE,KAAK,EAAE,SAAS;aAAE,KAAK,EAAE,YAAY;CAAW,CAAC;AAI3G,MAAM,WAAW,4BAA4B;IAC3C,wLAAwL;IACxL,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAgBD,qIAAqI;AACrI,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,aAAa,CA0CrG;AAED,gMAAgM;AAChM,eAAO,MAAM,qBAAqB,EAAE,aAA6C,CAAC"}
@@ -125,9 +125,46 @@ export function parsePomXml(content) {
125
125
  }
126
126
  return packages.sort((a, b) => a.name.localeCompare(b.name));
127
127
  }
128
+ // ── Cargo.lock (Rust, #4168 decision 12) ────────────────────────────────────
129
+ /**
130
+ * Parse a `Cargo.lock` (version 3 or 4) into a flat `BomPackage[]`: one
131
+ * entry per `[[package]]` with a `source`, so the workspace's own crates,
132
+ * which have none, are left out as the npm parser leaves out the root.
133
+ * A dependency is named `name` or `name version` in the lock; only the name
134
+ * is kept. A small line reader, not a TOML parser: the lock is written by
135
+ * cargo in one fixed shape.
136
+ */
137
+ export function parseCargoLock(content) {
138
+ const seen = new Map();
139
+ const blocks = content.split(/^\[\[package\]\]\s*$/m).slice(1);
140
+ for (const block of blocks) {
141
+ const body = block.split(/^\[/m)[0] ?? "";
142
+ const field = (key) => new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m").exec(body)?.[1];
143
+ const name = field("name");
144
+ const version = field("version");
145
+ if (!name || field("source") === undefined)
146
+ continue;
147
+ const deps = /^dependencies\s*=\s*\[([\s\S]*?)\]/m.exec(body)?.[1];
148
+ const dependsOn = deps ? [...deps.matchAll(/"([^"\s]+)[^"]*"/g)].map((m) => m[1]) : [];
149
+ const key = `${name}@${version ?? ""}`;
150
+ if (seen.has(key))
151
+ continue;
152
+ seen.set(key, {
153
+ name,
154
+ version,
155
+ type: "cargo",
156
+ purl: version ? `pkg:cargo/${encodeURIComponent(name)}@${version}` : undefined,
157
+ dependsOn: dependsOn.length ? [...new Set(dependsOn)] : undefined,
158
+ });
159
+ }
160
+ return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name) || (a.version ?? "").localeCompare(b.version ?? ""));
161
+ }
162
+ /** Each lockfile kind's parser and file name. */
163
+ export const LOCKFILE_PARSERS = { npm: parseNpmPackageLock, maven: parsePomXml, cargo: parseCargoLock };
164
+ export const LOCKFILE_NAMES = { npm: "package-lock.json", maven: "pom.xml", cargo: "Cargo.lock" };
128
165
  /** Locate the first readable lockfile/manifest under `dir` this generator knows how to parse, in preference order (npm before Maven — most chant components are Node-first). Returns `undefined` if none is found. */
129
166
  function findManifest(dir) {
130
- for (const [kind, filename] of [["npm", "package-lock.json"], ["maven", "pom.xml"]]) {
167
+ for (const [kind, filename] of [["npm", "package-lock.json"], ["maven", "pom.xml"], ["cargo", "Cargo.lock"]]) {
131
168
  const candidate = join(dir, filename);
132
169
  try {
133
170
  readFileSync(candidate, "utf-8");
@@ -160,12 +197,12 @@ export function createLockfileSbomGenerator(options = {}) {
160
197
  function generateFromDir(dir, subjectName, digest, format = DEFAULT_SBOM_FORMAT) {
161
198
  const manifest = findManifest(dir);
162
199
  if (!manifest) {
163
- throw new Error(`no supported lockfile/manifest found under "${dir}" (looked for package-lock.json, pom.xml) — ` +
200
+ throw new Error(`no supported lockfile/manifest found under "${dir}" (looked for package-lock.json, pom.xml, Cargo.lock) — ` +
164
201
  `the hermetic lockfile SBOM backend requires a lockfile on disk; use the deep-scan path (#610) for images with no lockfile`);
165
202
  }
166
203
  const content = readFileSync(manifest.path, "utf-8");
167
- const packages = manifest.kind === "npm" ? parseNpmPackageLock(content) : parsePomXml(content);
168
- const generatorName = manifest.kind === "npm" ? "chant-lockfile-sbom/package-lock.json" : "chant-lockfile-sbom/pom.xml";
204
+ const packages = LOCKFILE_PARSERS[manifest.kind](content);
205
+ const generatorName = `chant-lockfile-sbom/${LOCKFILE_NAMES[manifest.kind]}`;
169
206
  const doc = writeBom(format, { subjectName, subjectId: digest ?? manifest.path, packages, generator: generatorName }, now);
170
207
  const outDir = options.outDir ?? dir;
171
208
  mkdirSync(outDir, { recursive: true });
@@ -13,6 +13,14 @@
13
13
  * (`componentPlanDigest`, ./gate-plan.ts, #2574), so a wave mixing roots and
14
14
  * other components still has one digest that moves when any of them does.
15
15
  *
16
+ * The files a step without a `plan` deploys from are bound too (#4155,
17
+ * ./deploy-inputs.ts). A component with no plannable step folds them into its
18
+ * composition digest. A component with one gets a member more,
19
+ * `<component>#inputs`, over its other steps' files, so a `lambda-deploy`
20
+ * source or a Helm chart beside a `cfn-deploy` moves the wave's digest. A step
21
+ * whose content chant can't name is a hole on that member, so the plan note
22
+ * lists it as unbound.
23
+ *
16
24
  * Known limit: a step input that reads an earlier step of the same
17
25
  * component (`@Phase.field`) has no value before the component runs, so it
18
26
  * plans as `undefined`. Cross-component wiring (`stackOutput()`,
@@ -40,6 +48,8 @@ export type WaveComponentPlan = {
40
48
  component: string;
41
49
  error: string;
42
50
  };
51
+ /** The member a planning component's other steps add (#4155): their files, under the component's name. */
52
+ export declare const inputsMemberName: (component: string) => string;
43
53
  /** Every capability step in a composition, nested phases included, gates left out. */
44
54
  export declare function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[];
45
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"wave-plan.d.ts","sourceRoot":"","sources":["../../src/components/wave-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAA6C,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAE9H,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,cAAc,CAAA;CAAE,GAC9H;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAUzC,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,EAAE,CAc5E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,aAAa,CAGf;AAED,oHAAoH;AACpH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,OAAO,CAAC,iBAAiB,CAAC,CAkC5B;AAUD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAU9C;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAChD,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,aAAa,EAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAYnC"}
1
+ {"version":3,"file":"wave-plan.d.ts","sourceRoot":"","sources":["../../src/components/wave-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAA6C,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAI9H,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,cAAc,CAAA;CAAE,GAC9H;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAWzC,0GAA0G;AAC1G,eAAO,MAAM,gBAAgB,cAAe,MAAM,KAAG,MAA+B,CAAC;AAErF,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,EAAE,CAc5E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,aAAa,CAGf;AAED,oHAAoH;AACpH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,OAAO,CAAC,iBAAiB,CAAC,CAgD5B;AAUD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAU9C;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAChD,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,aAAa,EAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAYnC"}