@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,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read contract for `chant workspace ls --json` (#2534, #2524 D15): the
|
|
3
|
+
* output schema is a valid draft 2020-12 document, its closed code lists match
|
|
4
|
+
* the code, and real output validates against it, for the chant repo's own
|
|
5
|
+
* declaration (#2557) and for small workspaces built here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import Ajv2020 from "ajv/dist/2020";
|
|
13
|
+
import { afterAll, describe, expect, test } from "vitest";
|
|
14
|
+
import { WORKSPACE_ERROR_CODES } from "./declaration";
|
|
15
|
+
import { GROUP_REASON_CODES, listWorkspace, LS_CONTRACT_VERSION, LS_OUTPUT_SCHEMA_ID, MEMBER_REASON_CODES, type LsDocument } from "./ls";
|
|
16
|
+
import schema from "./ls.schema.json";
|
|
17
|
+
|
|
18
|
+
const REPO = join(import.meta.dirname, "..", "..", "..", "..");
|
|
19
|
+
|
|
20
|
+
const ajv = new Ajv2020({ strict: true, allErrors: true });
|
|
21
|
+
const validate = ajv.compile(schema);
|
|
22
|
+
|
|
23
|
+
function expectValid(doc: LsDocument): void {
|
|
24
|
+
const ok = validate(doc);
|
|
25
|
+
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function result(doc: LsDocument): Extract<LsDocument, { members: unknown }> {
|
|
29
|
+
if ("error" in doc) throw new Error(`${doc.error.code}: ${doc.error.message}`);
|
|
30
|
+
return doc;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const scratch: string[] = [];
|
|
34
|
+
afterAll(() => {
|
|
35
|
+
for (const d of scratch) rmSync(d, { recursive: true, force: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function git(cwd: string, ...args: string[]): string {
|
|
39
|
+
return execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "-c", "commit.gpgsign=false", ...args], {
|
|
40
|
+
cwd,
|
|
41
|
+
encoding: "utf-8",
|
|
42
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
43
|
+
}).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A git repository holding `files`. */
|
|
47
|
+
function repo(files: Record<string, string>): string {
|
|
48
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-ls-")));
|
|
49
|
+
scratch.push(root);
|
|
50
|
+
for (const [path, text] of Object.entries(files)) {
|
|
51
|
+
mkdirSync(dirname(join(root, path)), { recursive: true });
|
|
52
|
+
writeFileSync(join(root, path), text);
|
|
53
|
+
}
|
|
54
|
+
git(root, "init", "-q");
|
|
55
|
+
return root;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const declaration = (members: unknown[]) => JSON.stringify({ name: "acme", schema: 1, members }, null, 2);
|
|
59
|
+
|
|
60
|
+
describe("ls output schema", () => {
|
|
61
|
+
test("is a valid draft 2020-12 document with the published $id", () => {
|
|
62
|
+
expect(schema.$schema).toBe("https://json-schema.org/draft/2020-12/schema");
|
|
63
|
+
expect(ajv.validateSchema(schema)).toBe(true);
|
|
64
|
+
expect(schema.$id).toBe(LS_OUTPUT_SCHEMA_ID);
|
|
65
|
+
expect(LS_CONTRACT_VERSION).toBe(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("lists exactly the reason and error codes the code can return", () => {
|
|
69
|
+
expect(schema.$defs.member.properties.reason.oneOf[1].properties!.code.enum).toEqual([...MEMBER_REASON_CODES]);
|
|
70
|
+
expect(schema.$defs.group.properties.reason.oneOf[1].properties!.code.enum).toEqual([...GROUP_REASON_CODES]);
|
|
71
|
+
expect(schema.$defs.failure.properties.error.properties.code.enum).toEqual([...WORKSPACE_ERROR_CODES]);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("chant workspace ls on the chant repo (#2557)", () => {
|
|
76
|
+
const doc = result(listWorkspace({ cwd: join(REPO, "lexicons", "aws", "examples") }));
|
|
77
|
+
|
|
78
|
+
test("validates, from any directory below the root, with every member readable", () => {
|
|
79
|
+
expectValid(doc);
|
|
80
|
+
expect(doc.workspace).toMatchObject({ name: "chant", root: ".", file: "chant.workspace.json", schema: 1 });
|
|
81
|
+
expect(doc.members.filter((m) => !m.readable)).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("declares every package and every lexicon as a member", () => {
|
|
85
|
+
const dirs = new Set(doc.members.map((m) => m.dir));
|
|
86
|
+
for (const parent of ["packages", "lexicons"]) {
|
|
87
|
+
for (const d of readdirSync(join(REPO, parent), { withFileTypes: true })) {
|
|
88
|
+
if (d.isDirectory()) expect(dirs.has(`${parent}/${d.name}`), `${parent}/${d.name} is not declared in chant.workspace.json`).toBe(true);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("declares the examples and fixtures as example groups (ws-051)", () => {
|
|
94
|
+
expect(doc.groups.map((g) => [g.name, g.glob])).toEqual([
|
|
95
|
+
["examples", ["examples/*"]],
|
|
96
|
+
["lexicon-examples", ["lexicons/*/examples/*"]],
|
|
97
|
+
["fixtures", ["test/forgejo-preview-e2e", "test/leftness"]],
|
|
98
|
+
]);
|
|
99
|
+
for (const g of doc.groups) expect(g.reason, g.name).toBeNull();
|
|
100
|
+
expect(doc.groups[0].matches).toContain("examples/getting-started");
|
|
101
|
+
expect(doc.groups[0].skipped).toContain("examples/terraform-carve-out");
|
|
102
|
+
expect(doc.groups[1].matches).toContain("lexicons/aws/examples/lambda-api");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("chant workspace ls on built workspaces", () => {
|
|
107
|
+
test("lists a member it can't read with a reason code, and the read still succeeds", () => {
|
|
108
|
+
const root = repo({
|
|
109
|
+
"chant.workspace.json": declaration([
|
|
110
|
+
{ name: "web", dir: "apps/web", kind: "chant", roles: [{ name: "frontend", path: "src" }] },
|
|
111
|
+
{ name: "gone", dir: "apps/gone", kind: "chant" },
|
|
112
|
+
{ name: "infra", dir: "infra", kind: "terraform" },
|
|
113
|
+
{ name: "tools", dir: "tools", kind: "chant" },
|
|
114
|
+
{ name: "vendor", dir: "vendor", kind: "other", because: "vendored" },
|
|
115
|
+
{ name: "examples", kind: "examples", glob: "examples/*" },
|
|
116
|
+
]),
|
|
117
|
+
"apps/web/chant.config.ts": "",
|
|
118
|
+
"infra/main.tf": "",
|
|
119
|
+
"tools/README.md": "",
|
|
120
|
+
"vendor/x.txt": "",
|
|
121
|
+
"examples/README.md": "",
|
|
122
|
+
});
|
|
123
|
+
const doc = result(listWorkspace({ cwd: join(root, "apps", "web") }));
|
|
124
|
+
expectValid(doc);
|
|
125
|
+
expect(doc.members.map((m) => [m.name, m.readable, m.reason?.code ?? null])).toEqual([
|
|
126
|
+
["web", true, null],
|
|
127
|
+
["gone", false, "dir-missing"],
|
|
128
|
+
["infra", false, "unknown-kind"],
|
|
129
|
+
["tools", false, "kind-probe-failed"],
|
|
130
|
+
["vendor", true, null],
|
|
131
|
+
]);
|
|
132
|
+
expect(doc.members[2].reason?.message).toMatch(/known kinds: chant, other, workspace/);
|
|
133
|
+
expect(doc.members[0].roles).toEqual([{ name: "frontend", path: "src" }]);
|
|
134
|
+
expect(doc.groups[0].reason?.code).toBe("no-matches");
|
|
135
|
+
expect(doc.summary).toEqual({ members: 5, unreadable: 3, groups: 1, matches: 0 });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("a nested workspace member is readable by its own declaration, and ls inside it lists the inner one", () => {
|
|
139
|
+
const root = repo({
|
|
140
|
+
"chant.workspace.json": declaration([{ name: "kit", dir: "vendor/kit", kind: "workspace" }]),
|
|
141
|
+
"vendor/kit/chant.workspace.jsonc": "// inner\n" + JSON.stringify({ name: "kit", schema: 1, members: [] }),
|
|
142
|
+
});
|
|
143
|
+
expect(result(listWorkspace({ cwd: root })).members[0].readable).toBe(true);
|
|
144
|
+
expect(result(listWorkspace({ cwd: join(root, "vendor", "kit") })).workspace).toMatchObject({ name: "kit", root: "vendor/kit", file: "chant.workspace.jsonc" });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("every failure validates with its code, and a parse error carries its location", () => {
|
|
148
|
+
const empty = repo({});
|
|
149
|
+
const ambiguous = repo({ "chant.workspace.json": declaration([]), "chant.workspace.jsonc": declaration([]) });
|
|
150
|
+
const broken = repo({ "chant.workspace.json": '{\n "name": "acme",\n "schema": 1\n "members": []\n}\n' });
|
|
151
|
+
const invalid = repo({ "chant.workspace.json": declaration([{ name: "_members", dir: "a", kind: "chant" }]) });
|
|
152
|
+
const placement = repo({ "chant.workspace.json": declaration([{ name: "a", dir: "a", kind: "chant" }, { name: "b", dir: "a/b", kind: "chant" }]) });
|
|
153
|
+
const tooOld = repo({ "chant.workspace.json": JSON.stringify({ name: "acme", schema: 1, minReader: "999.0.0", members: [] }) });
|
|
154
|
+
const outside = realpathSync(mkdtempSync(join(tmpdir(), "chant-ls-nogit-")));
|
|
155
|
+
scratch.push(outside);
|
|
156
|
+
const docs = [
|
|
157
|
+
listWorkspace({ cwd: empty }),
|
|
158
|
+
listWorkspace({ cwd: ambiguous }),
|
|
159
|
+
listWorkspace({ cwd: broken }),
|
|
160
|
+
listWorkspace({ cwd: invalid }),
|
|
161
|
+
listWorkspace({ cwd: placement }),
|
|
162
|
+
listWorkspace({ cwd: tooOld }),
|
|
163
|
+
listWorkspace({ cwd: outside, at: "HEAD" }),
|
|
164
|
+
listWorkspace({ cwd: empty, at: "no-such-rev" }),
|
|
165
|
+
];
|
|
166
|
+
for (const doc of docs) expectValid(doc);
|
|
167
|
+
expect(docs.map((d) => ("error" in d ? d.error.code : "ok"))).toEqual([
|
|
168
|
+
"declaration-missing",
|
|
169
|
+
"declaration-ambiguous",
|
|
170
|
+
"declaration-unparseable",
|
|
171
|
+
"declaration-invalid",
|
|
172
|
+
"placement-invalid",
|
|
173
|
+
"reader-too-old",
|
|
174
|
+
"not-a-git-repository",
|
|
175
|
+
"revision-unknown",
|
|
176
|
+
]);
|
|
177
|
+
const parseError = docs[2] as Extract<LsDocument, { error: unknown }>;
|
|
178
|
+
expect(parseError.error.location).toEqual({ file: "chant.workspace.json", line: 4, column: 3 });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("--at reads the declaration and members from a commit, offline", () => {
|
|
182
|
+
const root = repo({
|
|
183
|
+
"chant.workspace.json": declaration([{ name: "web", dir: "web", kind: "chant" }, { name: "ex", kind: "examples", glob: "examples/*" }]),
|
|
184
|
+
"web/chant.config.ts": "",
|
|
185
|
+
"examples/one/chant.config.ts": "",
|
|
186
|
+
});
|
|
187
|
+
git(root, "add", "-A");
|
|
188
|
+
git(root, "commit", "-q", "-m", "one");
|
|
189
|
+
const first = git(root, "rev-parse", "HEAD");
|
|
190
|
+
// Change the working tree after the commit: --at must not see it.
|
|
191
|
+
rmSync(join(root, "web"), { recursive: true });
|
|
192
|
+
writeFileSync(join(root, "chant.workspace.json"), declaration([]));
|
|
193
|
+
const doc = result(listWorkspace({ cwd: root, at: "HEAD" }));
|
|
194
|
+
expectValid(doc);
|
|
195
|
+
expect(doc.at).toBe(first);
|
|
196
|
+
expect(doc.members.map((m) => [m.name, m.readable])).toEqual([["web", true]]);
|
|
197
|
+
expect(doc.groups[0].matches).toEqual(["examples/one"]);
|
|
198
|
+
expect(result(listWorkspace({ cwd: root })).members).toEqual([]);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/chant/schemas/workspace/ls/v1/ls.schema.json",
|
|
4
|
+
"title": "chant workspace ls output",
|
|
5
|
+
"description": "What `chant workspace ls --json` prints, version 1 of the read contract for listing a workspace (#2524 D15, #2534). Readers ignore fields they do not know; a field is only ever added within a version. The reason and error codes are closed lists: a new code is a new contract version. Contract version 1 is written by chant 0.81.0 and newer; a reader that needs it refuses output whose `contract` it doesn't know. Every code is in the one closed list of `reason-codes.ts`.",
|
|
6
|
+
"oneOf": [{ "$ref": "#/$defs/result" }, { "$ref": "#/$defs/failure" }],
|
|
7
|
+
"$defs": {
|
|
8
|
+
"result": {
|
|
9
|
+
"description": "The declaration was read. Exit code 0, even when some members can't be read.",
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["$schema", "contract", "chant", "at", "workspace", "members", "groups", "summary"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"$schema": { "const": "https://intentius.io/chant/schemas/workspace/ls/v1/ls.schema.json" },
|
|
14
|
+
"contract": { "const": 1 },
|
|
15
|
+
"chant": { "type": "string", "description": "The version of the chant that read the workspace." },
|
|
16
|
+
"at": {
|
|
17
|
+
"description": "The full commit id the workspace was read at (--at), or null for the working tree.",
|
|
18
|
+
"type": ["string", "null"],
|
|
19
|
+
"pattern": "^[0-9a-f]{40,64}$"
|
|
20
|
+
},
|
|
21
|
+
"error": false,
|
|
22
|
+
"workspace": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": ["name", "root", "file", "schema", "minReader", "pins"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"name": { "type": "string", "description": "The declared name. With the revision, it identifies the workspace." },
|
|
27
|
+
"root": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "The workspace root relative to the git root, with / separators, \".\" for the git root itself. An absolute path outside a git repository."
|
|
30
|
+
},
|
|
31
|
+
"file": { "enum": ["chant.workspace.json", "chant.workspace.jsonc"] },
|
|
32
|
+
"schema": { "const": 1 },
|
|
33
|
+
"minReader": { "type": ["string", "null"] },
|
|
34
|
+
"pins": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["package", "version", "path", "integrity"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"package": { "type": ["string", "null"] },
|
|
41
|
+
"version": { "type": ["string", "null"] },
|
|
42
|
+
"path": { "type": ["string", "null"] },
|
|
43
|
+
"integrity": { "type": ["string", "null"] }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"members": {
|
|
50
|
+
"description": "Every member, in declaration order, readable or not.",
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": { "$ref": "#/$defs/member" }
|
|
53
|
+
},
|
|
54
|
+
"groups": {
|
|
55
|
+
"description": "Every example group (ws-051), in declaration order. Their matches are not members.",
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": { "$ref": "#/$defs/group" }
|
|
58
|
+
},
|
|
59
|
+
"summary": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["members", "unreadable", "groups", "matches"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"members": { "type": "integer", "minimum": 0 },
|
|
64
|
+
"unreadable": { "type": "integer", "minimum": 0, "description": "Members with a reason." },
|
|
65
|
+
"groups": { "type": "integer", "minimum": 0 },
|
|
66
|
+
"matches": { "type": "integer", "minimum": 0, "description": "Projects across all groups." }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"member": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["name", "dir", "kind", "roles", "upstream", "because", "readable", "reason"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"name": { "type": "string" },
|
|
76
|
+
"dir": { "type": "string", "description": "Relative to the workspace root, with / separators; \".\" is the root member." },
|
|
77
|
+
"kind": { "type": "string" },
|
|
78
|
+
"roles": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"required": ["name", "path"],
|
|
83
|
+
"properties": {
|
|
84
|
+
"name": { "type": "string" },
|
|
85
|
+
"path": { "type": ["string", "null"], "description": "Relative to the member's directory, or null for the whole member." }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"upstream": { "type": ["string", "null"] },
|
|
90
|
+
"because": { "type": ["string", "null"] },
|
|
91
|
+
"readable": { "type": "boolean", "description": "True exactly when reason is null." },
|
|
92
|
+
"reason": {
|
|
93
|
+
"oneOf": [
|
|
94
|
+
{ "type": "null" },
|
|
95
|
+
{
|
|
96
|
+
"type": "object",
|
|
97
|
+
"required": ["code", "message"],
|
|
98
|
+
"properties": {
|
|
99
|
+
"code": { "enum": ["dir-missing", "unknown-kind", "kind-probe-failed"] },
|
|
100
|
+
"message": { "type": "string" }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"if": { "type": "object", "properties": { "readable": { "const": true } }, "required": ["readable"] },
|
|
107
|
+
"then": { "type": "object", "properties": { "reason": { "type": "null" } } },
|
|
108
|
+
"else": { "type": "object", "properties": { "reason": { "type": "object" } } }
|
|
109
|
+
},
|
|
110
|
+
"group": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"required": ["name", "kind", "glob", "matches", "skipped", "reason"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"name": { "type": "string" },
|
|
115
|
+
"kind": { "const": "examples" },
|
|
116
|
+
"glob": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
117
|
+
"matches": {
|
|
118
|
+
"type": "array",
|
|
119
|
+
"items": { "type": "string" },
|
|
120
|
+
"description": "Directories the globs match that hold a chant project, relative to the workspace root, sorted. They are built and linted, and nothing else."
|
|
121
|
+
},
|
|
122
|
+
"skipped": {
|
|
123
|
+
"type": "array",
|
|
124
|
+
"items": { "type": "string" },
|
|
125
|
+
"description": "Directories the globs match that hold no chant project. They are left alone."
|
|
126
|
+
},
|
|
127
|
+
"reason": {
|
|
128
|
+
"oneOf": [
|
|
129
|
+
{ "type": "null" },
|
|
130
|
+
{
|
|
131
|
+
"type": "object",
|
|
132
|
+
"required": ["code", "message"],
|
|
133
|
+
"properties": {
|
|
134
|
+
"code": { "enum": ["no-matches"] },
|
|
135
|
+
"message": { "type": "string" }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"failure": {
|
|
143
|
+
"description": "The declaration could not be read, and nothing is listed. Exit code 1.",
|
|
144
|
+
"type": "object",
|
|
145
|
+
"required": ["$schema", "contract", "chant", "error"],
|
|
146
|
+
"properties": {
|
|
147
|
+
"$schema": { "const": "https://intentius.io/chant/schemas/workspace/ls/v1/ls.schema.json" },
|
|
148
|
+
"contract": { "const": 1 },
|
|
149
|
+
"chant": { "type": "string" },
|
|
150
|
+
"error": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"required": ["code", "message", "location"],
|
|
153
|
+
"properties": {
|
|
154
|
+
"code": {
|
|
155
|
+
"enum": [
|
|
156
|
+
"declaration-missing",
|
|
157
|
+
"declaration-ambiguous",
|
|
158
|
+
"declaration-unparseable",
|
|
159
|
+
"declaration-invalid",
|
|
160
|
+
"placement-invalid",
|
|
161
|
+
"reader-too-old",
|
|
162
|
+
"root-chant-required",
|
|
163
|
+
"not-a-git-repository",
|
|
164
|
+
"revision-unknown"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"message": { "type": "string" },
|
|
168
|
+
"location": {
|
|
169
|
+
"description": "Where in the declaration the problem is, or null when it is not in the file.",
|
|
170
|
+
"oneOf": [
|
|
171
|
+
{ "type": "null" },
|
|
172
|
+
{
|
|
173
|
+
"type": "object",
|
|
174
|
+
"required": ["file", "line", "column"],
|
|
175
|
+
"properties": {
|
|
176
|
+
"file": { "type": "string" },
|
|
177
|
+
"line": { "type": "integer", "minimum": 1 },
|
|
178
|
+
"column": { "type": "integer", "minimum": 1 }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"members": false,
|
|
186
|
+
"workspace": false
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace ls [dir] [--at <rev>] [--json]` (#2534): list a workspace's
|
|
3
|
+
* members and example groups from its declaration.
|
|
4
|
+
*
|
|
5
|
+
* A member that can't be read is still listed, with a reason code, and the
|
|
6
|
+
* command still exits 0 (#2524 D15, ws-020). Failing on it is the job of
|
|
7
|
+
* `chant workspace check`. Only a declaration that can't be read exits 1.
|
|
8
|
+
*
|
|
9
|
+
* The `--json` output is part of the read contract, versioned like
|
|
10
|
+
* `workspace records` and described by `ls.schema.json` beside this file.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import { formatError } from "../cli/format";
|
|
16
|
+
import type { CommandContext } from "../cli/registry";
|
|
17
|
+
import {
|
|
18
|
+
readDeclaration,
|
|
19
|
+
readerVersion,
|
|
20
|
+
resolveGroups,
|
|
21
|
+
WorkspaceReadError,
|
|
22
|
+
type Declaration,
|
|
23
|
+
type ErrorLocation,
|
|
24
|
+
type Member,
|
|
25
|
+
type WorkspaceErrorCode,
|
|
26
|
+
} from "./declaration";
|
|
27
|
+
import type { ReasonCode } from "./reason-codes";
|
|
28
|
+
import { loadKindRegistry, probeKind, type KindRegistry } from "./kinds";
|
|
29
|
+
import type { WorkspaceTree } from "./tree";
|
|
30
|
+
import { handToRootChant, locateWorkspace } from "./which-chant";
|
|
31
|
+
|
|
32
|
+
/** The version of the `ls` output this chant writes. */
|
|
33
|
+
export const LS_CONTRACT_VERSION = 1;
|
|
34
|
+
|
|
35
|
+
/** `$id` of the JSON Schema for the `--json` output, shipped beside this file. */
|
|
36
|
+
export const LS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/ls/v1/ls.schema.json";
|
|
37
|
+
|
|
38
|
+
const USAGE = "chant workspace ls [dir] [--at <rev>] [--json]";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Why one member can't be read. Closed, like the error codes: a new code is a
|
|
42
|
+
* contract change.
|
|
43
|
+
*/
|
|
44
|
+
export const MEMBER_REASON_CODES = [
|
|
45
|
+
/** The member's directory does not exist. */
|
|
46
|
+
"dir-missing",
|
|
47
|
+
/** No built-in kind or pinned package supplies the member's kind. */
|
|
48
|
+
"unknown-kind",
|
|
49
|
+
/** The directory is not what its kind reads, such as a `chant` member with no chant config. */
|
|
50
|
+
"kind-probe-failed",
|
|
51
|
+
] as const satisfies readonly ReasonCode[];
|
|
52
|
+
export type MemberReasonCode = (typeof MEMBER_REASON_CODES)[number];
|
|
53
|
+
|
|
54
|
+
/** Why a group lists nothing. */
|
|
55
|
+
export const GROUP_REASON_CODES = [
|
|
56
|
+
/** No directory the globs match holds a chant project. */
|
|
57
|
+
"no-matches",
|
|
58
|
+
] as const satisfies readonly ReasonCode[];
|
|
59
|
+
export type GroupReasonCode = (typeof GROUP_REASON_CODES)[number];
|
|
60
|
+
|
|
61
|
+
export interface LsMember {
|
|
62
|
+
name: string;
|
|
63
|
+
dir: string;
|
|
64
|
+
kind: string;
|
|
65
|
+
roles: { name: string; path: string | null }[];
|
|
66
|
+
upstream: string | null;
|
|
67
|
+
because: string | null;
|
|
68
|
+
readable: boolean;
|
|
69
|
+
reason: { code: MemberReasonCode; message: string } | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface LsGroup {
|
|
73
|
+
name: string;
|
|
74
|
+
kind: "examples";
|
|
75
|
+
glob: string[];
|
|
76
|
+
matches: string[];
|
|
77
|
+
skipped: string[];
|
|
78
|
+
reason: { code: GroupReasonCode; message: string } | null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type LsDocument =
|
|
82
|
+
| {
|
|
83
|
+
$schema: string;
|
|
84
|
+
contract: number;
|
|
85
|
+
chant: string;
|
|
86
|
+
at: string | null;
|
|
87
|
+
workspace: {
|
|
88
|
+
name: string;
|
|
89
|
+
/** The workspace root relative to the git root, `"."` for the git root itself. Absolute outside git. */
|
|
90
|
+
root: string;
|
|
91
|
+
file: string;
|
|
92
|
+
schema: number;
|
|
93
|
+
minReader: string | null;
|
|
94
|
+
pins: Declaration["pins"];
|
|
95
|
+
};
|
|
96
|
+
members: LsMember[];
|
|
97
|
+
groups: LsGroup[];
|
|
98
|
+
summary: { members: number; unreadable: number; groups: number; matches: number };
|
|
99
|
+
}
|
|
100
|
+
| {
|
|
101
|
+
$schema: string;
|
|
102
|
+
contract: number;
|
|
103
|
+
chant: string;
|
|
104
|
+
error: { code: WorkspaceErrorCode; message: string; location: ErrorLocation | null };
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export interface LsQuery {
|
|
108
|
+
/** Where the walk up to the declaration starts. */
|
|
109
|
+
cwd: string;
|
|
110
|
+
at?: string;
|
|
111
|
+
kinds?: KindRegistry;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A member's reason code, or null when its kind can read it. */
|
|
115
|
+
export function memberReason(member: Member, tree: WorkspaceTree, kinds: KindRegistry): LsMember["reason"] {
|
|
116
|
+
if (tree.stat(member.dir === "." ? "" : member.dir) !== "dir") {
|
|
117
|
+
return { code: "dir-missing", message: `${member.dir} is not a directory${tree.label}` };
|
|
118
|
+
}
|
|
119
|
+
const kind = kinds.get(member.kind);
|
|
120
|
+
if (!kind) {
|
|
121
|
+
return { code: "unknown-kind", message: `no built-in kind or pinned package supplies kind ${member.kind}; known kinds: ${kinds.names().join(", ")}` };
|
|
122
|
+
}
|
|
123
|
+
if (!probeKind(kind, tree, member.dir === "." ? "" : member.dir)) {
|
|
124
|
+
return { code: "kind-probe-failed", message: `${member.dir} is not ${kind.description}` };
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Find the workspace, read it and build the document `--json` prints. Never throws a {@link WorkspaceReadError}. */
|
|
130
|
+
export function listWorkspace(query: LsQuery): LsDocument {
|
|
131
|
+
const chant = readerVersion();
|
|
132
|
+
const head = { $schema: LS_OUTPUT_SCHEMA_ID, contract: LS_CONTRACT_VERSION, chant };
|
|
133
|
+
try {
|
|
134
|
+
const located = locateWorkspace(query.cwd, query.at);
|
|
135
|
+
const { tree, rootOnDisk, at } = located;
|
|
136
|
+
const declaration = readDeclaration(tree, "", { rootChant: true });
|
|
137
|
+
// Kinds come from the pinned packages installed in the working tree, also
|
|
138
|
+
// for --at: they are read as data, never run (#2535).
|
|
139
|
+
const kinds = query.kinds ?? loadKindRegistry(declaration.pins, rootOnDisk).registry;
|
|
140
|
+
const groups = resolveGroups(declaration, tree);
|
|
141
|
+
const members: LsMember[] = declaration.members.map((m) => {
|
|
142
|
+
const reason = memberReason(m, tree, kinds);
|
|
143
|
+
return {
|
|
144
|
+
name: m.name,
|
|
145
|
+
dir: m.dir,
|
|
146
|
+
kind: m.kind,
|
|
147
|
+
roles: m.roles,
|
|
148
|
+
upstream: m.upstream,
|
|
149
|
+
because: m.because,
|
|
150
|
+
readable: reason === null,
|
|
151
|
+
reason,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
const lsGroups: LsGroup[] = groups.map((g) => ({
|
|
155
|
+
name: g.group.name,
|
|
156
|
+
kind: "examples",
|
|
157
|
+
glob: g.group.globs,
|
|
158
|
+
matches: g.matches,
|
|
159
|
+
skipped: g.skipped,
|
|
160
|
+
reason:
|
|
161
|
+
g.matches.length === 0
|
|
162
|
+
? { code: "no-matches", message: `no directory ${g.group.globs.join(" or ")} matches${tree.label} holds a chant project` }
|
|
163
|
+
: null,
|
|
164
|
+
}));
|
|
165
|
+
return {
|
|
166
|
+
...head,
|
|
167
|
+
at,
|
|
168
|
+
workspace: {
|
|
169
|
+
name: declaration.name,
|
|
170
|
+
root: located.root,
|
|
171
|
+
file: declaration.file,
|
|
172
|
+
schema: declaration.schema,
|
|
173
|
+
minReader: declaration.minReader,
|
|
174
|
+
pins: declaration.pins,
|
|
175
|
+
},
|
|
176
|
+
members,
|
|
177
|
+
groups: lsGroups,
|
|
178
|
+
summary: {
|
|
179
|
+
members: members.length,
|
|
180
|
+
unreadable: members.filter((m) => !m.readable).length,
|
|
181
|
+
groups: lsGroups.length,
|
|
182
|
+
matches: lsGroups.reduce((n, g) => n + g.matches.length, 0),
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (!(err instanceof WorkspaceReadError)) throw err;
|
|
187
|
+
return { ...head, error: { code: err.code, message: err.message, location: err.location ?? null } };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function runWorkspaceLs(ctx: CommandContext): Promise<number> {
|
|
192
|
+
const { args } = ctx;
|
|
193
|
+
const cwd = resolve(args.extraPositional ?? ".");
|
|
194
|
+
if (!existsSync(cwd)) {
|
|
195
|
+
console.error(formatError({ message: `${cwd} does not exist`, hint: USAGE }));
|
|
196
|
+
return 1;
|
|
197
|
+
}
|
|
198
|
+
// The root's chant reads the declaration (ws-021).
|
|
199
|
+
const handed = await handToRootChant(cwd, args.at);
|
|
200
|
+
if (handed !== undefined) return handed;
|
|
201
|
+
const doc = listWorkspace({ cwd, at: args.at });
|
|
202
|
+
if (args.json) {
|
|
203
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
204
|
+
} else if ("error" in doc) {
|
|
205
|
+
const l = doc.error.location;
|
|
206
|
+
const where = l ? `${l.file}:${l.line}:${l.column}: ` : "";
|
|
207
|
+
console.error(formatError({ message: `${doc.error.code}: ${where}${doc.error.message}`, hint: USAGE }));
|
|
208
|
+
} else {
|
|
209
|
+
console.log(formatLs(doc));
|
|
210
|
+
}
|
|
211
|
+
return "error" in doc ? 1 : 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function table(rows: string[][]): string[] {
|
|
215
|
+
const widths = rows[0].map((_, c) => Math.max(...rows.map((r) => r[c].length)));
|
|
216
|
+
return rows.map((r) => r.map((cell, c) => (c === r.length - 1 ? cell : cell.padEnd(widths[c]))).join(" ").trimEnd());
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function formatLs(doc: Extract<LsDocument, { members: unknown }>): string {
|
|
220
|
+
const lines: string[] = [];
|
|
221
|
+
const w = doc.workspace;
|
|
222
|
+
lines.push(`${w.name} (${w.root === "." ? w.file : `${w.root}/${w.file}`}${doc.at ? ` at ${doc.at.slice(0, 8)}` : ""})`);
|
|
223
|
+
if (doc.members.length > 0) {
|
|
224
|
+
lines.push("");
|
|
225
|
+
const rows = [["MEMBER", "KIND", "DIR", "ROLES"]];
|
|
226
|
+
for (const m of doc.members) {
|
|
227
|
+
rows.push([m.name, m.kind, m.dir, m.roles.map((r) => (r.path ? `${r.name}:${r.path}` : r.name)).join(",")]);
|
|
228
|
+
}
|
|
229
|
+
const rendered = table(rows);
|
|
230
|
+
lines.push(rendered[0]);
|
|
231
|
+
doc.members.forEach((m, i) => {
|
|
232
|
+
lines.push(rendered[i + 1]);
|
|
233
|
+
if (m.reason) lines.push(` ${m.reason.code}: ${m.reason.message}`);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (doc.groups.length > 0) {
|
|
237
|
+
lines.push("");
|
|
238
|
+
const rows = [["GROUP", "KIND", "GLOB", "PROJECTS"]];
|
|
239
|
+
for (const g of doc.groups) rows.push([g.name, g.kind, g.glob.join(" "), String(g.matches.length)]);
|
|
240
|
+
const rendered = table(rows);
|
|
241
|
+
lines.push(rendered[0]);
|
|
242
|
+
doc.groups.forEach((g, i) => {
|
|
243
|
+
lines.push(rendered[i + 1]);
|
|
244
|
+
if (g.reason) lines.push(` ${g.reason.code}: ${g.reason.message}`);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const s = doc.summary;
|
|
248
|
+
lines.push("");
|
|
249
|
+
lines.push(
|
|
250
|
+
`${s.members} member${s.members === 1 ? "" : "s"}, ${s.unreadable} unreadable; ` +
|
|
251
|
+
`${s.groups} example group${s.groups === 1 ? "" : "s"} with ${s.matches} project${s.matches === 1 ? "" : "s"}`,
|
|
252
|
+
);
|
|
253
|
+
return lines.join("\n");
|
|
254
|
+
}
|