@starci/hfs 4.2.17 → 4.4.0
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 +28 -0
- package/README.md +7 -0
- package/db/migrate.mjs +361 -0
- package/db/post-cutover.mjs +136 -0
- package/emit/db-types.mjs +10 -1
- package/lint/run.mjs +1 -1
- package/package.json +4 -1
- package/release/gate.mjs +298 -0
- package/release/index.mjs +103 -0
- package/release/push.mjs +110 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/rules.yaml +3437 -160
- package/runtime/knowledge/hfs/slots.yaml +430 -92
- package/runtime/knowledge/patterns/be/api.yaml +92 -2
- package/runtime/knowledge/patterns/be/cli.yaml +37 -9
- package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
- package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
- package/runtime/knowledge/patterns/be/projections.yaml +4 -2
- package/runtime/knowledge/patterns/be/queues.yaml +11 -3
- package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
- package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
- package/runtime/knowledge/patterns/be/saga.yaml +20 -3
- package/runtime/knowledge/patterns/be/webhooks.yaml +210 -36
- package/runtime/knowledge/patterns/fe/folder.yaml +4 -0
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/failure-codes.yaml +3128 -47
- package/runtime/modules/models/runtimes.yaml +1 -1
- package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
- package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
- package/runtime/packages/cli/src/validate-args.mjs +213 -0
- package/runtime/scripts/api/git/add.mjs +5 -0
- package/runtime/scripts/api/git/cat-file.mjs +5 -0
- package/runtime/scripts/api/git/init.mjs +7 -0
- package/runtime/scripts/api/git/lib.mjs +3 -0
- package/runtime/scripts/api/git/tag.mjs +5 -0
- package/runtime/scripts/api/node/lib.mjs +20 -0
- package/runtime/scripts/api/node/run-node.mjs +6 -0
- package/runtime/scripts/api/sops/lib.mjs +41 -0
- package/runtime/scripts/api/sops/resolve-sops.mjs +7 -0
- package/runtime/scripts/api/sops/seal.mjs +1 -1
- package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
- package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
- package/runtime/scripts/api/supabase/database-query.mjs +5 -0
- package/runtime/scripts/api/supabase/lib.mjs +44 -0
- package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
- package/runtime/scripts/hfs/app-wiring.mjs +201 -0
- package/runtime/scripts/hfs/architecture/backend-app-files.mjs +103 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +22 -10
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +157 -41
- package/runtime/scripts/hfs/architecture/canon-clones.mjs +240 -0
- package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +143 -0
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
- package/runtime/scripts/hfs/architecture/clones.mjs +17 -8
- package/runtime/scripts/hfs/architecture/connection-map.mjs +14 -12
- package/runtime/scripts/hfs/architecture/context-map.mjs +25 -14
- package/runtime/scripts/hfs/architecture/context-owner.mjs +1 -2
- package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +23 -50
- package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
- package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +104 -0
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +30 -11
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
- package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +16 -3
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +17 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +49 -25
- package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
- package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
- package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
- package/runtime/scripts/hfs/architecture/register-once.mjs +15 -7
- package/runtime/scripts/hfs/architecture/required-files.mjs +2 -1
- package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
- package/runtime/scripts/hfs/architecture/staged-consumers.mjs +37 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +214 -17
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -6
- package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
- package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
- package/runtime/scripts/hfs/architecture/tiers.mjs +11 -4
- package/runtime/scripts/hfs/architecture/typescript.mjs +1 -8
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +9 -13
- package/runtime/scripts/hfs/check-results.mjs +28 -0
- package/runtime/scripts/hfs/check.mjs +63 -72
- package/runtime/scripts/hfs/coverage-from.mjs +70 -0
- package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
- package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
- package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
- package/runtime/scripts/hfs/doc-commands.mjs +367 -0
- package/runtime/scripts/hfs/edition-slots.mjs +2 -2
- package/runtime/scripts/hfs/example-secrets.mjs +158 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +91 -6
- package/runtime/scripts/hfs/path-findings.mjs +32 -2
- package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
- package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
- package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
- package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
- package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
- package/runtime/scripts/hfs/rules/assets.mjs +79 -0
- package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
- package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
- package/runtime/scripts/hfs/rules/contract.mjs +66 -5
- package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
- package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
- package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
- package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
- package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +317 -37
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
- package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
- package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
- package/runtime/scripts/hfs/rules/database.mjs +11 -2
- package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
- package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
- package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
- package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
- package/runtime/scripts/hfs/rules/docker.mjs +24 -7
- package/runtime/scripts/hfs/rules/edition.mjs +11 -10
- package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
- package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
- package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
- package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
- package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
- package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
- package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
- package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -0
- package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
- package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
- package/runtime/scripts/hfs/rules/saga.mjs +8 -16
- package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
- package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +26 -0
- package/runtime/scripts/hfs/rules/services.mjs +4 -4
- package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
- package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
- package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
- package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +168 -0
- package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
- package/runtime/scripts/hfs/rules/stacks.mjs +13 -8
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
- package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
- package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
- package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
- package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
- package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
- package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
- package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
- package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
- package/runtime/scripts/hfs/sealed-members.mjs +101 -0
- package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
- package/runtime/scripts/hfs/secret-document.mjs +97 -0
- package/runtime/scripts/hfs/secret-edit.mjs +170 -0
- package/runtime/scripts/hfs/secret-errors.mjs +20 -0
- package/runtime/scripts/hfs/secret-files.mjs +124 -0
- package/runtime/scripts/hfs/secret-init.mjs +300 -0
- package/runtime/scripts/hfs/secret-output.mjs +100 -0
- package/runtime/scripts/hfs/secret-sops.mjs +130 -0
- package/runtime/scripts/hfs/secret-store.mjs +163 -0
- package/runtime/scripts/hfs/secret.mjs +162 -193
- package/runtime/scripts/hfs/slot-imports.mjs +18 -3
- package/runtime/scripts/hfs/slot-required.mjs +13 -0
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +2 -1
- package/runtime/scripts/hfs/slots.mjs +1 -0
- package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
- package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
- package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
- package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
- package/runtime/scripts/hfs/tree-findings.mjs +22 -0
- package/runtime/scripts/lib/command-tokens.mjs +126 -0
- package/runtime/scripts/lib/compose-values.mjs +11 -0
- package/runtime/scripts/lib/deployment-findings.mjs +7 -0
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/list.mjs +4 -0
- package/runtime/scripts/lib/roots.mjs +59 -0
- package/runtime/scripts/lib/runtime-example.mjs +13 -0
- package/runtime/scripts/lib/secret-patterns.mjs +60 -5
- package/runtime/scripts/lib/sql-ast.mjs +32 -0
- package/runtime/scripts/lib/stack-services.mjs +1 -1
- package/runtime/scripts/lib/swarm-stack.mjs +126 -0
- package/runtime/scripts/lib/trim-end.mjs +8 -0
- package/runtime/scripts/lib/ts-analysis.mjs +23 -0
- package/runtime/scripts/lib/ts-ast.mjs +27 -2
- package/runtime/scripts/lib/workflow-events.mjs +8 -0
- package/scaffold/add-app.mjs +77 -59
- package/scaffold/add-cli-lite.mjs +24 -17
- package/scaffold/add-compose.mjs +79 -0
- package/scaffold/add-database-lite.mjs +14 -0
- package/scaffold/add-fe-overlay.mjs +122 -0
- package/scaffold/add-fe-page.mjs +269 -0
- package/scaffold/add-inbox-lite.mjs +87 -124
- package/scaffold/add-plan.mjs +183 -0
- package/scaffold/add-table.mjs +132 -70
- package/scaffold/add.mjs +186 -204
- package/scaffold/app.mjs +128 -43
- package/scaffold/edition-gate.mjs +3 -3
- package/scaffold/image.mjs +2 -2
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +2 -2
- package/src/main.mjs +238 -119
- package/stack/age-install.mjs +37 -0
- package/stack/backup.mjs +100 -0
- package/stack/build-args.mjs +42 -0
- package/stack/build.mjs +173 -0
- package/stack/cli.mjs +130 -0
- package/stack/config.mjs +57 -0
- package/stack/context.mjs +57 -0
- package/stack/converge.mjs +107 -0
- package/stack/deploy.mjs +301 -0
- package/stack/digest.mjs +16 -0
- package/stack/docker-transport.mjs +53 -0
- package/stack/edge-alias.mjs +118 -0
- package/stack/errors.mjs +21 -0
- package/stack/image-scan.mjs +152 -0
- package/stack/ingress-create.mjs +114 -0
- package/stack/ingress-publish.mjs +273 -0
- package/stack/ingress.mjs +706 -0
- package/stack/jobs.mjs +51 -0
- package/stack/live-restore.mjs +177 -0
- package/stack/prepare.mjs +266 -0
- package/stack/private-file.mjs +22 -0
- package/stack/provision.mjs +90 -0
- package/stack/prune.mjs +46 -0
- package/stack/release-store.mjs +87 -0
- package/stack/remote.mjs +205 -0
- package/stack/render.mjs +149 -0
- package/stack/retire.mjs +47 -0
- package/stack/sealed.mjs +149 -0
- package/stack/task.mjs +42 -0
- package/stack/verify.mjs +99 -0
- package/sync/ci-commands.mjs +13 -0
- package/sync/index.mjs +127 -64
- package/sync/stack-images.mjs +127 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
- package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/codegen-graphql.mjs.partial +13 -2
- package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
- package/templates/app/codegen-prelude.mjs.partial +8 -0
- package/templates/app/gitignore +4 -0
- package/templates/app/gitignore-lite +4 -0
- package/templates/app/hooks/husky/pre-commit +7 -12
- package/templates/app/package-scripts/package.json +1 -0
- package/templates/app/package-scripts/supabase.json +7 -0
- package/templates/app/package-scripts-lite/package.json +1 -7
- package/templates/app/skeleton/.editorconfig +1 -0
- package/templates/app/skeleton/.gitattributes +7 -1
- package/templates/app/skeleton/.github/dependabot.yml +17 -0
- package/templates/app/skeleton/.nvmrc +1 -1
- package/templates/app/skeleton/.sops.yaml +1 -1
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
- package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
- package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
- package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
- package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
- package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/README.md +6 -6
- package/templates/app/skeleton/scripts/codegen.mjs +1 -2
- package/templates/app/skeleton-lite/.editorconfig +16 -0
- package/templates/app/skeleton-lite/.gitattributes +8 -0
- package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
- package/templates/app/skeleton-lite/.nvmrc +1 -0
- package/templates/app/skeleton-lite/.sops.yaml +3 -2
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +193 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
- package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
- package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
- package/templates/app/skeleton-lite/.starciwork/workspace.yaml +9 -0
- package/templates/app/skeleton-lite/README.md +8 -4
- package/templates/app/skeleton-lite/supabase/config.toml +8 -1
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
- package/templates/app/starciwork.gitignore-lite +6 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/inbox/database-client.spec.ts.tpl +22 -28
- package/templates/be/inbox/database-client.ts.tpl +14 -28
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
- package/templates/be/inbox/event-index.ts.tpl +1 -0
- package/templates/be/inbox/events.json.tpl +10 -0
- package/templates/be/inbox/migration.sql.tpl +1 -0
- package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
- package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
- package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
- package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
- package/templates/be/inbox/platform/migration.sql.tpl +48 -0
- package/templates/be/inbox/service.spec.ts.tpl +49 -20
- package/templates/be/inbox/service.ts.tpl +27 -16
- package/templates/be/inbox/sql.ts.tpl +2 -2
- package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -3
- package/templates/be/inbox/webhook-door.ts.tpl +4 -3
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
- package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
- package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
- package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
- package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
- package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
- package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
- package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
- package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
- package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
- package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
- package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
- package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
- package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +3 -3
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +3 -3
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
- package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
- package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
- package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
- package/templates/be/skeleton/apps/__app__/src/__app__.env.ts +21 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +1 -1
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -45
- package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
- package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +44 -0
- package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
- package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/main.ts +18 -10
- package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
- package/templates/be/skeleton/apps/cli/src/cli.env.ts +13 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +7 -17
- package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +3 -2
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
- package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
- package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
- package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
- package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
- package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +6 -6
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
- package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
- package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
- package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +25 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +1 -1
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +18 -58
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +47 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +5 -13
- package/templates/be/skeleton-lite/apps/cli/src/cli.env.ts +17 -0
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
- package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/feature.module.ts.tpl +2 -1
- package/templates/be/table/api/feature.options.ts.tpl +2 -0
- package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/graphql.module.ts.tpl +3 -2
- package/templates/be/table/api/graphql.options.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +1 -0
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
- package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
- package/templates/fe/auth/supabase-client.ts.tpl +34 -0
- package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
- package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
- package/upgrade/check.mjs +3 -1
- package/upgrade/file-state.mjs +68 -0
- package/upgrade/index.mjs +58 -94
- package/runtime/scripts/api/git/log.mjs +0 -5
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +0 -55
|
@@ -3,25 +3,34 @@ id: be.feature.webhooks
|
|
|
3
3
|
title: Provider webhooks
|
|
4
4
|
purpose: |-
|
|
5
5
|
Given a delivery a payment, identity or shipping provider POSTs to us, how is it received? A webhook is a feature KIND:
|
|
6
|
-
`be/src/features/webhooks/<provider>/` holds one signed door
|
|
7
|
-
replay window),
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
`be/src/features/webhooks/<provider>/` holds one signed door whose first call proves the delivery came from the provider
|
|
7
|
+
(signature and replay window), either separately or inside its ONE domain method that combines verification and intake. The
|
|
8
|
+
intake records the delivery (deduplicated through the inbox) and publishes a typed receipt event with
|
|
9
|
+
`eventBus.publish(event, tx)` inside its own transaction, because publishing is allowed only from `modules/domain`.
|
|
10
|
+
A reactor or job consumes the durable receipt and dispatches the processing command. Neither the door, its HTTP module,
|
|
11
|
+
nor its called domain intake dispatches on a command/query bus or feature handler. A provider may receive a declared fixed `PROVIDER_ACK` body;
|
|
12
|
+
a subscription provider may also use one GET verifier that echoes its challenge result.
|
|
10
13
|
appliesTo:
|
|
11
14
|
- backend
|
|
12
15
|
family: be
|
|
13
16
|
provenance:
|
|
14
|
-
reviewedAt: 2026-10-
|
|
17
|
+
reviewedAt: 2026-10-14
|
|
15
18
|
sources:
|
|
16
19
|
- knowledge/hfs/README.md
|
|
17
20
|
- knowledge/hfs/slots.yaml
|
|
18
21
|
- knowledge/hfs/rules.yaml
|
|
19
22
|
- knowledge/patterns/be/api-auth.yaml
|
|
20
23
|
- knowledge/patterns/be/idempotency.yaml
|
|
24
|
+
- knowledge/patterns/be/event-bus.yaml
|
|
25
|
+
- knowledge/patterns/be/reactors.yaml
|
|
26
|
+
- knowledge/patterns/be/jobs.yaml
|
|
21
27
|
- owner decision 2026-10-02 (feature kinds: webhooks, realtime, reactors, saga, jobs, cli; no kind imports another)
|
|
28
|
+
- owner decision 2026-10-14 (literal provider ACK, one optional GET challenge, durable inbox handoff before reactor/job command dispatch)
|
|
22
29
|
limitations: |-
|
|
23
|
-
Static checks prove the door's shape, that
|
|
24
|
-
one
|
|
30
|
+
Static checks prove the door's shape, that an unconditional verification call is its first statement and that it makes
|
|
31
|
+
one intake call, either after a separate proof or combined with it. They resolve direct command/query-bus and feature-handler receivers in the called
|
|
32
|
+
domain intake; helper calls, dynamic receivers and unavailable method bodies need semantic review. They do not prove that a domain verifier completes proof
|
|
33
|
+
before intake, that the signature scheme matches the provider's documentation, that the replay window
|
|
25
34
|
is the right length or that the intake is idempotent; the provider fake of the test world and the three
|
|
26
35
|
`webhooks/*` scenarios prove those against the real stack.
|
|
27
36
|
rules:
|
|
@@ -33,7 +42,8 @@ rules:
|
|
|
33
42
|
A provider webhook lives in `be/src/features/webhooks/<provider>/` (slot `be.feature.webhooks`), enabled by declaring the pattern
|
|
34
43
|
`webhooks` in `hfs.json` `sides.be.patterns`. The folder holds `index.ts` (the module class an app composes), `transport/http/<provider>-http.module.ts`, `transport/http/<provider>.webhook.ts` (the
|
|
35
44
|
door), `<provider>.webhook.spec.ts` (its spec — the kind's door has no service of its own, so the door carries it) and an optional `transport/http/dto/` of typed
|
|
36
|
-
payload classes (`<event>.request.ts`).
|
|
45
|
+
payload classes (`<event>.request.ts`). `PROVIDER_ACK`, when needed, is an exported typed literal constant in the door or
|
|
46
|
+
its own HTTP module. Nothing else exists there: no application layer, no handler, no consumer. The kind
|
|
37
47
|
imports only `modules/*`, never another kind, and only an api-kind app composes `<provider>-http.module.ts`.
|
|
38
48
|
rationale: |-
|
|
39
49
|
A webhook is an external caller we do not control, so its door is kept apart from the api features a client uses. One place
|
|
@@ -76,24 +86,130 @@ rules:
|
|
|
76
86
|
- Confirm the provider endpoint is one door and that a second endpoint of the same provider is a second door only when it has a different proof.
|
|
77
87
|
relatedExamples: [webhooks]
|
|
78
88
|
- id: BE-WEBHOOK-2
|
|
79
|
-
title: The door verifies first
|
|
89
|
+
title: The door verifies first and hands the delivery to one domain intake
|
|
80
90
|
kind: mandatory
|
|
81
|
-
hfsRules: [R182, R183, R41, R42, R203]
|
|
91
|
+
hfsRules: [R182, R183, R41, R42, R203, R263]
|
|
82
92
|
requirement: |-
|
|
83
|
-
`<provider>.webhook.ts` is one `@Controller` with one `@Post` handler
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
`<provider>.webhook.ts` is one `@Controller` with exactly one `@Post` handler and optionally one `@Get` subscription
|
|
94
|
+
challenge handler. Each handler is declared
|
|
95
|
+
`@Public({ reason: PublicReason.SignedWebhook })`, thin (R203 `BE_FEATURE_THIN`). The handler's FIRST statement is an unconditional
|
|
96
|
+
direct verification call, awaited when async: either `this.<signature>.verify({ provider, rawBody, signature, timestamp })`
|
|
97
|
+
of the `WebhookSignatureService` of `platform/http-security` or a domain verifier. No data access, nested call or other work
|
|
98
|
+
may run in the door before that proof call. The shared service uses HMAC-SHA256 over `<timestamp>.<raw body>`, a constant-time
|
|
99
|
+
comparison and a replay window on the signed timestamp; its provider key, secret and tolerance are `platform/http-security`
|
|
100
|
+
options (`options.webhooks[provider]`). A domain verifier owns the provider's proof when secrets belong to individual
|
|
101
|
+
connections, as for the billing payment webhooks; it refuses an invalid delivery before intake.
|
|
102
|
+
A domain verifier is a method on a service declared in a domain `<name>.service.ts` whose declared class name ends in
|
|
103
|
+
`Verifier` or `verify`, or an exact domain service method declared by the adjacent `<provider>-http.module.ts` as
|
|
104
|
+
`export const webhookVerifiers = [BillingService.prototype.receiveCassoWebhook] as const`. The array names TypeScript
|
|
105
|
+
method symbols, never strings; a local alias or a class from the platform, integrations or a package grants no domain proof.
|
|
106
|
+
A separate verification call is followed by exactly one domain intake call. A domain method that verifies and then performs
|
|
107
|
+
intake may be the first and only call; a separate domain verification call does not count as intake. The door injects only
|
|
108
|
+
domain services and, when used, the signature service (injected with `@InjectWebhookSignature()`), holds no branch, loop or
|
|
109
|
+
`try`, and calls nothing else (no parsing, mapping or publishing). Neither the door nor its HTTP module imports or injects
|
|
110
|
+
`CommandBus`, `QueryBus` from `@nestjs/cqrs`, or a feature command handler. The called domain intake must not dispatch on
|
|
111
|
+
a command or query bus or feature handler either; it records the inbox receipt and outbox event so a reactor or job can dispatch after commit.
|
|
112
|
+
The POST handler returns nothing (an empty body) or exactly `return PROVIDER_ACK`. That exported constant belongs to the
|
|
113
|
+
same webhook feature, is declared with `as const` or a readonly literal type, and contains only literal object values or
|
|
114
|
+
a literal string. Calls, interpolated templates, spreads, input-derived values, inline returned objects and constants of
|
|
115
|
+
another feature are refused. A provider acknowledgement does not weaken the first-statement proof requirement.
|
|
116
|
+
The optional GET starts with a declared domain verifier's challenge call, awaited when async, and returns exactly that
|
|
117
|
+
call's result: `const challenge = this.verifier.challenge(query); return challenge`, or directly returns that call.
|
|
118
|
+
It makes no intake call and computes no other response. `@RateLimit(RateTier.Strict)` is required on both signed-webhook handlers.
|
|
119
|
+
`SignedWebhook` metadata belongs only to this HTTP door or the exact domain verifier named in its module's exported
|
|
120
|
+
`webhookVerifiers`. A public route whose path contains webhook, callback or events belongs in the webhook door too.
|
|
90
121
|
rationale: |-
|
|
91
|
-
The signature is the authentication of a public door. Making
|
|
92
|
-
|
|
122
|
+
The signature is the authentication of a public door. Making verification the first statement removes every door path that
|
|
123
|
+
skips it; a combined domain method must finish that proof before intake. Keeping one intake and no decision leaves the door
|
|
124
|
+
with only the proof and the hand-over to test.
|
|
93
125
|
cases:
|
|
94
126
|
- id: case-1
|
|
95
127
|
when: A delivery whose raw body must be hashed
|
|
96
128
|
write: 'enable the raw body for the app (`NestFactory.create(..., { rawBody: true })` in the api app) and read `request.rawBody`; never hash a re-serialized `body`.'
|
|
129
|
+
- id: case-2
|
|
130
|
+
when: A payment provider uses a different secret for each billing connection
|
|
131
|
+
write: 'declare BillingService.prototype.receiveCassoWebhook in the adjacent HTTP module webhookVerifiers array; the first and only handler statement awaits that method, which resolves the connection secret, verifies the provider proof and delegates to the intake. The door never reads the connection or payment data itself.'
|
|
132
|
+
- id: case-3
|
|
133
|
+
when: 'A provider requires exactly {"success": true} instead of an empty acknowledgement'
|
|
134
|
+
write: |-
|
|
135
|
+
In the webhook door's own file declare `export const PROVIDER_ACK = { success: true } as const`.
|
|
136
|
+
After the first verification call and the one domain intake call, return `PROVIDER_ACK` with the provider's required
|
|
137
|
+
status code. `return { success: true }`, `return { success: delivery.accepted }`, a spread, a computed constant,
|
|
138
|
+
and an ACK imported from another feature remain findings.
|
|
139
|
+
- id: case-4
|
|
140
|
+
when: A Meta-style provider uses GET subscription verification and POST deliveries
|
|
141
|
+
write: |-
|
|
142
|
+
Worked tree (the webhook and reactor are separate kinds; the app composes their modules):
|
|
143
|
+
be/src/features/webhooks/meta/index.ts
|
|
144
|
+
be/src/features/webhooks/meta/transport/http/meta-http.module.ts
|
|
145
|
+
be/src/features/webhooks/meta/transport/http/meta.webhook.ts
|
|
146
|
+
be/src/features/webhooks/meta/transport/http/meta.webhook.spec.ts
|
|
147
|
+
be/src/features/webhooks/meta/transport/http/dto/meta-challenge.request.ts
|
|
148
|
+
be/src/features/webhooks/meta/transport/http/dto/meta-event.request.ts
|
|
149
|
+
be/src/modules/domain/meta/index.ts
|
|
150
|
+
be/src/modules/domain/meta/meta-verifier.service.ts
|
|
151
|
+
be/src/modules/domain/meta/meta.service.ts
|
|
152
|
+
be/src/modules/events/core/meta-delivery-recorded.event.ts
|
|
153
|
+
be/src/features/reactors/meta-intake/index.ts
|
|
154
|
+
be/src/features/reactors/meta-intake/meta-intake.module.ts
|
|
155
|
+
be/src/features/reactors/meta-intake/application/process-meta-delivery.command.ts
|
|
156
|
+
be/src/features/reactors/meta-intake/application/process-meta-delivery.handler.ts
|
|
157
|
+
be/src/features/reactors/meta-intake/application/process-meta-delivery.contracts.ts
|
|
158
|
+
be/src/features/reactors/meta-intake/transport/message/meta-delivery-recorded.consumer.ts
|
|
159
|
+
be/src/features/reactors/meta-intake/transport/message/meta-intake-message.module.ts
|
|
160
|
+
|
|
161
|
+
meta.webhook.ts:
|
|
162
|
+
import { Body, Controller, Get, Headers, HttpCode, HttpStatus, Post, Query, Req } from "@nestjs/common"
|
|
163
|
+
import type { RawBodyRequest } from "@nestjs/common"
|
|
164
|
+
import type { Request } from "express"
|
|
165
|
+
import { Public, PublicReason } from "@modules/domain/identity"
|
|
166
|
+
import { MetaService, MetaVerifier } from "@modules/domain/meta"
|
|
167
|
+
import { RateLimit, RateTier } from "@modules/platform/http-security"
|
|
168
|
+
import { MetaChallengeRequest } from "./dto/meta-challenge.request"
|
|
169
|
+
import { MetaEventRequest } from "./dto/meta-event.request"
|
|
170
|
+
|
|
171
|
+
export const PROVIDER_ACK = "EVENT_RECEIVED" as const
|
|
172
|
+
|
|
173
|
+
@Controller("webhooks/meta")
|
|
174
|
+
export class MetaWebhook {
|
|
175
|
+
constructor(private readonly verifier: MetaVerifier, private readonly deliveries: MetaService) {}
|
|
176
|
+
|
|
177
|
+
@Get()
|
|
178
|
+
@Public({ reason: PublicReason.SignedWebhook })
|
|
179
|
+
@RateLimit(RateTier.Strict)
|
|
180
|
+
challenge(@Query() query: MetaChallengeRequest): string {
|
|
181
|
+
const challenge = this.verifier.challenge(query)
|
|
182
|
+
return challenge
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@Post()
|
|
186
|
+
@HttpCode(HttpStatus.OK)
|
|
187
|
+
@Public({ reason: PublicReason.SignedWebhook })
|
|
188
|
+
@RateLimit(RateTier.Strict)
|
|
189
|
+
async receive(
|
|
190
|
+
@Req() request: RawBodyRequest<Request>,
|
|
191
|
+
@Headers("x-hub-signature-256") signature: string | undefined,
|
|
192
|
+
@Body() delivery: MetaEventRequest,
|
|
193
|
+
): Promise<typeof PROVIDER_ACK> {
|
|
194
|
+
await this.verifier.verify({ rawBody: request.rawBody, signature })
|
|
195
|
+
await this.deliveries.recordMetaDelivery(delivery)
|
|
196
|
+
return PROVIDER_ACK
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
meta-http.module.ts:
|
|
201
|
+
import { Module } from "@nestjs/common"
|
|
202
|
+
import { MetaWebhook } from "./meta.webhook"
|
|
203
|
+
|
|
204
|
+
@Module({ controllers: [MetaWebhook] })
|
|
205
|
+
export class MetaHttpModule {}
|
|
206
|
+
|
|
207
|
+
MetaVerifier is declared in meta-verifier.service.ts. Its challenge method proves the subscription mode and verify
|
|
208
|
+
token before returning the challenge field unchanged; its verify method owns Meta's signature proof and delivery
|
|
209
|
+
freshness policy. MetaService.recordMetaDelivery only records the delivery and its outbox receipt in one transaction
|
|
210
|
+
(case-1 of BE-WEBHOOK-3). The reactor consumes that receipt and dispatches ProcessMetaDeliveryCommand after commit.
|
|
211
|
+
The provider's real GET and POST proof schemes belong in the verifier, rather than being assumed to match the shared
|
|
212
|
+
timestamp/HMAC convention. The api app composes MetaHttpModule; an api or worker app composes MetaIntakeMessageModule.
|
|
97
213
|
verification:
|
|
98
214
|
automated:
|
|
99
215
|
- BE_WEBHOOK_SHAPE
|
|
@@ -101,47 +217,81 @@ rules:
|
|
|
101
217
|
- BE_DEFAULT_DENY
|
|
102
218
|
- BE_INPUT_BOUNDED
|
|
103
219
|
- BE_FEATURE_THIN
|
|
220
|
+
- BE_WEBHOOK_SCOPE
|
|
221
|
+
- BE_WEBHOOK_INBOX_KEY
|
|
104
222
|
manual:
|
|
105
|
-
- Review that the raw body, not a re-serialized one, is verified,
|
|
223
|
+
- Review that the raw body, not a re-serialized one, is verified, that the replay window matches the provider's tolerance, and that a domain verifier refuses the delivery before intake or business data access.
|
|
224
|
+
- Trace intake helpers and dynamic dispatch that static resolution cannot inspect; none may dispatch a command before the receipt is durable.
|
|
106
225
|
relatedExamples: [webhooks]
|
|
107
226
|
- id: BE-WEBHOOK-3
|
|
108
|
-
title: The intake
|
|
227
|
+
title: The intake records the inbox receipt; a reactor or job dispatches after commit
|
|
109
228
|
kind: mandatory
|
|
110
|
-
hfsRules: [R80, R152, R151]
|
|
229
|
+
hfsRules: [R80, R152, R151, R263]
|
|
111
230
|
requirement: |-
|
|
112
|
-
The
|
|
231
|
+
The sanctioned path is `verify -> inbox.record -> reactor/job dispatches the command`. `inbox.record` describes the
|
|
232
|
+
domain intake's durable recording responsibility, not a new method on the shared `Inbox` port: the real port has `claim`.
|
|
233
|
+
The domain intake method (`PaymentService.recordPaymentDelivery`) is the delivery method of R80: its first awaited expression
|
|
113
234
|
claims `(source, eventId)` in the `Inbox` and it returns when the claim answers false, so a redelivery is a no-op. It then
|
|
114
|
-
records the
|
|
235
|
+
records the provider payload or a durable receipt reference and calls
|
|
236
|
+
`this.eventBus.publish(PaymentDeliveryRecordedEvent.create({...}), manager)` inside the same
|
|
115
237
|
transaction (`entityManager.transaction`). The events are typed classes in `modules/events/<service>/<event>.event.ts` that
|
|
116
|
-
equal their `be/contracts/<service>/events.json` entry (`payment.
|
|
117
|
-
|
|
238
|
+
equal their `be/contracts/<service>/events.json` entry (`payment.delivery-recorded`). The webhook door never imports
|
|
239
|
+
`modules/events` or the event bus. The door, HTTP module and called intake do not reach a command or query bus or feature
|
|
240
|
+
handler. A reactor consumes the receipt event and dispatches its own command, forwarding the delivered `eventId` and
|
|
241
|
+
receipt reference; its domain service claims that separate consumer delivery before processing the receipt. A job may
|
|
242
|
+
instead consume a transactionally enqueued receipt reference under the queue and fenced-job patterns.
|
|
243
|
+
This responsibility follows every intake identified by its first `*Id` parameter, a DTO delivery-id property or raw-body
|
|
244
|
+
signature input. The inbox key is the stable provider delivery id, and a duplicate returns before work. No `process`
|
|
245
|
+
callback, domain processing or command dispatch runs inside the receipt transaction; those run only after commit.
|
|
118
246
|
rationale: |-
|
|
119
247
|
Recording the delivery and writing the outbox row in one transaction means a crash cannot lose or duplicate the event, and
|
|
120
248
|
keeping the publish in `modules/domain` keeps one place that owns the transaction boundary.
|
|
121
249
|
cases:
|
|
122
250
|
- id: case-1
|
|
123
|
-
when: A
|
|
124
|
-
write:
|
|
251
|
+
when: A payment provider retries a delivery whose processing belongs in a reactor
|
|
252
|
+
write: |-
|
|
253
|
+
The POST handler verifies first, then calls PaymentService.recordPaymentDelivery(delivery) once. That domain method
|
|
254
|
+
runs entityManager.transaction(async (manager) => { ... }): its first awaited statement claims
|
|
255
|
+
this.inbox.claim(PAYMENT_SOURCE, delivery.eventId, manager), and a false answer returns. With the same manager it
|
|
256
|
+
saves the durable receipt and publishes PaymentDeliveryRecordedEvent carrying that receipt's id. Its method never
|
|
257
|
+
dispatches CommandBus.execute. The outbox relay delivers the committed event to
|
|
258
|
+
features/reactors/payment-intake/transport/message/payment-delivery-recorded.consumer.ts:
|
|
259
|
+
|
|
260
|
+
async handle(delivery: EventDelivery<PaymentDeliveryRecordedEvent>): Promise<void> {
|
|
261
|
+
await this.commandBus.execute(new ProcessPaymentDeliveryCommand({
|
|
262
|
+
request: { eventId: delivery.eventId, receiptId: delivery.event.payload.receiptId },
|
|
263
|
+
}))
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
The command's handler delegates to its domain service; that service claims the reactor delivery id and processes the
|
|
267
|
+
saved receipt in its transaction. A duplicate provider POST records nothing more, and a duplicate relay delivery
|
|
268
|
+
processes nothing twice. The command, handler and consumer live in the reactor's own kind; no feature imports the
|
|
269
|
+
webhook kind. The equivalent job path enqueues a receipt reference in the recording transaction and processes it
|
|
270
|
+
through the job's platform claim and fencing, never synchronously in the POST handler.
|
|
125
271
|
verification:
|
|
126
272
|
automated:
|
|
127
273
|
- BE_INBOX_DEDUPE_MISSING
|
|
128
274
|
- BE_OUTBOX_WRITE_TX
|
|
129
275
|
- BE_EVENT_CLASS_CONTRACT
|
|
276
|
+
- BE_WEBHOOK_SCOPE
|
|
277
|
+
- BE_WEBHOOK_INBOX_KEY
|
|
130
278
|
manual:
|
|
131
|
-
- Confirm each provider
|
|
279
|
+
- Confirm each accepted provider delivery records at most one receipt and outbox event, and the reactor or job alone dispatches processing commands.
|
|
132
280
|
relatedExamples: [webhooks]
|
|
133
281
|
- id: BE-WEBHOOK-4
|
|
134
282
|
title: Three behaviours are proven on the test world
|
|
135
283
|
kind: mandatory
|
|
136
|
-
hfsRules: [R155]
|
|
284
|
+
hfsRules: [R155, R284]
|
|
137
285
|
requirement: |-
|
|
138
|
-
Declaring the pattern `webhooks` requires e2e or integration tests
|
|
286
|
+
Declaring the pattern `webhooks` requires e2e or integration tests in full and integration tests in lite (R284),
|
|
287
|
+
titled `webhooks/<scenario>: ...` for each scenario
|
|
139
288
|
`bad-signature-is-refused` (a tampered body is refused and nothing is published), `replay-is-refused` (a stale timestamp is
|
|
140
289
|
refused and nothing is published) and `accepted-delivery-publishes-once` (a valid delivery publishes the event once and a
|
|
141
290
|
redelivery publishes nothing more). The provider is the network-edge fake of the test world (`world.fake.<provider>`), which
|
|
142
291
|
signs with the test secret; a real provider is never called. The unit spec `<provider>.webhook.spec.ts` covers the door
|
|
143
|
-
itself with the Nest testing module
|
|
144
|
-
domain service.
|
|
292
|
+
itself with the Nest testing module and the verifier it uses: a real `WebhookSignatureService` configured with a test secret
|
|
293
|
+
or a real domain verifier configured with the connection's test secret, plus a double of a separate intake service. A method
|
|
294
|
+
that combines verification and intake has its domain service spec proving refusal before intake and the door spec proving delegation.
|
|
145
295
|
rationale: |-
|
|
146
296
|
The door has no logic of its own, so the risk is in the wiring: the raw body, the headers, the real signature scheme and the
|
|
147
297
|
transaction behind the intake. Only a signed request through the real app proves it.
|
|
@@ -152,6 +302,7 @@ rules:
|
|
|
152
302
|
verification:
|
|
153
303
|
automated:
|
|
154
304
|
- BE_PATTERN_SPEC_MISSING
|
|
305
|
+
- BE_LITE_PROOF_LANE_MISSING
|
|
155
306
|
manual:
|
|
156
307
|
- Confirm the fake signs exactly as the provider documents and that a contract spec guards its payload fixtures.
|
|
157
308
|
relatedExamples: [webhooks]
|
|
@@ -160,6 +311,14 @@ files:
|
|
|
160
311
|
slot: be.feature.webhooks
|
|
161
312
|
template: webhooks/webhook.index.ts.tpl
|
|
162
313
|
role: The public surface of the kind folder, the module class an api-kind app composes.
|
|
314
|
+
- path: src/features/webhooks/<provider>/transport/http/<provider>-http.options.ts
|
|
315
|
+
slot: be.feature.webhooks.http
|
|
316
|
+
template: webhooks/webhook.http.options.ts.tpl
|
|
317
|
+
role: The options of the module (the configurable module base needs a type); empty until the module takes some.
|
|
318
|
+
- path: src/features/webhooks/<provider>/transport/http/<provider>-http.module-definition.ts
|
|
319
|
+
slot: be.feature.webhooks.http
|
|
320
|
+
template: webhooks/webhook.http.module-definition.ts.tpl
|
|
321
|
+
role: The configurable-module base of the module, registered as register.
|
|
163
322
|
- path: src/features/webhooks/<provider>/transport/http/<provider>-http.module.ts
|
|
164
323
|
slot: be.feature.webhooks.http
|
|
165
324
|
template: webhooks/webhook.http.module.ts.tpl
|
|
@@ -167,7 +326,7 @@ files:
|
|
|
167
326
|
- path: src/features/webhooks/<provider>/transport/http/<provider>.webhook.ts
|
|
168
327
|
slot: be.feature.webhooks.http
|
|
169
328
|
template: webhooks/webhook.door.ts.tpl
|
|
170
|
-
role: The signed door, one controller with one POST handler.
|
|
329
|
+
role: The signed door, one controller with one POST handler and optionally one GET challenge verifier; an own-feature PROVIDER_ACK is a fixed literal.
|
|
171
330
|
rules: [BE_WEBHOOK_SHAPE, BE_WEBHOOK_UNVERIFIED, BE_DEFAULT_DENY, BE_INPUT_BOUNDED]
|
|
172
331
|
spec: src/features/webhooks/<provider>/transport/http/<provider>.webhook.spec.ts
|
|
173
332
|
- path: src/features/webhooks/<provider>/transport/http/<provider>.webhook.spec.ts
|
|
@@ -181,12 +340,12 @@ files:
|
|
|
181
340
|
template: webhooks/webhook.request.ts.tpl
|
|
182
341
|
- path: src/modules/platform/http-security/webhook-signature.service.ts
|
|
183
342
|
slot: be.platform
|
|
184
|
-
role: WebhookSignatureService, the
|
|
343
|
+
role: WebhookSignatureService, the shared signature and replay proof when provider secrets are platform options.
|
|
185
344
|
spec: src/modules/platform/http-security/webhook-signature.service.spec.ts
|
|
186
345
|
optional: true
|
|
187
346
|
- path: src/modules/domain/<capability>/<capability>.service.ts
|
|
188
347
|
slot: be.domain
|
|
189
|
-
role: The intake method, which
|
|
348
|
+
role: The domain verifier or intake method, which may combine per-connection proof with inbox claim, durable receipt recording and outbox publishing in its transaction; no command/query-bus or feature-handler dispatch.
|
|
190
349
|
rules: [BE_INBOX_DEDUPE_MISSING, BE_OUTBOX_WRITE_TX]
|
|
191
350
|
optional: true
|
|
192
351
|
- path: src/modules/events/<service>/<event>.event.ts
|
|
@@ -194,9 +353,24 @@ files:
|
|
|
194
353
|
role: The typed event the intake publishes.
|
|
195
354
|
rules: [BE_EVENT_CLASS_CONTRACT]
|
|
196
355
|
optional: true
|
|
356
|
+
- path: src/features/reactors/<reactor>/transport/message/<event>.consumer.ts
|
|
357
|
+
slot: be.transport.message
|
|
358
|
+
role: Optional reactor consumer of the durable receipt event; forwards its eventId and receipt reference in one command after commit.
|
|
359
|
+
rules: [BE_EVENT_CONSUMER_SHAPE, BE_FEATURE_THIN]
|
|
360
|
+
optional: true
|
|
197
361
|
- path: src/tests/e2e/<area>/<provider>-webhook.e2e-spec.ts
|
|
198
362
|
slot: be.tests.e2e
|
|
199
363
|
role: "Titles webhooks/<scenario>: ... prove the pattern against the provider fake."
|
|
200
364
|
spec: webhooks/bad-signature-is-refused, webhooks/replay-is-refused, webhooks/accepted-delivery-publishes-once
|
|
201
365
|
rules: [BE_PATTERN_SPEC_MISSING]
|
|
202
366
|
optional: true
|
|
367
|
+
sourceMaintenance: >-
|
|
368
|
+
The add webhook verb keeps the default empty acknowledgement and does not add a consumer. In full, the generated POST
|
|
369
|
+
verifies first and calls the supplied domain service's record<Provider>Delivery once. Implement that method as the
|
|
370
|
+
transactional inbox receipt/outbox recording intake above, then add its reactor or job separately.
|
|
371
|
+
In lite, the generated POST verifies first and calls <Provider>InboxService.record<Provider>Delivery. This record-only
|
|
372
|
+
domain intake uses recordWebhookDelivery to insert a pending provider inbox row atomically in Supabase Postgres, unique on
|
|
373
|
+
(provider, delivery_id), without processing it or marking it complete. Add a reactor or job separately to consume the
|
|
374
|
+
committed pending row and dispatch the processing command (reactors.yaml, jobs.yaml). The --service option validates the
|
|
375
|
+
future domain processing owner; the webhook writer does not import or call that owner synchronously and does not
|
|
376
|
+
synthesize a consumer.
|
|
@@ -174,6 +174,9 @@ rules:
|
|
|
174
174
|
component.tsx. Pure blocks, composites, branches and leaves use index.tsx only and never gain an
|
|
175
175
|
empty twin. classNames.ts is present only when the unit owns class strings. Budgets: component.tsx 300 lines,
|
|
176
176
|
connected index.tsx 200 lines, at most 6 data hooks and 6 useState per connected unit.
|
|
177
|
+
A composition-only feature may render an existing public pure component owner directly instead of creating a
|
|
178
|
+
forwarding component.tsx. The route-state overlay host of FE-BOUNDARY-5 renders components/branches/OverlayHost;
|
|
179
|
+
a connected block still requires its exact private Base twin.
|
|
177
180
|
rationale: |
|
|
178
181
|
Stable basenames make class names and the connected and presentational split discoverable without forwarding
|
|
179
182
|
twins, and budgets keep a unit reviewable.
|
|
@@ -303,6 +306,7 @@ rules:
|
|
|
303
306
|
envelope, the generated wire types (from be/contracts/, through the root codegen) and the axios calls live in
|
|
304
307
|
modules/api; the session lives in modules/auth; mutation feedback in modules/toast; environment reading in
|
|
305
308
|
modules/config; every href builder in modules/routes; locale routing in modules/i18n; colour values in modules/brand.
|
|
309
|
+
Only hooks/swr imports the API transport operations; feature owners and other hooks consume those SWR hooks.
|
|
306
310
|
A helper name defined twice across hook files is a finding.
|
|
307
311
|
rationale: |
|
|
308
312
|
A rule that says what a hooks folder is also says where everything else goes, so nobody copies a helper sixteen
|
|
@@ -82,7 +82,7 @@ overall:
|
|
|
82
82
|
rule: typescript:S3776
|
|
83
83
|
via: overall.issues
|
|
84
84
|
# Lite is the same quality policy, coverage included: a lite back end keeps full's unit run and its 100 per file on the measured
|
|
85
|
-
# logic files (owner ruling 2026-10-12, R212 case-1). Readers select this block from hfs.json edition; the gate keeps its own
|
|
85
|
+
# logic files (owner ruling 2026-10-12, R212 case-1 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4). Readers select this block from hfs.json edition; the gate keeps its own
|
|
86
86
|
# name so a lite project's server gate stays a separate object, and every threshold below equals full's.
|
|
87
87
|
lite:
|
|
88
88
|
gate:
|