@starci/hfs 4.0.8 → 4.0.10
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/LICENSE +21 -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 +131 -24
- package/package.json +16 -10
- package/report/sonar.mjs +8 -8
- package/runtime/engine/admission.mjs +308 -0
- package/runtime/engine/canonical-json.mjs +10 -0
- package/runtime/engine/config.mjs +486 -0
- package/runtime/engine/db/blob.mjs +315 -0
- package/runtime/engine/db/ledger-paths.mjs +83 -0
- package/runtime/engine/db/ledger.mjs +1118 -0
- package/runtime/engine/db/machine-connection.mjs +135 -0
- package/runtime/engine/db/machine-schema.mjs +84 -0
- package/runtime/engine/db/machine.mjs +1367 -0
- package/runtime/engine/db/migrations/machine/0001-init.sql +924 -0
- package/runtime/engine/db/migrations/runtime/0001-init.sql +1108 -0
- package/runtime/engine/db/provider-reservations.mjs +101 -0
- package/runtime/engine/digest.mjs +16 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/engine/secrets.mjs +130 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +162 -31
- 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 +611 -51
- package/runtime/modules/models/registry.yaml +553 -0
- package/runtime/modules/models/runtimes.yaml +695 -0
- package/runtime/modules/ops/_labels.yaml +53 -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 +16 -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 +18 -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/node/lib.mjs +14 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/process/lib.mjs +111 -0
- package/runtime/scripts/api/process/owned-process.mjs +78 -0
- package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -0
- package/runtime/scripts/api/process/run-program.mjs +7 -0
- package/runtime/scripts/api/process/stop-owned-process.mjs +9 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -12
- package/runtime/scripts/api/sops/lib.mjs +255 -0
- package/runtime/scripts/api/sops/seal.mjs +22 -0
- package/runtime/scripts/connectors/lib.mjs +488 -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 +2 -2
- 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 +209 -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/clip.mjs +19 -0
- package/runtime/scripts/lib/display-names.mjs +259 -0
- 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/example-refs.mjs +158 -0
- package/runtime/scripts/lib/fs-kind.mjs +14 -4
- 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-schema.mjs +52 -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/mutation-fence.mjs +15 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/process-identity.mjs +6 -0
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/read-yaml.mjs +18 -0
- package/runtime/scripts/lib/redact.mjs +161 -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/sleep.mjs +7 -0
- package/runtime/scripts/lib/sops-envelope.mjs +112 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -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,113 @@
|
|
|
1
|
+
schema: starci/knowledge-source@1
|
|
2
|
+
id: be.domain
|
|
3
|
+
title: Domain capabilities (module family domain)
|
|
4
|
+
purpose: |-
|
|
5
|
+
Given a business capability (orders, payments, catalog), where does its decision live? In `modules/domain/<capability>/`: services that
|
|
6
|
+
own the rules, the entities and migrations of its tables on one connection, its errors and its messages. The capability belongs to
|
|
7
|
+
one bounded context, so everything inside it is synchronous and runs in one transaction; the context talks to other contexts only
|
|
8
|
+
through events. Features reach it through its public `index.ts`.
|
|
9
|
+
appliesTo:
|
|
10
|
+
- backend
|
|
11
|
+
family: be
|
|
12
|
+
provenance:
|
|
13
|
+
reviewedAt: 2026-10-02
|
|
14
|
+
sources:
|
|
15
|
+
- knowledge/hfs/README.md
|
|
16
|
+
- knowledge/hfs/slots.yaml
|
|
17
|
+
- knowledge/hfs/rules.yaml
|
|
18
|
+
- knowledge/patterns/be/services.yaml
|
|
19
|
+
- knowledge/patterns/be/persistence.yaml
|
|
20
|
+
- knowledge/patterns/be/event-bus.yaml
|
|
21
|
+
- owner decision 2026-10-02 (bounded contexts)
|
|
22
|
+
limitations: |-
|
|
23
|
+
Static checks prove the folder shape, the one connection per context, the coupling between contexts and the one-transaction rule. They do
|
|
24
|
+
not prove that a rule is the right business rule; the service specs and the e2e run do.
|
|
25
|
+
rules:
|
|
26
|
+
- id: BE-DOMAIN-1
|
|
27
|
+
title: A capability owns its rules, its tables and its transaction
|
|
28
|
+
kind: mandatory
|
|
29
|
+
hfsRules: [R175, R176, R174, R30]
|
|
30
|
+
requirement: |-
|
|
31
|
+
A capability folder holds `index.ts`, `<capability>.module.ts`, `<capability>.service.ts` with its `<capability>.service.spec.ts` beside it,
|
|
32
|
+
`<capability>.contracts.ts`, `errors/`, `messages/` and `persistence/` (its entities, migrations, SQL and rows on the connection of its
|
|
33
|
+
context). A service opens one transaction on that connection and publishes its events inside it (`eventBus.publish(event, tx)`, event-bus.yaml).
|
|
34
|
+
No relation, foreign key or import reaches a capability of another context; contexts are coupled by events only.
|
|
35
|
+
rationale: |-
|
|
36
|
+
A capability that owns its rules and its tables can change them without asking another team, and a transaction that touches one connection
|
|
37
|
+
stays atomic.
|
|
38
|
+
cases:
|
|
39
|
+
- id: case-1
|
|
40
|
+
when: Order and payment must agree
|
|
41
|
+
write: 'The payment service commits its row and publishes PaymentSettledEvent in one transaction; an order reactor consumes it and the order service updates its own copy.'
|
|
42
|
+
verification:
|
|
43
|
+
automated:
|
|
44
|
+
- BE_CONTEXT_COUPLING
|
|
45
|
+
- BE_CONTEXT_TRANSACTION
|
|
46
|
+
- BE_CONTEXT_OWNER
|
|
47
|
+
- BE_PUBLIC_SURFACE
|
|
48
|
+
- ARCH_OWNER_EXPORT_BYPASS
|
|
49
|
+
- ARCH_OWNER_EXPORT_STAR
|
|
50
|
+
- HFS_ALIAS_REEXPORT
|
|
51
|
+
- ARCH_PACKAGE_EXPORT_BYPASS
|
|
52
|
+
manual:
|
|
53
|
+
- Confirm the capability is named after the business capability, not after a technical layer.
|
|
54
|
+
relatedExamples: [domain]
|
|
55
|
+
- id: BE-DOMAIN-2
|
|
56
|
+
title: Work inside one context is synchronous; an event crosses contexts or starts async work
|
|
57
|
+
kind: mandatory
|
|
58
|
+
hfsRules: [R152, R175]
|
|
59
|
+
requirement: |-
|
|
60
|
+
A handler that needs another capability of the same context calls its service in the same transaction and no event exists for it. An event is
|
|
61
|
+
published only when another context must react or when the work it starts is asynchronous by nature (a job, a saga step, a reactor, a projection
|
|
62
|
+
recompute), and only from a domain service inside its transaction.
|
|
63
|
+
rationale: |-
|
|
64
|
+
An event inside one context adds a delivery that can fail between two writes the transaction would have made atomic.
|
|
65
|
+
cases:
|
|
66
|
+
- id: case-1
|
|
67
|
+
when: Checkout reserves stock in the same context
|
|
68
|
+
write: 'The order service calls the catalog service in its transaction; PlacementCompletedEvent is published only for the billing context.'
|
|
69
|
+
verification:
|
|
70
|
+
automated:
|
|
71
|
+
- BE_OUTBOX_WRITE_TX
|
|
72
|
+
- BE_CONTEXT_COUPLING
|
|
73
|
+
manual:
|
|
74
|
+
- Confirm no published event is consumed by the context that published it only to tell itself something.
|
|
75
|
+
relatedExamples: [domain]
|
|
76
|
+
files:
|
|
77
|
+
- path: src/modules/domain/<capability>/index.ts
|
|
78
|
+
slot: be.domain
|
|
79
|
+
role: The public surface (services, contracts, errors and the entity and migration arrays), never an entity class used elsewhere.
|
|
80
|
+
rules: [BE_PUBLIC_SURFACE]
|
|
81
|
+
- path: src/modules/domain/<capability>/<capability>.module.ts
|
|
82
|
+
slot: be.domain
|
|
83
|
+
role: Registers the services of the capability.
|
|
84
|
+
- path: src/modules/domain/<capability>/<capability>.service.ts
|
|
85
|
+
slot: be.domain
|
|
86
|
+
role: The rules, in one transaction on the connection of the context.
|
|
87
|
+
rules: [BE_CONTEXT_TRANSACTION, BE_OUTBOX_WRITE_TX]
|
|
88
|
+
- path: src/modules/domain/<capability>/<capability>.service.spec.ts
|
|
89
|
+
slot: be.domain
|
|
90
|
+
role: The unit spec beside the service, with a few mocks.
|
|
91
|
+
- path: src/modules/domain/<capability>/<capability>.contracts.ts
|
|
92
|
+
slot: be.domain
|
|
93
|
+
role: The plain types the service takes and returns.
|
|
94
|
+
- path: src/modules/domain/<capability>/errors/<capability>.error.ts
|
|
95
|
+
slot: be.errors
|
|
96
|
+
role: The error family of the capability.
|
|
97
|
+
- path: src/modules/domain/<capability>/messages/<capability>.messages.ts
|
|
98
|
+
slot: be.domain.messages
|
|
99
|
+
role: The keyed vi and en text of the capability.
|
|
100
|
+
- path: src/modules/domain/<capability>/persistence/connection.ts
|
|
101
|
+
slot: be.persistence
|
|
102
|
+
role: The entity and migration arrays the apps register on the connection of the context.
|
|
103
|
+
- path: src/modules/domain/<capability>/persistence/entities/<table>.entity.ts
|
|
104
|
+
slot: be.persistence
|
|
105
|
+
role: One table of the capability; no relation to another context.
|
|
106
|
+
rules: [BE_CONTEXT_COUPLING]
|
|
107
|
+
- path: src/modules/domain/<capability>/persistence/migrations/<epochMs13>-<name>.ts
|
|
108
|
+
slot: be.persistence
|
|
109
|
+
role: One migration; no foreign key onto another context.
|
|
110
|
+
rules: [BE_CONTEXT_COUPLING]
|
|
111
|
+
- path: src/modules/domain/<capability>/persistence/<name>.sql.ts
|
|
112
|
+
slot: be.persistence
|
|
113
|
+
role: The statements of the capability, one owner per table.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
schema: starci/knowledge-source@1
|
|
2
|
+
id: be.event-bus
|
|
3
|
+
title: Event bus
|
|
4
|
+
purpose: |-
|
|
5
|
+
Given a fact another context or a background process must react to, how does it travel? Work inside ONE context stays
|
|
6
|
+
synchronous: the handler calls that context's domain service in one transaction and no event exists. An event exists only to
|
|
7
|
+
cross contexts or to trigger asynchronous work (jobs, sagas, reactors, projections). It is a typed event class, published with
|
|
8
|
+
`eventBus.publish(event, tx)` by a domain service inside its transaction, and travels one durable path: transactional outbox,
|
|
9
|
+
relay, Kafka, consumer with retry and dead letter. `platform/event-bus` is the one owner and the only importer of `kafkajs`.
|
|
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.transport.message, be.feature.reactors, be.events, be.platform.event-bus]
|
|
16
|
+
pattern: event-bus
|
|
17
|
+
provenance:
|
|
18
|
+
reviewedAt: 2026-10-02
|
|
19
|
+
sources:
|
|
20
|
+
- knowledge/hfs/README.md
|
|
21
|
+
- knowledge/hfs/slots.yaml
|
|
22
|
+
- knowledge/hfs/rules.yaml
|
|
23
|
+
- owner decision 2026-10-02 (Kafka only, transactional outbox, typed event classes, publish(event, tx))
|
|
24
|
+
- https://microservices.io/patterns/data/transactional-outbox.html
|
|
25
|
+
limitations: |-
|
|
26
|
+
Static checks prove who may publish, with which transaction, and that a consumer is a named door that forwards the event
|
|
27
|
+
id. They do not prove broker configuration, partition keys, schema evolution or the operational handling of a dead letter;
|
|
28
|
+
the test-world scenarios (`event-bus/*`) prove the delivery behaviour against a real broker.
|
|
29
|
+
rules:
|
|
30
|
+
- id: BE-EVENT-BUS-1
|
|
31
|
+
title: Inside one context nothing is an event
|
|
32
|
+
kind: mandatory
|
|
33
|
+
hfsRules: [R152, R26]
|
|
34
|
+
requirement: |-
|
|
35
|
+
Work that stays inside one context (one connection, one set of tables) is synchronous: the handler dispatches to that
|
|
36
|
+
context's domain service, which does the whole change in one transaction. No event, no queue and no bus is involved, and a
|
|
37
|
+
caller that needs the result reads it from the return value. A fact is published as an event only when (a) another context
|
|
38
|
+
must react to it, or (b) the work it triggers is asynchronous by nature (a job, a saga step, a reactor, a projection
|
|
39
|
+
refresh). One transaction touches one context's connection only.
|
|
40
|
+
rationale: |-
|
|
41
|
+
An event inside one context buys eventual consistency, a failure mode and a broker round trip for work the transaction
|
|
42
|
+
already makes atomic. Events are the price of crossing a boundary, not a style.
|
|
43
|
+
cases:
|
|
44
|
+
- id: case-1
|
|
45
|
+
when: 'A buyer adds an item to the cart (same context, the answer is needed now)'
|
|
46
|
+
write: 'add-cart-item.handler.ts dispatches to CartService.addItem, which updates the cart rows in one transaction and returns the cart. No event. (passing)'
|
|
47
|
+
- id: case-2
|
|
48
|
+
when: 'Order needs to know whether a payment settled (payment is another context)'
|
|
49
|
+
write: 'Order never reads the payment tables or imports the payment service. PaymentService publishes PaymentSettledEvent in its transaction; an order reactor keeps a local copy of the status, or a projection serves the read. (a direct read is the violation)'
|
|
50
|
+
verification:
|
|
51
|
+
automated:
|
|
52
|
+
- BE_OUTBOX_WRITE_TX
|
|
53
|
+
- BE_TIER_DIRECTION
|
|
54
|
+
- FE_TIER_DIRECTION
|
|
55
|
+
- ARCH_PACKAGE_IMPORTS_APP
|
|
56
|
+
manual:
|
|
57
|
+
- Confirm each published event crosses a context or starts asynchronous work, and no handler publishes to tell itself something.
|
|
58
|
+
relatedExamples: [event-bus]
|
|
59
|
+
- id: BE-EVENT-BUS-2
|
|
60
|
+
title: An event is a typed class that equals its vendored contract
|
|
61
|
+
kind: mandatory
|
|
62
|
+
hfsRules: [R151]
|
|
63
|
+
requirement: |-
|
|
64
|
+
Events are classes in `be/src/modules/events/<service>/<event>.event.ts`, one per file, named after the event
|
|
65
|
+
(`order.placed` is `order-placed.event.ts`): a class extending the platform `BaseEvent` with literal
|
|
66
|
+
`static readonly eventName` and `static readonly version`, a payload typed in the class, a `create()` that builds it
|
|
67
|
+
and a `parse()` that reads a delivered payload back. Each class is an entry of the vendored contract
|
|
68
|
+
`be/contracts/<service>/events.json` at the same name and version and every contract entry has its class. Schemas
|
|
69
|
+
evolve only additively; a breaking change is a new event name (`<event>.v2`).
|
|
70
|
+
rationale: |-
|
|
71
|
+
The class is what the publisher and the consumer compile against; the contract is what another service pins. Two
|
|
72
|
+
sources of truth drift, so a check holds them equal.
|
|
73
|
+
cases:
|
|
74
|
+
- id: case-1
|
|
75
|
+
when: A new event
|
|
76
|
+
write: 'modules/events/order/order-placed.event.ts with eventName "order.placed" and version 1, then `starci app emit` so be/contracts/order/events.json lists it.'
|
|
77
|
+
verification:
|
|
78
|
+
automated:
|
|
79
|
+
- BE_EVENT_CLASS_CONTRACT
|
|
80
|
+
manual:
|
|
81
|
+
- Confirm a payload change is additive.
|
|
82
|
+
relatedExamples: [event-bus]
|
|
83
|
+
- id: BE-EVENT-BUS-3
|
|
84
|
+
title: Publish through the bus, in the domain transaction, to Kafka only
|
|
85
|
+
kind: mandatory
|
|
86
|
+
hfsRules: [R152, R90, R87]
|
|
87
|
+
requirement: |-
|
|
88
|
+
`eventBus.publish(event, tx)` is called only in `modules/domain`, with the manager of the transaction that makes the
|
|
89
|
+
domain change (`this.entityManager.transaction(async (manager) => { ...; await this.eventBus.publish(event, manager) })`).
|
|
90
|
+
It writes a row in the outbox table of that transaction's connection; the relay hands committed rows to Kafka (topic
|
|
91
|
+
`events.<service>`, key = aggregate id) and marks them sent. `kafkajs` is imported only by `platform/event-bus`. Nest's
|
|
92
|
+
`EventBus`, `@EventsHandler`, `@OnEvent` and `EventEmitter2` do not exist, and nothing publishes to Kafka directly.
|
|
93
|
+
There is no in-process or local transport: unit specs use a recording fake of the `EventBus` port, allowed in `*.spec.ts`.
|
|
94
|
+
rationale: |-
|
|
95
|
+
The outbox row commits with the domain change, so a rolled-back change never emits and a committed one is never lost;
|
|
96
|
+
Kafka then gives ordered, replayable, cross-service delivery.
|
|
97
|
+
cases:
|
|
98
|
+
- id: case-1
|
|
99
|
+
when: A domain service places an order
|
|
100
|
+
write: 'await this.entityManager.transaction(async (manager) => { await manager.save(...); await this.eventBus.publish(OrderPlacedEvent.create({ ... }), manager) })'
|
|
101
|
+
verification:
|
|
102
|
+
automated:
|
|
103
|
+
- BE_OUTBOX_WRITE_TX
|
|
104
|
+
- BE_INFRA_OWNER
|
|
105
|
+
- BE_CQRS_SHAPE
|
|
106
|
+
- BE_READONLY_BOUNDARY
|
|
107
|
+
manual: []
|
|
108
|
+
relatedExamples: [event-bus]
|
|
109
|
+
- id: BE-EVENT-BUS-4
|
|
110
|
+
title: A consumer is a thin door; delivery is at least once with retry and a dead letter
|
|
111
|
+
kind: mandatory
|
|
112
|
+
hfsRules: [R153, R80]
|
|
113
|
+
requirement: |-
|
|
114
|
+
A consumer is `transport/message/<event>.consumer.ts` implementing `EventConsumer<E>` with `readonly event = <Event>Event`;
|
|
115
|
+
`handle(delivery)` dispatches one command and forwards `delivery.eventId`. Delivery is at least once: the domain service the
|
|
116
|
+
command reaches claims the event id in `platform/inbox` as its first awaited expression, inside its own transaction, so a
|
|
117
|
+
redelivery is a no-op. A throw schedules a retry with exponential backoff on the retry topic (`events.<service>.retry`);
|
|
118
|
+
after the event's `attempts` the message goes to the dead-letter topic (`events.<service>.dlq`), which an operator reads
|
|
119
|
+
and requeues through `eventBus.deadLetters(event)` and `eventBus.requeue(id)` (a cli command). `eventBus.pendingRetries(event)`
|
|
120
|
+
lets a failure-injection spec wait for a retry.
|
|
121
|
+
rationale: |-
|
|
122
|
+
A claim persisted before the handler finishes would lose the event on a crash; claiming in the effect's transaction makes
|
|
123
|
+
the effect and the claim one atomic fact.
|
|
124
|
+
cases:
|
|
125
|
+
- id: case-1
|
|
126
|
+
when: A consumer of order.placed
|
|
127
|
+
write: 'order-placed.consumer.ts: handle(delivery) { await this.commandBus.execute(new IssueInvoiceCommand({ request: { eventId: delivery.eventId, ... } })) }'
|
|
128
|
+
verification:
|
|
129
|
+
automated:
|
|
130
|
+
- BE_EVENT_CONSUMER_SHAPE
|
|
131
|
+
- BE_INBOX_DEDUPE_MISSING
|
|
132
|
+
manual:
|
|
133
|
+
- Confirm the retry budget and backoff fit the consumer's side effects.
|
|
134
|
+
relatedExamples: [event-bus]
|
|
135
|
+
- id: BE-EVENT-BUS-5
|
|
136
|
+
title: A declared event bus is proven on the test world
|
|
137
|
+
kind: mandatory
|
|
138
|
+
hfsRules: [R155]
|
|
139
|
+
requirement: |-
|
|
140
|
+
An app that declares the pattern (`hfs.json` `sides.be.patterns: ["event-bus"]`) has e2e or integration tests, titled
|
|
141
|
+
`event-bus/<scenario>: ...`, for each scenario of `ruleParams.be.patternScenarios.event-bus`, run against a real Kafka:
|
|
142
|
+
`atomic-with-transaction` (the outbox row exists exactly when the domain change commits), `relay-only-after-commit` (nothing is
|
|
143
|
+
published for a rolled-back transaction), `redelivery-then-dead-letter` (a failing consumer is retried, then buried) and
|
|
144
|
+
`duplicate-delivery-is-a-noop` (a redelivered event changes nothing twice).
|
|
145
|
+
rationale: |-
|
|
146
|
+
Delivery behaviour is not visible to a linter; a real broker run is the only proof.
|
|
147
|
+
cases:
|
|
148
|
+
- id: case-1
|
|
149
|
+
when: The order e2e suite
|
|
150
|
+
write: "it('event-bus/atomic-with-transaction: the row commits with the order', ...)"
|
|
151
|
+
verification:
|
|
152
|
+
automated:
|
|
153
|
+
- BE_PATTERN_SPEC_MISSING
|
|
154
|
+
manual: []
|
|
155
|
+
relatedExamples: [event-bus]
|
|
156
|
+
files:
|
|
157
|
+
- path: src/modules/platform/outbox/index.ts
|
|
158
|
+
slot: be.platform
|
|
159
|
+
role: "The public surface of the shared outbox relay."
|
|
160
|
+
template: outbox/platform/index.ts.tpl
|
|
161
|
+
- path: src/modules/platform/outbox/outbox.log-events.ts
|
|
162
|
+
slot: be.platform
|
|
163
|
+
role: "The log event of a failed relay pass."
|
|
164
|
+
template: outbox/platform/outbox.log-events.ts.tpl
|
|
165
|
+
- path: src/modules/platform/outbox/outbox-relay.policy.ts
|
|
166
|
+
slot: be.platform
|
|
167
|
+
role: "The one relay loop the event bus and the queues share: read, deliver and mark in one transaction."
|
|
168
|
+
template: outbox/platform/outbox-relay.policy.ts.tpl
|
|
169
|
+
- path: src/modules/platform/outbox/outbox-relay.policy.spec.ts
|
|
170
|
+
slot: be.platform
|
|
171
|
+
template: outbox/platform/outbox-relay.policy.spec.ts.tpl
|
|
172
|
+
- path: src/modules/platform/event-bus/errors/event-bus.error.ts
|
|
173
|
+
slot: be.platform.event-bus
|
|
174
|
+
template: event-bus/platform/errors/event-bus.error.ts.tpl
|
|
175
|
+
- path: src/modules/platform/event-bus/event-bus.config.ts
|
|
176
|
+
slot: be.platform.event-bus
|
|
177
|
+
template: event-bus/platform/event-bus.config.ts.tpl
|
|
178
|
+
- path: src/modules/platform/event-bus/event-bus.contracts.ts
|
|
179
|
+
slot: be.platform.event-bus
|
|
180
|
+
template: event-bus/platform/event-bus.contracts.ts.tpl
|
|
181
|
+
- path: src/modules/platform/event-bus/event-bus.decorators.ts
|
|
182
|
+
slot: be.platform.event-bus
|
|
183
|
+
template: event-bus/platform/event-bus.decorators.ts.tpl
|
|
184
|
+
- path: src/modules/platform/event-bus/event-bus.log-events.ts
|
|
185
|
+
slot: be.platform.event-bus
|
|
186
|
+
template: event-bus/platform/event-bus.log-events.ts.tpl
|
|
187
|
+
- path: src/modules/platform/event-bus/event-bus.module-definition.ts
|
|
188
|
+
slot: be.platform.event-bus
|
|
189
|
+
template: event-bus/platform/event-bus.module-definition.ts.tpl
|
|
190
|
+
- path: src/modules/platform/event-bus/event-bus.module.ts
|
|
191
|
+
slot: be.platform.event-bus
|
|
192
|
+
template: event-bus/platform/event-bus.module.ts.tpl
|
|
193
|
+
- path: src/modules/platform/event-bus/event-bus.options.ts
|
|
194
|
+
slot: be.platform.event-bus
|
|
195
|
+
template: event-bus/platform/event-bus.options.ts.tpl
|
|
196
|
+
- path: src/modules/platform/event-bus/event-bus.port.ts
|
|
197
|
+
slot: be.platform.event-bus
|
|
198
|
+
template: event-bus/platform/event-bus.port.ts.tpl
|
|
199
|
+
- path: src/modules/platform/event-bus/event-bus.service.spec.ts
|
|
200
|
+
slot: be.platform.event-bus
|
|
201
|
+
template: event-bus/platform/event-bus.service.spec.ts.tpl
|
|
202
|
+
- path: src/modules/platform/event-bus/event-bus.service.ts
|
|
203
|
+
slot: be.platform.event-bus
|
|
204
|
+
role: "publish (outbox write through tx), dead letters, requeue, pending retries."
|
|
205
|
+
template: event-bus/platform/event-bus.service.ts.tpl
|
|
206
|
+
- path: src/modules/platform/event-bus/event-envelope.policy.ts
|
|
207
|
+
slot: be.platform.event-bus
|
|
208
|
+
template: event-bus/platform/event-envelope.policy.ts.tpl
|
|
209
|
+
- path: src/modules/platform/event-bus/event-envelope.policy.spec.ts
|
|
210
|
+
slot: be.platform.event-bus
|
|
211
|
+
template: event-bus/platform/event-envelope.policy.spec.ts.tpl
|
|
212
|
+
- path: src/modules/platform/event-bus/event-relay.service.spec.ts
|
|
213
|
+
slot: be.platform.event-bus
|
|
214
|
+
template: event-bus/platform/event-relay.service.spec.ts.tpl
|
|
215
|
+
- path: src/modules/platform/event-bus/event-relay.service.ts
|
|
216
|
+
slot: be.platform.event-bus
|
|
217
|
+
role: "Relay of committed outbox rows to Kafka, per registered connection."
|
|
218
|
+
template: event-bus/platform/event-relay.service.ts.tpl
|
|
219
|
+
- path: src/modules/platform/event-bus/event-runner.service.spec.ts
|
|
220
|
+
slot: be.platform.event-bus
|
|
221
|
+
template: event-bus/platform/event-runner.service.spec.ts.tpl
|
|
222
|
+
- path: src/modules/platform/event-bus/event-runner.service.ts
|
|
223
|
+
slot: be.platform.event-bus
|
|
224
|
+
role: "Consumer registry and Kafka read loop with retry and dead letter."
|
|
225
|
+
template: event-bus/platform/event-runner.service.ts.tpl
|
|
226
|
+
- path: src/modules/platform/event-bus/event-transport.port.ts
|
|
227
|
+
slot: be.platform.event-bus
|
|
228
|
+
template: event-bus/platform/event-transport.port.ts.tpl
|
|
229
|
+
- path: src/modules/platform/event-bus/event.policy.ts
|
|
230
|
+
slot: be.platform.event-bus
|
|
231
|
+
role: "Topics, attempts and backoff."
|
|
232
|
+
template: event-bus/platform/event.policy.ts.tpl
|
|
233
|
+
- path: src/modules/platform/event-bus/event.policy.spec.ts
|
|
234
|
+
slot: be.platform.event-bus
|
|
235
|
+
template: event-bus/platform/event.policy.spec.ts.tpl
|
|
236
|
+
- path: src/modules/platform/event-bus/index.ts
|
|
237
|
+
slot: be.platform.event-bus
|
|
238
|
+
template: event-bus/platform/index.ts.tpl
|
|
239
|
+
- path: src/modules/platform/event-bus/kafka-event-transport.client.ts
|
|
240
|
+
slot: be.platform.event-bus
|
|
241
|
+
role: "The one kafkajs importer."
|
|
242
|
+
template: event-bus/platform/kafka-event-transport.client.ts.tpl
|
|
243
|
+
- path: src/modules/platform/event-bus/kafka-event-transport.client.spec.ts
|
|
244
|
+
slot: be.platform.event-bus
|
|
245
|
+
template: event-bus/platform/kafka-event-transport.client.spec.ts.tpl
|
|
246
|
+
- path: src/modules/platform/event-bus/messages/event-bus.messages.ts
|
|
247
|
+
slot: be.domain.messages
|
|
248
|
+
template: event-bus/platform/messages/event-bus.messages.ts.tpl
|
|
249
|
+
- path: src/modules/platform/event-bus/persistence/connection.ts
|
|
250
|
+
slot: be.persistence
|
|
251
|
+
template: event-bus/platform/persistence/connection.ts.tpl
|
|
252
|
+
- path: src/modules/platform/event-bus/persistence/entities/event-outbox.entity.ts
|
|
253
|
+
slot: be.persistence
|
|
254
|
+
template: event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl
|
|
255
|
+
- path: src/modules/platform/event-bus/persistence/event-bus.rows.ts
|
|
256
|
+
slot: be.persistence
|
|
257
|
+
template: event-bus/platform/persistence/event-bus.rows.ts.tpl
|
|
258
|
+
- path: src/modules/platform/event-bus/persistence/event-bus.sql.ts
|
|
259
|
+
slot: be.persistence
|
|
260
|
+
template: event-bus/platform/persistence/event-bus.sql.ts.tpl
|
|
261
|
+
- path: src/modules/platform/event-bus/persistence/migrations/<epochMs13>-create-event-outbox.ts
|
|
262
|
+
slot: be.persistence
|
|
263
|
+
template: event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl
|
|
264
|
+
- path: src/modules/events/<service>/index.ts
|
|
265
|
+
slot: be.events
|
|
266
|
+
- path: src/modules/events/<service>/<event>.event.ts
|
|
267
|
+
slot: be.events
|
|
268
|
+
role: One typed event class.
|
|
269
|
+
rules: [BE_EVENT_CLASS_CONTRACT]
|
|
270
|
+
- path: contracts/<service>/events.json
|
|
271
|
+
slot: be.contract.events
|
|
272
|
+
role: The emitted catalog of the service events (name, version, payload shape); the event classes equal it, so a consumer in another service reads one contract.
|
|
273
|
+
rules: [BE_EVENT_CLASS_CONTRACT]
|
|
274
|
+
- path: src/features/<kind>/<feature>/transport/message/<event>.consumer.ts
|
|
275
|
+
slot: be.transport.message
|
|
276
|
+
role: The consumer door of one event.
|
|
277
|
+
rules: [BE_EVENT_CONSUMER_SHAPE]
|
|
278
|
+
- path: src/features/<kind>/<feature>/transport/message/<feature>-message.module.ts
|
|
279
|
+
slot: be.transport.message
|
|
280
|
+
role: Registers the consumers with the registry.
|
|
281
|
+
- path: src/tests/e2e/<area>/<event>.e2e-spec.ts
|
|
282
|
+
slot: be.tests.e2e
|
|
283
|
+
role: "The consumed event's e2e flow; titles event-bus/<scenario>: ... prove the pattern."
|
|
284
|
+
spec: event-bus/atomic-with-transaction, event-bus/relay-only-after-commit, event-bus/redelivery-then-dead-letter, event-bus/duplicate-delivery-is-a-noop
|
|
285
|
+
rules: [BE_PATTERN_SPEC_MISSING]
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
schema: starci/knowledge-source@1
|
|
2
|
+
id: be.jobs
|
|
3
|
+
title: Fenced background jobs
|
|
4
|
+
purpose: |-
|
|
5
|
+
Given work that runs in the background and must survive a worker that stalls, restarts or is replaced, how is it claimed and
|
|
6
|
+
settled? A job is a row whose `fencingToken` the claim bumps. Every write on a claimed job carries the token the writer holds and
|
|
7
|
+
is refused when it is stale, so a zombie worker (one that lost its claim but kept running) changes nothing, and every external
|
|
8
|
+
effect carries a run key that includes the token, so the provider drops a repeat. `platform/jobs` is the one owner of the row.
|
|
9
|
+
appliesTo:
|
|
10
|
+
- backend
|
|
11
|
+
family: be
|
|
12
|
+
# The slot ids this topic teaches (knowledge/hfs/slots.yaml); scripts/gates/read-digest.mjs inverts these
|
|
13
|
+
# into the READ-step slot -> pattern-topic map (knowledge/op-gate.yaml kinds), each slot's topics in index.yaml order.
|
|
14
|
+
slots: [be.feature.jobs, be.feature.jobs.steps, be.platform.jobs]
|
|
15
|
+
pattern: fenced-job
|
|
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
|
+
- owner decision 2026-10-02 (fenced jobs with a required fencing token)
|
|
23
|
+
- https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
|
|
24
|
+
limitations: |-
|
|
25
|
+
Static checks prove who writes the job row, that the token is required in the type and not cast around, that effects carry a
|
|
26
|
+
run key and that a processor has the fenced base. They do not prove that the provider honours the idempotency key or that a
|
|
27
|
+
step is safe to repeat; the test-world scenarios (`fenced-job/*`) prove the zombie and re-dispatch behaviour against real
|
|
28
|
+
Postgres and a double of the provider.
|
|
29
|
+
rules:
|
|
30
|
+
- id: BE-JOBS-1
|
|
31
|
+
title: The job row has one owner and one atomic claim
|
|
32
|
+
kind: mandatory
|
|
33
|
+
hfsRules: [R157, R86]
|
|
34
|
+
requirement: |-
|
|
35
|
+
`modules/platform/jobs` holds the job entity (`id`, `kind`, `status`, `currentStep`, `fencingToken`, `claimedBy`,
|
|
36
|
+
`leaseExpiresAt`, `payload`, a unique `jobKey`), `job-claim.service.ts` and the persistence of the table. `claim()` is one
|
|
37
|
+
atomic upsert keyed by the delivery (`INSERT ... ON CONFLICT (job_key) DO UPDATE SET fencing_token = jobs.fencing_token + 1 ... WHERE
|
|
38
|
+
status = 'failed' OR the lease expired ... RETURNING`, a `SqlText` constant): the job row is created by its first claim, the token is
|
|
39
|
+
bumped by the store and returned, and a done job or a live claim matches nothing. `JobRunnerService` calls it for every BullMQ
|
|
40
|
+
delivery (the job key is the BullMQ job id), then `process`, then the guarded `complete`. Nothing else writes the row: no
|
|
41
|
+
`manager.update`, `increment`, `save`, `insert`, `delete` or query-builder write on the job entity outside `platform/jobs`. The table
|
|
42
|
+
exists on every connection that runs jobs.
|
|
43
|
+
rationale: |-
|
|
44
|
+
A read-modify-write claim races, and a second writer defeats the fence. One owner and one atomic statement keep the token
|
|
45
|
+
monotonic.
|
|
46
|
+
cases:
|
|
47
|
+
- id: case-1
|
|
48
|
+
when: A worker picks up a job
|
|
49
|
+
write: 'const job = await this.claims.claim({ kind: "send-receipt", jobKey: delivery.id, payload, workerId, leaseMs }) // token = previous + 1'
|
|
50
|
+
verification:
|
|
51
|
+
automated:
|
|
52
|
+
- BE_JOB_WRITE_OUTSIDE_OWNER
|
|
53
|
+
- BE_SQL_TABLE_OWNER
|
|
54
|
+
manual:
|
|
55
|
+
- Confirm the claim SQL is a single statement and the lease length fits the longest step.
|
|
56
|
+
relatedExamples: [fenced-job]
|
|
57
|
+
- id: BE-JOBS-2
|
|
58
|
+
title: Every write to a claimed job carries expectedFencingToken, required in the type
|
|
59
|
+
kind: mandatory
|
|
60
|
+
hfsRules: [R158]
|
|
61
|
+
requirement: |-
|
|
62
|
+
`JobClaims.advance`, `complete` and `fail` take `GuardedWrite & {...}` where `GuardedWrite = { jobId: string; expectedFencingToken: number }`:
|
|
63
|
+
the token is required, never optional or nullable, and a call never passes it through `as`, `!` or `any`. Each write is
|
|
64
|
+
`UPDATE ... WHERE id = $1 AND fencing_token = $2`; zero rows updated throws `JobFencedOut` and the worker stops with no
|
|
65
|
+
further effect. A step or processor never catches around a guarded write: the `FencedProcessor` base owns the failure path.
|
|
66
|
+
rationale: |-
|
|
67
|
+
An optional guard is a guard some caller forgets. The token is checked atomically by the store that accepts the write, which
|
|
68
|
+
is the only place a stale worker can be refused.
|
|
69
|
+
cases:
|
|
70
|
+
- id: case-1
|
|
71
|
+
when: A step finishes
|
|
72
|
+
write: 'await this.claims.advance({ jobId: job.jobId, expectedFencingToken: job.fencingToken, step: "send" })'
|
|
73
|
+
verification:
|
|
74
|
+
automated:
|
|
75
|
+
- BE_JOB_FENCE_REQUIRED
|
|
76
|
+
manual: []
|
|
77
|
+
relatedExamples: [fenced-job]
|
|
78
|
+
- id: BE-JOBS-3
|
|
79
|
+
title: An external effect carries a run key that includes the token
|
|
80
|
+
kind: mandatory
|
|
81
|
+
hfsRules: [R159]
|
|
82
|
+
requirement: |-
|
|
83
|
+
`JobClaims.runKey(job, step)` returns a `RunKey` (`<jobId>:<step>:<fencingToken>`); it is the only way to make one. The step's
|
|
84
|
+
one delegating call hands the payload as delivered and the run key to the module service that owns the effect, and every
|
|
85
|
+
call that reaches an integration passes the key as the provider's idempotency key, so a repeated effect of a stale
|
|
86
|
+
worker is dropped by the provider. A read from an integration belongs in the application layer, not in a step. When the
|
|
87
|
+
provider cannot enforce idempotency, constrain overlap and reconcile before a retry.
|
|
88
|
+
rationale: |-
|
|
89
|
+
Fencing the local row does not fence the remote effect. The key ties the effect to one claim.
|
|
90
|
+
cases:
|
|
91
|
+
- id: case-1
|
|
92
|
+
when: A step sends a mail
|
|
93
|
+
write: 'await this.mail.send(message, this.claims.runKey(job, "send"))'
|
|
94
|
+
verification:
|
|
95
|
+
automated:
|
|
96
|
+
- BE_JOB_RUN_KEY
|
|
97
|
+
manual:
|
|
98
|
+
- Confirm the provider documents idempotent handling of its key.
|
|
99
|
+
relatedExamples: [fenced-job]
|
|
100
|
+
- id: BE-JOBS-4
|
|
101
|
+
title: A job is a processor plus steps, started by a producer and run by a worker or api app
|
|
102
|
+
kind: mandatory
|
|
103
|
+
hfsRules: [R160, R156, R46, R203]
|
|
104
|
+
requirement: |-
|
|
105
|
+
`features/jobs/<job>/transport/queue/<job>.processor.ts` declares a class extending `FencedProcessor` (it claims, calls `process(job)` and
|
|
106
|
+
settles through the guarded writes); `steps/<step>.step.ts` declares a class implementing `JobStep`; `application/` holds
|
|
107
|
+
plain CQRS. The processor and every step are thin feature files (R203 `BE_FEATURE_THIN`): they map the claimed job and
|
|
108
|
+
delegate into `modules/`; the claim, the fence, the settle and the retry live in `platform/jobs`. A domain service starts
|
|
109
|
+
the job with a typed queue producer `enqueueX(payload, tx)` inside its transaction
|
|
110
|
+
(queues.yaml). The jobs kind imports only `modules/*`: it never imports an api, saga or reactor feature, and it talks to
|
|
111
|
+
them through events. An app of kind `worker` or `api` composes the job queue module.
|
|
112
|
+
rationale: |-
|
|
113
|
+
One place and one base class mean a job cannot skip the fence, and the kind boundary keeps a long job from running inside a
|
|
114
|
+
request.
|
|
115
|
+
cases:
|
|
116
|
+
- id: case-1
|
|
117
|
+
when: A new background job send-receipt
|
|
118
|
+
write: 'features/jobs/send-receipt/{transport/queue/send-receipt.processor.ts, transport/queue/send-receipt-queue.module.ts, send-receipt.module.ts, steps/mail.step.ts, application/send-receipt.command.ts}'
|
|
119
|
+
verification:
|
|
120
|
+
automated:
|
|
121
|
+
- BE_JOB_SHAPE
|
|
122
|
+
- BE_KIND_ISOLATION
|
|
123
|
+
- BE_BACKGROUND_UNOWNED
|
|
124
|
+
- BE_FEATURE_THIN
|
|
125
|
+
manual:
|
|
126
|
+
- Confirm each step is one external effect that is safe to repeat under the same run key.
|
|
127
|
+
relatedExamples: [fenced-job]
|
|
128
|
+
- id: BE-JOBS-5
|
|
129
|
+
title: A declared fenced-job is proven on the test world
|
|
130
|
+
kind: mandatory
|
|
131
|
+
hfsRules: [R155]
|
|
132
|
+
requirement: |-
|
|
133
|
+
An app that declares `fenced-job` has tests titled `fenced-job/<scenario>: ...` for `claim-bumps-token`,
|
|
134
|
+
`zombie-fenced-out` (a worker with a stale token gets `JobFencedOut` and the external double sees zero calls) and
|
|
135
|
+
`redispatch-isolated` (a re-dispatch claims a higher token, the old worker's write is refused and the new run's effects carry
|
|
136
|
+
their own run key), run on the test world with real Postgres.
|
|
137
|
+
rationale: |-
|
|
138
|
+
The fence is a runtime property; only a paused old worker resuming after reassignment shows it holds.
|
|
139
|
+
cases:
|
|
140
|
+
- id: case-1
|
|
141
|
+
when: The send-receipt e2e suite
|
|
142
|
+
write: "it('fenced-job/zombie-fenced-out: the stale worker changes nothing', ...)"
|
|
143
|
+
verification:
|
|
144
|
+
automated:
|
|
145
|
+
- BE_PATTERN_SPEC_MISSING
|
|
146
|
+
manual: []
|
|
147
|
+
relatedExamples: [fenced-job]
|
|
148
|
+
files:
|
|
149
|
+
- path: src/modules/platform/jobs/index.ts
|
|
150
|
+
slot: be.platform.jobs
|
|
151
|
+
role: "Public surface of the capability."
|
|
152
|
+
template: jobs/platform/jobs.index.ts.tpl
|
|
153
|
+
- path: src/modules/platform/jobs/jobs.module.ts
|
|
154
|
+
slot: be.platform.jobs
|
|
155
|
+
role: "Dynamic module registered once per app that runs jobs, over the connection that holds the job table."
|
|
156
|
+
template: jobs/platform/jobs.module.ts.tpl
|
|
157
|
+
- path: src/modules/platform/jobs/jobs.module-definition.ts
|
|
158
|
+
slot: be.platform.jobs
|
|
159
|
+
template: jobs/platform/jobs.module-definition.ts.tpl
|
|
160
|
+
- path: src/modules/platform/jobs/jobs.options.ts
|
|
161
|
+
slot: be.platform.jobs
|
|
162
|
+
template: jobs/platform/jobs.options.ts.tpl
|
|
163
|
+
- path: src/modules/platform/jobs/jobs.config.ts
|
|
164
|
+
slot: be.platform.jobs
|
|
165
|
+
template: jobs/platform/jobs.config.ts.tpl
|
|
166
|
+
- path: src/modules/platform/jobs/jobs.decorators.ts
|
|
167
|
+
slot: be.platform.jobs
|
|
168
|
+
role: "InjectJobClaims, InjectJobProcessorRegistry and their tokens."
|
|
169
|
+
template: jobs/platform/jobs.decorators.ts.tpl
|
|
170
|
+
- path: src/modules/platform/jobs/jobs.contracts.ts
|
|
171
|
+
slot: be.platform.jobs
|
|
172
|
+
role: "ClaimedJob, GuardedWrite (the token is required), RunKey."
|
|
173
|
+
template: jobs/platform/jobs.contracts.ts.tpl
|
|
174
|
+
- path: src/modules/platform/jobs/jobs.port.ts
|
|
175
|
+
slot: be.platform.jobs
|
|
176
|
+
role: "JobClaims, JobStep and JobProcessorRegistry ports."
|
|
177
|
+
rules: [BE_JOB_FENCE_REQUIRED]
|
|
178
|
+
template: jobs/platform/jobs.port.ts.tpl
|
|
179
|
+
- path: src/modules/platform/jobs/job-claim.service.ts
|
|
180
|
+
slot: be.platform.jobs
|
|
181
|
+
role: "The claim and the guarded writes; the only writer of the job row."
|
|
182
|
+
rules: [BE_JOB_WRITE_OUTSIDE_OWNER]
|
|
183
|
+
template: jobs/platform/job-claim.service.ts.tpl
|
|
184
|
+
- path: src/modules/platform/jobs/job-claim.service.spec.ts
|
|
185
|
+
slot: be.platform.jobs
|
|
186
|
+
template: jobs/platform/job-claim.service.spec.ts.tpl
|
|
187
|
+
- path: src/modules/platform/jobs/job-runner.service.ts
|
|
188
|
+
slot: be.platform.jobs
|
|
189
|
+
role: "Subscribes processors to their queues; claim, process, complete or fail, fenced out means stop."
|
|
190
|
+
template: jobs/platform/job-runner.service.ts.tpl
|
|
191
|
+
- path: src/modules/platform/jobs/job-runner.service.spec.ts
|
|
192
|
+
slot: be.platform.jobs
|
|
193
|
+
template: jobs/platform/job-runner.service.spec.ts.tpl
|
|
194
|
+
- path: src/modules/platform/jobs/fenced.processor.ts
|
|
195
|
+
slot: be.platform.jobs
|
|
196
|
+
role: "The base class every processor extends."
|
|
197
|
+
template: jobs/platform/fenced.processor.ts.tpl
|
|
198
|
+
- path: src/modules/platform/jobs/fenced.processor.spec.ts
|
|
199
|
+
slot: be.platform.jobs
|
|
200
|
+
template: jobs/platform/fenced.processor.spec.ts.tpl
|
|
201
|
+
- path: src/modules/platform/jobs/jobs.log-events.ts
|
|
202
|
+
slot: be.platform.jobs
|
|
203
|
+
template: jobs/platform/jobs.log-events.ts.tpl
|
|
204
|
+
- path: src/modules/platform/jobs/errors/jobs.error.ts
|
|
205
|
+
slot: be.platform.jobs
|
|
206
|
+
role: "JobFencedOut."
|
|
207
|
+
template: jobs/platform/jobs.error.ts.tpl
|
|
208
|
+
- path: src/modules/platform/jobs/messages/jobs.messages.ts
|
|
209
|
+
slot: be.domain.messages
|
|
210
|
+
template: jobs/platform/jobs.messages.ts.tpl
|
|
211
|
+
- path: src/modules/platform/jobs/persistence/connection.ts
|
|
212
|
+
slot: be.persistence
|
|
213
|
+
role: "The job entity array registered on every connection that runs jobs."
|
|
214
|
+
template: jobs/platform/persistence.connection.ts.tpl
|
|
215
|
+
- path: src/modules/platform/jobs/persistence/entities/job.entity.ts
|
|
216
|
+
slot: be.persistence
|
|
217
|
+
template: jobs/platform/persistence.job.entity.ts.tpl
|
|
218
|
+
- path: src/modules/platform/jobs/persistence/jobs.sql.ts
|
|
219
|
+
slot: be.persistence
|
|
220
|
+
role: "The claim and guarded-write statements as SqlText constants."
|
|
221
|
+
template: jobs/platform/persistence.jobs.sql.ts.tpl
|
|
222
|
+
- path: src/modules/platform/jobs/persistence/jobs.rows.ts
|
|
223
|
+
slot: be.persistence
|
|
224
|
+
template: jobs/platform/persistence.jobs.rows.ts.tpl
|
|
225
|
+
- path: src/modules/platform/jobs/persistence/migrations/<epochMs13>-create-jobs.ts
|
|
226
|
+
slot: be.persistence
|
|
227
|
+
template: jobs/platform/persistence.migration.ts.tpl
|
|
228
|
+
- path: src/features/jobs/<job>/index.ts
|
|
229
|
+
slot: be.feature.jobs
|
|
230
|
+
template: jobs/job.index.ts.tpl
|
|
231
|
+
- path: src/features/jobs/<job>/<job>.module.ts
|
|
232
|
+
slot: be.feature.jobs
|
|
233
|
+
template: jobs/job.module.ts.tpl
|
|
234
|
+
- path: src/features/jobs/<job>/transport/queue/<job>-queue.module.ts
|
|
235
|
+
slot: be.feature.jobs.queue
|
|
236
|
+
template: jobs/job.queue.module.ts.tpl
|
|
237
|
+
role: The queue transport module of the job; composed by an api or worker app, registers the processor.
|
|
238
|
+
- path: src/features/jobs/<job>/transport/queue/<job>.processor.ts
|
|
239
|
+
slot: be.feature.jobs.queue
|
|
240
|
+
template: jobs/job.processor.ts.tpl
|
|
241
|
+
role: 'The processor of one job; extends FencedProcessor — thin (R203 `BE_FEATURE_THIN`): `process` maps the claimed job and delegates into modules/.'
|
|
242
|
+
rules: [BE_JOB_SHAPE]
|
|
243
|
+
- path: src/features/jobs/<job>/steps/<step>.step.ts
|
|
244
|
+
slot: be.feature.jobs.steps
|
|
245
|
+
template: jobs/job.step.ts.tpl
|
|
246
|
+
role: 'One external effect; implements JobStep — thin (R203 `BE_FEATURE_THIN`): the fence call first, then one delegating call that hands the payload and the run key to a service of the modules; the payload guards live there.'
|
|
247
|
+
rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
|
|
248
|
+
- path: src/features/jobs/<job>/application/<action>.command.ts
|
|
249
|
+
slot: be.feature.application
|
|
250
|
+
optional: true
|
|
251
|
+
- path: src/features/jobs/<job>/application/<action>.handler.ts
|
|
252
|
+
slot: be.feature.application
|
|
253
|
+
optional: true
|
|
254
|
+
- path: src/features/jobs/<job>/application/<action>.contracts.ts
|
|
255
|
+
slot: be.feature.application
|
|
256
|
+
optional: true
|
|
257
|
+
- path: src/tests/e2e/<area>/<job>.e2e-spec.ts
|
|
258
|
+
slot: be.tests.e2e
|
|
259
|
+
role: "Titles fenced-job/<scenario>: ... prove the pattern."
|
|
260
|
+
spec: fenced-job/claim-bumps-token, fenced-job/zombie-fenced-out, fenced-job/redispatch-isolated
|
|
261
|
+
rules: [BE_PATTERN_SPEC_MISSING]
|