@intentius/chant 0.46.0 → 0.50.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.
- package/dist/audit/catalog.d.ts +13 -3
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +30 -3
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +9 -2
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/fetch.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +6 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +95 -0
- package/dist/audit/secrets.d.ts.map +1 -0
- package/dist/audit/wrangler.d.ts +33 -0
- package/dist/audit/wrangler.d.ts.map +1 -0
- package/dist/build.d.ts +3 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +7 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +23 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +31 -0
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +12 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +32 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -0
- package/dist/cli/handlers/scenario.d.ts +39 -0
- package/dist/cli/handlers/scenario.d.ts.map +1 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts +35 -2
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts +6 -0
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/types.d.ts +29 -1
- package/dist/cli/mcp/types.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +1 -1
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +14 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/reporters/stylish.d.ts +15 -1
- package/dist/cli/reporters/stylish.d.ts.map +1 -1
- package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
- package/dist/components/auto-release.d.ts +4 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +17 -2
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +7 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +15 -0
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts +2 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/ensure-secret.d.ts +50 -0
- package/dist/components/verbs/ensure-secret.d.ts.map +1 -0
- package/dist/components/verbs/index.d.ts +13 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts +76 -0
- package/dist/components/verbs/r2-sync.d.ts.map +1 -0
- package/dist/components/verbs/run-agent.d.ts +499 -0
- package/dist/components/verbs/run-agent.d.ts.map +1 -0
- package/dist/components/verbs/sign.d.ts +30 -0
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts +108 -0
- package/dist/components/verbs/wrangler.d.ts.map +1 -0
- package/dist/composite.d.ts +6 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +14 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/collect.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-rank.d.ts +66 -0
- package/dist/discovery/fold-rank.d.ts.map +1 -0
- package/dist/discovery/index.d.ts +15 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +17 -0
- package/dist/discovery/param-deps.d.ts.map +1 -0
- package/dist/effect-receipt.d.ts +177 -0
- package/dist/effect-receipt.d.ts.map +1 -0
- package/dist/fold/fold.d.ts +55 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/subset.d.ts +20 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +2 -0
- package/dist/lexicon-schema.d.ts.map +1 -1
- package/dist/lexicon.d.ts +137 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +33 -5
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/converge-ledger.d.ts +90 -0
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -0
- package/dist/lifecycle/deep-diff.d.ts +18 -0
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts +9 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +33 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +145 -21
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +6 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +113 -0
- package/dist/lifecycle/lease.d.ts.map +1 -0
- package/dist/lifecycle/observation-baseline.d.ts +21 -3
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/receipt-plan.d.ts +62 -0
- package/dist/lifecycle/receipt-plan.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +20 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario-eval.d.ts +42 -0
- package/dist/lifecycle/scenario-eval.d.ts.map +1 -0
- package/dist/lifecycle/scenario.d.ts +163 -0
- package/dist/lifecycle/scenario.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +63 -0
- package/dist/lifecycle/symptoms.d.ts.map +1 -0
- package/dist/lifecycle/teardown.d.ts +6 -4
- package/dist/lifecycle/teardown.d.ts.map +1 -1
- package/dist/lifecycle/unobserved-gate.d.ts +67 -0
- package/dist/lifecycle/unobserved-gate.d.ts.map +1 -0
- package/dist/lint/knowledge-checks.d.ts +48 -0
- package/dist/lint/knowledge-checks.d.ts.map +1 -0
- package/dist/lint/output-checks.d.ts +5 -0
- package/dist/lint/output-checks.d.ts.map +1 -0
- package/dist/lint/output-docs.d.ts +94 -0
- package/dist/lint/output-docs.d.ts.map +1 -0
- package/dist/lint/pipeline-change-gate.d.ts +101 -0
- package/dist/lint/pipeline-change-gate.d.ts.map +1 -0
- package/dist/lint/post-synth.d.ts +41 -0
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/receipt-checks.d.ts +9 -0
- package/dist/lint/receipt-checks.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts +11 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts.map +1 -0
- package/dist/lint/rules/cor022-receipt-leaf.d.ts +13 -0
- package/dist/lint/rules/cor022-receipt-leaf.d.ts.map +1 -0
- package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts +3 -0
- package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts.map +1 -0
- package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -1
- package/dist/lint/rules/index.d.ts +3 -1
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts +7 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/okf-read.d.ts +78 -0
- package/dist/okf-read.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +139 -0
- package/dist/op/activity-contract.d.ts.map +1 -0
- package/dist/op/builders.d.ts +140 -3
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/converge-rule.d.ts +161 -0
- package/dist/op/converge-rule.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +39 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -0
- package/dist/op/index.d.ts +18 -2
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +2 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/op-verb-class.d.ts +42 -0
- package/dist/op/op-verb-class.d.ts.map +1 -0
- package/dist/op/operator.d.ts +128 -0
- package/dist/op/operator.d.ts.map +1 -0
- package/dist/op/receipt-store.d.ts +138 -0
- package/dist/op/receipt-store.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +187 -0
- package/dist/op/step-output-ref.d.ts.map +1 -0
- package/dist/op/types.d.ts +49 -2
- package/dist/op/types.d.ts.map +1 -1
- package/dist/provenance.d.ts +73 -3
- package/dist/provenance.d.ts.map +1 -1
- package/dist/runtime-adapter.d.ts +7 -1
- package/dist/runtime-adapter.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts +138 -0
- package/dist/secret-materialization.d.ts.map +1 -0
- package/dist/secret-provenance.d.ts +218 -0
- package/dist/secret-provenance.d.ts.map +1 -0
- package/dist/serializer.d.ts +29 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/toml.d.ts +40 -5
- package/dist/toml.d.ts.map +1 -1
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/audit/catalog.test.ts +1 -1
- package/src/audit/catalog.ts +75 -3
- package/src/audit/core.test.ts +57 -0
- package/src/audit/core.ts +0 -0
- package/src/audit/detect-bundle.test.ts +1 -1
- package/src/audit/discover.test.ts +24 -0
- package/src/audit/discover.ts +40 -4
- package/src/audit/fetch.test.ts +216 -3
- package/src/audit/fetch.ts +270 -59
- package/src/audit/report-html.ts +5 -2
- package/src/audit/report-model.ts +9 -0
- package/src/audit/report.test.ts +22 -0
- package/src/audit/report.ts +3 -2
- package/src/audit/rules-doc.ts +13 -1
- package/src/audit/secrets.test.ts +303 -0
- package/src/audit/secrets.ts +406 -0
- package/src/audit/wrangler.test.ts +230 -0
- package/src/audit/wrangler.ts +290 -0
- package/src/build.test.ts +41 -0
- package/src/build.ts +39 -6
- package/src/cli/command-group.ts +1 -1
- package/src/cli/commands/__fixtures__/audit-fountain/agents/fleet.yaml +27 -0
- package/src/cli/commands/__fixtures__/audit-fountain/k8s/deploy.yaml +16 -0
- package/src/cli/commands/__fixtures__/audit-fountain-clean/fleet.yaml +20 -0
- package/src/cli/commands/__fixtures__/schemas/sarif-2.1.0.schema.json +2882 -0
- package/src/cli/commands/audit.test.ts +268 -1
- package/src/cli/commands/audit.ts +87 -18
- package/src/cli/commands/build.test.ts +245 -0
- package/src/cli/commands/build.ts +210 -21
- package/src/cli/commands/lint.ts +15 -3
- package/src/cli/handlers/build.ts +2 -0
- package/src/cli/handlers/components.test.ts +199 -1
- package/src/cli/handlers/components.ts +160 -3
- package/src/cli/handlers/explain.test.ts +70 -1
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +12 -3
- package/src/cli/handlers/lifecycle.test.ts +115 -1
- package/src/cli/handlers/lifecycle.ts +96 -15
- package/src/cli/handlers/operator.test.ts +255 -0
- package/src/cli/handlers/operator.ts +240 -0
- package/src/cli/handlers/scenario.test.ts +456 -0
- package/src/cli/handlers/scenario.ts +330 -0
- package/src/cli/main.test.ts +23 -0
- package/src/cli/main.ts +72 -1
- package/src/cli/mcp/resource-handlers.ts +38 -1
- package/src/cli/mcp/server.test.ts +323 -3
- package/src/cli/mcp/server.ts +84 -7
- package/src/cli/mcp/tools/explain.ts +51 -2
- package/src/cli/mcp/types.ts +27 -1
- package/src/cli/plugins.ts +4 -2
- package/src/cli/registry.ts +14 -2
- package/src/cli/reporters/stylish.test.ts +154 -0
- package/src/cli/reporters/stylish.ts +154 -33
- package/src/codegen/docs-rule-scanning.test.ts +42 -0
- package/src/codegen/docs-rule-scanning.ts +25 -2
- package/src/components/README.md +7 -0
- package/src/components/auto-release.ts +6 -0
- package/src/components/capability.ts +17 -2
- package/src/components/cli-support.test.ts +17 -0
- package/src/components/cli-support.ts +13 -1
- package/src/components/component-schema.test.ts +32 -0
- package/src/components/component.schema.json +6 -0
- package/src/components/component.test.ts +21 -0
- package/src/components/component.ts +15 -0
- package/src/components/driver.ts +12 -4
- package/src/components/registry.test.ts +7 -2
- package/src/components/starter-plugin.ts +17 -0
- package/src/components/verbs/ensure-secret.test.ts +130 -0
- package/src/components/verbs/ensure-secret.ts +79 -0
- package/src/components/verbs/index.ts +13 -0
- package/src/components/verbs/r2-sync.test.ts +107 -0
- package/src/components/verbs/r2-sync.ts +124 -0
- package/src/components/verbs/run-agent.test.ts +683 -0
- package/src/components/verbs/run-agent.ts +786 -0
- package/src/components/verbs/sign.test.ts +19 -0
- package/src/components/verbs/sign.ts +34 -2
- package/src/components/verbs/wrangler.test.ts +170 -0
- package/src/components/verbs/wrangler.ts +241 -0
- package/src/composite.ts +31 -2
- package/src/config.test.ts +15 -0
- package/src/config.ts +30 -0
- package/src/deep-observation.test.ts +19 -0
- package/src/deep-observation.ts +17 -0
- package/src/discovery/collect.ts +11 -2
- package/src/discovery/fold-import.test.ts +54 -0
- package/src/discovery/fold-import.ts +178 -38
- package/src/discovery/fold-rank.test.ts +197 -0
- package/src/discovery/fold-rank.ts +346 -0
- package/src/discovery/index.ts +16 -1
- package/src/discovery/param-deps.test.ts +118 -0
- package/src/discovery/param-deps.ts +170 -0
- package/src/effect-receipt-exclusion.test.ts +190 -0
- package/src/effect-receipt.test.ts +419 -0
- package/src/effect-receipt.ts +412 -0
- package/src/fold/fold.test.ts +6 -2
- package/src/fold/fold.ts +184 -3
- package/src/fold/subset.test.ts +95 -6
- package/src/fold/subset.ts +66 -2
- package/src/index.ts +4 -0
- package/src/lexicon-schema.ts +3 -0
- package/src/lexicon.ts +151 -5
- package/src/lifecycle/change-set.ts +46 -7
- package/src/lifecycle/converge-ledger.test.ts +199 -0
- package/src/lifecycle/converge-ledger.ts +179 -0
- package/src/lifecycle/deep-diff.test.ts +79 -1
- package/src/lifecycle/deep-diff.ts +23 -0
- package/src/lifecycle/deep-observe.ts +13 -2
- package/src/lifecycle/gate-ledger.test.ts +103 -0
- package/src/lifecycle/gate-ledger.ts +140 -0
- package/src/lifecycle/git.test.ts +430 -0
- package/src/lifecycle/git.ts +446 -84
- package/src/lifecycle/index.ts +6 -0
- package/src/lifecycle/lease.test.ts +343 -0
- package/src/lifecycle/lease.ts +270 -0
- package/src/lifecycle/observation-baseline.test.ts +46 -0
- package/src/lifecycle/observation-baseline.ts +33 -1
- package/src/lifecycle/receipt-plan.test.ts +250 -0
- package/src/lifecycle/receipt-plan.ts +249 -0
- package/src/lifecycle/release-ledger.ts +20 -0
- package/src/lifecycle/scenario-eval.test.ts +199 -0
- package/src/lifecycle/scenario-eval.ts +158 -0
- package/src/lifecycle/scenario.test.ts +195 -0
- package/src/lifecycle/scenario.ts +321 -0
- package/src/lifecycle/symptoms.test.ts +116 -0
- package/src/lifecycle/symptoms.ts +126 -0
- package/src/lifecycle/teardown.test.ts +31 -0
- package/src/lifecycle/teardown.ts +6 -4
- package/src/lifecycle/unobserved-gate.test.ts +109 -0
- package/src/lifecycle/unobserved-gate.ts +102 -0
- package/src/lint/knowledge-checks.test.ts +80 -0
- package/src/lint/knowledge-checks.ts +74 -0
- package/src/lint/output-checks.test.ts +85 -0
- package/src/lint/output-checks.ts +99 -0
- package/src/lint/output-docs.test.ts +220 -0
- package/src/lint/output-docs.ts +204 -0
- package/src/lint/pipeline-change-gate.test.ts +144 -0
- package/src/lint/pipeline-change-gate.ts +153 -0
- package/src/lint/post-synth.test.ts +97 -0
- package/src/lint/post-synth.ts +60 -0
- package/src/lint/receipt-checks.test.ts +101 -0
- package/src/lint/receipt-checks.ts +93 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.ts +26 -0
- package/src/lint/rules/comp/comp.test.ts +49 -1
- package/src/lint/rules/cor022-receipt-leaf.test.ts +116 -0
- package/src/lint/rules/cor022-receipt-leaf.ts +130 -0
- package/src/lint/rules/cor024-receipt-secret-pointer.test.ts +121 -0
- package/src/lint/rules/cor024-receipt-secret-pointer.ts +218 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +35 -3
- package/src/lint/rules/evl001-non-literal-expression.ts +7 -0
- package/src/lint/rules/index.ts +7 -1
- package/src/lsp/lexicon-providers.test.ts +44 -0
- package/src/lsp/lexicon-providers.ts +11 -1
- package/src/okf-read.test.ts +149 -0
- package/src/okf-read.ts +197 -0
- package/src/op/activity-contract.test.ts +180 -0
- package/src/op/activity-contract.ts +278 -0
- package/src/op/builders-exports.test.ts +17 -1
- package/src/op/builders.ts +198 -6
- package/src/op/converge-rule.test.ts +179 -0
- package/src/op/converge-rule.ts +311 -0
- package/src/op/effect-step.test.ts +311 -0
- package/src/op/generate-pipeline.test.ts +53 -0
- package/src/op/generate-pipeline.ts +99 -0
- package/src/op/index.ts +40 -3
- package/src/op/local-executor.test.ts +92 -0
- package/src/op/local-executor.ts +212 -29
- package/src/op/op-verb-class.test.ts +126 -0
- package/src/op/op-verb-class.ts +115 -0
- package/src/op/op.test.ts +25 -2
- package/src/op/operator.test.ts +346 -0
- package/src/op/operator.ts +213 -0
- package/src/op/receipt-store.ts +211 -0
- package/src/op/step-output-ref.test.ts +334 -0
- package/src/op/step-output-ref.ts +453 -0
- package/src/op/types.ts +51 -2
- package/src/provenance.test.ts +151 -4
- package/src/provenance.ts +118 -4
- package/src/runtime-adapter.ts +31 -10
- package/src/secret-materialization.test.ts +199 -0
- package/src/secret-materialization.ts +235 -0
- package/src/secret-provenance.test.ts +388 -0
- package/src/secret-provenance.ts +475 -0
- package/src/serializer.ts +30 -0
- package/src/toml.test.ts +157 -384
- package/src/toml.ts +371 -5
- package/src/yaml.test.ts +88 -0
- package/src/yaml.ts +76 -6
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity contracts — the Stage 1 fix for chant #1288 ("Ops are the one
|
|
3
|
+
* place chant accepts untyped arguments and resolves names at runtime").
|
|
4
|
+
*
|
|
5
|
+
* Maintainer decision (#1288, 2026-08-25): a staged approach. Stage 1 (this
|
|
6
|
+
* module) is registered args/return schemas per activity, validated by
|
|
7
|
+
* `chant build` against every step — non-breaking, no change to
|
|
8
|
+
* {@link ActivityStep.args}'s `Record<string, unknown>` shape or the step
|
|
9
|
+
* builders. Stage 2 (a separate PR) regenerates the step builders as fully
|
|
10
|
+
* typed wrappers for editor completion and go-to-definition. #1289 (op.json
|
|
11
|
+
* IR) and #1290 (step-output references) build on these contracts — #1290
|
|
12
|
+
* specifically needs a declared `returns` schema to validate a later step's
|
|
13
|
+
* reference into an earlier one's output against, which is why `returns` is
|
|
14
|
+
* part of the shape here even though nothing in Stage 1 reads it for that.
|
|
15
|
+
*
|
|
16
|
+
* An activity declares a contract alongside its implementation — the same
|
|
17
|
+
* "registration surface" the issue asked for (`activity-registry.ts`'s
|
|
18
|
+
* `collectActivities` already does this for implementations; this is the
|
|
19
|
+
* schema-shaped sibling). `chant build` — via a lexicon's own post-synth
|
|
20
|
+
* check, e.g. the temporal lexicon's TMP012 — resolves each step's `fn`
|
|
21
|
+
* against a contract map built the same way and validates `args` and
|
|
22
|
+
* `outcomeAttribute.from`. A step whose `fn` has no registered contract is
|
|
23
|
+
* skipped (not an error): this is deliberately incremental — a lexicon opts
|
|
24
|
+
* an activity in by declaring a contract for it, and the k8s/aws/azure/gcp/
|
|
25
|
+
* fly activity sets are expected to pick this up lexicon by lexicon rather
|
|
26
|
+
* than all at once (see the issue's "worth checking this lands cleanly"
|
|
27
|
+
* note).
|
|
28
|
+
*
|
|
29
|
+
* Ownership is decentralized on purpose: each lexicon declares contracts for
|
|
30
|
+
* the activities it implements and validates them with its own post-synth
|
|
31
|
+
* check (the same `rulePrefix`-per-lexicon pattern every other check in
|
|
32
|
+
* chant already uses), rather than a shared cross-lexicon registry. A
|
|
33
|
+
* `Temporal::Op` step can call an activity contributed by any lexicon, and
|
|
34
|
+
* `PostSynthContext.entities` already carries the whole resolved graph to
|
|
35
|
+
* every lexicon's checks, so no new plumbing is needed for that to work.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
import type { OpConfig, PhaseDefinition, ActivityStep, StepDefinition } from "./types";
|
|
40
|
+
|
|
41
|
+
/** Every literal value {@link ActivityStep.profile} may hold. Kept in sync with `types.ts`'s `ActivityStep["profile"]`. */
|
|
42
|
+
export const KNOWN_ACTIVITY_PROFILES = [
|
|
43
|
+
"fastIdempotent",
|
|
44
|
+
"longInfra",
|
|
45
|
+
"k8sWait",
|
|
46
|
+
"humanGate",
|
|
47
|
+
"argoSync",
|
|
48
|
+
"policyCheck",
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
const CONTRACT_BRAND = Symbol.for("chant.op.activityContract");
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A registered activity's args/return schemas — the declaration that lets
|
|
55
|
+
* `chant build` catch a typo'd or mistyped step before it ever reaches a
|
|
56
|
+
* cluster.
|
|
57
|
+
*
|
|
58
|
+
* Author `args` with `z.strictObject(...)` (or an equivalent that rejects
|
|
59
|
+
* unrecognized keys), not `z.object(...)`. Zod's default `.object()` silently
|
|
60
|
+
* drops a key it doesn't recognize instead of failing — exactly the
|
|
61
|
+
* `helmInstall("api", "./chart", { nameSpace: "prod" })` failure class the
|
|
62
|
+
* issue names, where the misspelled key vanishes instead of erroring. A
|
|
63
|
+
* strict schema turns that into a build error.
|
|
64
|
+
*/
|
|
65
|
+
export interface ActivityContract<Args = unknown, Return = unknown> {
|
|
66
|
+
readonly [CONTRACT_BRAND]: true;
|
|
67
|
+
/** The activity's registered name — must match a step's `fn`. */
|
|
68
|
+
name: string;
|
|
69
|
+
/** Schema every step's `args` (defaulted to `{}` when omitted) must satisfy. */
|
|
70
|
+
args: z.ZodType<Args>;
|
|
71
|
+
/**
|
|
72
|
+
* Schema the activity resolves to. Optional: an activity that returns
|
|
73
|
+
* nothing meaningful (or hasn't had its return type written down yet) can
|
|
74
|
+
* omit it. Needed to validate a step's `outcomeAttribute.from` path, and —
|
|
75
|
+
* per #1290 — a later step's reference into this one's output.
|
|
76
|
+
*/
|
|
77
|
+
returns?: z.ZodType<Return>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Declare an activity contract. */
|
|
81
|
+
export function activityContract<ArgsSchema extends z.ZodTypeAny, ReturnSchema extends z.ZodTypeAny = never>(
|
|
82
|
+
name: string,
|
|
83
|
+
args: ArgsSchema,
|
|
84
|
+
returns?: ReturnSchema,
|
|
85
|
+
): ActivityContract<z.infer<ArgsSchema>, ReturnSchema extends z.ZodTypeAny ? z.infer<ReturnSchema> : unknown> {
|
|
86
|
+
return {
|
|
87
|
+
[CONTRACT_BRAND]: true,
|
|
88
|
+
name,
|
|
89
|
+
args,
|
|
90
|
+
...(returns ? { returns } : {}),
|
|
91
|
+
} as ActivityContract<z.infer<ArgsSchema>, ReturnSchema extends z.ZodTypeAny ? z.infer<ReturnSchema> : unknown>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Structural guard for a value produced by {@link activityContract}. */
|
|
95
|
+
export function isActivityContract(value: unknown): value is ActivityContract {
|
|
96
|
+
return typeof value === "object" && value !== null && (value as Record<symbol, unknown>)[CONTRACT_BRAND] === true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Add every {@link ActivityContract} exported from an activity-contracts
|
|
101
|
+
* module to `into`, keyed by its declared `name` — the schema-shaped sibling
|
|
102
|
+
* of `activity-registry.ts`'s `collectActivities`.
|
|
103
|
+
*/
|
|
104
|
+
export function collectActivityContracts(mod: Record<string, unknown>, into: Map<string, ActivityContract>): void {
|
|
105
|
+
for (const value of Object.values(mod)) {
|
|
106
|
+
if (isActivityContract(value)) into.set(value.name, value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Validation ──────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
export interface ActivityContractIssue {
|
|
113
|
+
/** The Op the offending step belongs to. */
|
|
114
|
+
opName: string;
|
|
115
|
+
/** The phase the offending step belongs to. */
|
|
116
|
+
phase: string;
|
|
117
|
+
/** The activity name the offending step calls. */
|
|
118
|
+
fn: string;
|
|
119
|
+
/** Human-readable description of the mismatch. */
|
|
120
|
+
message: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Every `ActivityStep` in a phase, including ones nested inside an `EffectStep`. */
|
|
124
|
+
function activityStepsOf(steps: StepDefinition[]): ActivityStep[] {
|
|
125
|
+
return steps.flatMap((s) => (s.kind === "activity" ? [s] : s.kind === "effect" ? s.steps.filter((n) => n.kind === "activity") : []));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Same global symbol `step-output-ref.ts` brands a `StepOutputRef` with —
|
|
129
|
+
// `Symbol.for(...)` interns by string key, so this recognizes one without
|
|
130
|
+
// importing that module (which itself imports `pathExistsInSchema` below;
|
|
131
|
+
// importing the other way would make the two files a cycle).
|
|
132
|
+
const STEP_OUTPUT_REF_BRAND = Symbol.for("chant.op.stepOutputRef");
|
|
133
|
+
function isStepOutputRefValue(value: unknown): boolean {
|
|
134
|
+
return typeof value === "object" && value !== null && (value as Record<symbol, unknown>)[STEP_OUTPUT_REF_BRAND] === true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The value at `path` (a zod issue's `.path`) inside `obj`, or `undefined` if any segment doesn't resolve. */
|
|
138
|
+
function valueAtPath(obj: unknown, path: ReadonlyArray<PropertyKey>): unknown {
|
|
139
|
+
let current = obj;
|
|
140
|
+
for (const segment of path) {
|
|
141
|
+
if (current === null || typeof current !== "object") return undefined;
|
|
142
|
+
current = (current as Record<PropertyKey, unknown>)[segment];
|
|
143
|
+
}
|
|
144
|
+
return current;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Unwrap `ZodOptional`/`ZodNullable`/`ZodDefault` (and similar) down to the schema they wrap. */
|
|
148
|
+
function unwrap(schema: z.ZodTypeAny): z.ZodTypeAny {
|
|
149
|
+
let current = schema;
|
|
150
|
+
while (typeof (current as unknown as { unwrap?: () => z.ZodTypeAny }).unwrap === "function") {
|
|
151
|
+
current = (current as unknown as { unwrap: () => z.ZodTypeAny }).unwrap();
|
|
152
|
+
}
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Does dot-path `path` resolve to a field that exists on `schema`? Object
|
|
158
|
+
* shapes only — a return schema whose root (or an intermediate segment) is
|
|
159
|
+
* a `z.record(...)`/`z.array(...)` rather than a `z.object(...)` hard-errors
|
|
160
|
+
* (returns `false`) instead of skipping, deliberately (chant #1290 comment
|
|
161
|
+
* on #1288's pre-merge review): a record's keys are dynamic and an array's
|
|
162
|
+
* elements are index-addressed, neither of which a dot-path segment can
|
|
163
|
+
* check against in any way that's more meaningful than "the author probably
|
|
164
|
+
* meant something else." No declared `returns` schema needs this today, so
|
|
165
|
+
* there's no live case to design against yet. The escape hatch is an empty
|
|
166
|
+
* path — `outcomeAttribute.from` omitted, or a {@link StepOutputRef}'s
|
|
167
|
+
* `path` omitted — which references the whole return value and never calls
|
|
168
|
+
* this function; a record/array-returning activity's whole value is always
|
|
169
|
+
* a valid reference target.
|
|
170
|
+
*/
|
|
171
|
+
export function pathExistsInSchema(schema: z.ZodTypeAny, path: string): boolean {
|
|
172
|
+
return schemaAtPath(schema, path.split(".")) !== undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The zod schema at property-key path `path` inside `schema`, walking
|
|
177
|
+
* through `z.ZodObject` shapes only (unwrapping optional/nullable/default at
|
|
178
|
+
* each level, same as {@link pathExistsInSchema}). `undefined` when a
|
|
179
|
+
* segment doesn't resolve, or an intermediate schema isn't a `z.ZodObject` —
|
|
180
|
+
* same record/array hard-stop {@link pathExistsInSchema} documents. An empty
|
|
181
|
+
* `path` returns `schema` itself (unwrapped).
|
|
182
|
+
*/
|
|
183
|
+
export function schemaAtPath(schema: z.ZodTypeAny, path: ReadonlyArray<string>): z.ZodTypeAny | undefined {
|
|
184
|
+
let current = unwrap(schema);
|
|
185
|
+
for (const segment of path) {
|
|
186
|
+
if (!(current instanceof z.ZodObject)) return undefined;
|
|
187
|
+
const shape = current.shape as Record<string, z.ZodTypeAny>;
|
|
188
|
+
if (!(segment in shape)) return undefined;
|
|
189
|
+
current = unwrap(shape[segment]);
|
|
190
|
+
}
|
|
191
|
+
return current;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A primitive-shape classification of a zod schema — `string`/`number`/
|
|
196
|
+
* `boolean`/`object`/`array`, or `undefined` for anything else (a union,
|
|
197
|
+
* enum, literal, `z.any()`/`z.unknown()`, a transform, …). Used by the
|
|
198
|
+
* step-output-ref cross-contract type check (chant #1950-3) to compare a
|
|
199
|
+
* producer's declared return type against a consumer's declared arg type at
|
|
200
|
+
* the same structural position — deliberately shallow: it bails (returns
|
|
201
|
+
* `undefined`) on anything fancier than these five kinds rather than trying
|
|
202
|
+
* to reason about it, per that check's "bail out silently" design.
|
|
203
|
+
*/
|
|
204
|
+
export type PrimitiveSchemaKind = "string" | "number" | "boolean" | "object" | "array";
|
|
205
|
+
|
|
206
|
+
export function primitiveKindOf(schema: z.ZodTypeAny): PrimitiveSchemaKind | undefined {
|
|
207
|
+
const s = unwrap(schema);
|
|
208
|
+
if (s instanceof z.ZodString) return "string";
|
|
209
|
+
if (s instanceof z.ZodNumber) return "number";
|
|
210
|
+
if (s instanceof z.ZodBoolean) return "boolean";
|
|
211
|
+
if (s instanceof z.ZodObject) return "object";
|
|
212
|
+
if (s instanceof z.ZodArray) return "array";
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validate every activity step in an Op's phases (main and `onFailure`)
|
|
218
|
+
* against a contract map. A step whose `fn` has no entry in `contracts` is
|
|
219
|
+
* skipped — Stage 1 is opt-in per activity, not a hard requirement that
|
|
220
|
+
* every activity have a declared contract.
|
|
221
|
+
*
|
|
222
|
+
* Catches the four failure classes chant #1288 names:
|
|
223
|
+
* - an unrecognized `profile` (checked against {@link KNOWN_ACTIVITY_PROFILES}, independent of whether `fn` has a contract),
|
|
224
|
+
* - an args key the declared schema doesn't recognize,
|
|
225
|
+
* - an args value of the wrong type (including a required key that's missing),
|
|
226
|
+
* - an `outcomeAttribute.from` path that can't exist on the declared return type.
|
|
227
|
+
*/
|
|
228
|
+
export function validateActivitySteps(
|
|
229
|
+
config: Pick<OpConfig, "name" | "phases" | "onFailure">,
|
|
230
|
+
contracts: ReadonlyMap<string, ActivityContract>,
|
|
231
|
+
): ActivityContractIssue[] {
|
|
232
|
+
const issues: ActivityContractIssue[] = [];
|
|
233
|
+
const phasesToWalk: PhaseDefinition[] = [...config.phases, ...(config.onFailure ?? [])];
|
|
234
|
+
|
|
235
|
+
for (const phase of phasesToWalk) {
|
|
236
|
+
for (const step of activityStepsOf(phase.steps)) {
|
|
237
|
+
if (step.profile && !(KNOWN_ACTIVITY_PROFILES as readonly string[]).includes(step.profile)) {
|
|
238
|
+
issues.push({
|
|
239
|
+
opName: config.name,
|
|
240
|
+
phase: phase.name,
|
|
241
|
+
fn: step.fn,
|
|
242
|
+
message: `unknown profile "${step.profile}" (known: ${KNOWN_ACTIVITY_PROFILES.join(", ")})`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const contract = contracts.get(step.fn);
|
|
247
|
+
if (!contract) continue;
|
|
248
|
+
|
|
249
|
+
const parsed = contract.args.safeParse(step.args ?? {});
|
|
250
|
+
if (!parsed.success) {
|
|
251
|
+
for (const issue of parsed.error.issues) {
|
|
252
|
+
// A step-output reference (#1290) sitting at this path is a
|
|
253
|
+
// placeholder object at build time, not the value it will
|
|
254
|
+
// resolve to — so an args-schema type mismatch here is a false
|
|
255
|
+
// positive; TMP013 (`validateStepOutputRefs`) is what validates
|
|
256
|
+
// a reference, against the *producer's* declared return schema.
|
|
257
|
+
// An unrecognized-key issue's path is the parent object (`[]`
|
|
258
|
+
// for a top-level extra key), which is never itself a reference,
|
|
259
|
+
// so a genuinely misspelled key is still caught either way.
|
|
260
|
+
if (isStepOutputRefValue(valueAtPath(step.args, issue.path))) continue;
|
|
261
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(args)";
|
|
262
|
+
issues.push({ opName: config.name, phase: phase.name, fn: step.fn, message: `args.${path}: ${issue.message}` });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (step.outcomeAttribute?.from && contract.returns && !pathExistsInSchema(contract.returns, step.outcomeAttribute.from)) {
|
|
267
|
+
issues.push({
|
|
268
|
+
opName: config.name,
|
|
269
|
+
phase: phase.name,
|
|
270
|
+
fn: step.fn,
|
|
271
|
+
message: `outcomeAttribute.from "${step.outcomeAttribute.from}" does not exist on "${step.fn}"'s declared return type`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return issues;
|
|
278
|
+
}
|
|
@@ -50,7 +50,23 @@ describe("op builders are all reachable (#1715)", () => {
|
|
|
50
50
|
"spriteApplyServices", "spriteTaskCreate", "spriteTaskRefresh",
|
|
51
51
|
"spriteTaskRelease", "spritesUp", "spritesDown",
|
|
52
52
|
]);
|
|
53
|
-
|
|
53
|
+
// chant #1288 Stage 2 — these are the temporal lexicon's OWN activities
|
|
54
|
+
// (build.ts, shell.ts, wait.ts, ...), so its barrel re-exports a fully
|
|
55
|
+
// typed twin from its own `./op/builders` instead of core's untyped
|
|
56
|
+
// original — same name, same import path, deliberately not sourced from
|
|
57
|
+
// `@intentius/chant/op` any more. See `lexicons/temporal/src/index.ts`
|
|
58
|
+
// and `lexicons/temporal/src/op/builders.ts`.
|
|
59
|
+
const TEMPORAL_TYPED_SURFACE = new Set([
|
|
60
|
+
"build", "shell", "waitForStack", "lifecycleSnapshot", "teardown", "envTeardown", "httpCheck", "policyGate",
|
|
61
|
+
]);
|
|
62
|
+
// chant #1288 Stage 2 — an internal helper (`takeProfile`'s sibling) for
|
|
63
|
+
// the lexicon-owned typed step-builder wrappers to reuse, not an
|
|
64
|
+
// Op-authoring primitive itself; nothing authors an Op with it directly,
|
|
65
|
+
// so it has no place in the Op-authoring barrel.
|
|
66
|
+
const INTERNAL_HELPERS = new Set(["takeProfileAndId"]);
|
|
67
|
+
const expected = Object.keys(builders).filter(
|
|
68
|
+
(n) => !FLY_SURFACE.has(n) && !TEMPORAL_TYPED_SURFACE.has(n) && !INTERNAL_HELPERS.has(n),
|
|
69
|
+
);
|
|
54
70
|
const missing = expected.filter((name) => !reExported.has(name));
|
|
55
71
|
expect(
|
|
56
72
|
missing,
|
package/src/op/builders.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { OpResource } from "./resource";
|
|
2
|
-
import type { OpConfig, PhaseDefinition, StepDefinition, ActivityStep, GateStep } from "./types";
|
|
2
|
+
import type { OpConfig, PhaseDefinition, StepDefinition, ActivityStep, GateStep, EffectStep } from "./types";
|
|
3
|
+
import { isEffectReceipt, type EffectReceiptDeclaration } from "../effect-receipt";
|
|
4
|
+
import { receiptCheckInput } from "./receipt-store";
|
|
5
|
+
import { makeOutProxy, type StepOutputRef } from "./step-output-ref";
|
|
6
|
+
|
|
7
|
+
/** An `activity()` result — the plain `ActivityStep` shape plus the `.out` reference sugar (#1290). */
|
|
8
|
+
export interface NamedActivityStep extends ActivityStep {
|
|
9
|
+
/**
|
|
10
|
+
* `step.out.someField` builds a {@link StepOutputRef} into this step's
|
|
11
|
+
* declared return value, for use in a later step's `args`. Throws on
|
|
12
|
+
* access if this step has no `id` (pass one via `activity()`'s third
|
|
13
|
+
* argument) — there is nothing for a reference to name otherwise.
|
|
14
|
+
*/
|
|
15
|
+
readonly out: Record<string, StepOutputRef>;
|
|
16
|
+
}
|
|
3
17
|
|
|
4
18
|
// ── Core builders ─────────────────────────────────────────────────────────────
|
|
5
19
|
|
|
@@ -31,18 +45,44 @@ export function phase(
|
|
|
31
45
|
return { name, steps, ...(opts?.parallel ? { parallel: true } : {}) };
|
|
32
46
|
}
|
|
33
47
|
|
|
34
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Reference a pre-built or custom activity by function name.
|
|
50
|
+
*
|
|
51
|
+
* The third argument is a profile string (unchanged) or, to name the step
|
|
52
|
+
* so a later step can reference its output (#1290), an options object:
|
|
53
|
+
* `activity("lifecycleDiff", { env: "prod" }, { id: "diff" })`. Named or
|
|
54
|
+
* not, the result carries `.out` — `diff.out.driftedStacks` builds a
|
|
55
|
+
* reference to this step's declared return value; see `stepOutput()` in
|
|
56
|
+
* `@intentius/chant/op` for the equivalent on a step authored as a plain
|
|
57
|
+
* object literal instead of via this builder.
|
|
58
|
+
*/
|
|
35
59
|
export function activity(
|
|
36
60
|
fn: string,
|
|
37
61
|
args?: Record<string, unknown>,
|
|
38
|
-
profile?: ActivityStep["profile"],
|
|
39
|
-
):
|
|
40
|
-
|
|
62
|
+
opts?: ActivityStep["profile"] | { profile?: ActivityStep["profile"]; id?: string },
|
|
63
|
+
): NamedActivityStep {
|
|
64
|
+
const profile = typeof opts === "string" ? opts : opts?.profile;
|
|
65
|
+
const id = typeof opts === "string" ? undefined : opts?.id;
|
|
66
|
+
const step = {
|
|
41
67
|
kind: "activity",
|
|
42
68
|
fn,
|
|
43
69
|
...(args && Object.keys(args).length > 0 ? { args } : {}),
|
|
44
70
|
...(profile ? { profile } : {}),
|
|
45
|
-
|
|
71
|
+
...(id ? { id } : {}),
|
|
72
|
+
} as NamedActivityStep;
|
|
73
|
+
Object.defineProperty(step, "out", {
|
|
74
|
+
enumerable: false,
|
|
75
|
+
get(): Record<string, StepOutputRef> {
|
|
76
|
+
if (!step.id) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`activity(${JSON.stringify(fn)}, ...).out: this step has no id — pass one via ` +
|
|
79
|
+
`activity(fn, args, { id: "..." }) before referencing its output.`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return makeOutProxy(step.id);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return step;
|
|
46
86
|
}
|
|
47
87
|
|
|
48
88
|
/** Insert a human gate — the workflow pauses until the named signal is received. */
|
|
@@ -58,6 +98,62 @@ export function gate(
|
|
|
58
98
|
};
|
|
59
99
|
}
|
|
60
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Wrap nested steps in read-compare-run-write over an effect receipt (#1834,
|
|
103
|
+
* epic #1703). At run: read the live receipt through the receipt store,
|
|
104
|
+
* compare it against the resolved expectation, and on a match skip the nested
|
|
105
|
+
* steps ("effect already applied"). On a mismatch the nested steps run in
|
|
106
|
+
* authored order, and ONLY when every one of them succeeds is the receipt
|
|
107
|
+
* written — last, once. This step is the sole writer of a receipt (decision
|
|
108
|
+
* 3); a nested-step failure leaves the receipt untouched (stale), so the next
|
|
109
|
+
* run re-proposes the effect.
|
|
110
|
+
*
|
|
111
|
+
* `receipt` is the typed EffectReceipt declaration — import the const from
|
|
112
|
+
* your receipts module. There is no string form: a receipt named by string
|
|
113
|
+
* would sever the step from the declaration that lint, plan, and the lexicon
|
|
114
|
+
* row all key on.
|
|
115
|
+
*
|
|
116
|
+
* A gate authored inside `steps` pauses only when the effect will fire (the
|
|
117
|
+
* matched path never reaches it). The receipt-store activities (`receiptRead`,
|
|
118
|
+
* `receiptWrite`) are provided by the receipt row's lexicon (#1835, aws).
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import { seededReceipt } from "./receipts";
|
|
123
|
+
* phase("Seed", [
|
|
124
|
+
* effect(seededReceipt, [
|
|
125
|
+
* shell("npm run db:seed"),
|
|
126
|
+
* ]),
|
|
127
|
+
* ]),
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function effect(
|
|
131
|
+
receipt: EffectReceiptDeclaration,
|
|
132
|
+
steps: Array<ActivityStep | GateStep>,
|
|
133
|
+
opts?: { description?: string },
|
|
134
|
+
): EffectStep {
|
|
135
|
+
if (!isEffectReceipt(receipt)) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
"effect(): `receipt` must be the EffectReceipt declaration itself — import the const; there is no string form",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
for (const step of steps) {
|
|
141
|
+
if ((step as { kind?: unknown }).kind === "effect") {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`effect("${receipt.name}"): effect steps do not nest — one receipt witnesses one effect`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const { receipt: ref, expectation } = receiptCheckInput(receipt);
|
|
148
|
+
return {
|
|
149
|
+
kind: "effect",
|
|
150
|
+
receipt: ref,
|
|
151
|
+
...(expectation !== undefined ? { expectation } : {}),
|
|
152
|
+
steps,
|
|
153
|
+
...(opts?.description ? { description: opts.description } : {}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
61
157
|
// ── Pre-built activity shortcuts ──────────────────────────────────────────────
|
|
62
158
|
|
|
63
159
|
/**
|
|
@@ -78,6 +174,22 @@ function takeProfile(
|
|
|
78
174
|
return { args, profile };
|
|
79
175
|
}
|
|
80
176
|
|
|
177
|
+
/**
|
|
178
|
+
* {@link takeProfile}'s sibling for the typed, lexicon-owned step-builder
|
|
179
|
+
* wrappers added in chant #1288 Stage 2 (e.g. `kubectlApply` in
|
|
180
|
+
* `@intentius/chant-lexicon-k8s`): also pulls `id` out of the opts bag so it
|
|
181
|
+
* routes to the step's `id` field (enabling `.out` — #1290) instead of
|
|
182
|
+
* leaking into the activity's args, the same way `profile` would without
|
|
183
|
+
* this split.
|
|
184
|
+
*/
|
|
185
|
+
export function takeProfileAndId(
|
|
186
|
+
opts: (Record<string, unknown> & { profile?: ActivityStep["profile"]; id?: string }) | undefined,
|
|
187
|
+
): { args: Record<string, unknown>; profile?: ActivityStep["profile"]; id?: string } {
|
|
188
|
+
if (!opts) return { args: {} };
|
|
189
|
+
const { profile, id, ...args } = opts as { profile?: ActivityStep["profile"]; id?: string } & Record<string, unknown>;
|
|
190
|
+
return { args, profile, id };
|
|
191
|
+
}
|
|
192
|
+
|
|
81
193
|
/** Run an npm build script in the given project directory. `opts.script` selects the script (default `build`, e.g. `build:aws`); `opts.env` adds env vars. */
|
|
82
194
|
export const build = (path: string, opts?: Record<string, unknown>): ActivityStep => {
|
|
83
195
|
const { args, profile } = takeProfile(opts);
|
|
@@ -100,6 +212,22 @@ export const helmInstall = (
|
|
|
100
212
|
return activity("helmInstall", { name, chart, ...args }, profile ?? "longInfra");
|
|
101
213
|
};
|
|
102
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Deploy a recorded pinned render by its `sha256:` content digest (chant
|
|
217
|
+
* #1242): the helm lexicon's `helmInstall` activity loads the render from
|
|
218
|
+
* the render store, verifies the digest, and installs those exact bytes as
|
|
219
|
+
* a structure-preserving wrapper chart — no deploy-time render. Defaults to
|
|
220
|
+
* the `longInfra` profile (override via `opts.profile`).
|
|
221
|
+
*/
|
|
222
|
+
export const helmInstallPinned = (
|
|
223
|
+
name: string,
|
|
224
|
+
contentDigest: string,
|
|
225
|
+
opts?: { namespace?: string; profile?: ActivityStep["profile"]; [k: string]: unknown },
|
|
226
|
+
): ActivityStep => {
|
|
227
|
+
const { args, profile } = takeProfile(opts);
|
|
228
|
+
return activity("helmInstall", { name, contentDigest, ...args }, profile ?? "longInfra");
|
|
229
|
+
};
|
|
230
|
+
|
|
103
231
|
/** Poll for stack readiness (kubectl rollout, CloudFormation complete, etc). Defaults to the `k8sWait` profile (override via `opts.profile`). */
|
|
104
232
|
export const waitForStack = (name: string, opts?: Record<string, unknown>): ActivityStep => {
|
|
105
233
|
const { args, profile } = takeProfile(opts);
|
|
@@ -133,6 +261,28 @@ export const shell = (
|
|
|
133
261
|
): ActivityStep =>
|
|
134
262
|
activity("shellCmd", { cmd, ...(opts?.env ? { env: opts.env } : {}) }, opts?.profile);
|
|
135
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Ensure a `generated-once` secret exists in the target store (#1829, epic
|
|
266
|
+
* #1365). The op-step surface over the same engine as the `ensure-secret`
|
|
267
|
+
* capability verb (`ensureSecretMaterialization`, core's
|
|
268
|
+
* secret-materialization module): read-then-write — if the secret exists, its
|
|
269
|
+
* declared key-set and any declared `metadata` are verified and the step
|
|
270
|
+
* stops (present means done); if absent, one value per key is minted at
|
|
271
|
+
* apply time and written straight to the store. Never mints over an existing
|
|
272
|
+
* value, never rotates implicitly; a mismatch fails the step loudly, naming
|
|
273
|
+
* key names and metadata keys — never values. No output of the activity
|
|
274
|
+
* carries secret material. The backing activity is provided by the store's
|
|
275
|
+
* lexicon (k8s, #1830).
|
|
276
|
+
*/
|
|
277
|
+
export const ensureSecret = (
|
|
278
|
+
name: string,
|
|
279
|
+
keys: string[],
|
|
280
|
+
opts?: { metadata?: Record<string, string>; profile?: ActivityStep["profile"]; [k: string]: unknown },
|
|
281
|
+
): ActivityStep => {
|
|
282
|
+
const { args, profile } = takeProfile(opts);
|
|
283
|
+
return activity("ensureSecret", { name, keys, ...args }, profile);
|
|
284
|
+
};
|
|
285
|
+
|
|
136
286
|
/** Run `chant teardown` in the given project directory. Uses `longInfra` profile. */
|
|
137
287
|
export const teardown = (path: string): ActivityStep =>
|
|
138
288
|
activity("chantTeardown", { path }, "longInfra");
|
|
@@ -200,6 +350,48 @@ export const k3dDown = (name: string, opts?: Record<string, unknown>): ActivityS
|
|
|
200
350
|
return activity("k3dDown", { name, ...args }, profile ?? "fastIdempotent");
|
|
201
351
|
};
|
|
202
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Run the pinned k3s installer against a reachable host (`role`: `"server"`
|
|
355
|
+
* or `"agent"`). Idempotent on an already-installed matching version.
|
|
356
|
+
* Defaults to the `longInfra` profile (the installer downloads and starts
|
|
357
|
+
* the binary); override via `opts.profile`.
|
|
358
|
+
*
|
|
359
|
+
* The implementation lives in the k3s lexicon (chant #1601) — the project's
|
|
360
|
+
* `chant.config.ts` must list `"k3s"` in `lexicons` for the activity to load.
|
|
361
|
+
* Bounded exactly as `k3dUp`/`k3dDown` (chant #1410): drives the reachable-host
|
|
362
|
+
* case only, no host provisioning, no SSH orchestration.
|
|
363
|
+
*
|
|
364
|
+
* `opts` accepts `configFile` (required — path to the chant-emitted
|
|
365
|
+
* config.yaml, passed as `--config`), `version` (overrides the lexicon's
|
|
366
|
+
* pinned `INSTALL_K3S_VERSION`), and `tokenFile` (path to a file holding the
|
|
367
|
+
* join token, passed to the installer as `K3S_TOKEN_FILE`). There is no
|
|
368
|
+
* `token` option — the join secret's value never travels through this step;
|
|
369
|
+
* only a file path does (the #1601 token boundary, same stance as #1365).
|
|
370
|
+
*/
|
|
371
|
+
export const k3sInstall = (
|
|
372
|
+
role: "server" | "agent",
|
|
373
|
+
opts: { configFile: string; version?: string; tokenFile?: string; profile?: ActivityStep["profile"]; [k: string]: unknown },
|
|
374
|
+
): ActivityStep => {
|
|
375
|
+
const { args, profile } = takeProfile(opts);
|
|
376
|
+
return activity("k3sInstall", { role, ...args }, profile ?? "longInfra");
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Uninstall k3s from a reachable host (`role`: `"server"` or `"agent"`).
|
|
381
|
+
* Defaults to the `fastIdempotent` profile (override via `opts.profile`).
|
|
382
|
+
* Implementation lives in the k3s lexicon (chant #1601) — requires `"k3s"`
|
|
383
|
+
* in the project's `lexicons`. A host where k3s was never installed is a
|
|
384
|
+
* no-op success, the same shape as `k3dDown` against an already-gone
|
|
385
|
+
* cluster.
|
|
386
|
+
*/
|
|
387
|
+
export const k3sUninstall = (
|
|
388
|
+
role: "server" | "agent",
|
|
389
|
+
opts?: Record<string, unknown>,
|
|
390
|
+
): ActivityStep => {
|
|
391
|
+
const { args, profile } = takeProfile(opts);
|
|
392
|
+
return activity("k3sUninstall", { role, ...args }, profile ?? "fastIdempotent");
|
|
393
|
+
};
|
|
394
|
+
|
|
203
395
|
/**
|
|
204
396
|
* Boot a local Floci AWS emulator in Docker and point subsequent steps at it —
|
|
205
397
|
* sets `AWS_ENDPOINT_URL` + test creds in the process env so a following
|