@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
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
# the CI, the hooks and .starciwork (slots of profile `app`); `be/` and `fe/` are its two sides, each laid out as the old
|
|
5
5
|
# standalone repository root (slots of profile be or fe, paths relative to the side folder). Every tracked path of every
|
|
6
6
|
# repository must match exactly one slot. Checks, lint factories, the architecture machine, templates and the why
|
|
7
|
-
# catalog READ this file through scripts/
|
|
7
|
+
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
8
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
9
9
|
schema: starci/hfs-slots@2
|
|
10
|
-
version: 2.
|
|
10
|
+
version: 2.1.0
|
|
11
11
|
|
|
12
12
|
versioning:
|
|
13
13
|
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 2).
|
|
@@ -24,6 +24,9 @@ versioning:
|
|
|
24
24
|
a slot is never edited in place; it gets retiredIn (a major) and a successor slot id. After that major a
|
|
25
25
|
path matching a retired slot is HFS_SLOT_RETIRED (error).
|
|
26
26
|
pins: knowledge/hfs/canon-pins.yaml carries the exact versions of every @starci/* package and framework this MAJOR supports.
|
|
27
|
+
# 2.1.0 is such a minor: it ADDS the edition mechanism (the `editions` list, the per-slot `editions`, `litePresence`, `lite`
|
|
28
|
+
# overlays and `provider`, and `sides.<side>.reads` of an app-root tree) and the lite slots; an app without `edition` in its
|
|
29
|
+
# hfs.json is edition full and every answer for it is unchanged.
|
|
27
30
|
|
|
28
31
|
# Field meaning, per slot.
|
|
29
32
|
# id stable name, never changes meaning inside a major.
|
|
@@ -38,6 +41,13 @@ versioning:
|
|
|
38
41
|
# tier node of the import direction matrix (tiers below); none = takes no part in import checks;
|
|
39
42
|
# inherit = the tier of the enclosing owner.
|
|
40
43
|
# owner true when every directory matching the slot is an owner: the unit cross-owner imports and cycles use.
|
|
44
|
+
# anonymousActions on a directory slot: the Server Action files (relative to it) that authenticate or end a session and so run for an anonymous principal (FE_AUTH_SESSION_TRUST exempts them from refusing it; they still parse their input).
|
|
45
|
+
# entries on a directory slot that sits inside an owner (not an owner itself): the files, besides the owner's index.ts, an importer
|
|
46
|
+
# outside the owner may enter through (a client-safe entry of a module whose barrel is server-only). Each is a relative
|
|
47
|
+
# path the slot allows. Everything else of the owner is entered through index.ts only (ARCH_OWNER_EXPORT_BYPASS).
|
|
48
|
+
# outcomeHome true on a file slot: the one file that declares the repository's Outcome union. A repository holds exactly one such
|
|
49
|
+
# file (modules/api/outcome.ts, the api package's outcome.ts, or modules/db/outcome.ts of a lite app),
|
|
50
|
+
# and no other file declares an Outcome union (one-outcome-union, FE_TRANSPORT_OWNER).
|
|
41
51
|
# requires files or directories that must exist inside each instance; <var> is replaced by the bound name,
|
|
42
52
|
# a leading / means repository root; a trailing / means directory.
|
|
43
53
|
# requiredInstances variable -> names that must exist as instances (config, logging ... of platform).
|
|
@@ -45,42 +55,89 @@ versioning:
|
|
|
45
55
|
# requiredWhen connections: required only when hfs.json declares a database connection.
|
|
46
56
|
# allows / forbids names inside the instance; the loader exposes them, slot-check enforces them.
|
|
47
57
|
# tests unit-beside | e2e | none.
|
|
48
|
-
#
|
|
58
|
+
# coverage required | none, on every tracked slot of profile be. A required slot's subtree is measured at 100 per file on every
|
|
59
|
+
# ruleParams.be.logicRoles file (lines, branches, functions, statements); a none slot's subtree is never measured and
|
|
60
|
+
# is excluded from Sonar's coverage. scripts/hfs/coverage-scope.mjs derives the jest per-file thresholds, Sonar's
|
|
61
|
+
# sonar.coverage.exclusions and the Codecov paths and components from this field and nowhere else (R204).
|
|
62
|
+
# Orthogonal to the lite `editions`: an edition never changes what a slot measures.
|
|
63
|
+
# composedBy the app kinds allowed to compose a transport slot (an app of another kind imports none of its modules); on an
|
|
64
|
+
# owner slot (be.cli) the app kinds that list its root module, every folder of the root holding one static module.
|
|
49
65
|
# budget size budgets (lines per file, files per slot, exports).
|
|
66
|
+
# trigger the trigger kind (triggerKinds) of a feature-tier slot: api, webhooks, realtime, saga, reactors, jobs or cli. A feature of one kind
|
|
67
|
+
# never imports a feature of another (BE_KIND_ISOLATION); the two meet only through the event bus.
|
|
68
|
+
# pattern the design pattern whose declaration (hfs.json sides.<side>.patterns) enables this opt-in slot; a path in the slot of an
|
|
69
|
+
# undeclared pattern is HFS_SLOT_NOT_ENABLED, and a declared pattern's proof scenarios are ruleParams.be.patternScenarios.
|
|
50
70
|
# appKind the app kind of hfs.json apps[].kind whose directory this slot describes.
|
|
51
|
-
# managedBy the template directory
|
|
71
|
+
# managedBy the template directory starci app sync renders this slot's files from. The slot path then lists literal files
|
|
52
72
|
# only, and each is rendered from templates/<profile or common>/<managedBy>/<path, leading dots dropped>;
|
|
73
|
+
# liteManagedBy the template directory starci app sync renders under edition lite (default: managedBy). A slot with only
|
|
74
|
+
# liteManagedBy is managed only in lite; its effective lite.path still lists literal files;
|
|
53
75
|
# the managed file list lives here and nowhere in code. A difference is HFS_MANAGED_FILE_DRIFT (a
|
|
54
76
|
# back end's tsconfig.json: HFS_TS_STRICT when a flag or an option was added).
|
|
55
77
|
# rules rule ids (the catalog lands with knowledge/hfs/rules.yaml).
|
|
78
|
+
# editions the editions (below) the slot exists in; default all of them. A slot outside the declared edition owns
|
|
79
|
+
# no path - under lite, what full keeps absent is removed, and what lite forbids is a finding.
|
|
80
|
+
# litePresence the presence under edition lite (default: presence): a bare value, or {be, fe} for a both-sides slot.
|
|
81
|
+
# A slot forbidden in lite is external there and must name its goesTo.
|
|
82
|
+
# lite the fields the slot carries under lite only (path, requires, allows, forbids, minInstances,
|
|
83
|
+
# requiredInstances), each shaped as the same field in the slot body; every other field is the edition's.
|
|
84
|
+
# Under lite `tests` is none on every slot: a lite app has no test world.
|
|
85
|
+
# provider the hfs.json connections[].provider that enables this opt-in slot (supabase); never an app kind.
|
|
56
86
|
# since / retiredIn / successor lifecycle.
|
|
57
87
|
presenceValues: [required, optional, opt-in, forbidden]
|
|
58
88
|
trackedValues: [tracked, ignored, external]
|
|
59
89
|
testValues: [unit-beside, e2e, none]
|
|
60
90
|
|
|
91
|
+
# The editions a repository declares in hfs.json `edition` (absent means full). Lite is this same manifest filtered: a slot
|
|
92
|
+
# whose `editions` lacks lite does not exist for a lite app, `litePresence` replaces presence and a `lite` map replaces the
|
|
93
|
+
# fields it names. Upgrade lite -> full adds only (the forbidden test world becomes legal again); downgrade is not supported.
|
|
94
|
+
editions: [full, lite]
|
|
95
|
+
|
|
61
96
|
# The two sides of an app: the folder of each is its profile name (be/, fe/). `reads` lists the only paths of the OTHER side a side
|
|
62
|
-
# may read (hfs.json sides.<side>.reads names a subset); nothing else crosses sides.
|
|
97
|
+
# may read (hfs.json sides.<side>.reads names a subset), or a tree an app-root slot owns (supabase/types/); nothing else crosses sides.
|
|
63
98
|
sides:
|
|
64
|
-
be: {reads: []}
|
|
65
|
-
fe: {reads: [be/contracts/]} # the
|
|
99
|
+
be: {reads: [supabase/types/]} # the generated Database types of a Supabase connection (server code types its PostgREST calls)
|
|
100
|
+
fe: {reads: [be/contracts/, supabase/types/]} # be/contracts/: the committed contract snapshots, the input of the front end's codegen; supabase/types/: the same generated types
|
|
101
|
+
|
|
102
|
+
# The trigger kinds of the back end's features: what starts the work of a feature. A feature slot names its kind in `trigger`; no kind imports another
|
|
103
|
+
# (BE_KIND_ISOLATION), every cross-kind call is an event on the bus, and only be/apps/* compose features.
|
|
104
|
+
triggerKinds: [api, webhooks, realtime, saga, reactors, jobs, cli]
|
|
66
105
|
|
|
67
106
|
appKinds:
|
|
68
|
-
be: [api, worker,
|
|
107
|
+
be: [api, worker, cli]
|
|
69
108
|
fe: [next]
|
|
70
109
|
|
|
110
|
+
# RT_SLOT_ID_SHAPE — the slot-id grammar and the suffix vocabulary law, stated once (scripts/checks/check-slot-id-shape.mjs
|
|
111
|
+
# reads this block): an id is dot-separated lowercase kebab segments whose first segment is one of `prefixes`;
|
|
112
|
+
# `<side>.app.<kind>` names a kind of appKinds.<side>; any other deeper id extends a declared slot id (its `parent`)
|
|
113
|
+
# or names `parent` explicitly. The role-suffix vocabulary never shares a word with bannedSuffixes and holds no
|
|
114
|
+
# synonym pair beyond `sameConceptPairs` — the one owner-approved pair (step + saga-step: `.saga-step.ts` stays).
|
|
115
|
+
# Every pair in `refusedPairs` may never both be live suffixes.
|
|
116
|
+
# `glossary` (S9-02): the five names for "handles an incoming thing" are five distinct kinds, not synonyms —
|
|
117
|
+
# consumer answers an event, subscription serves realtime, webhook is its own kind, processor runs a job, handler
|
|
118
|
+
# is CQRS. That is why appKinds may say worker while bannedSuffixes refuses the worker/job file suffixes.
|
|
119
|
+
naming:
|
|
120
|
+
prefixes: [app, be, fe, repo]
|
|
121
|
+
sameConceptPairs: [[step, saga-step]]
|
|
122
|
+
refusedPairs: [[use-case, handler], [listener, subscription], [job, processor], [worker, processor], [dto, input], [repository, mapper]]
|
|
123
|
+
glossary: {consumer: event, subscription: realtime, webhook: webhook, processor: job, handler: cqrs}
|
|
124
|
+
|
|
71
125
|
# ---------------------------------------------------------------------------------------------------------
|
|
72
126
|
# Tiers and the dependency direction matrix (owner granularity; type-only imports count; no cycles anywhere)
|
|
73
127
|
# mayImport lists the tiers a file of that tier may import. Importing inside the same owner is always allowed.
|
|
74
128
|
# ---------------------------------------------------------------------------------------------------------
|
|
75
129
|
tiers:
|
|
76
130
|
be:
|
|
77
|
-
app: {mayImport: [feature, domain, platform, integrations, package]}
|
|
78
|
-
feature: {mayImport: [domain, platform, integrations, package]} # never another feature
|
|
79
|
-
domain: {mayImport: [domain, platform, integrations, package], acyclic: true}
|
|
131
|
+
app: {mayImport: [feature, domain, events, queues, projections, platform, integrations, package]}
|
|
132
|
+
feature: {mayImport: [domain, events, queues, projections, platform, integrations, package]} # never another feature
|
|
133
|
+
domain: {mayImport: [domain, events, queues, platform, integrations, package], acyclic: true} # a domain service writes the outbox: it publishes events and enqueues queue jobs inside its own transaction
|
|
134
|
+
events: {mayImport: [events, platform, package], acyclic: true} # modules/events/<service>: typed event classes (pattern event-bus)
|
|
135
|
+
queues: {mayImport: [queues, events, platform, package], acyclic: true} # modules/queues/<queue>: typed producers and schedulers (pattern queue)
|
|
136
|
+
projections: {mayImport: [projections, domain, events, platform, integrations, package], acyclic: true} # modules/projections/<name>: read models (pattern projection)
|
|
80
137
|
integrations: {mayImport: [platform, package]}
|
|
81
138
|
platform: {mayImport: [platform, package], acyclic: true}
|
|
82
|
-
e2e: {mayImport: [e2e, app, feature, domain, platform, integrations, fixtures, package]}
|
|
83
|
-
fixtures: {mayImport: [domain, platform, integrations, package]} # public contracts only
|
|
139
|
+
e2e: {mayImport: [e2e, app, feature, domain, events, queues, projections, platform, integrations, fixtures, package]}
|
|
140
|
+
fixtures: {mayImport: [domain, events, queues, projections, platform, integrations, package]} # public contracts only
|
|
84
141
|
package: {mayImport: [package]}
|
|
85
142
|
fe:
|
|
86
143
|
route: {mayImport: [feature, foundation, modules, transport, package]} # app/** adapters
|
|
@@ -97,14 +154,17 @@ tiers:
|
|
|
97
154
|
crossOwner: every import across owners targets the owner's public entry (index.ts or index.tsx); never export *.
|
|
98
155
|
crossApp: apps never import each other; shared code is a packages/<pkg> slot.
|
|
99
156
|
|
|
100
|
-
# Parameters the lint factories and checks read per profile (through ruleParams(profile) in scripts/
|
|
157
|
+
# Parameters the lint factories and checks read per profile (through ruleParams(profile) in scripts/hfs/slots.mjs, which
|
|
158
|
+
# merges common under the profile: a side restates a common key only to override it).
|
|
101
159
|
ruleParams:
|
|
102
|
-
|
|
103
|
-
# HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft.
|
|
160
|
+
common:
|
|
161
|
+
# HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft. Slot
|
|
162
|
+
# budgets (component.tsx 300, hooks 200, modules 400) are stricter than this cap where they apply.
|
|
104
163
|
fileLines: {soft: 500, hardGrowth: true}
|
|
105
|
-
# R21 HFS_DUPLICATE_CODE: the
|
|
106
|
-
# a token-normalised block of at least `lines` source lines and `tokens` tokens that appears twice.
|
|
164
|
+
# R21 HFS_DUPLICATE_CODE: the one threshold of duplicate code of both sides (the architecture machine reads it here;
|
|
165
|
+
# no other file states it): a token-normalised block of at least `lines` source lines and `tokens` tokens that appears twice.
|
|
107
166
|
duplicateBlock: {lines: 8, tokens: 60}
|
|
167
|
+
be:
|
|
108
168
|
# R90 BE_INFRA_OWNER: raw infrastructure library (a module specifier or a global reference) -> the platform or
|
|
109
169
|
# integration capabilities allowed to import it; [] means nowhere in a back end. A specifier also covers its subpaths.
|
|
110
170
|
infraOwners:
|
|
@@ -120,14 +180,15 @@ ruleParams:
|
|
|
120
180
|
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability (retired local rule must-use-cache-service)
|
|
121
181
|
redis: [integrations/cache, integrations/redis]
|
|
122
182
|
ioredis: [integrations/cache, integrations/redis]
|
|
123
|
-
bullmq: [platform/
|
|
124
|
-
"@nestjs/bullmq": [platform/
|
|
125
|
-
kafkajs: [platform/
|
|
126
|
-
"@nestjs/schedule": [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"node:timers
|
|
183
|
+
bullmq: [platform/queue] # pattern queue: BullMQ jobs and job schedulers (the relay of the outbox into queues)
|
|
184
|
+
"@nestjs/bullmq": [platform/queue]
|
|
185
|
+
kafkajs: [platform/event-bus] # pattern event-bus: Kafka is the only event transport
|
|
186
|
+
"@nestjs/schedule": [] # no @Cron / @Interval / @Timeout anywhere: a schedule is a BullMQ job scheduler of a queue
|
|
187
|
+
eventemitter2: []
|
|
188
|
+
setInterval: [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
189
|
+
setTimeout: [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
190
|
+
"node:timers": [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
191
|
+
"node:timers/promises": [platform/retry, platform/event-bus, platform/queue, platform/http]
|
|
131
192
|
winston: [platform/logging]
|
|
132
193
|
dayjs: [platform/clock, platform/primitives]
|
|
133
194
|
moment: [platform/clock, platform/primitives]
|
|
@@ -135,6 +196,22 @@ ruleParams:
|
|
|
135
196
|
"@nestjs/config": []
|
|
136
197
|
dotenv: []
|
|
137
198
|
"@nestjs/event-emitter": []
|
|
199
|
+
"@nestjs/platform-socket.io": [platform/realtime]
|
|
200
|
+
socket.io: [platform/realtime]
|
|
201
|
+
graphql-subscriptions: [platform/realtime]
|
|
202
|
+
graphql-ws: [platform/realtime]
|
|
203
|
+
# R87 BE_CQRS_SHAPE no-event-bus: the in-process event vocabulary the rule refuses. `imports` maps a module
|
|
204
|
+
# specifier to the member names that are an in-process event; `[]` flags the package itself (its emitter packages
|
|
205
|
+
# are the `[]` owners of infraOwners above). `classes` maps a module specifier to the class names that are an
|
|
206
|
+
# in-process bus when constructed or extended.
|
|
207
|
+
eventBus:
|
|
208
|
+
imports:
|
|
209
|
+
"@nestjs/cqrs": [EventBus, EventsHandler, IEventHandler]
|
|
210
|
+
"@nestjs/event-emitter": []
|
|
211
|
+
classes:
|
|
212
|
+
events: [EventEmitter]
|
|
213
|
+
"node:events": [EventEmitter]
|
|
214
|
+
rxjs: [Subject, BehaviorSubject, ReplaySubject, AsyncSubject]
|
|
138
215
|
# R48 BE_SPEC_QUALITY spec-infra-double-from-kit: in a `<name>.service.spec.ts` a provider `{ provide: TOKEN, useValue: X }` takes X from
|
|
139
216
|
# the kit named here (the package root of `kit`). The token name is normalised to UPPER_SNAKE (`EntityManager` -> ENTITY_MANAGER), the
|
|
140
217
|
# FIRST entry whose `token` regex matches decides, and `fallback` decides for every other token. `forms` say what X may be:
|
|
@@ -147,23 +224,90 @@ ruleParams:
|
|
|
147
224
|
- {token: "(?:^|_)ENTITY_MANAGER$", double: mockEntityManager, forms: [call]}
|
|
148
225
|
- {token: "(?:^|_)TRANSACTION(?:_RUNNER)?$", double: fakeTransaction, forms: [call]}
|
|
149
226
|
- {token: "(?:^|_)CLOCK$", double: FakeClock, forms: [new]}
|
|
150
|
-
- {token: "(?:^|_)
|
|
227
|
+
- {token: "(?:^|_)EVENT_BUS$", double: recordingEventBus, forms: [call]}
|
|
228
|
+
- {token: "(?:^|_)QUEUE_OUTBOX$", double: recordingQueueOutbox, forms: [call]}
|
|
229
|
+
- {token: "(?:^|_)INBOX(?:_|$)", double: fakeInbox, forms: [call]}
|
|
151
230
|
- {token: "(?:^|_)CACHE(?:_|$)", double: fakeCache, forms: [call]}
|
|
152
231
|
- {token: "(?:^|_)(?:LOCK|LEASE|FENCE|HOLD)(?:_|$)", double: fakeLock, forms: [call]}
|
|
153
232
|
- {token: "(?:^|_)(?:IDS|ID_GENERATOR|ID_FACTORY)$", double: fakeIds, forms: [call]}
|
|
154
233
|
fallback: {double: mock, forms: [call, primitive, array]}
|
|
234
|
+
# R85 BE_RAW_INJECT injected-param-name: the names of the ports every class injects the same way (BE-CONVENTION 1.6: the parameter name
|
|
235
|
+
# is the camelCase of the port). An entry matches a constructor parameter that carries an injector and is annotated with a type
|
|
236
|
+
# declared outside the owner (a package, platform/* or integrations/*): `type` is the exact declared name, `typeSuffix` the end of
|
|
237
|
+
# it. The parameter is named one of `names`, or ends in `nameSuffix` when the entry has one (`orderEntityManager` for a class that
|
|
238
|
+
# injects two connections, `paymentOptions` beside `options`). A type no entry names is free to be named for what it is.
|
|
239
|
+
paramNames:
|
|
240
|
+
- {type: Clock, names: [clock]}
|
|
241
|
+
- {type: Logger, names: [logger]}
|
|
242
|
+
- {type: Cache, names: [cache]}
|
|
243
|
+
- {type: CommandBus, names: [commandBus]}
|
|
244
|
+
- {type: QueryBus, names: [queryBus]}
|
|
245
|
+
- {type: EntityManager, names: [entityManager], nameSuffix: EntityManager}
|
|
246
|
+
- {typeSuffix: Options, names: [options], nameSuffix: Options}
|
|
155
247
|
# R89 BE_SOURCE_FORM: the closed role-suffix vocabulary of a source file name (<kebab-name>.<suffix>.ts; BE-CONVENTION 1.15)
|
|
156
248
|
# and the suffixes that are never a role. index.ts, main.ts, app.module.ts and the migrations of be.persistence are the
|
|
157
249
|
# only names outside it.
|
|
158
|
-
suffixes: [module, module-definition, decorators, options, config, connection, service, command, query, handler, contracts, port, resolver, controller, gateway,
|
|
250
|
+
suffixes: [module, module-definition, decorators, options, config, connection, service, command, query, handler, compensation, saga-state, saga-step, saga, contracts, port, resolver, controller, gateway, webhook, subscription, consumer, cli, event, queue, projection, projection-entity, processor, step, input, type, args, request, response, mapper, policy, entity, sql, rows, error, messages, log-events, cache-keys, guard, interceptor, filter, client, spec, integration-spec, e2e-spec, contract-spec, global-setup, builder]
|
|
159
251
|
# R47 BE_CONTRACT_UNGUARDED: the world's shape vocabulary. `helper` names the function a contract spec imports to erase the values of a JSON
|
|
160
252
|
# payload (`shapeOf`); a contract spec proves a fake's fixtures by asserting helper(real) equals helper(fixture).
|
|
161
253
|
contractShape: {helper: shapeOf}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
254
|
+
# THE list of unit-tested roles of a back end whose spec is REQUIRED (the ONE source every consumer reads: the eslint unit-test rules, hfs
|
|
255
|
+
# spec-placement and the cli spec check, the architecture machine's unit-spec checks and the unit-run gate). A role's subject is
|
|
256
|
+
# `<name>.<role>.ts` (inside `slot` when one is named) and its unit spec `<name>.<spec>.ts` beside it. What the unit run MEASURES is not
|
|
257
|
+
# listed here: it is `logicRoles` over the slots whose `coverage` is required (scripts/hfs/coverage-scope.mjs). `exempt` lists the rules
|
|
258
|
+
# the role is exempt from: a cli command is an action runner, not a thin transport (R149). A lane that adds a spec-required role adds
|
|
259
|
+
# its entry here and nowhere else.
|
|
260
|
+
unitRoles:
|
|
261
|
+
- {role: service, spec: service.spec}
|
|
262
|
+
- {role: cli, spec: cli.spec, slot: be.cli, exempt: [BE_TRANSPORT_SHAPE]}
|
|
263
|
+
# R204 HFS_COVERAGE_SCOPE_DRIFT. THE roles that carry logic: a file `<name>.<role>.ts` of one of these roles
|
|
264
|
+
# inside a slot whose `coverage` is required is measured at 100 per file on lines, branches, functions and statements (the three
|
|
265
|
+
# generated consumers: jest thresholds, sonar.coverage.exclusions, codecov.yml). A role that is not listed here (module, module-definition,
|
|
266
|
+
# options, config, command, query, contracts, port, event, queue, entity, sql, rows, messages, log-events, error, decorators, cache-keys,
|
|
267
|
+
# projection-entity, connection, and every file with no role: index.ts, main.ts, migrations) is declaration or composition and is never measured.
|
|
268
|
+
# A unit spec beside such a file (`<name>.<role>.spec.ts`) is legal but not required: the file owes 100, not a spec of its own.
|
|
269
|
+
logicRoles: [service, policy, projection, guard, mapper, client, filter, interceptor, processor, step, saga, saga-step, compensation, consumer, webhook]
|
|
270
|
+
# R203 BE_FEATURE_THIN. THE roles a feature-tier file is judged thin by: a file `<name>.<role>.ts` of one of these roles inside a slot of tier
|
|
271
|
+
# feature maps parameters and makes ONE delegating call into modules/ (a service, the command or query bus, the event bus, a queue producer): no
|
|
272
|
+
# branch, loop, ternary, try or throw, no computation; orchestration (step order, compensation, claim, fencing, retry) lives in platform/*.
|
|
273
|
+
thinRoles: [handler, resolver, controller, consumer, processor, step, saga, saga-step, compensation, webhook, gateway, subscription, cli, mapper, service]
|
|
274
|
+
# BE_PATTERN_SPEC_MISSING: a declared pattern (hfs.json sides.be.patterns) is proven on the test world. Each id below needs a test titled
|
|
275
|
+
# "<pattern>/<id>: ..." in an e2e or integration spec (be/src/tests/e2e, be/src/tests/integration).
|
|
276
|
+
patternScenarios:
|
|
277
|
+
event-bus: [atomic-with-transaction, relay-only-after-commit, redelivery-then-dead-letter, duplicate-delivery-is-a-noop]
|
|
278
|
+
queue: [enqueue-atomic-with-transaction, relay-job-id-is-outbox-id, scheduler-fires-once]
|
|
279
|
+
webhooks: [bad-signature-is-refused, replay-is-refused, accepted-delivery-publishes-once]
|
|
280
|
+
realtime: [subscriber-receives-push, foreign-topic-is-isolated, disconnect-releases-subscription]
|
|
281
|
+
fenced-job: [claim-bumps-token, zombie-fenced-out, redispatch-isolated]
|
|
282
|
+
projection: [recompute-is-idempotent, replay-rebuilds]
|
|
283
|
+
# BE_KIND_DECLARATION: the patterns an app that uses a trigger kind declares in hfs.json sides.be.patterns; the platform capabilities of those
|
|
284
|
+
# patterns (the slots of tier platform that name the pattern) must exist.
|
|
285
|
+
kindPatterns:
|
|
286
|
+
api: []
|
|
287
|
+
webhooks: []
|
|
288
|
+
realtime: []
|
|
289
|
+
saga: [saga]
|
|
290
|
+
reactors: [event-bus]
|
|
291
|
+
jobs: [fenced-job, queue]
|
|
292
|
+
cli: []
|
|
293
|
+
# starci app add <noun> <name>: the pattern topic (knowledge/patterns/be/<topic>.yaml) whose files tree is generated, the path variable of the
|
|
294
|
+
# instance, what it registers in hfs.json, the options it needs (--<option>), the other nouns generated with it under the same name (a job is
|
|
295
|
+
# the consumer of its queue, so `also: [queue]`) and the defaults of the other variables ("@name" = the name).
|
|
296
|
+
addKinds:
|
|
297
|
+
job: {topic: jobs, variable: job, trigger: jobs, patterns: [fenced-job, queue], also: [queue], defaults: {step: "@name"}}
|
|
298
|
+
reactor: {topic: reactors, variable: reactor, trigger: reactors, patterns: [event-bus], needs: [event, from, service], defaults: {action: "@name"}}
|
|
299
|
+
queue: {topic: queues, variable: queue, patterns: [queue]}
|
|
300
|
+
webhook: {topic: webhooks, variable: provider, trigger: webhooks, patterns: [webhooks], needs: [service], defaults: {event: "@name"}}
|
|
301
|
+
realtime: {topic: realtime, variable: channel, trigger: realtime, patterns: [realtime], needs: [service]}
|
|
302
|
+
saga: {topic: saga, variable: saga, trigger: saga, patterns: [saga], needs: [owner, from, failed, done, service], defaults: {step: "@name"}}
|
|
303
|
+
projection: {topic: projections, variable: name, patterns: [projection], needs: [connection]}
|
|
304
|
+
api: {topic: api, variable: feature, trigger: api, patterns: [], needs: [service], defaults: {action: "@name"}}
|
|
305
|
+
cli: {topic: cli, variable: group, trigger: cli, patterns: [], needs: [service], wire: {file: src/features/cli/cli.module.ts, import: "./@@group@@/@@group@@.module", symbol: "@@Group@@Module"}}
|
|
306
|
+
bannedSuffixes: [use-case, repository, fixture, factory, store, worker, scheduler, cron, job, listener, dto, util, utils, helper, helpers, rules, types, constants, int-spec, harness-spec, test]
|
|
307
|
+
# Edition lite (ruleParams(manifest, 'be', 'lite') merges these keys over the base): the schema authority is the Supabase
|
|
308
|
+
# migration tree (supabase/migrations/*.sql, forward-only), so a lite back end holds no TypeORM migration or entity under
|
|
309
|
+
# be/ (R34/R35 read schemaAuthority; be.persistence's lite overlay allows neither).
|
|
310
|
+
lite: {schemaAuthority: supabase, unitRoles: []} # lite has no test world: no service or cli command owes a unit spec
|
|
167
311
|
|
|
168
312
|
slots:
|
|
169
313
|
|
|
@@ -186,14 +330,28 @@ slots:
|
|
|
186
330
|
rules: [HFS_README_*]
|
|
187
331
|
- id: app.package-manifest
|
|
188
332
|
profiles: [app]
|
|
189
|
-
# The one package.json of the app:
|
|
333
|
+
# The one package.json of the app, at the app root: be's dependencies and every tool, and every script; npm workspaces are
|
|
334
|
+
# exactly fe/apps/* and fe/packages/* (HFS_MONO_WORKSPACES), each with its own package.json. be has no package.json of its own.
|
|
190
335
|
path: package.json
|
|
191
336
|
presence: required
|
|
192
337
|
tracked: tracked
|
|
193
338
|
tier: none
|
|
194
339
|
tests: none
|
|
195
340
|
managedBy: package-scripts # the `scripts` block only; the rest of the file is the repository's
|
|
196
|
-
|
|
341
|
+
liteManagedBy: package-scripts-lite
|
|
342
|
+
rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW, HFS_CANON_PIN_DRIFT, HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES, HFS_MONO_WORKSPACE_DEP]
|
|
343
|
+
- id: app.task-graph
|
|
344
|
+
profiles: [app]
|
|
345
|
+
# The turbo task graph of the app (build, dev, lint, typecheck over the fe workspaces; build depends on ^build and caches
|
|
346
|
+
# .next and dist): rendered by starci app sync from templates/app/task-graph/turbo.json, judged for drift by starci app check. turbo needs
|
|
347
|
+
# it beside the root package.json and lockfile.
|
|
348
|
+
path: turbo.json
|
|
349
|
+
presence: required
|
|
350
|
+
tracked: tracked
|
|
351
|
+
tier: none
|
|
352
|
+
tests: none
|
|
353
|
+
managedBy: task-graph
|
|
354
|
+
rules: [HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES]
|
|
197
355
|
- id: app.lockfile
|
|
198
356
|
profiles: [app]
|
|
199
357
|
path: package-lock.json
|
|
@@ -209,7 +367,7 @@ slots:
|
|
|
209
367
|
tracked: tracked
|
|
210
368
|
tier: none
|
|
211
369
|
tests: none
|
|
212
|
-
rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by
|
|
370
|
+
rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by starci app sync (templates/app/gitignore)
|
|
213
371
|
- id: app.tool-config
|
|
214
372
|
profiles: [app]
|
|
215
373
|
path: "{.editorconfig,.nvmrc}"
|
|
@@ -227,6 +385,7 @@ slots:
|
|
|
227
385
|
tier: none
|
|
228
386
|
tests: none
|
|
229
387
|
managedBy: tool-config
|
|
388
|
+
liteManagedBy: tool-config-lite # the same files plus the generated supabase/types, which the Supabase CLI writes unformatted
|
|
230
389
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
|
|
231
390
|
- id: app.tool-config-optional
|
|
232
391
|
profiles: [app]
|
|
@@ -251,7 +410,9 @@ slots:
|
|
|
251
410
|
tracked: tracked
|
|
252
411
|
tier: none
|
|
253
412
|
tests: none
|
|
413
|
+
lite: {path: sonar-project.properties}
|
|
254
414
|
managedBy: quality-config
|
|
415
|
+
liteManagedBy: quality-config-lite
|
|
255
416
|
rules: [HFS_SONAR_CONFIG, HFS_MANAGED_FILE_DRIFT]
|
|
256
417
|
- id: app.hooks
|
|
257
418
|
profiles: [app]
|
|
@@ -270,16 +431,63 @@ slots:
|
|
|
270
431
|
tier: none
|
|
271
432
|
tests: none
|
|
272
433
|
managedBy: ci-workflows
|
|
434
|
+
liteManagedBy: ci-workflows-lite
|
|
273
435
|
rules: [HFS_CI_E2E_AUTOMATIC, HFS_CI_MISSING_CANON, HFS_MANAGED_FILE_DRIFT]
|
|
274
436
|
- id: app.ci-e2e
|
|
275
437
|
profiles: [app]
|
|
276
438
|
path: ".github/workflows/e2e.yml"
|
|
277
439
|
presence: optional # on: workflow_dispatch only
|
|
440
|
+
litePresence: forbidden # a lite app has no e2e run
|
|
278
441
|
tracked: tracked
|
|
279
442
|
tier: none
|
|
280
443
|
tests: none
|
|
444
|
+
goesTo: "the full edition: a lite app has no e2e workflow (no tests, nothing to run)"
|
|
281
445
|
managedBy: ci-workflows
|
|
282
446
|
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
447
|
+
- id: app.ci-images
|
|
448
|
+
profiles: [app]
|
|
449
|
+
# Builds every image of the app (one matrix entry per declared app, path-filtered), never pushes: rendered by starci app sync.
|
|
450
|
+
path: ".github/workflows/images.yml"
|
|
451
|
+
presence: required
|
|
452
|
+
tracked: tracked
|
|
453
|
+
tier: none
|
|
454
|
+
tests: none
|
|
455
|
+
managedBy: ci-workflows
|
|
456
|
+
liteManagedBy: ci-workflows-lite
|
|
457
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
458
|
+
- id: app.ci-db
|
|
459
|
+
profiles: [app]
|
|
460
|
+
# The database deploy workflow: workflow_dispatch only (like app.ci-e2e); starci app sync renders it under the managed ci group.
|
|
461
|
+
path: ".github/workflows/db-deploy.yml"
|
|
462
|
+
presence: optional
|
|
463
|
+
tracked: tracked
|
|
464
|
+
tier: none
|
|
465
|
+
tests: none
|
|
466
|
+
liteManagedBy: ci-workflows-lite
|
|
467
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
468
|
+
since: 2.1.0
|
|
469
|
+
- id: app.dockerignore
|
|
470
|
+
profiles: [app]
|
|
471
|
+
# The one build-context filter of every image (the context is the app root): rendered by starci app sync; it keeps secrets and build output out.
|
|
472
|
+
path: .dockerignore
|
|
473
|
+
presence: required
|
|
474
|
+
tracked: tracked
|
|
475
|
+
tier: none
|
|
476
|
+
tests: none
|
|
477
|
+
managedBy: docker-ignore
|
|
478
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
479
|
+
- id: app.browser
|
|
480
|
+
profiles: [app]
|
|
481
|
+
# The browser journey of the product (Playwright, owner ruling: one minimal journey per example app, run manually): the config, its
|
|
482
|
+
# journeys and the script that provisions the stack it runs against. It is the product's, never the front end's (FE_NO_TESTS keeps
|
|
483
|
+
# refusing any test or test tooling inside fe/). Declared by `browser: true` in hfs.json; the managed script `test:browser` exists only then.
|
|
484
|
+
path: "browser/"
|
|
485
|
+
presence: opt-in
|
|
486
|
+
tracked: tracked
|
|
487
|
+
tier: none
|
|
488
|
+
tests: none
|
|
489
|
+
requires: [playwright.config.ts, journeys/]
|
|
490
|
+
allows: [playwright.config.ts, stack.mjs, "journeys/*.ts"]
|
|
283
491
|
- id: app.github-meta
|
|
284
492
|
profiles: [app]
|
|
285
493
|
path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE/**,dependabot.yml}"
|
|
@@ -290,13 +498,16 @@ slots:
|
|
|
290
498
|
- id: app.scripts
|
|
291
499
|
profiles: [app]
|
|
292
500
|
# The app's operational scripts only (`*.mjs`, `*.cjs`, `*.ps1`, `*.sh`), or nothing: an empty folder is `scripts/.gitkeep`, which
|
|
293
|
-
# `
|
|
501
|
+
# `starci app scaffold` writes. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`,
|
|
294
502
|
# a local eslint plugin: HFS_REPO_LOCAL_CHECK) and anything that re-implements a canon check is not an operational script and has no place here.
|
|
295
503
|
path: "scripts/{.gitkeep,*.mjs,*.cjs,*.ps1,*.sh}"
|
|
296
504
|
presence: optional
|
|
297
505
|
tracked: tracked
|
|
298
506
|
tier: none
|
|
299
507
|
tests: none
|
|
508
|
+
lite:
|
|
509
|
+
path: scripts/codegen.mjs
|
|
510
|
+
liteManagedBy: codegen-lite
|
|
300
511
|
rules: [HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK] # one-off codemods and fix-* scripts are agent output, not tooling
|
|
301
512
|
- id: app.sides
|
|
302
513
|
profiles: [app]
|
|
@@ -314,13 +525,29 @@ slots:
|
|
|
314
525
|
tracked: tracked
|
|
315
526
|
tier: none
|
|
316
527
|
tests: none
|
|
317
|
-
requires: [.gitignore, workspace.yaml,
|
|
318
|
-
allows: [workspace.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
528
|
+
requires: [.gitignore, workspace.yaml, index.yaml]
|
|
529
|
+
allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
319
530
|
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
320
531
|
"features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
|
|
321
532
|
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"]
|
|
322
533
|
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived/, "work/node records"]
|
|
534
|
+
# Lite keeps the product catalog, feature overviews, grammar and brand drawing records; UAT records belong to the forbidden slot below.
|
|
535
|
+
lite:
|
|
536
|
+
requires: [.gitignore, workspace.yaml, index.yaml]
|
|
537
|
+
allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml", "features/<feature>/ui/<name>/index.yaml", "features/<feature>/ui/<name>/assets/<approved-file>"]
|
|
323
538
|
rules: [HFS_AGENT_DATA_TRACKED, HFS_WORK_NODE_RETIRED, HFS_IDENTITY_CUSTODY]
|
|
539
|
+
- id: app.starciwork.uat
|
|
540
|
+
profiles: [app]
|
|
541
|
+
# The UAT records of the full edition: a lite app has no UAT (no tests, no record of one), so the trees are forbidden paths
|
|
542
|
+
# under lite only. In full this slot does not exist and .starciwork answers for them.
|
|
543
|
+
editions: [lite]
|
|
544
|
+
path: ".starciwork/{_resources/identities,_resources/fixtures,features/<feature>/uat,features/<feature>/br}/"
|
|
545
|
+
presence: forbidden
|
|
546
|
+
tracked: external
|
|
547
|
+
tier: none
|
|
548
|
+
tests: none
|
|
549
|
+
goesTo: "the full edition: a lite app has no UAT records (no identities or fixtures resources, no br/ac evidence, no uat directories)"
|
|
550
|
+
since: 2.1.0
|
|
324
551
|
- id: app.starcistacks
|
|
325
552
|
profiles: [app]
|
|
326
553
|
# The stack declarations and the sealed custody of the app, at the app root beside be/, fe/ and .starciwork (never under a side).
|
|
@@ -331,8 +558,14 @@ slots:
|
|
|
331
558
|
tests: none
|
|
332
559
|
requires: [application-stacks.yaml]
|
|
333
560
|
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,k8s,terraform}/**", "<env>/infra/metadata.json",
|
|
334
|
-
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/seeds/**"]
|
|
561
|
+
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
335
562
|
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
563
|
+
# Lite runs one managed host: the reduced shape keeps the stack declaration, the environment descriptor and the sealed
|
|
564
|
+
# secrets; infra/, runtime/ and seeds/ are the full edition's.
|
|
565
|
+
lite:
|
|
566
|
+
requires: []
|
|
567
|
+
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep"]
|
|
568
|
+
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
336
569
|
rules: [HFS_STACKS_SHAPE, HFS_STACKS_IN_SIDE, HFS_PLAINTEXT_SECRET, HFS_IDENTITY_CUSTODY]
|
|
337
570
|
- id: app.sops
|
|
338
571
|
profiles: [app]
|
|
@@ -359,7 +592,7 @@ slots:
|
|
|
359
592
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
360
593
|
- id: app.agent-output
|
|
361
594
|
profiles: [app]
|
|
362
|
-
path: "{report*.json,*.tmp.json,%*%,nul,.
|
|
595
|
+
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
363
596
|
presence: forbidden
|
|
364
597
|
tracked: external
|
|
365
598
|
tier: none
|
|
@@ -373,7 +606,7 @@ slots:
|
|
|
373
606
|
tracked: external
|
|
374
607
|
tier: none
|
|
375
608
|
tests: none
|
|
376
|
-
goesTo: "
|
|
609
|
+
goesTo: "<lanes root>/<project>/<lane>/ (outside every repository) for a lane; a Kernel workflow's worktree is created and owned by Orca outside the app checkout, never tracked, and removed by the runtime's host-side controller once the workflow's finish marked it release-pending"
|
|
377
610
|
- id: app.plaintext-env
|
|
378
611
|
profiles: [app]
|
|
379
612
|
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
@@ -384,10 +617,92 @@ slots:
|
|
|
384
617
|
goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
385
618
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
386
619
|
|
|
620
|
+
# ----- app root: the Supabase project of an app whose connection declares provider: supabase (every lite app) -------
|
|
621
|
+
- id: app.supabase
|
|
622
|
+
profiles: [app]
|
|
623
|
+
# The one Supabase project directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
|
|
624
|
+
path: "supabase/"
|
|
625
|
+
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
626
|
+
provider: supabase
|
|
627
|
+
litePresence: required
|
|
628
|
+
tracked: tracked
|
|
629
|
+
tier: none
|
|
630
|
+
tests: none
|
|
631
|
+
since: 2.1.0
|
|
632
|
+
- id: app.supabase.config
|
|
633
|
+
profiles: [app]
|
|
634
|
+
# The project configuration: auth keys are env(NAME) references, never literals (L08 judges its policy keys).
|
|
635
|
+
path: "supabase/config.toml"
|
|
636
|
+
presence: opt-in
|
|
637
|
+
provider: supabase
|
|
638
|
+
litePresence: required
|
|
639
|
+
tracked: tracked
|
|
640
|
+
tier: none
|
|
641
|
+
tests: none
|
|
642
|
+
since: 2.1.0
|
|
643
|
+
- id: app.supabase.migrations
|
|
644
|
+
profiles: [app]
|
|
645
|
+
# The schema authority of an app on Supabase: forward-only SQL migrations named <timestamp14>_<kebab-name>.sql,
|
|
646
|
+
# one file per change, immutable once on origin/main (L02-L08 judge them).
|
|
647
|
+
path: "supabase/migrations/<stamp>_<name>.sql"
|
|
648
|
+
presence: opt-in
|
|
649
|
+
provider: supabase
|
|
650
|
+
litePresence: required
|
|
651
|
+
lite: {minInstances: 1}
|
|
652
|
+
tracked: tracked
|
|
653
|
+
tier: none
|
|
654
|
+
tests: none
|
|
655
|
+
since: 2.1.0
|
|
656
|
+
- id: app.supabase.seed
|
|
657
|
+
profiles: [app]
|
|
658
|
+
path: "supabase/seed.sql"
|
|
659
|
+
presence: optional # synthetic dev data only; a lite app seeds nothing it cannot regenerate
|
|
660
|
+
tracked: tracked
|
|
661
|
+
tier: none
|
|
662
|
+
tests: none
|
|
663
|
+
since: 2.1.0
|
|
664
|
+
- id: app.supabase.types
|
|
665
|
+
profiles: [app]
|
|
666
|
+
# The generated Database types (database.types.ts) contract:emit writes from the migrations; both sides read it
|
|
667
|
+
# (sides.<side>.reads supabase/types/); drift against regeneration is L09.
|
|
668
|
+
path: "supabase/types/database.types.ts"
|
|
669
|
+
presence: opt-in
|
|
670
|
+
provider: supabase
|
|
671
|
+
litePresence: required
|
|
672
|
+
tracked: tracked
|
|
673
|
+
tier: none
|
|
674
|
+
tests: none
|
|
675
|
+
since: 2.1.0
|
|
676
|
+
- id: app.supabase.templates
|
|
677
|
+
profiles: [app]
|
|
678
|
+
path: "supabase/templates/*.html"
|
|
679
|
+
presence: optional # the auth e-mail templates Supabase serves
|
|
680
|
+
tracked: tracked
|
|
681
|
+
tier: none
|
|
682
|
+
tests: none
|
|
683
|
+
since: 2.1.0
|
|
684
|
+
- id: app.supabase.local-state
|
|
685
|
+
profiles: [app]
|
|
686
|
+
path: "supabase/{.temp,.branches}/"
|
|
687
|
+
presence: optional
|
|
688
|
+
tracked: ignored # the local CLI's working state: exists, never committed
|
|
689
|
+
tier: none
|
|
690
|
+
tests: none
|
|
691
|
+
since: 2.1.0
|
|
692
|
+
- id: app.supabase.functions
|
|
693
|
+
profiles: [app]
|
|
694
|
+
path: "supabase/functions/"
|
|
695
|
+
presence: forbidden # a Deno runtime is a second language runtime outside the lint canon and the slot machine (lite v1)
|
|
696
|
+
tracked: external
|
|
697
|
+
tier: none
|
|
698
|
+
tests: none
|
|
699
|
+
goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a cli command on a schedule (pg_cron / pg_net)"
|
|
700
|
+
since: 2.1.0
|
|
701
|
+
|
|
387
702
|
# ----- side root (both sides): what the old standalone repository root held, less the app-root files ----------
|
|
388
703
|
- id: repo.side-root-forbidden
|
|
389
704
|
profiles: [be, fe]
|
|
390
|
-
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
|
|
705
|
+
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,supabase/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
|
|
391
706
|
presence: forbidden
|
|
392
707
|
tracked: external
|
|
393
708
|
tier: none
|
|
@@ -395,7 +710,7 @@ slots:
|
|
|
395
710
|
goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar and Codecov configuration, scripts/, .starciwork, .starcistacks and .sops.yaml per app"
|
|
396
711
|
- id: be.tool-config
|
|
397
712
|
profiles: [be]
|
|
398
|
-
# Managed files (
|
|
713
|
+
# Managed files (starci app sync renders them, starci app check compares them): the whole tool configuration of a back end. Each is
|
|
399
714
|
# a reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/be.json and adds the
|
|
400
715
|
# three path aliases and excludes the world, integration, e2e and contract trees (fixtures stay in the root program: unit
|
|
401
716
|
# specs import them); tsconfig.build.json puts an overlay preset after it and adds only the path-relative options a
|
|
@@ -409,15 +724,21 @@ slots:
|
|
|
409
724
|
tracked: tracked
|
|
410
725
|
tier: none
|
|
411
726
|
tests: none
|
|
727
|
+
# Lite: no test tsconfig (no test tree) and no jest configuration; both are forbidden paths there (repo.tests-forbidden).
|
|
728
|
+
lite: {path: "{tsconfig.json,tsconfig.build.json,eslint.config.mjs}"}
|
|
729
|
+
coverage: none
|
|
412
730
|
managedBy: tool-config
|
|
413
731
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_TS_STRICT]
|
|
414
732
|
- id: be.nest-cli
|
|
415
733
|
profiles: [be]
|
|
734
|
+
# The Nest monorepo of the back end: `monorepo: true` and one project per declared be app (apps/<app>, even for one service).
|
|
416
735
|
path: nest-cli.json
|
|
417
736
|
presence: required
|
|
418
737
|
tracked: tracked
|
|
419
738
|
tier: none
|
|
420
739
|
tests: none
|
|
740
|
+
coverage: none
|
|
741
|
+
rules: [HFS_MONO_NEST_PROJECTS]
|
|
421
742
|
- id: be.tool-config-local
|
|
422
743
|
profiles: [be]
|
|
423
744
|
path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.prettierrc.*,prettier.config.*,jest.config.ts,jest.config.mjs,jest.config.cjs,jest.config.json,jest.config.e2e.js}"
|
|
@@ -429,7 +750,7 @@ slots:
|
|
|
429
750
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
430
751
|
- id: fe.tool-config
|
|
431
752
|
profiles: [fe]
|
|
432
|
-
# Managed files (
|
|
753
|
+
# Managed files (starci app sync renders them, starci app check compares them): the tool configuration of a front end. Each is a
|
|
433
754
|
# reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/next.json and only
|
|
434
755
|
# names the preset and nothing else (each app's own tsconfig.json, in fe.app.next, carries its aliases and include; there is
|
|
435
756
|
# no test tsconfig: a front end has no tests, FE_NO_TESTS); eslint.config.mjs is the one-line starciFeConfig call;
|
|
@@ -442,22 +763,14 @@ slots:
|
|
|
442
763
|
tests: none
|
|
443
764
|
managedBy: tool-config
|
|
444
765
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_RULE_OFF_WITHOUT_REPLACEMENT, HFS_TS_STRICT]
|
|
445
|
-
- id: fe.tool-config-repo
|
|
446
|
-
profiles: [fe]
|
|
447
|
-
# The one tool file a preset cannot render because it carries the repository's own facts: turbo.json (its task graph).
|
|
448
|
-
path: turbo.json
|
|
449
|
-
presence: optional
|
|
450
|
-
tracked: tracked
|
|
451
|
-
tier: none
|
|
452
|
-
tests: none
|
|
453
766
|
- id: fe.tool-config-local
|
|
454
767
|
profiles: [fe]
|
|
455
|
-
path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.js,stylelint.config.cjs,stylelint.config.ts,stylelint.config.json,prettier.config.*,.prettierrc.*,lint-staged.config.*,.lintstagedrc
|
|
768
|
+
path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.js,stylelint.config.cjs,stylelint.config.ts,stylelint.config.json,prettier.config.*,.prettierrc.*,lint-staged.config.*,.lintstagedrc*,turbo.json}"
|
|
456
769
|
presence: forbidden
|
|
457
770
|
tracked: external
|
|
458
771
|
tier: none
|
|
459
772
|
tests: none
|
|
460
|
-
goesTo: "nowhere: a front end has exactly the managed tool configuration (fe.tool-config); a change to a rule or a flag is proposed in the .claude runtime"
|
|
773
|
+
goesTo: "nowhere: a front end has exactly the managed tool configuration (fe.tool-config); the task graph is the app root's turbo.json (app.task-graph); a change to a rule or a flag is proposed in the .claude runtime"
|
|
461
774
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
462
775
|
- id: repo.docs
|
|
463
776
|
profiles: [be, fe]
|
|
@@ -466,6 +779,7 @@ slots:
|
|
|
466
779
|
tracked: tracked
|
|
467
780
|
tier: none
|
|
468
781
|
tests: none
|
|
782
|
+
coverage: none
|
|
469
783
|
rules: [HFS_ROOT_MARKDOWN_FORBIDDEN]
|
|
470
784
|
- id: repo.docs-media
|
|
471
785
|
profiles: [be, fe]
|
|
@@ -474,6 +788,7 @@ slots:
|
|
|
474
788
|
tracked: tracked
|
|
475
789
|
tier: none
|
|
476
790
|
tests: none
|
|
791
|
+
coverage: none
|
|
477
792
|
budget: {bytes: 512000}
|
|
478
793
|
- id: repo.packages
|
|
479
794
|
profiles: [be, fe]
|
|
@@ -484,21 +799,21 @@ slots:
|
|
|
484
799
|
owner: true
|
|
485
800
|
requires: [package.json, src/index.ts, tsconfig.json]
|
|
486
801
|
tests: unit-beside
|
|
802
|
+
coverage: none
|
|
487
803
|
rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
488
804
|
- id: repo.app-image
|
|
489
805
|
profiles: [be, fe]
|
|
806
|
+
# One image per app: every declared app has its own Dockerfile (apps/<app>/Dockerfile), built from the APP ROOT (the folder with
|
|
807
|
+
# the one package.json). It is app-owned (starci app scaffold writes it once from templates/<side>/image) and judged by the docker rules.
|
|
808
|
+
# A Dockerfile anywhere else is owned by no slot.
|
|
490
809
|
path: "apps/<app>/Dockerfile"
|
|
491
|
-
presence:
|
|
492
|
-
|
|
493
|
-
tier: none
|
|
494
|
-
tests: none
|
|
495
|
-
- id: repo.dockerignore
|
|
496
|
-
profiles: [be, fe]
|
|
497
|
-
path: .dockerignore
|
|
498
|
-
presence: optional
|
|
810
|
+
presence: required
|
|
811
|
+
litePresence: {fe: optional} # lite: a Next app may be built by its host (Netlify, Vercel); a be image stays required
|
|
499
812
|
tracked: tracked
|
|
500
813
|
tier: none
|
|
501
814
|
tests: none
|
|
815
|
+
coverage: none
|
|
816
|
+
rules: [HFS_DOCKER_BUILD_CONTEXT, HFS_DOCKER_STAGES, HFS_DOCKER_ENTRY, HFS_DOCKER_BASE_PIN, HFS_DOCKER_SECRETS]
|
|
502
817
|
|
|
503
818
|
# ----- never tracked: build output the tools need in place ---------------------------------------------
|
|
504
819
|
- id: repo.build-output
|
|
@@ -528,7 +843,7 @@ slots:
|
|
|
528
843
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
529
844
|
- id: repo.agent-output
|
|
530
845
|
profiles: [be, fe]
|
|
531
|
-
path: "{report*.json,*.tmp.json,%*%,nul,.
|
|
846
|
+
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
532
847
|
presence: forbidden
|
|
533
848
|
tracked: external
|
|
534
849
|
tier: none
|
|
@@ -542,7 +857,7 @@ slots:
|
|
|
542
857
|
tracked: external
|
|
543
858
|
tier: none
|
|
544
859
|
tests: none
|
|
545
|
-
goesTo: "
|
|
860
|
+
goesTo: "<lanes root>/<project>/<lane>/ (outside every repository)"
|
|
546
861
|
- id: repo.plaintext-env
|
|
547
862
|
profiles: [be, fe]
|
|
548
863
|
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
@@ -552,6 +867,21 @@ slots:
|
|
|
552
867
|
tests: none
|
|
553
868
|
goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
554
869
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
870
|
+
- id: repo.tests-forbidden
|
|
871
|
+
profiles: [be, fe]
|
|
872
|
+
# Edition lite has no test world, on either side: a spec file, a test directory or a test-tool configuration anywhere in
|
|
873
|
+
# a side is a forbidden path (a finding, not an ignored file). In full this slot does not exist and each test file is
|
|
874
|
+
# judged by its own slot (the be tests tree, FE_NO_TESTS on the fe side).
|
|
875
|
+
editions: [lite]
|
|
876
|
+
# No nested braces: braceVariants expands each inner group per outer option. `**/e2e/` also matches a root e2e/ but
|
|
877
|
+
# be.root-e2e wins that on specificity; both are forbidden.
|
|
878
|
+
path: "{**/*.spec.*,**/*.test.*,**/*-spec.*,src/tests/,**/__tests__/,**/e2e/,**/test-support/,**/__mocks__/,**/jest.*,**/vitest.*,**/playwright.*,**/cypress.config.*,**/tsconfig.e2e.json,**/e2e.yml,**/e2e.yaml}"
|
|
879
|
+
presence: forbidden
|
|
880
|
+
tracked: external
|
|
881
|
+
tier: none
|
|
882
|
+
tests: none
|
|
883
|
+
goesTo: "the full edition: a lite repository holds no spec, test directory or jest/vitest/playwright configuration"
|
|
884
|
+
since: 2.1.0
|
|
555
885
|
- id: be.root-e2e
|
|
556
886
|
profiles: [be]
|
|
557
887
|
path: "e2e/"
|
|
@@ -569,7 +899,8 @@ slots:
|
|
|
569
899
|
tracked: tracked
|
|
570
900
|
tier: none
|
|
571
901
|
tests: none
|
|
572
|
-
|
|
902
|
+
coverage: none
|
|
903
|
+
why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand); CI fails when the emitted file differs (a contract, not a build artefact)
|
|
573
904
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
574
905
|
- id: be.contract.openapi
|
|
575
906
|
profiles: [be]
|
|
@@ -578,8 +909,33 @@ slots:
|
|
|
578
909
|
tracked: tracked
|
|
579
910
|
tier: none
|
|
580
911
|
tests: none
|
|
581
|
-
|
|
912
|
+
coverage: none
|
|
913
|
+
why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the app's typed operation table `apps/<app>/src/operations.ts` (canon BE-OPERATIONS-1); CI fails when the emitted file differs
|
|
582
914
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
915
|
+
- id: be.contract.events
|
|
916
|
+
profiles: [be]
|
|
917
|
+
path: "contracts/<app>/events.json"
|
|
918
|
+
presence: opt-in
|
|
919
|
+
litePresence: forbidden # lite has no event bus, so no async contract to emit
|
|
920
|
+
tracked: tracked
|
|
921
|
+
tier: none
|
|
922
|
+
tests: none
|
|
923
|
+
coverage: none
|
|
924
|
+
why: the async contract of a service (`starci/event-contract@1`), emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the typed event classes of `src/modules/events/<service>/`; the consumers of its events rely on it (R166 HFS_EVENT_CONTRACT)
|
|
925
|
+
goesTo: "the full edition: a lite app has no event bus (no Kafka, no outbox), so no async contract exists to emit"
|
|
926
|
+
rules: [HFS_EVENT_CONTRACT]
|
|
927
|
+
- id: be.contract.events.pin
|
|
928
|
+
profiles: [be]
|
|
929
|
+
path: "contracts/<app>/events.pin.json"
|
|
930
|
+
presence: opt-in
|
|
931
|
+
litePresence: forbidden
|
|
932
|
+
tracked: tracked
|
|
933
|
+
tier: none
|
|
934
|
+
tests: none
|
|
935
|
+
coverage: none
|
|
936
|
+
why: the previously published event contract of a service, the same format as its events.json (copied over when the contract is published, never edited); the current events.json may only extend it additively, a breaking change is a new `<event>.v2` key (R177 BE_CONTRACT_BREAKING)
|
|
937
|
+
goesTo: "the full edition: a lite app has no event bus, so no published event contract to pin"
|
|
938
|
+
rules: [BE_CONTRACT_BREAKING]
|
|
583
939
|
|
|
584
940
|
# ----- BE: apps (one slot per app kind; a new kind = a new slot, minor bump) ---------------------------
|
|
585
941
|
- id: be.app.api
|
|
@@ -594,50 +950,53 @@ slots:
|
|
|
594
950
|
requires: [main.ts, app.module.ts]
|
|
595
951
|
allows: [main.ts, app.module.ts, "<app>.options.ts", operations.ts]
|
|
596
952
|
tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
|
|
953
|
+
coverage: none
|
|
597
954
|
budget: {app.module.ts: 250, main.ts: 80}
|
|
598
|
-
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED]
|
|
955
|
+
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED, BE_CONTEXT_OWNER]
|
|
599
956
|
- id: be.app.worker
|
|
600
957
|
profiles: [be]
|
|
601
958
|
path: "apps/<app>/src/"
|
|
602
|
-
appKind: worker # composes transport/
|
|
959
|
+
appKind: worker # composes transport/message modules and job processors only (an api app may compose them too: a worker app is for separate scaling)
|
|
603
960
|
presence: opt-in
|
|
961
|
+
litePresence: forbidden # lite has no queue or event bus to consume; background work is a cli command on a schedule
|
|
604
962
|
tracked: tracked
|
|
605
963
|
tier: app
|
|
606
964
|
owner: true
|
|
607
965
|
requires: [main.ts, app.module.ts]
|
|
608
966
|
allows: [main.ts, app.module.ts, "<app>.options.ts"]
|
|
609
967
|
tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
path: "apps/<app>/src/"
|
|
614
|
-
appKind: migrate # the ONLY process that runs migrations (one run per connection, before api/worker start)
|
|
615
|
-
presence: required
|
|
616
|
-
requiredWhen: connections # required as soon as hfs.json declares a connection
|
|
617
|
-
tracked: tracked
|
|
618
|
-
tier: app
|
|
619
|
-
owner: true
|
|
620
|
-
requires: [main.ts]
|
|
621
|
-
allows: [main.ts, "<app>.options.ts"]
|
|
622
|
-
tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
|
|
623
|
-
rules: [BE_SCHEMA_AUTHORITY, BE_ENTRYPOINT_ONLY_IN_APPS]
|
|
968
|
+
goesTo: "the full edition: a lite app has no worker (scheduled work is a cli command invoked by pg_cron / pg_net or a platform cron)"
|
|
969
|
+
coverage: none
|
|
970
|
+
rules: [BE_APP_COMPOSITION_ONLY, BE_BACKGROUND_UNOWNED, BE_CONTEXT_OWNER]
|
|
624
971
|
- id: be.app.cli
|
|
625
972
|
profiles: [be]
|
|
973
|
+
# The ONE app of every one-off action (migrate, seed, sync, backup, any operator command): be/apps/cli, on nest-commander.
|
|
974
|
+
# main.ts boots with CommandFactory.run (BE_CLI_BOOTSTRAP); app.module.ts composes the platform and the cli feature root
|
|
975
|
+
# (src/features/cli/cli.module.ts); cli.options.ts carries what it needs from the environment, the connections it migrates
|
|
976
|
+
# among them (the test world migrates through the same list). It is required as soon as the back end declares a connection
|
|
977
|
+
# or tracks a command, with its image apps/cli/Dockerfile (BE_CLI_REQUIRED): one image, run as `cli <group> <command>`.
|
|
626
978
|
path: "apps/<app>/src/"
|
|
627
979
|
appKind: cli
|
|
628
980
|
presence: opt-in
|
|
981
|
+
litePresence: optional # lite: schema migration is the Supabase CLI's job; a cli app exists only when the app declares one
|
|
629
982
|
tracked: tracked
|
|
630
983
|
tier: app
|
|
631
984
|
owner: true
|
|
632
985
|
requires: [main.ts, app.module.ts]
|
|
633
986
|
allows: [main.ts, app.module.ts, "<app>.options.ts"]
|
|
634
987
|
tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
|
|
988
|
+
coverage: none
|
|
989
|
+
rules: [BE_CLI_REQUIRED, BE_CLI_BOOTSTRAP, BE_CLI_OWNER, BE_SCHEMA_AUTHORITY]
|
|
635
990
|
|
|
636
991
|
# ----- BE: features ------------------------------------------------------------------------------------
|
|
992
|
+
# src/features/ is grouped by TRIGGER KIND (owner decision 2026-10-02): features/api/<feature>/ (a request: application/ and
|
|
993
|
+
# transport/<protocol>/, composed by the api apps), features/cli/ (a one-off action: the cli feature root, compiled only into
|
|
994
|
+
# apps/cli). Every kind imports only downward into modules/*; no kind imports another.
|
|
637
995
|
- id: be.feature
|
|
638
996
|
profiles: [be]
|
|
639
|
-
path: "src/features/<feature>/"
|
|
997
|
+
path: "src/features/api/<feature>/"
|
|
640
998
|
presence: required
|
|
999
|
+
trigger: api # a request-driven feature (graphql, http); the other trigger kinds have their own roots below
|
|
641
1000
|
tracked: tracked
|
|
642
1001
|
tier: feature
|
|
643
1002
|
owner: true
|
|
@@ -645,96 +1004,250 @@ slots:
|
|
|
645
1004
|
requires: [index.ts, "<feature>.module.ts", application/]
|
|
646
1005
|
allows: [index.ts, "<feature>.module.ts", application/, transport/, messages/] # nothing else at feature root
|
|
647
1006
|
tests: none
|
|
1007
|
+
coverage: none
|
|
648
1008
|
budget: {files: 250, indexExports: 60} # a feature above 250 source files must be split by product capability
|
|
649
1009
|
rules: [BE_FEATURE_SHAPE, BE_FEATURE_IMPORTS_FEATURE, BE_PUBLIC_SURFACE, BE_FEATURE_NOT_COMPOSED]
|
|
650
1010
|
- id: be.feature.application
|
|
651
1011
|
profiles: [be]
|
|
652
|
-
path: "src/features
|
|
1012
|
+
path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/" # inside a trigger kind (jobs, reactors, saga) the feature folder is one level down
|
|
1013
|
+
lite: {path: "src/features/api/<feature>/application/"} # lite has only the api kind; the other roots are forbidden slots
|
|
653
1014
|
presence: required
|
|
654
1015
|
tracked: tracked
|
|
655
1016
|
tier: feature
|
|
656
1017
|
allows: ["<action>.{command,query,handler,contracts}.ts"]
|
|
657
1018
|
forbids: [graphql/, http/, message/, schedule/, transport/] # no protocol names under application/
|
|
658
1019
|
tests: none
|
|
1020
|
+
coverage: none
|
|
659
1021
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_ENTITY_IN_CONTRACT, BE_CQRS_SHAPE]
|
|
1022
|
+
- id: be.feature.saga
|
|
1023
|
+
profiles: [be]
|
|
1024
|
+
path: "src/features/saga/<saga>/"
|
|
1025
|
+
pattern: saga # enabled by `patterns: ["saga"]` in hfs.json sides.be (R169 to R173); an undeclared saga folder is HFS_SLOT_NOT_ENABLED
|
|
1026
|
+
presence: opt-in # a saga: the orchestrator of a business flow that crosses services, started and advanced only by events (knowledge/patterns/be/saga.yaml)
|
|
1027
|
+
litePresence: forbidden
|
|
1028
|
+
trigger: saga
|
|
1029
|
+
tracked: tracked
|
|
1030
|
+
tier: feature
|
|
1031
|
+
owner: true
|
|
1032
|
+
requires: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga-state.ts", steps/, compensations/, transport/]
|
|
1033
|
+
allows: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga.service.spec.ts", "<saga>.saga-state.ts", "<saga>.saga.log-events.ts", application/, steps/, compensations/, transport/] # the orchestrator is a `*.service.ts` because a handler injects only services; unit-tested beside it
|
|
1034
|
+
tests: unit-beside # <saga>.saga.service.spec.ts beside the orchestrator; the state machine and its fence are platform/saga's service; each compensation path has an e2e spec
|
|
1035
|
+
goesTo: "the full edition: a lite app has no saga (no event bus to advance it)"
|
|
1036
|
+
coverage: none
|
|
1037
|
+
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED, BE_SAGA_STEP_COMPENSATION, BE_SAGA_STATE_VERSIONED, BE_SAGA_EVENT_CONTRACT, BE_SAGA_E2E_MISSING]
|
|
1038
|
+
- id: be.feature.saga.steps
|
|
1039
|
+
profiles: [be]
|
|
1040
|
+
path: "src/features/saga/<saga>/steps/"
|
|
1041
|
+
pattern: saga
|
|
1042
|
+
presence: opt-in
|
|
1043
|
+
litePresence: forbidden
|
|
1044
|
+
tracked: tracked
|
|
1045
|
+
tier: feature
|
|
1046
|
+
allows: ["<step>.saga-step.ts"] # one step of the flow: its name and the event it puts on the wire (the contract); its work is done by the domain, not by the step
|
|
1047
|
+
tests: none
|
|
1048
|
+
goesTo: "the full edition: a lite app has no saga"
|
|
1049
|
+
coverage: none
|
|
1050
|
+
rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT]
|
|
1051
|
+
- id: be.feature.saga.compensations
|
|
1052
|
+
profiles: [be]
|
|
1053
|
+
path: "src/features/saga/<saga>/compensations/"
|
|
1054
|
+
pattern: saga
|
|
1055
|
+
presence: opt-in
|
|
1056
|
+
litePresence: forbidden
|
|
1057
|
+
tracked: tracked
|
|
1058
|
+
tier: feature
|
|
1059
|
+
allows: ["<step>.compensation.ts"] # REQUIRED for every step: the command that undoes it, naming the failure event that triggers it
|
|
1060
|
+
tests: none
|
|
1061
|
+
goesTo: "the full edition: a lite app has no saga"
|
|
1062
|
+
coverage: none
|
|
1063
|
+
rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT, BE_SAGA_E2E_MISSING]
|
|
660
1064
|
- id: be.feature.application.support
|
|
661
1065
|
profiles: [be]
|
|
662
|
-
path: "src/features
|
|
1066
|
+
path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/support/"
|
|
1067
|
+
lite: {path: "src/features/api/<feature>/application/support/"}
|
|
663
1068
|
presence: optional # helpers and types local to ONE feature; same tier as application, never a shared home
|
|
664
1069
|
tracked: tracked
|
|
665
1070
|
tier: feature # feature tier: another feature cannot import it (features never import features)
|
|
666
1071
|
allows: ["<name>.<role>.ts"] # <role> from ruleParams.be.suffixes; no <name>.types.ts
|
|
667
1072
|
forbids: [graphql/, http/, message/, schedule/, transport/]
|
|
668
1073
|
tests: none
|
|
1074
|
+
coverage: none
|
|
669
1075
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_FEATURE_IMPORTS_FEATURE]
|
|
670
1076
|
since: 1.0.0
|
|
671
1077
|
- id: be.transport.http
|
|
672
1078
|
profiles: [be]
|
|
673
|
-
path: "src/features/<feature>/transport/http/"
|
|
1079
|
+
path: "src/features/api/<feature>/transport/http/"
|
|
674
1080
|
presence: optional
|
|
675
1081
|
tracked: tracked
|
|
676
1082
|
tier: feature
|
|
677
1083
|
requires: ["<feature>-http.module.ts"]
|
|
678
1084
|
allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts"]
|
|
679
1085
|
tests: none
|
|
1086
|
+
coverage: none
|
|
680
1087
|
composedBy: [api]
|
|
681
1088
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
682
1089
|
- id: be.transport.graphql
|
|
683
1090
|
profiles: [be]
|
|
684
|
-
path: "src/features/<feature>/transport/graphql/"
|
|
1091
|
+
path: "src/features/api/<feature>/transport/graphql/"
|
|
685
1092
|
presence: optional
|
|
686
1093
|
tracked: tracked
|
|
687
1094
|
tier: feature
|
|
688
1095
|
requires: ["<feature>-graphql.module.ts"]
|
|
689
1096
|
allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts"]
|
|
690
1097
|
tests: none
|
|
1098
|
+
coverage: none
|
|
691
1099
|
composedBy: [api]
|
|
692
1100
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
693
1101
|
- id: be.transport.message
|
|
694
1102
|
profiles: [be]
|
|
695
|
-
path: "src/features
|
|
696
|
-
presence: opt-in # queue
|
|
1103
|
+
path: "src/features/{api/,reactors/,saga/}<feature>/transport/message/"
|
|
1104
|
+
presence: opt-in # queue consumer: composed by the service that owns it (an api app) or by a worker app scaled apart
|
|
1105
|
+
litePresence: forbidden # a consumer reads the event bus, which lite does not have
|
|
1106
|
+
pattern: event-bus # a consumer reads the event bus; declaring the pattern enables the slot
|
|
697
1107
|
tracked: tracked
|
|
698
1108
|
tier: feature
|
|
699
1109
|
requires: ["<feature>-message.module.ts"]
|
|
700
1110
|
allows: ["<event>.consumer.ts"] # BE-CONVENTION 1.6: transport/message holds only consumers; a .message.ts is outside the closed suffix list
|
|
701
1111
|
tests: none
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
1112
|
+
coverage: none
|
|
1113
|
+
composedBy: [api, worker]
|
|
1114
|
+
goesTo: "the full edition: a lite app has no event bus, so no message transport (a third-party callback is a webhooks feature)"
|
|
1115
|
+
rules: [BE_BACKGROUND_UNOWNED, BE_SAGA_CONSUMER_DEDUPE]
|
|
1116
|
+
# ----- BE: feature kinds that are not an api: a webhook door and a realtime door. Each kind is a folder of src/features/ named by
|
|
1117
|
+
# the kind and holds one folder per provider or channel; a kind imports only modules/* (never another kind) and only an
|
|
1118
|
+
# api-kind app composes it. Their doors answer to the same door laws as every transport (default deny, bounded input,
|
|
1119
|
+
# no envelope), plus the kind laws of knowledge/patterns/be/webhooks.yaml and realtime.yaml.
|
|
1120
|
+
- id: be.feature.webhooks
|
|
705
1121
|
profiles: [be]
|
|
706
|
-
path: "src/features/<
|
|
707
|
-
presence: opt-in #
|
|
1122
|
+
path: "src/features/webhooks/<provider>/"
|
|
1123
|
+
presence: opt-in # a provider webhook intake: verifies the signature and replay window, then hands the delivery to ONE domain intake method
|
|
1124
|
+
pattern: webhooks # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/webhooks.yaml)
|
|
1125
|
+
trigger: webhooks
|
|
708
1126
|
tracked: tracked
|
|
709
1127
|
tier: feature
|
|
710
|
-
|
|
711
|
-
|
|
1128
|
+
owner: true
|
|
1129
|
+
requires: [index.ts, transport/]
|
|
1130
|
+
allows: [index.ts, transport/]
|
|
712
1131
|
tests: none
|
|
713
|
-
|
|
714
|
-
rules: [
|
|
715
|
-
|
|
1132
|
+
coverage: none
|
|
1133
|
+
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1134
|
+
since: 1.0.0
|
|
1135
|
+
- id: be.feature.webhooks.http
|
|
1136
|
+
profiles: [be]
|
|
1137
|
+
path: "src/features/webhooks/<provider>/transport/http/"
|
|
1138
|
+
presence: opt-in # the one door of the provider: its module, the signed webhook and the door's own spec
|
|
1139
|
+
pattern: webhooks
|
|
1140
|
+
tracked: tracked
|
|
1141
|
+
tier: feature
|
|
1142
|
+
requires: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts"]
|
|
1143
|
+
allows: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts", dto/]
|
|
1144
|
+
tests: unit-beside # the door's own spec (<provider>.webhook.spec.ts) beside the door: the one non-service unit spec of this kind
|
|
1145
|
+
lite: # a lite app has no test world: the door and its module only
|
|
1146
|
+
requires: ["<provider>-http.module.ts", "<provider>.webhook.ts"]
|
|
1147
|
+
allows: ["<provider>-http.module.ts", "<provider>.webhook.ts", dto/]
|
|
1148
|
+
coverage: none
|
|
1149
|
+
composedBy: [api]
|
|
1150
|
+
rules: [BE_WEBHOOK_SHAPE, BE_WEBHOOK_UNVERIFIED, BE_DEFAULT_DENY, BE_INPUT_BOUNDED]
|
|
1151
|
+
since: 1.0.0
|
|
1152
|
+
- id: be.feature.webhooks.dto
|
|
716
1153
|
profiles: [be]
|
|
717
|
-
path: "src/features/<
|
|
1154
|
+
path: "src/features/webhooks/<provider>/transport/http/dto/"
|
|
718
1155
|
presence: opt-in
|
|
1156
|
+
pattern: webhooks
|
|
719
1157
|
tracked: tracked
|
|
720
1158
|
tier: feature
|
|
721
|
-
|
|
722
|
-
allows: ["<channel>.gateway.ts", "dto/*.ts"]
|
|
1159
|
+
allows: ["<event>.request.ts"] # the typed provider payload, validated and bounded (BE_INPUT_BOUNDED)
|
|
723
1160
|
tests: none
|
|
1161
|
+
coverage: none
|
|
724
1162
|
composedBy: [api]
|
|
1163
|
+
rules: [BE_INPUT_BOUNDED]
|
|
725
1164
|
since: 1.0.0
|
|
726
|
-
- id: be.feature.
|
|
1165
|
+
- id: be.feature.realtime
|
|
727
1166
|
profiles: [be]
|
|
728
|
-
path: "src/features/<
|
|
729
|
-
presence: opt-in # a
|
|
1167
|
+
path: "src/features/realtime/<channel>/"
|
|
1168
|
+
presence: opt-in # a push channel: reads and pushes only (a socket gateway or a GraphQL subscription), no domain write
|
|
1169
|
+
litePresence: forbidden # under lite realtime is Supabase Realtime (the client subscribes to channels; nothing serves them in be/)
|
|
1170
|
+
pattern: realtime # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/realtime.yaml)
|
|
1171
|
+
trigger: realtime
|
|
730
1172
|
tracked: tracked
|
|
731
1173
|
tier: feature
|
|
732
|
-
|
|
733
|
-
|
|
1174
|
+
owner: true
|
|
1175
|
+
requires: [index.ts, transport/]
|
|
1176
|
+
allows: [index.ts, transport/]
|
|
734
1177
|
tests: none
|
|
735
|
-
|
|
736
|
-
|
|
1178
|
+
goesTo: "the full edition: under lite realtime is Supabase Realtime (RLS-governed), not a be kind"
|
|
1179
|
+
coverage: none
|
|
1180
|
+
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1181
|
+
since: 1.0.0
|
|
1182
|
+
- id: be.feature.realtime.graphql
|
|
1183
|
+
profiles: [be]
|
|
1184
|
+
path: "src/features/realtime/<channel>/transport/graphql/"
|
|
1185
|
+
presence: opt-in # a GraphQL subscription over graphql-ws
|
|
1186
|
+
litePresence: forbidden
|
|
1187
|
+
pattern: realtime
|
|
1188
|
+
tracked: tracked
|
|
1189
|
+
tier: feature
|
|
1190
|
+
goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
|
|
1191
|
+
requires: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts"]
|
|
1192
|
+
allows: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
|
|
1193
|
+
tests: unit-beside # the door's own spec beside the door (a subscription has no service of its own)
|
|
1194
|
+
coverage: none
|
|
1195
|
+
composedBy: [api]
|
|
1196
|
+
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
|
|
1197
|
+
since: 1.0.0
|
|
1198
|
+
- id: be.feature.realtime.dto
|
|
1199
|
+
profiles: [be]
|
|
1200
|
+
path: "src/features/realtime/<channel>/transport/graphql/dto/"
|
|
1201
|
+
presence: opt-in
|
|
1202
|
+
litePresence: forbidden
|
|
1203
|
+
pattern: realtime
|
|
1204
|
+
tracked: tracked
|
|
1205
|
+
tier: feature
|
|
1206
|
+
goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
|
|
1207
|
+
allows: ["<name>.{input,type,args}.ts"]
|
|
1208
|
+
tests: none
|
|
1209
|
+
coverage: none
|
|
1210
|
+
composedBy: [api]
|
|
1211
|
+
rules: [BE_INPUT_BOUNDED]
|
|
1212
|
+
since: 1.0.0
|
|
1213
|
+
- id: be.feature.realtime.websocket
|
|
1214
|
+
profiles: [be]
|
|
1215
|
+
path: "src/features/realtime/<channel>/transport/websocket/"
|
|
1216
|
+
presence: opt-in # a socket gateway
|
|
1217
|
+
litePresence: forbidden
|
|
1218
|
+
pattern: realtime
|
|
1219
|
+
tracked: tracked
|
|
1220
|
+
tier: feature
|
|
1221
|
+
goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
|
|
1222
|
+
requires: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1223
|
+
allows: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1224
|
+
tests: unit-beside # the door's own spec beside the door (a gateway has no service of its own)
|
|
1225
|
+
coverage: none
|
|
1226
|
+
composedBy: [api]
|
|
1227
|
+
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
|
|
737
1228
|
since: 1.0.0
|
|
1229
|
+
- id: be.cli
|
|
1230
|
+
profiles: [be]
|
|
1231
|
+
# The cli feature root: every one-off action of the back end, compiled only into apps/cli. A group is a nest-commander
|
|
1232
|
+
# @Command (<group>/<group>.cli.ts) whose sub-commands are @SubCommand classes extending CommandRunner
|
|
1233
|
+
# (<group>/subs/<name>.cli.ts), each with its unit spec beside it (<name>.cli.spec.ts): a command is an action runner, not a
|
|
1234
|
+
# thin transport (BE_CLI_COMMAND_SHAPE). `.cli.ts` because `.command.ts` is the CQRS message (HFS delta 3). Every module of
|
|
1235
|
+
# the root is a static @Module (a feature holds no module definition): cli.module.ts imports each group module, a group
|
|
1236
|
+
# module provides its commands, and a command takes its inputs from a platform capability (the connections and their opener
|
|
1237
|
+
# from platform/database) or from a command-line @Option.
|
|
1238
|
+
path: "src/features/cli/"
|
|
1239
|
+
presence: optional
|
|
1240
|
+
trigger: cli # the one cli feature root: a one-off action never imports a feature of another kind (BE_KIND_ISOLATION)
|
|
1241
|
+
tracked: tracked
|
|
1242
|
+
tier: feature
|
|
1243
|
+
owner: true
|
|
1244
|
+
requires: [index.ts, cli.module.ts]
|
|
1245
|
+
# A composed root: an app of kind cli lists cli.module.ts, never a group module (BE_MODULE_SHAPE).
|
|
1246
|
+
composedBy: [cli]
|
|
1247
|
+
allows: [index.ts, cli.module.ts, "<group>/<group>.cli.ts", "<group>/<group>.cli.spec.ts", "<group>/<group>.module.ts", "<group>/subs/<name>.cli.ts", "<group>/subs/<name>.cli.spec.ts"]
|
|
1248
|
+
tests: unit-beside
|
|
1249
|
+
coverage: none
|
|
1250
|
+
rules: [BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER, BE_CLI_REQUIRED, BE_MODULE_SHAPE, BE_SCHEMA_AUTHORITY]
|
|
738
1251
|
|
|
739
1252
|
# ----- BE: modules (three tiers, closed set) -----------------------------------------------------------
|
|
740
1253
|
- id: be.domain
|
|
@@ -748,8 +1261,9 @@ slots:
|
|
|
748
1261
|
allows: ["<capability>.module.ts", "<capability>.config.ts", "<capability>.options.ts", errors/, messages/, persistence/, policies/, "*.service.ts", "*.service.spec.ts", "*.contracts.ts"]
|
|
749
1262
|
forbids: [testing/, exceptions/, utils/, helpers/, shared/]
|
|
750
1263
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
|
|
1264
|
+
coverage: required
|
|
751
1265
|
budget: {indexExports: 60}
|
|
752
|
-
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER]
|
|
1266
|
+
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER, BE_CONTEXT_COUPLING, BE_CONTEXT_TRANSACTION]
|
|
753
1267
|
- id: be.errors
|
|
754
1268
|
profiles: [be]
|
|
755
1269
|
path: "src/modules/{domain,integrations}/<capability>/errors/"
|
|
@@ -758,6 +1272,7 @@ slots:
|
|
|
758
1272
|
tier: inherit
|
|
759
1273
|
allows: ["<capability>.error.ts", "<name>.error.ts"] # one family per capability, extends platform/errors DomainError
|
|
760
1274
|
tests: none
|
|
1275
|
+
coverage: none
|
|
761
1276
|
rules: [BE_ERROR_HOME]
|
|
762
1277
|
- id: be.domain.messages
|
|
763
1278
|
profiles: [be]
|
|
@@ -767,29 +1282,41 @@ slots:
|
|
|
767
1282
|
tier: inherit
|
|
768
1283
|
allows: ["<capability>.messages.ts"] # one keyed catalog per capability, vi and en, read through the platform/i18n MessageCatalog port
|
|
769
1284
|
tests: none
|
|
1285
|
+
coverage: none
|
|
770
1286
|
rules: [BE_USER_COPY_LITERAL]
|
|
771
1287
|
since: 1.0.0
|
|
772
1288
|
- id: be.feature.messages
|
|
773
1289
|
profiles: [be]
|
|
774
|
-
path: "src/features/<feature>/messages/"
|
|
1290
|
+
path: "src/features/api/<feature>/messages/"
|
|
775
1291
|
presence: optional
|
|
776
1292
|
tracked: tracked
|
|
777
1293
|
tier: feature
|
|
778
1294
|
allows: ["<feature>.messages.ts"] # one keyed catalog per feature, vi and en, read through the platform/i18n MessageCatalog port
|
|
779
1295
|
tests: none
|
|
1296
|
+
coverage: none
|
|
780
1297
|
rules: [BE_USER_COPY_LITERAL]
|
|
781
1298
|
since: 1.0.0
|
|
782
1299
|
- id: be.persistence
|
|
783
1300
|
profiles: [be]
|
|
784
|
-
path: "src/modules/{domain,platform}/<capability>/persistence/"
|
|
1301
|
+
path: "src/modules/{domain,platform,projections}/<capability>/persistence/"
|
|
1302
|
+
# R178: the platform capabilities whose tables exist on EVERY connection that uses them (the event-bus outbox, the inbox of every service, the job table); their arrays may be
|
|
1303
|
+
# registered on several declared connections. Any other capability registers its arrays on exactly one (BE_SCHEMA_OWNER).
|
|
1304
|
+
perConnection: [event-bus, inbox, jobs]
|
|
785
1305
|
presence: optional
|
|
786
1306
|
tracked: tracked
|
|
787
1307
|
tier: inherit
|
|
788
1308
|
requires: [connection.ts] # the capability's <c>Entities / <c>Migrations arrays (the owner index re-exports the two arrays by name); no CONNECTION alias: the connection is the one the apps register the arrays on
|
|
789
1309
|
allows: ["entities/<table>.entity.ts", "migrations/<epochMs13>-<kebab-name>.ts", "<name>.sql.ts", "<name>.rows.ts", connection.ts]
|
|
790
1310
|
forbids: [index.ts, "<name>.repository.ts"]
|
|
791
|
-
|
|
792
|
-
|
|
1311
|
+
# Lite keeps the shared-EntityManager persistence (raw SqlText + rows types over the generated Database row types) but the
|
|
1312
|
+
# schema authority is supabase/migrations (ruleParams.be.lite.schemaAuthority): no TypeORM entity and no be migration.
|
|
1313
|
+
lite:
|
|
1314
|
+
requires: [] # no TypeORM arrays to re-export: the persistence of a lite capability is its sql and rows files
|
|
1315
|
+
allows: ["<name>.sql.ts", "<name>.rows.ts", connection.ts]
|
|
1316
|
+
forbids: [index.ts, "<name>.repository.ts", entities/, migrations/]
|
|
1317
|
+
tests: none
|
|
1318
|
+
coverage: none
|
|
1319
|
+
rules: [BE_SCHEMA_OWNER, BE_SCHEMA_AUTHORITY, BE_SQL_OUTSIDE_PERSISTENCE, BE_SQL_TABLE_OWNER, BE_SQL_RETURNING_SHAPE, BE_CONTEXT_COUPLING, BE_CONTEXT_PLATFORM_TABLES]
|
|
793
1320
|
- id: be.platform
|
|
794
1321
|
profiles: [be]
|
|
795
1322
|
path: "src/modules/platform/<capability>/"
|
|
@@ -808,8 +1335,160 @@ slots:
|
|
|
808
1335
|
requires: [index.ts]
|
|
809
1336
|
forbids: [common/, utils/, shared/, testing/] # primitives live in platform/primitives
|
|
810
1337
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
|
|
1338
|
+
coverage: required
|
|
811
1339
|
budget: {indexExports: 60}
|
|
812
1340
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
|
|
1341
|
+
# ----- BE: pattern slots (opt-in; enabled by hfs.json sides.be.patterns). A path under one is HFS_SLOT_NOT_ENABLED unless its pattern is declared.
|
|
1342
|
+
- id: be.platform.event-bus
|
|
1343
|
+
profiles: [be]
|
|
1344
|
+
path: "src/modules/platform/event-bus/"
|
|
1345
|
+
presence: opt-in
|
|
1346
|
+
litePresence: forbidden # Kafka is exactly the footprint lite removes
|
|
1347
|
+
pattern: event-bus # the ONE durable event transport: transactional outbox, relay, Kafka, inbox, retry and dead letter (knowledge/patterns/be/event-bus.yaml)
|
|
1348
|
+
tracked: tracked
|
|
1349
|
+
tier: platform
|
|
1350
|
+
owner: true
|
|
1351
|
+
goesTo: "the full edition: a lite app has no event bus (no Kafka, no outbox)"
|
|
1352
|
+
requires: [index.ts, event-bus.module.ts, event-bus.port.ts, event-bus.contracts.ts, event-bus.service.ts, event-bus.service.spec.ts]
|
|
1353
|
+
tests: unit-beside
|
|
1354
|
+
coverage: required
|
|
1355
|
+
budget: {indexExports: 60}
|
|
1356
|
+
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1357
|
+
- id: be.platform.queue
|
|
1358
|
+
profiles: [be]
|
|
1359
|
+
path: "src/modules/platform/queue/"
|
|
1360
|
+
presence: opt-in
|
|
1361
|
+
litePresence: forbidden # BullMQ/Redis is exactly the footprint lite removes
|
|
1362
|
+
pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
|
|
1363
|
+
tracked: tracked
|
|
1364
|
+
tier: platform
|
|
1365
|
+
owner: true
|
|
1366
|
+
goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command; pg_cron / pg_net fires it)"
|
|
1367
|
+
requires: [index.ts, queue.module.ts, queue.port.ts, queue.contracts.ts, queue-outbox.service.ts, queue-outbox.service.spec.ts]
|
|
1368
|
+
tests: unit-beside
|
|
1369
|
+
coverage: required
|
|
1370
|
+
budget: {indexExports: 60}
|
|
1371
|
+
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1372
|
+
- id: be.events
|
|
1373
|
+
profiles: [be]
|
|
1374
|
+
path: "src/modules/events/<service>/"
|
|
1375
|
+
presence: opt-in
|
|
1376
|
+
litePresence: forbidden # no event bus, so no typed events
|
|
1377
|
+
pattern: event-bus # typed event classes of one service; each equals an entry of be/contracts/<service>/events.json
|
|
1378
|
+
tracked: tracked
|
|
1379
|
+
tier: events
|
|
1380
|
+
owner: true
|
|
1381
|
+
goesTo: "the full edition: a lite app has no event bus, so no modules/events"
|
|
1382
|
+
requires: [index.ts]
|
|
1383
|
+
allows: ["<event>.event.ts", index.ts]
|
|
1384
|
+
tests: none
|
|
1385
|
+
coverage: none
|
|
1386
|
+
rules: [BE_EVENT_CLASS_CONTRACT, BE_TIER_DIRECTION]
|
|
1387
|
+
- id: be.queues
|
|
1388
|
+
profiles: [be]
|
|
1389
|
+
path: "src/modules/queues/<queue>/"
|
|
1390
|
+
presence: opt-in
|
|
1391
|
+
litePresence: forbidden # no queue, so no typed producers
|
|
1392
|
+
pattern: queue # a typed producer of one queue: enqueueX(payload, tx) writes the outbox; its schedulers live beside it
|
|
1393
|
+
tracked: tracked
|
|
1394
|
+
tier: queues
|
|
1395
|
+
owner: true
|
|
1396
|
+
goesTo: "the full edition: a lite app has no job queue, so no modules/queues"
|
|
1397
|
+
requires: [index.ts, "<queue>.queue.ts"] # the module exists when a domain module imports the producer; a scheduler-only queue has none
|
|
1398
|
+
allows: ["<queue>.queue.ts", "<queue>.module.ts", "<queue>.contracts.ts", index.ts]
|
|
1399
|
+
tests: none
|
|
1400
|
+
coverage: none
|
|
1401
|
+
rules: [BE_OUTBOX_WRITE_TX, BE_TIER_DIRECTION]
|
|
1402
|
+
- id: be.feature.reactors
|
|
1403
|
+
profiles: [be]
|
|
1404
|
+
path: "src/features/reactors/<reactor>/"
|
|
1405
|
+
presence: opt-in
|
|
1406
|
+
litePresence: forbidden # no event bus, so nothing reacts to a domain event
|
|
1407
|
+
pattern: event-bus # a reactor consumes events of another context and keeps a local copy or triggers a projection
|
|
1408
|
+
trigger: reactors
|
|
1409
|
+
tracked: tracked
|
|
1410
|
+
tier: feature
|
|
1411
|
+
owner: true
|
|
1412
|
+
requires: [index.ts, "<reactor>.module.ts", transport/]
|
|
1413
|
+
allows: [index.ts, "<reactor>.module.ts", application/, transport/]
|
|
1414
|
+
tests: none
|
|
1415
|
+
goesTo: "the full edition: a lite app has no event bus, so no reactors (a third-party callback is a webhooks feature)"
|
|
1416
|
+
coverage: none
|
|
1417
|
+
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1418
|
+
- id: be.feature.jobs
|
|
1419
|
+
profiles: [be]
|
|
1420
|
+
path: "src/features/jobs/<job>/"
|
|
1421
|
+
presence: opt-in
|
|
1422
|
+
litePresence: forbidden # no fenced-job platform, so no job tree
|
|
1423
|
+
pattern: fenced-job # a background job: a processor with a fenced claim, steps and the application it dispatches to
|
|
1424
|
+
trigger: jobs
|
|
1425
|
+
tracked: tracked
|
|
1426
|
+
tier: feature
|
|
1427
|
+
owner: true
|
|
1428
|
+
requires: [index.ts, "<job>.module.ts", steps/, transport/]
|
|
1429
|
+
allows: [index.ts, "<job>.module.ts", application/, steps/, transport/]
|
|
1430
|
+
tests: none
|
|
1431
|
+
goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command; pg_cron / pg_net fires it)"
|
|
1432
|
+
coverage: none
|
|
1433
|
+
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1434
|
+
- id: be.feature.jobs.queue
|
|
1435
|
+
profiles: [be]
|
|
1436
|
+
path: "src/features/jobs/<job>/transport/queue/"
|
|
1437
|
+
presence: opt-in # the queue transport of the job: its module and the processor the job runner hands the fenced claim to
|
|
1438
|
+
litePresence: forbidden
|
|
1439
|
+
pattern: fenced-job
|
|
1440
|
+
tracked: tracked
|
|
1441
|
+
tier: feature
|
|
1442
|
+
requires: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1443
|
+
allows: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1444
|
+
tests: none
|
|
1445
|
+
coverage: none
|
|
1446
|
+
composedBy: [api, worker]
|
|
1447
|
+
goesTo: "the full edition: a lite app has no job queue"
|
|
1448
|
+
rules: [BE_BACKGROUND_UNOWNED]
|
|
1449
|
+
- id: be.feature.jobs.steps
|
|
1450
|
+
profiles: [be]
|
|
1451
|
+
path: "src/features/jobs/<job>/steps/"
|
|
1452
|
+
presence: opt-in
|
|
1453
|
+
litePresence: forbidden
|
|
1454
|
+
pattern: fenced-job # one external effect per step; it takes the claimed job and writes through the guarded methods
|
|
1455
|
+
tracked: tracked
|
|
1456
|
+
tier: feature
|
|
1457
|
+
allows: ["<step>.step.ts"]
|
|
1458
|
+
tests: none
|
|
1459
|
+
goesTo: "the full edition: a lite app has no job queue"
|
|
1460
|
+
coverage: none
|
|
1461
|
+
rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
|
|
1462
|
+
- id: be.platform.jobs
|
|
1463
|
+
profiles: [be]
|
|
1464
|
+
path: "src/modules/platform/jobs/"
|
|
1465
|
+
presence: opt-in
|
|
1466
|
+
litePresence: forbidden # no fenced-job primitive without a queue
|
|
1467
|
+
pattern: fenced-job # the ONE fencing primitive: the job entity, the claim that bumps the token, guarded writes (knowledge/patterns/be/jobs.yaml)
|
|
1468
|
+
tracked: tracked
|
|
1469
|
+
tier: platform
|
|
1470
|
+
owner: true
|
|
1471
|
+
goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command)"
|
|
1472
|
+
requires: [index.ts, jobs.module.ts, jobs.port.ts, jobs.contracts.ts, job-claim.service.ts, job-claim.service.spec.ts, fenced.processor.ts]
|
|
1473
|
+
tests: unit-beside
|
|
1474
|
+
coverage: required
|
|
1475
|
+
budget: {indexExports: 60}
|
|
1476
|
+
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_JOB_WRITE_OUTSIDE_OWNER, BE_JOB_FENCE_REQUIRED]
|
|
1477
|
+
- id: be.projections
|
|
1478
|
+
profiles: [be]
|
|
1479
|
+
path: "src/modules/projections/<name>/"
|
|
1480
|
+
presence: opt-in
|
|
1481
|
+
litePresence: forbidden # a read model replayed off an event stream needs the event bus lite does not have
|
|
1482
|
+
pattern: projection # a read model: only its projection writes its table; recompute is idempotent and replayable; readers use get*
|
|
1483
|
+
tracked: tracked
|
|
1484
|
+
tier: projections
|
|
1485
|
+
owner: true
|
|
1486
|
+
goesTo: "the full edition: a lite app has no event stream to project; a read shape is a view or a read-* in fe modules/db"
|
|
1487
|
+
requires: [index.ts, "<name>.module.ts", "<name>.projection.ts", "<name>.projection-entity.ts"]
|
|
1488
|
+
allows: [index.ts, "<name>.module.ts", "<name>.module-definition.ts", "<name>.options.ts", "<name>.projection.ts", "<name>.projection-entity.ts", "<name>.contracts.ts", persistence/]
|
|
1489
|
+
tests: unit-beside # a measured slot: <name>.projection.spec.ts beside its projection is legal (the file owes 100 per file; a spec of its own is optional)
|
|
1490
|
+
coverage: required
|
|
1491
|
+
rules: [BE_PROJECTION_WRITE_OWNER, BE_PROJECTION_SHAPE, BE_TIER_DIRECTION, BE_CONTEXT_COUPLING]
|
|
813
1492
|
- id: be.integrations
|
|
814
1493
|
profiles: [be]
|
|
815
1494
|
path: "src/modules/integrations/<provider>/"
|
|
@@ -819,6 +1498,7 @@ slots:
|
|
|
819
1498
|
owner: true
|
|
820
1499
|
requires: [index.ts, "<provider>.config.ts"]
|
|
821
1500
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts; plus src/tests/integration/<provider>/ (R112)
|
|
1501
|
+
coverage: required
|
|
822
1502
|
budget: {indexExports: 60}
|
|
823
1503
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
|
|
824
1504
|
- id: be.integrations.model
|
|
@@ -828,7 +1508,26 @@ slots:
|
|
|
828
1508
|
tracked: tracked
|
|
829
1509
|
tier: integrations
|
|
830
1510
|
tests: none
|
|
1511
|
+
coverage: required
|
|
831
1512
|
why: an ML or LLM model client is an integration; weights and datasets are never in git
|
|
1513
|
+
- id: be.integrations.supabase
|
|
1514
|
+
profiles: [be]
|
|
1515
|
+
# The only be import of @supabase/* (L10): the Auth admin and Storage admin client, plus the JWKS-based verification of
|
|
1516
|
+
# the Supabase access token the guard chain reads (L16). The connection itself is ordinary Postgres (the shared
|
|
1517
|
+
# EntityManager of the declared connection); what lives here are the service calls no database role covers.
|
|
1518
|
+
path: "src/modules/integrations/supabase/"
|
|
1519
|
+
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
1520
|
+
provider: supabase
|
|
1521
|
+
litePresence: required
|
|
1522
|
+
tracked: tracked
|
|
1523
|
+
tier: integrations
|
|
1524
|
+
owner: true
|
|
1525
|
+
requires: [index.ts, supabase.config.ts, supabase.options.ts, supabase.decorators.ts, supabase.client.ts, supabase.jwks.ts] # Secrets, no defaults (R44); the options and the named injector (R83); the admin client; the token verification
|
|
1526
|
+
allows: [index.ts, supabase.config.ts, supabase.options.ts, supabase.decorators.ts, supabase.client.ts, supabase.jwks.ts, errors/]
|
|
1527
|
+
tests: unit-beside
|
|
1528
|
+
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT]
|
|
1529
|
+
coverage: required
|
|
1530
|
+
since: 2.1.0
|
|
832
1531
|
|
|
833
1532
|
# ----- BE: tests (owner test layout 2026-09-30). Unit specs are <name>.service.spec.ts beside their service and nothing else; everything else
|
|
834
1533
|
# lives under src/tests/, one folder per kind, and the folder and the file suffix always agree (a file whose suffix does
|
|
@@ -837,13 +1536,16 @@ slots:
|
|
|
837
1536
|
profiles: [be]
|
|
838
1537
|
path: "src/tests/world/"
|
|
839
1538
|
presence: optional
|
|
1539
|
+
litePresence: forbidden # a lite app has no test world
|
|
840
1540
|
tracked: tracked
|
|
841
1541
|
tier: e2e
|
|
1542
|
+
goesTo: "the full edition: a lite app has no test world (no containers, fakes or useTestWorld)"
|
|
842
1543
|
allows: [global-setup.ts, global-teardown.ts, use-test-world.ts, "fakes/<provider>/server.ts", fakes/] # jest globalSetup/globalTeardown default-export (Jest API); fakes/<provider>/server.ts is the fake server of a provider, a literal file name BE_SOURCE_FORM reads as its role; plus root files with a role suffix (test-world-files, R47)
|
|
843
1544
|
tests: none
|
|
1545
|
+
coverage: none
|
|
844
1546
|
why: >-
|
|
845
1547
|
the ONLY test infrastructure location, shared by integration and e2e: global-setup.ts starts the shared
|
|
846
|
-
infrastructure (containers) and runs
|
|
1548
|
+
infrastructure (containers) and runs the migrations once over the cli app's connections; use-test-world.ts exports
|
|
847
1549
|
useTestWorld({ apps: { <name>: { module, listen? } } } | { modules: [...] }) -> world.apps.<name>.api,
|
|
848
1550
|
world.db.<connection> (the shared EntityManager), world.infra.<service> (latency/cut/restore on a REAL service of the repository's
|
|
849
1551
|
own stack, the app root's `.starcistacks/<env>`, each behind toxiproxy), world.fake.<provider> (a network-edge fake of an external SaaS started by the world, with
|
|
@@ -854,27 +1556,36 @@ slots:
|
|
|
854
1556
|
profiles: [be]
|
|
855
1557
|
path: "src/tests/world/kit/"
|
|
856
1558
|
presence: optional
|
|
1559
|
+
litePresence: forbidden
|
|
857
1560
|
tracked: tracked
|
|
858
1561
|
tier: e2e
|
|
1562
|
+
goesTo: "the full edition: a lite app has no test world"
|
|
859
1563
|
allows: ["<name>.ts"] # plain kebab names, like platform/primitives (BE-CONVENTION 1.11 names primitives/time.ts): the world's inlined test helpers (poll, free-ports)
|
|
860
1564
|
tests: none
|
|
1565
|
+
coverage: none
|
|
861
1566
|
why: the helpers the test world needs (polling, free ports, transports), inlined so the world imports no other repository; a bare <name>.ts entry in allows is what admits a plain name in BE_SOURCE_FORM
|
|
862
1567
|
- id: be.tests.fixtures
|
|
863
1568
|
profiles: [be]
|
|
864
1569
|
path: "src/tests/fixtures/"
|
|
865
1570
|
presence: optional
|
|
1571
|
+
litePresence: forbidden
|
|
866
1572
|
tracked: tracked
|
|
867
1573
|
tier: fixtures
|
|
1574
|
+
goesTo: "the full edition: a lite app has no test fixtures"
|
|
868
1575
|
allows: [i18n/] # i18n/ is the one folder that may carry localized text
|
|
869
1576
|
tests: none
|
|
1577
|
+
coverage: none
|
|
870
1578
|
why: payload fixtures with a role suffix (the doubles come from @starci/jest-preset, not from here); the test data builders live in its builders/ slot; importable by unit specs, integration and e2e; never imports a feature
|
|
871
1579
|
- id: be.tests.fixtures.builders
|
|
872
1580
|
profiles: [be]
|
|
873
1581
|
path: "src/tests/fixtures/builders/*.builder.ts"
|
|
874
1582
|
presence: optional
|
|
1583
|
+
litePresence: forbidden
|
|
875
1584
|
tracked: tracked
|
|
876
1585
|
tier: fixtures
|
|
1586
|
+
goesTo: "the full edition: a lite app has no test fixtures"
|
|
877
1587
|
tests: none
|
|
1588
|
+
coverage: none
|
|
878
1589
|
why: >-
|
|
879
1590
|
the ONLY home of test data builders, one <area>.builder.ts per area, shared by every spec of that area: pure object builders
|
|
880
1591
|
(commissionRow(overrides), accrueInput(overrides)) for unit specs and persisting builders (orderBuilder(world.db.primary).pending().build(overrides))
|
|
@@ -885,36 +1596,48 @@ slots:
|
|
|
885
1596
|
profiles: [be]
|
|
886
1597
|
path: "src/tests/fixtures/i18n/"
|
|
887
1598
|
presence: optional
|
|
1599
|
+
litePresence: forbidden
|
|
888
1600
|
tracked: tracked
|
|
889
1601
|
tier: fixtures
|
|
1602
|
+
goesTo: "the full edition: a lite app has no test fixtures"
|
|
890
1603
|
allows: ["<name>.<role>.ts"] # data files that deliberately carry localized text (a real Vietnamese string a parser or formatter must accept); no specs here
|
|
891
1604
|
tests: none
|
|
1605
|
+
coverage: none
|
|
892
1606
|
why: the ONLY test-fixture location where Vietnamese or another language may appear in source (LANG_NOT_ENGLISH); placement is the marker, there is no comment pragma
|
|
893
1607
|
- id: be.tests.integration
|
|
894
1608
|
profiles: [be]
|
|
895
1609
|
path: "src/tests/integration/<capability>/*.integration-spec.ts"
|
|
896
1610
|
presence: optional
|
|
1611
|
+
litePresence: forbidden
|
|
897
1612
|
tracked: tracked
|
|
898
1613
|
tier: e2e
|
|
899
1614
|
tests: e2e
|
|
1615
|
+
goesTo: "the full edition: a lite app has no test tree"
|
|
1616
|
+
coverage: none
|
|
900
1617
|
why: one capability module through useTestWorld({ modules }), no HTTP door of ours (SQL, transactions, concurrency, inbox claims on the real database; and every integration client, `<capability>` being its provider folder, against our real infra, a real peer app or the library fake at the network edge, with its success, its ErrorCode refusal mapping and an outage through the world, R112); run by test:integration
|
|
901
1618
|
rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
|
|
902
1619
|
- id: be.tests.e2e
|
|
903
1620
|
profiles: [be]
|
|
904
1621
|
path: "src/tests/e2e/<area>/*.e2e-spec.ts"
|
|
905
1622
|
presence: optional
|
|
1623
|
+
litePresence: forbidden
|
|
906
1624
|
tracked: tracked
|
|
907
1625
|
tier: e2e
|
|
908
1626
|
tests: e2e
|
|
1627
|
+
goesTo: "the full edition: a lite app has no test tree"
|
|
1628
|
+
coverage: none
|
|
909
1629
|
why: flow e2e through useTestWorld({ apps }); external services are the world's network fakes; run by test:e2e
|
|
910
1630
|
rules: [HFS_CI_E2E_AUTOMATIC, BE_TEST_TOPOLOGY, BE_E2E_FLOW]
|
|
911
1631
|
- id: be.tests.contract
|
|
912
1632
|
profiles: [be]
|
|
913
1633
|
path: "src/tests/contract/<provider>/*.contract-spec.ts"
|
|
914
1634
|
presence: optional
|
|
1635
|
+
litePresence: forbidden
|
|
915
1636
|
tracked: tracked
|
|
916
1637
|
tier: e2e
|
|
917
1638
|
tests: e2e
|
|
1639
|
+
goesTo: "the full edition: a lite app has no test tree"
|
|
1640
|
+
coverage: none
|
|
918
1641
|
why: provider sandbox contracts, skipped without sandbox config; run only by test:contract, never part of test or test:e2e
|
|
919
1642
|
rules: [BE_TEST_TOPOLOGY]
|
|
920
1643
|
- id: be.tests.e2e-world-retired
|
|
@@ -929,16 +1652,18 @@ slots:
|
|
|
929
1652
|
# ----- FE: apps ----------------------------------------------------------------------------------------
|
|
930
1653
|
- id: fe.app.next
|
|
931
1654
|
profiles: [fe]
|
|
932
|
-
|
|
1655
|
+
# Each fe app is an npm workspace: its package.json is @<project>/<app> with its own runtime dependencies and the fixed
|
|
1656
|
+
# workspace scripts (HFS_MONO_FE_WORKSPACE); turbo runs them.
|
|
1657
|
+
path: "apps/<app>/{package.json,next.config.ts,tsconfig.json,postcss.config.mjs}"
|
|
933
1658
|
appKind: next
|
|
934
1659
|
presence: required
|
|
935
1660
|
tracked: tracked
|
|
936
1661
|
tier: none
|
|
937
1662
|
minInstances: 1
|
|
938
|
-
requires: [next.config.ts, tsconfig.json, postcss.config.mjs, src/app/, src/modules/i18n/,
|
|
1663
|
+
requires: [package.json, next.config.ts, tsconfig.json, postcss.config.mjs, src/app/, src/modules/i18n/,
|
|
939
1664
|
"src/app/global-error.tsx", "src/app/[locale]/layout.tsx", "src/app/[locale]/error.tsx", "src/app/[locale]/not-found.tsx", "src/app/[locale]/loading.tsx"]
|
|
940
1665
|
tests: none
|
|
941
|
-
rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS]
|
|
1666
|
+
rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS, HFS_MONO_FE_WORKSPACE, HFS_MONO_WORKSPACE_DEP]
|
|
942
1667
|
- id: fe.app-optional
|
|
943
1668
|
profiles: [fe]
|
|
944
1669
|
path: "apps/<app>/public/"
|
|
@@ -979,6 +1704,18 @@ slots:
|
|
|
979
1704
|
tests: none
|
|
980
1705
|
goesTo: "apps/<app>/src/proxy.ts: Next >= 16 renamed middleware to proxy and refuses the old name"
|
|
981
1706
|
rules: [FE_NEXT_CONVENTIONS]
|
|
1707
|
+
- id: fe.route.callback
|
|
1708
|
+
profiles: [fe]
|
|
1709
|
+
# The auth callback of a Supabase session (the PKCE code exchange): the one non-health route handler of an app with a Supabase
|
|
1710
|
+
# connection, in either edition (an upgrade lite -> full leaves it as it is, fe/** is never touched); an app without one has no
|
|
1711
|
+
# route handler in fe/ (L15).
|
|
1712
|
+
path: "apps/<app>/src/app/auth/callback/route.ts"
|
|
1713
|
+
presence: opt-in
|
|
1714
|
+
provider: supabase
|
|
1715
|
+
tracked: tracked
|
|
1716
|
+
tier: route
|
|
1717
|
+
tests: none
|
|
1718
|
+
since: 2.1.0
|
|
982
1719
|
- id: fe.feature
|
|
983
1720
|
profiles: [fe]
|
|
984
1721
|
path: "apps/<app>/src/features/{pages,layouts,overlays}/<name>/"
|
|
@@ -1061,6 +1798,7 @@ slots:
|
|
|
1061
1798
|
presence: optional
|
|
1062
1799
|
tracked: tracked
|
|
1063
1800
|
tier: transport
|
|
1801
|
+
outcomeHome: true
|
|
1064
1802
|
tests: none
|
|
1065
1803
|
why: the one Outcome union of a one-app repository; a shared union is fe.package.api.outcome
|
|
1066
1804
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
@@ -1109,10 +1847,47 @@ slots:
|
|
|
1109
1847
|
tier: foundation
|
|
1110
1848
|
tests: none
|
|
1111
1849
|
why: shared types with no runtime behaviour; a component may import them
|
|
1850
|
+
- id: fe.modules.db
|
|
1851
|
+
profiles: [fe]
|
|
1852
|
+
# The db transport of an app on Supabase (the ONLY place that imports @supabase/ssr or @supabase/supabase-js, L10): the
|
|
1853
|
+
# server client factory (cookies, anon key, RLS applies), the browser client factory, getPrincipal() (React cache,
|
|
1854
|
+
# auth.getClaims()) and toOutcome() mapping {data, error} into the same Outcome vocabulary modules/api uses. Readers
|
|
1855
|
+
# live in <domain>/read-*.ts, Server Actions (schema-parsed input) in <domain>/write-*.ts; the row types come from
|
|
1856
|
+
# supabase/types/database.types.ts (sides.fe.reads). The barrel index.ts is server-only (it re-exports server code), so
|
|
1857
|
+
# browser.ts, the browser client factory a client hook imports, is a second public entry of the owner (`entries`).
|
|
1858
|
+
path: "apps/<app>/src/modules/db/"
|
|
1859
|
+
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
1860
|
+
provider: supabase
|
|
1861
|
+
litePresence: required
|
|
1862
|
+
tracked: tracked
|
|
1863
|
+
tier: transport
|
|
1864
|
+
requires: [index.ts, server.ts, browser.ts, principal.ts]
|
|
1865
|
+
allows: [index.ts, server.ts, browser.ts, principal.ts, outcome.ts, "<domain>/read-*.ts", "<domain>/write-*.ts", "<domain>/<domain>.mapper.ts"]
|
|
1866
|
+
entries: [browser.ts]
|
|
1867
|
+
anonymousActions: [auth/write-sign-in.ts, auth/write-sign-out.ts] # the Server Actions that run for an anonymous principal by design (FE_AUTH_SESSION_TRUST)
|
|
1868
|
+
tests: none
|
|
1869
|
+
since: 2.1.0
|
|
1870
|
+
- id: fe.modules.db.outcome
|
|
1871
|
+
profiles: [fe]
|
|
1872
|
+
# A lite app has no modules/api: its db transport is the transport, so the one Outcome union lives here (toOutcome maps
|
|
1873
|
+
# {data, error} into it). Under full the union stays fe.transport.outcome / fe.package.api.outcome.
|
|
1874
|
+
editions: [lite]
|
|
1875
|
+
path: "apps/<app>/src/modules/db/outcome.ts"
|
|
1876
|
+
presence: opt-in
|
|
1877
|
+
provider: supabase
|
|
1878
|
+
litePresence: optional
|
|
1879
|
+
tracked: tracked
|
|
1880
|
+
tier: transport
|
|
1881
|
+
outcomeHome: true
|
|
1882
|
+
tests: none
|
|
1883
|
+
why: the one Outcome union of a lite app; its db owner is the transport
|
|
1884
|
+
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
1885
|
+
since: 2.1.0
|
|
1112
1886
|
- id: fe.package.ui
|
|
1113
1887
|
profiles: [fe]
|
|
1114
1888
|
path: "packages/<family>-ui/"
|
|
1115
1889
|
presence: opt-in
|
|
1890
|
+
litePresence: optional # a lite app scaffolds no packages; one appears by need, without an optionalSlots entry
|
|
1116
1891
|
tracked: tracked
|
|
1117
1892
|
tier: package
|
|
1118
1893
|
owner: true
|
|
@@ -1127,6 +1902,7 @@ slots:
|
|
|
1127
1902
|
profiles: [fe]
|
|
1128
1903
|
path: "packages/<family>-api/"
|
|
1129
1904
|
presence: opt-in
|
|
1905
|
+
litePresence: optional
|
|
1130
1906
|
tracked: tracked
|
|
1131
1907
|
tier: package
|
|
1132
1908
|
owner: true
|
|
@@ -1149,6 +1925,7 @@ slots:
|
|
|
1149
1925
|
presence: optional # enabled with its package (fe.package.api is the opt-in)
|
|
1150
1926
|
tracked: tracked
|
|
1151
1927
|
tier: package
|
|
1928
|
+
outcomeHome: true
|
|
1152
1929
|
tests: none
|
|
1153
1930
|
why: the repository's one Outcome union
|
|
1154
1931
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
@@ -1156,6 +1933,7 @@ slots:
|
|
|
1156
1933
|
profiles: [fe]
|
|
1157
1934
|
path: "packages/<family>-i18n/"
|
|
1158
1935
|
presence: opt-in
|
|
1936
|
+
litePresence: optional
|
|
1159
1937
|
tracked: tracked
|
|
1160
1938
|
tier: package
|
|
1161
1939
|
owner: true
|
|
@@ -1163,14 +1941,28 @@ slots:
|
|
|
1163
1941
|
tests: none
|
|
1164
1942
|
why: the next-intl stack (routing, navigation, request config) written once, exported as the createAppI18n factory every app calls
|
|
1165
1943
|
rules: [FE_I18N_PLACEMENT, FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
1944
|
+
- id: fe.package.db
|
|
1945
|
+
profiles: [fe]
|
|
1946
|
+
# The shared Supabase client factories once a second fe app needs them (server, browser, principal, toOutcome) -
|
|
1947
|
+
# mirroring fe.transport.client vs fe.package.api.client; the one Outcome union is re-exported from
|
|
1948
|
+
# fe.package.api.outcome when the api package exists.
|
|
1949
|
+
path: "packages/<family>-db/"
|
|
1950
|
+
presence: opt-in
|
|
1951
|
+
provider: supabase
|
|
1952
|
+
tracked: tracked
|
|
1953
|
+
tier: package
|
|
1954
|
+
owner: true
|
|
1955
|
+
requires: [package.json, src/index.ts, tsconfig.json]
|
|
1956
|
+
tests: none
|
|
1957
|
+
since: 2.1.0
|
|
1166
1958
|
|
|
1167
1959
|
# Checks that read this manifest (all ship from .claude; none keeps its own path list)
|
|
1168
1960
|
consumers:
|
|
1169
|
-
- scripts/
|
|
1170
|
-
- scripts/
|
|
1171
|
-
- scripts/
|
|
1961
|
+
- scripts/hfs/slots.mjs # the loader every consumer below goes through
|
|
1962
|
+
- scripts/hfs/check.mjs # starci app check: HFS_SLOT_* / tracked / external, the side view per side
|
|
1963
|
+
- scripts/hfs/architecture.mjs # tiers, direction matrix, cycles, reachability
|
|
1172
1964
|
- packages/eslint/be starciBeConfig({hfs}) # file globs for rules come from slots
|
|
1173
1965
|
- packages/eslint/fe starciFeConfig({hfs})
|
|
1174
1966
|
- packages/stylelint # @starci/stylelint-canon: colour and brand allowances from fe.modules.brand
|
|
1175
|
-
- packages/hfs/sync # renders managedBy templates (
|
|
1967
|
+
- packages/hfs/sync # renders managedBy templates (starci app sync) and judges them (starci app check)
|
|
1176
1968
|
- modules/kernel/failure-codes.yaml # every rule id listed above has a Vietnamese why
|