@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
package/scaffold/app.mjs
CHANGED
|
@@ -1,40 +1,46 @@
|
|
|
1
|
-
//
|
|
1
|
+
// starci app scaffold <name> - the first app tree, rendered from its canonical declaration, skeleton and managed targets.
|
|
2
|
+
// The selected edition owns the app root and its be/fe sides under knowledge/hfs/slots.yaml.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// entrypoint, platform config/logging/errors/clock/cqrs, the liveness capability and the health feature)
|
|
9
|
-
// <name>/fe/ the front-end side: the managed tool configuration and the templates/fe/skeleton tree (the next-intl
|
|
10
|
-
// [locale] shell with vi default, as-needed prefix and proxy.ts; each route slot mounts one pages feature
|
|
11
|
-
// drawn with @starci/grammar)
|
|
12
|
-
//
|
|
13
|
-
// The skeleton files are written once from templates/<app|be|fe>/skeleton ({{project}}, {{app}} and {{appPascal}} filled, a
|
|
14
|
-
// `__app__` folder named after the side's app); the managed files are the render of `hfs sync` (sync/index.mjs), so a fresh app
|
|
4
|
+
// The skeleton files are written once from templates/<app|be|fe>/skeleton ({{project}}, {{app}} and {{appPascal}} filled). Path
|
|
5
|
+
// variables: a `__app__` folder is written once per app of the side, named after it; an fe `apps/<name>/` folder is the skeleton of
|
|
6
|
+
// the declared fe app <name> alone (landing and app differ); `__project__` in a path is the project name (packages/__project__-ui is
|
|
7
|
+
// packages/<name>-ui). The workspace manifests and every tsconfig.json are written from code, never kept as template files (a
|
|
8
|
+
// tsconfig.json under templates/ would be a TypeScript project of this repository); the managed files are the render of `starci app sync` (sync/index.mjs), so a fresh app
|
|
15
9
|
// is in sync by construction. The lockfile is never written by hand: once the files are written, npm resolves the real one
|
|
16
10
|
// (`npm install --package-lock-only`, no node_modules, no scripts), so `npm ci` installs the new app as it is. When npm cannot
|
|
17
|
-
// resolve it the scaffold fails (HFS_SCAFFOLD_LOCK_FAILED), names the step and
|
|
18
|
-
//
|
|
11
|
+
// resolve it the scaffold fails (HFS_SCAFFOLD_LOCK_FAILED), names the step and unlinks its own generated entries. Replaced or
|
|
12
|
+
// occupied entries are retained with a cleanup message. The only existing root accepted is an empty unborn main Git repository.
|
|
19
13
|
import fs from 'node:fs';
|
|
20
14
|
import path from 'node:path';
|
|
21
15
|
import { spawnSync } from 'node:child_process';
|
|
22
|
-
import { loadSlotManifest, resolveRepoDeclaration } from '../runtime/scripts/
|
|
16
|
+
import { loadSlotManifest, resolveRepoDeclaration } from '../runtime/scripts/hfs/slots.mjs';
|
|
23
17
|
import { parseYaml } from '../runtime/engine/yaml.mjs';
|
|
24
|
-
import {
|
|
18
|
+
import { dbTypesPath, generateDbTypes } from '../emit/db-types.mjs';
|
|
19
|
+
import { TEMPLATES_DIR, appSource, imageFiles, render, renderTargets, writeTargets } from '../sync/index.mjs';
|
|
25
20
|
import { ScaffoldError } from './service.mjs';
|
|
21
|
+
import { SUPABASE_PORT_VARIABLES, supabasePortVars } from './supabase-ports.mjs';
|
|
22
|
+
import { FE_APP_SCRIPTS, PACKAGE_MANAGER, WORKSPACES, WORKSPACE_LINT, feAppPackageName } from '../runtime/scripts/hfs/rules/monorepo.mjs';
|
|
23
|
+
import { isLinkLike } from '../runtime/scripts/api/fs/is-link-like.mjs';
|
|
24
|
+
import { unlinkOnly } from '../runtime/scripts/api/fs/lib.mjs';
|
|
25
|
+
import { revParseQuery } from '../runtime/scripts/api/git/rev-parse-query.mjs';
|
|
26
|
+
import { lsFiles } from '../runtime/scripts/api/git/ls-files.mjs';
|
|
27
|
+
import { insidePath, samePath } from '../runtime/scripts/lib/path-key.mjs';
|
|
26
28
|
|
|
27
29
|
const NAME = /^[a-z][a-z0-9-]*$/;
|
|
28
30
|
const APP_DIR = '__app__';
|
|
31
|
+
/** The path variable of the project name: `packages/__project__-ui` is written as `packages/<project>-ui`. */
|
|
32
|
+
const PROJECT_DIR = '__project__';
|
|
33
|
+
const TIMESTAMP = '__timestamp__';
|
|
29
34
|
const PINS_FILE = path.join(import.meta.dirname, '..', 'runtime', 'knowledge', 'hfs', 'canon-pins.yaml');
|
|
30
35
|
const SONAR_GATE_FILE = path.join(import.meta.dirname, '..', 'runtime', 'knowledge', 'sonar-gate.yaml');
|
|
31
36
|
const pascal = name => name.split('-').map(part => part[0].toUpperCase() + part.slice(1)).join('');
|
|
37
|
+
const bundledPins = () => parseYaml(fs.readFileSync(PINS_FILE, 'utf8')).pins;
|
|
32
38
|
|
|
33
39
|
/**
|
|
34
40
|
* A JSON file as prettier prints it (the app's format check judges every file the scaffold writes): an object one key per line, an
|
|
35
41
|
* array of plain values on one line, any other array one item per line.
|
|
36
42
|
*/
|
|
37
|
-
function jsonText(value) {
|
|
43
|
+
export function jsonText(value) {
|
|
38
44
|
const print = (item, indent) => {
|
|
39
45
|
const inner = `${indent} `;
|
|
40
46
|
if (Array.isArray(item)) {
|
|
@@ -51,46 +57,158 @@ function jsonText(value) {
|
|
|
51
57
|
return `${print(value, '')}\n`;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
/**
|
|
60
|
+
/** A package.json as Prettier prints it (notably, package workspaces stay one entry per line). */
|
|
61
|
+
export const packageJsonText = value => `${JSON.stringify(value, null, 2)}\n`;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The apps a new app starts with: on the be side the `core` api app and the `cli` app over one `primary` connection, two Next apps
|
|
65
|
+
* (landing, app) on the fe side, which read the be contracts for their codegen.
|
|
66
|
+
*/
|
|
55
67
|
export const STARTER_SIDES = Object.freeze({
|
|
56
|
-
be: Object.freeze({ apps: [{ name: 'api', kind: 'api' }] }),
|
|
57
|
-
fe:
|
|
68
|
+
be: Object.freeze({ apps: [{ name: 'core', kind: 'api' }, { name: 'cli', kind: 'cli' }], kinds: ['api', 'cli'], connections: [{ name: 'primary', envPrefix: 'PRIMARY_DB', owner: 'core', isolation: 'database' }] }),
|
|
69
|
+
// fe: the landing and the product app, over the shared ui and i18n packages (both opt-in slots, enabled here).
|
|
70
|
+
fe: Object.freeze({ apps: [{ name: 'landing', kind: 'next' }, { name: 'app', kind: 'next' }], reads: ['be/contracts/'], optionalSlots: ['fe.package.ui', 'fe.package.i18n'] }),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/** The lite starter is the upgrade-safe subset: one API, one web app and one Supabase schema authority. */
|
|
74
|
+
const LITE_STARTER_SIDES = Object.freeze({
|
|
75
|
+
be: Object.freeze({
|
|
76
|
+
apps: [{ name: 'api', kind: 'api' }],
|
|
77
|
+
kinds: ['api'],
|
|
78
|
+
connections: [{ name: 'primary', envPrefix: 'PRIMARY_DB', owner: 'api', isolation: 'schema', provider: 'supabase' }],
|
|
79
|
+
reads: ['supabase/types/'],
|
|
80
|
+
}),
|
|
81
|
+
fe: Object.freeze({ apps: [{ name: 'web', kind: 'next' }], reads: ['be/contracts/', 'supabase/types/'] }),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/** The checked local Supabase auth posture of every newly scaffolded lite app. */
|
|
85
|
+
const LITE_STARTER_SUPABASE = Object.freeze({
|
|
86
|
+
enableSignup: false,
|
|
87
|
+
jwtExpiry: 3600,
|
|
88
|
+
siteUrl: 'http://127.0.0.1:3000',
|
|
89
|
+
redirectUrls: ['http://127.0.0.1:3000/auth/callback'],
|
|
58
90
|
});
|
|
59
91
|
|
|
60
92
|
/**
|
|
61
|
-
* The dependencies of the starter: what the skeleton imports and the tools the managed scripts and configs run.
|
|
62
|
-
* canon pin (knowledge/hfs/canon-pins.yaml)
|
|
63
|
-
*
|
|
93
|
+
* The dependencies of the starter: what the skeleton imports and the tools the managed scripts and configs run. Every name
|
|
94
|
+
* takes its canon pin (knowledge/hfs/canon-pins.yaml). The root package.json holds the back end's runtime and every tool;
|
|
95
|
+
* each fe app workspace declares the packages its own source imports (HFS_MONO_WORKSPACE_DEP), FE_APP_DEPENDENCIES.
|
|
64
96
|
*/
|
|
65
97
|
const STARTER_DEPENDENCIES = Object.freeze({
|
|
66
98
|
dependencies: {
|
|
67
|
-
'@
|
|
68
|
-
'@nestjs/platform-express': null, '@
|
|
69
|
-
'
|
|
99
|
+
'@nestjs/common': null, '@nestjs/core': null, '@nestjs/cqrs': null,
|
|
100
|
+
'@nestjs/platform-express': null, '@nestjs/typeorm': null, 'nest-commander': null, pg: null,
|
|
101
|
+
'class-transformer': null, 'class-validator': null, 'reflect-metadata': null, rxjs: null, tslib: null, typeorm: null,
|
|
70
102
|
},
|
|
71
103
|
devDependencies: {
|
|
72
|
-
'@nestjs/testing': null, '@starci/
|
|
73
|
-
'@starci/prettier-config': null, '@starci/stylelint-canon': null, '@starci/test-world': null, '@starci/tsconfig': null, '@tailwindcss/postcss':
|
|
74
|
-
'@types/jest': null, '@types/node': null, '@types/react':
|
|
75
|
-
husky:
|
|
76
|
-
'ts-node-dev':
|
|
104
|
+
turbo: null, '@nestjs/testing': null, '@starci/cli': null, '@starci/eslint-canon-be': null, '@starci/eslint-canon-fe': null, '@starci/jest-preset': null,
|
|
105
|
+
'@starci/prettier-config': null, '@starci/stylelint-canon': null, '@starci/test-world': null, '@starci/tsconfig': null, '@tailwindcss/postcss': null, '@types/express': null,
|
|
106
|
+
'@types/jest': null, '@types/node': null, '@types/react': null, '@types/react-dom': null, eslint: null, 'eslint-plugin-react-hooks': null,
|
|
107
|
+
husky: null, jest: null, 'postcss-value-parser': null, prettier: null, stylelint: null, tailwindcss: null, 'ts-jest': null,
|
|
108
|
+
'ts-node-dev': null, 'tsc-alias': null, 'tsconfig-paths': null, typescript: null,
|
|
77
109
|
},
|
|
78
110
|
});
|
|
79
111
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
112
|
+
/** Lite keeps the production stack and canon tools, but has no test runtime or test types. */
|
|
113
|
+
const LITE_STARTER_DEPENDENCIES = Object.freeze({
|
|
114
|
+
dependencies: {
|
|
115
|
+
'@nestjs/common': null, '@nestjs/core': null, '@nestjs/cqrs': null,
|
|
116
|
+
'@nestjs/platform-express': null, '@nestjs/typeorm': null, '@supabase/supabase-js': null,
|
|
117
|
+
'class-transformer': null, 'class-validator': null, jose: null, pg: null,
|
|
118
|
+
'reflect-metadata': null, rxjs: null, tslib: null, typeorm: null,
|
|
119
|
+
},
|
|
120
|
+
devDependencies: {
|
|
121
|
+
turbo: null, '@starci/eslint-canon-be': null, '@starci/eslint-canon-fe': null, '@starci/cli': null,
|
|
122
|
+
'@starci/prettier-config': null, '@starci/stylelint-canon': null, '@starci/tsconfig': null, '@tailwindcss/postcss': null,
|
|
123
|
+
'@types/express': null, '@types/node': null, '@types/react': null, '@types/react-dom': null,
|
|
124
|
+
eslint: null, 'eslint-plugin-react-hooks': null, husky: null, 'postcss-value-parser': null, prettier: null,
|
|
125
|
+
stylelint: null, supabase: null, tailwindcss: null, 'ts-node-dev': null, 'tsc-alias': null,
|
|
126
|
+
'tsconfig-paths': null, typescript: null,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The runtime dependencies of every fe app workspace of the starter: what its skeleton imports (the two workspace packages at "*",
|
|
132
|
+
* next with next-intl, react, the grammar and the HeroUI styles of its globals.css), @heroui/react (the grammar's peer, which every app
|
|
133
|
+
* declares: the grammar contract) and react-dom (next's peer). `@<project>/<pkg>` names are filled with the project.
|
|
134
|
+
*/
|
|
135
|
+
const FE_APP_DEPENDENCIES = Object.freeze({
|
|
136
|
+
'@<project>/i18n': '*', '@<project>/ui': '*',
|
|
137
|
+
'@heroui/react': null, '@heroui/styles': null, '@starci/grammar': null, next: null, 'next-intl': null, react: null, 'react-dom': null,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/** The sole lite FE workspace owns its Supabase clients directly; no shared FE package is emitted. */
|
|
141
|
+
const LITE_FE_APP_DEPENDENCIES = Object.freeze({
|
|
142
|
+
'@heroui/react': null, '@heroui/styles': null, '@starci/grammar': null, '@supabase/ssr': null,
|
|
143
|
+
'@supabase/supabase-js': null, next: null, 'next-intl': null, react: null, 'react-dom': null, 'server-only': null,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The shared fe workspace packages of the starter, fe/packages/<project>-<name> named @<project>/<name>: each one's exported
|
|
148
|
+
* subpaths (built to dist) and the runtime dependencies its own source imports. ui holds the drawings both apps mount; i18n the
|
|
149
|
+
* next-intl stack (the entry, a server module: createAppI18n; ./proxy, the locale negotiation the apps' proxy.ts re-exports;
|
|
150
|
+
* ./routing, client-safe: the default locale a client boundary reads).
|
|
151
|
+
*/
|
|
152
|
+
const FE_PACKAGES = Object.freeze({
|
|
153
|
+
ui: { exports: ['.'], dependencies: { '@heroui/react': null, '@starci/grammar': null, 'next-intl': null, react: null } },
|
|
154
|
+
i18n: { exports: ['.', './proxy', './routing'], dependencies: { next: null, 'next-intl': null, 'server-only': null } },
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** The scripts of every fe package workspace: tsc builds it to dist and type-checks it; it lints with the workspace lint. */
|
|
158
|
+
const PACKAGE_WORKSPACE_SCRIPTS = Object.freeze({ build: 'tsc -p tsconfig.build.json', lint: WORKSPACE_LINT, typecheck: 'tsc --noEmit -p tsconfig.json' });
|
|
82
159
|
|
|
83
|
-
/** The
|
|
84
|
-
|
|
160
|
+
/** The app hfs.json of a new app called `name`; full remains the byte-for-byte default declaration. */
|
|
161
|
+
const starterDeclaration = (name, manifest = loadSlotManifest(), edition = 'full') => ({
|
|
162
|
+
hfs: manifest.major,
|
|
163
|
+
kind: 'app',
|
|
164
|
+
...(edition === 'full' ? {} : { edition }),
|
|
165
|
+
project: name,
|
|
166
|
+
...(edition === 'lite' ? { supabase: structuredClone(LITE_STARTER_SUPABASE) } : {}),
|
|
167
|
+
sides: structuredClone(edition === 'lite' ? LITE_STARTER_SIDES : STARTER_SIDES),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/** A dependency section at the canon pins (a null range takes the pin, which must exist; every pinned name is null). */
|
|
171
|
+
function pinnedSection(entries, pins) {
|
|
85
172
|
const pinned = (dependency, range) => {
|
|
86
173
|
if (range !== null) return range;
|
|
87
174
|
if (!pins[dependency]) throw new ScaffoldError('HFS_SCAFFOLD_PIN_MISSING', `${dependency} has no canon pin in knowledge/hfs/canon-pins.yaml`);
|
|
88
175
|
return pins[dependency].version;
|
|
89
176
|
};
|
|
90
|
-
|
|
91
|
-
|
|
177
|
+
return Object.fromEntries(Object.entries(entries).sort(([a], [b]) => a.localeCompare(b)).map(([dependency, range]) => [dependency, pinned(dependency, range)]));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The root package.json of a new app, the monorepo root: name, workspaces, npm and its edition's dependencies at canon pins. */
|
|
181
|
+
function packageManifest(name, pins, edition) {
|
|
182
|
+
const dependencies = edition === 'lite' ? LITE_STARTER_DEPENDENCIES : STARTER_DEPENDENCIES;
|
|
183
|
+
return { name, version: '0.0.0', private: true, packageManager: PACKAGE_MANAGER, workspaces: [...WORKSPACES], dependencies: pinnedSection(dependencies.dependencies, pins), devDependencies: pinnedSection(dependencies.devDependencies, pins) };
|
|
92
184
|
}
|
|
93
185
|
|
|
186
|
+
/** A dependency section of an fe workspace with `@<project>/` names filled and the canon pins taken. */
|
|
187
|
+
const workspaceSection = (entries, project, pins) => pinnedSection(Object.fromEntries(Object.entries(entries).map(([name, range]) => [name.replace('@<project>/', `@${project}/`), range])), pins);
|
|
188
|
+
|
|
189
|
+
/** The package.json of the fe app workspace `app` of the app `project`: @<project>/<app>, private, the workspace scripts, its own dependencies. */
|
|
190
|
+
function feAppManifest(project, app, pins, edition) {
|
|
191
|
+
const dependencies = edition === 'lite' ? LITE_FE_APP_DEPENDENCIES : FE_APP_DEPENDENCIES;
|
|
192
|
+
return { name: feAppPackageName(project, app), version: '0.0.0', private: true, scripts: { ...FE_APP_SCRIPTS }, dependencies: workspaceSection(dependencies, project, pins) };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The package.json of the fe package workspace `name` (FE_PACKAGES) of the app `project`: @<project>/<name>, private, built to dist. */
|
|
196
|
+
function fePackageManifest(project, name, pins) {
|
|
197
|
+
const { exports, dependencies } = FE_PACKAGES[name];
|
|
198
|
+
const built = subpath => (subpath === '.' ? 'index' : subpath.slice(2));
|
|
199
|
+
return {
|
|
200
|
+
name: `@${project}/${name}`, version: '0.0.0', private: true, type: 'module',
|
|
201
|
+
exports: Object.fromEntries(exports.map(subpath => [subpath, { types: `./dist/${built(subpath)}.d.ts`, default: `./dist/${built(subpath)}.js` }])),
|
|
202
|
+
types: './dist/index.d.ts',
|
|
203
|
+
scripts: { ...PACKAGE_WORKSPACE_SCRIPTS },
|
|
204
|
+
dependencies: workspaceSection(dependencies, project, pins),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The tsconfig.json of an fe package workspace (the Next preset over its src/) and its tsconfig.build.json (emits src/ to dist/ with declarations). */
|
|
209
|
+
const fePackageTsconfig = () => ({ extends: '@starci/tsconfig/next.json', include: ['src/**/*.ts', 'src/**/*.tsx'], exclude: ['node_modules', 'dist'] });
|
|
210
|
+
const fePackageBuildTsconfig = () => ({ extends: './tsconfig.json', compilerOptions: { rootDir: 'src', outDir: 'dist', noEmit: false, incremental: false, declaration: true } });
|
|
211
|
+
|
|
94
212
|
function listFiles(dir, base = dir) {
|
|
95
213
|
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
|
|
96
214
|
const full = path.join(dir, entry.name);
|
|
@@ -119,8 +237,8 @@ function nextAppTsconfig() {
|
|
|
119
237
|
};
|
|
120
238
|
}
|
|
121
239
|
|
|
122
|
-
/** The names a skeleton file may fill: {{project}}, {{app}}, {{appPascal}} (the side's app) and {{
|
|
123
|
-
const SKELETON_VARIABLES = Object.freeze(['project', 'app', 'appPascal', 'sonarGate']);
|
|
240
|
+
/** The names a skeleton file may fill: {{project}}, {{app}}, {{appPascal}} (the side's app), {{sonarGate}} and the lite Supabase port block ({{supabasePort<Section>}}, supabase-ports.mjs). */
|
|
241
|
+
const SKELETON_VARIABLES = Object.freeze(['project', 'app', 'appPascal', 'sonarGate', ...SUPABASE_PORT_VARIABLES]);
|
|
124
242
|
|
|
125
243
|
/**
|
|
126
244
|
* A skeleton file with its variables filled. Only the skeleton names are variables: every other `{{name}}` is source the app keeps
|
|
@@ -133,25 +251,85 @@ function fill(source, vars, file) {
|
|
|
133
251
|
});
|
|
134
252
|
}
|
|
135
253
|
|
|
136
|
-
/**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
254
|
+
/** Let sync's renderer expand partials first while leaving every source placeholder for `fill` to validate or preserve. */
|
|
255
|
+
const PARTIAL_VARIABLES = new Proxy(Object.create(null), {
|
|
256
|
+
get: (_target, key) => `{{${String(key)}}}`,
|
|
257
|
+
getOwnPropertyDescriptor: (_target, key) => ({ configurable: true, value: `{{${String(key)}}}` }),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
/** The be skeleton folder of the one cli app (slot be.app.cli: always named `cli`), written only when hfs.json declares it. */
|
|
261
|
+
const CLI_APP_DIR = 'apps/cli/';
|
|
262
|
+
const declaresCliApp = app => app.sides.be.apps.some(entry => entry.kind === 'cli' && entry.name === 'cli');
|
|
263
|
+
|
|
264
|
+
const LITE_APP_REUSED = new Set(['.editorconfig', '.gitattributes', '.nvmrc', '.starciwork/workspace.yaml',
|
|
265
|
+
'.starciwork/index.yaml', '.starciwork/features/system-health/index.yaml']);
|
|
266
|
+
const LITE_DEFERRED_GENERATOR_FILES = new Set([
|
|
267
|
+
'src/modules/platform/database/database.sql.ts',
|
|
268
|
+
'src/modules/platform/database/migration-runner.service.ts',
|
|
269
|
+
'src/modules/platform/database/seed-runner.service.ts',
|
|
270
|
+
]);
|
|
271
|
+
const liteBaseFile = (scope, rel) => {
|
|
272
|
+
if (scope === 'app') return LITE_APP_REUSED.has(rel);
|
|
273
|
+
if (scope === 'fe') return false;
|
|
274
|
+
return !rel.startsWith('apps/cli/')
|
|
275
|
+
&& !rel.startsWith('src/features/cli/')
|
|
276
|
+
&& !rel.startsWith('src/modules/domain/note/')
|
|
277
|
+
&& rel !== 'src/modules/platform/database/connection-source.client.ts'
|
|
278
|
+
&& rel !== 'src/modules/platform/database/migrate-connections.client.ts'
|
|
279
|
+
&& rel !== 'src/modules/platform/database/seed-connections.client.ts'
|
|
280
|
+
&& !LITE_DEFERRED_GENERATOR_FILES.has(rel)
|
|
281
|
+
&& !rel.startsWith('src/tests/')
|
|
282
|
+
&& !rel.endsWith('.spec.ts');
|
|
283
|
+
};
|
|
284
|
+
const liteOverlayFile = (scope, rel) => scope !== 'be' || !LITE_DEFERRED_GENERATOR_FILES.has(rel);
|
|
285
|
+
|
|
286
|
+
/** One skeleton directory rendered to app-relative files; `include` filters source-relative paths before variables are expanded. */
|
|
287
|
+
function skeletonDirectory(scope, directory, app, vars, include = () => true) {
|
|
288
|
+
const dir = path.join(TEMPLATES_DIR, scope, directory);
|
|
289
|
+
if (!fs.existsSync(dir)) throw new ScaffoldError('HFS_SCAFFOLD_SKELETON_MISSING', `no skeleton templates in templates/${scope}/${directory}`);
|
|
140
290
|
const apps = scope === 'app' ? [] : app.sides[scope].apps.filter(entry => scope === 'fe' || entry.kind === 'api');
|
|
141
291
|
const prefix = scope === 'app' ? '' : `${scope}/`;
|
|
142
292
|
const files = [];
|
|
143
|
-
for (const rel of listFiles(dir)) {
|
|
144
|
-
const source = fs.readFileSync(path.join(dir, rel), 'utf8').replace(/\r\n/g, '\n');
|
|
293
|
+
for (const rel of listFiles(dir).filter(include)) {
|
|
294
|
+
const source = render(fs.readFileSync(path.join(dir, rel), 'utf8').replace(/\r\n/g, '\n'), PARTIAL_VARIABLES);
|
|
145
295
|
const once = { project: app.project, ...vars };
|
|
296
|
+
const output = candidate => candidate.split(PROJECT_DIR).join(app.project).split(TIMESTAMP).join(vars.timestamp);
|
|
146
297
|
if (!rel.includes(APP_DIR)) {
|
|
147
|
-
|
|
298
|
+
if (scope === 'be' && rel.startsWith(CLI_APP_DIR) && !declaresCliApp(app)) continue;
|
|
299
|
+
const fixedApp = apps.find(entry => rel.startsWith(`apps/${entry.name}/`));
|
|
300
|
+
if (scope === 'fe' && rel.startsWith('apps/') && !fixedApp) continue;
|
|
301
|
+
const appVars = fixedApp ? { app: fixedApp.name, appPascal: pascal(fixedApp.name) } : {};
|
|
302
|
+
files.push({ path: `${prefix}${output(rel)}`, content: fill(source, { ...once, ...appVars }, `${directory}/${rel}`) });
|
|
148
303
|
continue;
|
|
149
304
|
}
|
|
150
|
-
for (const entry of apps) files.push({ path: `${prefix}${rel.split(APP_DIR).join(entry.name)}`, content: fill(source, { ...once, app: entry.name, appPascal: pascal(entry.name) }, rel) });
|
|
305
|
+
for (const entry of apps) files.push({ path: `${prefix}${output(rel.split(APP_DIR).join(entry.name))}`, content: fill(source, { ...once, app: entry.name, appPascal: pascal(entry.name) }, `${directory}/${rel}`) });
|
|
151
306
|
}
|
|
152
307
|
return files;
|
|
153
308
|
}
|
|
154
309
|
|
|
310
|
+
/** The canonical product catalog and feature nodes shared by scaffold and upgrades of older lite apps. */
|
|
311
|
+
export function scaffoldWorkSeeds(project) {
|
|
312
|
+
return skeletonDirectory('app', 'skeleton', { project }, {},
|
|
313
|
+
rel => rel === '.starciwork/index.yaml' || rel.startsWith('.starciwork/features/'));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The skeleton files of one scope (app root, be, fe): [{ path, content }], app-relative; a `__app__` file once per api app of the
|
|
318
|
+
* be side (once per app of the fe side), the be `apps/cli/` folder once when the cli app is declared, an fe `apps/<name>/` folder
|
|
319
|
+
* once for the declared fe app <name> (none other), `__project__` in a path replaced by the project name.
|
|
320
|
+
*/
|
|
321
|
+
function skeletonOf(scope, app, vars) {
|
|
322
|
+
const files = app.edition === 'lite'
|
|
323
|
+
? [...skeletonDirectory(scope, 'skeleton', app, vars, rel => liteBaseFile(scope, rel)), ...skeletonDirectory(scope, 'skeleton-lite', app, vars, rel => liteOverlayFile(scope, rel))]
|
|
324
|
+
: skeletonDirectory(scope, 'skeleton', app, vars);
|
|
325
|
+
const unique = [...new Map(files.map(file => [file.path, file])).values()];
|
|
326
|
+
// Every declared fe app has a skeleton: its own apps/<name>/ folder or the shared __app__ one.
|
|
327
|
+
for (const entry of scope === 'fe' ? app.sides.fe.apps : []) {
|
|
328
|
+
if (!unique.some(file => file.path.startsWith(`fe/apps/${entry.name}/`))) throw new ScaffoldError('HFS_SCAFFOLD_SKELETON_MISSING', `no skeleton templates for the fe app ${entry.name}`);
|
|
329
|
+
}
|
|
330
|
+
return unique;
|
|
331
|
+
}
|
|
332
|
+
|
|
155
333
|
/** The npm step that resolves the lockfile of a new app, exactly as the error names it. */
|
|
156
334
|
export const LOCK_STEP = 'npm install --package-lock-only --ignore-scripts --no-audit --no-fund';
|
|
157
335
|
|
|
@@ -170,37 +348,148 @@ export function npmLock(root) {
|
|
|
170
348
|
return { ok: false, detail: `exit ${run.status ?? 'none'}: ${reason}` };
|
|
171
349
|
}
|
|
172
350
|
|
|
351
|
+
/** Read-only admission of the one existing-root case; a failed Git query cannot prove an empty repository. */
|
|
352
|
+
function emptyUnbornGit(root, stat) {
|
|
353
|
+
try {
|
|
354
|
+
if (!stat.isDirectory() || isLinkLike(root, { stat })) return false;
|
|
355
|
+
const entries = fs.readdirSync(root);
|
|
356
|
+
if (entries.length !== 1 || entries[0] !== '.git') return false;
|
|
357
|
+
const git = path.join(root, '.git');
|
|
358
|
+
const metadata = fs.lstatSync(git);
|
|
359
|
+
if (!metadata.isDirectory() || isLinkLike(git, { stat: metadata })) return false;
|
|
360
|
+
const head = path.join(git, 'HEAD');
|
|
361
|
+
if (!fs.lstatSync(head).isFile() || isLinkLike(head) || fs.readFileSync(head, 'utf8').trim() !== 'ref: refs/heads/main') return false;
|
|
362
|
+
const top = revParseQuery(['--show-toplevel'], { cwd: root });
|
|
363
|
+
const unborn = revParseQuery(['--verify', '--quiet', 'HEAD'], { cwd: root });
|
|
364
|
+
const refs = revParseQuery(['--all'], { cwd: root });
|
|
365
|
+
const index = lsFiles(['--cached', '-z'], { cwd: root });
|
|
366
|
+
return !top.error && top.status === 0 && samePath(path.resolve(top.stdout.trim()), path.resolve(root))
|
|
367
|
+
&& !unborn.error && unborn.status === 1 && !refs.error && refs.status === 0 && !refs.stdout.trim()
|
|
368
|
+
&& !index.error && index.status === 0 && !index.stdout;
|
|
369
|
+
} catch { return false; }
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Own only created entries. Rollback unlinks them individually, never a tree or a path reached through a replacement link. */
|
|
373
|
+
function scaffoldWrites(root, original) {
|
|
374
|
+
const files = new Map();
|
|
375
|
+
const directories = new Map();
|
|
376
|
+
const unexpected = new Set();
|
|
377
|
+
let rootStat = original;
|
|
378
|
+
const identity = (a, b) => a && b && a.dev === b.dev && a.ino === b.ino;
|
|
379
|
+
const stat = file => fs.lstatSync(file, { throwIfNoEntry: false });
|
|
380
|
+
const directory = dir => {
|
|
381
|
+
if (!samePath(dir, root)) directory(path.dirname(dir));
|
|
382
|
+
let current = stat(dir);
|
|
383
|
+
if (!current) {
|
|
384
|
+
fs.mkdirSync(dir, { recursive: samePath(dir, root) });
|
|
385
|
+
current = stat(dir);
|
|
386
|
+
directories.set(dir, current);
|
|
387
|
+
if (samePath(dir, root)) rootStat = current;
|
|
388
|
+
}
|
|
389
|
+
const owned = samePath(dir, root) ? rootStat : directories.get(dir);
|
|
390
|
+
if (!current.isDirectory() || isLinkLike(dir, { stat: current }) || !identity(current, owned)) throw new ScaffoldError('HFS_SCAFFOLD_EXISTS', `${dir} is not a plain scaffold-owned directory`);
|
|
391
|
+
};
|
|
392
|
+
const reachable = file => {
|
|
393
|
+
const parent = path.dirname(file);
|
|
394
|
+
if (!samePath(parent, root) && !insidePath(root, parent)) return false;
|
|
395
|
+
let dir = root;
|
|
396
|
+
for (const part of ['', ...path.relative(root, parent).split(path.sep).filter(Boolean)]) {
|
|
397
|
+
if (part) dir = path.join(dir, part);
|
|
398
|
+
const current = stat(dir);
|
|
399
|
+
if (!current?.isDirectory() || isLinkLike(dir, { stat: current })) return false;
|
|
400
|
+
if (samePath(dir, root) && !identity(current, rootStat)) return false;
|
|
401
|
+
}
|
|
402
|
+
return true;
|
|
403
|
+
};
|
|
404
|
+
const claim = relative => {
|
|
405
|
+
const target = path.resolve(root, relative);
|
|
406
|
+
if (!insidePath(root, target) || relative.split(/[\\/]/)[0] === '.git') throw new ScaffoldError('HFS_SCAFFOLD_EXISTS', `${relative} is outside the scaffold file scope`);
|
|
407
|
+
directory(path.dirname(target));
|
|
408
|
+
if (files.has(target)) {
|
|
409
|
+
const current = stat(target);
|
|
410
|
+
if (!current?.isFile() || !identity(current, files.get(target))) throw new ScaffoldError('HFS_SCAFFOLD_EXISTS', `${target} replaced a scaffold file`);
|
|
411
|
+
} else {
|
|
412
|
+
const fd = fs.openSync(target, 'wx');
|
|
413
|
+
try { files.set(target, fs.fstatSync(fd)); } finally { fs.closeSync(fd); }
|
|
414
|
+
}
|
|
415
|
+
return target;
|
|
416
|
+
};
|
|
417
|
+
return {
|
|
418
|
+
claim,
|
|
419
|
+
write(relative, content) { fs.writeFileSync(claim(relative), content); },
|
|
420
|
+
lockfile() {
|
|
421
|
+
const target = path.join(root, 'package-lock.json');
|
|
422
|
+
if (reachable(target)) {
|
|
423
|
+
const current = stat(target);
|
|
424
|
+
if (current?.isFile() && !isLinkLike(target, { stat: current })) files.set(target, current);
|
|
425
|
+
else if (current) unexpected.add(target);
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
rollback() {
|
|
429
|
+
const held = [...unexpected];
|
|
430
|
+
for (const [target, owned] of [...files, ...[...directories].reverse()]) {
|
|
431
|
+
try {
|
|
432
|
+
if (!samePath(target, root) && !reachable(target)) { held.push(target); continue; }
|
|
433
|
+
const current = stat(target);
|
|
434
|
+
if (!current) continue;
|
|
435
|
+
if (!identity(current, owned) || isLinkLike(target, { stat: current }) || !unlinkOnly(target)) held.push(target);
|
|
436
|
+
} catch (error) { held.push(`${target}: ${error.message}`); }
|
|
437
|
+
}
|
|
438
|
+
return held;
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
173
443
|
/**
|
|
174
|
-
* `
|
|
444
|
+
* `starci app scaffold <name>`: writes the new app under `into`, resolves its lockfile with npm (`lock`, npmLock), and returns
|
|
175
445
|
* `{ root, files }` (app-relative paths, sorted). `presets` is what sync loads from the installed @starci/jest-preset (the Sonar
|
|
176
|
-
* exclusions); the CLI passes the one it resolves.
|
|
446
|
+
* exclusions); the CLI passes the one it resolves. Failure removes only scaffold-owned entries, retaining an existing .git.
|
|
177
447
|
*/
|
|
178
|
-
export function scaffoldApp({ name, into, presets, manifest = loadSlotManifest(), lock = npmLock }) {
|
|
448
|
+
export function scaffoldApp({ name, into, presets, edition = 'full', manifest = loadSlotManifest(), pins = bundledPins(), lock = npmLock, emitTypes = generateDbTypes, now = () => new Date() }) {
|
|
179
449
|
if (!NAME.test(String(name))) throw new ScaffoldError('HFS_SCAFFOLD_NAME_INVALID', `the app name ${name} must be kebab-case (a project name: ${NAME})`);
|
|
180
|
-
const root = path.
|
|
181
|
-
|
|
182
|
-
|
|
450
|
+
const root = path.resolve(into, name);
|
|
451
|
+
const original = fs.lstatSync(root, { throwIfNoEntry: false });
|
|
452
|
+
if (original && !emptyUnbornGit(root, original)) throw new ScaffoldError('HFS_SCAFFOLD_EXISTS', `${root} already exists; only a plain directory containing solely .git with unborn main and an empty index can be scaffolded`);
|
|
453
|
+
const declaration = starterDeclaration(name, manifest, edition);
|
|
183
454
|
const app = resolveRepoDeclaration(manifest, declaration);
|
|
184
|
-
const
|
|
185
|
-
const
|
|
455
|
+
const pkg = packageManifest(name, pins, app.edition);
|
|
456
|
+
const timestamp = now().toISOString().replace(/\D/g, '').slice(0, 14);
|
|
186
457
|
const files = [
|
|
187
458
|
{ path: 'hfs.json', content: jsonText(declaration) },
|
|
188
|
-
{ path: 'package.json', content:
|
|
459
|
+
{ path: 'package.json', content: packageJsonText(pkg) },
|
|
189
460
|
{ path: 'be/nest-cli.json', content: jsonText(nestCli(app)) },
|
|
190
461
|
...app.sides.fe.apps.map(entry => ({ path: `fe/apps/${entry.name}/tsconfig.json`, content: jsonText(nextAppTsconfig()) })),
|
|
191
|
-
...
|
|
462
|
+
...app.sides.fe.apps.map(entry => ({ path: `fe/apps/${entry.name}/package.json`, content: packageJsonText(feAppManifest(name, entry.name, pins, app.edition)) })),
|
|
463
|
+
...imageFiles(app),
|
|
464
|
+
...(app.edition === 'lite' ? [] : Object.keys(FE_PACKAGES).flatMap(pkg => [
|
|
465
|
+
{ path: `fe/packages/${name}-${pkg}/package.json`, content: packageJsonText(fePackageManifest(name, pkg, pins)) },
|
|
466
|
+
{ path: `fe/packages/${name}-${pkg}/tsconfig.json`, content: jsonText(fePackageTsconfig()) },
|
|
467
|
+
{ path: `fe/packages/${name}-${pkg}/tsconfig.build.json`, content: jsonText(fePackageBuildTsconfig()) },
|
|
468
|
+
])),
|
|
469
|
+
...['app', 'be', 'fe'].flatMap(scope => skeletonOf(scope, app, { sonarGate: parseYaml(fs.readFileSync(SONAR_GATE_FILE, 'utf8')).gate.name, timestamp, ...supabasePortVars(name) })),
|
|
192
470
|
];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
471
|
+
const writes = scaffoldWrites(root, original);
|
|
472
|
+
try {
|
|
473
|
+
for (const file of files) writes.write(file.path, file.content);
|
|
474
|
+
const targets = renderTargets(declaration, presets, { manifest, source: appSource(root) });
|
|
475
|
+
for (const target of targets) writes.claim(target.path);
|
|
476
|
+
writeTargets(root, targets);
|
|
477
|
+
if (app.edition === 'lite') {
|
|
478
|
+
try {
|
|
479
|
+
const text = emitTypes({ root });
|
|
480
|
+
if (typeof text !== 'string' || !text.trim()) throw new Error('the Supabase CLI returned no database types');
|
|
481
|
+
writes.write(dbTypesPath, text);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
throw new ScaffoldError('HFS_SCAFFOLD_TYPES_FAILED', `Supabase database types could not be generated for ${root} (${String(error?.message ?? error)}). Start the local Supabase stack and run starci app scaffold ${name} --edition lite again`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
let locked;
|
|
487
|
+
try { locked = lock(root); } finally { writes.lockfile(); }
|
|
488
|
+
if (!locked.ok) throw new ScaffoldError('HFS_SCAFFOLD_LOCK_FAILED', `\`${LOCK_STEP}\` could not resolve the lockfile of ${root} (${locked.detail}). Check the network and the npm registry, then run starci app scaffold ${name} again`);
|
|
489
|
+
return { root, files: [...new Set([...files.map(file => file.path), ...targets.map(target => target.path), ...(app.edition === 'lite' ? [dbTypesPath] : []), 'package-lock.json'])].sort() };
|
|
490
|
+
} catch (error) {
|
|
491
|
+
const held = writes.rollback();
|
|
492
|
+
if (held.length) error.message += `; cleanup retained replaced or occupied entries: ${held.join(', ')}`;
|
|
493
|
+
throw error;
|
|
204
494
|
}
|
|
205
|
-
return { root, files: [...new Set([...files.map(file => file.path), ...targets.map(target => target.path), 'package-lock.json'])].sort() };
|
|
206
495
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The edition gate of `starci app add` and `starci app new` (design 8.6 R8): a verb whose file tree lands in a slot the app's
|
|
2
|
+
// edition does not have - the slot's `editions` exclude it, or its `litePresence` is forbidden - is a full-edition
|
|
3
|
+
// capability and refuses before anything is planned or written. The slot resolver of the app answers, the same
|
|
4
|
+
// view `starci app check` reads; the noun's name is never the test, so a slot the lite manifest forbids refuses every
|
|
5
|
+
// verb that writes into it, with no noun list kept anywhere. The refusal carries no failure code by design: the
|
|
6
|
+
// scaffolders of packages/hfs keep their codes out of the runtime catalog (like HFS_SCAFFOLD_LOCK_FAILED).
|
|
7
|
+
|
|
8
|
+
/** The refusal of a verb a non-full edition does not carry: printed verbatim by the CLI, exit 2, nothing written. */
|
|
9
|
+
export class EditionRefusal extends Error {
|
|
10
|
+
constructor(command) {
|
|
11
|
+
super(`${command}: full edition only; run starci app upgrade --edition full`);
|
|
12
|
+
this.name = 'EditionRefusal';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Refuses `command` (`add <noun>`, `new <noun>`) when one of `slotIds` - the slots the verb writes into - does not
|
|
18
|
+
* exist for the edition of `resolver`'s repository or is forbidden under it. `resolver` is the
|
|
19
|
+
* createSlotResolver() view of the app; every scope answers a slot id. Under full every verb slot exists and none is
|
|
20
|
+
* forbidden, so the gate never fires. A slot id the view does not answer (an `editions` exclusion, or an unknown id) refuses
|
|
21
|
+
* in every edition alike: the view cannot tell them apart, and the callers name only slots of the manifest.
|
|
22
|
+
*/
|
|
23
|
+
export function refuseInEdition({ resolver, slotIds, command }) {
|
|
24
|
+
if (slotIds.some((id) => resolver.slot(id) === null || resolver.slot(id).presence === 'forbidden')) throw new EditionRefusal(command);
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// starci app new image - the Dockerfile of every declared app that has none, written from the image canon
|
|
2
|
+
// (templates/<side>/image/<kind>, the same render starci app scaffold uses). It never overwrites a file.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { imageFiles, loadHfs, validateHfs } from '../sync/index.mjs';
|
|
6
|
+
|
|
7
|
+
/** Writes the missing Dockerfiles of the app at `repoRoot` (the folder of hfs.json); the app-relative paths written. */
|
|
8
|
+
export function newImages({ repoRoot }) {
|
|
9
|
+
const files = imageFiles(validateHfs(loadHfs(repoRoot)));
|
|
10
|
+
const written = [];
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
const target = path.join(repoRoot, ...file.path.split('/'));
|
|
13
|
+
if (fs.existsSync(target)) continue;
|
|
14
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
15
|
+
fs.writeFileSync(target, file.content);
|
|
16
|
+
written.push(file.path);
|
|
17
|
+
}
|
|
18
|
+
return written;
|
|
19
|
+
}
|