@intentius/chant 0.101.0 → 0.102.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/agents/checks.d.ts.map +1 -1
- package/dist/attrref.d.ts.map +1 -1
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/nginx.d.ts.map +1 -1
- package/dist/audit/prior-art.d.ts +48 -0
- package/dist/audit/prior-art.d.ts.map +1 -1
- package/dist/behaviour-kinds.d.ts.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +16 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts +8 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +9 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +9 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/lsp/server.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/build.d.ts.map +1 -1
- package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/tools/import.d.ts.map +1 -1
- package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
- package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
- package/dist/cli/mcp/tools/search.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +11 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/naming.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts +67 -3
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/process-runner.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts.map +1 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +19 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/declarable.d.ts +16 -0
- package/dist/declarable.d.ts.map +1 -1
- package/dist/discovery/cache.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +16 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.d.ts +37 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +3 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-layout.d.ts.map +1 -1
- package/dist/import/base-parser.d.ts.map +1 -1
- package/dist/import/embedded.d.ts +7 -0
- package/dist/import/embedded.d.ts.map +1 -1
- package/dist/import/parser-options.d.ts +14 -0
- package/dist/import/parser-options.d.ts.map +1 -0
- package/dist/import/parser.d.ts +17 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts.map +1 -1
- package/dist/lexicon-output.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +3 -3
- package/dist/lexicon.d.ts +55 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/assert-live.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +15 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/work-lease.d.ts +61 -2
- package/dist/lifecycle/work-lease.d.ts.map +1 -1
- package/dist/lint/config.d.ts +8 -8
- package/dist/lint/engine.d.ts +6 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +11 -11
- package/dist/op/activities/apply.d.ts +79 -9
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
- package/dist/op/activity-contract.d.ts +1 -1
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +12 -5
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts.map +1 -1
- package/dist/op/decide-read-inputs.d.ts.map +1 -1
- package/dist/op/emulator-lifecycle.d.ts +21 -2
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts +5 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-wait.d.ts.map +1 -1
- package/dist/op/gate.d.ts +19 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -0
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +6 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/run-context.d.ts +39 -0
- package/dist/op/run-context.d.ts.map +1 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts.map +1 -1
- package/dist/op/steward-beside.d.ts.map +1 -1
- package/dist/op/steward-points.d.ts.map +1 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +8 -3
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +24 -4
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/pseudo-parameter.d.ts.map +1 -1
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts.map +1 -1
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
- package/dist/workspace/agent-cli.d.ts +5 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/box-factory.d.ts +73 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +5 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +10 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts +67 -6
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +12 -3
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +368 -0
- package/dist/workspace/conformance/writer.d.ts.map +1 -0
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +91 -4
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +234 -5
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/identity.d.ts +111 -0
- package/dist/workspace/identity.d.ts.map +1 -0
- package/dist/workspace/intent-cli.d.ts +7 -4
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-record.d.ts +8 -1
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +149 -5
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/kinds.d.ts +18 -4
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +2 -2
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/principal-classes.d.ts +114 -0
- package/dist/workspace/principal-classes.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +21 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/records-checkout.d.ts +54 -0
- package/dist/workspace/records-checkout.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +27 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-since.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +5 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +15 -0
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +164 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -0
- package/dist/workspace/runs.d.ts +423 -0
- package/dist/workspace/runs.d.ts.map +1 -0
- package/dist/workspace/source-block.d.ts +1 -1
- package/dist/workspace/source-block.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +15 -3
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +10 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +52 -0
- package/dist/workspace/symbols.d.ts.map +1 -0
- package/dist/workspace/trailer-joins.d.ts +46 -0
- package/dist/workspace/trailer-joins.d.ts.map +1 -0
- package/dist/workspace/trailers.d.ts +89 -0
- package/dist/workspace/trailers.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/seal.d.ts +33 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +39 -3
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work.d.ts +26 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/dist/workspace/workspace-principals.schema.json +60 -0
- package/dist/workspace/write-scope.d.ts +79 -19
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/yaml.d.ts +21 -39
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/audit/prior-art.ts +15 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/carve-emit-state.test.ts +4 -0
- package/src/cli/commands/carve-emit.ts +5 -19
- package/src/cli/commands/check-lexicon.test.ts +62 -1
- package/src/cli/commands/check-lexicon.ts +36 -6
- package/src/cli/commands/import-live.test.ts +18 -0
- package/src/cli/commands/import-parser-options.test.ts +133 -0
- package/src/cli/commands/import.ts +21 -3
- package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
- package/src/cli/commands/init.test.ts +8 -1
- package/src/cli/commands/init.ts +23 -8
- package/src/cli/commands/lint.test.ts +37 -0
- package/src/cli/commands/onboard.test.ts +53 -3
- package/src/cli/commands/onboard.ts +52 -4
- package/src/cli/commands/pinned-upgrade.ts +73 -33
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/dev.ts +8 -6
- package/src/cli/handlers/lifecycle.test.ts +67 -0
- package/src/cli/handlers/lifecycle.ts +6 -5
- package/src/cli/handlers/misc.ts +2 -0
- package/src/cli/handlers/operator.ts +87 -3
- package/src/cli/handlers/run.test.ts +111 -0
- package/src/cli/handlers/run.ts +85 -24
- package/src/cli/handlers/scenario.ts +2 -2
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +65 -11
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +29 -2
- package/src/cli/registry.ts +11 -2
- package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
- package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
- package/src/codegen/pinned-upgrade.test.ts +104 -0
- package/src/codegen/pinned-upgrade.ts +250 -56
- package/src/codegen/testdata/postgres-tags.json +242 -0
- package/src/config.test.ts +21 -0
- package/src/config.ts +15 -0
- package/src/declarable.test.ts +21 -0
- package/src/declarable.ts +19 -0
- package/src/discovery/fold-composite.test.ts +41 -5
- package/src/discovery/fold-import.test.ts +129 -0
- package/src/discovery/fold-import.ts +643 -131
- package/src/discovery/fold-package-composite.test.ts +264 -0
- package/src/discovery/fold-specifiers.test.ts +191 -0
- package/src/discovery/param-deps.test.ts +66 -1
- package/src/discovery/param-deps.ts +45 -0
- package/src/discovery/specifier-resolve.ts +344 -0
- package/src/fold/fold.test.ts +39 -1
- package/src/fold/fold.ts +83 -9
- package/src/fold-provenance.test.ts +140 -0
- package/src/graph-ir.test.ts +83 -0
- package/src/graph-ir.ts +29 -0
- package/src/import/embedded.test.ts +20 -0
- package/src/import/embedded.ts +13 -0
- package/src/import/parser-options.ts +46 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +55 -3
- package/src/lifecycle/gate-ledger.ts +16 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +6 -4
- package/src/lifecycle/snapshot.ts +2 -2
- package/src/lifecycle/work-lease.test.ts +1 -1
- package/src/lifecycle/work-lease.ts +93 -2
- package/src/lint/engine.test.ts +55 -0
- package/src/lint/engine.ts +30 -4
- package/src/lint/output-docs.test.ts +59 -56
- package/src/op/activities/apply.test.ts +189 -5
- package/src/op/activities/apply.ts +230 -8
- package/src/op/activities/index.ts +3 -0
- package/src/op/activities/lexicon-upgrade.test.ts +160 -6
- package/src/op/activities/lexicon-upgrade.ts +154 -51
- package/src/op/composites/apply-op.ts +12 -5
- package/src/op/composites/composites.test.ts +30 -3
- package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
- package/src/op/composites/lexicon-upgrade-op.ts +29 -8
- package/src/op/emulator-lifecycle.test.ts +23 -0
- package/src/op/emulator-lifecycle.ts +52 -14
- package/src/op/gate-approval.test.ts +74 -0
- package/src/op/gate-approval.ts +5 -1
- package/src/op/gate.ts +31 -5
- package/src/op/index.ts +2 -0
- package/src/op/local-executor.test.ts +15 -0
- package/src/op/local-executor.ts +40 -12
- package/src/op/local-output.ts +1 -1
- package/src/op/operator.ts +4 -0
- package/src/op/run-context.test.ts +141 -0
- package/src/op/run-context.ts +51 -0
- package/src/op/runtimes/local.test.ts +14 -0
- package/src/op/runtimes/local.ts +2 -0
- package/src/op/steward-beside.ts +2 -0
- package/src/op/types.ts +8 -3
- package/src/op/work-lease-run.test.ts +47 -0
- package/src/op/work-lease-run.ts +73 -11
- package/src/provenance.ts +35 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/agent-cli.ts +7 -3
- package/src/workspace/agent.schema.json +22 -0
- package/src/workspace/box-factory.test.ts +320 -0
- package/src/workspace/box-factory.ts +103 -0
- package/src/workspace/changes.schema.json +12 -20
- package/src/workspace/changes.ts +4 -1
- package/src/workspace/checks/boxes.test.ts +2 -0
- package/src/workspace/checks.ts +29 -5
- package/src/workspace/compose-graph.ts +7 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
- package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
- package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
- package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
- package/src/workspace/conformance/index.mjs +30 -3
- package/src/workspace/conformance/index.ts +103 -15
- package/src/workspace/conformance/vitest.ts +44 -2
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
- package/src/workspace/conformance/writer.test.ts +156 -0
- package/src/workspace/conformance/writer.ts +880 -0
- package/src/workspace/decide.ts +7 -1
- package/src/workspace/declaration.schema.json +234 -5
- package/src/workspace/declaration.ts +174 -10
- package/src/workspace/graph-cli.ts +5 -2
- package/src/workspace/graph.schema.json +247 -0
- package/src/workspace/identity.test.ts +196 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/intent-cli.ts +64 -10
- package/src/workspace/intent-record.schema.json +208 -0
- package/src/workspace/intent-record.ts +35 -1
- package/src/workspace/intent-why.test.ts +274 -0
- package/src/workspace/intent.schema.json +718 -7
- package/src/workspace/intent.test.ts +11 -2
- package/src/workspace/intent.ts +495 -32
- package/src/workspace/kinds.ts +44 -37
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/reason-codes.test.ts +11 -3
- package/src/workspace/reason-codes.ts +24 -1
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +103 -11
- package/src/workspace/records-close.schema.json +1 -0
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.ts +28 -6
- package/src/workspace/records.schema.json +72 -1
- package/src/workspace/records.ts +27 -0
- package/src/workspace/runs-cli.ts +381 -0
- package/src/workspace/runs-write.schema.json +583 -0
- package/src/workspace/runs.schema.json +810 -0
- package/src/workspace/runs.test.ts +242 -0
- package/src/workspace/runs.ts +709 -0
- package/src/workspace/status-contract.test.ts +5 -0
- package/src/workspace/status-gates.ts +22 -8
- package/src/workspace/status.schema.json +127 -2
- package/src/workspace/status.ts +21 -1
- package/src/workspace/symbols.ts +180 -0
- package/src/workspace/trailer-joins.test.ts +171 -0
- package/src/workspace/trailer-joins.ts +82 -0
- package/src/workspace/trailers.ts +148 -0
- package/src/workspace/trust/seal.ts +52 -1
- package/src/workspace/work-cli.e2e.test.ts +28 -1
- package/src/workspace/work-cli.ts +133 -8
- package/src/workspace/work-evidence.schema.json +2 -0
- package/src/workspace/work-history.schema.json +73 -1
- package/src/workspace/work-history.test.ts +48 -5
- package/src/workspace/work-kind-fields.test.ts +154 -0
- package/src/workspace/work-lease.schema.json +14 -1
- package/src/workspace/work.ts +82 -0
- package/src/workspace/workspace-principals.schema.json +60 -0
- package/src/workspace/write-scope.test.ts +128 -2
- package/src/workspace/write-scope.ts +170 -45
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdir, writeFile, rm, realpath } from "node:fs/promises";
|
|
3
|
+
import { join, dirname, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { discover } from "./index";
|
|
6
|
+
import { build } from "../build";
|
|
7
|
+
import { walkValue } from "../serializer-walker";
|
|
8
|
+
import type { Declarable } from "../declarable";
|
|
9
|
+
import type { Serializer } from "../serializer";
|
|
10
|
+
import { foldExecutionCounts, resetFoldExecutionCounts } from "./fold-import";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* chant#3247 — a composite a lexicon PACKAGE publishes is interpreted like a
|
|
14
|
+
* project composite, so its fields keep their parameter provenance, and a
|
|
15
|
+
* composite behind a barrel re-export is found in the module that defines it.
|
|
16
|
+
*
|
|
17
|
+
* The real azure lexicon's `ActivityLogSink` is the package case: it is
|
|
18
|
+
* exported from the package root through `src/index.ts` and
|
|
19
|
+
* `src/composites/index.ts`, and its body is one `new PolicyAssignment({...})`
|
|
20
|
+
* reading two props. The fixtures sit under the repo's `.cache/` so a bare
|
|
21
|
+
* `@intentius/chant-lexicon-*` import resolves through this checkout's
|
|
22
|
+
* `node_modules`, as it does for an installed project. chant's own package
|
|
23
|
+
* specifiers are never `paths`-mapped, so the package route is the one taken.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const repoRoot = resolve(thisDir, "../../../..");
|
|
28
|
+
const compositePath = resolve(thisDir, "../composite");
|
|
29
|
+
|
|
30
|
+
const AZURE = "@intentius/chant-lexicon-azure";
|
|
31
|
+
const AWS = "@intentius/chant-lexicon-aws";
|
|
32
|
+
|
|
33
|
+
const MODULE_MARKER = "__chant3247CompositeModuleEvaluated";
|
|
34
|
+
type MarkerHost = Record<string, boolean | undefined>;
|
|
35
|
+
const moduleRan = (): boolean | undefined => (globalThis as unknown as MarkerHost)[MODULE_MARKER];
|
|
36
|
+
const clearMarker = (): void => {
|
|
37
|
+
delete (globalThis as unknown as MarkerHost)[MODULE_MARKER];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Renders every entity's ref-resolved props, enough to compare a fold build with a run build byte for byte. */
|
|
41
|
+
function propsSerializer(name: string): Serializer {
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
rulePrefix: "TEST",
|
|
45
|
+
serialize: (entities) => {
|
|
46
|
+
const names = new Map<Declarable, string>();
|
|
47
|
+
for (const [entityName, entity] of entities) names.set(entity, entityName);
|
|
48
|
+
const out: Record<string, unknown> = {};
|
|
49
|
+
for (const [entityName, entity] of [...entities].sort(([a], [b]) => a.localeCompare(b))) {
|
|
50
|
+
out[entityName] = {
|
|
51
|
+
type: entity.entityType,
|
|
52
|
+
props: walkValue((entity as unknown as { props: unknown }).props, names, {
|
|
53
|
+
attrRef: (logicalName, attribute) => ({ GetAtt: [logicalName, attribute] }),
|
|
54
|
+
resourceRef: (logicalName) => ({ Ref: logicalName }),
|
|
55
|
+
propertyDeclarable: (e, walk) => walk((e as unknown as { props: unknown }).props),
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(out, null, 2);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let seq = 0;
|
|
65
|
+
|
|
66
|
+
describe("lexicon-package and re-exported composites are interpreted (chant#3247)", () => {
|
|
67
|
+
let testDir: string;
|
|
68
|
+
let srcDir: string;
|
|
69
|
+
|
|
70
|
+
beforeEach(async () => {
|
|
71
|
+
const dir = join(repoRoot, ".cache", `chant-3247-${process.pid}-${seq++}`);
|
|
72
|
+
await rm(dir, { recursive: true, force: true });
|
|
73
|
+
await mkdir(dir, { recursive: true });
|
|
74
|
+
testDir = await realpath(dir);
|
|
75
|
+
srcDir = join(testDir, "src");
|
|
76
|
+
await mkdir(srcDir, { recursive: true });
|
|
77
|
+
clearMarker();
|
|
78
|
+
resetFoldExecutionCounts();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterEach(async () => {
|
|
82
|
+
clearMarker();
|
|
83
|
+
await rm(testDir, { recursive: true, force: true });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const CALL_SINK = `
|
|
87
|
+
import { ActivityLogSink } from ${JSON.stringify(AZURE)};
|
|
88
|
+
export const sink = ActivityLogSink({ workspaceId: "/subscriptions/s/workspaces/logs", location: "eastus" });
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
test("a package composite reached through its barrel is interpreted, and every field keeps its origin", async () => {
|
|
92
|
+
await writeFile(join(srcDir, "main.ts"), CALL_SINK);
|
|
93
|
+
|
|
94
|
+
const folded = await build(srcDir, [propsSerializer("azure")], undefined, { fold: true, lexicons: ["azure"] });
|
|
95
|
+
|
|
96
|
+
expect(folded.errors).toEqual([]);
|
|
97
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 1, factoryInvocations: 0 });
|
|
98
|
+
const fields = folded.foldProvenance.sinkAssignmentActivityLogSink!.fields;
|
|
99
|
+
const param = (...parameters: string[]) => ({
|
|
100
|
+
kind: "composite-parameter",
|
|
101
|
+
composite: "ActivityLogSink",
|
|
102
|
+
instance: "sink",
|
|
103
|
+
parameters,
|
|
104
|
+
});
|
|
105
|
+
expect(fields.location).toEqual(param("location"));
|
|
106
|
+
expect(fields["parameters.logAnalytics.value"]).toEqual(param("workspaceId"));
|
|
107
|
+
expect(fields.name).toEqual({ kind: "composite-literal", composite: "ActivityLogSink", instance: "sink" });
|
|
108
|
+
|
|
109
|
+
// Interpreting is not a second evaluation: the run path writes the same bytes.
|
|
110
|
+
const run = await build(srcDir, [propsSerializer("azure")], undefined, { fold: false, lexicons: ["azure"] });
|
|
111
|
+
expect(run.errors).toEqual([]);
|
|
112
|
+
expect(folded.outputs.get("azure")).toEqual(run.outputs.get("azure"));
|
|
113
|
+
expect(run.foldProvenance.sinkAssignmentActivityLogSink!.fields.location).toEqual({
|
|
114
|
+
kind: "unknown",
|
|
115
|
+
reason: "composite-not-interpreted",
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("under --sandbox the package's own imports are trusted, so it is still interpreted", async () => {
|
|
120
|
+
await writeFile(join(srcDir, "main.ts"), CALL_SINK);
|
|
121
|
+
|
|
122
|
+
const result = await discover(srcDir, { fold: true, sandbox: true, lexicons: ["azure"] });
|
|
123
|
+
|
|
124
|
+
expect(result.errors).toEqual([]);
|
|
125
|
+
expect(result.foldDecisions.find((d) => d.file.endsWith("main.ts"))?.mode).toBe("fold");
|
|
126
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 1, factoryInvocations: 0 });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("a lexicon the build did not load is not followed", async () => {
|
|
130
|
+
await writeFile(join(srcDir, "main.ts"), CALL_SINK);
|
|
131
|
+
|
|
132
|
+
await discover(srcDir, { fold: true, lexicons: ["aws"] });
|
|
133
|
+
|
|
134
|
+
expect(foldExecutionCounts().factoryInterpretations).toBe(0);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a project barrel's `export *` and renamed `export { } from` reach the defining module", async () => {
|
|
138
|
+
await mkdir(join(testDir, "lib", "composites"), { recursive: true });
|
|
139
|
+
await writeFile(
|
|
140
|
+
join(testDir, "lib", "composites", "web.ts"),
|
|
141
|
+
`
|
|
142
|
+
import { Bucket } from ${JSON.stringify(AWS)};
|
|
143
|
+
import { Composite } from ${JSON.stringify(compositePath)};
|
|
144
|
+
globalThis[${JSON.stringify(MODULE_MARKER)}] = true;
|
|
145
|
+
export const WebApp = Composite((props) => {
|
|
146
|
+
const bucket = new Bucket({ BucketName: props.name });
|
|
147
|
+
return { bucket };
|
|
148
|
+
}, "WebApp");
|
|
149
|
+
`,
|
|
150
|
+
);
|
|
151
|
+
await writeFile(join(testDir, "lib", "composites", "index.ts"), `export * from "./web";\n`);
|
|
152
|
+
await writeFile(
|
|
153
|
+
join(testDir, "lib", "index.ts"),
|
|
154
|
+
`export * from "./composites/index";\nexport { WebApp as Site } from "./composites/web";\n`,
|
|
155
|
+
);
|
|
156
|
+
await writeFile(
|
|
157
|
+
join(srcDir, "main.ts"),
|
|
158
|
+
`
|
|
159
|
+
import { WebApp, Site } from "../lib";
|
|
160
|
+
export const web = WebApp({ name: "data" });
|
|
161
|
+
export const site = Site({ name: "site" });
|
|
162
|
+
`,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const result = await discover(srcDir, { fold: true, lexicons: ["aws"] });
|
|
166
|
+
|
|
167
|
+
expect(result.errors).toEqual([]);
|
|
168
|
+
expect(moduleRan(), "the defining module was read, never imported").toBeUndefined();
|
|
169
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 2, factoryInvocations: 0 });
|
|
170
|
+
expect([...result.entities.keys()].sort()).toEqual(["siteBucket", "webBucket"]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
/** A stand-in lexicon package installed in the fixture's own `node_modules`. */
|
|
174
|
+
async function installFakeLexicon(name: string, files: Record<string, string>, exportsTarget: string): Promise<void> {
|
|
175
|
+
const pkgDir = join(testDir, "node_modules", "@intentius", `chant-lexicon-${name}`);
|
|
176
|
+
for (const [file, source] of Object.entries(files)) {
|
|
177
|
+
await mkdir(dirname(join(pkgDir, file)), { recursive: true });
|
|
178
|
+
await writeFile(join(pkgDir, file), source);
|
|
179
|
+
}
|
|
180
|
+
await writeFile(
|
|
181
|
+
join(pkgDir, "package.json"),
|
|
182
|
+
JSON.stringify({ name: `@intentius/chant-lexicon-${name}`, type: "module", exports: { ".": exportsTarget } }),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const WEBAPP_DEFINITION = `
|
|
187
|
+
import { Bucket } from ${JSON.stringify(AWS)};
|
|
188
|
+
import { Composite } from "@intentius/chant/composite";
|
|
189
|
+
globalThis[${JSON.stringify(MODULE_MARKER)}] = true;
|
|
190
|
+
export const WebApp = Composite((props) => {
|
|
191
|
+
const bucket = new Bucket({ BucketName: props.name });
|
|
192
|
+
return { bucket };
|
|
193
|
+
}, "WebApp");
|
|
194
|
+
`;
|
|
195
|
+
|
|
196
|
+
test("a package whose module is compiled JavaScript is invoked, not interpreted", async () => {
|
|
197
|
+
// The body is admissible (a single expression), so only the module's build
|
|
198
|
+
// shape keeps it from being interpreted.
|
|
199
|
+
await installFakeLexicon(
|
|
200
|
+
"fakejs3247",
|
|
201
|
+
{
|
|
202
|
+
"dist/index.js": `
|
|
203
|
+
import { Composite } from "@intentius/chant/composite";
|
|
204
|
+
globalThis[${JSON.stringify(MODULE_MARKER)}] = true;
|
|
205
|
+
export const Wrap = Composite((props) => ({ bucket: props.bucket }), "Wrap");
|
|
206
|
+
`,
|
|
207
|
+
},
|
|
208
|
+
"./dist/index.js",
|
|
209
|
+
);
|
|
210
|
+
await writeFile(
|
|
211
|
+
join(srcDir, "main.ts"),
|
|
212
|
+
`
|
|
213
|
+
import { Bucket } from ${JSON.stringify(AWS)};
|
|
214
|
+
import { Wrap } from "@intentius/chant-lexicon-fakejs3247";
|
|
215
|
+
export const web = Wrap({ bucket: new Bucket({ BucketName: "data" }) });
|
|
216
|
+
`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
await discover(srcDir, { fold: true, lexicons: ["aws", "fakejs3247"] });
|
|
220
|
+
|
|
221
|
+
// Declined before the module was parsed, so the call went to invocation.
|
|
222
|
+
// Whether that import then succeeds is the stand-in's business: plain Node
|
|
223
|
+
// cannot load chant's TypeScript source from a `.js` file, so it is not
|
|
224
|
+
// asserted here.
|
|
225
|
+
expect(foldExecutionCounts().factoryInterpretations).toBe(0);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("a package re-export that leaves the package's directory is not followed", async () => {
|
|
229
|
+
await mkdir(join(testDir, "shared"), { recursive: true });
|
|
230
|
+
await writeFile(join(testDir, "shared", "web.ts"), WEBAPP_DEFINITION);
|
|
231
|
+
await installFakeLexicon(
|
|
232
|
+
"fakeescape3247",
|
|
233
|
+
{ "src/index.ts": `export { WebApp } from "../../../../shared/web";\n` },
|
|
234
|
+
"./src/index.ts",
|
|
235
|
+
);
|
|
236
|
+
await writeFile(
|
|
237
|
+
join(srcDir, "main.ts"),
|
|
238
|
+
`import { WebApp } from "@intentius/chant-lexicon-fakeescape3247";\nexport const web = WebApp({ name: "data" });\n`,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const result = await discover(srcDir, { fold: true, lexicons: ["aws", "fakeescape3247"] });
|
|
242
|
+
|
|
243
|
+
expect(result.errors).toEqual([]);
|
|
244
|
+
expect(foldExecutionCounts().factoryInterpretations).toBe(0);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("the same package composite, TypeScript source, is interpreted", async () => {
|
|
248
|
+
await installFakeLexicon(
|
|
249
|
+
"fakets3247",
|
|
250
|
+
{ "src/index.ts": `export * from "./composites/index";\n`, "src/composites/index.ts": `export * from "./web";\n`, "src/composites/web.ts": WEBAPP_DEFINITION },
|
|
251
|
+
"./src/index.ts",
|
|
252
|
+
);
|
|
253
|
+
await writeFile(
|
|
254
|
+
join(srcDir, "main.ts"),
|
|
255
|
+
`import { WebApp } from "@intentius/chant-lexicon-fakets3247";\nexport const web = WebApp({ name: "data" });\n`,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const result = await discover(srcDir, { fold: true, lexicons: ["aws", "fakets3247"] });
|
|
259
|
+
|
|
260
|
+
expect(result.errors).toEqual([]);
|
|
261
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 1, factoryInvocations: 0 });
|
|
262
|
+
expect([...result.entities.keys()]).toEqual(["webBucket"]);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { foldProject } from "./fold-import";
|
|
6
|
+
import { packageExportsTarget, tsconfigPathsTarget } from "./specifier-resolve";
|
|
7
|
+
import type { IntrinsicDef } from "../lexicon";
|
|
8
|
+
|
|
9
|
+
/** `e` from `esm-only/sub/x` is an intrinsic, so fold has to import that subpath to revive the call. */
|
|
10
|
+
const intrinsics = [{ name: "e", lexicon: "esm", foldsAsCall: true } as unknown as IntrinsicDef];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* chant#3090 — the import specifiers `tsc` and `tsx` accept that fold's own
|
|
14
|
+
* resolver did not: a `.js` or `.mjs` specifier naming a `.ts` or `.mts`
|
|
15
|
+
* source, an `exports` subpath pattern with only an `import` condition, and a
|
|
16
|
+
* tsconfig `paths` entry. Each of these used to fall the importing file back
|
|
17
|
+
* to run with an "unresolved identifier".
|
|
18
|
+
*
|
|
19
|
+
* The fixture is the project from the issue, built in a temp directory
|
|
20
|
+
* because its `node_modules` would not be committed.
|
|
21
|
+
*/
|
|
22
|
+
describe("fold resolves the specifier forms tsc accepts (chant#3090)", () => {
|
|
23
|
+
let root: string;
|
|
24
|
+
|
|
25
|
+
const write = (rel: string, content: string): string => {
|
|
26
|
+
const path = join(root, rel);
|
|
27
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
28
|
+
writeFileSync(path, content);
|
|
29
|
+
return path;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
root = mkdtempSync(join(tmpdir(), "chant-fold-specifiers-"));
|
|
34
|
+
write("tsconfig.json", JSON.stringify({ compilerOptions: { paths: { "@app/*": ["./src/*"] } } }));
|
|
35
|
+
write("src/config.ts", 'export const a = "config";\n');
|
|
36
|
+
write("src/lib/index.ts", 'export const b = "lib";\n');
|
|
37
|
+
write("src/lib/helpers.mts", 'export const c = "helpers";\n');
|
|
38
|
+
write(
|
|
39
|
+
"node_modules/esm-only/package.json",
|
|
40
|
+
JSON.stringify({
|
|
41
|
+
name: "esm-only",
|
|
42
|
+
version: "1.0.0",
|
|
43
|
+
type: "module",
|
|
44
|
+
exports: { ".": { import: "./dist/index.js" }, "./sub/*": { import: "./sub/*.js" } },
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
write("node_modules/esm-only/dist/index.js", 'export const d = "esm-only";\n');
|
|
48
|
+
write("node_modules/esm-only/sub/x.js", "export function e(v) { return `sub:${v}`; }\n");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => rmSync(root, { recursive: true, force: true }));
|
|
52
|
+
|
|
53
|
+
const files = (): string[] => [
|
|
54
|
+
join(root, "src/config.ts"),
|
|
55
|
+
join(root, "src/lib/index.ts"),
|
|
56
|
+
join(root, "src/lib/helpers.mts"),
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
test("all six specifiers from the issue fold", async () => {
|
|
60
|
+
const main = write(
|
|
61
|
+
"src/main.ts",
|
|
62
|
+
[
|
|
63
|
+
'import { a } from "./config.js";',
|
|
64
|
+
'import { b } from "./lib";',
|
|
65
|
+
'import { c } from "./lib/helpers.mjs";',
|
|
66
|
+
'import { d } from "esm-only";',
|
|
67
|
+
'import { e } from "esm-only/sub/x";',
|
|
68
|
+
'import { a as a2 } from "@app/config";',
|
|
69
|
+
'export const values = [a, b, c, d, e("x"), a2];',
|
|
70
|
+
"",
|
|
71
|
+
].join("\n"),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const verdict = (await foldProject([main, ...files()], intrinsics, { lexiconPackages: ["esm-only"] })).get(main)!;
|
|
75
|
+
|
|
76
|
+
expect(verdict.reason).toBeUndefined();
|
|
77
|
+
expect(verdict.verdict).toBe("fold");
|
|
78
|
+
expect(Object.fromEntries(verdict.exports!)).toEqual({
|
|
79
|
+
values: ["config", "lib", "helpers", "esm-only", "sub:x", "config"],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("a .js specifier names the .ts source even when the .js file exists too", async () => {
|
|
84
|
+
write("src/config.js", 'export const a = "stale build output";\n');
|
|
85
|
+
const main = write("src/main.ts", 'import { a } from "./config.js";\nexport const v = a;\n');
|
|
86
|
+
|
|
87
|
+
const verdict = (await foldProject([main, ...files()])).get(main)!;
|
|
88
|
+
|
|
89
|
+
expect(verdict.verdict).toBe("fold");
|
|
90
|
+
expect(Object.fromEntries(verdict.exports!)).toEqual({ v: "config" });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("an unresolvable relative specifier keeps a run reason naming the specifier and the importing file", async () => {
|
|
94
|
+
const main = write("src/main.ts", 'import { a } from "./missing.js";\nexport const v = a;\n');
|
|
95
|
+
|
|
96
|
+
const verdict = (await foldProject([main, ...files()])).get(main)!;
|
|
97
|
+
|
|
98
|
+
// The `[fold:run]` line starts with the importing file; the reason names
|
|
99
|
+
// the specifier as the path it was looked for at.
|
|
100
|
+
expect(verdict.verdict).toBe("run");
|
|
101
|
+
expect(verdict.reason).toContain(join("src", "missing.js"));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("a paths pattern with no file behind it keeps a run reason", async () => {
|
|
105
|
+
const main = write("src/main.ts", 'import { a } from "@app/missing";\nexport const v = a;\n');
|
|
106
|
+
|
|
107
|
+
const verdict = (await foldProject([main, ...files()])).get(main)!;
|
|
108
|
+
|
|
109
|
+
expect(verdict.verdict).toBe("run");
|
|
110
|
+
expect(verdict.reason).toContain("unresolved identifier: a");
|
|
111
|
+
expect(verdict.reason).toContain('"@app/missing"');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("paths from an extended config resolve against the config that declares them", async () => {
|
|
115
|
+
write("tsconfig.json", JSON.stringify({ extends: "./configs/base" }));
|
|
116
|
+
write("configs/base.json", '{\n // JSONC, as tsc reads it\n "compilerOptions": { "paths": { "@app/*": ["../src/*"] } },\n}\n');
|
|
117
|
+
const main = write("src/main.ts", 'import { a } from "@app/config";\nexport const v = a;\n');
|
|
118
|
+
|
|
119
|
+
expect(tsconfigPathsTarget("@app/config", main)).toBe(join(root, "src/config.ts"));
|
|
120
|
+
const verdict = (await foldProject([main, ...files()])).get(main)!;
|
|
121
|
+
expect(verdict.verdict).toBe("fold");
|
|
122
|
+
expect(Object.fromEntries(verdict.exports!)).toEqual({ v: "config" });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("paths resolve against baseUrl when the chain sets one", async () => {
|
|
126
|
+
write("tsconfig.json", JSON.stringify({ extends: "./configs/base.json", compilerOptions: { baseUrl: "." } }));
|
|
127
|
+
write("configs/base.json", JSON.stringify({ compilerOptions: { paths: { "@app/*": ["src/*"] } } }));
|
|
128
|
+
const main = write("src/main.ts", "");
|
|
129
|
+
|
|
130
|
+
expect(tsconfigPathsTarget("@app/lib", main)).toBe(join(root, "src/lib/index.ts"));
|
|
131
|
+
expect(tsconfigPathsTarget("@other/lib", main)).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("an exact paths key wins over a pattern, and the longest pattern prefix wins", async () => {
|
|
135
|
+
write("src/special.ts", "export const s = 1;\n");
|
|
136
|
+
write(
|
|
137
|
+
"tsconfig.json",
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
compilerOptions: { paths: { "@app/*": ["./src/*"], "@app/lib/*": ["./src/lib/*"], "@app/x": ["./src/special.ts"] } },
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
const main = write("src/main.ts", "");
|
|
143
|
+
|
|
144
|
+
expect(tsconfigPathsTarget("@app/x", main)).toBe(join(root, "src/special.ts"));
|
|
145
|
+
expect(tsconfigPathsTarget("@app/lib/helpers.mjs", main)).toBe(join(root, "src/lib/helpers.mts"));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("a paths entry that maps an active lexicon's name is a project file, and its trust is decided on that path", async () => {
|
|
149
|
+
write("tsconfig.json", JSON.stringify({ compilerOptions: { paths: { "esm-only": ["./src/shadow.ts"] } } }));
|
|
150
|
+
const shadow = write(
|
|
151
|
+
"src/shadow.ts",
|
|
152
|
+
'export const d = "shadow";\nexport function make(v: string) { return `${v}:${process.pid}`; }\n',
|
|
153
|
+
);
|
|
154
|
+
const dataUser = write("src/data.ts", 'import { d } from "esm-only";\nexport const v = d;\n');
|
|
155
|
+
const callUser = write("src/call.ts", 'import { make } from "esm-only";\nexport const v = make("x");\n');
|
|
156
|
+
const all = [dataUser, callUser, shadow, ...files()];
|
|
157
|
+
|
|
158
|
+
const plain = await foldProject(all, [], { lexiconPackages: ["esm-only"] });
|
|
159
|
+
// The project file is folded, not the package's export read.
|
|
160
|
+
expect(Object.fromEntries(plain.get(dataUser)!.exports!)).toEqual({ v: "shadow" });
|
|
161
|
+
// Invoking it is invoking project code, which the default mode refuses,
|
|
162
|
+
// whatever the specifier's text says.
|
|
163
|
+
expect(plain.get(callUser)!.verdict).toBe("run");
|
|
164
|
+
expect(plain.get(callUser)!.reason).toContain(join("src", "shadow.ts"));
|
|
165
|
+
expect(plain.get(callUser)!.reason).toContain("does not invoke a declared project function");
|
|
166
|
+
|
|
167
|
+
const sandboxed = await foldProject(all, [], { lexiconPackages: ["esm-only"], sandbox: true });
|
|
168
|
+
expect(sandboxed.get(callUser)!.verdict).toBe("run");
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("packageExportsTarget (chant#3090)", () => {
|
|
173
|
+
test("reads condition objects in key order against import, node and default", () => {
|
|
174
|
+
expect(packageExportsTarget({ ".": { types: "./a.d.ts", import: "./a.mjs", default: "./a.cjs" } }, ".")).toBe("./a.mjs");
|
|
175
|
+
expect(packageExportsTarget({ require: "./r.cjs", node: { import: "./n.mjs" } }, ".")).toBe("./n.mjs");
|
|
176
|
+
expect(packageExportsTarget({ ".": { require: "./r.cjs" } }, ".")).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("an exact subpath key wins, then the longest pattern prefix", () => {
|
|
180
|
+
const map = { "./*": "./src/*.js", "./sub/*": "./sub/*.js", "./sub/x": "./exact.js", "./internal/*": null };
|
|
181
|
+
expect(packageExportsTarget(map, "./sub/x")).toBe("./exact.js");
|
|
182
|
+
expect(packageExportsTarget(map, "./sub/y")).toBe("./sub/y.js");
|
|
183
|
+
expect(packageExportsTarget(map, "./top")).toBe("./src/top.js");
|
|
184
|
+
expect(packageExportsTarget(map, "./internal/z")).toBeUndefined();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("a subpath is not exported by a root-only exports field", () => {
|
|
188
|
+
expect(packageExportsTarget("./index.js", "./sub")).toBeUndefined();
|
|
189
|
+
expect(packageExportsTarget({ import: "./index.js" }, "./sub")).toBeUndefined();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
2
|
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { collectConsts } from "../fold/fold";
|
|
4
|
-
import { collectCompositeOrigins, collectParamDependencies } from "./param-deps";
|
|
4
|
+
import { collectCompositeOrigins, collectParamDependencies, collectTagOrigins } from "./param-deps";
|
|
5
5
|
import type { PathOrigin } from "../provenance";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -227,3 +227,68 @@ describe("collectCompositeOrigins", () => {
|
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
229
|
});
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* chant #3212 — the same join for a registered tag. The fixture body ends in
|
|
233
|
+
* `return table\`...\``; `fields` stands in for what the tag reports per
|
|
234
|
+
* interpolation, and `props` for the entity it built.
|
|
235
|
+
*/
|
|
236
|
+
function tagOriginsOf(body: string, fields: string[][], props: Record<string, unknown>): Record<string, PathOrigin> {
|
|
237
|
+
const file = ts.createSourceFile("factory.ts", `const f = (props) => { ${body} };`, ts.ScriptTarget.Latest, true);
|
|
238
|
+
const consts = new Map<string, ts.Expression>();
|
|
239
|
+
let tagged: ts.TaggedTemplateExpression | undefined;
|
|
240
|
+
const visit = (node: ts.Node): void => {
|
|
241
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
242
|
+
consts.set(node.name.text, node.initializer);
|
|
243
|
+
}
|
|
244
|
+
if (ts.isReturnStatement(node) && node.expression && ts.isTaggedTemplateExpression(node.expression)) {
|
|
245
|
+
tagged = node.expression;
|
|
246
|
+
}
|
|
247
|
+
ts.forEachChild(node, visit);
|
|
248
|
+
};
|
|
249
|
+
visit(file);
|
|
250
|
+
if (!tagged || !ts.isTemplateExpression(tagged.template)) throw new Error("fixture returns no interpolated tag");
|
|
251
|
+
return collectTagOrigins(
|
|
252
|
+
tagged.template.templateSpans.map((span) => span.expression),
|
|
253
|
+
fields,
|
|
254
|
+
props,
|
|
255
|
+
consts,
|
|
256
|
+
{ whole: new Set(["props"]), destructured: new Map() },
|
|
257
|
+
"WebService",
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
describe("collectTagOrigins", () => {
|
|
262
|
+
test("a field an interpolation fed is that interpolation's parameters; every other key is fixed", () => {
|
|
263
|
+
expect(
|
|
264
|
+
tagOriginsOf(
|
|
265
|
+
"return table`CREATE TABLE ${props.name} ENGINE = MergeTree TTL ts + INTERVAL ${props.ttl.days} DAY`;",
|
|
266
|
+
[["name", "ddl"], ["ttl", "ddl"]],
|
|
267
|
+
{ name: "t", engine: "MergeTree", ttl: "ts + INTERVAL 30 DAY", ddl: "...", absent: undefined },
|
|
268
|
+
),
|
|
269
|
+
).toEqual({
|
|
270
|
+
name: fromParam("name"),
|
|
271
|
+
ttl: fromParam("ttl.days"),
|
|
272
|
+
ddl: fromParam("name", "ttl.days"),
|
|
273
|
+
engine: fixed,
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("a body const is followed, a sibling entity is not, and an interpolation reading no parameter adds nothing", () => {
|
|
278
|
+
expect(
|
|
279
|
+
tagOriginsOf(
|
|
280
|
+
"const base = `${props.name}_daily`; const events = new Thing({}); return table`CREATE TABLE ${base} ENGINE = ${\"Summing\"} COMMENT ${events}`;",
|
|
281
|
+
[["name"], ["engine"], ["comment"]],
|
|
282
|
+
{ name: "t_daily", engine: "Summing", comment: "events" },
|
|
283
|
+
),
|
|
284
|
+
).toEqual({ name: fromParam("name"), engine: fixed, comment: fixed });
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("a parameter recorded under a key outranks the key's fixed record", () => {
|
|
288
|
+
expect(
|
|
289
|
+
tagOriginsOf("return table`ENGINE = Replacing(${props.version})`;", [["engine.args"]], {
|
|
290
|
+
engine: { name: "Replacing", args: ["v"] },
|
|
291
|
+
}),
|
|
292
|
+
).toEqual({ "engine.args": fromParam("version"), engine: fixed });
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -361,3 +361,48 @@ export function collectCompositeOrigins(
|
|
|
361
361
|
walk(props, "");
|
|
362
362
|
return out;
|
|
363
363
|
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Path → composite origin for an entity a registered tag built inside an
|
|
367
|
+
* interpreted composite factory body (chant #3212).
|
|
368
|
+
*
|
|
369
|
+
* The tag reports which props paths each interpolation fed (`fields`, from
|
|
370
|
+
* `setInterpolationFields`), because only the tag parsed the text. This joins
|
|
371
|
+
* that with the factory parameters each interpolation's expression reads,
|
|
372
|
+
* walked exactly as {@link collectCompositeOrigins} walks a property's
|
|
373
|
+
* expression, so a body `const` is followed and a sibling entity is not.
|
|
374
|
+
*
|
|
375
|
+
* A path fed by an interpolation that reads a parameter is that parameter. Every
|
|
376
|
+
* other top-level key of `props` is something the factory's text fixes, and is
|
|
377
|
+
* recorded `composite-literal` at the key, which a parameter recorded deeper
|
|
378
|
+
* still outranks by longest prefix.
|
|
379
|
+
*/
|
|
380
|
+
export function collectTagOrigins(
|
|
381
|
+
interpolations: readonly ts.Expression[],
|
|
382
|
+
fields: ReadonlyArray<readonly string[]>,
|
|
383
|
+
props: unknown,
|
|
384
|
+
consts: ReadonlyMap<string, ts.Expression>,
|
|
385
|
+
scope: CompositeParamScope,
|
|
386
|
+
composite: string,
|
|
387
|
+
): Record<string, PathOrigin> {
|
|
388
|
+
const out: Record<string, PathOrigin> = {};
|
|
389
|
+
|
|
390
|
+
interpolations.forEach((expr, index) => {
|
|
391
|
+
const found = new Set<string>();
|
|
392
|
+
readCompositeParameters(expr, consts, scope, found);
|
|
393
|
+
if (found.size === 0) return;
|
|
394
|
+
for (const path of fields[index] ?? []) {
|
|
395
|
+
const existing = out[path];
|
|
396
|
+
const merged = new Set(existing?.kind === "composite-parameter" ? existing.parameters : []);
|
|
397
|
+
for (const parameter of found) merged.add(parameter);
|
|
398
|
+
out[path] = { kind: "composite-parameter", composite, parameters: [...merged].sort() };
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
if (props !== null && typeof props === "object" && !Array.isArray(props)) {
|
|
403
|
+
for (const [key, value] of Object.entries(props)) {
|
|
404
|
+
if (value !== undefined) out[key] ??= { kind: "composite-literal", composite };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}
|