@starci/hfs 4.0.8 → 4.0.9
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 +81 -39
- package/emit/contracts.mjs +56 -9
- package/emit/db-types.mjs +169 -0
- package/emit/events.mjs +41 -0
- package/emit/operations-worker.mjs +1 -1
- package/emit/operations.mjs +2 -2
- package/emit/schema-worker.mjs +1 -1
- package/lint/bound-sys.cjs +50 -0
- package/lint/run.mjs +53 -19
- package/package.json +14 -9
- package/report/sonar.mjs +8 -8
- package/runtime/engine/config.mjs +494 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +160 -29
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +912 -120
- package/runtime/knowledge/patterns/be/api.yaml +369 -0
- package/runtime/knowledge/patterns/be/cli.yaml +297 -0
- package/runtime/knowledge/patterns/be/domain.yaml +113 -0
- package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
- package/runtime/knowledge/patterns/be/projections.yaml +178 -0
- package/runtime/knowledge/patterns/be/queues.yaml +236 -0
- package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
- package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
- package/runtime/knowledge/patterns/be/saga.yaml +248 -0
- package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
- package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
- package/runtime/knowledge/sonar-gate.yaml +52 -18
- package/runtime/modules/kernel/failure-codes.yaml +571 -51
- package/runtime/modules/models/registry.yaml +591 -0
- package/runtime/modules/models/runtimes.yaml +701 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
- package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
- package/runtime/scripts/api/fs/lib.mjs +10 -2
- package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
- package/runtime/scripts/api/git/config-get.mjs +10 -0
- package/runtime/scripts/api/git/diff.mjs +5 -0
- package/runtime/scripts/api/git/lib.mjs +16 -26
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -5
- package/runtime/scripts/api/sops/lib.mjs +35 -0
- package/runtime/scripts/api/sops/seal.mjs +18 -0
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
- package/runtime/scripts/hfs/architecture/config.mjs +9 -6
- package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
- package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
- package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
- package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +1 -1
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
- package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/hfs/architecture/index.mjs +17 -4
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
- package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
- package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
- package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
- package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
- package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
- package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
- package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
- package/runtime/scripts/hfs/architecture.mjs +4 -4
- package/runtime/scripts/hfs/check.mjs +75 -72
- package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
- package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
- package/runtime/scripts/hfs/edition-slots.mjs +66 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/hfs/path-findings.mjs +1 -1
- package/runtime/scripts/hfs/repo-identity.mjs +9 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
- package/runtime/scripts/hfs/rules/cli.mjs +37 -0
- package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
- package/runtime/scripts/hfs/rules/contract.mjs +10 -9
- package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
- package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
- package/runtime/scripts/hfs/rules/database.mjs +68 -0
- package/runtime/scripts/hfs/rules/deps.mjs +1 -1
- package/runtime/scripts/hfs/rules/docker.mjs +259 -0
- package/runtime/scripts/hfs/rules/edition.mjs +111 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
- package/runtime/scripts/hfs/rules/read.mjs +1 -1
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +175 -0
- package/runtime/scripts/hfs/slots.mjs +78 -108
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/hfs/tree.mjs +5 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
- package/runtime/scripts/hfs/view.mjs +3 -2
- package/runtime/scripts/lib/dockerfile.mjs +124 -0
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +103 -0
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +24 -5
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/regex.mjs +4 -0
- package/runtime/scripts/lib/same-text.mjs +5 -0
- package/runtime/scripts/lib/secret-patterns.mjs +4 -3
- package/runtime/scripts/lib/sleep-sync.mjs +2 -1
- package/runtime/scripts/lib/sops-envelope.mjs +58 -0
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/lib/walk.mjs +4 -3
- package/scaffold/add-app.mjs +174 -0
- package/scaffold/add-cli-lite.mjs +257 -0
- package/scaffold/add-inbox-lite.mjs +314 -0
- package/scaffold/add-table.mjs +344 -0
- package/scaffold/add.mjs +299 -0
- package/scaffold/app.mjs +358 -69
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +32 -26
- package/scaffold/supabase-ports.mjs +26 -0
- package/src/main.mjs +327 -0
- package/sync/cli.mjs +6 -9
- package/sync/format.mjs +1 -1
- package/sync/hygiene.mjs +10 -10
- package/sync/index.mjs +167 -104
- package/sync/managed.mjs +19 -11
- package/sync/sonar-key.mjs +5 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
- package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
- package/templates/app/ci-workflows/sonar-steps.yml +9 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
- package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
- package/templates/app/docker-ignore/dockerignore +50 -0
- package/templates/app/gitignore-lite +35 -0
- package/templates/app/hooks/husky/pre-commit +3 -8
- package/templates/app/hooks/husky/pre-push +45 -6
- package/templates/app/package-scripts/core.json +8 -0
- package/templates/app/package-scripts/package.json +3 -10
- package/templates/app/package-scripts-lite/package.json +15 -0
- package/templates/app/quality-config/codecov.yml +15 -3
- package/templates/app/quality-config/sonar-base.properties +4 -0
- package/templates/app/quality-config/sonar-project.properties +1 -4
- package/templates/app/quality-config-lite/sonar-project.properties +5 -0
- package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
- package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
- package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
- package/templates/app/skeleton/README.md +7 -5
- package/templates/app/skeleton-lite/.sops.yaml +3 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
- package/templates/app/skeleton-lite/README.md +40 -0
- package/templates/app/skeleton-lite/supabase/config.toml +41 -0
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
- package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
- package/templates/app/task-graph/turbo.json +23 -0
- package/templates/app/tool-config-lite/prettierignore +2 -0
- package/templates/app/tool-config-lite/prettierrc +1 -0
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
- package/templates/be/common/main-listen.ts.partial +14 -0
- package/templates/be/common/sql-ident.ts.partial +7 -0
- package/templates/be/common/sql-tag.ts.partial +5 -0
- package/templates/be/image/api/Dockerfile +38 -0
- package/templates/be/image/cli/Dockerfile +38 -0
- package/templates/be/image/worker/Dockerfile +36 -0
- package/templates/be/inbox/database-client.ts.tpl +50 -0
- package/templates/be/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/migration.sql.tpl +29 -0
- package/templates/be/inbox/module-definition.ts.tpl +6 -0
- package/templates/be/inbox/module.ts.tpl +14 -0
- package/templates/be/inbox/options.ts.tpl +2 -0
- package/templates/be/inbox/service.ts.tpl +33 -0
- package/templates/be/inbox/sql.ts.tpl +16 -0
- package/templates/be/inbox/webhook-door.ts.tpl +36 -0
- package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
- package/templates/be/patterns/api/application.command.ts.tpl +10 -0
- package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
- package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
- package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
- package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
- package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
- package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
- package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
- package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/http.module.ts.tpl +7 -0
- package/templates/be/patterns/api/http.request.ts.tpl +10 -0
- package/templates/be/patterns/api/http.response.ts.tpl +5 -0
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
- package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
- package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
- package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
- package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
- package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
- package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
- package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
- package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
- package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
- package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
- package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
- package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
- package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
- package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
- package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
- package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
- package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
- package/templates/be/patterns/projections/migration.ts.tpl +18 -0
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
- package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
- package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
- package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
- package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
- package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
- package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
- package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
- package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
- package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
- package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
- package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
- package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
- package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
- package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
- package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
- package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
- package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
- package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
- package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
- package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
- package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
- package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
- package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/index.ts +1 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
- package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
- package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
- package/templates/be/table/error.ts.tpl +16 -0
- package/templates/be/table/index.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +51 -0
- package/templates/be/table/module-definition.ts.tpl +7 -0
- package/templates/be/table/module.ts.tpl +14 -0
- package/templates/be/table/options.ts.tpl +2 -0
- package/templates/be/table/rows.ts.tpl +4 -0
- package/templates/be/table/service.ts.tpl +23 -0
- package/templates/be/table/sql.ts.tpl +4 -0
- package/templates/be/tool-config/jest.config.js +3 -1
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
- package/templates/common/gitignore.base +1 -2
- package/templates/fe/app/package.json.tpl +1 -0
- package/templates/fe/app/tsconfig.json +12 -0
- package/templates/fe/common/app-layout.tsx.partial +8 -0
- package/templates/fe/common/site-shell.tsx.partial +9 -0
- package/templates/fe/image/next/Dockerfile +42 -0
- package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
- package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
- package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
- package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
- package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
- package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
- package/templates/fe/table/read.ts.tpl +16 -0
- package/templates/fe/table/write.ts.tpl +16 -0
- package/upgrade/check.mjs +137 -0
- package/upgrade/index.mjs +524 -0
- package/bin/hfs.mjs +0 -233
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
- package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
- /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
schema: starci/knowledge-source@1
|
|
2
|
+
id: be.feature.realtime
|
|
3
|
+
title: Realtime channels
|
|
4
|
+
purpose: |-
|
|
5
|
+
Given a state change a connected client must see without asking, how is it pushed? Realtime is a feature KIND:
|
|
6
|
+
`be/src/features/realtime/<channel>/` holds one door per channel, a socket gateway (`<channel>.gateway.ts`) or a GraphQL
|
|
7
|
+
subscription (`<x>.subscription.ts`), that READS and PUSHES only. It never writes. The push is fed by a reactor: an event
|
|
8
|
+
consumer in `features/reactors/` receives the domain event and publishes to a topic of the `RealtimeHub` of `platform/realtime`;
|
|
9
|
+
the door only subscribes the client to a topic of that hub, scoped to the client's principal.
|
|
10
|
+
appliesTo:
|
|
11
|
+
- backend
|
|
12
|
+
family: be
|
|
13
|
+
provenance:
|
|
14
|
+
reviewedAt: 2026-10-02
|
|
15
|
+
sources:
|
|
16
|
+
- knowledge/hfs/README.md
|
|
17
|
+
- knowledge/hfs/slots.yaml
|
|
18
|
+
- knowledge/hfs/rules.yaml
|
|
19
|
+
- knowledge/patterns/be/transport.yaml
|
|
20
|
+
- knowledge/patterns/be/api-auth.yaml
|
|
21
|
+
- owner decision 2026-10-02 (feature kinds: webhooks, realtime, reactors, saga, jobs, cli; no kind imports another)
|
|
22
|
+
limitations: |-
|
|
23
|
+
Static checks prove the door's role, that the hub is the only thing injected and that a subscribed topic is built from the
|
|
24
|
+
principal. They do not prove the topic naming is collision free across channels, that an initial snapshot is consistent with
|
|
25
|
+
the stream, or the behaviour of the socket under reconnects; the three `realtime/*` scenarios prove delivery, isolation and
|
|
26
|
+
cleanup on the real stack.
|
|
27
|
+
rules:
|
|
28
|
+
- id: BE-REALTIME-1
|
|
29
|
+
title: A channel is a kind folder with one door, its module and its own spec
|
|
30
|
+
kind: mandatory
|
|
31
|
+
hfsRules: [R01, R02, R47, R184]
|
|
32
|
+
requirement: |-
|
|
33
|
+
A push channel lives in `be/src/features/realtime/<channel>/` (slot `be.feature.realtime`), enabled by declaring the pattern
|
|
34
|
+
`realtime` in `hfs.json` `sides.be.patterns`. The folder holds `index.ts` (the module class an app composes), `transport/<protocol>/<channel>-<protocol>.module.ts`, ONE door and its spec: either
|
|
35
|
+
`transport/websocket/<channel>.gateway.ts` (one `@WebSocketGateway` class whose handlers are `@SubscribeMessage`) with
|
|
36
|
+
`<channel>.gateway.spec.ts`, or `transport/graphql/<channel>.subscription.ts` (one `@Resolver` class whose operations are `@Subscription`) with
|
|
37
|
+
`<channel>.subscription.spec.ts`, plus optional `dto/` (`<name>.{input,type,args}.ts`). A door carries no query, mutation, field
|
|
38
|
+
resolver or REST route. The kind imports only `modules/*`, never another kind, and only an api-kind app composes its module.
|
|
39
|
+
rationale: |-
|
|
40
|
+
One file per door keeps a protocol's machinery in one place, and keeping queries and mutations out guarantees that what a
|
|
41
|
+
client can do through a realtime door is listen.
|
|
42
|
+
cases:
|
|
43
|
+
- id: case-1
|
|
44
|
+
when: A GraphQL subscription of order status
|
|
45
|
+
write: 'features/realtime/order-status/index.ts, transport/graphql/order-status-graphql.module.ts, transport/graphql/order-status.subscription.ts and transport/graphql/order-status.subscription.spec.ts.'
|
|
46
|
+
- id: case-2
|
|
47
|
+
when: A socket gateway
|
|
48
|
+
write: 'features/realtime/chat/transport/websocket/chat-websocket.module.ts, transport/websocket/chat.gateway.ts and transport/websocket/chat.gateway.spec.ts.'
|
|
49
|
+
verification:
|
|
50
|
+
automated:
|
|
51
|
+
- HFS_SLOT_UNDECLARED
|
|
52
|
+
- HFS_SLOT_AMBIGUOUS
|
|
53
|
+
- HFS_SLOT_NOT_ENABLED
|
|
54
|
+
- HFS_FORBIDDEN_PRESENT
|
|
55
|
+
- HFS_TRACKED_MUST_BE_IGNORED
|
|
56
|
+
- HFS_ROOT_ENTRY_FORBIDDEN
|
|
57
|
+
- HFS_ROOT_MARKDOWN_FORBIDDEN
|
|
58
|
+
- HFS_ROOT_SRC_FORBIDDEN_FE
|
|
59
|
+
- HFS_SRC_LAYOUT_INVALID
|
|
60
|
+
- HFS_MODULE_TIER_INVALID
|
|
61
|
+
- HFS_WORK_IN_FE
|
|
62
|
+
- HFS_STACKS_IN_SIDE
|
|
63
|
+
- HFS_SLOT_REQUIRED_MISSING
|
|
64
|
+
- HFS_MIN_INSTANCES
|
|
65
|
+
- HFS_REQUIRED_FILE_MISSING
|
|
66
|
+
- HFS_ROOT_ENTRY_MISSING
|
|
67
|
+
- HFS_APPS_REQUIRED
|
|
68
|
+
- HFS_APP_LAYOUT_INVALID
|
|
69
|
+
- HFS_README_TITLE_INVALID
|
|
70
|
+
- HFS_README_DESCRIPTION_INVALID
|
|
71
|
+
- HFS_README_SECTION_MISSING
|
|
72
|
+
- HFS_README_SECTION_ORDER
|
|
73
|
+
- HFS_README_DEVELOPMENT_INCOMPLETE
|
|
74
|
+
- HFS_README_WORK_POINTER_MISSING
|
|
75
|
+
- HFS_README_PRIVATE_URL
|
|
76
|
+
- HFS_README_BADGE_NOT_LIVE
|
|
77
|
+
- BE_TEST_TOPOLOGY
|
|
78
|
+
- BE_CONTRACT_UNGUARDED
|
|
79
|
+
- BE_REALTIME_SHAPE
|
|
80
|
+
manual:
|
|
81
|
+
- Confirm a query or mutation an old subscription door carried now lives in an api feature.
|
|
82
|
+
relatedExamples: [realtime]
|
|
83
|
+
- id: BE-REALTIME-2
|
|
84
|
+
title: A realtime door reads and pushes, it never writes
|
|
85
|
+
kind: mandatory
|
|
86
|
+
hfsRules: [R185, R90, R203]
|
|
87
|
+
requirement: |-
|
|
88
|
+
A realtime door is thin (R203 `BE_FEATURE_THIN`): it maps the subscription or the message and makes one delegating call
|
|
89
|
+
to the hub. The only value it injects is the `RealtimeHub` of `platform/realtime` (`subscribe(topic)` returns an async
|
|
90
|
+
iterable of frames, `publish(topic, frame)` pushes one; a topic is a typed `RealtimeTopic<T>` built by the owning domain
|
|
91
|
+
contract, never a string written in a door). No `EntityManager`, `CommandBus`, `QueryBus`, event bus, queue or
|
|
92
|
+
service of `modules/domain` is injected: a domain service may write, so it is not provably read-only. The data a client
|
|
93
|
+
needs is pushed by a reactor; the socket library and the pub/sub engine (`socket.io`, `graphql-subscriptions`, `graphql-ws`)
|
|
94
|
+
are imported only by `platform/realtime`.
|
|
95
|
+
rationale: |-
|
|
96
|
+
A door that can only touch the hub cannot change business state, so the biggest risk of a long-lived unauthenticated-looking
|
|
97
|
+
connection, a write through it, is removed by type.
|
|
98
|
+
cases:
|
|
99
|
+
- id: case-1
|
|
100
|
+
when: A subscription that pushes order status changes
|
|
101
|
+
write: |-
|
|
102
|
+
@Resolver()
|
|
103
|
+
export class OrderStatusSubscription {
|
|
104
|
+
constructor(private readonly hub: RealtimeHub) {}
|
|
105
|
+
|
|
106
|
+
@Subscription(() => OrderStatusChangedType)
|
|
107
|
+
orderStatusChanged(@CurrentPrincipal() principal: Principal, @Args("orderId") orderId: string): AsyncIterable<OrderStatusChangedType> {
|
|
108
|
+
return this.hub.subscribe(orderStatusTopic(principal.id, orderId))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
verification:
|
|
112
|
+
automated:
|
|
113
|
+
- BE_REALTIME_WRITES
|
|
114
|
+
- BE_INFRA_OWNER
|
|
115
|
+
- BE_FEATURE_THIN
|
|
116
|
+
manual:
|
|
117
|
+
- Confirm the frame type carries only what the principal may see.
|
|
118
|
+
relatedExamples: [realtime]
|
|
119
|
+
- id: BE-REALTIME-3
|
|
120
|
+
title: The topic is built from the principal
|
|
121
|
+
kind: mandatory
|
|
122
|
+
hfsRules: [R186, R41]
|
|
123
|
+
requirement: |-
|
|
124
|
+
Every `@SubscribeMessage` and `@Subscription` handler takes `@CurrentPrincipal()` of `domain/identity` and builds the topic it
|
|
125
|
+
passes to `hub.subscribe` from the principal's id (plus a client-supplied id only inside that prefix) with the topic
|
|
126
|
+
builder of the owning domain contract, so one client cannot
|
|
127
|
+
listen to another's channel. The reactor that pushes uses the same topic builder from the owning domain contract, never a
|
|
128
|
+
hand-written string. The default-deny guards apply to the door like to any other.
|
|
129
|
+
rationale: |-
|
|
130
|
+
A topic taken from client input alone is an authorization hole: knowing an order id would be enough to watch it.
|
|
131
|
+
cases:
|
|
132
|
+
- id: case-1
|
|
133
|
+
when: Subscribing to one order's status
|
|
134
|
+
write: 'hub.subscribe(orderStatusTopic(principal.id, orderId)); the reactor publishes to orderStatusTopic(buyerId, orderId) of the order owner, so a different principal receives nothing.'
|
|
135
|
+
verification:
|
|
136
|
+
automated:
|
|
137
|
+
- BE_REALTIME_TOPIC_SCOPE
|
|
138
|
+
- BE_DEFAULT_DENY
|
|
139
|
+
manual:
|
|
140
|
+
- Review that the topic builder shared with the reactor lives in the domain contract and is used by both sides.
|
|
141
|
+
relatedExamples: [realtime]
|
|
142
|
+
- id: BE-REALTIME-4
|
|
143
|
+
title: Three behaviours are proven on the test world
|
|
144
|
+
kind: mandatory
|
|
145
|
+
hfsRules: [R155]
|
|
146
|
+
requirement: |-
|
|
147
|
+
Declaring the pattern `realtime` requires e2e or integration tests titled `realtime/<scenario>: ...` for each scenario
|
|
148
|
+
`subscriber-receives-push` (a connected client receives the frame a reactor pushes after the domain event),
|
|
149
|
+
`foreign-topic-is-isolated` (another principal subscribed to the same channel receives nothing) and
|
|
150
|
+
`disconnect-releases-subscription` (after the client disconnects the hub holds no subscription for it). The client is a real
|
|
151
|
+
socket or GraphQL-over-websocket client of the test world. The unit spec of the door covers its wiring with the Nest testing
|
|
152
|
+
module and a recording hub double.
|
|
153
|
+
rationale: |-
|
|
154
|
+
The door has no logic, so the risk is delivery, isolation and leaks on a real connection, which only a client against the
|
|
155
|
+
real app shows.
|
|
156
|
+
cases:
|
|
157
|
+
- id: case-1
|
|
158
|
+
when: The e2e of the order status channel
|
|
159
|
+
write: 'e2e/order/order-status-push.e2e-spec.ts has `it("realtime/subscriber-receives-push: ...")`, `it("realtime/foreign-topic-is-isolated: ...")` and `it("realtime/disconnect-releases-subscription: ...")`.'
|
|
160
|
+
verification:
|
|
161
|
+
automated:
|
|
162
|
+
- BE_PATTERN_SPEC_MISSING
|
|
163
|
+
manual:
|
|
164
|
+
- Confirm the e2e client waits with the world's waitFor and never sleeps.
|
|
165
|
+
relatedExamples: [realtime]
|
|
166
|
+
files:
|
|
167
|
+
- path: src/features/realtime/<channel>/index.ts
|
|
168
|
+
slot: be.feature.realtime
|
|
169
|
+
template: realtime/channel.index.ts.tpl
|
|
170
|
+
role: The public surface of the kind folder, the module class an api-kind app composes.
|
|
171
|
+
- path: src/features/realtime/<channel>/transport/graphql/<channel>-graphql.module.ts
|
|
172
|
+
slot: be.feature.realtime.graphql
|
|
173
|
+
template: realtime/channel.graphql.module.ts.tpl
|
|
174
|
+
role: Nest module of the subscription door, composed only by an api-kind app.
|
|
175
|
+
- path: src/features/realtime/<channel>/transport/websocket/<channel>-websocket.module.ts
|
|
176
|
+
slot: be.feature.realtime.websocket
|
|
177
|
+
role: Nest module of the gateway door, composed only by an api-kind app.
|
|
178
|
+
optional: true
|
|
179
|
+
- path: src/features/realtime/<channel>/transport/websocket/<channel>.gateway.ts
|
|
180
|
+
slot: be.feature.realtime.websocket
|
|
181
|
+
role: A socket door (one WebSocketGateway class); exactly one of gateway or subscription per channel file name.
|
|
182
|
+
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE]
|
|
183
|
+
spec: src/features/realtime/<channel>/transport/websocket/<channel>.gateway.spec.ts
|
|
184
|
+
optional: true
|
|
185
|
+
- path: src/features/realtime/<channel>/transport/websocket/<channel>.gateway.spec.ts
|
|
186
|
+
slot: be.feature.realtime.websocket
|
|
187
|
+
role: Unit spec of the gateway beside it.
|
|
188
|
+
optional: true
|
|
189
|
+
- path: src/features/realtime/<channel>/transport/graphql/<channel>.subscription.ts
|
|
190
|
+
slot: be.feature.realtime.graphql
|
|
191
|
+
template: realtime/channel.subscription.ts.tpl
|
|
192
|
+
role: A GraphQL subscription door (one Resolver class).
|
|
193
|
+
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE]
|
|
194
|
+
spec: src/features/realtime/<channel>/transport/graphql/<channel>.subscription.spec.ts
|
|
195
|
+
- path: src/features/realtime/<channel>/transport/graphql/<channel>.subscription.spec.ts
|
|
196
|
+
slot: be.feature.realtime.graphql
|
|
197
|
+
template: realtime/channel.subscription.spec.ts.tpl
|
|
198
|
+
role: Unit spec of the subscription beside it.
|
|
199
|
+
- path: src/features/realtime/<channel>/transport/graphql/dto/<channel>-changed.type.ts
|
|
200
|
+
slot: be.feature.realtime.dto
|
|
201
|
+
template: realtime/channel.type.ts.tpl
|
|
202
|
+
role: "The typed frame of the door, what the subscriber may see of a change."
|
|
203
|
+
- path: src/features/realtime/<channel>/transport/graphql/dto/<channel>.input.ts
|
|
204
|
+
slot: be.feature.realtime.dto
|
|
205
|
+
template: realtime/channel.input.ts.tpl
|
|
206
|
+
role: The typed, bounded arguments of the door.
|
|
207
|
+
rules: [BE_INPUT_BOUNDED]
|
|
208
|
+
- path: src/modules/platform/realtime/realtime.hub.ts
|
|
209
|
+
slot: be.platform
|
|
210
|
+
role: RealtimeHub, the one owner of the socket and subscription engine (publish and subscribe).
|
|
211
|
+
spec: src/modules/platform/realtime/realtime.service.spec.ts
|
|
212
|
+
optional: true
|
|
213
|
+
- path: src/features/reactors/<name>/transport/message/<event>.consumer.ts
|
|
214
|
+
slot: be.transport.message
|
|
215
|
+
role: The reactor consumer that receives the domain event and pushes the frame through the hub.
|
|
216
|
+
optional: true
|
|
217
|
+
- path: src/tests/e2e/<area>/<channel>-push.e2e-spec.ts
|
|
218
|
+
slot: be.tests.e2e
|
|
219
|
+
role: "Titles realtime/<scenario>: ... prove the pattern against a real client."
|
|
220
|
+
spec: realtime/subscriber-receives-push, realtime/foreign-topic-is-isolated, realtime/disconnect-releases-subscription
|
|
221
|
+
rules: [BE_PATTERN_SPEC_MISSING]
|
|
222
|
+
optional: true
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
schema: starci/knowledge-source@1
|
|
2
|
+
id: be.saga
|
|
3
|
+
title: Sagas
|
|
4
|
+
purpose: |-
|
|
5
|
+
Given a flow that spans services and must be undone when a later step fails, how is it written so that hfs can check it?
|
|
6
|
+
A saga is a feature of the saga kind (`be/src/features/saga/<saga>/`): an orchestrator that lists the steps and, for each, the compensation that undoes it; the
|
|
7
|
+
persisted state of a run with a version fence; consumers that advance it through the inbox; and the events it relies on,
|
|
8
|
+
declared in the vendored contracts. The saga pattern is declared (`patterns: ["saga"]` in hfs.json sides.be); an
|
|
9
|
+
undeclared saga folder is refused.
|
|
10
|
+
appliesTo:
|
|
11
|
+
- backend
|
|
12
|
+
family: be
|
|
13
|
+
# The slot ids this topic teaches (knowledge/hfs/slots.yaml); scripts/gates/read-digest.mjs inverts these
|
|
14
|
+
# into the READ-step slot -> pattern-topic map (knowledge/op-gate.yaml kinds), each slot's topics in index.yaml order.
|
|
15
|
+
slots: [be.feature.saga]
|
|
16
|
+
provenance:
|
|
17
|
+
reviewedAt: 2026-10-02
|
|
18
|
+
sources:
|
|
19
|
+
- knowledge/hfs/README.md
|
|
20
|
+
- knowledge/hfs/slots.yaml
|
|
21
|
+
- knowledge/hfs/rules.yaml
|
|
22
|
+
- knowledge/patterns/be/services.yaml
|
|
23
|
+
- examples/ecommerce-app
|
|
24
|
+
limitations: |-
|
|
25
|
+
The checks read the folder tree, the step and compensation event names, the contracts, the consumers' use of the delivery id
|
|
26
|
+
and the e2e specs. They cannot see that a compensation really undoes its step, that a step is idempotent, or that the
|
|
27
|
+
failure injected by an e2e spec is the right one; the state machine and its fence are proven by the unit spec of
|
|
28
|
+
platform/saga, the flow by the e2e specs, and the semantics by review.
|
|
29
|
+
files:
|
|
30
|
+
- path: src/features/saga/<saga>/index.ts
|
|
31
|
+
slot: be.feature.saga
|
|
32
|
+
template: saga/saga.index.ts.tpl
|
|
33
|
+
role: "The public surface of the kind folder: the message module an app composes."
|
|
34
|
+
- path: src/features/saga/<saga>/<saga>.module.ts
|
|
35
|
+
slot: be.feature.saga
|
|
36
|
+
template: saga/saga.module.ts.tpl
|
|
37
|
+
role: "The application module: handlers, orchestrator, step and compensation."
|
|
38
|
+
- path: src/features/saga/<saga>/<saga>.saga.service.ts
|
|
39
|
+
slot: be.feature.saga
|
|
40
|
+
template: saga/saga.service.ts.tpl
|
|
41
|
+
role: "The orchestrator. A `*.service.ts` because a handler injects only services; thin (R203 `BE_FEATURE_THIN`) like every feature file — it maps and delegates, and the run's orchestration lives in `platform/saga` — and it owes its `<saga>.saga.service.spec.ts` beside it (a `service` of `ruleParams.be.unitRoles`), which asserts the delegation. It hands `platform/saga` each run with the compensation OBJECT of the step (`compensation`, never a callback): the platform logs `Compensating`, orders the steps and moves the run through the version fence; its slot is `coverage: none`, so it is never a coverage target."
|
|
42
|
+
suffix: saga.service
|
|
43
|
+
rules: [R169, R170, R203]
|
|
44
|
+
- path: src/features/saga/<saga>/<saga>.saga.service.spec.ts
|
|
45
|
+
slot: be.feature.saga
|
|
46
|
+
template: saga/saga.service.spec.ts.tpl
|
|
47
|
+
role: "Unit spec of the orchestrator beside it."
|
|
48
|
+
- path: src/features/saga/<saga>/<saga>.saga-state.ts
|
|
49
|
+
slot: be.feature.saga
|
|
50
|
+
template: saga/saga.state.ts.tpl
|
|
51
|
+
role: "The typed state of a run, with `status` and `version: number`, the fence."
|
|
52
|
+
suffix: saga-state
|
|
53
|
+
rules: [R170]
|
|
54
|
+
- path: src/features/saga/<saga>/steps/<step>.saga-step.ts
|
|
55
|
+
slot: be.feature.saga.steps
|
|
56
|
+
template: saga/saga.step.ts.tpl
|
|
57
|
+
role: "One step. Names its event (`readonly event = \"<name>\"`); the domain does the work and announces the event in its transaction."
|
|
58
|
+
suffix: step
|
|
59
|
+
rules: [R169, R171]
|
|
60
|
+
- path: src/features/saga/<saga>/compensations/<step>.compensation.ts
|
|
61
|
+
slot: be.feature.saga.compensations
|
|
62
|
+
template: saga/saga.compensation.ts.tpl
|
|
63
|
+
role: "The compensation of the step of the same name; REQUIRED for every step. Names the step it undoes and the failure event, and dispatches one command; the orchestrator passes it to `platform/saga` as the compensation object."
|
|
64
|
+
suffix: compensation
|
|
65
|
+
rules: [R169, R171, R173]
|
|
66
|
+
- path: src/features/saga/<saga>/application/undo-<saga>.command.ts
|
|
67
|
+
slot: be.feature.application
|
|
68
|
+
template: saga/saga.undo.command.ts.tpl
|
|
69
|
+
role: "The undo command of the saga (command): undoes what the saga did; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
70
|
+
rules: [R87]
|
|
71
|
+
- path: src/features/saga/<saga>/application/undo-<saga>.contracts.ts
|
|
72
|
+
slot: be.feature.application
|
|
73
|
+
template: saga/saga.undo.contracts.ts.tpl
|
|
74
|
+
role: "The undo command of the saga (contracts): undoes what the saga did; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
75
|
+
rules: [R87]
|
|
76
|
+
- path: src/features/saga/<saga>/application/undo-<saga>.handler.ts
|
|
77
|
+
slot: be.feature.application
|
|
78
|
+
template: saga/saga.undo.handler.ts.tpl
|
|
79
|
+
role: "The undo command of the saga (handler): undoes what the saga did; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
80
|
+
rules: [R87]
|
|
81
|
+
- path: src/features/saga/<saga>/application/compensate-<saga>.command.ts
|
|
82
|
+
slot: be.feature.application
|
|
83
|
+
template: saga/saga.compensate.command.ts.tpl
|
|
84
|
+
role: "The compensate command of the saga (command): compensates a failed run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
85
|
+
rules: [R87]
|
|
86
|
+
- path: src/features/saga/<saga>/application/compensate-<saga>.contracts.ts
|
|
87
|
+
slot: be.feature.application
|
|
88
|
+
template: saga/saga.compensate.contracts.ts.tpl
|
|
89
|
+
role: "The compensate command of the saga (contracts): compensates a failed run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
90
|
+
rules: [R87]
|
|
91
|
+
- path: src/features/saga/<saga>/application/compensate-<saga>.handler.ts
|
|
92
|
+
slot: be.feature.application
|
|
93
|
+
template: saga/saga.compensate.handler.ts.tpl
|
|
94
|
+
role: "The compensate command of the saga (handler): compensates a failed run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
95
|
+
rules: [R87]
|
|
96
|
+
- path: src/features/saga/<saga>/application/complete-<saga>.command.ts
|
|
97
|
+
slot: be.feature.application
|
|
98
|
+
template: saga/saga.complete.command.ts.tpl
|
|
99
|
+
role: "The complete command of the saga (command): completes a finished run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
100
|
+
rules: [R87]
|
|
101
|
+
- path: src/features/saga/<saga>/application/complete-<saga>.contracts.ts
|
|
102
|
+
slot: be.feature.application
|
|
103
|
+
template: saga/saga.complete.contracts.ts.tpl
|
|
104
|
+
role: "The complete command of the saga (contracts): completes a finished run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
105
|
+
rules: [R87]
|
|
106
|
+
- path: src/features/saga/<saga>/application/complete-<saga>.handler.ts
|
|
107
|
+
slot: be.feature.application
|
|
108
|
+
template: saga/saga.complete.handler.ts.tpl
|
|
109
|
+
role: "The complete command of the saga (handler): completes a finished run; plain CQRS (BE-CQRS-1), thin handlers calling one service."
|
|
110
|
+
rules: [R87]
|
|
111
|
+
- path: src/features/saga/<saga>/transport/message/<failed>.consumer.ts
|
|
112
|
+
slot: be.transport.message
|
|
113
|
+
template: saga/saga.failed.consumer.ts.tpl
|
|
114
|
+
role: "A thin door that hands the failure delivery, with its `eventId`, to the compensate command; the saga takes it through the inbox."
|
|
115
|
+
suffix: consumer
|
|
116
|
+
rules: [R80, R88, R172]
|
|
117
|
+
- path: src/features/saga/<saga>/transport/message/<done>.consumer.ts
|
|
118
|
+
slot: be.transport.message
|
|
119
|
+
template: saga/saga.done.consumer.ts.tpl
|
|
120
|
+
role: "A thin door that hands the delivery of the last event, with its `eventId`, to the complete command; the saga takes it through the inbox."
|
|
121
|
+
suffix: consumer
|
|
122
|
+
rules: [R80, R88, R172]
|
|
123
|
+
- path: src/features/saga/<saga>/transport/message/<saga>-message.module.ts
|
|
124
|
+
slot: be.transport.message
|
|
125
|
+
template: saga/saga.message.module.ts.tpl
|
|
126
|
+
role: "The message transport module: registers the consumers with the event bus; composed only by an app."
|
|
127
|
+
- path: contracts/<service>/events.json
|
|
128
|
+
slot: be.contract.events
|
|
129
|
+
role: 'Every event of the saga, emitted from the publisher''s events.ts; a compensating event declares `compensates`.'
|
|
130
|
+
rules: [R166, R171]
|
|
131
|
+
optional: true
|
|
132
|
+
- path: src/modules/platform/saga/
|
|
133
|
+
slot: be.platform
|
|
134
|
+
role: 'The shared store: `saga_states` (status and version fence) and the event claims, with the state machine in `saga.service.ts` — a measured file of a `coverage: required` slot, held at 100 per file with its spec beside it.'
|
|
135
|
+
rules: [R170, R80]
|
|
136
|
+
optional: true
|
|
137
|
+
- path: src/tests/e2e/<area>/<name>.e2e-spec.ts
|
|
138
|
+
slot: be.tests.e2e
|
|
139
|
+
suffix: e2e-spec
|
|
140
|
+
role: One spec per compensation path that names the compensation event and injects a failure through the world (an infra or app outage, a fake's failNext), then reads the compensated state back; plus one spec from the api call to the final state.
|
|
141
|
+
rules: [R173, R167]
|
|
142
|
+
optional: true
|
|
143
|
+
rules:
|
|
144
|
+
- id: BE-SAGA-1
|
|
145
|
+
title: Every step has a compensation, and the orchestrator lists them all
|
|
146
|
+
kind: mandatory
|
|
147
|
+
hfsRules: [R169, R01]
|
|
148
|
+
requirement: |-
|
|
149
|
+
A saga is `be/src/features/saga/<saga>/` with `<saga>.saga.service.ts`, `steps/<step>.saga-step.ts` and
|
|
150
|
+
`compensations/<step>.compensation.ts`: every step has a compensation of the same name and every compensation a step,
|
|
151
|
+
and the orchestrator passes every compensation of its folder to `platform/saga` (the compensation names the step it
|
|
152
|
+
undoes). The folders are slots enabled by the declared pattern; a saga folder in a product that does not declare
|
|
153
|
+
`patterns: ["saga"]` is `HFS_SLOT_NOT_ENABLED`.
|
|
154
|
+
rationale: |-
|
|
155
|
+
A forward step with no way back leaves a half-done flow when a later step fails. Naming the pair by file makes the
|
|
156
|
+
missing half a finding, and listing both in the orchestrator keeps a step nobody runs from hiding.
|
|
157
|
+
cases:
|
|
158
|
+
- id: case-1
|
|
159
|
+
when: A new step
|
|
160
|
+
write: 'features/saga/place-order/steps/reserve-order.saga-step.ts and features/saga/place-order/compensations/reserve-order.compensation.ts; the orchestrator passes the compensation object to `platform/saga`.'
|
|
161
|
+
verification:
|
|
162
|
+
automated:
|
|
163
|
+
- BE_SAGA_STEP_COMPENSATION
|
|
164
|
+
- HFS_SLOT_UNDECLARED
|
|
165
|
+
- HFS_SLOT_AMBIGUOUS
|
|
166
|
+
- HFS_SLOT_NOT_ENABLED
|
|
167
|
+
- HFS_FORBIDDEN_PRESENT
|
|
168
|
+
- HFS_TRACKED_MUST_BE_IGNORED
|
|
169
|
+
- HFS_ROOT_ENTRY_FORBIDDEN
|
|
170
|
+
- HFS_ROOT_MARKDOWN_FORBIDDEN
|
|
171
|
+
- HFS_ROOT_SRC_FORBIDDEN_FE
|
|
172
|
+
- HFS_SRC_LAYOUT_INVALID
|
|
173
|
+
- HFS_MODULE_TIER_INVALID
|
|
174
|
+
- HFS_WORK_IN_FE
|
|
175
|
+
- HFS_STACKS_IN_SIDE
|
|
176
|
+
manual:
|
|
177
|
+
- Check the compensation undoes every effect of the step, including the ones another service made.
|
|
178
|
+
relatedExamples: [saga]
|
|
179
|
+
- id: BE-SAGA-2
|
|
180
|
+
title: The state of a run is typed, versioned and moved only through the fenced store
|
|
181
|
+
kind: mandatory
|
|
182
|
+
hfsRules: [R170, R80, R172, R203]
|
|
183
|
+
requirement: |-
|
|
184
|
+
`<saga>.saga-state.ts` exports the state type with `status` and `version: number`. The orchestrator is a thin feature
|
|
185
|
+
file (R203 `BE_FEATURE_THIN`) and the run's orchestration lives in `platform/saga`: the orchestrator moves a run only
|
|
186
|
+
through it, a transition names the version it read and moves it by one, and takes its event through the
|
|
187
|
+
inbox first. A consumer of a saga passes the id of the delivery (`message.eventId`) to its command.
|
|
188
|
+
rationale: |-
|
|
189
|
+
Deliveries repeat and arrive late. The version fence stops a zombie delivery from moving a run twice, and the inbox makes
|
|
190
|
+
a redelivery a no-op.
|
|
191
|
+
cases:
|
|
192
|
+
- id: case-1
|
|
193
|
+
when: A failure event arrives twice
|
|
194
|
+
write: 'compensate({ saga, correlationId, eventId, compensation }) claims the event, moves running to compensating at version 1, logs Compensating and runs compensation.run(correlationId), settles compensated; the second delivery answers duplicate.'
|
|
195
|
+
verification:
|
|
196
|
+
automated:
|
|
197
|
+
- BE_SAGA_STATE_VERSIONED
|
|
198
|
+
- BE_INBOX_DEDUPE_MISSING
|
|
199
|
+
- BE_SAGA_CONSUMER_DEDUPE
|
|
200
|
+
- BE_FEATURE_THIN
|
|
201
|
+
manual:
|
|
202
|
+
- Check a failing compensation gives the event back so the redelivery resumes the run.
|
|
203
|
+
relatedExamples: [saga]
|
|
204
|
+
- id: BE-SAGA-3
|
|
205
|
+
title: Every event of a step or compensation is in a vendored contract
|
|
206
|
+
kind: mandatory
|
|
207
|
+
hfsRules: [R171, R166]
|
|
208
|
+
requirement: |-
|
|
209
|
+
A step and a compensation each declare `readonly event = "<name>"`; both events are declared in a vendored contract
|
|
210
|
+
(`be/contracts/<service>/events.json`), and the contract of the compensation's event declares `compensates: "<event of
|
|
211
|
+
the step>"`. A consumer lists what it reads in `consumes.ts`.
|
|
212
|
+
rationale: |-
|
|
213
|
+
The failure event is the only thing that tells the saga to undo a step; declaring which step it undoes in the contract
|
|
214
|
+
makes the pairing reviewable in the provider and judged in the consumer.
|
|
215
|
+
cases:
|
|
216
|
+
- id: case-1
|
|
217
|
+
when: Billing rejects an invoice
|
|
218
|
+
write: 'billing/src/events.ts: "billing.invoice-rejected": { version: 1, compensates: "order.placed", payload: { ... } }.'
|
|
219
|
+
verification:
|
|
220
|
+
automated:
|
|
221
|
+
- BE_SAGA_EVENT_CONTRACT
|
|
222
|
+
- HFS_EVENT_CONTRACT
|
|
223
|
+
manual:
|
|
224
|
+
- Review the payload of a new failure event for what the compensation needs.
|
|
225
|
+
relatedExamples: [saga]
|
|
226
|
+
- id: BE-SAGA-4
|
|
227
|
+
title: Every compensation path has an e2e spec that injects a failure
|
|
228
|
+
kind: mandatory
|
|
229
|
+
hfsRules: [R173, R167]
|
|
230
|
+
requirement: |-
|
|
231
|
+
For each compensation, `be/src/tests/e2e/<area>/*.e2e-spec.ts` boots the real apps through `useTestWorld`, names the
|
|
232
|
+
compensation's event, injects a failure through the world (`during`, `cut` or `latency` of an infra service or an app, a
|
|
233
|
+
fake's `failNext`, `interruptDatabase`) and reads the compensated state back. One more spec runs the saga from the api
|
|
234
|
+
call to its final state.
|
|
235
|
+
rationale: |-
|
|
236
|
+
The compensation exists for the failures; a spec that only runs the happy flow proves nothing about it. A fault injected
|
|
237
|
+
at the moment the failure event is delivered proves the retry and the resume.
|
|
238
|
+
cases:
|
|
239
|
+
- id: case-1
|
|
240
|
+
when: The order compensation
|
|
241
|
+
write: 'e2e/order/invoice-rejected.e2e-spec.ts cuts the order database while billing.invoice-rejected is delivered, restores it and asserts the order cancelled, the stock back and the saga run compensated at version 3.'
|
|
242
|
+
verification:
|
|
243
|
+
automated:
|
|
244
|
+
- BE_SAGA_E2E_MISSING
|
|
245
|
+
- BE_ASYNC_SPEC_MISSING
|
|
246
|
+
manual:
|
|
247
|
+
- Check the injected fault hits the compensation, not only the step before it.
|
|
248
|
+
relatedExamples: [saga]
|