@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,50 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common"
|
|
2
|
+
import type { CanActivate, ExecutionContext } from "@nestjs/common"
|
|
3
|
+
import type { Reflector } from "@nestjs/core"
|
|
4
|
+
import { InjectSupabaseAccessTokenVerifier } from "@modules/integrations/supabase"
|
|
5
|
+
import type { VerifySupabaseAccessToken } from "@modules/integrations/supabase"
|
|
6
|
+
import { InjectReflector } from "@modules/platform/composition"
|
|
7
|
+
import type { Principal } from "@modules/platform/cqrs"
|
|
8
|
+
import { requestOf } from "@modules/platform/http-security"
|
|
9
|
+
import { unwrapOutcome } from "@modules/platform/primitives"
|
|
10
|
+
import { admit } from "./admission.policy"
|
|
11
|
+
import { IdentityError } from "./errors/identity.error"
|
|
12
|
+
import type { PublicMetadata } from "./identity.contracts"
|
|
13
|
+
import { PUBLIC_KEY } from "./identity.decorators"
|
|
14
|
+
|
|
15
|
+
const bearerToken = (authorization: string | undefined): string | undefined => {
|
|
16
|
+
const [scheme, token, extra] = authorization?.split(" ") ?? []
|
|
17
|
+
return scheme?.toLowerCase() === "bearer" && token && extra === undefined ? token : undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Injectable()
|
|
21
|
+
/** The third app guard and default-deny gate: non-public doors require a verified Supabase principal. */
|
|
22
|
+
export class AuthGuard implements CanActivate {
|
|
23
|
+
constructor(
|
|
24
|
+
@InjectReflector() private readonly reflector: Reflector,
|
|
25
|
+
@InjectSupabaseAccessTokenVerifier()
|
|
26
|
+
private readonly verifyAccessToken: VerifySupabaseAccessToken,
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
/** Verifies the bearer token when the door is not public, then applies the admission policy. */
|
|
30
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
31
|
+
const metadata = this.reflector.getAllAndOverride<PublicMetadata | undefined>(PUBLIC_KEY, [
|
|
32
|
+
context.getHandler(),
|
|
33
|
+
context.getClass(),
|
|
34
|
+
])
|
|
35
|
+
let principal: Principal | undefined
|
|
36
|
+
if (metadata === undefined) {
|
|
37
|
+
const request = requestOf(context)
|
|
38
|
+
const token = bearerToken(request.headers.authorization)
|
|
39
|
+
if (token !== undefined) {
|
|
40
|
+
const verified = await this.verifyAccessToken(token)
|
|
41
|
+
if (verified.kind === "ok") {
|
|
42
|
+
principal = verified.value
|
|
43
|
+
request.principal = verified.value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
unwrapOutcome(admit(metadata, principal), IdentityError)
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Principal } from "@modules/platform/cqrs"
|
|
2
|
+
|
|
3
|
+
declare module "express-serve-static-core" {
|
|
4
|
+
/** The request carries the principal established by the default-deny auth guard. */
|
|
5
|
+
interface Request {
|
|
6
|
+
/** The authenticated Supabase caller, when the door is not public. */
|
|
7
|
+
principal?: Principal
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Why a door is open to anonymous callers; the vocabulary is closed. */
|
|
12
|
+
export enum PublicReason {
|
|
13
|
+
/** Liveness and readiness probes. */
|
|
14
|
+
Health = "health",
|
|
15
|
+
/** A webhook whose signature is verified before its domain intake runs. */
|
|
16
|
+
SignedWebhook = "signed-webhook",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The metadata `@Public` attaches to a door. */
|
|
20
|
+
export interface PublicMetadata {
|
|
21
|
+
/** Why the door is anonymous. */
|
|
22
|
+
readonly reason: PublicReason
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What identity admission returns to the guard. */
|
|
26
|
+
export type IdentityAdmission = Principal | PublicReason
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SetMetadata, createParamDecorator } from "@nestjs/common"
|
|
2
|
+
import type { ExecutionContext } from "@nestjs/common"
|
|
3
|
+
import { requestOf } from "@modules/platform/http-security"
|
|
4
|
+
import type { Principal } from "@modules/platform/cqrs"
|
|
5
|
+
import { IdentityError, IdentityErrorCode } from "./errors/identity.error"
|
|
6
|
+
import type { PublicMetadata } from "./identity.contracts"
|
|
7
|
+
|
|
8
|
+
/** Metadata key of `@Public`. */
|
|
9
|
+
export const PUBLIC_KEY = "domain.identity.public"
|
|
10
|
+
|
|
11
|
+
/** Opens a door to anonymous callers, stating why; every other door needs a verified Supabase session. */
|
|
12
|
+
export const Public = (metadata: PublicMetadata): ReturnType<typeof SetMetadata> => SetMetadata(PUBLIC_KEY, metadata)
|
|
13
|
+
|
|
14
|
+
/** The authenticated Supabase caller established by the default-deny guard. */
|
|
15
|
+
export const CurrentPrincipal = createParamDecorator((_data: unknown, context: ExecutionContext): Principal => {
|
|
16
|
+
const principal = requestOf(context).principal
|
|
17
|
+
if (!principal) throw new IdentityError({ code: IdentityErrorCode.Unauthenticated })
|
|
18
|
+
return principal
|
|
19
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common"
|
|
2
|
+
import type { DynamicModule } from "@nestjs/common"
|
|
3
|
+
import { SUPABASE_ACCESS_TOKEN_VERIFIER } from "@modules/integrations/supabase"
|
|
4
|
+
import { AuthGuard } from "./auth.guard"
|
|
5
|
+
import { ConfigurableModuleClass, OPTIONS_TYPE } from "./identity.module-definition"
|
|
6
|
+
|
|
7
|
+
@Module({})
|
|
8
|
+
/** The identity capability: its default-deny guard admits only public doors or a verified Supabase principal. */
|
|
9
|
+
export class IdentityModule extends ConfigurableModuleClass {
|
|
10
|
+
/** Registers identity once per app with the verifier parsed at the composition root. */
|
|
11
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
|
12
|
+
const base = super.register(options)
|
|
13
|
+
return {
|
|
14
|
+
...base,
|
|
15
|
+
providers: [
|
|
16
|
+
...(base.providers ?? []),
|
|
17
|
+
{
|
|
18
|
+
provide: SUPABASE_ACCESS_TOKEN_VERIFIER,
|
|
19
|
+
useValue: options.verifyAccessToken,
|
|
20
|
+
},
|
|
21
|
+
AuthGuard,
|
|
22
|
+
],
|
|
23
|
+
exports: [AuthGuard],
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VerifySupabaseAccessToken } from "@modules/integrations/supabase"
|
|
2
|
+
|
|
3
|
+
/** The verified identity source used by the default-deny guard. */
|
|
4
|
+
export interface IdentityOptions {
|
|
5
|
+
/** Cryptographically verifies a Supabase access token before it can become a principal. */
|
|
6
|
+
readonly verifyAccessToken: VerifySupabaseAccessToken
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AuthGuard } from "./auth.guard"
|
|
2
|
+
export { IDENTITY_ERROR_KINDS } from "./errors/identity.error"
|
|
3
|
+
export { IDENTITY_MESSAGES } from "./messages/identity.messages"
|
|
4
|
+
export { PublicReason } from "./identity.contracts"
|
|
5
|
+
export { Public } from "./identity.decorators"
|
|
6
|
+
export { IdentityModule } from "./identity.module"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DomainError } from "@modules/platform/errors"
|
|
2
|
+
import type { ErrorKind } from "@modules/platform/errors"
|
|
3
|
+
|
|
4
|
+
/** Codes of the Supabase integration. */
|
|
5
|
+
export enum SupabaseErrorCode {
|
|
6
|
+
/** The access token failed signature or required-claim verification. */
|
|
7
|
+
AccessTokenInvalid = "SUPABASE_ACCESS_TOKEN_INVALID",
|
|
8
|
+
/** A privileged Auth or Storage operation could not reach Supabase. */
|
|
9
|
+
ProviderUnavailable = "SUPABASE_PROVIDER_UNAVAILABLE",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** How each Supabase integration code travels. */
|
|
13
|
+
export const SUPABASE_ERROR_KINDS: Record<SupabaseErrorCode, ErrorKind> = {
|
|
14
|
+
[SupabaseErrorCode.AccessTokenInvalid]: "unauthenticated",
|
|
15
|
+
[SupabaseErrorCode.ProviderUnavailable]: "unavailable",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The one error class of the Supabase integration. */
|
|
19
|
+
export class SupabaseError extends DomainError<SupabaseErrorCode> {}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { createSupabaseAdminClient } from "./supabase.client"
|
|
2
|
+
export { parseSupabaseConfig } from "./supabase.config"
|
|
3
|
+
export {
|
|
4
|
+
InjectSupabaseAccessTokenVerifier,
|
|
5
|
+
SUPABASE_ACCESS_TOKEN_VERIFIER,
|
|
6
|
+
SUPABASE_ADMIN_CLIENT,
|
|
7
|
+
} from "./supabase.decorators"
|
|
8
|
+
export { SUPABASE_ERROR_KINDS } from "./errors/supabase.error"
|
|
9
|
+
export { createSupabaseAccessTokenVerifier } from "./supabase.jwks"
|
|
10
|
+
export type { SupabaseOptions, VerifySupabaseAccessToken } from "./supabase.options"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createClient } from "@supabase/supabase-js"
|
|
2
|
+
import type { SupabaseClient } from "@supabase/supabase-js"
|
|
3
|
+
import type { Database } from "../../../../../supabase/types/database.types"
|
|
4
|
+
import type { SupabaseOptions } from "./supabase.options"
|
|
5
|
+
|
|
6
|
+
/** The privileged surface exposed by this integration: Auth administration and Storage, never database queries. */
|
|
7
|
+
export interface SupabaseAdminClient {
|
|
8
|
+
/** Supabase Auth administration. */
|
|
9
|
+
readonly auth: SupabaseClient<Database>["auth"]["admin"]
|
|
10
|
+
/** Supabase Storage administration. */
|
|
11
|
+
readonly storage: SupabaseClient<Database>["storage"]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Creates the back-end-only client and narrows it to Auth and Storage administration. */
|
|
15
|
+
export const createSupabaseAdminClient = (options: SupabaseOptions): SupabaseAdminClient => {
|
|
16
|
+
const client: SupabaseClient<Database> = createClient<Database>(options.url, options.serviceRoleKey.reveal(), {
|
|
17
|
+
auth: {
|
|
18
|
+
autoRefreshToken: false,
|
|
19
|
+
detectSessionInUrl: false,
|
|
20
|
+
persistSession: false,
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
return { auth: client.auth.admin, storage: client.storage }
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EnvSource } from "@modules/platform/config"
|
|
2
|
+
import type { SupabaseOptions } from "./supabase.options"
|
|
3
|
+
|
|
4
|
+
/** Reads Supabase configuration with no credential or localhost defaults. */
|
|
5
|
+
export const parseSupabaseConfig = (env: EnvSource): SupabaseOptions => ({
|
|
6
|
+
url: env.url("SUPABASE_URL"),
|
|
7
|
+
serviceRoleKey: env.secret("SUPABASE_SERVICE_ROLE_KEY"),
|
|
8
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { injector } from "@modules/platform/composition"
|
|
2
|
+
import type { TypedParameterDecorator } from "@modules/platform/composition"
|
|
3
|
+
import type { SupabaseAdminClient } from "./supabase.client"
|
|
4
|
+
import type { VerifySupabaseAccessToken } from "./supabase.options"
|
|
5
|
+
|
|
6
|
+
/** Token of the integration's Auth and Storage administrator. */
|
|
7
|
+
export const SUPABASE_ADMIN_CLIENT: unique symbol = Symbol("integrations.supabase.admin-client")
|
|
8
|
+
/** Token of the cryptographic Supabase access-token verifier. */
|
|
9
|
+
export const SUPABASE_ACCESS_TOKEN_VERIFIER: unique symbol = Symbol("integrations.supabase.access-token-verifier")
|
|
10
|
+
|
|
11
|
+
/** Injects the Auth and Storage administrator. Parameter type: SupabaseAdminClient. */
|
|
12
|
+
export const InjectSupabaseAdminClient = (): TypedParameterDecorator<SupabaseAdminClient> =>
|
|
13
|
+
injector<SupabaseAdminClient>(SUPABASE_ADMIN_CLIENT)
|
|
14
|
+
|
|
15
|
+
/** Injects the access-token verifier. Parameter type: VerifySupabaseAccessToken. */
|
|
16
|
+
export const InjectSupabaseAccessTokenVerifier = (): TypedParameterDecorator<VerifySupabaseAccessToken> =>
|
|
17
|
+
injector<VerifySupabaseAccessToken>(SUPABASE_ACCESS_TOKEN_VERIFIER)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from "jose"
|
|
2
|
+
import { ok, refused } from "@modules/platform/primitives"
|
|
3
|
+
import { SupabaseErrorCode } from "./errors/supabase.error"
|
|
4
|
+
import type { SupabaseOptions, SupabasePrincipal, VerifySupabaseAccessToken } from "./supabase.options"
|
|
5
|
+
|
|
6
|
+
const AUDIENCE = "authenticated"
|
|
7
|
+
|
|
8
|
+
/** Creates the verifier for this project's remote JWKS, exact issuer, authenticated audience and pinned algorithm. */
|
|
9
|
+
export const createSupabaseAccessTokenVerifier = (options: SupabaseOptions): VerifySupabaseAccessToken => {
|
|
10
|
+
const issuer = `${options.url.replace(/\/$/, "")}/auth/v1`
|
|
11
|
+
const keys = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`))
|
|
12
|
+
return async (token) => {
|
|
13
|
+
try {
|
|
14
|
+
const { payload } = await jwtVerify(token, keys, {
|
|
15
|
+
algorithms: ["ES256"],
|
|
16
|
+
audience: AUDIENCE,
|
|
17
|
+
issuer,
|
|
18
|
+
})
|
|
19
|
+
if (typeof payload.sub !== "string" || typeof payload.exp !== "number" || payload.role !== AUDIENCE) {
|
|
20
|
+
return refused(SupabaseErrorCode.AccessTokenInvalid)
|
|
21
|
+
}
|
|
22
|
+
const principal: SupabasePrincipal = {
|
|
23
|
+
id: payload.sub,
|
|
24
|
+
role: AUDIENCE,
|
|
25
|
+
...(typeof payload.email === "string" ? { email: payload.email } : {}),
|
|
26
|
+
}
|
|
27
|
+
return ok(principal)
|
|
28
|
+
} catch (cause) {
|
|
29
|
+
return refused(SupabaseErrorCode.AccessTokenInvalid, { cause: String(cause) })
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Secret } from "@modules/platform/config"
|
|
2
|
+
import type { Outcome } from "@modules/platform/primitives"
|
|
3
|
+
import type { SupabaseErrorCode } from "./errors/supabase.error"
|
|
4
|
+
|
|
5
|
+
/** The verified identity claims admitted from a Supabase access token. */
|
|
6
|
+
export interface SupabasePrincipal {
|
|
7
|
+
/** The Auth user identifier from the verified `sub` claim. */
|
|
8
|
+
readonly id: string
|
|
9
|
+
/** The verified email claim when the provider issued one. */
|
|
10
|
+
readonly email?: string
|
|
11
|
+
/** Supabase's authenticated role, required by the verifier. */
|
|
12
|
+
readonly role: "authenticated"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Verifies one bearer token and returns only claims that passed every cryptographic and semantic check. */
|
|
16
|
+
export type VerifySupabaseAccessToken = (token: string) => Promise<Outcome<SupabasePrincipal, SupabaseErrorCode>>
|
|
17
|
+
|
|
18
|
+
/** Supabase configuration parsed once at API boot. */
|
|
19
|
+
export interface SupabaseOptions {
|
|
20
|
+
/** The project origin, used for Auth, Storage, issuer and JWKS endpoints. */
|
|
21
|
+
readonly url: string
|
|
22
|
+
/** The service-role credential, held only by the back-end integration. */
|
|
23
|
+
readonly serviceRoleKey: Secret
|
|
24
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common"
|
|
2
|
+
import type { DynamicModule } from "@nestjs/common"
|
|
3
|
+
import { TypeOrmModule, getDataSourceToken } from "@nestjs/typeorm"
|
|
4
|
+
import type { DataSource } from "typeorm"
|
|
5
|
+
import { DATABASE_OPTIONS } from "./database.port"
|
|
6
|
+
import { ConfigurableModuleClass, OPTIONS_TYPE } from "./database.module-definition"
|
|
7
|
+
import { PRIMARY_CONNECTION } from "./primary.connection"
|
|
8
|
+
import { PRIMARY_ENTITY_MANAGER } from "./primary.decorators"
|
|
9
|
+
|
|
10
|
+
/** The token each declared connection provides its shared EntityManager under. */
|
|
11
|
+
type EntityManagerToken = typeof PRIMARY_ENTITY_MANAGER
|
|
12
|
+
|
|
13
|
+
const ENTITY_MANAGER_TOKENS: ReadonlyMap<string, EntityManagerToken> = new Map<string, EntityManagerToken>([
|
|
14
|
+
[PRIMARY_CONNECTION, PRIMARY_ENTITY_MANAGER],
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
@Module({})
|
|
18
|
+
/** Opens each named Supabase PostgreSQL connection and exposes its shared EntityManager without entities or migrations. */
|
|
19
|
+
export class DatabaseModule extends ConfigurableModuleClass {
|
|
20
|
+
/** Registers the capability once per app with the connections it opens. */
|
|
21
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
|
22
|
+
const base = super.register(options)
|
|
23
|
+
const managers = options.connections.flatMap((connection) => {
|
|
24
|
+
const token = ENTITY_MANAGER_TOKENS.get(connection.name)
|
|
25
|
+
return token === undefined
|
|
26
|
+
? []
|
|
27
|
+
: [
|
|
28
|
+
{
|
|
29
|
+
provide: token,
|
|
30
|
+
inject: [getDataSourceToken(connection.name)],
|
|
31
|
+
useFactory: (source: DataSource) => source.manager,
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
})
|
|
35
|
+
return {
|
|
36
|
+
...base,
|
|
37
|
+
imports: [
|
|
38
|
+
...(base.imports ?? []),
|
|
39
|
+
...options.connections.map((connection) =>
|
|
40
|
+
TypeOrmModule.forRoot({
|
|
41
|
+
name: connection.name,
|
|
42
|
+
type: "postgres",
|
|
43
|
+
url: connection.url.reveal(),
|
|
44
|
+
schema: connection.schema,
|
|
45
|
+
synchronize: false,
|
|
46
|
+
retryAttempts: 2,
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
],
|
|
50
|
+
providers: [...(base.providers ?? []), ...managers],
|
|
51
|
+
exports: [DATABASE_OPTIONS, ...managers.map((manager) => manager.provide)],
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Secret } from "@modules/platform/config"
|
|
2
|
+
|
|
3
|
+
/** What the primary connection parser reads from the environment. */
|
|
4
|
+
export interface DatabaseConnectionConfig {
|
|
5
|
+
/** The logical connection name declared in `hfs.json`. */
|
|
6
|
+
readonly name: string
|
|
7
|
+
/** The Supabase PostgreSQL provider; schema changes remain under `supabase/migrations`. */
|
|
8
|
+
readonly provider: "supabase"
|
|
9
|
+
/** The dedicated least-privilege application role's connection URL. */
|
|
10
|
+
readonly url: Secret
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Runtime connection options add only the isolated PostgreSQL schema; lite has no entity or migration registry. */
|
|
14
|
+
export interface DatabaseConnectionOptions extends DatabaseConnectionConfig {
|
|
15
|
+
/** The schema named by the `hfs.json` isolation contract. */
|
|
16
|
+
readonly schema?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Options of the database capability. */
|
|
20
|
+
export interface DatabaseOptions {
|
|
21
|
+
/** The connections this app opens; one per physical database. */
|
|
22
|
+
readonly connections: ReadonlyArray<DatabaseConnectionOptions>
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { injector } from "@modules/platform/composition"
|
|
2
|
+
import type { TypedParameterDecorator } from "@modules/platform/composition"
|
|
3
|
+
import type { DatabaseOptions } from "./database.options"
|
|
4
|
+
|
|
5
|
+
/** Token of the options of the database capability. */
|
|
6
|
+
export const DATABASE_OPTIONS: unique symbol = Symbol("platform.database.options")
|
|
7
|
+
|
|
8
|
+
/** Injects the options of the database capability. Parameter type: DatabaseOptions. */
|
|
9
|
+
export const InjectDatabaseOptions = (): TypedParameterDecorator<DatabaseOptions> =>
|
|
10
|
+
injector<DatabaseOptions>(DATABASE_OPTIONS)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EntityManager } from "typeorm"
|
|
2
|
+
import { DatabaseError, DatabaseErrorCode } from "./errors/database.error"
|
|
3
|
+
|
|
4
|
+
declare const sqlTextBrand: unique symbol
|
|
5
|
+
declare const sqlIdentBrand: unique symbol
|
|
6
|
+
|
|
7
|
+
/** SQL text built by the `sql` tag: the only kind of string `EntityManager.query` accepts. */
|
|
8
|
+
export type SqlText = string & { readonly [sqlTextBrand]: true }
|
|
9
|
+
|
|
10
|
+
/** A checked identifier: the only value a `sql` tag substitution accepts. */
|
|
11
|
+
export type SqlIdent = string & { readonly [sqlIdentBrand]: true }
|
|
12
|
+
|
|
13
|
+
/** The biggest page a client can ask for. */
|
|
14
|
+
export const PAGE_SIZE_MAX = 100
|
|
15
|
+
/** The biggest number of rows a server-side list read returns. */
|
|
16
|
+
export const LIST_ROWS_MAX = 500
|
|
17
|
+
/** The size of the keyset batches a background scan reads. */
|
|
18
|
+
export const BATCH_ROWS = 500
|
|
19
|
+
|
|
20
|
+
const IDENT_PATTERN = /^[a-z_][a-z0-9_]*$/
|
|
21
|
+
|
|
22
|
+
function assertSqlText(_value: string): asserts _value is SqlText {
|
|
23
|
+
// The brand is compile-time only; the `sql` tag is its single caller.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertSqlIdent(_value: string): asserts _value is SqlIdent {
|
|
27
|
+
// The brand is compile-time only; `ident` is the single caller after checking the name.
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Builds SQL text whose only substitutions are already checked identifiers. */
|
|
31
|
+
{{> be/common/sql-tag.ts.partial}}
|
|
32
|
+
|
|
33
|
+
/** Checks a dynamic identifier against the names the caller allows and brands it. */
|
|
34
|
+
{{> be/common/sql-ident.ts.partial}}
|
|
35
|
+
|
|
36
|
+
type NotFoundFactory = () => never
|
|
37
|
+
|
|
38
|
+
/** Requires the row selected by an id-and-owner statement and lets the capability own its typed not-found error. */
|
|
39
|
+
export const requireOwnedRow = async <T>(
|
|
40
|
+
entityManager: EntityManager,
|
|
41
|
+
statement: SqlText,
|
|
42
|
+
id: string,
|
|
43
|
+
ownerId: string,
|
|
44
|
+
notFound: NotFoundFactory,
|
|
45
|
+
): Promise<T> => {
|
|
46
|
+
const row = (await entityManager.query<Array<T>>(statement, [id, ownerId]))[0]
|
|
47
|
+
if (row === undefined) return notFound()
|
|
48
|
+
return row
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { spawn } from "node:child_process"
|
|
2
|
+
import { Injectable } from "@nestjs/common"
|
|
3
|
+
|
|
4
|
+
const runDbPush = (): Promise<void> =>
|
|
5
|
+
new Promise((resolve, reject) => {
|
|
6
|
+
const command = process.platform === "win32" ? "npm.cmd" : "npm"
|
|
7
|
+
const child = spawn(command, ["run", "db:push"], { stdio: "inherit" })
|
|
8
|
+
child.once("error", reject)
|
|
9
|
+
child.once("exit", (code) =>
|
|
10
|
+
code === 0 ? resolve() : reject(new Error(`db:push exited ${code ?? "without a status"}`)),
|
|
11
|
+
)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
@Injectable()
|
|
15
|
+
/** The lite migration runner delegates the Supabase schema authority to the managed db:push wrapper. */
|
|
16
|
+
export class MigrationRunnerService {
|
|
17
|
+
/** Runs the managed wrapper and propagates a CLI or migration failure. */
|
|
18
|
+
async run(): Promise<void> {
|
|
19
|
+
await runDbPush()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EnvSource } from "@modules/platform/config"
|
|
2
|
+
import type { DatabaseConnectionOptions } from "./database.options"
|
|
3
|
+
import { PRIMARY_CONNECTION } from "./primary.connection"
|
|
4
|
+
|
|
5
|
+
/** Reads the least-privilege Supabase PostgreSQL connection with no default. */
|
|
6
|
+
export const parsePrimaryDatabaseConfig = (env: EnvSource): DatabaseConnectionOptions => ({
|
|
7
|
+
name: PRIMARY_CONNECTION,
|
|
8
|
+
provider: "supabase",
|
|
9
|
+
url: env.secret("PRIMARY_DB_URL"),
|
|
10
|
+
schema: env.string("PRIMARY_DB_SCHEMA"),
|
|
11
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises"
|
|
2
|
+
import { Injectable } from "@nestjs/common"
|
|
3
|
+
import type { EntityManager } from "typeorm"
|
|
4
|
+
import type { SqlText } from "./database.sql"
|
|
5
|
+
import { InjectPrimaryEntityManager } from "./primary.decorators"
|
|
6
|
+
|
|
7
|
+
const SEED_FILE = "supabase/seed.sql"
|
|
8
|
+
|
|
9
|
+
function assertSeedText(_text: string): asserts _text is SqlText {
|
|
10
|
+
// The brand is compile-time only; this function's caller reads the tracked seed file verbatim.
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const seedText = (text: string): SqlText => {
|
|
14
|
+
assertSeedText(text)
|
|
15
|
+
return text
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
/** Executes the one tracked Supabase seed through the shared least-privilege EntityManager. */
|
|
20
|
+
export class SeedRunnerService {
|
|
21
|
+
constructor(@InjectPrimaryEntityManager() private readonly entityManager: EntityManager) {}
|
|
22
|
+
|
|
23
|
+
/** Reads and executes the tracked seed when it contains at least one statement. */
|
|
24
|
+
async run(): Promise<void> {
|
|
25
|
+
const text = seedText(await readFile(SEED_FILE, "utf8"))
|
|
26
|
+
if (text.trim() !== "") await this.entityManager.query(text)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common"
|
|
2
|
+
import type { DynamicModule } from "@nestjs/common"
|
|
3
|
+
import { WEBHOOK_SIGNATURE } from "./http-security.decorators"
|
|
4
|
+
import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } from "./http-security.module-definition"
|
|
5
|
+
import { RequestValidationService } from "./request-validation.service"
|
|
6
|
+
import { WebhookSignatureService } from "./webhook-signature.service"
|
|
7
|
+
|
|
8
|
+
@Module({})
|
|
9
|
+
/** The http-security capability: the options its guards (registered by each api app) read. */
|
|
10
|
+
export class HttpSecurityModule extends ConfigurableModuleClass {
|
|
11
|
+
/** Registers the capability once per app. */
|
|
12
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
|
13
|
+
const base = super.register(options)
|
|
14
|
+
return {
|
|
15
|
+
...base,
|
|
16
|
+
providers: [
|
|
17
|
+
...(base.providers ?? []),
|
|
18
|
+
RequestValidationService,
|
|
19
|
+
WebhookSignatureService,
|
|
20
|
+
{ provide: WEBHOOK_SIGNATURE, useExisting: WebhookSignatureService },
|
|
21
|
+
],
|
|
22
|
+
exports: [MODULE_OPTIONS_TOKEN, RequestValidationService, WebhookSignatureService, WEBHOOK_SIGNATURE],
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { HTTP_SECURITY_ERROR_KINDS } from "./errors/http-security.error"
|
|
2
|
+
export { requestOf } from "./execution-request.mapper"
|
|
3
|
+
export { parseHttpSecurityConfig } from "./http-security.config"
|
|
4
|
+
export { HttpSecurityModule } from "./http-security.module"
|
|
5
|
+
export type { HttpSecurityOptions } from "./http-security.options"
|
|
6
|
+
export { HTTP_SECURITY_MESSAGES } from "./messages/http-security.messages"
|
|
7
|
+
export { OriginGuard } from "./origin.guard"
|
|
8
|
+
export { RateLimitGuard } from "./rate-limit.guard"
|
|
9
|
+
export { RequestValidationService } from "./request-validation.service"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DomainError } from "@modules/platform/errors"
|
|
2
|
+
import type { ErrorKind } from "@modules/platform/errors"
|
|
3
|
+
|
|
4
|
+
/** Codes of the {{name}} capability. */
|
|
5
|
+
export enum {{Name}}ErrorCode {
|
|
6
|
+
/** No row owned by the authenticated principal has the requested id. */
|
|
7
|
+
NotFound = "{{upper}}_NOT_FOUND",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** How each {{name}} code travels. */
|
|
11
|
+
export const {{upper}}_ERROR_KINDS: Record<{{Name}}ErrorCode, ErrorKind> = {
|
|
12
|
+
[{{Name}}ErrorCode.NotFound]: "not-found",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The one error class of the {{name}} capability. */
|
|
16
|
+
export class {{Name}}Error extends DomainError<{{Name}}ErrorCode> {}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
create table public.{{tableSql}} (
|
|
2
|
+
id uuid primary key default gen_random_uuid(),
|
|
3
|
+
owner_id uuid not null references auth.users (id) on delete cascade,
|
|
4
|
+
created_at timestamptz not null default now(),
|
|
5
|
+
updated_at timestamptz not null default now()
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
create index {{table}}_owner_id_idx on public.{{tableSql}} (owner_id);
|
|
9
|
+
|
|
10
|
+
alter table public.{{tableSql}} enable row level security;
|
|
11
|
+
|
|
12
|
+
create trigger {{table}}_set_updated_at
|
|
13
|
+
before update on public.{{tableSql}}
|
|
14
|
+
for each row
|
|
15
|
+
execute function private.set_updated_at();
|
|
16
|
+
|
|
17
|
+
create policy "{{table}}_authenticated_select"
|
|
18
|
+
on public.{{tableSql}}
|
|
19
|
+
for select
|
|
20
|
+
to authenticated
|
|
21
|
+
using (owner_id = (select auth.uid()));
|
|
22
|
+
|
|
23
|
+
create policy "{{table}}_authenticated_insert"
|
|
24
|
+
on public.{{tableSql}}
|
|
25
|
+
for insert
|
|
26
|
+
to authenticated
|
|
27
|
+
with check (owner_id = (select auth.uid()));
|
|
28
|
+
|
|
29
|
+
create policy "{{table}}_authenticated_update"
|
|
30
|
+
on public.{{tableSql}}
|
|
31
|
+
for update
|
|
32
|
+
to authenticated
|
|
33
|
+
using (owner_id = (select auth.uid()))
|
|
34
|
+
with check (owner_id = (select auth.uid()));
|
|
35
|
+
|
|
36
|
+
create policy "{{table}}_authenticated_delete"
|
|
37
|
+
on public.{{tableSql}}
|
|
38
|
+
for delete
|
|
39
|
+
to authenticated
|
|
40
|
+
using (owner_id = (select auth.uid()));
|
|
41
|
+
|
|
42
|
+
create policy "{{table}}_app_be_select"
|
|
43
|
+
on public.{{tableSql}}
|
|
44
|
+
for select
|
|
45
|
+
to app_be
|
|
46
|
+
using (owner_id is not null);
|
|
47
|
+
|
|
48
|
+
revoke all on table public.{{tableSql}} from public, anon;
|
|
49
|
+
grant select, insert, delete on table public.{{tableSql}} to authenticated;
|
|
50
|
+
grant update (updated_at) on table public.{{tableSql}} to authenticated;
|
|
51
|
+
grant select on table public.{{tableSql}} to app_be;
|
|
@@ -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}} capability. */
|
|
5
|
+
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<{{Name}}Options>()
|
|
6
|
+
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
7
|
+
.build()
|