@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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TF023 (#2110): Terraform state, or the `.terraform/` working directory,
|
|
3
|
+
* committed to the repository.
|
|
4
|
+
*
|
|
5
|
+
* Every other rule in the TF family is a post-synth check over a parsed root
|
|
6
|
+
* module, contributed by the terraform lexicon. This one is not about HCL at
|
|
7
|
+
* all: the finding is that a path exists in version control, so there is
|
|
8
|
+
* nothing for `chant build` to check and nothing for the lexicon's parser to
|
|
9
|
+
* read. It therefore takes the shape `secrets.ts` established and `wrangler.ts`
|
|
10
|
+
* and `nginx.ts` copied, a path detector plus a check over the discovered file
|
|
11
|
+
* list, called from the audit CLI beside `scanForSecrets`, with its catalog
|
|
12
|
+
* entry in core's own `RULE_CATALOG` (./catalog.ts) rather than a lexicon's.
|
|
13
|
+
* It runs whether or not the terraform lexicon is installed.
|
|
14
|
+
*
|
|
15
|
+
* A state file is a committed secret store: it records every attribute of every
|
|
16
|
+
* managed resource, including the ones providers mark sensitive, in plaintext
|
|
17
|
+
* JSON. `.terraform/` is milder (providers and vendored modules) but is still
|
|
18
|
+
* hundreds of megabytes of machine-generated content, and it can hold a
|
|
19
|
+
* `terraform.tfstate` of its own for the workspace-local backend.
|
|
20
|
+
*
|
|
21
|
+
* Pure: no fs, no network, no Node-only global, so it is Workers-safe (epic #350). The
|
|
22
|
+
* walk that decides which paths reach here is `discover.ts`'s
|
|
23
|
+
* `collectCandidates`, which is also where a locally ignored path is dropped:
|
|
24
|
+
* see `isTerraformStatePath` there and `gitignoreCoversTerraformState` below.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { AuditFinding } from "./core";
|
|
28
|
+
|
|
29
|
+
/** The minimal file shape the check needs (matches `discover.ts`'s `RepoFile`). */
|
|
30
|
+
export interface ScannableFile {
|
|
31
|
+
path: string;
|
|
32
|
+
content: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** `terraform.tfstate`, `terraform.tfstate.backup`, and any other `*.tfstate`. */
|
|
36
|
+
export function isTerraformStateFile(path: string): boolean {
|
|
37
|
+
const name = path.split("/").pop() ?? path;
|
|
38
|
+
return /\.tfstate(\.backup)?$/i.test(name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The `.terraform` working directory itself, or anything inside it. */
|
|
42
|
+
export function isTerraformWorkDir(path: string): boolean {
|
|
43
|
+
const parts = path.split("/");
|
|
44
|
+
return parts.includes(".terraform");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Either of the two shapes TF023 reports. */
|
|
48
|
+
export function isTerraformStatePath(path: string): boolean {
|
|
49
|
+
return isTerraformStateFile(path) || isTerraformWorkDir(path);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Does a `.gitignore` body already exclude Terraform state? Deliberately
|
|
54
|
+
* narrow: it recognises the handful of patterns the style guide's own
|
|
55
|
+
* `.gitignore` section and `github/gitignore`'s `Terraform.gitignore` use
|
|
56
|
+
* (`*.tfstate`, `*.tfstate.*`, `.terraform/`, `.terraform*`, with an optional
|
|
57
|
+
* leading slash or a leading double-star), not the whole gitignore grammar. A repository that
|
|
58
|
+
* writes something more exotic gets a finding it can suppress; the alternative,
|
|
59
|
+
* a partial gitignore engine, would be wrong in quieter ways.
|
|
60
|
+
*
|
|
61
|
+
* Used by the local walk only (`discover.ts`), where "on disk" and "tracked by
|
|
62
|
+
* git" are different questions and nearly every Terraform working tree has an
|
|
63
|
+
* ignored `.terraform/` in it. A fetched repository's file list is already
|
|
64
|
+
* exactly the tracked files, so nothing filters it.
|
|
65
|
+
*/
|
|
66
|
+
function escapeRegExp(literal: string): string {
|
|
67
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function gitignoreCoversTerraformState(gitignore: string, path: string): boolean {
|
|
71
|
+
const name = path.split("/").pop() ?? path;
|
|
72
|
+
for (const raw of gitignore.split("\n")) {
|
|
73
|
+
const line = raw.trim();
|
|
74
|
+
if (line === "" || line.startsWith("#") || line.startsWith("!")) continue;
|
|
75
|
+
const pattern = line.replace(/^\*\*\//, "").replace(/^\//, "").replace(/\/$/, "");
|
|
76
|
+
if (pattern === "" || pattern.includes("/")) continue;
|
|
77
|
+
// `*` matches within one path segment, as git's own glob does. The pattern
|
|
78
|
+
// is compared against the file's own name and against every segment of its
|
|
79
|
+
// path, which is what makes `.terraform/` match `infra/prod/.terraform`.
|
|
80
|
+
const re = new RegExp(`^${pattern.split("*").map(escapeRegExp).join("[^/]*")}$`);
|
|
81
|
+
if (re.test(name) || path.split("/").some((segment) => re.test(segment))) return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Report TF023 for every committed state file and `.terraform/` directory in
|
|
88
|
+
* the file list. One finding per path, and one for the `.terraform` directory
|
|
89
|
+
* as a whole rather than per file inside it: a vendored provider binary is not
|
|
90
|
+
* a separate finding from the directory that holds it.
|
|
91
|
+
*/
|
|
92
|
+
export function auditTerraformState(files: ScannableFile[]): AuditFinding[] {
|
|
93
|
+
const findings: AuditFinding[] = [];
|
|
94
|
+
const workDirs = new Set<string>();
|
|
95
|
+
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
if (isTerraformWorkDir(file.path)) {
|
|
98
|
+
const parts = file.path.split("/");
|
|
99
|
+
const dir = parts.slice(0, parts.indexOf(".terraform") + 1).join("/");
|
|
100
|
+
if (workDirs.has(dir)) continue;
|
|
101
|
+
workDirs.add(dir);
|
|
102
|
+
findings.push({
|
|
103
|
+
checkId: "TF023",
|
|
104
|
+
severity: "warning",
|
|
105
|
+
message:
|
|
106
|
+
`\`${dir}\` is committed to the repository. Terraform's working directory holds downloaded ` +
|
|
107
|
+
"providers and vendored child modules, and the local backend keeps its state there too. " +
|
|
108
|
+
"Delete it from version control and add `.terraform/` to .gitignore.",
|
|
109
|
+
file: dir,
|
|
110
|
+
lexicon: "terraform",
|
|
111
|
+
entity: ".terraform",
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!isTerraformStateFile(file.path)) continue;
|
|
116
|
+
findings.push({
|
|
117
|
+
checkId: "TF023",
|
|
118
|
+
severity: "error",
|
|
119
|
+
message:
|
|
120
|
+
`\`${file.path}\` is committed to the repository. A state file records every attribute of every ` +
|
|
121
|
+
"managed resource in plaintext, including the ones providers mark sensitive, so a committed " +
|
|
122
|
+
"state file is a committed secret store. Remove it from version control, add `*.tfstate` and " +
|
|
123
|
+
"`*.tfstate.backup` to .gitignore, move the state to a remote backend, and rotate anything it held.",
|
|
124
|
+
file: file.path,
|
|
125
|
+
lexicon: "terraform",
|
|
126
|
+
entity: file.path.split("/").pop() ?? file.path,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return findings;
|
|
131
|
+
}
|
package/src/build.ts
CHANGED
|
@@ -3,12 +3,14 @@ import type { Serializer, SerializerResult } from "./serializer";
|
|
|
3
3
|
import type { OwnershipMarker } from "./ownership";
|
|
4
4
|
import type { BuildError, DiscoveryErrorType } from "./errors";
|
|
5
5
|
import type { IntrinsicDef, BuildRootContribution, BuildRootContributor } from "./lexicon";
|
|
6
|
-
import type
|
|
6
|
+
import { getProvenance, type BuildParamProvenance } from "./provenance";
|
|
7
|
+
import { foldProvenanceOfEntities, type FoldProvenance } from "./fold-provenance";
|
|
7
8
|
import { DiscoveryError, BuildError as BuildErrorClass } from "./errors";
|
|
8
9
|
import { LexiconOutput, isLexiconOutput } from "./lexicon-output";
|
|
9
10
|
import { isSecretDeclaration } from "./secret-provenance";
|
|
10
11
|
import { splitReceiptEntities } from "./effect-receipt";
|
|
11
12
|
import { isScenario } from "./lifecycle/scenario";
|
|
13
|
+
import { isOpEntity } from "./op/resource";
|
|
12
14
|
import { AttrRef } from "./attrref";
|
|
13
15
|
import { isAttrRefLike } from "./utils";
|
|
14
16
|
import { isChildProject, type ChildProjectInstance } from "./child-project";
|
|
@@ -282,6 +284,22 @@ export interface BuildResult {
|
|
|
282
284
|
* supplies none.
|
|
283
285
|
*/
|
|
284
286
|
buildParams: BuildParamProvenance[];
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* chant #2161 — emitted property path to origin, per entity: which composite
|
|
290
|
+
* parameter produced the field, which literal the composite fixes, which the
|
|
291
|
+
* author declared directly, and which this build could not attribute.
|
|
292
|
+
*
|
|
293
|
+
* Beside the output on purpose, and never inside it. Nothing here is passed
|
|
294
|
+
* to a serializer, so it cannot reach the document an applier writes; see
|
|
295
|
+
* ./fold-provenance.ts's module doc and `fold-provenance-exclusion.test.ts`.
|
|
296
|
+
*
|
|
297
|
+
* Every entity with properties appears. The origins are as fine as the build
|
|
298
|
+
* managed: a run build, or a fold build over a factory outside the
|
|
299
|
+
* interpretable subset, records `unknown` for a composite's fields rather
|
|
300
|
+
* than guessing.
|
|
301
|
+
*/
|
|
302
|
+
foldProvenance: FoldProvenance;
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
/**
|
|
@@ -308,6 +326,13 @@ export function partitionByLexicon(
|
|
|
308
326
|
// Plan scenarios (#1292) are serializer-neutral the same way: a checkable
|
|
309
327
|
// expectation the CLI reads off the entity map, never output.
|
|
310
328
|
if (isScenario(entity)) continue;
|
|
329
|
+
// Op declarations (#2118) too. An Op's build output is its `op.json` IR,
|
|
330
|
+
// and core writes that itself from the entity map (`./cli/commands/build.ts`)
|
|
331
|
+
// rather than routing it through a lexicon serializer — the Op model, the
|
|
332
|
+
// executor and the IR are all core's, and no lexicon renders an Op entity
|
|
333
|
+
// into its own manifest. Keeping them out of every partition means no
|
|
334
|
+
// "No serializer found" warning fires for core's own `chant` lexicon.
|
|
335
|
+
if (isOpEntity(entity)) continue;
|
|
311
336
|
const lexicon = entity.lexicon;
|
|
312
337
|
if (!partitions.has(lexicon)) {
|
|
313
338
|
partitions.set(lexicon, new Map());
|
|
@@ -852,6 +877,11 @@ async function buildFromDiscoveryResult(
|
|
|
852
877
|
foldDecisions: discoveryResult.foldDecisions,
|
|
853
878
|
lexiconVersions: { ...(options?.lexiconVersions ?? {}) },
|
|
854
879
|
buildParams: options?.buildParams ?? [],
|
|
880
|
+
// chant #2161 — read off the entities AFTER every step that can change
|
|
881
|
+
// them (the shared-props merge in `expandComposite`, the build-root merge
|
|
882
|
+
// above), and deliberately after Step 7, so nothing about computing it can
|
|
883
|
+
// reach a serializer.
|
|
884
|
+
foldProvenance: foldProvenanceOfEntities(discoveryResult.entities, getProvenance),
|
|
855
885
|
};
|
|
856
886
|
}
|
|
857
887
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
NO_CLAIMED_FIELDS,
|
|
4
|
+
claimedFieldPaths,
|
|
5
|
+
claimedFieldsFromPaths,
|
|
6
|
+
claimedFieldsOfProps,
|
|
7
|
+
claimedFieldsOfTree,
|
|
8
|
+
classifyLiveField,
|
|
9
|
+
heldBy,
|
|
10
|
+
isClaimed,
|
|
11
|
+
} from "./claimed-fields";
|
|
12
|
+
import { UNRESOLVED, type DeepNormalizationHooks } from "./deep-observation";
|
|
13
|
+
|
|
14
|
+
describe("claimedFieldsOfProps", () => {
|
|
15
|
+
test("walks props to leaf paths in the diff's grammar", () => {
|
|
16
|
+
const claimed = claimedFieldsOfProps(
|
|
17
|
+
{ spec: { replicas: 2, template: { spec: { containers: [{ name: "app", image: "a:1" }] } } } },
|
|
18
|
+
{ entityType: "K8s::Apps::Deployment" },
|
|
19
|
+
);
|
|
20
|
+
expect(claimedFieldPaths(claimed)).toEqual([
|
|
21
|
+
"spec.replicas",
|
|
22
|
+
"spec.template.spec.containers[0].image",
|
|
23
|
+
"spec.template.spec.containers[0].name",
|
|
24
|
+
]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("addresses set-like arrays by the lexicon's own order key", () => {
|
|
28
|
+
const hooks: DeepNormalizationHooks = {
|
|
29
|
+
orderKey: (el) => (el.pattern === "Tags" ? String((el.element as { Key: string }).Key) : undefined),
|
|
30
|
+
};
|
|
31
|
+
const claimed = claimedFieldsOfProps(
|
|
32
|
+
{ Tags: [{ Key: "env", Value: "prod" }] },
|
|
33
|
+
{ entityType: "AWS::S3::Bucket", hooks },
|
|
34
|
+
);
|
|
35
|
+
expect(claimedFieldPaths(claimed)).toEqual(["Tags[#env].Key", "Tags[#env].Value"]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("a pruned property is not claimed", () => {
|
|
39
|
+
// The hooks decide what is even in the tree, so the claim inherits them
|
|
40
|
+
// rather than second-guessing them: a field the lexicon subtracts on both
|
|
41
|
+
// sides is not a field chant is asserting anything about.
|
|
42
|
+
const hooks: DeepNormalizationHooks = { prune: (node) => node.pattern.startsWith("status") };
|
|
43
|
+
const claimed = claimedFieldsOfProps(
|
|
44
|
+
{ spec: { replicas: 2 }, status: { readyReplicas: 2 } },
|
|
45
|
+
{ entityType: "T", hooks },
|
|
46
|
+
);
|
|
47
|
+
expect(claimedFieldPaths(claimed)).toEqual(["spec.replicas"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("an unevaluated intrinsic is still claimed", () => {
|
|
51
|
+
// chant set the field; it just cannot say what the reference resolves to.
|
|
52
|
+
const claimed = claimedFieldsOfProps({ BucketName: UNRESOLVED }, { entityType: "T" });
|
|
53
|
+
expect(isClaimed(claimed, "BucketName")).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("an empty declaration claims nothing", () => {
|
|
57
|
+
expect(claimedFieldPaths(claimedFieldsOfProps({}, { entityType: "T" }))).toEqual([]);
|
|
58
|
+
expect(claimedFieldPaths(NO_CLAIMED_FIELDS)).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("agrees with the tree form on an already-normalized tree", () => {
|
|
62
|
+
const props = { spec: { replicas: 2 } };
|
|
63
|
+
expect(claimedFieldPaths(claimedFieldsOfProps(props, { entityType: "T" }))).toEqual(
|
|
64
|
+
claimedFieldPaths(claimedFieldsOfTree(props, { entityType: "T" })),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("isClaimed", () => {
|
|
70
|
+
const claimed = claimedFieldsFromPaths(["Tags[0].Value", "spec.replicas"]);
|
|
71
|
+
|
|
72
|
+
test("matches exactly", () => {
|
|
73
|
+
expect(isClaimed(claimed, "spec.replicas")).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("does not match by index-erased pattern", () => {
|
|
77
|
+
// A pattern match would answer "declared" for a path with no declared value
|
|
78
|
+
// to compare against, which is neither of the two declared classifications.
|
|
79
|
+
expect(isClaimed(claimed, "Tags[3].Value")).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("does not match an ancestor's claim", () => {
|
|
83
|
+
// Declaring `spec.replicas` says nothing about `spec.strategy.type`.
|
|
84
|
+
expect(isClaimed(claimed, "spec.strategy.type")).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("an absent claim claims nothing", () => {
|
|
88
|
+
expect(isClaimed(undefined, "spec.replicas")).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("classifyLiveField", () => {
|
|
93
|
+
const claimed = claimedFieldsFromPaths(["spec.replicas"]);
|
|
94
|
+
|
|
95
|
+
test("declared and equal", () => {
|
|
96
|
+
expect(
|
|
97
|
+
classifyLiveField({ claimed, path: "spec.replicas", declaredValue: 2, liveValue: 2 }),
|
|
98
|
+
).toBe("declared-equal");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("declared and different", () => {
|
|
102
|
+
expect(
|
|
103
|
+
classifyLiveField({ claimed, path: "spec.replicas", declaredValue: 2, liveValue: 5 }),
|
|
104
|
+
).toBe("declared-changed");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("undeclared", () => {
|
|
108
|
+
expect(
|
|
109
|
+
classifyLiveField({ claimed, path: "metadata.labels.team", declaredValue: undefined, liveValue: "platform" }),
|
|
110
|
+
).toBe("undeclared");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("structural equality, not identity", () => {
|
|
114
|
+
const shaped = claimedFieldsFromPaths(["spec.selector"]);
|
|
115
|
+
expect(
|
|
116
|
+
classifyLiveField({
|
|
117
|
+
claimed: shaped,
|
|
118
|
+
path: "spec.selector",
|
|
119
|
+
declaredValue: { app: "web" },
|
|
120
|
+
liveValue: { app: "web" },
|
|
121
|
+
}),
|
|
122
|
+
).toBe("declared-equal");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("heldBy", () => {
|
|
127
|
+
test("the substrate's manager answers where it has one", () => {
|
|
128
|
+
expect(heldBy({ "spec.replicas": "hpa-controller" }, "spec.replicas")).toEqual({
|
|
129
|
+
holder: "hpa-controller",
|
|
130
|
+
source: "field-manager",
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("the claim answers where the substrate records nothing", () => {
|
|
135
|
+
expect(heldBy(undefined, "spec.replicas")).toEqual({ source: "claimed-fields" });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("the claim answers for a path the ownership metadata does not cover", () => {
|
|
139
|
+
// Kubernetes' own case: a keyed list segment has no `managedFields` entry
|
|
140
|
+
// under that path, so the declaration is the only witness left.
|
|
141
|
+
expect(heldBy({ "spec.replicas": "hpa-controller" }, "spec.template.spec.containers[#sidecar].image")).toEqual({
|
|
142
|
+
source: "claimed-fields",
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The claimed-field set (#2160) — which properties this declaration ever set.
|
|
3
|
+
*
|
|
4
|
+
* chant's ownership question has always been answered per resource, from a
|
|
5
|
+
* marker: is this thing mine. The finer question was never asked. A live
|
|
6
|
+
* property tree carries fields nobody declared — an autoscaler's replica count,
|
|
7
|
+
* a controller's annotation, a value a person typed into a console — and the
|
|
8
|
+
* deep diff (./lifecycle/deep-diff.ts) reported every one of them as a
|
|
9
|
+
* difference, because a difference is all a two-way comparison can produce.
|
|
10
|
+
*
|
|
11
|
+
* Every lexicon has been paying for that in hand-written knowledge.
|
|
12
|
+
* `DeepNormalizationHooks.prune` is where the noise goes, and three of the four
|
|
13
|
+
* hooks exist because the live tree holds fields the declaration never
|
|
14
|
+
* mentions. Kubernetes is the one substrate that escapes it: the API server
|
|
15
|
+
* records a field manager per field (`metadata.managedFields`), so a foreign
|
|
16
|
+
* write is a fact about the object rather than a rule somebody maintains.
|
|
17
|
+
* `lexicons/gcp/src/deep-observe.ts` says why nobody else can do that — "a REST
|
|
18
|
+
* payload carries no field ownership" — and answers with a static per-kind
|
|
19
|
+
* table instead.
|
|
20
|
+
*
|
|
21
|
+
* The declaration is the table they are missing. A `ResourceDeclarable`'s
|
|
22
|
+
* `props` are exactly the fields chant set, they are available on every
|
|
23
|
+
* substrate, and they come from the same build that produced the entity.
|
|
24
|
+
* Nothing has to come back from the provider.
|
|
25
|
+
*
|
|
26
|
+
* ## The grammar is the diff's grammar
|
|
27
|
+
*
|
|
28
|
+
* A claim is a set of property paths in the form `flattenDeepProperties`
|
|
29
|
+
* produces: `spec.replicas`, `Tags[#env].Value`, `ingress[0].fromPort`. Same
|
|
30
|
+
* hooks, same normalization, same flattening — so a path in the claim and a
|
|
31
|
+
* path in the diff are the same string or they are different fields. Deriving
|
|
32
|
+
* the claim any other way (walking raw props, say) would give a set that agrees
|
|
33
|
+
* with the diff for most trees and disagrees for exactly the ones the hooks
|
|
34
|
+
* were written for.
|
|
35
|
+
*
|
|
36
|
+
* ## Membership is exact, deliberately
|
|
37
|
+
*
|
|
38
|
+
* A claimed path is matched exactly, never by its index-erased pattern
|
|
39
|
+
* (`Tags[].Value`). The pattern is the right test for
|
|
40
|
+
* {@link import("./deep-observation").DeepNode.counterpart}, which asks a
|
|
41
|
+
* shape question ("did source declare tag values at all") before array order is
|
|
42
|
+
* canonical. Classification asks a value question, and it pairs a declared
|
|
43
|
+
* value with a live one at the same path. A pattern match would answer
|
|
44
|
+
* "declared" for a path that has no declared value to compare against, which is
|
|
45
|
+
* neither of the two declared classifications and would quietly become a third
|
|
46
|
+
* meaning of "drift".
|
|
47
|
+
*
|
|
48
|
+
* ## What the claim does not decide
|
|
49
|
+
*
|
|
50
|
+
* It does not prune. A field chant never declared still reaches the diff, still
|
|
51
|
+
* appears in the report, and still carries its live value — it is reported as
|
|
52
|
+
* unclaimed rather than dropped, because chant #1191 already lost a
|
|
53
|
+
* console-added label by pruning what it could not attribute. What the claim
|
|
54
|
+
* changes is the verdict: unclaimed is not drift, and drift is the only thing
|
|
55
|
+
* that may become an update.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import {
|
|
59
|
+
deepValueEqual,
|
|
60
|
+
flattenDeepProperties,
|
|
61
|
+
normalizeDeepProperties,
|
|
62
|
+
type DeepNormalizationHooks,
|
|
63
|
+
} from "./deep-observation";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The set of property paths one declaration claims — every path its `props`
|
|
67
|
+
* flatten to, in the diff's own grammar.
|
|
68
|
+
*
|
|
69
|
+
* Opaque on purpose: {@link isClaimed} is the only membership test, so a
|
|
70
|
+
* consumer cannot accidentally invent a looser one (see the module doc on
|
|
71
|
+
* pattern matching).
|
|
72
|
+
*/
|
|
73
|
+
export interface ClaimedFieldSet {
|
|
74
|
+
/** Flattened property paths, exactly as the diff addresses them. */
|
|
75
|
+
readonly paths: ReadonlySet<string>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* How one LIVE value relates to the claim. Three answers where a two-way diff
|
|
80
|
+
* had two:
|
|
81
|
+
*
|
|
82
|
+
* - `declared-equal` — chant set this field and the cloud agrees. Nothing to say.
|
|
83
|
+
* - `declared-changed` — chant set this field and the cloud holds something
|
|
84
|
+
* else. This is drift, and it is the only classification that may become an
|
|
85
|
+
* update.
|
|
86
|
+
* - `undeclared` — the field has a value and chant never set it. Somebody else
|
|
87
|
+
* holds it. Reported, never proposed, never counted as drift.
|
|
88
|
+
*
|
|
89
|
+
* A declared path with no live value at all is not in this enumeration: there
|
|
90
|
+
* is no live value to classify. The diff keeps reporting that case as `absent`.
|
|
91
|
+
*/
|
|
92
|
+
export type FieldClaim = "declared-equal" | "declared-changed" | "undeclared";
|
|
93
|
+
|
|
94
|
+
/** Which source answered "who holds this field". */
|
|
95
|
+
export type FieldClaimSource =
|
|
96
|
+
/**
|
|
97
|
+
* The substrate named the manager. Kubernetes' `metadata.managedFields`, and
|
|
98
|
+
* nowhere else today — strictly better than the claim, because it says who.
|
|
99
|
+
*/
|
|
100
|
+
| "field-manager"
|
|
101
|
+
/**
|
|
102
|
+
* The declaration answered: the path is not in the claimed-field set, so
|
|
103
|
+
* chant never set it. Says that it is somebody else's without saying whose,
|
|
104
|
+
* which is the best any substrate without ownership metadata can do.
|
|
105
|
+
*/
|
|
106
|
+
| "claimed-fields";
|
|
107
|
+
|
|
108
|
+
/** An empty claim — a declaration with no props, and the safe default. */
|
|
109
|
+
export const NO_CLAIMED_FIELDS: ClaimedFieldSet = { paths: new Set<string>() };
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Build a claim from an already-normalized declared property tree.
|
|
113
|
+
*
|
|
114
|
+
* `options` must be the same {@link FlattenDeepOptions} the diff flattens the
|
|
115
|
+
* declared side with, or the two disagree about keyed list segments.
|
|
116
|
+
*/
|
|
117
|
+
export function claimedFieldsOfTree(
|
|
118
|
+
declared: Record<string, unknown>,
|
|
119
|
+
options: { entityType: string; hooks?: DeepNormalizationHooks },
|
|
120
|
+
): ClaimedFieldSet {
|
|
121
|
+
const flat = flattenDeepProperties(declared, {
|
|
122
|
+
entityType: options.entityType,
|
|
123
|
+
side: "declared",
|
|
124
|
+
...(options.hooks ? { hooks: options.hooks } : {}),
|
|
125
|
+
});
|
|
126
|
+
return { paths: new Set(flat.keys()) };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Build a claim straight from a declaration's raw `props` — the entry point for
|
|
131
|
+
* a consumer that has an entity and no diff.
|
|
132
|
+
*
|
|
133
|
+
* Normalizes with the lexicon's own hooks first, exactly as the diff does, so
|
|
134
|
+
* pruned and masked paths land the same way on both routes. `livePaths` is the
|
|
135
|
+
* counterpart set (`deepPathSet` over the live tree) where the caller has one:
|
|
136
|
+
* without it the pass runs one-sided and any hook gated on
|
|
137
|
+
* {@link import("./deep-observation").DeepNode.counterpart} sees `"unknown"`,
|
|
138
|
+
* which is the honest answer and prunes nothing default-related.
|
|
139
|
+
*/
|
|
140
|
+
export function claimedFieldsOfProps(
|
|
141
|
+
props: Record<string, unknown>,
|
|
142
|
+
options: {
|
|
143
|
+
entityType: string;
|
|
144
|
+
hooks?: DeepNormalizationHooks;
|
|
145
|
+
livePaths?: ReadonlySet<string>;
|
|
146
|
+
},
|
|
147
|
+
): ClaimedFieldSet {
|
|
148
|
+
const normalized = normalizeDeepProperties(props, {
|
|
149
|
+
entityType: options.entityType,
|
|
150
|
+
side: "declared",
|
|
151
|
+
...(options.hooks ? { hooks: options.hooks } : {}),
|
|
152
|
+
...(options.livePaths ? { counterpartPaths: options.livePaths } : {}),
|
|
153
|
+
});
|
|
154
|
+
return claimedFieldsOfTree(normalized, options);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** True when this declaration set the property at `path`. */
|
|
158
|
+
export function isClaimed(claimed: ClaimedFieldSet | undefined, path: string): boolean {
|
|
159
|
+
return claimed !== undefined && claimed.paths.has(path);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The claim as a sorted array — the wire form, for
|
|
164
|
+
* {@link import("./deep-observation").DeepResourceObservation.claimedFields}
|
|
165
|
+
* and for anything serializing a result to JSON.
|
|
166
|
+
*/
|
|
167
|
+
export function claimedFieldPaths(claimed: ClaimedFieldSet): string[] {
|
|
168
|
+
return [...claimed.paths].sort();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Rebuild a claim from its wire form. */
|
|
172
|
+
export function claimedFieldsFromPaths(paths: Iterable<string>): ClaimedFieldSet {
|
|
173
|
+
return { paths: new Set(paths) };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Classify one live value against the claim.
|
|
178
|
+
*
|
|
179
|
+
* `declaredValue` is only read when the path is claimed; pass whatever the
|
|
180
|
+
* declared tree holds there (including `undefined`, which is a legal declared
|
|
181
|
+
* value on a claimed path only in the sense that the flattener never emits it).
|
|
182
|
+
*/
|
|
183
|
+
export function classifyLiveField(input: {
|
|
184
|
+
claimed: ClaimedFieldSet | undefined;
|
|
185
|
+
path: string;
|
|
186
|
+
declaredValue: unknown;
|
|
187
|
+
liveValue: unknown;
|
|
188
|
+
}): FieldClaim {
|
|
189
|
+
if (!isClaimed(input.claimed, input.path)) return "undeclared";
|
|
190
|
+
return deepValueEqual(input.declaredValue, input.liveValue) ? "declared-equal" : "declared-changed";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Who holds an undeclared live field, and which source said so.
|
|
195
|
+
*
|
|
196
|
+
* The manager wins where the substrate records one: "owned by `hpa-controller`"
|
|
197
|
+
* and "chant never set it" are the same verdict with very different amounts of
|
|
198
|
+
* information in them, and an operator can act on the first. The claim is the
|
|
199
|
+
* fallback, and it answers on every substrate.
|
|
200
|
+
*/
|
|
201
|
+
export function heldBy(fieldOwners: Record<string, string> | undefined, path: string): {
|
|
202
|
+
holder?: string;
|
|
203
|
+
source: FieldClaimSource;
|
|
204
|
+
} {
|
|
205
|
+
const manager = fieldOwners?.[path];
|
|
206
|
+
if (manager) return { holder: manager, source: "field-manager" };
|
|
207
|
+
return { source: "claimed-fields" };
|
|
208
|
+
}
|
|
@@ -9,8 +9,8 @@ import { formatError, formatInfo } from "./format";
|
|
|
9
9
|
* out (chant #1108) so every command that discovers project source before
|
|
10
10
|
* running it resolves `chant.config.ts`'s declared `buildParams` the exact
|
|
11
11
|
* same way: `chant build` (`../commands/build.ts`'s `buildCommand`), the
|
|
12
|
-
* component deploy driver (`./handlers/run.ts`'s `chant run --components`
|
|
13
|
-
*
|
|
12
|
+
* component deploy driver (`./handlers/run.ts`'s `chant run --components`),
|
|
13
|
+
* and generate mode (`./handlers/build.ts`'s
|
|
14
14
|
* `chant build --components --generate <lexicon>`).
|
|
15
15
|
*
|
|
16
16
|
* Before #1108, only `buildCommand` ran this sequence — `chant run
|
package/src/cli/command-group.ts
CHANGED
|
@@ -57,6 +57,20 @@ export interface CommandGroup {
|
|
|
57
57
|
description: string;
|
|
58
58
|
/** Verbs in this group. */
|
|
59
59
|
commands: CommandGroupCommand[];
|
|
60
|
+
/**
|
|
61
|
+
* The verb to run when the group name is given on its own — `chant acp`
|
|
62
|
+
* rather than `chant acp serve` (#2125). Named here rather than assumed to
|
|
63
|
+
* be the first verb, because a group whose bare form should be a usage
|
|
64
|
+
* error (`chant kube`) must stay one: omitting this field is how a group
|
|
65
|
+
* says it has no default.
|
|
66
|
+
*
|
|
67
|
+
* The token after the group name decides which reading applies, and that
|
|
68
|
+
* decision belongs to the caller that still holds the raw argv
|
|
69
|
+
* (`../cli/main.ts`'s `tryPluginCommand`): with a default verb, `chant acp
|
|
70
|
+
* --durable-requests` has no verb token to consume, so the flag is the
|
|
71
|
+
* default verb's argument rather than a verb it does not have.
|
|
72
|
+
*/
|
|
73
|
+
defaultVerb?: string;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pretend-provider-lock-metadata
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
resource "null_resource" "root" {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
resource "null_resource" "foo" {}
|
|
@@ -8,8 +8,16 @@
|
|
|
8
8
|
"dist/"
|
|
9
9
|
],
|
|
10
10
|
"exports": {
|
|
11
|
-
".":
|
|
12
|
-
|
|
11
|
+
".": {
|
|
12
|
+
"development": "./src/index.ts",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./src/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"./*": {
|
|
17
|
+
"development": "./src/*.ts",
|
|
18
|
+
"types": "./dist/*.d.ts",
|
|
19
|
+
"default": "./src/*.ts"
|
|
20
|
+
},
|
|
13
21
|
"./manifest": "./dist/manifest.json",
|
|
14
22
|
"./meta": "./dist/meta.json",
|
|
15
23
|
"./types": "./dist/types/index.d.ts"
|
|
@@ -18,8 +26,9 @@
|
|
|
18
26
|
"generate": "npx tsx src/codegen/generate-cli.ts",
|
|
19
27
|
"validate": "npx tsx src/validate-cli.ts",
|
|
20
28
|
"docs": "npx tsx src/codegen/docs-cli.ts",
|
|
21
|
-
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
22
|
-
"prepack": "npm run generate && npm run validate && npm run build"
|
|
29
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete",
|
|
30
|
+
"prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
|
|
31
|
+
"bundle": "tsx src/package-cli.ts"
|
|
23
32
|
},
|
|
24
33
|
"devDependencies": {
|
|
25
34
|
"@intentius/chant": "*",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { generate, writeGeneratedFiles } from "./codegen/generate";
|
|
3
|
+
import { packageLexicon } from "./codegen/package";
|
|
4
|
+
import { writeBundleSpec } from "@intentius/chant/codegen/package";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
const srcDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
// 1. Generate src/generated/ files (writeGeneratedFiles resolves its own target)
|
|
11
|
+
const genResult = await generate({ verbose: true });
|
|
12
|
+
writeGeneratedFiles(genResult);
|
|
13
|
+
|
|
14
|
+
// 2. Run package pipeline and write dist/
|
|
15
|
+
const { spec, stats } = await packageLexicon({ verbose: true });
|
|
16
|
+
|
|
17
|
+
const distDir = join(dirname(srcDir), "dist");
|
|
18
|
+
writeBundleSpec(spec, distDir);
|
|
19
|
+
|
|
20
|
+
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
21
|
+
console.error(`dist/ written to ${distDir}`);
|