@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
|
@@ -4,7 +4,7 @@ import { existsSync, statSync, readFileSync, realpathSync } from "node:fs";
|
|
|
4
4
|
import { dirname, basename, join, sep, isAbsolute, resolve as resolvePath } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
-
import { isDeclarable, type Declarable } from "../declarable";
|
|
7
|
+
import { isDeclarable, isResourceDeclarable, type Declarable } from "../declarable";
|
|
8
8
|
import { Composite, isCompositeInstance, type CompositeInstance, type CompositeMembers } from "../composite";
|
|
9
9
|
import { isAttrRefLike } from "../utils";
|
|
10
10
|
import { isIntrinsic } from "../intrinsic";
|
|
@@ -28,6 +28,13 @@ import {
|
|
|
28
28
|
} from "../fold/fold";
|
|
29
29
|
import { isChantOwnedSpecifier, isFoldableHelperName } from "../fold/foldable-helpers";
|
|
30
30
|
import { lexiconModulePath, lexiconModuleRoot } from "../lexicon-module";
|
|
31
|
+
import {
|
|
32
|
+
isFile,
|
|
33
|
+
packageExportsTarget,
|
|
34
|
+
probeModuleFile,
|
|
35
|
+
splitBareSpecifier,
|
|
36
|
+
tsconfigPathsMatch,
|
|
37
|
+
} from "./specifier-resolve";
|
|
31
38
|
import {
|
|
32
39
|
briefNodeText,
|
|
33
40
|
callExpressionMessage,
|
|
@@ -44,10 +51,11 @@ import { importModule } from "./import";
|
|
|
44
51
|
import {
|
|
45
52
|
collectCompositeOrigins,
|
|
46
53
|
collectParamDependencies,
|
|
54
|
+
collectTagOrigins,
|
|
47
55
|
type CompositeParamScope,
|
|
48
56
|
} from "./param-deps";
|
|
49
|
-
import { setPathProvenance } from "../provenance";
|
|
50
|
-
import { intrinsicCallFoldsEagerly, type IntrinsicDef } from "../lexicon";
|
|
57
|
+
import { getInterpolationFields, setPathProvenance } from "../provenance";
|
|
58
|
+
import { intrinsicCallFoldsEagerly, intrinsicTagFolds, type IntrinsicDef } from "../lexicon";
|
|
51
59
|
import type { BuildParamValue } from "../build-params";
|
|
52
60
|
|
|
53
61
|
/**
|
|
@@ -292,7 +300,8 @@ export interface FoldSession {
|
|
|
292
300
|
readonly executing: boolean;
|
|
293
301
|
/**
|
|
294
302
|
* chant #1023 — per-build memo for {@link readFactoryModule}, keyed by the
|
|
295
|
-
* resolved absolute path of a module that DEFINES a composite
|
|
303
|
+
* resolved absolute path of a module that DEFINES a composite (and, since
|
|
304
|
+
* chant#3247, the lexicon package root it was reached in, if any). A composite
|
|
296
305
|
* defined once and called from a dozen sibling files is parsed, and its
|
|
297
306
|
* imports resolved, exactly once per build — the same reason
|
|
298
307
|
* {@link FoldSession.cache} exists for the files discovery folds directly.
|
|
@@ -454,14 +463,24 @@ function resolveModulePathMemoized(
|
|
|
454
463
|
resolvePathCache: Map<string, string>,
|
|
455
464
|
): string {
|
|
456
465
|
const isBare = !specifier.startsWith(".") && !isAbsolute(specifier);
|
|
466
|
+
// chant#3090 — a tsconfig `paths` answer depends on the importing file's
|
|
467
|
+
// directory and can name a project file that comes and goes between
|
|
468
|
+
// builds, so it is memoized per session and directory, like a relative one.
|
|
469
|
+
const key = `${dirname(fromFile)}\0${specifier}`;
|
|
457
470
|
if (isBare) {
|
|
471
|
+
const sessionCached = resolvePathCache.get(key);
|
|
472
|
+
if (sessionCached !== undefined) return sessionCached;
|
|
473
|
+
const mapped = pathsMappedTarget(specifier, fromFile);
|
|
474
|
+
if (mapped !== undefined) {
|
|
475
|
+
resolvePathCache.set(key, mapped);
|
|
476
|
+
return mapped;
|
|
477
|
+
}
|
|
458
478
|
const cached = bareSpecifierPathCache.get(specifier);
|
|
459
479
|
if (cached !== undefined) return cached;
|
|
460
480
|
const resolved = resolveModulePath(specifier, fromFile);
|
|
461
481
|
bareSpecifierPathCache.set(specifier, resolved);
|
|
462
482
|
return resolved;
|
|
463
483
|
}
|
|
464
|
-
const key = `${dirname(fromFile)}\0${specifier}`;
|
|
465
484
|
const cached = resolvePathCache.get(key);
|
|
466
485
|
if (cached !== undefined) return cached;
|
|
467
486
|
const resolved = resolveModulePath(specifier, fromFile);
|
|
@@ -543,10 +562,10 @@ export interface FoldExecutionCounts {
|
|
|
543
562
|
factoryInvocations: number;
|
|
544
563
|
/**
|
|
545
564
|
* Of {@link factoryInvocations}, the ones whose callee came from a PROJECT
|
|
546
|
-
* FILE (a relative/absolute specifier
|
|
547
|
-
* chant's own — i.e. the ones that
|
|
548
|
-
*
|
|
549
|
-
* classify.
|
|
565
|
+
* FILE (a relative/absolute specifier, or one a tsconfig `paths` entry
|
|
566
|
+
* maps) rather than a lexicon package or chant's own — i.e. the ones that
|
|
567
|
+
* execute project-authored code here ({@link isProjectImport}); no package
|
|
568
|
+
* resolution is performed to classify.
|
|
550
569
|
*/
|
|
551
570
|
projectFactoryInvocations: number;
|
|
552
571
|
/**
|
|
@@ -927,16 +946,21 @@ function collectImports(sourceFile: ts.SourceFile): CollectedImports {
|
|
|
927
946
|
/**
|
|
928
947
|
* chant #1020 hang fix — best-effort fast path for resolving a BARE
|
|
929
948
|
* specifier by walking `node_modules` directly with the same primitives
|
|
930
|
-
* (`
|
|
949
|
+
* (`statSync`/`readFileSync`) the relative-specifier branch below already
|
|
931
950
|
* uses, reading `package.json`'s "exports"/"main" field by hand instead of
|
|
932
951
|
* calling `createRequire(fromFile).resolve(specifier)`. Returns `undefined`
|
|
933
|
-
* — never throws —
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
* `
|
|
938
|
-
*
|
|
939
|
-
*
|
|
952
|
+
* — never throws — when the package is not found, its `exports` map does not
|
|
953
|
+
* export the subpath under the `import`/`node`/`default` conditions, or a
|
|
954
|
+
* subpath of a package without `exports` has no `package.json` of its own:
|
|
955
|
+
* the caller then falls back to the slow, authoritative
|
|
956
|
+
* `createRequire().resolve()` path, so such a specifier is still resolved
|
|
957
|
+
* correctly, just not quickly.
|
|
958
|
+
*
|
|
959
|
+
* chant#3090 — subpaths (`pkg/sub/x`) and `exports` pattern keys
|
|
960
|
+
* (`"./sub/*"`) resolve here too, with condition objects read in key order
|
|
961
|
+
* as an ESM import reads them ({@link packageExportsTarget}). Before, a
|
|
962
|
+
* subpath always reached the `createRequire` fallback, whose CommonJS
|
|
963
|
+
* conditions miss an `import`-only target.
|
|
940
964
|
*
|
|
941
965
|
* Why this exists at all: profiling traced the hang to
|
|
942
966
|
* `createRequire(fromFile).resolve(specifier)` ITSELF taking upwards of a
|
|
@@ -946,22 +970,22 @@ function collectImports(sourceFile: ts.SourceFile): CollectedImports {
|
|
|
946
970
|
* process (session-level and even process-wide-by-specifier caching, see
|
|
947
971
|
* {@link bareSpecifierPathCache}, only avoid paying that cost a SECOND
|
|
948
972
|
* time). The relative-specifier branch just below, using these exact same
|
|
949
|
-
*
|
|
973
|
+
* file-system primitives, never showed this slowdown anywhere
|
|
950
974
|
* in the same profiling — the cost is specific to Node's own
|
|
951
975
|
* `Module._resolveFilename` machinery, not to file-system access in
|
|
952
|
-
* general, so replacing just that one call with plain `
|
|
976
|
+
* general, so replacing just that one call with plain `statSync`/
|
|
953
977
|
* `readFileSync` sidesteps it entirely for the common case every
|
|
954
978
|
* `@intentius/chant*` package (and most well-formed npm packages) ships.
|
|
955
979
|
*/
|
|
956
980
|
function fastResolveBareSpecifier(specifier: string, fromFile: string): string | undefined {
|
|
981
|
+
const parts = splitBareSpecifier(specifier);
|
|
982
|
+
if (parts === undefined) return undefined;
|
|
957
983
|
let dir = dirname(fromFile);
|
|
958
984
|
for (;;) {
|
|
959
|
-
const packageDir = join(dir, "node_modules",
|
|
985
|
+
const packageDir = join(dir, "node_modules", parts.name);
|
|
960
986
|
if (existsSync(packageDir)) {
|
|
961
|
-
const
|
|
962
|
-
if (
|
|
963
|
-
const resolved = resolvePath(packageDir, entry);
|
|
964
|
-
if (!existsSync(resolved) || !statSync(resolved).isFile()) return undefined;
|
|
987
|
+
const resolved = fastResolvePackageEntry(packageDir, parts.subpath);
|
|
988
|
+
if (resolved === undefined || !isFile(resolved)) return undefined;
|
|
965
989
|
try {
|
|
966
990
|
return realpathSync(resolved);
|
|
967
991
|
} catch {
|
|
@@ -974,8 +998,14 @@ function fastResolveBareSpecifier(specifier: string, fromFile: string): string |
|
|
|
974
998
|
}
|
|
975
999
|
}
|
|
976
1000
|
|
|
977
|
-
/**
|
|
978
|
-
|
|
1001
|
+
/**
|
|
1002
|
+
* The absolute file `<packageDir>/package.json` names for `subpath` (`"."` or
|
|
1003
|
+
* `"./x"`), or `undefined` — see {@link fastResolveBareSpecifier}'s doc for
|
|
1004
|
+
* which shapes this recognizes. With an `exports` field, that map decides.
|
|
1005
|
+
* Without one, the root is `main` (or `index.js`), and a subpath is read as a
|
|
1006
|
+
* nested package directory with its own `package.json`.
|
|
1007
|
+
*/
|
|
1008
|
+
function fastResolvePackageEntry(packageDir: string, subpath: string): string | undefined {
|
|
979
1009
|
const pkgJsonPath = join(packageDir, "package.json");
|
|
980
1010
|
if (!existsSync(pkgJsonPath)) return undefined;
|
|
981
1011
|
let pkg: unknown;
|
|
@@ -988,68 +1018,63 @@ function fastResolvePackageEntry(packageDir: string): string | undefined {
|
|
|
988
1018
|
const exportsField = (pkg as Record<string, unknown>).exports;
|
|
989
1019
|
|
|
990
1020
|
if (exportsField !== undefined) {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
return undefined;
|
|
994
|
-
}
|
|
995
|
-
const exportsObj = exportsField as Record<string, unknown>;
|
|
996
|
-
// No "." key at all means the WHOLE object IS the "." export's own
|
|
997
|
-
// condition map (the shorthand form) — only when none of its OWN keys
|
|
998
|
-
// look like a subpath/condition-name ambiguity risk (a key starting
|
|
999
|
-
// with "." that isn't literally "." itself signals real subpath
|
|
1000
|
-
// exports present, so bail rather than misparse).
|
|
1001
|
-
const hasSubpathKeys = Object.keys(exportsObj).some((k) => k.startsWith(".") && k !== ".");
|
|
1002
|
-
const target = "." in exportsObj ? exportsObj["."] : hasSubpathKeys ? undefined : exportsObj;
|
|
1003
|
-
if (target === undefined) return undefined;
|
|
1004
|
-
if (typeof target === "string") return target;
|
|
1005
|
-
if (typeof target !== "object" || target === null || Array.isArray(target)) return undefined;
|
|
1006
|
-
const conditions = target as Record<string, unknown>;
|
|
1007
|
-
// Prefer "default" (present on every chant/lexicon package and the
|
|
1008
|
-
// overwhelming majority of well-formed dual-mode npm packages); "node"/
|
|
1009
|
-
// "import" as narrower fallbacks. Every chant package's own "default"
|
|
1010
|
-
// and "development" conditions point at the identical source file, so
|
|
1011
|
-
// which one Node's own algorithm would have picked never matters here.
|
|
1012
|
-
for (const key of ["default", "node", "import"]) {
|
|
1013
|
-
const val = conditions[key];
|
|
1014
|
-
if (typeof val === "string") return val;
|
|
1015
|
-
}
|
|
1016
|
-
return undefined;
|
|
1021
|
+
const target = packageExportsTarget(exportsField, subpath);
|
|
1022
|
+
return target === undefined ? undefined : resolvePath(packageDir, target);
|
|
1017
1023
|
}
|
|
1018
1024
|
|
|
1025
|
+
if (subpath !== ".") return fastResolvePackageEntry(join(packageDir, subpath), ".");
|
|
1019
1026
|
const main = (pkg as Record<string, unknown>).main;
|
|
1020
|
-
if (main !== undefined) return typeof main === "string" ? main : undefined;
|
|
1021
|
-
return "index.js";
|
|
1027
|
+
if (main !== undefined) return typeof main === "string" ? resolvePath(packageDir, main) : undefined;
|
|
1028
|
+
return resolvePath(packageDir, "index.js");
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* chant#3090 — the file a tsconfig `paths` entry maps a bare `specifier` to,
|
|
1033
|
+
* from the nearest `tsconfig.json` above `fromFile` ({@link tsconfigPathsTarget}),
|
|
1034
|
+
* or `undefined`.
|
|
1035
|
+
*
|
|
1036
|
+
* chant's own package specifiers ({@link isChantOwnedSpecifier}) are never
|
|
1037
|
+
* mapped. They resolve through `node_modules` to the modules the CLI has
|
|
1038
|
+
* already loaded, which is what {@link isTrustedExecutableBinding} relies on,
|
|
1039
|
+
* and a project's `paths` cannot point them at its own files. This repository's
|
|
1040
|
+
* root tsconfig maps every chant package to its source, and those entries
|
|
1041
|
+
* name the same files `node_modules` does.
|
|
1042
|
+
*
|
|
1043
|
+
* A specifier this maps is a project import ({@link isProjectImport}): it is
|
|
1044
|
+
* folded as a project file and its trust is decided on the resolved path,
|
|
1045
|
+
* even when its text names an active lexicon package.
|
|
1046
|
+
*/
|
|
1047
|
+
function pathsMappedTarget(specifier: string, fromFile: string): string | undefined {
|
|
1048
|
+
return pathsMatch(specifier, fromFile)?.target;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/** The tsconfig `paths` match behind {@link pathsMappedTarget}, including a match whose targets name no file. */
|
|
1052
|
+
function pathsMatch(specifier: string, fromFile: string): { key: string; target: string | undefined } | undefined {
|
|
1053
|
+
if (specifier.startsWith(".") || isAbsolute(specifier) || isChantOwnedSpecifier(specifier)) return undefined;
|
|
1054
|
+
return tsconfigPathsMatch(specifier, fromFile);
|
|
1022
1055
|
}
|
|
1023
1056
|
|
|
1024
1057
|
/**
|
|
1025
1058
|
* Resolve an import specifier to an absolute module path, the way the
|
|
1026
1059
|
* declaring file's own `import` would — without depending on a TS-aware
|
|
1027
1060
|
* loader being active. Relative/absolute specifiers are probed against real
|
|
1028
|
-
* TS/JS candidate files on disk
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1061
|
+
* TS/JS candidate files on disk ({@link probeModuleFile}, which maps `./x.js`
|
|
1062
|
+
* to `./x.ts` as `tsc` does); a bare specifier a tsconfig `paths` entry maps
|
|
1063
|
+
* resolves to that file ({@link pathsMappedTarget}); other bare package
|
|
1064
|
+
* specifiers try {@link fastResolveBareSpecifier} first, falling back to
|
|
1065
|
+
* Node's own CJS algorithm from the declaring file's location whenever that
|
|
1066
|
+
* returns `undefined`.
|
|
1032
1067
|
*/
|
|
1033
1068
|
function resolveModulePath(specifier: string, fromFile: string): string {
|
|
1034
1069
|
if (specifier.startsWith(".") || isAbsolute(specifier)) {
|
|
1035
1070
|
const base = specifier.startsWith(".") ? resolvePath(dirname(fromFile), specifier) : specifier;
|
|
1036
|
-
const candidates = [
|
|
1037
|
-
base,
|
|
1038
|
-
`${base}.ts`,
|
|
1039
|
-
`${base}.tsx`,
|
|
1040
|
-
`${base}.js`,
|
|
1041
|
-
`${base}.mjs`,
|
|
1042
|
-
join(base, "index.ts"),
|
|
1043
|
-
join(base, "index.js"),
|
|
1044
|
-
];
|
|
1045
|
-
for (const candidate of candidates) {
|
|
1046
|
-
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
|
|
1047
|
-
}
|
|
1048
1071
|
// Nothing found on disk under any probed extension — hand back the bare
|
|
1049
1072
|
// base and let `import()` fail with its own, more specific error.
|
|
1050
|
-
return base;
|
|
1073
|
+
return probeModuleFile(base) ?? base;
|
|
1051
1074
|
}
|
|
1052
1075
|
|
|
1076
|
+
const mapped = pathsMappedTarget(specifier, fromFile);
|
|
1077
|
+
if (mapped !== undefined) return mapped;
|
|
1053
1078
|
const fast = fastResolveBareSpecifier(specifier, fromFile);
|
|
1054
1079
|
if (fast !== undefined) return fast;
|
|
1055
1080
|
return createRequire(fromFile).resolve(specifier);
|
|
@@ -1150,6 +1175,22 @@ function activeLexiconPackage(specifier: string, lexiconPackages: ReadonlySet<st
|
|
|
1150
1175
|
return lexiconPackages.has(specifier) ? specifier : undefined;
|
|
1151
1176
|
}
|
|
1152
1177
|
|
|
1178
|
+
/**
|
|
1179
|
+
* chant#3090 — {@link activeLexiconPackage} for a specifier imported by
|
|
1180
|
+
* `fromFile`: `undefined` as well when a tsconfig `paths` entry maps the
|
|
1181
|
+
* specifier ({@link pathsMappedTarget}), because the import then names a
|
|
1182
|
+
* project file, not the package the build loaded. Only a specifier whose text
|
|
1183
|
+
* already matches pays the tsconfig lookup.
|
|
1184
|
+
*/
|
|
1185
|
+
function activeLexiconPackageImport(
|
|
1186
|
+
specifier: string,
|
|
1187
|
+
fromFile: string,
|
|
1188
|
+
lexiconPackages: ReadonlySet<string>,
|
|
1189
|
+
): string | undefined {
|
|
1190
|
+
if (activeLexiconPackage(specifier, lexiconPackages) === undefined) return undefined;
|
|
1191
|
+
return pathsMappedTarget(specifier, fromFile) === undefined ? specifier : undefined;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1153
1194
|
/**
|
|
1154
1195
|
* chant#2577 — the path-declared counterpart of {@link activeLexiconPackage}:
|
|
1155
1196
|
* the resolved module path when a relative or absolute `specifier` reaches
|
|
@@ -1163,7 +1204,7 @@ function activeLexiconModule(
|
|
|
1163
1204
|
lexiconModules: ReadonlySet<string>,
|
|
1164
1205
|
resolvePathCache: Map<string, string>,
|
|
1165
1206
|
): string | undefined {
|
|
1166
|
-
if (lexiconModules.size === 0 || !
|
|
1207
|
+
if (lexiconModules.size === 0 || !isProjectImport(specifier, fromFile)) return undefined;
|
|
1167
1208
|
let modulePath: string;
|
|
1168
1209
|
try {
|
|
1169
1210
|
modulePath = resolveModulePathMemoized(specifier, fromFile, resolvePathCache);
|
|
@@ -1248,7 +1289,7 @@ async function resolveActiveLexiconExport(
|
|
|
1248
1289
|
session: FoldSession,
|
|
1249
1290
|
): Promise<{ value: unknown } | undefined> {
|
|
1250
1291
|
let modulePath: string | undefined;
|
|
1251
|
-
if (
|
|
1292
|
+
if (activeLexiconPackageImport(binding.specifier, fromFile, session.lexiconPackages)) {
|
|
1252
1293
|
try {
|
|
1253
1294
|
modulePath = resolveModulePathMemoized(binding.specifier, fromFile, session.resolvePathCache);
|
|
1254
1295
|
} catch {
|
|
@@ -1276,6 +1317,27 @@ async function resolveActiveLexiconExport(
|
|
|
1276
1317
|
return { value };
|
|
1277
1318
|
}
|
|
1278
1319
|
|
|
1320
|
+
/**
|
|
1321
|
+
* chant#3247 — a plain data export of a lexicon package's own module, read off
|
|
1322
|
+
* the real module (see the package-root branch of {@link buildExternals}).
|
|
1323
|
+
* `undefined` for a missing export, a callable one, or a module that does not
|
|
1324
|
+
* import.
|
|
1325
|
+
*/
|
|
1326
|
+
async function packageDataExport(
|
|
1327
|
+
modulePath: string,
|
|
1328
|
+
name: string,
|
|
1329
|
+
session: FoldSession,
|
|
1330
|
+
): Promise<{ value: unknown } | undefined> {
|
|
1331
|
+
let mod: Record<string, unknown>;
|
|
1332
|
+
try {
|
|
1333
|
+
mod = await importModuleMemoized(modulePath, session.importCache);
|
|
1334
|
+
} catch {
|
|
1335
|
+
return undefined;
|
|
1336
|
+
}
|
|
1337
|
+
if (!(name in mod) || typeof mod[name] === "function") return undefined;
|
|
1338
|
+
return { value: mod[name] };
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1279
1341
|
// ─────────────────────────────────────────────────────────────────────────
|
|
1280
1342
|
// Resolution: given the scan + import map, compute the REAL runtime value
|
|
1281
1343
|
// (Declarable | CompositeInstance) each foldable export would have had if
|
|
@@ -1394,6 +1456,14 @@ interface ResolveCtx {
|
|
|
1394
1456
|
* innermost factory rather than the enclosing one.
|
|
1395
1457
|
*/
|
|
1396
1458
|
compositeParams?: CompositeParamRecorder;
|
|
1459
|
+
/**
|
|
1460
|
+
* chant#3247 — set only inside the body of a composite an active lexicon
|
|
1461
|
+
* PACKAGE publishes: that package's directory (see
|
|
1462
|
+
* {@link FactoryModuleScope.packageRoot}). It is also in this context's
|
|
1463
|
+
* {@link lexiconRoots}, which is what trusts the package's relative imports;
|
|
1464
|
+
* this field is what confines a nested factory lookup to the package.
|
|
1465
|
+
*/
|
|
1466
|
+
packageRoot?: string;
|
|
1397
1467
|
}
|
|
1398
1468
|
|
|
1399
1469
|
/** chant #2161 — what {@link stampCompositeOrigins} needs at one `new Type(...)` inside a factory body. */
|
|
@@ -1677,7 +1747,10 @@ async function invokeResolvedCallee(
|
|
|
1677
1747
|
// A package factory still runs here, which is what `F-Host-Admission`
|
|
1678
1748
|
// governs. A project-file callee that a `Composite` registered is
|
|
1679
1749
|
// INTERPRETED at step 4 and never reaches this line; only one that
|
|
1680
|
-
// interpretation declined does, and for that the verdict is `run`.
|
|
1750
|
+
// interpretation declined does, and for that the verdict is `run`. Since
|
|
1751
|
+
// chant#3247 a package's registered composite is interpreted at step 4 too,
|
|
1752
|
+
// and reaches this line only when interpretation declined; it is a
|
|
1753
|
+
// package's code, so it is still invoked.
|
|
1681
1754
|
// "Project file" is the specifier's ORIGIN, not its syntax.
|
|
1682
1755
|
// {@link isProjectFileSpecifier} is true of any absolute path, and chant's
|
|
1683
1756
|
// own modules are imported by absolute path throughout the tests, so the
|
|
@@ -1686,7 +1759,7 @@ async function invokeResolvedCallee(
|
|
|
1686
1759
|
// means: an active lexicon package or chant-core's own tree is a package's
|
|
1687
1760
|
// code, and everything else reached by a relative or absolute specifier is
|
|
1688
1761
|
// the project's.
|
|
1689
|
-
if (!ctx.executing &&
|
|
1762
|
+
if (!ctx.executing && isProjectImport(binding.specifier, ctx.file) && !isTrustedExecutableBinding(binding, ctx)) {
|
|
1690
1763
|
throw cheapError(
|
|
1691
1764
|
`"${calleeName}" is imported from "${binding.specifier}", which is a project file; ` +
|
|
1692
1765
|
"the default mode does not invoke project-owned code (`executing` would run it and fold what it returns)",
|
|
@@ -1730,7 +1803,10 @@ async function invokeResolvedCallee(
|
|
|
1730
1803
|
}
|
|
1731
1804
|
|
|
1732
1805
|
executionCounts.factoryInvocations += 1;
|
|
1733
|
-
|
|
1806
|
+
// chant#3247 — a relative import inside a lexicon package's own composite is
|
|
1807
|
+
// the package's code, not the project's.
|
|
1808
|
+
const packageInternal = ctx.packageRoot !== undefined && isUnderDirectory(modulePath, ctx.packageRoot);
|
|
1809
|
+
if (isProjectImport(binding.specifier, ctx.file) && !packageInternal) executionCounts.projectFactoryInvocations += 1;
|
|
1734
1810
|
|
|
1735
1811
|
return (Fn as (...fnArgs: unknown[]) => unknown)(...args);
|
|
1736
1812
|
}
|
|
@@ -1767,11 +1843,12 @@ async function invokeImportedCallee(
|
|
|
1767
1843
|
* the resolved argument values — which `fold()`'s envelope already folded
|
|
1768
1844
|
* and {@link reviveFoldedValue} has already revived by the time this runs.
|
|
1769
1845
|
*
|
|
1770
|
-
* Same two arms, same order, as the top-level path: interpret a
|
|
1771
|
-
*
|
|
1772
|
-
* import and invoke for real —
|
|
1773
|
-
* composite
|
|
1774
|
-
*
|
|
1846
|
+
* Same two arms, same order, as the top-level path: interpret a registered
|
|
1847
|
+
* `Composite` (chant #1023; a lexicon package's own since chant#3247) when its
|
|
1848
|
+
* body qualifies, otherwise import and invoke for real — the arm a
|
|
1849
|
+
* lexicon-package composite whose body leaves the subset (`Checkout`,
|
|
1850
|
+
* `SetupNode`, …) takes, exactly as it does for a top-level
|
|
1851
|
+
* `export const x = Checkout({...})`. An unbound
|
|
1775
1852
|
* callee name throws the same "function call as a value" message `fold()`
|
|
1776
1853
|
* itself would have, for a caller with no `FoldedCompositeStepCall` special
|
|
1777
1854
|
* case to fall into.
|
|
@@ -1825,6 +1902,7 @@ async function resolveCallArguments(
|
|
|
1825
1902
|
const args: unknown[] = [];
|
|
1826
1903
|
for (const argNode of node.arguments) {
|
|
1827
1904
|
const live = await resolveLiveValue(argNode, ctx);
|
|
1905
|
+
if (live === undefined) await resolveSameFileCallReferences(argNode, ctx);
|
|
1828
1906
|
// chant #1039 — a folded (non-live) argument may itself contain a
|
|
1829
1907
|
// registered intrinsic tagged template; revive it into the real value
|
|
1830
1908
|
// before the composite factory actually runs on it (see the "Intrinsic
|
|
@@ -1838,6 +1916,69 @@ async function resolveCallArguments(
|
|
|
1838
1916
|
return args;
|
|
1839
1917
|
}
|
|
1840
1918
|
|
|
1919
|
+
/**
|
|
1920
|
+
* chant#3325 — a composite call's props can name a same-file `const` bound to
|
|
1921
|
+
* another call (`left: notes.table`, with `export const notes =
|
|
1922
|
+
* TenantTable({...})` earlier in the file). `fold()` cannot answer that name:
|
|
1923
|
+
* it is synchronous, and the value is a composite instance only the live spine
|
|
1924
|
+
* builds. So before the argument folds, each such name it mentions is resolved
|
|
1925
|
+
* through {@link resolveMemoized}, the same promise the declarator and every
|
|
1926
|
+
* other reference to it share, and the instance goes into `externals`, where
|
|
1927
|
+
* `fold()`'s identifier branch reads it as it reads a pre-built `new` or tag
|
|
1928
|
+
* const (#1169, #3196).
|
|
1929
|
+
*
|
|
1930
|
+
* Only a call `fold()` has no case for qualifies: an imported callee that is
|
|
1931
|
+
* neither a registered authoring helper nor a registered intrinsic. For
|
|
1932
|
+
* anything else the name is left alone and folds exactly as before. A
|
|
1933
|
+
* resolution that fails, or gives anything but a Declarable or a composite
|
|
1934
|
+
* instance, is left alone too, so the reference rejects with the message it
|
|
1935
|
+
* always had. Inside a factory body `locals` is empty, so nothing here applies.
|
|
1936
|
+
*/
|
|
1937
|
+
async function resolveSameFileCallReferences(node: ts.Node, ctx: ResolveCtx): Promise<void> {
|
|
1938
|
+
const names = new Set<string>();
|
|
1939
|
+
const visit = (n: ts.Node): void => {
|
|
1940
|
+
if (ts.isIdentifier(n)) {
|
|
1941
|
+
if (isUnfoldableSameFileCall(n.text, ctx)) names.add(n.text);
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
if (ts.isFunctionLike(n) || ts.isTypeNode(n)) return;
|
|
1945
|
+
if (ts.isPropertyAccessExpression(n)) return visit(n.expression);
|
|
1946
|
+
if (ts.isPropertyAssignment(n)) {
|
|
1947
|
+
if (ts.isComputedPropertyName(n.name)) visit(n.name);
|
|
1948
|
+
return visit(n.initializer);
|
|
1949
|
+
}
|
|
1950
|
+
ts.forEachChild(n, visit);
|
|
1951
|
+
};
|
|
1952
|
+
visit(node);
|
|
1953
|
+
for (const name of names) {
|
|
1954
|
+
let resolved: LiveResolution;
|
|
1955
|
+
try {
|
|
1956
|
+
resolved = await resolveMemoized(ctx.locals.get(name)!.source, ctx);
|
|
1957
|
+
} catch {
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
if (resolved && (isDeclarable(resolved.value) || isCompositeInstance(resolved.value))) {
|
|
1961
|
+
ctx.externals.set(name, resolved.value);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
/** A same-file `const name = Callee(...)` that `fold()` would refuse as a function call used as a value. */
|
|
1967
|
+
function isUnfoldableSameFileCall(name: string, ctx: ResolveCtx): boolean {
|
|
1968
|
+
if (ctx.externals.has(name)) return false;
|
|
1969
|
+
const binding = ctx.locals.get(name);
|
|
1970
|
+
if (!binding || binding.propKey !== undefined || ctx.consts.get(name) !== binding.source) return false;
|
|
1971
|
+
const source = binding.source;
|
|
1972
|
+
if (!ts.isCallExpression(source) || !ts.isIdentifier(source.expression)) return false;
|
|
1973
|
+
const callee = source.expression.text;
|
|
1974
|
+
return (
|
|
1975
|
+
ctx.imports.has(callee) &&
|
|
1976
|
+
!isFoldableHelperName(callee) &&
|
|
1977
|
+
!ctx.intrinsics.some((i) => i.name === callee) &&
|
|
1978
|
+
!isFoldableFunction(ctx.externals.get(callee))
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1841
1982
|
// ─────────────────────────────────────────────────────────────────────────
|
|
1842
1983
|
// Composite factory interpretation (chant #1023, epic #1019 Phase 5).
|
|
1843
1984
|
//
|
|
@@ -1855,15 +1996,30 @@ async function resolveCallArguments(
|
|
|
1855
1996
|
// factory outside the subset costs a parse and nothing else:
|
|
1856
1997
|
//
|
|
1857
1998
|
// 1. **The callee is bound by an `import` from a PROJECT FILE** (a
|
|
1858
|
-
// relative/absolute specifier
|
|
1859
|
-
//
|
|
1860
|
-
//
|
|
1861
|
-
//
|
|
1862
|
-
//
|
|
1863
|
-
//
|
|
1864
|
-
//
|
|
1865
|
-
//
|
|
1866
|
-
//
|
|
1999
|
+
// relative/absolute specifier, or one a tsconfig `paths` entry maps), **or
|
|
2000
|
+
// from an ACTIVE LEXICON PACKAGE of this build whose module is TypeScript
|
|
2001
|
+
// source** (chant#3247). The package case is matched by text first — the
|
|
2002
|
+
// specifier, or its package root for a subpath, against
|
|
2003
|
+
// {@link FoldSession.lexiconPackages}, exactly as
|
|
2004
|
+
// {@link isTrustedExecutableBinding}'s arm 1 matches it — and only then
|
|
2005
|
+
// resolved; a module that resolves to compiled JavaScript or a declaration
|
|
2006
|
+
// file is not interpreted, so a package that ships only `dist` keeps
|
|
2007
|
+
// invoking. Every chant lexicon routes its exports at `./src/*.ts`
|
|
2008
|
+
// (check-lexicon tier 1), so in practice the source is always there.
|
|
2009
|
+
// Interpreting buys provenance: the run path cannot say which parameter
|
|
2010
|
+
// fed a field, and interpretation can (#2161, #3246).
|
|
2011
|
+
//
|
|
2012
|
+
// The trust boundary does not move. A package's composite is interpreted
|
|
2013
|
+
// with that PACKAGE's directory ({@link FactoryModuleScope.packageRoot})
|
|
2014
|
+
// as a trusted lexicon root for the length of its body and nothing else:
|
|
2015
|
+
// the package's own relative imports are trusted under `--sandbox` the way
|
|
2016
|
+
// a subpath of the same package already is, and they are never folded as
|
|
2017
|
+
// project files. The package was imported and run by `loadPlugins` before
|
|
2018
|
+
// discovery began, and every module of it is reachable as a subpath, so
|
|
2019
|
+
// reading its source adds no trust. What it may NOT do is reach outside
|
|
2020
|
+
// that directory: a relative import that leaves the package root is not
|
|
2021
|
+
// followed. A lexicon this build did not load stays as out of scope as
|
|
2022
|
+
// any other bare specifier.
|
|
1867
2023
|
// 2. **The defining module declares that export as `Composite(<fn>, "<name>")`**
|
|
1868
2024
|
// — a top-level `export const <name> = Composite(...)`, where `Composite`
|
|
1869
2025
|
// is bound in THAT module to an import of chant's own
|
|
@@ -1871,8 +2027,12 @@ async function resolveCallArguments(
|
|
|
1871
2027
|
// asks of an authoring helper, and the same answer). This is #1023's
|
|
1872
2028
|
// "recognize the callee is a registered Composite": a bare call to
|
|
1873
2029
|
// anything else — a project helper function, a `withDefaults(...)`
|
|
1874
|
-
// wrapper,
|
|
1875
|
-
// interpreted.
|
|
2030
|
+
// wrapper, an `import`-then-`export` alias — is not recognized and is not
|
|
2031
|
+
// interpreted. The defining module may sit behind BARREL re-exports
|
|
2032
|
+
// (chant#3247): `export { X } from "./x"`, `export { Y as X } from "./x"`
|
|
2033
|
+
// and `export * from "./x"` are followed, relative specifiers only, at most
|
|
2034
|
+
// {@link MAX_REEXPORT_HOPS} deep, and never out of a package's root. A
|
|
2035
|
+
// module that declares the name itself is answered by that declaration.
|
|
1876
2036
|
// 3. **`<fn>` is an arrow/function expression taking at most one parameter**,
|
|
1877
2037
|
// bound as a plain identifier or a simple object binding pattern (no
|
|
1878
2038
|
// default, rest, or nested pattern).
|
|
@@ -1986,6 +2146,15 @@ class InterpretationDepthError extends FoldError {}
|
|
|
1986
2146
|
interface FactoryModuleScope {
|
|
1987
2147
|
file: string;
|
|
1988
2148
|
sourceFile: ts.SourceFile;
|
|
2149
|
+
/**
|
|
2150
|
+
* chant#3247 — the directory of the active lexicon PACKAGE this module was
|
|
2151
|
+
* reached in, when it was reached through one (see rule 1 of the contract).
|
|
2152
|
+
* Interpreting a factory from it trusts that directory as a lexicon root —
|
|
2153
|
+
* its files are not folded as project files and are on the `--sandbox`
|
|
2154
|
+
* allowlist — and confines every further hop to it. `undefined` for a
|
|
2155
|
+
* project file, whose imports are decided exactly as before.
|
|
2156
|
+
*/
|
|
2157
|
+
packageRoot?: string;
|
|
1989
2158
|
/**
|
|
1990
2159
|
* The module's top-level `const`s, MINUS every one that resolves to a
|
|
1991
2160
|
* `new Type(...)` resource.
|
|
@@ -2046,15 +2215,25 @@ interface InterpretableFactory {
|
|
|
2046
2215
|
* module's own imports are not resolved yet — see
|
|
2047
2216
|
* {@link FactoryModuleScope.resolved}.
|
|
2048
2217
|
*/
|
|
2049
|
-
function readFactoryModule(
|
|
2050
|
-
|
|
2218
|
+
function readFactoryModule(
|
|
2219
|
+
modulePath: string,
|
|
2220
|
+
packageRoot: string | undefined,
|
|
2221
|
+
session: FoldSession,
|
|
2222
|
+
): Promise<FactoryModuleScope | undefined> {
|
|
2223
|
+
// chant#3247 — keyed by the package root as well: the same file read as a
|
|
2224
|
+
// project file and as package code resolves its imports differently.
|
|
2225
|
+
const key = packageRoot === undefined ? modulePath : `${packageRoot}\0${modulePath}`;
|
|
2226
|
+
const cached = session.factoryModules.get(key);
|
|
2051
2227
|
if (cached) return cached;
|
|
2052
|
-
const promise = readFactoryModuleCore(modulePath);
|
|
2053
|
-
session.factoryModules.set(
|
|
2228
|
+
const promise = readFactoryModuleCore(modulePath, packageRoot);
|
|
2229
|
+
session.factoryModules.set(key, promise);
|
|
2054
2230
|
return promise;
|
|
2055
2231
|
}
|
|
2056
2232
|
|
|
2057
|
-
async function readFactoryModuleCore(
|
|
2233
|
+
async function readFactoryModuleCore(
|
|
2234
|
+
modulePath: string,
|
|
2235
|
+
packageRoot: string | undefined,
|
|
2236
|
+
): Promise<FactoryModuleScope | undefined> {
|
|
2058
2237
|
let sourceFile: ts.SourceFile;
|
|
2059
2238
|
try {
|
|
2060
2239
|
const source = await readFile(modulePath, "utf-8");
|
|
@@ -2074,6 +2253,7 @@ async function readFactoryModuleCore(modulePath: string): Promise<FactoryModuleS
|
|
|
2074
2253
|
return {
|
|
2075
2254
|
file: modulePath,
|
|
2076
2255
|
sourceFile,
|
|
2256
|
+
packageRoot,
|
|
2077
2257
|
consts,
|
|
2078
2258
|
imports: collected.named,
|
|
2079
2259
|
namespaceImports: collected.namespaces,
|
|
@@ -2097,7 +2277,8 @@ function factoryModuleScopeResolved(
|
|
|
2097
2277
|
return Promise.resolve({ externals: new Map(), failures: new Map() });
|
|
2098
2278
|
}
|
|
2099
2279
|
session.stack.push(scope.file);
|
|
2100
|
-
|
|
2280
|
+
const roots = scope.packageRoot === undefined ? [] : [scope.packageRoot];
|
|
2281
|
+
scope.resolved = buildExternals(scope.file, scope.imports, scope.namespaceImports, session, roots).finally(() => {
|
|
2101
2282
|
const idx = session.stack.lastIndexOf(scope.file);
|
|
2102
2283
|
if (idx !== -1) session.stack.splice(idx, 1);
|
|
2103
2284
|
});
|
|
@@ -2141,7 +2322,7 @@ function findCompositeDefinition(
|
|
|
2141
2322
|
// lexicon exports a `Composite`, so nothing about a chant project moves.
|
|
2142
2323
|
const chantOwned = isChantOwnedHelperBinding(compositeBinding, { ...ctx, file: scope.file });
|
|
2143
2324
|
const hostOwned =
|
|
2144
|
-
|
|
2325
|
+
activeLexiconPackageImport(compositeBinding.specifier, scope.file, ctx.lexiconPackages) !== undefined ||
|
|
2145
2326
|
activeLexiconModule(compositeBinding.specifier, scope.file, ctx.lexiconModules, ctx.resolvePathCache) !==
|
|
2146
2327
|
undefined;
|
|
2147
2328
|
if (!chantOwned && !hostOwned) return undefined;
|
|
@@ -2158,6 +2339,218 @@ function findCompositeDefinition(
|
|
|
2158
2339
|
return undefined;
|
|
2159
2340
|
}
|
|
2160
2341
|
|
|
2342
|
+
/**
|
|
2343
|
+
* chant#3247 — how many barrel re-exports {@link findFactoryDefinition}
|
|
2344
|
+
* follows from the imported module to the one that defines the composite. A
|
|
2345
|
+
* lexicon's barrel reaches its composites in two (`src/index.ts` ->
|
|
2346
|
+
* `src/composites/index.ts` -> the file), a dialect subpath in three; past
|
|
2347
|
+
* this the lookup declines, and the composite is invoked as before.
|
|
2348
|
+
*/
|
|
2349
|
+
const MAX_REEXPORT_HOPS = 8;
|
|
2350
|
+
|
|
2351
|
+
/** Where an import of a composite factory leads: the module, and the lexicon package it lies in, if any. */
|
|
2352
|
+
interface FactoryOrigin {
|
|
2353
|
+
modulePath: string;
|
|
2354
|
+
/** See {@link FactoryModuleScope.packageRoot}. */
|
|
2355
|
+
packageRoot?: string;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
/** A TypeScript source file, not compiled JavaScript and not a declaration file. */
|
|
2359
|
+
function isTypeScriptSource(path: string): boolean {
|
|
2360
|
+
return /\.[cm]?tsx?$/.test(path) && !/\.d\.[cm]?ts$/.test(path);
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
/** Process-wide memo for {@link lexiconPackageDirectory}, keyed by package name and directory. */
|
|
2364
|
+
const lexiconPackageDirectoryCache = new Map<string, string | undefined>();
|
|
2365
|
+
|
|
2366
|
+
/**
|
|
2367
|
+
* chant#3247 — the directory of package `packageName` that holds the resolved
|
|
2368
|
+
* `modulePath`: the nearest ancestor whose `package.json` names that package.
|
|
2369
|
+
* `undefined` when none does before a `node_modules` directory or the
|
|
2370
|
+
* filesystem root, which declines interpretation. Reads `package.json` files
|
|
2371
|
+
* only; nothing is resolved or imported.
|
|
2372
|
+
*/
|
|
2373
|
+
function lexiconPackageDirectory(modulePath: string, packageName: string): string | undefined {
|
|
2374
|
+
const start = dirname(modulePath);
|
|
2375
|
+
const key = `${packageName}\0${start}`;
|
|
2376
|
+
if (lexiconPackageDirectoryCache.has(key)) return lexiconPackageDirectoryCache.get(key);
|
|
2377
|
+
let found: string | undefined;
|
|
2378
|
+
for (let dir = start; basename(dir) !== "node_modules"; ) {
|
|
2379
|
+
const pkgJson = join(dir, "package.json");
|
|
2380
|
+
if (existsSync(pkgJson)) {
|
|
2381
|
+
try {
|
|
2382
|
+
if ((JSON.parse(readFileSync(pkgJson, "utf-8")) as { name?: unknown }).name === packageName) {
|
|
2383
|
+
found = dir;
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
} catch {
|
|
2387
|
+
// An unreadable package.json is not the package's; keep walking.
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
const parent = dirname(dir);
|
|
2391
|
+
if (parent === dir) break;
|
|
2392
|
+
dir = parent;
|
|
2393
|
+
}
|
|
2394
|
+
lexiconPackageDirectoryCache.set(key, found);
|
|
2395
|
+
return found;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
/**
|
|
2399
|
+
* Rule 1 of the contract: where an import of a composite leads, or `undefined`
|
|
2400
|
+
* when the import is not one interpretation follows. No module is imported.
|
|
2401
|
+
*
|
|
2402
|
+
* - A project import (relative, absolute, or `paths`-mapped) resolves to its
|
|
2403
|
+
* file. Inside a package composite's body ({@link ResolveCtx.packageRoot})
|
|
2404
|
+
* it must stay inside that package.
|
|
2405
|
+
* - A bare specifier naming an active lexicon package, root or subpath, by
|
|
2406
|
+
* text, resolves through `node_modules` to a TypeScript source file inside
|
|
2407
|
+
* that package.
|
|
2408
|
+
* - Anything else is not followed.
|
|
2409
|
+
*/
|
|
2410
|
+
function factoryOrigin(specifier: string, fromFile: string, ctx: ResolveCtx): FactoryOrigin | undefined {
|
|
2411
|
+
if (isProjectImport(specifier, fromFile)) {
|
|
2412
|
+
let modulePath: string;
|
|
2413
|
+
try {
|
|
2414
|
+
modulePath = resolveModulePathMemoized(specifier, fromFile, ctx.resolvePathCache);
|
|
2415
|
+
} catch {
|
|
2416
|
+
return undefined;
|
|
2417
|
+
}
|
|
2418
|
+
if (ctx.packageRoot === undefined) return { modulePath };
|
|
2419
|
+
return isUnderDirectory(modulePath, ctx.packageRoot) ? { modulePath, packageRoot: ctx.packageRoot } : undefined;
|
|
2420
|
+
}
|
|
2421
|
+
// Text first, as {@link isTrustedExecutableBinding}'s arm 1 does: no bare
|
|
2422
|
+
// specifier outside the build's own lexicons is ever resolved here.
|
|
2423
|
+
const packageName =
|
|
2424
|
+
activeLexiconPackage(specifier, ctx.lexiconPackages) ??
|
|
2425
|
+
activeLexiconPackage(bareSpecifierPackageRoot(specifier) ?? "", ctx.lexiconPackages);
|
|
2426
|
+
if (packageName === undefined) return undefined;
|
|
2427
|
+
let modulePath: string;
|
|
2428
|
+
try {
|
|
2429
|
+
modulePath = resolveModulePathMemoized(specifier, fromFile, ctx.resolvePathCache);
|
|
2430
|
+
} catch {
|
|
2431
|
+
return undefined;
|
|
2432
|
+
}
|
|
2433
|
+
if (!isTypeScriptSource(modulePath)) return undefined;
|
|
2434
|
+
const packageRoot = lexiconPackageDirectory(modulePath, packageName);
|
|
2435
|
+
return packageRoot === undefined ? undefined : { modulePath, packageRoot };
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
/** One re-export {@link reExportsOf} found: where it leads, under which name, and whether it is an `export *`. */
|
|
2439
|
+
interface ReExport {
|
|
2440
|
+
specifier: string;
|
|
2441
|
+
imported: string;
|
|
2442
|
+
star: boolean;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
/**
|
|
2446
|
+
* chant#3247 — the re-exports of `exportName` that `sourceFile` forwards, in the order they are worth
|
|
2447
|
+
* trying. Empty when the module declares the name itself (its own
|
|
2448
|
+
* declaration is the answer, interpretable or not) or forwards it from nowhere
|
|
2449
|
+
* this follows. An explicit `export { ... } from` wins over `export *`, as it
|
|
2450
|
+
* does in the language; a type-only re-export, an `export * as ns`, and a bare
|
|
2451
|
+
* `export { X }` of an imported binding are not followed.
|
|
2452
|
+
*/
|
|
2453
|
+
function reExportsOf(sourceFile: ts.SourceFile, exportName: string): ReExport[] {
|
|
2454
|
+
const stars: ReExport[] = [];
|
|
2455
|
+
for (const statement of sourceFile.statements) {
|
|
2456
|
+
if (ts.isExportDeclaration(statement)) {
|
|
2457
|
+
if (statement.isTypeOnly) continue;
|
|
2458
|
+
const spec =
|
|
2459
|
+
statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)
|
|
2460
|
+
? statement.moduleSpecifier.text
|
|
2461
|
+
: undefined;
|
|
2462
|
+
const clause = statement.exportClause;
|
|
2463
|
+
if (clause === undefined) {
|
|
2464
|
+
if (spec !== undefined) stars.push({ specifier: spec, imported: exportName, star: true });
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
if (!ts.isNamedExports(clause)) {
|
|
2468
|
+
if (clause.name.text === exportName) return [];
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
for (const el of clause.elements) {
|
|
2472
|
+
if (el.name.text !== exportName) continue;
|
|
2473
|
+
if (spec === undefined || el.isTypeOnly) return [];
|
|
2474
|
+
return [{ specifier: spec, imported: (el.propertyName ?? el.name).text, star: false }];
|
|
2475
|
+
}
|
|
2476
|
+
continue;
|
|
2477
|
+
}
|
|
2478
|
+
if (!hasExportModifier(statement as { modifiers?: ts.NodeArray<ts.ModifierLike> })) continue;
|
|
2479
|
+
if (ts.isVariableStatement(statement)) {
|
|
2480
|
+
for (const decl of statement.declarationList.declarations) {
|
|
2481
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === exportName) return [];
|
|
2482
|
+
}
|
|
2483
|
+
} else if (
|
|
2484
|
+
(ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) || ts.isEnumDeclaration(statement)) &&
|
|
2485
|
+
statement.name?.text === exportName
|
|
2486
|
+
) {
|
|
2487
|
+
return [];
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
return stars;
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
/** A text pre-check for an `export *` target: false only when the file cannot export `name`. */
|
|
2494
|
+
async function mayProvideExport(file: string, name: string): Promise<boolean> {
|
|
2495
|
+
try {
|
|
2496
|
+
const text = await readFile(file, "utf-8");
|
|
2497
|
+
return text.includes(name) || /export\s*\*\s*from/.test(text);
|
|
2498
|
+
} catch {
|
|
2499
|
+
return false;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
/**
|
|
2504
|
+
* Rule 2, through barrels: find `exportName`'s `Composite(...)` declaration
|
|
2505
|
+
* starting at `origin` and following {@link reExportsOf} — relative
|
|
2506
|
+
* specifiers only, confined to `origin.packageRoot` when there is one, cycle
|
|
2507
|
+
* safe, and at most {@link MAX_REEXPORT_HOPS} deep. Reads and parses; imports
|
|
2508
|
+
* nothing.
|
|
2509
|
+
*/
|
|
2510
|
+
async function findFactoryDefinition(
|
|
2511
|
+
origin: FactoryOrigin,
|
|
2512
|
+
exportName: string,
|
|
2513
|
+
ctx: ResolveCtx,
|
|
2514
|
+
seen: Set<string> = new Set(),
|
|
2515
|
+
hops = 0,
|
|
2516
|
+
): Promise<
|
|
2517
|
+
| { scope: FactoryModuleScope; definition: NonNullable<ReturnType<typeof findCompositeDefinition>> }
|
|
2518
|
+
| undefined
|
|
2519
|
+
> {
|
|
2520
|
+
seen.add(origin.modulePath);
|
|
2521
|
+
const scope = await readFactoryModule(origin.modulePath, origin.packageRoot, ctx.session);
|
|
2522
|
+
if (!scope) return undefined;
|
|
2523
|
+
const definition = findCompositeDefinition(scope, exportName, ctx);
|
|
2524
|
+
if (definition) return { scope, definition };
|
|
2525
|
+
if (hops >= MAX_REEXPORT_HOPS) return undefined;
|
|
2526
|
+
|
|
2527
|
+
for (const { specifier, imported, star } of reExportsOf(scope.sourceFile, exportName)) {
|
|
2528
|
+
if (!isProjectFileSpecifier(specifier)) continue;
|
|
2529
|
+
let target: string;
|
|
2530
|
+
try {
|
|
2531
|
+
target = resolveModulePathMemoized(specifier, scope.file, ctx.resolvePathCache);
|
|
2532
|
+
} catch {
|
|
2533
|
+
continue;
|
|
2534
|
+
}
|
|
2535
|
+
if (seen.has(target) || !isTypeScriptSource(target)) continue;
|
|
2536
|
+
if (origin.packageRoot !== undefined && !isUnderDirectory(target, origin.packageRoot)) continue;
|
|
2537
|
+
// An `export *` target that neither names the export nor forwards a star
|
|
2538
|
+
// of its own cannot provide it, so it is not parsed. That keeps a
|
|
2539
|
+
// lexicon's generated barrel (megabytes, behind `export *` in most
|
|
2540
|
+
// lexicons) from being parsed for every call through the package root.
|
|
2541
|
+
if (star && !(await mayProvideExport(target, imported))) continue;
|
|
2542
|
+
const found = await findFactoryDefinition(
|
|
2543
|
+
{ modulePath: target, packageRoot: origin.packageRoot },
|
|
2544
|
+
imported,
|
|
2545
|
+
ctx,
|
|
2546
|
+
seen,
|
|
2547
|
+
hops + 1,
|
|
2548
|
+
);
|
|
2549
|
+
if (found) return found;
|
|
2550
|
+
}
|
|
2551
|
+
return undefined;
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2161
2554
|
/**
|
|
2162
2555
|
* Decide whether `binding`'s callee is an interpretable composite — rules 1-5
|
|
2163
2556
|
* of the contract above, in that order, with no evaluation and no import.
|
|
@@ -2167,9 +2560,10 @@ async function resolveInterpretableFactory(
|
|
|
2167
2560
|
binding: ImportBinding,
|
|
2168
2561
|
ctx: ResolveCtx,
|
|
2169
2562
|
): Promise<InterpretableFactory | undefined> {
|
|
2170
|
-
// Rule 1 — project
|
|
2171
|
-
// bare specifier
|
|
2172
|
-
|
|
2563
|
+
// Rule 1 — a project file, or an active lexicon package's TypeScript source.
|
|
2564
|
+
// A bare specifier outside the build's lexicons is refused on its text.
|
|
2565
|
+
const origin = factoryOrigin(binding.specifier, ctx.file, ctx);
|
|
2566
|
+
if (!origin) return undefined;
|
|
2173
2567
|
// Not `undefined`: that answer means "not interpretable" and would invoke
|
|
2174
2568
|
// the factory while still reporting the file folded. Giving up is a
|
|
2175
2569
|
// different fact and falls the file back to run, naming the bound (#2370).
|
|
@@ -2183,19 +2577,10 @@ async function resolveInterpretableFactory(
|
|
|
2183
2577
|
);
|
|
2184
2578
|
}
|
|
2185
2579
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
}
|
|
2190
|
-
return undefined;
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
const scope = await readFactoryModule(modulePath, ctx.session);
|
|
2194
|
-
if (!scope) return undefined;
|
|
2195
|
-
|
|
2196
|
-
// Rule 2.
|
|
2197
|
-
const definition = findCompositeDefinition(scope, binding.imported, ctx);
|
|
2198
|
-
if (!definition) return undefined;
|
|
2580
|
+
// Rule 2, following barrel re-exports to the defining module.
|
|
2581
|
+
const found = await findFactoryDefinition(origin, binding.imported, ctx);
|
|
2582
|
+
if (!found) return undefined;
|
|
2583
|
+
const { scope, definition } = found;
|
|
2199
2584
|
|
|
2200
2585
|
// Rules 3-5.
|
|
2201
2586
|
if (findFactorySubsetViolation(definition.fn) !== undefined) return undefined;
|
|
@@ -2471,6 +2856,27 @@ function stampCompositeOrigins(entity: unknown, node: ts.NewExpression, ctx: Res
|
|
|
2471
2856
|
for (const [path, origin] of Object.entries(origins)) setPathProvenance(entity, path, origin);
|
|
2472
2857
|
}
|
|
2473
2858
|
|
|
2859
|
+
/**
|
|
2860
|
+
* chant #3212 — the same record for an entity a registered tag built inside an
|
|
2861
|
+
* interpreted factory body (`` table`CREATE TABLE ${props.name} ...` ``).
|
|
2862
|
+
*
|
|
2863
|
+
* A tag's props come from parsing its text, so no object literal says which
|
|
2864
|
+
* field each `${...}` landed in. The tag says so itself through
|
|
2865
|
+
* `setInterpolationFields`; a tag that does not leaves the entity's fields
|
|
2866
|
+
* `unknown`, as before.
|
|
2867
|
+
*/
|
|
2868
|
+
function stampTagOrigins(entity: unknown, node: ts.TaggedTemplateExpression, ctx: ResolveCtx): void {
|
|
2869
|
+
const recorder = ctx.compositeParams;
|
|
2870
|
+
if (!recorder || !isDeclarable(entity) || !isResourceDeclarable(entity)) return;
|
|
2871
|
+
const fields = getInterpolationFields(entity);
|
|
2872
|
+
if (!fields) return;
|
|
2873
|
+
const interpolations = ts.isTemplateExpression(node.template)
|
|
2874
|
+
? node.template.templateSpans.map((span) => span.expression)
|
|
2875
|
+
: [];
|
|
2876
|
+
const origins = collectTagOrigins(interpolations, fields, entity.props, recorder.consts, recorder.scope, recorder.composite);
|
|
2877
|
+
for (const [path, origin] of Object.entries(origins)) setPathProvenance(entity, path, origin);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2474
2880
|
/**
|
|
2475
2881
|
* Interpret an admissible factory's body against `args`, and assemble the
|
|
2476
2882
|
* result through chant's own {@link Composite}.
|
|
@@ -2516,7 +2922,10 @@ async function interpretCompositeFactory(
|
|
|
2516
2922
|
resolvePathCache: ctx.resolvePathCache,
|
|
2517
2923
|
lexiconPackages: ctx.lexiconPackages,
|
|
2518
2924
|
lexiconModules: ctx.lexiconModules,
|
|
2519
|
-
|
|
2925
|
+
// chant#3247 — a package's composite trusts its own package directory for
|
|
2926
|
+
// the length of its body, and nothing outside it.
|
|
2927
|
+
lexiconRoots: scope.packageRoot === undefined ? ctx.lexiconRoots : [...ctx.lexiconRoots, scope.packageRoot],
|
|
2928
|
+
packageRoot: scope.packageRoot,
|
|
2520
2929
|
sandbox: ctx.sandbox,
|
|
2521
2930
|
executing: ctx.executing,
|
|
2522
2931
|
session: ctx.session,
|
|
@@ -2635,6 +3044,12 @@ async function interpretExpression(node: ts.Expression, ctx: ResolveCtx): Promis
|
|
|
2635
3044
|
|
|
2636
3045
|
if (ts.isNewExpression(node)) return interpretNewExpression(node, ctx);
|
|
2637
3046
|
|
|
3047
|
+
if (ts.isTaggedTemplateExpression(node)) {
|
|
3048
|
+
const value = (await resolveDeclaratorValue(node, ctx)).value;
|
|
3049
|
+
stampTagOrigins(value, node, ctx);
|
|
3050
|
+
return value;
|
|
3051
|
+
}
|
|
3052
|
+
|
|
2638
3053
|
if (ts.isObjectLiteralExpression(node)) {
|
|
2639
3054
|
const obj: Record<string, unknown> = {};
|
|
2640
3055
|
for (const prop of node.properties) {
|
|
@@ -2862,6 +3277,16 @@ async function resolveSymbolicValue(text: string, ctx: ResolveCtx): Promise<unkn
|
|
|
2862
3277
|
return value;
|
|
2863
3278
|
}
|
|
2864
3279
|
|
|
3280
|
+
/**
|
|
3281
|
+
* True when {@link reviveFoldedValue} hands `value` back unchanged: a
|
|
3282
|
+
* primitive, or one of the live object kinds its passthrough keeps (an
|
|
3283
|
+
* `AttrRef`, a Declarable, a composite instance, an `Intrinsic`).
|
|
3284
|
+
*/
|
|
3285
|
+
function revivesAsItself(value: unknown): boolean {
|
|
3286
|
+
if (value === null || typeof value !== "object") return true;
|
|
3287
|
+
return isAttrRefLike(value) || isDeclarable(value) || isCompositeInstance(value) || isIntrinsic(value);
|
|
3288
|
+
}
|
|
3289
|
+
|
|
2865
3290
|
/**
|
|
2866
3291
|
* Revive a folded value tree: replace any
|
|
2867
3292
|
* `{__intrinsic}`/`{__helper}`/`{__symbol}` envelope with the real value it
|
|
@@ -2904,9 +3329,7 @@ async function reviveFoldedValue(value: FoldedValue, ctx: ResolveCtx, requireLiv
|
|
|
2904
3329
|
// `Symbol.for("chant.intrinsic")` (../intrinsic.ts), a GLOBAL symbol, so it
|
|
2905
3330
|
// holds across separately-loaded copies of chant-core the way a bare
|
|
2906
3331
|
// `instanceof` would not.
|
|
2907
|
-
if (
|
|
2908
|
-
return value;
|
|
2909
|
-
}
|
|
3332
|
+
if (revivesAsItself(value)) return value;
|
|
2910
3333
|
|
|
2911
3334
|
if (Array.isArray(value)) {
|
|
2912
3335
|
const revived: unknown[] = [];
|
|
@@ -2939,7 +3362,14 @@ async function reviveFoldedValue(value: FoldedValue, ctx: ResolveCtx, requireLiv
|
|
|
2939
3362
|
return (Fn as (...fnArgs: unknown[]) => unknown)(...revived);
|
|
2940
3363
|
}
|
|
2941
3364
|
for (const v of intrinsic.values) revived.push(await reviveFoldedValue(v, ctx, true));
|
|
2942
|
-
|
|
3365
|
+
// chant #3196 — hand the tag what a real tagged call hands it: a frozen
|
|
3366
|
+
// strings array carrying a frozen, non-enumerable `raw`. A tag that reads
|
|
3367
|
+
// `strings.raw` (a SQL tag keeps backslashes as written) would otherwise
|
|
3368
|
+
// build different text folded than run.
|
|
3369
|
+
const strings = Object.freeze(
|
|
3370
|
+
Object.defineProperty([...intrinsic.strings], "raw", { value: Object.freeze([...intrinsic.raw]) }),
|
|
3371
|
+
);
|
|
3372
|
+
return (Fn as (...fnArgs: unknown[]) => unknown)(strings, ...revived);
|
|
2943
3373
|
}
|
|
2944
3374
|
|
|
2945
3375
|
if ("__helper" in value) {
|
|
@@ -3067,7 +3497,7 @@ async function reviveHelperCall(call: FoldedHelperCall, ctx: ResolveCtx): Promis
|
|
|
3067
3497
|
*/
|
|
3068
3498
|
function isChantOwnedHelperBinding(binding: ImportBinding, ctx: ResolveCtx): boolean {
|
|
3069
3499
|
if (isChantOwnedSpecifier(binding.specifier)) return true;
|
|
3070
|
-
if (!
|
|
3500
|
+
if (!isProjectImport(binding.specifier, ctx.file)) return false;
|
|
3071
3501
|
let targetPath: string;
|
|
3072
3502
|
try {
|
|
3073
3503
|
targetPath = resolveModulePathMemoized(binding.specifier, ctx.file, ctx.resolvePathCache);
|
|
@@ -3123,17 +3553,25 @@ function isChantOwnedHelperBinding(binding: ImportBinding, ctx: ResolveCtx): boo
|
|
|
3123
3553
|
* {@link FoldSession.lexiconPackages} takes for #1063.
|
|
3124
3554
|
*/
|
|
3125
3555
|
function isTrustedExecutableBinding(binding: ImportBinding, ctx: ResolveCtx): boolean {
|
|
3126
|
-
|
|
3556
|
+
// chant#3090 — a specifier a tsconfig `paths` entry maps names a project
|
|
3557
|
+
// file whatever its text says, so neither text arm applies to it: it is
|
|
3558
|
+
// resolved and decided on the path below, like a relative specifier.
|
|
3559
|
+
const pathsMapped = pathsMappedTarget(binding.specifier, ctx.file) !== undefined;
|
|
3560
|
+
if (!pathsMapped && activeLexiconPackage(binding.specifier, ctx.lexiconPackages) !== undefined) return true;
|
|
3127
3561
|
// chant #1995 — arm 1's subpath case: same allowlist, matched against the
|
|
3128
3562
|
// specifier's package ROOT (text only, see bareSpecifierPackageRoot) rather
|
|
3129
3563
|
// than the specifier itself, so `<activeLexiconPkg>/generated/index` is
|
|
3130
3564
|
// trusted exactly like the bare package import already is.
|
|
3131
3565
|
const subpathRoot = bareSpecifierPackageRoot(binding.specifier);
|
|
3132
|
-
if (subpathRoot !== undefined && activeLexiconPackage(subpathRoot, ctx.lexiconPackages) !== undefined)
|
|
3566
|
+
if (!pathsMapped && subpathRoot !== undefined && activeLexiconPackage(subpathRoot, ctx.lexiconPackages) !== undefined) {
|
|
3567
|
+
return true;
|
|
3568
|
+
}
|
|
3133
3569
|
// Only a chant-shaped or project-relative specifier is worth resolving; any
|
|
3134
3570
|
// other bare specifier is untrusted by definition, and resolving it to find
|
|
3135
3571
|
// that out would cost the pathological cold `require.resolve` (chant#1020).
|
|
3136
|
-
if (!isProjectFileSpecifier(binding.specifier) && !isChantOwnedSpecifier(binding.specifier))
|
|
3572
|
+
if (!pathsMapped && !isProjectFileSpecifier(binding.specifier) && !isChantOwnedSpecifier(binding.specifier)) {
|
|
3573
|
+
return false;
|
|
3574
|
+
}
|
|
3137
3575
|
let targetPath: string;
|
|
3138
3576
|
try {
|
|
3139
3577
|
targetPath = resolveModulePathMemoized(binding.specifier, ctx.file, ctx.resolvePathCache);
|
|
@@ -3415,6 +3853,28 @@ function stampParamDependencies(entity: unknown, node: ts.NewExpression, ctx: Re
|
|
|
3415
3853
|
async function preresolveResourceConsts(ctx: ResolveCtx): Promise<Map<ts.Expression, unknown>> {
|
|
3416
3854
|
const built = new Map<ts.Expression, unknown>();
|
|
3417
3855
|
for (const [name, initializer] of ctx.consts) {
|
|
3856
|
+
// chant #3196 — a same-file `const x = tag\`...\`` for a registered tag
|
|
3857
|
+
// is built once here too, so the export and every reference to `x` share
|
|
3858
|
+
// the one value running the module gives them. That matters when the tag
|
|
3859
|
+
// returns an entity (a SQL `table\`...\``): a second call would build a
|
|
3860
|
+
// second entity discovery never registers.
|
|
3861
|
+
if (
|
|
3862
|
+
ts.isTaggedTemplateExpression(initializer) &&
|
|
3863
|
+
ts.isIdentifier(initializer.tag) &&
|
|
3864
|
+
ctx.intrinsics.some((i) => i.name === (initializer.tag as ts.Identifier).text && intrinsicTagFolds(i))
|
|
3865
|
+
) {
|
|
3866
|
+
try {
|
|
3867
|
+
const value = await reviveFoldedValue(fold(initializer, ctx.consts, ctx.intrinsics, ctx.externals), ctx, false);
|
|
3868
|
+
built.set(initializer, value);
|
|
3869
|
+
// Only a value `reviveFoldedValue` passes through unchanged is safe to
|
|
3870
|
+
// hand a reference. Any other object would be walked into a plain
|
|
3871
|
+
// copy, so a reference to one keeps re-folding the initializer.
|
|
3872
|
+
if (revivesAsItself(value)) ctx.externals.set(name, value);
|
|
3873
|
+
} catch (err) {
|
|
3874
|
+
ctx.prebuildFailures?.set(name, describeFoldFailure(err, ctx));
|
|
3875
|
+
}
|
|
3876
|
+
continue;
|
|
3877
|
+
}
|
|
3418
3878
|
if (!ts.isNewExpression(initializer) || !ts.isIdentifier(initializer.expression)) continue;
|
|
3419
3879
|
try {
|
|
3420
3880
|
const spec = foldResource(initializer, ctx.consts, ctx.intrinsics, ctx.externals);
|
|
@@ -3593,6 +4053,15 @@ function isProjectFileSpecifier(specifier: string): boolean {
|
|
|
3593
4053
|
return specifier.startsWith(".") || isAbsolute(specifier);
|
|
3594
4054
|
}
|
|
3595
4055
|
|
|
4056
|
+
/**
|
|
4057
|
+
* chant#3090 — {@link isProjectFileSpecifier} for an import in `fromFile`,
|
|
4058
|
+
* counting a bare specifier that a tsconfig `paths` entry maps to a file
|
|
4059
|
+
* ({@link pathsMappedTarget}) as the project file it names.
|
|
4060
|
+
*/
|
|
4061
|
+
function isProjectImport(specifier: string, fromFile: string): boolean {
|
|
4062
|
+
return isProjectFileSpecifier(specifier) || pathsMappedTarget(specifier, fromFile) !== undefined;
|
|
4063
|
+
}
|
|
4064
|
+
|
|
3596
4065
|
/**
|
|
3597
4066
|
* Eagerly resolve every project-file import binding of `file`'s to its real
|
|
3598
4067
|
* cross-file value (see this section's own doc above). A binding that isn't
|
|
@@ -3609,7 +4078,16 @@ async function buildExternals(
|
|
|
3609
4078
|
imports: Map<string, ImportBinding>,
|
|
3610
4079
|
namespaceImports: Map<string, NamespaceImportBinding>,
|
|
3611
4080
|
session: FoldSession,
|
|
4081
|
+
/**
|
|
4082
|
+
* chant#3247 — trusted lexicon directories beyond the session's own
|
|
4083
|
+
* {@link FoldSession.lexiconRoots}: the package root of a lexicon-package
|
|
4084
|
+
* composite whose defining module this is. Files under one are treated as
|
|
4085
|
+
* a path lexicon's are, never folded as project files.
|
|
4086
|
+
*/
|
|
4087
|
+
extraLexiconRoots: readonly string[] = [],
|
|
3612
4088
|
): Promise<{ externals: Map<string, unknown>; failures: Map<string, string>; liveSources: Set<string> }> {
|
|
4089
|
+
const lexiconRoots =
|
|
4090
|
+
extraLexiconRoots.length === 0 ? session.lexiconRoots : [...session.lexiconRoots, ...extraLexiconRoots];
|
|
3613
4091
|
const externals = new Map<string, unknown>();
|
|
3614
4092
|
const failures = new Map<string, string>();
|
|
3615
4093
|
// chant #1044 — see `FoldFileResult.liveSources`.
|
|
@@ -3652,7 +4130,7 @@ async function buildExternals(
|
|
|
3652
4130
|
externals.set(localName, session.buildParams);
|
|
3653
4131
|
continue;
|
|
3654
4132
|
}
|
|
3655
|
-
if (
|
|
4133
|
+
if (isProjectImport(binding.specifier, file)) {
|
|
3656
4134
|
try {
|
|
3657
4135
|
const targetPath = resolveModulePathMemoized(binding.specifier, file, session.resolvePathCache);
|
|
3658
4136
|
if (targetPath === paramsModulePath()) {
|
|
@@ -3666,7 +4144,7 @@ async function buildExternals(
|
|
|
3666
4144
|
}
|
|
3667
4145
|
}
|
|
3668
4146
|
|
|
3669
|
-
if (!
|
|
4147
|
+
if (!isProjectImport(binding.specifier, file)) {
|
|
3670
4148
|
// chant #1063 — a bare specifier naming one of THIS BUILD's active
|
|
3671
4149
|
// lexicon packages resolves to that package's real export, so a plain
|
|
3672
4150
|
// data export a lexicon publishes (`Azure`/`GCP`'s pseudo-parameter
|
|
@@ -3697,6 +4175,20 @@ async function buildExternals(
|
|
|
3697
4175
|
// it resolves lazily, through the pre-existing `importModule`
|
|
3698
4176
|
// mechanism, only once a constructor/composite-factory/intrinsic tag
|
|
3699
4177
|
// actually consumes it.
|
|
4178
|
+
//
|
|
4179
|
+
// chant#3090 — except that a tsconfig `paths` entry which matches the
|
|
4180
|
+
// specifier but names no file is recorded, so the reason says which
|
|
4181
|
+
// import failed and why.
|
|
4182
|
+
const unmatched = pathsMatch(binding.specifier, file);
|
|
4183
|
+
if (unmatched) {
|
|
4184
|
+
failures.set(
|
|
4185
|
+
localName,
|
|
4186
|
+
locatedMessage(
|
|
4187
|
+
binding.specifierNode,
|
|
4188
|
+
`tsconfig paths entry "${unmatched.key}" matches "${binding.specifier}" but names no file`,
|
|
4189
|
+
),
|
|
4190
|
+
);
|
|
4191
|
+
}
|
|
3700
4192
|
continue;
|
|
3701
4193
|
}
|
|
3702
4194
|
let targetPath: string;
|
|
@@ -3718,7 +4210,20 @@ async function buildExternals(
|
|
|
3718
4210
|
// subpath (see the bare-specifier branch above) its data exports are not
|
|
3719
4211
|
// read as values. A constructor or composite from it resolves through the
|
|
3720
4212
|
// import path, where `isTrustedExecutableBinding` admits it.
|
|
3721
|
-
|
|
4213
|
+
//
|
|
4214
|
+
// chant#3247 — a file of the lexicon PACKAGE whose composite is being
|
|
4215
|
+
// interpreted is that package's own module, which `loadPlugins` already
|
|
4216
|
+
// imported through the package's barrel. Its plain data exports (an
|
|
4217
|
+
// action table, a policy constant) are read off the real module, so the
|
|
4218
|
+
// value is the run path's own object, identity included. Callables stay
|
|
4219
|
+
// out, as {@link resolveActiveLexiconExport} keeps them out: a
|
|
4220
|
+
// constructor or composite resolves through the import path.
|
|
4221
|
+
if (inPathLexiconRoot(targetPath, extraLexiconRoots)) {
|
|
4222
|
+
const value = await packageDataExport(targetPath, binding.imported, session);
|
|
4223
|
+
if (value) externals.set(localName, value.value);
|
|
4224
|
+
continue;
|
|
4225
|
+
}
|
|
4226
|
+
if (inPathLexiconRoot(targetPath, lexiconRoots)) continue;
|
|
3722
4227
|
const result = await foldFileMemoized(targetPath, session);
|
|
3723
4228
|
if (!result.ok) {
|
|
3724
4229
|
failures.set(localName, locatedMessage(binding.specifierNode, result.reason));
|
|
@@ -3737,7 +4242,7 @@ async function buildExternals(
|
|
|
3737
4242
|
}
|
|
3738
4243
|
|
|
3739
4244
|
for (const [localName, binding] of namespaceImports) {
|
|
3740
|
-
if (!
|
|
4245
|
+
if (!isProjectImport(binding.specifier, file)) continue;
|
|
3741
4246
|
let targetPath: string;
|
|
3742
4247
|
try {
|
|
3743
4248
|
targetPath = resolveModulePathMemoized(binding.specifier, file, session.resolvePathCache);
|
|
@@ -3746,7 +4251,7 @@ async function buildExternals(
|
|
|
3746
4251
|
}
|
|
3747
4252
|
// chant#2577 — a namespace import of a lexicon is left alone, as the
|
|
3748
4253
|
// bare-specifier check above leaves a package's alone.
|
|
3749
|
-
if (session.lexiconModules.has(targetPath) || inPathLexiconRoot(targetPath,
|
|
4254
|
+
if (session.lexiconModules.has(targetPath) || inPathLexiconRoot(targetPath, lexiconRoots)) continue;
|
|
3750
4255
|
const result = await foldFileMemoized(targetPath, session);
|
|
3751
4256
|
if (!result.ok) {
|
|
3752
4257
|
failures.set(localName, locatedMessage(binding.specifierNode, result.reason));
|
|
@@ -3799,7 +4304,9 @@ function hasObjectIdentity(value: unknown): boolean {
|
|
|
3799
4304
|
* back to run, exactly as before #1020.
|
|
3800
4305
|
*/
|
|
3801
4306
|
async function resolveDeclaratorValue(node: ts.Expression, ctx: ResolveCtx): Promise<{ value: unknown }> {
|
|
3802
|
-
const
|
|
4307
|
+
// chant#3325 — memoized, so an exported `const x = Composite(...)` and a
|
|
4308
|
+
// reference to `x` elsewhere in the file hold the one instance.
|
|
4309
|
+
const live = await resolveMemoized(node, ctx);
|
|
3803
4310
|
if (live !== undefined) return live;
|
|
3804
4311
|
const folded = fold(node, ctx.consts, ctx.intrinsics, ctx.externals);
|
|
3805
4312
|
return { value: await reviveFoldedValue(folded, ctx, false) };
|
|
@@ -3915,6 +4422,11 @@ async function tryFoldFileCore(file: string, session: FoldSession): Promise<Fold
|
|
|
3915
4422
|
applyResolvedValue(decl.name, marker, entities, exportedValues);
|
|
3916
4423
|
continue;
|
|
3917
4424
|
}
|
|
4425
|
+
// chant #3196 — a registered tagged template the pre-pass built.
|
|
4426
|
+
if (prebuiltResources.has(decl.node)) {
|
|
4427
|
+
applyResolvedValue(decl.name, prebuiltResources.get(decl.node), entities, exportedValues);
|
|
4428
|
+
continue;
|
|
4429
|
+
}
|
|
3918
4430
|
let value: unknown;
|
|
3919
4431
|
try {
|
|
3920
4432
|
value = (await resolveDeclaratorValue(decl.node, ctx)).value;
|
|
@@ -4137,7 +4649,7 @@ export async function buildProjectImportEdges(files: readonly string[]): Promise
|
|
|
4137
4649
|
for (const file of files) {
|
|
4138
4650
|
const targets = new Set<string>();
|
|
4139
4651
|
const addTarget = (specifier: string): void => {
|
|
4140
|
-
if (!
|
|
4652
|
+
if (!isProjectImport(specifier, file)) return; // package import, not a sibling source file
|
|
4141
4653
|
let resolved: string;
|
|
4142
4654
|
try {
|
|
4143
4655
|
resolved = resolveModulePath(specifier, file);
|