@intentius/chant 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +45 -5
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/proof.d.ts +3 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +4 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts +3 -0
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +8 -2
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +39 -0
- package/dist/cli/handlers/components.d.ts.map +1 -0
- package/dist/cli/handlers/dev.d.ts +3 -0
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts +4 -2
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -0
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +35 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +22 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.d.ts +110 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -0
- package/dist/codegen/pinned-upgrade.d.ts +153 -0
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
- package/dist/codegen/rolling-upgrade.d.ts +138 -0
- package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
- package/dist/codegen/surface-snapshot.d.ts +173 -0
- package/dist/codegen/surface-snapshot.d.ts.map +1 -0
- package/dist/codegen/version-bump.d.ts +21 -0
- package/dist/codegen/version-bump.d.ts.map +1 -0
- package/dist/components/auto-release.d.ts +93 -0
- package/dist/components/auto-release.d.ts.map +1 -0
- package/dist/components/builders.d.ts +37 -0
- package/dist/components/builders.d.ts.map +1 -0
- package/dist/components/capability-plugin-loader.d.ts +111 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -0
- package/dist/components/capability-plugin-schema.d.ts +24 -0
- package/dist/components/capability-plugin-schema.d.ts.map +1 -0
- package/dist/components/capability-plugin.d.ts +85 -0
- package/dist/components/capability-plugin.d.ts.map +1 -0
- package/dist/components/capability.d.ts +89 -0
- package/dist/components/capability.d.ts.map +1 -0
- package/dist/components/cli-support.d.ts +204 -0
- package/dist/components/cli-support.d.ts.map +1 -0
- package/dist/components/component.d.ts +179 -0
- package/dist/components/component.d.ts.map +1 -0
- package/dist/components/config-defaults.d.ts +41 -0
- package/dist/components/config-defaults.d.ts.map +1 -0
- package/dist/components/discover.d.ts +60 -0
- package/dist/components/discover.d.ts.map +1 -0
- package/dist/components/driver-output.d.ts +20 -0
- package/dist/components/driver-output.d.ts.map +1 -0
- package/dist/components/driver.d.ts +209 -0
- package/dist/components/driver.d.ts.map +1 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/manifest-persistence.d.ts +79 -0
- package/dist/components/manifest-persistence.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
- package/dist/components/pilots/lambda.pilot.d.ts +35 -0
- package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
- package/dist/components/pilots/project.d.ts +13 -0
- package/dist/components/pilots/project.d.ts.map +1 -0
- package/dist/components/presets/ecs-fargate.d.ts +59 -0
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
- package/dist/components/presets/index.d.ts +29 -0
- package/dist/components/presets/index.d.ts.map +1 -0
- package/dist/components/presets/lambda.d.ts +35 -0
- package/dist/components/presets/lambda.d.ts.map +1 -0
- package/dist/components/presets/single-host-compose.d.ts +43 -0
- package/dist/components/presets/single-host-compose.d.ts.map +1 -0
- package/dist/components/registry.d.ts +29 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/starter-plugin.d.ts +51 -0
- package/dist/components/starter-plugin.d.ts.map +1 -0
- package/dist/components/temporal-codegen-loader.d.ts +27 -0
- package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/bom-writer.d.ts +155 -0
- package/dist/components/verbs/bom-writer.d.ts.map +1 -0
- package/dist/components/verbs/build-archive.d.ts +205 -0
- package/dist/components/verbs/build-archive.d.ts.map +1 -0
- package/dist/components/verbs/build.d.ts +150 -0
- package/dist/components/verbs/build.d.ts.map +1 -0
- package/dist/components/verbs/cloud-executor.d.ts +88 -0
- package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/component-bom.d.ts +103 -0
- package/dist/components/verbs/component-bom.d.ts.map +1 -0
- package/dist/components/verbs/index.d.ts +49 -0
- package/dist/components/verbs/index.d.ts.map +1 -0
- package/dist/components/verbs/license-policy.d.ts +56 -0
- package/dist/components/verbs/license-policy.d.ts.map +1 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/process-runner.d.ts +64 -0
- package/dist/components/verbs/process-runner.d.ts.map +1 -0
- package/dist/components/verbs/reproducibility.d.ts +80 -0
- package/dist/components/verbs/reproducibility.d.ts.map +1 -0
- package/dist/components/verbs/sbom-generator.d.ts +157 -0
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/sbom.d.ts +79 -0
- package/dist/components/verbs/sbom.d.ts.map +1 -0
- package/dist/components/verbs/shell.d.ts +35 -0
- package/dist/components/verbs/shell.d.ts.map +1 -0
- package/dist/components/verbs/sign.d.ts +222 -0
- package/dist/components/verbs/sign.d.ts.map +1 -0
- package/dist/components/verbs/stub.d.ts +18 -0
- package/dist/components/verbs/stub.d.ts.map +1 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/verify.d.ts +106 -0
- package/dist/components/verbs/verify.d.ts.map +1 -0
- package/dist/components/verbs/vex.d.ts +65 -0
- package/dist/components/verbs/vex.d.ts.map +1 -0
- package/dist/components/verbs/vuln-gate.d.ts +91 -0
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
- package/dist/components/verbs/vuln-scan.d.ts +116 -0
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
- package/dist/components/verbs/wait-verify.d.ts +85 -0
- package/dist/components/verbs/wait-verify.d.ts.map +1 -0
- package/dist/config.d.ts +205 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +78 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts +121 -0
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
- package/dist/lifecycle/build-ledger.d.ts +225 -0
- package/dist/lifecycle/build-ledger.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +84 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +5 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
- package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +122 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -0
- package/dist/lifecycle/status.d.ts +150 -0
- package/dist/lifecycle/status.d.ts.map +1 -0
- package/dist/lint/component-checks.d.ts +95 -0
- package/dist/lint/component-checks.d.ts.map +1 -0
- package/dist/lint/engine.d.ts +23 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/presets/strict.json +8 -1
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +18 -0
- package/dist/lint/rules/comp/index.d.ts.map +1 -0
- package/dist/lint/rules/comp/support.d.ts +71 -0
- package/dist/lint/rules/comp/support.d.ts.map +1 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts +11 -6
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +24 -0
- package/dist/op/activity-runtime.d.ts.map +1 -0
- package/dist/op/builders.d.ts +56 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/ownership.d.ts +20 -14
- package/dist/ownership.d.ts.map +1 -1
- package/package.json +14 -1
- package/src/audit/catalog.test.ts +74 -22
- package/src/audit/catalog.ts +74 -471
- package/src/audit/proof.ts +4 -2
- package/src/audit/report-html.test.ts +14 -7
- package/src/audit/report-model.ts +20 -9
- package/src/audit/report.test.ts +36 -13
- package/src/audit/report.ts +2 -0
- package/src/audit/rules-doc.test.ts +8 -6
- package/src/audit/rules-doc.ts +13 -5
- package/src/cli/commands/audit.ts +23 -18
- package/src/cli/commands/component-lint.test.ts +158 -0
- package/src/cli/commands/doctor.test.ts +116 -2
- package/src/cli/commands/doctor.ts +46 -0
- package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
- package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
- package/src/cli/commands/lexicon-surface-diff.ts +185 -0
- package/src/cli/commands/lint.ts +142 -1
- package/src/cli/commands/pinned-upgrade.ts +204 -0
- package/src/cli/handlers/build.ts +58 -1
- package/src/cli/handlers/components.test.ts +504 -0
- package/src/cli/handlers/components.ts +404 -0
- package/src/cli/handlers/dev.ts +60 -1
- package/src/cli/handlers/graph.ts +47 -2
- package/src/cli/handlers/misc.ts +45 -1
- package/src/cli/handlers/run.test.ts +967 -3
- package/src/cli/handlers/run.ts +618 -11
- package/src/cli/main.test.ts +66 -0
- package/src/cli/main.ts +105 -2
- package/src/cli/registry.ts +35 -0
- package/src/codegen/fetch.test.ts +84 -3
- package/src/codegen/fetch.ts +76 -2
- package/src/codegen/lexicon-regen.test.ts +298 -0
- package/src/codegen/lexicon-regen.ts +545 -0
- package/src/codegen/pinned-upgrade.test.ts +399 -0
- package/src/codegen/pinned-upgrade.ts +423 -0
- package/src/codegen/rolling-upgrade.test.ts +333 -0
- package/src/codegen/rolling-upgrade.ts +320 -0
- package/src/codegen/surface-snapshot.test.ts +461 -0
- package/src/codegen/surface-snapshot.ts +546 -0
- package/src/codegen/version-bump.test.ts +47 -0
- package/src/codegen/version-bump.ts +39 -0
- package/src/components/README.md +130 -0
- package/src/components/SPRAWL-VALIDATION.md +181 -0
- package/src/components/__fixtures__/alb-ecs-service.json +51 -0
- package/src/components/__fixtures__/dynamodb-infra.json +32 -0
- package/src/components/__fixtures__/emr-job-consumer.json +26 -0
- package/src/components/__fixtures__/jar-lib-producer.json +16 -0
- package/src/components/__fixtures__/lambda-image-processor.json +28 -0
- package/src/components/__fixtures__/neo4j-fanout.json +34 -0
- package/src/components/__fixtures__/single-host-compose.json +29 -0
- package/src/components/auto-release.test.ts +217 -0
- package/src/components/auto-release.ts +216 -0
- package/src/components/builders.test.ts +45 -0
- package/src/components/builders.ts +71 -0
- package/src/components/capability-plugin-loader.ts +218 -0
- package/src/components/capability-plugin-schema.ts +56 -0
- package/src/components/capability-plugin.test.ts +235 -0
- package/src/components/capability-plugin.ts +108 -0
- package/src/components/capability.ts +117 -0
- package/src/components/cli-support.test.ts +579 -0
- package/src/components/cli-support.ts +481 -0
- package/src/components/component-schema.test.ts +265 -0
- package/src/components/component.schema.json +235 -0
- package/src/components/component.test.ts +251 -0
- package/src/components/component.ts +255 -0
- package/src/components/config-defaults.test.ts +263 -0
- package/src/components/config-defaults.ts +145 -0
- package/src/components/discover.test.ts +352 -0
- package/src/components/discover.ts +167 -0
- package/src/components/driver-output.ts +71 -0
- package/src/components/driver.test.ts +638 -0
- package/src/components/driver.ts +690 -0
- package/src/components/index.ts +113 -0
- package/src/components/manifest-persistence.test.ts +152 -0
- package/src/components/manifest-persistence.ts +164 -0
- package/src/components/pilots/README.md +33 -0
- package/src/components/pilots/alb-ecs.pilot.ts +63 -0
- package/src/components/pilots/dynamodb.pilot.ts +59 -0
- package/src/components/pilots/jar-emr.pilot.ts +69 -0
- package/src/components/pilots/lambda.pilot.ts +55 -0
- package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
- package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
- package/src/components/pilots/pilots.test.ts +126 -0
- package/src/components/pilots/project.ts +13 -0
- package/src/components/presets/comp007-reduction.test.ts +122 -0
- package/src/components/presets/ecs-fargate.test.ts +84 -0
- package/src/components/presets/ecs-fargate.ts +103 -0
- package/src/components/presets/index.ts +32 -0
- package/src/components/presets/lambda.test.ts +35 -0
- package/src/components/presets/lambda.ts +53 -0
- package/src/components/presets/single-host-compose.test.ts +65 -0
- package/src/components/presets/single-host-compose.ts +64 -0
- package/src/components/registry.test.ts +112 -0
- package/src/components/registry.ts +37 -0
- package/src/components/starter-plugin.ts +105 -0
- package/src/components/temporal-codegen-loader.ts +41 -0
- package/src/components/verbs/__fixtures__/package-lock.json +45 -0
- package/src/components/verbs/__fixtures__/pom.xml +37 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
- package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
- package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
- package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
- package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
- package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
- package/src/components/verbs/bom-writer.test.ts +331 -0
- package/src/components/verbs/bom-writer.ts +386 -0
- package/src/components/verbs/build-archive.test.ts +251 -0
- package/src/components/verbs/build-archive.ts +296 -0
- package/src/components/verbs/build.test.ts +222 -0
- package/src/components/verbs/build.ts +252 -0
- package/src/components/verbs/cloud-executor.ts +181 -0
- package/src/components/verbs/component-bom.test.ts +219 -0
- package/src/components/verbs/component-bom.ts +189 -0
- package/src/components/verbs/index.ts +49 -0
- package/src/components/verbs/license-policy.ts +115 -0
- package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
- package/src/components/verbs/process-runner.test.ts +79 -0
- package/src/components/verbs/process-runner.ts +113 -0
- package/src/components/verbs/reproducibility.test.ts +24 -0
- package/src/components/verbs/reproducibility.ts +100 -0
- package/src/components/verbs/sbom-generator.test.ts +96 -0
- package/src/components/verbs/sbom-generator.ts +209 -0
- package/src/components/verbs/sbom.test.ts +184 -0
- package/src/components/verbs/sbom.ts +152 -0
- package/src/components/verbs/shell.test.ts +36 -0
- package/src/components/verbs/shell.ts +51 -0
- package/src/components/verbs/sign.test.ts +210 -0
- package/src/components/verbs/sign.ts +350 -0
- package/src/components/verbs/stub.ts +34 -0
- package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
- package/src/components/verbs/tool-sbom-generator.ts +192 -0
- package/src/components/verbs/verify.test.ts +159 -0
- package/src/components/verbs/verify.ts +179 -0
- package/src/components/verbs/vex.ts +182 -0
- package/src/components/verbs/vuln-gate.test.ts +201 -0
- package/src/components/verbs/vuln-gate.ts +170 -0
- package/src/components/verbs/vuln-scan.test.ts +145 -0
- package/src/components/verbs/vuln-scan.ts +266 -0
- package/src/components/verbs/wait-verify.test.ts +111 -0
- package/src/components/verbs/wait-verify.ts +171 -0
- package/src/config.test.ts +74 -1
- package/src/config.ts +219 -0
- package/src/index.ts +32 -0
- package/src/lexicon.ts +88 -0
- package/src/lifecycle/build-ledger-store.test.ts +217 -0
- package/src/lifecycle/build-ledger-store.ts +165 -0
- package/src/lifecycle/build-ledger.test.ts +349 -0
- package/src/lifecycle/build-ledger.ts +305 -0
- package/src/lifecycle/git.test.ts +118 -0
- package/src/lifecycle/git.ts +149 -14
- package/src/lifecycle/index.ts +5 -0
- package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
- package/src/lifecycle/oras-referrer-lookup.ts +110 -0
- package/src/lifecycle/release-ledger.test.ts +209 -0
- package/src/lifecycle/release-ledger.ts +185 -0
- package/src/lifecycle/status.test.ts +330 -0
- package/src/lifecycle/status.ts +278 -0
- package/src/lint/component-checks.ts +148 -0
- package/src/lint/config.test.ts +21 -0
- package/src/lint/engine.ts +11 -2
- package/src/lint/presets/relaxed.json +8 -1
- package/src/lint/presets/strict.json +8 -1
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
- package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
- package/src/lint/rules/comp/comp.test.ts +378 -0
- package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
- package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
- package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
- package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
- package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
- package/src/lint/rules/comp/index.ts +37 -0
- package/src/lint/rules/comp/support.ts +173 -0
- package/src/lsp/lexicon-providers.test.ts +55 -0
- package/src/lsp/lexicon-providers.ts +13 -3
- package/src/op/activity-registry.test.ts +38 -0
- package/src/op/activity-registry.ts +31 -12
- package/src/op/activity-runtime.ts +73 -0
- package/src/op/builders.ts +92 -0
- package/src/op/index.ts +3 -1
- package/src/ownership.test.ts +24 -26
- package/src/ownership.ts +26 -38
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin interpret driver — Phase 1 (#556), saga rollback hardened in Phase 4
|
|
3
|
+
* (#565, epic #551).
|
|
4
|
+
*
|
|
5
|
+
* One generic orchestrator that runs an arbitrary set of components on the
|
|
6
|
+
* local in-process executor: it orders components by `dependsOn` (reusing the
|
|
7
|
+
* same Kahn-layering approach `computeStackGraph` uses for `chant graph
|
|
8
|
+
* --stacks`, see ../build.ts), then for each component in order runs its
|
|
9
|
+
* `deploy` composition by dispatching every step to the `CapabilityRegistry`
|
|
10
|
+
* by `kind`. It resolves the three wiring reference forms from
|
|
11
|
+
* component.schema.json (`@Phase.field` prior-step references,
|
|
12
|
+
* `@<component>.publish.<field>` cross-component artifact references, and
|
|
13
|
+
* passes through `$env.*`/`stackOutput` values as opaque env config), runs
|
|
14
|
+
* `parallel` phases concurrently, rejects a `gate` locally (matching
|
|
15
|
+
* ../op/local-executor.ts's `LocalGateUnsupportedError`), and on terminal
|
|
16
|
+
* failure unwinds every executed step in reverse via that step's capability
|
|
17
|
+
* `rollback`, then runs `onFailure` phases in reverse order (best-effort),
|
|
18
|
+
* mirroring the Op local executor's saga semantics.
|
|
19
|
+
*
|
|
20
|
+
* A step whose capability declares no `rollback` is never silently passed
|
|
21
|
+
* over during unwind: it gets a `"rollback-opted-out"` record (see
|
|
22
|
+
* `rollbackExecuted`), carrying the step's `noRollback` reason when the
|
|
23
|
+
* composition declared one. This mirrors, at run time, the opt-out the
|
|
24
|
+
* COMP003 lint rule (../lint/rules/comp/comp003-mutating-no-rollback.ts)
|
|
25
|
+
* already requires at author time — the driver reports the same fact the
|
|
26
|
+
* lint rule already made a component author state.
|
|
27
|
+
*
|
|
28
|
+
* This unwind runs entirely in-process: if the process crashes mid-rollback
|
|
29
|
+
* on the local executor, nothing resumes it — there is no persisted run state
|
|
30
|
+
* to resume from. That is the documented Temporal boundary (see
|
|
31
|
+
* docs/components/orchestration.mdx#rollback-comes-free and
|
|
32
|
+
* docs/guide/local-vs-temporal.mdx): the *mechanism* (reverse-order unwind
|
|
33
|
+
* calling each capability's `rollback`) is capability-agnostic and works
|
|
34
|
+
* identically on both executors, but *durable resume of a rollback already in
|
|
35
|
+
* progress* is Temporal-only, the same boundary that already applies to
|
|
36
|
+
* forward `onFailure` compensation on an Op.
|
|
37
|
+
*
|
|
38
|
+
* Contains zero per-component logic: nothing in this module branches on a
|
|
39
|
+
* component or capability `kind`/name. All behavior is either generic
|
|
40
|
+
* (ordering, phase/step dispatch, wiring resolution) or delegated to the
|
|
41
|
+
* `CapabilityRegistry` the caller supplies.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { topoSort } from "../codegen/topo-sort";
|
|
45
|
+
import type { CapabilityRegistry, DeployContext } from "./capability";
|
|
46
|
+
|
|
47
|
+
// ── Component-shaped input (mirrors component.schema.json / ./component.ts) ──
|
|
48
|
+
|
|
49
|
+
/** A wiring reference or literal value, as it appears in a step's fields (schema `WiringValue`). */
|
|
50
|
+
export type WiringValue =
|
|
51
|
+
| string
|
|
52
|
+
| { stackOutput: { stack: string; name: string } }
|
|
53
|
+
| Record<string, unknown>;
|
|
54
|
+
|
|
55
|
+
/** A single capability invocation — the leaf unit of a composition (schema `Step`). */
|
|
56
|
+
export interface DriverStep {
|
|
57
|
+
kind: string;
|
|
58
|
+
[param: string]: unknown;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A gate step — pauses for an external signal; unsupported on the local executor (schema `Gate`). */
|
|
62
|
+
export interface DriverGate {
|
|
63
|
+
kind: "gate";
|
|
64
|
+
signalName: string;
|
|
65
|
+
timeout?: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** One named phase of a deploy composition (schema `Phase`). A step may itself be a nested `Phase` (fan-out). */
|
|
70
|
+
export interface DriverPhase {
|
|
71
|
+
phase: string;
|
|
72
|
+
steps: Array<DriverStep | DriverGate | DriverPhase>;
|
|
73
|
+
parallel?: boolean;
|
|
74
|
+
onFailure?: DriverPhase[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The subset of the Component contract the driver needs to run a deploy (schema-shaped; see component.schema.json). */
|
|
78
|
+
export interface DriverComponent {
|
|
79
|
+
name: string;
|
|
80
|
+
dependsOn?: string[];
|
|
81
|
+
deploy: DriverPhase[];
|
|
82
|
+
/** Component-level saga compensation, run in reverse order on terminal failure (schema `rollback`). */
|
|
83
|
+
rollback?: DriverPhase[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isGateStep(step: DriverStep | DriverGate | DriverPhase): step is DriverGate {
|
|
87
|
+
return (step as { kind?: unknown }).kind === "gate";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isPhaseStep(step: DriverStep | DriverGate | DriverPhase): step is DriverPhase {
|
|
91
|
+
return typeof (step as { phase?: unknown }).phase === "string" && Array.isArray((step as DriverPhase).steps);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Errors ────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/** Thrown when a component's composition contains a `gate` — gates need a durable runtime, matching chant's Op local executor. */
|
|
97
|
+
export class DriverGateUnsupportedError extends Error {
|
|
98
|
+
constructor(
|
|
99
|
+
public readonly component: string,
|
|
100
|
+
public readonly signalName: string,
|
|
101
|
+
) {
|
|
102
|
+
super(
|
|
103
|
+
`component "${component}": gate "${signalName}" is not supported on the local executor — ` +
|
|
104
|
+
`gates need a durable runtime. Re-run with a durable (Temporal) backend.`,
|
|
105
|
+
);
|
|
106
|
+
this.name = "DriverGateUnsupportedError";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Thrown when a dependency cycle is found among `dependsOn` edges. */
|
|
111
|
+
export class DependencyCycleError extends Error {
|
|
112
|
+
constructor(public readonly cycle: string[]) {
|
|
113
|
+
super(`dependency cycle among components: ${cycle.join(" -> ")}`);
|
|
114
|
+
this.name = "DependencyCycleError";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Thrown when a component's `dependsOn` names a component not present in the run set. */
|
|
119
|
+
export class UnknownDependencyError extends Error {
|
|
120
|
+
constructor(
|
|
121
|
+
public readonly component: string,
|
|
122
|
+
public readonly dependency: string,
|
|
123
|
+
) {
|
|
124
|
+
super(`component "${component}" depends on unknown component "${dependency}"`);
|
|
125
|
+
this.name = "UnknownDependencyError";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Thrown on terminal run failure; carries the partial run result for rendering/inspection. */
|
|
130
|
+
export class DriverRunFailure extends Error {
|
|
131
|
+
constructor(public readonly result: DriverRunResult) {
|
|
132
|
+
super(`interpret run failed at component "${result.failedComponent ?? "unknown"}"`);
|
|
133
|
+
this.name = "DriverRunFailure";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Records ───────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
export interface DriverStepRecord {
|
|
140
|
+
component: string;
|
|
141
|
+
phase: string;
|
|
142
|
+
kind: string;
|
|
143
|
+
status: "ok" | "fail" | "skipped" | "rolled-back" | "rollback-opted-out";
|
|
144
|
+
durationMs: number;
|
|
145
|
+
output?: unknown;
|
|
146
|
+
error?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface DriverComponentResult {
|
|
150
|
+
component: string;
|
|
151
|
+
ok: boolean;
|
|
152
|
+
records: DriverStepRecord[];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface DriverRunResult {
|
|
156
|
+
/** Component order actually attempted, in run order. */
|
|
157
|
+
order: string[];
|
|
158
|
+
/** Parallel-safe waves the order was derived from (see resolveComponentGraph). */
|
|
159
|
+
waves: string[][];
|
|
160
|
+
results: DriverComponentResult[];
|
|
161
|
+
ok: boolean;
|
|
162
|
+
/** Name of the component that terminated the run, if any. */
|
|
163
|
+
failedComponent?: string;
|
|
164
|
+
/**
|
|
165
|
+
* The accumulated cross-component/cross-stack outputs after the run — each
|
|
166
|
+
* component's `publish` output and, for an applied stack, its `cfn-deploy`
|
|
167
|
+
* outputs, keyed by component name. Seeded from `options.componentOutputs`
|
|
168
|
+
* and grown as components complete. The CLI's `--dump-outputs` serializes
|
|
169
|
+
* this so a later, separate run (a downstream CI job) can `--seed-outputs`
|
|
170
|
+
* it and resolve `stackOutput()`/`@<name>.publish.*` references to a
|
|
171
|
+
* component that ran in an earlier job.
|
|
172
|
+
*/
|
|
173
|
+
componentOutputs: Record<string, Record<string, unknown>>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── Graph: order + parallel-safe waves from `dependsOn` ──────────────────────
|
|
177
|
+
|
|
178
|
+
export interface ComponentGraph {
|
|
179
|
+
/** Flat topological order — every dependency before its dependents. */
|
|
180
|
+
order: string[];
|
|
181
|
+
/** Waves: components in the same wave share no dependency and may run concurrently. */
|
|
182
|
+
waves: string[][];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Resolve run order and parallel-safe waves from each component's `dependsOn`.
|
|
187
|
+
* Reuses the generic `topoSort` (../codegen/topo-sort.ts) for the flat order,
|
|
188
|
+
* and the same Kahn-layering approach `computeStackGraph` uses for `chant
|
|
189
|
+
* graph --stacks` (../build.ts) to additionally group independent components
|
|
190
|
+
* into waves. This is a plain string-graph algorithm — no lexicon/AttrRef
|
|
191
|
+
* machinery — since a component's `dependsOn` is already a flat name list.
|
|
192
|
+
*/
|
|
193
|
+
export function resolveComponentGraph(components: DriverComponent[]): ComponentGraph {
|
|
194
|
+
const byName = new Map(components.map((c) => [c.name, c]));
|
|
195
|
+
for (const c of components) {
|
|
196
|
+
for (const dep of c.dependsOn ?? []) {
|
|
197
|
+
if (!byName.has(dep)) throw new UnknownDependencyError(c.name, dep);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Kahn layering (mirrors computeStackGraph's wave computation in ../build.ts):
|
|
202
|
+
// a component is ready once every dependency it names has already been placed.
|
|
203
|
+
// Computed first because it doubles as cycle detection: `topoSort` below is a
|
|
204
|
+
// plain DFS with no cycle guard, so it must only run once a cycle is ruled out.
|
|
205
|
+
const deps = new Map<string, Set<string>>();
|
|
206
|
+
for (const c of components) deps.set(c.name, new Set(c.dependsOn ?? []));
|
|
207
|
+
|
|
208
|
+
const remaining = new Set(deps.keys());
|
|
209
|
+
const waves: string[][] = [];
|
|
210
|
+
while (remaining.size > 0) {
|
|
211
|
+
const wave = [...remaining]
|
|
212
|
+
.filter((n) => [...deps.get(n)!].every((d) => !remaining.has(d)))
|
|
213
|
+
.sort();
|
|
214
|
+
if (wave.length === 0) {
|
|
215
|
+
throw new DependencyCycleError([...remaining].sort());
|
|
216
|
+
}
|
|
217
|
+
for (const n of wave) remaining.delete(n);
|
|
218
|
+
waves.push(wave);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const order = topoSort(
|
|
222
|
+
components,
|
|
223
|
+
(c) => c.name,
|
|
224
|
+
(c) => c.dependsOn ?? [],
|
|
225
|
+
).map((c) => c.name);
|
|
226
|
+
|
|
227
|
+
return { order, waves };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── Wiring resolution ─────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
const PRIOR_STEP_REF = /^@([A-Za-z0-9_ ]+)\.([A-Za-z0-9_.]+)$/;
|
|
233
|
+
const COMPONENT_ARTIFACT_REF = /^@([a-z0-9]+(?:-[a-z0-9]+)*)\.publish\.(uri|digest|key)$/;
|
|
234
|
+
|
|
235
|
+
/** Dot-path lookup, matching ../op/local-executor.ts's resolvePath. */
|
|
236
|
+
function resolvePath(value: unknown, path: string): unknown {
|
|
237
|
+
return path
|
|
238
|
+
.split(".")
|
|
239
|
+
.reduce<unknown>((acc, key) => (acc == null ? acc : (acc as Record<string, unknown>)[key]), value);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Resolve one wiring value against already-produced outputs. Three reference
|
|
244
|
+
* forms, all resolved here rather than by any component-specific code:
|
|
245
|
+
* - `@Phase.field` — a prior step's output within the same component, keyed
|
|
246
|
+
* by phase name (`phaseOutputs`).
|
|
247
|
+
* - `@<component>.publish.uri|digest|key` — another component's published
|
|
248
|
+
* artifact output (`componentOutputs`), per composition-and-wiring.mdx.
|
|
249
|
+
* - `{ stackOutput: { stack, name } }` — a cross-stack output; Phase 1 has no
|
|
250
|
+
* cross-stack apply-order integration (out of scope per #556), so this
|
|
251
|
+
* resolves via the same `componentOutputs` map keyed by `stack`/`name`,
|
|
252
|
+
* letting tests and callers seed cross-stack values the same way as
|
|
253
|
+
* artifact outputs. `$env.*` and plain literals pass through unchanged —
|
|
254
|
+
* env resolution is the caller's `DeployContext.vars`, not the driver's.
|
|
255
|
+
*/
|
|
256
|
+
export function resolveWiring(
|
|
257
|
+
value: WiringValue,
|
|
258
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
259
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
260
|
+
): unknown {
|
|
261
|
+
if (typeof value === "string") {
|
|
262
|
+
const priorStep = value.match(PRIOR_STEP_REF);
|
|
263
|
+
if (priorStep) {
|
|
264
|
+
const [, phaseName, field] = priorStep;
|
|
265
|
+
return resolvePath(phaseOutputs[phaseName], field);
|
|
266
|
+
}
|
|
267
|
+
const artifactRef = value.match(COMPONENT_ARTIFACT_REF);
|
|
268
|
+
if (artifactRef) {
|
|
269
|
+
const [, componentName, field] = artifactRef;
|
|
270
|
+
return resolvePath(componentOutputs[componentName]?.publish, field);
|
|
271
|
+
}
|
|
272
|
+
return value; // literal, or `$env.*` — left for the caller to resolve via DeployContext.
|
|
273
|
+
}
|
|
274
|
+
if (value && typeof value === "object" && "stackOutput" in value) {
|
|
275
|
+
const { stack, name } = (value as { stackOutput: { stack: string; name: string } }).stackOutput;
|
|
276
|
+
return resolvePath(componentOutputs[stack], name);
|
|
277
|
+
}
|
|
278
|
+
return value;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Deep-walk an object, resolving every string/stackOutput wiring value found.
|
|
283
|
+
* Arrays and nested objects are walked too.
|
|
284
|
+
*
|
|
285
|
+
* Exported (not just used internally) so the durable Temporal path
|
|
286
|
+
* (`@intentius/chant-lexicon-temporal`'s component workflow codegen, see
|
|
287
|
+
* epic #551 #589) can resolve a step's wiring the same way inside a Temporal
|
|
288
|
+
* *activity* — the workflow itself only accumulates `phaseOutputs`/
|
|
289
|
+
* `componentOutputs` and passes them through; resolution logic stays in one
|
|
290
|
+
* place so local and durable execution can never silently diverge.
|
|
291
|
+
*/
|
|
292
|
+
export function resolveStepInput(
|
|
293
|
+
input: Record<string, unknown>,
|
|
294
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
295
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
296
|
+
): Record<string, unknown> {
|
|
297
|
+
const walk = (value: unknown): unknown => {
|
|
298
|
+
if (typeof value === "string") return resolveWiring(value, phaseOutputs, componentOutputs);
|
|
299
|
+
if (Array.isArray(value)) return value.map(walk);
|
|
300
|
+
if (value && typeof value === "object") {
|
|
301
|
+
if ("stackOutput" in value) return resolveWiring(value as WiringValue, phaseOutputs, componentOutputs);
|
|
302
|
+
const out: Record<string, unknown> = {};
|
|
303
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = walk(v);
|
|
304
|
+
return out;
|
|
305
|
+
}
|
|
306
|
+
return value;
|
|
307
|
+
};
|
|
308
|
+
return walk(input) as Record<string, unknown>;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ── Step + phase execution ───────────────────────────────────────────────
|
|
312
|
+
|
|
313
|
+
/** A step executed and its resolved input, kept so saga rollback can call the same capability's `rollback` with the same input. */
|
|
314
|
+
interface ExecutedStep {
|
|
315
|
+
step: DriverStep;
|
|
316
|
+
resolvedInput: Record<string, unknown>;
|
|
317
|
+
phaseName: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class StepFailure extends Error {
|
|
321
|
+
constructor(
|
|
322
|
+
public readonly records: DriverStepRecord[],
|
|
323
|
+
public readonly executed: ExecutedStep[],
|
|
324
|
+
) {
|
|
325
|
+
super("step failed");
|
|
326
|
+
this.name = "StepFailure";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Run a single capability step. Never throws for a capability-run failure; returns a fail record instead. */
|
|
331
|
+
async function runCapabilityStep(
|
|
332
|
+
step: DriverStep,
|
|
333
|
+
phaseName: string,
|
|
334
|
+
ctx: DeployContext,
|
|
335
|
+
registry: CapabilityRegistry,
|
|
336
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
337
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
338
|
+
): Promise<{ record: DriverStepRecord; resolvedInput: Record<string, unknown>; output?: unknown }> {
|
|
339
|
+
const { kind, ...rest } = step;
|
|
340
|
+
const resolvedInput = resolveStepInput(rest, phaseOutputs, componentOutputs);
|
|
341
|
+
const start = Date.now();
|
|
342
|
+
try {
|
|
343
|
+
const capability = registry.resolve(kind);
|
|
344
|
+
const output = await capability.run(ctx, resolvedInput as never);
|
|
345
|
+
return {
|
|
346
|
+
record: {
|
|
347
|
+
component: ctx.component,
|
|
348
|
+
phase: phaseName,
|
|
349
|
+
kind,
|
|
350
|
+
status: "ok",
|
|
351
|
+
durationMs: Date.now() - start,
|
|
352
|
+
output,
|
|
353
|
+
},
|
|
354
|
+
resolvedInput,
|
|
355
|
+
output,
|
|
356
|
+
};
|
|
357
|
+
} catch (err) {
|
|
358
|
+
return {
|
|
359
|
+
record: {
|
|
360
|
+
component: ctx.component,
|
|
361
|
+
phase: phaseName,
|
|
362
|
+
kind,
|
|
363
|
+
status: "fail",
|
|
364
|
+
durationMs: Date.now() - start,
|
|
365
|
+
error: errMessage(err),
|
|
366
|
+
},
|
|
367
|
+
resolvedInput,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Run a phase's steps. A step may be a `Gate` (rejected — local executor has
|
|
374
|
+
* no durable wait) or a nested `Phase` (a fan-out unit; recursed into,
|
|
375
|
+
* inheriting `parallel` from its own definition, not its parent's). Steps run
|
|
376
|
+
* sequentially unless `phase.parallel` is set, in which case they run via
|
|
377
|
+
* `Promise.all`, matching ../op/local-executor.ts's phase semantics.
|
|
378
|
+
*/
|
|
379
|
+
async function runPhase(
|
|
380
|
+
phaseDef: DriverPhase,
|
|
381
|
+
ctx: DeployContext,
|
|
382
|
+
registry: CapabilityRegistry,
|
|
383
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
384
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
385
|
+
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> {
|
|
386
|
+
const gate = phaseDef.steps.find(isGateStep);
|
|
387
|
+
if (gate) throw new DriverGateUnsupportedError(ctx.component, gate.signalName);
|
|
388
|
+
|
|
389
|
+
const entries = phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s));
|
|
390
|
+
|
|
391
|
+
const runEntry = async (
|
|
392
|
+
entry: DriverStep | DriverPhase,
|
|
393
|
+
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean }> => {
|
|
394
|
+
if (isPhaseStep(entry)) {
|
|
395
|
+
try {
|
|
396
|
+
const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs);
|
|
397
|
+
return { ...nested, failed: false };
|
|
398
|
+
} catch (err) {
|
|
399
|
+
if (err instanceof StepFailure) return { records: err.records, executed: err.executed, failed: true };
|
|
400
|
+
throw err;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const { record, resolvedInput, output } = await runCapabilityStep(
|
|
404
|
+
entry,
|
|
405
|
+
phaseDef.phase,
|
|
406
|
+
ctx,
|
|
407
|
+
registry,
|
|
408
|
+
phaseOutputs,
|
|
409
|
+
componentOutputs,
|
|
410
|
+
);
|
|
411
|
+
if (record.status === "ok") {
|
|
412
|
+
phaseOutputs[phaseDef.phase] = { ...(phaseOutputs[phaseDef.phase] ?? {}), ...(output as object) };
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
records: [record],
|
|
416
|
+
executed: record.status === "ok" ? [{ step: entry, resolvedInput, phaseName: phaseDef.phase }] : [],
|
|
417
|
+
failed: record.status === "fail",
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
if (phaseDef.parallel) {
|
|
422
|
+
const results = await Promise.all(entries.map(runEntry));
|
|
423
|
+
const records = results.flatMap((r) => r.records);
|
|
424
|
+
const executed = results.flatMap((r) => r.executed);
|
|
425
|
+
if (results.some((r) => r.failed)) throw new StepFailure(records, executed);
|
|
426
|
+
return { records, executed };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const records: DriverStepRecord[] = [];
|
|
430
|
+
const executed: ExecutedStep[] = [];
|
|
431
|
+
for (let i = 0; i < entries.length; i++) {
|
|
432
|
+
const result = await runEntry(entries[i]);
|
|
433
|
+
records.push(...result.records);
|
|
434
|
+
executed.push(...result.executed);
|
|
435
|
+
if (result.failed) {
|
|
436
|
+
for (const skipped of entries.slice(i + 1)) {
|
|
437
|
+
const skippedKind = isPhaseStep(skipped) ? skipped.phase : (skipped as DriverStep).kind;
|
|
438
|
+
records.push({
|
|
439
|
+
component: ctx.component,
|
|
440
|
+
phase: phaseDef.phase,
|
|
441
|
+
kind: skippedKind,
|
|
442
|
+
status: "skipped",
|
|
443
|
+
durationMs: 0,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
throw new StepFailure(records, executed);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return { records, executed };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Roll back every executed step in reverse order via its capability's
|
|
454
|
+
* `rollback`, if declared. Best-effort: a rollback failure is recorded but
|
|
455
|
+
* does not stop the unwind.
|
|
456
|
+
*
|
|
457
|
+
* A step whose capability declares no `rollback` is not silently passed over:
|
|
458
|
+
* it gets its own `"rollback-opted-out"` record, carrying the step's
|
|
459
|
+
* `noRollback` reason when the composition declared one (the same opt-out
|
|
460
|
+
* property COMP003 lint requires at author time — see
|
|
461
|
+
* ../lint/rules/comp/comp003-mutating-no-rollback.ts) or a generic fallback
|
|
462
|
+
* message otherwise. This is decided purely by whether `capability.rollback`
|
|
463
|
+
* exists — never by the step's `kind` or the component's name — so it stays
|
|
464
|
+
* capability-agnostic like the rest of this module.
|
|
465
|
+
*/
|
|
466
|
+
async function rollbackExecuted(
|
|
467
|
+
executed: ExecutedStep[],
|
|
468
|
+
ctx: DeployContext,
|
|
469
|
+
registry: CapabilityRegistry,
|
|
470
|
+
): Promise<DriverStepRecord[]> {
|
|
471
|
+
const records: DriverStepRecord[] = [];
|
|
472
|
+
for (const { step, resolvedInput, phaseName } of [...executed].reverse()) {
|
|
473
|
+
const start = Date.now();
|
|
474
|
+
try {
|
|
475
|
+
const capability = registry.resolve(step.kind);
|
|
476
|
+
if (!capability.rollback) {
|
|
477
|
+
const reason = typeof step.noRollback === "string" ? step.noRollback : undefined;
|
|
478
|
+
records.push({
|
|
479
|
+
component: ctx.component,
|
|
480
|
+
phase: phaseName,
|
|
481
|
+
kind: step.kind,
|
|
482
|
+
status: "rollback-opted-out",
|
|
483
|
+
durationMs: Date.now() - start,
|
|
484
|
+
error: reason ?? `capability "${step.kind}" declares no rollback and the step has no "noRollback" reason`,
|
|
485
|
+
});
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
await capability.rollback(ctx, resolvedInput as never);
|
|
489
|
+
records.push({
|
|
490
|
+
component: ctx.component,
|
|
491
|
+
phase: phaseName,
|
|
492
|
+
kind: step.kind,
|
|
493
|
+
status: "rolled-back",
|
|
494
|
+
durationMs: Date.now() - start,
|
|
495
|
+
});
|
|
496
|
+
} catch (err) {
|
|
497
|
+
records.push({
|
|
498
|
+
component: ctx.component,
|
|
499
|
+
phase: phaseName,
|
|
500
|
+
kind: step.kind,
|
|
501
|
+
status: "fail",
|
|
502
|
+
durationMs: Date.now() - start,
|
|
503
|
+
error: errMessage(err),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return records;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ── Per-component deploy ─────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Run one component's `deploy` composition. On terminal failure: unwind every
|
|
514
|
+
* executed step in reverse via its capability's `rollback` (saga
|
|
515
|
+
* compensation), then run the component's declared `rollback` phases in
|
|
516
|
+
* reverse order (best-effort), matching ../op/local-executor.ts's `onFailure`
|
|
517
|
+
* handling. Cross-component artifact outputs this component published (if
|
|
518
|
+
* any) are recorded into `componentOutputs` under its own name so downstream
|
|
519
|
+
* components can reference `@<name>.publish.*`.
|
|
520
|
+
*/
|
|
521
|
+
export async function runComponentDeploy(
|
|
522
|
+
component: DriverComponent,
|
|
523
|
+
ctx: DeployContext,
|
|
524
|
+
registry: CapabilityRegistry,
|
|
525
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
526
|
+
): Promise<DriverComponentResult> {
|
|
527
|
+
const phaseOutputs: Record<string, Record<string, unknown>> = {};
|
|
528
|
+
const records: DriverStepRecord[] = [];
|
|
529
|
+
const allExecuted: ExecutedStep[] = [];
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
for (const phaseDef of component.deploy) {
|
|
533
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs);
|
|
534
|
+
records.push(...result.records);
|
|
535
|
+
allExecuted.push(...result.executed);
|
|
536
|
+
}
|
|
537
|
+
} catch (err) {
|
|
538
|
+
if (err instanceof StepFailure) {
|
|
539
|
+
records.push(...err.records);
|
|
540
|
+
allExecuted.push(...err.executed);
|
|
541
|
+
} else {
|
|
542
|
+
throw err;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
records.push(...(await rollbackExecuted(allExecuted, ctx, registry)));
|
|
546
|
+
|
|
547
|
+
for (const phaseDef of [...(component.rollback ?? [])].reverse()) {
|
|
548
|
+
try {
|
|
549
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs);
|
|
550
|
+
records.push(...result.records);
|
|
551
|
+
} catch (compErr) {
|
|
552
|
+
if (compErr instanceof StepFailure) records.push(...compErr.records);
|
|
553
|
+
else throw compErr;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return { component: component.name, ok: false, records };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Publish-family outputs (publish-image / publish-artifact / load-image-on-host all
|
|
561
|
+
// return at least one of uri/digest/key — see ../verbs/publish.ts) become this
|
|
562
|
+
// component's `@<name>.publish.*` for downstream cross-component references.
|
|
563
|
+
const publishOutput = findPublishOutput(phaseOutputs);
|
|
564
|
+
if (publishOutput) {
|
|
565
|
+
componentOutputs[component.name] = { ...componentOutputs[component.name], publish: publishOutput };
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Stack outputs from an apply step (cfn-deploy returns `CfnDeployOutput.outputs`)
|
|
569
|
+
// become resolvable by downstream components' `stackOutput(<name>, ...)`
|
|
570
|
+
// references — the cross-stack apply-order integration deferred in #556.
|
|
571
|
+
// Merged at the top level of this component's entry (peer to `publish`, which
|
|
572
|
+
// is namespaced under its own key), so `resolveWiring`'s stackOutput branch —
|
|
573
|
+
// `resolvePath(componentOutputs[stack], name)` — finds each output by name.
|
|
574
|
+
// Keyed by the component's own name, which by convention is the stack name a
|
|
575
|
+
// `stackOutput` reference targets (see the pilots and composition-and-wiring.mdx).
|
|
576
|
+
const stackOutputs = findStackOutputs(phaseOutputs);
|
|
577
|
+
if (stackOutputs) {
|
|
578
|
+
componentOutputs[component.name] = { ...componentOutputs[component.name], ...stackOutputs };
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return { component: component.name, ok: true, records };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Find the output of the last step that looks like a publish result (carries
|
|
586
|
+
* `uri`, `digest`, or `key`) across every phase this component ran, so the
|
|
587
|
+
* driver can populate `@<component>.publish.*` for a downstream consumer.
|
|
588
|
+
* Generic by shape, not by capability `kind` — any capability whose output
|
|
589
|
+
* carries one of these fields is eligible, keeping the driver free of
|
|
590
|
+
* per-capability branching.
|
|
591
|
+
*/
|
|
592
|
+
function findPublishOutput(
|
|
593
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
594
|
+
): Record<string, unknown> | undefined {
|
|
595
|
+
let found: Record<string, unknown> | undefined;
|
|
596
|
+
for (const output of Object.values(phaseOutputs)) {
|
|
597
|
+
if (output && ("uri" in output || "digest" in output || "key" in output)) {
|
|
598
|
+
found = { ...found, ...output };
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return found;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Find the stack `outputs` map produced by an apply-style step (cfn-deploy
|
|
606
|
+
* returns `CfnDeployOutput.outputs`) across every phase this component ran, so
|
|
607
|
+
* a deployed stack's outputs can seed downstream `stackOutput()` resolution.
|
|
608
|
+
* Generic by shape — any output carrying an `outputs` record is eligible —
|
|
609
|
+
* keeping the driver free of per-capability branching, the same way
|
|
610
|
+
* `findPublishOutput` is.
|
|
611
|
+
*/
|
|
612
|
+
function findStackOutputs(
|
|
613
|
+
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
614
|
+
): Record<string, unknown> | undefined {
|
|
615
|
+
let found: Record<string, unknown> | undefined;
|
|
616
|
+
for (const output of Object.values(phaseOutputs)) {
|
|
617
|
+
const outputs = output?.outputs;
|
|
618
|
+
if (outputs && typeof outputs === "object" && !Array.isArray(outputs)) {
|
|
619
|
+
found = { ...found, ...(outputs as Record<string, unknown>) };
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return found;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function errMessage(err: unknown): string {
|
|
626
|
+
return err instanceof Error ? err.message : String(err);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// ── Public API: the interpret driver ─────────────────────────────────────
|
|
630
|
+
|
|
631
|
+
export interface InterpretRunOptions {
|
|
632
|
+
/** Target environment name, threaded into every capability's `DeployContext`. */
|
|
633
|
+
env: string;
|
|
634
|
+
/** Environment config resolved ahead of the run (registry URLs, cluster names, ...) — passed through as `DeployContext.vars`. */
|
|
635
|
+
vars?: Record<string, unknown>;
|
|
636
|
+
/** Pre-seeded cross-component/cross-stack outputs (e.g. from a prior run, or a caller resolving `stackOutput` externally). Merged with outputs this run produces. */
|
|
637
|
+
componentOutputs?: Record<string, Record<string, unknown>>;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Run a set of components to completion on the local in-process executor:
|
|
642
|
+
* resolve dependency order and parallel-safe waves from `dependsOn`, then run
|
|
643
|
+
* each wave's components (independent components within a wave run
|
|
644
|
+
* concurrently; components across waves run in wave order), dispatching
|
|
645
|
+
* every step to `registry` by `kind`. Stops the whole run at the first failed
|
|
646
|
+
* component (after that component's own saga rollback completes), returning
|
|
647
|
+
* a result with `ok: false`; throws `DriverRunFailure` carrying that result
|
|
648
|
+
* so callers can choose to inspect or propagate it.
|
|
649
|
+
*
|
|
650
|
+
* Zero per-component logic: this function and everything it calls dispatches
|
|
651
|
+
* purely on the generic `Component`/`Phase`/`Step` shapes and the registry —
|
|
652
|
+
* no branch anywhere names a specific component or capability.
|
|
653
|
+
*/
|
|
654
|
+
export async function runInterpretDriver(
|
|
655
|
+
components: DriverComponent[],
|
|
656
|
+
registry: CapabilityRegistry,
|
|
657
|
+
options: InterpretRunOptions,
|
|
658
|
+
): Promise<DriverRunResult> {
|
|
659
|
+
const { order, waves } = resolveComponentGraph(components);
|
|
660
|
+
const byName = new Map(components.map((c) => [c.name, c]));
|
|
661
|
+
const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
|
|
662
|
+
|
|
663
|
+
const results: DriverComponentResult[] = [];
|
|
664
|
+
let failedComponent: string | undefined;
|
|
665
|
+
|
|
666
|
+
waveLoop: for (const wave of waves) {
|
|
667
|
+
const waveComponents = wave.map((name) => byName.get(name)!);
|
|
668
|
+
const waveResults = await Promise.all(
|
|
669
|
+
waveComponents.map((component) =>
|
|
670
|
+
runComponentDeploy(
|
|
671
|
+
component,
|
|
672
|
+
{ env: options.env, component: component.name, vars: options.vars },
|
|
673
|
+
registry,
|
|
674
|
+
componentOutputs,
|
|
675
|
+
),
|
|
676
|
+
),
|
|
677
|
+
);
|
|
678
|
+
results.push(...waveResults);
|
|
679
|
+
const failed = waveResults.find((r) => !r.ok);
|
|
680
|
+
if (failed) {
|
|
681
|
+
failedComponent = failed.component;
|
|
682
|
+
break waveLoop;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const ok = failedComponent === undefined;
|
|
687
|
+
const result: DriverRunResult = { order, waves, results, ok, failedComponent, componentOutputs };
|
|
688
|
+
if (!ok) throw new DriverRunFailure(result);
|
|
689
|
+
return result;
|
|
690
|
+
}
|