@intentius/chant 0.124.31 → 0.124.33
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/bin/chant +7 -2
- package/dist/ci-jobs.d.ts +4 -0
- package/dist/ci-jobs.d.ts.map +1 -1
- package/dist/ci-jobs.js +9 -2
- package/dist/cli/commands/check-lexicon-plugin.d.ts +7 -4
- package/dist/cli/commands/check-lexicon-plugin.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-plugin.js +10 -7
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +18 -12
- package/dist/cli/commands/import.d.ts +10 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/import.js +37 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.js +4 -3
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/onboard.js +8 -4
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +2 -1
- package/dist/cli/dependencies.d.ts +18 -0
- package/dist/cli/dependencies.d.ts.map +1 -0
- package/dist/cli/dependencies.js +120 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +38 -4
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +12 -4
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +16 -6
- package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
- package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-health.js +150 -0
- package/dist/cli/handlers/lifecycle.d.ts +26 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +1 -1
- package/dist/cli/handlers/lint.d.ts.map +1 -1
- package/dist/cli/handlers/lint.js +6 -0
- package/dist/cli/handlers/operator.d.ts +2 -2
- package/dist/cli/handlers/operator.js +3 -3
- package/dist/cli/handlers/pr.d.ts +16 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +610 -32
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +19 -2
- package/dist/cli/handlers/release.d.ts +16 -1
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +83 -11
- package/dist/cli/handlers/run.d.ts +13 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +38 -15
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +121 -33
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +37 -0
- package/dist/cli/registry.d.ts +29 -6
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +6 -2
- package/dist/codegen/generate-post-synth-barrel.js +2 -2
- package/dist/codegen/lexicon-regen.d.ts +4 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.js +1 -1
- package/dist/codegen/pinned-upgrade.js +2 -2
- package/dist/components/capability.d.ts +44 -3
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/capability.js +21 -0
- package/dist/components/changed-files.d.ts +7 -1
- package/dist/components/changed-files.d.ts.map +1 -1
- package/dist/components/changed-files.js +9 -1
- package/dist/components/deploy-inputs.d.ts +91 -0
- package/dist/components/deploy-inputs.d.ts.map +1 -0
- package/dist/components/deploy-inputs.js +184 -0
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +56 -6
- package/dist/components/fan-out-record.d.ts +6 -1
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +24 -0
- package/dist/components/fan-out-run.d.ts +20 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +21 -1
- package/dist/components/gate-plan.d.ts +8 -0
- package/dist/components/gate-plan.d.ts.map +1 -1
- package/dist/components/gate-plan.js +6 -0
- package/dist/components/pr-pipeline.d.ts +98 -12
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +146 -23
- package/dist/components/pr-run.d.ts +128 -30
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +290 -70
- package/dist/components/release-compile.d.ts +37 -2
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +64 -8
- package/dist/components/release-promote.d.ts +17 -3
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +79 -18
- package/dist/components/verbs/bom-writer.d.ts +1 -1
- package/dist/components/verbs/bom-writer.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
- package/dist/components/wave-plan.d.ts +15 -2
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +44 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-import.js +16 -6
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.js +6 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.js +8 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/fold.js +68 -1
- package/dist/fold/subset.d.ts +37 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/fold/subset.js +88 -0
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +32 -7
- package/dist/health.d.ts +110 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +105 -0
- package/dist/health.schema.json +191 -0
- package/dist/lexicon-module.d.ts +10 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon-module.js +53 -8
- package/dist/lexicon-plugin-helpers.d.ts.map +1 -1
- package/dist/lexicon-plugin-helpers.js +6 -4
- package/dist/lexicon.d.ts +29 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/affected.d.ts.map +1 -1
- package/dist/lifecycle/affected.js +7 -1
- package/dist/lifecycle/merge-record.d.ts +104 -0
- package/dist/lifecycle/merge-record.d.ts.map +1 -0
- package/dist/lifecycle/merge-record.js +102 -0
- package/dist/lifecycle/publish-attachments.d.ts +8 -5
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
- package/dist/lifecycle/publish-attachments.js +27 -13
- package/dist/lifecycle/publish-npm.d.ts +3 -2
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +4 -3
- package/dist/lifecycle/release-at.d.ts +40 -0
- package/dist/lifecycle/release-at.d.ts.map +1 -0
- package/dist/lifecycle/release-at.js +83 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +23 -12
- package/dist/lifecycle/release-ledger.d.ts +21 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +24 -0
- package/dist/lifecycle/release-publish.d.ts +28 -12
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +29 -17
- package/dist/lifecycle/release-store.d.ts +25 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +40 -13
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lifecycle/work-dir.d.ts +45 -0
- package/dist/lifecycle/work-dir.d.ts.map +1 -0
- package/dist/lifecycle/work-dir.js +78 -0
- package/dist/lint/discover.d.ts +17 -9
- package/dist/lint/discover.d.ts.map +1 -1
- package/dist/lint/discover.js +62 -54
- package/dist/lint/rules/op/index.d.ts +2 -1
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +4 -2
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
- package/dist/module-source.d.ts +57 -0
- package/dist/module-source.d.ts.map +1 -0
- package/dist/module-source.js +104 -0
- package/dist/op/activities/activity-contracts.d.ts +5 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +3 -0
- package/dist/op/activities/factory.js +1 -1
- package/dist/op/activities/shell.d.ts +38 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +89 -3
- package/dist/op/builders.d.ts +7 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +7 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +13 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +3 -0
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +20 -5
- package/dist/op/operator.d.ts +2 -2
- package/dist/op/operator.js +2 -2
- package/dist/op/runtime.d.ts +2 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/steward.d.ts +6 -4
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/steward.js +5 -3
- package/dist/op/work-lease-run.d.ts +20 -7
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/op/work-lease-run.js +56 -12
- package/dist/pr-forge.d.ts +10 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +39 -0
- package/dist/pr-loop.d.ts +155 -7
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +137 -11
- package/dist/pr-report-url.d.ts +4 -4
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/pr-report-url.js +5 -5
- package/dist/runner-start.d.ts +179 -0
- package/dist/runner-start.d.ts.map +1 -0
- package/dist/runner-start.js +277 -0
- package/dist/ts-loader.d.ts +38 -4
- package/dist/ts-loader.d.ts.map +1 -1
- package/dist/ts-loader.js +190 -16
- package/dist/wave-runtime.mjs +238 -231
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts +34 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/fountain-retired-kinds.js +176 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -1
- package/dist/workspace/chant-migrations.js +2 -0
- package/dist/workspace/check.schema.json +70 -2
- package/dist/workspace/checks/boxes.d.ts +6 -6
- package/dist/workspace/checks/boxes.js +9 -9
- package/dist/workspace/checks/security.d.ts +35 -0
- package/dist/workspace/checks/security.d.ts.map +1 -0
- package/dist/workspace/checks/security.js +305 -0
- package/dist/workspace/checks.d.ts +2 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +5 -0
- package/dist/workspace/ci-builds.d.ts +163 -0
- package/dist/workspace/ci-builds.d.ts.map +1 -0
- package/dist/workspace/ci-builds.js +315 -0
- package/dist/workspace/ci-green-cli.d.ts +2 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +25 -5
- package/dist/workspace/ci-green-forge.d.ts +18 -4
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +12 -0
- package/dist/workspace/ci-green-workflow.d.ts +55 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +128 -1
- package/dist/workspace/ci-green.d.ts +117 -16
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +358 -43
- package/dist/workspace/ci-last-green.schema.json +49 -0
- package/dist/workspace/ci-list.schema.json +47 -7
- package/dist/workspace/ci-report-url.d.ts +18 -0
- package/dist/workspace/ci-report-url.d.ts.map +1 -0
- package/dist/workspace/ci-report-url.js +33 -0
- package/dist/workspace/ci-run-index.d.ts +13 -1
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.js +6 -4
- package/dist/workspace/ci-run-index.schema.json +6 -4
- package/dist/workspace/ci-run.d.ts +85 -6
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +262 -64
- package/dist/workspace/ci-runner-key.d.ts +16 -3
- package/dist/workspace/ci-runner-key.d.ts.map +1 -1
- package/dist/workspace/ci-runner-key.js +30 -10
- package/dist/workspace/ci-runner-start.d.ts +77 -0
- package/dist/workspace/ci-runner-start.d.ts.map +1 -0
- package/dist/workspace/ci-runner-start.js +185 -0
- package/dist/workspace/ci-serve.d.ts +130 -11
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +323 -40
- package/dist/workspace/declaration.d.ts +25 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +14 -2
- package/dist/workspace/declaration.schema.json +30 -2
- package/dist/workspace/export.d.ts +1 -1
- package/dist/workspace/export.d.ts.map +1 -1
- package/dist/workspace/export.js +7 -3
- package/dist/workspace/ls.d.ts +8 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -1
- package/dist/workspace/ls.schema.json +9 -2
- package/dist/workspace/member-pipeline.d.ts +1 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +2 -0
- package/dist/workspace/merge-record.schema.json +49 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -1
- package/dist/workspace/pin-integrity.js +3 -2
- package/dist/workspace/pr-report.schema.json +75 -8
- package/dist/workspace/reason-codes.d.ts +18 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +20 -2
- package/dist/workspace/release-candidates.d.ts +129 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -0
- package/dist/workspace/release-candidates.js +233 -0
- package/dist/workspace/release-candidates.schema.json +353 -0
- package/dist/workspace/run-event.schema.json +3 -1
- package/dist/workspace/security.d.ts +40 -0
- package/dist/workspace/security.d.ts.map +1 -0
- package/dist/workspace/security.js +99 -0
- package/dist/workspace/status.schema.json +3 -1
- package/dist/workspace/write-scope.d.ts +9 -0
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/workspace/write-scope.js +11 -1
- package/package.json +8 -1
- package/src/__fixtures__/pr-loop/pr-apply-wave-waiting.note.golden.md +48 -0
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
- package/src/ci-jobs.ts +11 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +3 -3
- package/src/cli/commands/audit.test.ts +2 -2
- package/src/cli/commands/check-lexicon-plugin.test.ts +9 -0
- package/src/cli/commands/check-lexicon-plugin.ts +11 -8
- package/src/cli/commands/check-lexicon.test.ts +1 -1
- package/src/cli/commands/check-lexicon.ts +16 -11
- package/src/cli/commands/import.ts +40 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +4 -3
- package/src/cli/commands/init-lexicon.test.ts +8 -5
- package/src/cli/commands/onboard.test.ts +45 -0
- package/src/cli/commands/onboard.ts +8 -4
- package/src/cli/commands/update.test.ts +6 -0
- package/src/cli/commands/update.ts +4 -1
- package/src/cli/compiled-cli.e2e.test.ts +76 -11
- package/src/cli/dependencies.e2e.test.ts +101 -0
- package/src/cli/dependencies.test.ts +93 -0
- package/src/cli/dependencies.ts +122 -0
- package/src/cli/handlers/build.test.ts +105 -0
- package/src/cli/handlers/build.ts +35 -4
- package/src/cli/handlers/fan-out.test.ts +73 -0
- package/src/cli/handlers/fan-out.ts +11 -4
- package/src/cli/handlers/graph.test.ts +76 -0
- package/src/cli/handlers/graph.ts +19 -12
- package/src/cli/handlers/lifecycle-health.test.ts +123 -0
- package/src/cli/handlers/lifecycle-health.ts +157 -0
- package/src/cli/handlers/lifecycle.ts +1 -1
- package/src/cli/handlers/lint.test.ts +42 -0
- package/src/cli/handlers/lint.ts +6 -0
- package/src/cli/handlers/operator.ts +3 -3
- package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
- package/src/cli/handlers/pr-record.test.ts +201 -0
- package/src/cli/handlers/pr-waves.test.ts +449 -0
- package/src/cli/handlers/pr.test.ts +156 -0
- package/src/cli/handlers/pr.ts +648 -33
- package/src/cli/handlers/promote.test.ts +43 -1
- package/src/cli/handlers/promote.ts +19 -3
- package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
- package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
- package/src/cli/handlers/release.e2e.test.ts +19 -28
- package/src/cli/handlers/release.ts +81 -11
- package/src/cli/handlers/run-components-exit.test.ts +99 -0
- package/src/cli/handlers/run-signer.test.ts +79 -0
- package/src/cli/handlers/run.ts +34 -16
- package/src/cli/main.test.ts +18 -0
- package/src/cli/main.ts +120 -33
- package/src/cli/parse-args.ts +28 -0
- package/src/cli/registry.ts +35 -8
- package/src/codegen/generate-post-synth-barrel.ts +2 -2
- package/src/codegen/lexicon-regen.ts +1 -1
- package/src/codegen/pinned-upgrade.ts +2 -2
- package/src/codegen/post-synth-barrel-guards.test.ts +6 -11
- package/src/components/capability.ts +51 -3
- package/src/components/changed-files.test.ts +16 -1
- package/src/components/changed-files.ts +9 -1
- package/src/components/deploy-inputs.test.ts +108 -0
- package/src/components/deploy-inputs.ts +225 -0
- package/src/components/driver.test.ts +36 -1
- package/src/components/driver.ts +87 -4
- package/src/components/fan-out-record.ts +29 -1
- package/src/components/fan-out-run.test.ts +35 -0
- package/src/components/fan-out-run.ts +40 -1
- package/src/components/gate-plan.ts +9 -0
- package/src/components/pr-pipeline.test.ts +100 -0
- package/src/components/pr-pipeline.ts +184 -28
- package/src/components/pr-run.test.ts +323 -18
- package/src/components/pr-run.ts +355 -76
- package/src/components/release-compile.test.ts +34 -0
- package/src/components/release-compile.ts +89 -8
- package/src/components/release-promote.test.ts +55 -2
- package/src/components/release-promote.ts +93 -23
- package/src/components/verbs/bom-writer.ts +1 -1
- package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
- package/src/components/wave-plan.ts +48 -8
- package/src/discovery/fold-host-method.test.ts +87 -0
- package/src/discovery/fold-import.ts +15 -5
- package/src/discovery/fold-package-composite.test.ts +27 -0
- package/src/discovery/fold-specifiers.test.ts +8 -0
- package/src/discovery/sandbox/bundle.ts +6 -0
- package/src/discovery/specifier-resolve.ts +8 -2
- package/src/fold/fold.test.ts +72 -0
- package/src/fold/fold.ts +71 -0
- package/src/fold/subset.test.ts +31 -0
- package/src/fold/subset.ts +99 -0
- package/src/graph-detail.test.ts +57 -0
- package/src/graph-detail.ts +33 -7
- package/src/health.schema.json +191 -0
- package/src/health.test.ts +62 -0
- package/src/health.ts +178 -0
- package/src/lexicon-dist.e2e.test.ts +254 -0
- package/src/lexicon-module.test.ts +47 -0
- package/src/lexicon-module.ts +49 -7
- package/src/lexicon-plugin-helpers.ts +6 -4
- package/src/lexicon.ts +43 -1
- package/src/lifecycle/affected.test.ts +7 -0
- package/src/lifecycle/affected.ts +6 -1
- package/src/lifecycle/merge-record.test.ts +111 -0
- package/src/lifecycle/merge-record.ts +154 -0
- package/src/lifecycle/publish-attachments.ts +25 -13
- package/src/lifecycle/publish-npm.ts +4 -3
- package/src/lifecycle/publish-targets.test.ts +34 -4
- package/src/lifecycle/release-at.test.ts +48 -0
- package/src/lifecycle/release-at.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +41 -0
- package/src/lifecycle/release-cut.ts +22 -11
- package/src/lifecycle/release-ledger.ts +34 -0
- package/src/lifecycle/release-publish.test.ts +43 -13
- package/src/lifecycle/release-publish.ts +38 -18
- package/src/lifecycle/release-store.ts +57 -13
- package/src/lifecycle/run-ledger.test.ts +13 -0
- package/src/lifecycle/run-ledger.ts +1 -0
- package/src/lifecycle/work-dir.test.ts +77 -0
- package/src/lifecycle/work-dir.ts +82 -0
- package/src/lint/discover.test.ts +46 -2
- package/src/lint/discover.ts +55 -49
- package/src/lint/rules/op/index.ts +4 -2
- package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
- package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
- package/src/module-source.test.ts +85 -0
- package/src/module-source.ts +112 -0
- package/src/op/activities/activity-contracts.ts +3 -0
- package/src/op/activities/factory.ts +1 -1
- package/src/op/activities/shell-container.e2e.test.ts +67 -0
- package/src/op/activities/shell.test.ts +44 -1
- package/src/op/activities/shell.ts +110 -3
- package/src/op/builders.ts +7 -0
- package/src/op/discover-root.test.ts +31 -2
- package/src/op/discover.ts +14 -4
- package/src/op/gate.test.ts +30 -0
- package/src/op/gate.ts +3 -0
- package/src/op/op-waves-run.ts +20 -5
- package/src/op/op-waves.test.ts +49 -0
- package/src/op/operator.ts +2 -2
- package/src/op/runtime.ts +2 -0
- package/src/op/step-console.test.ts +4 -2
- package/src/op/steward.ts +6 -4
- package/src/op/work-lease-run.test.ts +88 -2
- package/src/op/work-lease-run.ts +52 -12
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +42 -0
- package/src/pr-forge.ts +40 -0
- package/src/pr-loop-bundle.test.ts +2 -1
- package/src/pr-loop.test.ts +146 -1
- package/src/pr-loop.ts +278 -17
- package/src/pr-report-url.ts +7 -7
- package/src/runner-start-bundle.test.ts +29 -0
- package/src/runner-start.test.ts +179 -0
- package/src/runner-start.ts +329 -0
- package/src/terraform/__fixtures__/advise.test.ts +21 -2
- package/src/ts-loader.test.ts +89 -1
- package/src/ts-loader.ts +180 -13
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/chant-migrations/fountain-retired-kinds.test.ts +33 -0
- package/src/workspace/chant-migrations/fountain-retired-kinds.ts +190 -0
- package/src/workspace/chant-migrations.e2e.test.ts +5 -0
- package/src/workspace/chant-migrations.ts +2 -0
- package/src/workspace/check-contract.test.ts +3 -2
- package/src/workspace/check.schema.json +70 -2
- package/src/workspace/checks/boxes.ts +9 -9
- package/src/workspace/checks/security.test.ts +138 -0
- package/src/workspace/checks/security.ts +305 -0
- package/src/workspace/checks.ts +5 -0
- package/src/workspace/ci-builds.e2e.test.ts +208 -0
- package/src/workspace/ci-builds.test.ts +174 -0
- package/src/workspace/ci-builds.ts +385 -0
- package/src/workspace/ci-green-builds.test.ts +180 -0
- package/src/workspace/ci-green-cli.test.ts +54 -0
- package/src/workspace/ci-green-cli.ts +26 -5
- package/src/workspace/ci-green-forge.ts +30 -4
- package/src/workspace/ci-green-runs.test.ts +53 -11
- package/src/workspace/ci-green-workflow.test.ts +84 -1
- package/src/workspace/ci-green-workflow.ts +139 -2
- package/src/workspace/ci-green.test.ts +6 -3
- package/src/workspace/ci-green.ts +408 -48
- package/src/workspace/ci-last-green.schema.json +49 -0
- package/src/workspace/ci-list.schema.json +47 -7
- package/src/workspace/ci-report-url.test.ts +220 -0
- package/src/workspace/ci-report-url.ts +33 -0
- package/src/workspace/ci-run-index.schema.json +6 -4
- package/src/workspace/ci-run-index.test.ts +42 -3
- package/src/workspace/ci-run-index.ts +19 -5
- package/src/workspace/ci-run.test.ts +203 -8
- package/src/workspace/ci-run.ts +295 -63
- package/src/workspace/ci-runner-key.test.ts +55 -17
- package/src/workspace/ci-runner-key.ts +36 -11
- package/src/workspace/ci-runner-start.test.ts +151 -0
- package/src/workspace/ci-runner-start.ts +193 -0
- package/src/workspace/ci-serve-builds.test.ts +86 -0
- package/src/workspace/ci-serve.test.ts +209 -6
- package/src/workspace/ci-serve.ts +341 -42
- package/src/workspace/declaration.schema.json +30 -2
- package/src/workspace/declaration.test.ts +40 -0
- package/src/workspace/declaration.ts +40 -3
- package/src/workspace/export.ts +6 -3
- package/src/workspace/ls.schema.json +9 -2
- package/src/workspace/ls.ts +10 -4
- package/src/workspace/member-pipeline.ts +2 -1
- package/src/workspace/merge-record.schema.json +49 -0
- package/src/workspace/pin-integrity.test.ts +5 -0
- package/src/workspace/pin-integrity.ts +3 -2
- package/src/workspace/pr-report.schema.json +75 -8
- package/src/workspace/reason-codes.test.ts +2 -0
- package/src/workspace/reason-codes.ts +20 -2
- package/src/workspace/release-candidates.schema.json +353 -0
- package/src/workspace/release-candidates.test.ts +106 -0
- package/src/workspace/release-candidates.ts +315 -0
- package/src/workspace/run-event.schema.json +3 -1
- package/src/workspace/security.test.ts +70 -0
- package/src/workspace/security.ts +118 -0
- package/src/workspace/status.schema.json +3 -1
- package/src/workspace/write-scope.test.ts +29 -1
- package/src/workspace/write-scope.ts +12 -1
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
* (`componentPlanDigest`, ./gate-plan.ts, #2574), so a wave mixing roots and
|
|
14
14
|
* other components still has one digest that moves when any of them does.
|
|
15
15
|
*
|
|
16
|
+
* The files a step without a `plan` deploys from are bound too (#4155,
|
|
17
|
+
* ./deploy-inputs.ts). A component with no plannable step folds them into its
|
|
18
|
+
* composition digest. A component with one gets a member more,
|
|
19
|
+
* `<component>#inputs`, over its other steps' files, so a `lambda-deploy`
|
|
20
|
+
* source or a Helm chart beside a `cfn-deploy` moves the wave's digest. A step
|
|
21
|
+
* whose content chant can't name is a hole on that member, so the plan note
|
|
22
|
+
* lists it as unbound.
|
|
23
|
+
*
|
|
16
24
|
* Known limit: a step input that reads an earlier step of the same
|
|
17
25
|
* component (`@Phase.field`) has no value before the component runs, so it
|
|
18
26
|
* plans as `undefined`. Cross-component wiring (`stackOutput()`,
|
|
@@ -21,7 +29,9 @@
|
|
|
21
29
|
|
|
22
30
|
import { resolveStepInput, unresolvedWiringSources, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
|
|
23
31
|
import { componentPlanDigest } from "./gate-plan";
|
|
24
|
-
import
|
|
32
|
+
import { componentDeployInputs, type ComponentDeployInputs } from "./deploy-inputs";
|
|
33
|
+
import { computePlanDigest } from "../lifecycle/plan-digest";
|
|
34
|
+
import type { CapabilityRegistry, DeployContext, PlannedOutputs } from "./capability";
|
|
25
35
|
import type { WaveMember } from "../gated-waves";
|
|
26
36
|
import type { ChangeSetPart } from "../change-set";
|
|
27
37
|
|
|
@@ -29,20 +39,26 @@ import type { ChangeSetPart } from "../change-set";
|
|
|
29
39
|
* What planning one component produced: its members, what its steps hand to
|
|
30
40
|
* `run`, and each member's change-set part (#3183), or why it could not plan.
|
|
31
41
|
* A member whose capability returns no part gets one with no entries, so the
|
|
32
|
-
* parts always cover the members.
|
|
42
|
+
* parts always cover the members. `outputs` is what its steps' plans say the
|
|
43
|
+
* component will expose once applied (#3999), merged the way the driver
|
|
44
|
+
* merges a run's outputs; absent when no step's plan says.
|
|
33
45
|
*/
|
|
34
46
|
export type WaveComponentPlan =
|
|
35
|
-
| { component: string; members: WaveMember[]; plans: Record<string, unknown>; parts: ChangeSetPart[] }
|
|
47
|
+
| { component: string; members: WaveMember[]; plans: Record<string, unknown>; parts: ChangeSetPart[]; outputs?: PlannedOutputs }
|
|
36
48
|
| { component: string; error: string };
|
|
37
49
|
|
|
38
|
-
/** A part with no entries, for a member whose planner describes no changes. */
|
|
39
|
-
function emptyPart(member: WaveMember): ChangeSetPart {
|
|
50
|
+
/** A part with no entries, for a member whose planner describes no changes. `unbound` steps become its holes (#4155). */
|
|
51
|
+
function emptyPart(member: WaveMember, unbound: ComponentDeployInputs["unbound"] = []): ChangeSetPart {
|
|
52
|
+
const holes = unbound.map((u) => ({ address: `${u.phase}/${u.kind}`, type: u.kind, reason: `unbound: ${u.reason}` }));
|
|
40
53
|
return {
|
|
41
|
-
member: { member: member.member, lexicon: "chant", planner: "chant", status: "planned", planDigest: member.planDigest, holes
|
|
54
|
+
member: { member: member.member, lexicon: "chant", planner: "chant", status: "planned", planDigest: member.planDigest, holes },
|
|
42
55
|
entries: [],
|
|
43
56
|
};
|
|
44
57
|
}
|
|
45
58
|
|
|
59
|
+
/** The member a planning component's other steps add (#4155): their files, under the component's name. */
|
|
60
|
+
export const inputsMemberName = (component: string): string => `${component}#inputs`;
|
|
61
|
+
|
|
46
62
|
/** Every capability step in a composition, nested phases included, gates left out. */
|
|
47
63
|
export function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
|
|
48
64
|
const out: DriverStep[] = [];
|
|
@@ -84,6 +100,7 @@ export async function planWaveComponent(
|
|
|
84
100
|
const members: WaveMember[] = [];
|
|
85
101
|
const plans: Record<string, unknown> = {};
|
|
86
102
|
const parts: ChangeSetPart[] = [];
|
|
103
|
+
let outputs: PlannedOutputs | undefined;
|
|
87
104
|
for (const step of capabilitySteps(component.deploy)) {
|
|
88
105
|
const capability = registry.resolve(step.kind);
|
|
89
106
|
if (!capability.plan) continue;
|
|
@@ -94,25 +111,48 @@ export async function planWaveComponent(
|
|
|
94
111
|
members.push(member);
|
|
95
112
|
parts.push(planned.changeSet ?? emptyPart(member));
|
|
96
113
|
if (planned.artifact !== undefined) plans[planned.member] = planned.artifact;
|
|
114
|
+
if (planned.outputs) outputs = mergePlannedOutputs(outputs, planned.outputs);
|
|
97
115
|
}
|
|
116
|
+
// #4155: the steps without a plan, which no member above covers.
|
|
117
|
+
const planless = (step: DriverStep): boolean => !registry.has(step.kind) || registry.resolve(step.kind).plan === undefined;
|
|
98
118
|
if (members.length === 0) {
|
|
119
|
+
const read = componentDeployInputs(component.deploy, process.cwd());
|
|
99
120
|
const member = {
|
|
100
121
|
member: component.name,
|
|
101
122
|
planDigest: componentPlanDigest({
|
|
102
123
|
environment: ctx.env,
|
|
103
124
|
component,
|
|
104
125
|
...(ctx.vars ? { vars: ctx.vars } : {}),
|
|
126
|
+
inputs: read.inputs,
|
|
105
127
|
}),
|
|
106
128
|
};
|
|
107
129
|
members.push(member);
|
|
108
|
-
parts.push(emptyPart(member));
|
|
130
|
+
parts.push(emptyPart(member, read.unbound));
|
|
131
|
+
} else {
|
|
132
|
+
const read = componentDeployInputs(component.deploy, process.cwd(), planless);
|
|
133
|
+
if (read.inputs.length > 0 || read.unbound.length > 0) {
|
|
134
|
+
const member = {
|
|
135
|
+
member: inputsMemberName(component.name),
|
|
136
|
+
planDigest: computePlanDigest("component-inputs", { environment: ctx.env, component: component.name, inputs: read.inputs }),
|
|
137
|
+
};
|
|
138
|
+
members.push(member);
|
|
139
|
+
parts.push(emptyPart(member, read.unbound));
|
|
140
|
+
}
|
|
109
141
|
}
|
|
110
|
-
return { component: component.name, members, plans, parts };
|
|
142
|
+
return { component: component.name, members, plans, parts, ...(outputs ? { outputs } : {}) };
|
|
111
143
|
} catch (err) {
|
|
112
144
|
return { component: component.name, error: err instanceof Error ? err.message : String(err) };
|
|
113
145
|
}
|
|
114
146
|
}
|
|
115
147
|
|
|
148
|
+
/** `next` over `prior`, as a later step's outputs win over an earlier one's in a run. */
|
|
149
|
+
function mergePlannedOutputs(prior: PlannedOutputs | undefined, next: PlannedOutputs): PlannedOutputs {
|
|
150
|
+
const known = { ...(prior?.known ?? {}), ...next.known };
|
|
151
|
+
const unknown = new Set([...(prior?.unknown ?? []).filter((n) => !(n in next.known)), ...next.unknown]);
|
|
152
|
+
for (const n of unknown) delete known[n];
|
|
153
|
+
return { known, unknown: [...unknown].sort() };
|
|
154
|
+
}
|
|
155
|
+
|
|
116
156
|
/**
|
|
117
157
|
* The outputs `component` exposes now, read from each step whose capability
|
|
118
158
|
* has `outputs` (#3183), merged the way the driver merges a run's outputs.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { foldProject } from "../index";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* chant#4011 — a method call reached `Function`, so a file reporting
|
|
9
|
+
* `[fold:fold]` could put the clock, `process.env` or `Math.random()` in its
|
|
10
|
+
* output. The issue's repro and its variants now get a `run` verdict, and a
|
|
11
|
+
* file of listed host methods folds to the same bytes whatever the
|
|
12
|
+
* environment says.
|
|
13
|
+
*/
|
|
14
|
+
describe("a folded file cannot reach host behaviour through a method call (chant#4011)", () => {
|
|
15
|
+
let root: string;
|
|
16
|
+
const saved = { HOME: process.env.HOME, TZ: process.env.TZ };
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
root = mkdtempSync(join(tmpdir(), "chant-4011-"));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
process.env.HOME = saved.HOME;
|
|
24
|
+
if (saved.TZ === undefined) delete process.env.TZ;
|
|
25
|
+
else process.env.TZ = saved.TZ;
|
|
26
|
+
rmSync(root, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function verdictOf(source: string) {
|
|
30
|
+
const file = join(root, "c.ts");
|
|
31
|
+
writeFileSync(file, source);
|
|
32
|
+
return (await foldProject([file], [], {})).get(file)!;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cases: Record<string, string> = {
|
|
36
|
+
"the issue's repro": [
|
|
37
|
+
"export const cfg = {",
|
|
38
|
+
' stamp: [].constructor.constructor("return String(Date.now())").call(),',
|
|
39
|
+
' home: `${[].constructor.constructor("return process.env.HOME").call()}`,',
|
|
40
|
+
"};",
|
|
41
|
+
].join("\n"),
|
|
42
|
+
"Math.random": 'export const cfg = { r: [].constructor.constructor("return Math.random()").call() };',
|
|
43
|
+
"process.env": 'export const cfg = { h: [].constructor.constructor("return process.env.HOME").call() };',
|
|
44
|
+
"new Date(...).toString()":
|
|
45
|
+
'export const cfg = { d: [].constructor.constructor("return new Date(0).toString()").call() };',
|
|
46
|
+
toLocaleString: "export const cfg = { n: (1234567.891).toLocaleString() };",
|
|
47
|
+
"Function through a prototype chain":
|
|
48
|
+
'export const cfg = { p: "".__proto__.constructor.constructor("return String(Date.now())").call() };',
|
|
49
|
+
"Function under an unlisted name":
|
|
50
|
+
'const o = { f: [].constructor.constructor };\nexport const cfg = { q: o.f("return 1") };',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
for (const [name, source] of Object.entries(cases)) {
|
|
54
|
+
test(`${name}: run, not fold`, async () => {
|
|
55
|
+
const verdict = await verdictOf(source);
|
|
56
|
+
expect(verdict.verdict).toBe("run");
|
|
57
|
+
expect(verdict.reason).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test("listed host methods fold to identical output under a different HOME and TZ", async () => {
|
|
62
|
+
const source = [
|
|
63
|
+
"const parts = ['a', 'b', 'c'];",
|
|
64
|
+
"export const cfg = {",
|
|
65
|
+
" name: parts.join('-'),",
|
|
66
|
+
" tail: 'x.y.z'.split('.').slice(1).join('/'),",
|
|
67
|
+
" pi: (3.14159).toFixed(2),",
|
|
68
|
+
" upper: 'chant'.toUpperCase(),",
|
|
69
|
+
"};",
|
|
70
|
+
].join("\n");
|
|
71
|
+
|
|
72
|
+
process.env.HOME = "/tmp/h1";
|
|
73
|
+
process.env.TZ = "UTC";
|
|
74
|
+
const first = await verdictOf(source);
|
|
75
|
+
process.env.HOME = "/tmp/h2";
|
|
76
|
+
process.env.TZ = "Asia/Kolkata";
|
|
77
|
+
const second = await verdictOf(source);
|
|
78
|
+
|
|
79
|
+
expect(first.verdict).toBe("fold");
|
|
80
|
+
expect(second.verdict).toBe("fold");
|
|
81
|
+
const bytes = (v: typeof first) => JSON.stringify(Object.fromEntries(v.exports!));
|
|
82
|
+
expect(bytes(first)).toBe(bytes(second));
|
|
83
|
+
expect(JSON.parse(bytes(first))).toEqual({
|
|
84
|
+
cfg: { name: "a-b-c", tail: "y/z", pi: "3.14", upper: "CHANT" },
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -28,6 +28,7 @@ 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 { compiledModuleSource } from "../module-source";
|
|
31
32
|
import {
|
|
32
33
|
isFile,
|
|
33
34
|
packageExportsTarget,
|
|
@@ -2050,10 +2051,11 @@ function isUnfoldableSameFileCall(name: string, ctx: ResolveCtx): boolean {
|
|
|
2050
2051
|
// specifier, or its package root for a subpath, against
|
|
2051
2052
|
// {@link FoldSession.lexiconPackages}, exactly as
|
|
2052
2053
|
// {@link isTrustedExecutableBinding}'s arm 1 matches it — and only then
|
|
2053
|
-
// resolved; a module that resolves to
|
|
2054
|
-
//
|
|
2055
|
-
// invoking. Every chant lexicon
|
|
2056
|
-
//
|
|
2054
|
+
// resolved; a module that resolves to a declaration file, or to compiled
|
|
2055
|
+
// JavaScript with no `src/` source beside its `dist/`, is not interpreted,
|
|
2056
|
+
// so a package that ships only `dist` keeps invoking. Every chant lexicon
|
|
2057
|
+
// publishes `src/` beside the `dist/*.js` its exports name (#3981), and
|
|
2058
|
+
// a compiled module is read from that source.
|
|
2057
2059
|
// Interpreting buys provenance: the run path cannot say which parameter
|
|
2058
2060
|
// fed a field, and interpretation can (#2161, #3246).
|
|
2059
2061
|
//
|
|
@@ -2478,7 +2480,15 @@ function factoryOrigin(specifier: string, fromFile: string, ctx: ResolveCtx): Fa
|
|
|
2478
2480
|
} catch {
|
|
2479
2481
|
return undefined;
|
|
2480
2482
|
}
|
|
2481
|
-
if (!isTypeScriptSource(modulePath))
|
|
2483
|
+
if (!isTypeScriptSource(modulePath)) {
|
|
2484
|
+
// #3981 — a lexicon's exports resolve to the compiled dist/*.js it
|
|
2485
|
+
// publishes, beside the src/ it was built from. The source is what is
|
|
2486
|
+
// read; an import the body makes still loads the compiled module, since
|
|
2487
|
+
// the compiled CLI points a lexicon's src/*.ts at its dist/*.js.
|
|
2488
|
+
const source = compiledModuleSource(modulePath);
|
|
2489
|
+
if (source === undefined || !isFile(source)) return undefined;
|
|
2490
|
+
modulePath = source;
|
|
2491
|
+
}
|
|
2482
2492
|
const packageRoot = lexiconPackageDirectory(modulePath, packageName);
|
|
2483
2493
|
return packageRoot === undefined ? undefined : { modulePath, packageRoot };
|
|
2484
2494
|
}
|
|
@@ -238,6 +238,33 @@ describe("lexicon-package and re-exported composites are interpreted (chant#3247
|
|
|
238
238
|
expect(foldExecutionCounts().factoryInterpretations).toBe(0);
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
+
test("a package whose exports name compiled JavaScript is interpreted from the src/ it ships beside dist/ (#3981)", async () => {
|
|
242
|
+
// As a chant lexicon publishes: exports on dist/*.js, the source under src/.
|
|
243
|
+
await installFakeLexicon(
|
|
244
|
+
"fakedist3981",
|
|
245
|
+
{
|
|
246
|
+
"dist/index.js": `export * from "./composites/index.js";\n`,
|
|
247
|
+
"dist/composites/index.js": `export * from "./web.js";\n`,
|
|
248
|
+
"dist/composites/web.js": `throw new Error("the compiled module is not read");\n`,
|
|
249
|
+
"src/index.ts": `export * from "./composites/index";\n`,
|
|
250
|
+
"src/composites/index.ts": `export * from "./web";\n`,
|
|
251
|
+
"src/composites/web.ts": WEBAPP_DEFINITION,
|
|
252
|
+
},
|
|
253
|
+
"./dist/index.js",
|
|
254
|
+
);
|
|
255
|
+
await writeFile(
|
|
256
|
+
join(srcDir, "main.ts"),
|
|
257
|
+
`import { WebApp } from "@intentius/chant-lexicon-fakedist3981";\nexport const web = WebApp({ name: "data" });\n`,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const result = await discover(srcDir, { fold: true, lexicons: ["aws", "fakedist3981"] });
|
|
261
|
+
|
|
262
|
+
expect(result.errors).toEqual([]);
|
|
263
|
+
expect(moduleRan(), "the defining module was read, never imported").toBeUndefined();
|
|
264
|
+
expect(foldExecutionCounts()).toMatchObject({ factoryInterpretations: 1, factoryInvocations: 0 });
|
|
265
|
+
expect([...result.entities.keys()]).toEqual(["webBucket"]);
|
|
266
|
+
});
|
|
267
|
+
|
|
241
268
|
test("a package re-export that leaves the package's directory is not followed", async () => {
|
|
242
269
|
await mkdir(join(testDir, "shared"), { recursive: true });
|
|
243
270
|
await writeFile(join(testDir, "shared", "web.ts"), WEBAPP_DEFINITION);
|
|
@@ -188,4 +188,12 @@ describe("packageExportsTarget (chant#3090)", () => {
|
|
|
188
188
|
expect(packageExportsTarget("./index.js", "./sub")).toBeUndefined();
|
|
189
189
|
expect(packageExportsTarget({ import: "./index.js" }, "./sub")).toBeUndefined();
|
|
190
190
|
});
|
|
191
|
+
test("from source, a lexicon's development export wins over the dist/*.js its default names (#3981)", () => {
|
|
192
|
+
const lexicon = {
|
|
193
|
+
".": { development: "./src/index.ts", types: "./dist/index.d.ts", default: "./dist/index.js" },
|
|
194
|
+
"./*": { development: "./src/*.ts", types: "./dist/*.d.ts", default: "./dist/*.js" },
|
|
195
|
+
};
|
|
196
|
+
expect(packageExportsTarget(lexicon, ".")).toBe("./src/index.ts");
|
|
197
|
+
expect(packageExportsTarget(lexicon, "./generated/index")).toBe("./src/generated/index.ts");
|
|
198
|
+
});
|
|
191
199
|
});
|
|
@@ -3,6 +3,7 @@ import { existsSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync,
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, join, resolve as resolvePath } from "node:path";
|
|
6
|
+
import { SOURCE_EXPORT_CONDITIONS } from "../../module-source";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* chant #1045 Phase 2 — bundles the generated sandbox driver (`./driver.ts`)
|
|
@@ -219,6 +220,11 @@ export async function bundleDriver(driverSource: string): Promise<BundleResult>
|
|
|
219
220
|
format: "esm",
|
|
220
221
|
absWorkingDir: bundleDir,
|
|
221
222
|
logLevel: "silent",
|
|
223
|
+
// #3981 — from source, a lexicon bundles from its src/*.ts, as the
|
|
224
|
+
// unsandboxed build imports it; compiled, from the dist/*.js it ships.
|
|
225
|
+
// Setting `conditions` replaces esbuild's default ["module"], so it is
|
|
226
|
+
// kept, and compiled the option is left out as before.
|
|
227
|
+
...(SOURCE_EXPORT_CONDITIONS.length > 0 ? { conditions: ["module", ...SOURCE_EXPORT_CONDITIONS] } : {}),
|
|
222
228
|
plugins: [plugin],
|
|
223
229
|
});
|
|
224
230
|
} catch (err) {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import * as ts from "typescript";
|
|
25
25
|
import { readFileSync, statSync } from "node:fs";
|
|
26
26
|
import { dirname, extname, isAbsolute, join, resolve as resolvePath } from "node:path";
|
|
27
|
+
import { SOURCE_EXPORT_CONDITIONS } from "../module-source";
|
|
27
28
|
|
|
28
29
|
/** True when `path` is an existing regular file. */
|
|
29
30
|
export function isFile(path: string): boolean {
|
|
@@ -89,8 +90,13 @@ export function splitBareSpecifier(specifier: string): { name: string; subpath:
|
|
|
89
90
|
return { name, subpath: rest === "" ? "." : `./${rest}` };
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The conditions an ESM import in Node matches, which is what the run path
|
|
95
|
+
* does. From source, `development` too (#3981): the run path there resolves a
|
|
96
|
+
* chant package to its `src/*.ts`, not to the compiled `dist/*.js` its
|
|
97
|
+
* `default` names.
|
|
98
|
+
*/
|
|
99
|
+
const EXPORT_CONDITIONS: ReadonlySet<string> = new Set(["import", "node", "default", ...SOURCE_EXPORT_CONDITIONS]);
|
|
94
100
|
|
|
95
101
|
/**
|
|
96
102
|
* The target an `exports` value selects, with `star` substituted for each `*`
|
package/src/fold/fold.test.ts
CHANGED
|
@@ -1375,3 +1375,75 @@ describe("fold — same-file functions without externals (chant#2436)", () => {
|
|
|
1375
1375
|
expect(() => fold(consts.get("x")!, consts, [], new Map())).toThrow(FoldError);
|
|
1376
1376
|
});
|
|
1377
1377
|
});
|
|
1378
|
+
|
|
1379
|
+
// chant#4011 — since #1966 a method call folded whenever the receiver was real
|
|
1380
|
+
// and the property was a function, so `.constructor.constructor` reached
|
|
1381
|
+
// `Function` and a folded file could read the clock, the environment or a
|
|
1382
|
+
// random number. Each case below folded to a value before the fix.
|
|
1383
|
+
describe("fold — a method call cannot reach Function or other host behaviour (#4011)", () => {
|
|
1384
|
+
const refuses = (src: string) => expect(() => foldConst(src, "x")).toThrow(FoldError);
|
|
1385
|
+
|
|
1386
|
+
test("Date.now() through [].constructor.constructor", () => {
|
|
1387
|
+
refuses(`const x = [].constructor.constructor("return String(Date.now())").call();`);
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
test("process.env through [].constructor.constructor", () => {
|
|
1391
|
+
refuses("const x = `${[].constructor.constructor(\"return process.env.HOME\").call()}`;");
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
test("Math.random() through [].constructor.constructor", () => {
|
|
1395
|
+
refuses(`const x = [].constructor.constructor("return Math.random()").call();`);
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
test("Function reached through a prototype chain on a string", () => {
|
|
1399
|
+
refuses(`const x = "".__proto__.constructor.constructor("return Date.now()").call();`);
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
test("Function stored under a name the table does not list", () => {
|
|
1403
|
+
// No refused name appears in the call: `f` is a plain property whose value
|
|
1404
|
+
// is Function, read by property access. The host-method table catches it.
|
|
1405
|
+
refuses(`const o = { f: [].constructor.constructor }; const x = o.f("return 1");`);
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
test("Function stored under a listed name is still refused: the table checks identity", () => {
|
|
1409
|
+
refuses(`const o = { toString: [].constructor.constructor }; const x = o.toString("return 1");`);
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
test("a static method on a host constructor has no row", () => {
|
|
1413
|
+
refuses(`const A = [].constructor; const x = A.of(1, 2);`);
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
test("a function passed to a listed host method is refused", () => {
|
|
1417
|
+
refuses(`const x = "abc".replace("b", [].constructor);`);
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
test("toLocaleString follows the host locale and is refused", () => {
|
|
1421
|
+
refuses(`const x = (1234567.891).toLocaleString();`);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
test("the error names the refused method", () => {
|
|
1425
|
+
expect(() => foldConst(`const x = [].constructor.constructor("return 1");`, "x")).toThrow(
|
|
1426
|
+
/`\.constructor\(\.\.\.\)` is not foldable/,
|
|
1427
|
+
);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
test("listed host methods still fold", () => {
|
|
1431
|
+
expect(foldConst(`const x = ["a", "b"].join(",");`, "x")).toBe("a,b");
|
|
1432
|
+
expect(foldConst(`const x = "a.b.c".split(".").slice(1).join("-");`, "x")).toBe("b-c");
|
|
1433
|
+
expect(foldConst(`const x = (3.14159).toFixed(2);`, "x")).toBe("3.14");
|
|
1434
|
+
expect(foldConst(`const x = "Abc".toUpperCase();`, "x")).toBe("ABC");
|
|
1435
|
+
expect(foldConst(`const x = true.toString();`, "x")).toBe("true");
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
test("a method written in package code is not a host method and still folds", () => {
|
|
1439
|
+
// The github lexicon's `Expression.prototype.toString` is this shape.
|
|
1440
|
+
class Expr {
|
|
1441
|
+
toString(): string {
|
|
1442
|
+
return "${{ github.actor }}";
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const consts = parseConsts(`const x = actor.toString();`);
|
|
1446
|
+
const externals = new Map<string, unknown>([["actor", new Expr()]]);
|
|
1447
|
+
expect(fold(consts.get("x") as ts.Expression, consts, [], externals)).toBe("${{ github.actor }}");
|
|
1448
|
+
});
|
|
1449
|
+
});
|
package/src/fold/fold.ts
CHANGED
|
@@ -6,12 +6,15 @@ import {
|
|
|
6
6
|
SUPPORTED_UNARY_OPERATORS,
|
|
7
7
|
UNSUPPORTED_OBJECT_MEMBER_MESSAGE,
|
|
8
8
|
UNSUPPORTED_UNARY_MESSAGE,
|
|
9
|
+
FOLDABLE_HOST_METHODS,
|
|
10
|
+
REFUSED_METHOD_NAMES,
|
|
9
11
|
briefNodeText,
|
|
10
12
|
callExpressionMessage,
|
|
11
13
|
computedPropertyNameMessage,
|
|
12
14
|
dynamicElementAccessMessage,
|
|
13
15
|
isLiteralElementKey,
|
|
14
16
|
isLiteralPropertyName,
|
|
17
|
+
refusedMethodMessage,
|
|
15
18
|
unsupportedBinaryMessage,
|
|
16
19
|
unsupportedExpressionMessage,
|
|
17
20
|
type SubsetRuleId,
|
|
@@ -1700,8 +1703,15 @@ export function fold(
|
|
|
1700
1703
|
// named property on it is actually a function. Calling it with the folded
|
|
1701
1704
|
// arguments is then no different from what running the file would do:
|
|
1702
1705
|
// the receiver is the same real object either way.
|
|
1706
|
+
//
|
|
1707
|
+
// chant#4011 — "never checked by name" let `[].constructor.constructor`
|
|
1708
|
+
// reach `Function`, so a folded file could run any code. A name in
|
|
1709
|
+
// REFUSED_METHOD_NAMES is refused on every receiver, before the receiver
|
|
1710
|
+
// is folded, exactly as EVL001 refuses it; a host (native) method must be
|
|
1711
|
+
// in FOLDABLE_HOST_METHODS for its receiver's type ({@link hostMethodRefusal}).
|
|
1703
1712
|
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
1704
1713
|
const methodName = node.expression.name.text;
|
|
1714
|
+
if (REFUSED_METHOD_NAMES.has(methodName)) throw foldError(node, refusedMethodMessage(methodName));
|
|
1705
1715
|
const receiver = fold(node.expression.expression, consts, intrinsics, externals);
|
|
1706
1716
|
// chant #2328 — a call is a link of an optional chain like any other:
|
|
1707
1717
|
// `a?.b()` and `a?.b.c()` on a nullish `a` are `undefined` in
|
|
@@ -1722,7 +1732,22 @@ export function fold(
|
|
|
1722
1732
|
if (typeof method !== "function") {
|
|
1723
1733
|
throw foldError(node, `"${methodName}" is not a callable method on the folded value — falls back to run`);
|
|
1724
1734
|
}
|
|
1735
|
+
// The arguments fold first, so `list.map((n) => …)` keeps refusing on
|
|
1736
|
+
// its callback, the reason the docs give for it.
|
|
1725
1737
|
const args = node.arguments.map((arg) => fold(arg, consts, intrinsics, externals));
|
|
1738
|
+
if (isNativeFunction(method)) {
|
|
1739
|
+
const refusal = hostMethodRefusal(receiver, methodName, method);
|
|
1740
|
+
if (refusal) throw foldError(node, refusal);
|
|
1741
|
+
// A host method handed a function calls it (`"x".replace("x", fn)`),
|
|
1742
|
+
// and the only functions a fold produces as values are host ones read
|
|
1743
|
+
// by property access (`[].constructor`), so none is passed in.
|
|
1744
|
+
if (args.some((arg) => typeof arg === "function")) {
|
|
1745
|
+
throw foldError(
|
|
1746
|
+
node,
|
|
1747
|
+
`a function passed to the host method \`.${methodName}(...)\` is not foldable — falls back to run`,
|
|
1748
|
+
);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1726
1751
|
return (method as (...methodArgs: unknown[]) => unknown).apply(receiver, args) as FoldedValue;
|
|
1727
1752
|
}
|
|
1728
1753
|
|
|
@@ -1732,6 +1757,52 @@ export function fold(
|
|
|
1732
1757
|
throw foldError(node, unsupportedExpressionMessage(node));
|
|
1733
1758
|
}
|
|
1734
1759
|
|
|
1760
|
+
/**
|
|
1761
|
+
* chant#4011 — true when `fn` is provided by the JavaScript engine rather than
|
|
1762
|
+
* written in JavaScript. A lexicon's own method (`Expression.prototype.toString`)
|
|
1763
|
+
* is JavaScript source; `Function`, `Date.now` and `Array.prototype.join` are
|
|
1764
|
+
* not. `Function.prototype.toString` prints a native function's body as
|
|
1765
|
+
* `[native code]` (ECMA-262, NativeFunction), and is read off the prototype so
|
|
1766
|
+
* a receiver cannot override it.
|
|
1767
|
+
*/
|
|
1768
|
+
function isNativeFunction(fn: unknown): boolean {
|
|
1769
|
+
return typeof fn === "function" && /\{\s*\[native code\]\s*\}\s*$/.test(Function.prototype.toString.call(fn));
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
const HOST_PROTOTYPES = {
|
|
1773
|
+
string: String.prototype,
|
|
1774
|
+
number: Number.prototype,
|
|
1775
|
+
boolean: Boolean.prototype,
|
|
1776
|
+
array: Array.prototype,
|
|
1777
|
+
object: Object.prototype,
|
|
1778
|
+
} as const;
|
|
1779
|
+
|
|
1780
|
+
/**
|
|
1781
|
+
* chant#4011 — why a host method `method`, read as `receiver[methodName]`,
|
|
1782
|
+
* does not fold, or `undefined` when it does. It folds only when the
|
|
1783
|
+
* receiver's type has a row in {@link FOLDABLE_HOST_METHODS} listing the name
|
|
1784
|
+
* and `method` is the function that type's prototype carries under it. A
|
|
1785
|
+
* function receiver (`[].constructor.from(...)`) has no row; neither does a
|
|
1786
|
+
* host object such as a `Date`, which has its own prototype.
|
|
1787
|
+
*/
|
|
1788
|
+
function hostMethodRefusal(receiver: unknown, methodName: string, method: unknown): string | undefined {
|
|
1789
|
+
let kind: keyof typeof HOST_PROTOTYPES | undefined;
|
|
1790
|
+
if (typeof receiver === "string") kind = "string";
|
|
1791
|
+
else if (typeof receiver === "number") kind = "number";
|
|
1792
|
+
else if (typeof receiver === "boolean") kind = "boolean";
|
|
1793
|
+
else if (Array.isArray(receiver)) kind = "array";
|
|
1794
|
+
else if (typeof receiver === "object" && receiver !== null) kind = "object";
|
|
1795
|
+
const listed =
|
|
1796
|
+
kind !== undefined &&
|
|
1797
|
+
FOLDABLE_HOST_METHODS[kind].has(methodName) &&
|
|
1798
|
+
(HOST_PROTOTYPES[kind] as unknown as Record<string, unknown>)[methodName] === method;
|
|
1799
|
+
if (listed) return undefined;
|
|
1800
|
+
return (
|
|
1801
|
+
`the host method \`.${methodName}(...)\` is not in the foldable table for a ${kind ?? typeof receiver} ` +
|
|
1802
|
+
"receiver (chant#4011) — falls back to run"
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1735
1806
|
/**
|
|
1736
1807
|
* Fold a resource constructor call to its spec.
|
|
1737
1808
|
*
|
package/src/fold/subset.test.ts
CHANGED
|
@@ -691,3 +691,34 @@ describe("findSubsetViolation — project-local calls (chant#2435)", () => {
|
|
|
691
691
|
expect(() => findSubsetViolation(synthetic)).toThrow(/Cannot read|undefined|getSourceFile|getText/i);
|
|
692
692
|
});
|
|
693
693
|
});
|
|
694
|
+
|
|
695
|
+
// chant#4011 — a refused method name is decidable from syntax, so EVL001 and
|
|
696
|
+
// fold() refuse it together. Before the fix both accepted every case here.
|
|
697
|
+
describe("subset — method names that reach Function or the host locale (#4011)", () => {
|
|
698
|
+
const cases = [
|
|
699
|
+
`[].constructor.constructor("return Date.now()").call()`,
|
|
700
|
+
`[].constructor.constructor("return process.env.HOME").call()`,
|
|
701
|
+
`"".__proto__.constructor.constructor("return Math.random()").call()`,
|
|
702
|
+
`(1234.5).toLocaleString()`,
|
|
703
|
+
];
|
|
704
|
+
for (const expr of cases) {
|
|
705
|
+
test(expr, () => {
|
|
706
|
+
const source = `const bad = new Thing({ x: ${expr} });`;
|
|
707
|
+
const sourceFile = ts.createSourceFile("t.ts", source, ts.ScriptTarget.Latest, true);
|
|
708
|
+
const consts = collectConsts(sourceFile);
|
|
709
|
+
expect(() => foldResource(consts.get("bad") as ts.NewExpression, consts, [])).toThrow(FoldError);
|
|
710
|
+
|
|
711
|
+
const context: LintContext = { sourceFile, entities: [], filePath: "t.ts", lexicon: undefined };
|
|
712
|
+
expect(evl001NonLiteralExpressionRule.check(context)).toHaveLength(1);
|
|
713
|
+
const violation = findSubsetViolation((consts.get("bad") as ts.NewExpression).arguments![0]);
|
|
714
|
+
expect(violation?.message).toContain("chant#4011");
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
test("a listed method keeps passing EVL001", () => {
|
|
719
|
+
const source = `const ok = new Thing({ x: ["a", "b"].join(",") });`;
|
|
720
|
+
const sourceFile = ts.createSourceFile("t.ts", source, ts.ScriptTarget.Latest, true);
|
|
721
|
+
const context: LintContext = { sourceFile, entities: [], filePath: "t.ts", lexicon: undefined };
|
|
722
|
+
expect(evl001NonLiteralExpressionRule.check(context)).toHaveLength(0);
|
|
723
|
+
});
|
|
724
|
+
});
|