@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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
8
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
9
9
|
schema: starci/hfs-slots@3
|
|
10
|
-
version: 3.
|
|
10
|
+
version: 3.7.0
|
|
11
11
|
|
|
12
12
|
versioning:
|
|
13
13
|
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 3).
|
|
@@ -42,7 +42,9 @@ versioning:
|
|
|
42
42
|
# 3.5.0 is a minor (alpha.27): it ADDS the opt-in app.browser-config slot, the managed browser/tsconfig.json that type-checks the browser
|
|
43
43
|
# journey (enabled with app.browser by `browser: true`).
|
|
44
44
|
# 3.6.0 is a minor (alpha.30): it ADDS the opt-in be.feature.realtime.sse slot, the server-sent-events door of the realtime kind (owner decision
|
|
45
|
-
# 2026-10-24: lite may serve SSE, and SSE is the default door for one-way push), and lets fe.modules.api hold the SSE stream
|
|
45
|
+
# 2026-10-24: lite may serve SSE, and SSE is the default door for one-way push), and lets fe.modules.api hold the SSE stream operations (sse/).
|
|
46
|
+
# 3.7.0 is a minor (alpha.31): it ADDS the optional fe.transport.stream slot, the fetch reader of server-sent-events streams (the stream half of the
|
|
47
|
+
# transport client layer), and lets fe.hooks.swr hold the SWR subscription hooks (api/sse/subscriptions/useSubscribe<X>Swr.ts, one useSWRSubscription per stream operation).
|
|
46
48
|
|
|
47
49
|
# Field meaning, per slot.
|
|
48
50
|
# id stable name, never changes meaning inside a major.
|
|
@@ -109,15 +111,18 @@ testValues: [unit-beside, e2e, none]
|
|
|
109
111
|
# The editions a repository declares in hfs.json `edition` (absent means full). ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite IS
|
|
110
112
|
# the full architecture with Supabase backing services - the same monorepo, slots, CQRS shape and patterns (queue,
|
|
111
113
|
# event bus, saga, reactors, projection, realtime, webhooks, fenced job), declared in hfs.json the same way, and the same unit
|
|
112
|
-
# specs (unitRoles, logicRoles, 100 per file, @starci/jest-preset doubles, builders). What differs is ADAPTERS and DELIVERY
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
114
|
+
# specs (unitRoles, logicRoles, 100 per file, @starci/jest-preset doubles, builders). What differs is ADAPTERS and DELIVERY
|
|
115
|
+
# (ruling:R212/case-10): schema authority in supabase/migrations, the queue port on BullMQ over the Redis hfs.json
|
|
116
|
+
# sides.be.backing declares (as in full, fed by the Postgres outbox), event-bus ports on pgmq-backed topics or in-process
|
|
117
|
+
# delivery, cache and locks on Postgres or the declared Redis, schedules on pg_cron (+ pg_net) enqueueing through the queue
|
|
118
|
+
# outbox, storage and auth on Supabase. Realtime uses SSE or the socket.io gateway
|
|
119
|
+
# in the api process, which scales horizontally and stateless (memory hub for one replica or tests; the Redis hub and @socket.io/redis-adapter when Redis is declared, else the Postgres hub and @socket.io/postgres-adapter, for N replicas; websocket only, no sticky sessions). Integration and provider sandbox contract specs are optional;
|
|
116
120
|
# declared patterns owe their integration proofs (R284). Lite has no e2e world. Production uses declared docker-swarm under
|
|
117
|
-
# .starcistacks/production/infra/swarm/ (owner ruling 2026-10-14); no self-hosted database
|
|
121
|
+
# .starcistacks/production/infra/swarm/ (owner ruling 2026-10-14); lite runs no self-hosted database, and runs only the backing
|
|
122
|
+
# services sides.be.backing declares. The front end is full's (R97).
|
|
118
123
|
# Lite is this same manifest filtered: a slot whose `editions` lacks lite does not exist for a lite app, `litePresence` replaces
|
|
119
|
-
# presence and a `lite` map replaces the fields it names.
|
|
120
|
-
#
|
|
124
|
+
# presence and a `lite` map replaces the fields it names. The edition is chosen once at scaffold; there is no upgrade
|
|
125
|
+
# or downgrade between lite and full. Lite contracts are allowed (owner decision 2026-10-14).
|
|
121
126
|
editions: [full, lite]
|
|
122
127
|
|
|
123
128
|
# The two sides of an app: the folder of each is its profile name (be/, fe/). `reads` lists the only paths of the OTHER side a side
|
|
@@ -189,6 +194,9 @@ ruleParams:
|
|
|
189
194
|
# R259: btree_gist supplies UUID equality for the booking interval exclusion constraint.
|
|
190
195
|
# R259: unaccent is a first-party Supabase extension used by Vietnamese-content search functions.
|
|
191
196
|
extensions: [pgmq, pg_cron, pgcrypto, pg_trgm, vector, uuid-ossp, pg_net, supabase_vault, unaccent, btree_gist]
|
|
197
|
+
# R258 DB_CRON_OWNED: THE queue outbox of a Supabase schema (the relay hands its unsent rows to BullMQ, ruling:R212/case-10). A pg_cron
|
|
198
|
+
# job enqueues only by inserting one row into this table, writing only these columns, one of which is `queue`.
|
|
199
|
+
queueOutbox: {table: public.queue, columns: [queue, payload, created_at, correlation_id]}
|
|
192
200
|
common:
|
|
193
201
|
# HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft. Slot
|
|
194
202
|
# budgets (component.tsx 300, hooks 200, modules 400) are stricter than this cap where they apply.
|
|
@@ -210,12 +218,19 @@ ruleParams:
|
|
|
210
218
|
"node:https": [platform/http]
|
|
211
219
|
http: [platform/http]
|
|
212
220
|
https: [platform/http]
|
|
221
|
+
"@aws-sdk/client-s3": [integrations/object-storage] # the ObjectStorage port: one S3 adapter for MinIO, S3, Spaces and Supabase Storage
|
|
222
|
+
"@aws-sdk/s3-request-presigner": [integrations/object-storage]
|
|
213
223
|
cache-manager: [integrations/cache, integrations/redis]
|
|
214
224
|
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability
|
|
215
225
|
redis: [integrations/cache, integrations/redis]
|
|
216
|
-
ioredis: [integrations/cache, integrations/redis]
|
|
226
|
+
ioredis: [integrations/cache, integrations/redis, platform/realtime]
|
|
227
|
+
"@keyv/redis": [integrations/cache, integrations/redis]
|
|
228
|
+
cache-manager-redis-yet: [integrations/cache, integrations/redis]
|
|
229
|
+
cache-manager-ioredis-yet: [integrations/cache, integrations/redis] # the Redis hub and the sharded socket.io adapter of platform/realtime
|
|
217
230
|
bullmq: [platform/queue] # pattern queue: BullMQ jobs and job schedulers (the relay of the outbox into queues)
|
|
218
231
|
"@nestjs/bullmq": [platform/queue]
|
|
232
|
+
bull: [platform/queue]
|
|
233
|
+
"@nestjs/bull": [platform/queue]
|
|
219
234
|
kafkajs: [platform/event-bus] # pattern event-bus: Kafka is the only event transport
|
|
220
235
|
"@nestjs/schedule": [] # no @Cron / @Interval / @Timeout anywhere: a schedule is a BullMQ job scheduler of a queue
|
|
221
236
|
eventemitter2: []
|
|
@@ -224,6 +239,8 @@ ruleParams:
|
|
|
224
239
|
"node:timers": [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
225
240
|
"node:timers/promises": [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
226
241
|
winston: [platform/logging]
|
|
242
|
+
"node:async_hooks": [platform/logging]
|
|
243
|
+
nestjs-cls: []
|
|
227
244
|
dayjs: [platform/clock, platform/primitives]
|
|
228
245
|
moment: [platform/clock, platform/primitives]
|
|
229
246
|
date-fns-tz: [platform/clock, platform/primitives]
|
|
@@ -234,6 +251,9 @@ ruleParams:
|
|
|
234
251
|
socket.io: [platform/realtime]
|
|
235
252
|
graphql-subscriptions: [platform/realtime]
|
|
236
253
|
graphql-ws: [platform/realtime]
|
|
254
|
+
"@socket.io/postgres-adapter": [platform/realtime] # the stateless socket.io adapter of a scaled lite back end without Redis
|
|
255
|
+
"@socket.io/redis-adapter": [platform/realtime] # the sharded socket.io adapter of a scaled back end on the declared Redis
|
|
256
|
+
pg: [platform/realtime] # the LISTEN connection of the Postgres hub; every other query goes through TypeORM
|
|
237
257
|
# R87 BE_CQRS_SHAPE no-event-bus: the in-process event vocabulary the rule refuses. `imports` maps a module
|
|
238
258
|
# specifier to the member names that are an in-process event; `[]` flags the package itself (its emitter packages
|
|
239
259
|
# are the `[]` owners of infraOwners above). `classes` maps a module specifier to the class names that are an
|
|
@@ -249,8 +269,13 @@ ruleParams:
|
|
|
249
269
|
# R48 BE_SPEC_QUALITY spec-infra-double-from-kit: in a `<name>.service.spec.ts` a provider `{ provide: TOKEN, useValue: X }` takes X from
|
|
250
270
|
# the kit named here (the package root of `kit`). The token name is normalised to UPPER_SNAKE (`EntityManager` -> ENTITY_MANAGER), the
|
|
251
271
|
# FIRST entry whose `token` regex matches decides, and `fallback` decides for every other token. `forms` say what X may be:
|
|
272
|
+
# Inbox-like names resolve their token declaration first: genuine platform/inbox aliases keep fakeInbox, while a business service
|
|
273
|
+
# or queue producer containing Inbox keeps its collaborator double. An unresolved origin retains the lexical table choice.
|
|
252
274
|
# call = `double(...)`, new = `new Double(...)`, curried = `double(...)(...)`, object = a plain object literal (REAL values),
|
|
253
275
|
# primitive = a string/number/boolean literal, array = an array literal (multi-providers of doubles). A const bound to such a value counts.
|
|
276
|
+
# An entry may add `from`, a regex over the path of the file that DECLARES its port's token: the declaration decides, not the import written
|
|
277
|
+
# in the spec. A token declared in another module that ends alike is a different port (`CORRELATION_IDS` of logging is not `IDS` of ids), and
|
|
278
|
+
# the owner's token stays that port however it is re-exported or renamed. A token that cannot be resolved is judged by its spelling.
|
|
254
279
|
specDoubles:
|
|
255
280
|
kit: "@starci/jest-preset"
|
|
256
281
|
doubles:
|
|
@@ -263,7 +288,7 @@ ruleParams:
|
|
|
263
288
|
- {token: "(?:^|_)INBOX(?:_|$)", double: fakeInbox, forms: [call]}
|
|
264
289
|
- {token: "(?:^|_)CACHE(?:_|$)", double: fakeCache, forms: [call]}
|
|
265
290
|
- {token: "(?:^|_)(?:LOCK|LEASE|FENCE|HOLD)(?:_|$)", double: fakeLock, forms: [call]}
|
|
266
|
-
- {token: "(?:^|_)(?:IDS|ID_GENERATOR|ID_FACTORY)$", double: fakeIds, forms: [call]}
|
|
291
|
+
- {token: "(?:^|_)(?:IDS|ID_GENERATOR|ID_FACTORY)$", from: "/src/modules/platform/ids/", double: fakeIds, forms: [call]}
|
|
267
292
|
fallback: {double: mock, forms: [call, primitive, array]}
|
|
268
293
|
# R85 BE_RAW_INJECT injected-param-name: the names of the ports every class injects the same way (BE-CONVENTION 1.6: the parameter name
|
|
269
294
|
# is the camelCase of the port). An entry matches a constructor parameter that carries an injector and is annotated with a type
|
|
@@ -281,7 +306,7 @@ ruleParams:
|
|
|
281
306
|
# R89 BE_SOURCE_FORM: the closed role-suffix vocabulary of a source file name (<kebab-name>.<suffix>.ts; BE-CONVENTION 1.15)
|
|
282
307
|
# and the suffixes that are never a role. index.ts, main.ts, app.module.ts and the migrations of be.persistence are the
|
|
283
308
|
# only names outside it.
|
|
284
|
-
suffixes: [module, module-definition, decorators, options, config, connection, service, schema, command, query, handler, templates, compensation, saga-state, saga-step, saga, contracts, port, resolver, controller, gateway, webhook, subscription, consumer, cli, event, queue, projection, projection-entity, processor, step, input, type, args, request, response, mapper, policy, entity, sql, rows, error, messages, log-events, cache-keys, guard, interceptor, filter, client, spec, integration-spec, e2e-spec, contract-spec, global-setup, builder]
|
|
309
|
+
suffixes: [module, module-definition, decorators, options, config, connection, service, schema, command, query, handler, templates, compensation, saga-state, saga-step, saga, contracts, port, resolver, controller, gateway, webhook, subscription, consumer, cli, event, queue, projection, projection-entity, processor, step, input, type, args, request, response, mapper, policy, entity, sql, rows, error, messages, log-events, cache-keys, guard, interceptor, filter, client, adapter, spec, integration-spec, e2e-spec, contract-spec, global-setup, builder]
|
|
285
310
|
# R47 BE_CONTRACT_UNGUARDED: the world's shape vocabulary. `helper` names the function a contract spec imports to erase the values of a JSON
|
|
286
311
|
# payload (`shapeOf`); a contract spec proves a fake's fixtures by asserting helper(real) equals helper(fixture).
|
|
287
312
|
contractShape: {helper: shapeOf}
|
|
@@ -300,7 +325,10 @@ ruleParams:
|
|
|
300
325
|
# options, config, command, query, contracts, port, event, queue, entity, sql, rows, messages, log-events, error, decorators, cache-keys,
|
|
301
326
|
# projection-entity, connection, and every file with no role: index.ts, main.ts, migrations) is declaration or composition and is never measured.
|
|
302
327
|
# A unit spec beside such a file (`<name>.<role>.spec.ts`) is legal but not required: the file owes 100, not a spec of its own.
|
|
303
|
-
logicRoles: [service, schema, policy, projection, guard, mapper, client, filter, interceptor, processor, step, saga, saga-step, compensation, consumer, webhook]
|
|
328
|
+
logicRoles: [service, schema, policy, projection, guard, mapper, client, adapter, filter, interceptor, processor, step, saga, saga-step, compensation, consumer, webhook]
|
|
329
|
+
# R281 BE_SPEC_FORM_ALL_ROLES. THE logic roles whose file declares values Nest never constructs (a zod schema): its spec parses the declared
|
|
330
|
+
# value directly and builds no testing module; every other logic role is built through `Test.createTestingModule`.
|
|
331
|
+
valueRoles: [schema]
|
|
304
332
|
# R203 BE_FEATURE_THIN. THE roles a feature-tier file is judged thin by: a file `<name>.<role>.ts` of one of these roles inside a slot of tier
|
|
305
333
|
# feature maps parameters and makes ONE delegating call into modules/ (a service, the command or query bus, the event bus, a queue producer): no
|
|
306
334
|
# branch, loop, ternary, try or throw, no computation; orchestration (step order, compensation, claim, fencing, retry) lives in platform/*.
|
|
@@ -308,12 +336,14 @@ ruleParams:
|
|
|
308
336
|
# BE_PATTERN_SPEC_MISSING: a declared pattern (hfs.json sides.be.patterns) is proven on the test world. Each id below needs a test titled
|
|
309
337
|
# "<pattern>/<id>: ..." in an e2e or integration spec (be/src/tests/e2e, be/src/tests/integration).
|
|
310
338
|
patternScenarios:
|
|
339
|
+
logging: [correlation-survives-queue]
|
|
311
340
|
event-bus: [atomic-with-transaction, relay-only-after-commit, redelivery-then-dead-letter, duplicate-delivery-is-a-noop]
|
|
312
341
|
queue: [enqueue-atomic-with-transaction, relay-job-id-is-outbox-id, scheduler-fires-once]
|
|
313
342
|
webhooks: [bad-signature-is-refused, replay-is-refused, accepted-delivery-publishes-once]
|
|
314
|
-
realtime: [subscriber-receives-push, foreign-topic-is-isolated, disconnect-releases-subscription]
|
|
343
|
+
realtime: [subscriber-receives-push, foreign-topic-is-isolated, disconnect-releases-subscription, push-crosses-replicas, resume-after-reconnect]
|
|
315
344
|
fenced-job: [claim-bumps-token, zombie-fenced-out, redispatch-isolated]
|
|
316
345
|
projection: [recompute-is-idempotent, replay-rebuilds]
|
|
346
|
+
idempotency: [repeat-returns-same-outcome, reused-key-other-body-is-refused]
|
|
317
347
|
# BE_KIND_DECLARATION: the patterns an app that uses a trigger kind declares in hfs.json sides.be.patterns; the platform capabilities of those
|
|
318
348
|
# patterns (the slots of tier platform that name the pattern) must exist.
|
|
319
349
|
kindPatterns:
|
|
@@ -338,23 +368,29 @@ ruleParams:
|
|
|
338
368
|
api: {topic: api, variable: feature, trigger: api, patterns: [], needs: [service], defaults: {action: "@name"}}
|
|
339
369
|
cli: {topic: cli, variable: group, trigger: cli, patterns: [], needs: [service], wire: {file: src/features/cli/cli.module.ts, import: "./@@group@@/@@group@@.module", symbol: "@@Group@@Module"}}
|
|
340
370
|
bannedSuffixes: [use-case, repository, fixture, factory, store, worker, scheduler, cron, job, listener, dto, util, utils, helper, helpers, rules, types, constants, int-spec, harness-spec, test]
|
|
371
|
+
# ruling:R212/case-10 (R212 HFS_BACKING_UNDECLARED / HFS_BACKING_UNUSED, R276 STACKS_LITE_SELFHOSTED_DB): THE backing services a back
|
|
372
|
+
# end may declare in hfs.json sides.be.backing. `clients` are the libraries that consume the service: one of them as a dependency
|
|
373
|
+
# is its consumer, and under lite none of them may be a dependency while the service is undeclared. `patterns` are the declared
|
|
374
|
+
# patterns that need the service. `images` are the image repositories (the last path segment) a stack runs it from.
|
|
375
|
+
# `editions` limits admission: owner:lite-vector-is-pgvector keeps vector search on Supabase pgvector in lite.
|
|
376
|
+
backing:
|
|
377
|
+
redis: {clients: [bullmq, "@nestjs/bullmq", bull, "@nestjs/bull", ioredis, redis, "@keyv/redis", cache-manager-redis-yet, cache-manager-ioredis-yet, "@socket.io/redis-adapter"], patterns: [queue, fenced-job], images: [redis, redis-stack, redis-stack-server]}
|
|
378
|
+
qdrant: {clients: ["@qdrant/js-client-rest", "@qdrant/qdrant-js"], patterns: [], images: [qdrant], editions: [full]}
|
|
341
379
|
# Edition lite (ruleParams(manifest, 'be', 'lite') merges these keys over the base; a map merges one level deep): the schema
|
|
342
380
|
# authority is the Supabase migration tree (supabase/migrations/*.sql, forward-only), so a lite back end holds no TypeORM
|
|
343
381
|
# migration or entity under be/ (R34/R35 read schemaAuthority; be.persistence's lite overlay allows neither). The unit roles,
|
|
344
|
-
# logic roles and spec doubles are full's (owner ruling 2026-10-12 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4). infraOwners:
|
|
345
|
-
# platform/queue and
|
|
346
|
-
#
|
|
347
|
-
#
|
|
382
|
+
# logic roles and spec doubles are full's (owner ruling 2026-10-12 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4). infraOwners: full's owners,
|
|
383
|
+
# bullmq in platform/queue and ioredis/redis in integrations/cache and integrations/redis (ruling:R212/case-10: the queue is
|
|
384
|
+
# Redis in both editions; a backing library is legal only when hfs.json sides.be.backing declares its service, which
|
|
385
|
+
# HFS_BACKING_UNDECLARED checks), except Kafka, which is owned nowhere (platform/event-bus runs Supabase Queues (pgmq) through
|
|
386
|
+
# the shared EntityManager); realtime is the same socket.io gateway as full, run inside the api process, scaled horizontally and stateless (hub redis when
|
|
387
|
+
# Redis is declared, else hub postgres with @socket.io/postgres-adapter; no sticky sessions).
|
|
348
388
|
lite:
|
|
349
389
|
schemaAuthority: supabase
|
|
350
390
|
infraOwners:
|
|
351
|
-
bullmq: []
|
|
352
|
-
"@nestjs/bullmq": []
|
|
353
391
|
kafkajs: []
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
graphql-subscriptions: []
|
|
357
|
-
graphql-ws: []
|
|
392
|
+
"@qdrant/js-client-rest": []
|
|
393
|
+
"@qdrant/qdrant-js": []
|
|
358
394
|
|
|
359
395
|
slots:
|
|
360
396
|
|
|
@@ -730,7 +766,7 @@ slots:
|
|
|
730
766
|
- id: app.starcistacks
|
|
731
767
|
since: 3.1.0
|
|
732
768
|
profiles: [app]
|
|
733
|
-
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with Supabase backing;
|
|
769
|
+
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with Supabase backing; owner:dev-docker-prod-swarm keeps dependencies in Docker Linux containers in dev and the app and dependencies on Swarm in prod except hosted Supabase."
|
|
734
770
|
exemplifiedBy:
|
|
735
771
|
- {path: packages/hfs/templates/app/skeleton/.starcistacks/, edition: full}
|
|
736
772
|
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks/, edition: lite}
|
|
@@ -916,7 +952,7 @@ slots:
|
|
|
916
952
|
tracked: external
|
|
917
953
|
tier: none
|
|
918
954
|
tests: none
|
|
919
|
-
goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a job of a
|
|
955
|
+
goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a job of a queue (BullMQ on the declared Redis, its tick enqueued through the queue outbox) or a cli command on a schedule (pg_cron / pg_net)"
|
|
920
956
|
since: 2.1.0
|
|
921
957
|
|
|
922
958
|
# ----- side root (both sides): what a standalone repository root holds, less the app-root files ----------
|
|
@@ -1436,7 +1472,7 @@ slots:
|
|
|
1436
1472
|
since: 1.0.0
|
|
1437
1473
|
- id: be.feature.realtime
|
|
1438
1474
|
profiles: [be]
|
|
1439
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4) and ruling:R212/case-5 (owner decision 2026-10-24):
|
|
1475
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4) and ruling:R212/case-5 (owner decision 2026-10-24): both editions serve server-sent events (the default door for one-way push), the socket.io gateway (two-way) and the restricted GraphQL subscription (ruling:R212/case-7) from the api process (owner:lite-graphql-subscription-door), and scale on the hub of their declared infrastructure (BE-REALTIME-7)."
|
|
1440
1476
|
path: "src/features/realtime/<channel>/"
|
|
1441
1477
|
presence: opt-in # a push channel: reads and pushes only (an SSE controller, a socket gateway or a GraphQL subscription), no domain write
|
|
1442
1478
|
pattern: realtime # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/realtime.yaml)
|
|
@@ -1449,9 +1485,8 @@ slots:
|
|
|
1449
1485
|
tests: none
|
|
1450
1486
|
lite:
|
|
1451
1487
|
why: >-
|
|
1452
|
-
a push channel
|
|
1453
|
-
|
|
1454
|
-
Redis adapter; the front end listens through a use<Channel>Stream hook over modules/api/sse, or hooks/socketio (knowledge/patterns/be/realtime.yaml, liteAdapter)
|
|
1488
|
+
a push channel with the three doors of both editions: server-sent events (be.feature.realtime.sse, the default for one-way push, owner decision 2026-10-24), the socket.io gateway (two-way, owner ruling 2026-10-13) and the restricted GraphQL subscription (ruling:R212/case-7); all run inside the be api process, which scales horizontally and stateless
|
|
1489
|
+
on the hub of its declared infrastructure; the front end listens through a useSubscribe<Channel>Swr hook over modules/api/sse, or hooks/socketio (knowledge/patterns/be/realtime.yaml, liteAdapter)
|
|
1455
1490
|
coverage: none
|
|
1456
1491
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1457
1492
|
since: 1.0.0
|
|
@@ -1459,14 +1494,11 @@ slots:
|
|
|
1459
1494
|
- {path: packages/hfs/src/hfs-stacks-swarm.spec.mjs, edition: lite}
|
|
1460
1495
|
- id: be.feature.realtime.graphql
|
|
1461
1496
|
profiles: [be]
|
|
1462
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite pushes through the SSE door or the in-process socket.io gateway; the GraphQL subscription door remains full-only."
|
|
1463
1497
|
path: "src/features/realtime/<channel>/transport/graphql/"
|
|
1464
|
-
presence: opt-in # a GraphQL subscription over graphql-ws
|
|
1465
|
-
litePresence: forbidden # ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite pushes through the api's SSE door or its in-process socket.io gateway
|
|
1498
|
+
presence: opt-in # a GraphQL subscription over graphql-ws, served in both editions (ruling:R212/case-7) and restricted: only when the client already uses GraphQL and needs the typed schema payload (knowledge/patterns/be/realtime.yaml)
|
|
1466
1499
|
pattern: realtime
|
|
1467
1500
|
tracked: tracked
|
|
1468
1501
|
tier: feature
|
|
1469
|
-
goesTo: "src/features/realtime/<channel>/transport/sse/ (be.feature.realtime.sse, the default for one-way push) or transport/websocket/ (be.feature.realtime.websocket): lite runs either door in the api process"
|
|
1470
1502
|
requires: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts"]
|
|
1471
1503
|
allows: ["<channel>-graphql.module.ts", "<channel>-graphql.module-definition.ts", "<channel>-graphql.options.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
|
|
1472
1504
|
tests: unit-beside # the door's own spec beside the door (a subscription has no service of its own)
|
|
@@ -1476,14 +1508,11 @@ slots:
|
|
|
1476
1508
|
since: 1.0.0
|
|
1477
1509
|
- id: be.feature.realtime.dto
|
|
1478
1510
|
profiles: [be]
|
|
1479
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): GraphQL subscription DTOs exist only with the full subscription door; lite uses SSE or socket.io contracts."
|
|
1480
1511
|
path: "src/features/realtime/<channel>/transport/graphql/dto/"
|
|
1481
1512
|
presence: opt-in
|
|
1482
|
-
litePresence: forbidden # the dto of the graphql door, which lite does not serve
|
|
1483
1513
|
pattern: realtime
|
|
1484
1514
|
tracked: tracked
|
|
1485
1515
|
tier: feature
|
|
1486
|
-
goesTo: "src/features/realtime/<channel>/transport/sse/ (be.feature.realtime.sse, the default for one-way push) or transport/websocket/ (be.feature.realtime.websocket): lite runs either door in the api process"
|
|
1487
1516
|
allows: ["<name>.{input,type,args}.ts"]
|
|
1488
1517
|
tests: none
|
|
1489
1518
|
coverage: none
|
|
@@ -1647,13 +1676,28 @@ slots:
|
|
|
1647
1676
|
# resolved through the RequestLocale provider (request: stored user preference, else Accept-Language, else
|
|
1648
1677
|
# vi; non-request: recipient's stored preference, else vi - see knowledge/patterns/be/messages.yaml);
|
|
1649
1678
|
# database when a connection exists.
|
|
1650
|
-
requiredInstances: {capability: [config,
|
|
1679
|
+
requiredInstances: {capability: [config, errors, primitives, clock, i18n]}
|
|
1651
1680
|
requires: [index.ts]
|
|
1652
1681
|
forbids: [common/, utils/, shared/, testing/] # primitives live in platform/primitives
|
|
1653
1682
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
|
|
1654
1683
|
coverage: required
|
|
1655
1684
|
budget: {indexExports: 60}
|
|
1656
1685
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
|
|
1686
|
+
- id: be.platform.logging
|
|
1687
|
+
since: 3.5.0
|
|
1688
|
+
profiles: [be]
|
|
1689
|
+
path: "src/modules/platform/logging/"
|
|
1690
|
+
presence: required
|
|
1691
|
+
pattern: logging
|
|
1692
|
+
tracked: tracked
|
|
1693
|
+
tier: platform
|
|
1694
|
+
owner: true
|
|
1695
|
+
requires: [index.ts]
|
|
1696
|
+
forbids: [common/, utils/, shared/, testing/]
|
|
1697
|
+
tests: unit-beside
|
|
1698
|
+
coverage: required
|
|
1699
|
+
budget: {indexExports: 60}
|
|
1700
|
+
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
|
|
1657
1701
|
# ----- BE: pattern slots (opt-in; enabled by hfs.json sides.be.patterns). A path under one is HFS_SLOT_NOT_ENABLED unless its pattern is declared.
|
|
1658
1702
|
- id: be.platform.event-bus
|
|
1659
1703
|
since: 3.1.0
|
|
@@ -1670,7 +1714,7 @@ slots:
|
|
|
1670
1714
|
the same EventPublisher and EventConsumer ports on Supabase Queues (pgmq): the outbox relay sends each committed row to the
|
|
1671
1715
|
pgmq queue of every consumer group of its topic through the shared EntityManager, a consumer reads with a visibility
|
|
1672
1716
|
timeout, retries by letting it lapse and archives the message on dead letter (read_ct over the budget); a single api may
|
|
1673
|
-
deliver in process. No Kafka (knowledge/patterns/be/
|
|
1717
|
+
deliver in process. No Kafka (knowledge/patterns/be/event-bus.yaml liteAdapter)
|
|
1674
1718
|
requires: [index.ts, event-bus.module.ts, event-bus.port.ts, event-bus.contracts.ts, event-bus.service.ts, event-bus.service.spec.ts]
|
|
1675
1719
|
tests: unit-beside
|
|
1676
1720
|
coverage: required
|
|
@@ -1681,7 +1725,7 @@ slots:
|
|
|
1681
1725
|
- id: be.platform.queue
|
|
1682
1726
|
since: 3.1.0
|
|
1683
1727
|
profiles: [be]
|
|
1684
|
-
editionWhy: "ruling:R212/case-
|
|
1728
|
+
editionWhy: "ruling:R212/case-10: both editions implement the queue ports with BullMQ on Redis; lite declares its Redis in hfs.json sides.be.backing and keeps the outbox table in a supabase migration."
|
|
1685
1729
|
path: "src/modules/platform/queue/"
|
|
1686
1730
|
presence: opt-in
|
|
1687
1731
|
pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
|
|
@@ -1690,10 +1734,10 @@ slots:
|
|
|
1690
1734
|
owner: true
|
|
1691
1735
|
lite:
|
|
1692
1736
|
why: >-
|
|
1693
|
-
the same QueueOutbox and QueueWorkerRegistry ports
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1737
|
+
the same QueueOutbox and QueueWorkerRegistry ports and full's BullMQ adapter on the declared Redis (backing: [redis]): the
|
|
1738
|
+
relay adds each committed outbox row with the outbox id as the job id, workers retry with exponential backoff and keep a
|
|
1739
|
+
job that exhausts its attempts as failed (the dead letter); the outbox table comes from a supabase migration, and a
|
|
1740
|
+
schedule is a BullMQ job scheduler or a pg_cron tick that inserts one outbox row (knowledge/patterns/be/queues.yaml liteAdapter)
|
|
1697
1741
|
requires: [index.ts, queue.module.ts, queue.port.ts, queue.contracts.ts, queue-outbox.service.ts, queue-outbox.service.spec.ts]
|
|
1698
1742
|
tests: unit-beside
|
|
1699
1743
|
coverage: required
|
|
@@ -1786,7 +1830,7 @@ slots:
|
|
|
1786
1830
|
- id: be.platform.jobs
|
|
1787
1831
|
since: 3.1.0
|
|
1788
1832
|
profiles: [be]
|
|
1789
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite persists fenced claims in Supabase migrations and dispatches through
|
|
1833
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite persists fenced claims in Supabase migrations and dispatches through the BullMQ queue on its declared Redis (ruling:R212/case-10); full uses TypeORM schema authority and the same queue adapter."
|
|
1790
1834
|
path: "src/modules/platform/jobs/"
|
|
1791
1835
|
presence: opt-in
|
|
1792
1836
|
pattern: fenced-job # the ONE fencing primitive: the job entity, the claim that bumps the token, guarded writes (knowledge/patterns/be/jobs.yaml)
|
|
@@ -2138,8 +2182,8 @@ slots:
|
|
|
2138
2182
|
rules: [FE_HOOKS_ARE_HOOKS, FE_DATA_FRESHNESS]
|
|
2139
2183
|
- id: fe.hooks.swr
|
|
2140
2184
|
profiles: [fe]
|
|
2141
|
-
# The data hooks (fe-stack data, FE-DATA-SWR): one useSWR / useSWRMutation wrapper per modules/api operation, keyed
|
|
2142
|
-
# QUERY_<X>_SWR_KEY / MUTATE_<X>_SWR_KEY plus the identity of the request; SwrSideEffects.tsx runs the app-wide hooks once.
|
|
2185
|
+
# The data hooks (fe-stack data, FE-DATA-SWR): one useSWR / useSWRMutation / useSWRSubscription wrapper per modules/api operation, keyed
|
|
2186
|
+
# QUERY_<X>_SWR_KEY / MUTATE_<X>_SWR_KEY / SUBSCRIBE_<X>_SWR_KEY plus the identity of the request; SwrSideEffects.tsx runs the app-wide hooks once.
|
|
2143
2187
|
path: "apps/<app>/src/hooks/swr/"
|
|
2144
2188
|
presence: optional
|
|
2145
2189
|
tracked: tracked
|
|
@@ -2148,7 +2192,9 @@ slots:
|
|
|
2148
2192
|
requires: [index.ts]
|
|
2149
2193
|
allows: [index.ts, SwrSideEffects.tsx, api/index.ts, "api/{graphql,rest}/index.ts", "api/{graphql,rest}/{queries,mutations}/index.ts",
|
|
2150
2194
|
"api/graphql/queries/useQuery*Swr.ts", "api/graphql/mutations/useMutate*Swr.ts",
|
|
2151
|
-
"api/rest/queries/useGet*Swr.ts", "api/rest/mutations/use{Post,Put,Patch,Delete}*Swr.ts"
|
|
2195
|
+
"api/rest/queries/useGet*Swr.ts", "api/rest/mutations/use{Post,Put,Patch,Delete}*Swr.ts",
|
|
2196
|
+
"api/graphql/subscriptions/index.ts", "api/graphql/subscriptions/useSubscribe*Swr.ts",
|
|
2197
|
+
"api/sse/index.ts", "api/sse/subscriptions/index.ts", "api/sse/subscriptions/useSubscribe*Swr.ts"]
|
|
2152
2198
|
roles: {entry: index.ts, effects: SwrSideEffects.tsx}
|
|
2153
2199
|
tests: none
|
|
2154
2200
|
budget: {file: 200, barrel: 400}
|
|
@@ -2271,18 +2317,19 @@ slots:
|
|
|
2271
2317
|
tier: transport
|
|
2272
2318
|
# The browser's calls to the back end (owner ruling 2026-10-13, fe-stack graphql/rest/codegen, FE-API-GRAPHQL): the auth and
|
|
2273
2319
|
# no-auth Apollo clients with their link chain (graphql/clients), one file per operation (graphql/queries/query-<x>.ts,
|
|
2274
|
-
# graphql/mutations/mutation-<x>.ts with its gql document map and typed request/response in types/), the generated wire types
|
|
2320
|
+
# graphql/mutations/mutation-<x>.ts and graphql/subscriptions/subscription-<x>.ts, each with its gql document map and typed request/response in types/), the generated wire types
|
|
2275
2321
|
# (graphql/__generated__), the axios calls (rest/<area>/*.ts) for uploads, presigned URLs and auth endpoints and the fetch readers of
|
|
2276
|
-
# server-sent-events streams (sse/<area
|
|
2322
|
+
# server-sent-events streams (sse/<area>/stream-<channel>.ts, one operation per stream, run on the stream reader of sse/clients). A repository has
|
|
2277
2323
|
# exactly ONE client folder and ONE GraphQLResponse union: the app's (fe.transport.client/outcome) when one app calls the back end,
|
|
2278
2324
|
# or the api package's (fe.package.api.client/outcome) when several do (FE_TRANSPORT_OWNER, machine).
|
|
2279
2325
|
requires: [index.ts]
|
|
2280
2326
|
allows: [index.ts, graphql/index.ts, graphql/types.ts, "graphql/clients/**", "graphql/queries/query-*.ts", "graphql/queries/types/*.ts",
|
|
2281
|
-
"graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts", "graphql/
|
|
2282
|
-
"
|
|
2327
|
+
"graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts", "graphql/subscriptions/subscription-*.ts", "graphql/subscriptions/types/*.ts",
|
|
2328
|
+
"graphql/__generated__/**", "rest/index.ts", "rest/<area>/*.ts",
|
|
2329
|
+
"sse/clients/**", "sse/*/stream-*.ts"]
|
|
2283
2330
|
roles: {entry: index.ts, query: "query-*.ts", mutation: "mutation-*.ts"}
|
|
2284
2331
|
# A hook imports its one operation file directly (one file per operation, FE-API-GRAPHQL), never through a barrel of every operation.
|
|
2285
|
-
entries: ["graphql/queries/query-*.ts", "graphql/queries/types/*.ts", "graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts", "sse
|
|
2332
|
+
entries: ["graphql/queries/query-*.ts", "graphql/queries/types/*.ts", "graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts", "graphql/subscriptions/subscription-*.ts", "graphql/subscriptions/types/*.ts", "sse/*/stream-*.ts"]
|
|
2286
2333
|
tests: none
|
|
2287
2334
|
rules: [FE_TRANSPORT_OWNER, FE_HTTP_STATUS_COLLAPSE, FE_WIRE_GENERATED]
|
|
2288
2335
|
- id: fe.transport.generated
|
|
@@ -2307,6 +2354,19 @@ slots:
|
|
|
2307
2354
|
tests: none
|
|
2308
2355
|
why: the auth and no-auth Apollo clients and their link chain (retry, error, timeout, token refresh, attach access token, http) of the one app that calls the back end; when several apps do, the shared client is fe.package.api.client
|
|
2309
2356
|
rules: [FE_TRANSPORT_OWNER]
|
|
2357
|
+
- id: fe.transport.stream
|
|
2358
|
+
since: 3.7.0
|
|
2359
|
+
profiles: [fe]
|
|
2360
|
+
path: "apps/<app>/src/modules/api/sse/clients/"
|
|
2361
|
+
presence: optional
|
|
2362
|
+
tracked: tracked
|
|
2363
|
+
tier: transport
|
|
2364
|
+
allows: ["*.ts"]
|
|
2365
|
+
tests: none
|
|
2366
|
+
why: "the fetch reader of server-sent-events streams of the one app that calls the back end, the stream half of the transport client layer (FE-REALTIME): it opens the stream with the bearer access token, parses the events and reopens it with Last-Event-ID after a capped, doubling wait; a stream operation (sse/<area>/stream-<channel>.ts) runs on it and a useSubscribe hook of hooks/swr reads that operation; a fetch here asks for text/event-stream and only a stream operation imports the reader (FE_TRANSPORT_OWNER); a repository where several apps stream keeps one reader in each of them"
|
|
2367
|
+
rules: [FE_TRANSPORT_OWNER]
|
|
2368
|
+
exemplifiedBy:
|
|
2369
|
+
- {path: packages/eslint/fe/r50-transport-owner-coverage.spec.mjs, editions: [lite, full]}
|
|
2310
2370
|
- id: fe.transport.outcome
|
|
2311
2371
|
since: 3.1.0
|
|
2312
2372
|
profiles: [fe]
|
|
@@ -318,7 +318,7 @@ rules:
|
|
|
318
318
|
- id: BE-OPERATIONS-1
|
|
319
319
|
title: A versioned operation is declared once in the app's typed operation table
|
|
320
320
|
kind: mandatory
|
|
321
|
-
hfsRules: [R95, R88, R23, R278]
|
|
321
|
+
hfsRules: [R95, R88, R23, R278, R304, R305]
|
|
322
322
|
requirement: |-
|
|
323
323
|
A versioned operation `<name>@<version>` (for example `sales.policy@1`) that an app serves over one operation route is
|
|
324
324
|
declared once, in the app's operation table, which `be/apps/<app>/src/operations.ts` exports as `OPERATIONS`. A feature cannot
|
|
@@ -326,38 +326,63 @@ rules:
|
|
|
326
326
|
domain capability that owns the operations, and the app file re-exports it from the capability's public surface. The table is made by
|
|
327
327
|
`defineOperations` of the platform capability `operations` (`be/src/modules/platform/operations/`, which declares
|
|
328
328
|
`OperationContract<Input, Output, RefusalCode>`, `query`, `mutation`, `defineOperations`, `OperationRequest<Table>` and
|
|
329
|
-
`OperationReply<Table>`)
|
|
330
|
-
`
|
|
331
|
-
|
|
329
|
+
`OperationReply<Table>`) and the `OperationValidationPolicy` pipe. Every entry supplies `query({ input, output, refusal? })` or
|
|
330
|
+
`mutation({ input, output, refusal? })`: input and output are named zod schema exports from bounded `*.schema.ts` files
|
|
331
|
+
in a domain, platform or integrations owner (R304/R305). The schemas infer the closed input and output types; never
|
|
332
|
+
`any`, `unknown`, `Record<string, unknown>`, `object`, an unbound generic, a function or `Date`. `refusal` is a readonly
|
|
333
|
+
list of literal codes, or omitted for an operation that never refuses. The one route of the app takes
|
|
334
|
+
`@Body(new OperationValidationPolicy(OPERATIONS)) request: OperationRequest<Table>` and answers
|
|
332
335
|
`Promise<OperationReply<Table>>` of the same table; the wire is `{ operation, requestId, input }` in and `{ operation, requestId,
|
|
333
336
|
outcome }` out, `outcome` being the canon Outcome `{ kind: "ok", value }` or `{ kind: "refused", code }`. `starci app emit`
|
|
334
|
-
derives `be/contracts/<app>/openapi.json` (OpenAPI 3.1, `x-operation: "<name>@<version>"`, a oneOf over the operations)
|
|
335
|
-
|
|
337
|
+
derives `be/contracts/<app>/openapi.json` (OpenAPI 3.1, `x-operation: "<name>@<version>"`, a oneOf over the operations) with
|
|
338
|
+
`z.toJSONSchema`: input uses the schema's input representation and output its output representation. Refusal codes
|
|
339
|
+
remain a closed union checked by TypeScript. `OperationRequest<Table>` describes parsed input, inferred from the input
|
|
340
|
+
schema's output type; defaults and transforms must agree with what the handler receives. Parsed input and output remain
|
|
341
|
+
closed JSON types, including after a transform: `any`, `unknown` and `Date` are still refused independently of the wire
|
|
342
|
+
schema. Root `undefined` and `void` cannot fill the request input or reply value; optional object fields remain valid.
|
|
343
|
+
Emission evaluates only the schema declarations, whose runtime imports are
|
|
344
|
+
zod or other `*.schema.ts` modules; bound constants belong in those declaration modules too. Type-only imports are
|
|
345
|
+
erased. Emission never runs app boot or table module side effects and refuses open schemas (a non-strict `z.object`; use `z.strictObject`) or Zod-unrepresentable
|
|
346
|
+
values, and publishes a fixed-format string such as `z.uuid()` with its real maximum length. String, array and dictionary bounds must survive conversion as `maxLength`, `maxItems` and `maxProperties`;
|
|
347
|
+
a custom count refinement that Zod drops is refused rather than emitted as an unbounded dictionary. Prefer finite
|
|
348
|
+
object fields for operation dictionaries. Custom refinements do not automatically become JSON Schema constraints.
|
|
349
|
+
Enable `be.contract.openapi` in `hfs.json` under `sides.be.optionalSlots` to own the emitted snapshot. The full `starci app check` fails
|
|
350
|
+
when the committed file is stale.
|
|
351
|
+
`OperationRequest<Table>` is erased at runtime and does not give ValidationPipe a DTO to validate. The platform-owned
|
|
352
|
+
operation pipe validates the envelope, the registered operation name and the selected input schema before dispatch,
|
|
353
|
+
passes the parsed input onward, and maps malformed requests to `OPERATIONS_INVALID_REQUEST`, an invalid AbstractException
|
|
354
|
+
answered as HTTP 400 with `{ code, kind, message, params }` by the canon error filter. A plain generic annotation or a
|
|
355
|
+
pipe for another table proves no validation. Keep zod imports in the schema
|
|
356
|
+
declaration files; the platform pipe bounds both envelope identifiers, `operation` and `requestId`, to 1..128 characters, the value the emitted contract states. Do not import zod in the transport. Specs validate successful
|
|
357
|
+
operation output against the declared output schema and cover refused input and declared outcomes; all logic-role files
|
|
358
|
+
have 100% coverage. The emitted contract is the FE boundary; FE never imports BE schemas and has no specs.
|
|
336
359
|
A public REST operation in either edition belongs to this nonempty table, and its route decorator supplies its own
|
|
337
360
|
versioned-operation evidence; a class-level reference does not justify a route absent from the table.
|
|
338
|
-
The body of the route is typed by an alias, which leaves no class at run time, so the global pipe validates nothing there.
|
|
339
|
-
The route therefore binds the body to `OperationValidationService` of `platform/http-security`, given a request class that
|
|
340
|
-
mirrors the envelope (`@IsIn` on `operation`, a bounded `requestId`, a `@ValidateNested()` input class with the bounds of
|
|
341
|
-
BE-AUTH-5), and the owning domain service still refuses an input outside its own bounds.
|
|
342
361
|
rationale: |-
|
|
343
362
|
The operation set of an app is a contract the front end (`fe/`, through the root codegen) and other consumers generate a client from. A name-to-string registry with `unknown`
|
|
344
363
|
bodies and results forces every client to hand-type the wire; one typed table gives the client generator, the route and the
|
|
345
|
-
handlers the same types.
|
|
364
|
+
handlers the same types. Runtime schema parsing closes the generic ValidationPipe gap and carries actual bounds into
|
|
365
|
+
the emitted contract instead of losing them when TypeScript erases the declaration.
|
|
346
366
|
cases:
|
|
347
367
|
- id: case-1
|
|
348
|
-
when: A query
|
|
368
|
+
when: A versioned query served through the schema-validated table
|
|
349
369
|
write: |-
|
|
350
|
-
// modules/domain/
|
|
370
|
+
// modules/domain/order/order-list.schema.ts (R304/R305)
|
|
371
|
+
export const OrdersListInputSchema = z.strictObject({
|
|
372
|
+
first: z.number().int().min(1).max(100),
|
|
373
|
+
after: z.string().max(512).nullish(),
|
|
374
|
+
})
|
|
375
|
+
export const OrdersListOutputSchema = z.strictObject({ items: z.array(OrderSummarySchema).max(100).readonly(), ... })
|
|
376
|
+
// modules/domain/order/order-operations.contracts.ts
|
|
351
377
|
export const OPERATIONS = defineOperations({
|
|
352
|
-
"
|
|
353
|
-
"sales.close@1": mutation<SalesCloseAsk, SalesOpportunityView>(),
|
|
378
|
+
"orders.list@1": query({ input: OrdersListInputSchema, output: OrdersListOutputSchema, refusal: [OrderErrorCode.PageSizeInvalid] }),
|
|
354
379
|
})
|
|
355
|
-
// be/apps/
|
|
356
|
-
export { OPERATIONS } from "@modules/domain/
|
|
357
|
-
// features/api/
|
|
380
|
+
// be/apps/order/src/operations.ts
|
|
381
|
+
export { OPERATIONS } from "@modules/domain/order"
|
|
382
|
+
// features/api/checkout/transport/http/operations.controller.ts
|
|
358
383
|
@Post("operations")
|
|
359
384
|
async invoke(
|
|
360
|
-
@Body(new
|
|
385
|
+
@Body(new OperationValidationPolicy(OPERATIONS)) request: OperationRequest<typeof OPERATIONS>,
|
|
361
386
|
): Promise<OperationReply<typeof OPERATIONS>> { ... }
|
|
362
387
|
verification:
|
|
363
388
|
automated:
|
|
@@ -366,9 +391,10 @@ rules:
|
|
|
366
391
|
- HFS_CONTRACT_SNAPSHOT_DRIFT
|
|
367
392
|
- BE_REST_DOOR_EVIDENCE
|
|
368
393
|
- BE_REST_ROUTE_UNDECLARED
|
|
394
|
+
- BE_SCHEMA_PLACE
|
|
395
|
+
- BE_SCHEMA_BOUNDED
|
|
369
396
|
manual:
|
|
370
397
|
- Confirm each refusal code is one the operation's owner can actually answer.
|
|
371
|
-
- Confirm that the request class mirrors the envelope and the input bounds of the table.
|
|
372
398
|
relatedExamples: [operations]
|
|
373
399
|
files:
|
|
374
400
|
- path: src/features/api/<feature>/index.ts
|