@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,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery + loading for capability plugins — the capability-side analogue
|
|
3
|
+
* of ../cli/plugins.ts's `loadPlugin`/`loadPlugins` for lexicons.
|
|
4
|
+
*
|
|
5
|
+
* Naming convention mirrors lexicons exactly, substituting "capability" for
|
|
6
|
+
* "lexicon": a capability plugin package is
|
|
7
|
+
* `@intentius/chant-capability-<name>` and is expected to export a value
|
|
8
|
+
* satisfying `CapabilityPlugin` (./capability-plugin.ts), detected at
|
|
9
|
+
* runtime with `isCapabilityPlugin` — the same dynamic-import + shape-guard
|
|
10
|
+
* strategy `loadPlugin` uses for `@intentius/chant-lexicon-<name>`.
|
|
11
|
+
*
|
|
12
|
+
* `buildCapabilityRegistry` is the Phase 2 replacement for Phase 1's
|
|
13
|
+
* `createCapabilityRegistry` (./registry.ts): it always loads the built-in
|
|
14
|
+
* `starterCapabilityPlugin` (./starter-plugin.ts) first — preserving every
|
|
15
|
+
* Phase 1 kind with zero behavior change — then loads any additional
|
|
16
|
+
* requested plugin packages on top, registering every capability they
|
|
17
|
+
* contribute into one `CapabilityRegistry` the driver resolves verbs
|
|
18
|
+
* through. Duplicate `kind` registration across plugins throws (via
|
|
19
|
+
* `CapabilityRegistry.register`'s existing guard), the same hard-conflict
|
|
20
|
+
* discipline `checkConflicts` (../cli/conflict-check.ts) applies to
|
|
21
|
+
* duplicate lexicon rule IDs.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { CapabilityRegistry } from "./capability";
|
|
25
|
+
import type { CapabilityPlugin } from "./capability-plugin";
|
|
26
|
+
import { isCapabilityPlugin } from "./capability-plugin";
|
|
27
|
+
import { starterCapabilityPlugin } from "./starter-plugin";
|
|
28
|
+
|
|
29
|
+
/** Thrown when a dynamically-imported capability package does not satisfy `CapabilityPlugin` — the "malformed capability package" case #559 asks doctor/validation to detect. */
|
|
30
|
+
export class MalformedCapabilityPluginError extends Error {
|
|
31
|
+
constructor(
|
|
32
|
+
public readonly packageName: string,
|
|
33
|
+
reason: string,
|
|
34
|
+
) {
|
|
35
|
+
super(`capability plugin package "${packageName}" is malformed: ${reason}`);
|
|
36
|
+
this.name = "MalformedCapabilityPluginError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Thrown when two loaded capability plugins register the same `kind` — mirrors the lexicon loader's hard rule-ID conflict. */
|
|
41
|
+
export class DuplicateCapabilityKindError extends Error {
|
|
42
|
+
constructor(
|
|
43
|
+
public readonly kind: string,
|
|
44
|
+
public readonly plugins: string[],
|
|
45
|
+
) {
|
|
46
|
+
super(`capability "${kind}" is registered by more than one plugin: ${plugins.join(", ")}`);
|
|
47
|
+
this.name = "DuplicateCapabilityKindError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Dynamically import and validate a single capability plugin package by
|
|
53
|
+
* name. Mirrors ../cli/plugins.ts's `loadPlugin` for lexicons: imports
|
|
54
|
+
* `@intentius/chant-capability-{name}`, scans its exports for a value
|
|
55
|
+
* satisfying `isCapabilityPlugin`, and throws a descriptive
|
|
56
|
+
* `MalformedCapabilityPluginError` if the package exists but exports nothing
|
|
57
|
+
* usable (the malformed-package case), or the original import error if the
|
|
58
|
+
* package cannot be found at all.
|
|
59
|
+
*/
|
|
60
|
+
export async function loadCapabilityPlugin(name: string): Promise<CapabilityPlugin> {
|
|
61
|
+
const packageName = `@intentius/chant-capability-${name}`;
|
|
62
|
+
let mod: Record<string, unknown>;
|
|
63
|
+
try {
|
|
64
|
+
mod = (await import(packageName)) as Record<string, unknown>;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`capability plugin package "${packageName}" could not be loaded: ${err instanceof Error ? err.message : String(err)}`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const value of Object.values(mod)) {
|
|
72
|
+
if (isCapabilityPlugin(value)) {
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw new MalformedCapabilityPluginError(
|
|
78
|
+
packageName,
|
|
79
|
+
`does not export a CapabilityPlugin (expected an export with string "name", string "version", and a "capabilities" function)`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Load and initialize multiple named capability plugins, in order. Calls
|
|
85
|
+
* `init()` on each plugin if present — mirrors `loadPlugins`'s lexicon
|
|
86
|
+
* `init()` hook.
|
|
87
|
+
*/
|
|
88
|
+
export async function loadCapabilityPlugins(names: string[]): Promise<CapabilityPlugin[]> {
|
|
89
|
+
const plugins: CapabilityPlugin[] = [];
|
|
90
|
+
for (const name of names) {
|
|
91
|
+
const plugin = await loadCapabilityPlugin(name);
|
|
92
|
+
if (plugin.init) {
|
|
93
|
+
await plugin.init();
|
|
94
|
+
}
|
|
95
|
+
plugins.push(plugin);
|
|
96
|
+
}
|
|
97
|
+
return plugins;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Load the capability plugin a *lexicon* package contributes, if any. A cloud
|
|
102
|
+
* lexicon (`@intentius/chant-lexicon-<name>`) is the natural home for that
|
|
103
|
+
* cloud's operational leaves (`cfn-deploy`, `emr-*`, …) — the same seam that
|
|
104
|
+
* already carries the lexicon's synthesis, `describeResources`, and ownership
|
|
105
|
+
* markers (docs/components/cloud-boundary). So when a project declares
|
|
106
|
+
* `lexicons: ["aws"]`, its aws leaves come along automatically: this imports
|
|
107
|
+
* the lexicon package and returns the first export satisfying
|
|
108
|
+
* `isCapabilityPlugin`.
|
|
109
|
+
*
|
|
110
|
+
* Tolerant by design — a lexicon that contributes *no* capabilities (most do
|
|
111
|
+
* not yet) simply returns `null` rather than throwing, unlike
|
|
112
|
+
* `loadCapabilityPlugin`'s strict `@intentius/chant-capability-<name>` contract
|
|
113
|
+
* where the package's whole reason to exist is the plugin.
|
|
114
|
+
*/
|
|
115
|
+
export async function loadCapabilityPluginFromLexicon(name: string): Promise<CapabilityPlugin | null> {
|
|
116
|
+
const packageName = `@intentius/chant-lexicon-${name}`;
|
|
117
|
+
let mod: Record<string, unknown>;
|
|
118
|
+
try {
|
|
119
|
+
mod = (await import(packageName)) as Record<string, unknown>;
|
|
120
|
+
} catch {
|
|
121
|
+
return null; // lexicon not installed / not resolvable here — nothing to contribute.
|
|
122
|
+
}
|
|
123
|
+
for (const value of Object.values(mod)) {
|
|
124
|
+
if (isCapabilityPlugin(value)) return value;
|
|
125
|
+
}
|
|
126
|
+
return null; // lexicon present but contributes no capability plugin.
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Load the capability plugins contributed by the given lexicons (skipping those that contribute none), initializing each. */
|
|
130
|
+
export async function loadCapabilityPluginsFromLexicons(names: string[]): Promise<CapabilityPlugin[]> {
|
|
131
|
+
const plugins: CapabilityPlugin[] = [];
|
|
132
|
+
for (const name of names) {
|
|
133
|
+
const plugin = await loadCapabilityPluginFromLexicon(name);
|
|
134
|
+
if (!plugin) continue;
|
|
135
|
+
if (plugin.init) await plugin.init();
|
|
136
|
+
plugins.push(plugin);
|
|
137
|
+
}
|
|
138
|
+
return plugins;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Register every capability every plugin contributes into one registry, in
|
|
143
|
+
* plugin order. Throws `DuplicateCapabilityKindError` if two plugins
|
|
144
|
+
* register the same `kind` (via a pre-check, so the error names every
|
|
145
|
+
* offending plugin rather than surfacing only the second registration
|
|
146
|
+
* attempt's generic `CapabilityRegistry.register` message).
|
|
147
|
+
*/
|
|
148
|
+
export function registerCapabilityPlugins(
|
|
149
|
+
registry: CapabilityRegistry,
|
|
150
|
+
plugins: CapabilityPlugin[],
|
|
151
|
+
): CapabilityRegistry {
|
|
152
|
+
const owners = new Map<string, string[]>();
|
|
153
|
+
for (const plugin of plugins) {
|
|
154
|
+
for (const capability of plugin.capabilities()) {
|
|
155
|
+
const existing = owners.get(capability.kind) ?? [];
|
|
156
|
+
existing.push(plugin.name);
|
|
157
|
+
owners.set(capability.kind, existing);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const [kind, pluginNames] of owners) {
|
|
161
|
+
if (pluginNames.length > 1) {
|
|
162
|
+
throw new DuplicateCapabilityKindError(kind, pluginNames);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const plugin of plugins) {
|
|
167
|
+
for (const capability of plugin.capabilities()) {
|
|
168
|
+
registry.register(capability);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return registry;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface BuildCapabilityRegistryOptions {
|
|
175
|
+
/**
|
|
176
|
+
* Additional capability plugin package names to load and register on top
|
|
177
|
+
* of the built-in starter plugin (e.g. third-party/cloud-specific
|
|
178
|
+
* capability packages `@intentius/chant-capability-<name>`). Defaults to
|
|
179
|
+
* none — Phase 1 behavior (just the starter set) is preserved when omitted.
|
|
180
|
+
*/
|
|
181
|
+
plugins?: string[];
|
|
182
|
+
/**
|
|
183
|
+
* Lexicon names (from `chant.config.ts`'s `lexicons`) whose packages may
|
|
184
|
+
* *also* contribute a capability plugin — the primary way a cloud's
|
|
185
|
+
* operational leaves are loaded (e.g. `["aws"]` pulls the aws lexicon's
|
|
186
|
+
* `cfn-deploy`/`emr-*`/… leaves). Lexicons contributing no capabilities are
|
|
187
|
+
* silently skipped; see `loadCapabilityPluginsFromLexicons`.
|
|
188
|
+
*/
|
|
189
|
+
lexicons?: string[];
|
|
190
|
+
/**
|
|
191
|
+
* Skip loading the built-in starter plugin. Only meaningful for tests that
|
|
192
|
+
* want a registry seeded purely from explicit `plugins`; real callers
|
|
193
|
+
* should leave this unset so the Phase 1 verb set is always present.
|
|
194
|
+
*/
|
|
195
|
+
includeStarter?: boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Build a `CapabilityRegistry` through the Phase 2 plugin contract: always
|
|
200
|
+
* registers the built-in `starterCapabilityPlugin` first (preserving every
|
|
201
|
+
* Phase 1 `kind` — see ./starter-plugin.ts), then loads and registers any
|
|
202
|
+
* additional named plugin packages. This is the Phase 2 entry point;
|
|
203
|
+
* `createCapabilityRegistry` (./registry.ts) remains available and now
|
|
204
|
+
* delegates to this same starter plugin, so existing Phase 1 callers (and
|
|
205
|
+
* the pilots) are unaffected.
|
|
206
|
+
*/
|
|
207
|
+
export async function buildCapabilityRegistry(
|
|
208
|
+
options: BuildCapabilityRegistryOptions = {},
|
|
209
|
+
): Promise<CapabilityRegistry> {
|
|
210
|
+
const registry = new CapabilityRegistry();
|
|
211
|
+
const plugins: CapabilityPlugin[] = [];
|
|
212
|
+
if (options.includeStarter !== false) {
|
|
213
|
+
plugins.push(starterCapabilityPlugin);
|
|
214
|
+
}
|
|
215
|
+
plugins.push(...(await loadCapabilityPluginsFromLexicons(options.lexicons ?? [])));
|
|
216
|
+
plugins.push(...(await loadCapabilityPlugins(options.plugins ?? [])));
|
|
217
|
+
return registerCapabilityPlugins(registry, plugins);
|
|
218
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema + validation for `CapabilityManifest` (./capability-plugin.ts) —
|
|
3
|
+
* the capability-side analogue of ../lexicon-schema.ts's
|
|
4
|
+
* `LexiconManifestSchema`/`validateManifest`. Used to detect a malformed
|
|
5
|
+
* capability package's manifest (#559 acceptance criteria: "Doctor/validation
|
|
6
|
+
* covers a malformed capability package") with the same descriptive-error
|
|
7
|
+
* shape lexicon manifest validation uses.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import type { CapabilityManifest } from "./capability-plugin";
|
|
12
|
+
|
|
13
|
+
const semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/;
|
|
14
|
+
|
|
15
|
+
export const CapabilityManifestSchema = z.object({
|
|
16
|
+
name: z.string().min(1, "manifest name must not be empty"),
|
|
17
|
+
version: z.string().regex(semverRegex, "version must be valid semver (X.Y.Z)"),
|
|
18
|
+
chantVersion: z.string().optional(),
|
|
19
|
+
kinds: z.array(z.string().min(1)).optional(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type CapabilityManifestParsed = z.infer<typeof CapabilityManifestSchema>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validate a capability manifest from unknown input (object or JSON string).
|
|
26
|
+
* Throws a descriptive error on invalid input — mirrors
|
|
27
|
+
* ../lexicon-schema.ts's `validateManifest`.
|
|
28
|
+
*/
|
|
29
|
+
export function validateCapabilityManifest(data: unknown): CapabilityManifest {
|
|
30
|
+
if (data === null || data === undefined) {
|
|
31
|
+
throw new Error("capability manifest data is empty");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof data === "string") {
|
|
35
|
+
try {
|
|
36
|
+
data = JSON.parse(data);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
const msg = err instanceof SyntaxError ? err.message : String(err);
|
|
39
|
+
throw new Error(`invalid JSON in capability manifest: ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
44
|
+
throw new Error("capability manifest must be a JSON object");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const result = CapabilityManifestSchema.safeParse(data);
|
|
48
|
+
if (!result.success) {
|
|
49
|
+
const issue = result.error.issues[0];
|
|
50
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : undefined;
|
|
51
|
+
const prefix = path ? `capability manifest field ${path}` : "capability manifest";
|
|
52
|
+
throw new Error(`${prefix}: ${issue.message}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result.data as CapabilityManifest;
|
|
56
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Phase 2 capability plugin contract (#559, epic #551):
|
|
3
|
+
* `isCapabilityPlugin` (./capability-plugin.ts), manifest validation
|
|
4
|
+
* (./capability-plugin-schema.ts), and plugin loading/registration/registry
|
|
5
|
+
* building (./capability-plugin-loader.ts) — mirroring how
|
|
6
|
+
* ../lexicon.test-equivalent coverage (../cli/plugins.test.ts,
|
|
7
|
+
* ../lexicon-schema.test.ts) validates the lexicon plugin contract.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "vitest";
|
|
11
|
+
import { isCapabilityPlugin, type CapabilityPlugin } from "./capability-plugin";
|
|
12
|
+
import { validateCapabilityManifest, CapabilityManifestSchema } from "./capability-plugin-schema";
|
|
13
|
+
import {
|
|
14
|
+
loadCapabilityPlugin,
|
|
15
|
+
loadCapabilityPluginFromLexicon,
|
|
16
|
+
loadCapabilityPluginsFromLexicons,
|
|
17
|
+
registerCapabilityPlugins,
|
|
18
|
+
buildCapabilityRegistry,
|
|
19
|
+
MalformedCapabilityPluginError,
|
|
20
|
+
DuplicateCapabilityKindError,
|
|
21
|
+
} from "./capability-plugin-loader";
|
|
22
|
+
import { CapabilityRegistry } from "./capability";
|
|
23
|
+
import { STARTER_VERB_FAMILIES } from "./registry";
|
|
24
|
+
import { starterCapabilityPlugin } from "./starter-plugin";
|
|
25
|
+
|
|
26
|
+
const ALL_STARTER_KINDS = Object.values(STARTER_VERB_FAMILIES).flat();
|
|
27
|
+
|
|
28
|
+
// A trivial well-formed plugin used across several tests below.
|
|
29
|
+
function fakePlugin(name: string, kinds: string[]): CapabilityPlugin {
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
capabilities: () => kinds.map((kind) => ({ kind, run: async () => ({}) })),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("isCapabilityPlugin", () => {
|
|
38
|
+
test("accepts a well-formed plugin", () => {
|
|
39
|
+
expect(isCapabilityPlugin(fakePlugin("demo", ["demo-verb"]))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("accepts the real starter plugin", () => {
|
|
43
|
+
expect(isCapabilityPlugin(starterCapabilityPlugin)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("rejects null/non-object", () => {
|
|
47
|
+
expect(isCapabilityPlugin(null)).toBe(false);
|
|
48
|
+
expect(isCapabilityPlugin(undefined)).toBe(false);
|
|
49
|
+
expect(isCapabilityPlugin("a string")).toBe(false);
|
|
50
|
+
expect(isCapabilityPlugin(42)).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("rejects a value missing name", () => {
|
|
54
|
+
expect(isCapabilityPlugin({ version: "1.0.0", capabilities: () => [] })).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("rejects a value missing version", () => {
|
|
58
|
+
expect(isCapabilityPlugin({ name: "demo", capabilities: () => [] })).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("rejects a value whose capabilities is not a function (malformed package)", () => {
|
|
62
|
+
expect(isCapabilityPlugin({ name: "demo", version: "1.0.0", capabilities: [] })).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("rejects a value with no capabilities field at all (malformed package)", () => {
|
|
66
|
+
expect(isCapabilityPlugin({ name: "demo", version: "1.0.0" })).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("rejects a value whose name/version are the wrong type", () => {
|
|
70
|
+
expect(isCapabilityPlugin({ name: 123, version: "1.0.0", capabilities: () => [] })).toBe(false);
|
|
71
|
+
expect(isCapabilityPlugin({ name: "demo", version: 1, capabilities: () => [] })).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("validateCapabilityManifest", () => {
|
|
76
|
+
test("parses a valid manifest object", () => {
|
|
77
|
+
const m = validateCapabilityManifest({
|
|
78
|
+
name: "aws",
|
|
79
|
+
version: "1.0.0",
|
|
80
|
+
chantVersion: ">=0.1.0",
|
|
81
|
+
kinds: ["cfn-deploy", "ecs-update-service"],
|
|
82
|
+
});
|
|
83
|
+
expect(m.name).toBe("aws");
|
|
84
|
+
expect(m.version).toBe("1.0.0");
|
|
85
|
+
expect(m.chantVersion).toBe(">=0.1.0");
|
|
86
|
+
expect(m.kinds).toEqual(["cfn-deploy", "ecs-update-service"]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("parses a valid JSON string", () => {
|
|
90
|
+
const m = validateCapabilityManifest(JSON.stringify({ name: "gcp", version: "0.1.0" }));
|
|
91
|
+
expect(m.name).toBe("gcp");
|
|
92
|
+
expect(m.version).toBe("0.1.0");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("optional fields can be omitted", () => {
|
|
96
|
+
const m = validateCapabilityManifest({ name: "gcp", version: "2.0.0" });
|
|
97
|
+
expect(m.chantVersion).toBeUndefined();
|
|
98
|
+
expect(m.kinds).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("rejects empty input", () => {
|
|
102
|
+
expect(() => validateCapabilityManifest(null)).toThrow(/empty/);
|
|
103
|
+
expect(() => validateCapabilityManifest(undefined)).toThrow(/empty/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("rejects invalid JSON string (malformed package manifest)", () => {
|
|
107
|
+
expect(() => validateCapabilityManifest("not valid json{{{")).toThrow(/invalid JSON/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("rejects a non-object (malformed package manifest)", () => {
|
|
111
|
+
expect(() => validateCapabilityManifest("just a string")).toThrow(/invalid JSON|must be a JSON object/);
|
|
112
|
+
expect(() => validateCapabilityManifest(["array", "not", "object"])).toThrow(/must be a JSON object/);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("rejects a manifest missing name", () => {
|
|
116
|
+
expect(() => validateCapabilityManifest({ version: "1.0.0" })).toThrow(/name/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("rejects a manifest with an empty name", () => {
|
|
120
|
+
expect(() => validateCapabilityManifest({ name: "", version: "1.0.0" })).toThrow(/must not be empty/);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("rejects a manifest with a non-semver version (malformed package)", () => {
|
|
124
|
+
expect(() => validateCapabilityManifest({ name: "aws", version: "not-a-version" })).toThrow(/semver/);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("rejects a manifest with an empty kind entry", () => {
|
|
128
|
+
const result = CapabilityManifestSchema.safeParse({ name: "aws", version: "1.0.0", kinds: [""] });
|
|
129
|
+
expect(result.success).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("loadCapabilityPlugin", () => {
|
|
134
|
+
test("throws a descriptive error for a package that cannot be found", async () => {
|
|
135
|
+
await expect(loadCapabilityPlugin("nonexistent-does-not-exist")).rejects.toThrow(
|
|
136
|
+
/could not be loaded/,
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("registerCapabilityPlugins", () => {
|
|
142
|
+
test("registers every capability every plugin contributes", () => {
|
|
143
|
+
const registry = new CapabilityRegistry();
|
|
144
|
+
registerCapabilityPlugins(registry, [fakePlugin("demo-a", ["verb-a", "verb-b"]), fakePlugin("demo-b", ["verb-c"])]);
|
|
145
|
+
expect(registry.has("verb-a")).toBe(true);
|
|
146
|
+
expect(registry.has("verb-b")).toBe(true);
|
|
147
|
+
expect(registry.has("verb-c")).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("throws DuplicateCapabilityKindError when two plugins register the same kind (malformed/conflicting package set)", () => {
|
|
151
|
+
const registry = new CapabilityRegistry();
|
|
152
|
+
expect(() =>
|
|
153
|
+
registerCapabilityPlugins(registry, [fakePlugin("demo-a", ["shared-verb"]), fakePlugin("demo-b", ["shared-verb"])]),
|
|
154
|
+
).toThrow(DuplicateCapabilityKindError);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("DuplicateCapabilityKindError names every offending plugin", () => {
|
|
158
|
+
const registry = new CapabilityRegistry();
|
|
159
|
+
try {
|
|
160
|
+
registerCapabilityPlugins(registry, [fakePlugin("demo-a", ["shared-verb"]), fakePlugin("demo-b", ["shared-verb"])]);
|
|
161
|
+
expect.unreachable();
|
|
162
|
+
} catch (err) {
|
|
163
|
+
expect(err).toBeInstanceOf(DuplicateCapabilityKindError);
|
|
164
|
+
const dup = err as DuplicateCapabilityKindError;
|
|
165
|
+
expect(dup.kind).toBe("shared-verb");
|
|
166
|
+
expect(dup.plugins).toEqual(["demo-a", "demo-b"]);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a duplicate-kind conflict leaves the registry unmodified (all-or-nothing)", () => {
|
|
171
|
+
const registry = new CapabilityRegistry();
|
|
172
|
+
try {
|
|
173
|
+
registerCapabilityPlugins(registry, [fakePlugin("demo-a", ["shared-verb"]), fakePlugin("demo-b", ["shared-verb"])]);
|
|
174
|
+
} catch {
|
|
175
|
+
/* expected */
|
|
176
|
+
}
|
|
177
|
+
expect(registry.has("shared-verb")).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe("buildCapabilityRegistry (Phase 2 entry point)", () => {
|
|
182
|
+
test("preserves every Phase 1 starter kind with no behavior change", async () => {
|
|
183
|
+
const registry = await buildCapabilityRegistry();
|
|
184
|
+
for (const kind of ALL_STARTER_KINDS) {
|
|
185
|
+
expect(registry.has(kind)).toBe(true);
|
|
186
|
+
}
|
|
187
|
+
expect(registry.kinds()).toEqual([...ALL_STARTER_KINDS].sort());
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("layers an explicitly-provided plugin on top of the starter set", async () => {
|
|
191
|
+
// buildCapabilityRegistry's `plugins` option loads packages by name via
|
|
192
|
+
// loadCapabilityPlugin; exercise the registration/layering behavior
|
|
193
|
+
// directly through registerCapabilityPlugins + the starter plugin,
|
|
194
|
+
// since a real third-party package isn't installed in this repo.
|
|
195
|
+
const registry = new CapabilityRegistry();
|
|
196
|
+
registerCapabilityPlugins(registry, [starterCapabilityPlugin, fakePlugin("acme", ["acme-verb"])]);
|
|
197
|
+
for (const kind of ALL_STARTER_KINDS) {
|
|
198
|
+
expect(registry.has(kind)).toBe(true);
|
|
199
|
+
}
|
|
200
|
+
expect(registry.has("acme-verb")).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("includeStarter: false omits the starter plugin", async () => {
|
|
204
|
+
const registry = await buildCapabilityRegistry({ includeStarter: false, plugins: [] });
|
|
205
|
+
expect(registry.kinds()).toEqual([]);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("a lexicon that contributes no capability plugin is silently skipped (starter set intact)", async () => {
|
|
209
|
+
// `loadCapabilityPluginsFromLexicons` is tolerant: an unresolvable or
|
|
210
|
+
// capability-less lexicon contributes nothing rather than throwing, unlike
|
|
211
|
+
// the strict `@intentius/chant-capability-<name>` path.
|
|
212
|
+
const registry = await buildCapabilityRegistry({ lexicons: ["definitely-not-a-real-lexicon"] });
|
|
213
|
+
expect(registry.kinds()).toEqual([...ALL_STARTER_KINDS].sort());
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe("loadCapabilityPluginFromLexicon", () => {
|
|
218
|
+
test("returns null for a lexicon package that cannot be resolved", async () => {
|
|
219
|
+
expect(await loadCapabilityPluginFromLexicon("definitely-not-a-real-lexicon")).toBeNull();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("loadCapabilityPluginsFromLexicons skips unresolvable lexicons without throwing", async () => {
|
|
223
|
+
expect(await loadCapabilityPluginsFromLexicons(["definitely-not-a-real-lexicon"])).toEqual([]);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("MalformedCapabilityPluginError", () => {
|
|
228
|
+
test("carries the offending package name and a descriptive message", () => {
|
|
229
|
+
const err = new MalformedCapabilityPluginError("@intentius/chant-capability-broken", "no capabilities() export");
|
|
230
|
+
expect(err.packageName).toBe("@intentius/chant-capability-broken");
|
|
231
|
+
expect(err.message).toContain("@intentius/chant-capability-broken");
|
|
232
|
+
expect(err.message).toContain("no capabilities() export");
|
|
233
|
+
expect(err.name).toBe("MalformedCapabilityPluginError");
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability plugin contract — Phase 2 (#559, epic #551).
|
|
3
|
+
*
|
|
4
|
+
* Promotes the Phase 1 capability registry convention
|
|
5
|
+
* (`createCapabilityRegistry`/`STARTER_VERB_FAMILIES`, ./registry.ts) to a
|
|
6
|
+
* first-class chant plugin contract, mirroring the lexicon plugin contract
|
|
7
|
+
* (../lexicon.ts, ../cli/plugins.ts) so capabilities become typed,
|
|
8
|
+
* discoverable packages resolved by `kind`, the same way lexicons are
|
|
9
|
+
* discovered and resolved by resource type.
|
|
10
|
+
*
|
|
11
|
+
* Shape parity with `LexiconPlugin` (../lexicon.ts):
|
|
12
|
+
* - `name`/`version` mirror `LexiconManifest.name`/`.version`.
|
|
13
|
+
* - `capabilities()` is the required lifecycle method (like a lexicon's
|
|
14
|
+
* `serializer`/`generate`/`validate`) every capability package must
|
|
15
|
+
* implement — it returns the typed `Capability` instances this package
|
|
16
|
+
* contributes, registered by `kind` the same way a lexicon's resource
|
|
17
|
+
* types are registered by `resourceType`.
|
|
18
|
+
* - `families()`, `init()` are optional extensions, mirroring
|
|
19
|
+
* `LexiconPlugin`'s optional `lintRules`/`init`/etc.
|
|
20
|
+
* - `isCapabilityPlugin` is the runtime type guard `loadCapabilityPlugin`
|
|
21
|
+
* (./capability-plugin-loader.ts) uses to validate a dynamically-imported
|
|
22
|
+
* package before trusting it — the same defensive shape check
|
|
23
|
+
* `isLexiconPlugin` performs for lexicons.
|
|
24
|
+
*
|
|
25
|
+
* The starter verb set (./registry.ts) becomes the built-in, always-loaded
|
|
26
|
+
* plugin (`starterCapabilityPlugin`, ./starter-plugin.ts) under this same
|
|
27
|
+
* contract — see that module's docstring for the Phase 1 -> Phase 2
|
|
28
|
+
* migration path and the "no behavior change" guarantee.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type { Capability } from "./capability";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Manifest for a packaged capability plugin — the capability-side analogue
|
|
35
|
+
* of `LexiconManifest` (../lexicon.ts). Kept as a plain data shape (rather
|
|
36
|
+
* than folded into `CapabilityPlugin`) so it can be serialized alongside a
|
|
37
|
+
* package (e.g. into a future tarball/`meta.json`, matching how lexicons
|
|
38
|
+
* carry `LexiconManifest` in their `BundleSpec`) independent of the runtime
|
|
39
|
+
* plugin object.
|
|
40
|
+
*/
|
|
41
|
+
export interface CapabilityManifest {
|
|
42
|
+
/** Package/plugin name (e.g. "aws", "gcp"), not a capability `kind`. */
|
|
43
|
+
name: string;
|
|
44
|
+
/** Plugin package version (semver). */
|
|
45
|
+
version: string;
|
|
46
|
+
/** Minimum/compatible chant core version, checked the same way as `LexiconManifest.chantVersion` (see ../lexicon-manifest.ts's `checkVersionCompatibility`). */
|
|
47
|
+
chantVersion?: string;
|
|
48
|
+
/** Every capability `kind` this plugin contributes — informational/validation aid; must match what `capabilities()` actually returns. */
|
|
49
|
+
kinds?: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Plugin interface for capability packages — the capability-side analogue of
|
|
54
|
+
* `LexiconPlugin` (../lexicon.ts). A capability plugin ships one or more
|
|
55
|
+
* typed `Capability` implementations, discovered and loaded the same way a
|
|
56
|
+
* lexicon plugin is: dynamically imported by a package-naming convention
|
|
57
|
+
* (`@intentius/chant-capability-<name>`, see ./capability-plugin-loader.ts),
|
|
58
|
+
* validated with a runtime shape guard, then registered into a
|
|
59
|
+
* `CapabilityRegistry` the driver resolves verbs through by `kind`.
|
|
60
|
+
*
|
|
61
|
+
* Required lifecycle method enforces consistency: every capability package
|
|
62
|
+
* must be able to enumerate the capabilities it contributes.
|
|
63
|
+
*/
|
|
64
|
+
export interface CapabilityPlugin {
|
|
65
|
+
// ── Required ──────────────────────────────────────────────
|
|
66
|
+
/** Human-readable plugin/package name (e.g. "aws", "gcp"), not a capability `kind`. */
|
|
67
|
+
readonly name: string;
|
|
68
|
+
|
|
69
|
+
/** Plugin package version (semver), mirrors `LexiconManifest.version`. */
|
|
70
|
+
readonly version: string;
|
|
71
|
+
|
|
72
|
+
/** Return every `Capability` this plugin contributes, keyed for registration by its own `kind`. */
|
|
73
|
+
capabilities(): Array<Capability<never, unknown>>;
|
|
74
|
+
|
|
75
|
+
// ── Optional extensions ───────────────────────────────────
|
|
76
|
+
/** Minimum/compatible chant core version this plugin requires (checked via ../lexicon-manifest.ts's `checkVersionCompatibility`). */
|
|
77
|
+
readonly chantVersion?: string;
|
|
78
|
+
|
|
79
|
+
/** Capability kinds grouped by family, informational — mirrors `STARTER_VERB_FAMILIES` (./registry.ts) shape for third-party plugins that want to publish the same grouping. */
|
|
80
|
+
families?(): Record<string, readonly string[]>;
|
|
81
|
+
|
|
82
|
+
/** Optional initialization hook, called once after load (mirrors `LexiconPlugin.init`). */
|
|
83
|
+
init?(): void | Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Type guard to check if a value is a `CapabilityPlugin`. Checks for the
|
|
88
|
+
* required `name`/`version`/`capabilities` shape, the same defensive check
|
|
89
|
+
* `isLexiconPlugin` (../lexicon.ts) performs before a dynamically-imported
|
|
90
|
+
* package is trusted and registered. This is also chant's detector for a
|
|
91
|
+
* *malformed* capability package (#559 acceptance criteria): a package
|
|
92
|
+
* missing any of these fails the guard and is rejected by the loader with a
|
|
93
|
+
* descriptive error rather than registered half-working.
|
|
94
|
+
*/
|
|
95
|
+
export function isCapabilityPlugin(value: unknown): value is CapabilityPlugin {
|
|
96
|
+
if (
|
|
97
|
+
typeof value !== "object" ||
|
|
98
|
+
value === null ||
|
|
99
|
+
!("name" in value) ||
|
|
100
|
+
typeof (value as Record<string, unknown>).name !== "string" ||
|
|
101
|
+
!("version" in value) ||
|
|
102
|
+
typeof (value as Record<string, unknown>).version !== "string"
|
|
103
|
+
) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const obj = value as Record<string, unknown>;
|
|
107
|
+
return typeof obj.capabilities === "function";
|
|
108
|
+
}
|