@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
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
// scripts/lib/package-at.mjs — a package the runtime does not ship (playwright, esbuild, tailwindcss) comes from the
|
|
2
2
|
// project that owns it: node resolution from that project's directory, walking up its node_modules.
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { createRequire } from 'node:module';
|
|
6
|
+
import { skillRoot } from '../../engine/runtime-root.mjs';
|
|
5
7
|
|
|
6
8
|
/** The package.json of `name` as resolved from `dir`, or null. */
|
|
7
9
|
export const packageAt = (dir, name) => {
|
|
8
10
|
try { return createRequire(path.join(dir, 'package.json')).resolve(`${name}/package.json`); } catch { return null; }
|
|
9
11
|
};
|
|
10
12
|
|
|
13
|
+
/** This runtime's own `starci` bin (packages/cli is the only package that declares one). */
|
|
14
|
+
const RUNTIME_STARCI_BIN = path.join(skillRoot, 'packages', 'cli', 'bin', 'starci.mjs');
|
|
15
|
+
|
|
16
|
+
/** The `starci` bin of the @starci/cli that `dir` installs (a product runs its own pinned CLI), else this runtime's own. */
|
|
17
|
+
export function starciBin(dir) {
|
|
18
|
+
const packageFile = packageAt(dir, '@starci/cli');
|
|
19
|
+
if (packageFile) {
|
|
20
|
+
try {
|
|
21
|
+
const { bin } = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
22
|
+
const rel = typeof bin === 'string' ? bin : bin?.starci;
|
|
23
|
+
const file = rel ? path.join(path.dirname(packageFile), rel) : null;
|
|
24
|
+
if (file && fs.existsSync(file)) return file;
|
|
25
|
+
} catch { /* an unreadable install falls back to the runtime's own CLI */ }
|
|
26
|
+
}
|
|
27
|
+
return RUNTIME_STARCI_BIN;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
/** The first of `names` resolvable from the first of `dirs` that has one: {name, root, packageFile, version} or null. */
|
|
12
31
|
export function findPackage(dirs, names) {
|
|
13
32
|
for (const dir of dirs.filter(Boolean)) {
|
|
@@ -23,3 +42,21 @@ export function findPackage(dirs, names) {
|
|
|
23
42
|
|
|
24
43
|
/** The CommonJS entry of a package found by findPackage. */
|
|
25
44
|
export const requirePackage = (found) => createRequire(found.packageFile)(found.name);
|
|
45
|
+
|
|
46
|
+
/** The TypeScript compiler resolvable from the first of `dirs` (an app root, then the caller's own), or null. */
|
|
47
|
+
export const loadTypescript = (...dirs) => {
|
|
48
|
+
const located = findPackage(dirs, ['typescript']);
|
|
49
|
+
return located ? requirePackage(located) : null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The app's command line and the app implementation behind it: `bin` the `starci` bin of the app's installed @starci/cli, else
|
|
54
|
+
* this runtime's packages/cli/bin/starci.mjs (starciBin); `dir` the @starci/hfs that answers
|
|
55
|
+
* `starci app` - the app's own install, else the one beside its @starci/cli, else this runtime's packages/hfs - whose runtime
|
|
56
|
+
* slice the gate's base lint (scripts/gates/gate.mjs) and the READ digest (scripts/gates/read-digest.mjs) load in-process.
|
|
57
|
+
*/
|
|
58
|
+
export function hfsEntry(root) {
|
|
59
|
+
const cli = packageAt(root, '@starci/cli');
|
|
60
|
+
const manifest = packageAt(root, '@starci/hfs') ?? (cli && packageAt(path.dirname(cli), '@starci/hfs'));
|
|
61
|
+
return { dir: manifest ? path.dirname(manifest) : path.join(skillRoot, 'packages', 'hfs'), bin: starciBin(root) };
|
|
62
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// path-key.mjs — spelling and comparing paths the way this host's filesystem does.
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
|
|
4
5
|
const WIN = process.platform === 'win32';
|
|
@@ -13,5 +14,61 @@ export const sameOrUnder = (file, prefix) => file === prefix || file.startsWith(
|
|
|
13
14
|
export const foldCase = (p) => (WIN ? p.toLowerCase() : p);
|
|
14
15
|
/** Whether two spellings name the same path on this host's filesystem. */
|
|
15
16
|
export const samePath = (a, b) => foldCase(a) === foldCase(b);
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* A path's comparable identity: absolute, forward slashes, no trailing slash, case-folded on Windows.
|
|
19
|
+
* `fold: true` folds case on every platform (a comparison against a lowercase-stored root needs it).
|
|
20
|
+
*/
|
|
21
|
+
export const pathKey = (p, { fold = WIN } = {}) => {
|
|
22
|
+
const key = slash(path.resolve(p)).replace(/\/+$/, '');
|
|
23
|
+
return fold ? key.toLowerCase() : key;
|
|
24
|
+
};
|
|
25
|
+
/** `a` is `b` or under it, or the reverse: two path spellings whose trees touch. */
|
|
26
|
+
export const pathsOverlap = (a, b) => sameOrUnder(a, b) || sameOrUnder(b, a);
|
|
27
|
+
/**
|
|
28
|
+
* A path-ish string in compare form, options in spelling order:
|
|
29
|
+
* `trim` blanks at the ends, `collapse` folds '//' runs to '/', `dot` drops a leading './',
|
|
30
|
+
* `glob` strips a trailing glob ('star': '*'/'**' with their slash, 'double': a '/**' only),
|
|
31
|
+
* `fold` lowercases. A trailing '/' always goes. Null reads as ''.
|
|
32
|
+
*/
|
|
33
|
+
export const normPath = (p, { trim = false, collapse = false, dot = false, glob = null, fold = false } = {}) => {
|
|
34
|
+
let s = String(p ?? '');
|
|
35
|
+
if (trim) s = s.trim();
|
|
36
|
+
s = slash(s);
|
|
37
|
+
if (collapse) s = s.replace(/\/{2,}/g, '/');
|
|
38
|
+
if (dot) s = s.replace(/^\.\//, '');
|
|
39
|
+
if (glob === 'star') s = s.replace(/\/?\*+$/, '');
|
|
40
|
+
else if (glob === 'double') s = s.replace(/\/\*\*$/, '');
|
|
41
|
+
s = s.replace(/\/+$/, '');
|
|
42
|
+
return fold ? s.toLowerCase() : s;
|
|
43
|
+
};
|
|
44
|
+
/** A work-relative path in one spelling: forward slashes, no leading ./, no trailing slash. */
|
|
45
|
+
export const normWork = (p) => String(p ?? '').trim().replaceAll('\\', '/').replace(/^\.\/+/, '').replace(/\/+$/, '');
|
|
46
|
+
|
|
47
|
+
/** `p` resolved and dereferenced (realpath where the path exists) — the canonical spelling of a checkout dir. */
|
|
48
|
+
export const realPath = (p) => {
|
|
49
|
+
const resolved = path.resolve(p);
|
|
50
|
+
try { return fs.realpathSync.native(resolved); } catch { return resolved; }
|
|
51
|
+
};
|
|
52
|
+
/** `p` resolved and case-folded where the filesystem ignores case — the Map key of an absolute path. */
|
|
53
|
+
export const resolvedKey = (p) => foldCase(path.resolve(p));
|
|
54
|
+
/** Whether `a` and `b` resolve to the same path (always case-folded, so differently-cased spellings count). */
|
|
55
|
+
export const sameResolvedPath = (a, b) => pathKey(a, { fold: true }) === pathKey(b, { fold: true });
|
|
56
|
+
/**
|
|
57
|
+
* Whether `file` sits strictly under `root`. `key` normalizes both sides first (e.g. the registry's
|
|
58
|
+
* realpath-folded treeKey) when the callers' spellings need it.
|
|
59
|
+
*/
|
|
60
|
+
export const insidePath = (root, file, { key = (p) => p } = {}) => {
|
|
61
|
+
const rel = path.relative(key(root), key(file));
|
|
62
|
+
return Boolean(rel) && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* A path-ish value (a string or a {path} entry) in relative compare form: trimmed, forward slashes,
|
|
66
|
+
* no trailing '/**' glob or slashes, no leading './'; `fold` lowercases for a case-insensitive key.
|
|
67
|
+
*/
|
|
68
|
+
export const normRel = (p, { fold = false } = {}) => {
|
|
69
|
+
const s = String(typeof p === 'string' ? p : p?.path ?? '').trim().replace(/\\/g, '/').replace(/\/\*\*$/, '').replace(/\/+$/, '').replace(/^\.\//, '');
|
|
70
|
+
return fold ? s.toLowerCase() : s;
|
|
71
|
+
};
|
|
72
|
+
/** Whether `file` equals or sits under one of `prefixes` (posix spellings; `dot` counts a '.' prefix as covering all). */
|
|
73
|
+
export const underAny = (file, prefixes, { dot = false } = {}) =>
|
|
74
|
+
(prefixes ?? []).some((p) => (dot && p === '.') || sameOrUnder(file, p));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// read-text.mjs — the forgiving text read: a missing or unreadable file is the caller's fallback,
|
|
2
|
+
// never a throw (the JSON twin is ./json.mjs readJsonFile, the YAML twin ./read-yaml.mjs).
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
/** The text of `rel` under `root` ('/'-separated rel), or null when it is missing or unreadable. */
|
|
7
|
+
export const readTextFile = (root, rel) => {
|
|
8
|
+
try { return fs.readFileSync(path.join(root, ...String(rel).split('/')), 'utf8'); } catch { return null; }
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** `file` read and parsed by `parse`, or `fallback` when it is missing, unreadable or malformed. */
|
|
12
|
+
export const readParsedFile = (file, parse, fallback = null) => {
|
|
13
|
+
try { return parse(fs.readFileSync(file, 'utf8')); } catch { return fallback; }
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Feed `file`'s lines to `onLine` reading in bounded chunks — a diff or a patch can exceed any single
|
|
18
|
+
* string. `eol` is the line-ending pattern or literal (default CRLF-aware); a last unterminated line is fed too.
|
|
19
|
+
*/
|
|
20
|
+
export function forEachFileLine(file, onLine, { chunkBytes = 8 * 1024 * 1024, eol = /\r?\n/ } = {}) {
|
|
21
|
+
const fd = fs.openSync(file, 'r');
|
|
22
|
+
try {
|
|
23
|
+
const buf = Buffer.alloc(chunkBytes);
|
|
24
|
+
let carry = '';
|
|
25
|
+
for (;;) {
|
|
26
|
+
const n = fs.readSync(fd, buf, 0, chunkBytes, null);
|
|
27
|
+
if (n <= 0) break;
|
|
28
|
+
const lines = (carry + buf.toString('utf8', 0, n)).split(eol);
|
|
29
|
+
carry = lines.pop();
|
|
30
|
+
for (const l of lines) onLine(l);
|
|
31
|
+
}
|
|
32
|
+
if (carry) onLine(carry);
|
|
33
|
+
} finally { fs.closeSync(fd); }
|
|
34
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// same-text.mjs - text equality that ignores the line-ending form and a trailing blank tail (a git runner may trim stdout, a work tree may
|
|
2
|
+
// carry CRLF): the one comparison the database rules use for a committed file against its base or against a regenerated one.
|
|
3
|
+
|
|
4
|
+
/** True when both texts exist and are equal once CRLF is folded to LF and trailing whitespace at the end is dropped. */
|
|
5
|
+
export const sameText = (a, b) => a !== null && b !== null && a.replace(/\r\n/g, '\n').trimEnd() === b.replace(/\r\n/g, '\n').trimEnd();
|
|
@@ -23,11 +23,12 @@ export const SECRET_PATTERNS = [
|
|
|
23
23
|
{ name: 'telegram-bot-token', re: /\b\d{8,10}:AA[0-9A-Za-z_-]{33}\b/ },
|
|
24
24
|
{ name: 'stripe-secret', re: /\b(?:sk|rk)_live_[0-9A-Za-z]{20,}\b/ },
|
|
25
25
|
{ name: 'jwt', re: /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/ },
|
|
26
|
+
// A value written `env(NAME)` (the Supabase config.toml reference form) names a variable, not a secret.
|
|
26
27
|
// A keyword-assigned value that names itself a stand-in (a test stub's `accessToken: "fixture-..."`) is no
|
|
27
28
|
// candidate; only this heuristic takes the exemption, never a provider-shaped token above.
|
|
28
|
-
// A spec file's keyword-assigned values are test inputs (
|
|
29
|
-
// form
|
|
30
|
-
{ name: 'assigned-secret', re: /\b(?:password|passwd|secret|api[_-]?key|access[_-]?token|client[_-]?secret)\b\s*[:=]\s*['"]([^'"\s$<{]{12,})['"]/i, placeholder: /fixture|stub|fake|dummy|placeholder|example|sample|changeme|redacted|mock/i,
|
|
29
|
+
// A spec file's keyword-assigned values are test inputs (a spec's sign-in
|
|
30
|
+
// form password and a mocked accessToken once refused a push): the heuristic skips spec files too.
|
|
31
|
+
{ name: 'assigned-secret', re: /\b(?:password|passwd|secret|api[_-]?key|access[_-]?token|client[_-]?secret)\b\s*[:=]\s*['"](?!env\([A-Z_][A-Z0-9_]*\)['"])([^'"\s$<{]{12,})['"]/i, placeholder: /fixture|stub|fake|dummy|placeholder|example|sample|changeme|redacted|mock/i,
|
|
31
32
|
skipFile: /\.(?:spec|test|e2e-spec)\.[cm]?[jt]sx?$/ },
|
|
32
33
|
];
|
|
33
34
|
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* waiting, counted logically by their callers, not by the wall clock) cost milliseconds and the run no longer
|
|
7
7
|
* depends on machine load. Unset in production.
|
|
8
8
|
*/
|
|
9
|
+
import { readEnv } from './env.mjs';
|
|
9
10
|
const scaleOf = () => {
|
|
10
|
-
const raw =
|
|
11
|
+
const raw = readEnv('STARCI_SLEEP_SCALE');
|
|
11
12
|
if (raw === undefined || raw === '') return 1;
|
|
12
13
|
const value = Number(raw);
|
|
13
14
|
return Number.isFinite(value) && value >= 0 ? value : 1;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// sops-envelope.mjs — the pure rules of a test credential (owner ruling push-scan-test-secrets-encrypted, 2026-09-28):
|
|
2
|
+
// where it lives in a product repository's `.starcistacks`, the command that stores it, the sops format of its path, and
|
|
3
|
+
// whether a committed file is a sops envelope with no plaintext value (the push secret scan's test). Reading one is
|
|
4
|
+
// scripts/uat/test-secret.mjs; the sops binary is found by scripts/api/sops/lib.mjs.
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { parseYaml } from '../../engine/yaml.mjs';
|
|
7
|
+
|
|
8
|
+
const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
9
|
+
const STACK = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
10
|
+
/** One sops-encrypted leaf: `ENC[AES256_GCM,data:...,iv:...,tag:...,type:...]` (data may be empty). */
|
|
11
|
+
const SOPS_VALUE = /^ENC\[AES256_GCM,data:[A-Za-z0-9+/=]*,iv:[A-Za-z0-9+/=]+,tag:[A-Za-z0-9+/=]+,type:[a-z]+\]$/;
|
|
12
|
+
|
|
13
|
+
/** Where test credential `name` of `stack` lives in `repo`: {plain, enc, rel}. `rel` is the path the repo's
|
|
14
|
+
* `stack-secret.mjs set` takes. */
|
|
15
|
+
export function testSecretPaths(name, { repo, stack = 'dev' } = {}) {
|
|
16
|
+
if (!NAME.test(String(name ?? ''))) throw new Error(`test secret name must match ${NAME} (got ${JSON.stringify(name)})`);
|
|
17
|
+
if (!STACK.test(String(stack ?? ''))) throw new Error(`stack must match ${STACK} (got ${JSON.stringify(stack)})`);
|
|
18
|
+
if (!repo) throw new Error('test secret needs {repo}: the product repository whose .starcistacks holds it');
|
|
19
|
+
const rel = `${stack}/secrets/test/${name}`;
|
|
20
|
+
const plain = path.join(path.resolve(String(repo)), '.starcistacks', stack, 'secrets', 'test', name);
|
|
21
|
+
return { plain, enc: `${plain}.enc`, rel };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The command that stores a test credential, in the product repository's own tooling. */
|
|
25
|
+
export const setCommand = (name = '<name>', stack = 'dev') => `node scripts/stack-secret.mjs set ${stack}/secrets/test/${name}`;
|
|
26
|
+
|
|
27
|
+
/** sops' format for a plaintext path, as the repository's stack-secret.mjs formatFor decides it. */
|
|
28
|
+
export const sopsFormatFor = (file) => (file.endsWith('.env') ? 'dotenv' : /\.json$/.test(file) ? 'json' : /\.(ya?ml|kubeconfig)$/.test(file) ? 'yaml' : 'binary');
|
|
29
|
+
|
|
30
|
+
/** Every leaf outside the `sops` block is an ENC[...] value (or null); the block itself carries a mac. */
|
|
31
|
+
const allEncrypted = (node) => {
|
|
32
|
+
if (node === null) return true;
|
|
33
|
+
if (Array.isArray(node)) return node.every(allEncrypted);
|
|
34
|
+
if (typeof node === 'object') return Object.values(node).every(allEncrypted);
|
|
35
|
+
return typeof node === 'string' && SOPS_VALUE.test(node);
|
|
36
|
+
};
|
|
37
|
+
const sopsTree = (doc) => !!doc && typeof doc === 'object' && !Array.isArray(doc) && !!doc.sops && typeof doc.sops === 'object'
|
|
38
|
+
&& typeof doc.sops.mac === 'string' && SOPS_VALUE.test(doc.sops.mac)
|
|
39
|
+
&& Object.keys(doc).length > 1 && Object.entries(doc).every(([k, v]) => k === 'sops' || allEncrypted(v));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* True only for a sops-encrypted file with no plaintext value: binary/json form ({"data": ENC[...], "sops": {...}}),
|
|
43
|
+
* yaml form (every value ENC[...] plus the sops block) or dotenv form (KEY=ENC[...] lines plus sops_* metadata).
|
|
44
|
+
* The sops metadata must carry its mac. A key kept plaintext through `unencrypted_suffix` does not pass.
|
|
45
|
+
*/
|
|
46
|
+
export function isSopsEnvelope(text) {
|
|
47
|
+
const src = String(text ?? '').replace(/^/, '');
|
|
48
|
+
if (!src.trim()) return false;
|
|
49
|
+
try { return sopsTree(JSON.parse(src)); } catch { /* not JSON */ }
|
|
50
|
+
const lines = src.split(/\r?\n/).filter((l) => l.trim() && !/^\s*#/.test(l));
|
|
51
|
+
if (lines.length && lines.every((l) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(l))) {
|
|
52
|
+
const pairs = lines.map((l) => [l.slice(0, l.indexOf('=')), l.slice(l.indexOf('=') + 1)]);
|
|
53
|
+
const meta = pairs.filter(([k]) => k.startsWith('sops_'));
|
|
54
|
+
const vals = pairs.filter(([k]) => !k.startsWith('sops_'));
|
|
55
|
+
return vals.length > 0 && vals.every(([, v]) => SOPS_VALUE.test(v)) && meta.some(([k, v]) => k === 'sops_mac' && SOPS_VALUE.test(v));
|
|
56
|
+
}
|
|
57
|
+
try { return sopsTree(parseYaml(src)); } catch { return false; }
|
|
58
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// stack-declaration.mjs - the one reader of a repository's stack declaration (.starcistacks/application-stacks.yaml): where it
|
|
2
|
-
// lives, its bounded read and parse, and the stack block of one service as declared. scripts/
|
|
3
|
-
// services contract) and scripts/
|
|
2
|
+
// lives, its bounded read and parse, and the stack block of one service as declared. scripts/gates/starcistacks.mjs (the
|
|
3
|
+
// services contract) and scripts/hfs/rules/stacks.mjs (the .starcistacks shape, R10) both read it here; this file imports
|
|
4
4
|
// nothing of the kernel, so the published @starci/hfs bundle carries it without the ledger.
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parseYaml } from '../../engine/yaml.mjs';
|
|
4
|
+
import { words } from './dockerfile.mjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The services of a repository's own stack definition, read from `.starcistacks/application-stacks.yaml` and the compose
|
|
@@ -14,11 +15,11 @@ import { parseYaml } from '../../engine/yaml.mjs';
|
|
|
14
15
|
* service and is returned as `proxy`.
|
|
15
16
|
*/
|
|
16
17
|
export const STACKS_DIRECTORY = '.starcistacks';
|
|
17
|
-
|
|
18
|
+
const STACKS_FILE = 'application-stacks.yaml';
|
|
18
19
|
export const DEFAULT_ENVIRONMENT = 'dev';
|
|
19
20
|
|
|
20
21
|
/** Test-only value the `*_FILE` secret variables of a compose service become (the file variable is replaced by the plain variable of the same name). Never a real secret: the test stack listens on loopback only. */
|
|
21
|
-
|
|
22
|
+
const TEST_SECRET_VALUE = 'starci-test-secret';
|
|
22
23
|
|
|
23
24
|
const NOT_RUN_ROLES = new Set(['service', 'observability']);
|
|
24
25
|
const NOT_RUN_PROFILES = new Set(['app']);
|
|
@@ -44,8 +45,6 @@ const KNOWN_IMAGES = Object.freeze({
|
|
|
44
45
|
clickhouse: { kind: 'database', aliases: ['clickhouse', 'database', 'db'], probe: { tcp: true } },
|
|
45
46
|
kafka: { kind: 'queue', aliases: ['kafka', 'queue', 'broker'], probe: { tcp: true } },
|
|
46
47
|
redpanda: { kind: 'queue', aliases: ['redpanda', 'kafka', 'queue', 'broker'], probe: { tcp: true } },
|
|
47
|
-
rabbitmq: { kind: 'queue', aliases: ['rabbitmq', 'amqp', 'queue', 'broker'], probe: { tcp: true } },
|
|
48
|
-
nats: { kind: 'queue', aliases: ['nats', 'queue', 'broker'], probe: { tcp: true } },
|
|
49
48
|
elasticsearch: { kind: 'search', aliases: ['elasticsearch', 'elastic', 'search'], probe: { tcp: true } },
|
|
50
49
|
opensearch: { kind: 'search', aliases: ['opensearch', 'search'], probe: { tcp: true } },
|
|
51
50
|
});
|
|
@@ -58,14 +57,14 @@ const KNOWN_IMAGES = Object.freeze({
|
|
|
58
57
|
export const STATEFUL_KINDS = Object.freeze(new Set(['database', 'cache', 'identity', 'storage', 'mail', 'queue', 'search']));
|
|
59
58
|
|
|
60
59
|
/** The repository name of an image reference: `quay.io/keycloak/keycloak:26.0` -> `keycloak`. */
|
|
61
|
-
|
|
60
|
+
const repositoryOf = image => {
|
|
62
61
|
const withoutDigest = image.split('@')[0];
|
|
63
62
|
const lastSegment = withoutDigest.split('/').at(-1) ?? withoutDigest;
|
|
64
63
|
return lastSegment.split(':')[0];
|
|
65
64
|
};
|
|
66
65
|
|
|
67
66
|
/** The repository path of an image reference without its tag: `quay.io/keycloak/keycloak:26.0` -> `quay.io/keycloak/keycloak`. */
|
|
68
|
-
|
|
67
|
+
const imagePathOf = image => {
|
|
69
68
|
const withoutDigest = image.split('@')[0];
|
|
70
69
|
const lastSlash = withoutDigest.lastIndexOf('/');
|
|
71
70
|
const colon = withoutDigest.indexOf(':', lastSlash + 1);
|
|
@@ -73,30 +72,7 @@ export const imagePathOf = image => {
|
|
|
73
72
|
};
|
|
74
73
|
|
|
75
74
|
/** True when the image reference carries a tag or digest (an external image, not a placeholder of the repository's own build). */
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
/** The shell-like words of a compose `command` string, double and single quotes grouping a word. */
|
|
79
|
-
const words = text => {
|
|
80
|
-
const out = [];
|
|
81
|
-
let current = '';
|
|
82
|
-
let quote = null;
|
|
83
|
-
let started = false;
|
|
84
|
-
for (const char of text) {
|
|
85
|
-
if (quote) {
|
|
86
|
-
if (char === quote) quote = null;
|
|
87
|
-
else current += char;
|
|
88
|
-
} else if (char === '"' || char === "'") {
|
|
89
|
-
quote = char;
|
|
90
|
-
started = true;
|
|
91
|
-
} else if (/\s/u.test(char)) {
|
|
92
|
-
if (started || current) out.push(current);
|
|
93
|
-
current = '';
|
|
94
|
-
started = false;
|
|
95
|
-
} else current += char;
|
|
96
|
-
}
|
|
97
|
-
if (started || current) out.push(current);
|
|
98
|
-
return out;
|
|
99
|
-
};
|
|
75
|
+
const isTagged = image => image.includes('@') || image.slice(image.lastIndexOf('/') + 1).includes(':');
|
|
100
76
|
|
|
101
77
|
const asList = value => (Array.isArray(value) ? value : value === undefined || value === null ? [] : [value]);
|
|
102
78
|
|
|
@@ -129,7 +105,7 @@ const mountsOf = (value, directory) => {
|
|
|
129
105
|
continue;
|
|
130
106
|
}
|
|
131
107
|
const parts = String(entry).split(':');
|
|
132
|
-
// A Windows drive letter is not a separator:
|
|
108
|
+
// A Windows drive letter is not a separator: a `<drive>:<path>:/y` mount keeps its drive.
|
|
133
109
|
const source = /^[A-Za-z]$/u.test(parts[0]) && parts.length > 2 ? `${parts.shift()}:${parts.shift()}` : parts.shift();
|
|
134
110
|
const target = parts.shift();
|
|
135
111
|
if (!source || !target || !(source.startsWith('.') || path.isAbsolute(source))) continue;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// ts-ast.mjs — the small reads the hfs checks share over TypeScript's own AST: a node's name or
|
|
2
|
+
// literal text, a node's 1-based position, a named property's initializer text. `ts` is the caller's
|
|
3
|
+
// already-resolved compiler (see package-at.mjs loadTypescript); nothing here resolves a package.
|
|
4
|
+
|
|
5
|
+
/** The text of an identifier or a string-literal-like `node`, or null (property names, literal specifiers). */
|
|
6
|
+
export const nameText = (ts, node) => (node && (ts.isIdentifier(node) || ts.isStringLiteralLike(node)) ? node.text : null);
|
|
7
|
+
|
|
8
|
+
/** The text of a string-literal-like `node` (a string literal or a no-substitution template), or null. */
|
|
9
|
+
export const literalText = (ts, node) => (node && ts.isStringLiteralLike(node) ? node.text : null);
|
|
10
|
+
|
|
11
|
+
/** The 1-based {line, column} of `node` in `sourceFile`. */
|
|
12
|
+
export const sourceLocation = (sourceFile, node) => {
|
|
13
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
14
|
+
return { line: position.line + 1, column: position.character + 1 };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The `.text` of `key`'s property initializer in `text` (parsed as `file`): a property assignment
|
|
19
|
+
* inside an object literal, or — `declaration: true` — a class property declaration. `kind` is
|
|
20
|
+
* 'string' (a string-literal-like initializer) or 'identifier'. The last match wins; null when none.
|
|
21
|
+
*/
|
|
22
|
+
export function propertyText(ts, text, { file = 'source.ts', key, declaration = false, kind = 'string' } = {}) {
|
|
23
|
+
const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
24
|
+
let value = null;
|
|
25
|
+
const visit = (node) => {
|
|
26
|
+
const isProp = declaration ? ts.isPropertyDeclaration(node) : ts.isPropertyAssignment(node);
|
|
27
|
+
if (isProp && node.name && ts.isIdentifier(node.name) && node.name.text === key && node.initializer) {
|
|
28
|
+
const ok = kind === 'identifier' ? ts.isIdentifier(node.initializer) : ts.isStringLiteralLike(node.initializer);
|
|
29
|
+
if (ok) value = node.initializer.text;
|
|
30
|
+
}
|
|
31
|
+
ts.forEachChild(node, visit);
|
|
32
|
+
};
|
|
33
|
+
visit(source);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
+
// walk.mjs - filesystem walking helpers: isInside (containment) and walkFiles (depth-first file listing).
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import {fileURLToPath} from 'node:url';
|
|
4
4
|
|
|
5
|
-
/** Whether `target` is `root` itself or inside it (filesystem paths, either separator). */
|
|
6
|
-
export function isInside(root, target) {
|
|
5
|
+
/** Whether `target` is `root` itself or inside it (filesystem paths, either separator). `includeSelf: false` excludes `target === root`. */
|
|
6
|
+
export function isInside(root, target, { includeSelf = true } = {}) {
|
|
7
7
|
const relative = path.relative(root, target);
|
|
8
|
-
|
|
8
|
+
if (relative === '') return includeSelf;
|
|
9
|
+
return !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative);
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
/** True when this module is the process entry point (`node file.mjs`, not an import). */
|
|
12
|
-
/** The real path of a file, or its resolved path when it cannot be read (a missing file is never the entry module). */
|
|
13
|
-
const realPathOf = (file) => { try { return fs.realpathSync.native(path.resolve(file)); } catch { return path.resolve(file); } };
|
|
14
|
-
/**
|
|
15
|
-
* True when the module at `metaUrl` is the process entry point. Both sides are compared as REAL paths: Node gives a module
|
|
16
|
-
* its symlink-resolved URL, while argv[1] keeps the path it was started with, so an entry reached through a node_modules
|
|
17
|
-
* junction or symlink (a lane worktree, an npx shim) would otherwise look like an import and silently do nothing.
|
|
18
|
-
*/
|
|
19
|
-
export const isMain = (metaUrl, argv = process.argv) => Boolean(argv[1]) && realPathOf(argv[1]) === realPathOf(fileURLToPath(metaUrl));
|
|
20
|
-
|
|
21
11
|
/**
|
|
22
12
|
* Every entry under `dir` that is not a directory, depth-first; `filter` sees the entry name and full
|
|
23
13
|
* path. `sorted` sorts each directory's entries by name (default: readdir order). `exclude`
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
loadSlotManifest,
|
|
5
|
+
readRepoDeclaration,
|
|
6
|
+
resolveRepoDeclaration,
|
|
7
|
+
} from "../runtime/scripts/hfs/slots.mjs";
|
|
8
|
+
import { imageFiles, TEMPLATES_DIR } from "../sync/index.mjs";
|
|
9
|
+
import { ScaffoldError, pascalOf } from "./service.mjs";
|
|
10
|
+
import { jsonText, packageJsonText } from "./app.mjs";
|
|
11
|
+
|
|
12
|
+
const NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
13
|
+
|
|
14
|
+
const listFiles = (dir, base = dir) =>
|
|
15
|
+
fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
16
|
+
const full = path.join(dir, entry.name);
|
|
17
|
+
return entry.isDirectory()
|
|
18
|
+
? listFiles(full, base)
|
|
19
|
+
: [path.relative(base, full).split(path.sep).join("/")];
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const fill = (text, values, file) =>
|
|
23
|
+
text.replace(/\{\{(project|app|appPascal)\}\}/g, (whole, key) => {
|
|
24
|
+
if (values[key] === undefined)
|
|
25
|
+
throw new ScaffoldError(
|
|
26
|
+
"HFS_ADD_PLACEHOLDER_UNKNOWN",
|
|
27
|
+
`app template ${file} uses ${whole}, which add app does not provide`,
|
|
28
|
+
);
|
|
29
|
+
return values[key];
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Adds one Next workspace from the edition's canonical app skeleton, then registers it in hfs.json.
|
|
34
|
+
* The root workspace glob is added only when an older app manifest does not already carry it.
|
|
35
|
+
*/
|
|
36
|
+
export function addApp({ root, name }) {
|
|
37
|
+
if (!NAME.test(String(name)))
|
|
38
|
+
throw new ScaffoldError(
|
|
39
|
+
"HFS_ADD_NAME_INVALID",
|
|
40
|
+
`app name ${name} must be kebab-case (letters, digits and single dashes)`,
|
|
41
|
+
);
|
|
42
|
+
const declarationFile = path.join(root, "hfs.json");
|
|
43
|
+
const packageFile = path.join(root, "package.json");
|
|
44
|
+
if (!fs.existsSync(declarationFile) || !fs.existsSync(packageFile))
|
|
45
|
+
throw new ScaffoldError(
|
|
46
|
+
"HFS_ADD_NOT_AN_APP",
|
|
47
|
+
"add app runs at the app root containing hfs.json and package.json",
|
|
48
|
+
);
|
|
49
|
+
const manifest = loadSlotManifest();
|
|
50
|
+
const repo = readRepoDeclaration(manifest, root);
|
|
51
|
+
if (!repo.sides?.fe)
|
|
52
|
+
throw new ScaffoldError(
|
|
53
|
+
"HFS_ADD_NO_FRONT_END",
|
|
54
|
+
"this app has no front-end side",
|
|
55
|
+
);
|
|
56
|
+
if (repo.edition === "lite")
|
|
57
|
+
throw new ScaffoldError(
|
|
58
|
+
"HFS_ADD_LITE_SINGLE_APP",
|
|
59
|
+
"a lite app has one front-end app; a second app needs the shared <project>-ui and <project>-i18n packages: run starci app upgrade --edition full",
|
|
60
|
+
);
|
|
61
|
+
if (repo.sides.fe.apps.some((app) => app.name === name))
|
|
62
|
+
throw new ScaffoldError(
|
|
63
|
+
"HFS_ADD_EXISTS",
|
|
64
|
+
`front-end app ${name} is already declared`,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const source = path.join(
|
|
68
|
+
TEMPLATES_DIR,
|
|
69
|
+
"fe",
|
|
70
|
+
repo.edition === "lite" ? "skeleton-lite/apps/web" : "skeleton/apps/app",
|
|
71
|
+
);
|
|
72
|
+
if (!fs.existsSync(source))
|
|
73
|
+
throw new ScaffoldError(
|
|
74
|
+
"HFS_ADD_TEMPLATE_MISSING",
|
|
75
|
+
`the ${repo.edition} Next app skeleton is not carried by this package`,
|
|
76
|
+
);
|
|
77
|
+
const values = {
|
|
78
|
+
project: repo.project,
|
|
79
|
+
app: name,
|
|
80
|
+
appPascal: pascalOf(name),
|
|
81
|
+
};
|
|
82
|
+
const targetBase = `fe/apps/${name}`;
|
|
83
|
+
const planned = listFiles(source).map((relative) => ({
|
|
84
|
+
relative: `${targetBase}/${relative}`,
|
|
85
|
+
body: fill(
|
|
86
|
+
fs
|
|
87
|
+
.readFileSync(path.join(source, ...relative.split("/")), "utf8")
|
|
88
|
+
.replace(/\r\n/g, "\n"),
|
|
89
|
+
values,
|
|
90
|
+
relative,
|
|
91
|
+
),
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
const firstApp = repo.sides.fe.apps.find((app) => app.kind === "next");
|
|
95
|
+
const firstPackage = firstApp
|
|
96
|
+
? path.join(root, "fe", "apps", firstApp.name, "package.json")
|
|
97
|
+
: null;
|
|
98
|
+
if (!firstPackage || !fs.existsSync(firstPackage))
|
|
99
|
+
throw new ScaffoldError(
|
|
100
|
+
"HFS_ADD_APP_PACKAGE_SOURCE",
|
|
101
|
+
"add app needs the package.json of an existing declared Next app as the edition-matched dependency source",
|
|
102
|
+
);
|
|
103
|
+
const workspacePackage = JSON.parse(fs.readFileSync(firstPackage, "utf8"));
|
|
104
|
+
workspacePackage.name = `@${repo.project}/${name}`;
|
|
105
|
+
const appTemplates = path.join(TEMPLATES_DIR, "fe", "app");
|
|
106
|
+
planned.push(
|
|
107
|
+
{
|
|
108
|
+
relative: `${targetBase}/package.json`,
|
|
109
|
+
body:
|
|
110
|
+
fs
|
|
111
|
+
.readFileSync(path.join(appTemplates, "package.json.tpl"), "utf8")
|
|
112
|
+
.replace("{{packageJson}}", packageJsonText(workspacePackage).trimEnd()),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
relative: `${targetBase}/tsconfig.json`,
|
|
116
|
+
body: fs.readFileSync(path.join(appTemplates, "tsconfig.json"), "utf8"),
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
const declaration = JSON.parse(fs.readFileSync(declarationFile, "utf8"));
|
|
120
|
+
declaration.sides.fe.apps.push({ name, kind: "next" });
|
|
121
|
+
if (repo.edition === "full") {
|
|
122
|
+
const image = imageFiles(
|
|
123
|
+
resolveRepoDeclaration(manifest, declaration),
|
|
124
|
+
).find((file) => file.path === `${targetBase}/Dockerfile`);
|
|
125
|
+
if (!image)
|
|
126
|
+
throw new ScaffoldError(
|
|
127
|
+
"HFS_ADD_TEMPLATE_MISSING",
|
|
128
|
+
`the Dockerfile of front-end app ${name} did not render`,
|
|
129
|
+
);
|
|
130
|
+
planned.push({ relative: image.path, body: image.content });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const clashes = planned
|
|
134
|
+
.filter((file) =>
|
|
135
|
+
fs.existsSync(path.join(root, ...file.relative.split("/"))),
|
|
136
|
+
)
|
|
137
|
+
.map((file) => file.relative);
|
|
138
|
+
if (clashes.length)
|
|
139
|
+
throw new ScaffoldError(
|
|
140
|
+
"HFS_ADD_EXISTS",
|
|
141
|
+
`front-end app ${name} already exists: ${clashes.join(", ")}`,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const rootPackage = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
|
145
|
+
const oldDeclaration = fs.readFileSync(declarationFile, "utf8");
|
|
146
|
+
const oldPackage = fs.readFileSync(packageFile, "utf8");
|
|
147
|
+
rootPackage.workspaces = [
|
|
148
|
+
...new Set([...(rootPackage.workspaces ?? []), "fe/apps/*"]),
|
|
149
|
+
];
|
|
150
|
+
const created = [];
|
|
151
|
+
try {
|
|
152
|
+
for (const file of planned) {
|
|
153
|
+
const target = path.join(root, ...file.relative.split("/"));
|
|
154
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
155
|
+
fs.writeFileSync(target, file.body);
|
|
156
|
+
created.push(target);
|
|
157
|
+
}
|
|
158
|
+
fs.writeFileSync(declarationFile, jsonText(declaration));
|
|
159
|
+
fs.writeFileSync(packageFile, packageJsonText(rootPackage));
|
|
160
|
+
} catch (error) {
|
|
161
|
+
for (const file of [...created].reverse())
|
|
162
|
+
if (fs.existsSync(file)) fs.rmSync(file);
|
|
163
|
+
const appDir = path.join(root, "fe", "apps", name);
|
|
164
|
+
if (fs.existsSync(appDir))
|
|
165
|
+
fs.rmSync(appDir, { recursive: true, force: true });
|
|
166
|
+
fs.writeFileSync(declarationFile, oldDeclaration);
|
|
167
|
+
fs.writeFileSync(packageFile, oldPackage);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
created: planned.map((file) => file.relative),
|
|
172
|
+
registered: { app: name, workspace: "fe/apps/*" },
|
|
173
|
+
};
|
|
174
|
+
}
|