@intentius/chant 0.124.52 → 0.124.54
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/discover.d.ts.map +1 -1
- package/dist/audit/discover.js +3 -1
- package/dist/behaviour.d.ts +1 -1
- package/dist/behaviour.d.ts.map +1 -1
- package/dist/capabilities/templates.d.ts +65 -0
- package/dist/capabilities/templates.d.ts.map +1 -0
- package/dist/capabilities/templates.js +193 -0
- package/dist/capabilities/templates.json +145 -0
- package/dist/change-set-graph.d.ts +35 -3
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +60 -9
- package/dist/change-set.d.ts +65 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +23 -12
- package/dist/ci-identity.d.ts +7 -2
- package/dist/ci-identity.d.ts.map +1 -1
- package/dist/ci-identity.js +4 -1
- package/dist/cli/commands/import-index.d.ts +52 -0
- package/dist/cli/commands/import-index.d.ts.map +1 -0
- package/dist/cli/commands/import-index.js +226 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/components.js +11 -2
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +22 -4
- package/dist/cli/handlers/index-cloud.d.ts +14 -0
- package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
- package/dist/cli/handlers/index-cloud.js +22 -0
- package/dist/cli/handlers/index-diff.d.ts +18 -0
- package/dist/cli/handlers/index-diff.d.ts.map +1 -0
- package/dist/cli/handlers/index-diff.js +142 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-drift.js +64 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +188 -28
- package/dist/cli/handlers/misc.d.ts +9 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +77 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +15 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +89 -15
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +28 -4
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/cli/handlers/scenario.js +8 -7
- package/dist/cli/handlers/search.d.ts +34 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +216 -36
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +55 -1
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
- package/dist/cli/mcp/lifecycle-tools.js +3 -2
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.js +4 -3
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +55 -2
- package/dist/cli/registry.d.ts +19 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cloud-lister/aws.d.ts +35 -0
- package/dist/cloud-lister/aws.d.ts.map +1 -0
- package/dist/cloud-lister/aws.js +203 -0
- package/dist/cloud-lister/azure.d.ts +34 -0
- package/dist/cloud-lister/azure.d.ts.map +1 -0
- package/dist/cloud-lister/azure.js +129 -0
- package/dist/cloud-lister/common.d.ts +40 -0
- package/dist/cloud-lister/common.d.ts.map +1 -0
- package/dist/cloud-lister/common.js +83 -0
- package/dist/cloud-lister/credentials.d.ts +50 -0
- package/dist/cloud-lister/credentials.d.ts.map +1 -0
- package/dist/cloud-lister/credentials.js +86 -0
- package/dist/cloud-lister/fakes.d.ts +27 -0
- package/dist/cloud-lister/fakes.d.ts.map +1 -0
- package/dist/cloud-lister/fakes.js +50 -0
- package/dist/cloud-lister/gcp.d.ts +42 -0
- package/dist/cloud-lister/gcp.d.ts.map +1 -0
- package/dist/cloud-lister/gcp.js +143 -0
- package/dist/cloud-lister/guard.d.ts +49 -0
- package/dist/cloud-lister/guard.d.ts.map +1 -0
- package/dist/cloud-lister/guard.js +103 -0
- package/dist/cloud-lister/ids.d.ts +23 -0
- package/dist/cloud-lister/ids.d.ts.map +1 -0
- package/dist/cloud-lister/ids.js +42 -0
- package/dist/cloud-lister/index.d.ts +67 -0
- package/dist/cloud-lister/index.d.ts.map +1 -0
- package/dist/cloud-lister/index.js +102 -0
- package/dist/cloud-lister/kubernetes.d.ts +64 -0
- package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
- package/dist/cloud-lister/kubernetes.js +276 -0
- package/dist/cloud-lister/markers.d.ts +31 -0
- package/dist/cloud-lister/markers.d.ts.map +1 -0
- package/dist/cloud-lister/markers.js +73 -0
- package/dist/cloud-lister/mask.d.ts +31 -0
- package/dist/cloud-lister/mask.d.ts.map +1 -0
- package/dist/cloud-lister/mask.js +74 -0
- package/dist/cloud-lister/sigv4.d.ts +29 -0
- package/dist/cloud-lister/sigv4.d.ts.map +1 -0
- package/dist/cloud-lister/sigv4.js +54 -0
- package/dist/cloud-lister/transport.d.ts +55 -0
- package/dist/cloud-lister/transport.d.ts.map +1 -0
- package/dist/cloud-lister/transport.js +86 -0
- package/dist/cloud-lister/types.d.ts +121 -0
- package/dist/cloud-lister/types.d.ts.map +1 -0
- package/dist/cloud-lister/types.js +24 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +6 -0
- package/dist/components/capability-plugin.d.ts +9 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability.d.ts +215 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +21 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +58 -14
- package/dist/components/declared-reads.d.ts +38 -0
- package/dist/components/declared-reads.d.ts.map +1 -0
- package/dist/components/declared-reads.js +131 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +4 -0
- package/dist/components/driver.d.ts +34 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +66 -3
- package/dist/components/fan-out-run.d.ts +23 -1
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +82 -14
- package/dist/components/fan-out-support.d.ts +6 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out-support.js +4 -1
- package/dist/components/fan-out.d.ts +28 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/groups.d.ts +229 -0
- package/dist/components/groups.d.ts.map +1 -0
- package/dist/components/groups.js +760 -0
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -0
- package/dist/components/plan-shape-conformance.d.ts +48 -0
- package/dist/components/plan-shape-conformance.d.ts.map +1 -0
- package/dist/components/plan-shape-conformance.js +122 -0
- package/dist/components/pr-run.d.ts +41 -4
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +209 -39
- package/dist/components/promote-rounds.d.ts +9 -0
- package/dist/components/promote-rounds.d.ts.map +1 -1
- package/dist/components/promote-rounds.js +23 -2
- package/dist/components/promote.d.ts +17 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +36 -6
- package/dist/components/release-gate.d.ts +23 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +97 -33
- package/dist/components/run-groups.d.ts +58 -0
- package/dist/components/run-groups.d.ts.map +1 -0
- package/dist/components/run-groups.js +78 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +6 -1
- package/dist/components/verbs/index.d.ts +1 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +1 -0
- package/dist/components/verbs/member-outputs.d.ts +187 -0
- package/dist/components/verbs/member-outputs.d.ts.map +1 -0
- package/dist/components/verbs/member-outputs.js +218 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/deep-observation.d.ts +1 -13
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/deep-observation.js +5 -22
- package/dist/drift-record.d.ts +542 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +886 -0
- package/dist/engine-facts.d.ts +144 -0
- package/dist/engine-facts.d.ts.map +1 -0
- package/dist/engine-facts.js +14 -0
- package/dist/graph-ir.d.ts +153 -2
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +224 -6
- package/dist/graph-refs.d.ts +145 -0
- package/dist/graph-refs.d.ts.map +1 -1
- package/dist/graph-refs.js +305 -0
- package/dist/graph-schema/fixtures/adopt.json +15 -0
- package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/dist/graph-schema/fixtures/index.json +68 -0
- package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/dist/graph-schema/fixtures.d.ts +23 -0
- package/dist/graph-schema/fixtures.d.ts.map +1 -0
- package/dist/graph-schema/fixtures.js +246 -0
- package/dist/graph-schema/generate.d.ts +15 -0
- package/dist/graph-schema/generate.d.ts.map +1 -0
- package/dist/graph-schema/generate.js +267 -0
- package/dist/graph-schema/graph.schema.json +1387 -0
- package/dist/health.schema.json +2 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/instance-nodes.d.ts +159 -0
- package/dist/instance-nodes.d.ts.map +1 -0
- package/dist/instance-nodes.js +231 -0
- package/dist/lexicon.d.ts +72 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/adoption-file.d.ts +56 -0
- package/dist/lifecycle/adoption-file.d.ts.map +1 -0
- package/dist/lifecycle/adoption-file.js +123 -0
- package/dist/lifecycle/cloud-index.d.ts +186 -0
- package/dist/lifecycle/cloud-index.d.ts.map +1 -0
- package/dist/lifecycle/cloud-index.js +618 -0
- package/dist/lifecycle/derive-edges.d.ts +59 -0
- package/dist/lifecycle/derive-edges.d.ts.map +1 -0
- package/dist/lifecycle/derive-edges.js +126 -0
- package/dist/lifecycle/git.d.ts +10 -9
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +7 -1
- package/dist/lifecycle/identity-match.d.ts +133 -0
- package/dist/lifecycle/identity-match.d.ts.map +1 -0
- package/dist/lifecycle/identity-match.js +147 -0
- package/dist/lifecycle/index-diff.d.ts +224 -0
- package/dist/lifecycle/index-diff.d.ts.map +1 -0
- package/dist/lifecycle/index-diff.js +327 -0
- package/dist/lifecycle/index-document.d.ts +130 -0
- package/dist/lifecycle/index-document.d.ts.map +1 -0
- package/dist/lifecycle/index-document.js +189 -0
- package/dist/lifecycle/index-drift.d.ts +121 -0
- package/dist/lifecycle/index-drift.d.ts.map +1 -0
- package/dist/lifecycle/index-drift.js +178 -0
- package/dist/lifecycle/index-import.d.ts +168 -0
- package/dist/lifecycle/index-import.d.ts.map +1 -0
- package/dist/lifecycle/index-import.js +515 -0
- package/dist/lifecycle/index-join.d.ts +129 -0
- package/dist/lifecycle/index-join.d.ts.map +1 -0
- package/dist/lifecycle/index-join.js +248 -0
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/observation-baseline.d.ts +61 -21
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/observation-baseline.js +144 -19
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +6 -1
- package/dist/lifecycle/query.d.ts +45 -4
- package/dist/lifecycle/query.d.ts.map +1 -1
- package/dist/lifecycle/query.js +89 -7
- package/dist/lifecycle/replay.d.ts +10 -3
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/replay.js +10 -6
- package/dist/lifecycle/scenario.d.ts +4 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
- package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-fake.js +21 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-s3.js +254 -0
- package/dist/lifecycle/snapshot-store.d.ts +170 -0
- package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store.js +832 -0
- package/dist/lifecycle/snapshot.d.ts +11 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +18 -5
- package/dist/lifecycle/type-listing.d.ts +148 -0
- package/dist/lifecycle/type-listing.d.ts.map +1 -0
- package/dist/lifecycle/type-listing.js +166 -0
- package/dist/lint/component-checks.d.ts +15 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/component-checks.js +1 -0
- package/dist/lint/presets/relaxed.json +2 -1
- package/dist/lint/presets/strict.json +2 -1
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
- package/dist/lint/rules/comp/index.d.ts +2 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/comp/index.js +4 -1
- package/dist/observation.d.ts +19 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +18 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +6 -4
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +4 -2
- package/dist/op/activity-failure.d.ts +27 -0
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +5 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -8
- package/dist/pr-loop.d.ts +56 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +51 -1
- package/dist/pulumi-urn.d.ts +101 -0
- package/dist/pulumi-urn.d.ts.map +1 -0
- package/dist/pulumi-urn.js +131 -0
- package/dist/release-floors.d.ts +23 -0
- package/dist/release-floors.d.ts.map +1 -0
- package/dist/release-floors.js +22 -0
- package/dist/sensitive-key.d.ts +23 -0
- package/dist/sensitive-key.d.ts.map +1 -0
- package/dist/sensitive-key.js +31 -0
- package/dist/wave-runtime.mjs +234 -234
- package/dist/workspace/adoption-report.d.ts +53 -0
- package/dist/workspace/adoption-report.d.ts.map +1 -0
- package/dist/workspace/adoption-report.js +76 -0
- package/dist/workspace/capabilities-contract.d.ts +7 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -1
- package/dist/workspace/capabilities-contract.js +2 -1
- package/dist/workspace/capabilities.schema.json +98 -0
- package/dist/workspace/change-set.schema.json +42 -3
- package/dist/workspace/ci-cloud-identity.d.ts +13 -1
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
- package/dist/workspace/ci-cloud-identity.js +30 -1
- package/dist/workspace/compose-graph.d.ts +10 -1
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +6 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +6 -1
- package/dist/workspace/drift-cli.d.ts +81 -0
- package/dist/workspace/drift-cli.d.ts.map +1 -0
- package/dist/workspace/drift-cli.js +317 -0
- package/dist/workspace/drift.schema.json +672 -0
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +3 -0
- package/dist/workspace/graph.schema.json +72 -0
- package/dist/workspace/init.d.ts +5 -1
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +55 -9
- package/dist/workspace/ledger.schema.json +1 -1
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +2 -0
- package/dist/workspace/pr-report.schema.json +51 -2
- package/dist/workspace/promote-waves.d.ts +112 -0
- package/dist/workspace/promote-waves.d.ts.map +1 -0
- package/dist/workspace/promote-waves.js +290 -0
- package/dist/workspace/promote.d.ts +84 -2
- package/dist/workspace/promote.d.ts.map +1 -1
- package/dist/workspace/promote.js +347 -61
- package/dist/workspace/reason-codes.d.ts +12 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +14 -0
- package/dist/workspace/search-contract.d.ts +3 -3
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/search-contract.js +2 -0
- package/dist/workspace/search.schema.json +103 -5
- package/package.json +14 -1
- package/src/__fixtures__/drift-record/README.md +58 -0
- package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
- package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
- package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
- package/src/__fixtures__/drift-record/component-status.json +91 -0
- package/src/__fixtures__/drift-record/deep-diff.json +259 -0
- package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
- package/src/__fixtures__/drift-record/live-diff.json +125 -0
- package/src/audit/discover.e2e.test.ts +14 -0
- package/src/audit/discover.ts +3 -1
- package/src/behaviour.ts +1 -1
- package/src/capabilities/templates.json +145 -0
- package/src/capabilities/templates.test.ts +212 -0
- package/src/capabilities/templates.ts +204 -0
- package/src/change-set-bundle.test.ts +83 -0
- package/src/change-set-graph.ts +80 -9
- package/src/change-set.test.ts +24 -0
- package/src/change-set.ts +77 -14
- package/src/ci-identity-targets.test.ts +23 -0
- package/src/ci-identity.ts +10 -4
- package/src/cli/commands/import-index.test.ts +117 -0
- package/src/cli/commands/import-index.ts +265 -0
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/handlers/components.test.ts +9 -0
- package/src/cli/handlers/components.ts +12 -2
- package/src/cli/handlers/fan-out.ts +17 -3
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +26 -4
- package/src/cli/handlers/index-cloud.ts +32 -0
- package/src/cli/handlers/index-diff.test.ts +68 -0
- package/src/cli/handlers/index-diff.ts +146 -0
- package/src/cli/handlers/ledger-root-member.test.ts +161 -0
- package/src/cli/handlers/lifecycle-drift.ts +68 -0
- package/src/cli/handlers/lifecycle.test.ts +33 -11
- package/src/cli/handlers/lifecycle.ts +205 -30
- package/src/cli/handlers/misc.ts +73 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
- package/src/cli/handlers/pr.ts +14 -2
- package/src/cli/handlers/promote.test.ts +64 -0
- package/src/cli/handlers/promote.ts +81 -6
- package/src/cli/handlers/query.ts +27 -4
- package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
- package/src/cli/handlers/run-groups-cli.test.ts +229 -0
- package/src/cli/handlers/scenario.test.ts +13 -0
- package/src/cli/handlers/scenario.ts +8 -7
- package/src/cli/handlers/search-index.test.ts +255 -0
- package/src/cli/handlers/search-json.test.ts +1 -1
- package/src/cli/handlers/search-ownership.test.ts +194 -0
- package/src/cli/handlers/search.ts +271 -38
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +56 -1
- package/src/cli/mcp/lifecycle-tools.ts +3 -2
- package/src/cli/mcp/resource-handlers.ts +4 -3
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +40 -2
- package/src/cli/registry.ts +19 -1
- package/src/cloud-lister/aws.test.ts +202 -0
- package/src/cloud-lister/aws.ts +226 -0
- package/src/cloud-lister/azure.test.ts +132 -0
- package/src/cloud-lister/azure.ts +142 -0
- package/src/cloud-lister/common.ts +115 -0
- package/src/cloud-lister/credentials.ts +110 -0
- package/src/cloud-lister/fakes.ts +66 -0
- package/src/cloud-lister/gcp.test.ts +121 -0
- package/src/cloud-lister/gcp.ts +159 -0
- package/src/cloud-lister/guard.test.ts +198 -0
- package/src/cloud-lister/guard.ts +111 -0
- package/src/cloud-lister/ids.ts +54 -0
- package/src/cloud-lister/index.ts +134 -0
- package/src/cloud-lister/kubernetes.test.ts +160 -0
- package/src/cloud-lister/kubernetes.ts +284 -0
- package/src/cloud-lister/markers.ts +74 -0
- package/src/cloud-lister/mask.ts +77 -0
- package/src/cloud-lister/shape.test.ts +81 -0
- package/src/cloud-lister/sigv4.ts +70 -0
- package/src/cloud-lister/transport.ts +118 -0
- package/src/cloud-lister/types.ts +135 -0
- package/src/components/capability-plugin-loader.ts +4 -0
- package/src/components/capability-plugin.ts +10 -0
- package/src/components/capability.ts +229 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/declared-reads.test.ts +103 -0
- package/src/components/declared-reads.ts +134 -0
- package/src/components/deploy-units.ts +4 -0
- package/src/components/driver.ts +89 -4
- package/src/components/fan-out-run.ts +95 -15
- package/src/components/fan-out-support.ts +10 -1
- package/src/components/fan-out.ts +30 -0
- package/src/components/groups.test.ts +320 -0
- package/src/components/groups.ts +880 -0
- package/src/components/index.ts +42 -0
- package/src/components/plan-shape-conformance.ts +130 -0
- package/src/components/pr-env-run-bundle.test.ts +2 -0
- package/src/components/pr-run.test.ts +516 -3
- package/src/components/pr-run.ts +231 -41
- package/src/components/promote-rounds.ts +25 -2
- package/src/components/promote.ts +55 -5
- package/src/components/registry.test.ts +1 -1
- package/src/components/release-gate.ts +115 -30
- package/src/components/release-groups.test.ts +362 -0
- package/src/components/run-groups.ts +113 -0
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +1 -0
- package/src/components/verbs/member-outputs.test.ts +205 -0
- package/src/components/verbs/member-outputs.ts +308 -0
- package/src/config.ts +44 -0
- package/src/deep-observation.ts +5 -23
- package/src/drift-record.test.ts +596 -0
- package/src/drift-record.ts +1206 -0
- package/src/engine-facts.guard.test.ts +111 -0
- package/src/engine-facts.test.ts +178 -0
- package/src/engine-facts.ts +154 -0
- package/src/gated-waves-bundle.test.ts +2 -0
- package/src/graph-ir-live.test.ts +92 -0
- package/src/graph-ir.ts +317 -7
- package/src/graph-refs.ts +416 -0
- package/src/graph-schema/fixtures/adopt.json +15 -0
- package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/src/graph-schema/fixtures/index.json +68 -0
- package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/src/graph-schema/fixtures.ts +267 -0
- package/src/graph-schema/generate.ts +263 -0
- package/src/graph-schema/graph-schema.test.ts +153 -0
- package/src/graph-schema/graph.schema.json +1387 -0
- package/src/health.schema.json +2 -1
- package/src/identity.ts +2 -0
- package/src/index.ts +2 -0
- package/src/instance-nodes.ts +288 -0
- package/src/lexicon.ts +76 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
- package/src/lifecycle/adoption-file.test.ts +100 -0
- package/src/lifecycle/adoption-file.ts +154 -0
- package/src/lifecycle/cloud-index.test.ts +517 -0
- package/src/lifecycle/cloud-index.ts +736 -0
- package/src/lifecycle/derive-edges.test.ts +193 -0
- package/src/lifecycle/derive-edges.ts +182 -0
- package/src/lifecycle/git.ts +10 -4
- package/src/lifecycle/identity-match.test.ts +308 -0
- package/src/lifecycle/identity-match.ts +257 -0
- package/src/lifecycle/index-diff.test.ts +198 -0
- package/src/lifecycle/index-diff.ts +476 -0
- package/src/lifecycle/index-document.test.ts +64 -0
- package/src/lifecycle/index-document.ts +299 -0
- package/src/lifecycle/index-drift.test.ts +191 -0
- package/src/lifecycle/index-drift.ts +304 -0
- package/src/lifecycle/index-import.test.ts +255 -0
- package/src/lifecycle/index-import.ts +634 -0
- package/src/lifecycle/index-join.test.ts +250 -0
- package/src/lifecycle/index-join.ts +304 -0
- package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/member-ledger.test.ts +25 -7
- package/src/lifecycle/observation-baseline.test.ts +72 -1
- package/src/lifecycle/observation-baseline.ts +190 -27
- package/src/lifecycle/observe.ts +6 -1
- package/src/lifecycle/query.test.ts +69 -1
- package/src/lifecycle/query.ts +103 -10
- package/src/lifecycle/replay.test.ts +9 -5
- package/src/lifecycle/replay.ts +9 -5
- package/src/lifecycle/scenario.ts +4 -3
- package/src/lifecycle/snapshot-store-fake.ts +29 -0
- package/src/lifecycle/snapshot-store-s3.ts +316 -0
- package/src/lifecycle/snapshot-store.test.ts +385 -0
- package/src/lifecycle/snapshot-store.ts +981 -0
- package/src/lifecycle/snapshot.test.ts +13 -0
- package/src/lifecycle/snapshot.ts +23 -6
- package/src/lifecycle/type-listing.test.ts +166 -0
- package/src/lifecycle/type-listing.ts +309 -0
- package/src/lint/component-checks.ts +17 -1
- package/src/lint/config.test.ts +3 -0
- package/src/lint/presets/relaxed.json +2 -1
- package/src/lint/presets/strict.json +2 -1
- package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
- package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
- package/src/lint/rules/comp/comp.test.ts +2 -1
- package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
- package/src/lint/rules/comp/comp008.test.ts +77 -0
- package/src/lint/rules/comp/index.ts +4 -1
- package/src/observation-edges.test.ts +33 -0
- package/src/observation.ts +36 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +8 -5
- package/src/op/activity-failure.ts +36 -0
- package/src/op/index.ts +2 -1
- package/src/op/local-executor.test.ts +80 -0
- package/src/op/local-executor.ts +27 -9
- package/src/pr-loop-bundle.test.ts +5 -2
- package/src/pr-loop.test.ts +43 -0
- package/src/pr-loop.ts +111 -2
- package/src/pulumi-urn.ts +151 -0
- package/src/release-floors.ts +22 -0
- package/src/sensitive-key.ts +34 -0
- package/src/workspace/adoption-report.test.ts +61 -0
- package/src/workspace/adoption-report.ts +103 -0
- package/src/workspace/capabilities-contract.test.ts +4 -0
- package/src/workspace/capabilities-contract.ts +9 -1
- package/src/workspace/capabilities.schema.json +98 -0
- package/src/workspace/change-set.schema.json +42 -3
- package/src/workspace/ci-cloud-identity.ts +31 -1
- package/src/workspace/compose-graph.test.ts +12 -0
- package/src/workspace/compose-graph.ts +12 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
- package/src/workspace/conformance/index.ts +6 -1
- package/src/workspace/drift-cli.test.ts +65 -0
- package/src/workspace/drift-cli.ts +365 -0
- package/src/workspace/drift.schema.json +672 -0
- package/src/workspace/graph-cli.ts +5 -0
- package/src/workspace/graph.schema.json +72 -0
- package/src/workspace/init.test.ts +38 -1
- package/src/workspace/init.ts +50 -8
- package/src/workspace/intent-gaps.test.ts +18 -2
- package/src/workspace/ledger.schema.json +1 -1
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/pr-report.schema.json +51 -2
- package/src/workspace/promote-waves.test.ts +296 -0
- package/src/workspace/promote-waves.ts +340 -0
- package/src/workspace/promote.test.ts +158 -4
- package/src/workspace/promote.ts +398 -57
- package/src/workspace/reason-codes.test.ts +4 -1
- package/src/workspace/reason-codes.ts +14 -0
- package/src/workspace/search-contract.ts +2 -0
- package/src/workspace/search.schema.json +103 -5
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Guard (#4527, ruled on #4520 Q1): engine facts are for people and for
|
|
2
|
+
// readers that show the graph, never for a decision. Nothing under a change
|
|
3
|
+
// set, the lifecycle or an Op names `EngineFacts` or imports
|
|
4
|
+
// ./engine-facts.ts, so a delete, a plan or a gate cannot start reading a
|
|
5
|
+
// fact without this test failing. The only files let through are the ones
|
|
6
|
+
// CARRIES_FACTS_ONLY names, each with its reason. `IRNode.ownership` and
|
|
7
|
+
// `ResourceMetadata.ownership` keep feeding the painters and the change set.
|
|
8
|
+
import { describe, it, expect } from "vitest";
|
|
9
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const coreSrc = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const repo = resolve(coreSrc, "..", "..", "..");
|
|
15
|
+
|
|
16
|
+
/** What a decision path must not contain: the type's name, or an import of its module. */
|
|
17
|
+
const FORBIDDEN = [/\bEngineFacts\b/, /["'][^"']*\/engine-facts(?:\.ts|\.js)?["']/];
|
|
18
|
+
|
|
19
|
+
/** The offending lines of one file's text, as `line: text`. */
|
|
20
|
+
export function engineFactsUses(text: string): string[] {
|
|
21
|
+
const hits: string[] = [];
|
|
22
|
+
text.split("\n").forEach((line, i) => {
|
|
23
|
+
if (FORBIDDEN.some((re) => re.test(line))) hits.push(`${i + 1}: ${line.trim()}`);
|
|
24
|
+
});
|
|
25
|
+
return hits;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function walk(dir: string, out: string[] = []): string[] {
|
|
29
|
+
for (const name of readdirSync(dir)) {
|
|
30
|
+
if (name === "node_modules" || name === "dist") continue;
|
|
31
|
+
const path = join(dir, name);
|
|
32
|
+
if (statSync(path).isDirectory()) walk(path, out);
|
|
33
|
+
else if (/\.(?:ts|mts|tsx|mjs|js)$/.test(name)) out.push(path);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Files under a decision path that carry engine facts without deciding on
|
|
40
|
+
* them, by repo-relative path, each with why. Adding one needs the same
|
|
41
|
+
* check: read the file and confirm no create, delete, adopt, plan or gate
|
|
42
|
+
* choice reads a fact. A file that does decide on facts gets its code fixed,
|
|
43
|
+
* never an entry here.
|
|
44
|
+
*/
|
|
45
|
+
const CARRIES_FACTS_ONLY: Readonly<Record<string, string>> = {
|
|
46
|
+
// #4521: types a cloud listing into index nodes. It copies the listing's
|
|
47
|
+
// markers, cloud ids and observedAt into IRNode.facts and returns the node;
|
|
48
|
+
// nothing in it branches on a fact's value. Pure, no I/O, and nothing it
|
|
49
|
+
// returns is an Op, a change set or a gate.
|
|
50
|
+
"packages/core/src/lifecycle/type-listing.ts":
|
|
51
|
+
"#4521 index typing: copies listed facts into IRNode.facts for the index and decides nothing on them",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Every source file on a decision path: core's change set, lifecycle and Ops, and each lexicon's Ops and change-set files. */
|
|
55
|
+
function decisionPathFiles(): string[] {
|
|
56
|
+
const files = new Set<string>();
|
|
57
|
+
for (const f of walk(join(coreSrc, "lifecycle"))) files.add(f);
|
|
58
|
+
for (const f of walk(join(coreSrc, "op"))) files.add(f);
|
|
59
|
+
for (const f of walk(coreSrc)) if (/(?:^|[\\/])change-set[^\\/]*$/.test(relative(coreSrc, f))) files.add(f);
|
|
60
|
+
const lexicons = join(repo, "lexicons");
|
|
61
|
+
for (const lexicon of readdirSync(lexicons)) {
|
|
62
|
+
const src = join(lexicons, lexicon, "src");
|
|
63
|
+
let isDir = false;
|
|
64
|
+
try {
|
|
65
|
+
isDir = statSync(src).isDirectory();
|
|
66
|
+
} catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!isDir) continue;
|
|
70
|
+
for (const f of walk(src)) {
|
|
71
|
+
const rel = relative(src, f);
|
|
72
|
+
if (/^op[\\/]/.test(rel) || /(?:^|[\\/])change-set[^\\/]*$/.test(rel)) files.add(f);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...files].sort();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe("engine facts never reach a decision path (#4527)", () => {
|
|
79
|
+
it("scans the change set, the lifecycle and the Ops", () => {
|
|
80
|
+
const files = decisionPathFiles().map((f) => relative(repo, f));
|
|
81
|
+
expect(files).toContain("packages/core/src/change-set.ts");
|
|
82
|
+
expect(files).toContain("packages/core/src/lifecycle/change-set.ts");
|
|
83
|
+
expect(files.some((f) => f.startsWith("packages/core/src/op/"))).toBe(true);
|
|
84
|
+
expect(files.some((f) => /^lexicons\/[^/]+\/src\/op\//.test(f))).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("no file there names EngineFacts or imports engine-facts", () => {
|
|
88
|
+
const offenders: string[] = [];
|
|
89
|
+
for (const file of decisionPathFiles()) {
|
|
90
|
+
if (relative(repo, file) in CARRIES_FACTS_ONLY) continue;
|
|
91
|
+
for (const hit of engineFactsUses(readFileSync(file, "utf-8"))) offenders.push(`${relative(repo, file)}:${hit}`);
|
|
92
|
+
}
|
|
93
|
+
expect(offenders).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("each exemption names a file on a decision path that still uses engine facts", () => {
|
|
97
|
+
const files = new Set(decisionPathFiles().map((f) => relative(repo, f)));
|
|
98
|
+
for (const [file, reason] of Object.entries(CARRIES_FACTS_ONLY)) {
|
|
99
|
+
expect(files.has(file), `${file} is not on a decision path; drop its exemption`).toBe(true);
|
|
100
|
+
expect(engineFactsUses(readFileSync(join(repo, file), "utf-8")).length, `${file} no longer uses engine facts; drop its exemption`).toBeGreaterThan(0);
|
|
101
|
+
expect(reason.trim()).not.toBe("");
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("catches a BREAK: an import of the type, of the module, or a use by name", () => {
|
|
106
|
+
expect(engineFactsUses('import type { EngineFacts } from "../engine-facts";')).toHaveLength(1);
|
|
107
|
+
expect(engineFactsUses('import * as f from "../engine-facts.ts";')).toHaveLength(1);
|
|
108
|
+
expect(engineFactsUses("const x: import('../graph-ir').IRNode['facts'] = y as EngineFacts;")).toHaveLength(1);
|
|
109
|
+
expect(engineFactsUses('import type { IRNode } from "../graph-ir";')).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Engine facts on graph nodes and edge grain (#4527, designed on #4520).
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { buildGraphIr, buildLiveGraphIr, entityFacts, sourceOverlayGraphs, type GraphIR, type LiveObservation } from "./graph-ir";
|
|
4
|
+
import { DECLARABLE_MARKER, type Declarable } from "./declarable";
|
|
5
|
+
import { ENGINE_FACTS_FLOOR, type EngineFacts } from "./engine-facts";
|
|
6
|
+
import { NEXT_RELEASE } from "./release-floors";
|
|
7
|
+
|
|
8
|
+
const facts: EngineFacts = {
|
|
9
|
+
ownership: "bound",
|
|
10
|
+
ownedBy: "marker",
|
|
11
|
+
markers: { owner: "web", address: "aws_s3_bucket.logs", encoding: "escaped", source: "live" },
|
|
12
|
+
cloudIds: [{ kind: "import-id", value: "web-logs" }],
|
|
13
|
+
observedAt: "2026-10-26T12:00:00Z",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe("buildLiveGraphIr: engine facts (#4527)", () => {
|
|
17
|
+
it("copies ResourceMetadata.facts onto the node beside physicalId", () => {
|
|
18
|
+
const ir = buildLiveGraphIr([
|
|
19
|
+
{ lexicon: "terraform", resources: { "aws_s3_bucket.logs": { type: "aws_s3_bucket", status: "present", physicalId: "web-logs", facts } } },
|
|
20
|
+
]);
|
|
21
|
+
const node = ir.nodes[0]!;
|
|
22
|
+
expect(node.physicalId).toBe("web-logs");
|
|
23
|
+
expect(node.facts).toEqual(facts);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("leaves IRNode.ownership to the metadata's own verdict, whatever the facts say", () => {
|
|
27
|
+
const ir = buildLiveGraphIr([
|
|
28
|
+
{
|
|
29
|
+
lexicon: "terraform",
|
|
30
|
+
resources: { q: { type: "aws_sqs_queue", status: "present", ownership: "unknown", facts: { ownership: "foreign" } } },
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
expect(ir.nodes[0]!.ownership).toBeUndefined();
|
|
34
|
+
expect(ir.nodes[0]!.facts).toEqual({ ownership: "foreign" });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("adds no facts field when the observation has none", () => {
|
|
38
|
+
const ir = buildLiveGraphIr([{ lexicon: "aws", resources: { b: { type: "AWS::S3::Bucket", status: "CREATE_COMPLETE" } } }]);
|
|
39
|
+
expect("facts" in ir.nodes[0]!).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("copies an instance's facts onto its instance node", () => {
|
|
43
|
+
const instanceFacts: EngineFacts = { ownership: "bound", ownedBy: "record", cloudIds: [{ kind: "import-id", value: "eipalloc-1" }] };
|
|
44
|
+
const ir = buildLiveGraphIr([
|
|
45
|
+
{
|
|
46
|
+
lexicon: "terraform",
|
|
47
|
+
resources: {
|
|
48
|
+
"aws_eip.pool": {
|
|
49
|
+
type: "aws_eip",
|
|
50
|
+
status: "present",
|
|
51
|
+
instances: [
|
|
52
|
+
{ key: "[0]", address: "aws_eip.pool[0]", verdict: "present", physicalId: "eipalloc-0", facts: { ownership: "pending" } },
|
|
53
|
+
{ key: "[1]", address: "aws_eip.pool[1]", verdict: "present", physicalId: "eipalloc-1", facts: instanceFacts },
|
|
54
|
+
{ key: "[2]", address: "aws_eip.pool[2]", verdict: "present" },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
const byId = new Map(ir.nodes.map((n) => [n.id, n]));
|
|
61
|
+
expect(byId.get("aws_eip.pool[0]")!.facts).toEqual({ ownership: "pending" });
|
|
62
|
+
expect(byId.get("aws_eip.pool[1]")!.facts).toEqual(instanceFacts);
|
|
63
|
+
expect("facts" in byId.get("aws_eip.pool[2]")!).toBe(false);
|
|
64
|
+
expect("facts" in byId.get("aws_eip.pool")!).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("buildLiveGraphIr: edge grain (#4527)", () => {
|
|
69
|
+
const pair = {
|
|
70
|
+
lexicon: "pulumi",
|
|
71
|
+
resources: {
|
|
72
|
+
policy: { type: "aws:s3/bucketPolicy:BucketPolicy", status: "present" },
|
|
73
|
+
bucket: { type: "aws:s3/bucketV2:BucketV2", status: "present" },
|
|
74
|
+
},
|
|
75
|
+
} satisfies LiveObservation;
|
|
76
|
+
|
|
77
|
+
it("passes grain through unchanged", () => {
|
|
78
|
+
const ir = buildLiveGraphIr([{ ...pair, edges: [{ from: "policy", to: "bucket", kind: "ref", viaAttr: "dependencies", grain: "instance" }] }]);
|
|
79
|
+
expect(ir.edges).toEqual([{ from: "policy", to: "bucket", kind: "ref", viaAttr: "dependencies", grain: "instance" }]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("keeps two edges between the same pair that differ only in grain", () => {
|
|
83
|
+
const ir = buildLiveGraphIr([
|
|
84
|
+
{
|
|
85
|
+
...pair,
|
|
86
|
+
edges: [
|
|
87
|
+
{ from: "policy", to: "bucket", kind: "ref", viaAttr: "dependencies", grain: "instance" },
|
|
88
|
+
{ from: "policy", to: "bucket", kind: "ref", viaAttr: "dependencies", grain: "block" },
|
|
89
|
+
{ from: "policy", to: "bucket", kind: "ref", viaAttr: "dependencies" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
expect(ir.edges.map((e) => e.grain)).toEqual([undefined, "block", "instance"]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("still dedupes edges with the same grain", () => {
|
|
97
|
+
const edge = { from: "policy", to: "bucket", kind: "ref" as const, viaAttr: "dependencies", grain: "instance" as const };
|
|
98
|
+
const ir = buildLiveGraphIr([{ ...pair, edges: [edge] }, { ...pair, edges: [edge] }]);
|
|
99
|
+
expect(ir.edges).toHaveLength(1);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("sourceOverlayGraphs: engine facts and grain (#4527)", () => {
|
|
104
|
+
const n = (id: string) => ({ id, kind: "aws_s3_bucket", lexicon: "terraform", attrs: {} });
|
|
105
|
+
const declared: GraphIR = { nodes: [n("aws_s3_bucket.logs"), n("aws_s3_bucket.planned")], edges: [], groups: {} };
|
|
106
|
+
const live: GraphIR = {
|
|
107
|
+
nodes: [
|
|
108
|
+
{ ...n("aws_s3_bucket.logs"), physicalId: "web-logs", facts },
|
|
109
|
+
{ ...n("aws_s3_bucket.stray"), facts: { ownership: "unowned" } },
|
|
110
|
+
],
|
|
111
|
+
edges: [
|
|
112
|
+
{ from: "aws_s3_bucket.stray", to: "aws_s3_bucket.logs", kind: "ref", viaAttr: "dependencies", grain: "block" },
|
|
113
|
+
{ from: "aws_s3_bucket.stray", to: "aws_s3_bucket.logs", kind: "ref", viaAttr: "dependencies", grain: "instance" },
|
|
114
|
+
],
|
|
115
|
+
groups: {},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
it("carries the observed facts onto the managed declared node, as it does physicalId", () => {
|
|
119
|
+
const ir = sourceOverlayGraphs(declared, live);
|
|
120
|
+
const logs = ir.nodes.find((x) => x.id === "aws_s3_bucket.logs")!;
|
|
121
|
+
expect(logs.physicalId).toBe("web-logs");
|
|
122
|
+
expect(logs.facts).toEqual(facts);
|
|
123
|
+
expect("facts" in ir.nodes.find((x) => x.id === "aws_s3_bucket.planned")!).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("keeps a foreign node's facts and both grains of its live edges", () => {
|
|
127
|
+
const ir = sourceOverlayGraphs(declared, live);
|
|
128
|
+
expect(ir.nodes.find((x) => x.id === "aws_s3_bucket.stray")!.facts).toEqual({ ownership: "unowned" });
|
|
129
|
+
expect(ir.edges.map((e) => e.grain).sort()).toEqual(["block", "instance"]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("buildGraphIr: an entity's facts and reference grain (#4529)", () => {
|
|
134
|
+
// The shape the pulumi lexicon's member read gives a plunge member's entity.
|
|
135
|
+
function entity(type: string, references: unknown[], withFacts?: EngineFacts): Declarable {
|
|
136
|
+
const e = { lexicon: "pulumi", entityType: type, kind: "resource", props: {}, references, [DECLARABLE_MARKER]: true };
|
|
137
|
+
if (withFacts) Object.defineProperty(e, "facts", { value: withFacts, enumerable: false });
|
|
138
|
+
return e as unknown as Declarable;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
it("copies an entity's facts onto its node, and keeps them out of attrs", () => {
|
|
142
|
+
const ir = buildGraphIr(new Map([["m/a", entity("aws:s3/bucket:Bucket", [], facts)]]));
|
|
143
|
+
expect(ir.nodes[0]!.facts).toEqual(facts);
|
|
144
|
+
expect(ir.nodes[0]!.attrs).toEqual({});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("keeps an enumerable facts field out of attrs too", () => {
|
|
148
|
+
const e = { ...(entity("aws:s3/bucket:Bucket", []) as unknown as Record<string, unknown>), facts };
|
|
149
|
+
const ir = buildGraphIr(new Map([["m/a", e as unknown as Declarable]]));
|
|
150
|
+
expect(ir.nodes[0]!.facts).toEqual(facts);
|
|
151
|
+
expect(ir.nodes[0]!.attrs).toEqual({});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("adds no facts field to an entity with none", () => {
|
|
155
|
+
expect(entityFacts(entity("x", []))).toBeUndefined();
|
|
156
|
+
expect("facts" in buildGraphIr(new Map([["m/a", entity("x", [])]])).nodes[0]!).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("passes a reference's grain to its edge, and two grains between one pair are two edges", () => {
|
|
160
|
+
const ir = buildGraphIr(
|
|
161
|
+
new Map([
|
|
162
|
+
["m/a", entity("x", [{ to: "m/b", viaAttr: "bucket", grain: "property" }, { to: "m/b", viaAttr: "bucket", grain: "instance" }, { to: "m/b", viaAttr: "dependencies" }])],
|
|
163
|
+
["m/b", entity("y", [{ to: "m/a", viaAttr: "parent", grain: "sideways" }])],
|
|
164
|
+
]),
|
|
165
|
+
);
|
|
166
|
+
expect(ir.edges.map((e) => [e.from, e.viaAttr, e.grain ?? null])).toEqual([
|
|
167
|
+
["m/a", "bucket", "instance"],
|
|
168
|
+
["m/a", "bucket", "property"],
|
|
169
|
+
["m/a", "dependencies", null],
|
|
170
|
+
// A grain outside the three is not carried.
|
|
171
|
+
["m/b", "parent", null],
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("names its floor as the next release", () => {
|
|
176
|
+
expect(ENGINE_FACTS_FLOOR).toBe(NEXT_RELEASE);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine facts on graph nodes, and the engine graph document (#4527, designed
|
|
3
|
+
* on #4520 and ruled there 2026-10-26).
|
|
4
|
+
*
|
|
5
|
+
* An ownership engine (choudoufu, plunge) prints one JSON document per read:
|
|
6
|
+
* a `GraphIR` at version 1 whose `meta.engineGraph` says which engine read
|
|
7
|
+
* what, and whose nodes carry {@link EngineFacts} under `facts`. behold#1027
|
|
8
|
+
* loads it as it is; chant's terraform and pulumi lexicons read the same
|
|
9
|
+
* document onto `ResourceMetadata.facts`, which `buildLiveGraphIr` copies to
|
|
10
|
+
* the node.
|
|
11
|
+
*
|
|
12
|
+
* Facts are for people and for readers that show the graph. Nothing in chant
|
|
13
|
+
* decides from them: no change set, lifecycle step or Op imports this module
|
|
14
|
+
* (./engine-facts.guard.test.ts), and `IRNode.ownership` keeps its own meaning.
|
|
15
|
+
*
|
|
16
|
+
* The JSON Schema in ./graph-schema/graph.schema.json is generated from these
|
|
17
|
+
* types and ./lifecycle/index-document.ts (`npm run generate:graph-schema`).
|
|
18
|
+
*/
|
|
19
|
+
import type { GraphIR } from "./graph-ir";
|
|
20
|
+
import { NEXT_RELEASE } from "./release-floors";
|
|
21
|
+
|
|
22
|
+
/** The format of {@link EngineGraphMeta}. Moves only for a change a reader could not ignore. */
|
|
23
|
+
export const ENGINE_GRAPH_FORMAT = 1;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The first chant whose graph carries `nodes[].facts` and `edges[].grain`
|
|
27
|
+
* (#4527, #4529, #4528): a live read through an engine (the terraform
|
|
28
|
+
* lexicon's read of `choudoufu live-graph -json -from-plan`, which writes
|
|
29
|
+
* `ResourceMetadata.facts` and the observation's `edges`), and a plunge
|
|
30
|
+
* member's checked-in `engine-graph.json` in the pulumi lexicon's member
|
|
31
|
+
* read. A reader treats a graph from an older chant as having neither. The
|
|
32
|
+
* engine side's floor for the terraform read is that lexicon's
|
|
33
|
+
* `MIN_CHOUDOUFU_GRAPH_VERSION`.
|
|
34
|
+
*/
|
|
35
|
+
export const ENGINE_FACTS_FLOOR = NEXT_RELEASE;
|
|
36
|
+
|
|
37
|
+
/** The marker values an engine read or recorded for one node. */
|
|
38
|
+
export interface EngineMarkers {
|
|
39
|
+
/** The estate name, or plunge's `<project>/<stack>`. A digest under encoding `digest`. */
|
|
40
|
+
owner?: string;
|
|
41
|
+
/** The tofu-address value, or plunge's URN without stack and project. Same encoding rule as `owner`. */
|
|
42
|
+
address?: string;
|
|
43
|
+
/** How `owner` and `address` are written on the object. */
|
|
44
|
+
encoding: "plain" | "escaped" | "digest";
|
|
45
|
+
/** `live`: read off the live object. `record`: what the engine's record says it wrote. */
|
|
46
|
+
source: "live" | "record";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One cloud identifier of a node. */
|
|
50
|
+
export interface EngineCloudId {
|
|
51
|
+
kind: "import-id" | "arn" | "provider-id" | "self-link" | "k8s-uid";
|
|
52
|
+
value: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** One version of a node's record. No record values. */
|
|
56
|
+
export interface EngineRecordVersion {
|
|
57
|
+
id: string;
|
|
58
|
+
/** ISO 8601 UTC. */
|
|
59
|
+
writtenAt: string;
|
|
60
|
+
current: boolean;
|
|
61
|
+
deleted: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The versions of a node's record, when the run asked for them (`-history`, `--versions`). */
|
|
65
|
+
export interface EngineRecordVersions {
|
|
66
|
+
/** False for a store that replaces records in place (local, Kubernetes). */
|
|
67
|
+
kept: boolean;
|
|
68
|
+
/** Newest first. */
|
|
69
|
+
versions: EngineRecordVersion[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What an ownership engine observed about one node (#4520). Never read for a
|
|
74
|
+
* decision. Every field is optional: one the engine cannot supply is absent,
|
|
75
|
+
* never guessed, and a reader shows it as unknown.
|
|
76
|
+
*/
|
|
77
|
+
export interface EngineFacts {
|
|
78
|
+
/** The engine's ownership verdict. A node built from configuration alone has none. */
|
|
79
|
+
ownership?: "bound" | "unowned" | "pending" | "foreign";
|
|
80
|
+
markers?: EngineMarkers;
|
|
81
|
+
/** What bound the node: a marker, the engine's record, or its name. */
|
|
82
|
+
ownedBy?: "marker" | "record" | "name";
|
|
83
|
+
/** The node's cloud ids. The first is the primary one and equals `physicalId` when both are set. */
|
|
84
|
+
cloudIds?: EngineCloudId[];
|
|
85
|
+
recordVersions?: EngineRecordVersions;
|
|
86
|
+
/** ISO 8601 UTC, when this node's live facts were read. */
|
|
87
|
+
observedAt?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* What an edge joins (#4520): two blocks, two instances, or a property of one
|
|
92
|
+
* instance to another. Absent on an edge whose source does not say.
|
|
93
|
+
*/
|
|
94
|
+
export type EdgeGrain = "block" | "instance" | "property";
|
|
95
|
+
|
|
96
|
+
/** The engine that printed the document. */
|
|
97
|
+
export interface EngineGraphEngine {
|
|
98
|
+
/** `choudoufu` or `plunge`. */
|
|
99
|
+
name: string;
|
|
100
|
+
version?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** What the read covered: a choudoufu estate or a plunge stack. */
|
|
104
|
+
export type EngineGraphScope =
|
|
105
|
+
| { kind: "estate"; estate: string }
|
|
106
|
+
| { kind: "stack"; project: string; stack: string };
|
|
107
|
+
|
|
108
|
+
/** The marker keys and encodings the engine passes into its sweep packages, printed once per document. */
|
|
109
|
+
export interface EngineMarkerScheme {
|
|
110
|
+
ownerKey: string;
|
|
111
|
+
addressKey: string;
|
|
112
|
+
/** Keys a long value continues into (`tofu-slot`, `plunge-urn-2`). */
|
|
113
|
+
continuationKeys?: string[];
|
|
114
|
+
encoding: {
|
|
115
|
+
tags: "plain" | "escaped" | "digest";
|
|
116
|
+
labels: "plain" | "escaped" | "digest";
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A node the engine could not describe, and why. */
|
|
121
|
+
export interface EngineGraphGap {
|
|
122
|
+
address: string;
|
|
123
|
+
reason: string;
|
|
124
|
+
detail?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** `meta.engineGraph` on an engine graph document. */
|
|
128
|
+
export interface EngineGraphMeta {
|
|
129
|
+
/** {@link ENGINE_GRAPH_FORMAT}. */
|
|
130
|
+
format: 1;
|
|
131
|
+
engine: EngineGraphEngine;
|
|
132
|
+
scope: EngineGraphScope;
|
|
133
|
+
/** ISO 8601 UTC, when the read started. Absent when nothing was read live. */
|
|
134
|
+
observedAt?: string;
|
|
135
|
+
markerScheme?: EngineMarkerScheme;
|
|
136
|
+
gaps?: EngineGraphGap[];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The open `meta` bag of an engine graph document, holding `engineGraph`. */
|
|
140
|
+
export interface EngineGraphDocumentMeta {
|
|
141
|
+
engineGraph: EngineGraphMeta;
|
|
142
|
+
[key: string]: unknown;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The one JSON document `choudoufu live-graph -json` and `plunge graph --json`
|
|
147
|
+
* print. A `GraphIR` at version 1: node ids are the engine's own addresses
|
|
148
|
+
* (choudoufu's instance address, plunge's URN), and readers map them with
|
|
149
|
+
* `entityKeyFor`, `observedInstanceNodeId` and `pulumiNodeKey`.
|
|
150
|
+
*/
|
|
151
|
+
export interface EngineGraphDocument extends Omit<GraphIR, "version" | "meta"> {
|
|
152
|
+
version: 1;
|
|
153
|
+
meta: EngineGraphDocumentMeta;
|
|
154
|
+
}
|
|
@@ -47,6 +47,8 @@ describe("the gated-wave bundle", () => {
|
|
|
47
47
|
"packages/core/src/gated-waves.ts",
|
|
48
48
|
"packages/core/src/intrinsic.ts",
|
|
49
49
|
"packages/core/src/lifecycle/plan-digest.ts",
|
|
50
|
+
// The next-release floor constant (#4480): no imports.
|
|
51
|
+
"packages/core/src/release-floors.ts",
|
|
50
52
|
]);
|
|
51
53
|
expect(bytes).toBeLessThan(24 * 1024);
|
|
52
54
|
});
|
|
@@ -333,3 +333,95 @@ describe("observed edges (#1271)", () => {
|
|
|
333
333
|
expect(ir.edges?.map((e) => e.from)).toEqual(["app-subnet", "web-vpc"]);
|
|
334
334
|
});
|
|
335
335
|
});
|
|
336
|
+
|
|
337
|
+
// #4488: the live graph builds instance ids with the planned graph's rule
|
|
338
|
+
// (#4486), so a live instance under an expanded module call lands on the
|
|
339
|
+
// node the declared graph lists for it.
|
|
340
|
+
describe("buildLiveGraphIr: instances under an expanded module call (#4488)", () => {
|
|
341
|
+
const block = "estate/module.app/aws_eip.pool";
|
|
342
|
+
const call = "estate/module.app";
|
|
343
|
+
const live = buildLiveGraphIr([
|
|
344
|
+
{
|
|
345
|
+
lexicon: "terraform",
|
|
346
|
+
resources: {
|
|
347
|
+
[block]: {
|
|
348
|
+
type: "resource",
|
|
349
|
+
status: "bound",
|
|
350
|
+
ownership: "owned",
|
|
351
|
+
instances: [
|
|
352
|
+
{ key: "[0]", scope: "module.app[0]", address: "module.app[0].aws_eip.pool[0]", verdict: "present", ownership: "owned", status: "bound" },
|
|
353
|
+
{ key: "[1]", scope: "module.app[1]", address: "module.app[1].aws_eip.pool[1]", verdict: "present", ownership: "owned", status: "bound" },
|
|
354
|
+
{ key: "[0]", scope: "module.app[1]", address: "module.app[1].aws_eip.pool[0]", verdict: "absent" },
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
"estate/module.app/aws_eip.single": {
|
|
358
|
+
type: "resource",
|
|
359
|
+
status: "bound",
|
|
360
|
+
ownership: "owned",
|
|
361
|
+
instances: [{ scope: "module.app[0]", address: "module.app[0].aws_eip.single", verdict: "present", ownership: "owned" }],
|
|
362
|
+
},
|
|
363
|
+
[call]: {
|
|
364
|
+
type: "module",
|
|
365
|
+
status: "bound",
|
|
366
|
+
ownership: "owned",
|
|
367
|
+
instances: [{ key: "[0]", address: "module.app[0]", verdict: "present", ownership: "owned" }],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
]);
|
|
372
|
+
const byId = new Map(live.nodes.map((n) => [n.id, n]));
|
|
373
|
+
|
|
374
|
+
it("puts the module key on the call segment and the block's own key at the end", () => {
|
|
375
|
+
const n = byId.get("estate/module.app[0]/aws_eip.pool[0]");
|
|
376
|
+
expect(n).toMatchObject({ instanceOf: block, instanceKey: "[0]", instanceScope: "module.app[0]", ownership: "owned" });
|
|
377
|
+
expect(byId.get("estate/module.app[1]/aws_eip.pool[1]")).toMatchObject({ instanceOf: block, instanceScope: "module.app[1]" });
|
|
378
|
+
expect(byId.has(`${block}[0]`)).toBe(false);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("draws no node for an absent instance", () => {
|
|
382
|
+
expect(byId.has("estate/module.app[1]/aws_eip.pool[0]")).toBe(false);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("leaves instanceKey off an instance only the module call expands", () => {
|
|
386
|
+
const n = byId.get("estate/module.app[0]/aws_eip.single")!;
|
|
387
|
+
expect(n.instanceOf).toBe("estate/module.app/aws_eip.single");
|
|
388
|
+
expect(n.instanceScope).toBe("module.app[0]");
|
|
389
|
+
expect(n.instanceKey).toBeUndefined();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("keeps the <block><key> rule with no module call expanded", () => {
|
|
393
|
+
expect(byId.get("estate/module.app[0]")).toMatchObject({ instanceOf: call, instanceKey: "[0]" });
|
|
394
|
+
expect(byId.get("estate/module.app[0]")!.instanceScope).toBeUndefined();
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("matches the declared instance node in the overlay", () => {
|
|
398
|
+
const declared: GraphIR = {
|
|
399
|
+
nodes: [
|
|
400
|
+
{ id: block, kind: "resource", lexicon: "terraform", attrs: {} },
|
|
401
|
+
{ id: "estate/module.app[0]/aws_eip.pool[0]", kind: "resource", lexicon: "terraform", attrs: {}, instanceOf: block, instanceKey: "[0]", instanceScope: "module.app[0]" },
|
|
402
|
+
{ id: "estate/module.app[1]/aws_eip.pool[0]", kind: "resource", lexicon: "terraform", attrs: {}, instanceOf: block, instanceKey: "[0]", instanceScope: "module.app[1]" },
|
|
403
|
+
],
|
|
404
|
+
edges: [],
|
|
405
|
+
groups: {},
|
|
406
|
+
};
|
|
407
|
+
const out = new Map(overlayGraphs(live, declared).nodes.map((n) => [n.id, n.attrs._status]));
|
|
408
|
+
expect(out.get("estate/module.app[0]/aws_eip.pool[0]")).toBe("good");
|
|
409
|
+
expect(out.get("estate/module.app[1]/aws_eip.pool[0]")).toBe("accent");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("keeps the member prefix in a workspace", () => {
|
|
413
|
+
const ws = buildLiveGraphIr([
|
|
414
|
+
{
|
|
415
|
+
lexicon: "terraform",
|
|
416
|
+
resources: {
|
|
417
|
+
[`net/${block}`]: {
|
|
418
|
+
type: "resource",
|
|
419
|
+
status: "bound",
|
|
420
|
+
instances: [{ key: "[2]", scope: "module.app[\"x\"]", address: "module.app[\"x\"].aws_eip.pool[2]", verdict: "present" }],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
]);
|
|
425
|
+
expect(ws.nodes.map((n) => n.id)).toContain('net/estate/module.app["x"]/aws_eip.pool[2]');
|
|
426
|
+
});
|
|
427
|
+
});
|