@intentius/chant 0.79.0 → 0.81.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/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +30 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +13 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +44 -3
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +20 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +14 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +46 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +264 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +104 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +48 -4
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +32 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +17 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +110 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +82 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +59 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +42 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +81 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +4 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +102 -36
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +81 -1
- package/src/audit/terraform-state.ts +25 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +132 -29
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +21 -0
- package/src/cli/commands/import-agents.ts +15 -4
- package/src/cli/commands/init.ts +76 -25
- package/src/cli/commands/lint.test.ts +52 -1
- package/src/cli/commands/lint.ts +41 -71
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +18 -5
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +6 -1
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +39 -10
- package/src/cli/handlers/promote.test.ts +377 -0
- package/src/cli/handlers/promote.ts +391 -0
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +158 -0
- package/src/cli/handlers/run-generate.ts +168 -0
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +41 -6
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +137 -1
- package/src/cli/main.ts +290 -24
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +43 -5
- package/src/cli/registry.ts +46 -1
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +9 -3
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +20 -51
- package/src/components/driver.test.ts +90 -12
- package/src/components/driver.ts +43 -5
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/gate-plan.ts +53 -0
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +645 -0
- package/src/components/promote.ts +641 -0
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +161 -11
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +91 -59
- package/src/discovery/fold-import.ts +127 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +18 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +215 -0
- package/src/lexicon.ts +105 -1
- package/src/lifecycle/build-ledger-store.ts +13 -2
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +206 -0
- package/src/lifecycle/legacy-digest.ts +103 -0
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +53 -3
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -23
- package/src/op/gate-approval.ts +3 -1
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/runtimes/local.ts +2 -1
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +198 -0
- package/src/workspace/records-contract.test.ts +93 -0
- package/src/workspace/records.schema.json +150 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +384 -0
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
package/src/config.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { BuildParamsConfig } from "./build-params";
|
|
|
10
10
|
import type { BuildParamProvenance } from "./provenance";
|
|
11
11
|
import { findProjectConfig } from "./project-root";
|
|
12
12
|
import { evaluateProjectConfig } from "./config-sandbox";
|
|
13
|
+
import { lexiconNames, pathLexiconRoot, registerLexiconDeclarations, type LexiconDeclaration } from "./lexicon-module";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* One project-declared environment (chant #1166). Historically always a bare
|
|
@@ -125,8 +126,17 @@ const EnvironmentEntrySchema = z.union([
|
|
|
125
126
|
}),
|
|
126
127
|
]);
|
|
127
128
|
|
|
129
|
+
const LexiconEntrySchema = z.union([
|
|
130
|
+
z.string().min(1),
|
|
131
|
+
z.object({
|
|
132
|
+
name: z.string().min(1),
|
|
133
|
+
module: z.string().min(1),
|
|
134
|
+
root: z.string().min(1).optional(),
|
|
135
|
+
}).strict(),
|
|
136
|
+
]);
|
|
137
|
+
|
|
128
138
|
export const ChantConfigSchema = z.object({
|
|
129
|
-
lexicons: z.array(
|
|
139
|
+
lexicons: z.array(LexiconEntrySchema).optional(),
|
|
130
140
|
capabilities: z.array(z.string().min(1)).optional(),
|
|
131
141
|
environments: z.array(EnvironmentEntrySchema).optional(),
|
|
132
142
|
sourceDir: z.string().min(1).optional(),
|
|
@@ -184,6 +194,9 @@ export const ChantConfigSchema = z.object({
|
|
|
184
194
|
knowledge: z.object({
|
|
185
195
|
dir: z.string().min(1).optional(),
|
|
186
196
|
}).optional(),
|
|
197
|
+
exclude: z.array(z.string().min(1)).optional(),
|
|
198
|
+
include: z.array(z.string().min(1)).optional(),
|
|
199
|
+
rootOnly: z.boolean().optional(),
|
|
187
200
|
}).passthrough();
|
|
188
201
|
|
|
189
202
|
/**
|
|
@@ -192,8 +205,25 @@ export const ChantConfigSchema = z.object({
|
|
|
192
205
|
* Loaded from `chant.config.ts` (preferred) or `chant.config.json`.
|
|
193
206
|
*/
|
|
194
207
|
export interface ChantConfig {
|
|
195
|
-
/**
|
|
196
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Lexicons to load. A bare name (`"aws"`) loads the package
|
|
210
|
+
* `@intentius/chant-lexicon-aws`. `{ name, module }` (#2520) loads a
|
|
211
|
+
* project-local lexicon from a module path, resolved against the directory
|
|
212
|
+
* holding this config; see `./lexicon-module.ts`. An optional `root`
|
|
213
|
+
* (chant#2590) names the directory holding the lexicon's other files.
|
|
214
|
+
*
|
|
215
|
+
* Once loaded, this holds names only: the loader replaces each
|
|
216
|
+
* `{ name, module }` entry with its name and records the path in
|
|
217
|
+
* {@link ChantConfig.lexiconModules}.
|
|
218
|
+
*/
|
|
219
|
+
lexicons?: LexiconDeclaration[];
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Set by the config loader, never written by hand: each path-declared
|
|
223
|
+
* lexicon's name mapped to its absolute module path. Absent when every
|
|
224
|
+
* `lexicons` entry is a bare name.
|
|
225
|
+
*/
|
|
226
|
+
lexiconModules?: Record<string, string>;
|
|
197
227
|
|
|
198
228
|
/**
|
|
199
229
|
* Capability plugin package names to load in addition to the built-in
|
|
@@ -418,14 +448,57 @@ export interface ChantConfig {
|
|
|
418
448
|
/** Bundle directory, relative to the project root. Defaults to `"knowledge"`. */
|
|
419
449
|
dir?: string;
|
|
420
450
|
};
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Globs, relative to the directory holding this config, naming files that
|
|
454
|
+
* source discovery must skip (#2519): `build`, `lint`, `list`, `explain`
|
|
455
|
+
* and every other command that walks the project for declarations never
|
|
456
|
+
* import or lint a matching file. A pattern matches a file when it matches
|
|
457
|
+
* the file's path or the path of any directory above it, so `"ops"` and
|
|
458
|
+
* `"ops/**"` both skip everything under `ops/`. Matched with picomatch,
|
|
459
|
+
* dot files included. Op discovery and component discovery honour it too
|
|
460
|
+
* (#2527). See `./discovery/walk.ts`.
|
|
461
|
+
*/
|
|
462
|
+
exclude?: string[];
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Globs that re-admit files {@link exclude} would skip, and, since #2527,
|
|
466
|
+
* files the discovery walk skips by default: dot-directories, `dist`,
|
|
467
|
+
* git-ignored paths and child projects. `include` always wins over
|
|
468
|
+
* `exclude`, and it never narrows discovery on its own: a file nothing
|
|
469
|
+
* skips is discovered whether or not it matches an `include`. It never
|
|
470
|
+
* re-admits `node_modules`, `.git`, test files, workspace members, or
|
|
471
|
+
* files carrying the generated or skip marker.
|
|
472
|
+
*/
|
|
473
|
+
include?: string[];
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Lets `chant build` and `chant lint` run on this project when it is the
|
|
477
|
+
* root of a declared workspace (#2537, #2524 D0). Without it, or the
|
|
478
|
+
* `--root-only` flag, both commands refuse there with `WSP000` and point to
|
|
479
|
+
* `chant workspace build` and `lint`. With it they act on the root project
|
|
480
|
+
* alone. Member directories and example-group matches are left out of
|
|
481
|
+
* discovery either way, as they are for every walk under a declaration
|
|
482
|
+
* (#2527, `workspaceMemberDirs` in `./discovery/walk.ts`). It means nothing
|
|
483
|
+
* in a project with no `chant.workspace.json`.
|
|
484
|
+
*/
|
|
485
|
+
rootOnly?: boolean;
|
|
421
486
|
}
|
|
422
487
|
|
|
423
488
|
/**
|
|
424
489
|
* Resolved project configuration with metadata about how it was loaded.
|
|
425
490
|
*/
|
|
491
|
+
/**
|
|
492
|
+
* A configuration as the loader returns it. `lexicons` holds names only: each
|
|
493
|
+
* `{ name, module }` entry has been replaced by its name and its path moved
|
|
494
|
+
* to `lexiconModules` (chant #2520), so a reader of a loaded config never
|
|
495
|
+
* sees the object form.
|
|
496
|
+
*/
|
|
497
|
+
export type LoadedChantConfig = Omit<ChantConfig, "lexicons"> & { lexicons?: string[] };
|
|
498
|
+
|
|
426
499
|
export interface ResolvedConfig {
|
|
427
500
|
/** The loaded configuration */
|
|
428
|
-
config:
|
|
501
|
+
config: LoadedChantConfig;
|
|
429
502
|
|
|
430
503
|
/** Path to the config file that was loaded, or undefined if defaults */
|
|
431
504
|
configPath?: string;
|
|
@@ -434,7 +507,7 @@ export interface ResolvedConfig {
|
|
|
434
507
|
/**
|
|
435
508
|
* Default configuration when no config file exists.
|
|
436
509
|
*/
|
|
437
|
-
export const DEFAULT_CHANT_CONFIG:
|
|
510
|
+
export const DEFAULT_CHANT_CONFIG: LoadedChantConfig = {};
|
|
438
511
|
|
|
439
512
|
/**
|
|
440
513
|
* Load project configuration from a directory.
|
|
@@ -499,14 +572,56 @@ export async function loadChantConfig(dir: string): Promise<ResolvedConfig> {
|
|
|
499
572
|
* config declaring any project-level key still wins where it stands.
|
|
500
573
|
*/
|
|
501
574
|
export async function loadChantConfigUpward(startDir: string): Promise<ResolvedConfig> {
|
|
575
|
+
const { dir, configPath } = findProjectConfigPastFragments(startDir);
|
|
576
|
+
if (configPath) warnIfFragmentShadowsProjectConfig(configPath);
|
|
577
|
+
return loadChantConfig(dir);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** The walk {@link loadChantConfigUpward} makes, lint-only fragments skipped, without its warning. */
|
|
581
|
+
export function findProjectConfigPastFragments(startDir: string): { dir: string; configPath?: string } {
|
|
502
582
|
let { dir, configPath } = findProjectConfig(startDir);
|
|
503
583
|
while (configPath && isLintOnlyFragment(configPath)) {
|
|
504
584
|
const parent = dirname(dir);
|
|
505
585
|
if (parent === dir) break;
|
|
506
586
|
({ dir, configPath } = findProjectConfig(parent));
|
|
507
587
|
}
|
|
508
|
-
|
|
509
|
-
|
|
588
|
+
return { dir, configPath };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** A project's discovery globs (#2519) and the directory they are relative to. */
|
|
592
|
+
export interface DiscoveryGlobs {
|
|
593
|
+
/** The directory holding the config that declared the globs. */
|
|
594
|
+
root: string;
|
|
595
|
+
exclude: string[];
|
|
596
|
+
include: string[];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* The `exclude`/`include` globs of the project `startDir` belongs to, found by
|
|
601
|
+
* the same upward walk as {@link loadChantConfigUpward}. `undefined` when the
|
|
602
|
+
* project declares neither. `include` re-admits what `exclude` skips, and
|
|
603
|
+
* also what the discovery walk skips by default (#2527), so it means
|
|
604
|
+
* something on its own.
|
|
605
|
+
*
|
|
606
|
+
* A config that fails to load is left to the command's own config load to
|
|
607
|
+
* report, so discovery keeps working where it did before, unless the failure
|
|
608
|
+
* is in `exclude` or `include` themselves: ignoring those would discover the
|
|
609
|
+
* very files the project asked to skip.
|
|
610
|
+
*/
|
|
611
|
+
export async function resolveDiscoveryGlobs(startDir: string): Promise<DiscoveryGlobs | undefined> {
|
|
612
|
+
const { dir, configPath } = findProjectConfigPastFragments(startDir);
|
|
613
|
+
if (!configPath) return undefined;
|
|
614
|
+
let config: ChantConfig;
|
|
615
|
+
try {
|
|
616
|
+
({ config } = await loadChantConfig(dir));
|
|
617
|
+
} catch (err) {
|
|
618
|
+
if (err instanceof InvalidChantConfigError && (err.key === "exclude" || err.key === "include")) throw err;
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
const exclude = config.exclude ?? [];
|
|
622
|
+
const include = config.include ?? [];
|
|
623
|
+
if (exclude.length === 0 && include.length === 0) return undefined;
|
|
624
|
+
return { root: dir, exclude, include };
|
|
510
625
|
}
|
|
511
626
|
|
|
512
627
|
/**
|
|
@@ -531,7 +646,7 @@ const LINT_FRAGMENT_KEYS = new Set(["$schema", "extends", "rules", "overrides",
|
|
|
531
646
|
*/
|
|
532
647
|
const isCommentKey = (k: string): boolean => k.startsWith("_");
|
|
533
648
|
|
|
534
|
-
function isLintOnlyFragment(configPath: string): boolean {
|
|
649
|
+
export function isLintOnlyFragment(configPath: string): boolean {
|
|
535
650
|
if (!configPath.endsWith("chant.config.json")) return false;
|
|
536
651
|
try {
|
|
537
652
|
const parsed = JSON.parse(readFileSync(configPath, "utf-8")) as unknown;
|
|
@@ -813,10 +928,20 @@ export function resolveKnowledgeDir(config: ChantConfig, projectPath: string): s
|
|
|
813
928
|
return join(projectPath, config.knowledge?.dir ?? "knowledge");
|
|
814
929
|
}
|
|
815
930
|
|
|
931
|
+
/**
|
|
932
|
+
* A config that failed schema validation. `key` is the top-level key of the
|
|
933
|
+
* first problem. `name` stays `"Error"`, so the printed error reads as before.
|
|
934
|
+
*/
|
|
935
|
+
export class InvalidChantConfigError extends Error {
|
|
936
|
+
constructor(message: string, readonly key?: string) {
|
|
937
|
+
super(message);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
816
941
|
/**
|
|
817
942
|
* Validate and normalize a raw config object into ChantConfig shape.
|
|
818
943
|
*/
|
|
819
|
-
function normalizeConfig(raw: Record<string, unknown>, source?: string):
|
|
944
|
+
function normalizeConfig(raw: Record<string, unknown>, source?: string): LoadedChantConfig {
|
|
820
945
|
if (typeof raw !== "object" || raw === null) {
|
|
821
946
|
return DEFAULT_CHANT_CONFIG;
|
|
822
947
|
}
|
|
@@ -826,8 +951,33 @@ function normalizeConfig(raw: Record<string, unknown>, source?: string): ChantCo
|
|
|
826
951
|
const issue = result.error.issues[0];
|
|
827
952
|
const path = issue.path.length > 0 ? issue.path.join(".") : undefined;
|
|
828
953
|
const loc = source ? ` in ${source}` : "";
|
|
829
|
-
throw new
|
|
954
|
+
throw new InvalidChantConfigError(
|
|
955
|
+
`Invalid chant config${loc}: ${path ? `${path}: ` : ""}${issue.message}`,
|
|
956
|
+
issue.path.length > 0 ? String(issue.path[0]) : undefined,
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// chant #2520 — a lexicon declared by module path. The path is recorded for
|
|
961
|
+
// the loaders and the entry becomes its name, so every reader of `lexicons`
|
|
962
|
+
// keeps seeing names. A config of plain names is returned untouched.
|
|
963
|
+
const lexicons = (raw as ChantConfig).lexicons;
|
|
964
|
+
const baseDir = source !== undefined ? dirname(source) : process.cwd();
|
|
965
|
+
const sourceDir = (raw as ChantConfig).sourceDir;
|
|
966
|
+
// chant#2590 — a declared `root` must stay inside the project and must not
|
|
967
|
+
// contain its source directory. Refused here rather than narrowed, since
|
|
968
|
+
// the project asked for it by name.
|
|
969
|
+
for (const [i, entry] of (lexicons ?? []).entries()) {
|
|
970
|
+
if (typeof entry === "string" || entry.root === undefined) continue;
|
|
971
|
+
const { problem } = pathLexiconRoot(entry, baseDir, sourceDir);
|
|
972
|
+
if (problem !== undefined) {
|
|
973
|
+
const loc = source ? ` in ${source}` : "";
|
|
974
|
+
throw new InvalidChantConfigError(`Invalid chant config${loc}: lexicons.${i}.root: ${problem}`, "lexicons");
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const recorded = registerLexiconDeclarations(lexicons, baseDir, sourceDir);
|
|
978
|
+
if (Object.keys(recorded).length > 0) {
|
|
979
|
+
return { ...(raw as ChantConfig), lexicons: lexiconNames(lexicons), lexiconModules: recorded };
|
|
830
980
|
}
|
|
831
981
|
|
|
832
|
-
return raw as
|
|
982
|
+
return raw as LoadedChantConfig;
|
|
833
983
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant #2514 — `contentDigest` is real SHA-256 over UTF-8, and the manifest
|
|
3
|
+
* and SBOM digests built on it separate inputs one byte apart.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, test } from "vitest";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { contentDigest } from "./content-digest";
|
|
8
|
+
import { canonicalJson } from "./effect-receipt";
|
|
9
|
+
import {
|
|
10
|
+
addArchiveEntry,
|
|
11
|
+
computeManifestDigest,
|
|
12
|
+
contentDigest as archiveContentDigest,
|
|
13
|
+
createBuildArchiveManifest,
|
|
14
|
+
} from "./components/verbs/build-archive";
|
|
15
|
+
import { createGenerateSbomCapability } from "./components/verbs/sbom";
|
|
16
|
+
import type { SbomDocument, SbomGenerator } from "./components/verbs/sbom-generator";
|
|
17
|
+
import { computePlanDigest } from "./lifecycle/plan-digest";
|
|
18
|
+
import { hashProps } from "./lifecycle/digest";
|
|
19
|
+
import { isLegacyContentDigest } from "./lifecycle/legacy-digest";
|
|
20
|
+
|
|
21
|
+
describe("contentDigest (#2514)", () => {
|
|
22
|
+
test("matches the FIPS 180-2 test vectors", () => {
|
|
23
|
+
expect(contentDigest("abc")).toBe("sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
|
|
24
|
+
expect(contentDigest("")).toBe("sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("hashes the UTF-8 bytes, not UTF-16 code units", () => {
|
|
28
|
+
const text = "héllo, 世界 🌍";
|
|
29
|
+
const expected = createHash("sha256").update(Buffer.from(text, "utf8")).digest("hex");
|
|
30
|
+
expect(contentDigest(text)).toBe(`sha256:${expected}`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("the build archive exports the same function", () => {
|
|
34
|
+
expect(archiveContentDigest).toBe(contentDigest);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("its output is never mistaken for the old form", () => {
|
|
38
|
+
for (const input of ["", "x", "hello", "{}"]) {
|
|
39
|
+
expect(isLegacyContentDigest(contentDigest(input))).toBe(false);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("inputs one byte apart get different digests", () => {
|
|
44
|
+
expect(contentDigest("template-a")).not.toBe(contentDigest("template-b"));
|
|
45
|
+
expect(contentDigest("abc")).not.toBe(contentDigest("abc "));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("manifest digest (#2514)", () => {
|
|
50
|
+
const entry = (digest: string, path = "web.json") => ({ kind: "template" as const, path, digest });
|
|
51
|
+
|
|
52
|
+
test("is contentDigest over the canonical JSON of the sorted entries", () => {
|
|
53
|
+
const a = entry(contentDigest("{}"), "b.json");
|
|
54
|
+
const b = entry(contentDigest("[]"), "a.json");
|
|
55
|
+
const expected = contentDigest(
|
|
56
|
+
canonicalJson([
|
|
57
|
+
{ kind: b.kind, path: b.path, digest: b.digest },
|
|
58
|
+
{ kind: a.kind, path: a.path, digest: a.digest },
|
|
59
|
+
]),
|
|
60
|
+
);
|
|
61
|
+
expect(computeManifestDigest([a, b])).toBe(expected);
|
|
62
|
+
expect(computeManifestDigest([b, a])).toBe(expected);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("templates one byte apart give different entry and manifest digests", () => {
|
|
66
|
+
const base = createBuildArchiveManifest("web", { now: () => new Date(0) });
|
|
67
|
+
const one = addArchiveEntry(base, entry(contentDigest('{"Resources":{"A":1}}')));
|
|
68
|
+
const two = addArchiveEntry(base, entry(contentDigest('{"Resources":{"A":2}}')));
|
|
69
|
+
expect(one.contents[0]!.digest).not.toBe(two.contents[0]!.digest);
|
|
70
|
+
expect(one.manifestDigest).not.toBe(two.manifestDigest);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("a path cannot be read as a separator", () => {
|
|
74
|
+
// Under the old `kind:path:digest` join these two could be confused.
|
|
75
|
+
const x = computeManifestDigest([entry("sha256:1", "a:template:b")]);
|
|
76
|
+
const y = computeManifestDigest([entry("template:b:sha256:1", "a")]);
|
|
77
|
+
expect(x).not.toBe(y);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("SBOM digest (#2514)", () => {
|
|
82
|
+
function fixedGenerator(bytes: string): SbomGenerator {
|
|
83
|
+
const doc = async (): Promise<SbomDocument> => ({
|
|
84
|
+
format: "spdx",
|
|
85
|
+
mediaType: "application/spdx+json",
|
|
86
|
+
bytes,
|
|
87
|
+
generator: "test",
|
|
88
|
+
});
|
|
89
|
+
return { forImage: doc, forJar: doc, forZip: doc, forDir: doc };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
test("SBOMs one byte apart get different digests, each real SHA-256", async () => {
|
|
93
|
+
const ctx = { env: "dev", component: "web" };
|
|
94
|
+
const input = { artifactType: "image" as const, path: "archive/web.tar", digest: contentDigest("image") };
|
|
95
|
+
const a = await createGenerateSbomCapability(fixedGenerator('{"packages":[1]}')).run(ctx, input);
|
|
96
|
+
const b = await createGenerateSbomCapability(fixedGenerator('{"packages":[2]}')).run(ctx, input);
|
|
97
|
+
expect(a.digest).toBe(contentDigest('{"packages":[1]}'));
|
|
98
|
+
expect(a.digest).not.toBe(b.digest);
|
|
99
|
+
expect(a.manifest.manifestDigest).not.toBe(b.manifest.manifestDigest);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("other hashes use contentDigest over canonicalJson (#2514)", () => {
|
|
104
|
+
test("a plan digest", () => {
|
|
105
|
+
const subject = { b: 1, a: [true, null] };
|
|
106
|
+
expect(computePlanDigest("lifecycle-diff", subject)).toBe(
|
|
107
|
+
contentDigest(canonicalJson({ kind: "lifecycle-diff", subject })),
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("a plan digest orders keys by code unit, not by locale", () => {
|
|
112
|
+
// localeCompare puts "a" before "B"; canonicalJson puts "B" first.
|
|
113
|
+
expect(computePlanDigest("k", { a: 1, B: 2 })).toBe(contentDigest('{"kind":"k","subject":{"B":2,"a":1}}'));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("a props hash, which drops what JSON cannot hold instead of throwing", () => {
|
|
117
|
+
const props = { Name: "x", fn: () => 1, nested: { z: 1, a: undefined } };
|
|
118
|
+
expect(hashProps(props)).toBe(contentDigest('{"Name":"x","nested":{"z":1}}').slice("sha256:".length));
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one content digest chant computes itself (chant #2514).
|
|
3
|
+
*
|
|
4
|
+
* `contentDigest(x)` is `"sha256:"` followed by the lowercase hex SHA-256 of
|
|
5
|
+
* the UTF-8 bytes of `x`. Anything core hashes goes through it, and anything
|
|
6
|
+
* structured is first put in canonical form with `canonicalJson`
|
|
7
|
+
* (./effect-receipt.ts), so the same value always hashes the same way. The
|
|
8
|
+
* build archive re-exports it from ./components/verbs/build-archive.ts, where
|
|
9
|
+
* it used to live.
|
|
10
|
+
*
|
|
11
|
+
* Before #2514 this function computed a 32-bit string hash and repeated its
|
|
12
|
+
* eight hex digits eight times. Values recorded then are recognised and
|
|
13
|
+
* flagged by ./lifecycle/legacy-digest.ts.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createHash } from "node:crypto";
|
|
17
|
+
|
|
18
|
+
/** `sha256:<64 hex>` over the UTF-8 bytes of `input`. */
|
|
19
|
+
export function contentDigest(input: string): string {
|
|
20
|
+
return `sha256:${createHash("sha256").update(input, "utf8").digest("hex")}`;
|
|
21
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
|
-
import { findInfraFiles } from "./files";
|
|
2
|
+
import { findInfraFiles, compileDiscoveryFilter, hasSkipMarker, SKIP_MARKER } from "./files";
|
|
3
3
|
import { withTestDir } from "@intentius/chant-test-utils";
|
|
4
4
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { join } from "node:path";
|
|
@@ -169,3 +169,129 @@ describe("findInfraFiles", () => {
|
|
|
169
169
|
});
|
|
170
170
|
});
|
|
171
171
|
});
|
|
172
|
+
|
|
173
|
+
describe("findInfraFiles — project exclude/include globs (#2519)", () => {
|
|
174
|
+
/** Lay out a project: `files` maps a relative path to its contents. */
|
|
175
|
+
async function project(testDir: string, config: Record<string, unknown> | undefined, files: Record<string, string>) {
|
|
176
|
+
if (config) await writeFile(join(testDir, "chant.config.json"), JSON.stringify(config));
|
|
177
|
+
for (const [rel, body] of Object.entries(files)) {
|
|
178
|
+
await mkdir(join(testDir, rel, ".."), { recursive: true });
|
|
179
|
+
await writeFile(join(testDir, rel), body);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const rel = (testDir: string, files: string[]) => files.map((f) => f.slice(testDir.length + 1)).sort();
|
|
183
|
+
|
|
184
|
+
const LAYOUT = {
|
|
185
|
+
"src/app.ts": "export const app = {};",
|
|
186
|
+
"src/lib/util.ts": "export const util = {};",
|
|
187
|
+
"ops/run.ts": "process.exit(2);",
|
|
188
|
+
"ops/keep.ts": "export const keep = {};",
|
|
189
|
+
".stage/copy.ts": "export const copy = {};",
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
test("with neither key configured, every file but the dot directory's is discovered", async () => {
|
|
193
|
+
await withTestDir(async (testDir) => {
|
|
194
|
+
await project(testDir, { lexicons: ["aws"] }, LAYOUT);
|
|
195
|
+
expect(rel(testDir, await findInfraFiles(testDir))).toEqual([
|
|
196
|
+
"ops/keep.ts", "ops/run.ts", "src/app.ts", "src/lib/util.ts",
|
|
197
|
+
]);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test.each([["ops"], ["ops/**"], ["ops/*.ts"]])("exclude %s skips everything under ops/", async (pattern) => {
|
|
202
|
+
await withTestDir(async (testDir) => {
|
|
203
|
+
await project(testDir, { exclude: [pattern] }, LAYOUT);
|
|
204
|
+
expect(rel(testDir, await findInfraFiles(testDir))).toEqual(["src/app.ts", "src/lib/util.ts"]);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("include re-admits a dot directory the walk skips by default (#2527)", async () => {
|
|
209
|
+
await withTestDir(async (testDir) => {
|
|
210
|
+
await project(testDir, { include: [".stage"] }, LAYOUT);
|
|
211
|
+
expect(rel(testDir, await findInfraFiles(testDir))).toContain(".stage/copy.ts");
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("include wins over exclude", async () => {
|
|
216
|
+
await withTestDir(async (testDir) => {
|
|
217
|
+
await project(testDir, { exclude: ["ops"], include: ["ops/keep.ts"] }, LAYOUT);
|
|
218
|
+
expect(rel(testDir, await findInfraFiles(testDir))).toEqual([
|
|
219
|
+
"ops/keep.ts", "src/app.ts", "src/lib/util.ts",
|
|
220
|
+
]);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("exclude everything and include a directory narrows discovery to it", async () => {
|
|
225
|
+
await withTestDir(async (testDir) => {
|
|
226
|
+
await project(testDir, { exclude: ["**"], include: ["src"] }, LAYOUT);
|
|
227
|
+
expect(rel(testDir, await findInfraFiles(testDir))).toEqual(["src/app.ts", "src/lib/util.ts"]);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("include alone changes nothing", async () => {
|
|
232
|
+
await withTestDir(async (testDir) => {
|
|
233
|
+
await project(testDir, { include: ["src"] }, LAYOUT);
|
|
234
|
+
expect(await findInfraFiles(testDir)).toHaveLength(4);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("patterns stay relative to the project root when discovery starts in a subdirectory", async () => {
|
|
239
|
+
await withTestDir(async (testDir) => {
|
|
240
|
+
await project(testDir, { exclude: ["src/lib"] }, LAYOUT);
|
|
241
|
+
expect(rel(testDir, await findInfraFiles(join(testDir, "src")))).toEqual(["src/app.ts"]);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("include does not re-admit test files", async () => {
|
|
246
|
+
await withTestDir(async (testDir) => {
|
|
247
|
+
await project(testDir, { exclude: ["ops"], include: ["**"] }, { ...LAYOUT, "src/app.test.ts": "test();" });
|
|
248
|
+
expect(rel(testDir, await findInfraFiles(testDir))).not.toContain("src/app.test.ts");
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("explicit globs override the config; null applies none", async () => {
|
|
253
|
+
await withTestDir(async (testDir) => {
|
|
254
|
+
await project(testDir, { exclude: ["ops"] }, LAYOUT);
|
|
255
|
+
expect(await findInfraFiles(testDir, { globs: null })).toHaveLength(4);
|
|
256
|
+
const only = await findInfraFiles(testDir, { globs: { root: testDir, exclude: ["src"], include: [] } });
|
|
257
|
+
expect(rel(testDir, only)).toEqual(["ops/keep.ts", "ops/run.ts"]);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("compileDiscoveryFilter (#2519)", () => {
|
|
263
|
+
test("is undefined with no exclude patterns", () => {
|
|
264
|
+
expect(compileDiscoveryFilter(undefined)).toBeUndefined();
|
|
265
|
+
expect(compileDiscoveryFilter({ root: "/p", exclude: [], include: ["**"] })).toBeUndefined();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("never skips a file outside the root", () => {
|
|
269
|
+
const skip = compileDiscoveryFilter({ root: "/p/app", exclude: ["**"], include: [] })!;
|
|
270
|
+
expect(skip("/p/app/x.ts")).toBe(true);
|
|
271
|
+
expect(skip("/p/other/x.ts")).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("findInfraFiles — skip marker (#2519)", () => {
|
|
276
|
+
test.each([
|
|
277
|
+
["a line comment", `// ${SKIP_MARKER}\nprocess.exit(2);`],
|
|
278
|
+
["a line comment after a shebang", `#!/usr/bin/env -S node --import tsx\n// ${SKIP_MARKER}: a runner, not a declaration\nprocess.exit(2);`],
|
|
279
|
+
["a block comment", `/**\n * Runs an Op.\n * ${SKIP_MARKER}\n */\nprocess.exit(2);`],
|
|
280
|
+
])("skips a file carrying the marker in %s", async (_label, body) => {
|
|
281
|
+
await withTestDir(async (testDir) => {
|
|
282
|
+
await writeFile(join(testDir, "app.ts"), "export const app = {};");
|
|
283
|
+
await writeFile(join(testDir, "run.ts"), body);
|
|
284
|
+
const files = await findInfraFiles(testDir);
|
|
285
|
+
expect(files).toHaveLength(1);
|
|
286
|
+
expect(files[0]).toMatch(/app\.ts$/);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test.each([
|
|
291
|
+
["in a string", `export const s = "${SKIP_MARKER}";`],
|
|
292
|
+
["as a longer word", `// ${SKIP_MARKER}-not\nexport const a = {};`],
|
|
293
|
+
["past the head", `${"// padding\n".repeat(100)}// ${SKIP_MARKER}\nexport const a = {};`],
|
|
294
|
+
])("does not treat the marker %s as a skip", (_label, body) => {
|
|
295
|
+
expect(hasSkipMarker(body)).toBe(false);
|
|
296
|
+
});
|
|
297
|
+
});
|