@intentius/chant 0.124.51 → 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 +17 -3
- 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 +224 -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/covered-round.d.ts +173 -0
- package/dist/components/covered-round.d.ts.map +1 -0
- package/dist/components/covered-round.js +200 -0
- 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 +42 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +74 -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 +7 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -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 +62 -7
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +354 -53
- 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/components/wave-plan.d.ts +2 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +4 -1
- 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 +92 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +62 -3
- 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 +76 -3
- 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 +16 -3
- 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 +238 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/covered-round.test.ts +166 -0
- package/src/components/covered-round.ts +293 -0
- 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 +101 -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 +59 -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 +728 -4
- package/src/components/pr-run.ts +395 -55
- 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/components/wave-plan.ts +5 -2
- 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 +57 -1
- package/src/pr-loop.ts +154 -6
- 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 +76 -3
- 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,634 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant import --index` (#4524, the step from tier 1 to tier 2 of
|
|
3
|
+
* INTENTIUS/behold#1025): typed source for indexed resources. Nothing is
|
|
4
|
+
* applied and nothing is written to the cloud.
|
|
5
|
+
*
|
|
6
|
+
* {@link planIndexImport} is the pure part. It takes the documents of one
|
|
7
|
+
* index read, a selection and the lexicons, and returns one import IR per
|
|
8
|
+
* output group plus the report. The caller (../cli/commands/import-index.ts)
|
|
9
|
+
* hands each IR to the lexicon's own `templateGenerator()`, the codegen
|
|
10
|
+
* `chant import <template>` uses, and writes the files.
|
|
11
|
+
*
|
|
12
|
+
* Rules:
|
|
13
|
+
* - each selected node goes to the lexicon its kind belongs to. A lexicon
|
|
14
|
+
* writes it only through its `indexImport` seam ({@link IndexImportCodegen}).
|
|
15
|
+
* A node of unknown kind, or of a lexicon with no seam, is reported as
|
|
16
|
+
* unsupported by name and written nowhere;
|
|
17
|
+
* - a node already declared in the project (the identity matcher, #4523,
|
|
18
|
+
* bound an entity to it) is reported as declared with the entity's name and
|
|
19
|
+
* is not imported again ({@link declaredNodes});
|
|
20
|
+
* - a derived edge (#4522, `origin: "derived"`) becomes a reference when its
|
|
21
|
+
* target is imported into the same group and the lexicon can spell the
|
|
22
|
+
* value as a reference to it. Otherwise the value stays a literal, and a
|
|
23
|
+
* comment in the file names the edge and why;
|
|
24
|
+
* - a value the listing masked (`[REDACTED]`) is never written. Each one
|
|
25
|
+
* becomes a parameter to fill in;
|
|
26
|
+
* - output is deterministic: nodes, edges, parameters and logical ids are
|
|
27
|
+
* ordered by id, so the same index and selection give the same IR, and the
|
|
28
|
+
* same files.
|
|
29
|
+
*
|
|
30
|
+
* The report names each resource's identity (substrate, scope id, listed
|
|
31
|
+
* type, location, native id) and cloud ids, for the adoption step that
|
|
32
|
+
* follows (INTENTIUS/choudoufu#2284).
|
|
33
|
+
*/
|
|
34
|
+
import { createHash } from "node:crypto";
|
|
35
|
+
import { MASKED } from "../deep-observation";
|
|
36
|
+
import type { IREdge, IRNode } from "../graph-ir";
|
|
37
|
+
import { readPath } from "../graph-refs";
|
|
38
|
+
import type { ParameterIR, ResourceIR, TemplateIR } from "../import/parser";
|
|
39
|
+
import { matchDeclaredToIndex, type DeclaredEntity, type IndexIdentity } from "./identity-match";
|
|
40
|
+
import type { IndexDocument } from "./index-document";
|
|
41
|
+
|
|
42
|
+
/** What to import from the index. Within one field any value matches; every field given must match. */
|
|
43
|
+
export interface IndexImportSelection {
|
|
44
|
+
/** Listed types or lexicon kinds (`AWS::S3::Bucket`). */
|
|
45
|
+
types?: readonly string[];
|
|
46
|
+
/** Native ids or node ids. */
|
|
47
|
+
names?: readonly string[];
|
|
48
|
+
/** Tags the node carries, every one with this value. */
|
|
49
|
+
tags?: Readonly<Record<string, string>>;
|
|
50
|
+
/** Node ids, a subgraph from a query (`chant search --index ... --json`). */
|
|
51
|
+
nodes?: readonly string[];
|
|
52
|
+
/** Every node in the index. */
|
|
53
|
+
all?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** An imported node as a reference target. */
|
|
57
|
+
export interface IndexImportTarget {
|
|
58
|
+
logicalId: string;
|
|
59
|
+
node: IRNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A lexicon's index import seam (a plugin's `indexImport`). Data plus pure
|
|
64
|
+
* functions that turn an indexed node into the lexicon's own import IR
|
|
65
|
+
* ({@link ResourceIR}), which its `templateGenerator()` then writes.
|
|
66
|
+
*/
|
|
67
|
+
export interface IndexImportCodegen {
|
|
68
|
+
/**
|
|
69
|
+
* The resource for one indexed node: its import IR type and the
|
|
70
|
+
* properties a declaration can hold (server-assigned fields left out), or
|
|
71
|
+
* why the node cannot be written. `attributes` are resource-level
|
|
72
|
+
* attributes under the lexicon's own keys (the aws lexicon writes
|
|
73
|
+
* `DeletionPolicy: Retain` on every imported resource, #4536).
|
|
74
|
+
*/
|
|
75
|
+
resource(node: IRNode): { type: string; properties: Record<string, unknown>; attributes?: Record<string, unknown> } | { unsupported: string };
|
|
76
|
+
/**
|
|
77
|
+
* `value`, held by another node, as a reference to `target` in this
|
|
78
|
+
* lexicon's IR, or undefined when the value is not one the lexicon can
|
|
79
|
+
* spell as a reference (it then stays a literal).
|
|
80
|
+
*/
|
|
81
|
+
reference(value: string, target: IndexImportTarget): unknown | undefined;
|
|
82
|
+
/** The parameter that stands in for a masked value. */
|
|
83
|
+
secretParameter(name: string, context: { node: IRNode; logicalId: string; path: string }): ParameterIR;
|
|
84
|
+
/** A reference to that parameter, as a property value in this lexicon's IR. */
|
|
85
|
+
parameterReference(name: string): unknown;
|
|
86
|
+
/** The type's short name for logical ids (`Bucket` for `AWS::S3::Bucket`). Absent: the kind's last segment. */
|
|
87
|
+
typeLabel?(node: IRNode): string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** What {@link planIndexImport} needs from a lexicon. A `LexiconPlugin` is one. */
|
|
91
|
+
export interface IndexImportLexicon {
|
|
92
|
+
name: string;
|
|
93
|
+
indexImport?: IndexImportCodegen;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** One reference a node holds, as the import wrote it. */
|
|
97
|
+
export interface IndexImportReference {
|
|
98
|
+
path: string;
|
|
99
|
+
target: string;
|
|
100
|
+
rule?: string;
|
|
101
|
+
written: "reference" | "literal";
|
|
102
|
+
/** Why a literal stayed a literal. */
|
|
103
|
+
reason?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** One selected resource in the report. */
|
|
107
|
+
export interface IndexImportResourceReport {
|
|
108
|
+
node: string;
|
|
109
|
+
identity: { substrate: string; scopeId: string; listedType: string; location: string; nativeId: string };
|
|
110
|
+
cloudIds: Array<{ kind: string; value: string }>;
|
|
111
|
+
/** The ownership markers the listing read off the resource, when any. */
|
|
112
|
+
markers?: unknown;
|
|
113
|
+
lexicon: string;
|
|
114
|
+
kind: string;
|
|
115
|
+
status: "imported" | "declared" | "unsupported";
|
|
116
|
+
reason?: string;
|
|
117
|
+
/** With `declared`: the entity the matcher bound to it. */
|
|
118
|
+
entity?: string;
|
|
119
|
+
/** With `imported`: the logical id and the group's directory (`""` is the output root). */
|
|
120
|
+
logicalId?: string;
|
|
121
|
+
group?: string;
|
|
122
|
+
references?: IndexImportReference[];
|
|
123
|
+
parameters?: string[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface IndexImportParameterReport {
|
|
127
|
+
name: string;
|
|
128
|
+
node: string;
|
|
129
|
+
path: string;
|
|
130
|
+
group: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The import report (`chant-index-import.json`), deterministic for one index and selection. */
|
|
134
|
+
export interface IndexImportReport {
|
|
135
|
+
report: "chant-index-import/v1";
|
|
136
|
+
index: Array<{ scopeKey: string; observedAt: string; digest: string; source: string }>;
|
|
137
|
+
selection: IndexImportSelection;
|
|
138
|
+
resources: IndexImportResourceReport[];
|
|
139
|
+
parameters: IndexImportParameterReport[];
|
|
140
|
+
groups: Array<{ group: string; lexicon: string; location: string; resources: number }>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** One output group: a lexicon and a location, written to `dir` under the output. */
|
|
144
|
+
export interface IndexImportGroup {
|
|
145
|
+
/** `""` when the import has one group, else `<lexicon>/<location>`. */
|
|
146
|
+
dir: string;
|
|
147
|
+
lexicon: string;
|
|
148
|
+
location: string;
|
|
149
|
+
ir: TemplateIR;
|
|
150
|
+
/** Comment lines (without `// `) to put at the top of the group's first file. */
|
|
151
|
+
header: string[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface IndexImportPlan {
|
|
155
|
+
groups: IndexImportGroup[];
|
|
156
|
+
report: IndexImportReport;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface PlanOptions {
|
|
160
|
+
/** Node id -> declared entity name, from {@link declaredNodes}. */
|
|
161
|
+
declared?: ReadonlyMap<string, string>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const byString = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0);
|
|
165
|
+
|
|
166
|
+
/** Locations that hold global types, as derive-edges.ts reads them. */
|
|
167
|
+
const GLOBAL_LOCATIONS: ReadonlySet<string> = new Set(["global", "cluster", ""]);
|
|
168
|
+
|
|
169
|
+
function canonicalSelection(sel: IndexImportSelection): IndexImportSelection {
|
|
170
|
+
const sortU = (xs?: readonly string[]) => (xs && xs.length > 0 ? [...new Set(xs)].sort(byString) : undefined);
|
|
171
|
+
const out: IndexImportSelection = {};
|
|
172
|
+
if (sel.all) out.all = true;
|
|
173
|
+
const types = sortU(sel.types);
|
|
174
|
+
if (types) out.types = types;
|
|
175
|
+
const names = sortU(sel.names);
|
|
176
|
+
if (names) out.names = names;
|
|
177
|
+
const nodes = sortU(sel.nodes);
|
|
178
|
+
if (nodes) out.nodes = nodes;
|
|
179
|
+
if (sel.tags && Object.keys(sel.tags).length > 0) {
|
|
180
|
+
out.tags = Object.fromEntries(Object.keys(sel.tags).sort(byString).map((k) => [k, sel.tags![k]]));
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** True when the selection names nothing: no `all` and no filter. An empty selection selects no node. */
|
|
186
|
+
export function selectionIsEmpty(sel: IndexImportSelection): boolean {
|
|
187
|
+
const c = canonicalSelection(sel);
|
|
188
|
+
return !c.all && !c.types && !c.names && !c.nodes && !c.tags;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Whether a node is selected. */
|
|
192
|
+
export function nodeSelected(node: IRNode, sel: IndexImportSelection): boolean {
|
|
193
|
+
if (selectionIsEmpty(sel)) return false;
|
|
194
|
+
if (sel.all) return true;
|
|
195
|
+
if (sel.types && sel.types.length > 0 && !sel.types.includes(node.listedType ?? node.kind) && !sel.types.includes(node.kind)) return false;
|
|
196
|
+
if (sel.names && sel.names.length > 0 && !sel.names.includes(node.nativeId ?? "") && !sel.names.includes(node.id)) return false;
|
|
197
|
+
if (sel.nodes && sel.nodes.length > 0 && !sel.nodes.includes(node.id)) return false;
|
|
198
|
+
if (sel.tags) {
|
|
199
|
+
for (const [k, v] of Object.entries(sel.tags)) if (node.tags?.[k] !== v) return false;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The indexed nodes the project already declares, keyed by node id, with
|
|
206
|
+
* the entity's name: the identity matcher (#4523) run once per location the
|
|
207
|
+
* index holds, for each lexicon that declares an `indexIdentity`. A node two
|
|
208
|
+
* entities bind keeps the first by entity name.
|
|
209
|
+
*/
|
|
210
|
+
export function declaredNodes(
|
|
211
|
+
docs: readonly IndexDocument[],
|
|
212
|
+
entitiesByLexicon: ReadonlyMap<string, readonly DeclaredEntity[]>,
|
|
213
|
+
identities: ReadonlyArray<{ name: string; indexIdentity?: IndexIdentity }>,
|
|
214
|
+
): Map<string, string> {
|
|
215
|
+
const out = new Map<string, string>();
|
|
216
|
+
if (docs.length === 0) return out;
|
|
217
|
+
const scope = docs[0].stamp.scope;
|
|
218
|
+
if (scope.kind !== "cloud") return out;
|
|
219
|
+
const locations = [...new Set(docs.flatMap((d) => d.graph.nodes.map((n) => n.location ?? "")))]
|
|
220
|
+
.filter((l) => !GLOBAL_LOCATIONS.has(l))
|
|
221
|
+
.sort(byString);
|
|
222
|
+
for (const { name, indexIdentity } of [...identities].sort((a, b) => byString(a.name, b.name))) {
|
|
223
|
+
if (!indexIdentity || indexIdentity.substrate !== scope.substrate) continue;
|
|
224
|
+
const entities = entitiesByLexicon.get(name) ?? [];
|
|
225
|
+
if (entities.length === 0) continue;
|
|
226
|
+
// A global type matches at `global` whatever the region; one pass with no region covers it.
|
|
227
|
+
for (const region of locations.length > 0 ? locations : [undefined]) {
|
|
228
|
+
const { matches } = matchDeclaredToIndex(entities, docs, indexIdentity, region ? { region } : {});
|
|
229
|
+
for (const m of Object.values(matches)) {
|
|
230
|
+
if (m.verdict === "bound" && !out.has(m.node)) out.set(m.node, m.entity);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function pascalWords(s: string): string {
|
|
238
|
+
return s
|
|
239
|
+
.split(/[^A-Za-z0-9]+/)
|
|
240
|
+
.filter(Boolean)
|
|
241
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
242
|
+
.join("");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function shortHash(s: string): string {
|
|
246
|
+
return createHash("sha256").update(s).digest("hex").slice(0, 8);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The kind's last `::`, `/` or `.` segment: `Bucket` for `AWS::S3::Bucket`. */
|
|
250
|
+
function defaultTypeLabel(node: IRNode): string {
|
|
251
|
+
const parts = node.kind.split(/::|\/|\./).filter(Boolean);
|
|
252
|
+
return pascalWords(parts[parts.length - 1] ?? "Resource") || "Resource";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* `<TypeLabel><NativeId in PascalCase>`, letters and digits only. A long id
|
|
257
|
+
* keeps its tail and a hash of the node id. Collisions take a number, in
|
|
258
|
+
* node id order, so the names are stable.
|
|
259
|
+
*/
|
|
260
|
+
function assignLogicalIds(nodes: readonly IRNode[], label: (n: IRNode) => string): Map<string, string> {
|
|
261
|
+
const out = new Map<string, string>();
|
|
262
|
+
const used = new Set<string>();
|
|
263
|
+
for (const n of [...nodes].sort((a, b) => byString(a.id, b.id))) {
|
|
264
|
+
const native = n.nativeId ?? n.id;
|
|
265
|
+
// An ARN or a URL: its last path segment names the object.
|
|
266
|
+
const tail = native.includes("/") ? native.slice(native.lastIndexOf("/") + 1) || native : native;
|
|
267
|
+
const prefix = label(n);
|
|
268
|
+
let words = tail.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
269
|
+
// `vpc-0a1` under `VPC` reads `VPC0a1`, not `VPCVpc0a1`.
|
|
270
|
+
if (words.length > 1 && words[0].toLowerCase() === prefix.toLowerCase()) words = words.slice(1);
|
|
271
|
+
let id = pascalWords(words.join("-"));
|
|
272
|
+
if (id.length > 48) id = `${id.slice(-40)}${shortHash(n.id)}`;
|
|
273
|
+
let base = `${prefix}${id}`;
|
|
274
|
+
if (!/^[A-Za-z]/.test(base)) base = `R${base}`;
|
|
275
|
+
let name = base;
|
|
276
|
+
for (let i = 2; used.has(name); i++) name = `${base}${i}`;
|
|
277
|
+
used.add(name);
|
|
278
|
+
out.set(n.id, name);
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** The path a derived edge's rule names on its holder, or undefined (identity-match and tag rules name none). */
|
|
284
|
+
export function edgeRulePath(edge: IREdge, holder: IRNode): string | undefined {
|
|
285
|
+
const rule = edge.rule;
|
|
286
|
+
if (!rule) return undefined;
|
|
287
|
+
const prefix = `${holder.lexicon}:${holder.kind}:`;
|
|
288
|
+
if (!rule.startsWith(prefix)) return undefined;
|
|
289
|
+
const path = rule.slice(prefix.length);
|
|
290
|
+
return path.startsWith("tag:") ? undefined : path;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Rewrite the string leaves at `path` (`a.b`, `arr[].id`, as readPath reads
|
|
295
|
+
* it). A path that ends on an array rewrites its string elements. Returns
|
|
296
|
+
* the new tree and the dotted paths it changed.
|
|
297
|
+
*/
|
|
298
|
+
function rewriteAtPath(
|
|
299
|
+
tree: Record<string, unknown>,
|
|
300
|
+
path: string,
|
|
301
|
+
fn: (value: string) => unknown | undefined,
|
|
302
|
+
): { tree: Record<string, unknown>; changed: string[] } {
|
|
303
|
+
const changed: string[] = [];
|
|
304
|
+
const parts = path.split(".");
|
|
305
|
+
const walk = (value: unknown, i: number, at: string): unknown => {
|
|
306
|
+
if (i === parts.length) {
|
|
307
|
+
if (typeof value === "string") {
|
|
308
|
+
const r = fn(value);
|
|
309
|
+
if (r !== undefined) {
|
|
310
|
+
changed.push(at);
|
|
311
|
+
return r;
|
|
312
|
+
}
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
315
|
+
if (Array.isArray(value)) return value.map((v, j) => walk(v, i, `${at}[${j}]`));
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
|
|
319
|
+
const part = parts[i];
|
|
320
|
+
const isArr = part.endsWith("[]");
|
|
321
|
+
const key = isArr ? part.slice(0, -2) : part;
|
|
322
|
+
const obj = value as Record<string, unknown>;
|
|
323
|
+
if (!(key in obj)) return value;
|
|
324
|
+
const here = at === "" ? key : `${at}.${key}`;
|
|
325
|
+
const child = obj[key];
|
|
326
|
+
let next: unknown;
|
|
327
|
+
if (isArr) {
|
|
328
|
+
if (!Array.isArray(child)) return value;
|
|
329
|
+
next = child.map((c, j) => walk(c, i + 1, `${here}[${j}]`));
|
|
330
|
+
} else {
|
|
331
|
+
next = walk(child, i + 1, here);
|
|
332
|
+
}
|
|
333
|
+
return next === child ? value : { ...obj, [key]: next };
|
|
334
|
+
};
|
|
335
|
+
return { tree: walk(tree, 0, "") as Record<string, unknown>, changed };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Every string leaf, outside `Tags`, as a dotted path (identity-match edges name no path). */
|
|
339
|
+
function stringLeafPaths(tree: unknown, at = ""): string[] {
|
|
340
|
+
if (typeof tree === "string") return [at];
|
|
341
|
+
if (Array.isArray(tree)) return tree.flatMap((v, j) => stringLeafPaths(v, `${at}[${j}]`));
|
|
342
|
+
if (tree === null || typeof tree !== "object") return [];
|
|
343
|
+
return Object.keys(tree as Record<string, unknown>)
|
|
344
|
+
.sort(byString)
|
|
345
|
+
.filter((k) => !(at === "" && (k === "Tags" || k === "tags")))
|
|
346
|
+
.flatMap((k) => stringLeafPaths((tree as Record<string, unknown>)[k], at === "" ? k : `${at}.${k}`));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** A path with explicit indices (`a[2].b`) as readPath/rewriteAtPath segments over one leaf. */
|
|
350
|
+
function exactPathParts(path: string): Array<string | number> {
|
|
351
|
+
const out: Array<string | number> = [];
|
|
352
|
+
for (const seg of path.split(".")) {
|
|
353
|
+
const m = /^([^[]*)((?:\[\d+\])*)$/.exec(seg);
|
|
354
|
+
if (!m) {
|
|
355
|
+
out.push(seg);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (m[1] !== "") out.push(m[1]);
|
|
359
|
+
for (const idx of m[2].matchAll(/\[(\d+)\]/g)) out.push(Number(idx[1]));
|
|
360
|
+
}
|
|
361
|
+
return out;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function setExact(tree: unknown, parts: ReadonlyArray<string | number>, value: unknown): unknown {
|
|
365
|
+
if (parts.length === 0) return value;
|
|
366
|
+
const [head, ...rest] = parts;
|
|
367
|
+
if (typeof head === "number") {
|
|
368
|
+
if (!Array.isArray(tree)) return tree;
|
|
369
|
+
const copy = [...tree];
|
|
370
|
+
copy[head] = setExact(copy[head], rest, value);
|
|
371
|
+
return copy;
|
|
372
|
+
}
|
|
373
|
+
if (tree === null || typeof tree !== "object" || Array.isArray(tree)) return tree;
|
|
374
|
+
const obj = tree as Record<string, unknown>;
|
|
375
|
+
return { ...obj, [head]: setExact(obj[head], rest, value) };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function getExact(tree: unknown, parts: ReadonlyArray<string | number>): unknown {
|
|
379
|
+
let cur = tree;
|
|
380
|
+
for (const p of parts) {
|
|
381
|
+
if (cur === null || typeof cur !== "object") return undefined;
|
|
382
|
+
cur = (cur as Record<string | number, unknown>)[p];
|
|
383
|
+
}
|
|
384
|
+
return cur;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Every masked leaf, as a dotted path with indices, in key order. */
|
|
388
|
+
function maskedPaths(tree: unknown, at = ""): string[] {
|
|
389
|
+
if (tree === MASKED) return [at];
|
|
390
|
+
if (Array.isArray(tree)) return tree.flatMap((v, j) => maskedPaths(v, `${at}[${j}]`));
|
|
391
|
+
if (tree === null || typeof tree !== "object") return [];
|
|
392
|
+
return Object.keys(tree as Record<string, unknown>)
|
|
393
|
+
.sort(byString)
|
|
394
|
+
.flatMap((k) => maskedPaths((tree as Record<string, unknown>)[k], at === "" ? k : `${at}.${k}`));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function identityOf(doc: IndexDocument, node: IRNode): IndexImportResourceReport["identity"] {
|
|
398
|
+
const scope = doc.stamp.scope;
|
|
399
|
+
return {
|
|
400
|
+
substrate: scope.kind === "cloud" ? scope.substrate : node.lexicon,
|
|
401
|
+
scopeId: scope.kind === "cloud" ? scope.scopeId : doc.stamp.scopeKey,
|
|
402
|
+
listedType: node.listedType ?? node.kind,
|
|
403
|
+
location: node.location ?? "",
|
|
404
|
+
nativeId: node.nativeId ?? node.physicalId ?? node.id,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Plan an import from the documents of one index read. Pure; throws only on
|
|
410
|
+
* documents from two accounts or two reads, as derive-edges.ts does.
|
|
411
|
+
*/
|
|
412
|
+
export function planIndexImport(
|
|
413
|
+
docs: readonly IndexDocument[],
|
|
414
|
+
selection: IndexImportSelection,
|
|
415
|
+
lexicons: readonly IndexImportLexicon[],
|
|
416
|
+
options: PlanOptions = {},
|
|
417
|
+
): IndexImportPlan {
|
|
418
|
+
const first = docs[0]?.stamp;
|
|
419
|
+
for (const d of docs) {
|
|
420
|
+
if (first && (d.stamp.observedAt !== first.observedAt || JSON.stringify(scopeAccount(d)) !== JSON.stringify(scopeAccount(docs[0])))) {
|
|
421
|
+
throw new Error(`an index import reads one read of one account: ${first.scopeKey} and ${d.stamp.scopeKey} differ`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const sel = canonicalSelection(selection);
|
|
425
|
+
const declared = options.declared ?? new Map<string, string>();
|
|
426
|
+
const byLexicon = new Map(lexicons.map((l) => [l.name, l]));
|
|
427
|
+
|
|
428
|
+
const docOf = new Map<string, IndexDocument>();
|
|
429
|
+
const nodes: IRNode[] = [];
|
|
430
|
+
for (const d of docs) {
|
|
431
|
+
for (const n of d.graph.nodes) {
|
|
432
|
+
if (docOf.has(n.id)) continue;
|
|
433
|
+
docOf.set(n.id, d);
|
|
434
|
+
nodes.push(n);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
nodes.sort((a, b) => byString(a.id, b.id));
|
|
438
|
+
const nodeById = new Map(nodes.map((n) => [n.id, n]));
|
|
439
|
+
const edges = docs
|
|
440
|
+
.flatMap((d) => d.graph.edges)
|
|
441
|
+
.filter((e) => e.origin === "derived")
|
|
442
|
+
.sort((a, b) => byString(`${a.from}\u0000${a.rule ?? ""}\u0000${a.to}`, `${b.from}\u0000${b.rule ?? ""}\u0000${b.to}`));
|
|
443
|
+
|
|
444
|
+
const reports = new Map<string, IndexImportResourceReport>();
|
|
445
|
+
const writable: Array<{ node: IRNode; codegen: IndexImportCodegen; type: string; properties: Record<string, unknown>; attributes?: Record<string, unknown> }> = [];
|
|
446
|
+
for (const node of nodes) {
|
|
447
|
+
if (!nodeSelected(node, sel)) continue;
|
|
448
|
+
const doc = docOf.get(node.id)!;
|
|
449
|
+
const base: IndexImportResourceReport = {
|
|
450
|
+
node: node.id,
|
|
451
|
+
identity: identityOf(doc, node),
|
|
452
|
+
cloudIds: (node.facts?.cloudIds ?? []).map((c) => ({ kind: c.kind, value: c.value })),
|
|
453
|
+
...(node.facts?.markers ? { markers: node.facts.markers } : {}),
|
|
454
|
+
lexicon: node.lexicon,
|
|
455
|
+
kind: node.kind,
|
|
456
|
+
status: "unsupported",
|
|
457
|
+
};
|
|
458
|
+
const entity = declared.get(node.id);
|
|
459
|
+
if (entity !== undefined) {
|
|
460
|
+
reports.set(node.id, { ...base, status: "declared", entity, reason: `already declared as ${entity}; not imported again` });
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (node.unmappedKind) {
|
|
464
|
+
reports.set(node.id, { ...base, reason: `no lexicon kind maps the listed type ${node.listedType ?? node.kind}` });
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const lexicon = byLexicon.get(node.lexicon);
|
|
468
|
+
if (!lexicon) {
|
|
469
|
+
reports.set(node.id, { ...base, reason: `the ${node.lexicon} lexicon is not loaded` });
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (!lexicon.indexImport) {
|
|
473
|
+
reports.set(node.id, { ...base, reason: `the ${node.lexicon} lexicon has no index import codegen (indexImport)` });
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const r = lexicon.indexImport.resource(node);
|
|
477
|
+
if ("unsupported" in r) {
|
|
478
|
+
reports.set(node.id, { ...base, reason: r.unsupported });
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
reports.set(node.id, { ...base, status: "imported" });
|
|
482
|
+
writable.push({ node, codegen: lexicon.indexImport, type: r.type, properties: r.properties, ...(r.attributes ? { attributes: r.attributes } : {}) });
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Groups: lexicon + location. Global nodes join their lexicon's only regional group, when there is one.
|
|
486
|
+
const regional = new Map<string, Set<string>>();
|
|
487
|
+
for (const w of writable) {
|
|
488
|
+
const loc = w.node.location ?? "";
|
|
489
|
+
if (!GLOBAL_LOCATIONS.has(loc)) (regional.get(w.node.lexicon) ?? regional.set(w.node.lexicon, new Set()).get(w.node.lexicon)!).add(loc);
|
|
490
|
+
}
|
|
491
|
+
const groupLocation = (n: IRNode): string => {
|
|
492
|
+
const loc = n.location ?? "";
|
|
493
|
+
if (!GLOBAL_LOCATIONS.has(loc)) return loc;
|
|
494
|
+
const regions = regional.get(n.lexicon);
|
|
495
|
+
return regions && regions.size === 1 ? [...regions][0] : loc === "" ? "global" : loc;
|
|
496
|
+
};
|
|
497
|
+
const groupKeys = [...new Set(writable.map((w) => `${w.node.lexicon}/${groupLocation(w.node)}`))].sort(byString);
|
|
498
|
+
const single = groupKeys.length === 1;
|
|
499
|
+
const groupDir = (n: IRNode) => (single ? "" : `${n.lexicon}/${groupLocation(n)}`);
|
|
500
|
+
|
|
501
|
+
const logicalIds = new Map<string, string>();
|
|
502
|
+
for (const key of groupKeys) {
|
|
503
|
+
const members = writable.filter((w) => `${w.node.lexicon}/${groupLocation(w.node)}` === key);
|
|
504
|
+
const codegen = members[0].codegen;
|
|
505
|
+
const label = (n: IRNode) => codegen.typeLabel?.(n) ?? defaultTypeLabel(n);
|
|
506
|
+
for (const [id, name] of assignLogicalIds(members.map((m) => m.node), label)) logicalIds.set(id, name);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const groups: IndexImportGroup[] = [];
|
|
510
|
+
const parameterReports: IndexImportParameterReport[] = [];
|
|
511
|
+
for (const key of groupKeys) {
|
|
512
|
+
const members = writable.filter((w) => `${w.node.lexicon}/${groupLocation(w.node)}` === key);
|
|
513
|
+
const dir = groupDir(members[0].node);
|
|
514
|
+
const header: string[] = [];
|
|
515
|
+
const resources: ResourceIR[] = [];
|
|
516
|
+
const parameters: ParameterIR[] = [];
|
|
517
|
+
const usedParams = new Set<string>();
|
|
518
|
+
for (const w of members) {
|
|
519
|
+
const logicalId = logicalIds.get(w.node.id)!;
|
|
520
|
+
const report = reports.get(w.node.id)!;
|
|
521
|
+
report.logicalId = logicalId;
|
|
522
|
+
report.group = dir;
|
|
523
|
+
let props = w.properties;
|
|
524
|
+
const references: IndexImportReference[] = [];
|
|
525
|
+
|
|
526
|
+
for (const e of edges.filter((x) => x.from === w.node.id)) {
|
|
527
|
+
const target = nodeById.get(e.to);
|
|
528
|
+
const path = edgeRulePath(e, w.node);
|
|
529
|
+
const literal = (reason: string) => {
|
|
530
|
+
const at = path ?? e.viaAttr ?? "";
|
|
531
|
+
references.push({ path: at, target: e.to, ...(e.rule ? { rule: e.rule } : {}), written: "literal", reason });
|
|
532
|
+
const values = path ? readPath(props, path) : [];
|
|
533
|
+
header.push(
|
|
534
|
+
`${logicalId}.${at}${values.length > 0 ? ` = ${values.map((v) => JSON.stringify(v)).join(", ")}` : ""} stays a literal: ` +
|
|
535
|
+
`derived edge ${e.rule ?? "(no rule)"} to ${e.to}; ${reason}`,
|
|
536
|
+
);
|
|
537
|
+
};
|
|
538
|
+
if (e.rule && edgeRulePath(e, w.node) === undefined && e.rule.startsWith(`${w.node.lexicon}:${w.node.kind}:tag:`)) {
|
|
539
|
+
literal("a tag rule names no property to reference");
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (!target) {
|
|
543
|
+
literal("the target is not in the index");
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const targetDeclared = declared.get(target.id);
|
|
547
|
+
if (targetDeclared !== undefined) {
|
|
548
|
+
literal(`the target is already declared as ${targetDeclared}`);
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const targetReport = reports.get(target.id);
|
|
552
|
+
if (!targetReport) {
|
|
553
|
+
literal("the target is not in the selection");
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (targetReport.status !== "imported") {
|
|
557
|
+
literal(`the target is not imported (${targetReport.reason ?? targetReport.status})`);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (groupDir(target) !== dir || target.lexicon !== w.node.lexicon) {
|
|
561
|
+
literal(`the target is written to another group (${groupDir(target) || "."})`);
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
const t: IndexImportTarget = { logicalId: logicalIds.get(target.id)!, node: target };
|
|
565
|
+
const changed: string[] = [];
|
|
566
|
+
if (path) {
|
|
567
|
+
const r = rewriteAtPath(props, path, (v) => w.codegen.reference(v, t));
|
|
568
|
+
props = r.tree;
|
|
569
|
+
changed.push(...r.changed);
|
|
570
|
+
} else {
|
|
571
|
+
for (const leaf of stringLeafPaths(props)) {
|
|
572
|
+
const parts = exactPathParts(leaf);
|
|
573
|
+
const v = getExact(props, parts);
|
|
574
|
+
if (typeof v !== "string") continue;
|
|
575
|
+
const ref = w.codegen.reference(v, t);
|
|
576
|
+
if (ref === undefined) continue;
|
|
577
|
+
props = setExact(props, parts, ref) as Record<string, unknown>;
|
|
578
|
+
changed.push(leaf);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (changed.length === 0) {
|
|
582
|
+
literal(`the ${w.node.lexicon} lexicon cannot spell the value as a reference to ${t.logicalId}`);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
for (const at of changed) references.push({ path: at, target: e.to, ...(e.rule ? { rule: e.rule } : {}), written: "reference" });
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const params: string[] = [];
|
|
589
|
+
for (const at of maskedPaths(props)) {
|
|
590
|
+
let name = `${logicalId}${pascalWords(at)}`;
|
|
591
|
+
for (let i = 2; usedParams.has(name); i++) name = `${logicalId}${pascalWords(at)}${i}`;
|
|
592
|
+
usedParams.add(name);
|
|
593
|
+
parameters.push(w.codegen.secretParameter(name, { node: w.node, logicalId, path: at }));
|
|
594
|
+
props = setExact(props, exactPathParts(at), w.codegen.parameterReference(name)) as Record<string, unknown>;
|
|
595
|
+
params.push(name);
|
|
596
|
+
parameterReports.push({ name, node: w.node.id, path: at, group: dir });
|
|
597
|
+
header.push(`${logicalId}.${at} was masked in the index; fill in the parameter ${name}`);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (references.length > 0) report.references = references;
|
|
601
|
+
if (params.length > 0) report.parameters = params;
|
|
602
|
+
resources.push({ logicalId, type: w.type, properties: props, ...(w.attributes && Object.keys(w.attributes).length > 0 ? { attributes: w.attributes } : {}) });
|
|
603
|
+
}
|
|
604
|
+
const [lexicon, ...loc] = key.split("/");
|
|
605
|
+
groups.push({ dir, lexicon, location: loc.join("/"), ir: { resources, parameters }, header });
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const report: IndexImportReport = {
|
|
609
|
+
report: "chant-index-import/v1",
|
|
610
|
+
index: [...docs]
|
|
611
|
+
.map((d) => ({ scopeKey: d.stamp.scopeKey, observedAt: d.stamp.observedAt, digest: d.stamp.digest, source: d.stamp.source.name }))
|
|
612
|
+
.sort((a, b) => byString(a.scopeKey, b.scopeKey)),
|
|
613
|
+
selection: sel,
|
|
614
|
+
resources: [...reports.values()].sort((a, b) => byString(a.node, b.node)),
|
|
615
|
+
parameters: parameterReports,
|
|
616
|
+
groups: groups.map((g) => ({ group: g.dir, lexicon: g.lexicon, location: g.location, resources: g.ir.resources.length })),
|
|
617
|
+
};
|
|
618
|
+
return { groups, report };
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function scopeAccount(d: IndexDocument): unknown {
|
|
622
|
+
const s = d.stamp.scope;
|
|
623
|
+
return s.kind === "cloud" ? [s.substrate, s.scopeId] : [s.kind, d.stamp.scopeKey];
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** The header comment for a group's first file. Same plan, same text. */
|
|
627
|
+
export function indexImportHeader(plan: IndexImportPlan, group: IndexImportGroup): string {
|
|
628
|
+
const lines = [
|
|
629
|
+
"Generated by chant import --index. Nothing was applied. Review before committing.",
|
|
630
|
+
...plan.report.index.map((i) => `index ${i.scopeKey} read ${i.observedAt} (digest ${i.digest.slice(0, 12)})`),
|
|
631
|
+
...group.header,
|
|
632
|
+
];
|
|
633
|
+
return lines.map((l) => `// ${l.replace(/\r?\n/g, " ")}`).join("\n") + "\n\n";
|
|
634
|
+
}
|