@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
package/emit/operations.mjs
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
* The versioned operations of an api app, read from its typed operation table and printed as OpenAPI 3.1.
|
|
3
3
|
*
|
|
4
4
|
* The table is the canon's one declaration of an app's operation surface (knowledge/patterns/be/api.yaml, BE-OPERATIONS-1):
|
|
5
|
-
* `apps/<app>/src/operations.ts` exports `OPERATIONS`, made by `defineOperations({ "<id>@<version>":
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* `apps/<app>/src/operations.ts` exports `OPERATIONS`, made by `defineOperations({ "<id>@<version>": query({ input, output }) })`.
|
|
6
|
+
* Input and output are exported zod schemas from *.schema.ts. Only schema modules are evaluated, never app modules; the app's
|
|
7
|
+
* z.toJSONSchema carries their constraints into OpenAPI. Open schemas and unrepresentable wire values are refused; the
|
|
8
|
+
* TypeScript checker separately proves closed parsed input/output types and refusal codes. A failure always names the operation.
|
|
9
9
|
*
|
|
10
10
|
* The wire is one route, `POST /operations`: the body is `{ operation, requestId, input }` and the answer is `{ operation, requestId,
|
|
11
11
|
* outcome }`, `outcome` being `{ kind: "ok", value }` or `{ kind: "refused", code, params? }`. Each operation contributes one request
|
|
12
12
|
* and one reply component carrying `x-operation: "<id>@<version>"`; the route's body and answer are the oneOf over them.
|
|
13
13
|
*/
|
|
14
14
|
import { createSchemaBuilder, stable } from './type-schema.mjs';
|
|
15
|
+
import { INPUT_BOUNDS_OPENAPI_EXTENSION, INPUT_BOUNDS_VERSION } from './input-bounds.mjs';
|
|
16
|
+
import { operationSchemaReader } from './operation-schemas.mjs';
|
|
17
|
+
import path from 'node:path';
|
|
15
18
|
|
|
16
19
|
/** The export of `apps/<app>/src/operations.ts` that holds the table. */
|
|
17
20
|
const OPERATIONS_EXPORT = 'OPERATIONS';
|
|
@@ -27,19 +30,32 @@ const ID = /^[a-z][A-Za-z0-9.]*@[1-9][0-9]*$/;
|
|
|
27
30
|
/** The component name stem of an operation id: `sales.policy@1` gives `sales.policy.v1`. */
|
|
28
31
|
const stemOf = (id) => id.replace('@', '.v');
|
|
29
32
|
|
|
33
|
+
/** Preserve R95 on parsed values even when an input transform still has a closed JSON wire representation. */
|
|
34
|
+
function assertParsedTypes(builder, member, fail) {
|
|
35
|
+
for (const label of ['input', 'output']) {
|
|
36
|
+
const type = member(label);
|
|
37
|
+
try {
|
|
38
|
+
builder.schemaOf(type, `parsed ${label}`);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
fail(error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
/**
|
|
31
46
|
* Reads the operation table of a source file of a program. Answers null when the file is not in the program; throws when it
|
|
32
47
|
* exports no `OPERATIONS` or an operation is not decidable.
|
|
33
48
|
*/
|
|
34
|
-
export function readOperations({ ts, program, file }) {
|
|
49
|
+
export function readOperations({ ts, program, file, repoRoot = path.resolve(path.dirname(file), '../../..') }) {
|
|
35
50
|
const checker = program.getTypeChecker();
|
|
36
51
|
const sourceFile = program.getSourceFile(file);
|
|
37
52
|
if (!sourceFile) return null;
|
|
38
53
|
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
39
54
|
const exported = moduleSymbol ? checker.getExportsOfModule(moduleSymbol).find((symbol) => symbol.getName() === OPERATIONS_EXPORT) : null;
|
|
40
55
|
if (!exported) throw new Error(`${file} does not export ${OPERATIONS_EXPORT}, the app's operation table`);
|
|
41
|
-
const
|
|
42
|
-
const
|
|
56
|
+
const readSchema = operationSchemaReader({ ts, program, repoRoot });
|
|
57
|
+
const parsedTypes = createSchemaBuilder({ ts, checker, schemaBacked: true });
|
|
58
|
+
const table = checker.getTypeOfSymbol(exported.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported);
|
|
43
59
|
const operations = [];
|
|
44
60
|
for (const prop of [...checker.getPropertiesOfType(table)].sort((a, b) => (a.getName() < b.getName() ? -1 : 1))) {
|
|
45
61
|
const id = prop.getName();
|
|
@@ -47,6 +63,7 @@ export function readOperations({ ts, program, file }) {
|
|
|
47
63
|
throw new Error(`operation ${id}: ${why}`);
|
|
48
64
|
};
|
|
49
65
|
if (!ID.test(id)) fail('the key must be "<name>@<positive version>", for example "sales.policy@1"');
|
|
66
|
+
if (id.length > 128) fail('the operation id exceeds the runtime envelope limit of 128 characters');
|
|
50
67
|
const contract = checker.getTypeOfSymbol(prop);
|
|
51
68
|
const kindType = contract.getProperty('kind') ? checker.getTypeOfSymbol(contract.getProperty('kind')) : null;
|
|
52
69
|
const kind = kindType?.isStringLiteral() ? kindType.value : null;
|
|
@@ -59,15 +76,17 @@ export function readOperations({ ts, program, file }) {
|
|
|
59
76
|
if (!symbol) fail(`the contract has no ${name} type`);
|
|
60
77
|
return checker.getTypeOfSymbol(symbol);
|
|
61
78
|
};
|
|
62
|
-
const
|
|
79
|
+
const schema = (label) => {
|
|
63
80
|
try {
|
|
64
|
-
return
|
|
81
|
+
return readSchema(prop, label);
|
|
65
82
|
} catch (error) {
|
|
66
|
-
return fail(error.message);
|
|
83
|
+
return fail(`${label} schema: ${error.message}`);
|
|
67
84
|
}
|
|
68
85
|
};
|
|
69
|
-
const input =
|
|
70
|
-
const output =
|
|
86
|
+
const input = schema('input');
|
|
87
|
+
const output = schema('output');
|
|
88
|
+
// This check never supplies emitted schemas: z.toJSONSchema remains the wire authority.
|
|
89
|
+
assertParsedTypes(parsedTypes, member, fail);
|
|
71
90
|
const refusalType = member('refusal');
|
|
72
91
|
let refusal = [];
|
|
73
92
|
if ((refusalType.flags & ts.TypeFlags.Never) === 0) {
|
|
@@ -78,12 +97,23 @@ export function readOperations({ ts, program, file }) {
|
|
|
78
97
|
operations.push({ id, kind, input, output, refusal });
|
|
79
98
|
}
|
|
80
99
|
if (operations.length === 0) throw new Error(`${file}: ${OPERATIONS_EXPORT} declares no operation`);
|
|
81
|
-
return { operations, components:
|
|
100
|
+
return { operations, components: {} };
|
|
82
101
|
}
|
|
83
102
|
|
|
84
103
|
/** The OpenAPI 3.1 document of an app's operations, as sorted-key JSON text with one final newline. */
|
|
85
104
|
export function openApiText({ app, operations, components }) {
|
|
86
105
|
const schemas = { ...components };
|
|
106
|
+
schemas['Operation.InvalidRequest'] = {
|
|
107
|
+
type: 'object',
|
|
108
|
+
additionalProperties: false,
|
|
109
|
+
required: ['code', 'kind', 'message', 'params'],
|
|
110
|
+
properties: {
|
|
111
|
+
code: { const: 'OPERATIONS_INVALID_REQUEST' },
|
|
112
|
+
kind: { const: 'invalid' },
|
|
113
|
+
message: { type: 'string', description: 'The localized canonical error message.' },
|
|
114
|
+
params: { type: 'object', additionalProperties: false },
|
|
115
|
+
},
|
|
116
|
+
};
|
|
87
117
|
const requests = [];
|
|
88
118
|
const replies = [];
|
|
89
119
|
for (const operation of operations) {
|
|
@@ -92,15 +122,17 @@ export function openApiText({ app, operations, components }) {
|
|
|
92
122
|
const refusedOutcome = { type: 'object', required: ['code', 'kind'], properties: { code: { enum: operation.refusal, type: 'string' }, kind: { const: 'refused' }, params: { type: 'object', additionalProperties: { type: ['string', 'number', 'boolean'] } } } };
|
|
93
123
|
schemas[`Operation.${stem}.Request`] = {
|
|
94
124
|
type: 'object',
|
|
125
|
+
additionalProperties: false,
|
|
95
126
|
required: ['input', 'operation', 'requestId'],
|
|
96
|
-
properties: { input: operation.input, operation: { const: operation.id }, requestId: { type: 'string', minLength: 1 } },
|
|
127
|
+
properties: { input: operation.input, operation: { const: operation.id }, requestId: { type: 'string', minLength: 1, maxLength: 128 } },
|
|
97
128
|
'x-kind': operation.kind,
|
|
98
129
|
'x-operation': operation.id,
|
|
99
130
|
};
|
|
100
131
|
schemas[`Operation.${stem}.Reply`] = {
|
|
101
132
|
type: 'object',
|
|
133
|
+
additionalProperties: false,
|
|
102
134
|
required: ['operation', 'outcome', 'requestId'],
|
|
103
|
-
properties: { operation: { const: operation.id }, outcome: operation.refusal.length ? { oneOf: [okOutcome, refusedOutcome] } : okOutcome, requestId: { type: 'string' } },
|
|
135
|
+
properties: { operation: { const: operation.id }, outcome: operation.refusal.length ? { oneOf: [okOutcome, refusedOutcome] } : okOutcome, requestId: { type: 'string', minLength: 1, maxLength: 128 } },
|
|
104
136
|
'x-kind': operation.kind,
|
|
105
137
|
'x-operation': operation.id,
|
|
106
138
|
};
|
|
@@ -109,6 +141,7 @@ export function openApiText({ app, operations, components }) {
|
|
|
109
141
|
}
|
|
110
142
|
const document = {
|
|
111
143
|
openapi: '3.1.0',
|
|
144
|
+
[INPUT_BOUNDS_OPENAPI_EXTENSION]: INPUT_BOUNDS_VERSION,
|
|
112
145
|
info: { title: `${app} operations`, version: '1' },
|
|
113
146
|
'x-operations': operations.map((operation) => ({ id: operation.id, kind: operation.kind })),
|
|
114
147
|
paths: {
|
|
@@ -116,7 +149,10 @@ export function openApiText({ app, operations, components }) {
|
|
|
116
149
|
post: {
|
|
117
150
|
operationId: 'invokeOperation',
|
|
118
151
|
requestBody: { required: true, content: { 'application/json': { schema: { oneOf: requests } } } },
|
|
119
|
-
responses: {
|
|
152
|
+
responses: {
|
|
153
|
+
200: { description: 'The reply of the invoked operation: its outcome is ok or a declared refusal.', content: { 'application/json': { schema: { oneOf: replies } } } },
|
|
154
|
+
400: { description: 'The envelope, operation id or input failed runtime schema validation before dispatch.', content: { 'application/json': { schema: { $ref: '#/components/schemas/Operation.InvalidRequest' } } } },
|
|
155
|
+
},
|
|
120
156
|
},
|
|
121
157
|
},
|
|
122
158
|
},
|
package/emit/schema-worker.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The child process of `starci app emit` for one app.
|
|
2
2
|
// Prints the schema the app serves on stdout; exit 3 when the app's module graph has no GraphQL server.
|
|
3
|
-
// Loads the repository's own typescript, @nestjs
|
|
3
|
+
// Loads the repository's own typescript, @nestjs/*, graphql and (when installed) class-validator (resolved from the repository, never from hfs).
|
|
4
4
|
//
|
|
5
5
|
// Two steps: (1) the module graph of the app root is read from source (static-graph.mjs) and yields the resolver classes the
|
|
6
6
|
// GraphQL server would collect; (2) only those classes are loaded, and Nest's own GraphQLSchemaFactory builds the schema.
|
|
@@ -13,6 +13,7 @@ import path from 'node:path';
|
|
|
13
13
|
import { aliasTarget, aliasesOf, appModulePath } from './contracts.mjs';
|
|
14
14
|
import { compilerOptionsOf } from './compiler.mjs';
|
|
15
15
|
import { createGraphReader } from './static-graph.mjs';
|
|
16
|
+
import { graphqlInputBounds, withInputBounds } from './graphql-input-bounds.mjs';
|
|
16
17
|
|
|
17
18
|
const [rootArgument, app] = process.argv.slice(2);
|
|
18
19
|
const repoRoot = path.resolve(rootArgument);
|
|
@@ -94,7 +95,7 @@ const classOf = ({ file, name }) => {
|
|
|
94
95
|
};
|
|
95
96
|
|
|
96
97
|
// The packages of the repository under emission, resolved from it (never from hfs, which declares none of them).
|
|
97
|
-
const FROM_REPOSITORY = { nestGraphql: '@nestjs/graphql', nestCore: '@nestjs/core', graphql: 'graphql' };
|
|
98
|
+
const FROM_REPOSITORY = { nestGraphql: '@nestjs/graphql', nestCore: '@nestjs/core', graphql: 'graphql', classValidator: 'class-validator' };
|
|
98
99
|
const graphql = require(FROM_REPOSITORY.nestGraphql);
|
|
99
100
|
// the constants and the scalar factory are internals of the package: reached by file, its `exports` map hides them
|
|
100
101
|
const graphqlDist = path.dirname(require.resolve(FROM_REPOSITORY.nestGraphql));
|
|
@@ -110,8 +111,20 @@ const includeModules = composition.include.map(classOf);
|
|
|
110
111
|
|
|
111
112
|
const { NestFactory } = require(FROM_REPOSITORY.nestCore);
|
|
112
113
|
const { GraphQLSchemaBuilderModule, GraphQLSchemaFactory } = graphql;
|
|
113
|
-
const { lexicographicSortSchema, printSchema } = require(FROM_REPOSITORY.graphql);
|
|
114
|
+
const { lexicographicSortSchema, parse, print, printSchema } = require(FROM_REPOSITORY.graphql);
|
|
114
115
|
const context = await NestFactory.createApplicationContext(GraphQLSchemaBuilderModule, { logger: false });
|
|
115
116
|
const schema = await context.get(GraphQLSchemaFactory).create(resolvers, { scalarsMap, includeModules });
|
|
116
|
-
|
|
117
|
+
const printed = printSchema(lexicographicSortSchema(schema));
|
|
118
|
+
const { TypeMetadataStorage } = require(path.join(graphqlDist, 'schema-builder', 'storages', 'type-metadata.storage.js'));
|
|
119
|
+
// A repository without class-validator has no validators, hence no bounds: only that package's absence is tolerated.
|
|
120
|
+
let validationStorage = { getTargetValidationMetadatas: () => [] };
|
|
121
|
+
try {
|
|
122
|
+
validationStorage = require(FROM_REPOSITORY.classValidator).getMetadataStorage();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error?.code !== 'MODULE_NOT_FOUND' || !String(error.message).includes(`'${FROM_REPOSITORY.classValidator}'`)) throw error;
|
|
125
|
+
}
|
|
126
|
+
const bounds = graphqlInputBounds({ schema, typeStorage: TypeMetadataStorage, validationStorage });
|
|
127
|
+
const document = parse(printed);
|
|
128
|
+
const bounded = withInputBounds(document, bounds);
|
|
129
|
+
process.stdout.write(bounded === document ? printed : print(bounded));
|
|
117
130
|
await context.close();
|
package/emit/type-schema.mjs
CHANGED
|
@@ -18,8 +18,12 @@ export function stable(value) {
|
|
|
18
18
|
|
|
19
19
|
const canonical = (value) => JSON.stringify(stable(value));
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* A builder over one checker; `components` fills as named object types are met. A schema-backed caller has already proved
|
|
23
|
+
* the wire schema is closed, so its inferred empty object is meaningful (for example z.strictObject({})). Other callers
|
|
24
|
+
* retain the strict type-only rule: an empty object type alone proves no wire shape.
|
|
25
|
+
*/
|
|
26
|
+
export function createSchemaBuilder({ ts, checker, schemaBacked = false }) {
|
|
23
27
|
const F = ts.TypeFlags;
|
|
24
28
|
const components = new Map();
|
|
25
29
|
const nameOf = new Map();
|
|
@@ -98,6 +102,12 @@ export function createSchemaBuilder({ ts, checker }) {
|
|
|
98
102
|
return { $ref: `#/components/schemas/${name}` };
|
|
99
103
|
}
|
|
100
104
|
|
|
105
|
+
function indexSchema(index, path) {
|
|
106
|
+
// z.strictObject({}) infers Record<string, never>: this index forbids keys rather than admitting a never-valued field.
|
|
107
|
+
if (schemaBacked && (index.type.flags & F.Never) !== 0) return false;
|
|
108
|
+
return schemaOf(index.type, `${path}[key]`);
|
|
109
|
+
}
|
|
110
|
+
|
|
101
111
|
function plainObject(type, path) {
|
|
102
112
|
const properties = {};
|
|
103
113
|
const required = [];
|
|
@@ -109,15 +119,17 @@ export function createSchemaBuilder({ ts, checker }) {
|
|
|
109
119
|
const kept = withoutUndefined(propType);
|
|
110
120
|
if (kept.length === 0) fail(`${path}.${prop.getName()}`, 'undefined is not a wire value');
|
|
111
121
|
const hadUndefined = kept.length < members.length;
|
|
112
|
-
|
|
122
|
+
const propertyPath = `${path}.${prop.getName()}`;
|
|
123
|
+
const schema = !hadUndefined ? schemaOf(propType, propertyPath) : kept.length === 1 ? schemaOf(kept[0], propertyPath) : membersSchema(kept, propertyPath);
|
|
124
|
+
properties[prop.getName()] = schema;
|
|
113
125
|
if (!optional && !hadUndefined) required.push(prop.getName());
|
|
114
126
|
}
|
|
115
127
|
const index = checker.getIndexInfosOfType(type).find((info) => info.keyType.flags & F.String);
|
|
116
|
-
if (props.length === 0 && !index) fail(path, 'an object type with no members and no index signature says nothing');
|
|
128
|
+
if (props.length === 0 && !index && !schemaBacked) fail(path, 'an object type with no members and no index signature says nothing');
|
|
117
129
|
const result = { type: 'object' };
|
|
118
130
|
if (props.length) result.properties = properties;
|
|
119
131
|
if (required.length) result.required = required;
|
|
120
|
-
if (index) result.additionalProperties =
|
|
132
|
+
if (index) result.additionalProperties = indexSchema(index, path);
|
|
121
133
|
return result;
|
|
122
134
|
}
|
|
123
135
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starci/hfs",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.33",
|
|
4
4
|
"description": "The StarCi app command implementation for scaffolding, linting, checking and synchronizing one product repository.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"private": false,
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/main.mjs",
|
|
10
|
+
"./form-bounds": "./emit/form-bounds.mjs",
|
|
10
11
|
"./package.json": "./package.json",
|
|
11
12
|
"./runtime/*": "./runtime/*"
|
|
12
13
|
},
|
|
@@ -15,13 +16,19 @@
|
|
|
15
16
|
"smol-toml": "1.9.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
19
|
+
"@graphql-codegen/cli": "7.4.6",
|
|
20
|
+
"@graphql-codegen/client-preset": "6.2.3",
|
|
18
21
|
"@nestjs/common": "11.2.5",
|
|
19
22
|
"@nestjs/core": "11.2.5",
|
|
23
|
+
"@nestjs/graphql": "13.4.5",
|
|
20
24
|
"@nestjs/testing": "11.2.5",
|
|
25
|
+
"class-validator": "0.15.1",
|
|
26
|
+
"graphql": "16.14.2",
|
|
21
27
|
"prettier": "3.9.6",
|
|
22
28
|
"reflect-metadata": "0.2.2",
|
|
23
29
|
"rxjs": "7.8.2",
|
|
24
|
-
"typescript": "5.9.3"
|
|
30
|
+
"typescript": "5.9.3",
|
|
31
|
+
"zod": "4.6.7"
|
|
25
32
|
},
|
|
26
33
|
"files": [
|
|
27
34
|
"LICENSE",
|
package/release/gate.mjs
CHANGED
|
@@ -16,7 +16,9 @@ import { sonarQualityGate } from './sonar-quality-gate.mjs';
|
|
|
16
16
|
export const RELEASE_CODE = 'RELEASE_CONFIG_INVALID';
|
|
17
17
|
const SONAR_UNREACHABLE = 'SONAR_UNREACHABLE';
|
|
18
18
|
|
|
19
|
-
const FIELDS = new Set(['gates', 'tagPrefix', 'defaultBranch', '
|
|
19
|
+
const FIELDS = new Set(['gates', 'tagPrefix', 'defaultBranch', 'requireCi']);
|
|
20
|
+
/** Release-block keys that no longer exist, each with what replaced it. */
|
|
21
|
+
const REMOVED_FIELDS = new Map([['tagMatchesVersion', 'the release tag is always <tagPrefix><package.json version>; delete the key']]);
|
|
20
22
|
/** The note a release that does not wait for CI carries in its L4 record and its push output (owner ruling release-require-ci-per-project). */
|
|
21
23
|
export const CI_NOT_REQUIRED = 'CI not required by release.requireCi=false (owner ruling release-require-ci-per-project): the local release gate, this L4 record and the annotated tag, is the release proof';
|
|
22
24
|
const GATE_FIELDS = new Set(['name', 'command', 'summary', 'env']);
|
|
@@ -36,7 +38,6 @@ const SCRIPT_GATES = ['format:check', 'typecheck', 'build:be', 'lint', 'db:lint'
|
|
|
36
38
|
const WORLD_SCRIPTS = ['test:integration', 'test:e2e', 'test:contract'];
|
|
37
39
|
const DB_TYPES_SCRIPTS = ['db:start', 'db:reset', 'db:types', 'db:stop'];
|
|
38
40
|
const DB_TYPES_COMMITTED = { name: 'db-types-committed', command: ['git', 'diff', '--exit-code', '--', 'supabase/types/database.types.ts'], summary: null, env: {} };
|
|
39
|
-
const MAX_PATCH = 100;
|
|
40
41
|
|
|
41
42
|
/** True for a branch or remote name git can take as one argument (no leading dash, only path-like characters). */
|
|
42
43
|
export const refName = (value) => typeof value === 'string' && REF_NAME.test(value);
|
|
@@ -133,21 +134,23 @@ function gatesOf(list, problems) {
|
|
|
133
134
|
export function normalizeRelease(block, scripts = {}, services = {}, options = {}) {
|
|
134
135
|
const raw = block ?? {};
|
|
135
136
|
if (!plain(raw)) return { problems: ['release: must be a mapping'] };
|
|
136
|
-
const problems =
|
|
137
|
+
const problems = [];
|
|
138
|
+
for (const key of Object.keys(raw)) {
|
|
139
|
+
if (REMOVED_FIELDS.has(key)) problems.push(`release.${key}: is removed (${REMOVED_FIELDS.get(key)})`);
|
|
140
|
+
else if (!FIELDS.has(key)) problems.push(`release.${key}: is not a field of the release block`);
|
|
141
|
+
}
|
|
137
142
|
const tagPrefix = raw.tagPrefix ?? 'v';
|
|
138
143
|
const defaultBranch = raw.defaultBranch ?? 'main';
|
|
139
|
-
const tagMatchesVersion = raw.tagMatchesVersion ?? false;
|
|
140
144
|
// The candidate CI of `release push` is an app's choice and off by default (owner ruling release-require-ci-per-project); the local gate never is.
|
|
141
145
|
const requireCi = raw.requireCi ?? false;
|
|
142
146
|
if (typeof tagPrefix !== 'string' || !PREFIX.test(tagPrefix)) problems.push(`release.tagPrefix: must match ${PREFIX}`);
|
|
143
147
|
if (!refName(defaultBranch)) problems.push(`release.defaultBranch: must match ${REF_NAME}`);
|
|
144
|
-
if (typeof tagMatchesVersion !== 'boolean') problems.push('release.tagMatchesVersion: must be true or false');
|
|
145
148
|
if (typeof requireCi !== 'boolean') problems.push('release.requireCi: must be true or false');
|
|
146
149
|
const quality = releaseServices(services, options);
|
|
147
150
|
const audit = auditGate(quality.audit);
|
|
148
151
|
const gates = raw.gates === undefined ? defaultGates(scripts, audit) : gatesOf(raw.gates, problems);
|
|
149
152
|
if (raw.gates !== undefined && audit && !gates.some((gate) => gate.name === 'audit')) gates.push(audit);
|
|
150
|
-
return problems.length === 0 ? { config: { gates, tagPrefix, defaultBranch,
|
|
153
|
+
return problems.length === 0 ? { config: { gates, tagPrefix, defaultBranch, requireCi, services: quality } } : { problems };
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
/* ---- git through the seam */
|
|
@@ -169,6 +172,9 @@ export function annotatedOnHead(ctx, prefix) {
|
|
|
169
172
|
return names.filter((name) => git(ctx, ['cat-file', '-t', `refs/tags/${name}`], { allowFail: true }).stdout === 'tag');
|
|
170
173
|
}
|
|
171
174
|
|
|
175
|
+
/** Every tag on HEAD whose name ends in a version, annotated or not, under any prefix: a release commit carries exactly one. */
|
|
176
|
+
const versionTagsOnHead = (ctx) => lines(git(ctx, ['tag', '--points-at', 'HEAD']).stdout).filter((name) => /[0-9]+[.][0-9]+[.][0-9]+$/u.test(name));
|
|
177
|
+
|
|
172
178
|
const dirtyPaths = (ctx) => lines(git(ctx, ['status', '--porcelain', '--untracked-files=normal']).stdout);
|
|
173
179
|
|
|
174
180
|
/** Tag names that exist locally or on the remote. */
|
|
@@ -179,24 +185,11 @@ function takenTags(ctx, prefix, remote) {
|
|
|
179
185
|
return new Set([...local, ...remoteNames]);
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
/**
|
|
183
|
-
function nextFree(prefix, version, taken) {
|
|
184
|
-
const [, major, minor] = VERSION.exec(version);
|
|
185
|
-
for (let patch = 0; patch < MAX_PATCH; patch += 1) {
|
|
186
|
-
const candidate = `${prefix}${major}.${minor}.${patch}`;
|
|
187
|
-
if (!taken.has(candidate)) return candidate;
|
|
188
|
-
}
|
|
189
|
-
throw refusal('RELEASE_NO_FREE_TAG', `no free tag ${prefix}${major}.${minor}.0 to ${prefix}${major}.${minor}.${MAX_PATCH - 1}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** The tag this release uses and its state: on-head (reused), free (will be created) or taken (exists on another commit). */
|
|
188
|
+
/** The tag this release uses is `<prefix><package version>`; its state is on-head (reused), free (will be created) or taken (exists on another commit). */
|
|
193
189
|
function chooseTag(ctx, config, { prefix, remote }) {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const tag = config.tagMatchesVersion ? exact : (onHead[0] ?? nextFree(prefix, config.version, taken));
|
|
198
|
-
if (onHead.includes(tag)) return { tag, state: 'on-head' };
|
|
199
|
-
return { tag, state: taken.has(tag) ? 'taken' : 'free' };
|
|
190
|
+
const tag = `${prefix}${config.version}`;
|
|
191
|
+
if (annotatedOnHead(ctx, prefix).includes(tag)) return { tag, state: 'on-head' };
|
|
192
|
+
return { tag, state: takenTags(ctx, prefix, remote).has(tag) ? 'taken' : 'free' };
|
|
200
193
|
}
|
|
201
194
|
|
|
202
195
|
/* ---- gate results */
|
|
@@ -344,6 +337,8 @@ function inspect(ctx, config, options) {
|
|
|
344
337
|
if (!PREFIX.test(prefix)) throw refusal('RELEASE_USAGE', `--tag-prefix must match ${PREFIX}`);
|
|
345
338
|
const chosen = chooseTag(ctx, config, { prefix, remote: 'origin' });
|
|
346
339
|
if (chosen.state === 'taken') red.push(`tag ${chosen.tag} already exists on another commit (local or remote)`);
|
|
340
|
+
const stray = versionTagsOnHead(ctx).filter((name) => name !== chosen.tag);
|
|
341
|
+
if (stray.length > 0) red.push(`HEAD already carries the release tag ${stray[0]} but package.json version ${config.version} gives ${chosen.tag}: a release commit carries exactly one tag, so set the version to match the tag or release a new commit`);
|
|
347
342
|
out.push(tagLine(chosen));
|
|
348
343
|
return { head, chosen, red, out };
|
|
349
344
|
}
|
package/release/index.mjs
CHANGED
|
@@ -53,7 +53,7 @@ function readPackage(root) {
|
|
|
53
53
|
try { return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); } catch { throw refusal('RELEASE_PACKAGE_UNREADABLE', 'package.json is not readable JSON'); }
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/** The normalised `release:` block (tolerant of absence) with the package version: { root, version, gates, tagPrefix, defaultBranch,
|
|
56
|
+
/** The normalised `release:` block (tolerant of absence) with the package version: { root, version, gates, tagPrefix, defaultBranch, requireCi }. */
|
|
57
57
|
function loadReleaseConfig(root) {
|
|
58
58
|
const pkg = readPackage(root);
|
|
59
59
|
if (typeof pkg.version !== 'string' || !VERSION.test(pkg.version)) throw refusal(RELEASE_CODE, 'package.json needs a version like 1.2.3 (the release tag derives from it)');
|
package/release/push.mjs
CHANGED
|
@@ -22,14 +22,13 @@ const greenRecord = (record, head, tag) => record?.head === head && record?.tag
|
|
|
22
22
|
&& Array.isArray(record.gates) && record.gates.length > 0 && record.gates.every((gate) => gate?.pass === true);
|
|
23
23
|
|
|
24
24
|
/** The release gate as the hook reads it: the annotated tag on HEAD whose L4 record names head and tag. Returns { tag } or { reason }. */
|
|
25
|
-
function gateState(ctx, head, prefix) {
|
|
25
|
+
function gateState(ctx, head, prefix, expected) {
|
|
26
26
|
const tags = annotatedOnHead(ctx, prefix);
|
|
27
|
-
if (tags.
|
|
27
|
+
if (!tags.includes(expected)) return { reason: `no ANNOTATED tag ${expected} (the package.json version) on HEAD${tags.length > 0 ? `, which carries ${tags.join(', ')}` : ''}: run starci app release gate --execute first` };
|
|
28
28
|
const file = path.join(releaseDir(ctx), `${head}.l4.json`);
|
|
29
29
|
if (!fs.existsSync(file)) return { reason: `no L4 record ${file}: run starci app release gate --execute first` };
|
|
30
30
|
const record = readRecord(file);
|
|
31
|
-
|
|
32
|
-
return tag ? { tag } : { reason: `the L4 record does not name head ${head.slice(0, 10)} and one of ${tags.join(', ')} with every gate green` };
|
|
31
|
+
return greenRecord(record, head, expected) ? { tag: expected } : { reason: `the L4 record does not name head ${head.slice(0, 10)} and ${expected} with every gate green` };
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
/** The remote branch tip, or null on an empty remote; an unreachable remote is a failure (the state cannot be judged). */
|
|
@@ -93,7 +92,7 @@ export async function releasePush(ctx, config, options = {}) {
|
|
|
93
92
|
const branch = config.defaultBranch;
|
|
94
93
|
assertNames(remote, branch);
|
|
95
94
|
const head = git(ctx, ['rev-parse', 'HEAD']).stdout;
|
|
96
|
-
const gate = gateState(ctx, head, config.tagPrefix);
|
|
95
|
+
const gate = gateState(ctx, head, config.tagPrefix, `${config.tagPrefix}${config.version}`);
|
|
97
96
|
const out = [`HEAD ${head}`, gateLine(gate)];
|
|
98
97
|
const data = { head, tag: gate.tag ?? null, remote, branch, refs: [], hook: null, pushed: false };
|
|
99
98
|
if (!gate.tag) return refuse(out, [gate.reason], data, 'nothing pushed.');
|