@starci/hfs 4.3.0 → 4.4.0
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 +11 -0
- package/db/migrate.mjs +361 -0
- package/db/post-cutover.mjs +136 -0
- package/emit/db-types.mjs +10 -1
- package/lint/run.mjs +1 -1
- package/package.json +4 -1
- package/release/gate.mjs +298 -0
- package/release/index.mjs +103 -0
- package/release/push.mjs +110 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/rules.yaml +3401 -152
- package/runtime/knowledge/hfs/slots.yaml +344 -62
- package/runtime/knowledge/patterns/be/api.yaml +32 -5
- package/runtime/knowledge/patterns/be/cli.yaml +14 -4
- package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
- package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
- package/runtime/knowledge/patterns/be/projections.yaml +4 -2
- package/runtime/knowledge/patterns/be/queues.yaml +11 -3
- package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
- package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
- package/runtime/knowledge/patterns/be/saga.yaml +20 -3
- package/runtime/knowledge/patterns/be/webhooks.yaml +23 -4
- package/runtime/knowledge/patterns/fe/folder.yaml +1 -0
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/failure-codes.yaml +3113 -43
- package/runtime/modules/models/runtimes.yaml +1 -1
- package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
- package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
- package/runtime/packages/cli/src/validate-args.mjs +213 -0
- package/runtime/scripts/api/git/add.mjs +5 -0
- package/runtime/scripts/api/git/init.mjs +7 -0
- package/runtime/scripts/api/git/lib.mjs +3 -0
- package/runtime/scripts/api/git/tag.mjs +5 -0
- package/runtime/scripts/api/node/lib.mjs +20 -0
- package/runtime/scripts/api/node/run-node.mjs +6 -0
- package/runtime/scripts/api/sops/lib.mjs +41 -0
- package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
- package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
- package/runtime/scripts/api/supabase/database-query.mjs +5 -0
- package/runtime/scripts/api/supabase/lib.mjs +44 -0
- package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
- package/runtime/scripts/hfs/app-wiring.mjs +201 -0
- package/runtime/scripts/hfs/architecture/backend-app-files.mjs +1 -8
- package/runtime/scripts/hfs/architecture/backend.mjs +2 -7
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
- package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +9 -41
- package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
- package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +28 -13
- package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
- package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
- package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -2
- package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +211 -11
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -1
- package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
- package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
- package/runtime/scripts/hfs/architecture/tiers.mjs +3 -3
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +8 -2
- package/runtime/scripts/hfs/check-results.mjs +28 -0
- package/runtime/scripts/hfs/check.mjs +46 -47
- package/runtime/scripts/hfs/coverage-from.mjs +70 -0
- package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
- package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
- package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
- package/runtime/scripts/hfs/doc-commands.mjs +367 -0
- package/runtime/scripts/hfs/edition-slots.mjs +2 -2
- package/runtime/scripts/hfs/example-secrets.mjs +158 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +67 -7
- package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
- package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
- package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
- package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
- package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
- package/runtime/scripts/hfs/rules/assets.mjs +79 -0
- package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
- package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
- package/runtime/scripts/hfs/rules/contract.mjs +66 -5
- package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
- package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
- package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
- package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
- package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +286 -7
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
- package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
- package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
- package/runtime/scripts/hfs/rules/database.mjs +9 -0
- package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
- package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
- package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
- package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
- package/runtime/scripts/hfs/rules/docker.mjs +24 -7
- package/runtime/scripts/hfs/rules/edition.mjs +9 -8
- package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
- package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
- package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
- package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
- package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
- package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
- package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
- package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
- package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
- package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
- package/runtime/scripts/hfs/rules/saga.mjs +8 -16
- package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
- package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +13 -0
- package/runtime/scripts/hfs/rules/services.mjs +4 -4
- package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
- package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
- package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
- package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +41 -1
- package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
- package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
- package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
- package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
- package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
- package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
- package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
- package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
- package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
- package/runtime/scripts/hfs/sealed-members.mjs +101 -0
- package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
- package/runtime/scripts/hfs/secret-edit.mjs +170 -0
- package/runtime/scripts/hfs/secret-init.mjs +127 -19
- package/runtime/scripts/hfs/secret-sops.mjs +38 -6
- package/runtime/scripts/hfs/secret-store.mjs +56 -54
- package/runtime/scripts/hfs/secret.mjs +34 -5
- package/runtime/scripts/hfs/slot-imports.mjs +18 -5
- package/runtime/scripts/hfs/slots.mjs +1 -0
- package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
- package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
- package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
- package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
- package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
- package/runtime/scripts/lib/command-tokens.mjs +126 -0
- package/runtime/scripts/lib/compose-values.mjs +11 -0
- package/runtime/scripts/lib/deployment-findings.mjs +7 -0
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/list.mjs +4 -0
- package/runtime/scripts/lib/runtime-example.mjs +13 -0
- package/runtime/scripts/lib/secret-patterns.mjs +60 -5
- package/runtime/scripts/lib/sql-ast.mjs +32 -0
- package/runtime/scripts/lib/stack-services.mjs +1 -1
- package/runtime/scripts/lib/swarm-stack.mjs +5 -18
- package/runtime/scripts/lib/trim-end.mjs +8 -0
- package/runtime/scripts/lib/ts-analysis.mjs +5 -2
- package/runtime/scripts/lib/ts-ast.mjs +27 -2
- package/runtime/scripts/lib/workflow-events.mjs +8 -0
- package/scaffold/add-app.mjs +77 -59
- package/scaffold/add-cli-lite.mjs +14 -6
- package/scaffold/add-compose.mjs +79 -0
- package/scaffold/add-database-lite.mjs +14 -0
- package/scaffold/add-fe-overlay.mjs +122 -0
- package/scaffold/add-fe-page.mjs +269 -0
- package/scaffold/add-inbox-lite.mjs +74 -42
- package/scaffold/add-table.mjs +110 -87
- package/scaffold/add.mjs +159 -44
- package/scaffold/app.mjs +124 -39
- package/scaffold/edition-gate.mjs +3 -3
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +2 -2
- package/src/main.mjs +229 -116
- package/stack/age-install.mjs +37 -0
- package/stack/backup.mjs +100 -0
- package/stack/build-args.mjs +42 -0
- package/stack/build.mjs +173 -0
- package/stack/cli.mjs +130 -0
- package/stack/config.mjs +57 -0
- package/stack/context.mjs +57 -0
- package/stack/converge.mjs +107 -0
- package/stack/deploy.mjs +301 -0
- package/stack/digest.mjs +16 -0
- package/stack/docker-transport.mjs +53 -0
- package/stack/edge-alias.mjs +118 -0
- package/stack/errors.mjs +21 -0
- package/stack/image-scan.mjs +152 -0
- package/stack/ingress-create.mjs +114 -0
- package/stack/ingress-publish.mjs +273 -0
- package/stack/ingress.mjs +706 -0
- package/stack/jobs.mjs +51 -0
- package/stack/live-restore.mjs +177 -0
- package/stack/prepare.mjs +266 -0
- package/stack/private-file.mjs +22 -0
- package/stack/provision.mjs +90 -0
- package/stack/prune.mjs +46 -0
- package/stack/release-store.mjs +87 -0
- package/stack/remote.mjs +205 -0
- package/stack/render.mjs +149 -0
- package/stack/retire.mjs +47 -0
- package/stack/sealed.mjs +149 -0
- package/stack/task.mjs +42 -0
- package/stack/verify.mjs +99 -0
- package/sync/ci-commands.mjs +13 -0
- package/sync/index.mjs +108 -52
- package/sync/stack-images.mjs +127 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
- package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
- package/templates/app/codegen-graphql.mjs.partial +13 -2
- package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
- package/templates/app/codegen-prelude.mjs.partial +8 -0
- package/templates/app/gitignore +3 -0
- package/templates/app/gitignore-lite +3 -0
- package/templates/app/package-scripts/package.json +1 -0
- package/templates/app/package-scripts/supabase.json +7 -0
- package/templates/app/package-scripts-lite/package.json +1 -7
- package/templates/app/skeleton/.editorconfig +1 -0
- package/templates/app/skeleton/.gitattributes +7 -1
- package/templates/app/skeleton/.github/dependabot.yml +17 -0
- package/templates/app/skeleton/.nvmrc +1 -1
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
- package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
- package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
- package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
- package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
- package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
- package/templates/app/skeleton/README.md +6 -6
- package/templates/app/skeleton/scripts/codegen.mjs +1 -2
- package/templates/app/skeleton-lite/.editorconfig +16 -0
- package/templates/app/skeleton-lite/.gitattributes +8 -0
- package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
- package/templates/app/skeleton-lite/.nvmrc +1 -0
- package/templates/app/skeleton-lite/.sops.yaml +3 -2
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +178 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
- package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
- package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
- package/templates/app/skeleton-lite/README.md +5 -3
- package/templates/app/skeleton-lite/supabase/config.toml +8 -1
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/inbox/database-client.spec.ts.tpl +17 -18
- package/templates/be/inbox/database-client.ts.tpl +12 -11
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
- package/templates/be/inbox/event-index.ts.tpl +1 -0
- package/templates/be/inbox/events.json.tpl +10 -0
- package/templates/be/inbox/migration.sql.tpl +1 -0
- package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
- package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
- package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
- package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
- package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
- package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
- package/templates/be/inbox/platform/migration.sql.tpl +48 -0
- package/templates/be/inbox/service.spec.ts.tpl +42 -9
- package/templates/be/inbox/service.ts.tpl +23 -8
- package/templates/be/inbox/webhook-door.spec.ts.tpl +1 -1
- package/templates/be/inbox/webhook-door.ts.tpl +2 -2
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
- package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
- package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
- package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
- package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
- package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
- package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
- package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
- package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
- package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
- package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
- package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
- package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
- package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
- package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
- package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
- package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +11 -9
- package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
- package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +2 -0
- package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
- package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton/apps/__app__/src/main.ts +15 -1
- package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +6 -14
- package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
- package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
- package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
- package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
- package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
- package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
- package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
- package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
- package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
- package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
- package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
- package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
- package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
- package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
- package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
- package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +14 -18
- package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +2 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +2 -3
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
- package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/feature.module.ts.tpl +2 -1
- package/templates/be/table/api/feature.options.ts.tpl +2 -0
- package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
- package/templates/be/table/api/graphql.module.ts.tpl +3 -2
- package/templates/be/table/api/graphql.options.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +1 -0
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
- package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
- package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
- package/templates/fe/auth/supabase-client.ts.tpl +34 -0
- package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
- package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
- package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
- package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
- package/upgrade/check.mjs +3 -1
- package/upgrade/index.mjs +53 -40
package/scaffold/add-app.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "../runtime/scripts/hfs/slots.mjs";
|
|
8
8
|
import { imageFiles, render, TEMPLATES_DIR } from "../sync/index.mjs";
|
|
9
9
|
import { ScaffoldError, pascalOf } from "./service.mjs";
|
|
10
|
-
import { PARTIAL_VARIABLES, coreFeApps, jsonText, packageJsonText } from "./app.mjs";
|
|
10
|
+
import { PARTIAL_VARIABLES, bundledPins, callbackSourceFiles, coreFeApps, hasAuthCallback, jsonText, packageJsonText } from "./app.mjs";
|
|
11
11
|
|
|
12
12
|
/** The shared fe app core (modules/api, auth, toast, redux, the providers, the hooks): the base tree, then the edition overlay. */
|
|
13
13
|
const CORE_DIRS = Object.freeze({
|
|
@@ -35,37 +35,8 @@ const fill = (text, values, file) =>
|
|
|
35
35
|
return values[key];
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
* 2026-10-13: lite and full have one fe structure), then registers it in hfs.json. The root workspace glob is added only when an
|
|
41
|
-
* older app manifest does not already carry it.
|
|
42
|
-
*/
|
|
43
|
-
export function addApp({ root, name }) {
|
|
44
|
-
if (!NAME.test(String(name)))
|
|
45
|
-
throw new ScaffoldError(
|
|
46
|
-
"HFS_ADD_NAME_INVALID",
|
|
47
|
-
`app name ${name} must be kebab-case (letters, digits and single dashes)`,
|
|
48
|
-
);
|
|
49
|
-
const declarationFile = path.join(root, "hfs.json");
|
|
50
|
-
const packageFile = path.join(root, "package.json");
|
|
51
|
-
if (!fs.existsSync(declarationFile) || !fs.existsSync(packageFile))
|
|
52
|
-
throw new ScaffoldError(
|
|
53
|
-
"HFS_ADD_NOT_AN_APP",
|
|
54
|
-
"add app runs at the app root containing hfs.json and package.json",
|
|
55
|
-
);
|
|
56
|
-
const manifest = loadSlotManifest();
|
|
57
|
-
const repo = readRepoDeclaration(manifest, root);
|
|
58
|
-
if (!repo.sides?.fe)
|
|
59
|
-
throw new ScaffoldError(
|
|
60
|
-
"HFS_ADD_NO_FRONT_END",
|
|
61
|
-
"this app has no front-end side",
|
|
62
|
-
);
|
|
63
|
-
if (repo.sides.fe.apps.some((app) => app.name === name))
|
|
64
|
-
throw new ScaffoldError(
|
|
65
|
-
"HFS_ADD_EXISTS",
|
|
66
|
-
`front-end app ${name} is already declared`,
|
|
67
|
-
);
|
|
68
|
-
|
|
38
|
+
/** Render the edition skeleton followed by its shared core and edition overlay. */
|
|
39
|
+
function planAppTemplates(repo, name) {
|
|
69
40
|
const source = path.join(
|
|
70
41
|
TEMPLATES_DIR,
|
|
71
42
|
"fe",
|
|
@@ -94,8 +65,13 @@ export function addApp({ root, name }) {
|
|
|
94
65
|
if (!fs.existsSync(dir)) continue;
|
|
95
66
|
for (const relative of listFiles(dir)) byPath.set(relative, { relative: `${targetBase}/${relative}`, body: readTemplate(dir, relative) });
|
|
96
67
|
}
|
|
97
|
-
|
|
68
|
+
return callbackSourceFiles([...byPath.values()].map(file => ({ path: file.relative, content: file.body })), repo)
|
|
69
|
+
.map(file => ({ relative: file.path, body: file.content }));
|
|
70
|
+
}
|
|
98
71
|
|
|
72
|
+
/** The package and TypeScript manifests of a new Next workspace. */
|
|
73
|
+
function planWorkspaceManifests(root, repo, name) {
|
|
74
|
+
const targetBase = `fe/apps/${name}`;
|
|
99
75
|
// The dependency source is a product app (it carries the core's fe-stack packages), else the first Next app.
|
|
100
76
|
const firstApp = coreFeApps(repo.sides.fe.apps)[0] ?? repo.sides.fe.apps.find((app) => app.kind === "next");
|
|
101
77
|
const firstPackage = firstApp
|
|
@@ -108,8 +84,14 @@ export function addApp({ root, name }) {
|
|
|
108
84
|
);
|
|
109
85
|
const workspacePackage = JSON.parse(fs.readFileSync(firstPackage, "utf8"));
|
|
110
86
|
workspacePackage.name = `@${repo.project}/${name}`;
|
|
87
|
+
if (hasAuthCallback(repo)) {
|
|
88
|
+
const pins = bundledPins();
|
|
89
|
+
workspacePackage.dependencies ??= {};
|
|
90
|
+
for (const dependency of ["@supabase/ssr", "@supabase/supabase-js", "server-only"])
|
|
91
|
+
workspacePackage.dependencies[dependency] = pins[dependency].version;
|
|
92
|
+
}
|
|
111
93
|
const appTemplates = path.join(TEMPLATES_DIR, "fe", "app");
|
|
112
|
-
|
|
94
|
+
return [
|
|
113
95
|
{
|
|
114
96
|
relative: `${targetBase}/package.json`,
|
|
115
97
|
body:
|
|
@@ -121,32 +103,10 @@ export function addApp({ root, name }) {
|
|
|
121
103
|
relative: `${targetBase}/tsconfig.json`,
|
|
122
104
|
body: fs.readFileSync(path.join(appTemplates, "tsconfig.json"), "utf8"),
|
|
123
105
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
declaration.sides.fe.apps.push({ name, kind: "next" });
|
|
127
|
-
if (repo.edition === "full") {
|
|
128
|
-
const image = imageFiles(
|
|
129
|
-
resolveRepoDeclaration(manifest, declaration),
|
|
130
|
-
).find((file) => file.path === `${targetBase}/Dockerfile`);
|
|
131
|
-
if (!image)
|
|
132
|
-
throw new ScaffoldError(
|
|
133
|
-
"HFS_ADD_TEMPLATE_MISSING",
|
|
134
|
-
`the Dockerfile of front-end app ${name} did not render`,
|
|
135
|
-
);
|
|
136
|
-
planned.push({ relative: image.path, body: image.content });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const clashes = planned
|
|
140
|
-
.filter((file) =>
|
|
141
|
-
fs.existsSync(path.join(root, ...file.relative.split("/"))),
|
|
142
|
-
)
|
|
143
|
-
.map((file) => file.relative);
|
|
144
|
-
if (clashes.length)
|
|
145
|
-
throw new ScaffoldError(
|
|
146
|
-
"HFS_ADD_EXISTS",
|
|
147
|
-
`front-end app ${name} already exists: ${clashes.join(", ")}`,
|
|
148
|
-
);
|
|
106
|
+
];
|
|
107
|
+
}
|
|
149
108
|
|
|
109
|
+
function persistAppFiles({ root, name, planned, declaration, declarationFile, packageFile }) {
|
|
150
110
|
const rootPackage = JSON.parse(fs.readFileSync(packageFile, "utf8"));
|
|
151
111
|
const oldDeclaration = fs.readFileSync(declarationFile, "utf8");
|
|
152
112
|
const oldPackage = fs.readFileSync(packageFile, "utf8");
|
|
@@ -173,6 +133,64 @@ export function addApp({ root, name }) {
|
|
|
173
133
|
fs.writeFileSync(packageFile, oldPackage);
|
|
174
134
|
throw error;
|
|
175
135
|
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Adds one Next workspace from the edition's canonical app skeleton and the shared app core (both editions, owner ruling
|
|
140
|
+
* 2026-10-13: lite and full have one fe structure), then registers it in hfs.json. The root workspace glob is added only when an
|
|
141
|
+
* older app manifest does not already carry it.
|
|
142
|
+
*/
|
|
143
|
+
export function addApp({ root, name }) {
|
|
144
|
+
if (!NAME.test(String(name)))
|
|
145
|
+
throw new ScaffoldError(
|
|
146
|
+
"HFS_ADD_NAME_INVALID",
|
|
147
|
+
`app name ${name} must be kebab-case (letters, digits and single dashes)`,
|
|
148
|
+
);
|
|
149
|
+
const declarationFile = path.join(root, "hfs.json");
|
|
150
|
+
const packageFile = path.join(root, "package.json");
|
|
151
|
+
if (!fs.existsSync(declarationFile) || !fs.existsSync(packageFile))
|
|
152
|
+
throw new ScaffoldError(
|
|
153
|
+
"HFS_ADD_NOT_AN_APP",
|
|
154
|
+
"add app runs at the app root containing hfs.json and package.json",
|
|
155
|
+
);
|
|
156
|
+
const manifest = loadSlotManifest();
|
|
157
|
+
const repo = readRepoDeclaration(manifest, root);
|
|
158
|
+
if (!repo.sides?.fe)
|
|
159
|
+
throw new ScaffoldError(
|
|
160
|
+
"HFS_ADD_NO_FRONT_END",
|
|
161
|
+
"this app has no front-end side",
|
|
162
|
+
);
|
|
163
|
+
if (repo.sides.fe.apps.some((app) => app.name === name))
|
|
164
|
+
throw new ScaffoldError(
|
|
165
|
+
"HFS_ADD_EXISTS",
|
|
166
|
+
`front-end app ${name} is already declared`,
|
|
167
|
+
);
|
|
168
|
+
const targetBase = `fe/apps/${name}`;
|
|
169
|
+
const planned = [...planAppTemplates(repo, name), ...planWorkspaceManifests(root, repo, name)];
|
|
170
|
+
const declaration = JSON.parse(fs.readFileSync(declarationFile, "utf8"));
|
|
171
|
+
declaration.sides.fe.apps.push({ name, kind: "next" });
|
|
172
|
+
const image = imageFiles(
|
|
173
|
+
resolveRepoDeclaration(manifest, declaration),
|
|
174
|
+
).find((file) => file.path === `${targetBase}/Dockerfile`);
|
|
175
|
+
if (!image && repo.edition !== "lite")
|
|
176
|
+
throw new ScaffoldError(
|
|
177
|
+
"HFS_ADD_TEMPLATE_MISSING",
|
|
178
|
+
`the Dockerfile of front-end app ${name} did not render`,
|
|
179
|
+
);
|
|
180
|
+
if (image) planned.push({ relative: image.path, body: image.content });
|
|
181
|
+
|
|
182
|
+
const clashes = planned
|
|
183
|
+
.filter((file) =>
|
|
184
|
+
fs.existsSync(path.join(root, ...file.relative.split("/"))),
|
|
185
|
+
)
|
|
186
|
+
.map((file) => file.relative);
|
|
187
|
+
if (clashes.length)
|
|
188
|
+
throw new ScaffoldError(
|
|
189
|
+
"HFS_ADD_EXISTS",
|
|
190
|
+
`front-end app ${name} already exists: ${clashes.join(", ")}`,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
persistAppFiles({ root, name, planned, declaration, declarationFile, packageFile });
|
|
176
194
|
return {
|
|
177
195
|
created: planned.map((file) => file.relative),
|
|
178
196
|
registered: { app: name, workspace: "fe/apps/*" },
|
|
@@ -109,10 +109,15 @@ function enableLiteCliDatabase(root) {
|
|
|
109
109
|
text = text.replace(anchor, `${anchor}\nimport { MigrationRunnerService } from "./migration-runner.service"`);
|
|
110
110
|
}
|
|
111
111
|
if (!text.includes('import { SeedRunnerService } from "./seed-runner.service"')) {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// The import of the entity-manager token, with or without the probe token beside it.
|
|
113
|
+
const anchor = /^import \{[^}]*\bPRIMARY_ENTITY_MANAGER\b[^}]*\} from "\.\/primary\.decorators"$/m.exec(text)?.[0];
|
|
114
|
+
if (!anchor) throw new ScaffoldError("HFS_ADD_CLI_DATABASE", `${relative} has an unknown import shape`);
|
|
114
115
|
text = text.replace(anchor, `${anchor}\nimport { SeedRunnerService } from "./seed-runner.service"`);
|
|
115
116
|
}
|
|
117
|
+
const probeProviders = "providers: [...(base.providers ?? []), ...managers, ...probes]";
|
|
118
|
+
if (text.includes(probeProviders)) {
|
|
119
|
+
text = text.replace(probeProviders, "providers: [...(base.providers ?? []), ...managers, ...probes, MigrationRunnerService, SeedRunnerService]");
|
|
120
|
+
}
|
|
116
121
|
const providers = "providers: [...(base.providers ?? []), ...managers]";
|
|
117
122
|
if (text.includes(providers)) {
|
|
118
123
|
text = text.replace(providers, "providers: [...(base.providers ?? []), ...managers, MigrationRunnerService, SeedRunnerService]");
|
|
@@ -180,6 +185,9 @@ export function ensureLiteCli({ root, manifest, repo }) {
|
|
|
180
185
|
"be/apps/cli/src/app.module.ts",
|
|
181
186
|
read("be/skeleton/apps/cli/src/app.module.ts"),
|
|
182
187
|
],
|
|
188
|
+
["be/apps/cli/src/compose/platform.ts", read("be/skeleton-lite/apps/cli/src/compose/platform.ts")],
|
|
189
|
+
["be/apps/cli/src/compose/cli.ts", read("be/skeleton/apps/cli/src/compose/cli.ts")],
|
|
190
|
+
["be/src/tests/fixtures/builders/cli.builder.ts", read("be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts")],
|
|
183
191
|
["be/apps/cli/src/cli.env.ts", read("be/skeleton-lite/apps/cli/src/cli.env.ts")],
|
|
184
192
|
["be/apps/cli/src/cli.options.ts", CLI_OPTIONS],
|
|
185
193
|
[
|
|
@@ -203,7 +211,7 @@ export function ensureLiteCli({ root, manifest, repo }) {
|
|
|
203
211
|
read("be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts"),
|
|
204
212
|
],
|
|
205
213
|
["be/src/features/cli/migrate/subs/run.cli.ts", MIGRATE_RUN],
|
|
206
|
-
//
|
|
214
|
+
// ruling:R212/case-1, amended by ruling:R212/case-4: each cli command of a lite back end owes its unit spec, as in full (ruleParams.be.unitRoles).
|
|
207
215
|
[
|
|
208
216
|
"be/src/features/cli/migrate/migrate.cli.spec.ts",
|
|
209
217
|
read("be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts"),
|
|
@@ -255,7 +263,7 @@ export function ensureLiteCli({ root, manifest, repo }) {
|
|
|
255
263
|
);
|
|
256
264
|
const nest = JSON.parse(fs.readFileSync(nestFile, "utf8"));
|
|
257
265
|
nest.projects = {
|
|
258
|
-
...
|
|
266
|
+
...nest.projects,
|
|
259
267
|
cli: {
|
|
260
268
|
type: "application",
|
|
261
269
|
root: "apps/cli",
|
|
@@ -269,9 +277,9 @@ export function ensureLiteCli({ root, manifest, repo }) {
|
|
|
269
277
|
const pin = parseYaml(fs.readFileSync(PINS_FILE, "utf8")).pins?.["nest-commander"]?.version;
|
|
270
278
|
if (typeof pin !== "string" || pin === "")
|
|
271
279
|
throw new ScaffoldError("HFS_ADD_CANON_PIN_MISSING", "add cli needs the nest-commander canon pin");
|
|
272
|
-
packageManifest.dependencies = { ...
|
|
280
|
+
packageManifest.dependencies = { ...packageManifest.dependencies, "nest-commander": pin };
|
|
273
281
|
packageManifest.scripts = {
|
|
274
|
-
...
|
|
282
|
+
...packageManifest.scripts,
|
|
275
283
|
...managedScripts(resolved, appSource(root)),
|
|
276
284
|
};
|
|
277
285
|
for (const [relative, body] of files) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { TEMPLATES_DIR } from '../sync/index.mjs';
|
|
4
|
+
import { ScaffoldError } from './service.mjs';
|
|
5
|
+
|
|
6
|
+
/** Adds `importLine` and one `symbol` entry of the `imports` array of a module file's lines, once. */
|
|
7
|
+
export function addModuleImport(lines, { importLine, entry, relative, noun }) {
|
|
8
|
+
const active = compositionCode(lines.join('\n')).split('\n');
|
|
9
|
+
const imported = active.some(line => line.replace(/;\s*$/, '') === importLine);
|
|
10
|
+
const registered = active.some(line => line.trim() === `${entry},` ||
|
|
11
|
+
(/\bimports:\s*\[/.test(line) && !line.trim().startsWith('//') && line.replace(/\s+/g, '').includes(entry.replace(/\s+/g, ''))));
|
|
12
|
+
const lastImport = active.reduce((last, line, index) => line.startsWith('import ') ? index : last, -1);
|
|
13
|
+
const importsLine = line => /\bimports:\s*\[/.test(line) && !line.trim().startsWith('//');
|
|
14
|
+
if (lastImport < 0 || active.findIndex(importsLine) < 0) throw new ScaffoldError('HFS_ADD_WIRE_INVALID', `add ${noun} could not find the imports array in ${relative}`);
|
|
15
|
+
if (imported && registered) return;
|
|
16
|
+
if (!imported) lines.splice(lastImport + 1, 0, importLine);
|
|
17
|
+
if (registered) return;
|
|
18
|
+
const imports = compositionCode(lines.join('\n')).split('\n').findIndex(importsLine);
|
|
19
|
+
const indent = /^(\s*)/.exec(lines[imports])?.[1] ?? '';
|
|
20
|
+
if (/^\s*imports: \[$/.test(lines[imports])) lines.splice(imports + 1, 0, `${indent} ${entry},`);
|
|
21
|
+
else lines[imports] = lines[imports].replace(/(\bimports:\s*\[)\s*/, `$1${entry}, `);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Masks comments without changing offsets or treating quoted module specifiers as comments. */
|
|
25
|
+
export function compositionCode(text) {
|
|
26
|
+
return text.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g,
|
|
27
|
+
value => value.startsWith('/') ? value.replace(/[^\r\n]/g, ' ') : value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** Reads the explicitly typed expression body of a group without treating comments as active declarations. */
|
|
32
|
+
export function readComposeGroup({ repoRoot, repo, app, area, noun }) {
|
|
33
|
+
const relative = `be/apps/${app}/src/compose/${area}.ts`;
|
|
34
|
+
const file = path.join(repoRoot, ...relative.split('/'));
|
|
35
|
+
const exists = fs.existsSync(file);
|
|
36
|
+
const skeleton = repo.edition === 'lite' ? 'skeleton-lite/apps/api' : 'skeleton/apps/__app__';
|
|
37
|
+
const template = path.join(TEMPLATES_DIR, 'be', ...skeleton.split('/'), 'src', 'compose', `${area}.ts`);
|
|
38
|
+
const text = fs.readFileSync(exists ? file : template, 'utf8').replace(/\r\n/g, '\n');
|
|
39
|
+
const argument = area === 'domain' && repo.edition === 'lite' ? 'verifyAccessToken: VerifySupabaseAccessToken' : '';
|
|
40
|
+
const marker = `export const ${area}Modules = (${argument}): Array<Type<unknown> | DynamicModule> => [`;
|
|
41
|
+
const declarationAt = compositionCode(text).indexOf(`\n${marker}`);
|
|
42
|
+
const active = declarationAt >= 0;
|
|
43
|
+
const tail = active ? text.slice(declarationAt + 1 + marker.length).trimEnd().replace(/;$/, '').trimEnd() : '';
|
|
44
|
+
const body = tail.endsWith(']') ? tail.slice(0, -1) : undefined;
|
|
45
|
+
if (body === undefined) throw new ScaffoldError('HFS_ADD_WIRE_INVALID', `add ${noun} needs the typed ${area}Modules group in ${relative}`);
|
|
46
|
+
return { relative, file, exists, text, marker, body };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Creates a typed group from the edition's template, or adds members without duplicating inline registrations or semicolon imports. */
|
|
50
|
+
export function planComposeGroup(input) {
|
|
51
|
+
const { relative, file, exists, marker, body, text: original } = readComposeGroup(input);
|
|
52
|
+
let text = original;
|
|
53
|
+
let compactBody = compositionCode(body).replace(/\s+/g, '');
|
|
54
|
+
for (const { importLine, entry } of input.entries) {
|
|
55
|
+
const lines = text.split('\n');
|
|
56
|
+
if (!lines.some(line => line.replace(/;\s*$/, '') === importLine)) {
|
|
57
|
+
const lastImport = lines.reduce((last, line, index) => line.startsWith('import ') ? index : last, -1);
|
|
58
|
+
lines.splice(lastImport + 1, 0, importLine);
|
|
59
|
+
text = lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
const compactEntry = entry.replace(/\s+/g, '');
|
|
62
|
+
const members = `,${compactBody},`;
|
|
63
|
+
if (!members.includes(`,${compactEntry},`)) {
|
|
64
|
+
const afterMarker = compositionCode(text).indexOf(marker) + marker.length;
|
|
65
|
+
const empty = text.slice(afterMarker).startsWith(']');
|
|
66
|
+
text = `${text.slice(0, afterMarker)}\n ${entry},${empty ? '\n' : ''}${text.slice(afterMarker)}`;
|
|
67
|
+
compactBody = `${compactEntry},${compactBody}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { relative, file, exists, text };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Writes the planned group and reports a newly created composition file. */
|
|
74
|
+
export function writeComposeGroup(input) {
|
|
75
|
+
const { relative, file, exists, text } = planComposeGroup(input);
|
|
76
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
77
|
+
fs.writeFileSync(file, text);
|
|
78
|
+
return exists ? [] : [relative];
|
|
79
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ScaffoldError } from './service.mjs';
|
|
2
|
+
|
|
3
|
+
/** Enables the lazy SQL identifier guard's error family when a lite generator installs database.sql.ts. */
|
|
4
|
+
export function withSqlIdentifierError(text) {
|
|
5
|
+
const code = ' IdentifierRejected = "DATABASE_IDENTIFIER_REJECTED",';
|
|
6
|
+
if (text.includes(code)) return text;
|
|
7
|
+
const enumMarker = 'export enum DatabaseErrorCode {';
|
|
8
|
+
const kindsMarker = 'export const DATABASE_ERROR_KINDS: Record<DatabaseErrorCode, ErrorKind> = {';
|
|
9
|
+
if (!text.includes(enumMarker) || !text.includes(kindsMarker)) {
|
|
10
|
+
throw new ScaffoldError('HFS_ADD_DATABASE_ERROR', 'platform/database has an unknown error-family shape');
|
|
11
|
+
}
|
|
12
|
+
return text.replace(enumMarker, enumMarker + '\n /** A dynamic SQL identifier was not on the names its caller allows. */\n' + code)
|
|
13
|
+
.replace(kindsMarker, kindsMarker + '\n [DatabaseErrorCode.IdentifierRejected]: "internal",');
|
|
14
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ScaffoldError, pascalOf } from "./service.mjs";
|
|
4
|
+
import { feName, readFeContext, readFeFile, requireNewFeOwner, planFeCatalogs, writeFePlan } from "./add-fe-page.mjs";
|
|
5
|
+
|
|
6
|
+
function loadTypeScript(root) {
|
|
7
|
+
try { return createRequire(path.join(root, "package.json"))("typescript"); } catch {
|
|
8
|
+
throw new ScaffoldError("HFS_ADD_TYPESCRIPT_MISSING", "add overlay reads the host page with its installed TypeScript compiler");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function pageArrow(ts, source, symbol) {
|
|
13
|
+
for (const statement of source.statements) {
|
|
14
|
+
if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue;
|
|
15
|
+
const candidate = statement.declarationList.declarations.find((declaration) => declaration.name.getText(source) === symbol);
|
|
16
|
+
if (candidate?.initializer && ts.isArrowFunction(candidate.initializer)) return candidate.initializer;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function pageRender(ts, arrow) {
|
|
22
|
+
if (!arrow) return null;
|
|
23
|
+
if (!ts.isBlock(arrow.body)) {
|
|
24
|
+
let body = arrow.body;
|
|
25
|
+
while (ts.isParenthesizedExpression(body)) body = body.expression;
|
|
26
|
+
return body;
|
|
27
|
+
}
|
|
28
|
+
const returns = arrow.body.statements.filter(ts.isReturnStatement);
|
|
29
|
+
const unconditional = arrow.body.statements.every((statement) => ts.isVariableStatement(statement) || ts.isExpressionStatement(statement) || ts.isReturnStatement(statement));
|
|
30
|
+
if (!unconditional || returns.length !== 1) return null;
|
|
31
|
+
let render = returns[0].expression;
|
|
32
|
+
while (render && ts.isParenthesizedExpression(render)) render = render.expression;
|
|
33
|
+
return render;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mountOverlay({ ts, before, relative, pageSymbol, symbol }) {
|
|
37
|
+
const source = ts.createSourceFile(relative, before, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
38
|
+
const client = source.statements.some((statement) => ts.isExpressionStatement(statement) && ts.isStringLiteral(statement.expression) && statement.expression.text === "use client");
|
|
39
|
+
const render = pageRender(ts, pageArrow(ts, source, pageSymbol));
|
|
40
|
+
const isJsx = render && (ts.isJsxElement(render) || ts.isJsxSelfClosingElement(render) || ts.isJsxFragment(render));
|
|
41
|
+
if (source.parseDiagnostics.length || client || !isJsx) {
|
|
42
|
+
throw new ScaffoldError("HFS_ADD_WIRE_INVALID", `${relative} must export the server ${pageSymbol} arrow with one unconditional JSX return`);
|
|
43
|
+
}
|
|
44
|
+
if (before.split(/[^A-Za-z0-9_]/).includes(symbol)) {
|
|
45
|
+
throw new ScaffoldError("HFS_ADD_EXISTS", `${relative} already references ${symbol}`);
|
|
46
|
+
}
|
|
47
|
+
const from = render.getStart(source);
|
|
48
|
+
const wrapped = ["(", " <>", ` ${before.slice(from, render.end)}`, ` <${symbol} />`, " </>", " )"].join("\n");
|
|
49
|
+
const body = before.slice(0, from) + wrapped + before.slice(render.end);
|
|
50
|
+
const imports = source.statements.filter(ts.isImportDeclaration);
|
|
51
|
+
const last = imports.at(-1)?.end ?? 0;
|
|
52
|
+
const importLine = `import { ${symbol} } from "@/features/overlays/${symbol}"`;
|
|
53
|
+
return `${body.slice(0, last)}\n${importLine}${body.slice(last)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function overlaySources(symbol, namespace) {
|
|
57
|
+
return {
|
|
58
|
+
entry: `"use client"
|
|
59
|
+
|
|
60
|
+
import { useTranslations } from "next-intl"
|
|
61
|
+
import { ${symbol}Base } from "./component"
|
|
62
|
+
|
|
63
|
+
/** Resolve the overlay's copy at its client interaction boundary. */
|
|
64
|
+
export const ${symbol} = () => {
|
|
65
|
+
const t = useTranslations(${JSON.stringify(namespace)})
|
|
66
|
+
return <${symbol}Base props={{ title: t("title"), open: t("open"), close: t("close"), description: t("description") }} />
|
|
67
|
+
}
|
|
68
|
+
`,
|
|
69
|
+
component: `import { Button, Dialog, Text } from "@starci/grammar/common"
|
|
70
|
+
|
|
71
|
+
/** Settled labels and content for the overlay's accessible dialog. */
|
|
72
|
+
export interface ${symbol}BaseProps {
|
|
73
|
+
readonly props: {
|
|
74
|
+
readonly title: string
|
|
75
|
+
readonly open: string
|
|
76
|
+
readonly close: string
|
|
77
|
+
readonly description: string
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Draw an accessible dialog with an intrinsic trigger and dismissal. */
|
|
82
|
+
export const ${symbol}Base = (props: ${symbol}BaseProps) => (
|
|
83
|
+
<Dialog
|
|
84
|
+
title={props.props.title}
|
|
85
|
+
closeLabel={props.props.close}
|
|
86
|
+
trigger={<Button>{props.props.open}</Button>}
|
|
87
|
+
>
|
|
88
|
+
<Text>{props.props.description}</Text>
|
|
89
|
+
</Dialog>
|
|
90
|
+
)
|
|
91
|
+
`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Add a localized client overlay and mount its public entry in a server feature page. */
|
|
96
|
+
export function addFeOverlay({ root, app, name, page, messages, ts }) {
|
|
97
|
+
feName(name, "overlay name");
|
|
98
|
+
if (page === undefined) throw new ScaffoldError("HFS_ADD_OPTION_MISSING", "add overlay needs --page naming its server feature page");
|
|
99
|
+
feName(page, "host page name");
|
|
100
|
+
const context = readFeContext({ root, app });
|
|
101
|
+
const symbol = `${pascalOf(name)}Overlay`;
|
|
102
|
+
const owner = `${context.source}/features/overlays/${symbol}`;
|
|
103
|
+
requireNewFeOwner(context, owner);
|
|
104
|
+
const pageSymbol = `${pascalOf(page)}Page`;
|
|
105
|
+
const relative = `${context.source}/features/pages/${pageSymbol}/index.tsx`;
|
|
106
|
+
const before = readFeFile(context, relative);
|
|
107
|
+
const body = mountOverlay({ ts: ts ?? loadTypeScript(context.root), before, relative, pageSymbol, symbol });
|
|
108
|
+
const catalogs = planFeCatalogs(context, {
|
|
109
|
+
group: "overlays", name, messages,
|
|
110
|
+
defaults: {
|
|
111
|
+
vi: { title: "Th\u00f4ng tin", open: "M\u1edf th\u00f4ng tin", close: "\u0110\u00f3ng", description: "Xem th\u00f4ng tin c\u1ee7a trang n\u00e0y." },
|
|
112
|
+
en: { title: "Information", open: "Open information", close: "Close", description: "View information about this page." },
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const sources = overlaySources(symbol, catalogs.namespace);
|
|
116
|
+
const created = [
|
|
117
|
+
{ relative: `${owner}/index.tsx`, body: sources.entry },
|
|
118
|
+
{ relative: `${owner}/component.tsx`, body: sources.component },
|
|
119
|
+
];
|
|
120
|
+
const updates = [...catalogs.updates, { relative, before, body }];
|
|
121
|
+
return { ...writeFePlan(context, created, updates), registered: { app: context.app, overlay: symbol, page: pageSymbol } };
|
|
122
|
+
}
|