@starci/hfs 4.0.7 → 4.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +86 -43
- 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 +54 -20
- package/package.json +14 -9
- package/report/sonar.mjs +8 -8
- package/runtime/engine/config.mjs +494 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/engine/runtime-root.mjs +5 -0
- package/runtime/engine/yaml.mjs +3 -3
- package/runtime/knowledge/hfs/canon-pins.yaml +163 -32
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +915 -123
- 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 +133 -27
- package/runtime/knowledge/sonar-gate.yaml +53 -19
- package/runtime/modules/kernel/failure-codes.yaml +580 -50
- package/runtime/modules/models/registry.yaml +591 -0
- package/runtime/modules/models/runtimes.yaml +701 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
- package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
- package/runtime/scripts/api/fs/lib.mjs +14 -0
- package/runtime/scripts/api/fs/safe-remove.mjs +104 -0
- 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 +17 -27
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/sops/decrypt.mjs +10 -6
- package/runtime/scripts/api/sops/lib.mjs +35 -0
- package/runtime/scripts/api/sops/seal.mjs +18 -0
- package/runtime/scripts/{lib/hfs-allows.mjs → hfs/allows.mjs} +3 -3
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/{checks → hfs}/architecture/backend.mjs +17 -24
- package/runtime/scripts/{checks → hfs}/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/{checks → hfs}/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/{checks → hfs}/architecture/clones.mjs +4 -4
- package/runtime/scripts/{checks → hfs}/architecture/config.mjs +11 -8
- package/runtime/scripts/{checks → hfs}/architecture/connection-map.mjs +21 -9
- 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/{checks → hfs}/architecture/contract-fixture-guard.mjs +5 -11
- package/runtime/scripts/{checks → hfs}/architecture/contracts.mjs +33 -162
- package/runtime/scripts/{checks → hfs}/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/{checks → hfs}/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/{checks → hfs}/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/{checks → hfs}/architecture/fe-slot-allows.mjs +4 -4
- package/runtime/scripts/{checks → hfs}/architecture/feature-shape.mjs +6 -3
- package/runtime/scripts/{checks → hfs}/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/{checks → hfs}/architecture/frontend.mjs +17 -43
- package/runtime/scripts/{checks → hfs}/architecture/hfs-graph.mjs +1 -1
- package/runtime/scripts/{checks → hfs}/architecture/hfs.mjs +27 -42
- package/runtime/scripts/{checks → hfs}/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/{checks → hfs}/architecture/index.mjs +17 -4
- package/runtime/scripts/{checks → hfs}/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/{checks → hfs}/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data-contract.mjs +96 -0
- package/runtime/scripts/{checks → hfs}/architecture/next-data.mjs +40 -168
- package/runtime/scripts/{checks → hfs}/architecture/owners.mjs +35 -15
- package/runtime/scripts/{checks → hfs}/architecture/register-once.mjs +2 -1
- package/runtime/scripts/{checks → hfs}/architecture/registration.mjs +23 -81
- package/runtime/scripts/{checks → hfs}/architecture/required-files.mjs +21 -13
- package/runtime/scripts/{checks → 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/{checks → hfs}/architecture/source-names.mjs +27 -147
- package/runtime/scripts/{checks → 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/{checks → hfs}/architecture/surface.mjs +10 -4
- package/runtime/scripts/{checks → hfs}/architecture/test-world-files.mjs +11 -21
- package/runtime/scripts/{checks → hfs}/architecture/tiers.mjs +47 -5
- package/runtime/scripts/{checks → hfs}/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/{checks → hfs}/architecture/typescript.mjs +13 -27
- package/runtime/scripts/{checks → hfs}/architecture.mjs +5 -5
- package/runtime/scripts/{lib/hfs-check.mjs → hfs/check.mjs} +95 -84
- 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 +247 -0
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/{lib/hfs-path-findings.mjs → hfs/path-findings.mjs} +7 -7
- package/runtime/scripts/{lib → 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/{lib/hfs-rules → hfs/rules}/contract.mjs +11 -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/{lib/hfs-rules → 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 +66 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/fe-no-tests.mjs +6 -5
- package/runtime/scripts/{lib/hfs-rules/frontend.mjs → hfs/rules/frontend-tree.mjs} +9 -9
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/peer-integrations.mjs +3 -3
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/pipeline.mjs +11 -21
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/proof-commands.mjs +2 -2
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/read.mjs +1 -1
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/secrets.mjs +4 -4
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/spec-placement.mjs +1 -1
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/stacks.mjs +5 -5
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/{lib/hfs-rules → hfs/rules}/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +175 -0
- package/runtime/scripts/{lib/hfs-slots.mjs → hfs/slots.mjs} +114 -150
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/{lib/hfs-tree.mjs → hfs/tree.mjs} +6 -5
- package/runtime/scripts/{checks → hfs}/typescript-programs.mjs +0 -5
- package/runtime/scripts/{lib/hfs-view.mjs → hfs/view.mjs} +6 -5
- package/runtime/scripts/lib/dockerfile.mjs +124 -0
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +103 -0
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +429 -0
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/is-main.mjs +13 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +26 -7
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +59 -2
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/regex.mjs +4 -0
- package/runtime/scripts/lib/same-text.mjs +5 -0
- package/runtime/scripts/lib/secret-patterns.mjs +4 -3
- package/runtime/scripts/lib/sleep-sync.mjs +2 -1
- package/runtime/scripts/lib/sops-envelope.mjs +58 -0
- package/runtime/scripts/lib/stack-declaration.mjs +2 -2
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/{checks/common.mjs → lib/walk.mjs} +5 -15
- 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 +359 -70
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +33 -27
- 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 +17 -17
- package/sync/index.mjs +171 -71
- 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 +2 -5
- 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/origin.guard.ts +2 -2
- 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 +3 -3
- 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/landing/next.config.ts +21 -0
- 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/apps/__app__ → skeleton-lite/apps/web}/src/modules/i18n/request.ts +4 -2
- 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/engine/admission.mjs +0 -284
- package/runtime/engine/digest.mjs +0 -10
- package/runtime/engine/ledger-db.mjs +0 -1245
- package/runtime/engine/machine-db.mjs +0 -1565
- package/runtime/engine/migrations/machine/0001-init.sql +0 -887
- package/runtime/engine/migrations/machine/0002-worktrees-no-workflow-kind.sql +0 -13
- package/runtime/engine/migrations/machine/0003-worktrees-workflow-orca.sql +0 -21
- package/runtime/engine/migrations/runtime/0001-init.sql +0 -1072
- package/runtime/engine/migrations/runtime/0003-usage-unavailable.sql +0 -13
- package/runtime/engine/migrations/runtime/0004-attempt-why.sql +0 -43
- package/runtime/engine/migrations/runtime/0005-ended-workflow-views.sql +0 -93
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/artifact-hold.mjs +0 -89
- package/runtime/scripts/lib/artifact-store.mjs +0 -103
- package/runtime/scripts/lib/redact.mjs +0 -148
- package/runtime/scripts/lib/safe-remove.mjs +0 -275
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/next.config.ts +0 -11
- 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 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -17
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -26
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -25
- 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 -27
- 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 -32
- 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/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/runtime/scripts/{checks → hfs}/architecture/config-unread.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/constructor-deps.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/cross-app-duplicate.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/default-deny.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/error-codes.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/error-masked.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/hooks-are-hooks.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/injection-token-exported.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/package-shape.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/reachability.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/route-files-thin.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/sql-tokens.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/symbols.mjs +0 -0
- /package/runtime/scripts/{checks → hfs}/architecture/unit-spec-providers.mjs +0 -0
- /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,62 @@
|
|
|
1
|
+
/** A job a worker claimed: the token it must pass to every write. */
|
|
2
|
+
export interface ClaimedJob {
|
|
3
|
+
/** The id of the job row. */
|
|
4
|
+
readonly jobId: string
|
|
5
|
+
/** The kind of the job: the queue its processor consumes. */
|
|
6
|
+
readonly kind: string
|
|
7
|
+
/** The fencing token of this claim; the claim bumped it, so an older worker holds a smaller one. */
|
|
8
|
+
readonly fencingToken: number
|
|
9
|
+
/** The last step a worker recorded, or null before the first. */
|
|
10
|
+
readonly currentStep: string | null
|
|
11
|
+
/** The payload the producer wrote. */
|
|
12
|
+
readonly payload: object
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** What a worker asks for when it claims a delivery. */
|
|
16
|
+
export interface ClaimParams {
|
|
17
|
+
/** The kind of the job. */
|
|
18
|
+
readonly kind: string
|
|
19
|
+
/** The key of the delivery: the BullMQ job id. A second claim of the same key bumps the token of the same row. */
|
|
20
|
+
readonly jobKey: string
|
|
21
|
+
/** The payload to store on the first claim. */
|
|
22
|
+
readonly payload: object
|
|
23
|
+
/** The name of the claiming worker. */
|
|
24
|
+
readonly workerId: string
|
|
25
|
+
/** How long the claim lasts. */
|
|
26
|
+
readonly leaseMs: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The target of one guarded write: the job and the token the writer holds. Required in the type, never optional. */
|
|
30
|
+
export interface GuardedWrite {
|
|
31
|
+
/** The id of the job row. */
|
|
32
|
+
readonly jobId: string
|
|
33
|
+
/** The token the writer received from its claim. */
|
|
34
|
+
readonly expectedFencingToken: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A guarded write that records a step. */
|
|
38
|
+
export interface AdvanceWrite extends GuardedWrite {
|
|
39
|
+
/** The step that finished. */
|
|
40
|
+
readonly step: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A guarded write that fails the job. */
|
|
44
|
+
export interface FailWrite extends GuardedWrite {
|
|
45
|
+
/** Why the job failed. */
|
|
46
|
+
readonly reason: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The idempotency key of an external effect: only `JobClaims.runKey` makes one. */
|
|
50
|
+
export type RunKey = string & {
|
|
51
|
+
/** The brand that keeps a plain string from passing as a run key; it never exists at run time. */
|
|
52
|
+
readonly __runKey: unique symbol
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The shape of a run key: `<jobId>:<step>:<fencingToken>`. */
|
|
56
|
+
const RUN_KEY = /^[^:]+:[^:]+:\d+$/u
|
|
57
|
+
|
|
58
|
+
/** True when the text has the shape of a run key; the one place a string becomes a `RunKey`. */
|
|
59
|
+
export const isRunKey = (text: string): text is RunKey => RUN_KEY.test(text)
|
|
60
|
+
|
|
61
|
+
/** What a claim answers: the claimed job, or null when the job is done or another worker holds a live claim. */
|
|
62
|
+
export type ClaimResult = ClaimedJob | null
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EntityManager } from "typeorm"
|
|
2
|
+
import { injector } from "@modules/platform/composition"
|
|
3
|
+
import type { TypedParameterDecorator } from "@modules/platform/composition"
|
|
4
|
+
import type { JobsOptions } from "./jobs.options"
|
|
5
|
+
import type { JobClaims, JobProcessorRegistry } from "./jobs.port"
|
|
6
|
+
|
|
7
|
+
/** Token of the options of the jobs, exported so a spec can provide it. */
|
|
8
|
+
export const JOBS_OPTIONS: unique symbol = Symbol("platform.jobs.options")
|
|
9
|
+
|
|
10
|
+
/** Token of the JobClaims port. */
|
|
11
|
+
export const JOB_CLAIMS: unique symbol = Symbol("platform.jobs.claims")
|
|
12
|
+
|
|
13
|
+
/** Token of the registry a job module registers its processor with. */
|
|
14
|
+
export const JOB_PROCESSOR_REGISTRY: unique symbol = Symbol("platform.jobs.processor-registry")
|
|
15
|
+
|
|
16
|
+
/** Token of the entity managers of the connections that hold the job table, in the order the options name them. */
|
|
17
|
+
export const JOBS_MANAGERS: unique symbol = Symbol("platform.jobs.managers")
|
|
18
|
+
|
|
19
|
+
/** Injects the options of the jobs. Parameter type: JobsOptions. */
|
|
20
|
+
export const InjectJobsOptions = (): TypedParameterDecorator<JobsOptions> => injector<JobsOptions>(JOBS_OPTIONS)
|
|
21
|
+
|
|
22
|
+
/** Injects the JobClaims port. Parameter type: JobClaims. */
|
|
23
|
+
export const InjectJobClaims = (): TypedParameterDecorator<JobClaims> => injector<JobClaims>(JOB_CLAIMS)
|
|
24
|
+
|
|
25
|
+
/** Injects the processor registry. Parameter type: JobProcessorRegistry. */
|
|
26
|
+
export const InjectJobProcessorRegistry = (): TypedParameterDecorator<JobProcessorRegistry> =>
|
|
27
|
+
injector<JobProcessorRegistry>(JOB_PROCESSOR_REGISTRY)
|
|
28
|
+
|
|
29
|
+
/** Injects the entity managers of the connections that hold the job table. Parameter type: ReadonlyArray<EntityManager>. */
|
|
30
|
+
export const InjectJobsManagers = (): TypedParameterDecorator<ReadonlyArray<EntityManager>> =>
|
|
31
|
+
injector<ReadonlyArray<EntityManager>>(JOBS_MANAGERS)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DomainError } from "@modules/platform/errors"
|
|
2
|
+
import type { ErrorKind } from "@modules/platform/errors"
|
|
3
|
+
|
|
4
|
+
/** Codes of the jobs capability. */
|
|
5
|
+
export enum JobsErrorCode {
|
|
6
|
+
/** A write carried a fencing token that is no longer the job's: a newer worker owns it. */
|
|
7
|
+
FencedOut = "JOBS_FENCED_OUT",
|
|
8
|
+
/** A run key could not be made: the job id or the step name carries a separator. */
|
|
9
|
+
RunKeyInvalid = "JOBS_RUN_KEY_INVALID",
|
|
10
|
+
/** The jobs capability was registered with no connection to hold the job table. */
|
|
11
|
+
ConnectionMissing = "JOBS_CONNECTION_MISSING",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** How each jobs code travels. */
|
|
15
|
+
export const JOBS_ERROR_KINDS: Record<JobsErrorCode, ErrorKind> = {
|
|
16
|
+
[JobsErrorCode.FencedOut]: "conflict",
|
|
17
|
+
[JobsErrorCode.RunKeyInvalid]: "internal",
|
|
18
|
+
[JobsErrorCode.ConnectionMissing]: "internal",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The one error class of the jobs capability. */
|
|
22
|
+
export class JobsError extends DomainError<JobsErrorCode> {}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { JOBS_ERROR_KINDS } from "./errors/jobs.error"
|
|
2
|
+
export { parseJobsConfig } from "./jobs.config"
|
|
3
|
+
export type { JobsConfig } from "./jobs.config"
|
|
4
|
+
export { isRunKey } from "./jobs.contracts"
|
|
5
|
+
export type { ClaimedJob, RunKey } from "./jobs.contracts"
|
|
6
|
+
export { InjectJobClaims, InjectJobProcessorRegistry } from "./jobs.decorators"
|
|
7
|
+
export { JobsModule } from "./jobs.module"
|
|
8
|
+
export type { JobClaims, JobProcessorRegistry, JobStep } from "./jobs.port"
|
|
9
|
+
export { FencedProcessor } from "./fenced.processor"
|
|
10
|
+
export { JOBS_MESSAGES } from "./messages/jobs.messages"
|
|
11
|
+
export { jobsEntities, jobsMigrations } from "./persistence/connection"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Log events of the jobs. */
|
|
2
|
+
export enum JobsLogEvent {
|
|
3
|
+
/** A write of a stale worker was refused: a newer worker owns the job and this one stopped with no further effect. */
|
|
4
|
+
FencedOut = "jobs.fenced-out",
|
|
5
|
+
/** A delivery failed and the job was marked failed for BullMQ to retry. */
|
|
6
|
+
DeliveryFailed = "jobs.delivery.failed",
|
|
7
|
+
/** A failed delivery could not even be recorded as failed; the lease will expire and the job will be claimed again. */
|
|
8
|
+
FailureNotRecorded = "jobs.delivery.failure-not-recorded",
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MessageBundle } from "@modules/platform/i18n"
|
|
2
|
+
|
|
3
|
+
/** Display text of the jobs codes, Vietnamese and English. */
|
|
4
|
+
export const JOBS_MESSAGES: MessageBundle = {
|
|
5
|
+
vi: {
|
|
6
|
+
"errors.JOBS_FENCED_OUT": "Tác vụ {{jobId}} đã được worker khác tiếp nhận; worker này dừng lại.",
|
|
7
|
+
"errors.JOBS_RUN_KEY_INVALID": "Không tạo được khóa chạy cho tác vụ {{jobId}} ở bước {{step}}.",
|
|
8
|
+
"errors.JOBS_CONNECTION_MISSING": "Năng lực tác vụ chưa được khai báo kết nối cơ sở dữ liệu.",
|
|
9
|
+
},
|
|
10
|
+
en: {
|
|
11
|
+
"errors.JOBS_FENCED_OUT": "Job {{jobId}} is owned by a newer worker; this worker stops.",
|
|
12
|
+
"errors.JOBS_RUN_KEY_INVALID": "No run key can be made for job {{jobId}} at step {{step}}.",
|
|
13
|
+
"errors.JOBS_CONNECTION_MISSING": "The jobs capability was registered with no database connection.",
|
|
14
|
+
},
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
2
|
+
import { JOBS_OPTIONS } from "./jobs.decorators"
|
|
3
|
+
import type { JobsOptions } from "./jobs.options"
|
|
4
|
+
|
|
5
|
+
/** The configurable-module base of the jobs; `isGlobal` is decided by the app root. */
|
|
6
|
+
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<JobsOptions>({
|
|
7
|
+
optionsInjectionToken: JOBS_OPTIONS,
|
|
8
|
+
})
|
|
9
|
+
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
10
|
+
.build()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common"
|
|
2
|
+
import type { DynamicModule } from "@nestjs/common"
|
|
3
|
+
import type { EntityManager } from "typeorm"
|
|
4
|
+
import { JOB_CLAIMS, JOB_PROCESSOR_REGISTRY, JOBS_MANAGERS } from "./jobs.decorators"
|
|
5
|
+
import { ConfigurableModuleClass, OPTIONS_TYPE } from "./jobs.module-definition"
|
|
6
|
+
import { JobClaimService } from "./job-claim.service"
|
|
7
|
+
import { JobRunnerService } from "./job-runner.service"
|
|
8
|
+
|
|
9
|
+
@Module({})
|
|
10
|
+
/** The fenced jobs of a service: the claim service that owns the job row and the runner that subscribes processors to their queues. Needs the queue capability registered in the same app. */
|
|
11
|
+
export class JobsModule extends ConfigurableModuleClass {
|
|
12
|
+
/** Registers the capability once per app that runs jobs. */
|
|
13
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
|
14
|
+
const base = super.register(options)
|
|
15
|
+
return {
|
|
16
|
+
...base,
|
|
17
|
+
providers: [
|
|
18
|
+
...(base.providers ?? []),
|
|
19
|
+
{
|
|
20
|
+
provide: JOBS_MANAGERS,
|
|
21
|
+
useFactory: (manager: EntityManager) => [manager],
|
|
22
|
+
inject: [options.connection],
|
|
23
|
+
},
|
|
24
|
+
JobClaimService,
|
|
25
|
+
JobRunnerService,
|
|
26
|
+
{ provide: JOB_CLAIMS, useExisting: JobClaimService },
|
|
27
|
+
{ provide: JOB_PROCESSOR_REGISTRY, useExisting: JobRunnerService },
|
|
28
|
+
],
|
|
29
|
+
exports: [JOB_CLAIMS, JOB_PROCESSOR_REGISTRY],
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InjectionToken } from "@nestjs/common"
|
|
2
|
+
|
|
3
|
+
/** What the jobs of one app need: the connection that holds the job table, who this worker is, and how long a claim lasts. */
|
|
4
|
+
export interface JobsOptions {
|
|
5
|
+
/** The token of the shared entity manager of the connection that holds the job table of the app. */
|
|
6
|
+
readonly connection: InjectionToken
|
|
7
|
+
/** The name this worker writes into `claimed_by`; it tells operators who held a job, the fence never reads it. */
|
|
8
|
+
readonly workerId: string
|
|
9
|
+
/** How long a claim lasts: after it a stalled job may be claimed again, and the old worker is fenced out. */
|
|
10
|
+
readonly leaseMs: number
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AdvanceWrite,
|
|
3
|
+
ClaimedJob,
|
|
4
|
+
ClaimParams,
|
|
5
|
+
ClaimResult,
|
|
6
|
+
FailWrite,
|
|
7
|
+
GuardedWrite,
|
|
8
|
+
RunKey,
|
|
9
|
+
} from "./jobs.contracts"
|
|
10
|
+
import type { FencedProcessor } from "./fenced.processor"
|
|
11
|
+
|
|
12
|
+
/** The one door to the job row; `platform/jobs` implements it and nothing else writes the table. */
|
|
13
|
+
export interface JobClaims {
|
|
14
|
+
/** Claims the delivery in one atomic statement and bumps the fencing token; null when the job is done or another worker holds a live claim. */
|
|
15
|
+
claim(params: ClaimParams): Promise<ClaimResult>
|
|
16
|
+
/** Records a finished step; throws `JobFencedOut` when the token is stale. */
|
|
17
|
+
advance(write: AdvanceWrite): Promise<void>
|
|
18
|
+
/** Completes the job; throws `JobFencedOut` when the token is stale. */
|
|
19
|
+
complete(write: GuardedWrite): Promise<void>
|
|
20
|
+
/** Fails the job so BullMQ can retry it; throws `JobFencedOut` when the token is stale. */
|
|
21
|
+
fail(write: FailWrite): Promise<void>
|
|
22
|
+
/** The idempotency key of one step of this claim: `<jobId>:<step>:<fencingToken>`. */
|
|
23
|
+
runKey(job: ClaimedJob, step: string): RunKey
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** One external effect of a job. */
|
|
27
|
+
export interface JobStep {
|
|
28
|
+
/** Performs the effect of the step for the claimed job. */
|
|
29
|
+
run(job: ClaimedJob): Promise<void>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Where a job module registers its processor. */
|
|
33
|
+
export interface JobProcessorRegistry {
|
|
34
|
+
/** Subscribes the processor to its queue. */
|
|
35
|
+
add(processor: FencedProcessor): void
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JobEntity } from "./entities/job.entity"
|
|
2
|
+
import { CreateJobs@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-jobs"
|
|
3
|
+
|
|
4
|
+
/** The entities of the jobs, for every connection that runs jobs. */
|
|
5
|
+
export const jobsEntities = [JobEntity]
|
|
6
|
+
|
|
7
|
+
/** The migrations of the jobs, in the order they run. */
|
|
8
|
+
export const jobsMigrations = [CreateJobs@@epochMs13@@]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"
|
|
2
|
+
|
|
3
|
+
@Entity("jobs")
|
|
4
|
+
/** One background job: its fencing token is bumped by the claim only, and every other write carries the token it holds. */
|
|
5
|
+
export class JobEntity {
|
|
6
|
+
/** The id of the job. */
|
|
7
|
+
@PrimaryGeneratedColumn("uuid", { name: "id" })
|
|
8
|
+
id!: string
|
|
9
|
+
|
|
10
|
+
/** The kind of the job: the queue its processor consumes. */
|
|
11
|
+
@Column({ name: "kind", type: "varchar", length: 200 })
|
|
12
|
+
kind!: string
|
|
13
|
+
|
|
14
|
+
/** The key of the delivery (the BullMQ job id); unique, so a repeated delivery meets the same row. */
|
|
15
|
+
@Column({ name: "job_key", type: "varchar", length: 200, unique: true })
|
|
16
|
+
jobKey!: string
|
|
17
|
+
|
|
18
|
+
/** The state of the job: running, failed or done. */
|
|
19
|
+
@Column({ name: "status", type: "varchar", length: 16 })
|
|
20
|
+
status!: "running" | "failed" | "done"
|
|
21
|
+
|
|
22
|
+
/** The last step a worker recorded. */
|
|
23
|
+
@Column({ name: "current_step", type: "varchar", length: 200, nullable: true })
|
|
24
|
+
currentStep!: string | null
|
|
25
|
+
|
|
26
|
+
/** The fencing token: it only grows, one per claim. */
|
|
27
|
+
@Column({ name: "fencing_token", type: "bigint" })
|
|
28
|
+
fencingToken!: string
|
|
29
|
+
|
|
30
|
+
/** The worker that holds the claim, for operators. */
|
|
31
|
+
@Column({ name: "claimed_by", type: "varchar", length: 200, nullable: true })
|
|
32
|
+
claimedBy!: string | null
|
|
33
|
+
|
|
34
|
+
/** When the claim expires; after it a stalled job may be claimed again. */
|
|
35
|
+
@Column({ name: "lease_expires_at", type: "timestamptz", nullable: true })
|
|
36
|
+
leaseExpiresAt!: Date | null
|
|
37
|
+
|
|
38
|
+
/** The payload of the job: ids, never an entity. */
|
|
39
|
+
@Column({ name: "payload", type: "jsonb" })
|
|
40
|
+
payload!: object
|
|
41
|
+
|
|
42
|
+
/** Why the last delivery failed. */
|
|
43
|
+
@Column({ name: "error", type: "text", nullable: true })
|
|
44
|
+
error!: string | null
|
|
45
|
+
|
|
46
|
+
/** When the job was first claimed. */
|
|
47
|
+
@Column({ name: "created_at", type: "timestamptz" })
|
|
48
|
+
createdAt!: Date
|
|
49
|
+
|
|
50
|
+
/** When the row last changed. */
|
|
51
|
+
@Column({ name: "updated_at", type: "timestamptz" })
|
|
52
|
+
updatedAt!: Date
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** One row a claim returns. */
|
|
2
|
+
export interface ClaimedRow {
|
|
3
|
+
/** The id of the job row. */
|
|
4
|
+
readonly id: string
|
|
5
|
+
/** The kind of the job. */
|
|
6
|
+
readonly kind: string
|
|
7
|
+
/** The fencing token after the claim (a bigint arrives as text). */
|
|
8
|
+
readonly fencing_token: string
|
|
9
|
+
/** The last recorded step, or null. */
|
|
10
|
+
readonly current_step: string | null
|
|
11
|
+
/** The payload, as the database returns the jsonb value. */
|
|
12
|
+
readonly payload: object
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sql } from "@modules/platform/database"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claims a delivery in one statement ($1 kind, $2 job key, $3 worker, $4 lease end, $5 payload, $6 instant). A new key inserts the row
|
|
5
|
+
* with token 1; an existing key is claimed again only when the job failed or its lease expired, and then the token is bumped by the
|
|
6
|
+
* store. A done job and a live claim match nothing, so no row comes back.
|
|
7
|
+
*/
|
|
8
|
+
export const CLAIM_JOB = sql`INSERT INTO jobs (kind, job_key, status, fencing_token, claimed_by, lease_expires_at, payload, created_at, updated_at)
|
|
9
|
+
VALUES ($1, $2, 'running', 1, $3, $4, $5::jsonb, $6, $6)
|
|
10
|
+
ON CONFLICT (job_key) DO UPDATE
|
|
11
|
+
SET fencing_token = jobs.fencing_token + 1, claimed_by = $3, lease_expires_at = $4, status = 'running', error = NULL, updated_at = $6
|
|
12
|
+
WHERE jobs.status = 'failed' OR (jobs.status = 'running' AND jobs.lease_expires_at < $6)
|
|
13
|
+
RETURNING id, kind, fencing_token, current_step, payload`
|
|
14
|
+
|
|
15
|
+
/** The guarded writes wrap their UPDATE in a CTE: TypeORM returns a bare `UPDATE ... RETURNING` as `[rows, count]`, a SELECT as rows. */
|
|
16
|
+
|
|
17
|
+
/** Records a step ($1 job id, $2 expected token, $3 step, $4 instant); no row comes back when the token is stale. */
|
|
18
|
+
export const ADVANCE_JOB = sql`WITH changed AS (UPDATE jobs SET current_step = $3, updated_at = $4
|
|
19
|
+
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
20
|
+
RETURNING id)
|
|
21
|
+
SELECT id FROM changed`
|
|
22
|
+
|
|
23
|
+
/** Completes the job ($1 job id, $2 expected token, $3 instant). */
|
|
24
|
+
export const COMPLETE_JOB = sql`WITH changed AS (UPDATE jobs SET status = 'done', lease_expires_at = NULL, updated_at = $3
|
|
25
|
+
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
26
|
+
RETURNING id)
|
|
27
|
+
SELECT id FROM changed`
|
|
28
|
+
|
|
29
|
+
/** Fails the job ($1 job id, $2 expected token, $3 reason, $4 instant). */
|
|
30
|
+
export const FAIL_JOB = sql`WITH changed AS (UPDATE jobs SET status = 'failed', error = $3, lease_expires_at = NULL, updated_at = $4
|
|
31
|
+
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
32
|
+
RETURNING id)
|
|
33
|
+
SELECT id FROM changed`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MigrationInterface, QueryRunner } from "typeorm"
|
|
2
|
+
|
|
3
|
+
/** Creates the job table the fenced jobs claim and settle their rows in. */
|
|
4
|
+
export class CreateJobs@@epochMs13@@ implements MigrationInterface {
|
|
5
|
+
name = "CreateJobs@@epochMs13@@"
|
|
6
|
+
|
|
7
|
+
/** Creates the table; the job key is unique and the token starts at 1 with the first claim. */
|
|
8
|
+
async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(`CREATE TABLE jobs (
|
|
10
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
11
|
+
kind varchar(200) NOT NULL,
|
|
12
|
+
job_key varchar(200) NOT NULL UNIQUE,
|
|
13
|
+
status varchar(16) NOT NULL CHECK (status IN ('running', 'failed', 'done')),
|
|
14
|
+
current_step varchar(200),
|
|
15
|
+
fencing_token bigint NOT NULL CHECK (fencing_token >= 1),
|
|
16
|
+
claimed_by varchar(200),
|
|
17
|
+
lease_expires_at timestamptz,
|
|
18
|
+
payload jsonb NOT NULL,
|
|
19
|
+
error text,
|
|
20
|
+
created_at timestamptz NOT NULL,
|
|
21
|
+
updated_at timestamptz NOT NULL
|
|
22
|
+
)`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Drops the table. */
|
|
26
|
+
async down(queryRunner: QueryRunner): Promise<void> {
|
|
27
|
+
await queryRunner.query(`DROP TABLE jobs`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { fakeTransaction, mock, mockEntityManager } from "@starci/jest-preset"
|
|
2
|
+
import type { MockEntityManager } from "@starci/jest-preset"
|
|
3
|
+
import type { EntityManager } from "typeorm"
|
|
4
|
+
import type { Logger } from "@modules/platform/logging"
|
|
5
|
+
import { OutboxLogEvent } from "./outbox.log-events"
|
|
6
|
+
import { OutboxRelayPolicy } from "./outbox-relay.policy"
|
|
7
|
+
|
|
8
|
+
interface Row {
|
|
9
|
+
readonly id: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class ProbeRelay extends OutboxRelayPolicy<Row> {
|
|
13
|
+
readonly select = jest.fn<Promise<Array<Row>>, [EntityManager]>()
|
|
14
|
+
readonly deliver = jest.fn<Promise<void>, [ReadonlyArray<Row>]>()
|
|
15
|
+
readonly mark = jest.fn<Promise<void>, [EntityManager, ReadonlyArray<Row>]>()
|
|
16
|
+
readonly idle = jest.fn<Promise<void>, []>()
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
protected readonly managers: ReadonlyArray<EntityManager>,
|
|
20
|
+
protected readonly logger: Logger,
|
|
21
|
+
protected readonly outbox = "probe",
|
|
22
|
+
) {
|
|
23
|
+
super()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const build = (...managers: Array<MockEntityManager>) => {
|
|
28
|
+
const logger = mock<Logger>()
|
|
29
|
+
const relay = new ProbeRelay(managers, logger)
|
|
30
|
+
return { relay, logger }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("OutboxRelayPolicy", () => {
|
|
34
|
+
describe("relay", () => {
|
|
35
|
+
it("delivers and marks every connection's waiting rows in its own transaction", async () => {
|
|
36
|
+
const first = fakeTransaction(mockEntityManager())
|
|
37
|
+
const second = fakeTransaction(mockEntityManager())
|
|
38
|
+
const { relay } = build(first.em, second.em)
|
|
39
|
+
const rows = [{ id: "row-1" }, { id: "row-2" }]
|
|
40
|
+
relay.select.mockResolvedValueOnce(rows).mockResolvedValueOnce([])
|
|
41
|
+
relay.deliver.mockResolvedValue(undefined)
|
|
42
|
+
relay.mark.mockResolvedValue(undefined)
|
|
43
|
+
|
|
44
|
+
await expect(relay.relay()).resolves.toBe(2)
|
|
45
|
+
|
|
46
|
+
expect(relay.deliver).toHaveBeenCalledWith(rows)
|
|
47
|
+
expect(relay.mark).toHaveBeenCalledWith(expect.any(Object), rows)
|
|
48
|
+
expect(relay.mark).toHaveBeenCalledTimes(1)
|
|
49
|
+
expect(first.commits).toBe(1)
|
|
50
|
+
expect(second.commits).toBe(1)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("leaves the rows unmarked when delivery fails", async () => {
|
|
54
|
+
const transaction = fakeTransaction(mockEntityManager())
|
|
55
|
+
const { relay } = build(transaction.em)
|
|
56
|
+
const failure = new Error("transport down")
|
|
57
|
+
relay.select.mockResolvedValue([{ id: "row-1" }])
|
|
58
|
+
relay.deliver.mockRejectedValue(failure)
|
|
59
|
+
|
|
60
|
+
await expect(relay.relay()).rejects.toBe(failure)
|
|
61
|
+
|
|
62
|
+
expect(relay.mark).not.toHaveBeenCalled()
|
|
63
|
+
expect(transaction.rollbacks).toBe(1)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe("the loop", () => {
|
|
68
|
+
it("repeats immediately after work, idles after an empty or failed pass, logs the failure and stops on shutdown", async () => {
|
|
69
|
+
const { relay, logger } = build()
|
|
70
|
+
const failure = new Error("outbox unreadable")
|
|
71
|
+
jest.spyOn(relay, "relay").mockResolvedValueOnce(1).mockResolvedValueOnce(0).mockRejectedValueOnce(failure)
|
|
72
|
+
let waits = 0
|
|
73
|
+
let shutdown: Promise<void> = Promise.resolve()
|
|
74
|
+
let reachedShutdown: () => void = () => undefined
|
|
75
|
+
const stopped = new Promise<void>((resolve) => {
|
|
76
|
+
reachedShutdown = resolve
|
|
77
|
+
})
|
|
78
|
+
relay.idle.mockImplementation(() => {
|
|
79
|
+
waits += 1
|
|
80
|
+
if (waits === 2) {
|
|
81
|
+
shutdown = relay.onApplicationShutdown()
|
|
82
|
+
reachedShutdown()
|
|
83
|
+
}
|
|
84
|
+
return Promise.resolve()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
relay.onApplicationBootstrap()
|
|
88
|
+
await stopped
|
|
89
|
+
await shutdown
|
|
90
|
+
|
|
91
|
+
expect(relay.relay).toHaveBeenCalledTimes(3)
|
|
92
|
+
expect(relay.idle).toHaveBeenCalledTimes(2)
|
|
93
|
+
expect(logger.error).toHaveBeenCalledWith(OutboxLogEvent.RelayFailed, failure, { outbox: "probe" })
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { OnApplicationBootstrap, OnApplicationShutdown } from "@nestjs/common"
|
|
2
|
+
import type { EntityManager } from "typeorm"
|
|
3
|
+
import type { Logger } from "@modules/platform/logging"
|
|
4
|
+
import { OutboxLogEvent } from "./outbox.log-events"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The relay of an outbox: the one loop the event bus and the queues share. It reads the rows that wait on each connection of the app,
|
|
8
|
+
* in the order they were written, hands them over and marks them sent in the same transaction. A row that is not committed is not
|
|
9
|
+
* visible to the relay, so nothing leaves for a rolled-back change; a crash between the delivery and the mark delivers the row again,
|
|
10
|
+
* which the receiver absorbs. The loop runs from the start of the app to its shutdown and pauses only when the outbox is empty.
|
|
11
|
+
* A concrete relay says how a batch is read, delivered and marked, which outbox it is and how it waits.
|
|
12
|
+
*/
|
|
13
|
+
export abstract class OutboxRelayPolicy<Row extends { readonly id: string }>
|
|
14
|
+
implements OnApplicationBootstrap, OnApplicationShutdown
|
|
15
|
+
{
|
|
16
|
+
private running = false
|
|
17
|
+
private loop: Promise<void> = Promise.resolve()
|
|
18
|
+
|
|
19
|
+
/** The entity managers of the connections that hold an outbox, read in this order. */
|
|
20
|
+
protected abstract readonly managers: ReadonlyArray<EntityManager>
|
|
21
|
+
|
|
22
|
+
/** Where a failed pass is logged. */
|
|
23
|
+
protected abstract readonly logger: Logger
|
|
24
|
+
|
|
25
|
+
/** The name of the outbox this relay serves, written into the log line of a failed pass. */
|
|
26
|
+
protected abstract readonly outbox: string
|
|
27
|
+
|
|
28
|
+
/** One pass over every connection: relays the oldest waiting rows of each and answers how many it relayed. */
|
|
29
|
+
async relay(): Promise<number> {
|
|
30
|
+
let relayed = 0
|
|
31
|
+
for (const manager of this.managers) relayed += await this.relayOf(manager)
|
|
32
|
+
return relayed
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Starts the relay loop over the outbox of every connection. */
|
|
36
|
+
onApplicationBootstrap(): void {
|
|
37
|
+
this.running = true
|
|
38
|
+
this.loop = this.run()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Stops the loop and waits for its last pass. */
|
|
42
|
+
async onApplicationShutdown(): Promise<void> {
|
|
43
|
+
this.running = false
|
|
44
|
+
await this.loop
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Reads and locks the oldest rows that wait, at most one batch; a second relay skips the rows this one holds. */
|
|
48
|
+
protected abstract select(tx: EntityManager): Promise<Array<Row>>
|
|
49
|
+
|
|
50
|
+
/** Hands the rows over; a throw rolls the transaction back, so the rows stay waiting. */
|
|
51
|
+
protected abstract deliver(rows: ReadonlyArray<Row>): Promise<void>
|
|
52
|
+
|
|
53
|
+
/** Marks the rows as handed over, in the transaction that read them. */
|
|
54
|
+
protected abstract mark(tx: EntityManager, rows: ReadonlyArray<Row>): Promise<void>
|
|
55
|
+
|
|
56
|
+
/** Waits before the next pass over an empty outbox. */
|
|
57
|
+
protected abstract idle(): Promise<void>
|
|
58
|
+
|
|
59
|
+
private relayOf(manager: EntityManager): Promise<number> {
|
|
60
|
+
return manager.transaction(async (tx) => {
|
|
61
|
+
const rows = await this.select(tx)
|
|
62
|
+
if (rows.length === 0) return 0
|
|
63
|
+
await this.deliver(rows)
|
|
64
|
+
await this.mark(tx, rows)
|
|
65
|
+
return rows.length
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async run(): Promise<void> {
|
|
70
|
+
while (this.running) {
|
|
71
|
+
const relayed = await this.relay().catch((cause: unknown) => {
|
|
72
|
+
this.logger.error(OutboxLogEvent.RelayFailed, cause, { outbox: this.outbox })
|
|
73
|
+
return 0
|
|
74
|
+
})
|
|
75
|
+
if (relayed === 0 && this.running) await this.idle()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MigrationInterface, QueryRunner } from "typeorm"
|
|
2
|
+
|
|
3
|
+
/** Creates the @@name@@ read-model table. */
|
|
4
|
+
export class Create@@Name@@Projection@@epochMs13@@ implements MigrationInterface {
|
|
5
|
+
name = "Create@@Name@@Projection@@epochMs13@@"
|
|
6
|
+
|
|
7
|
+
/** Creates the table. */
|
|
8
|
+
async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(`CREATE TABLE @@nameSnake@@ (
|
|
10
|
+
id uuid PRIMARY KEY
|
|
11
|
+
)`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Drops the table. */
|
|
15
|
+
async down(queryRunner: QueryRunner): Promise<void> {
|
|
16
|
+
await queryRunner.query(`DROP TABLE @@nameSnake@@`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { @@Name@@ProjectionEntity } from "../@@name@@.projection-entity"
|
|
2
|
+
import { Create@@Name@@Projection@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-@@name@@"
|
|
3
|
+
|
|
4
|
+
/** The entities of the @@name@@ read model, for the connection that holds them. */
|
|
5
|
+
export const @@nameCamel@@Entities = [@@Name@@ProjectionEntity]
|
|
6
|
+
|
|
7
|
+
/** The migrations of the @@name@@ read model, in the order they run. */
|
|
8
|
+
export const @@nameCamel@@Migrations = [Create@@Name@@Projection@@epochMs13@@]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Entity, PrimaryColumn } from "typeorm"
|
|
2
|
+
|
|
3
|
+
@Entity("@@nameSnake@@")
|
|
4
|
+
/** The read-model row of one @@name@@: written only by its projection. */
|
|
5
|
+
export class @@Name@@ProjectionEntity {
|
|
6
|
+
/** The id of the aggregate the row summarises. */
|
|
7
|
+
@PrimaryColumn({ name: "id", type: "uuid" })
|
|
8
|
+
id!: string
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
2
|
+
import type { @@Name@@Options } from "./@@name@@.options"
|
|
3
|
+
|
|
4
|
+
/** The configurable-module base of the @@name@@ projection; `isGlobal` is decided by the app root. */
|
|
5
|
+
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<@@Name@@Options>()
|
|
6
|
+
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
7
|
+
.build()
|