@intentius/chant 0.80.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/discover.d.ts +8 -16
- 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 +4 -5
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +25 -9
- package/dist/cli/commands/audit.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.map +1 -1
- package/dist/cli/commands/lint.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/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/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +7 -4
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/run-generate.d.ts.map +1 -1
- 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/plugins.d.ts +12 -7
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +32 -0
- 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/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/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/promote.d.ts +60 -7
- package/dist/components/promote.d.ts.map +1 -1
- 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 +32 -10
- 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 +10 -5
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +20 -4
- 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 +4 -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 +51 -6
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon.d.ts +58 -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 +41 -8
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -1
- 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 +18 -2
- 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/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/discover.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/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/records-cli.d.ts +28 -4
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records.d.ts.map +1 -1
- 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 +2 -1
- package/src/audit/discover.ts +66 -78
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +4 -9
- package/src/audit/terraform-state.ts +4 -5
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +89 -60
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +18 -0
- package/src/cli/commands/import-agents.ts +6 -2
- package/src/cli/commands/init.ts +22 -3
- package/src/cli/commands/lint.test.ts +17 -23
- package/src/cli/commands/lint.ts +24 -84
- package/src/cli/commands/update.ts +13 -4
- package/src/cli/commands/vendor.ts +11 -6
- 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.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 +5 -3
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +37 -9
- package/src/cli/handlers/promote.test.ts +118 -2
- package/src/cli/handlers/promote.ts +82 -16
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +6 -8
- package/src/cli/handlers/run-generate.ts +36 -1
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +28 -5
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +105 -1
- package/src/cli/main.ts +230 -17
- package/src/cli/plugins.ts +17 -17
- package/src/cli/registry.ts +32 -0
- 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/cli-support.ts +6 -1
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +13 -68
- package/src/components/driver.test.ts +78 -1
- package/src/components/driver.ts +43 -7
- package/src/components/gate-plan.ts +53 -0
- package/src/components/promote.test.ts +276 -2
- package/src/components/promote.ts +119 -15
- 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 +2 -2
- package/src/config.ts +48 -15
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +10 -10
- package/src/discovery/files.ts +25 -81
- package/src/discovery/fold-import.ts +49 -5
- 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/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +4 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.ts +108 -10
- package/src/lexicon.ts +79 -1
- package/src/lifecycle/build-ledger-store.ts +11 -10
- 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 +115 -23
- package/src/lifecycle/legacy-digest.ts +60 -19
- 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 +23 -7
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- 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/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -27
- 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/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/records-cli.ts +86 -14
- package/src/workspace/records-contract.test.ts +5 -0
- package/src/workspace/records.schema.json +32 -2
- package/src/workspace/records.ts +3 -2
- 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/dist/components/unbound-gate-approval.d.ts +0 -24
- package/dist/components/unbound-gate-approval.d.ts.map +0 -1
- package/dist/discovery/convergence.d.ts +0 -97
- package/dist/discovery/convergence.d.ts.map +0 -1
- package/src/cli/commands/audit-walk-warnings.test.ts +0 -156
- package/src/components/unbound-gate-approval.test.ts +0 -82
- package/src/components/unbound-gate-approval.ts +0 -51
- package/src/discovery/convergence.test.ts +0 -231
- package/src/discovery/convergence.ts +0 -399
- package/src/op/discover-convergence.test.ts +0 -70
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declaration checks (#2535; #2524 D16, ws-028).
|
|
3
|
+
*
|
|
4
|
+
* A {@link WorkspaceCheck} looks at the declaration, the tree it describes
|
|
5
|
+
* and the kinds the workspace can read, and returns findings in the
|
|
6
|
+
* post-synth diagnostic shape with a `WSP` id. `chant workspace check` runs
|
|
7
|
+
* them and prints the findings through lint's reporters, so `--format json`
|
|
8
|
+
* and `--format sarif` come for free. `chant lint` never loads this module.
|
|
9
|
+
*
|
|
10
|
+
* The declaration sets the severity of a check through `checks`, and
|
|
11
|
+
* suppresses a check for one entry through that entry's `suppress`. Some
|
|
12
|
+
* checks are fixed: an unknown kind, a tie between probes and a probe that
|
|
13
|
+
* claims an `other` directory always fail (#2524 D3), and a declaration that
|
|
14
|
+
* can't be read has no settings to apply.
|
|
15
|
+
*
|
|
16
|
+
* Checks that need more than the declaration and the tree read facts
|
|
17
|
+
* gathered before any check runs ({@link WorkspaceFacts}): member ledgers
|
|
18
|
+
* (./checks/ledgers.ts), recorded pipelines (./checks/pipelines.ts) and
|
|
19
|
+
* generated files (./checks/generated.ts). All of them share one id range
|
|
20
|
+
* (#2641):
|
|
21
|
+
*
|
|
22
|
+
* | Ids | Checks |
|
|
23
|
+
* |---|---|
|
|
24
|
+
* | WSP001 to WSP011 | the declaration and member kinds (this file) |
|
|
25
|
+
* | WSP071, WSP072 | member ledgers |
|
|
26
|
+
* | WSP081 to WSP083 | recorded pipelines |
|
|
27
|
+
* | WSP091 to WSP097 | member links (#2539) |
|
|
28
|
+
* | WSP101 to WSP106 | generated files |
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { realpathSync } from "node:fs";
|
|
32
|
+
import { relative, sep } from "node:path";
|
|
33
|
+
import type { LintDiagnostic, LintRule, Severity } from "../lint/rule";
|
|
34
|
+
import type { PostSynthDiagnostic } from "../lint/post-synth";
|
|
35
|
+
import { readDeclaration, resolveGroups, WorkspaceReadError, type Declaration, type Entry, type ErrorLocation, type ResolvedGroup } from "./declaration";
|
|
36
|
+
import type { ReasonCode } from "./reason-codes";
|
|
37
|
+
import { parseJsonText, pointerToken, type TextLocation } from "./jsonc";
|
|
38
|
+
import { loadKindRegistry, probeKind, resolveKind, type KindLoadProblem, type KindRegistry } from "./kinds";
|
|
39
|
+
import { gitTop, workingTree, type WorkspaceTree } from "./tree";
|
|
40
|
+
import { LINK_CHECKS, linkTable } from "./checks/links";
|
|
41
|
+
import type { LinkTableRow } from "./links";
|
|
42
|
+
import { gatherGeneratedFacts, GENERATED_CHECKS, type GeneratedFileFacts } from "./checks/generated";
|
|
43
|
+
import { gatherLedgerFacts, LEDGER_CHECKS, type MemberLedgerFacts } from "./checks/ledgers";
|
|
44
|
+
import { gatherPipelineFacts, PIPELINE_CHECKS, type MemberPipelineFacts } from "./checks/pipelines";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* What the checks beyond the declaration read, gathered from the checkout
|
|
48
|
+
* before any check runs. A field left out was not gathered, and the checks
|
|
49
|
+
* that read it find nothing.
|
|
50
|
+
*/
|
|
51
|
+
export interface WorkspaceFacts {
|
|
52
|
+
/** Each `chant` member's ownership stack, environments and chant version. */
|
|
53
|
+
ledgers?: readonly MemberLedgerFacts[];
|
|
54
|
+
/** Each member's recorded generated files and the environments they deploy. */
|
|
55
|
+
pipelines?: readonly MemberPipelineFacts[];
|
|
56
|
+
/** Each declared or implicit generated file, with what its generator produced. */
|
|
57
|
+
generated?: readonly GeneratedFileFacts[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** What every declaration check reads. */
|
|
61
|
+
export interface WorkspaceCheckContext {
|
|
62
|
+
declaration: Declaration;
|
|
63
|
+
tree: WorkspaceTree;
|
|
64
|
+
groups: ResolvedGroup[];
|
|
65
|
+
kinds: KindRegistry;
|
|
66
|
+
/** Why some pinned kinds could not be read. */
|
|
67
|
+
kindProblems: KindLoadProblem[];
|
|
68
|
+
/** The facts gathered before the checks ran. */
|
|
69
|
+
facts?: WorkspaceFacts;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A finding from a declaration check: the post-synth diagnostic shape, with
|
|
74
|
+
* `entity` naming the entry it is about and `pointer` the place in the
|
|
75
|
+
* declaration to show.
|
|
76
|
+
*/
|
|
77
|
+
export interface WorkspaceDiagnostic extends PostSynthDiagnostic {
|
|
78
|
+
/** A JSON Pointer into the declaration. */
|
|
79
|
+
pointer: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WorkspaceCheck {
|
|
83
|
+
/** `WSP` and three digits. Public once shipped. */
|
|
84
|
+
id: string;
|
|
85
|
+
/** A short name, for listings. */
|
|
86
|
+
name: string;
|
|
87
|
+
description: string;
|
|
88
|
+
severity: Severity;
|
|
89
|
+
/** False when the declaration may neither change the severity nor suppress it. */
|
|
90
|
+
configurable: boolean;
|
|
91
|
+
check(ctx: WorkspaceCheckContext): WorkspaceDiagnostic[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A diagnostic ready for lint's reporters, with the entry it is about. */
|
|
95
|
+
export interface WorkspaceFinding extends LintDiagnostic {
|
|
96
|
+
entity?: string;
|
|
97
|
+
/** The read contract's reason code, for a WSP001 finding: why the declaration can't be read (#2536). */
|
|
98
|
+
code?: ReasonCode;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface SuppressedFinding extends WorkspaceFinding {
|
|
102
|
+
reason: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface DeclarationCheckReport {
|
|
106
|
+
/** The declaration file, relative to the directory the report was made for. */
|
|
107
|
+
file: string;
|
|
108
|
+
diagnostics: WorkspaceFinding[];
|
|
109
|
+
suppressed: SuppressedFinding[];
|
|
110
|
+
/** The member links, declared and inferred, as resolved in source (#2539). */
|
|
111
|
+
links: LinkTableRow[];
|
|
112
|
+
/** Whether an error-severity finding is active. */
|
|
113
|
+
ok: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const HELP = "https://intentius.io/chant/cli/workspace-check/#declaration-checks";
|
|
117
|
+
|
|
118
|
+
const memberDir = (dir: string) => (dir === "." ? "" : dir);
|
|
119
|
+
|
|
120
|
+
function entryFinding(check: WorkspaceCheck, entry: Entry, field: string | null, message: string): WorkspaceDiagnostic {
|
|
121
|
+
return {
|
|
122
|
+
checkId: check.id,
|
|
123
|
+
severity: check.severity,
|
|
124
|
+
message,
|
|
125
|
+
entity: entry.name,
|
|
126
|
+
pointer: field ? `${entry.pointer}/${field}` : entry.pointer,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The dir exists and the kind is known: the members every probe check reads. */
|
|
131
|
+
function readableMembers(ctx: WorkspaceCheckContext) {
|
|
132
|
+
return ctx.declaration.members.filter((m) => ctx.tree.stat(memberDir(m.dir)) === "dir" && ctx.kinds.get(m.kind) !== undefined);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The first id of the read failure, used by {@link runDeclarationChecks} when the declaration can't be read. */
|
|
136
|
+
export const UNREADABLE_CHECK_ID = "WSP001";
|
|
137
|
+
|
|
138
|
+
export const WORKSPACE_CHECKS: readonly WorkspaceCheck[] = [
|
|
139
|
+
{
|
|
140
|
+
id: "WSP001",
|
|
141
|
+
name: "declaration-unreadable",
|
|
142
|
+
description: "The declaration can be read: it parses, matches the schema and keeps the placement rules.",
|
|
143
|
+
severity: "error",
|
|
144
|
+
configurable: false,
|
|
145
|
+
// Reported by runDeclarationChecks, since a declaration that can't be read gives no context.
|
|
146
|
+
check: () => [],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "WSP002",
|
|
150
|
+
name: "kinds-unreadable",
|
|
151
|
+
description: "Every pinned package's kinds can be read: it is installed at the pinned version, and its ./workspace-kinds file is valid kind data.",
|
|
152
|
+
severity: "error",
|
|
153
|
+
configurable: false,
|
|
154
|
+
check(ctx) {
|
|
155
|
+
return ctx.kindProblems.map((p) => ({ checkId: this.id, severity: this.severity, message: p.message, pointer: `/pins/${p.pin}` }));
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: "WSP003",
|
|
160
|
+
name: "kind-unknown",
|
|
161
|
+
description: "Every member's kind is built in or supplied by a pinned package. Unknown kinds fail closed.",
|
|
162
|
+
severity: "error",
|
|
163
|
+
configurable: false,
|
|
164
|
+
check(ctx) {
|
|
165
|
+
return ctx.declaration.members
|
|
166
|
+
.filter((m) => ctx.kinds.get(m.kind) === undefined)
|
|
167
|
+
.map((m) => entryFinding(this, m, "kind", `member ${m.name} has kind ${m.kind}, which no built-in kind or pinned package supplies; known kinds: ${ctx.kinds.names().join(", ")}`));
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "WSP004",
|
|
172
|
+
name: "member-dir-missing",
|
|
173
|
+
description: "Every member's directory exists.",
|
|
174
|
+
severity: "error",
|
|
175
|
+
configurable: true,
|
|
176
|
+
check(ctx) {
|
|
177
|
+
return ctx.declaration.members
|
|
178
|
+
.filter((m) => ctx.tree.stat(memberDir(m.dir)) !== "dir")
|
|
179
|
+
.map((m) => entryFinding(this, m, "dir", `member ${m.name}'s directory ${m.dir} does not exist${ctx.tree.label}`));
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "WSP005",
|
|
184
|
+
name: "kind-probe-failed",
|
|
185
|
+
description: "Every member's directory is what its kind reads, such as a chant config for a chant member.",
|
|
186
|
+
severity: "error",
|
|
187
|
+
configurable: true,
|
|
188
|
+
check(ctx) {
|
|
189
|
+
return readableMembers(ctx)
|
|
190
|
+
.filter((m) => !probeKind(ctx.kinds.get(m.kind)!, ctx.tree, memberDir(m.dir)))
|
|
191
|
+
.map((m) => entryFinding(this, m, "kind", `member ${m.name} (${m.dir}) is not ${ctx.kinds.get(m.kind)!.description}`));
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "WSP006",
|
|
196
|
+
name: "kind-probe-tie",
|
|
197
|
+
description: "No directory is claimed by two kinds of the same highest precedence. Ties fail.",
|
|
198
|
+
severity: "error",
|
|
199
|
+
configurable: false,
|
|
200
|
+
check(ctx) {
|
|
201
|
+
const out: WorkspaceDiagnostic[] = [];
|
|
202
|
+
for (const m of readableMembers(ctx)) {
|
|
203
|
+
const { tie } = resolveKind(ctx.kinds, ctx.tree, memberDir(m.dir), m.dir === "." ? ["workspace"] : []);
|
|
204
|
+
if (tie.length === 0) continue;
|
|
205
|
+
const names = tie.map((k) => `${k.name} (${k.source})`).join(" and ");
|
|
206
|
+
out.push(entryFinding(this, m, "kind", `the probes of ${names} all claim ${m.dir} with precedence ${tie[0].precedence}; a tie fails, so one of those kinds needs a different precedence`));
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: "WSP007",
|
|
213
|
+
name: "kind-outranked",
|
|
214
|
+
description: "When several kinds claim a member's directory, the declared kind is the one the precedence order picks.",
|
|
215
|
+
severity: "error",
|
|
216
|
+
configurable: true,
|
|
217
|
+
check(ctx) {
|
|
218
|
+
const out: WorkspaceDiagnostic[] = [];
|
|
219
|
+
for (const m of readableMembers(ctx)) {
|
|
220
|
+
if (m.kind === "other") continue;
|
|
221
|
+
const { winner } = resolveKind(ctx.kinds, ctx.tree, memberDir(m.dir), m.dir === "." ? ["workspace"] : []);
|
|
222
|
+
if (!winner || winner.name === m.kind) continue;
|
|
223
|
+
out.push(entryFinding(this, m, "kind", `member ${m.name} is declared ${m.kind}, and kind ${winner.name} (precedence ${winner.precedence}) claims ${m.dir} first; declare it as ${winner.name}`));
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: "WSP008",
|
|
230
|
+
name: "other-claimed",
|
|
231
|
+
description: "No other member's directory is claimed by a registered kind's probe.",
|
|
232
|
+
severity: "error",
|
|
233
|
+
configurable: false,
|
|
234
|
+
check(ctx) {
|
|
235
|
+
const out: WorkspaceDiagnostic[] = [];
|
|
236
|
+
for (const m of readableMembers(ctx)) {
|
|
237
|
+
if (m.kind !== "other") continue;
|
|
238
|
+
const { claims } = resolveKind(ctx.kinds, ctx.tree, memberDir(m.dir), m.dir === "." ? ["workspace"] : []);
|
|
239
|
+
if (claims.length === 0) continue;
|
|
240
|
+
out.push(entryFinding(this, m, "kind", `member ${m.name} is declared other, and kind ${claims[0].name} claims ${m.dir}: it is ${claims[0].description}; declare it as ${claims[0].name}`));
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "WSP009",
|
|
247
|
+
name: "other-member",
|
|
248
|
+
description: "A member of kind other is one chant does not read. It is reported so that it stays a decision.",
|
|
249
|
+
severity: "warning",
|
|
250
|
+
configurable: true,
|
|
251
|
+
check(ctx) {
|
|
252
|
+
return ctx.declaration.members
|
|
253
|
+
.filter((m) => m.kind === "other")
|
|
254
|
+
.map((m) => entryFinding(this, m, null, `member ${m.name} (${m.dir}) is kind other, which chant does not read: ${m.because}`));
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: "WSP010",
|
|
259
|
+
name: "group-empty",
|
|
260
|
+
description: "Every example group matches at least one chant project.",
|
|
261
|
+
severity: "warning",
|
|
262
|
+
configurable: true,
|
|
263
|
+
check(ctx) {
|
|
264
|
+
return ctx.groups
|
|
265
|
+
.filter((g) => g.matches.length === 0)
|
|
266
|
+
.map((g) => entryFinding(this, g.group, "glob", `example group ${g.group.name} matches no chant project${ctx.tree.label}`));
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "WSP011",
|
|
271
|
+
name: "check-settings-invalid",
|
|
272
|
+
description: "The declaration's checks and suppress settings name known, configurable WSP ids.",
|
|
273
|
+
severity: "error",
|
|
274
|
+
configurable: false,
|
|
275
|
+
check(ctx) {
|
|
276
|
+
const out: WorkspaceDiagnostic[] = [];
|
|
277
|
+
for (const id of Object.keys(ctx.declaration.checks)) {
|
|
278
|
+
const problem = settingProblem(id);
|
|
279
|
+
if (problem) out.push({ checkId: this.id, severity: this.severity, message: `checks sets ${id}, which ${problem}`, pointer: `/checks/${pointerToken(id)}` });
|
|
280
|
+
}
|
|
281
|
+
for (const e of ctx.declaration.entries) {
|
|
282
|
+
e.suppress.forEach((s, i) => {
|
|
283
|
+
const problem = settingProblem(s.check);
|
|
284
|
+
if (problem) out.push(entryFinding(this, e, `suppress/${i}/check`, `${e.name} suppresses ${s.check}, which ${problem}`));
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
// Member ledgers (#2538).
|
|
291
|
+
...LEDGER_CHECKS,
|
|
292
|
+
// Recorded pipelines (#2542).
|
|
293
|
+
...PIPELINE_CHECKS,
|
|
294
|
+
// Member links (#2539), WSP091 to WSP097.
|
|
295
|
+
...LINK_CHECKS,
|
|
296
|
+
// Generated files (#2541).
|
|
297
|
+
...GENERATED_CHECKS,
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
const BY_ID = new Map(WORKSPACE_CHECKS.map((c) => [c.id, c]));
|
|
301
|
+
|
|
302
|
+
function settingProblem(id: string): string | undefined {
|
|
303
|
+
const check = BY_ID.get(id);
|
|
304
|
+
if (!check) return `is not a declaration check; known checks: ${WORKSPACE_CHECKS.map((c) => c.id).join(", ")}`;
|
|
305
|
+
if (!check.configurable) return `is fixed: it can't be turned down or suppressed`;
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The checks' metadata as lint rules, for the SARIF reporter's rule list.
|
|
311
|
+
* They are never run as lint rules.
|
|
312
|
+
*/
|
|
313
|
+
export function workspaceCheckRules(): LintRule[] {
|
|
314
|
+
return WORKSPACE_CHECKS.map((c) => ({
|
|
315
|
+
id: c.id,
|
|
316
|
+
severity: c.severity,
|
|
317
|
+
category: "correctness",
|
|
318
|
+
description: `${c.name}: ${c.description}`,
|
|
319
|
+
helpUri: HELP,
|
|
320
|
+
check: () => [],
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Run every check over a readable declaration. The settings are not applied yet. */
|
|
325
|
+
export function runWorkspaceChecks(ctx: WorkspaceCheckContext): WorkspaceDiagnostic[] {
|
|
326
|
+
return WORKSPACE_CHECKS.flatMap((c) => c.check(ctx));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Apply the declaration's `checks` severities and entries' `suppress` to the
|
|
331
|
+
* findings. Fixed checks keep their severity and can't be suppressed; a
|
|
332
|
+
* setting that tries is itself a WSP011 finding.
|
|
333
|
+
*/
|
|
334
|
+
export function applyCheckSettings(
|
|
335
|
+
declaration: Declaration,
|
|
336
|
+
findings: WorkspaceDiagnostic[],
|
|
337
|
+
): { active: WorkspaceDiagnostic[]; suppressed: (WorkspaceDiagnostic & { reason: string })[] } {
|
|
338
|
+
const active: WorkspaceDiagnostic[] = [];
|
|
339
|
+
const suppressed: (WorkspaceDiagnostic & { reason: string })[] = [];
|
|
340
|
+
const byName = new Map(declaration.entries.map((e) => [e.name, e]));
|
|
341
|
+
for (const f of findings) {
|
|
342
|
+
const check = BY_ID.get(f.checkId);
|
|
343
|
+
if (!check?.configurable) {
|
|
344
|
+
active.push(f);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const setting = declaration.checks[f.checkId];
|
|
348
|
+
if (setting === "off") continue;
|
|
349
|
+
const finding = setting ? { ...f, severity: setting } : f;
|
|
350
|
+
const suppression = f.entity !== undefined ? byName.get(f.entity)?.suppress.find((s) => s.check === f.checkId) : undefined;
|
|
351
|
+
if (suppression) suppressed.push({ ...finding, reason: suppression.because });
|
|
352
|
+
else active.push(finding);
|
|
353
|
+
}
|
|
354
|
+
return { active, suppressed };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export interface DeclarationCheckOptions {
|
|
358
|
+
/**
|
|
359
|
+
* Gather the facts the ledger, pipeline and generated-file checks read.
|
|
360
|
+
* On by default. Off, only the declaration and kind checks find anything,
|
|
361
|
+
* and no member config is read.
|
|
362
|
+
*/
|
|
363
|
+
gather?: boolean;
|
|
364
|
+
/** Run each declared generator and compare its output (`--generated`). Off by default: generators run member code. */
|
|
365
|
+
runGenerators?: boolean;
|
|
366
|
+
/**
|
|
367
|
+
* The files to check, such as a revision's (`chant workspace check --at`,
|
|
368
|
+
* #2536); the working tree under `root` by default. Kinds still come from
|
|
369
|
+
* the packages installed under `root`. With a tree, no facts are
|
|
370
|
+
* gathered: they describe the working tree, not the revision.
|
|
371
|
+
*/
|
|
372
|
+
tree?: WorkspaceTree;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Gather {@link WorkspaceFacts} for the declaration in `root`. Member
|
|
377
|
+
* configs are read statically, never run: the ledger facts read `ownership`
|
|
378
|
+
* and `environments`, and the generated-file facts read `lexicons`. Declared
|
|
379
|
+
* generators run only when `runGenerators` says so, and they are the only
|
|
380
|
+
* member code this runs.
|
|
381
|
+
*/
|
|
382
|
+
export async function gatherWorkspaceFacts(root: string, declaration: Declaration, options: { runGenerators?: boolean } = {}): Promise<WorkspaceFacts> {
|
|
383
|
+
// Pipeline records hold paths relative to the repository root.
|
|
384
|
+
const top = gitTop(root);
|
|
385
|
+
const prefix = top ? relative(realpath(top), realpath(root)).split(sep).join("/") || "." : ".";
|
|
386
|
+
return {
|
|
387
|
+
ledgers: await gatherLedgerFacts(root, declaration),
|
|
388
|
+
pipelines: gatherPipelineFacts(root, declaration, prefix),
|
|
389
|
+
generated: await gatherGeneratedFacts(root, declaration, { runGenerators: options.runGenerators === true }),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function realpath(path: string): string {
|
|
394
|
+
try {
|
|
395
|
+
return realpathSync(path);
|
|
396
|
+
} catch {
|
|
397
|
+
return path;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Read the declaration in `root` (an absolute directory holding one, or the
|
|
403
|
+
* files of `options.tree` when given, such as a revision's), load the kinds
|
|
404
|
+
* its pins supply from `root`, gather the facts the member checks read, run
|
|
405
|
+
* every check and apply the declaration's settings. `file` in the findings
|
|
406
|
+
* is the declaration's path as `display` says, relative to where the
|
|
407
|
+
* command runs. Never throws a {@link WorkspaceReadError}: a declaration that
|
|
408
|
+
* can't be read is one WSP001 finding.
|
|
409
|
+
*/
|
|
410
|
+
export async function runDeclarationChecks(
|
|
411
|
+
root: string,
|
|
412
|
+
display: (file: string) => string = (f) => f,
|
|
413
|
+
options: DeclarationCheckOptions = {},
|
|
414
|
+
): Promise<DeclarationCheckReport> {
|
|
415
|
+
const tree = options.tree ?? workingTree(root);
|
|
416
|
+
let declaration: Declaration;
|
|
417
|
+
let groups: ResolvedGroup[];
|
|
418
|
+
try {
|
|
419
|
+
declaration = readDeclaration(tree, "", { rootChant: true });
|
|
420
|
+
groups = resolveGroups(declaration, tree);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
if (!(err instanceof WorkspaceReadError)) throw err;
|
|
423
|
+
const location: ErrorLocation = err.location ?? { file: "chant.workspace.json", line: 1, column: 1 };
|
|
424
|
+
const diagnostic: WorkspaceFinding = {
|
|
425
|
+
file: display(location.file),
|
|
426
|
+
line: location.line,
|
|
427
|
+
column: location.column,
|
|
428
|
+
ruleId: UNREADABLE_CHECK_ID,
|
|
429
|
+
severity: "error",
|
|
430
|
+
message: `${err.code}: ${err.message}`,
|
|
431
|
+
code: err.code,
|
|
432
|
+
};
|
|
433
|
+
return { file: display(location.file), diagnostics: [diagnostic], suppressed: [], links: [], ok: false };
|
|
434
|
+
}
|
|
435
|
+
const { registry, problems } = loadKindRegistry(declaration.pins, root);
|
|
436
|
+
const facts = options.gather === false || options.tree ? {} : await gatherWorkspaceFacts(root, declaration, options);
|
|
437
|
+
const ctx: WorkspaceCheckContext = { declaration, tree, groups, kinds: registry, kindProblems: problems, facts };
|
|
438
|
+
const findings = runWorkspaceChecks(ctx);
|
|
439
|
+
const { active, suppressed } = applyCheckSettings(declaration, findings);
|
|
440
|
+
|
|
441
|
+
const parsed = parseJsonText(tree.read(declaration.file), { jsonc: declaration.file.endsWith(".jsonc") });
|
|
442
|
+
const locate = (pointer: string): TextLocation => (parsed.ok ? parsed.locate(pointer) : { line: 1, column: 1 });
|
|
443
|
+
const file = display(declaration.file);
|
|
444
|
+
const toFinding = (d: WorkspaceDiagnostic): WorkspaceFinding => ({
|
|
445
|
+
file,
|
|
446
|
+
...locate(d.pointer),
|
|
447
|
+
ruleId: d.checkId,
|
|
448
|
+
severity: d.severity,
|
|
449
|
+
message: d.message,
|
|
450
|
+
...(d.entity !== undefined ? { entity: d.entity } : {}),
|
|
451
|
+
});
|
|
452
|
+
const order = (a: LintDiagnostic, b: LintDiagnostic) => a.line - b.line || a.column - b.column || (a.ruleId < b.ruleId ? -1 : a.ruleId > b.ruleId ? 1 : 0);
|
|
453
|
+
const diagnostics = active.map(toFinding).sort(order);
|
|
454
|
+
return {
|
|
455
|
+
file,
|
|
456
|
+
diagnostics,
|
|
457
|
+
suppressed: suppressed.map((s) => ({ ...toFinding(s), reason: s.reason })).sort(order),
|
|
458
|
+
links: linkTable(ctx),
|
|
459
|
+
ok: !diagnostics.some((d) => d.severity === "error"),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composed workspace IR (#2537, #2524 D8): `<member>/<id>` ids,
|
|
3
|
+
* `groups.byMember`, and v1 output from old chants upgraded in place.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from "vitest";
|
|
7
|
+
import { GRAPH_IR_VERSION, type GraphIR } from "../graph-ir";
|
|
8
|
+
import { parseDeclaration } from "./declaration";
|
|
9
|
+
import { composeWorkspaceGraph, readMemberIr, WORKSPACE_GRAPH_VERSION, type ComposedMember } from "./compose-graph";
|
|
10
|
+
|
|
11
|
+
const member = (name: string, dir: string): ComposedMember => ({
|
|
12
|
+
name,
|
|
13
|
+
dir,
|
|
14
|
+
kind: "chant",
|
|
15
|
+
status: "composed",
|
|
16
|
+
reason: null,
|
|
17
|
+
chant: "0.80.0",
|
|
18
|
+
irVersion: 1,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const web: GraphIR = {
|
|
22
|
+
version: 1,
|
|
23
|
+
nodes: [
|
|
24
|
+
{ id: "Bucket", kind: "S3Bucket", lexicon: "aws", attrs: { name: "b" } },
|
|
25
|
+
{ id: "web::Fn", kind: "LambdaFunction", lexicon: "aws", compositeInstance: "api", attrs: { env: { BUCKET: { $ref: "Bucket.Arn" } } } },
|
|
26
|
+
],
|
|
27
|
+
edges: [{ from: "web::Fn", to: "Bucket", kind: "ref", viaAttr: "env" }],
|
|
28
|
+
groups: { byLexicon: { aws: ["Bucket", "web::Fn"] }, byStack: { web: ["web::Fn"], aws: ["Bucket"] }, byComposite: { Api: ["web::Fn"] } },
|
|
29
|
+
exports: [{ name: "BucketArn", node: "Bucket", attr: "Arn" }],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const db: GraphIR = {
|
|
33
|
+
nodes: [{ id: "Bucket", kind: "GcsBucket", lexicon: "gcp", attrs: {} }],
|
|
34
|
+
edges: [],
|
|
35
|
+
groups: { byLexicon: { gcp: ["Bucket"] }, byStack: { gcp: ["Bucket"] } },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe("readMemberIr", () => {
|
|
39
|
+
test("stamps version 1 on output from a chant older than the version field", () => {
|
|
40
|
+
const read = readMemberIr(JSON.stringify(db));
|
|
41
|
+
expect("ir" in read && read.ir.version).toBe(1);
|
|
42
|
+
expect("ir" in read && read.irVersion).toBe(null);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("keeps a versioned IR as it is", () => {
|
|
46
|
+
const read = readMemberIr(JSON.stringify(web));
|
|
47
|
+
expect("ir" in read && read.irVersion).toBe(GRAPH_IR_VERSION);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("refuses an IR newer than this chant reads, with a reason code", () => {
|
|
51
|
+
const read = readMemberIr(JSON.stringify({ ...web, version: GRAPH_IR_VERSION + 1 }));
|
|
52
|
+
expect("reason" in read && read.reason.code).toBe("ir-version-unsupported");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("gives output-unreadable for anything that is not an IR", () => {
|
|
56
|
+
for (const text of ["not json", "{}", '{"nodes": []}', "null"]) {
|
|
57
|
+
const read = readMemberIr(text);
|
|
58
|
+
expect("reason" in read && read.reason.code, text).toBe("output-unreadable");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("composeWorkspaceGraph", () => {
|
|
64
|
+
const doc = composeWorkspaceGraph({ name: "acme", root: "/w" }, [
|
|
65
|
+
{ member: member("web", "apps/web"), ir: web },
|
|
66
|
+
{ member: member("db", "db"), ir: (readMemberIr(JSON.stringify(db)) as { ir: GraphIR }).ir },
|
|
67
|
+
{ member: { ...member("docs", "docs"), kind: "other", status: "skipped", reason: { code: "kind-not-run", message: "other" }, chant: null, irVersion: null } },
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
test("prefixes every id with its member, keeping :: for stacks", () => {
|
|
71
|
+
expect(doc.version).toBe(WORKSPACE_GRAPH_VERSION);
|
|
72
|
+
expect(doc.nodes.map((n) => n.id)).toEqual(["db/Bucket", "web/Bucket", "web/web::Fn"]);
|
|
73
|
+
expect(doc.nodes.find((n) => n.id === "web/web::Fn")).toMatchObject({
|
|
74
|
+
member: "web",
|
|
75
|
+
compositeInstance: "web/api",
|
|
76
|
+
attrs: { env: { BUCKET: { $ref: "web/Bucket.Arn" } } },
|
|
77
|
+
});
|
|
78
|
+
expect(doc.edges).toEqual([{ from: "web/web::Fn", to: "web/Bucket", kind: "ref", viaAttr: "env", member: "web" }]);
|
|
79
|
+
expect(doc.exports).toEqual([{ name: "BucketArn", node: "web/Bucket", attr: "Arn", member: "web" }]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("groups nodes by member, merges shared groups and prefixes member-scoped ones", () => {
|
|
83
|
+
expect(doc.groups.byMember).toEqual({ web: ["web/Bucket", "web/web::Fn"], db: ["db/Bucket"] });
|
|
84
|
+
expect(doc.groups.byLexicon).toEqual({ aws: ["web/Bucket", "web/web::Fn"], gcp: ["db/Bucket"] });
|
|
85
|
+
expect(doc.groups.byComposite).toEqual({ Api: ["web/web::Fn"] });
|
|
86
|
+
expect(doc.groups.byStack).toEqual({ "db/gcp": ["db/Bucket"], "web/aws": ["web/Bucket"], "web/web": ["web/web::Fn"] });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("lists every member with its status, and leaves the link and record sections empty", () => {
|
|
90
|
+
expect(doc.members.map((m) => [m.name, m.status])).toEqual([
|
|
91
|
+
["web", "composed"],
|
|
92
|
+
["db", "composed"],
|
|
93
|
+
["docs", "skipped"],
|
|
94
|
+
]);
|
|
95
|
+
expect(doc.links).toEqual([]);
|
|
96
|
+
expect(doc.records).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("does not change the members' own IRs", () => {
|
|
100
|
+
expect(web.nodes[0].id).toBe("Bucket");
|
|
101
|
+
expect(web.edges[0].from).toBe("web::Fn");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("the links section (#2539)", () => {
|
|
106
|
+
test("inferred joins carry their label, and a declared link suppresses the inferred edge it covers", () => {
|
|
107
|
+
const consumer: GraphIR = {
|
|
108
|
+
version: 1,
|
|
109
|
+
nodes: [
|
|
110
|
+
{ id: "bucketArn", kind: "Parameter", lexicon: "aws", attrs: {} },
|
|
111
|
+
{ id: "BUCKET_ARN", kind: "Parameter", lexicon: "aws", attrs: {} },
|
|
112
|
+
],
|
|
113
|
+
edges: [],
|
|
114
|
+
groups: {},
|
|
115
|
+
imports: [
|
|
116
|
+
{ name: "bucketArn", node: "bucketArn" },
|
|
117
|
+
{ name: "BUCKET_ARN", node: "BUCKET_ARN" },
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
const declaration = parseDeclaration(
|
|
121
|
+
JSON.stringify({
|
|
122
|
+
name: "acme",
|
|
123
|
+
schema: 1,
|
|
124
|
+
members: [
|
|
125
|
+
{ name: "web", dir: "web", kind: "chant" },
|
|
126
|
+
{ name: "jobs", dir: "jobs", kind: "chant" },
|
|
127
|
+
{ name: "app", dir: "app", kind: "chant", links: [{ member: "web", output: "BucketArn" }] },
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
"chant.workspace.json",
|
|
131
|
+
);
|
|
132
|
+
const inputs = [
|
|
133
|
+
{ member: member("web", "web"), ir: structuredClone(web) },
|
|
134
|
+
{ member: member("jobs", "jobs"), ir: structuredClone(consumer) },
|
|
135
|
+
{ member: member("app", "app"), ir: structuredClone(consumer) },
|
|
136
|
+
];
|
|
137
|
+
const doc = composeWorkspaceGraph({ name: "acme", root: "/w" }, inputs, { declaration });
|
|
138
|
+
expect(
|
|
139
|
+
doc.links.map((r) => (r.status === "ambiguous" ? "" : `${r.origin} ${r.label} ${r.from ?? r.consumer} -> ${r.to}`)),
|
|
140
|
+
).toEqual([
|
|
141
|
+
"declared exact app -> web/Bucket",
|
|
142
|
+
"inferred:joinKey folded jobs/BUCKET_ARN -> web/Bucket",
|
|
143
|
+
"inferred:joinKey folded jobs/bucketArn -> web/Bucket",
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|