@intentius/chant 0.57.0 → 0.59.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 +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +36 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +26 -9
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- 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 +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts +13 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +22 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +40 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +24 -22
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +21 -6
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +73 -37
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +10 -22
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/detectLexicon.d.ts +13 -0
- package/dist/detectLexicon.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +273 -16
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +79 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +149 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +85 -30
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/change-signal.d.ts +91 -0
- package/dist/op/change-signal.d.ts.map +1 -0
- package/dist/op/composites/apply-op.d.ts +118 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +40 -0
- package/dist/op/gate-name.d.ts.map +1 -0
- package/dist/op/gate-summary.d.ts +43 -0
- package/dist/op/gate-summary.d.ts.map +1 -0
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +29 -9
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +185 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +93 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +96 -29
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/dist/terraform/bridge.d.ts +26 -7
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve-provider.d.ts +28 -2
- package/dist/terraform/carve-provider.d.ts.map +1 -1
- package/dist/terraform/data-source-shape.d.ts +77 -0
- package/dist/terraform/data-source-shape.d.ts.map +1 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/providers/kubernetes.d.ts +5 -0
- package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
- package/dist/terraform/tier-map.d.ts +16 -4
- package/dist/terraform/tier-map.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +84 -18
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +202 -18
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +191 -11
- package/src/cli/commands/audit.ts +78 -28
- package/src/cli/commands/build.test.ts +330 -9
- package/src/cli/commands/build.ts +170 -24
- package/src/cli/commands/carve-bridge.test.ts +237 -14
- package/src/cli/commands/carve-bridge.ts +21 -17
- package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +194 -1
- package/src/cli/commands/lint.ts +205 -7
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +341 -55
- package/src/cli/handlers/run.test.ts +568 -1295
- package/src/cli/handlers/run.ts +446 -912
- package/src/cli/main.test.ts +22 -16
- package/src/cli/main.ts +66 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/plugins.test.ts +68 -2
- package/src/cli/plugins.ts +39 -0
- package/src/cli/registry.ts +40 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +4 -3
- package/src/components/__fixtures__/neo4j-fanout.json +1 -1
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +28 -12
- package/src/components/cli-support.ts +60 -47
- package/src/components/component-schema.test.ts +18 -2
- package/src/components/component.schema.json +18 -5
- package/src/components/component.test.ts +2 -2
- package/src/components/component.ts +27 -7
- package/src/components/config-defaults.test.ts +2 -2
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +109 -20
- package/src/components/driver.ts +201 -84
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/neo4j-fanout.pilot.ts +3 -3
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.test.ts +19 -0
- package/src/components/verbs/run-agent.ts +12 -24
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/detectLexicon.ts +18 -1
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.test.ts +1 -1
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +82 -2
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +3 -3
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +296 -16
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +154 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/pipeline-change-gate.test.ts +2 -2
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +27 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +161 -0
- package/src/op/activities/reconcile.ts +354 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +125 -41
- package/src/op/change-signal.test.ts +117 -0
- package/src/op/change-signal.ts +169 -0
- package/src/op/composites/apply-op.ts +218 -0
- package/src/op/composites/composites.test.ts +379 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +110 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +29 -14
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate-name.test.ts +65 -0
- package/src/op/gate-name.ts +60 -0
- package/src/op/gate-summary.ts +84 -0
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +48 -8
- package/src/op/local-executor.test.ts +178 -34
- package/src/op/local-executor.ts +371 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +292 -0
- package/src/op/op-ir.ts +419 -0
- package/src/op/op-verb-class.test.ts +2 -2
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +28 -7
- package/src/op/operator.test.ts +543 -6
- package/src/op/operator.ts +203 -20
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +106 -26
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/src/terraform/aws-resources.test.ts +13 -4
- package/src/terraform/bridge.test.ts +22 -9
- package/src/terraform/bridge.ts +89 -28
- package/src/terraform/carve-provider.ts +38 -2
- package/src/terraform/data-source-shape.ts +95 -0
- package/src/terraform/graph.ts +35 -7
- package/src/terraform/providers/kubernetes.ts +48 -2
- package/src/terraform/tier-map.ts +21 -6
- package/src/terraform/types.ts +8 -0
- package/dist/cli/handlers/op-progress.d.ts +0 -90
- package/dist/cli/handlers/op-progress.d.ts.map +0 -1
- package/dist/cli/handlers/run-client.d.ts +0 -127
- package/dist/cli/handlers/run-client.d.ts.map +0 -1
- package/dist/cli/handlers/run-report.d.ts +0 -5
- package/dist/cli/handlers/run-report.d.ts.map +0 -1
- package/dist/components/temporal-codegen-loader.d.ts +0 -27
- package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
- package/src/cli/handlers/op-progress.test.ts +0 -268
- package/src/cli/handlers/op-progress.ts +0 -248
- package/src/cli/handlers/run-client.test.ts +0 -82
- package/src/cli/handlers/run-client.ts +0 -219
- package/src/cli/handlers/run-report.test.ts +0 -62
- package/src/cli/handlers/run-report.ts +0 -122
- package/src/components/temporal-codegen-loader.ts +0 -41
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
package/src/provenance.ts
CHANGED
|
@@ -71,11 +71,27 @@ export interface EntityProvenance {
|
|
|
71
71
|
* is governed by `tier`, which is what an editor needs to know. Under-reports
|
|
72
72
|
* (a shape the collector does not follow drops a dependency) and never
|
|
73
73
|
* over-reports a parameter the expression does not mention.
|
|
74
|
+
* - `composite-parameter` — the fold interpreted the composite's factory body,
|
|
75
|
+
* and the expression for this path reads the named factory parameter paths
|
|
76
|
+
* (chant #2161). Same syntactic-dependency discipline as `build-param`, and
|
|
77
|
+
* for the same reason: a shape the collector does not follow drops a
|
|
78
|
+
* parameter, it never invents one.
|
|
79
|
+
* - `composite-literal` — the fold interpreted the factory body and the
|
|
80
|
+
* expression for this path reads no parameter at all. The composite fixes
|
|
81
|
+
* this field, and no argument at the call site moves it.
|
|
82
|
+
*
|
|
83
|
+
* The coarse `composite` and the two fine kinds are not alternatives. A build
|
|
84
|
+
* records `composite` when it expanded a composite without interpreting its
|
|
85
|
+
* body (the run path, an inadmissible factory), and that is why
|
|
86
|
+
* ./fold-provenance.ts answers `unknown` there rather than falling through to a
|
|
87
|
+
* direct declaration.
|
|
74
88
|
*/
|
|
75
89
|
export type PathOrigin =
|
|
76
90
|
| { kind: "authored" }
|
|
77
91
|
| { kind: "composite"; composite: string; instance: string }
|
|
78
|
-
| { kind: "build-param"; params: string[] }
|
|
92
|
+
| { kind: "build-param"; params: string[] }
|
|
93
|
+
| { kind: "composite-parameter"; composite: string; parameters: string[] }
|
|
94
|
+
| { kind: "composite-literal"; composite: string };
|
|
79
95
|
|
|
80
96
|
/** True when `prefix` addresses `path` or an ancestor of it, on a segment boundary. */
|
|
81
97
|
function isPathPrefix(prefix: string, path: string): boolean {
|
|
@@ -170,6 +186,10 @@ export function describePathOrigin(origin: PathOrigin): string {
|
|
|
170
186
|
return `composite ${origin.composite} (${origin.instance})`;
|
|
171
187
|
case "build-param":
|
|
172
188
|
return `param ${origin.params.join(", ")}`;
|
|
189
|
+
case "composite-parameter":
|
|
190
|
+
return `composite ${origin.composite} parameter ${origin.parameters.join(", ")}`;
|
|
191
|
+
case "composite-literal":
|
|
192
|
+
return `composite ${origin.composite} literal`;
|
|
173
193
|
}
|
|
174
194
|
}
|
|
175
195
|
|
|
@@ -4,6 +4,7 @@ import { DECLARABLE_MARKER, type Declarable } from "./declarable";
|
|
|
4
4
|
import { INTRINSIC_MARKER } from "./intrinsic";
|
|
5
5
|
import { AttrRef } from "./attrref";
|
|
6
6
|
import { createResource } from "./runtime";
|
|
7
|
+
import { heldElsewhere } from "./held-elsewhere";
|
|
7
8
|
|
|
8
9
|
function makeDeclarable(type: string, kind: "resource" | "property" = "resource", props?: Record<string, unknown>): Declarable & { props?: Record<string, unknown> } {
|
|
9
10
|
const d: Declarable & { props?: Record<string, unknown> } = {
|
|
@@ -145,3 +146,44 @@ describe("walkValue", () => {
|
|
|
145
146
|
});
|
|
146
147
|
});
|
|
147
148
|
});
|
|
149
|
+
|
|
150
|
+
// #2162 — a `heldElsewhere()` marker is never written to the applied
|
|
151
|
+
// payload: the field is omitted entirely so the provider defaults it once at
|
|
152
|
+
// creation and the named holder owns it from there. Every apply this
|
|
153
|
+
// synthesizes omits it, not only the first — re-asserting even the field's
|
|
154
|
+
// own value on a later apply would fight whatever the holder wrote in
|
|
155
|
+
// between.
|
|
156
|
+
describe("walkValue — heldElsewhere() markers (#2162)", () => {
|
|
157
|
+
test("a top-level held value walks to undefined", () => {
|
|
158
|
+
const names = new Map<Declarable, string>();
|
|
159
|
+
const marker = heldElsewhere<number>({ by: "hpa", reason: "x" });
|
|
160
|
+
expect(walkValue(marker, names, mockVisitor)).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("an object key holding a marker is omitted entirely, not set to undefined", () => {
|
|
164
|
+
const names = new Map<Declarable, string>();
|
|
165
|
+
const value = {
|
|
166
|
+
replicas: heldElsewhere<number>({ by: "hpa", reason: "the autoscaler owns replicas after the first apply" }),
|
|
167
|
+
image: "app:1",
|
|
168
|
+
};
|
|
169
|
+
const result = walkValue(value, names, mockVisitor);
|
|
170
|
+
expect(result).toEqual({ image: "app:1" });
|
|
171
|
+
expect(Object.keys(result as object)).not.toContain("replicas");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("a held element inside an array is dropped, not left as a hole", () => {
|
|
175
|
+
const names = new Map<Declarable, string>();
|
|
176
|
+
const value = { tolerations: ["a", heldElsewhere<string>({ by: "controller", reason: "x" }), "b"] };
|
|
177
|
+
expect(walkValue(value, names, mockVisitor)).toEqual({ tolerations: ["a", "b"] });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("nested inside a property-kind declarable's own props, still omitted", () => {
|
|
181
|
+
const names = new Map<Declarable, string>();
|
|
182
|
+
const scaleTarget = makeDeclarable("K8s::Autoscaling::HorizontalPodAutoscaler.Behavior", "property", {
|
|
183
|
+
scaleUp: heldElsewhere<Record<string, unknown>>({ by: "hpa", reason: "the controller tunes its own policy" }),
|
|
184
|
+
stabilizationWindowSeconds: 60,
|
|
185
|
+
});
|
|
186
|
+
const result = walkValue(scaleTarget, names, mockVisitor);
|
|
187
|
+
expect(result).toEqual({ stabilizationWindowSeconds: 60 });
|
|
188
|
+
});
|
|
189
|
+
});
|
package/src/serializer-walker.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Declarable } from "./declarable";
|
|
9
9
|
import { isPropertyDeclarable } from "./declarable";
|
|
10
|
+
import { isHeldElsewhere } from "./held-elsewhere";
|
|
10
11
|
import { INTRINSIC_MARKER } from "./intrinsic";
|
|
11
12
|
import { AttrRef } from "./attrref";
|
|
12
13
|
import { isAttrRefLike } from "./utils";
|
|
@@ -33,6 +34,16 @@ export function walkValue(
|
|
|
33
34
|
return value;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// A `heldElsewhere()` marker (#2162) is never written to the applied
|
|
38
|
+
// payload — the provider defaults the field once at actual creation, and
|
|
39
|
+
// whoever the declaration names as holder owns it from there. Every apply
|
|
40
|
+
// this synthesizes therefore omits the field entirely, not only the
|
|
41
|
+
// first: re-asserting even the field's own value on a later apply would
|
|
42
|
+
// fight whatever the holder wrote in between. Checked first, ahead of
|
|
43
|
+
// every other dispatch branch below — a held marker is never an AttrRef,
|
|
44
|
+
// an Intrinsic, or authored Declarable data.
|
|
45
|
+
if (isHeldElsewhere(value)) return undefined;
|
|
46
|
+
|
|
36
47
|
// Handle AttrRef
|
|
37
48
|
if (isAttrRefLike(value)) {
|
|
38
49
|
const name = value.getLogicalName();
|
|
@@ -77,9 +88,13 @@ export function walkValue(
|
|
|
77
88
|
return visitor.propertyDeclarable(decl, (v) => walkValue(v, entityNames, visitor));
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
// Handle arrays
|
|
91
|
+
// Handle arrays. A held marker element is filtered out before recursing —
|
|
92
|
+
// `walkValue` would return `undefined` for it too, but leaving it in the
|
|
93
|
+
// array would emit a hole (`[a, null, b]`) rather than omitting it.
|
|
81
94
|
if (Array.isArray(value)) {
|
|
82
|
-
return value
|
|
95
|
+
return value
|
|
96
|
+
.filter((item) => !isHeldElsewhere(item))
|
|
97
|
+
.map((item) => walkValue(item, entityNames, visitor));
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
// Handle serialized AttrRef envelopes (produced by AttrRef.toJSON() inside intrinsics)
|
|
@@ -92,6 +107,10 @@ export function walkValue(
|
|
|
92
107
|
if (typeof value === "object") {
|
|
93
108
|
const result: Record<string, unknown> = {};
|
|
94
109
|
for (const [key, val] of Object.entries(value)) {
|
|
110
|
+
// A held marker key is omitted entirely (#2162), not set to
|
|
111
|
+
// `undefined` — the two read differently to a YAML emitter, and only
|
|
112
|
+
// omission is "the provider never hears about this field".
|
|
113
|
+
if (isHeldElsewhere(val)) continue;
|
|
95
114
|
const outKey = key;
|
|
96
115
|
result[outKey] = walkValue(val, entityNames, visitor);
|
|
97
116
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { AWS_CARVE_TYPES, AWS_FOLD_MAPPERS, awsCarveType, applyAwsMapper, applyAwsFold } from "./aws-resources";
|
|
3
|
-
import { tierMap, foldParentOf, identityAttrOf, canBridge, canCarveEmit, carveEmitTypes } from "./tier-map";
|
|
3
|
+
import { tierMap, foldParentOf, identityAttrOf, canBridge, dataSourceShapeOf, canCarveEmit, carveEmitTypes } from "./tier-map";
|
|
4
4
|
import { canAdoptFromState } from "./adopt-state";
|
|
5
5
|
|
|
6
6
|
describe("AWS carve-out table", () => {
|
|
@@ -27,13 +27,22 @@ describe("AWS carve-out table", () => {
|
|
|
27
27
|
for (const t of carveEmitTypes()) expect(canAdoptFromState(t)).toBe(true);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test("canBridge
|
|
31
|
-
// A data source body is flat `attr = value`; a dotted path is not HCL.
|
|
30
|
+
test("canBridge follows the data-source shape, not the identity attribute (#2015, #2034)", () => {
|
|
31
|
+
// A data source body is flat `attr = value`; a dotted path is not HCL. So a
|
|
32
|
+
// dotted identity attribute bridges only through a declared shape, which is
|
|
33
|
+
// what `kubernetes_manifest` has: `data "kubernetes_resource"`.
|
|
32
34
|
expect(identityAttrOf("kubernetes_manifest")).toContain(".");
|
|
33
|
-
expect(canBridge("kubernetes_manifest")).toBe(
|
|
35
|
+
expect(canBridge("kubernetes_manifest")).toBe(true);
|
|
36
|
+
expect(dataSourceShapeOf("kubernetes_manifest")!.type).toBe("kubernetes_resource");
|
|
37
|
+
// A plain identity attribute implies its own same-type shape.
|
|
34
38
|
expect(canBridge("aws_s3_bucket")).toBe(true);
|
|
39
|
+
expect(dataSourceShapeOf("aws_s3_bucket")).toEqual({
|
|
40
|
+
type: "aws_s3_bucket",
|
|
41
|
+
args: [{ name: "bucket", from: "bucket", required: true }],
|
|
42
|
+
});
|
|
35
43
|
// No identity entry at all is fine — the bridge writes a TODO comment.
|
|
36
44
|
expect(canBridge("random_pet")).toBe(true);
|
|
45
|
+
expect(dataSourceShapeOf("random_pet")).toEqual({ type: "random_pet" });
|
|
37
46
|
for (const t of AWS_CARVE_TYPES) expect(canBridge(t.tfType)).toBe(true);
|
|
38
47
|
});
|
|
39
48
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { buildFixtureGraph } from "./__fixtures__/build-graph";
|
|
3
3
|
import { boundaryReport } from "./carve";
|
|
4
|
-
import { generateBridge, type
|
|
4
|
+
import { generateBridge, type CarvedDataSource } from "./bridge";
|
|
5
|
+
import { dataSourceShapeOf } from "./tier-map";
|
|
5
6
|
import type { Hcl2JsonTree } from "./types";
|
|
6
7
|
|
|
7
8
|
const workedExample: Hcl2JsonTree = {
|
|
@@ -35,8 +36,10 @@ const API_TF = `resource "aws_lambda_function" "api" {
|
|
|
35
36
|
}
|
|
36
37
|
`;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// The read-back for the bucket: the shape its identity attribute implies, plus
|
|
40
|
+
// the literal the graph resolved out of the block.
|
|
41
|
+
const identities = new Map<string, CarvedDataSource>([
|
|
42
|
+
["aws_s3_bucket.assets", { shape: dataSourceShapeOf("aws_s3_bucket"), values: { bucket: "myapp-assets-prod" } }],
|
|
40
43
|
]);
|
|
41
44
|
|
|
42
45
|
describe("generateBridge — inbound (data-source rewrite)", () => {
|
|
@@ -51,18 +54,28 @@ describe("generateBridge — inbound (data-source rewrite)", () => {
|
|
|
51
54
|
);
|
|
52
55
|
});
|
|
53
56
|
|
|
54
|
-
test("
|
|
57
|
+
test("a type with no shape gets a TODO body, never a dotted attr assignment (#2015)", () => {
|
|
55
58
|
const report = boundaryReport(buildFixtureGraph(workedExample), "aws_s3_bucket.assets")!;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
59
|
+
// A dotted identity attribute implies no shape at all — `canBridge` refuses
|
|
60
|
+
// such a type before the bridge runs — but an empty read-back still has to
|
|
61
|
+
// produce a block Terraform can parse, not `manifest.metadata.name = "x"`.
|
|
62
|
+
const noShape = new Map<string, CarvedDataSource>([["aws_s3_bucket.assets", {}]]);
|
|
63
|
+
const plan = generateBridge(report, [{ path: "api.tf", content: API_TF }], noShape);
|
|
60
64
|
|
|
61
|
-
// `manifest.metadata.name = "demo-config"` is not valid HCL.
|
|
62
65
|
expect(plan.dataSources[0].hcl).not.toContain("manifest.metadata.name =");
|
|
63
66
|
expect(plan.dataSources[0].hcl).toContain("# TODO: identify the resource");
|
|
64
67
|
});
|
|
65
68
|
|
|
69
|
+
test("a shape whose required argument has no literal falls back to the TODO (#2034)", () => {
|
|
70
|
+
const report = boundaryReport(buildFixtureGraph(workedExample), "aws_s3_bucket.assets")!;
|
|
71
|
+
const unresolved = new Map<string, CarvedDataSource>([
|
|
72
|
+
["aws_s3_bucket.assets", { shape: dataSourceShapeOf("aws_s3_bucket"), values: {} }],
|
|
73
|
+
]);
|
|
74
|
+
const plan = generateBridge(report, [{ path: "api.tf", content: API_TF }], unresolved);
|
|
75
|
+
// A `data "aws_s3_bucket" "assets" {}` with no `bucket` would not plan.
|
|
76
|
+
expect(plan.dataSources[0].hcl).toContain("# TODO: identify the resource");
|
|
77
|
+
});
|
|
78
|
+
|
|
66
79
|
test("rewrites survivor references to the data source", () => {
|
|
67
80
|
const report = boundaryReport(buildFixtureGraph(workedExample), "aws_s3_bucket.assets")!;
|
|
68
81
|
const plan = generateBridge(report, [{ path: "api.tf", content: API_TF }], identities);
|
package/src/terraform/bridge.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* - inbound edge → the survivor loses its reference to the carved resource.
|
|
8
8
|
* Bridge: add a `data` source for the (now chant-managed) resource and
|
|
9
9
|
* rewrite the survivor's `type.name.attr` references to `data.type.name.attr`.
|
|
10
|
+
* The provider's data-source shape (`data-source-shape.ts`, #2034) decides
|
|
11
|
+
* both halves — the data type and body, and the attribute path survivors
|
|
12
|
+
* read through, which need not be the resource's own.
|
|
10
13
|
* Required immediately, or `terraform plan` errors on the dangling ref.
|
|
11
14
|
* An `output` block reading the carved resource is such a survivor (#1638):
|
|
12
15
|
* same data source, same textual rewrite, applied to the output's value.
|
|
@@ -22,18 +25,32 @@
|
|
|
22
25
|
*/
|
|
23
26
|
|
|
24
27
|
import { deferredParamName, type CarveReport } from "./carve";
|
|
28
|
+
import { rewriteReadPath, type DataSourceShape, type ShapeField } from "./data-source-shape";
|
|
25
29
|
import { exciseResourceBlocks, type ExciseTarget } from "./excise";
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
/** How one carved resource is read back, and the literals to render it with. */
|
|
32
|
+
export interface CarvedDataSource {
|
|
33
|
+
/**
|
|
34
|
+
* The type's data-source shape (`dataSourceShapeOf`): the `data` type, its
|
|
35
|
+
* arguments and where each comes from, and the survivor path translation.
|
|
36
|
+
*/
|
|
37
|
+
shape?: DataSourceShape;
|
|
38
|
+
/**
|
|
39
|
+
* Literals resolved from the carved block, keyed by the shape field's source
|
|
40
|
+
* path — `{ bucket: "myapp-assets-prod" }`, or for a manifest
|
|
41
|
+
* `{ "manifest.apiVersion": "v1", "manifest.metadata.name": "app-config" }`.
|
|
42
|
+
*/
|
|
43
|
+
values?: Record<string, string>;
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
export interface DataSourceBlock {
|
|
35
47
|
/** Carved resource address the data source stands in for. */
|
|
36
48
|
address: string;
|
|
49
|
+
/**
|
|
50
|
+
* The `data` type. Usually the carved resource's own type; the shape can
|
|
51
|
+
* name another, as `kubernetes_manifest` reads back through
|
|
52
|
+
* `data "kubernetes_resource"` (#2034).
|
|
53
|
+
*/
|
|
37
54
|
type: string;
|
|
38
55
|
name: string;
|
|
39
56
|
hcl: string;
|
|
@@ -73,22 +90,64 @@ export interface BridgePlan {
|
|
|
73
90
|
runbook: string;
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* `aws_s3_bucket.assets` → a regex matching that reference head plus the
|
|
95
|
+
* attribute path that follows it, not already `data.`-prefixed. The path is
|
|
96
|
+
* captured because the data source may expose it under a different attribute:
|
|
97
|
+
* a survivor reading `kubernetes_manifest.app_config.manifest.data.x` has to
|
|
98
|
+
* come out as `data.kubernetes_resource.app_config.object.data.x`.
|
|
99
|
+
*/
|
|
77
100
|
function referenceRegex(address: string): RegExp {
|
|
78
101
|
const escaped = address.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
79
102
|
// (?<!data\.) — do not double-prefix an existing data source reference.
|
|
80
|
-
return new RegExp(`(?<!data\\.)\\b${escaped}\\b`, "g");
|
|
103
|
+
return new RegExp(`(?<!data\\.)\\b${escaped}\\b((?:\\.[A-Za-z_][A-Za-z0-9_-]*)*)`, "g");
|
|
81
104
|
}
|
|
82
105
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
const IDENTIFY_TODO = " # TODO: identify the resource (its name/id was interpolated in the source)";
|
|
107
|
+
|
|
108
|
+
/** `terraform fmt` aligns the `=` of consecutive assignments; so do we. */
|
|
109
|
+
function alignedAssignments(pairs: Array<[string, string]>, indent: string): string[] {
|
|
110
|
+
const width = Math.max(...pairs.map(([key]) => key.length));
|
|
111
|
+
return pairs.map(([key, value]) => `${indent}${key.padEnd(width)} = ${JSON.stringify(value)}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Resolved `[name, value]` pairs, or null when a required field has no literal. */
|
|
115
|
+
function resolveFields(fields: readonly ShapeField[], values: Record<string, string>): Array<[string, string]> | null {
|
|
116
|
+
const pairs: Array<[string, string]> = [];
|
|
117
|
+
for (const field of fields) {
|
|
118
|
+
const value = values[field.from];
|
|
119
|
+
if (value === undefined) {
|
|
120
|
+
if (field.required) return null;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
pairs.push([field.name, value]);
|
|
124
|
+
}
|
|
125
|
+
return pairs;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The `data` block standing in for the carved resource. A shape whose required
|
|
130
|
+
* arguments the carved block did not supply as literals renders the TODO body
|
|
131
|
+
* instead of a block `terraform validate` would reject.
|
|
132
|
+
*/
|
|
133
|
+
function dataSourceHcl(shape: DataSourceShape, name: string, values: Record<string, string>): string {
|
|
134
|
+
const body: string[] = [];
|
|
135
|
+
const args = resolveFields(shape.args ?? [], values);
|
|
136
|
+
if (args === null) return todoBlock(shape.type, name);
|
|
137
|
+
if (args.length) body.push(...alignedAssignments(args, " "));
|
|
138
|
+
for (const block of shape.blocks ?? []) {
|
|
139
|
+
const fields = resolveFields(block.fields, values);
|
|
140
|
+
if (fields === null) return todoBlock(shape.type, name);
|
|
141
|
+
if (!fields.length) continue;
|
|
142
|
+
if (body.length) body.push("");
|
|
143
|
+
body.push(` ${block.name} {`, ...alignedAssignments(fields, " "), " }");
|
|
144
|
+
}
|
|
145
|
+
if (!body.length) body.push(IDENTIFY_TODO);
|
|
146
|
+
return `data ${JSON.stringify(shape.type)} ${JSON.stringify(name)} {\n${body.join("\n")}\n}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function todoBlock(type: string, name: string): string {
|
|
150
|
+
return `data ${JSON.stringify(type)} ${JSON.stringify(name)} {\n${IDENTIFY_TODO}\n}`;
|
|
92
151
|
}
|
|
93
152
|
|
|
94
153
|
/**
|
|
@@ -96,26 +155,26 @@ function dataSourceHcl(type: string, name: string, identity: CarvedIdentity): st
|
|
|
96
155
|
*
|
|
97
156
|
* @param report the boundary report (from `boundaryReport`)
|
|
98
157
|
* @param files every `.tf` file in the estate, as { path, content }
|
|
99
|
-
* @param
|
|
158
|
+
* @param readBacks how each carved address is read back as a data source, and
|
|
159
|
+
* the literals to render it with (`dataSourceShapeOf` +
|
|
160
|
+
* the graph node's `dataSourceValues`)
|
|
100
161
|
*/
|
|
101
162
|
export function generateBridge(
|
|
102
163
|
report: CarveReport,
|
|
103
164
|
files: Array<{ path: string; content: string }>,
|
|
104
|
-
|
|
165
|
+
readBacks: Map<string, CarvedDataSource>,
|
|
105
166
|
): BridgePlan {
|
|
106
167
|
// Carved resources that something still depends on need a data source.
|
|
107
|
-
const carvedWithInbound = new Map<string, { type: string; name: string }>();
|
|
168
|
+
const carvedWithInbound = new Map<string, { type: string; name: string; readBack: CarvedDataSource }>();
|
|
108
169
|
for (const e of report.inbound) {
|
|
109
170
|
const [type, ...rest] = e.carved.split(".");
|
|
110
|
-
carvedWithInbound.set(e.carved, { type, name: rest.join(".") });
|
|
171
|
+
carvedWithInbound.set(e.carved, { type, name: rest.join("."), readBack: readBacks.get(e.carved) ?? {} });
|
|
111
172
|
}
|
|
112
173
|
|
|
113
|
-
const dataSources: DataSourceBlock[] = [...carvedWithInbound.entries()].map(([address, { type, name }]) =>
|
|
114
|
-
|
|
115
|
-
type,
|
|
116
|
-
|
|
117
|
-
hcl: dataSourceHcl(type, name, identities.get(address) ?? {}),
|
|
118
|
-
}));
|
|
174
|
+
const dataSources: DataSourceBlock[] = [...carvedWithInbound.entries()].map(([address, { type, name, readBack }]) => {
|
|
175
|
+
const shape = readBack.shape ?? { type };
|
|
176
|
+
return { address, type: shape.type, name, hcl: dataSourceHcl(shape, name, readBack.values ?? {}) };
|
|
177
|
+
});
|
|
119
178
|
|
|
120
179
|
// Excise the carve set's own blocks (#998): after `terraform state rm`, a
|
|
121
180
|
// block left behind would re-create the resource on the next apply. Then
|
|
@@ -129,8 +188,10 @@ export function generateBridge(
|
|
|
129
188
|
const rewrites: FileRewrite[] = files.map(({ path, content }) => {
|
|
130
189
|
const excision = exciseResourceBlocks(content, exciseTargets);
|
|
131
190
|
let rewritten = excision.content;
|
|
132
|
-
for (const address of carvedWithInbound
|
|
133
|
-
|
|
191
|
+
for (const [address, { type, name, readBack }] of carvedWithInbound) {
|
|
192
|
+
const shape = readBack.shape;
|
|
193
|
+
const head = `data.${shape?.type ?? type}.${name}`;
|
|
194
|
+
rewritten = rewritten.replace(referenceRegex(address), (_match, path: string) => head + rewriteReadPath(shape, path));
|
|
134
195
|
}
|
|
135
196
|
return { path, original: content, rewritten, changed: rewritten !== content, excised: excision.excised };
|
|
136
197
|
});
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* not require plugin loading for the paths that run without one.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
import { identityAttrShape, type DataSourceShape } from "./data-source-shape";
|
|
31
32
|
import { BUILTIN_CARVE_PROVIDERS } from "./providers";
|
|
32
33
|
import type { StateResource } from "./state";
|
|
33
34
|
|
|
@@ -95,10 +96,20 @@ export interface CarveProvider {
|
|
|
95
96
|
readonly tiers: Readonly<Record<string, TierInfo>>;
|
|
96
97
|
/**
|
|
97
98
|
* Terraform type → the HCL attribute carrying the physical name. A dotted
|
|
98
|
-
* entry is a path into nested
|
|
99
|
-
*
|
|
99
|
+
* entry is a path into nested values (`manifest.metadata.name`), which a
|
|
100
|
+
* flat `attr = value` body cannot express, so it implies no data-source
|
|
101
|
+
* shape on its own — see {@link dataSourceShapes}.
|
|
100
102
|
*/
|
|
101
103
|
readonly identityAttrs?: Readonly<Record<string, string>>;
|
|
104
|
+
/**
|
|
105
|
+
* Terraform type → how `carve bridge` reads it back as a `data` source
|
|
106
|
+
* (#2034): the data-source type, where each argument comes from in the
|
|
107
|
+
* carved body, and how a survivor's attribute path translates. Needed when
|
|
108
|
+
* the data source is not the resource's own type, or when its body is more
|
|
109
|
+
* than one flat argument. A type with a plain identity attribute and no
|
|
110
|
+
* entry here gets that attribute's shape ({@link identityAttrShape}).
|
|
111
|
+
*/
|
|
112
|
+
readonly dataSourceShapes?: Readonly<Record<string, DataSourceShape>>;
|
|
102
113
|
/**
|
|
103
114
|
* Sub-resource Terraform type → parent type, for types Terraform splits out
|
|
104
115
|
* of the resource the native spec keeps them in.
|
|
@@ -169,6 +180,7 @@ interface RegistryIndex {
|
|
|
169
180
|
prefixes: Array<{ prefix: string; provider: CarveProvider }>;
|
|
170
181
|
tiers: Record<string, TierInfo>;
|
|
171
182
|
identityAttrs: Record<string, string>;
|
|
183
|
+
dataSourceShapes: Record<string, DataSourceShape>;
|
|
172
184
|
foldsInto: Record<string, string>;
|
|
173
185
|
/** Terraform type → the provider that emits it. */
|
|
174
186
|
emitters: Map<string, CarveProvider>;
|
|
@@ -182,6 +194,7 @@ function index(): RegistryIndex {
|
|
|
182
194
|
prefixes: [],
|
|
183
195
|
tiers: {},
|
|
184
196
|
identityAttrs: {},
|
|
197
|
+
dataSourceShapes: {},
|
|
185
198
|
foldsInto: {},
|
|
186
199
|
emitters: new Map(),
|
|
187
200
|
};
|
|
@@ -189,6 +202,7 @@ function index(): RegistryIndex {
|
|
|
189
202
|
for (const prefix of provider.tfTypePrefixes) built.prefixes.push({ prefix, provider });
|
|
190
203
|
Object.assign(built.tiers, provider.tiers);
|
|
191
204
|
if (provider.identityAttrs) Object.assign(built.identityAttrs, provider.identityAttrs);
|
|
205
|
+
if (provider.dataSourceShapes) Object.assign(built.dataSourceShapes, provider.dataSourceShapes);
|
|
192
206
|
if (provider.foldsInto) Object.assign(built.foldsInto, provider.foldsInto);
|
|
193
207
|
for (const tfType of provider.emitTypes ?? []) built.emitters.set(tfType, provider);
|
|
194
208
|
}
|
|
@@ -235,3 +249,25 @@ export function carveIdentityAttr(tfType: string): string | undefined {
|
|
|
235
249
|
export function carveFoldParent(tfType: string): string | undefined {
|
|
236
250
|
return index().foldsInto[tfType];
|
|
237
251
|
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* How `carve bridge` reads this type back as a `data` source (#2034), or
|
|
255
|
+
* undefined when it cannot be read back at all — which is what makes
|
|
256
|
+
* `canBridge` refuse it.
|
|
257
|
+
*
|
|
258
|
+
* Resolution order:
|
|
259
|
+
* 1. a shape the owning provider declared for the type;
|
|
260
|
+
* 2. otherwise the shape a plain identity attribute implies (same type, one
|
|
261
|
+
* required argument) — the path every AWS and google type takes;
|
|
262
|
+
* 3. otherwise undefined, when the identity attribute is a dotted path into
|
|
263
|
+
* nested values and no provider declared a shape for it;
|
|
264
|
+
* 4. and for a type with no identity attribute at all, the bare data-source
|
|
265
|
+
* type: the bridge still emits a block, with a TODO for the body.
|
|
266
|
+
*/
|
|
267
|
+
export function carveDataSourceShape(tfType: string): DataSourceShape | undefined {
|
|
268
|
+
const declared = index().dataSourceShapes[tfType];
|
|
269
|
+
if (declared) return declared;
|
|
270
|
+
const attr = index().identityAttrs[tfType];
|
|
271
|
+
if (attr === undefined) return { type: tfType };
|
|
272
|
+
return identityAttrShape(tfType, attr);
|
|
273
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a carved Terraform type is read back as a `data` source (#2034).
|
|
3
|
+
*
|
|
4
|
+
* `carve bridge` replaces a carved resource with a data source the survivors
|
|
5
|
+
* read instead. Until now a carve provider said only which attribute carries
|
|
6
|
+
* the physical name (`identityAttrs`), and the bridge assumed the data source
|
|
7
|
+
* was the same type as the resource, with that one attribute in its body:
|
|
8
|
+
*
|
|
9
|
+
* data "aws_s3_bucket" "assets" { bucket = "myapp-assets-prod" }
|
|
10
|
+
*
|
|
11
|
+
* That assumption holds for AWS and google, and breaks for Kubernetes twice
|
|
12
|
+
* over. `kubernetes_manifest` names itself by `manifest.metadata.name`, a path
|
|
13
|
+
* into a nested value that a flat `attr = value` body cannot express; and the
|
|
14
|
+
* kubernetes provider ships no `kubernetes_manifest` data source at all
|
|
15
|
+
* (verified against hashicorp/kubernetes v3.2.1: 27 data sources, none a
|
|
16
|
+
* manifest). Its generic read is `data "kubernetes_resource"`, with a
|
|
17
|
+
* different type, different arguments, a nested `metadata` block, and its own
|
|
18
|
+
* attribute path for survivors to read through.
|
|
19
|
+
*
|
|
20
|
+
* So a provider contributes a *shape*: the data-source type, where each
|
|
21
|
+
* argument comes from in the carved body, and how a survivor's attribute path
|
|
22
|
+
* translates. An identity attribute is one instance of that shape
|
|
23
|
+
* ({@link identityAttrShape}), so nothing that bridges today changes.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** One argument of the data-source body, and where its value comes from. */
|
|
27
|
+
export interface ShapeField {
|
|
28
|
+
/** Argument name in the emitted data source, e.g. `api_version`. */
|
|
29
|
+
name: string;
|
|
30
|
+
/**
|
|
31
|
+
* Dotted path into the carved resource's HCL body the literal is read from,
|
|
32
|
+
* e.g. `manifest.apiVersion`. Nested blocks are walked by segment.
|
|
33
|
+
*/
|
|
34
|
+
from: string;
|
|
35
|
+
/**
|
|
36
|
+
* A shape that cannot resolve a required field renders the TODO body rather
|
|
37
|
+
* than a data source missing an argument Terraform needs.
|
|
38
|
+
*/
|
|
39
|
+
required?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A nested block in the data-source body, e.g. `metadata { name = "x" }`. */
|
|
43
|
+
export interface ShapeBlock {
|
|
44
|
+
name: string;
|
|
45
|
+
fields: readonly ShapeField[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface DataSourceShape {
|
|
49
|
+
/** The `data` type standing in for the carved resource, e.g. `kubernetes_resource`. */
|
|
50
|
+
type: string;
|
|
51
|
+
/** Flat `name = value` arguments of the body. */
|
|
52
|
+
args?: readonly ShapeField[];
|
|
53
|
+
/** Nested `name { ... }` blocks of the body. */
|
|
54
|
+
blocks?: readonly ShapeBlock[];
|
|
55
|
+
/**
|
|
56
|
+
* The carved resource's top-level attribute → the data source's, for the
|
|
57
|
+
* survivor rewrite. `kubernetes_manifest` exposes both `manifest` (what the
|
|
58
|
+
* config declared) and `object` (the API server's read-back), and
|
|
59
|
+
* `kubernetes_resource` exposes only `object`, so both map to `object`. An
|
|
60
|
+
* attribute with no entry passes through unchanged, which is what every
|
|
61
|
+
* same-type identity-attribute shape wants.
|
|
62
|
+
*/
|
|
63
|
+
readAttrs?: Readonly<Record<string, string>>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The shape a plain identity attribute implies: the same type as the resource,
|
|
68
|
+
* one required argument, no path translation. Returns undefined for a dotted
|
|
69
|
+
* attribute, which is a path into nested values and needs a declared shape.
|
|
70
|
+
*/
|
|
71
|
+
export function identityAttrShape(tfType: string, attr: string): DataSourceShape | undefined {
|
|
72
|
+
if (attr.includes(".")) return undefined;
|
|
73
|
+
return { type: tfType, args: [{ name: attr, from: attr, required: true }] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Every source path the shape reads, for the graph to resolve out of the carved block. */
|
|
77
|
+
export function shapeSourcePaths(shape: DataSourceShape): string[] {
|
|
78
|
+
const paths = (shape.args ?? []).map((f) => f.from);
|
|
79
|
+
for (const block of shape.blocks ?? []) for (const f of block.fields) paths.push(f.from);
|
|
80
|
+
return [...new Set(paths)];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Translate a survivor's attribute path onto the data source. `path` is the
|
|
85
|
+
* text after the address, leading dot included (`.manifest.data.log_level`) or
|
|
86
|
+
* empty. Only the first segment is mapped: everything under it is the object's
|
|
87
|
+
* own structure, which the data source reproduces.
|
|
88
|
+
*/
|
|
89
|
+
export function rewriteReadPath(shape: DataSourceShape | undefined, path: string): string {
|
|
90
|
+
if (!path || !shape?.readAttrs) return path;
|
|
91
|
+
const [head, ...rest] = path.slice(1).split(".");
|
|
92
|
+
const mapped = shape.readAttrs[head];
|
|
93
|
+
if (mapped === undefined) return path;
|
|
94
|
+
return `.${[mapped, ...rest].join(".")}`;
|
|
95
|
+
}
|