@starci/hfs 4.3.0 → 4.4.1
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 +14 -0
- package/README.md +1 -1
- package/db/migrate.mjs +361 -0
- package/db/post-cutover.mjs +136 -0
- package/emit/contracts.mjs +2 -2
- package/emit/db-types.mjs +12 -3
- package/lint/run.mjs +3 -3
- package/package.json +4 -1
- package/release/gate.mjs +298 -0
- package/release/index.mjs +103 -0
- package/release/push.mjs +110 -0
- package/report/sonar.mjs +1 -1
- package/runtime/config.example.yaml +7 -6
- package/runtime/engine/config.mjs +10 -18
- package/runtime/engine/removed-vocabulary.mjs +0 -2
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/rules.yaml +2855 -163
- package/runtime/knowledge/hfs/slots.yaml +333 -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 +3091 -43
- package/runtime/modules/kernel/removed-vocabulary.yaml +30 -0
- package/runtime/modules/models/runtimes.yaml +5 -7
- package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
- package/runtime/packages/cli/src/catalog.generated.mjs +13769 -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 +4 -1
- 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 +42 -1
- 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/config.mjs +1 -1
- 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/index.mjs +1 -1
- 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 +4 -4
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +8 -2
- package/runtime/scripts/hfs/architecture.mjs +1 -1
- 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/coverage-scope.mjs +1 -1
- 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 +69 -9
- package/runtime/scripts/hfs/param-names.mjs +1 -1
- package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
- package/runtime/scripts/hfs/rule-catalog.mjs +158 -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/cli.mjs +1 -1
- package/runtime/scripts/hfs/rules/contract-compat.mjs +1 -1
- package/runtime/scripts/hfs/rules/contract.mjs +67 -6
- 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 +2 -2
- 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 +3 -2
- 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/hfs/typescript-programs.mjs +0 -8
- 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/env.mjs +2 -2
- package/runtime/scripts/lib/git.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +0 -2
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/language.mjs +1 -1
- package/runtime/scripts/lib/list.mjs +4 -0
- package/runtime/scripts/lib/roots.mjs +2 -4
- 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 +1 -0
- package/scaffold/add-fe-page.mjs +1 -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/scaffold/supabase-ports.mjs +1 -1
- 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/format.mjs +1 -1
- package/sync/hygiene.mjs +1 -1
- package/sync/index.mjs +110 -54
- 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 +54 -41
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Infer lexical request identities for SWR hooks without a declared lifecycle contract.
|
|
2
|
+
import { unwrapExpression } from './typescript.mjs';
|
|
3
|
+
import { anyDescendant, normalizedSymbol } from './ast-walks.mjs';
|
|
4
|
+
import { sourceLocation } from '../../lib/ts-ast.mjs';
|
|
5
|
+
import { functionNode, constInitializer, inferredSymbol } from './next-data-symbols.mjs';
|
|
6
|
+
|
|
7
|
+
export function inferredFetcher(ts, checker, expression, seen = new Set()) {
|
|
8
|
+
if (!expression) return null;
|
|
9
|
+
expression = unwrapExpression(ts, expression);
|
|
10
|
+
if (ts.isArrowFunction(expression) || ts.isFunctionExpression(expression)) return expression;
|
|
11
|
+
const symbol = normalizedSymbol(ts, checker, expression);
|
|
12
|
+
if (!symbol || seen.has(symbol)) return null;
|
|
13
|
+
for (const declaration of symbol.getDeclarations?.() ?? []) {
|
|
14
|
+
const fn = functionNode(ts, declaration);
|
|
15
|
+
if (fn) return fn;
|
|
16
|
+
}
|
|
17
|
+
const initializer = constInitializer(ts, symbol);
|
|
18
|
+
return initializer ? inferredFetcher(ts, checker, initializer, new Set(seen).add(symbol)) : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function importedIdentityReference(ts, checker, expression) {
|
|
22
|
+
const reference = ts.isShorthandPropertyAssignment(expression.parent)
|
|
23
|
+
? checker.getShorthandAssignmentValueSymbol(expression.parent) : checker.getSymbolAtLocation(expression);
|
|
24
|
+
return (reference?.getDeclarations?.() ?? []).some(declaration => ts.isImportSpecifier(declaration)
|
|
25
|
+
|| ts.isImportClause(declaration) || ts.isNamespaceImport(declaration) || ts.isImportEqualsDeclaration(declaration));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function identifierIdentityAccess(ts, checker, expression, seen) {
|
|
29
|
+
const imported = importedIdentityReference(ts, checker, expression);
|
|
30
|
+
const symbol = inferredSymbol(ts, checker, expression);
|
|
31
|
+
const initializer = symbol && !seen.has(symbol) ? constInitializer(ts, symbol) : null;
|
|
32
|
+
if (initializer) {
|
|
33
|
+
const alias = identityAccess(ts, checker, initializer, new Set(seen).add(symbol));
|
|
34
|
+
if (alias) return { ...alias, imported: imported || alias.imported };
|
|
35
|
+
}
|
|
36
|
+
return { symbol, parts: [expression.text], imported };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isSelectedMember(ts, expression) {
|
|
40
|
+
return ts.isPropertyAccessExpression(expression) || ts.isElementAccessExpression(expression)
|
|
41
|
+
&& ts.isStringLiteralLike(expression.argumentExpression);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function selectedMemberKey(ts, expression) {
|
|
45
|
+
return ts.isPropertyAccessExpression(expression) ? expression.name.text : expression.argumentExpression.text;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function identityAccess(ts, checker, expression, seen = new Set()) {
|
|
49
|
+
expression = unwrapExpression(ts, expression);
|
|
50
|
+
if (ts.isIdentifier(expression)) return identifierIdentityAccess(ts, checker, expression, seen);
|
|
51
|
+
if (isSelectedMember(ts, expression)) {
|
|
52
|
+
const base = identityAccess(ts, checker, expression.expression, seen);
|
|
53
|
+
return base ? { ...base, parts: [...base.parts, selectedMemberKey(ts, expression)] } : null;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function containerWritten(ts, checker, symbol, source) {
|
|
59
|
+
const rootSymbol = expression => {
|
|
60
|
+
expression = unwrapExpression(ts, expression);
|
|
61
|
+
while (ts.isPropertyAccessExpression(expression) || ts.isElementAccessExpression(expression)) expression = unwrapExpression(ts, expression.expression);
|
|
62
|
+
return ts.isIdentifier(expression) ? inferredSymbol(ts, checker, expression) : null;
|
|
63
|
+
};
|
|
64
|
+
return anyDescendant(ts, source, node => {
|
|
65
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment
|
|
66
|
+
&& node.operatorToken.kind <= ts.SyntaxKind.LastAssignment) return rootSymbol(node.left) === symbol;
|
|
67
|
+
if (ts.isDeleteExpression(node)) return rootSymbol(node.expression) === symbol;
|
|
68
|
+
if (ts.isPostfixUnaryExpression(node) || ts.isPrefixUnaryExpression(node)
|
|
69
|
+
&& [ts.SyntaxKind.PlusPlusToken, ts.SyntaxKind.MinusMinusToken].includes(node.operator)) return rootSymbol(node.operand) === symbol;
|
|
70
|
+
return false;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function selectedRequestAlias(ts, checker, expression, seen) {
|
|
75
|
+
const symbol = inferredSymbol(ts, checker, expression);
|
|
76
|
+
const initializer = symbol && !seen.has(symbol) ? constInitializer(ts, symbol) : null;
|
|
77
|
+
if (!initializer) return null;
|
|
78
|
+
const value = unwrapExpression(ts, initializer);
|
|
79
|
+
if (ts.isObjectLiteralExpression(value) && (initializer.getSourceFile() !== expression.getSourceFile()
|
|
80
|
+
|| containerWritten(ts, checker, symbol, expression.getSourceFile()))) return null;
|
|
81
|
+
if (ts.isObjectLiteralExpression(value) || ts.isIdentifier(value) || ts.isPropertyAccessExpression(value)
|
|
82
|
+
|| ts.isElementAccessExpression(value)) return { symbol, value };
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function selectedRequestProperty(ts, object, key) {
|
|
87
|
+
if (!ts.isObjectLiteralExpression(object) || !object.properties.every(property => ts.isPropertyAssignment(property)
|
|
88
|
+
|| ts.isShorthandPropertyAssignment(property))) return null;
|
|
89
|
+
return [...object.properties].reverse().find(candidate => (ts.isIdentifier(candidate.name)
|
|
90
|
+
|| ts.isStringLiteralLike(candidate.name)) && candidate.name.text === key) ?? null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function selectedRequestValue(ts, checker, expression, seen = new Set()) {
|
|
94
|
+
expression = unwrapExpression(ts, expression);
|
|
95
|
+
if (ts.isIdentifier(expression)) {
|
|
96
|
+
const alias = selectedRequestAlias(ts, checker, expression, seen);
|
|
97
|
+
return alias ? selectedRequestValue(ts, checker, alias.value, new Set(seen).add(alias.symbol)) : expression;
|
|
98
|
+
}
|
|
99
|
+
if (!isSelectedMember(ts, expression)) return expression;
|
|
100
|
+
const object = selectedRequestValue(ts, checker, expression.expression, seen);
|
|
101
|
+
const property = selectedRequestProperty(ts, object, selectedMemberKey(ts, expression));
|
|
102
|
+
if (!property) return expression;
|
|
103
|
+
const value = ts.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
104
|
+
return selectedRequestValue(ts, checker, value, seen);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function capturedInput(ts, access, region) {
|
|
108
|
+
const declarations = access.symbol?.getDeclarations?.() ?? [];
|
|
109
|
+
return declarations.some(declaration => {
|
|
110
|
+
if (declaration.getSourceFile() === region.getSourceFile() && declaration.pos >= region.pos && declaration.end <= region.end) return false;
|
|
111
|
+
if (declaration.getSourceFile().isDeclarationFile && !access.imported) return false;
|
|
112
|
+
return access.imported || ts.isVariableDeclaration(declaration) || ts.isParameter(declaration) || ts.isBindingElement(declaration);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function capturedIdentityAlias(ts, access, seen) {
|
|
117
|
+
// Follow transparent aliases and request objects, so {id} and an alias of id carry the same identity.
|
|
118
|
+
if (access.parts.length !== 1 || seen.has(access.symbol)) return null;
|
|
119
|
+
const initializer = constInitializer(ts, access.symbol);
|
|
120
|
+
if (!initializer) return null;
|
|
121
|
+
const value = unwrapExpression(ts, initializer);
|
|
122
|
+
return ts.isCallExpression(value) || ts.isAwaitExpression(value) || ts.isNewExpression(value) ? null : value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function recordInferredIdentity(scope, access, seen, visit) {
|
|
126
|
+
const { ts, call, region, reasons, identities } = scope;
|
|
127
|
+
if (access?.imported && !(access.symbol?.getDeclarations?.() ?? []).length) {
|
|
128
|
+
reasons.push(`${call.source.fileName}:${sourceLocation(call.source, call.node).line} imported SWR request input ${access.parts.join('.')} cannot be resolved; dataLifecycle is required to declare identities`);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (!access || !capturedInput(ts, access, region)) return false;
|
|
132
|
+
const alias = capturedIdentityAlias(ts, access, seen);
|
|
133
|
+
if (alias) visit(alias, new Set(seen).add(access.symbol));
|
|
134
|
+
else {
|
|
135
|
+
const binding = access.parts.join('.');
|
|
136
|
+
identities.set(binding, { id: binding, binding, ...access, gatesRequest: false, resource: call.kind === 'mutation' });
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function visitRequestChildren(ts, node, seen, visit) {
|
|
142
|
+
if (ts.isPropertyAssignment(node)) { visit(node.initializer, seen); return; }
|
|
143
|
+
// A method name is not a request input; its receiver and arguments can be.
|
|
144
|
+
if (ts.isCallExpression(node)) {
|
|
145
|
+
const callee = unwrapExpression(ts, node.expression);
|
|
146
|
+
if (ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)) visit(callee.expression, seen);
|
|
147
|
+
for (const argument of node.arguments) visit(argument, seen);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (ts.isVariableDeclaration(node)) {
|
|
151
|
+
if (node.initializer) visit(node.initializer, seen);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
ts.forEachChild(node, child => { visit(child, seen); });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Infer lexical request inputs; parameters declared inside the fetcher are supplied by SWR. */
|
|
158
|
+
export function inferredIdentities(context, call, region, reasons) {
|
|
159
|
+
const { ts } = context;
|
|
160
|
+
const identities = new Map();
|
|
161
|
+
const scope = { ts, call, region, reasons, identities };
|
|
162
|
+
const visit = (node, seen = new Set()) => {
|
|
163
|
+
const selectedValue = selectedRequestValue(ts, call.checker, node);
|
|
164
|
+
if (selectedValue !== node && selectedValue !== unwrapExpression(ts, node)) { visit(selectedValue, seen); return; }
|
|
165
|
+
if (recordInferredIdentity(scope, identityAccess(ts, call.checker, node), seen, visit)) return;
|
|
166
|
+
visitRequestChildren(ts, node, seen, visit);
|
|
167
|
+
};
|
|
168
|
+
visit(region.body ?? region);
|
|
169
|
+
return [...identities.values()];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function inferredKeyBindings(context, call, reasons) {
|
|
173
|
+
const key = call.node.arguments[0];
|
|
174
|
+
// A key or its null guard can contain bindings that are not captured request inputs.
|
|
175
|
+
return key ? inferredIdentities(context, call, key, reasons).map(identity => ({ ...identity, id: `key:${identity.id}`, resource: false })) : [];
|
|
176
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Symbol reads shared by declared and inferred SWR lifecycle checks.
|
|
2
|
+
import { unwrapExpression } from './typescript.mjs';
|
|
3
|
+
import { normalizedSymbol, normalizedSymbolValue } from './ast-walks.mjs';
|
|
4
|
+
import { isConstVariable } from '../../lib/ts-ast.mjs';
|
|
5
|
+
|
|
6
|
+
export function functionNode(ts, declaration) {
|
|
7
|
+
if (ts.isFunctionDeclaration(declaration) && declaration.body) return declaration;
|
|
8
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
9
|
+
const value = unwrapExpression(ts, declaration.initializer);
|
|
10
|
+
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) return value;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function constInitializer(ts, symbol) {
|
|
16
|
+
const declarations = symbol?.getDeclarations?.() ?? [];
|
|
17
|
+
if (declarations.length !== 1 || !ts.isVariableDeclaration(declarations[0]) || !declarations[0].initializer
|
|
18
|
+
|| !isConstVariable(ts, declarations[0])) return null;
|
|
19
|
+
return declarations[0].initializer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function inferredSymbol(ts, checker, node) {
|
|
23
|
+
return ts.isIdentifier(node) && ts.isShorthandPropertyAssignment(node.parent)
|
|
24
|
+
? normalizedSymbolValue(ts, checker, checker.getShorthandAssignmentValueSymbol(node.parent))
|
|
25
|
+
: normalizedSymbol(ts, checker, node);
|
|
26
|
+
}
|
|
@@ -3,9 +3,11 @@ import { canonical, isInside } from './config.mjs';
|
|
|
3
3
|
import { parseContract, installedSWR } from './next-data-contract.mjs';
|
|
4
4
|
import { relativePath, unwrapExpression } from './typescript.mjs';
|
|
5
5
|
import { anyDescendant, normalizedSymbol, normalizedSymbolValue, valueSymbol as sharedValueSymbol } from './ast-walks.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import { sourceLocation } from '../../lib/ts-ast.mjs';
|
|
7
7
|
import { byCodeUnit } from '../../lib/list.mjs';
|
|
8
8
|
import { createNextDataKeyInspector } from './next-data-key.mjs';
|
|
9
|
+
import { functionNode, constInitializer, inferredSymbol } from './next-data-symbols.mjs';
|
|
10
|
+
import { inferredFetcher, inferredIdentities, inferredKeyBindings } from './next-data-inference.mjs';
|
|
9
11
|
export const SWR_KEY_RULE_ID = 'FE_SWR_KEY_IDENTITY';
|
|
10
12
|
export const SWR_MUTATION_RULE_ID = 'FE_SWR_MUTATION_RESOURCE_IDENTITY';
|
|
11
13
|
export const SWR_DATA_RULE_IDS = [SWR_KEY_RULE_ID, SWR_MUTATION_RULE_ID];
|
|
@@ -155,15 +157,6 @@ function discoverCalls(config, context, targets) {
|
|
|
155
157
|
return calls;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
function functionNode(ts, declaration) {
|
|
159
|
-
if (ts.isFunctionDeclaration(declaration) && declaration.body) return declaration;
|
|
160
|
-
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
161
|
-
const value = unwrapExpression(ts, declaration.initializer);
|
|
162
|
-
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) return value;
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
160
|
function exportedHook(config, context, entry, reasons) {
|
|
168
161
|
const source = context.files.find(candidate => canonical(candidate.fileName) === canonical(path.join(config.root, ...entry.path.split('/'))));
|
|
169
162
|
if (!source) {
|
|
@@ -223,19 +216,15 @@ function resolvedIdentities(ts, checker, fn, entry, reasons) {
|
|
|
223
216
|
return identities.length === entry.identities.length ? identities : null;
|
|
224
217
|
}
|
|
225
218
|
|
|
226
|
-
function constInitializer(ts, symbol) {
|
|
227
|
-
const declarations = symbol?.getDeclarations?.() ?? [];
|
|
228
|
-
if (declarations.length !== 1 || !ts.isVariableDeclaration(declarations[0]) || !declarations[0].initializer
|
|
229
|
-
|| !isConstVariable(ts, declarations[0])) return null;
|
|
230
|
-
return declarations[0].initializer;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
219
|
function violation(config, call, ruleId, message, extra = {}) {
|
|
234
220
|
return { ruleId, path: relativePath(config.root, call.source.fileName), ...sourceLocation(call.source, call.node), message, ...extra };
|
|
235
221
|
}
|
|
236
222
|
|
|
237
|
-
const
|
|
238
|
-
SWR_KEY_RULE_ID, SWR_MUTATION_RULE_ID }
|
|
223
|
+
const keyInspectorDependencies = { anyDescendant, normalizedSymbol, unwrapExpression, constInitializer, sourceLocation, violation,
|
|
224
|
+
SWR_KEY_RULE_ID, SWR_MUTATION_RULE_ID };
|
|
225
|
+
const inspectKey = createNextDataKeyInspector(keyInspectorDependencies);
|
|
226
|
+
|
|
227
|
+
const inspectInferredKey = createNextDataKeyInspector({ ...keyInspectorDependencies, normalizedSymbol: inferredSymbol });
|
|
239
228
|
|
|
240
229
|
function isUnshadowedCommonJsRequire(ts, checker, expression, seen = new Set(), depth = 0) {
|
|
241
230
|
expression = unwrapExpression(ts, expression);
|
|
@@ -372,7 +361,26 @@ function lifecycleCoverage(ruleIds, contract, installed, calls, details) {
|
|
|
372
361
|
swr: { name: installed.name, version: installed.version, major: installed.major } };
|
|
373
362
|
}
|
|
374
363
|
|
|
375
|
-
|
|
364
|
+
function inspectDefaultLifecycle(config, context, installed, lifecycleCalls, violations, reasons) {
|
|
365
|
+
for (const call of lifecycleCalls) {
|
|
366
|
+
const fetcher = inferredFetcher(context.ts, call.checker, call.node.arguments[1]);
|
|
367
|
+
if (!fetcher) {
|
|
368
|
+
reasons.push(`${relativePath(config.root, call.source.fileName)}:${sourceLocation(call.source, call.node).line} fetcher inputs cannot be inferred; dataLifecycle is required to declare identities`);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const entry = { id: `swr:${relativePath(config.root, call.source.fileName)}:${sourceLocation(call.source, call.node).line}`,
|
|
372
|
+
path: relativePath(config.root, call.source.fileName), export: call.owner?.name?.text ?? '<inferred hook>', kind: call.kind };
|
|
373
|
+
const identities = inferredIdentities(context, call, fetcher, reasons);
|
|
374
|
+
const selected = new Set(identities.map(identity => identity.id));
|
|
375
|
+
const findings = [];
|
|
376
|
+
inspectInferredKey(config, context, entry, call, [...identities, ...inferredKeyBindings(context, call, reasons)], findings, reasons);
|
|
377
|
+
violations.push(...findings.filter(finding => finding.identity === undefined || selected.has(finding.identity)));
|
|
378
|
+
}
|
|
379
|
+
const details = [...new Set(reasons)].sort(byCodeUnit);
|
|
380
|
+
return { violations, coverage: lifecycleCoverage([...SWR_DATA_RULE_IDS], { hooks: lifecycleCalls }, installed, lifecycleCalls.length, details) };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Check declared lifecycles, or infer captured request identities when production SWR has no declaration. */
|
|
376
384
|
export function checkFrontendDataLifecycle(config, context) {
|
|
377
385
|
const ruleIds = [...SWR_DATA_RULE_IDS];
|
|
378
386
|
const references = swrReferences(config, context);
|
|
@@ -381,17 +389,17 @@ export function checkFrontendDataLifecycle(config, context) {
|
|
|
381
389
|
return unavailableDataLifecycle(ruleIds, [String(error.message ?? error)]);
|
|
382
390
|
}
|
|
383
391
|
if (!contract && !references.found) return { violations: [], coverage: { status: 'not-applicable', ruleIds } };
|
|
384
|
-
if (!contract) return { violations: [], coverage: { status: 'unavailable', ruleIds,
|
|
385
|
-
details: ['package.json#starci.codePatterns.next.dataLifecycle is required when production source selects SWR', ...references.unsupported] } };
|
|
386
392
|
const reasons = [];
|
|
387
393
|
const violations = [];
|
|
388
394
|
reasons.push(...references.unsupported);
|
|
395
|
+
if (!contract && references.unsupported.length) reasons.push('package.json#starci.codePatterns.next.dataLifecycle is required for unsupported SWR bindings');
|
|
389
396
|
let installed;
|
|
390
397
|
try { installed = installedSWR(config.packageRoot ?? config.root); } catch (error) {
|
|
391
398
|
return { violations, coverage: { status: 'unavailable', ruleIds, details: [String(error.message ?? error)] } };
|
|
392
399
|
}
|
|
393
|
-
if (installed.major !== contract.swr.major) reasons.push(`installed swr ${installed.version} does not match declared major ${contract.swr.major}`);
|
|
400
|
+
if (contract && installed.major !== contract.swr.major) reasons.push(`installed swr ${installed.version} does not match declared major ${contract.swr.major}`);
|
|
394
401
|
const { lifecycleCalls } = lifecycleCallSets(config, context, installed, reasons);
|
|
402
|
+
if (!contract) return inspectDefaultLifecycle(config, context, installed, lifecycleCalls, violations, reasons);
|
|
395
403
|
const { hookCache, entriesByHook } = hookEntries(config, context, contract, reasons);
|
|
396
404
|
inspectHookLifecycle(config, context, lifecycleCalls, hookCache, entriesByHook, violations, reasons);
|
|
397
405
|
addUndeclaredLifecycleReasons(config, lifecycleCalls, hookCache, reasons);
|
|
@@ -96,9 +96,23 @@ function checkBackend(graph) {
|
|
|
96
96
|
const reached = reachFrom(graph, rootFiles);
|
|
97
97
|
const violations = [];
|
|
98
98
|
reportUncomposedBackendOwners(byUnit, reached, roots, counts, violations);
|
|
99
|
+
reportUncomposedTransports(graph, reached, roots, counts, violations);
|
|
99
100
|
return { violations, coverage: { status: 'checked', ...counts } };
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
/** A reached feature does not compose its other transport modules. Judge each module's runtime path. */
|
|
104
|
+
function reportUncomposedTransports(graph, reached, roots, counts, violations) {
|
|
105
|
+
for (const [rel, node] of graph.files) {
|
|
106
|
+
if (!node.owner || !rel.startsWith('src/features/') || TEST_FILE.test(rel)) continue;
|
|
107
|
+
if (!/\/transport\/[^/]+\/[^/]+\.module\.[cm]?[jt]sx?$/.test(rel) || reached.has(rel)) continue;
|
|
108
|
+
counts.notComposed += 1;
|
|
109
|
+
violations.push({
|
|
110
|
+
ruleId: 'BE_FEATURE_NOT_COMPOSED', path: rel, owner: node.owner.root, slot: node.slot, appRoots: roots,
|
|
111
|
+
message: `Transport module ${rel} is not composed into any app: no runtime import path leads from ${roots.join(', ')} to it (a type-only import composes nothing).`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
/** Route table of one app: arrays of segments, each {kind:'static'|'dyn'|'catch'|'optcatch', text}. */
|
|
103
117
|
function routeSegmentsOf(parts) {
|
|
104
118
|
const segments = [];
|
|
@@ -9,13 +9,13 @@ import { byCodeUnit } from '../../lib/list.mjs';
|
|
|
9
9
|
* reference's `CodingModule imports DeviceModule` shape). Each capability has one representative module: the only module
|
|
10
10
|
* of the capability an app registers, once, in `apps/<app>/src/app.module.ts` as `X.register({ isGlobal: true, ... })`, or in a
|
|
11
11
|
* composition group `apps/<app>/src/compose/<area>.ts` the root spreads (an app root here means the root and its groups).
|
|
12
|
-
* It may split itself into sub-modules imported as
|
|
12
|
+
* It may split itself into sub-modules imported as `imports: [Sub.register({})]`; a sub-module is never in an app and has one
|
|
13
13
|
* importer, its parent. Over every `@Module` class of the program:
|
|
14
14
|
*
|
|
15
15
|
* - a module in an app root's imports (a `X.register(...)` call or a listed class, helpers of the root included) is
|
|
16
16
|
* listed once there, is imported by no other module, and, when it is a capability module of src/modules, is registered
|
|
17
17
|
* with the literal `isGlobal: true` (a bare capability class is not a registration);
|
|
18
|
-
* - a module imported by a non-app module has exactly one importer and appears in no app root; the one exception is
|
|
18
|
+
* - a module imported by a non-app module (as `Sub.register({})`) has exactly one importer and appears in no app root; the one exception is
|
|
19
19
|
* the feature case: a feature's application module is imported by each protocol module of the same feature;
|
|
20
20
|
* - `isGlobal: true` appears only in an app root.
|
|
21
21
|
*/
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { isInside } from './config.mjs';
|
|
3
3
|
import { relativePath, unwrapExpression } from './typescript.mjs';
|
|
4
|
-
import { commonJsRequireReasons, decoratorCallee, moduleExportsOf, nodeDecorators, normalizedSymbol, normalizedSymbolValue, programSourcesOf, returnedExpressions, selectedNode, valueSymbol, violation } from './ast-walks.mjs';
|
|
4
|
+
import { commonJsRequireReasons, constructedDecoratorKind, decoratorCallee, moduleExportsOf, mutableDecoratorKind, nodeDecorators, normalizedSymbol, normalizedSymbolValue, programSourcesOf, returnedExpressions, selectedNode, valueSymbol, violation } from './ast-walks.mjs';
|
|
5
5
|
import { createBackendSourceShapeChecker } from './source-names-shape.mjs';
|
|
6
|
+
import { byCodeUnit } from '../../lib/list.mjs';
|
|
6
7
|
|
|
7
8
|
const SOURCE_LAYOUT_RULE_ID = 'BE_FEATURE_LAYOUT_INVALID';
|
|
8
9
|
const SOURCE_NAME_RULE_ID = 'BE_SOURCE_FORM';
|
|
9
10
|
const FRAMEWORK_EXPORTS = new Map([
|
|
10
|
-
['@nestjs/graphql', new Set(['Args', 'ArgsType', 'InputType', 'Mutation', 'ObjectType', 'Query', 'registerEnumType'])],
|
|
11
|
+
['@nestjs/graphql', new Set(['Args', 'ArgsType', 'InputType', 'Mutation', 'ObjectType', 'Query', 'Subscription', 'registerEnumType'])],
|
|
11
12
|
['typeorm', new Set(['Entity', 'EntitySchema', 'MigrationInterface', 'ViewEntity'])],
|
|
12
13
|
]);
|
|
13
14
|
const APPLICATION_ROLES = new Set(['command', 'contracts', 'handler', 'query']);
|
|
@@ -237,6 +238,11 @@ function graphqlField({ config, context, sourceFile, node, decorator, kind, nami
|
|
|
237
238
|
}
|
|
238
239
|
const methodName = node.name && (context.ts.isIdentifier(node.name) || context.ts.isStringLiteralLike(node.name)) ? node.name.text : null;
|
|
239
240
|
const name = selected.status === 'literal' ? selected.value : methodName;
|
|
241
|
+
if (selected.status === 'literal') {
|
|
242
|
+
if (!methodName) namingReasons.push(`${relativePath(config.root, sourceFile.fileName)} cannot prove the @${kind} method name`);
|
|
243
|
+
else if (selected.value !== methodName) violations.push(violation(config, sourceFile, selected.node, SOURCE_NAME_RULE_ID,
|
|
244
|
+
`GraphQL @${kind} field name ${selected.value} must equal its method name ${methodName}.`));
|
|
245
|
+
}
|
|
240
246
|
if (!name) namingReasons.push(`${relativePath(config.root, sourceFile.fileName)} cannot prove the @${kind} field name`);
|
|
241
247
|
else if (!camelCase(name)) violations.push(violation(config, sourceFile, selected.node ?? node.name ?? node, SOURCE_NAME_RULE_ID,
|
|
242
248
|
`GraphQL field name ${name} must be camelCase.`));
|
|
@@ -316,7 +322,46 @@ const checkBackendSourceShapeImpl = createBackendSourceShapeChecker({
|
|
|
316
322
|
TRACE_NEW,
|
|
317
323
|
});
|
|
318
324
|
|
|
325
|
+
function inspectSubscriptionSource({ config, context, sourceFile, checker, framework, configuredRoot, namingReasons, violations }) {
|
|
326
|
+
const visit = node => {
|
|
327
|
+
for (const decorator of nodeDecorators(context.ts, node)) {
|
|
328
|
+
const kind = decoratorKind(context.ts, checker, decorator, framework.bySymbol);
|
|
329
|
+
if (kind === 'Subscription') graphqlField({ config, context, sourceFile, node, decorator, kind, namingReasons, violations });
|
|
330
|
+
else if (!kind && !configuredRoot) {
|
|
331
|
+
const dynamic = mutableDecoratorKind(context.ts, checker, decorator, framework.bySymbol)
|
|
332
|
+
?? constructedDecoratorKind(context.ts, checker, decorator, framework.bySymbol, TRACE_NEW);
|
|
333
|
+
if (['Subscription', 'multiple framework', 'unproven framework'].includes(dynamic)) {
|
|
334
|
+
namingReasons.push(`${relativePath(config.root, sourceFile.fileName)} uses a mutable or constructed ${dynamic} decorator identity`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
context.ts.forEachChild(node, visit);
|
|
339
|
+
};
|
|
340
|
+
visit(sourceFile);
|
|
341
|
+
}
|
|
342
|
+
|
|
319
343
|
export function checkBackendSourceShape(config, context) {
|
|
320
|
-
|
|
344
|
+
const result = checkBackendSourceShapeImpl(config, context);
|
|
345
|
+
const roots = absoluteRoots(config.root, [...config.backend.features, ...config.backend.modules]);
|
|
346
|
+
const localFiles = new Set(context.files.map(file => canonical(file.fileName)));
|
|
347
|
+
const frameworkByChecker = new Map();
|
|
348
|
+
const namingReasons = [];
|
|
349
|
+
// The shared shape walk dispatches Query and Mutation; subscriptions use the same naming relation here.
|
|
350
|
+
for (const sourceFile of context.files) {
|
|
351
|
+
const fileName = canonical(sourceFile.fileName);
|
|
352
|
+
const configuredRoot = locatedRoot(roots, fileName);
|
|
353
|
+
const owner = config.hfs?.ownerOf?.(relativePath(config.root, fileName));
|
|
354
|
+
const slot = owner ? config.hfs.slot(owner.slot) : null;
|
|
355
|
+
if (!configuredRoot && !(slot?.tier === 'feature' && slot.profiles?.includes('be'))) continue;
|
|
356
|
+
const checker = context.checkerFor(fileName);
|
|
357
|
+
if (!frameworkByChecker.has(checker)) frameworkByChecker.set(checker, frameworkTargets(config, context, checker, localFiles));
|
|
358
|
+
const framework = frameworkByChecker.get(checker);
|
|
359
|
+
inspectSubscriptionSource({ config, context, sourceFile, checker, framework, configuredRoot, namingReasons, violations: result.violations });
|
|
360
|
+
}
|
|
361
|
+
if (namingReasons.length) result.coverage.naming = {
|
|
362
|
+
status: 'unavailable', reason: 'one or more backend source naming relations are not statically proved',
|
|
363
|
+
details: [...new Set([...(result.coverage.naming.details ?? []), ...namingReasons])].sort(byCodeUnit),
|
|
364
|
+
};
|
|
365
|
+
return result;
|
|
321
366
|
}
|
|
322
367
|
export { SOURCE_LAYOUT_RULE_ID, SOURCE_NAME_RULE_ID };
|