@starci/hfs 4.0.8 → 4.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +81 -39
- package/emit/contracts.mjs +56 -9
- package/emit/db-types.mjs +169 -0
- package/emit/events.mjs +41 -0
- package/emit/operations-worker.mjs +1 -1
- package/emit/operations.mjs +2 -2
- package/emit/schema-worker.mjs +1 -1
- package/lint/bound-sys.cjs +50 -0
- package/lint/run.mjs +131 -24
- package/package.json +16 -10
- package/report/sonar.mjs +8 -8
- package/runtime/engine/admission.mjs +308 -0
- package/runtime/engine/canonical-json.mjs +10 -0
- package/runtime/engine/config.mjs +486 -0
- package/runtime/engine/db/blob.mjs +315 -0
- package/runtime/engine/db/ledger-paths.mjs +83 -0
- package/runtime/engine/db/ledger.mjs +1118 -0
- package/runtime/engine/db/machine-connection.mjs +135 -0
- package/runtime/engine/db/machine-schema.mjs +84 -0
- package/runtime/engine/db/machine.mjs +1367 -0
- package/runtime/engine/db/migrations/machine/0001-init.sql +924 -0
- package/runtime/engine/db/migrations/runtime/0001-init.sql +1108 -0
- package/runtime/engine/db/provider-reservations.mjs +101 -0
- package/runtime/engine/digest.mjs +16 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/engine/secrets.mjs +130 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +162 -31
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +912 -120
- package/runtime/knowledge/patterns/be/api.yaml +369 -0
- package/runtime/knowledge/patterns/be/cli.yaml +297 -0
- package/runtime/knowledge/patterns/be/domain.yaml +113 -0
- package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
- package/runtime/knowledge/patterns/be/projections.yaml +178 -0
- package/runtime/knowledge/patterns/be/queues.yaml +236 -0
- package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
- package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
- package/runtime/knowledge/patterns/be/saga.yaml +248 -0
- package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
- package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
- package/runtime/knowledge/sonar-gate.yaml +52 -18
- package/runtime/modules/kernel/failure-codes.yaml +611 -51
- package/runtime/modules/models/registry.yaml +553 -0
- package/runtime/modules/models/runtimes.yaml +695 -0
- package/runtime/modules/ops/_labels.yaml +53 -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 +16 -2
- package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
- package/runtime/scripts/api/git/config-get.mjs +10 -0
- package/runtime/scripts/api/git/diff.mjs +5 -0
- package/runtime/scripts/api/git/lib.mjs +18 -26
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/node/lib.mjs +14 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/process/lib.mjs +111 -0
- package/runtime/scripts/api/process/owned-process.mjs +78 -0
- package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -0
- package/runtime/scripts/api/process/run-program.mjs +7 -0
- package/runtime/scripts/api/process/stop-owned-process.mjs +9 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -12
- package/runtime/scripts/api/sops/lib.mjs +255 -0
- package/runtime/scripts/api/sops/seal.mjs +22 -0
- package/runtime/scripts/connectors/lib.mjs +488 -0
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
- package/runtime/scripts/hfs/architecture/config.mjs +9 -6
- package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
- package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
- package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
- package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -2
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
- package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/hfs/architecture/index.mjs +17 -4
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
- package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
- package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
- package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
- package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
- package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
- package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
- package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
- package/runtime/scripts/hfs/architecture.mjs +4 -4
- package/runtime/scripts/hfs/check.mjs +75 -72
- package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
- package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
- package/runtime/scripts/hfs/edition-slots.mjs +66 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/hfs/path-findings.mjs +1 -1
- package/runtime/scripts/hfs/repo-identity.mjs +9 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
- package/runtime/scripts/hfs/rules/cli.mjs +37 -0
- package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
- package/runtime/scripts/hfs/rules/contract.mjs +10 -9
- package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
- package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
- package/runtime/scripts/hfs/rules/database.mjs +68 -0
- package/runtime/scripts/hfs/rules/deps.mjs +1 -1
- package/runtime/scripts/hfs/rules/docker.mjs +259 -0
- package/runtime/scripts/hfs/rules/edition.mjs +111 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
- package/runtime/scripts/hfs/rules/read.mjs +1 -1
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +209 -0
- package/runtime/scripts/hfs/slots.mjs +78 -108
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/hfs/tree.mjs +5 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
- package/runtime/scripts/hfs/view.mjs +3 -2
- package/runtime/scripts/lib/clip.mjs +19 -0
- package/runtime/scripts/lib/display-names.mjs +259 -0
- 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/example-refs.mjs +158 -0
- package/runtime/scripts/lib/fs-kind.mjs +14 -4
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/json-schema.mjs +52 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +24 -5
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/mutation-fence.mjs +15 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/process-identity.mjs +6 -0
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/read-yaml.mjs +18 -0
- package/runtime/scripts/lib/redact.mjs +161 -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/sleep.mjs +7 -0
- package/runtime/scripts/lib/sops-envelope.mjs +112 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -0
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/lib/walk.mjs +4 -3
- package/scaffold/add-app.mjs +174 -0
- package/scaffold/add-cli-lite.mjs +257 -0
- package/scaffold/add-inbox-lite.mjs +314 -0
- package/scaffold/add-table.mjs +344 -0
- package/scaffold/add.mjs +299 -0
- package/scaffold/app.mjs +358 -69
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +32 -26
- package/scaffold/supabase-ports.mjs +26 -0
- package/src/main.mjs +327 -0
- package/sync/cli.mjs +6 -9
- package/sync/format.mjs +1 -1
- package/sync/hygiene.mjs +10 -10
- package/sync/index.mjs +167 -104
- package/sync/managed.mjs +19 -11
- package/sync/sonar-key.mjs +5 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
- package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
- package/templates/app/ci-workflows/sonar-steps.yml +9 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
- package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
- package/templates/app/docker-ignore/dockerignore +50 -0
- package/templates/app/gitignore-lite +35 -0
- package/templates/app/hooks/husky/pre-commit +3 -8
- package/templates/app/hooks/husky/pre-push +45 -6
- package/templates/app/package-scripts/core.json +8 -0
- package/templates/app/package-scripts/package.json +3 -10
- package/templates/app/package-scripts-lite/package.json +15 -0
- package/templates/app/quality-config/codecov.yml +15 -3
- package/templates/app/quality-config/sonar-base.properties +4 -0
- package/templates/app/quality-config/sonar-project.properties +1 -4
- package/templates/app/quality-config-lite/sonar-project.properties +5 -0
- package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
- package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
- package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
- package/templates/app/skeleton/README.md +7 -5
- package/templates/app/skeleton-lite/.sops.yaml +3 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
- package/templates/app/skeleton-lite/README.md +40 -0
- package/templates/app/skeleton-lite/supabase/config.toml +41 -0
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
- package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
- package/templates/app/task-graph/turbo.json +23 -0
- package/templates/app/tool-config-lite/prettierignore +2 -0
- package/templates/app/tool-config-lite/prettierrc +1 -0
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
- package/templates/be/common/main-listen.ts.partial +14 -0
- package/templates/be/common/sql-ident.ts.partial +7 -0
- package/templates/be/common/sql-tag.ts.partial +5 -0
- package/templates/be/image/api/Dockerfile +38 -0
- package/templates/be/image/cli/Dockerfile +38 -0
- package/templates/be/image/worker/Dockerfile +36 -0
- package/templates/be/inbox/database-client.ts.tpl +50 -0
- package/templates/be/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/migration.sql.tpl +29 -0
- package/templates/be/inbox/module-definition.ts.tpl +6 -0
- package/templates/be/inbox/module.ts.tpl +14 -0
- package/templates/be/inbox/options.ts.tpl +2 -0
- package/templates/be/inbox/service.ts.tpl +33 -0
- package/templates/be/inbox/sql.ts.tpl +16 -0
- package/templates/be/inbox/webhook-door.ts.tpl +36 -0
- package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
- package/templates/be/patterns/api/application.command.ts.tpl +10 -0
- package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
- package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
- package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
- package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
- package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
- package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
- package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
- package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/http.module.ts.tpl +7 -0
- package/templates/be/patterns/api/http.request.ts.tpl +10 -0
- package/templates/be/patterns/api/http.response.ts.tpl +5 -0
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
- package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
- package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
- package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
- package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
- package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
- package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
- package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
- package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
- package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
- package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
- package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
- package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
- package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
- package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
- package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
- package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
- package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
- package/templates/be/patterns/projections/migration.ts.tpl +18 -0
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
- package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
- package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
- package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
- package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
- package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
- package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
- package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
- package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
- package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
- package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
- package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
- package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
- package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
- package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
- package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
- package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
- package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
- package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
- package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
- package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
- package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
- package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
- package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/index.ts +1 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
- package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
- package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
- package/templates/be/table/error.ts.tpl +16 -0
- package/templates/be/table/index.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +51 -0
- package/templates/be/table/module-definition.ts.tpl +7 -0
- package/templates/be/table/module.ts.tpl +14 -0
- package/templates/be/table/options.ts.tpl +2 -0
- package/templates/be/table/rows.ts.tpl +4 -0
- package/templates/be/table/service.ts.tpl +23 -0
- package/templates/be/table/sql.ts.tpl +4 -0
- package/templates/be/tool-config/jest.config.js +3 -1
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
- package/templates/common/gitignore.base +1 -2
- package/templates/fe/app/package.json.tpl +1 -0
- package/templates/fe/app/tsconfig.json +12 -0
- package/templates/fe/common/app-layout.tsx.partial +8 -0
- package/templates/fe/common/site-shell.tsx.partial +9 -0
- package/templates/fe/image/next/Dockerfile +42 -0
- package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
- package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
- package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
- package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
- package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
- package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
- package/templates/fe/table/read.ts.tpl +16 -0
- package/templates/fe/table/write.ts.tpl +16 -0
- package/upgrade/check.mjs +137 -0
- package/upgrade/index.mjs +524 -0
- package/bin/hfs.mjs +0 -233
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
- package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
- /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
package/sync/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// starci app sync: render, verify and write the files an app cannot `extends`.
|
|
2
2
|
//
|
|
3
3
|
// An app is one repository: its root holds the one package.json (all dependencies, all scripts), the lockfile, the hfs.json of
|
|
4
4
|
// kind app, CI, the hooks, the formatter and Sonar configuration and .starciwork; its sides be/ and fe/ hold each side's tool
|
|
@@ -8,26 +8,32 @@
|
|
|
8
8
|
// side (be, fe) for a side slot, whose files land under that side's folder. Two targets are not whole files of a managedBy slot
|
|
9
9
|
// and are listed in this module: the marked block of the root .gitignore, and .starciwork/.gitignore, which lives inside the
|
|
10
10
|
// .starciwork directory slot. Every file is rendered with the app's hfs.json (both sides and their apps) and, for the Sonar
|
|
11
|
-
// exclusions
|
|
12
|
-
//
|
|
11
|
+
// exclusions, the jest preset the app installs for its be side and the one coverage scope derived from the slot manifest
|
|
12
|
+
// (scripts/hfs/coverage-scope.mjs: the be/jest.config.js scope, sonar.coverage.exclusions and the codecov.yml paths and components). `--check` compares the sha256 of the rendered content with the
|
|
13
13
|
// file on disk and fails on any drift; `--write` rewrites the drifted files. `.gitignore` is the one shared file: only the marked
|
|
14
14
|
// block is managed and the app's own lines around it are left alone. The root package.json is managed by its `scripts` block
|
|
15
|
-
// only (mode scripts, compared as parsed JSON): the rest of the file (dependencies, npm workspaces
|
|
15
|
+
// only (mode scripts, compared as parsed JSON): the rest of the file (dependencies, the npm workspaces fe/apps/* and fe/packages/*,
|
|
16
|
+
// HFS_MONO_WORKSPACES) is the app's. The fe side runs through turbo (turbo.json, app.task-graph): each fe app and package is a workspace.
|
|
16
17
|
import { createHash } from 'node:crypto';
|
|
17
18
|
import { createRequire } from 'node:module';
|
|
18
19
|
import fs from 'node:fs';
|
|
19
20
|
import path from 'node:path';
|
|
20
21
|
import { braceVariants } from '../runtime/scripts/lib/glob.mjs';
|
|
21
|
-
import {
|
|
22
|
+
import { codecovPaths, coverageComponents, jestCoverage, sonarCoverageExclusions } from '../runtime/scripts/hfs/coverage-scope.mjs';
|
|
23
|
+
import { APP_SCOPE, SIDES, litePresenceOf, loadSlotManifest, resolveRepoDeclaration, slotInEdition } from '../runtime/scripts/hfs/slots.mjs';
|
|
24
|
+
import { managedGroupOf } from '../runtime/scripts/hfs/edition-slots.mjs';
|
|
25
|
+
import { feAppPackageName } from '../runtime/scripts/hfs/rules/monorepo.mjs';
|
|
26
|
+
import { DEFAULT_PORT, NODE_IMAGE, dockerfilePath } from '../runtime/scripts/hfs/rules/docker.mjs';
|
|
22
27
|
import { readDeclaredSonarKey } from './sonar-key.mjs';
|
|
23
28
|
|
|
24
29
|
export const TEMPLATES_DIR = path.join(import.meta.dirname, '..', 'templates');
|
|
25
|
-
|
|
30
|
+
const NODE_MAJOR = 22;
|
|
31
|
+
const APP_BINARY = 'starci app';
|
|
26
32
|
/** The scopes a target is rendered for: the app root, then each side. */
|
|
27
33
|
export const SCOPES = Object.freeze([APP_SCOPE, ...SIDES]);
|
|
28
|
-
export const BLOCK_BEGIN = '# >>>
|
|
29
|
-
export const BLOCK_END = '# <<<
|
|
30
|
-
const HEADER = scope => `# Generated by
|
|
34
|
+
export const BLOCK_BEGIN = '# >>> starci app sync (managed block; do not edit) >>>';
|
|
35
|
+
export const BLOCK_END = '# <<< starci app sync <<<';
|
|
36
|
+
const HEADER = scope => `# Generated by ${APP_BINARY} sync (${scope === APP_SCOPE ? 'app root' : `${scope} side`}). Do not edit: run "npx ${APP_BINARY} sync --write".`;
|
|
31
37
|
|
|
32
38
|
export class SyncError extends Error {
|
|
33
39
|
constructor(code, message) {
|
|
@@ -39,11 +45,11 @@ export class SyncError extends Error {
|
|
|
39
45
|
// mode: file = whole file, block = the marked block inside a file the app also writes to, scripts = the `scripts` block of
|
|
40
46
|
// package.json. A template that can hold a comment opens with {{header}}; a file whose exact content is specified (JSON, the
|
|
41
47
|
// eslint one-liner) has no header.
|
|
42
|
-
const MODE_OF_GROUP = Object.freeze({ 'package-scripts': 'scripts' });
|
|
48
|
+
const MODE_OF_GROUP = Object.freeze({ 'package-scripts': 'scripts', 'package-scripts-lite': 'scripts' });
|
|
43
49
|
const LITERAL_FILE = /^[^*?<>{}[\]]+[^/*?<>{}[\]]$/;
|
|
44
50
|
|
|
45
51
|
/** The targets no managedBy slot lists as a whole file (see the header of this module); both belong to the app root. */
|
|
46
|
-
|
|
52
|
+
const UNLISTED_TARGETS = Object.freeze([
|
|
47
53
|
{ path: '.gitignore', template: 'app/gitignore', mode: 'block' },
|
|
48
54
|
{ path: '.starciwork/.gitignore', template: 'app/starciwork.gitignore', mode: 'file' },
|
|
49
55
|
].map(Object.freeze));
|
|
@@ -56,18 +62,27 @@ const onSide = (scope, file) => (scope === APP_SCOPE ? file : `${scope}/${file}`
|
|
|
56
62
|
* list, then (app root) the unlisted ones. A slot that names managedBy lists literal files only, and each has a template under
|
|
57
63
|
* its scope or under common.
|
|
58
64
|
*/
|
|
59
|
-
export function targetsOf(scope, { manifest = loadSlotManifest(), hasTemplate = name => fs.existsSync(path.join(TEMPLATES_DIR, name)) } = {}) {
|
|
65
|
+
export function targetsOf(scope, { edition = 'full', manifest = loadSlotManifest(), hasTemplate = name => fs.existsSync(path.join(TEMPLATES_DIR, name)) } = {}) {
|
|
60
66
|
const listed = [];
|
|
61
67
|
for (const slot of manifest.slots) {
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
if (!slot.profiles.includes(scope) || !slotInEdition(manifest, slot, edition)) continue;
|
|
69
|
+
if (edition === 'lite' && litePresenceOf(slot, scope) === 'forbidden') continue;
|
|
70
|
+
const group = managedGroupOf(slot, edition);
|
|
71
|
+
if (group === undefined) continue;
|
|
72
|
+
const effectivePath = edition === 'lite' ? (slot.lite?.path ?? slot.path) : slot.path;
|
|
73
|
+
for (const file of braceVariants(effectivePath)) {
|
|
74
|
+
if (!LITERAL_FILE.test(file)) throw new SyncError('HFS_SYNC_MANIFEST_MANAGED', `slot ${slot.id} names ${edition === 'lite' && slot.liteManagedBy !== undefined ? 'liteManagedBy' : 'managedBy'} but its path ${effectivePath} is not a list of literal files (${file})`);
|
|
75
|
+
const template = [scope, 'common'].map(dir => templateName(dir, group, file)).find(hasTemplate);
|
|
76
|
+
if (!template) throw new SyncError('HFS_SYNC_TEMPLATE_MISSING', `slot ${slot.id} manages ${file} for ${scope}, but templates/{${scope},common}/${group} has no template for it`);
|
|
77
|
+
listed.push({ path: file, template, mode: MODE_OF_GROUP[group] ?? 'file', slot: slot.id });
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
|
-
|
|
80
|
+
const unlisted = scope === APP_SCOPE
|
|
81
|
+
? UNLISTED_TARGETS.map(target => edition === 'lite' && target.path === '.gitignore'
|
|
82
|
+
? { ...target, template: 'app/gitignore-lite' }
|
|
83
|
+
: { ...target })
|
|
84
|
+
: [];
|
|
85
|
+
return [...listed, ...unlisted];
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
const lf = text => text.replace(/\r\n/g, '\n');
|
|
@@ -75,7 +90,7 @@ export const hashOf = text => createHash('sha256').update(lf(text)).digest('hex'
|
|
|
75
90
|
|
|
76
91
|
/**
|
|
77
92
|
* The hfs.json sync reads: an app declaration valid for the manifest it pins (both sides, their apps, connections, optional slots,
|
|
78
|
-
* reads). Sync never renders configs for a declaration the eslint canons and
|
|
93
|
+
* reads). Sync never renders configs for a declaration the eslint canons and starci app check would refuse, so a pin bump cannot leave
|
|
79
94
|
* lint unable to start. Answers the resolved app.
|
|
80
95
|
*/
|
|
81
96
|
export function validateHfs(hfs, manifest = loadSlotManifest()) {
|
|
@@ -99,11 +114,9 @@ export function render(text, vars, readTemplate = readBundled) {
|
|
|
99
114
|
});
|
|
100
115
|
}
|
|
101
116
|
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*/
|
|
106
|
-
export async function loadPresets(root) {
|
|
117
|
+
/** What the app's sync reads from the jest preset it installs for its be side: the Sonar exclusions ({ sonarExclusions }); a lite app installs none. */
|
|
118
|
+
export async function loadPresets(root, edition = 'full') {
|
|
119
|
+
if (edition === 'lite') return {};
|
|
107
120
|
const name = '@starci/jest-preset';
|
|
108
121
|
const require = createRequire(path.join(root, 'package.json'));
|
|
109
122
|
let resolved;
|
|
@@ -112,110 +125,158 @@ export async function loadPresets(root) {
|
|
|
112
125
|
} catch {
|
|
113
126
|
throw new SyncError('HFS_SYNC_PRESET_MISSING', `${name} is not installed under ${root}; set it to the exact version in knowledge/hfs/canon-pins.yaml and reinstall`);
|
|
114
127
|
}
|
|
115
|
-
|
|
116
|
-
return { sonarExclusions: preset.sonarExclusions(), coverageSources: [...preset.COVERAGE_SOURCES] };
|
|
128
|
+
return { sonarExclusions: require(resolved).sonarExclusions() };
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
/** Where the be unit run writes the lcov report (jest `coverageDirectory` coverage under be/, reporter lcov), from the app root. */
|
|
120
|
-
|
|
132
|
+
const LCOV_REPORT = 'be/coverage/lcov.info';
|
|
133
|
+
|
|
134
|
+
const SOURCE_FILE = /\.[cm]?ts$/;
|
|
135
|
+
const SOURCE_SKIPPED = new Set(['node_modules', 'dist', 'coverage']);
|
|
121
136
|
|
|
122
137
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* can never drift; fe/ is outside it (a front end has no tests).
|
|
138
|
+
* The be source of the app at `root` the coverage components read: { files, read }, `files` be-relative posix paths of every `*.ts` file under
|
|
139
|
+
* be/apps and be/src (build output and dependencies skipped) and `read` their text. An app with no be folder has no files.
|
|
126
140
|
*/
|
|
127
|
-
export function
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
export function appSource(root) {
|
|
142
|
+
const files = [];
|
|
143
|
+
const walk = dir => {
|
|
144
|
+
let entries;
|
|
145
|
+
try { entries = fs.readdirSync(path.join(root, 'be', dir), { withFileTypes: true }); } catch { return; }
|
|
146
|
+
for (const entry of entries) {
|
|
147
|
+
const rel = `${dir}/${entry.name}`;
|
|
148
|
+
if (entry.isDirectory()) { if (!SOURCE_SKIPPED.has(entry.name)) walk(rel); } else if (SOURCE_FILE.test(entry.name)) files.push(rel);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
for (const dir of ['apps', 'src']) walk(dir);
|
|
152
|
+
return { files: files.sort(), read: file => fs.readFileSync(path.join(root, 'be', file), 'utf8') };
|
|
131
153
|
}
|
|
132
154
|
|
|
133
|
-
const COVERED_SUFFIX = /\*\.([a-z][a-z0-9-]*)\.ts$/;
|
|
134
|
-
const asGlob = (text) => String(text).replace(/<[^>]+>/g, '*');
|
|
135
|
-
|
|
136
155
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* - every be source role of the slot manifest's closed suffix vocabulary (ruleParams.be.suffixes, R89) other than the
|
|
140
|
-
* roles the coverage sources name (`service`), as `be/**` + `/*.<role>.ts`;
|
|
141
|
-
* - every be file name outside that vocabulary a be slot declares (`main.ts`, `index.ts`, `connection.ts`, the migrations,
|
|
142
|
-
* the test world's files, dto/ and kit/ files), as `be/<slot path>/<name>` with each `<placeholder>` a `*`;
|
|
143
|
-
* - all of fe/ (a front end has no tests).
|
|
144
|
-
* The result is sorted and has no duplicate. A coverage source that is not `<dir>/**` + `/*.<role>.ts` is refused: its
|
|
145
|
-
* complement cannot be written in Sonar's globs.
|
|
156
|
+
* The Codecov `individual_components` entries of components [{ id, name, paths }]: one `component_id` block each, `prefix` ahead of every path
|
|
157
|
+
* (the app folder in the runtime repository) and `ids` mapping a component id to its name there.
|
|
146
158
|
*/
|
|
147
|
-
export function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
for (const entry of [...(slot.requires ?? []), ...(slot.allows ?? [])]) {
|
|
159
|
-
if (!/\.ts$/.test(entry) || entry.includes('<role>')) continue;
|
|
160
|
-
for (const name of braceVariants(entry)) {
|
|
161
|
-
if (isRole.test(asGlob(name).replace(/\*/g, 'x'))) continue;
|
|
162
|
-
for (const dir of braceVariants(slot.path)) globs.add(`be/${asGlob(dir)}${asGlob(name)}`.replace(/\/\/+/g, '/'));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
globs.add('fe/**');
|
|
167
|
-
return [...globs].sort();
|
|
159
|
+
export function componentsYaml(components, { prefix = '', ids = id => id } = {}) {
|
|
160
|
+
return components.map(component => [` - component_id: ${ids(component.id)}`, ` name: ${ids(component.name)}`, ' paths:', ...component.paths.map(glob => ` - ${JSON.stringify(prefix + glob)}`)].join('\n')).join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The managed be/jest.config.js option text of a coverage scope { roots, roles, excludes }, laid out exactly as prettier lays it out (width 120, indent 4). */
|
|
164
|
+
export function jestCoverageSource(coverage) {
|
|
165
|
+
const list = (key, items) => {
|
|
166
|
+
const one = ` ${key}: [${items.map(item => JSON.stringify(item)).join(', ')}],`;
|
|
167
|
+
return one.length <= 120 ? one : [` ${key}: [`, ...items.map(item => ` ${JSON.stringify(item)},`), ' ],'].join('\n');
|
|
168
|
+
};
|
|
169
|
+
return ['{', list('roots', coverage.roots), list('roles', coverage.roles), list('excludes', coverage.excludes), ' }'].join('\n');
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
/**
|
|
171
|
-
* The script lines the apps add to the root package.json, each ending with a comma (the template puts them mid-object).
|
|
172
|
-
* script runs
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
173
|
+
* The script lines the apps add to the root package.json, each ending with a comma (the template puts them mid-object). A be
|
|
174
|
+
* script runs from be/ (`cd be && ...`, the folder its tsconfig and jest configuration are relative to) or runs the built app; an
|
|
175
|
+
* fe script runs the fe app's workspace (@<project>/<app>): through turbo for dev (so the packages it imports are built first) and
|
|
176
|
+
* through npm `-w` for start. be: `dev:be` runs the one api app from source and restarts it on change (`dev:be:<app>` each, with
|
|
177
|
+
* several), `start:<app>` runs a built api or worker app, `cli` the built cli app (`npm run cli -- <group> <command>`) and, when the
|
|
178
|
+
* back end declares a connection, `migrate` its migrate command (`cli migrate run`). fe: `dev:fe` runs the one Next app in
|
|
179
|
+
* development (`dev:fe:<app>` each, with several), `start:<app>` serves a built one. Images:
|
|
180
|
+
* `docker:build:<app>` builds the image of one app (the exact command its Dockerfile header states, tag `<project>/<app>:dev`) and
|
|
181
|
+
* `docker:build` builds every image, one after the other, never pushing.
|
|
176
182
|
*/
|
|
177
183
|
export function appScripts(app) {
|
|
178
184
|
const line = (name, command) => `${JSON.stringify(name)}: ${JSON.stringify(command)},`;
|
|
179
185
|
const be = app.sides.be.apps;
|
|
180
186
|
const fe = app.sides.fe.apps;
|
|
181
187
|
const apis = be.filter(entry => entry.kind === 'api');
|
|
182
|
-
const
|
|
188
|
+
const connections = app.sides.be.connections ?? [];
|
|
183
189
|
const watch = entry => `cd be && ts-node-dev --respawn -r tsconfig-paths/register apps/${entry.name}/src/main.ts`;
|
|
190
|
+
const built = entry => `node be/dist/apps/${entry.name}/src/main.js`;
|
|
191
|
+
const workspace = entry => feAppPackageName(app.project, entry.name);
|
|
192
|
+
const dev = entry => `${app.edition === 'lite' ? '' : 'npm run codegen --silent && '}turbo run dev --filter=${workspace(entry)}`;
|
|
193
|
+
const images = imageEntries(app);
|
|
194
|
+
const imageCommand = ({ side, entry }) => `docker build -f ${dockerfilePath(side, entry.name)} -t ${app.project}/${entry.name}:dev .`;
|
|
184
195
|
return [
|
|
185
196
|
...(apis.length === 1 ? [line('dev:be', watch(apis[0]))] : apis.map(entry => line(`dev:be:${entry.name}`, watch(entry)))),
|
|
186
|
-
...(fe.length === 1 ? [line('dev:fe',
|
|
187
|
-
...be.
|
|
188
|
-
|
|
197
|
+
...(fe.length === 1 ? [line('dev:fe', dev(fe[0]))] : fe.map(entry => line(`dev:fe:${entry.name}`, dev(entry)))),
|
|
198
|
+
...be.flatMap(entry => {
|
|
199
|
+
if (entry.kind !== 'cli') return [line(`start:${entry.name}`, built(entry))];
|
|
200
|
+
return [line('cli', built(entry)), ...(connections.length ? [line('migrate', `${built(entry)} ${MIGRATE_COMMAND}`)] : [])];
|
|
201
|
+
}),
|
|
202
|
+
...fe.map(entry => line(`start:${entry.name}`, `npm run start -w ${workspace(entry)}`)),
|
|
203
|
+
...(app.edition !== 'lite' && app.optionalSlots.includes('app.browser') ? [line('test:browser', 'playwright test -c browser/playwright.config.ts')] : []),
|
|
204
|
+
line('docker:build', images.map(image => `npm run docker:build:${image.entry.name}`).join(' && ')),
|
|
205
|
+
...images.map(image => line(`docker:build:${image.entry.name}`, imageCommand(image))),
|
|
189
206
|
].join('\n ');
|
|
190
207
|
}
|
|
191
208
|
|
|
209
|
+
/** The cli command that migrates every connection (src/features/cli/migrate/subs/run.cli.ts). */
|
|
210
|
+
const MIGRATE_COMMAND = 'migrate run';
|
|
211
|
+
|
|
192
212
|
/** True when the fe side opts into a workspace package (repo.packages or any fe.package.* slot): the sources then include fe/packages/. */
|
|
193
|
-
|
|
213
|
+
const opensPackages = side => (side.optionalSlots ?? []).some(id => id === 'repo.packages' || String(id).startsWith('fe.package.'));
|
|
194
214
|
|
|
195
215
|
/** The stylesheets of the fe side the CSS canon judges, relative to fe/: every app and every workspace package (slot fe.route, fe.package.*). */
|
|
196
216
|
export const STYLE_GLOB = '{apps,packages}/*/src/**/*.css';
|
|
197
217
|
|
|
218
|
+
/** The images a declaration renders commands and CI entries for. Lite's default deployable is the back-end API image. */
|
|
219
|
+
function imageEntries(app) {
|
|
220
|
+
return [...app.sides.be.apps.map(entry => ({ side: 'be', entry })), ...(app.edition === 'lite' ? [] : app.sides.fe.apps.map(entry => ({ side: 'fe', entry })))];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The build matrix of the images workflow: one entry per declared app (be then fe), each with the image name and its Dockerfile. The workflow
|
|
225
|
+
* runs on the release tag, so every image is built there; no path filter narrows it. A YAML block mapping per entry, indented for
|
|
226
|
+
* `strategy.matrix.include`.
|
|
227
|
+
*/
|
|
228
|
+
export function imageMatrix(app) {
|
|
229
|
+
const entry = (side, item) => ` - name: ${item.name}\n file: ${dockerfilePath(side, item.name)}`;
|
|
230
|
+
return imageEntries(app).map(image => entry(image.side, image.entry)).join('\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const IMAGE_TEMPLATE = Object.freeze({ api: 'be/image/api', worker: 'be/image/worker', cli: 'be/image/cli', next: 'fe/image/next' });
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The Dockerfile of every declared app, [{ path, content }] (app-relative): written once by starci app scaffold from templates/<side>/image/<kind>
|
|
237
|
+
* and then the app's own, judged by the docker rules (R187-R191). A Next image copies the fe packages only when the fe side opts into
|
|
238
|
+
* them and the be contracts only when the fe side reads them, so each COPY names a folder the app has.
|
|
239
|
+
*/
|
|
240
|
+
export function imageFiles(app) {
|
|
241
|
+
const side = app.sides.fe;
|
|
242
|
+
const inputs = [...(opensPackages(side) ? ['fe/packages'] : []), ...((side.reads ?? []).includes('be/contracts/') ? ['be/contracts'] : [])];
|
|
243
|
+
const copyInputs = inputs.map(folder => `COPY ${folder} ${folder}\n`).join('');
|
|
244
|
+
const entries = imageEntries(app).map(({ side: which, entry: item }) => ({ side: which, item }));
|
|
245
|
+
return entries.map(({ side: which, item }) => {
|
|
246
|
+
const template = IMAGE_TEMPLATE[item.kind];
|
|
247
|
+
const vars = { project: app.project, app: item.name, nodeImage: NODE_IMAGE, port: DEFAULT_PORT, copyInputs };
|
|
248
|
+
return { path: dockerfilePath(which, item.name), content: render(readBundled(`${template}/Dockerfile`), vars) };
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
198
252
|
/** Every value a template of `scope` can name, derived from hfs.json and the presets. */
|
|
199
|
-
export function variables(app, scope, presets, sonarKey, manifest = loadSlotManifest()) {
|
|
253
|
+
export function variables(app, scope, presets, sonarKey, manifest = loadSlotManifest(), source = { files: [], read: () => '' }) {
|
|
200
254
|
const fe = app.sides.fe;
|
|
201
255
|
const packages = opensPackages(fe);
|
|
202
|
-
const
|
|
203
|
-
const
|
|
256
|
+
const lite = app.edition === 'lite';
|
|
257
|
+
const providers = (app.sides.be.connections ?? []).map(connection => connection.provider);
|
|
204
258
|
return {
|
|
205
259
|
header: HEADER(scope),
|
|
260
|
+
appBinary: APP_BINARY,
|
|
206
261
|
appScripts: appScripts(app),
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
262
|
+
// turbo builds every fe app workspace; ^build (turbo.json) builds the packages each app imports first, and caches both.
|
|
263
|
+
buildFe: `${lite ? '' : 'npm run codegen --silent && '}turbo run build --filter=./fe/apps/*`,
|
|
264
|
+
// The be side with its tsc; every fe workspace with its own typecheck script through turbo (the packages are built first: ^build).
|
|
265
|
+
typecheck: `${lite ? '' : 'npm run codegen --silent && '}tsc -p be/tsconfig.json && turbo run typecheck`,
|
|
266
|
+
lintPrefix: lite ? '' : 'npm run codegen --silent && ',
|
|
210
267
|
nodeMajor: String(NODE_MAJOR),
|
|
211
268
|
sonarKey: sonarKey ?? app.project,
|
|
212
|
-
sonarExclusions: [presets?.sonarExclusions, '**/.next/**', '**/node_modules/**', '**/src/messages/**'].filter(Boolean).join(','),
|
|
269
|
+
sonarExclusions: (lite ? ['**/.next/**', '**/node_modules/**', '**/src/messages/**', 'supabase/types/**'] : [presets?.sonarExclusions, '**/.next/**', '**/node_modules/**', '**/src/messages/**']).filter(Boolean).join(','),
|
|
213
270
|
sonarSources: ['be/apps', 'be/src', 'fe/apps', ...(packages ? ['fe/packages'] : [])].join(','),
|
|
214
271
|
lcovReport: LCOV_REPORT,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
272
|
+
jestCoverage: !lite && scope === 'be' ? jestCoverageSource(jestCoverage(manifest, providers)) : '',
|
|
273
|
+
coverageExclusions: !lite && scope === APP_SCOPE ? sonarCoverageExclusions(manifest, providers).join(',') : '',
|
|
274
|
+
codecovPaths: !lite && scope === APP_SCOPE ? codecovPaths(manifest, providers).map(glob => ` - ${JSON.stringify(glob)}`).join('\n') : '',
|
|
275
|
+
codecovComponents: !lite && scope === APP_SCOPE ? componentsYaml(coverageComponents(manifest, { ...source, apps: app.sides.be.apps, providers })) : '',
|
|
276
|
+
tsconfigPaths: ['be/tsconfig.json', ...fe.apps.map(entry => `fe/apps/${entry.name}/tsconfig.json`), ...(packages ? ['fe/packages/*/tsconfig.json'] : [])].join(','),
|
|
218
277
|
styleGlob: STYLE_GLOB,
|
|
278
|
+
imageMatrix: imageMatrix(app),
|
|
279
|
+
imageProject: app.project,
|
|
219
280
|
};
|
|
220
281
|
}
|
|
221
282
|
|
|
@@ -236,12 +297,12 @@ function parseScripts(text) {
|
|
|
236
297
|
* its folder); a scripts target also carries `scripts`, the parsed block. `sonarKey` is the key the be side's stack declaration
|
|
237
298
|
* names; without one the key is the project.
|
|
238
299
|
*/
|
|
239
|
-
export function renderTargets(hfs, presets, { sonarKey, readTemplate = readBundled, manifest = loadSlotManifest() } = {}) {
|
|
300
|
+
export function renderTargets(hfs, presets, { sonarKey, readTemplate = readBundled, manifest = loadSlotManifest(), source } = {}) {
|
|
240
301
|
const app = validateHfs(hfs, manifest);
|
|
241
302
|
const hasTemplate = name => { try { return readTemplate(name) !== undefined; } catch { return false; } };
|
|
242
303
|
return SCOPES.flatMap(scope => {
|
|
243
|
-
const vars = variables(app, scope, presets, sonarKey, manifest);
|
|
244
|
-
return targetsOf(scope, { manifest, hasTemplate }).map(target => {
|
|
304
|
+
const vars = variables(app, scope, presets, sonarKey, manifest, source);
|
|
305
|
+
return targetsOf(scope, { edition: app.edition, manifest, hasTemplate }).map(target => {
|
|
245
306
|
const body = render(readTemplate(target.template), vars, readTemplate);
|
|
246
307
|
const where = onSide(scope, target.path);
|
|
247
308
|
if (target.mode === 'scripts') {
|
|
@@ -318,41 +379,43 @@ export function writeTargets(root, targets) {
|
|
|
318
379
|
}
|
|
319
380
|
|
|
320
381
|
/** The hfs.json object of the app at `root` (validated), or a SyncError. */
|
|
321
|
-
export function loadHfs(root) {
|
|
382
|
+
export function loadHfs(root, manifest = loadSlotManifest()) {
|
|
322
383
|
const file = path.join(root, 'hfs.json');
|
|
323
|
-
if (!fs.existsSync(file)) throw new SyncError('HFS_SYNC_HFS_INVALID', `${file} does not exist;
|
|
384
|
+
if (!fs.existsSync(file)) throw new SyncError('HFS_SYNC_HFS_INVALID', `${file} does not exist; starci app sync runs at the app root`);
|
|
324
385
|
let hfs;
|
|
325
386
|
try {
|
|
326
387
|
hfs = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
327
388
|
} catch (error) {
|
|
328
389
|
throw new SyncError('HFS_SYNC_HFS_INVALID', `${file} is not valid JSON: ${error.message}`);
|
|
329
390
|
}
|
|
330
|
-
validateHfs(hfs);
|
|
391
|
+
validateHfs(hfs, manifest);
|
|
331
392
|
return hfs;
|
|
332
393
|
}
|
|
333
394
|
|
|
334
395
|
/** The rendered targets of the app at `root`: its hfs.json, the Sonar key the be side's stack declaration names, the presets it installs. */
|
|
335
|
-
export async function renderRepo(root, { presets, parseYaml } = {}) {
|
|
336
|
-
const hfs = loadHfs(root);
|
|
396
|
+
export async function renderRepo(root, { presets, parseYaml, manifest = loadSlotManifest() } = {}) {
|
|
397
|
+
const hfs = loadHfs(root, manifest);
|
|
337
398
|
const sonarKey = await readDeclaredSonarKey(root, { parseYaml, fail: message => { throw new SyncError('HFS_SYNC_SONAR_KEY', message); } });
|
|
338
|
-
return { hfs, targets: renderTargets(hfs, presets ?? await loadPresets(root), { sonarKey }) };
|
|
399
|
+
return { hfs, targets: renderTargets(hfs, presets ?? await loadPresets(root, hfs.edition), { sonarKey, manifest, source: appSource(root) }) };
|
|
339
400
|
}
|
|
340
401
|
|
|
341
|
-
/** `
|
|
402
|
+
/** `starci app sync --check | --write [--cwd <dir>]` at the app root; returns 0 clean, 1 drift, 2 usage. */
|
|
342
403
|
export async function runSync(argv, { cwd = process.cwd(), out = line => process.stdout.write(`${line}\n`), presets, parseYaml } = {}) {
|
|
343
404
|
const check = argv.includes('--check'), write = argv.includes('--write');
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
405
|
+
const cwdAt = argv.indexOf('--cwd');
|
|
406
|
+
const known = new Set(['--check', '--write', '--cwd']);
|
|
407
|
+
const unknown = argv.find((arg, index) => !known.has(arg) && index !== cwdAt + 1);
|
|
408
|
+
if ([check, write].filter(Boolean).length !== 1 || unknown !== undefined || (cwdAt >= 0 && argv[cwdAt + 1] === undefined)) {
|
|
409
|
+
out('usage: starci app sync (--check | --write) [--cwd <dir>]');
|
|
347
410
|
return 2;
|
|
348
411
|
}
|
|
349
|
-
const root =
|
|
412
|
+
const root = cwdAt >= 0 ? path.resolve(cwd, argv[cwdAt + 1]) : cwd;
|
|
350
413
|
try {
|
|
351
414
|
const { targets } = await renderRepo(root, { presets, parseYaml });
|
|
352
415
|
if (write) {
|
|
353
416
|
const written = writeTargets(root, targets);
|
|
354
417
|
for (const file of written) out(`wrote ${file}`);
|
|
355
|
-
out(`
|
|
418
|
+
out(`starci app sync: ${written.length} written, ${targets.length - written.length} already in sync`);
|
|
356
419
|
return 0;
|
|
357
420
|
}
|
|
358
421
|
const results = checkTargets(root, targets);
|
|
@@ -362,7 +425,7 @@ export async function runSync(argv, { cwd = process.cwd(), out = line => process
|
|
|
362
425
|
const where = result.difference ? ` (line ${result.difference.line}: expected ${JSON.stringify(result.difference.expected)}, found ${JSON.stringify(result.difference.actual)})` : '';
|
|
363
426
|
out(`HFS_SYNC_DRIFT ${result.path}: ${result.status}, expected sha256 ${result.expectedHash.slice(0, 12)}${found}${where}`);
|
|
364
427
|
}
|
|
365
|
-
out(`
|
|
428
|
+
out(`starci app sync --check: ${results.length - bad.length} of ${results.length} in sync${bad.length ? '; run "npx starci app sync --write"' : ''}`);
|
|
366
429
|
return bad.length ? 1 : 0;
|
|
367
430
|
} catch (error) {
|
|
368
431
|
if (!(error instanceof SyncError)) throw error;
|
package/sync/managed.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The managed-file findings of `
|
|
1
|
+
// The managed-file findings of `starci app check`: the rendered set of `starci app sync`, compared
|
|
2
2
|
// with the tracked repository, and the tool configuration nothing may add to it.
|
|
3
3
|
// HFS_MANAGED_FILE_DRIFT (R05) a managed file that exists but differs from its render: tsconfig*.json (but see R22),
|
|
4
4
|
// jest.config.js (back end), .prettierrc, .prettierignore, husky hooks, both workflows and sonar
|
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
// configuration, and a package.json that carries a tool configuration key (`eslintConfig`, `stylelint`,
|
|
20
20
|
// `prettier`, `lint-staged`, `jest`). Forbidden tool-config files (.eslintrc*, .eslintignore, a second
|
|
21
21
|
// eslint.config.*) are refused by hfs-check through the slot manifest under the same code.
|
|
22
|
+
// HFS_COVERAGE_SCOPE_DRIFT (R204)
|
|
23
|
+
// a file that states the coverage scope no longer states what the slot manifest derives (the `coverage` field of
|
|
24
|
+
// knowledge/hfs/slots.yaml through scripts/hfs/coverage-scope.mjs): be/jest.config.js, codecov.yml, or the
|
|
25
|
+
// `sonar.coverage.exclusions` line of sonar-project.properties. The scope is never edited by hand.
|
|
22
26
|
// HFS_SONAR_CONFIG (R11) sonar-project.properties differs from its render (the render names no host URL, sources and tests that
|
|
23
|
-
// do not overlap, the `sonar.exclusions` of the jest preset (a back end), the be lcov with the
|
|
27
|
+
// do not overlap, the `sonar.exclusions` of the jest preset (a back end), the be lcov with the logic of be/src/modules as the coverage scope, the ESLint report and the
|
|
24
28
|
// HFS import files Sonar reads), or the stack declaration names a quality gate other than the one
|
|
25
29
|
// gate of knowledge/sonar-gate.yaml (bundled in the runtime copy). One edit is one finding.
|
|
26
30
|
// HFS_TS_STRICT (R22) the root tsconfig.json drift (either profile), named by flag (ts-strict.mjs) instead of by hash.
|
|
@@ -28,12 +32,16 @@ import fs from 'node:fs';
|
|
|
28
32
|
import path from 'node:path';
|
|
29
33
|
import { SyncError, checkTargets, renderRepo } from './index.mjs';
|
|
30
34
|
import { parseYaml as bundledParseYaml } from '../runtime/engine/yaml.mjs';
|
|
31
|
-
import { readDeclaredSonarGate } from './sonar-key.mjs';
|
|
35
|
+
import { readDeclaredSonarGate, sonarGateName } from './sonar-key.mjs';
|
|
32
36
|
import { TS_STRICT_FILE, tsStrictFindings } from './ts-strict.mjs';
|
|
33
37
|
|
|
34
38
|
// The managed one-liners whose difference from the render is the proof that no rule is off, warned or redefined (R17).
|
|
35
39
|
const ONE_LINER_FILES = new Set(['eslint.config.mjs', 'stylelint.config.mjs']);
|
|
36
40
|
const SONAR_PROPERTIES_FILE = 'sonar-project.properties';
|
|
41
|
+
// The files whose whole content is a statement of the coverage scope, and the one line of the Sonar file that is (R204).
|
|
42
|
+
const COVERAGE_SCOPE_FILES = new Set(['be/jest.config.js', 'codecov.yml']);
|
|
43
|
+
const SONAR_COVERAGE_LINE = 'sonar.coverage.exclusions=';
|
|
44
|
+
const statesCoverageScope = result => COVERAGE_SCOPE_FILES.has(result.path) || (result.path === SONAR_PROPERTIES_FILE && String(result.difference?.expected ?? '').startsWith(SONAR_COVERAGE_LINE));
|
|
37
45
|
const SONAR_GATE_FILE = new URL('../runtime/knowledge/sonar-gate.yaml', import.meta.url);
|
|
38
46
|
const MAX_SCANNED_BYTES = 1024 * 1024;
|
|
39
47
|
const RULE_FILE = /\.[cm]?[jt]s$/;
|
|
@@ -80,19 +88,19 @@ function driftFindings(repoRoot, targets) {
|
|
|
80
88
|
continue;
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
|
-
const code = target.mode === 'block' ? 'HFS_GITIGNORE_BLOCK_DRIFT' : ONE_LINER_FILES.has(path.posix.basename(result.path)) ? 'HFS_RULE_OFF_WITHOUT_REPLACEMENT' : result.path === SONAR_PROPERTIES_FILE ? 'HFS_SONAR_CONFIG' : 'HFS_MANAGED_FILE_DRIFT';
|
|
91
|
+
const code = target.mode === 'block' ? 'HFS_GITIGNORE_BLOCK_DRIFT' : ONE_LINER_FILES.has(path.posix.basename(result.path)) ? 'HFS_RULE_OFF_WITHOUT_REPLACEMENT' : statesCoverageScope(result) ? 'HFS_COVERAGE_SCOPE_DRIFT' : result.path === SONAR_PROPERTIES_FILE ? 'HFS_SONAR_CONFIG' : 'HFS_MANAGED_FILE_DRIFT';
|
|
84
92
|
const where = result.difference ? `; line ${result.difference.line} expected ${JSON.stringify(result.difference.expected)}, found ${JSON.stringify(result.difference.actual)}` : '';
|
|
85
|
-
const what = target.mode === 'block' ? `the managed block of ${result.path} is ${result.status === 'missing' ? 'missing' : 'not its render'}` : target.mode === 'scripts' ? 'the scripts block of package.json is not the rendered one' : `${result.path} is not its render${code === 'HFS_RULE_OFF_WITHOUT_REPLACEMENT' ? ', so a rule can be off or redefined in it' : ''}`;
|
|
86
|
-
findings.push({ code, level: 'error', path: result.path, mode: target.mode, expectedHash: result.expectedHash, ...(result.actualHash ? { actualHash: result.actualHash } : {}), message: `${what} (expected sha256 ${result.expectedHash.slice(0, 12)}${result.actualHash ? `, found ${result.actualHash.slice(0, 12)}` : ''}${where}); run "npx
|
|
93
|
+
const what = target.mode === 'block' ? `the managed block of ${result.path} is ${result.status === 'missing' ? 'missing' : 'not its render'}` : target.mode === 'scripts' ? 'the scripts block of package.json is not the rendered one' : `${result.path} is not its render${code === 'HFS_RULE_OFF_WITHOUT_REPLACEMENT' ? ', so a rule can be off or redefined in it' : code === 'HFS_COVERAGE_SCOPE_DRIFT' ? ', so it no longer states the coverage scope derived from the slot manifest' : ''}`;
|
|
94
|
+
findings.push({ code, level: 'error', path: result.path, mode: target.mode, expectedHash: result.expectedHash, ...(result.actualHash ? { actualHash: result.actualHash } : {}), message: `${what} (expected sha256 ${result.expectedHash.slice(0, 12)}${result.actualHash ? `, found ${result.actualHash.slice(0, 12)}` : ''}${where}); run "npx starci app sync --write"` });
|
|
87
95
|
}
|
|
88
96
|
return findings;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/** R11: the quality gate the be side's stack declaration names is the one gate of the bundled knowledge/sonar-gate.yaml. */
|
|
92
|
-
function sonarGateFindings(repoRoot, parseYaml) {
|
|
100
|
+
function sonarGateFindings(repoRoot, parseYaml, edition) {
|
|
93
101
|
const declared = readDeclaredSonarGate(repoRoot, { parseYaml });
|
|
94
102
|
if (declared === null) return [];
|
|
95
|
-
const gate = (parseYaml ?? bundledParseYaml)(fs.readFileSync(SONAR_GATE_FILE, 'utf8'))
|
|
103
|
+
const gate = sonarGateName((parseYaml ?? bundledParseYaml)(fs.readFileSync(SONAR_GATE_FILE, 'utf8')), edition);
|
|
96
104
|
if (declared.qualityGate === gate) return [];
|
|
97
105
|
return [{ code: 'HFS_SONAR_CONFIG', level: 'error', path: declared.file, message: `${declared.file} services.sonar.qualityGate is ${declared.qualityGate ?? 'absent'}; every product names the one gate ${gate} of knowledge/sonar-gate.yaml and states no threshold of its own` }];
|
|
98
106
|
}
|
|
@@ -153,17 +161,17 @@ function flagFindings(repoRoot, tracked, managed) {
|
|
|
153
161
|
* is unreadable has none: the slot check reports the declaration. A missing preset or an unreadable stack declaration is
|
|
154
162
|
* a SyncError the caller reports as a refusal.
|
|
155
163
|
*/
|
|
156
|
-
export async function managedFindings({ repoRoot, tracked, presets, parseYaml }) {
|
|
164
|
+
export async function managedFindings({ repoRoot, tracked, presets, parseYaml, manifest }) {
|
|
157
165
|
let rendered;
|
|
158
166
|
try {
|
|
159
|
-
rendered = await renderRepo(repoRoot, { presets, parseYaml });
|
|
167
|
+
rendered = await renderRepo(repoRoot, { presets, parseYaml, ...(manifest ? { manifest } : {}) });
|
|
160
168
|
} catch (error) {
|
|
161
169
|
if (error instanceof SyncError && error.code === 'HFS_SYNC_HFS_INVALID') return [];
|
|
162
170
|
throw error;
|
|
163
171
|
}
|
|
164
172
|
const { targets } = rendered;
|
|
165
173
|
const findings = driftFindings(repoRoot, targets);
|
|
166
|
-
findings.push(...sonarGateFindings(repoRoot, parseYaml));
|
|
174
|
+
findings.push(...sonarGateFindings(repoRoot, parseYaml, rendered.hfs.edition ?? 'full'));
|
|
167
175
|
findings.push(...ruleFileFindings(repoRoot, tracked));
|
|
168
176
|
findings.push(...toolKeyFindings(repoRoot, tracked));
|
|
169
177
|
findings.push(...flagFindings(repoRoot, tracked, new Set(targets.map(target => target.path))));
|
package/sync/sonar-key.mjs
CHANGED
|
@@ -52,3 +52,8 @@ export function readDeclaredSonarGate(root, { parseYaml } = {}) {
|
|
|
52
52
|
if (!sonar || sonar.mode === 'disabled') return null;
|
|
53
53
|
return { file: path.relative(root, file).split(path.sep).join('/'), qualityGate: typeof sonar.qualityGate === 'string' ? sonar.qualityGate : null };
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/** The quality-gate name for an app edition from the one sonar-gate document. */
|
|
57
|
+
export function sonarGateName(document, edition = 'full') {
|
|
58
|
+
return (edition === 'lite' ? document?.lite : document)?.gate?.name ?? null;
|
|
59
|
+
}
|
|
@@ -3,18 +3,26 @@ name: ci
|
|
|
3
3
|
|
|
4
4
|
on:
|
|
5
5
|
push:
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
workflow_dispatch:
|
|
8
8
|
|
|
9
9
|
permissions:
|
|
10
10
|
contents: read
|
|
11
11
|
|
|
12
|
+
# One run per ref: a release tag is never cancelled by another run.
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ci-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
12
17
|
jobs:
|
|
13
18
|
ci:
|
|
14
19
|
runs-on: ubuntu-latest
|
|
20
|
+
# id-token: write lets the Codecov upload authenticate with GitHub's OIDC token: no repository secret, no silent skip.
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
id-token: write
|
|
15
24
|
env:
|
|
16
25
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
17
|
-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
18
26
|
steps:
|
|
19
27
|
- uses: actions/checkout@v4
|
|
20
28
|
- uses: actions/setup-node@v4
|
|
@@ -32,9 +40,9 @@ jobs:
|
|
|
32
40
|
run: npm test -- --ci
|
|
33
41
|
- name: coverage upload
|
|
34
42
|
uses: codecov/codecov-action@v5
|
|
35
|
-
if: ${{ !cancelled() &&
|
|
43
|
+
if: ${{ !cancelled() && startsWith(github.ref, 'refs/tags/v') }}
|
|
36
44
|
with:
|
|
37
|
-
|
|
45
|
+
use_oidc: true
|
|
38
46
|
files: {{lcovReport}}
|
|
39
47
|
disable_search: true
|
|
40
48
|
fail_ci_if_error: true
|
|
@@ -42,12 +50,4 @@ jobs:
|
|
|
42
50
|
run: npm run build:be
|
|
43
51
|
- name: build fe
|
|
44
52
|
run: npm run build:fe
|
|
45
|
-
|
|
46
|
-
if: ${{ !cancelled() && env.SONAR_TOKEN != '' }}
|
|
47
|
-
env:
|
|
48
|
-
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
49
|
-
- uses: SonarSource/sonarqube-quality-gate-action@v1
|
|
50
|
-
if: ${{ !cancelled() && env.SONAR_TOKEN != '' }}
|
|
51
|
-
timeout-minutes: 10
|
|
52
|
-
env:
|
|
53
|
-
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
53
|
+
{{> app/ci-workflows/sonar-steps.yml}}
|