@starci/hfs 4.7.31 → 4.7.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/CHANGELOG.md +12 -0
- package/README.md +13 -6
- package/emit/contracts.mjs +4 -3
- package/emit/form-bounds-reader.ts.partial +58 -0
- package/emit/form-bounds.mjs +155 -0
- package/emit/graphql-input-bounds.mjs +97 -0
- package/emit/input-bounds.mjs +44 -0
- package/emit/operation-schemas.mjs +137 -0
- package/emit/operations-worker.mjs +2 -2
- package/emit/operations.mjs +52 -16
- package/emit/schema-worker.mjs +17 -4
- package/emit/type-schema.mjs +17 -5
- package/package.json +9 -2
- package/release/gate.mjs +18 -23
- package/release/index.mjs +1 -1
- package/release/push.mjs +4 -5
- package/runtime/engine/admission.mjs +331 -0
- package/runtime/engine/canonical-json.mjs +14 -0
- package/runtime/engine/db/blob.mjs +380 -0
- package/runtime/engine/db/event-compact.mjs +73 -0
- package/runtime/engine/db/ledger-open.mjs +38 -0
- package/runtime/engine/db/ledger-paths.mjs +87 -0
- package/runtime/engine/db/ledger.mjs +1068 -0
- package/runtime/engine/db/machine-connection.mjs +150 -0
- package/runtime/engine/db/machine-open.mjs +60 -0
- package/runtime/engine/db/machine-schema.mjs +105 -0
- package/runtime/engine/db/machine.mjs +1317 -0
- package/runtime/engine/db/provider-reservations.mjs +127 -0
- package/runtime/engine/db/provision-authority.mjs +76 -0
- package/runtime/engine/db/provision-lock.mjs +29 -0
- package/runtime/engine/db/provision-operations.mjs +146 -0
- package/runtime/engine/db/ref-value.mjs +67 -0
- package/runtime/engine/db/schema/machine.sql +920 -0
- package/runtime/engine/db/schema/runtime.sql +1113 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +65 -9
- package/runtime/knowledge/hfs/design-truths.yaml +149 -0
- package/runtime/knowledge/hfs/fe-stack.yaml +23 -5
- package/runtime/knowledge/hfs/rules.yaml +269 -48
- package/runtime/knowledge/hfs/slots.yaml +113 -53
- package/runtime/knowledge/patterns/be/api.yaml +46 -20
- package/runtime/knowledge/patterns/be/event-bus.yaml +19 -12
- package/runtime/knowledge/patterns/be/idempotency.yaml +265 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +17 -12
- package/runtime/knowledge/patterns/be/projections.yaml +2 -2
- package/runtime/knowledge/patterns/be/queues.yaml +57 -22
- package/runtime/knowledge/patterns/be/reactors.yaml +1 -1
- package/runtime/knowledge/patterns/be/realtime.yaml +326 -27
- package/runtime/knowledge/patterns/be/saga.yaml +2 -2
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/command-policy.yaml +249 -0
- package/runtime/modules/kernel/failure-codes.yaml +211 -52
- package/runtime/modules/kernel/hook-input.yaml +6 -0
- package/runtime/modules/kernel/protected-zone.yaml +81 -0
- package/runtime/modules/kernel/removed-vocabulary.yaml +62 -0
- package/runtime/modules/kernel/roles.yaml +405 -0
- package/runtime/modules/models/registry.yaml +3 -1
- package/runtime/modules/models/runtimes.yaml +2 -0
- package/runtime/modules/schemas/application-stacks.schema.yaml +10 -2
- package/runtime/packages/cli/src/catalog.generated.mjs +46 -59
- package/runtime/packages/grammar/scripts/build-stamp.mjs +98 -0
- package/runtime/scripts/agent/codex-app-server.mjs +79 -0
- package/runtime/scripts/agent/codex-mirror-source.mjs +34 -0
- package/runtime/scripts/agent/host-agents.mjs +80 -0
- package/runtime/scripts/agent/launch-trust-policy.mjs +53 -0
- package/runtime/scripts/agent/trust-launch.mjs +191 -0
- package/runtime/scripts/agent/trust.mjs +523 -0
- package/runtime/scripts/api/fs/rename-over.mjs +21 -0
- package/runtime/scripts/api/fs/temp-path.mjs +8 -0
- package/runtime/scripts/api/fs/write-json-file.mjs +10 -0
- package/runtime/scripts/api/git/diff-names.mjs +8 -0
- package/runtime/scripts/api/git/head-time.mjs +10 -0
- package/runtime/scripts/api/git/index-lock.mjs +78 -0
- package/runtime/scripts/api/git/is-ancestor.mjs +5 -0
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/merge-base-query.mjs +5 -0
- package/runtime/scripts/api/git/merge-tree.mjs +5 -0
- package/runtime/scripts/api/git/rev-list.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse.mjs +13 -0
- package/runtime/scripts/api/git/status-query.mjs +5 -0
- package/runtime/scripts/api/git/worktree-list-query.mjs +5 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/npm/dist-tag.mjs +8 -0
- package/runtime/scripts/api/npm/lib.mjs +32 -0
- package/runtime/scripts/api/npm/pack-dry-run.mjs +9 -0
- package/runtime/scripts/api/npm/run-npm.mjs +6 -0
- package/runtime/scripts/api/npm/view.mjs +30 -0
- package/runtime/scripts/api/npm/whoami.mjs +8 -0
- package/runtime/scripts/api/process/hide-child-windows.mjs +31 -0
- package/runtime/scripts/api/process/kill-tree.mjs +22 -0
- package/runtime/scripts/api/process/lib.mjs +125 -0
- package/runtime/scripts/api/process/probe-guard-command.mjs +74 -0
- package/runtime/scripts/api/process/process-list.mjs +28 -0
- package/runtime/scripts/api/process/system-tool.mjs +37 -0
- package/runtime/scripts/gates/canon-digest.mjs +81 -0
- package/runtime/scripts/gates/canon-pins.mjs +16 -0
- package/runtime/scripts/gates/commit-proof.mjs +53 -0
- package/runtime/scripts/gates/gate-input.mjs +98 -0
- package/runtime/scripts/gates/gate.mjs +669 -0
- package/runtime/scripts/gates/grammar-dist.mjs +206 -0
- package/runtime/scripts/gates/runtime-package-selection.mjs +27 -0
- package/runtime/scripts/gates/test-world-run.mjs +254 -0
- package/runtime/scripts/gates/type-impact.mjs +277 -0
- package/runtime/scripts/gates/type-program.mjs +113 -0
- package/runtime/scripts/guards/assigned-command.mjs +12 -0
- package/runtime/scripts/guards/call-rights.mjs +47 -0
- package/runtime/scripts/guards/command-guard.mjs +468 -0
- package/runtime/scripts/guards/command-policy.mjs +342 -0
- package/runtime/scripts/guards/critic-reach.mjs +82 -0
- package/runtime/scripts/guards/deps-guard.mjs +150 -0
- package/runtime/scripts/guards/env-dump-verdict.mjs +89 -0
- package/runtime/scripts/guards/git-pathspec.mjs +225 -0
- package/runtime/scripts/guards/git-policy.mjs +355 -0
- package/runtime/scripts/guards/guards-root.mjs +27 -0
- package/runtime/scripts/guards/hook-install.mjs +343 -0
- package/runtime/scripts/guards/hook-io.mjs +76 -0
- package/runtime/scripts/guards/hook-verdict.mjs +12 -0
- package/runtime/scripts/guards/install-verdict.mjs +41 -0
- package/runtime/scripts/guards/kernel-seat.mjs +63 -0
- package/runtime/scripts/guards/launch-verdict.mjs +74 -0
- package/runtime/scripts/guards/op-starci.mjs +16 -0
- package/runtime/scripts/guards/op-write-scope.mjs +27 -0
- package/runtime/scripts/guards/orca-self-lifecycle.mjs +39 -0
- package/runtime/scripts/guards/process-kill-verdict.mjs +106 -0
- package/runtime/scripts/guards/protected-zone.mjs +56 -0
- package/runtime/scripts/guards/refusals.mjs +27 -0
- package/runtime/scripts/guards/release-definition.mjs +185 -0
- package/runtime/scripts/guards/release-push-gate.mjs +72 -0
- package/runtime/scripts/guards/release-record.mjs +58 -0
- package/runtime/scripts/guards/release-suite-mode.mjs +15 -0
- package/runtime/scripts/guards/rights.mjs +268 -0
- package/runtime/scripts/guards/seat-tools.mjs +47 -0
- package/runtime/scripts/guards/shell-commands.mjs +328 -0
- package/runtime/scripts/guards/supervisor-seat.mjs +80 -0
- package/runtime/scripts/guards/verdict-process.mjs +81 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +10 -10
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +1 -1
- package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +53 -0
- package/runtime/scripts/hfs/architecture/reachability.mjs +1 -1
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +17 -6
- package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +105 -0
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +38 -52
- package/runtime/scripts/hfs/architecture/supabase-keys.mjs +261 -0
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +49 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +1 -1
- package/runtime/scripts/hfs/check.mjs +6 -4
- package/runtime/scripts/hfs/coverage-scope.mjs +28 -1
- package/runtime/scripts/hfs/db-restamp-verb.mjs +54 -14
- package/runtime/scripts/hfs/declaration-shape.mjs +4 -4
- package/runtime/scripts/hfs/declaration-slots.mjs +14 -3
- package/runtime/scripts/hfs/manifest-shape.mjs +10 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +18 -6
- package/runtime/scripts/hfs/rules/a15-findings.mjs +4 -1
- package/runtime/scripts/hfs/rules/bypass-markers.mjs +22 -7
- package/runtime/scripts/hfs/rules/database-migrations.mjs +69 -14
- package/runtime/scripts/hfs/rules/database-queue-sources.mjs +9 -7
- package/runtime/scripts/hfs/rules/database-queues.mjs +68 -18
- package/runtime/scripts/hfs/rules/edition.mjs +56 -12
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -10
- package/runtime/scripts/hfs/rules/fe-stack.mjs +12 -7
- package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +75 -9
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks-swarm.mjs +15 -4
- package/runtime/scripts/hfs/rules/text-integrity.mjs +73 -0
- package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +74 -0
- package/runtime/scripts/hfs/runtime-rules/release-notes.mjs +44 -0
- package/runtime/scripts/hfs/sealed-members.mjs +3 -3
- package/runtime/scripts/hfs/slot-side-problems.mjs +3 -3
- package/runtime/scripts/hfs/slots.mjs +2 -1
- package/runtime/scripts/hfs/stack-deploy-config.mjs +22 -5
- package/runtime/scripts/hfs/stacks-gate.mjs +28 -7
- package/runtime/scripts/hfs/view.mjs +2 -1
- package/runtime/scripts/lib/app-data.mjs +11 -0
- package/runtime/scripts/lib/bypass-markers.mjs +272 -75
- package/runtime/scripts/lib/check-format.mjs +7 -0
- package/runtime/scripts/lib/cli-arg.mjs +120 -0
- package/runtime/scripts/lib/css-rules.mjs +220 -0
- package/runtime/scripts/lib/dom-tree.mjs +4 -0
- package/runtime/scripts/lib/escape.mjs +16 -0
- package/runtime/scripts/lib/event-items.mjs +22 -0
- package/runtime/scripts/lib/failure-code-template-errors.mjs +161 -0
- package/runtime/scripts/lib/git-dir.mjs +120 -0
- package/runtime/scripts/lib/grammar-vendor-parity.mjs +377 -0
- package/runtime/scripts/lib/guard-command.mjs +24 -0
- package/runtime/scripts/lib/hook-limits.mjs +5 -0
- package/runtime/scripts/lib/interrupt-cleanup.mjs +38 -0
- package/runtime/scripts/lib/js-lex.mjs +200 -0
- package/runtime/scripts/lib/json-formats.mjs +0 -1
- package/runtime/scripts/lib/language.mjs +94 -1
- package/runtime/scripts/lib/loosening-budgets.mjs +44 -0
- package/runtime/scripts/lib/loosening-config.mjs +361 -0
- package/runtime/scripts/lib/loosening-state.mjs +223 -0
- package/runtime/scripts/lib/menu-parts.mjs +11 -0
- package/runtime/scripts/lib/normalize.mjs +25 -0
- package/runtime/scripts/lib/op-gate.mjs +15 -0
- package/runtime/scripts/lib/redact.mjs +208 -0
- package/runtime/scripts/lib/runtime-location.mjs +21 -0
- package/runtime/scripts/lib/runtime-package-inventory.mjs +21 -0
- package/runtime/scripts/lib/runtime-package-manifests.mjs +84 -0
- package/runtime/scripts/lib/runtime-publication-tags.mjs +99 -0
- package/runtime/scripts/lib/seat-env.mjs +43 -0
- package/runtime/scripts/lib/secret-patterns.mjs +58 -9
- package/runtime/scripts/lib/shell-source.mjs +4 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -0
- package/runtime/scripts/lib/swarm-stack.mjs +2 -1
- package/runtime/scripts/lib/tar-files.mjs +39 -0
- package/runtime/scripts/lib/ts-analysis.mjs +10 -0
- package/runtime/scripts/lib/worktree-exclude.mjs +35 -0
- package/runtime/scripts/lib/worktree-record.mjs +14 -0
- package/runtime/scripts/machine/artifact-hold.mjs +52 -0
- package/runtime/scripts/machine/ask-receipts.mjs +113 -0
- package/runtime/scripts/machine/evidence-store.mjs +223 -0
- package/runtime/scripts/machine/home.mjs +216 -0
- package/runtime/scripts/machine/host-lock.mjs +184 -0
- package/runtime/scripts/machine/lock-recovery.mjs +55 -0
- package/runtime/scripts/machine/roles-contract.mjs +90 -0
- package/runtime/scripts/machine/runtime-change.mjs +20 -0
- package/runtime/scripts/machine/workflow-tree.mjs +122 -0
- package/runtime/scripts/reconciler/checklist-items.mjs +7 -0
- package/runtime/scripts/reconciler/guard-row.mjs +25 -0
- package/runtime/scripts/work/asset-slot.mjs +284 -0
- package/runtime/scripts/work/brand/brand-colour.mjs +125 -0
- package/runtime/scripts/work/brand/brand-tokens.mjs +167 -0
- package/runtime/scripts/work/brand/brand-value-source.mjs +106 -0
- package/runtime/scripts/work/brand/brand.mjs +892 -0
- package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +60 -0
- package/runtime/scripts/work/draw/draw-dna-findings.mjs +256 -0
- package/runtime/scripts/work/draw/draw-dna-selector.mjs +38 -0
- package/runtime/scripts/work/draw/draw-dna.mjs +483 -0
- package/runtime/scripts/work/draw/draw-layer.mjs +305 -0
- package/runtime/scripts/work/draw/draw-rationale-page.mjs +237 -0
- package/runtime/scripts/work/draw/draw-rationale-ref.mjs +14 -0
- package/runtime/scripts/work/draw/draw-rationale.mjs +353 -0
- package/runtime/scripts/work/draw/draw-source.mjs +412 -0
- package/runtime/scripts/work/draw/draw-taste.mjs +278 -0
- package/runtime/scripts/work/draw/draw-typecheck.mjs +72 -0
- package/runtime/scripts/work/draw/rationale-findings.mjs +205 -0
- package/runtime/scripts/work/draw-grammar.mjs +175 -0
- package/runtime/scripts/work/draw-render-page.mjs +188 -0
- package/runtime/scripts/work/draw-render.mjs +626 -0
- package/runtime/scripts/work/grammar-proposal.mjs +279 -0
- package/runtime/scripts/work/png.mjs +237 -0
- package/runtime/scripts/work/render-tools.mjs +64 -0
- package/runtime/scripts/work/work-io.mjs +119 -0
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-plan.mjs +40 -13
- package/scaffold/add-queue-lite.mjs +372 -0
- package/scaffold/add.mjs +5 -2
- package/scaffold/app.mjs +1 -2
- package/scaffold/edition-gate.mjs +11 -7
- package/scaffold/lite-exports.mjs +10 -0
- package/scaffold/pattern-file-slots.mjs +33 -0
- package/scaffold/test-world.mjs +1 -1
- package/scaffold/yaml-block-edit.mjs +117 -0
- package/src/main.mjs +5 -4
- package/stack/deploy.mjs +7 -3
- package/stack/post-deploy.mjs +48 -0
- package/sync/index.mjs +10 -0
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +1 -1
- package/templates/app/codegen-graphql.mjs.partial +59 -12
- package/templates/app/hooks/husky/pre-commit +2 -2
- package/templates/app/hooks/husky/pre-push +10 -3
- package/templates/app/package-scripts/redis-dev.json +3 -0
- package/templates/app/package-scripts-lite/package.json +1 -0
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +36 -8
- package/templates/app/skeleton/.starcistacks/dev/README.md +1 -1
- package/templates/app/skeleton/.starcistacks/production/README.md +10 -9
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/postgres-init.sh +21 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +1 -1
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +1 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +47 -5
- package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +5 -3
- package/templates/app/skeleton/README.md +2 -0
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +2 -2
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +1 -1
- package/templates/app/skeleton-lite/README.md +1 -1
- package/templates/be/inbox/platform/client-operation-outcomes.sql.tpl +18 -0
- package/templates/be/inbox/platform/event-bus/errors/event-bus.error.ts.tpl +16 -0
- package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +10 -0
- package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +1 -1
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +15 -3
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +11 -3
- package/templates/be/inbox/platform/event-bus/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/event-bus/messages/event-bus.messages.ts.tpl +7 -0
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +2 -2
- package/templates/be/inbox/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
- package/templates/be/inbox/platform/idempotency/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
- package/templates/be/inbox/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
- package/templates/be/inbox/platform/inbox/client-operation.policy.ts.tpl +17 -0
- package/templates/be/inbox/platform/inbox/errors/inbox.error.ts.tpl +22 -0
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +21 -0
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +334 -11
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +55 -4
- package/templates/be/inbox/platform/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +26 -0
- package/templates/be/inbox/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
- package/templates/be/inbox/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
- package/templates/be/inbox/platform/migration.sql.tpl +1 -0
- package/templates/be/inbox/service.spec.ts.tpl +4 -4
- package/templates/be/inbox/service.ts.tpl +3 -1
- package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -2
- package/templates/be/inbox/webhook-door.ts.tpl +3 -2
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +3 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +2 -2
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +16 -3
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +11 -3
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +139 -50
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +25 -2
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +139 -78
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +10 -6
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +262 -159
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +32 -5
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +5 -1
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -4
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +1 -0
- package/templates/be/patterns/idempotency/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
- package/templates/be/patterns/idempotency/platform/idempotency/index.ts.tpl +2 -0
- package/templates/be/patterns/idempotency/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.ts.tpl +17 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.spec.ts.tpl +66 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.ts.tpl +24 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.spec.ts.tpl +60 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.ts.tpl +12 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.spec.ts.tpl +49 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.ts.tpl +12 -0
- package/templates/be/patterns/idempotency/platform/inbox/effectful.decorators.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/errors/inbox.error.ts.tpl +22 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.decorators.ts.tpl +17 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module.ts.tpl +28 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.options.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.port.ts.tpl +44 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.service.spec.ts.tpl +388 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.service.ts.tpl +77 -0
- package/templates/be/patterns/idempotency/platform/inbox/index.ts.tpl +10 -0
- package/templates/be/patterns/idempotency/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +9 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/entities/inbox-claim.entity.ts.tpl +25 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +39 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1789800005000-create-inbox-claims.ts.tpl +21 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1792800000000-client-operation-outcomes.ts.tpl +21 -0
- package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
- package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +1 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +4 -3
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +2 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +20 -2
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +1 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +4 -0
- package/templates/be/patterns/jobs/platform-lite/jobs.index.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform-lite/jobs.sql.tpl +44 -0
- package/templates/be/patterns/jobs/platform-lite/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +203 -18
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +78 -22
- package/templates/be/patterns/queues/platform/errors/queue.error.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/messages/queue.messages.ts.tpl +13 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +1 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +4 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +2 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -4
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +14 -2
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +5 -2
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +72 -74
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +23 -8
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +29 -8
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +7 -3
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +2 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +4 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +2 -1
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.spec.ts.tpl +417 -0
- package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.ts.tpl +172 -0
- package/templates/be/patterns/queues/platform-lite/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform-lite/queue-outbox.sql.tpl +32 -0
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.spec.ts.tpl +148 -0
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.ts.tpl +51 -0
- package/templates/be/patterns/queues/platform-lite/queue-transport.port.ts.tpl +163 -0
- package/templates/be/patterns/queues/platform-lite/queue.config.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform-lite/queue.error.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform-lite/queue.index.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform-lite/queue.messages.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform-lite/queue.module.ts.tpl +74 -0
- package/templates/be/patterns/queues/platform-lite/queue.options.ts.tpl +38 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +2 -2
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +2 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +5 -1
- package/templates/be/platform/graphql/graphql.contracts.ts +2 -1
- package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +1 -1
- package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +6 -1
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -4
- package/templates/be/skeleton/apps/cli/src/compose/platform.ts +4 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +7 -4
- package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http/errors/http.error.ts +22 -0
- package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.spec.ts +235 -0
- package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.ts +85 -0
- package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.spec.ts +92 -0
- package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.ts +52 -0
- package/templates/be/skeleton/src/modules/platform/http/http.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http/http.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/http/http.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/http/http.port.ts +40 -0
- package/templates/be/skeleton/src/modules/platform/http/index.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http/messages/http.messages.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.port.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/ids/index.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.spec.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.spec.ts +59 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.ts +21 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.contracts.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.spec.ts +150 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.ts +42 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.port.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.service.spec.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.service.ts +28 -0
- package/templates/be/skeleton/src/modules/platform/logging/index.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +39 -6
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +15 -3
- package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +17 -2
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +13 -1
- package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +13 -3
- package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.spec.ts +69 -0
- package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/primitives/record.policy.spec.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/primitives/record.policy.ts +3 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +6 -1
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +14 -1
- package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
- package/templates/be/test-world/world/test-apps.options.ts.tpl +2 -0
- package/templates/be/test-world/world/test-world.config.ts.tpl +1 -0
- package/upgrade/check.mjs +12 -135
- package/upgrade/file-state.mjs +1 -16
- package/upgrade/index.mjs +34 -361
- package/runtime/scripts/lib/check-code-loosening.mjs +0 -128
- package/runtime/scripts/lib/gate-loosening.mjs +0 -292
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +0 -89
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +0 -16
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +0 -13
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +0 -3
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +0 -12
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +0 -7
- package/templates/be/skeleton-lite/apps/api/src/main.ts +0 -26
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +0 -12
- package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +0 -7
- package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +0 -5
- package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +0 -9
- package/templates/be/upgrade-full/apps/cli/src/main.ts +0 -29
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +0 -7
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +0 -23
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// type-impact.mjs - compiler project impact, prerequisite stamps and stale buildinfo cleanup of the code gate.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { byCodeUnit } from '../lib/list.mjs';
|
|
5
|
+
import { sha256, sha256File } from '../../engine/digest.mjs';
|
|
6
|
+
import { pathKey, posixPath } from '../lib/path-key.mjs';
|
|
7
|
+
import { walkFiles, isInside } from '../lib/walk.mjs';
|
|
8
|
+
import { locateDeclaration, openHfs } from '../hfs/slots.mjs';
|
|
9
|
+
import { typeScriptProjectReferencePath } from '../hfs/typescript-programs.mjs';
|
|
10
|
+
|
|
11
|
+
/** The existing gate's TypeScript input suffixes; JavaScript is not silently given a new checkJs contract. */
|
|
12
|
+
export const TS_SOURCE = /\.(?:[cm]?tsx?)$/;
|
|
13
|
+
const regular = (file) => {
|
|
14
|
+
const stat = fs.lstatSync(file);
|
|
15
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`type input is not a regular file: ${file}`);
|
|
16
|
+
return stat;
|
|
17
|
+
};
|
|
18
|
+
/** Only the canonical output slot proves generation; a source directory may have any basename. */
|
|
19
|
+
const generatedOutput = (root) => {
|
|
20
|
+
const hfs = fs.existsSync(locateDeclaration(root).file) ? openHfs({ repoRoot: root }) : null;
|
|
21
|
+
return (file) => ['repo.build-output', 'app.build-output'].includes(hfs?.slotOf(posixPath(path.relative(root, file)))?.id);
|
|
22
|
+
};
|
|
23
|
+
const sourceFiles = (dir, root = dir) => {
|
|
24
|
+
const generated = generatedOutput(root);
|
|
25
|
+
return walkFiles(dir, { sorted: true, exclude: (name, full) => name === 'node_modules' || name === '.git' || generated(full) }).sort(byCodeUnit);
|
|
26
|
+
};
|
|
27
|
+
// Prerequisite stamps retain their cache/custody policy; it never decides compiler ownership.
|
|
28
|
+
const stampDirectories = new Set(['node_modules', '.git', 'dist', 'coverage', '.next', '.turbo', '.starci', '.starciwork', '.starcistacks', '.artifacts']);
|
|
29
|
+
const stampFiles = (dir) => walkFiles(dir, { sorted: true, exclude: (name) => stampDirectories.has(name) || name.endsWith('.tsbuildinfo') }).sort(byCodeUnit);
|
|
30
|
+
|
|
31
|
+
/** Read bounded compiler membership/references; config errors throw, while no-input diagnostics remain owed by compilation. */
|
|
32
|
+
export function readTypeProject(ts, root, project, sys) {
|
|
33
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(path.join(root, project), {}, sys);
|
|
34
|
+
if (!parsed) throw new Error(`${project} could not be read`);
|
|
35
|
+
const errors = parsed.errors.filter((diagnostic) => diagnostic.code !== 18003);
|
|
36
|
+
if (errors.length) throw new Error(`${project}: ${ts.flattenDiagnosticMessageText(errors[0].messageText, '\n')}`);
|
|
37
|
+
return { parsed, fileNames: parsed.fileNames, references: (parsed.projectReferences ?? []).map((reference) => typeScriptProjectReferencePath(ts, reference)) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Remove stale worktree *.tsbuildinfo files outside node_modules and .git, returning their root-relative paths. */
|
|
41
|
+
export function removeStaleBuildInfo(root) {
|
|
42
|
+
const files = walkFiles(root, { filter: (name) => name.endsWith('.tsbuildinfo'), exclude: (name, full, entry) => entry.isDirectory() && (name === 'node_modules' || name === '.git'), ignoreReadErrors: true });
|
|
43
|
+
for (const file of files) fs.rmSync(file, { force: true });
|
|
44
|
+
return files.map((file) => posixPath(path.relative(root, file)));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Exact current file bytes, path membership and missing scopes; an M-to-M edit never retains a stamp. */
|
|
48
|
+
export function inputStamp(root, paths) {
|
|
49
|
+
const top = path.resolve(root), rows = ['type-input-bytes-v1\0'];
|
|
50
|
+
for (const scope of [...new Set(paths.map(posixPath))].sort(byCodeUnit)) {
|
|
51
|
+
const at = path.resolve(top, scope);
|
|
52
|
+
if (!isInside(top, at)) throw new Error(`type input scope is outside the app: ${scope}`);
|
|
53
|
+
rows.push(`scope\0${scope}\0`);
|
|
54
|
+
if (!fs.existsSync(at)) { rows.push('missing\0'); continue; }
|
|
55
|
+
const stat = fs.lstatSync(at);
|
|
56
|
+
if (stat.isSymbolicLink()) throw new Error(`type input scope is linked: ${scope}`);
|
|
57
|
+
for (const file of stat.isDirectory() ? stampFiles(at) : [at]) {
|
|
58
|
+
const item = regular(file);
|
|
59
|
+
rows.push(`file\0${posixPath(path.relative(top, file))}\0${item.size}\0${sha256File(file)}\0`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return sha256(rows.join(''));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Whether a workspace exposes built declarations or JavaScript beneath dist/. */
|
|
66
|
+
export const exposesDist = (manifest) => /(?:^|["/])dist\//.test(JSON.stringify([manifest?.exports ?? null, manifest?.main ?? null, manifest?.types ?? null, manifest?.module ?? null]));
|
|
67
|
+
|
|
68
|
+
/** Declared workspace directories; the gate builds only its existing dist-exposing workspace owners. */
|
|
69
|
+
export function workspaceDirs(root, manifest) {
|
|
70
|
+
const globs = Array.isArray(manifest?.workspaces) ? manifest.workspaces : manifest?.workspaces?.packages ?? [];
|
|
71
|
+
return globs.flatMap((glob) => {
|
|
72
|
+
const clean = posixPath(glob).replace(/\/$/, '');
|
|
73
|
+
if (!clean.endsWith('/*')) return fs.existsSync(path.join(root, clean, 'package.json')) ? [clean] : [];
|
|
74
|
+
const parent = clean.slice(0, -2);
|
|
75
|
+
let entries = [];
|
|
76
|
+
try { entries = fs.readdirSync(path.join(root, parent), { withFileTypes: true }); } catch (error) {
|
|
77
|
+
if (error.code === 'ENOENT') return [];
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
return entries.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(root, parent, entry.name, 'package.json'))).map((entry) => `${parent}/${entry.name}`);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const manifestAt = (dir) => {
|
|
85
|
+
const file = path.join(dir, 'package.json');
|
|
86
|
+
return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : null;
|
|
87
|
+
};
|
|
88
|
+
const nearestProject = (root, file) => {
|
|
89
|
+
for (let dir = path.dirname(path.resolve(root, file)); ; dir = path.dirname(dir)) {
|
|
90
|
+
const config = path.join(dir, 'tsconfig.json');
|
|
91
|
+
if (fs.existsSync(config)) return posixPath(path.relative(root, config));
|
|
92
|
+
if (pathKey(dir) === pathKey(root) || path.dirname(dir) === dir) return null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The compiler programs of the sides that read `file` across sides (hfs.json `sides.<side>.reads`, such as the generated
|
|
97
|
+
* `supabase/types/` of a lite app): such a file has no tsconfig of its own and is typechecked by the programs that import it,
|
|
98
|
+
* so a change to it affects every tsconfig.json of each reading side. Empty when no side reads it or there is no hfs.json.
|
|
99
|
+
*/
|
|
100
|
+
const readerProjects = (root, file) => {
|
|
101
|
+
let declaration;
|
|
102
|
+
try { declaration = JSON.parse(fs.readFileSync(path.join(root, 'hfs.json'), 'utf8')); } catch { return []; }
|
|
103
|
+
const sides = Object.entries(declaration?.sides ?? {}).filter(([, side]) => (Array.isArray(side?.reads) ? side.reads : []).some((prefix) => typeof prefix === 'string' && file.startsWith(prefix)));
|
|
104
|
+
return sides.flatMap(([side]) => {
|
|
105
|
+
const dir = path.join(root, side);
|
|
106
|
+
return fs.existsSync(dir) ? sourceFiles(dir, root).filter((config) => path.basename(config) === 'tsconfig.json').map((config) => posixPath(path.relative(root, config))) : [];
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
const sharedImpact = (file) => /(?:^|\/)(?:tsconfig[^/]*\.json|package\.json|package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|turbo\.json|hfs\.json)$/.test(file) || file.startsWith('scripts/');
|
|
110
|
+
|
|
111
|
+
const addTo = (projects, project, source = null) => {
|
|
112
|
+
if (!projects.has(project)) projects.set(project, []);
|
|
113
|
+
if (source !== null) projects.get(project).push(source);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** Compiler-resolved configs, including references to custom-named configs, bounded to this app. */
|
|
117
|
+
const projectGraph = (root, configs, readProject) => {
|
|
118
|
+
const generated = generatedOutput(root);
|
|
119
|
+
const records = new Map(), active = new Set();
|
|
120
|
+
const visit = (config) => {
|
|
121
|
+
const absolute = path.resolve(root, config), project = posixPath(path.relative(root, absolute));
|
|
122
|
+
if (!isInside(root, absolute)) throw new Error(`GATE_TSC_OWNER_UNPROVEN project reference is outside the app: ${project}`);
|
|
123
|
+
if (active.has(project)) throw new Error(`GATE_TSC_OWNER_UNPROVEN circular project reference: ${project}`);
|
|
124
|
+
if (records.has(project)) return;
|
|
125
|
+
active.add(project);
|
|
126
|
+
const parsed = readProject(project);
|
|
127
|
+
const names = new Map(parsed.fileNames.map((file) => [pathKey(file), file])), files = new Set(names.keys());
|
|
128
|
+
// Missing, invalid and out-of-bound declared inputs remain obligations, even in an output slot.
|
|
129
|
+
const roots = [...files].filter((file) => !isInside(root, file) || !fs.existsSync(file) || !fs.lstatSync(file).isFile() || !generated(file));
|
|
130
|
+
const record = { project, dir: path.dirname(absolute), files, names, roots, references: [], parsed: parsed.parsed };
|
|
131
|
+
records.set(project, record);
|
|
132
|
+
for (const reference of parsed.references) {
|
|
133
|
+
const target = posixPath(path.relative(root, path.resolve(root, reference)));
|
|
134
|
+
record.references.push(target);
|
|
135
|
+
visit(target);
|
|
136
|
+
}
|
|
137
|
+
active.delete(project);
|
|
138
|
+
};
|
|
139
|
+
for (const config of configs) visit(config);
|
|
140
|
+
return records;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const ownsRoot = (record, file, records) => ![...records.values()].some((other) => other.files.has(file)
|
|
144
|
+
&& isInside(record.dir, other.dir, { includeSelf: false }));
|
|
145
|
+
|
|
146
|
+
/** An ancestor that contributes no roots of its own delegates to its nested or referenced programs. */
|
|
147
|
+
const aggregateProject = (record, records) => {
|
|
148
|
+
if (!record.roots.length) return record.references.length > 0 || [...records.values()].some((other) => other !== record && isInside(record.dir, other.dir, { includeSelf: false }));
|
|
149
|
+
return record.roots.every((file) => !ownsRoot(record, file, records));
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A config keeps its diagnostics and every root no deeper compiler includes. Residual roots use the
|
|
154
|
+
* nearest nested config's options, so typed app configuration resolves the same aliases as app source.
|
|
155
|
+
* Discovery uses the supplied sys, including the gate's read-only base overlay.
|
|
156
|
+
*/
|
|
157
|
+
export function readTypePrograms(ts, root, project, sys) {
|
|
158
|
+
const directory = path.dirname(path.resolve(root, project)), generated = generatedOutput(root);
|
|
159
|
+
const configs = sys.readDirectory(directory, ['.json'], ['**/node_modules/**', '**/.git/**'])
|
|
160
|
+
.filter((file) => path.basename(file) === 'tsconfig.json' && !generated(file));
|
|
161
|
+
const records = projectGraph(root, [project, ...configs], (config) => readTypeProject(ts, root, config, sys));
|
|
162
|
+
const record = records.get(posixPath(project)), groups = new Map();
|
|
163
|
+
const nested = [...records.values()].some((other) => isInside(record.dir, other.dir, { includeSelf: false }));
|
|
164
|
+
// Leaf programs retain their declared generated inputs (for example Next ambient declarations and route types).
|
|
165
|
+
const roots = nested ? record.roots : [...record.files];
|
|
166
|
+
for (const file of roots.filter((file) => ownsRoot(record, file, records))) {
|
|
167
|
+
const owners = [...records.values()].filter((other) => isInside(record.dir, other.dir) && isInside(other.dir, file));
|
|
168
|
+
const owner = owners.sort((a, b) => b.dir.length - a.dir.length || byCodeUnit(a.project, b.project))[0] ?? record;
|
|
169
|
+
if (!groups.has(owner.project)) groups.set(owner.project, { project: owner.project, parsed: owner.parsed, rootNames: [] });
|
|
170
|
+
groups.get(owner.project).rootNames.push(record.names.get(file));
|
|
171
|
+
}
|
|
172
|
+
// Ambient declarations are part of the chosen app's compiler context, including generated Next declarations.
|
|
173
|
+
for (const group of groups.values()) group.rootNames = [...new Map([...group.rootNames, ...group.parsed.fileNames.filter((file) => /\.d\.[cm]?ts$/.test(file))].map((file) => [pathKey(file), file])).values()];
|
|
174
|
+
return { parsed: record.parsed, groups: [...groups.values()] };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The deepest compiler scopes, plus referenced external roots; an excluded selected file still owes coverage. */
|
|
178
|
+
const sourceOwners = (file, records) => {
|
|
179
|
+
const candidates = [...records.values()].filter((record) => !aggregateProject(record, records));
|
|
180
|
+
const included = candidates.filter((record) => record.files.has(pathKey(file)));
|
|
181
|
+
const scopes = (included.length ? included : candidates).filter((record) => isInside(record.dir, file));
|
|
182
|
+
const depth = Math.max(...scopes.map((record) => record.dir.length));
|
|
183
|
+
const nearest = scopes.filter((record) => record.dir.length === depth);
|
|
184
|
+
const external = included.filter((record) => !isInside(record.dir, file));
|
|
185
|
+
return [...nearest, ...external];
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/** Select real programs through an aggregate/reference graph; each selected source remains a required program input. */
|
|
189
|
+
const addProject = (project, source, { records, projects, errors }, seen = new Set()) => {
|
|
190
|
+
if (seen.has(project)) return;
|
|
191
|
+
seen.add(project);
|
|
192
|
+
const record = records.get(project);
|
|
193
|
+
if (!record) { errors.push(`GATE_TSC_OWNER_UNPROVEN compiler project was not resolved: ${project}`); return; }
|
|
194
|
+
addTo(projects, project, source);
|
|
195
|
+
for (const reference of record.references) addProject(reference, null, { records, projects, errors }, seen);
|
|
196
|
+
if (aggregateProject(record, records)) {
|
|
197
|
+
for (const other of records.values()) {
|
|
198
|
+
if (isInside(record.dir, other.dir, { includeSelf: false })) addProject(other.project, null, { records, projects, errors }, seen);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/** Whether one selected file is a dist-workspace member after its owner checks, recording errors and the project. */
|
|
204
|
+
const scanSelected = (root, file, state) => {
|
|
205
|
+
const { removed, workspaces, records, errors } = state;
|
|
206
|
+
const at = path.resolve(root, file);
|
|
207
|
+
if (!isInside(root, at)) { errors.push(`GATE_TSC_OWNER_UNPROVEN selected input is outside the app: ${file}`); return false; }
|
|
208
|
+
if (!TS_SOURCE.test(file)) return false;
|
|
209
|
+
const exists = fs.existsSync(at), owners = sourceOwners(at, records);
|
|
210
|
+
if (!exists && !removed.has(file)) { errors.push(`GATE_TSC_SOURCE_MISSING selected TypeScript does not exist: ${file}`); return false; }
|
|
211
|
+
if (!owners.length && exists) {
|
|
212
|
+
const readers = readerProjects(root, file);
|
|
213
|
+
if (!readers.length) { errors.push(`GATE_TSC_CONFIG_MISSING no tsconfig.json owns selected TypeScript ${file}`); return false; }
|
|
214
|
+
for (const reader of readers) addProject(reader, null, state);
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
for (const owner of owners) addProject(owner.project, exists ? file : null, state);
|
|
218
|
+
return workspaces.some((dir) => file.startsWith(`${dir}/`));
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/** Whether a removed tsconfig still has TypeScript without compiler-proven ownership. */
|
|
222
|
+
const strandedConfig = (root, file, records) => {
|
|
223
|
+
const dir = path.dirname(path.join(root, file));
|
|
224
|
+
return fs.existsSync(dir) && sourceFiles(dir, root).filter((source) => TS_SOURCE.test(source)).some((source) => {
|
|
225
|
+
return !sourceOwners(source, records).some((owner) => owner.files.has(pathKey(source)));
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/** The whole-tree scan a broad impact triggers: every discovered program, stranded removals, and the no-program case. */
|
|
230
|
+
const broadScan = (root, selected, inventory, state) => {
|
|
231
|
+
const { projects, errors, records } = state;
|
|
232
|
+
for (const record of records.values()) addProject(record.project, null, state);
|
|
233
|
+
for (const file of selected.filter((file) => /(?:^|\/)tsconfig\.json$/.test(file) && !fs.existsSync(path.join(root, file)))) {
|
|
234
|
+
if (strandedConfig(root, file, records)) errors.push(`GATE_TSC_CONFIG_MISSING removed tsconfig.json still has TypeScript without a retained compiler owner: ${file}`);
|
|
235
|
+
}
|
|
236
|
+
if (!projects.size && inventory.some((file) => TS_SOURCE.test(file))) errors.push('GATE_TSC_CONFIG_MISSING configuration/install impact has TypeScript inputs but no tsconfig.json program');
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Selected TypeScript keeps its nearest compiler scopes and projects that include it from outside
|
|
241
|
+
* their own directories. Configuration, install and codegen changes,
|
|
242
|
+
* or a changed dist workspace, affect the actual discovered programs, including typed consumers.
|
|
243
|
+
* Only selected existing TypeScript becomes a required program input; generated assets elsewhere
|
|
244
|
+
* are not turned into source obligations. readProject supplies compiler-resolved fileNames and absolute
|
|
245
|
+
* reference config paths through the gate's bounded host. An unproved selected owner is an error, never a skip.
|
|
246
|
+
*/
|
|
247
|
+
export function typeImpact(root, files, { deleted = [], readProject } = {}) {
|
|
248
|
+
root = path.resolve(root);
|
|
249
|
+
const projects = new Map(), errors = [], removed = new Set(deleted.map((file) => posixPath(file)));
|
|
250
|
+
const selected = [...new Set(files.map((file) => posixPath(file)))];
|
|
251
|
+
const workspaces = workspaceDirs(root, manifestAt(root)).filter((dir) => exposesDist(manifestAt(path.join(root, dir))));
|
|
252
|
+
let broad = selected.some(sharedImpact) || selected.some((file) => TS_SOURCE.test(file) && workspaces.some((dir) => file.startsWith(`${dir}/`)));
|
|
253
|
+
if (!broad && !selected.some((file) => TS_SOURCE.test(file))) return { projects: [], required: projects, errors };
|
|
254
|
+
const inventory = sourceFiles(root), configs = inventory.filter((file) => path.basename(file) === 'tsconfig.json');
|
|
255
|
+
// A source-only check must not load an unrelated side's compiler or broken config. Start at its
|
|
256
|
+
// nearest scopes (or declared readers), then discover their descendants and reference closure.
|
|
257
|
+
const seeds = broad ? configs : selected.filter((file) => TS_SOURCE.test(file)).flatMap((file) => {
|
|
258
|
+
const nearest = nearestProject(root, file);
|
|
259
|
+
return nearest ? [path.join(root, nearest)] : readerProjects(root, file).map((project) => path.join(root, project));
|
|
260
|
+
});
|
|
261
|
+
// Ancestors contribute their reference closure, not all their unrelated descendant projects.
|
|
262
|
+
const ancestors = selected.filter((file) => TS_SOURCE.test(file)).flatMap((file) => configs.filter((config) => isInside(path.dirname(config), path.resolve(root, file))));
|
|
263
|
+
const records = projectGraph(root, [...new Set([...ancestors, ...configs.filter((config) => seeds.some((seed) => isInside(path.dirname(seed), config)))])], readProject);
|
|
264
|
+
const state = { removed, workspaces, projects, errors, records };
|
|
265
|
+
for (const file of selected) {
|
|
266
|
+
if (scanSelected(root, file, state)) broad = true;
|
|
267
|
+
}
|
|
268
|
+
if (broad) broadScan(root, selected, inventory, state);
|
|
269
|
+
return { projects: [...projects.keys()].sort(byCodeUnit), required: projects, errors };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Selected source must occur in the compiler's actual program, including an imported excluded root. */
|
|
273
|
+
export function requireTypeCoverage(root, project, required, sourceFilesOfProgram) {
|
|
274
|
+
const measured = new Set(sourceFilesOfProgram.map((file) => pathKey(file.fileName)));
|
|
275
|
+
const missing = required.filter((file) => !measured.has(pathKey(path.resolve(root, file))));
|
|
276
|
+
if (missing.length) throw new Error(`GATE_TSC_FILE_EXCLUDED ${project} did not typecheck selected source: ${missing.join(', ')}`);
|
|
277
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// type-program.mjs - bounded compiler programs, residual-root projections and base overlays for the code gate.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { sha256 } from '../../engine/digest.mjs';
|
|
5
|
+
import { pathKey, posixPath } from '../lib/path-key.mjs';
|
|
6
|
+
import { readTypePrograms, requireTypeCoverage } from './type-impact.mjs';
|
|
7
|
+
|
|
8
|
+
const TSC_BOUND = 'bounded-residual-v2';
|
|
9
|
+
/** A diagnostic as {path, line, code, message, key}; the key drops the position and the absolute root. */
|
|
10
|
+
function tscFinding(ts, diagnostic, root) {
|
|
11
|
+
const rootPaths = [path.resolve(root), posixPath(path.resolve(root))];
|
|
12
|
+
const scrub = (text) => rootPaths.reduce((acc, p) => acc.split(p).join('<ROOT>'), text);
|
|
13
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n').split('\n')[0];
|
|
14
|
+
const file = diagnostic.file ? posixPath(path.relative(root, diagnostic.file.fileName)) : null;
|
|
15
|
+
const line = diagnostic.file && diagnostic.start !== undefined ? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 : null;
|
|
16
|
+
return { engine: 'tsc', rule: `TS${diagnostic.code}`, path: file, line, message, key: `${file ?? '-'}|TS${diagnostic.code}|${scrub(message).replace(/\s+/g, ' ').trim()}` };
|
|
17
|
+
}
|
|
18
|
+
const errorsOf = (ts, diagnostics, root) => diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error).map((d) => tscFinding(ts, d, root));
|
|
19
|
+
const programDiagnostics = (program) => [...program.getConfigFileParsingDiagnostics(), ...program.getOptionsDiagnostics(), ...program.getGlobalDiagnostics(), ...program.getSyntacticDiagnostics(), ...program.getSemanticDiagnostics()];
|
|
20
|
+
const NO_OUTPUT = { noEmit: true, composite: false, declaration: false, declarationMap: false, emitDeclarationOnly: false, sourceMap: false };
|
|
21
|
+
|
|
22
|
+
/** Keep config/reference errors on the original roots; TypeScript omits some reference checks on zero-root programs. */
|
|
23
|
+
function configurationDiagnostics(ts, parsed, sys) {
|
|
24
|
+
const options = { ...parsed.options, ...NO_OUTPUT, incremental: false, preserveSymlinks: true };
|
|
25
|
+
const program = ts.createProgram({ rootNames: parsed.fileNames, options, projectReferences: parsed.projectReferences, host: boundHost(ts, ts.createCompilerHost(options), sys) });
|
|
26
|
+
return [...ts.getConfigFileParsingDiagnostics(parsed), ...program.getOptionsDiagnostics(), ...program.getGlobalDiagnostics()];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Deduplicate repeated program context, retaining distinct positions for the base comparison's diagnostic multiset.
|
|
30
|
+
const uniqueFindings = (ts, diagnostics, root) => errorsOf(ts, ts.sortAndDeduplicateDiagnostics(diagnostics), root);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `base` (a ts.sys shape) answering only for paths inside `bound`, TypeScript's lib directory and the `allow`ed files; realpath
|
|
34
|
+
* is the identity. Every program of the gate reads through one, so nothing above the app root satisfies an import.
|
|
35
|
+
*/
|
|
36
|
+
export function boundedSys(ts, bound, base = ts.sys, allow = []) {
|
|
37
|
+
const roots = [pathKey(bound), pathKey(path.dirname(ts.getDefaultLibFilePath({})))];
|
|
38
|
+
const extra = new Set(allow.map((p) => pathKey(p)));
|
|
39
|
+
const inside = (p) => { const key = pathKey(p); return extra.has(key) || roots.some((r) => key === r || key.startsWith(`${r}/`)); };
|
|
40
|
+
return { ...base,
|
|
41
|
+
fileExists: (p) => inside(p) && base.fileExists(p),
|
|
42
|
+
directoryExists: (p) => inside(p) && base.directoryExists(p),
|
|
43
|
+
readFile: (p, encoding) => (inside(p) ? base.readFile(p, encoding) : undefined),
|
|
44
|
+
getDirectories: (p) => (inside(p) ? base.getDirectories(p) : []),
|
|
45
|
+
readDirectory: (dir, ext, exclude, include, depth) => (inside(dir) ? base.readDirectory(dir, ext, exclude, include, depth).filter(inside) : []),
|
|
46
|
+
realpath: (p) => p,
|
|
47
|
+
onUnRecoverableConfigFileDiagnostic: () => {} };
|
|
48
|
+
}
|
|
49
|
+
/** The compiler host of `host` rewired onto a bounded sys: every lookup, and every source file, goes through it. */
|
|
50
|
+
function boundHost(ts, host, sys) {
|
|
51
|
+
return Object.assign(host, {
|
|
52
|
+
fileExists: sys.fileExists, directoryExists: sys.directoryExists, readFile: sys.readFile, getDirectories: sys.getDirectories, realpath: sys.realpath,
|
|
53
|
+
getSourceFile: (fileName, languageVersion) => { const text = sys.readFile(fileName); return text === undefined ? undefined : ts.createSourceFile(fileName, text, languageVersion); },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The head of one project, bounded to `bound`: original config diagnostics plus incremental residual groups. Each group's
|
|
58
|
+
* buildinfo lives in the worktree's git dir (the one outside-bound file its host may read). The incremental host's versioned
|
|
59
|
+
* getSourceFile reads through the same bound.
|
|
60
|
+
*/
|
|
61
|
+
export function headTsc(ts, root, project, { bound, cache, required = [] }) {
|
|
62
|
+
const sys = boundedSys(ts, bound), measured = [];
|
|
63
|
+
const { parsed, groups } = readTypePrograms(ts, root, project, sys);
|
|
64
|
+
const diagnostics = configurationDiagnostics(ts, parsed, sys);
|
|
65
|
+
for (const group of groups) {
|
|
66
|
+
const buildInfo = path.join(cache.worktree, 'tsbuildinfo', `${sha256([TSC_BOUND, project, group.project, ts.version, JSON.stringify(group.rootNames), sys.readFile(path.resolve(root, group.project))].join('\0')).slice(0, 24)}.tsbuildinfo`);
|
|
67
|
+
fs.mkdirSync(path.dirname(buildInfo), { recursive: true });
|
|
68
|
+
const hostSys = boundedSys(ts, bound, ts.sys, [buildInfo]);
|
|
69
|
+
const options = { ...group.parsed.options, ...NO_OUTPUT, incremental: true, tsBuildInfoFile: buildInfo, preserveSymlinks: true };
|
|
70
|
+
const program = ts.createIncrementalProgram({ rootNames: group.rootNames, options, projectReferences: group.parsed.projectReferences, host: ts.createIncrementalCompilerHost(options, hostSys) });
|
|
71
|
+
measured.push(...program.getProgram().getSourceFiles());
|
|
72
|
+
diagnostics.push(...programDiagnostics(program));
|
|
73
|
+
program.emit();
|
|
74
|
+
}
|
|
75
|
+
requireTypeCoverage(root, project, required, measured);
|
|
76
|
+
return uniqueFindings(ts, diagnostics, root);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The base program of one project, read-only: a host whose files are the working tree's except every path the delta changed,
|
|
80
|
+
* which reads its base blob (an added or renamed-to file does not exist, a deleted or renamed-from one comes back). A base
|
|
81
|
+
* finding on a rename source is keyed at its new path. Bounded to `bound` like the head. Recomputed only when head has errors;
|
|
82
|
+
* the working install and generated inputs are not proved by a shared (base, project) diagnostic cache.
|
|
83
|
+
*/
|
|
84
|
+
export function baseTsc(ts, root, project, { bound, delta, readBase }) {
|
|
85
|
+
const abs = (rel) => posixPath(path.resolve(root, rel));
|
|
86
|
+
const overlay = new Map([...delta.changed, ...delta.deleted].map((rel) => [abs(rel), rel]));
|
|
87
|
+
for (const rel of delta.added) overlay.set(abs(rel), rel);
|
|
88
|
+
const absent = (rel) => delta.added.has(rel) || delta.renamed.has(rel);
|
|
89
|
+
const read = (fileName) => {
|
|
90
|
+
const rel = overlay.get(posixPath(path.resolve(fileName)));
|
|
91
|
+
if (rel === undefined) return ts.sys.readFile(fileName);
|
|
92
|
+
return absent(rel) ? undefined : readBase(rel) ?? undefined;
|
|
93
|
+
};
|
|
94
|
+
const exists = (fileName) => { const rel = overlay.get(posixPath(path.resolve(fileName))); return rel === undefined ? ts.sys.fileExists(fileName) : !absent(rel) && readBase(rel) !== null; };
|
|
95
|
+
const deletedInputs = [...delta.deleted].map((rel) => path.resolve(root, rel));
|
|
96
|
+
const sys = boundedSys(ts, bound, { ...ts.sys, readFile: read, fileExists: exists,
|
|
97
|
+
readDirectory: (dir, ext, exclude, include, depth) => [...ts.sys.readDirectory(dir, ext, exclude, include, depth).filter((f) => exists(f)),
|
|
98
|
+
...deletedInputs.filter((f) => (!ext || ext.some((suffix) => f.endsWith(suffix))) && posixPath(f).startsWith(`${posixPath(path.resolve(dir))}/`))] });
|
|
99
|
+
// A newly added project has no base program to compare, rather than an unreadable required base config.
|
|
100
|
+
if (!sys.fileExists(path.resolve(root, project))) return [];
|
|
101
|
+
const { parsed, groups } = readTypePrograms(ts, root, project, sys);
|
|
102
|
+
const diagnostics = configurationDiagnostics(ts, parsed, sys);
|
|
103
|
+
for (const group of groups) {
|
|
104
|
+
const options = { ...group.parsed.options, ...NO_OUTPUT, incremental: false, preserveSymlinks: true };
|
|
105
|
+
const host = boundHost(ts, ts.createCompilerHost(options), sys);
|
|
106
|
+
const program = ts.createProgram({ rootNames: group.rootNames, options, projectReferences: group.parsed.projectReferences, host });
|
|
107
|
+
diagnostics.push(...programDiagnostics(program));
|
|
108
|
+
}
|
|
109
|
+
const movedTo = new Map([...delta.renamed].map(([to, from]) => [from, to]));
|
|
110
|
+
const moved = (finding) => (finding.path && movedTo.has(finding.path)
|
|
111
|
+
? { ...finding, path: movedTo.get(finding.path), key: `${movedTo.get(finding.path)}${finding.key.slice(finding.path.length)}` } : finding);
|
|
112
|
+
return uniqueFindings(ts, diagnostics, root).map(moved);
|
|
113
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// assigned-command.mjs - the command a parsed simple command really runs. PowerShell's `$p = Get-Process ...` and
|
|
2
|
+
// `$h = [Environment]::GetEnvironmentVariables()` parse as a command named '' (or '=get-process') whose arguments start
|
|
3
|
+
// with '=': the assigned command is the program, and the assignment words drop.
|
|
4
|
+
export function assignedCommand(c) {
|
|
5
|
+
const w = [c.program, ...c.args];
|
|
6
|
+
let at = 0;
|
|
7
|
+
while (at < w.length && (w[at] === '' || w[at] === '=')) at += 1;
|
|
8
|
+
if (at === 0 && !String(w[0]).startsWith('=')) return c;
|
|
9
|
+
const program = String(w[at] ?? '').replace(/^=/, '');
|
|
10
|
+
// Native programs are lowercased by the parser; a [Type]::Method word keeps its case for the matcher.
|
|
11
|
+
return { ...c, program: program.startsWith('[') ? program : program.toLowerCase(), args: w.slice(at + 1) };
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { findInOrder } from '../lib/in-order.mjs';
|
|
2
|
+
import { criticWriteVerdict } from './critic-reach.mjs';
|
|
3
|
+
import { fileWriteVerdict, redirectTargetsOf, runtimeRootOf, writeTargetsOf } from './rights.mjs';
|
|
4
|
+
import { policyVerdict } from './command-policy.mjs';
|
|
5
|
+
import { kernelSeatCallVerdict } from './kernel-seat.mjs';
|
|
6
|
+
|
|
7
|
+
// scripts/guards/call-rights.mjs — the rights refusals of one shell or file call: the file zones, the Kernel seat, the command policy.
|
|
8
|
+
/** The refusal of one file write for the rights role, or null: the zone declaration loads only for a role that can be refused. */
|
|
9
|
+
export async function fileRightsVerdict({ role, filePath, tool = 'Edit', edit = null, guard = null, shell = false }) {
|
|
10
|
+
if (role === 'critic') return criticWriteVerdict({ filePath, guard, tool });
|
|
11
|
+
if ((role !== 'supervisor' && role !== 'op') || !runtimeRootOf(filePath)) return null;
|
|
12
|
+
let zone = { runtimeRoot: runtimeRootOf(filePath) };
|
|
13
|
+
if (role === 'supervisor') {
|
|
14
|
+
const pz = await import('./protected-zone.mjs');
|
|
15
|
+
zone = { ...pz.zoneOfPath(filePath), catalogNames: pz.catalogNames };
|
|
16
|
+
}
|
|
17
|
+
return fileWriteVerdict({ role, filePath, tool, edit, guard, zone, shell });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The rights refusal of one parsed shell call: its commands (policyVerdict) and the files they write (fileRightsVerdict). */
|
|
21
|
+
export const policyToolVerdict = (command, verdict) => {
|
|
22
|
+
const whole = [command.word ?? command.program, ...command.args].join(' ');
|
|
23
|
+
return { tool: command.program, ...verdict, command: verdict.command === whole ? command.args.join(' ').slice(0, 200) : verdict.command };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export async function rightsOfCall({ commands, command, cwd, ctx, guard }) {
|
|
27
|
+
if (!ctx.role) return null;
|
|
28
|
+
// The file rights are the more specific refusal (a write into the protected zone, an op writing the runtime checkout): they come before the generic command policy.
|
|
29
|
+
if (ctx.role === 'supervisor' || ctx.role === 'op' || ctx.role === 'critic') {
|
|
30
|
+
// The Critic's writer programs are judged whole by the command policy (their content words are not paths): only its redirections are targets here.
|
|
31
|
+
const targets = [...(ctx.role === 'critic' ? [] : commands.flatMap((c) => writeTargetsOf(c))), ...redirectTargetsOf(command, cwd)];
|
|
32
|
+
let refusal = null;
|
|
33
|
+
await findInOrder(targets, async (filePath) => {
|
|
34
|
+
const v = await fileRightsVerdict({ role: ctx.role, filePath, tool: 'shell', guard, shell: true });
|
|
35
|
+
if (v) refusal = { tool: 'shell', ...v };
|
|
36
|
+
return Boolean(v);
|
|
37
|
+
});
|
|
38
|
+
if (refusal) return refusal;
|
|
39
|
+
}
|
|
40
|
+
const seat = kernelSeatCallVerdict({ role: ctx.role, command, cwd });
|
|
41
|
+
if (seat) return seat;
|
|
42
|
+
for (const c of commands) {
|
|
43
|
+
const v = policyVerdict({ role: ctx.role, command: c, guard, handle: ctx.handle, lockOwner: ctx.lockOwner, policy: ctx.policy });
|
|
44
|
+
if (v) return policyToolVerdict(c, v);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|