@starci/hfs 4.7.32 → 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 +9 -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,120 @@
|
|
|
1
|
+
// cli-arg.mjs — the `--name value` and `--flag` readers of the thin CLI entries (the scripts/api call files run as
|
|
2
|
+
// commands, scripts/agent/send.mjs). Pure.
|
|
3
|
+
import { textLine } from './escape.mjs';
|
|
4
|
+
|
|
5
|
+
/** The value after `--<name>` in argv, else `fallback`. */
|
|
6
|
+
export const arg = (argv, name, fallback = null) => {
|
|
7
|
+
const i = argv.indexOf(`--${name}`);
|
|
8
|
+
return i >= 0 ? argv[i + 1] : fallback;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** True when argv holds `--<name>`. */
|
|
12
|
+
export const flag = (argv, name) => argv.includes(`--${name}`);
|
|
13
|
+
|
|
14
|
+
/** The token after the literal `token` in argv, else `fallback` (absent or last). */
|
|
15
|
+
export const valueAfter = (argv, token, fallback = null) => {
|
|
16
|
+
const i = argv.indexOf(token);
|
|
17
|
+
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : fallback;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The `--name value` / boolean-flag scan the multi-option CLIs share: `spec` maps each accepted token to a setter
|
|
22
|
+
* `(opts, take)` where `take()` consumes the next token as the value (a missing one reports
|
|
23
|
+
* `<token> needs a value` through `fail`); boolean flags are setters that never call `take`. An unrecognized
|
|
24
|
+
* token reports `unrecognized argument: <token>` through `fail` (its return is ignored, so a non-throwing `fail`
|
|
25
|
+
* keeps scanning). Returns `opts`.
|
|
26
|
+
*/
|
|
27
|
+
export function parseOpts(argv, spec, fail) {
|
|
28
|
+
const opts = {};
|
|
29
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
30
|
+
const token = argv[i];
|
|
31
|
+
const set = spec[token];
|
|
32
|
+
if (!set) { fail(`unrecognized argument: ${token}`); continue; }
|
|
33
|
+
const take = () => {
|
|
34
|
+
i += 1;
|
|
35
|
+
if (argv[i] === undefined) fail(`${token} needs a value`);
|
|
36
|
+
return argv[i];
|
|
37
|
+
};
|
|
38
|
+
set(opts, take);
|
|
39
|
+
}
|
|
40
|
+
return opts;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The `--<name> value` pairs of `names` read from argv: every other token, a missing value and a `--`-led value throw
|
|
45
|
+
* an error suffixed with `usage`.
|
|
46
|
+
*/
|
|
47
|
+
export const valueFlags = (argv, names, usage) => {
|
|
48
|
+
const opts = {};
|
|
49
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
50
|
+
const arg = argv[i];
|
|
51
|
+
if (names.includes(arg)) {
|
|
52
|
+
if (argv[i + 1] === undefined || argv[i + 1].startsWith('--')) throw new Error(`${arg} needs a value; ${usage}`);
|
|
53
|
+
opts[arg.slice(2)] = argv[++i];
|
|
54
|
+
} else throw new Error(`unknown argument ${arg}; ${usage}`);
|
|
55
|
+
}
|
|
56
|
+
return opts;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** A parseOpts `fail` that throws `Error(message)`. */
|
|
60
|
+
export const argThrow = (message) => { throw new Error(message); };
|
|
61
|
+
|
|
62
|
+
/** Throw `Error('--<name> is required')` for the first of `names` `args` lacks a value for. */
|
|
63
|
+
export const needArgs = (args, names) => {
|
|
64
|
+
for (const name of names) if (!args[name]) throw new Error(`--${name} is required`);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Setter entries the `.starciwork`-record CLIs share: `--work <dir> --record <id> --cwd <dir>`. */
|
|
68
|
+
export const workRecordSpec = {
|
|
69
|
+
'--work': (o, take) => { o.work = take(); },
|
|
70
|
+
'--record': (o, take) => { o.record = take(); },
|
|
71
|
+
'--cwd': (o, take) => { o.cwd = take(); },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** `--work --record --cwd` for the example-record CLIs; throws on an unknown token or a missing value. */
|
|
75
|
+
export function parseExampleArgs(argv) {
|
|
76
|
+
const args = parseOpts(argv, workRecordSpec, argThrow);
|
|
77
|
+
needArgs(args, ['work', 'record', 'cwd']);
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Setter entries the record-scoped op CLIs share: `--op`, `--records` (csv, accumulated), `--state`, `--json`, `--help`/`-h` (calls `help`). */
|
|
82
|
+
const opRecordSpec = (help) => ({
|
|
83
|
+
'--op': (o, take) => { o.op = take(); },
|
|
84
|
+
'--records': (o, take) => { o.records = [...(o.records ?? []), ...take().split(',')]; },
|
|
85
|
+
'--state': (o, take) => { o.state = take(); },
|
|
86
|
+
'--json': (o) => { o.json = true; },
|
|
87
|
+
'--help': () => help(), '-h': () => help(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/** The final value of an accumulated `--records` list: trimmed, blanks dropped, deduplicated. */
|
|
91
|
+
const recordsList = (args) => [...new Set((args.records ?? []).map((s) => s.trim()).filter(Boolean))];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The `usage` + `parseArgs` pair the record-scoped op CLIs share: `usage` prints `usageText` to stderr and exits
|
|
95
|
+
* with the code; `parseArgs` scans the opRecordSpec options plus the CLI's own `spec` setters and resolves
|
|
96
|
+
* `records` to its deduplicated list (recordsList).
|
|
97
|
+
*/
|
|
98
|
+
export function opCli(usageText, spec) {
|
|
99
|
+
const usage = (code) => { console.error(usageText); process.exit(code); };
|
|
100
|
+
const parseArgs = (argv) => {
|
|
101
|
+
const a = parseOpts(argv, { ...opRecordSpec(() => usage(0)), ...spec }, () => usage(2));
|
|
102
|
+
a.records = recordsList(a);
|
|
103
|
+
return a;
|
|
104
|
+
};
|
|
105
|
+
return { usage, parseArgs };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The prelude the multi-verb lesson CLIs share (scripts/machine/lessons.mjs, scripts/supervisor/lesson-actions.mjs):
|
|
110
|
+
* `verb` is argv[0]; `value(n)` the token after `--n` (null when absent); `csv(v)` a trimmed non-empty list;
|
|
111
|
+
* `print(result, human)` logs the JSON record under `--json`, else the human text: one string is one line, an array
|
|
112
|
+
* holds one element per line; control characters inside a line (CR, LF, escape sequences) print as \uXXXX.
|
|
113
|
+
*/
|
|
114
|
+
export function verbCli(argv = process.argv.slice(2)) {
|
|
115
|
+
const value = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] ?? null : null; };
|
|
116
|
+
const csv = (v) => String(v ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
117
|
+
const asJson = argv.includes('--json');
|
|
118
|
+
const print = (r, human) => console.log(asJson ? JSON.stringify(r) : [human].flat().map((line) => textLine(line)).join('\n'));
|
|
119
|
+
return { argv, verb: argv[0], value, csv, asJson, print };
|
|
120
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// css-rules.mjs - a flat reading of a stylesheet: every style rule with its resolved selector, its `@apply` utilities and its
|
|
2
|
+
// declarations, nesting (`&`) and wrapping at-rules (@layer, @media, @supports) resolved. Pure: text in, rules out.
|
|
3
|
+
// The reader of the vendor CSS (scripts/work/ui/vendor-heroui.mjs) and of Grammar's own CSS (scripts/lib/grammar-vendor-parity.mjs).
|
|
4
|
+
|
|
5
|
+
import { byCodeUnit } from './list.mjs';
|
|
6
|
+
|
|
7
|
+
const SKIPPED_AT_RULES = new Set(['keyframes', 'property', 'font-face', 'counter-style', 'page', 'theme', 'utility', 'custom-variant', 'variant', 'source', 'plugin', 'config']);
|
|
8
|
+
|
|
9
|
+
/** The text without `/* ... *\/` comments. */
|
|
10
|
+
export const stripCssComments = (text) => String(text).replaceAll(/\/\*[\s\S]*?\*\//g, '');
|
|
11
|
+
|
|
12
|
+
/** Split `text` at top-level `separator` characters (outside parentheses, brackets and quotes). */
|
|
13
|
+
export function splitTopLevel(text, separator) {
|
|
14
|
+
const parts = [];
|
|
15
|
+
let depth = 0;
|
|
16
|
+
let quote = '';
|
|
17
|
+
let start = 0;
|
|
18
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
19
|
+
const char = text[index];
|
|
20
|
+
if (quote) {
|
|
21
|
+
if (char === '\\') index += 1;
|
|
22
|
+
else if (char === quote) quote = '';
|
|
23
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
24
|
+
else if (char === '(' || char === '[') depth += 1;
|
|
25
|
+
else if (char === ')' || char === ']') depth -= 1;
|
|
26
|
+
else if (char === separator && depth === 0) {
|
|
27
|
+
parts.push(text.slice(start, index));
|
|
28
|
+
start = index + 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
parts.push(text.slice(start));
|
|
32
|
+
return parts.map((part) => part.trim()).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The matching `}` of the `{` at `open`, or the text length. */
|
|
36
|
+
function blockEnd(text, open) {
|
|
37
|
+
let depth = 0;
|
|
38
|
+
let quote = '';
|
|
39
|
+
for (let index = open; index < text.length; index += 1) {
|
|
40
|
+
const char = text[index];
|
|
41
|
+
if (quote) {
|
|
42
|
+
if (char === '\\') index += 1;
|
|
43
|
+
else if (char === quote) quote = '';
|
|
44
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
45
|
+
else if (char === '{') depth += 1;
|
|
46
|
+
else if (char === '}') {
|
|
47
|
+
depth -= 1;
|
|
48
|
+
if (depth === 0) return index;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return text.length;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One block body as its declarations (statements) and its child blocks (prelude + body). */
|
|
55
|
+
function members(body) {
|
|
56
|
+
const statements = [];
|
|
57
|
+
const blocks = [];
|
|
58
|
+
let cursor = 0;
|
|
59
|
+
let statementStart = 0;
|
|
60
|
+
while (cursor < body.length) {
|
|
61
|
+
const char = body[cursor];
|
|
62
|
+
if (char === ';') {
|
|
63
|
+
statements.push(body.slice(statementStart, cursor).trim());
|
|
64
|
+
statementStart = cursor + 1;
|
|
65
|
+
} else if (char === '{') {
|
|
66
|
+
const end = blockEnd(body, cursor);
|
|
67
|
+
blocks.push({ prelude: body.slice(statementStart, cursor).trim(), body: body.slice(cursor + 1, end) });
|
|
68
|
+
cursor = end;
|
|
69
|
+
statementStart = end + 1;
|
|
70
|
+
} else if (char === '"' || char === "'") {
|
|
71
|
+
let next = cursor + 1;
|
|
72
|
+
while (next < body.length && body[next] !== char) next += body[next] === '\\' ? 2 : 1;
|
|
73
|
+
cursor = next;
|
|
74
|
+
}
|
|
75
|
+
cursor += 1;
|
|
76
|
+
}
|
|
77
|
+
const tail = body.slice(statementStart).trim();
|
|
78
|
+
if (tail) statements.push(tail);
|
|
79
|
+
return { statements: statements.filter(Boolean), blocks };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Resolve a nested selector list against its parent selector list (`&` is the parent; no `&` means a descendant). */
|
|
83
|
+
function resolveSelectors(parents, prelude) {
|
|
84
|
+
const own = splitTopLevel(prelude, ',');
|
|
85
|
+
if (!parents.length) return own;
|
|
86
|
+
return parents.flatMap((parent) => own.map((selector) => (selector.includes('&') ? selector.replaceAll('&', parent) : `${parent} ${selector}`)));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ruleFromStatements(selectors, wraps, statements) {
|
|
90
|
+
const apply = [];
|
|
91
|
+
const declarations = [];
|
|
92
|
+
for (const statement of statements) {
|
|
93
|
+
if (statement.startsWith('@apply ')) apply.push(...statement.slice(7).split(/\s+/).filter(Boolean));
|
|
94
|
+
else {
|
|
95
|
+
const colon = statement.indexOf(':');
|
|
96
|
+
if (colon > 0 && !statement.startsWith('@')) declarations.push({ property: statement.slice(0, colon).trim(), value: statement.slice(colon + 1).trim().replaceAll(/\s+/g, ' ') });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { selectors, wraps, apply, declarations };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function collect(body, parents, wraps, out) {
|
|
103
|
+
const { statements, blocks } = members(body);
|
|
104
|
+
if (parents.length && (statements.length > 0)) out.push(ruleFromStatements(parents, wraps, statements));
|
|
105
|
+
for (const block of blocks) {
|
|
106
|
+
if (block.prelude.startsWith('@')) {
|
|
107
|
+
const name = /^@([\w-]+)/.exec(block.prelude)?.[1] ?? '';
|
|
108
|
+
if (SKIPPED_AT_RULES.has(name)) continue;
|
|
109
|
+
collect(block.body, parents, [...wraps, block.prelude.replaceAll(/\s+/g, ' ')], out);
|
|
110
|
+
} else collect(block.body, resolveSelectors(parents, block.prelude), wraps, out);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Every style rule of `text`: `{selectors, wraps, apply, declarations}`. `selectors` are fully resolved, `wraps` the at-rule preludes
|
|
116
|
+
* (`@media (forced-colors: active)`, `@layer x`) the rule sits in, `apply` the `@apply` utilities and `declarations` the plain properties.
|
|
117
|
+
*/
|
|
118
|
+
export function cssRules(text) {
|
|
119
|
+
const out = [];
|
|
120
|
+
collect(stripCssComments(text), [], [], out);
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The subject compound of a selector: the part after the last combinator, outside brackets and parentheses. */
|
|
125
|
+
export function subjectCompound(selector) {
|
|
126
|
+
let depth = 0;
|
|
127
|
+
let cut = 0;
|
|
128
|
+
for (const [index, char] of [...selector].entries()) {
|
|
129
|
+
if (char === '(' || char === '[') depth += 1;
|
|
130
|
+
else if (char === ')' || char === ']') depth -= 1;
|
|
131
|
+
else if (depth === 0 && (char === ' ' || char === '>' || char === '+' || char === '~')) cut = index + 1;
|
|
132
|
+
}
|
|
133
|
+
return selector.slice(cut).trim();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The selector split into the text outside `[...]` and the bodies inside them. */
|
|
137
|
+
function bracketParts(selector) {
|
|
138
|
+
const outside = [];
|
|
139
|
+
const bodies = [];
|
|
140
|
+
let open = -1;
|
|
141
|
+
for (const [index, char] of [...selector].entries()) {
|
|
142
|
+
if (char === '[' && open < 0) open = index;
|
|
143
|
+
else if (char === ']' && open >= 0) {
|
|
144
|
+
bodies.push(selector.slice(open + 1, index));
|
|
145
|
+
open = -1;
|
|
146
|
+
} else if (open < 0) outside.push(char);
|
|
147
|
+
}
|
|
148
|
+
return { outside: outside.join(''), bodies };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The `.class` names a selector mentions, outside attribute brackets. */
|
|
152
|
+
export function selectorClasses(selector) {
|
|
153
|
+
return [...bracketParts(selector).outside.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)].map((match) => match[1]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** The `[data-...]` attribute names a selector mentions. */
|
|
157
|
+
export function selectorAttributes(selector) {
|
|
158
|
+
return bracketParts(selector).bodies.map((raw) => {
|
|
159
|
+
const body = raw.trim();
|
|
160
|
+
const equals = body.indexOf('=');
|
|
161
|
+
const name = (equals < 0 ? body : body.slice(0, equals)).replace(/[~|^$*]$/, '').trim();
|
|
162
|
+
const value = equals < 0 ? null : body.slice(equals + 1).trim().replaceAll(/^["']|["']$/g, '');
|
|
163
|
+
return { name, value };
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const FAMILY_BY_PROPERTY = [
|
|
168
|
+
[/^padding/, 'padding'],
|
|
169
|
+
[/^margin/, 'margin'],
|
|
170
|
+
[/^border-(?:top-|bottom-|start-|end-)*(?:start-|end-)*radius$|^border-radius$/, 'radius'],
|
|
171
|
+
[/^background/, 'background'],
|
|
172
|
+
[/^color$/, 'color'],
|
|
173
|
+
[/^(?:border|outline)/, 'border'],
|
|
174
|
+
[/^box-shadow$/, 'shadow'],
|
|
175
|
+
[/^(?:min-|max-)?(?:block-size|height)$/, 'block-size'],
|
|
176
|
+
[/^(?:min-|max-)?(?:inline-size|width)$/, 'inline-size'],
|
|
177
|
+
[/^(?:font|line-height|letter-spacing|text-transform)/, 'font'],
|
|
178
|
+
[/^(?:row-|column-)?gap$/, 'gap'],
|
|
179
|
+
[/^opacity$/, 'opacity'],
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
/** The paint family a CSS property belongs to (`padding-inline` is `padding`), or the property itself. */
|
|
183
|
+
export function propertyFamily(property) {
|
|
184
|
+
const name = String(property).trim().toLowerCase();
|
|
185
|
+
if (name.startsWith('--')) return null;
|
|
186
|
+
return FAMILY_BY_PROPERTY.find(([pattern]) => pattern.test(name))?.[1] ?? name;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const TEXT_SIZES = /^(?:xs|sm|base|lg|xl|[2-9]xl|\[.*\])$/;
|
|
190
|
+
const TEXT_FLOW = /^(?:left|center|right|start|end|justify|wrap|nowrap|balance|pretty|ellipsis|clip)$/;
|
|
191
|
+
const UTILITY_FAMILIES = [
|
|
192
|
+
[/^(?:p|px|py|ps|pe|pt|pb|pl|pr)-/, ['padding']],
|
|
193
|
+
[/^-?(?:m|mx|my|ms|me|mt|mb|ml|mr)-/, ['margin']],
|
|
194
|
+
[/^rounded/, ['radius']],
|
|
195
|
+
[/^bg-/, ['background']],
|
|
196
|
+
[/^(?:min-h|max-h|h)-/, ['block-size']],
|
|
197
|
+
[/^(?:min-w|max-w|w)-/, ['inline-size']],
|
|
198
|
+
[/^size-/, ['block-size', 'inline-size']],
|
|
199
|
+
[/^(?:gap|gap-x|gap-y)-/, ['gap']],
|
|
200
|
+
[/^(?:border|ring|outline)(?:-|$)/, ['border']],
|
|
201
|
+
[/^(?:shadow)(?:-|$)/, ['shadow']],
|
|
202
|
+
[/^(?:font|leading|tracking)-/, ['font']],
|
|
203
|
+
[/^opacity-/, ['opacity']],
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
/** The paint families one `@apply` utility sets (`ps-7` is `padding`, `text-muted` is `color`); an unrecognised utility sets none. */
|
|
207
|
+
export function utilityFamilies(utility) {
|
|
208
|
+
const bare = String(utility).replace(/^(?:[^\s:]+:)+/, '').replace(/^!/, '').replace(/!$/, '');
|
|
209
|
+
if (bare.startsWith('text-')) {
|
|
210
|
+
const rest = bare.slice(5);
|
|
211
|
+
if (TEXT_SIZES.test(rest)) return ['font'];
|
|
212
|
+
return TEXT_FLOW.test(rest) ? [] : ['color'];
|
|
213
|
+
}
|
|
214
|
+
return UTILITY_FAMILIES.find(([pattern]) => pattern.test(bare))?.[1] ?? [];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The paint families a rule sets, from its `@apply` utilities and its declarations. */
|
|
218
|
+
export function ruleFamilies(rule) {
|
|
219
|
+
return [...new Set([...rule.apply.flatMap(utilityFamilies), ...rule.declarations.map((entry) => propertyFamily(entry.property)).filter(Boolean)])].sort(byCodeUnit);
|
|
220
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// dom-tree.mjs — walks over the draw checkers' parsed-DOM element shape ({tag, attrs, parent}).
|
|
2
|
+
|
|
3
|
+
/** The ancestors of `el`, nearest first, stopping below the '#root' sentinel. */
|
|
4
|
+
export const ancestorsOf = (el) => { const out = []; for (let p = el.parent; p && p.tag !== '#root'; p = p.parent) { out.push(p); } return out; };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// escape.mjs — the escapes for text embedded in another syntax: HTML entities for markup (Telegram HTML, report blocks), one-line JSON for logs.
|
|
2
|
+
|
|
3
|
+
/** `s` with &, < and > entity-escaped. */
|
|
4
|
+
export const escapeHtml = (s) => String(s ?? '').replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
5
|
+
|
|
6
|
+
/** `c` (one character) with a C0 control character or U+2028/2029 written as \uXXXX. */
|
|
7
|
+
const visibleChar = (c) => {
|
|
8
|
+
const n = c.codePointAt(0);
|
|
9
|
+
return n < 0x20 || n === 0x2028 || n === 0x2029 ? String.raw`\u${n.toString(16).padStart(4, '0')}` : c;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** `value` as JSON on ONE log line: CR, LF, every other C0 control character and U+2028/2029 written as \uXXXX (JSON.stringify already does so for C0 inside strings, so well-formed data prints byte-identically). */
|
|
13
|
+
export const logLine = (value) => Array.from(String(JSON.stringify(value)), visibleChar).join('');
|
|
14
|
+
|
|
15
|
+
/** `text` as ONE plain line: CR, LF and every other control character except the tab written as \uXXXX; ordinary text, quotes and backslashes print unchanged. */
|
|
16
|
+
export const textLine = (text) => Array.from(String(text ?? ''), (c) => (c === '\t' ? c : visibleChar(c))).join('');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// event-items.mjs - the open items of an owed/resolved event pair, replayed from a workflow ledger (read only).
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The workflow's open items of an owed/resolved event pair (grammar proposals, asset slots): replay the kinds in seq
|
|
5
|
+
* order — a `resolvedKind` row deletes the item `keyOf` names, an `owedKind` row sets `item(payload,row)`; a row
|
|
6
|
+
* whose key is falsy is skipped. Returns the open items in first-seen order, [] when the table cannot be read.
|
|
7
|
+
*/
|
|
8
|
+
export function openEventItems(db,workflowId,{owedKind,resolvedKind,keyOf,item}){
|
|
9
|
+
let rows=[];
|
|
10
|
+
try{rows=db.prepare('SELECT kind,payload_json,created_at FROM events WHERE workflow_id=? AND kind IN (?,?) ORDER BY seq').all(workflowId,owedKind,resolvedKind);}
|
|
11
|
+
catch{return[];}
|
|
12
|
+
const open=new Map();
|
|
13
|
+
for(const row of rows){
|
|
14
|
+
let p={};
|
|
15
|
+
try{p=JSON.parse(row.payload_json??'{}')??{};}catch{p={};}
|
|
16
|
+
const key=keyOf(p);
|
|
17
|
+
if(!key)continue;
|
|
18
|
+
if(row.kind===resolvedKind){open.delete(key);continue;}
|
|
19
|
+
open.set(key,item(p,row));
|
|
20
|
+
}
|
|
21
|
+
return[...open.values()];
|
|
22
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Canonical platform templates can own shared application failures as well as runtime findings.
|
|
2
|
+
// A catalog entry stays current only when production code instantiates its genuine DomainError enum member.
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const CODE = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/;
|
|
6
|
+
const exported = (t, node) => node.modifiers?.some((modifier) => modifier.kind === t.SyntaxKind.ExportKeyword);
|
|
7
|
+
const textName = (t, node) => node && (t.isIdentifier(node) || t.isStringLiteralLike(node)) ? node.text : null;
|
|
8
|
+
|
|
9
|
+
/** An import path resolves inside the virtual backend source tree declared by the pattern files. */
|
|
10
|
+
function sourceTarget(specifier, owner, sources) {
|
|
11
|
+
let target = null;
|
|
12
|
+
if (specifier.startsWith('@modules/')) target = `src/modules/${specifier.slice('@modules/'.length)}`;
|
|
13
|
+
else if (specifier.startsWith('.')) target = path.posix.normalize(path.posix.join(path.posix.dirname(owner), specifier));
|
|
14
|
+
if (target === null) return null;
|
|
15
|
+
return [target, `${target}.ts`, `${target}/index.ts`].find((candidate) => sources.has(candidate)) ?? null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Index module bindings and explicit exports without importing or running a template. */
|
|
19
|
+
function moduleInfo(t, name, entry) {
|
|
20
|
+
const source = t.createSourceFile(name, entry.text, t.ScriptTarget.Latest, true, t.ScriptKind.TS);
|
|
21
|
+
const info = { name, file: entry.file, source, imports: new Map(), namespaces: new Map(), locals: new Map(), exports: new Map(), shadows: new Set() };
|
|
22
|
+
for (const statement of source.statements) {
|
|
23
|
+
if (t.isImportDeclaration(statement)) indexImport(t, info, statement);
|
|
24
|
+
if (t.isExportDeclaration(statement)) indexExport(t, info, statement);
|
|
25
|
+
if ((t.isEnumDeclaration(statement) || t.isClassDeclaration(statement)) && statement.name) {
|
|
26
|
+
const key = statement.name.text;
|
|
27
|
+
info.locals.set(key, statement);
|
|
28
|
+
if (exported(t, statement)) info.exports.set(key, { local: key });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const visit = (node) => {
|
|
32
|
+
const binding = scopedBinding(t, node);
|
|
33
|
+
if (binding) info.shadows.add(binding);
|
|
34
|
+
t.forEachChild(node, visit);
|
|
35
|
+
};
|
|
36
|
+
visit(source);
|
|
37
|
+
return info;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** A nested declaration makes a text-only binding ambiguous; it cannot supply catalog evidence. */
|
|
41
|
+
function scopedBinding(t, node) {
|
|
42
|
+
const named = t.isVariableDeclaration(node) || t.isParameter(node) || t.isBindingElement(node)
|
|
43
|
+
|| t.isClassDeclaration(node) || t.isEnumDeclaration(node) || t.isFunctionDeclaration(node);
|
|
44
|
+
if (!named || !node.name || !t.isIdentifier(node.name)) return null;
|
|
45
|
+
let scope = node.parent;
|
|
46
|
+
while (scope && !t.isSourceFile(scope)) {
|
|
47
|
+
if (t.isBlock(scope) || t.isFunctionLike(scope)) return node.name.text;
|
|
48
|
+
scope = scope.parent;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function indexImport(t, info, statement) {
|
|
54
|
+
const bindings = statement.importClause?.namedBindings;
|
|
55
|
+
if (!bindings || !t.isStringLiteralLike(statement.moduleSpecifier)) return;
|
|
56
|
+
const specifier = statement.moduleSpecifier.text;
|
|
57
|
+
if (t.isNamespaceImport(bindings)) info.namespaces.set(bindings.name.text, specifier);
|
|
58
|
+
else for (const binding of bindings.elements) info.imports.set(binding.name.text, { name: (binding.propertyName ?? binding.name).text, specifier });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function indexExport(t, info, statement) {
|
|
62
|
+
const clause = statement.exportClause;
|
|
63
|
+
if (!clause || !t.isNamedExports(clause)) return;
|
|
64
|
+
const specifier = statement.moduleSpecifier && t.isStringLiteralLike(statement.moduleSpecifier) ? statement.moduleSpecifier.text : null;
|
|
65
|
+
for (const binding of clause.elements) {
|
|
66
|
+
const name = (binding.propertyName ?? binding.name).text;
|
|
67
|
+
info.exports.set(binding.name.text, specifier ? { name, specifier } : { local: name });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Only a declared binding or a named import/export can establish an error family or enum identity. */
|
|
72
|
+
function localValue(info, name, modules, seen = new Set()) {
|
|
73
|
+
const identity = `${info.name}:${name}`;
|
|
74
|
+
if (seen.has(identity) || info.shadows.has(name)) return null;
|
|
75
|
+
const visited = new Set(seen).add(identity);
|
|
76
|
+
if (info.locals.has(name)) return { info, declaration: info.locals.get(name) };
|
|
77
|
+
const binding = info.imports.get(name);
|
|
78
|
+
return binding ? importedValue(info, binding, modules, visited) : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function importedValue(info, binding, modules, seen) {
|
|
82
|
+
const target = sourceTarget(binding.specifier, info.name, modules);
|
|
83
|
+
const owner = target && modules.get(target);
|
|
84
|
+
const identity = owner && `export:${owner.name}:${binding.name}`;
|
|
85
|
+
if (!owner || seen.has(identity)) return null;
|
|
86
|
+
const visited = new Set(seen).add(identity);
|
|
87
|
+
const exposed = owner?.exports.get(binding.name);
|
|
88
|
+
if (!exposed) return null;
|
|
89
|
+
return exposed.local ? localValue(owner, exposed.local, modules, visited) : importedValue(owner, exposed, modules, visited);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const accessParts = (t, expression) => {
|
|
93
|
+
if (t.isPropertyAccessExpression(expression) || t.isQualifiedName(expression)) {
|
|
94
|
+
return { base: expression.expression ?? expression.left, member: (expression.name ?? expression.right).text };
|
|
95
|
+
}
|
|
96
|
+
if (t.isElementAccessExpression(expression) && t.isStringLiteralLike(expression.argumentExpression)) return { base: expression.expression, member: expression.argumentExpression.text };
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function expressionValue(t, info, expression, modules) {
|
|
101
|
+
if (t.isIdentifier(expression)) return localValue(info, expression.text, modules);
|
|
102
|
+
const access = accessParts(t, expression);
|
|
103
|
+
const specifier = access && t.isIdentifier(access.base) && !info.shadows.has(access.base.text) && info.namespaces.get(access.base.text);
|
|
104
|
+
return specifier ? importedValue(info, { specifier, name: access.member }, modules, new Set()) : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** DomainError is the canonical imported base, not an identically named local class. */
|
|
108
|
+
function domainBase(t, info, expression) {
|
|
109
|
+
if (!t.isIdentifier(expression)) return false;
|
|
110
|
+
const binding = info.imports.get(expression.text);
|
|
111
|
+
return !info.shadows.has(expression.text) && binding?.name === 'DomainError' && binding.specifier === '@modules/platform/errors';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The generic enum on the real DomainError base owns the family's permitted codes. */
|
|
115
|
+
function errorEnum(t, value, modules) {
|
|
116
|
+
const node = value?.declaration;
|
|
117
|
+
if (!node || !t.isClassDeclaration(node) || node.members.some(t.isConstructorDeclaration)) return null;
|
|
118
|
+
const clause = node.heritageClauses?.find((item) => item.token === t.SyntaxKind.ExtendsKeyword);
|
|
119
|
+
const base = clause?.types[0];
|
|
120
|
+
const argument = base?.typeArguments?.[0];
|
|
121
|
+
if (!base || !domainBase(t, value.info, base.expression) || base.typeArguments?.length !== 1 || !argument || !t.isTypeReferenceNode(argument)) return null;
|
|
122
|
+
const enumeration = expressionValue(t, value.info, argument.typeName, modules);
|
|
123
|
+
return enumeration && t.isEnumDeclaration(enumeration.declaration) ? enumeration : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A static code field survives only when no later spread or dynamic key can replace it. */
|
|
127
|
+
function codeField(t, expression) {
|
|
128
|
+
if (!expression || !t.isObjectLiteralExpression(expression)) return null;
|
|
129
|
+
let code = null;
|
|
130
|
+
for (const property of expression.properties) {
|
|
131
|
+
if (t.isSpreadAssignment(property) || textName(t, property.name) === null) code = null;
|
|
132
|
+
else if (textName(t, property.name) === 'code') code = t.isPropertyAssignment(property) ? property.initializer : null;
|
|
133
|
+
}
|
|
134
|
+
return code;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Constructor and code expression must resolve to the same declared enum, including through public barrels. */
|
|
138
|
+
function constructedCode(t, info, node, modules) {
|
|
139
|
+
const family = errorEnum(t, expressionValue(t, info, node.expression, modules), modules);
|
|
140
|
+
const field = codeField(t, node.arguments?.[0]);
|
|
141
|
+
const access = field && accessParts(t, field);
|
|
142
|
+
const enumeration = access && expressionValue(t, info, access.base, modules);
|
|
143
|
+
if (!family || !enumeration || enumeration.declaration !== family.declaration) return null;
|
|
144
|
+
const member = family.declaration.members.find((item) => textName(t, item.name) === access.member);
|
|
145
|
+
return member?.initializer && t.isStringLiteralLike(member.initializer) && CODE.test(member.initializer.text) ? member.initializer.text : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Actual typed error constructions in declared production platform templates, with their source locations. */
|
|
149
|
+
export function templateErrorCodes(t, sources) {
|
|
150
|
+
const modules = new Map([...sources].map(([name, entry]) => [name, moduleInfo(t, name, entry)]));
|
|
151
|
+
const emitted = new Map();
|
|
152
|
+
for (const info of modules.values()) {
|
|
153
|
+
const visit = (node) => {
|
|
154
|
+
const code = t.isNewExpression(node) && constructedCode(t, info, node, modules);
|
|
155
|
+
if (code) emitted.set(code, { code, file: info.file, line: info.source.getLineAndCharacterOfPosition(node.getStart(info.source)).line + 1 });
|
|
156
|
+
t.forEachChild(node, visit);
|
|
157
|
+
};
|
|
158
|
+
visit(info.source);
|
|
159
|
+
}
|
|
160
|
+
return [...emitted.values()];
|
|
161
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// git-dir.mjs — a checkout's git directory and the git processes that may hold it, read without running git: the pure
|
|
2
|
+
// half of the stale `.git/index.lock` recovery (scripts/api/git/index-lock.mjs indexLock removes the lock;
|
|
3
|
+
// scripts/machine/lock-recovery.mjs reads the process table, records the removal and runs the command guard's pre-flight).
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { pathKey } from './path-key.mjs';
|
|
7
|
+
|
|
8
|
+
/** git.exe and its helpers (git-remote-https.exe, git-lfs.exe, ...). */
|
|
9
|
+
const GIT_IMAGE = /^git(?:-[\w.-]+)?(?:\.exe)?$/i;
|
|
10
|
+
/** Whether a process image name is git or one of its helpers. */
|
|
11
|
+
export const isGitImage = (name) => GIT_IMAGE.test(String(name ?? ''));
|
|
12
|
+
/** `file`'s lstat, or null when it cannot be read. */
|
|
13
|
+
export const lockStat = (file) => { try { return fs.lstatSync(file); } catch { return null; } };
|
|
14
|
+
|
|
15
|
+
const GITDIR_LINE = new RegExp([
|
|
16
|
+
'^gitdir:',
|
|
17
|
+
String.raw`\s*(.+)$`,
|
|
18
|
+
].join(''), 'm');
|
|
19
|
+
|
|
20
|
+
/** The git dir of the checkout at `repo`: `.git` itself, or the `gitdir:` a worktree's `.git` file names. Null when none. */
|
|
21
|
+
export function gitDirOf(repo) {
|
|
22
|
+
const dotGit = path.join(path.resolve(repo), '.git');
|
|
23
|
+
let st;
|
|
24
|
+
try { st = fs.lstatSync(dotGit); } catch { return null; }
|
|
25
|
+
if (st.isDirectory()) return dotGit;
|
|
26
|
+
if (!st.isFile()) return null;
|
|
27
|
+
try {
|
|
28
|
+
const m = GITDIR_LINE.exec(fs.readFileSync(dotGit, 'utf8'));
|
|
29
|
+
return m ? path.resolve(path.dirname(dotGit), m[1].trim()) : null;
|
|
30
|
+
} catch { return null; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The checkout `start` is in: the nearest directory at or above it holding `.git`, or null. */
|
|
34
|
+
export function checkoutOf(start) {
|
|
35
|
+
for (let dir = path.resolve(String(start ?? '.')); ; dir = path.dirname(dir)) {
|
|
36
|
+
if (lockStat(path.join(dir, '.git'))) return dir;
|
|
37
|
+
if (path.dirname(dir) === dir) return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const FULL_SHA = /^[0-9a-f]{40}$/;
|
|
42
|
+
|
|
43
|
+
function looseHeadRef(gitDir, common, ref, commonOnly) {
|
|
44
|
+
for (const dir of commonOnly ? [common] : [gitDir, common]) {
|
|
45
|
+
try {
|
|
46
|
+
const loose = fs.readFileSync(path.join(dir, ref), 'utf8').trim();
|
|
47
|
+
if (FULL_SHA.test(loose)) return loose;
|
|
48
|
+
} catch { /* packed */ }
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function packedHeadRef(common, ref) {
|
|
54
|
+
for (const line of fs.readFileSync(path.join(common, 'packed-refs'), 'utf8').split('\n')) {
|
|
55
|
+
const [sha, name] = line.trim().split(' ');
|
|
56
|
+
if (name === ref && FULL_SHA.test(sha)) return sha;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const HEADS_REF_LINE = new RegExp([
|
|
62
|
+
String.raw`^ref:\s*(refs\/heads\/\S+)`,
|
|
63
|
+
'$',
|
|
64
|
+
].join(''));
|
|
65
|
+
const ANY_REF_LINE = new RegExp([
|
|
66
|
+
String.raw`^ref:\s*(.+)`,
|
|
67
|
+
'$',
|
|
68
|
+
].join(''));
|
|
69
|
+
/**
|
|
70
|
+
* The commit the HEAD of the checkout at `root` names, read from .git with no spawn; null when it cannot be told that
|
|
71
|
+
* way (not a top level, reftable, an unreadable ref) and the read then runs live. `headsOnly` accepts only
|
|
72
|
+
* refs/heads/* symbolic refs; a loose ref reads from the common dir, as git does for a linked worktree — plus the git
|
|
73
|
+
* dir itself first unless `commonOnly`.
|
|
74
|
+
*/
|
|
75
|
+
export function headShaOf(root, { headsOnly = false, commonOnly = false } = {}) {
|
|
76
|
+
try {
|
|
77
|
+
let gitDir = path.join(root, '.git');
|
|
78
|
+
if (fs.statSync(gitDir).isFile()) {
|
|
79
|
+
const pointer = GITDIR_LINE.exec(fs.readFileSync(gitDir, 'utf8'))?.[1];
|
|
80
|
+
if (!pointer) return null;
|
|
81
|
+
gitDir = path.resolve(root, pointer.trim());
|
|
82
|
+
}
|
|
83
|
+
const head = fs.readFileSync(path.join(gitDir, 'HEAD'), 'utf8').trim();
|
|
84
|
+
if (FULL_SHA.test(head)) return head;
|
|
85
|
+
const ref = headsOnly ? HEADS_REF_LINE.exec(head)?.[1] : ANY_REF_LINE.exec(head)?.[1]?.trim();
|
|
86
|
+
if (!ref) return null;
|
|
87
|
+
let common = gitDir;
|
|
88
|
+
try { common = path.resolve(gitDir, fs.readFileSync(path.join(gitDir, 'commondir'), 'utf8').trim()); } catch { /* not a linked worktree */ }
|
|
89
|
+
const loose = looseHeadRef(gitDir, common, ref, commonOnly);
|
|
90
|
+
if (loose) return loose;
|
|
91
|
+
return packedHeadRef(common, ref);
|
|
92
|
+
} catch { /* no git metadata */ }
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Command-line words, double quotes grouping (Windows command lines quote paths with spaces). */
|
|
97
|
+
const words = (line) => [...String(line ?? '').matchAll(/"([^"]*)"|(\S+)/g)].map((m) => m[1] ?? m[2]);
|
|
98
|
+
|
|
99
|
+
/** The repositories a git command line names by -C, --git-dir or --work-tree (as given, unresolved). */
|
|
100
|
+
function reposNamed(commandLine) {
|
|
101
|
+
const w = words(commandLine), out = [];
|
|
102
|
+
for (let i = 0; i < w.length; i += 1) {
|
|
103
|
+
if (w[i] === '-C' || w[i] === '--git-dir' || w[i] === '--work-tree') { if (w[i + 1]) { out.push(w[i + 1]); } i += 1; continue; }
|
|
104
|
+
const m = /^--(?:git-dir|work-tree)=(.+)$/.exec(w[i]);
|
|
105
|
+
if (m) out.push(m[1]);
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Whether one git process may hold `repo`'s index: 'this' (it names the repo or a path inside it), 'other'
|
|
112
|
+
* (every repository it names is elsewhere) or 'unknown' (it names none; its cwd may be this repo).
|
|
113
|
+
*/
|
|
114
|
+
export function processOnRepo(proc, repo) {
|
|
115
|
+
const named = reposNamed(proc?.commandLine);
|
|
116
|
+
if (!named.length) return 'unknown';
|
|
117
|
+
const key = pathKey(path.resolve(repo));
|
|
118
|
+
const hit = named.some((p) => { const k = pathKey(path.resolve(p)); return k === key || k.startsWith(`${key}/`) || key.startsWith(`${k}/`); });
|
|
119
|
+
return hit ? 'this' : 'other';
|
|
120
|
+
}
|