@starci/hfs 4.0.8 → 4.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +81 -39
- package/emit/contracts.mjs +56 -9
- package/emit/db-types.mjs +169 -0
- package/emit/events.mjs +41 -0
- package/emit/operations-worker.mjs +1 -1
- package/emit/operations.mjs +2 -2
- package/emit/schema-worker.mjs +1 -1
- package/lint/bound-sys.cjs +50 -0
- package/lint/run.mjs +53 -19
- package/package.json +14 -9
- package/report/sonar.mjs +8 -8
- package/runtime/engine/config.mjs +494 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +160 -29
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +912 -120
- package/runtime/knowledge/patterns/be/api.yaml +369 -0
- package/runtime/knowledge/patterns/be/cli.yaml +297 -0
- package/runtime/knowledge/patterns/be/domain.yaml +113 -0
- package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
- package/runtime/knowledge/patterns/be/projections.yaml +178 -0
- package/runtime/knowledge/patterns/be/queues.yaml +236 -0
- package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
- package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
- package/runtime/knowledge/patterns/be/saga.yaml +248 -0
- package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
- package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
- package/runtime/knowledge/sonar-gate.yaml +52 -18
- package/runtime/modules/kernel/failure-codes.yaml +571 -51
- package/runtime/modules/models/registry.yaml +591 -0
- package/runtime/modules/models/runtimes.yaml +701 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
- package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
- package/runtime/scripts/api/fs/lib.mjs +10 -2
- package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
- package/runtime/scripts/api/git/config-get.mjs +10 -0
- package/runtime/scripts/api/git/diff.mjs +5 -0
- package/runtime/scripts/api/git/lib.mjs +16 -26
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -5
- package/runtime/scripts/api/sops/lib.mjs +35 -0
- package/runtime/scripts/api/sops/seal.mjs +18 -0
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
- package/runtime/scripts/hfs/architecture/config.mjs +9 -6
- package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
- package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
- package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
- package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +1 -1
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
- package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/hfs/architecture/index.mjs +17 -4
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
- package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
- package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
- package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
- package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
- package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
- package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
- package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
- package/runtime/scripts/hfs/architecture.mjs +4 -4
- package/runtime/scripts/hfs/check.mjs +75 -72
- package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
- package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
- package/runtime/scripts/hfs/edition-slots.mjs +66 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/hfs/path-findings.mjs +1 -1
- package/runtime/scripts/hfs/repo-identity.mjs +9 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
- package/runtime/scripts/hfs/rules/cli.mjs +37 -0
- package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
- package/runtime/scripts/hfs/rules/contract.mjs +10 -9
- package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
- package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
- package/runtime/scripts/hfs/rules/database.mjs +68 -0
- package/runtime/scripts/hfs/rules/deps.mjs +1 -1
- package/runtime/scripts/hfs/rules/docker.mjs +259 -0
- package/runtime/scripts/hfs/rules/edition.mjs +111 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
- package/runtime/scripts/hfs/rules/read.mjs +1 -1
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +175 -0
- package/runtime/scripts/hfs/slots.mjs +78 -108
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/hfs/tree.mjs +5 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
- package/runtime/scripts/hfs/view.mjs +3 -2
- package/runtime/scripts/lib/dockerfile.mjs +124 -0
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +103 -0
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +24 -5
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/regex.mjs +4 -0
- package/runtime/scripts/lib/same-text.mjs +5 -0
- package/runtime/scripts/lib/secret-patterns.mjs +4 -3
- package/runtime/scripts/lib/sleep-sync.mjs +2 -1
- package/runtime/scripts/lib/sops-envelope.mjs +58 -0
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/lib/walk.mjs +4 -3
- package/scaffold/add-app.mjs +174 -0
- package/scaffold/add-cli-lite.mjs +257 -0
- package/scaffold/add-inbox-lite.mjs +314 -0
- package/scaffold/add-table.mjs +344 -0
- package/scaffold/add.mjs +299 -0
- package/scaffold/app.mjs +358 -69
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +32 -26
- package/scaffold/supabase-ports.mjs +26 -0
- package/src/main.mjs +327 -0
- package/sync/cli.mjs +6 -9
- package/sync/format.mjs +1 -1
- package/sync/hygiene.mjs +10 -10
- package/sync/index.mjs +167 -104
- package/sync/managed.mjs +19 -11
- package/sync/sonar-key.mjs +5 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
- package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
- package/templates/app/ci-workflows/sonar-steps.yml +9 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
- package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
- package/templates/app/docker-ignore/dockerignore +50 -0
- package/templates/app/gitignore-lite +35 -0
- package/templates/app/hooks/husky/pre-commit +3 -8
- package/templates/app/hooks/husky/pre-push +45 -6
- package/templates/app/package-scripts/core.json +8 -0
- package/templates/app/package-scripts/package.json +3 -10
- package/templates/app/package-scripts-lite/package.json +15 -0
- package/templates/app/quality-config/codecov.yml +15 -3
- package/templates/app/quality-config/sonar-base.properties +4 -0
- package/templates/app/quality-config/sonar-project.properties +1 -4
- package/templates/app/quality-config-lite/sonar-project.properties +5 -0
- package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
- package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
- package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
- package/templates/app/skeleton/README.md +7 -5
- package/templates/app/skeleton-lite/.sops.yaml +3 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
- package/templates/app/skeleton-lite/README.md +40 -0
- package/templates/app/skeleton-lite/supabase/config.toml +41 -0
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
- package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
- package/templates/app/task-graph/turbo.json +23 -0
- package/templates/app/tool-config-lite/prettierignore +2 -0
- package/templates/app/tool-config-lite/prettierrc +1 -0
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
- package/templates/be/common/main-listen.ts.partial +14 -0
- package/templates/be/common/sql-ident.ts.partial +7 -0
- package/templates/be/common/sql-tag.ts.partial +5 -0
- package/templates/be/image/api/Dockerfile +38 -0
- package/templates/be/image/cli/Dockerfile +38 -0
- package/templates/be/image/worker/Dockerfile +36 -0
- package/templates/be/inbox/database-client.ts.tpl +50 -0
- package/templates/be/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/migration.sql.tpl +29 -0
- package/templates/be/inbox/module-definition.ts.tpl +6 -0
- package/templates/be/inbox/module.ts.tpl +14 -0
- package/templates/be/inbox/options.ts.tpl +2 -0
- package/templates/be/inbox/service.ts.tpl +33 -0
- package/templates/be/inbox/sql.ts.tpl +16 -0
- package/templates/be/inbox/webhook-door.ts.tpl +36 -0
- package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
- package/templates/be/patterns/api/application.command.ts.tpl +10 -0
- package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
- package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
- package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
- package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
- package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
- package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
- package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
- package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/http.module.ts.tpl +7 -0
- package/templates/be/patterns/api/http.request.ts.tpl +10 -0
- package/templates/be/patterns/api/http.response.ts.tpl +5 -0
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
- package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
- package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
- package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
- package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
- package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
- package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
- package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
- package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
- package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
- package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
- package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
- package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
- package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
- package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
- package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
- package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
- package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
- package/templates/be/patterns/projections/migration.ts.tpl +18 -0
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
- package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
- package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
- package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
- package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
- package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
- package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
- package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
- package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
- package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
- package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
- package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
- package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
- package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
- package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
- package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
- package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
- package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
- package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
- package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
- package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
- package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
- package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
- package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/index.ts +1 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
- package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
- package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
- package/templates/be/table/error.ts.tpl +16 -0
- package/templates/be/table/index.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +51 -0
- package/templates/be/table/module-definition.ts.tpl +7 -0
- package/templates/be/table/module.ts.tpl +14 -0
- package/templates/be/table/options.ts.tpl +2 -0
- package/templates/be/table/rows.ts.tpl +4 -0
- package/templates/be/table/service.ts.tpl +23 -0
- package/templates/be/table/sql.ts.tpl +4 -0
- package/templates/be/tool-config/jest.config.js +3 -1
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
- package/templates/common/gitignore.base +1 -2
- package/templates/fe/app/package.json.tpl +1 -0
- package/templates/fe/app/tsconfig.json +12 -0
- package/templates/fe/common/app-layout.tsx.partial +8 -0
- package/templates/fe/common/site-shell.tsx.partial +9 -0
- package/templates/fe/image/next/Dockerfile +42 -0
- package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
- package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
- package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
- package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
- package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
- package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
- package/templates/fe/table/read.ts.tpl +16 -0
- package/templates/fe/table/write.ts.tpl +16 -0
- package/upgrade/check.mjs +137 -0
- package/upgrade/index.mjs +524 -0
- package/bin/hfs.mjs +0 -233
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
- package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
- /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
|
@@ -3,7 +3,9 @@ import path from 'node:path';
|
|
|
3
3
|
import { canonical, isInside } from './config.mjs';
|
|
4
4
|
import { readJsonFile as readJson } from '../../lib/json.mjs';
|
|
5
5
|
import { frameworkPinnedRootFiles } from './framework-pinned.mjs';
|
|
6
|
-
import { isUnshadowedCommonJsRequire, reachableViolation, relativePath,
|
|
6
|
+
import { exportTargetStrings, isUnshadowedCommonJsRequire, reachableViolation, relativePath, unwrapExpression } from './typescript.mjs';
|
|
7
|
+
import { normalizedSymbolValue, returnedExpressions as sharedReturnedExpressions } from './ast-walks.mjs';
|
|
8
|
+
import { sourceLocation } from '../../lib/ts-ast.mjs';
|
|
7
9
|
|
|
8
10
|
const FEATURE_TIERS = new Set(['pages', 'layouts', 'overlays']);
|
|
9
11
|
const COMPONENT_TIERS = new Set(['blocks', 'composites', 'branches', 'leaves']);
|
|
@@ -52,23 +54,16 @@ function violation(config, sourceFile, node, ruleId, message, extra = {}) {
|
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
function exportTargets(value) {
|
|
56
|
-
if (typeof value === 'string') return [value];
|
|
57
|
-
if (Array.isArray(value)) return value.flatMap(exportTargets);
|
|
58
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) return Object.values(value).flatMap(exportTargets);
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
57
|
function grammarExport(manifest, packageName, specifier) {
|
|
63
58
|
const key = specifier === packageName ? '.' : `.${specifier.slice(packageName.length)}`;
|
|
64
59
|
const value = manifest?.exports?.[key];
|
|
65
|
-
const targets =
|
|
60
|
+
const targets = exportTargetStrings(value);
|
|
66
61
|
return targets.length > 0 && targets.every(target => target.startsWith('./') && !target.includes('\\') && !target.split('/').includes('..'));
|
|
67
62
|
}
|
|
68
63
|
|
|
69
64
|
function grammarExportTargets(manifest, packageRoot, packageName, specifier) {
|
|
70
65
|
const key = specifier === packageName ? '.' : `.${specifier.slice(packageName.length)}`;
|
|
71
|
-
return
|
|
66
|
+
return exportTargetStrings(manifest?.exports?.[key]).filter(target => target.startsWith('./') && !target.includes('\\') && !target.split('/').includes('..'))
|
|
72
67
|
.map(target => canonical(path.resolve(packageRoot, target)));
|
|
73
68
|
}
|
|
74
69
|
|
|
@@ -106,7 +101,7 @@ function checkGrammar(config, context) {
|
|
|
106
101
|
const violations = [];
|
|
107
102
|
const local = context.workspaces.find(workspace => workspace.name === grammar.package);
|
|
108
103
|
// Node resolution per consumer: in an npm-workspaces monorepo a consumer whose range differs from the
|
|
109
|
-
// hoisted copy gets its own apps/<app>/node_modules/<package> (
|
|
104
|
+
// hoisted copy gets its own apps/<app>/node_modules/<package> (one consumer: apps/app on 0.5.0 beside a hoisted
|
|
110
105
|
// 0.4.11). Every consumer is judged against the copy it actually resolves, never the hoisted one alone.
|
|
111
106
|
// An app installs once, at its root (config.packageRoot): a side folder has no node_modules of its own.
|
|
112
107
|
const installRoot = config.packageRoot ?? config.root;
|
|
@@ -391,7 +386,7 @@ function checkCustomHookLocations(config, context, sourceFile, roots) {
|
|
|
391
386
|
const violations = [];
|
|
392
387
|
const seen = new Set();
|
|
393
388
|
const report = (symbol, node) => {
|
|
394
|
-
const identity =
|
|
389
|
+
const identity = normalizedSymbolValue(ts, checker, symbol) ?? symbol;
|
|
395
390
|
if (!identity || seen.has(identity) || checker.getTypeOfSymbolAtLocation(identity, node).getCallSignatures().length === 0) return;
|
|
396
391
|
seen.add(identity);
|
|
397
392
|
violations.push(violation(config, sourceFile, node, 'FE_CUSTOM_HOOK_LOCATION',
|
|
@@ -411,7 +406,7 @@ function checkCustomHookLocations(config, context, sourceFile, roots) {
|
|
|
411
406
|
const module = checker.getSymbolAtLocation(sourceFile);
|
|
412
407
|
for (const exposed of module ? checker.getExportsOfModule(module) : []) {
|
|
413
408
|
if (!/^use[A-Z0-9]/.test(exposed.name)) continue;
|
|
414
|
-
const identity =
|
|
409
|
+
const identity = normalizedSymbolValue(ts, checker, exposed);
|
|
415
410
|
for (const declaration of identity?.getDeclarations?.() ?? []) if (declaration.getSourceFile() === sourceFile) {
|
|
416
411
|
const node = declarationName(ts, declaration) ?? declaration;
|
|
417
412
|
report(identity, node);
|
|
@@ -473,17 +468,6 @@ function checkPureAndData(config, context, sourceFile, roots) {
|
|
|
473
468
|
return violations;
|
|
474
469
|
}
|
|
475
470
|
|
|
476
|
-
function unaliasSymbol(ts, checker, value) {
|
|
477
|
-
let symbol = value ?? null;
|
|
478
|
-
const seen = new Set();
|
|
479
|
-
while (symbol && (symbol.flags & ts.SymbolFlags.Alias) && !seen.has(symbol)) {
|
|
480
|
-
seen.add(symbol);
|
|
481
|
-
const target = checker.getAliasedSymbol(symbol);
|
|
482
|
-
if (!target || target === symbol) break;
|
|
483
|
-
symbol = target;
|
|
484
|
-
}
|
|
485
|
-
return symbol;
|
|
486
|
-
}
|
|
487
471
|
|
|
488
472
|
function selectedSymbol(ts, checker, expression) {
|
|
489
473
|
const selected = unwrapExpression(ts, expression);
|
|
@@ -498,19 +482,9 @@ function selectedSymbol(ts, checker, expression) {
|
|
|
498
482
|
return null;
|
|
499
483
|
}
|
|
500
484
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (!body || !ts.isBlock(body)) return [];
|
|
505
|
-
const returned = [];
|
|
506
|
-
const visit = node => {
|
|
507
|
-
if (node !== body && ts.isFunctionLike(node)) return;
|
|
508
|
-
if (ts.isReturnStatement(node) && node.expression) returned.push(node.expression);
|
|
509
|
-
else ts.forEachChild(node, visit);
|
|
510
|
-
};
|
|
511
|
-
visit(body);
|
|
512
|
-
return returned;
|
|
513
|
-
}
|
|
485
|
+
/* The wider boundary set: `ts.isFunctionLike` (includes call signatures' declarations) rather than the
|
|
486
|
+
* four default callable kinds. */
|
|
487
|
+
const returnedExpressions = (ts, declaration) => sharedReturnedExpressions(ts, declaration, { functionLike: ts.isFunctionLike });
|
|
514
488
|
|
|
515
489
|
function knownWorldImport(specifier, imported) {
|
|
516
490
|
if (specifier === 'next-intl') return WORLD_INTL_CALLS.has(imported);
|
|
@@ -611,7 +585,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
611
585
|
};
|
|
612
586
|
|
|
613
587
|
const intrinsicUiHookSymbol = (symbol, checker) => {
|
|
614
|
-
const identity =
|
|
588
|
+
const identity = normalizedSymbolValue(ts, checker, symbol);
|
|
615
589
|
const declarations = identity?.getDeclarations?.() ?? [];
|
|
616
590
|
return declarations.length > 0 && declarations.every(declaration => intrinsicUiHookFile(declaration.getSourceFile().fileName));
|
|
617
591
|
};
|
|
@@ -668,7 +642,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
668
642
|
: ts.isPropertyAccessExpression(selected) ? selected.name.text : null;
|
|
669
643
|
if (['forwardRef', 'memo'].includes(name) && expression.arguments[0]) return expressionFunctions(expression.arguments[0], checker, seen);
|
|
670
644
|
}
|
|
671
|
-
const symbol =
|
|
645
|
+
const symbol = normalizedSymbolValue(ts, checker, selectedSymbol(ts, checker, expression));
|
|
672
646
|
if (!symbol || seen.has(symbol)) return [];
|
|
673
647
|
const nextSeen = new Set(seen).add(symbol);
|
|
674
648
|
const functions = [];
|
|
@@ -692,7 +666,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
692
666
|
for (const [namespace, declaration] of info.namespaces) if (namespace === raw) {
|
|
693
667
|
return declaration.worldEdge || knownWorldImport(declaration.specifier, propertyName ?? '');
|
|
694
668
|
}
|
|
695
|
-
const identity =
|
|
669
|
+
const identity = normalizedSymbolValue(ts, info.checker, raw);
|
|
696
670
|
return (identity?.getDeclarations?.() ?? []).some(declaration => {
|
|
697
671
|
const fileName = declaration.getSourceFile().fileName;
|
|
698
672
|
return insideAny(roots.hooks, fileName) || insideAny(roots.transport, fileName);
|
|
@@ -716,7 +690,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
716
690
|
if (namespaceIsWorld(namespace, sourceFile, property)) return true;
|
|
717
691
|
if (symbolIsWorld(selectedSymbol(ts, checker, expression), sourceFile)) return true;
|
|
718
692
|
}
|
|
719
|
-
const symbol =
|
|
693
|
+
const symbol = normalizedSymbolValue(ts, checker, selectedSymbol(ts, checker, expression));
|
|
720
694
|
if (!symbol || seen.has(symbol)) return false;
|
|
721
695
|
const nextSeen = new Set(seen).add(symbol);
|
|
722
696
|
const declarations = symbol.getDeclarations?.() ?? [];
|
|
@@ -763,7 +737,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
763
737
|
if (name === 'map' && expression.arguments[0]) return expressionFunctions(expression.arguments[0], checker)
|
|
764
738
|
.some(fn => functionHasRender(fn, checker, seen));
|
|
765
739
|
}
|
|
766
|
-
const symbol =
|
|
740
|
+
const symbol = normalizedSymbolValue(ts, checker, selectedSymbol(ts, checker, ts.isCallExpression(expression) ? expression.expression : expression));
|
|
767
741
|
if (!symbol || seen.has(symbol)) return false;
|
|
768
742
|
const nextSeen = new Set(seen).add(symbol);
|
|
769
743
|
if (ts.isIdentifier(expression)) for (const declaration of symbol.getDeclarations?.() ?? []) {
|
|
@@ -839,7 +813,7 @@ function checkWorldRenderBoundaries(config, context, roots) {
|
|
|
839
813
|
if (ts.isArrayLiteralExpression(expression)) return expression.elements.length > 0
|
|
840
814
|
&& expression.elements.every(item => renderBoundary(item, checker, seen, allowEmpty, expectedFile));
|
|
841
815
|
if (ts.isIdentifier(expression)) {
|
|
842
|
-
const symbol =
|
|
816
|
+
const symbol = normalizedSymbolValue(ts, checker, checker.getSymbolAtLocation(expression));
|
|
843
817
|
if (!symbol || seen.has(symbol)) return false;
|
|
844
818
|
const nextSeen = new Set(seen).add(symbol);
|
|
845
819
|
const values = (symbol.getDeclarations?.() ?? []).flatMap(declaration => ts.isVariableDeclaration(declaration) && declaration.initializer
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import { gitOutput } from '../../api/git/lib.mjs';
|
|
3
|
+
import { lsFiles } from '../../api/git/ls-files.mjs'; import { revParseQuery } from '../../api/git/rev-parse-query.mjs'; import { configGet } from '../../api/git/config-get.mjs'; import { gitOutputOf } from '../../lib/git.mjs';
|
|
5
4
|
import { repositoryName } from '../repo-identity.mjs';
|
|
6
5
|
import { braceVariants } from '../../lib/glob.mjs';
|
|
7
6
|
import { createSlotResolver, loadSlotManifest, openHfs } from '../slots.mjs';
|
|
8
7
|
import { isFeTestPath } from '../rules/fe-no-tests.mjs';
|
|
8
|
+
import { readTextFile } from '../../lib/read-text.mjs';
|
|
9
|
+
import { managedScriptNames } from './managed-scripts.mjs';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* HFS repository-tree check (knowledge/hfs/README.md): every StarCi repository is an
|
|
@@ -17,6 +18,7 @@ import { isFeTestPath } from '../rules/fe-no-tests.mjs';
|
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
20
|
export const HFS_RULE_IDS = [
|
|
21
|
+
'BE_TEST_TOPOLOGY',
|
|
20
22
|
'HFS_APPS_REQUIRED',
|
|
21
23
|
'HFS_APP_LAYOUT_INVALID',
|
|
22
24
|
'HFS_E2E_IN_AUTOMATIC_GATE',
|
|
@@ -37,7 +39,6 @@ export const HFS_RULE_IDS = [
|
|
|
37
39
|
'HFS_ROOT_SRC_FORBIDDEN_FE',
|
|
38
40
|
'HFS_SRC_LAYOUT_INVALID',
|
|
39
41
|
'HFS_STACKS_IN_SIDE',
|
|
40
|
-
'HFS_TEST_KIND_RETIRED',
|
|
41
42
|
'HFS_WORK_IN_FE',
|
|
42
43
|
];
|
|
43
44
|
|
|
@@ -46,7 +47,7 @@ const RUNTIME_ROOT_MARKDOWN = new Set(['README.md', 'CONTEXT.md', 'CONTRIBUTING.
|
|
|
46
47
|
const PRODUCT_ROOT_MARKDOWN = new Set(['README.md']);
|
|
47
48
|
const README_SECTIONS = ['Overview', 'Stack', 'Repository layout', 'Development'];
|
|
48
49
|
const BACKEND_SRC_CHILDREN = new Set(['features', 'modules', 'tests']);
|
|
49
|
-
const
|
|
50
|
+
const moduleTiersOf = resolver => new Set(resolver.slots().filter(slot => slot.profiles.includes('be')).flatMap(slot => braceVariants(slot.path)).map(variant => /^src\/modules\/([a-z][a-z-]*)\//.exec(variant)?.[1]).filter(Boolean)); // the folders of src/modules/ the slot manifest knows (domain, platform, integrations, events, queues, projections)
|
|
50
51
|
// Owner test layout 2026-09-30: unit `<name>.spec.ts`, integration, e2e and contract by folder and suffix; int-spec and harness-spec stay banned.
|
|
51
52
|
const RETIRED_TEST_SUFFIX = /\.(?:int|harness)-spec\.[cm]?[jt]sx?$/u;
|
|
52
53
|
const RETIRED_TEST_FOLDER = /^src\/tests\/(?:harness|live|e2e\/live)(?:\/|$)/u;
|
|
@@ -100,7 +101,7 @@ function slotTestChildren(resolver) {
|
|
|
100
101
|
*/
|
|
101
102
|
function ownsGitTopLevel(root) {
|
|
102
103
|
try {
|
|
103
|
-
const top =
|
|
104
|
+
const top = gitOutputOf(revParseQuery(['--show-toplevel'], { cwd: root }), 'git rev-parse --show-toplevel').trim();
|
|
104
105
|
const same = (a, b) => (process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b);
|
|
105
106
|
return same(fs.realpathSync(path.resolve(top)), fs.realpathSync(path.resolve(root)));
|
|
106
107
|
} catch {
|
|
@@ -113,8 +114,8 @@ function gitPaths(root) {
|
|
|
113
114
|
try {
|
|
114
115
|
// An empty index is still a Git tree. Falling back to disk in that case would count
|
|
115
116
|
// untracked build output as repository content.
|
|
116
|
-
|
|
117
|
-
const out =
|
|
117
|
+
gitOutputOf(revParseQuery(['--is-inside-work-tree'], { cwd: root }), 'git rev-parse --is-inside-work-tree');
|
|
118
|
+
const out = gitOutputOf(lsFiles(['--cached', '-z', '--', '.'], { cwd: root, maxBuffer: 256 * 1024 * 1024 }), 'git ls-files');
|
|
118
119
|
return out.split('\0').filter(Boolean);
|
|
119
120
|
} catch {
|
|
120
121
|
return null;
|
|
@@ -181,11 +182,12 @@ function treeView(root) {
|
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
// `hostname` is a WHATWG URL hostname: a name never holds ':', so after the brackets go only an IPv6 literal does.
|
|
184
186
|
function privateHost(hostname) {
|
|
185
187
|
const host = hostname.toLowerCase().replace(/^\[|\]$/g, '').replace(/\.$/, '');
|
|
186
188
|
if (host === 'localhost' || host === '::1' || host === '0.0.0.0' ||
|
|
187
189
|
/(?:\.localhost|\.local|\.internal|\.lan)$/u.test(host)) return true;
|
|
188
|
-
if (
|
|
190
|
+
if (host.includes(':')) return /^(?:::|f[cd][0-9a-f]*:|fe[89ab][0-9a-f]*:)/iu.test(host);
|
|
189
191
|
if (!host.includes('.')) return true;
|
|
190
192
|
const octets = host.split('.');
|
|
191
193
|
if (octets.length !== 4 || !octets.every(part => /^\d{1,3}$/u.test(part) && Number(part) <= 255)) return false;
|
|
@@ -197,20 +199,6 @@ function privateHost(hostname) {
|
|
|
197
199
|
// The scripts the README Development section shows; each is required only when the managed package-scripts template of the
|
|
198
200
|
// profile (packages/hfs/templates/<profile>/package-scripts/package.json, the one source of the managed script names) has it.
|
|
199
201
|
const DEVELOPMENT_SCRIPTS = ['typecheck', 'lint', 'build', 'test'];
|
|
200
|
-
// The templates sit beside the runtime in a checkout (packages/hfs/templates) and one level above the bundled runtime of @starci/hfs.
|
|
201
|
-
const TEMPLATE_ROOTS = [path.resolve(import.meta.dirname, '..', '..', '..', 'packages', 'hfs', 'templates'), path.resolve(import.meta.dirname, '..', '..', '..', '..', 'templates')];
|
|
202
|
-
const managedScriptCache = new Map();
|
|
203
|
-
|
|
204
|
-
/** The script names of the managed package-scripts template of a profile. The template holds a {{appScripts}} placeholder, so it is read by key, not parsed as JSON. */
|
|
205
|
-
function managedScriptNames(profile) {
|
|
206
|
-
if (!managedScriptCache.has(profile)) {
|
|
207
|
-
const file = TEMPLATE_ROOTS.map(dir => path.join(dir, profile, 'package-scripts', 'package.json')).find(candidate => fs.existsSync(candidate));
|
|
208
|
-
if (!file) throw Error(`The managed package-scripts template of profile ${profile} cannot be found next to the runtime.`);
|
|
209
|
-
managedScriptCache.set(profile, new Set([...fs.readFileSync(file, 'utf8').matchAll(/^\s*"([A-Za-z0-9:_.-]+)":\s*"/gmu)].map(match => match[1])));
|
|
210
|
-
}
|
|
211
|
-
return managedScriptCache.get(profile);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
202
|
/** The README command that runs a managed script: `npm test` for test, `npm run <name>` for the others (never a longer script name that starts with it). */
|
|
215
203
|
function scriptCommand(name) {
|
|
216
204
|
const escaped = name.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
@@ -218,7 +206,7 @@ function scriptCommand(name) {
|
|
|
218
206
|
}
|
|
219
207
|
|
|
220
208
|
/** Presentation checks shared by the product HFS gate and this runtime's own standalone gate. */
|
|
221
|
-
export function checkRepoPresentation({ root, runtime = false, tree = treeView(root), profile = 'app' }) {
|
|
209
|
+
export function checkRepoPresentation({ root, runtime = false, tree = treeView(root), profile = 'app', edition = 'full' }) {
|
|
222
210
|
const violations = [];
|
|
223
211
|
const finding = (ruleId, entry, message, line = 1) => violations.push({ ruleId, path: entry, line, column: 1, message });
|
|
224
212
|
for (const entry of tree.top) {
|
|
@@ -268,7 +256,7 @@ export function checkRepoPresentation({ root, runtime = false, tree = treeView(r
|
|
|
268
256
|
};
|
|
269
257
|
if (!runtime && headings.some(item => item.name === 'Development')) {
|
|
270
258
|
const development = sectionBody('Development');
|
|
271
|
-
const scripts = DEVELOPMENT_SCRIPTS.filter(name => managedScriptNames(profile).has(name));
|
|
259
|
+
const scripts = DEVELOPMENT_SCRIPTS.filter(name => managedScriptNames(profile, edition).has(name));
|
|
272
260
|
const commands = [/npm (?:ci|install)/u, ...scripts.map(scriptCommand)];
|
|
273
261
|
if (commands.some(command => !command.test(development)))
|
|
274
262
|
finding('HFS_README_DEVELOPMENT_INCOMPLETE', 'README.md',
|
|
@@ -319,9 +307,7 @@ const UNIT_RUN_SCRIPTS = ['test', 'test:unit', 'test:ci', 'test:affected', 'test
|
|
|
319
307
|
const runsE2e = text => E2E_COMMAND.test(String(text).replace(/--ignore-pattern[= ]+(?:"[^"]*"|'[^']*'|\S+)/gu, ''));
|
|
320
308
|
const withoutComments = text => text.split('\n').filter(line => !/^\s*#/u.test(line)).join('\n');
|
|
321
309
|
|
|
322
|
-
|
|
323
|
-
try { return fs.readFileSync(path.join(root, ...relative.split('/')), 'utf8'); } catch { return null; }
|
|
324
|
-
}
|
|
310
|
+
const readText = readTextFile;
|
|
325
311
|
|
|
326
312
|
// A repository-local core.hooksPath that points anywhere but husky's own directory switches the commit and push
|
|
327
313
|
// hooks off for that clone (a lane once redirected it to skip husky), so the gate every other clone runs never ran.
|
|
@@ -331,10 +317,9 @@ const HUSKY_HOOKS_PATH = /^\.husky(?:\/_)?\/?$/u;
|
|
|
331
317
|
/** A directory that is not the top level of its own work tree has no hooks of its own: the check is not applicable there. */
|
|
332
318
|
function hooksPathNotRedirected({ root, finding }) {
|
|
333
319
|
if (!ownsGitTopLevel(root)) return { status: 'not-applicable', reason: 'the checked root is not the top level of its own Git work tree, so it has no hooks of its own' };
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
} catch { return { status: 'checked' }; } // key not set: nothing is redirected
|
|
320
|
+
const set = configGet(root, 'core.hooksPath', { local: true });
|
|
321
|
+
if (!set.ok) return { status: 'checked' }; // key not set: nothing is redirected
|
|
322
|
+
const value = set.stdout;
|
|
338
323
|
if (value && !HUSKY_HOOKS_PATH.test(value.replaceAll('\\', '/')))
|
|
339
324
|
finding('HFS_HOOKS_PATH_REDIRECTED', '.git/config', `core.hooksPath is set to ${value} in this clone. Unset it (git config --local --unset core.hooksPath) and let husky own the hooks; a redirected path skips the pre-commit and pre-push gates.`);
|
|
340
325
|
return { status: 'checked' };
|
|
@@ -450,7 +435,7 @@ export function checkHfs(config) {
|
|
|
450
435
|
continue;
|
|
451
436
|
}
|
|
452
437
|
if (!tree.hasDir(`apps/${app}/src`)) missing.push('src/');
|
|
453
|
-
// A back-end app must hold exactly what the slot of its kind requires (
|
|
438
|
+
// A back-end app must hold exactly what the slot of its kind requires (each kind's slot names its own requires).
|
|
454
439
|
if (backend) for (const required of resolver.requiredFiles(`apps/${app}/src/main.ts`)) {
|
|
455
440
|
const directory = required.endsWith('/');
|
|
456
441
|
if (!(directory ? tree.hasDir(required.slice(0, -1)) : tree.hasFile(required))) missing.push(required.slice(`apps/${app}/`.length));
|
|
@@ -471,8 +456,8 @@ export function checkHfs(config) {
|
|
|
471
456
|
}
|
|
472
457
|
}
|
|
473
458
|
for (const tier of tree.children('src/modules').sort()) {
|
|
474
|
-
if (!
|
|
475
|
-
finding('HFS_MODULE_TIER_INVALID', `src/modules/${tier}`, `Module tier ${tier} is not one of
|
|
459
|
+
if (!moduleTiersOf(resolver).has(tier)) {
|
|
460
|
+
finding('HFS_MODULE_TIER_INVALID', `src/modules/${tier}`, `Module tier ${tier} is not one of ${[...moduleTiersOf(resolver)].sort().join(', ')}.`);
|
|
476
461
|
}
|
|
477
462
|
}
|
|
478
463
|
const testChildren = slotTestChildren(resolver);
|
|
@@ -488,11 +473,11 @@ export function checkHfs(config) {
|
|
|
488
473
|
// live/; a backend spec sits by its kind (beside its subject, or under src/tests/{integration,e2e,contract}/).
|
|
489
474
|
for (const file of tree.files()) {
|
|
490
475
|
if (RETIRED_TEST_SUFFIX.test(file))
|
|
491
|
-
finding('
|
|
476
|
+
finding('BE_TEST_TOPOLOGY', file, `${file} uses a retired test kind. Only unit *.spec.ts, *.integration-spec.ts, *.e2e-spec.ts and *.contract-spec.ts exist, each in its own folder under src/tests/.`);
|
|
492
477
|
else if (backend && RETIRED_TEST_FOLDER.test(file))
|
|
493
|
-
finding('
|
|
478
|
+
finding('BE_TEST_TOPOLOGY', file, `${file} sits in a retired test folder. Unit specs sit beside their subject, flows go under src/tests/e2e/<area>/ and test infrastructure under src/tests/world/.`);
|
|
494
479
|
else if (backend && /^src\/tests\//u.test(file) && EXTRA_TEST_CONFIG.test(file))
|
|
495
|
-
finding('
|
|
480
|
+
finding('BE_TEST_TOPOLOGY', file, `${file} is a per-lane test config. One root jest.config.js declares exactly the unit, integration, e2e and contract projects.`);
|
|
496
481
|
}
|
|
497
482
|
|
|
498
483
|
if (backend) testTreesOutOfDefaultProgram({ root: config.root, tree, finding });
|
|
@@ -511,13 +496,13 @@ export function checkHfs(config) {
|
|
|
511
496
|
|
|
512
497
|
/**
|
|
513
498
|
* The HFS tree check of the app root (`resolver` is the app's): the README, the root entries the app-root slots allow and require,
|
|
514
|
-
* the automatic gates (hooks, the root scripts they call, the workflows) and the hooks path. `
|
|
499
|
+
* the automatic gates (hooks, the root scripts they call, the workflows) and the hooks path. `starci app check` runs it once per app; the
|
|
515
500
|
* machine runs checkHfs once per side folder.
|
|
516
501
|
*/
|
|
517
502
|
export function checkAppRoot({ root, resolver, tree = treeView(root) }) {
|
|
518
503
|
const violations = [];
|
|
519
504
|
const finding = (ruleId, entry, message) => violations.push({ ruleId, path: entry, line: 1, column: 1, message });
|
|
520
|
-
violations.push(...checkRepoPresentation({ root, tree, profile: 'app' }).violations);
|
|
505
|
+
violations.push(...checkRepoPresentation({ root, tree, profile: 'app', edition: resolver.repo?.edition ?? 'full' }).violations);
|
|
521
506
|
const allowed = slotRootEntries(resolver, 'app');
|
|
522
507
|
for (const entry of [...tree.top].sort()) {
|
|
523
508
|
if (NON_NPM_ENTRIES.has(entry) || /\.md$/iu.test(entry)) continue;
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* R106 `i18n-keys` (FE_I18N_KEYS). The catalogs of an app (`apps/<app>/src/modules/i18n/messages/<locale>.json`, slot fe.modules.i18n) and
|
|
6
|
-
* the source that reads them agree, both ways. The key sets of the locales agree with each other (FE_I18N_CATALOG, `
|
|
6
|
+
* the source that reads them agree, both ways. The key sets of the locales agree with each other (FE_I18N_CATALOG, `starci app check`); this
|
|
7
7
|
* rule holds the source to the catalogs:
|
|
8
8
|
*
|
|
9
9
|
* - a key read with a literal is in every locale: `useTranslations("ns")` / `getTranslations("ns")` (also `{ namespace }`, `await`)
|
|
@@ -31,6 +31,11 @@ import { checkUnitSpecProviders, UNIT_SPEC_PROVIDERS_RULE_IDS } from './unit-spe
|
|
|
31
31
|
import { checkInjectionTokenExported, INJECTION_TOKEN_EXPORTED_RULE_IDS } from './injection-token-exported.mjs';
|
|
32
32
|
import { checkContractFixtureGuard, CONTRACT_FIXTURE_GUARD_RULE_IDS } from './contract-fixture-guard.mjs';
|
|
33
33
|
import { checkSchemaOwner, SCHEMA_OWNER_RULE_IDS } from './schema-owner.mjs';
|
|
34
|
+
import { checkSqlReturning, SQL_RETURNING_RULE_IDS } from './sql-returning.mjs';
|
|
35
|
+
import { checkContextOwner, CONTEXT_OWNER_RULE_IDS } from './context-owner.mjs';
|
|
36
|
+
import { checkContextCoupling, CONTEXT_COUPLING_RULE_IDS } from './context-coupling.mjs';
|
|
37
|
+
import { checkContextTransaction, CONTEXT_TRANSACTION_RULE_IDS } from './context-transaction.mjs';
|
|
38
|
+
import { checkContextPlatformTables, CONTEXT_PLATFORM_TABLES_RULE_IDS } from './context-platform-tables.mjs';
|
|
34
39
|
import { checkModulePerTransport, MODULE_PER_TRANSPORT_RULE_IDS } from './module-per-transport.mjs';
|
|
35
40
|
import { checkBackgroundUnowned, BACKGROUND_UNOWNED_RULE_IDS } from './background-unowned.mjs';
|
|
36
41
|
import { checkTransportOwner, TRANSPORT_OWNER_RULE_IDS } from './transport-owner.mjs';
|
|
@@ -42,6 +47,7 @@ import { checkPackageShape, PACKAGE_SHAPE_RULE_IDS } from './package-shape.mjs';
|
|
|
42
47
|
import { checkFeSlotAllows, FE_SLOT_ALLOWS_RULE_IDS } from './fe-slot-allows.mjs';
|
|
43
48
|
import { checkI18nKeys, I18N_KEYS_RULE_IDS } from './i18n-keys.mjs';
|
|
44
49
|
import { checkDocLanguage, DOC_LANGUAGE_RULE_IDS } from './doc-language.mjs';
|
|
50
|
+
import { checkBackendSupabase, checkFrontendSupabase, BACKEND_SUPABASE_RULE_IDS, FRONTEND_SUPABASE_RULE_IDS } from './supabase.mjs';
|
|
45
51
|
import { LINT_CODES, onLintSurface } from './surface.mjs';
|
|
46
52
|
|
|
47
53
|
export { REGISTRATION_RULE_IDS, SWR_DATA_RULE_IDS };
|
|
@@ -68,10 +74,16 @@ const BACKEND_MACHINE = {
|
|
|
68
74
|
unitSpecProviders: [checkUnitSpecProviders, UNIT_SPEC_PROVIDERS_RULE_IDS],
|
|
69
75
|
injectionTokenExported: [checkInjectionTokenExported, INJECTION_TOKEN_EXPORTED_RULE_IDS],
|
|
70
76
|
schemaOwner: [checkSchemaOwner, SCHEMA_OWNER_RULE_IDS],
|
|
77
|
+
sqlReturning: [checkSqlReturning, SQL_RETURNING_RULE_IDS],
|
|
78
|
+
contextOwner: [checkContextOwner, CONTEXT_OWNER_RULE_IDS],
|
|
79
|
+
contextCoupling: [checkContextCoupling, CONTEXT_COUPLING_RULE_IDS],
|
|
80
|
+
contextTransaction: [checkContextTransaction, CONTEXT_TRANSACTION_RULE_IDS],
|
|
81
|
+
contextPlatformTables: [checkContextPlatformTables, CONTEXT_PLATFORM_TABLES_RULE_IDS],
|
|
71
82
|
modulePerTransport: [checkModulePerTransport, MODULE_PER_TRANSPORT_RULE_IDS],
|
|
72
83
|
backgroundUnowned: [checkBackgroundUnowned, BACKGROUND_UNOWNED_RULE_IDS],
|
|
73
84
|
testWorldFiles: [checkTestWorldFiles, TEST_WORLD_FILES_RULE_IDS],
|
|
74
85
|
contractFixtureGuard: [checkContractFixtureGuard, CONTRACT_FIXTURE_GUARD_RULE_IDS],
|
|
86
|
+
supabaseBackend: [checkBackendSupabase, BACKEND_SUPABASE_RULE_IDS],
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
// The frontend repository machine (R21, R50, R54, R55, R56, R63, R94, R106): same shape, run for a front-end repository only.
|
|
@@ -84,6 +96,7 @@ const FRONTEND_MACHINE = {
|
|
|
84
96
|
crossAppDuplicate: [checkCrossAppDuplicate, CROSS_APP_DUPLICATE_RULE_IDS],
|
|
85
97
|
feSlotAllows: [checkFeSlotAllows, FE_SLOT_ALLOWS_RULE_IDS],
|
|
86
98
|
i18nKeys: [checkI18nKeys, I18N_KEYS_RULE_IDS],
|
|
99
|
+
supabaseFrontend: [checkFrontendSupabase, FRONTEND_SUPABASE_RULE_IDS],
|
|
87
100
|
};
|
|
88
101
|
|
|
89
102
|
/** Errors the machine reports when a program cannot be built or an edge cannot be proven: it cannot judge, so it refuses. */
|
|
@@ -125,8 +138,8 @@ const FRONTEND_RULE_IDS = [
|
|
|
125
138
|
'FE_SOURCE_LAYOUT_INVALID',
|
|
126
139
|
'FE_WORLD_OWNER_RENDER_BOUNDARY',
|
|
127
140
|
];
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
const OWNER_RULE_IDS = ['ARCH_OWNER_EXPORT_BYPASS', 'ARCH_OWNER_EXPORT_STAR'];
|
|
142
|
+
const GRAMMAR_RULE_IDS = ['ARCH_GRAMMAR_CONTRACT_INVALID', 'ARCH_GRAMMAR_EXPORT_BYPASS'];
|
|
130
143
|
|
|
131
144
|
/**
|
|
132
145
|
* Every code the machine reports as an error because it cannot judge (a check that cannot run is an error, never a pass).
|
|
@@ -152,7 +165,7 @@ export const HFS_MACHINE_RULE_IDS = Object.freeze((() => {
|
|
|
152
165
|
return [...new Set(machineIds().filter((id) => !ordinary.has(id)))].sort();
|
|
153
166
|
})());
|
|
154
167
|
|
|
155
|
-
/** Every code the machine can emit, derived from the rule id lists of its checks (`
|
|
168
|
+
/** Every code the machine can emit, derived from the rule id lists of its checks (`starci app check` ships exactly these why entries). */
|
|
156
169
|
export const ARCHITECTURE_RULE_IDS = Object.freeze([...new Set([
|
|
157
170
|
...COMMON_RULE_IDS, ...BACKEND_RULE_IDS, ...FRONTEND_RULE_IDS, ...HFS_RULE_IDS, ...TIER_RULE_IDS, ...REACHABILITY_RULE_IDS,
|
|
158
171
|
...DEAD_EXPORT_RULE_IDS, ...DOC_LANGUAGE_RULE_IDS, ...REQUIRED_FILE_RULE_IDS, ...CLONE_RULE_IDS, ...OWNER_RULE_IDS, ...GRAMMAR_RULE_IDS,
|
|
@@ -253,7 +266,7 @@ export function checkArchitecture({ repositoryRoot, injectedTypeScript, paths =
|
|
|
253
266
|
const asViolation = item => LINT_CODES.has(item.ruleId);
|
|
254
267
|
const allErrors = context.errors.filter(item => item.ruleId.startsWith('ARCH_TSCONFIG_') || !item.path || inScope(item));
|
|
255
268
|
const obligations = surface === 'all' ? violations : [...violations, ...context.errors.filter(asViolation).filter(inScope)];
|
|
256
|
-
// The lint surface is what an editor can show on a line of a TypeScript file; `
|
|
269
|
+
// The lint surface is what an editor can show on a line of a TypeScript file; `starci app check` keeps every other finding of the machine.
|
|
257
270
|
const onSurface = item => surface === 'all' || onLintSurface(config.root, item) === (surface === 'lint');
|
|
258
271
|
const errors = stable(surface === 'all' ? allErrors : allErrors.filter(item => !asViolation(item)));
|
|
259
272
|
const scopedViolations = stable(obligations.filter(inScope).filter(onSurface));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { canonical } from './config.mjs';
|
|
3
|
-
import { sourceLocation } from '
|
|
3
|
+
import { sourceLocation } from '../../lib/ts-ast.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The small TypeScript reading kit the R33/R38/R39/R41/R45/R84/R86 machine checks share (connection-map, sql-owner,
|
|
@@ -115,18 +115,51 @@ export function machineKit({ config, context, graph }) {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
/** The `{ provide: <token>, ... }` provider literals of `file` whose token is `name` imported from `moduleName`, in source order. */
|
|
118
|
+
const HELPER_DEPTH = 4;
|
|
119
|
+
|
|
120
|
+
/** The body a call or a spread identifier resolves to when it is a function or a const declared in a file of the program (not a package), else null. */
|
|
121
|
+
const helperBodyOf = (checker, node) => {
|
|
122
|
+
const target = ts.isCallExpression(node) ? node.expression : node;
|
|
123
|
+
for (const declaration of declarationsOf(checker, ts.isPropertyAccessExpression(target) ? target.name : target)) {
|
|
124
|
+
if (!graphPath(declaration)) continue;
|
|
125
|
+
if (ts.isFunctionDeclaration(declaration) && declaration.body) return { declaration, body: declaration.body };
|
|
126
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) return { declaration, body: declaration.initializer };
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The `{ provide: <token>, ... }` provider literals of `file` whose token is `name` imported from `moduleName`, in source order.
|
|
133
|
+
* A call or a spread of a function or a const declared in another file of the program is followed (to a depth of four), so a
|
|
134
|
+
* helper that builds the `APP_GUARD` or `APP_FILTER` entries outside the app root file is read too: the entry's `node` is then the
|
|
135
|
+
* call or spread inside the app root (where the entry is provided), its `useClass` and `checker` those of the helper's file.
|
|
136
|
+
*/
|
|
118
137
|
const providersOf = (file, name, moduleName) => {
|
|
119
|
-
const checker = checkerOf(file.sourceFile);
|
|
120
138
|
const found = [];
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
const scan = (root, anchor, depth) => {
|
|
141
|
+
const checker = checkerOf(root.getSourceFile());
|
|
142
|
+
walk(root, node => {
|
|
143
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
144
|
+
const provide = propertyOf(node, 'provide');
|
|
145
|
+
if (provide && isImportOf(checker, valueOfProperty(provide), name, moduleName)) {
|
|
146
|
+
const use = propertyOf(node, 'useClass');
|
|
147
|
+
found.push({ node: anchor ?? node, useClass: use ? valueOfProperty(use) : null, checker });
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
const reference = ts.isSpreadElement(node) ? node.expression : (ts.isCallExpression(node) ? node : null);
|
|
152
|
+
if (reference && depth < HELPER_DEPTH) {
|
|
153
|
+
const helper = helperBodyOf(checker, reference);
|
|
154
|
+
if (helper && !seen.has(helper.declaration)) {
|
|
155
|
+
seen.add(helper.declaration);
|
|
156
|
+
scan(helper.body, anchor ?? node, depth + 1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
scan(file.sourceFile, null, 0);
|
|
130
163
|
return found.sort((a, b) => a.node.getStart() - b.node.getStart());
|
|
131
164
|
};
|
|
132
165
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// managed-scripts.mjs - reads the script names from the managed package-script template for one HFS profile and
|
|
2
|
+
// edition. Templates stay the source of truth; README presentation checks consume only this cached name set.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { managedGroupOf } from '../edition-slots.mjs';
|
|
6
|
+
import { loadSlotManifest } from '../slots.mjs';
|
|
7
|
+
|
|
8
|
+
const TEMPLATE_ROOTS = [
|
|
9
|
+
path.resolve(import.meta.dirname, '..', '..', '..', 'packages', 'hfs', 'templates'),
|
|
10
|
+
path.resolve(import.meta.dirname, '..', '..', '..', '..', 'templates'),
|
|
11
|
+
];
|
|
12
|
+
const managedScriptCache = new Map();
|
|
13
|
+
|
|
14
|
+
/** The script names of a profile/edition template after its partials are expanded. */
|
|
15
|
+
export function managedScriptNames(profile, edition = 'full') {
|
|
16
|
+
const key = `${profile}:${edition}`;
|
|
17
|
+
if (!managedScriptCache.has(key)) {
|
|
18
|
+
const slot = loadSlotManifest().slots.find((entry) => entry.id === `${profile}.package-manifest`);
|
|
19
|
+
const group = slot && managedGroupOf(slot, edition);
|
|
20
|
+
const templates = group && TEMPLATE_ROOTS.find((dir) => fs.existsSync(path.join(dir, profile, group, 'package.json')));
|
|
21
|
+
if (!templates) throw Error(`The managed package-scripts template of profile ${profile} cannot be found next to the runtime.`);
|
|
22
|
+
const text = fs.readFileSync(path.join(templates, profile, group, 'package.json'), 'utf8')
|
|
23
|
+
.replace(/^\{\{> ([\w./-]+)\}\}\r?\n/gmu, (_, partial) => fs.readFileSync(path.join(templates, partial), 'utf8'));
|
|
24
|
+
managedScriptCache.set(key, new Set([...text.matchAll(/^\s*"([A-Za-z0-9:_.-]+)":\s*"/gmu)].map((match) => match[1])));
|
|
25
|
+
}
|
|
26
|
+
return managedScriptCache.get(key);
|
|
27
|
+
}
|
|
@@ -11,8 +11,11 @@ import { treeOf } from './required-files.mjs';
|
|
|
11
11
|
* anywhere in a feature, and an `@Module` class (or a `ConfigurableModuleBuilder`/`ConfigurableModuleClass` use) in a
|
|
12
12
|
* feature file that is neither the application module nor the transport module of its own folder are refused;
|
|
13
13
|
* - apps import only transport modules, of the kinds their app kind allows: the `composedBy` list of each transport slot
|
|
14
|
-
* in knowledge/hfs/slots.yaml (api: graphql, http, websocket; worker: schedule, message
|
|
15
|
-
* lists a feature's application module, or the transport module of another kind, is refused
|
|
14
|
+
* in knowledge/hfs/slots.yaml (api: graphql, http, websocket; worker: schedule, message). An app root that
|
|
15
|
+
* lists a feature's application module, or the transport module of another kind, is refused;
|
|
16
|
+
* - a composed root (an owner slot with `composedBy`, be.cli) holds one static module per folder, named after the folder
|
|
17
|
+
* (cli.module.ts, <group>/<group>.module.ts); an app of a composing kind lists the root module only, and an app of any
|
|
18
|
+
* other kind lists none of its modules.
|
|
16
19
|
*/
|
|
17
20
|
export const MODULE_PER_TRANSPORT_RULE_IDS = ['BE_MODULE_SHAPE'];
|
|
18
21
|
|
|
@@ -31,18 +34,30 @@ export function checkModulePerTransport(input) {
|
|
|
31
34
|
const owner = resolver.ownerOf(rel);
|
|
32
35
|
return owner && resolver.slot(owner.slot)?.tier === 'feature' ? owner : null;
|
|
33
36
|
};
|
|
34
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* {feature, root, protocol, composed} of a file inside a feature: protocol is the transport folder, null for the application
|
|
39
|
+
* side. composed is set for a composed root, an owner slot with `composedBy` (be.cli): an app of those kinds lists its root
|
|
40
|
+
* module, and every folder of the root holds one static module named after the folder.
|
|
41
|
+
*/
|
|
35
42
|
const placeOf = rel => {
|
|
36
43
|
const owner = featureOf(rel);
|
|
37
44
|
if (!owner) return null;
|
|
45
|
+
const slot = resolver.slot(owner.slot);
|
|
38
46
|
const below = rel.slice(owner.root.length + 1).split('/');
|
|
39
|
-
|
|
47
|
+
const feature = path.posix.basename(owner.root);
|
|
48
|
+
if (slot.composedBy) return { root: owner.root, feature, protocol: null, below, composed: slot };
|
|
49
|
+
return { root: owner.root, feature, protocol: below[0] === 'transport' && below.length > 2 ? below[1] : null, below, composed: null };
|
|
50
|
+
};
|
|
51
|
+
/** The one module file a folder of a composed root may hold: <folder>/<folder>.module.ts. */
|
|
52
|
+
const composedModuleOf = rel => {
|
|
53
|
+
const dir = path.posix.dirname(rel);
|
|
54
|
+
return `${dir}/${path.posix.basename(dir)}.module.ts`;
|
|
40
55
|
};
|
|
41
56
|
|
|
42
57
|
// The transport slots and the app kinds that compose them, by protocol folder name.
|
|
43
58
|
const composedBy = new Map();
|
|
44
59
|
for (const slot of resolver.slots()) {
|
|
45
|
-
if (!slot.composedBy) continue;
|
|
60
|
+
if (!slot.composedBy || slot.owner) continue;
|
|
46
61
|
composedBy.set(path.posix.basename(slot.path.replace(/\/+$/u, '')), slot.composedBy);
|
|
47
62
|
}
|
|
48
63
|
|
|
@@ -74,6 +89,11 @@ export function checkModulePerTransport(input) {
|
|
|
74
89
|
if (!isModule) continue;
|
|
75
90
|
modules.set(statement, { name: statement.name.text, file });
|
|
76
91
|
if (!place) continue;
|
|
92
|
+
if (place.composed) {
|
|
93
|
+
const own = composedModuleOf(file.rel);
|
|
94
|
+
if (file.rel !== own) report(file, statement.name, `${statement.name.text} is a Nest module declared in ${file.rel}; every folder of ${place.root} holds exactly one static module named after it (${own}), never a module per command.`, { feature: place.feature });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
77
97
|
const own = place.protocol === null
|
|
78
98
|
? `${place.root}/${place.feature}.module.ts`
|
|
79
99
|
: `${place.root}/transport/${place.protocol}/${place.feature}-${place.protocol}.module.ts`;
|
|
@@ -111,7 +131,11 @@ export function checkModulePerTransport(input) {
|
|
|
111
131
|
if (!place || seen.has(node)) continue;
|
|
112
132
|
seen.add(node);
|
|
113
133
|
references += 1;
|
|
114
|
-
if (place.
|
|
134
|
+
if (place.composed) {
|
|
135
|
+
const rootModule = `${place.root}/${place.feature}.module.ts`;
|
|
136
|
+
if (!place.composed.composedBy.includes(app.kind)) report(root, node, `App ${app.name} is of kind ${app.kind} and lists ${module.name} from ${place.root}; only an app of kind ${place.composed.composedBy.join(', ')} composes that root.`, { app: app.name, module: module.name });
|
|
137
|
+
else if (module.file.rel !== rootModule) report(root, node, `App ${app.name} lists ${module.name}, a group module inside ${place.root}; the app lists the root module (${rootModule}), which imports every group module.`, { app: app.name, module: module.name });
|
|
138
|
+
} else if (place.protocol === null) {
|
|
115
139
|
report(root, node, `App ${app.name} lists ${module.name}, the application module of feature ${place.feature}; apps import only transport modules (${place.feature}-<protocol>.module.ts), which import the application module themselves.`, { app: app.name, module: module.name });
|
|
116
140
|
} else if (!(composedBy.get(place.protocol) ?? []).includes(app.kind)) {
|
|
117
141
|
const allowed = [...composedBy].filter(([, kinds]) => kinds.includes(app.kind)).map(([protocol]) => protocol).sort();
|