@intentius/chant 0.13.1 → 0.15.1
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 +59 -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 +99 -0
- package/src/op/index.ts +3 -1
- package/src/ownership.test.ts +24 -26
- package/src/ownership.ts +26 -38
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP004: gate-needs-temporal
|
|
3
|
+
*
|
|
4
|
+
* Flags a `gate` step anywhere in a component's composition — a durable,
|
|
5
|
+
* human-approval wait that cannot run on the local in-process executor (see
|
|
6
|
+
* ../../../components/driver.ts's `DriverGateUnsupportedError` and
|
|
7
|
+
* docs/components/orchestration.mdx's "Temporal is optional" aside: "gates ...
|
|
8
|
+
* require the durable (Temporal) execution backend").
|
|
9
|
+
*
|
|
10
|
+
* The component contract has no per-component backend/executor field (the
|
|
11
|
+
* backend is chosen per *run*, via `chant run --temporal`, per epic #551 §8 —
|
|
12
|
+
* intentionally not part of the declaration, so the same component graduates
|
|
13
|
+
* to Temporal without changing its definition). That means lint cannot tell,
|
|
14
|
+
* from the declaration alone, whether a given `gate` will actually run under
|
|
15
|
+
* Temporal when this component is deployed — every `gate` is a standing
|
|
16
|
+
* "this component needs the durable backend" fact worth surfacing, not a
|
|
17
|
+
* mistake to silently allow. Acknowledge it explicitly with a **file-level**
|
|
18
|
+
* disable directive once the durable backend is genuinely intended (see
|
|
19
|
+
* lint-rules/disable-directives.mdx) — a COMP* diagnostic carries no real
|
|
20
|
+
* line/column (it is reported for the whole component; see
|
|
21
|
+
* ../../component-checks.ts), so only the file-level `chant-disable` form is
|
|
22
|
+
* honored for these rules — `-line`/`-next-line` do not suppress a COMP*
|
|
23
|
+
* diagnostic (see ../../../cli/commands/lint.ts's
|
|
24
|
+
* `runComponentCheckDiagnostics`):
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* // chant-disable COMP004 -- graduates to --temporal for the human approval wait
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Triggers on: any `gate` step in `deploy`/`rollback` with no matching
|
|
31
|
+
* file-level disable directive.
|
|
32
|
+
* OK: a component with no `gate` steps at all, or a `gate` whose file-level
|
|
33
|
+
* disable directive documents the Temporal graduation.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
37
|
+
import { walkComponent } from "./support";
|
|
38
|
+
|
|
39
|
+
export const comp004GateNeedsTemporalRule: ComponentCheck = {
|
|
40
|
+
id: "COMP004",
|
|
41
|
+
severity: "error",
|
|
42
|
+
category: "correctness",
|
|
43
|
+
description: "A gate step requires the durable (Temporal) execution backend — it cannot run on the local executor",
|
|
44
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
45
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
46
|
+
|
|
47
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
48
|
+
const { gates } = walkComponent(component);
|
|
49
|
+
for (const { gate, phaseName } of gates) {
|
|
50
|
+
diagnostics.push({
|
|
51
|
+
checkId: "COMP004",
|
|
52
|
+
severity: "error",
|
|
53
|
+
component: name,
|
|
54
|
+
file: filePath,
|
|
55
|
+
message:
|
|
56
|
+
`Component "${name}": gate "${gate.signalName}" (phase "${phaseName}") requires the durable (Temporal) ` +
|
|
57
|
+
`execution backend — it cannot run on the local in-process executor. If this component is meant to run ` +
|
|
58
|
+
`with "chant run --temporal", suppress with a file-level "// chant-disable COMP004 -- <reason>" comment ` +
|
|
59
|
+
`anywhere in this file to document that intent explicitly.`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return diagnostics;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP005: capability-kind-is-noun
|
|
3
|
+
*
|
|
4
|
+
* Flags a step `kind` that names a component rather than an operation — the
|
|
5
|
+
* "capability-per-component (renamed sprawl)" failure mode from epic #551
|
|
6
|
+
* §"Failure modes ... + guardrails": "Tell: capabilities named after nouns."
|
|
7
|
+
* Every starter-set verb is a verb-phrase describing an operation
|
|
8
|
+
* (`cfn-deploy`, `publish-image`, `wait-steady-state`, ...); a capability
|
|
9
|
+
* named after *this* component (or any other discovered component) is
|
|
10
|
+
* exactly the sprawl the capability model exists to remove — see
|
|
11
|
+
* docs/components/capabilities.mdx#the-discipline ("A capability describes
|
|
12
|
+
* an operation, never a component").
|
|
13
|
+
*
|
|
14
|
+
* Detection is structural, not NLP: a step `kind` is flagged when it equals,
|
|
15
|
+
* or is built from, a discovered component's own kebab-case `name` (e.g. a
|
|
16
|
+
* `search-service` component composing a `{ kind: "deploy-search-service" }`
|
|
17
|
+
* or `{ kind: "search-service" }` step) — the concrete, checkable form of
|
|
18
|
+
* "named after a component" the issue asks for, mirroring how
|
|
19
|
+
* ../../../components/capability.ts's own docstring defines the discipline
|
|
20
|
+
* ("never named after the component that happens to use it").
|
|
21
|
+
*
|
|
22
|
+
* Every registered `kind` is excluded from this check unconditionally,
|
|
23
|
+
* regardless of whether its dash-separated segments happen to overlap a
|
|
24
|
+
* component's name. The allowlist comes from `ctx.knownKinds` — the kinds the
|
|
25
|
+
* project's capability registry actually resolves, which `chant lint` builds
|
|
26
|
+
* from the project's config (core's starter set plus the active lexicons'
|
|
27
|
+
* leaves, e.g. `cfn-deploy`/`wait-for-stack` when `lexicons: ["aws"]`). Without
|
|
28
|
+
* this exclusion, a component literally named "stack", "job", "image", or
|
|
29
|
+
* "host" would falsely flag `wait-for-stack`/`emr-start-job-run`/`publish-image`/
|
|
30
|
+
* `load-image-on-host` — a real capability's verb-ness is an established fact of
|
|
31
|
+
* the registry, not something to re-adjudicate by substring match. When no
|
|
32
|
+
* registry was resolved (a direct unit test), it falls back to core's starter
|
|
33
|
+
* set (`CORE_STARTER_KINDS`).
|
|
34
|
+
*
|
|
35
|
+
* Triggers on: `{ kind: "search-service" }` or `{ kind: "deploy-search-service" }`
|
|
36
|
+
* in any component, when "search-service" is a discovered component's name and
|
|
37
|
+
* neither form is a registered verb.
|
|
38
|
+
* OK: `{ kind: "cfn-deploy" }`, `{ kind: "ecs-update-service" }`,
|
|
39
|
+
* `{ kind: "wait-for-stack" }` — verb-named, and/or a registered verb.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { STARTER_VERB_FAMILIES } from "../../../components/starter-plugin";
|
|
43
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
44
|
+
import { walkComponent } from "./support";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fallback allowlist when the check runs without a resolved registry (e.g. a
|
|
48
|
+
* direct unit test): core's own starter verbs. The real `chant lint` run passes
|
|
49
|
+
* `ctx.knownKinds` — every kind the project's registry actually resolves,
|
|
50
|
+
* including the active lexicons' leaves (`cfn-deploy`, `wait-for-stack`, …) —
|
|
51
|
+
* so a real project never depends on this fallback for its cloud verbs.
|
|
52
|
+
*/
|
|
53
|
+
const CORE_STARTER_KINDS: ReadonlySet<string> = new Set(Object.values(STARTER_VERB_FAMILIES).flat());
|
|
54
|
+
|
|
55
|
+
/** True if `kind` is exactly a component name, or that name embedded with `-`-joined affixes (e.g. "deploy-search-service", "search-service-apply"). */
|
|
56
|
+
function kindNamesComponent(kind: string, componentName: string): boolean {
|
|
57
|
+
if (kind === componentName) return true;
|
|
58
|
+
const parts = kind.split("-");
|
|
59
|
+
const nameParts = componentName.split("-");
|
|
60
|
+
if (nameParts.length === 0) return false;
|
|
61
|
+
for (let i = 0; i + nameParts.length <= parts.length; i++) {
|
|
62
|
+
if (nameParts.every((p, j) => parts[i + j] === p)) return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const comp005CapabilityKindIsNounRule: ComponentCheck = {
|
|
68
|
+
id: "COMP005",
|
|
69
|
+
severity: "error",
|
|
70
|
+
category: "style",
|
|
71
|
+
description: "A capability kind is named after a component (a noun) rather than an operation (a verb)",
|
|
72
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
73
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
74
|
+
const componentNames = [...ctx.components.keys()];
|
|
75
|
+
if (componentNames.length === 0) return diagnostics;
|
|
76
|
+
|
|
77
|
+
// Every known verb — the project's registered kinds when `chant lint`
|
|
78
|
+
// resolved a registry (includes active lexicons' leaves), else core's
|
|
79
|
+
// starter set. A known verb is never a "noun named after the component",
|
|
80
|
+
// regardless of any substring overlap with a component's name.
|
|
81
|
+
const knownKinds = ctx.knownKinds ?? CORE_STARTER_KINDS;
|
|
82
|
+
|
|
83
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
84
|
+
const { steps } = walkComponent(component);
|
|
85
|
+
const reported = new Set<string>();
|
|
86
|
+
|
|
87
|
+
for (const { step, phaseName } of steps) {
|
|
88
|
+
if (step.kind === "gate") continue;
|
|
89
|
+
if (knownKinds.has(step.kind)) continue;
|
|
90
|
+
const matchedComponent = componentNames.find((cn) => kindNamesComponent(step.kind, cn));
|
|
91
|
+
if (!matchedComponent) continue;
|
|
92
|
+
|
|
93
|
+
const dedupeKey = `${step.kind}:${phaseName}`;
|
|
94
|
+
if (reported.has(dedupeKey)) continue;
|
|
95
|
+
reported.add(dedupeKey);
|
|
96
|
+
|
|
97
|
+
diagnostics.push({
|
|
98
|
+
checkId: "COMP005",
|
|
99
|
+
severity: "error",
|
|
100
|
+
component: name,
|
|
101
|
+
file: filePath,
|
|
102
|
+
message:
|
|
103
|
+
`Component "${name}": step kind "${step.kind}" (phase "${phaseName}") is named after component ` +
|
|
104
|
+
`"${matchedComponent}" rather than an operation. A capability kind must be a verb ("cfn-deploy", ` +
|
|
105
|
+
`"publish-image", "wait-steady-state", ...), never a noun naming the component that uses it — see ` +
|
|
106
|
+
`docs/components/capabilities.mdx#the-discipline.`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return diagnostics;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP006: shell-needs-reason
|
|
3
|
+
*
|
|
4
|
+
* Flags a `shell` step with no (or empty/blank) `reason` — the escape-hatch
|
|
5
|
+
* discipline from epic #551 §7 and §"What chant adds": "raw `shell` without a
|
|
6
|
+
* declared reason" gets flagged. `shell` is the deliberate escape hatch
|
|
7
|
+
* (../../../components/verbs/shell.ts) for reaching past the capability set;
|
|
8
|
+
* `ShellInput.reason` is already a required field on the typed
|
|
9
|
+
* `createShellCapability` input, but a hand-authored/JSON-only component (the
|
|
10
|
+
* contract's whole point — see component-contract.mdx's "portable, not
|
|
11
|
+
* chant-only" framing) is not type-checked against that interface, so the raw
|
|
12
|
+
* JSON contract's `Step` (open `additionalProperties`) does not itself
|
|
13
|
+
* enforce it. This rule enforces the same discipline at the composition
|
|
14
|
+
* level, for every authoring form.
|
|
15
|
+
*
|
|
16
|
+
* Triggers on: `{ kind: "shell", cmd: "..." }` with no `reason`, or
|
|
17
|
+
* `reason: ""` / `reason: " "`.
|
|
18
|
+
* OK: `{ kind: "shell", cmd: "...", reason: "no capability covers <x> yet" }`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
22
|
+
import { walkComponent } from "./support";
|
|
23
|
+
|
|
24
|
+
export const comp006ShellNeedsReasonRule: ComponentCheck = {
|
|
25
|
+
id: "COMP006",
|
|
26
|
+
severity: "error",
|
|
27
|
+
category: "correctness",
|
|
28
|
+
description: 'A raw "shell" escape-hatch step has no declared reason',
|
|
29
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
30
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
31
|
+
|
|
32
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
33
|
+
const { steps } = walkComponent(component);
|
|
34
|
+
|
|
35
|
+
for (const { step, phaseName } of steps) {
|
|
36
|
+
if (step.kind !== "shell") continue;
|
|
37
|
+
const reason = (step as { reason?: unknown }).reason;
|
|
38
|
+
const hasReason = typeof reason === "string" && reason.trim().length > 0;
|
|
39
|
+
if (hasReason) continue;
|
|
40
|
+
|
|
41
|
+
diagnostics.push({
|
|
42
|
+
checkId: "COMP006",
|
|
43
|
+
severity: "error",
|
|
44
|
+
component: name,
|
|
45
|
+
file: filePath,
|
|
46
|
+
message:
|
|
47
|
+
`Component "${name}": "shell" step (phase "${phaseName}") is the escape hatch and must declare why no ` +
|
|
48
|
+
`capability covers this case — add a non-empty "reason" property (e.g. "reason: \\"no capability for X yet\\"").`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return diagnostics;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP007: composition-sprawl
|
|
3
|
+
*
|
|
4
|
+
* Flags two or more components whose `deploy` composition is structurally
|
|
5
|
+
* identical (same phases, same `parallel`/nesting shape, same step kinds in
|
|
6
|
+
* the same order) — the "composition copy-paste (declaration sprawl)"
|
|
7
|
+
* failure mode from epic #551 §"Failure modes ... + guardrails": "Guard:
|
|
8
|
+
* presets (Level 2) + a lint hint when an identical composition repeats
|
|
9
|
+
* across components." This is a *hint*, not a hard error (severity:
|
|
10
|
+
* "warning") — the epic frames it as "reach for a preset"
|
|
11
|
+
* (docs/components/capabilities.mdx#presets--reuse-without-a-closed-set),
|
|
12
|
+
* not a correctness defect.
|
|
13
|
+
*
|
|
14
|
+
* The fingerprint (../comp/support.ts's `compositionFingerprint`) compares
|
|
15
|
+
* phase names, `parallel` flags, nesting, and step *kinds* only — never
|
|
16
|
+
* `name`/`dependsOn`/literal wiring values/params, so two components that
|
|
17
|
+
* both do "build -> publish -> apply -> verify" with the same capability
|
|
18
|
+
* kinds but different templates/clusters/images still match (that is exactly
|
|
19
|
+
* the "own destiny is in the params, not the shape" case a preset should
|
|
20
|
+
* absorb).
|
|
21
|
+
*
|
|
22
|
+
* Triggers on: three components (arbitrary example) that all compose
|
|
23
|
+
* `docker-build` -> `publish-image` -> `cfn-deploy` + `ecs-update-service` ->
|
|
24
|
+
* `wait-steady-state` + `health-gate` with no meaningful structural
|
|
25
|
+
* difference.
|
|
26
|
+
* OK: components whose phase/step-kind shape differs (a different archetype,
|
|
27
|
+
* a different apply family, fan-out vs. no fan-out, etc.) — like the four
|
|
28
|
+
* real pilots, which were deliberately chosen to be structurally distinct
|
|
29
|
+
* (see ../../../components/SPRAWL-VALIDATION.md).
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
33
|
+
import { compositionFingerprint } from "./support";
|
|
34
|
+
|
|
35
|
+
export const comp007CompositionSprawlRule: ComponentCheck = {
|
|
36
|
+
id: "COMP007",
|
|
37
|
+
severity: "warning",
|
|
38
|
+
category: "style",
|
|
39
|
+
description: "An identical composition shape is repeated across components — a declaration-sprawl hint to reach for a preset",
|
|
40
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
41
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
42
|
+
|
|
43
|
+
const byFingerprint = new Map<string, string[]>();
|
|
44
|
+
for (const [name, { component }] of ctx.components) {
|
|
45
|
+
const fp = compositionFingerprint(component);
|
|
46
|
+
byFingerprint.set(fp, [...(byFingerprint.get(fp) ?? []), name]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const names of byFingerprint.values()) {
|
|
50
|
+
if (names.length < 2) continue;
|
|
51
|
+
const sorted = [...names].sort();
|
|
52
|
+
|
|
53
|
+
for (const name of sorted) {
|
|
54
|
+
const others = sorted.filter((n) => n !== name);
|
|
55
|
+
const { filePath } = ctx.components.get(name)!;
|
|
56
|
+
diagnostics.push({
|
|
57
|
+
checkId: "COMP007",
|
|
58
|
+
severity: "warning",
|
|
59
|
+
component: name,
|
|
60
|
+
file: filePath,
|
|
61
|
+
message:
|
|
62
|
+
`Component "${name}" composes an identical phase/step shape to ${others.map((n) => `"${n}"`).join(", ")} ` +
|
|
63
|
+
`— consider extracting a shared preset (docs/components/capabilities.mdx#presets) instead of repeating ` +
|
|
64
|
+
`the composition per component.`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return diagnostics;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP* composition lint rules (#562, epic #551) — the long-term entropy
|
|
3
|
+
* guards over the discovered `Component` graph. See ../../component-checks.ts
|
|
4
|
+
* for why these are `ComponentCheck`s (whole-project, post-discovery) rather
|
|
5
|
+
* than `LintRule`s (single-file AST), and docs/lint-rules/composition.mdx for
|
|
6
|
+
* the user-facing reference.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ComponentCheck } from "../../component-checks";
|
|
10
|
+
import { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
|
|
11
|
+
import { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
|
|
12
|
+
import { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
|
|
13
|
+
import { comp004GateNeedsTemporalRule } from "./comp004-gate-needs-temporal";
|
|
14
|
+
import { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
|
|
15
|
+
import { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
|
|
16
|
+
import { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
|
|
17
|
+
|
|
18
|
+
export { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
|
|
19
|
+
export { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
|
|
20
|
+
export { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
|
|
21
|
+
export { comp004GateNeedsTemporalRule } from "./comp004-gate-needs-temporal";
|
|
22
|
+
export { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
|
|
23
|
+
export { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
|
|
24
|
+
export { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
|
|
25
|
+
|
|
26
|
+
/** All seven COMP* checks, sorted by id. */
|
|
27
|
+
export function loadComponentChecks(): ComponentCheck[] {
|
|
28
|
+
return [
|
|
29
|
+
comp001PublishesNeverAppliesRule,
|
|
30
|
+
comp002DanglingWiringRefRule,
|
|
31
|
+
comp003MutatingNoRollbackRule,
|
|
32
|
+
comp004GateNeedsTemporalRule,
|
|
33
|
+
comp005CapabilityKindIsNounRule,
|
|
34
|
+
comp006ShellNeedsReasonRule,
|
|
35
|
+
comp007CompositionSprawlRule,
|
|
36
|
+
];
|
|
37
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared walking/wiring helpers for the COMP* composition rules (#562, epic
|
|
3
|
+
* #551). Every COMP rule operates over the real, discovered `Component`
|
|
4
|
+
* graph (see ../../component-checks.ts), not raw TypeScript source — these
|
|
5
|
+
* helpers flatten a component's `deploy`/`rollback` phases into steps and
|
|
6
|
+
* parse the wiring reference forms `../../../components/driver.ts` already
|
|
7
|
+
* resolves at runtime, so lint can check the same references before
|
|
8
|
+
* anything runs (per docs/components/orchestration.mdx).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Component, Gate, Phase, Step } from "../../../components/component";
|
|
12
|
+
|
|
13
|
+
/** True if a step/gate/nested-phase entry is a `Gate` (`kind: "gate"`). */
|
|
14
|
+
export function isGateEntry(entry: Step | Gate | Phase): entry is Gate {
|
|
15
|
+
return (entry as { kind?: unknown }).kind === "gate";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** True if a step/gate/nested-phase entry is itself a nested `Phase` (a fan-out unit) — see `component.ts`'s `isNestedPhase`. */
|
|
19
|
+
export function isPhaseEntry(entry: Step | Gate | Phase): entry is Phase {
|
|
20
|
+
return typeof (entry as Phase).phase === "string" && Array.isArray((entry as Phase).steps);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** True if a step/gate/nested-phase entry is a plain capability `Step` (neither a gate nor a nested phase). */
|
|
24
|
+
export function isStepEntry(entry: Step | Gate | Phase): entry is Step {
|
|
25
|
+
return !isGateEntry(entry) && !isPhaseEntry(entry);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One step found while walking a component, together with the (possibly nested) phase it lives directly under. */
|
|
29
|
+
export interface WalkedStep {
|
|
30
|
+
step: Step;
|
|
31
|
+
phaseName: string;
|
|
32
|
+
/**
|
|
33
|
+
* The exact `Phase` object this step lives directly under — object
|
|
34
|
+
* identity, not just `phaseName`, since two different nested fan-out
|
|
35
|
+
* phases (or a nested phase and its parent) can share the same display
|
|
36
|
+
* name. Rules that need "another step in this same phase" (e.g. COMP003's
|
|
37
|
+
* compensation-sibling check) should group by this reference, not by name.
|
|
38
|
+
*/
|
|
39
|
+
phase: Phase;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One gate found while walking a component, together with the phase it lives directly under. */
|
|
43
|
+
export interface WalkedGate {
|
|
44
|
+
gate: Gate;
|
|
45
|
+
phaseName: string;
|
|
46
|
+
phase: Phase;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Flatten every step and gate across a list of phases (including nested
|
|
51
|
+
* fan-out phases and `onFailure` compensation phases), matching
|
|
52
|
+
* `component.ts`'s own `allSteps` walk but keeping the immediate phase
|
|
53
|
+
* alongside each entry (needed for `@Phase.field` reference checks and for
|
|
54
|
+
* "same phase" sibling checks) and separating gates out explicitly.
|
|
55
|
+
*/
|
|
56
|
+
export function walkPhases(phases: Phase[] | undefined): { steps: WalkedStep[]; gates: WalkedGate[] } {
|
|
57
|
+
const steps: WalkedStep[] = [];
|
|
58
|
+
const gates: WalkedGate[] = [];
|
|
59
|
+
|
|
60
|
+
const walk = (ps: Phase[]) => {
|
|
61
|
+
for (const p of ps) {
|
|
62
|
+
for (const entry of p.steps) {
|
|
63
|
+
if (isPhaseEntry(entry)) {
|
|
64
|
+
walk([entry]);
|
|
65
|
+
} else if (isGateEntry(entry)) {
|
|
66
|
+
gates.push({ gate: entry, phaseName: p.phase, phase: p });
|
|
67
|
+
} else {
|
|
68
|
+
steps.push({ step: entry, phaseName: p.phase, phase: p });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (p.onFailure) walk(p.onFailure);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
walk(phases ?? []);
|
|
75
|
+
|
|
76
|
+
return { steps, gates };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Every step and gate across a component's `deploy` plus its top-level `rollback` phases. */
|
|
80
|
+
export function walkComponent(component: Component): { steps: WalkedStep[]; gates: WalkedGate[] } {
|
|
81
|
+
const deployWalk = walkPhases(component.deploy);
|
|
82
|
+
const rollbackWalk = walkPhases(component.rollback);
|
|
83
|
+
return {
|
|
84
|
+
steps: [...deployWalk.steps, ...rollbackWalk.steps],
|
|
85
|
+
gates: [...deployWalk.gates, ...rollbackWalk.gates],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Every distinct phase name declared directly in a component's `deploy` (top-level only, not nested fan-out phase names — those are addressed by the fan-out's own phase name, never the parent's). */
|
|
90
|
+
export function topLevelPhaseNames(component: Component): Set<string> {
|
|
91
|
+
return new Set((component.deploy ?? []).map((p) => p.phase));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Wiring reference parsing (mirrors ../../../components/driver.ts) ────────
|
|
95
|
+
|
|
96
|
+
/** `@Phase.field[...]` — a prior step's output within the same component, keyed by phase name. */
|
|
97
|
+
const PRIOR_STEP_REF = /^@([A-Za-z0-9_ ]+)\.([A-Za-z0-9_.]+)$/;
|
|
98
|
+
/** `@<component>.publish.uri|digest|key` — a cross-component artifact reference. */
|
|
99
|
+
const COMPONENT_ARTIFACT_REF = /^@([a-z0-9]+(?:-[a-z0-9]+)*)\.publish\.(uri|digest|key)$/;
|
|
100
|
+
|
|
101
|
+
export interface PriorStepRef {
|
|
102
|
+
kind: "prior-step";
|
|
103
|
+
phaseName: string;
|
|
104
|
+
field: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ComponentArtifactRef {
|
|
108
|
+
kind: "component-artifact";
|
|
109
|
+
componentName: string;
|
|
110
|
+
field: "uri" | "digest" | "key";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type WiringRef = PriorStepRef | ComponentArtifactRef;
|
|
114
|
+
|
|
115
|
+
/** Parse a single string value as one of the two name-bearing wiring reference forms (`@Phase.field`, `@component.publish.*`). Returns `undefined` for a plain literal, an `$env.*` reference, or a malformed `@` reference (schema validation, not lint, is responsible for rejecting malformed references). */
|
|
116
|
+
export function parseWiringRef(value: unknown): WiringRef | undefined {
|
|
117
|
+
if (typeof value !== "string") return undefined;
|
|
118
|
+
|
|
119
|
+
const artifactMatch = value.match(COMPONENT_ARTIFACT_REF);
|
|
120
|
+
if (artifactMatch) {
|
|
121
|
+
return { kind: "component-artifact", componentName: artifactMatch[1], field: artifactMatch[2] as "uri" | "digest" | "key" };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const priorStepMatch = value.match(PRIOR_STEP_REF);
|
|
125
|
+
if (priorStepMatch) {
|
|
126
|
+
return { kind: "prior-step", phaseName: priorStepMatch[1], field: priorStepMatch[2] };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Recursively collect every wiring-reference-shaped string found anywhere in a step's fields (imageRef, jar, revision, inputs, and any other open capability-specific property), matching how ../../../components/driver.ts's `resolveStepInput` walks a step's input. */
|
|
133
|
+
export function collectWiringRefs(step: Step): WiringRef[] {
|
|
134
|
+
const refs: WiringRef[] = [];
|
|
135
|
+
const walk = (value: unknown): void => {
|
|
136
|
+
if (typeof value === "string") {
|
|
137
|
+
const ref = parseWiringRef(value);
|
|
138
|
+
if (ref) refs.push(ref);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(value)) {
|
|
142
|
+
for (const v of value) walk(v);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (value && typeof value === "object") {
|
|
146
|
+
// `{ stackOutput: { stack, name } }` is a cross-stack reference, resolved
|
|
147
|
+
// by the graph, not a same-project component/phase reference — out of
|
|
148
|
+
// scope for this reference check (see component-contract.mdx).
|
|
149
|
+
if ("stackOutput" in value) return;
|
|
150
|
+
for (const v of Object.values(value as Record<string, unknown>)) walk(v);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const { kind: _kind, ...rest } = step;
|
|
154
|
+
walk(rest);
|
|
155
|
+
return refs;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** A structural fingerprint of a component's composition shape, ignoring `name`/`dependsOn`/wiring literal values — two components with the same fingerprint compose the exact same phase/step-kind shape (see COMP007, "identical composition repeated across components"). */
|
|
159
|
+
export function compositionFingerprint(component: Component): string {
|
|
160
|
+
const shape = (component.deploy ?? []).map((p) => fingerprintPhase(p));
|
|
161
|
+
return JSON.stringify(shape);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function fingerprintPhase(phase: Phase): unknown {
|
|
165
|
+
return {
|
|
166
|
+
parallel: phase.parallel ?? false,
|
|
167
|
+
steps: phase.steps.map((entry) => {
|
|
168
|
+
if (isPhaseEntry(entry)) return fingerprintPhase(entry);
|
|
169
|
+
if (isGateEntry(entry)) return { kind: "gate" };
|
|
170
|
+
return { kind: entry.kind };
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { LexiconIndex, lexiconCompletions, type LexiconEntry } from "./lexicon-providers";
|
|
3
|
+
import type { CompletionContext } from "./types";
|
|
4
|
+
|
|
5
|
+
function resourceEntry(resourceType: string): LexiconEntry {
|
|
6
|
+
return { resourceType, kind: "resource", lexicon: "test" };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Index with 80 "Sample*" resources plus a common "StorageBucket" inserted
|
|
11
|
+
* LAST — so in raw index order it lands well past the old 50-item cap. This is
|
|
12
|
+
* the shape that made gcp v1.152 drop StorageBucket from a "new S" completion.
|
|
13
|
+
*/
|
|
14
|
+
function largeSPrefixIndex(): LexiconIndex {
|
|
15
|
+
const entries: Record<string, LexiconEntry> = {};
|
|
16
|
+
for (let i = 0; i < 80; i++) {
|
|
17
|
+
entries[`Sample${String(i).padStart(3, "0")}`] = resourceEntry(`Test::Sample::N${i}`);
|
|
18
|
+
}
|
|
19
|
+
entries["StorageBucket"] = resourceEntry("Test::Storage::Bucket");
|
|
20
|
+
return new LexiconIndex(entries);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const newCtx = (linePrefix: string, wordAtCursor: string): CompletionContext =>
|
|
24
|
+
({
|
|
25
|
+
uri: "file:///t.ts",
|
|
26
|
+
content: linePrefix,
|
|
27
|
+
linePrefix,
|
|
28
|
+
wordAtCursor,
|
|
29
|
+
position: { line: 0, character: linePrefix.length },
|
|
30
|
+
}) as unknown as CompletionContext;
|
|
31
|
+
|
|
32
|
+
describe("lexiconCompletions resource ranking (#600)", () => {
|
|
33
|
+
test("a prefix returns every match — a common resource isn't truncated by a cap", () => {
|
|
34
|
+
const items = lexiconCompletions(newCtx("const x = new S", "S"), largeSPrefixIndex(), "Test resource");
|
|
35
|
+
const labels = items.map((i) => i.label);
|
|
36
|
+
// 81 resources start with "S"; StorageBucket (inserted 81st, past the old
|
|
37
|
+
// slice(0,50)) must still be present.
|
|
38
|
+
expect(labels).toContain("StorageBucket");
|
|
39
|
+
expect(labels.length).toBe(81);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("results are sorted deterministically (alphabetical)", () => {
|
|
43
|
+
const labels = lexiconCompletions(newCtx("const x = new S", "S"), largeSPrefixIndex(), "Test resource").map(
|
|
44
|
+
(i) => i.label,
|
|
45
|
+
);
|
|
46
|
+
expect(labels).toEqual([...labels].sort((a, b) => a.localeCompare(b)));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("the unfiltered `new ` dump stays capped at 100", () => {
|
|
50
|
+
const entries: Record<string, LexiconEntry> = {};
|
|
51
|
+
for (let i = 0; i < 200; i++) entries[`R${String(i).padStart(3, "0")}`] = resourceEntry(`T::R::${i}`);
|
|
52
|
+
const items = lexiconCompletions(newCtx("const x = new ", ""), new LexiconIndex(entries), "Test resource");
|
|
53
|
+
expect(items.length).toBe(100);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -94,11 +94,21 @@ export function lexiconCompletions(
|
|
|
94
94
|
// After `new ` — suggest resource class names
|
|
95
95
|
if (/\bnew\s+\w*$/.test(linePrefix)) {
|
|
96
96
|
const resources = index.getResourceNames();
|
|
97
|
-
const
|
|
98
|
-
|
|
97
|
+
const prefix = wordAtCursor?.toLowerCase();
|
|
98
|
+
const filtered = prefix
|
|
99
|
+
? resources.filter((r) => r.className.toLowerCase().startsWith(prefix))
|
|
99
100
|
: resources;
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
// Deterministic ordering (the raw index order is arbitrary).
|
|
103
|
+
const sorted = [...filtered].sort((a, b) => a.className.localeCompare(b.className));
|
|
104
|
+
|
|
105
|
+
// When the user has typed a prefix, the set is already narrowed — return
|
|
106
|
+
// every match so a common resource never gets truncated out of a short
|
|
107
|
+
// prefix on a large lexicon (e.g. StorageBucket for "S" on gcp/aws, #600).
|
|
108
|
+
// Only cap the unfiltered `new ` dump, to bound the payload.
|
|
109
|
+
const items = prefix ? sorted : sorted.slice(0, 100);
|
|
110
|
+
|
|
111
|
+
return items.map((r) => ({
|
|
102
112
|
label: r.className,
|
|
103
113
|
insertText: r.className,
|
|
104
114
|
kind: "resource" as const,
|
|
@@ -57,3 +57,41 @@ describe("loadActivities — heartbeat-shim safety", () => {
|
|
|
57
57
|
expect(activities.has("waitForStack")).toBe(true);
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
// Cloud appliers were relocated out of the temporal lexicon into their own
|
|
62
|
+
// lexicons (#706); the loader pulls them in per the project's configured
|
|
63
|
+
// `lexicons`. Proves the relocation resolves end-to-end.
|
|
64
|
+
describe("loadActivities — multi-lexicon (#706)", () => {
|
|
65
|
+
test("base (no lexicons): temporal activities present, cloud appliers absent", async () => {
|
|
66
|
+
const a = await loadActivities();
|
|
67
|
+
expect(a.has("kubectlApply")).toBe(true); // temporal base
|
|
68
|
+
expect(a.has("k3dUp")).toBe(true); // cloud-agnostic, stays in temporal
|
|
69
|
+
expect(a.has("gcpApply")).toBe(false); // relocated to gcp
|
|
70
|
+
expect(a.has("flociUp")).toBe(false); // relocated to aws
|
|
71
|
+
expect(a.has("azGroupEnsure")).toBe(false); // relocated to azure
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("gcp lexicon contributes the GCP applier", async () => {
|
|
75
|
+
const a = await loadActivities(["gcp"]);
|
|
76
|
+
expect(a.has("gcpApply")).toBe(true);
|
|
77
|
+
expect(a.has("kubectlApply")).toBe(true); // base still loaded alongside
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("aws lexicon contributes the Floci lifecycle", async () => {
|
|
81
|
+
const a = await loadActivities(["aws"]);
|
|
82
|
+
expect(a.has("flociUp")).toBe(true);
|
|
83
|
+
expect(a.has("flociDown")).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("azure lexicon contributes the resource-group lifecycle + ARM applier", async () => {
|
|
87
|
+
const a = await loadActivities(["azure"]);
|
|
88
|
+
expect(a.has("azGroupEnsure")).toBe(true);
|
|
89
|
+
expect(a.has("azGroupDelete")).toBe(true);
|
|
90
|
+
expect(a.has("azApply")).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("unknown lexicon is skipped without throwing", async () => {
|
|
94
|
+
const a = await loadActivities(["definitely-not-a-lexicon"]);
|
|
95
|
+
expect(a.has("kubectlApply")).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
});
|