@cosmicdrift/kumiko-framework 0.328.0 → 0.328.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/dist/api/anonymous-cookie.d.ts +8 -0
- package/dist/api/anonymous-cookie.js +40 -0
- package/dist/api/api-constants.d.ts +47 -0
- package/dist/api/api-constants.js +136 -0
- package/dist/api/auth-middleware.d.ts +42 -0
- package/dist/api/auth-middleware.js +450 -0
- package/dist/api/auth-routes.d.ts +83 -0
- package/dist/api/auth-routes.js +1003 -0
- package/dist/api/client-ip.d.ts +15 -0
- package/dist/api/client-ip.js +101 -0
- package/dist/api/csrf-middleware.d.ts +9 -0
- package/dist/api/csrf-middleware.js +67 -0
- package/dist/api/extra-route.d.ts +99 -0
- package/dist/api/extra-route.js +48 -0
- package/dist/api/http-cache.d.ts +31 -0
- package/dist/api/http-cache.js +102 -0
- package/dist/api/index.d.ts +32 -0
- package/dist/api/index.js +20 -0
- package/dist/api/jwt.d.ts +22 -0
- package/dist/api/jwt.js +199 -0
- package/dist/api/observability-middleware.d.ts +8 -0
- package/dist/api/observability-middleware.js +68 -0
- package/dist/api/origin-middleware.d.ts +12 -0
- package/dist/api/origin-middleware.js +120 -0
- package/dist/api/pat-route-guard.d.ts +2 -0
- package/dist/api/pat-route-guard.js +43 -0
- package/dist/api/pat-scope.d.ts +2 -0
- package/dist/api/pat-scope.js +13 -0
- package/dist/api/pii-leak-guard.d.ts +2 -0
- package/dist/api/pii-leak-guard.js +40 -0
- package/dist/api/post-auth-landing.d.ts +14 -0
- package/dist/api/post-auth-landing.js +92 -0
- package/dist/api/readiness.d.ts +23 -0
- package/dist/api/readiness.js +98 -0
- package/dist/api/redis-sse-broker.d.ts +13 -0
- package/dist/api/redis-sse-broker.js +138 -0
- package/dist/api/request-context.d.ts +34 -0
- package/dist/api/request-context.js +60 -0
- package/dist/api/request-id-middleware.d.ts +37 -0
- package/dist/api/request-id-middleware.js +112 -0
- package/dist/api/route-registrars.d.ts +24 -0
- package/dist/api/route-registrars.js +133 -0
- package/dist/api/routes.d.ts +20 -0
- package/dist/api/routes.js +373 -0
- package/dist/api/server.d.ts +81 -0
- package/dist/api/server.js +1068 -0
- package/dist/api/sse-broker.d.ts +36 -0
- package/dist/api/sse-broker.js +112 -0
- package/dist/api/sse-route.d.ts +24 -0
- package/dist/api/sse-route.js +61 -0
- package/dist/api/tokens.d.ts +1 -0
- package/dist/api/tokens.js +15 -0
- package/dist/api/websocket-route.d.ts +59 -0
- package/dist/api/websocket-route.js +239 -0
- package/dist/arg-parser.d.ts +8 -0
- package/dist/arg-parser.js +59 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/roles.d.ts +16 -0
- package/dist/auth/roles.js +35 -0
- package/dist/bun-db/connection.d.ts +16 -0
- package/dist/bun-db/connection.js +54 -0
- package/dist/bun-db/index.d.ts +4 -0
- package/dist/bun-db/index.js +4 -0
- package/dist/bun-db/query.d.ts +64 -0
- package/dist/bun-db/query.js +1145 -0
- package/dist/compliance/duration-spec.d.ts +7 -0
- package/dist/compliance/duration-spec.js +37 -0
- package/dist/compliance/index.d.ts +6 -0
- package/dist/compliance/index.js +7 -0
- package/dist/compliance/override-schema.d.ts +78 -0
- package/dist/compliance/override-schema.js +124 -0
- package/dist/compliance/profiles.d.ts +125 -0
- package/dist/compliance/profiles.js +284 -0
- package/dist/compliance/sub-processors.d.ts +69 -0
- package/dist/compliance/sub-processors.js +109 -0
- package/dist/consumer-cli.d.ts +5 -0
- package/dist/consumer-cli.js +116 -0
- package/dist/crypto/blind-index.d.ts +10 -0
- package/dist/crypto/blind-index.js +97 -0
- package/dist/crypto/ciphertext-pattern.d.ts +3 -0
- package/dist/crypto/ciphertext-pattern.js +16 -0
- package/dist/crypto/event-pii.d.ts +8 -0
- package/dist/crypto/event-pii.js +79 -0
- package/dist/crypto/in-memory-kms-adapter.d.ts +11 -0
- package/dist/crypto/in-memory-kms-adapter.js +33 -0
- package/dist/crypto/index.d.ts +11 -0
- package/dist/crypto/index.js +11 -0
- package/dist/crypto/is-self-pii-field.d.ts +5 -0
- package/dist/crypto/is-self-pii-field.js +4 -0
- package/dist/crypto/kek-source.d.ts +23 -0
- package/dist/crypto/kek-source.js +122 -0
- package/dist/crypto/kms-adapter.d.ts +26 -0
- package/dist/crypto/kms-adapter.js +46 -0
- package/dist/crypto/kms-wiring.d.ts +67 -0
- package/dist/crypto/kms-wiring.js +125 -0
- package/dist/crypto/pg-kms-adapter.d.ts +55 -0
- package/dist/crypto/pg-kms-adapter.js +232 -0
- package/dist/crypto/pii-field-encryption.d.ts +20 -0
- package/dist/crypto/pii-field-encryption.js +201 -0
- package/dist/crypto/request-kms-cache.d.ts +8 -0
- package/dist/crypto/request-kms-cache.js +21 -0
- package/dist/crypto/subject-resolver.d.ts +30 -0
- package/dist/crypto/subject-resolver.js +153 -0
- package/dist/crypto/system-event-pii.d.ts +4 -0
- package/dist/crypto/system-event-pii.js +37 -0
- package/dist/db/api.d.ts +18 -0
- package/dist/db/api.js +23 -0
- package/dist/db/apply-entity-event.d.ts +21 -0
- package/dist/db/apply-entity-event.js +175 -0
- package/dist/db/assert-exists-in.d.ts +23 -0
- package/dist/db/assert-exists-in.js +34 -0
- package/dist/db/blind-index-cleanup.d.ts +5 -0
- package/dist/db/blind-index-cleanup.js +112 -0
- package/dist/db/bun-provider.d.ts +2 -0
- package/dist/db/bun-provider.js +37 -0
- package/dist/db/collect-table-metas.d.ts +3 -0
- package/dist/db/collect-table-metas.js +76 -0
- package/dist/db/compound-types.d.ts +9 -0
- package/dist/db/compound-types.js +31 -0
- package/dist/db/config-seed.d.ts +19 -0
- package/dist/db/config-seed.js +75 -0
- package/dist/db/connection.d.ts +10 -0
- package/dist/db/connection.js +35 -0
- package/dist/db/cursor.d.ts +9 -0
- package/dist/db/cursor.js +39 -0
- package/dist/db/dialect.d.ts +100 -0
- package/dist/db/dialect.js +386 -0
- package/dist/db/eagerload.d.ts +18 -0
- package/dist/db/eagerload.js +191 -0
- package/dist/db/entity-field-encryption.d.ts +15 -0
- package/dist/db/entity-field-encryption.js +127 -0
- package/dist/db/entity-table-from-registry.d.ts +4 -0
- package/dist/db/entity-table-from-registry.js +25 -0
- package/dist/db/entity-table-meta-types.d.ts +1 -0
- package/dist/db/entity-table-meta-types.js +1 -0
- package/dist/db/entity-table-meta.d.ts +30 -0
- package/dist/db/entity-table-meta.js +423 -0
- package/dist/db/event-store-executor-context.d.ts +49 -0
- package/dist/db/event-store-executor-context.js +368 -0
- package/dist/db/event-store-executor-read.d.ts +10 -0
- package/dist/db/event-store-executor-read.js +683 -0
- package/dist/db/event-store-executor-write.d.ts +3 -0
- package/dist/db/event-store-executor-write.js +705 -0
- package/dist/db/event-store-executor.d.ts +20 -0
- package/dist/db/event-store-executor.js +11 -0
- package/dist/db/feature-table-sources.d.ts +6 -0
- package/dist/db/feature-table-sources.js +24 -0
- package/dist/db/index.d.ts +37 -0
- package/dist/db/index.js +29 -0
- package/dist/db/located-timestamp.d.ts +22 -0
- package/dist/db/located-timestamp.js +100 -0
- package/dist/db/migrate-generator.d.ts +81 -0
- package/dist/db/migrate-generator.js +428 -0
- package/dist/db/migrate-runner.d.ts +30 -0
- package/dist/db/migrate-runner.js +273 -0
- package/dist/db/money.d.ts +30 -0
- package/dist/db/money.js +153 -0
- package/dist/db/parent-ref-clause.d.ts +10 -0
- package/dist/db/parent-ref-clause.js +124 -0
- package/dist/db/pg-error.d.ts +10 -0
- package/dist/db/pg-error.js +63 -0
- package/dist/db/postgres-provider.d.ts +2 -0
- package/dist/db/postgres-provider.js +21 -0
- package/dist/db/queries/advisory-lock.d.ts +3 -0
- package/dist/db/queries/advisory-lock.js +5 -0
- package/dist/db/queries/backfill-pii.d.ts +38 -0
- package/dist/db/queries/backfill-pii.js +433 -0
- package/dist/db/queries/ddl.d.ts +4 -0
- package/dist/db/queries/ddl.js +22 -0
- package/dist/db/queries/entity-read.d.ts +2 -0
- package/dist/db/queries/entity-read.js +5 -0
- package/dist/db/queries/es-ops.d.ts +5 -0
- package/dist/db/queries/es-ops.js +10 -0
- package/dist/db/queries/event-consumer.d.ts +36 -0
- package/dist/db/queries/event-consumer.js +261 -0
- package/dist/db/queries/event-store-admin.d.ts +24 -0
- package/dist/db/queries/event-store-admin.js +71 -0
- package/dist/db/queries/event-store-transfer.d.ts +20 -0
- package/dist/db/queries/event-store-transfer.js +31 -0
- package/dist/db/queries/event-store.d.ts +54 -0
- package/dist/db/queries/event-store.js +231 -0
- package/dist/db/queries/pii-backfill-state.d.ts +7 -0
- package/dist/db/queries/pii-backfill-state.js +14 -0
- package/dist/db/queries/projection-rebuild.d.ts +6 -0
- package/dist/db/queries/projection-rebuild.js +65 -0
- package/dist/db/queries/raw-sql.d.ts +9 -0
- package/dist/db/queries/raw-sql.js +16 -0
- package/dist/db/queries/seed-context.d.ts +20 -0
- package/dist/db/queries/seed-context.js +25 -0
- package/dist/db/queries/shadow-swap.d.ts +18 -0
- package/dist/db/queries/shadow-swap.js +307 -0
- package/dist/db/queries/table-ops.d.ts +4 -0
- package/dist/db/queries/table-ops.js +8 -0
- package/dist/db/queries/test-stack.d.ts +6 -0
- package/dist/db/queries/test-stack.js +20 -0
- package/dist/db/query-api.d.ts +2 -0
- package/dist/db/query-api.js +6 -0
- package/dist/db/query.d.ts +1 -0
- package/dist/db/query.js +3 -0
- package/dist/db/rebuild-marker.d.ts +4 -0
- package/dist/db/rebuild-marker.js +96 -0
- package/dist/db/reference-data.d.ts +14 -0
- package/dist/db/reference-data.js +66 -0
- package/dist/db/render-ddl.d.ts +4 -0
- package/dist/db/render-ddl.js +55 -0
- package/dist/db/replay-migration-sql.d.ts +12 -0
- package/dist/db/replay-migration-sql.js +223 -0
- package/dist/db/schema-inspection.d.ts +3 -0
- package/dist/db/schema-inspection.js +60 -0
- package/dist/db/table-builder.d.ts +194 -0
- package/dist/db/table-builder.js +430 -0
- package/dist/db/tenant-db-runner.d.ts +4 -0
- package/dist/db/tenant-db-runner.js +17 -0
- package/dist/db/tenant-db.d.ts +32 -0
- package/dist/db/tenant-db.js +501 -0
- package/dist/derivatives/delete-stored-file.d.ts +6 -0
- package/dist/derivatives/delete-stored-file.js +70 -0
- package/dist/derivatives/derivatives-context.d.ts +21 -0
- package/dist/derivatives/derivatives-context.js +212 -0
- package/dist/derivatives/field-variants.d.ts +3 -0
- package/dist/derivatives/field-variants.js +16 -0
- package/dist/derivatives/index.d.ts +6 -0
- package/dist/derivatives/index.js +4 -0
- package/dist/derivatives/variant-key.d.ts +10 -0
- package/dist/derivatives/variant-key.js +100 -0
- package/dist/engine/access.d.ts +2 -0
- package/dist/engine/access.js +25 -0
- package/dist/engine/active-membership.d.ts +26 -0
- package/dist/engine/active-membership.js +25 -0
- package/dist/engine/boot-validator/access-declarations.d.ts +2 -0
- package/dist/engine/boot-validator/access-declarations.js +148 -0
- package/dist/engine/boot-validator/access-roles.d.ts +2 -0
- package/dist/engine/boot-validator/access-roles.js +76 -0
- package/dist/engine/boot-validator/action-wiring.d.ts +3 -0
- package/dist/engine/boot-validator/action-wiring.js +104 -0
- package/dist/engine/boot-validator/api-ext.d.ts +3 -0
- package/dist/engine/boot-validator/api-ext.js +54 -0
- package/dist/engine/boot-validator/boot-check.d.ts +2 -0
- package/dist/engine/boot-validator/boot-check.js +20 -0
- package/dist/engine/boot-validator/config-deps.d.ts +10 -0
- package/dist/engine/boot-validator/config-deps.js +180 -0
- package/dist/engine/boot-validator/custom-screen-write-qns.d.ts +8 -0
- package/dist/engine/boot-validator/custom-screen-write-qns.js +75 -0
- package/dist/engine/boot-validator/detail-screens.d.ts +6 -0
- package/dist/engine/boot-validator/detail-screens.js +31 -0
- package/dist/engine/boot-validator/entity-handler.d.ts +23 -0
- package/dist/engine/boot-validator/entity-handler.js +754 -0
- package/dist/engine/boot-validator/entity-list-screens.d.ts +4 -0
- package/dist/engine/boot-validator/entity-list-screens.js +75 -0
- package/dist/engine/boot-validator/gdpr-storage.d.ts +2 -0
- package/dist/engine/boot-validator/gdpr-storage.js +48 -0
- package/dist/engine/boot-validator/global-tenancy.d.ts +2 -0
- package/dist/engine/boot-validator/global-tenancy.js +24 -0
- package/dist/engine/boot-validator/i18n-keys.d.ts +2 -0
- package/dist/engine/boot-validator/i18n-keys.js +88 -0
- package/dist/engine/boot-validator/index.d.ts +34 -0
- package/dist/engine/boot-validator/index.js +196 -0
- package/dist/engine/boot-validator/nav.d.ts +20 -0
- package/dist/engine/boot-validator/nav.js +202 -0
- package/dist/engine/boot-validator/ownership.d.ts +36 -0
- package/dist/engine/boot-validator/ownership.js +268 -0
- package/dist/engine/boot-validator/parent-ref.d.ts +3 -0
- package/dist/engine/boot-validator/parent-ref.js +73 -0
- package/dist/engine/boot-validator/pii-retention.d.ts +2 -0
- package/dist/engine/boot-validator/pii-retention.js +204 -0
- package/dist/engine/boot-validator/projection-list-screens.d.ts +4 -0
- package/dist/engine/boot-validator/projection-list-screens.js +219 -0
- package/dist/engine/boot-validator/query-output-columns.d.ts +2 -0
- package/dist/engine/boot-validator/query-output-columns.js +160 -0
- package/dist/engine/boot-validator/query-refs.d.ts +2 -0
- package/dist/engine/boot-validator/query-refs.js +72 -0
- package/dist/engine/boot-validator/record-owned.d.ts +2 -0
- package/dist/engine/boot-validator/record-owned.js +16 -0
- package/dist/engine/boot-validator/screens.d.ts +34 -0
- package/dist/engine/boot-validator/screens.js +1665 -0
- package/dist/engine/boot-validator/security-baseline.d.ts +3 -0
- package/dist/engine/boot-validator/security-baseline.js +18 -0
- package/dist/engine/boot-validator/transfer-graph.d.ts +3 -0
- package/dist/engine/boot-validator/transfer-graph.js +120 -0
- package/dist/engine/boot-validator/url-prefill-fields.d.ts +2 -0
- package/dist/engine/boot-validator/url-prefill-fields.js +106 -0
- package/dist/engine/boot-validator/workspaces.d.ts +20 -0
- package/dist/engine/boot-validator/workspaces.js +81 -0
- package/dist/engine/boot-validator/zod-shape.d.ts +5 -0
- package/dist/engine/boot-validator/zod-shape.js +110 -0
- package/dist/engine/boot-validator.d.ts +1 -0
- package/dist/engine/boot-validator.js +1 -0
- package/dist/engine/build-app-schema.d.ts +14 -0
- package/dist/engine/build-app-schema.js +494 -0
- package/dist/engine/build-config-feature-schema.d.ts +15 -0
- package/dist/engine/build-config-feature-schema.js +582 -0
- package/dist/engine/build-target.d.ts +28 -0
- package/dist/engine/build-target.js +43 -0
- package/dist/engine/changeset-changes.d.ts +12 -0
- package/dist/engine/changeset-changes.js +104 -0
- package/dist/engine/codemod/index.d.ts +2 -0
- package/dist/engine/codemod/index.js +1 -0
- package/dist/engine/codemod/pipeline-codemod.d.ts +50 -0
- package/dist/engine/codemod/pipeline-codemod.js +529 -0
- package/dist/engine/config-helpers.d.ts +42 -0
- package/dist/engine/config-helpers.js +115 -0
- package/dist/engine/constants.d.ts +52 -0
- package/dist/engine/constants.js +79 -0
- package/dist/engine/create-app.d.ts +17 -0
- package/dist/engine/create-app.js +119 -0
- package/dist/engine/cross-tenant.d.ts +3 -0
- package/dist/engine/cross-tenant.js +18 -0
- package/dist/engine/dedupe-features.d.ts +2 -0
- package/dist/engine/dedupe-features.js +36 -0
- package/dist/engine/define-feature.d.ts +7 -0
- package/dist/engine/define-feature.js +143 -0
- package/dist/engine/define-handler.d.ts +21 -0
- package/dist/engine/define-handler.js +117 -0
- package/dist/engine/define-roles.d.ts +5 -0
- package/dist/engine/define-roles.js +14 -0
- package/dist/engine/define-step.d.ts +4 -0
- package/dist/engine/define-step.js +22 -0
- package/dist/engine/define-workflow.d.ts +89 -0
- package/dist/engine/define-workflow.js +70 -0
- package/dist/engine/effective-features.d.ts +6 -0
- package/dist/engine/effective-features.js +73 -0
- package/dist/engine/embedded-derived.d.ts +29 -0
- package/dist/engine/embedded-derived.js +82 -0
- package/dist/engine/entity-handlers.d.ts +67 -0
- package/dist/engine/entity-handlers.js +567 -0
- package/dist/engine/escape-hatch-declaration.d.ts +2 -0
- package/dist/engine/escape-hatch-declaration.js +5 -0
- package/dist/engine/event-helpers.d.ts +11 -0
- package/dist/engine/event-helpers.js +47 -0
- package/dist/engine/extension-names.d.ts +164 -0
- package/dist/engine/extension-names.js +172 -0
- package/dist/engine/extension-selector-plugins.d.ts +5 -0
- package/dist/engine/extension-selector-plugins.js +14 -0
- package/dist/engine/extensions/escape-hatch-usage.d.ts +2 -0
- package/dist/engine/extensions/escape-hatch-usage.js +12 -0
- package/dist/engine/extensions/storage-provider.d.ts +4 -0
- package/dist/engine/extensions/storage-provider.js +1 -0
- package/dist/engine/extensions/tenant-data.d.ts +15 -0
- package/dist/engine/extensions/tenant-data.js +7 -0
- package/dist/engine/extensions/tenant-resource.d.ts +14 -0
- package/dist/engine/extensions/tenant-resource.js +6 -0
- package/dist/engine/extensions/user-data.d.ts +152 -0
- package/dist/engine/extensions/user-data.js +12 -0
- package/dist/engine/factories.d.ts +195 -0
- package/dist/engine/factories.js +387 -0
- package/dist/engine/feature-ast/entity-field-types.d.ts +13 -0
- package/dist/engine/feature-ast/entity-field-types.js +35 -0
- package/dist/engine/feature-ast/extractors/ai-steps.d.ts +6 -0
- package/dist/engine/feature-ast/extractors/ai-steps.js +254 -0
- package/dist/engine/feature-ast/extractors/events.d.ts +5 -0
- package/dist/engine/feature-ast/extractors/events.js +262 -0
- package/dist/engine/feature-ast/extractors/handlers.d.ts +23 -0
- package/dist/engine/feature-ast/extractors/handlers.js +319 -0
- package/dist/engine/feature-ast/extractors/hooks.d.ts +23 -0
- package/dist/engine/feature-ast/extractors/hooks.js +228 -0
- package/dist/engine/feature-ast/extractors/index.d.ts +14 -0
- package/dist/engine/feature-ast/extractors/index.js +13 -0
- package/dist/engine/feature-ast/extractors/jobs-routes.d.ts +7 -0
- package/dist/engine/feature-ast/extractors/jobs-routes.js +141 -0
- package/dist/engine/feature-ast/extractors/projections-screens.d.ts +10 -0
- package/dist/engine/feature-ast/extractors/projections-screens.js +195 -0
- package/dist/engine/feature-ast/extractors/resolve-import.d.ts +14 -0
- package/dist/engine/feature-ast/extractors/resolve-import.js +152 -0
- package/dist/engine/feature-ast/extractors/round1.d.ts +10 -0
- package/dist/engine/feature-ast/extractors/round1.js +81 -0
- package/dist/engine/feature-ast/extractors/round2.d.ts +7 -0
- package/dist/engine/feature-ast/extractors/round2.js +158 -0
- package/dist/engine/feature-ast/extractors/round3.d.ts +22 -0
- package/dist/engine/feature-ast/extractors/round3.js +280 -0
- package/dist/engine/feature-ast/extractors/round5.d.ts +8 -0
- package/dist/engine/feature-ast/extractors/round5.js +63 -0
- package/dist/engine/feature-ast/extractors/round6.d.ts +4 -0
- package/dist/engine/feature-ast/extractors/round6.js +17 -0
- package/dist/engine/feature-ast/extractors/shared.d.ts +85 -0
- package/dist/engine/feature-ast/extractors/shared.js +326 -0
- package/dist/engine/feature-ast/index.d.ts +16 -0
- package/dist/engine/feature-ast/index.js +10 -0
- package/dist/engine/feature-ast/parse.d.ts +30 -0
- package/dist/engine/feature-ast/parse.js +464 -0
- package/dist/engine/feature-ast/patch.d.ts +207 -0
- package/dist/engine/feature-ast/patch.js +771 -0
- package/dist/engine/feature-ast/patcher.d.ts +211 -0
- package/dist/engine/feature-ast/patcher.js +277 -0
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +670 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1196 -0
- package/dist/engine/feature-ast/patterns.d.ts +302 -0
- package/dist/engine/feature-ast/patterns.js +113 -0
- package/dist/engine/feature-ast/render.d.ts +47 -0
- package/dist/engine/feature-ast/render.js +681 -0
- package/dist/engine/feature-ast/source-location.d.ts +17 -0
- package/dist/engine/feature-ast/source-location.js +27 -0
- package/dist/engine/feature-builder-state.d.ts +69 -0
- package/dist/engine/feature-builder-state.js +64 -0
- package/dist/engine/feature-changelog.d.ts +21 -0
- package/dist/engine/feature-changelog.js +76 -0
- package/dist/engine/feature-config-events-jobs.d.ts +58 -0
- package/dist/engine/feature-config-events-jobs.js +318 -0
- package/dist/engine/feature-entity-handlers.d.ts +38 -0
- package/dist/engine/feature-entity-handlers.js +185 -0
- package/dist/engine/feature-manifest.d.ts +73 -0
- package/dist/engine/feature-manifest.js +87 -0
- package/dist/engine/feature-ui-extensions.d.ts +68 -0
- package/dist/engine/feature-ui-extensions.js +488 -0
- package/dist/engine/field-access.d.ts +4 -0
- package/dist/engine/field-access.js +119 -0
- package/dist/engine/field-helpers.d.ts +4 -0
- package/dist/engine/field-helpers.js +28 -0
- package/dist/engine/handler-helpers.d.ts +19 -0
- package/dist/engine/handler-helpers.js +24 -0
- package/dist/engine/hook-helpers.d.ts +4 -0
- package/dist/engine/hook-helpers.js +13 -0
- package/dist/engine/i18n-key.d.ts +2 -0
- package/dist/engine/i18n-key.js +10 -0
- package/dist/engine/index.d.ts +77 -0
- package/dist/engine/index.js +70 -0
- package/dist/engine/instruction-fields.d.ts +2 -0
- package/dist/engine/instruction-fields.js +9 -0
- package/dist/engine/membership-roles.d.ts +5 -0
- package/dist/engine/membership-roles.js +48 -0
- package/dist/engine/object-form.d.ts +4 -0
- package/dist/engine/object-form.js +21 -0
- package/dist/engine/ownership.d.ts +12 -0
- package/dist/engine/ownership.js +339 -0
- package/dist/engine/parse-ref-target.d.ts +6 -0
- package/dist/engine/parse-ref-target.js +21 -0
- package/dist/engine/pattern-library/index.d.ts +2 -0
- package/dist/engine/pattern-library/index.js +3 -0
- package/dist/engine/pattern-library/library.d.ts +14 -0
- package/dist/engine/pattern-library/library.js +92 -0
- package/dist/engine/pattern-library/mixed-schemas.d.ts +16 -0
- package/dist/engine/pattern-library/mixed-schemas.js +569 -0
- package/dist/engine/pattern-library/opaque-schemas.d.ts +7 -0
- package/dist/engine/pattern-library/opaque-schemas.js +115 -0
- package/dist/engine/pattern-library/shared-fields.d.ts +120 -0
- package/dist/engine/pattern-library/shared-fields.js +71 -0
- package/dist/engine/pattern-library/static-schemas.d.ts +19 -0
- package/dist/engine/pattern-library/static-schemas.js +435 -0
- package/dist/engine/pattern-library/types.d.ts +158 -0
- package/dist/engine/pattern-library/types.js +36 -0
- package/dist/engine/personal-data-fields.d.ts +5 -0
- package/dist/engine/personal-data-fields.js +56 -0
- package/dist/engine/pipeline.d.ts +4 -0
- package/dist/engine/pipeline.js +87 -0
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +679 -0
- package/dist/engine/projection-helpers.d.ts +7 -0
- package/dist/engine/projection-helpers.js +61 -0
- package/dist/engine/qualified-name.d.ts +27 -0
- package/dist/engine/qualified-name.js +137 -0
- package/dist/engine/read-claim.d.ts +2 -0
- package/dist/engine/read-claim.js +28 -0
- package/dist/engine/registry-facade.d.ts +3 -0
- package/dist/engine/registry-facade.js +231 -0
- package/dist/engine/registry-ingest.d.ts +14 -0
- package/dist/engine/registry-ingest.js +415 -0
- package/dist/engine/registry-state.d.ts +93 -0
- package/dist/engine/registry-state.js +233 -0
- package/dist/engine/registry-validate.d.ts +25 -0
- package/dist/engine/registry-validate.js +669 -0
- package/dist/engine/registry.d.ts +2 -0
- package/dist/engine/registry.js +52 -0
- package/dist/engine/resolve-config-or-param.d.ts +18 -0
- package/dist/engine/resolve-config-or-param.js +76 -0
- package/dist/engine/role-assignment.d.ts +1 -0
- package/dist/engine/role-assignment.js +58 -0
- package/dist/engine/run-in.d.ts +2 -0
- package/dist/engine/run-in.js +28 -0
- package/dist/engine/run-pipeline.d.ts +25 -0
- package/dist/engine/run-pipeline.js +111 -0
- package/dist/engine/schema-builder.d.ts +8 -0
- package/dist/engine/schema-builder.js +406 -0
- package/dist/engine/screen-filter-ops.d.ts +3 -0
- package/dist/engine/screen-filter-ops.js +55 -0
- package/dist/engine/screen-helpers.d.ts +27 -0
- package/dist/engine/screen-helpers.js +100 -0
- package/dist/engine/soft-delete-cleanup.d.ts +10 -0
- package/dist/engine/soft-delete-cleanup.js +114 -0
- package/dist/engine/state-machine.d.ts +34 -0
- package/dist/engine/state-machine.js +44 -0
- package/dist/engine/steps/_duration-utils.d.ts +1 -0
- package/dist/engine/steps/_duration-utils.js +28 -0
- package/dist/engine/steps/_event-match.d.ts +2 -0
- package/dist/engine/steps/_event-match.js +88 -0
- package/dist/engine/steps/_no-return-guard.d.ts +2 -0
- package/dist/engine/steps/_no-return-guard.js +16 -0
- package/dist/engine/steps/_read-source.d.ts +6 -0
- package/dist/engine/steps/_read-source.js +7 -0
- package/dist/engine/steps/_resolver-utils.d.ts +11 -0
- package/dist/engine/steps/_resolver-utils.js +36 -0
- package/dist/engine/steps/_step-dispatch-constants.d.ts +13 -0
- package/dist/engine/steps/_step-dispatch-constants.js +32 -0
- package/dist/engine/steps/aggregate-append-event.d.ts +10 -0
- package/dist/engine/steps/aggregate-append-event.js +43 -0
- package/dist/engine/steps/aggregate-create.d.ts +6 -0
- package/dist/engine/steps/aggregate-create.js +38 -0
- package/dist/engine/steps/aggregate-update.d.ts +10 -0
- package/dist/engine/steps/aggregate-update.js +38 -0
- package/dist/engine/steps/branch.d.ts +8 -0
- package/dist/engine/steps/branch.js +66 -0
- package/dist/engine/steps/call-feature.d.ts +7 -0
- package/dist/engine/steps/call-feature.js +30 -0
- package/dist/engine/steps/compute.d.ts +2 -0
- package/dist/engine/steps/compute.js +29 -0
- package/dist/engine/steps/describe-workflow-step-error.d.ts +1 -0
- package/dist/engine/steps/describe-workflow-step-error.js +6 -0
- package/dist/engine/steps/for-each.d.ts +9 -0
- package/dist/engine/steps/for-each.js +88 -0
- package/dist/engine/steps/mail-send.d.ts +10 -0
- package/dist/engine/steps/mail-send.js +33 -0
- package/dist/engine/steps/read-find-many.d.ts +10 -0
- package/dist/engine/steps/read-find-many.js +32 -0
- package/dist/engine/steps/read-find-one.d.ts +9 -0
- package/dist/engine/steps/read-find-one.js +41 -0
- package/dist/engine/steps/retry.d.ts +9 -0
- package/dist/engine/steps/retry.js +76 -0
- package/dist/engine/steps/return.d.ts +4 -0
- package/dist/engine/steps/return.js +25 -0
- package/dist/engine/steps/unsafe-projection-delete.d.ts +8 -0
- package/dist/engine/steps/unsafe-projection-delete.js +35 -0
- package/dist/engine/steps/unsafe-projection-upsert.d.ts +8 -0
- package/dist/engine/steps/unsafe-projection-upsert.js +73 -0
- package/dist/engine/steps/wait-for-event.d.ts +8 -0
- package/dist/engine/steps/wait-for-event.js +63 -0
- package/dist/engine/steps/wait.d.ts +6 -0
- package/dist/engine/steps/wait.js +50 -0
- package/dist/engine/steps/webhook-send.d.ts +24 -0
- package/dist/engine/steps/webhook-send.js +38 -0
- package/dist/engine/system-user.d.ts +8 -0
- package/dist/engine/system-user.js +29 -0
- package/dist/engine/tier-resolver-extension.d.ts +62 -0
- package/dist/engine/tier-resolver-extension.js +55 -0
- package/dist/engine/types/config.d.ts +1 -0
- package/dist/engine/types/config.js +1 -0
- package/dist/engine/types/define-handler.d.ts +1 -0
- package/dist/engine/types/define-handler.js +1 -0
- package/dist/engine/types/entity-handlers.d.ts +1 -0
- package/dist/engine/types/entity-handlers.js +1 -0
- package/dist/engine/types/event-type-map.js +1 -0
- package/dist/engine/types/extension-options-map.js +1 -0
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/feature.js +1 -0
- package/dist/engine/types/fields.d.ts +1 -0
- package/dist/engine/types/fields.js +1 -0
- package/dist/engine/types/handlers.d.ts +2 -0
- package/dist/engine/types/handlers.js +1 -0
- package/dist/engine/types/hooks.d.ts +1 -0
- package/dist/engine/types/hooks.js +1 -0
- package/dist/engine/types/http-route.js +1 -0
- package/dist/engine/types/identifiers.js +1 -0
- package/dist/engine/types/index.d.ts +30 -0
- package/dist/engine/types/index.js +10 -0
- package/dist/engine/types/nav.d.ts +1 -0
- package/dist/engine/types/nav.js +1 -0
- package/dist/engine/types/ownership.d.ts +1 -0
- package/dist/engine/types/ownership.js +1 -0
- package/dist/engine/types/projection.d.ts +1 -0
- package/dist/engine/types/projection.js +1 -0
- package/dist/engine/types/relations.js +1 -0
- package/dist/engine/types/screen.d.ts +2 -0
- package/dist/engine/types/screen.js +1 -0
- package/dist/engine/types/step.d.ts +1 -0
- package/dist/engine/types/step.js +1 -0
- package/dist/engine/types/target-ref.js +1 -0
- package/dist/engine/types/tree-node.js +1 -0
- package/dist/engine/types/workspace.d.ts +1 -0
- package/dist/engine/types/workspace.js +1 -0
- package/dist/engine/validate-projection-allowlist.d.ts +10 -0
- package/dist/engine/validate-projection-allowlist.js +164 -0
- package/dist/engine/validation.d.ts +3 -0
- package/dist/engine/validation.js +23 -0
- package/dist/engine/where-rule-lint.d.ts +2 -0
- package/dist/engine/where-rule-lint.js +171 -0
- package/dist/engine/write-handler-qn-extract.d.ts +15 -0
- package/dist/engine/write-handler-qn-extract.js +36 -0
- package/dist/entrypoint/index.d.ts +83 -0
- package/dist/entrypoint/index.js +312 -0
- package/dist/env/_zod-introspect.d.ts +17 -0
- package/dist/env/_zod-introspect.js +35 -0
- package/dist/env/dry-run.d.ts +13 -0
- package/dist/env/dry-run.js +158 -0
- package/dist/env/index.d.ts +98 -0
- package/dist/env/index.js +313 -0
- package/dist/errors/classes.d.ts +109 -0
- package/dist/errors/classes.js +190 -0
- package/dist/errors/field-issue.d.ts +6 -0
- package/dist/errors/field-issue.js +1 -0
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.js +9 -0
- package/dist/errors/kumiko-error.d.ts +25 -0
- package/dist/errors/kumiko-error.js +43 -0
- package/dist/errors/member-resolution.d.ts +2 -0
- package/dist/errors/member-resolution.js +10 -0
- package/dist/errors/reasons.d.ts +23 -0
- package/dist/errors/reasons.js +81 -0
- package/dist/errors/serialize.d.ts +32 -0
- package/dist/errors/serialize.js +76 -0
- package/dist/errors/to-kumiko-error.d.ts +2 -0
- package/dist/errors/to-kumiko-error.js +9 -0
- package/dist/errors/transition-details.d.ts +7 -0
- package/dist/errors/transition-details.js +16 -0
- package/dist/errors/write-error-info.d.ts +20 -0
- package/dist/errors/write-error-info.js +99 -0
- package/dist/errors/zod-bridge.d.ts +3 -0
- package/dist/errors/zod-bridge.js +58 -0
- package/dist/es-ops/context.d.ts +19 -0
- package/dist/es-ops/context.js +70 -0
- package/dist/es-ops/index.d.ts +4 -0
- package/dist/es-ops/index.js +14 -0
- package/dist/es-ops/operations-schema.d.ts +5 -0
- package/dist/es-ops/operations-schema.js +40 -0
- package/dist/es-ops/runner.d.ts +29 -0
- package/dist/es-ops/runner.js +195 -0
- package/dist/es-ops/types.d.ts +92 -0
- package/dist/es-ops/types.js +14 -0
- package/dist/event-store/admin-api.d.ts +17 -0
- package/dist/event-store/admin-api.js +173 -0
- package/dist/event-store/archive.d.ts +14 -0
- package/dist/event-store/archive.js +55 -0
- package/dist/event-store/errors.d.ts +15 -0
- package/dist/event-store/errors.js +49 -0
- package/dist/event-store/event-store.d.ts +38 -0
- package/dist/event-store/event-store.js +310 -0
- package/dist/event-store/events-schema.d.ts +3 -0
- package/dist/event-store/events-schema.js +66 -0
- package/dist/event-store/index.d.ts +12 -0
- package/dist/event-store/index.js +12 -0
- package/dist/event-store/provenance-append.d.ts +13 -0
- package/dist/event-store/provenance-append.js +49 -0
- package/dist/event-store/rebuild-dead-letter.d.ts +25 -0
- package/dist/event-store/rebuild-dead-letter.js +71 -0
- package/dist/event-store/row-to-stored-event.d.ts +17 -0
- package/dist/event-store/row-to-stored-event.js +15 -0
- package/dist/event-store/snapshot.d.ts +26 -0
- package/dist/event-store/snapshot.js +155 -0
- package/dist/event-store/transfer.d.ts +13 -0
- package/dist/event-store/transfer.js +62 -0
- package/dist/event-store/types.d.ts +1 -0
- package/dist/event-store/types.js +1 -0
- package/dist/event-store/upcaster-dead-letter.d.ts +27 -0
- package/dist/event-store/upcaster-dead-letter.js +76 -0
- package/dist/event-store/upcaster.d.ts +14 -0
- package/dist/event-store/upcaster.js +86 -0
- package/dist/files/content-disposition.d.ts +3 -0
- package/dist/files/content-disposition.js +50 -0
- package/dist/files/feature.d.ts +9 -0
- package/dist/files/feature.js +26 -0
- package/dist/files/file-handle.d.ts +7 -0
- package/dist/files/file-handle.js +71 -0
- package/dist/files/file-ref-entity.d.ts +134 -0
- package/dist/files/file-ref-entity.js +32 -0
- package/dist/files/file-ref-table.d.ts +134 -0
- package/dist/files/file-ref-table.js +12 -0
- package/dist/files/file-routes.d.ts +34 -0
- package/dist/files/file-routes.js +433 -0
- package/dist/files/in-memory-provider.d.ts +7 -0
- package/dist/files/in-memory-provider.js +82 -0
- package/dist/files/index.d.ts +17 -0
- package/dist/files/index.js +13 -0
- package/dist/files/local-provider.d.ts +2 -0
- package/dist/files/local-provider.js +128 -0
- package/dist/files/provider-resolver.d.ts +58 -0
- package/dist/files/provider-resolver.js +87 -0
- package/dist/files/storage-tracking.d.ts +20 -0
- package/dist/files/storage-tracking.js +179 -0
- package/dist/files/types.d.ts +59 -0
- package/dist/files/types.js +276 -0
- package/dist/files/zip-stream.d.ts +31 -0
- package/dist/files/zip-stream.js +207 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +1 -0
- package/dist/i18n/index.d.ts +12 -0
- package/dist/i18n/index.js +27 -0
- package/dist/i18n/mail-registry.d.ts +7 -0
- package/dist/i18n/mail-registry.js +34 -0
- package/dist/i18n/request-locale.d.ts +22 -0
- package/dist/i18n/request-locale.js +62 -0
- package/dist/i18n/required-surface-keys.d.ts +33 -0
- package/dist/i18n/required-surface-keys.js +416 -0
- package/dist/i18n/settings-hub-keys.d.ts +14 -0
- package/dist/i18n/settings-hub-keys.js +29 -0
- package/dist/jobs/index.d.ts +2 -0
- package/dist/jobs/index.js +1 -0
- package/dist/jobs/job-runner.d.ts +54 -0
- package/dist/jobs/job-runner.js +1110 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/lifecycle/lifecycle.d.ts +23 -0
- package/dist/lifecycle/lifecycle.js +111 -0
- package/dist/lifecycle/signal-handlers.d.ts +10 -0
- package/dist/lifecycle/signal-handlers.js +40 -0
- package/dist/logging/index.d.ts +4 -0
- package/dist/logging/index.js +2 -0
- package/dist/logging/pino-logger.d.ts +9 -0
- package/dist/logging/pino-logger.js +61 -0
- package/dist/logging/types.js +1 -0
- package/dist/logging/utils.d.ts +8 -0
- package/dist/logging/utils.js +41 -0
- package/dist/migrations/index.d.ts +4 -0
- package/dist/migrations/index.js +8 -0
- package/dist/migrations/kumiko-drift.d.ts +26 -0
- package/dist/migrations/kumiko-drift.js +151 -0
- package/dist/migrations/pending-rebuilds.d.ts +81 -0
- package/dist/migrations/pending-rebuilds.js +163 -0
- package/dist/migrations/pii-event-backfill.d.ts +40 -0
- package/dist/migrations/pii-event-backfill.js +275 -0
- package/dist/migrations/projection-table-index.d.ts +5 -0
- package/dist/migrations/projection-table-index.js +21 -0
- package/dist/observability/console-provider.d.ts +10 -0
- package/dist/observability/console-provider.js +101 -0
- package/dist/observability/context.d.ts +10 -0
- package/dist/observability/context.js +13 -0
- package/dist/observability/escape-hatch-report.d.ts +23 -0
- package/dist/observability/escape-hatch-report.js +92 -0
- package/dist/observability/fallback.d.ts +4 -0
- package/dist/observability/fallback.js +28 -0
- package/dist/observability/ids.d.ts +2 -0
- package/dist/observability/ids.js +22 -0
- package/dist/observability/index.d.ts +15 -0
- package/dist/observability/index.js +15 -0
- package/dist/observability/metric-validator.d.ts +4 -0
- package/dist/observability/metric-validator.js +73 -0
- package/dist/observability/metrics-handle.d.ts +5 -0
- package/dist/observability/metrics-handle.js +102 -0
- package/dist/observability/metrics-wiring.d.ts +18 -0
- package/dist/observability/metrics-wiring.js +21 -0
- package/dist/observability/noop-provider.d.ts +2 -0
- package/dist/observability/noop-provider.js +105 -0
- package/dist/observability/prometheus-meter.d.ts +26 -0
- package/dist/observability/prometheus-meter.js +257 -0
- package/dist/observability/recording-meter.d.ts +31 -0
- package/dist/observability/recording-meter.js +127 -0
- package/dist/observability/recording-tracer.d.ts +32 -0
- package/dist/observability/recording-tracer.js +135 -0
- package/dist/observability/redis-wrapper.d.ts +3 -0
- package/dist/observability/redis-wrapper.js +121 -0
- package/dist/observability/sensitive-filter.d.ts +9 -0
- package/dist/observability/sensitive-filter.js +92 -0
- package/dist/observability/standard-metrics.d.ts +41 -0
- package/dist/observability/standard-metrics.js +237 -0
- package/dist/observability/types/index.js +1 -0
- package/dist/observability/types/metric.js +1 -0
- package/dist/observability/types/provider.js +1 -0
- package/dist/observability/types/span.js +1 -0
- package/dist/pipeline/active-membership.d.ts +12 -0
- package/dist/pipeline/active-membership.js +124 -0
- package/dist/pipeline/append-event-core.d.ts +12 -0
- package/dist/pipeline/append-event-core.js +84 -0
- package/dist/pipeline/auth-claims-resolver.d.ts +9 -0
- package/dist/pipeline/auth-claims-resolver.js +76 -0
- package/dist/pipeline/cascade-handler.d.ts +6 -0
- package/dist/pipeline/cascade-handler.js +85 -0
- package/dist/pipeline/dispatch-batch.d.ts +5 -0
- package/dist/pipeline/dispatch-batch.js +217 -0
- package/dist/pipeline/dispatch-query.d.ts +4 -0
- package/dist/pipeline/dispatch-query.js +141 -0
- package/dist/pipeline/dispatch-shared.d.ts +58 -0
- package/dist/pipeline/dispatch-shared.js +924 -0
- package/dist/pipeline/dispatch-stream.d.ts +3 -0
- package/dist/pipeline/dispatch-stream.js +89 -0
- package/dist/pipeline/dispatch-write.d.ts +6 -0
- package/dist/pipeline/dispatch-write.js +428 -0
- package/dist/pipeline/dispatcher-utils.d.ts +36 -0
- package/dist/pipeline/dispatcher-utils.js +139 -0
- package/dist/pipeline/dispatcher.d.ts +30 -0
- package/dist/pipeline/dispatcher.js +131 -0
- package/dist/pipeline/distributed-lock.d.ts +13 -0
- package/dist/pipeline/distributed-lock.js +42 -0
- package/dist/pipeline/entity-cache.d.ts +7 -0
- package/dist/pipeline/entity-cache.js +68 -0
- package/dist/pipeline/event-consumer-state.d.ts +16 -0
- package/dist/pipeline/event-consumer-state.js +96 -0
- package/dist/pipeline/event-dedup.d.ts +11 -0
- package/dist/pipeline/event-dedup.js +12 -0
- package/dist/pipeline/event-dispatcher-admin.d.ts +46 -0
- package/dist/pipeline/event-dispatcher-admin.js +196 -0
- package/dist/pipeline/event-dispatcher-delivery.d.ts +68 -0
- package/dist/pipeline/event-dispatcher-delivery.js +310 -0
- package/dist/pipeline/event-dispatcher.d.ts +52 -0
- package/dist/pipeline/event-dispatcher.js +523 -0
- package/dist/pipeline/event-retention.d.ts +20 -0
- package/dist/pipeline/event-retention.js +78 -0
- package/dist/pipeline/idempotency.d.ts +19 -0
- package/dist/pipeline/idempotency.js +115 -0
- package/dist/pipeline/index.d.ts +34 -0
- package/dist/pipeline/index.js +19 -0
- package/dist/pipeline/irreversible-operation-gate.d.ts +4 -0
- package/dist/pipeline/irreversible-operation-gate.js +57 -0
- package/dist/pipeline/lifecycle-pipeline.d.ts +29 -0
- package/dist/pipeline/lifecycle-pipeline.js +266 -0
- package/dist/pipeline/member-read-only-transaction.d.ts +3 -0
- package/dist/pipeline/member-read-only-transaction.js +68 -0
- package/dist/pipeline/member-reader.d.ts +7 -0
- package/dist/pipeline/member-reader.js +87 -0
- package/dist/pipeline/msp-rebuild.d.ts +12 -0
- package/dist/pipeline/msp-rebuild.js +163 -0
- package/dist/pipeline/multi-stream-apply-context.d.ts +18 -0
- package/dist/pipeline/multi-stream-apply-context.js +40 -0
- package/dist/pipeline/pending-gap-ranges.d.ts +12 -0
- package/dist/pipeline/pending-gap-ranges.js +99 -0
- package/dist/pipeline/projection-rebuild.d.ts +55 -0
- package/dist/pipeline/projection-rebuild.js +285 -0
- package/dist/pipeline/projection-state.d.ts +9 -0
- package/dist/pipeline/projection-state.js +51 -0
- package/dist/pipeline/projections-runner.d.ts +5 -0
- package/dist/pipeline/projections-runner.js +57 -0
- package/dist/pipeline/redis-keys.d.ts +8 -0
- package/dist/pipeline/redis-keys.js +10 -0
- package/dist/pipeline/system-hooks.d.ts +25 -0
- package/dist/pipeline/system-hooks.js +590 -0
- package/dist/pipeline/system-identity-switch.d.ts +30 -0
- package/dist/pipeline/system-identity-switch.js +248 -0
- package/dist/pipeline/tenant-timezone-cache.d.ts +17 -0
- package/dist/pipeline/tenant-timezone-cache.js +55 -0
- package/dist/pipeline/write-origin.d.ts +10 -0
- package/dist/pipeline/write-origin.js +110 -0
- package/dist/random/generate.d.ts +56 -0
- package/dist/random/generate.js +92 -0
- package/dist/random/index.d.ts +2 -0
- package/dist/random/index.js +2 -0
- package/dist/random/words.d.ts +2 -0
- package/dist/random/words.js +391 -0
- package/dist/rate-limit/bucket.d.ts +14 -0
- package/dist/rate-limit/bucket.js +20 -0
- package/dist/rate-limit/index.d.ts +3 -0
- package/dist/rate-limit/index.js +3 -0
- package/dist/rate-limit/middleware.d.ts +24 -0
- package/dist/rate-limit/middleware.js +101 -0
- package/dist/rate-limit/resolver.d.ts +9 -0
- package/dist/rate-limit/resolver.js +177 -0
- package/dist/redis/client.d.ts +9 -0
- package/dist/redis/client.js +44 -0
- package/dist/redis/index.d.ts +4 -0
- package/dist/redis/index.js +2 -0
- package/dist/redis/pubsub-signal.d.ts +11 -0
- package/dist/redis/pubsub-signal.js +79 -0
- package/dist/schema-cli.d.ts +21 -0
- package/dist/schema-cli.js +438 -0
- package/dist/scripts/codemod/crypto-shredding-testing-move.d.ts +2 -0
- package/dist/scripts/codemod/crypto-shredding-testing-move.js +109 -0
- package/dist/scripts/codemod/migrate-cross-tenant.d.ts +19 -0
- package/dist/scripts/codemod/migrate-cross-tenant.js +266 -0
- package/dist/scripts/codemod/migrate-db-raw.d.ts +23 -0
- package/dist/scripts/codemod/migrate-db-raw.js +345 -0
- package/dist/scripts/codemod/migrate-open-to-all.d.ts +22 -0
- package/dist/scripts/codemod/migrate-open-to-all.js +194 -0
- package/dist/scripts/codemod/pii-personal-migration.d.ts +11 -0
- package/dist/scripts/codemod/pii-personal-migration.js +615 -0
- package/dist/scripts/codemod/unprocessable-error-details-reason.d.ts +2 -0
- package/dist/scripts/codemod/unprocessable-error-details-reason.js +235 -0
- package/dist/search/derive-search-adapter-config.d.ts +3 -0
- package/dist/search/derive-search-adapter-config.js +23 -0
- package/dist/search/in-memory-adapter.d.ts +2 -0
- package/dist/search/in-memory-adapter.js +96 -0
- package/dist/search/index.d.ts +6 -0
- package/dist/search/index.js +8 -0
- package/dist/search/meilisearch-adapter.d.ts +10 -0
- package/dist/search/meilisearch-adapter.js +175 -0
- package/dist/search/purge-subject.d.ts +7 -0
- package/dist/search/purge-subject.js +178 -0
- package/dist/search/reindex-entity.d.ts +18 -0
- package/dist/search/reindex-entity.js +139 -0
- package/dist/search/types.js +1 -0
- package/dist/secrets/dek-cache.d.ts +13 -0
- package/dist/secrets/dek-cache.js +97 -0
- package/dist/secrets/derive-purpose-secret.d.ts +1 -0
- package/dist/secrets/derive-purpose-secret.js +16 -0
- package/dist/secrets/env-master-key-provider.d.ts +6 -0
- package/dist/secrets/env-master-key-provider.js +134 -0
- package/dist/secrets/envelope-cipher.d.ts +8 -0
- package/dist/secrets/envelope-cipher.js +36 -0
- package/dist/secrets/envelope.d.ts +3 -0
- package/dist/secrets/envelope.js +44 -0
- package/dist/secrets/index.d.ts +9 -0
- package/dist/secrets/index.js +9 -0
- package/dist/secrets/leak-guard.d.ts +1 -0
- package/dist/secrets/leak-guard.js +79 -0
- package/dist/secrets/rotation.d.ts +2 -0
- package/dist/secrets/rotation.js +29 -0
- package/dist/secrets/stored-envelope.d.ts +11 -0
- package/dist/secrets/stored-envelope.js +32 -0
- package/dist/secrets/types.d.ts +1 -0
- package/dist/seeding/entity-seed.d.ts +19 -0
- package/dist/seeding/entity-seed.js +12 -0
- package/dist/seeding/index.d.ts +2 -0
- package/dist/seeding/index.js +2 -0
- package/dist/seeding/types.d.ts +7 -0
- package/dist/seeding/types.js +1 -0
- package/dist/stack/db.d.ts +19 -0
- package/dist/stack/db.js +61 -0
- package/dist/stack/drain-event-consumers.d.ts +4 -0
- package/dist/stack/drain-event-consumers.js +48 -0
- package/dist/stack/drain-jobs.d.ts +20 -0
- package/dist/stack/drain-jobs.js +99 -0
- package/dist/stack/event-collector.d.ts +9 -0
- package/dist/stack/event-collector.js +12 -0
- package/dist/stack/index.d.ts +9 -0
- package/dist/stack/index.js +19 -0
- package/dist/stack/push-entity-projection-tables.d.ts +24 -0
- package/dist/stack/push-entity-projection-tables.js +73 -0
- package/dist/stack/redis.d.ts +11 -0
- package/dist/stack/redis.js +38 -0
- package/dist/stack/request-helper.d.ts +35 -0
- package/dist/stack/request-helper.js +144 -0
- package/dist/stack/table-helpers.d.ts +32 -0
- package/dist/stack/table-helpers.js +138 -0
- package/dist/stack/test-stack.d.ts +161 -0
- package/dist/stack/test-stack.js +398 -0
- package/dist/stack/test-users.d.ts +33 -0
- package/dist/stack/test-users.js +29 -0
- package/dist/testing/access-assertions.d.ts +2 -0
- package/dist/testing/access-assertions.js +13 -0
- package/dist/testing/assertions.d.ts +10 -0
- package/dist/testing/assertions.js +22 -0
- package/dist/testing/boot-validator-fixture.d.ts +3 -0
- package/dist/testing/boot-validator-fixture.js +101 -0
- package/dist/testing/closed-connection-error.d.ts +7 -0
- package/dist/testing/closed-connection-error.js +47 -0
- package/dist/testing/db-cleanup.d.ts +9 -0
- package/dist/testing/db-cleanup.js +31 -0
- package/dist/testing/e2e-generator.d.ts +49 -0
- package/dist/testing/e2e-generator.js +377 -0
- package/dist/testing/expect-error.d.ts +2 -0
- package/dist/testing/expect-error.js +22 -0
- package/dist/testing/file-provider-contract.d.ts +2 -0
- package/dist/testing/file-provider-contract.js +102 -0
- package/dist/testing/handler-context.d.ts +4 -0
- package/dist/testing/handler-context.js +72 -0
- package/dist/testing/http-cookies.d.ts +7 -0
- package/dist/testing/http-cookies.js +47 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.js +32 -0
- package/dist/testing/late-bound.d.ts +9 -0
- package/dist/testing/late-bound.js +31 -0
- package/dist/testing/multipart-helper.d.ts +22 -0
- package/dist/testing/multipart-helper.js +77 -0
- package/dist/testing/mutable-master-key-provider.d.ts +7 -0
- package/dist/testing/mutable-master-key-provider.js +35 -0
- package/dist/testing/observability-recorder.d.ts +9 -0
- package/dist/testing/observability-recorder.js +29 -0
- package/dist/testing/real-providers.d.ts +3 -0
- package/dist/testing/real-providers.js +21 -0
- package/dist/testing/seed.d.ts +9 -0
- package/dist/testing/seed.js +21 -0
- package/dist/testing/shared-entities.d.ts +256 -0
- package/dist/testing/shared-entities.js +56 -0
- package/dist/testing/utils.d.ts +1 -0
- package/dist/testing/utils.js +1 -0
- package/dist/testing/wait-for.d.ts +20 -0
- package/dist/testing/wait-for.js +46 -0
- package/dist/testing/without-ambient-temporal.d.ts +5 -0
- package/dist/testing/without-ambient-temporal.js +17 -0
- package/dist/time/boot-tz-warning.d.ts +5 -0
- package/dist/time/boot-tz-warning.js +24 -0
- package/dist/time/geo-tz.js +1 -0
- package/dist/time/iana.d.ts +1 -0
- package/dist/time/iana.js +18 -0
- package/dist/time/index.d.ts +6 -0
- package/dist/time/index.js +15 -0
- package/dist/time/legacy-date.d.ts +5 -0
- package/dist/time/legacy-date.js +15 -0
- package/dist/time/polyfill.d.ts +9 -0
- package/dist/time/polyfill.js +52 -0
- package/dist/time/tz-context.d.ts +13 -0
- package/dist/time/tz-context.js +80 -0
- package/dist/ui-types/app-schema.d.ts +26 -0
- package/dist/ui-types/app-schema.js +9 -0
- package/dist/ui-types/formality.d.ts +2 -0
- package/dist/ui-types/formality.js +5 -0
- package/dist/ui-types/index.d.ts +22 -0
- package/dist/ui-types/index.js +31 -0
- package/dist/ui-types/list-row-meta.d.ts +18 -0
- package/dist/ui-types/list-row-meta.js +65 -0
- package/dist/ui-types/plural.d.ts +12 -0
- package/dist/ui-types/plural.js +57 -0
- package/dist/upgrade-cli.d.ts +11 -0
- package/dist/upgrade-cli.js +532 -0
- package/dist/utils/assert.d.ts +1 -0
- package/dist/utils/assert.js +18 -0
- package/dist/utils/case.d.ts +1 -0
- package/dist/utils/case.js +6 -0
- package/dist/utils/compare.d.ts +1 -0
- package/dist/utils/compare.js +10 -0
- package/dist/utils/env-parse.d.ts +1 -0
- package/dist/utils/env-parse.js +13 -0
- package/dist/utils/ids.d.ts +2 -0
- package/dist/utils/ids.js +23 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/is-plain-object.d.ts +2 -0
- package/dist/utils/is-plain-object.js +4 -0
- package/dist/utils/parse-string-array-json.d.ts +2 -0
- package/dist/utils/parse-string-array-json.js +11 -0
- package/dist/utils/safe-json.d.ts +17 -0
- package/dist/utils/safe-json.js +47 -0
- package/dist/utils/serialization.d.ts +1 -0
- package/dist/utils/serialization.js +13 -0
- package/package.json +274 -93
- package/src/changes.json +6 -0
- package/src/scripts/codemod/pii-personal-migration.ts +5 -5
- package/src/__tests__/anonymous-access.integration.test.ts +0 -513
- package/src/__tests__/consumer-cli.integration.test.ts +0 -172
- package/src/__tests__/derived-fields.integration.test.ts +0 -126
- package/src/__tests__/entity-list-limits.integration.test.ts +0 -84
- package/src/__tests__/entity-permalink-open.integration.test.ts +0 -99
- package/src/__tests__/error-contract.integration.test.ts +0 -436
- package/src/__tests__/field-access.integration.test.ts +0 -273
- package/src/__tests__/fixtures/migrate-cross-tenant/manual-only.expected.ts +0 -44
- package/src/__tests__/fixtures/migrate-cross-tenant/manual-only.input.ts +0 -44
- package/src/__tests__/fixtures/migrate-cross-tenant/rewrite.expected.ts +0 -41
- package/src/__tests__/fixtures/migrate-cross-tenant/rewrite.input.ts +0 -41
- package/src/__tests__/fixtures/migrate-db-raw/global-fetch.expected.ts +0 -9
- package/src/__tests__/fixtures/migrate-db-raw/global-fetch.input.ts +0 -10
- package/src/__tests__/fixtures/migrate-db-raw/global-select.expected.ts +0 -6
- package/src/__tests__/fixtures/migrate-db-raw/global-select.input.ts +0 -12
- package/src/__tests__/fixtures/migrate-db-raw/import-pruning.expected.ts +0 -12
- package/src/__tests__/fixtures/migrate-db-raw/import-pruning.input.ts +0 -12
- package/src/__tests__/fixtures/migrate-db-raw/manual-only.expected.ts +0 -26
- package/src/__tests__/fixtures/migrate-db-raw/manual-only.input.ts +0 -26
- package/src/__tests__/fixtures/migrate-db-raw/own-tenant-const-shorthand.expected.ts +0 -10
- package/src/__tests__/fixtures/migrate-db-raw/own-tenant-const-shorthand.input.ts +0 -11
- package/src/__tests__/fixtures/migrate-db-raw/own-tenant-event.expected.ts +0 -9
- package/src/__tests__/fixtures/migrate-db-raw/own-tenant-event.input.ts +0 -15
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +0 -16
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +0 -16
- package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.expected.ts +0 -12
- package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.input.ts +0 -12
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +0 -7
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +0 -7
- package/src/__tests__/full-stack.integration.test.ts +0 -924
- package/src/__tests__/migrate-cross-tenant-codemod.test.ts +0 -106
- package/src/__tests__/migrate-db-raw-codemod.test.ts +0 -132
- package/src/__tests__/migrate-open-to-all-codemod.test.ts +0 -84
- package/src/__tests__/ownership-where-write-path.integration.test.ts +0 -147
- package/src/__tests__/ownership.integration.test.ts +0 -454
- package/src/__tests__/pii-personal-migration-report-codemod.test.ts +0 -160
- package/src/__tests__/reference-data.integration.test.ts +0 -243
- package/src/__tests__/related-list-parent-filter.integration.test.ts +0 -103
- package/src/__tests__/schema-cli-kms-slots.integration.test.ts +0 -172
- package/src/__tests__/schema-cli-rebuild.integration.test.ts +0 -189
- package/src/__tests__/schema-cli-status.integration.test.ts +0 -96
- package/src/__tests__/schema-cli-temporal-polyfill.test.ts +0 -48
- package/src/__tests__/schema-cli.integration.test.ts +0 -485
- package/src/__tests__/store-table.integration.test.ts +0 -94
- package/src/__tests__/transition-guard.integration.test.ts +0 -342
- package/src/__tests__/unprocessable-error-details-reason-codemod.test.ts +0 -205
- package/src/__tests__/upgrade-cli.test.ts +0 -723
- package/src/api/__tests__/api-constants-completeness.test.ts +0 -63
- package/src/api/__tests__/api.test.ts +0 -869
- package/src/api/__tests__/auth-middleware-anonymous-access-boot.test.ts +0 -40
- package/src/api/__tests__/auth-middleware-transport.test.ts +0 -102
- package/src/api/__tests__/auth-routes-cookie.test.ts +0 -330
- package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +0 -263
- package/src/api/__tests__/auth-routes-mfa-preauth-confirm.test.ts +0 -230
- package/src/api/__tests__/auth-routes-mfa-preauth-enable-start.test.ts +0 -263
- package/src/api/__tests__/auth-routes-mfa-verify.test.ts +0 -221
- package/src/api/__tests__/auth-routes-trusted-proxy.test.ts +0 -164
- package/src/api/__tests__/batch.integration.test.ts +0 -548
- package/src/api/__tests__/body-limit.test.ts +0 -184
- package/src/api/__tests__/client-ip.test.ts +0 -184
- package/src/api/__tests__/csrf-constants-sync.test.ts +0 -20
- package/src/api/__tests__/csrf-middleware.test.ts +0 -97
- package/src/api/__tests__/dispatcher-live.integration.test.ts +0 -292
- package/src/api/__tests__/extra-route-rejection.test.ts +0 -38
- package/src/api/__tests__/extra-routes.integration.test.ts +0 -663
- package/src/api/__tests__/http-cache.test.ts +0 -173
- package/src/api/__tests__/http-route-entry.integration.test.ts +0 -114
- package/src/api/__tests__/http-route-rate-limit.integration.test.ts +0 -148
- package/src/api/__tests__/jwt.test.ts +0 -316
- package/src/api/__tests__/login-rate-limiter-sweep.test.ts +0 -50
- package/src/api/__tests__/metrics-endpoint.test.ts +0 -132
- package/src/api/__tests__/nested-write-ownership.integration.test.ts +0 -268
- package/src/api/__tests__/nested-write.integration.test.ts +0 -210
- package/src/api/__tests__/origin-middleware.integration.test.ts +0 -273
- package/src/api/__tests__/origin-middleware.test.ts +0 -279
- package/src/api/__tests__/pat-route-guard.integration.test.ts +0 -126
- package/src/api/__tests__/pat-scope.test.ts +0 -36
- package/src/api/__tests__/pii-leak-guard.integration.test.ts +0 -112
- package/src/api/__tests__/post-auth-landing.test.ts +0 -100
- package/src/api/__tests__/readiness.test.ts +0 -76
- package/src/api/__tests__/redis-login-rate-limiter.integration.test.ts +0 -138
- package/src/api/__tests__/redis-sse-broker.integration.test.ts +0 -321
- package/src/api/__tests__/request-id-middleware.test.ts +0 -229
- package/src/api/__tests__/request-locale.integration.test.ts +0 -146
- package/src/api/__tests__/schema-route.integration.test.ts +0 -157
- package/src/api/__tests__/server-boot-guards.test.ts +0 -308
- package/src/api/__tests__/server-error-logging.test.ts +0 -342
- package/src/api/__tests__/server-jwt-ttl.test.ts +0 -58
- package/src/api/__tests__/sse-broker.test.ts +0 -267
- package/src/api/__tests__/sse-route.test.ts +0 -245
- package/src/api/__tests__/stream-escape-hatch.integration.test.ts +0 -203
- package/src/api/__tests__/websocket-route.test.ts +0 -552
- package/src/api/anonymous-cookie.ts +0 -61
- package/src/api/api-constants.ts +0 -153
- package/src/api/auth-middleware.ts +0 -694
- package/src/api/auth-routes.ts +0 -1590
- package/src/api/client-ip.ts +0 -131
- package/src/api/csrf-middleware.ts +0 -74
- package/src/api/extra-route.ts +0 -178
- package/src/api/http-cache.ts +0 -122
- package/src/api/index.ts +0 -117
- package/src/api/jwt.ts +0 -277
- package/src/api/observability-middleware.ts +0 -89
- package/src/api/origin-middleware.ts +0 -137
- package/src/api/pat-route-guard.ts +0 -51
- package/src/api/pat-scope.ts +0 -14
- package/src/api/pii-leak-guard.ts +0 -46
- package/src/api/post-auth-landing.ts +0 -105
- package/src/api/readiness.ts +0 -132
- package/src/api/redis-sse-broker.ts +0 -171
- package/src/api/request-context.ts +0 -137
- package/src/api/request-id-middleware.ts +0 -138
- package/src/api/route-registrars.ts +0 -193
- package/src/api/routes.ts +0 -432
- package/src/api/server.ts +0 -1550
- package/src/api/sse-broker.ts +0 -175
- package/src/api/sse-route.ts +0 -74
- package/src/api/tokens.ts +0 -16
- package/src/api/websocket-route.ts +0 -327
- package/src/arg-parser.ts +0 -66
- package/src/auth/__tests__/roles.test.ts +0 -24
- package/src/auth/index.ts +0 -7
- package/src/auth/roles.ts +0 -42
- package/src/bun-db/__tests__/PATTERN.md +0 -72
- package/src/bun-db/__tests__/_helpers.ts +0 -104
- package/src/bun-db/__tests__/batch-methods.integration.test.ts +0 -143
- package/src/bun-db/__tests__/batch-methods.test.ts +0 -21
- package/src/bun-db/__tests__/bun-test-db.ts +0 -19
- package/src/bun-db/__tests__/closed-connection-retry.integration.test.ts +0 -252
- package/src/bun-db/__tests__/coerce-row-plain-date.test.ts +0 -70
- package/src/bun-db/__tests__/coerce-row-temporal.test.ts +0 -41
- package/src/bun-db/__tests__/column-types.integration.test.ts +0 -132
- package/src/bun-db/__tests__/compound-types.integration.test.ts +0 -134
- package/src/bun-db/__tests__/decimal-field.integration.test.ts +0 -81
- package/src/bun-db/__tests__/extract-table-info.test.ts +0 -71
- package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +0 -235
- package/src/bun-db/__tests__/postgres-closed-socket-write.integration.test.ts +0 -87
- package/src/bun-db/__tests__/query-guards.test.ts +0 -111
- package/src/bun-db/__tests__/run-in-savepoint-if-supported.integration.test.ts +0 -47
- package/src/bun-db/__tests__/select-many-retry.integration.test.ts +0 -138
- package/src/bun-db/__tests__/smoke.integration.test.ts +0 -43
- package/src/bun-db/__tests__/sql-expr-brand.test.ts +0 -115
- package/src/bun-db/__tests__/sql-methods.integration.test.ts +0 -231
- package/src/bun-db/__tests__/where-patterns.integration.test.ts +0 -213
- package/src/bun-db/__tests__/write-brand.test.ts +0 -72
- package/src/bun-db/connection.ts +0 -87
- package/src/bun-db/index.ts +0 -36
- package/src/bun-db/query.ts +0 -1394
- package/src/compliance/__tests__/duration-spec.test.ts +0 -72
- package/src/compliance/__tests__/profiles.test.ts +0 -308
- package/src/compliance/__tests__/sub-processors.test.ts +0 -149
- package/src/compliance/duration-spec.ts +0 -44
- package/src/compliance/index.ts +0 -31
- package/src/compliance/override-schema.ts +0 -136
- package/src/compliance/profiles.ts +0 -424
- package/src/compliance/sub-processors.ts +0 -165
- package/src/consumer-cli.ts +0 -134
- package/src/crypto/__tests__/blind-index.test.ts +0 -132
- package/src/crypto/__tests__/event-pii.test.ts +0 -417
- package/src/crypto/__tests__/kek-rotation.integration.test.ts +0 -180
- package/src/crypto/__tests__/kek-source.test.ts +0 -441
- package/src/crypto/__tests__/kms-adapter-contract.ts +0 -134
- package/src/crypto/__tests__/kms-adapter.contract.test.ts +0 -4
- package/src/crypto/__tests__/kms-wiring.test.ts +0 -203
- package/src/crypto/__tests__/pg-kms-adapter.integration.test.ts +0 -154
- package/src/crypto/__tests__/pii-field-encryption.test.ts +0 -403
- package/src/crypto/__tests__/request-kms-cache.test.ts +0 -74
- package/src/crypto/__tests__/subject-id-schema.test.ts +0 -60
- package/src/crypto/__tests__/subject-key-record.test.ts +0 -109
- package/src/crypto/__tests__/subject-resolver.test.ts +0 -250
- package/src/crypto/__tests__/system-event-pii.test.ts +0 -77
- package/src/crypto/blind-index.ts +0 -118
- package/src/crypto/ciphertext-pattern.ts +0 -19
- package/src/crypto/event-pii.ts +0 -99
- package/src/crypto/in-memory-kms-adapter.ts +0 -50
- package/src/crypto/index.ts +0 -84
- package/src/crypto/is-self-pii-field.ts +0 -8
- package/src/crypto/kek-source.ts +0 -180
- package/src/crypto/kms-adapter.ts +0 -51
- package/src/crypto/kms-wiring.ts +0 -208
- package/src/crypto/pg-kms-adapter.ts +0 -303
- package/src/crypto/pii-field-encryption.ts +0 -266
- package/src/crypto/request-kms-cache.ts +0 -38
- package/src/crypto/subject-resolver.ts +0 -232
- package/src/crypto/system-event-pii.ts +0 -57
- package/src/db/__tests__/apply-entity-event-removed-column.integration.test.ts +0 -109
- package/src/db/__tests__/apply-entity-event-tenant.integration.test.ts +0 -159
- package/src/db/__tests__/assert-exists-in.integration.test.ts +0 -120
- package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +0 -190
- package/src/db/__tests__/big-int-field.test.ts +0 -132
- package/src/db/__tests__/blind-index.integration.test.ts +0 -280
- package/src/db/__tests__/build-filter-where.test.ts +0 -50
- package/src/db/__tests__/collect-table-metas.test.ts +0 -188
- package/src/db/__tests__/column-ddl.integration.test.ts +0 -122
- package/src/db/__tests__/compound-types.test.ts +0 -126
- package/src/db/__tests__/config-seed.integration.test.ts +0 -292
- package/src/db/__tests__/connection-close-outage.integration.test.ts +0 -110
- package/src/db/__tests__/connection-options.test.ts +0 -68
- package/src/db/__tests__/cursor.test.ts +0 -42
- package/src/db/__tests__/decimal-field.test.ts +0 -109
- package/src/db/__tests__/dialect-instant.test.ts +0 -47
- package/src/db/__tests__/eagerload.integration.test.ts +0 -374
- package/src/db/__tests__/entity-field-encryption.test.ts +0 -74
- package/src/db/__tests__/entity-table-from-registry.test.ts +0 -79
- package/src/db/__tests__/entity-table-meta-source.test.ts +0 -92
- package/src/db/__tests__/entity-table-types.test.ts +0 -162
- package/src/db/__tests__/event-store-executor-context.pii-roundtrip.test.ts +0 -67
- package/src/db/__tests__/event-store-executor-global-tenancy.integration.test.ts +0 -73
- package/src/db/__tests__/event-store-executor-list.integration.test.ts +0 -791
- package/src/db/__tests__/event-store-executor-money-rehydrate.integration.test.ts +0 -153
- package/src/db/__tests__/event-store-executor-reference-search.integration.test.ts +0 -643
- package/src/db/__tests__/event-store-executor-reference-sort.integration.test.ts +0 -256
- package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +0 -788
- package/src/db/__tests__/event-store-executor.integration.test.ts +0 -844
- package/src/db/__tests__/event-store-system-stream.integration.test.ts +0 -96
- package/src/db/__tests__/feature-table-sources.test.ts +0 -34
- package/src/db/__tests__/implicit-projection-equivalence.integration.test.ts +0 -393
- package/src/db/__tests__/instant-to-driver-temporal.test.ts +0 -21
- package/src/db/__tests__/list-filter-field-access.integration.test.ts +0 -218
- package/src/db/__tests__/list-pagination.test.ts +0 -28
- package/src/db/__tests__/list-row-meta-drift.test.ts +0 -15
- package/src/db/__tests__/located-timestamp.test.ts +0 -203
- package/src/db/__tests__/managed-recreate.integration.test.ts +0 -78
- package/src/db/__tests__/migrate-generator.test.ts +0 -383
- package/src/db/__tests__/migrate-runner.test.ts +0 -118
- package/src/db/__tests__/money.test.ts +0 -291
- package/src/db/__tests__/multi-row-insert.integration.test.ts +0 -88
- package/src/db/__tests__/number-field-fractional.integration.test.ts +0 -58
- package/src/db/__tests__/parent-ref-clause.test.ts +0 -213
- package/src/db/__tests__/parse-auto-verb.test.ts +0 -70
- package/src/db/__tests__/pg-error.test.ts +0 -57
- package/src/db/__tests__/rebuild-marker.test.ts +0 -180
- package/src/db/__tests__/replay-migration-sql.test.ts +0 -401
- package/src/db/__tests__/required-not-null-migration-safety.integration.test.ts +0 -109
- package/src/db/__tests__/run-in-own-transaction.integration.test.ts +0 -166
- package/src/db/__tests__/schema-inspection.test.ts +0 -27
- package/src/db/__tests__/schema-migration.integration.test.ts +0 -354
- package/src/db/__tests__/source-shadow-create.integration.test.ts +0 -59
- package/src/db/__tests__/system-db-view-export.test.ts +0 -107
- package/src/db/__tests__/table-builder-indexes.test.ts +0 -189
- package/src/db/__tests__/table-builder-meta-lockstep.test.ts +0 -224
- package/src/db/__tests__/table-builder-required.test.ts +0 -214
- package/src/db/__tests__/tenant-db-convention-cross-tenant.test.ts +0 -72
- package/src/db/__tests__/tenant-db-count.integration.test.ts +0 -92
- package/src/db/__tests__/tenant-db-declared-unsafe-raw.test.ts +0 -163
- package/src/db/__tests__/tenant-db-global.integration.test.ts +0 -134
- package/src/db/__tests__/tenant-db-global.test.ts +0 -352
- package/src/db/__tests__/tenant-db-no-raw.test.ts +0 -85
- package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +0 -123
- package/src/db/__tests__/tenant-db-where-merge.test.ts +0 -164
- package/src/db/__tests__/tenant-db.integration.test.ts +0 -575
- package/src/db/__tests__/unchecked-system-db.test.ts +0 -259
- package/src/db/__tests__/unique-violation-mapping.integration.test.ts +0 -200
- package/src/db/__tests__/with-systemdb-unsafe-raw-grant.test.ts +0 -71
- package/src/db/api.ts +0 -52
- package/src/db/apply-entity-event.ts +0 -211
- package/src/db/assert-exists-in.ts +0 -52
- package/src/db/blind-index-cleanup.ts +0 -145
- package/src/db/bun-provider.ts +0 -42
- package/src/db/collect-table-metas.ts +0 -98
- package/src/db/compound-types.ts +0 -47
- package/src/db/config-seed.ts +0 -104
- package/src/db/connection.ts +0 -57
- package/src/db/cursor.ts +0 -47
- package/src/db/dialect.ts +0 -544
- package/src/db/eagerload.ts +0 -260
- package/src/db/entity-field-encryption.ts +0 -158
- package/src/db/entity-table-from-registry.ts +0 -31
- package/src/db/entity-table-meta-types.ts +0 -2
- package/src/db/entity-table-meta.ts +0 -491
- package/src/db/event-store-executor-context.ts +0 -510
- package/src/db/event-store-executor-read.ts +0 -916
- package/src/db/event-store-executor-write.ts +0 -920
- package/src/db/event-store-executor.ts +0 -50
- package/src/db/feature-table-sources.ts +0 -35
- package/src/db/index.ts +0 -162
- package/src/db/located-timestamp.ts +0 -118
- package/src/db/migrate-generator.ts +0 -549
- package/src/db/migrate-runner.ts +0 -348
- package/src/db/money.ts +0 -211
- package/src/db/parent-ref-clause.ts +0 -182
- package/src/db/pg-error.ts +0 -74
- package/src/db/postgres-provider.ts +0 -28
- package/src/db/queries/__tests__/event-store-idempotency-index.integration.test.ts +0 -78
- package/src/db/queries/__tests__/shadow-swap.test.ts +0 -43
- package/src/db/queries/__tests__/unsafe-read-retrying.integration.test.ts +0 -115
- package/src/db/queries/advisory-lock.ts +0 -11
- package/src/db/queries/backfill-pii.ts +0 -578
- package/src/db/queries/ddl.ts +0 -45
- package/src/db/queries/entity-read.ts +0 -15
- package/src/db/queries/es-ops.ts +0 -17
- package/src/db/queries/event-consumer.ts +0 -418
- package/src/db/queries/event-store-admin.ts +0 -130
- package/src/db/queries/event-store-transfer.ts +0 -73
- package/src/db/queries/event-store.ts +0 -354
- package/src/db/queries/pii-backfill-state.ts +0 -33
- package/src/db/queries/projection-rebuild.ts +0 -104
- package/src/db/queries/raw-sql.ts +0 -28
- package/src/db/queries/seed-context.ts +0 -62
- package/src/db/queries/shadow-swap.ts +0 -378
- package/src/db/queries/table-ops.ts +0 -11
- package/src/db/queries/test-stack.ts +0 -32
- package/src/db/query-api.ts +0 -24
- package/src/db/query.ts +0 -35
- package/src/db/rebuild-marker.ts +0 -103
- package/src/db/reference-data.ts +0 -73
- package/src/db/render-ddl.ts +0 -57
- package/src/db/replay-migration-sql.ts +0 -257
- package/src/db/schema-inspection.ts +0 -81
- package/src/db/table-builder.ts +0 -692
- package/src/db/tenant-db-runner.ts +0 -23
- package/src/db/tenant-db.ts +0 -727
- package/src/derivatives/__tests__/derivatives-context.integration.test.ts +0 -231
- package/src/derivatives/__tests__/derivatives-context.test.ts +0 -369
- package/src/derivatives/__tests__/field-variants.test.ts +0 -62
- package/src/derivatives/__tests__/variant-key.test.ts +0 -183
- package/src/derivatives/__tests__/variant-route.integration.test.ts +0 -268
- package/src/derivatives/delete-stored-file.ts +0 -86
- package/src/derivatives/derivatives-context.ts +0 -314
- package/src/derivatives/field-variants.ts +0 -20
- package/src/derivatives/index.ts +0 -14
- package/src/derivatives/variant-key.ts +0 -108
- package/src/engine/__tests__/_pipeline-test-utils.ts +0 -23
- package/src/engine/__tests__/auth-claims-registrar.test.ts +0 -74
- package/src/engine/__tests__/boot-validator-action-wiring.test.ts +0 -362
- package/src/engine/__tests__/boot-validator-api-exposure.test.ts +0 -142
- package/src/engine/__tests__/boot-validator-boot-check.test.ts +0 -104
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +0 -381
- package/src/engine/__tests__/boot-validator-detail-for.test.ts +0 -106
- package/src/engine/__tests__/boot-validator-entity-list.test.ts +0 -120
- package/src/engine/__tests__/boot-validator-gdpr-storage.test.ts +0 -85
- package/src/engine/__tests__/boot-validator-i18n-keys.test.ts +0 -153
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +0 -92
- package/src/engine/__tests__/boot-validator-nav-area.test.ts +0 -116
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +0 -994
- package/src/engine/__tests__/boot-validator-projection-list.test.ts +0 -629
- package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +0 -578
- package/src/engine/__tests__/boot-validator-query-refs.test.ts +0 -151
- package/src/engine/__tests__/boot-validator-ref-entity.test.ts +0 -196
- package/src/engine/__tests__/boot-validator-s0-integration.test.ts +0 -172
- package/src/engine/__tests__/boot-validator.test.ts +0 -5984
- package/src/engine/__tests__/build-app-schema-settings-hub.test.ts +0 -246
- package/src/engine/__tests__/build-app-schema-url-prefill.test.ts +0 -222
- package/src/engine/__tests__/build-app-schema.test.ts +0 -1167
- package/src/engine/__tests__/build-config-feature-schema-extension-selector.test.ts +0 -395
- package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +0 -188
- package/src/engine/__tests__/build-config-feature-schema.test.ts +0 -511
- package/src/engine/__tests__/build-target.test.ts +0 -127
- package/src/engine/__tests__/changeset-changes.test.ts +0 -64
- package/src/engine/__tests__/claim-keys.test.ts +0 -274
- package/src/engine/__tests__/codemod-pipeline.test.ts +0 -682
- package/src/engine/__tests__/config-helpers.test.ts +0 -309
- package/src/engine/__tests__/content-collection.test.ts +0 -307
- package/src/engine/__tests__/decimal-field.test.ts +0 -59
- package/src/engine/__tests__/dedupe-features.test.ts +0 -85
- package/src/engine/__tests__/define-feature-entity-mapping.test.ts +0 -53
- package/src/engine/__tests__/define-roles.test.ts +0 -21
- package/src/engine/__tests__/define-workflow.test.ts +0 -79
- package/src/engine/__tests__/describe-workflow-step-error.test.ts +0 -17
- package/src/engine/__tests__/duration-utils.test.ts +0 -16
- package/src/engine/__tests__/effective-features.test.ts +0 -86
- package/src/engine/__tests__/embedded-derived.test.ts +0 -35
- package/src/engine/__tests__/engine.test.ts +0 -2406
- package/src/engine/__tests__/entity-create-caller-id.integration.test.ts +0 -87
- package/src/engine/__tests__/entity-exclude-fields.integration.test.ts +0 -134
- package/src/engine/__tests__/entity-handlers-escape-hatch.test.ts +0 -91
- package/src/engine/__tests__/entity-handlers.test.ts +0 -562
- package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +0 -153
- package/src/engine/__tests__/entity-restore-tenant-isolation.integration.test.ts +0 -168
- package/src/engine/__tests__/entity-write-crosstenant.integration.test.ts +0 -317
- package/src/engine/__tests__/event-helpers.test.ts +0 -68
- package/src/engine/__tests__/event-migration-declarative.test.ts +0 -67
- package/src/engine/__tests__/event-type-map-augmentation.test.ts +0 -24
- package/src/engine/__tests__/extend-entity-projection.test.ts +0 -133
- package/src/engine/__tests__/extends-registrar.test.ts +0 -279
- package/src/engine/__tests__/factories-long-text.test.ts +0 -95
- package/src/engine/__tests__/factories-number-unit.test.ts +0 -23
- package/src/engine/__tests__/factories-personal.test.ts +0 -216
- package/src/engine/__tests__/factories-time.test.ts +0 -94
- package/src/engine/__tests__/feature-crud-shorthand.test.ts +0 -73
- package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +0 -83
- package/src/engine/__tests__/feature-manifest.test.ts +0 -137
- package/src/engine/__tests__/field-access.test.ts +0 -65
- package/src/engine/__tests__/field-predicates.test.ts +0 -48
- package/src/engine/__tests__/form-money-currency-types.test.ts +0 -90
- package/src/engine/__tests__/hook-identity-switch-grant.test.ts +0 -188
- package/src/engine/__tests__/hook-phases.test.ts +0 -172
- package/src/engine/__tests__/http-route-anonymous-required.test.ts +0 -43
- package/src/engine/__tests__/identifiers.test.ts +0 -35
- package/src/engine/__tests__/lifecycle-hooks.test.ts +0 -243
- package/src/engine/__tests__/membership-roles.test.ts +0 -67
- package/src/engine/__tests__/metric-navigate-tab.test.ts +0 -167
- package/src/engine/__tests__/multiselect-filter.integration.test.ts +0 -141
- package/src/engine/__tests__/nav.test.ts +0 -497
- package/src/engine/__tests__/no-return-guard.test.ts +0 -17
- package/src/engine/__tests__/ownership.test.ts +0 -475
- package/src/engine/__tests__/parse-ref-target.test.ts +0 -43
- package/src/engine/__tests__/pipeline-engine.test.ts +0 -215
- package/src/engine/__tests__/pipeline-handler.integration.test.ts +0 -880
- package/src/engine/__tests__/pipeline-observability.integration.test.ts +0 -142
- package/src/engine/__tests__/pipeline-performance.integration.test.ts +0 -152
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +0 -288
- package/src/engine/__tests__/post-query-hook.test.ts +0 -144
- package/src/engine/__tests__/project-app-schema-for-roles.test.ts +0 -500
- package/src/engine/__tests__/projection-detail-actions.test.ts +0 -145
- package/src/engine/__tests__/projection-detail-singleton.test.ts +0 -86
- package/src/engine/__tests__/projection-detail-tabs.test.ts +0 -314
- package/src/engine/__tests__/projection-helpers.test.ts +0 -75
- package/src/engine/__tests__/projection-list-filter.integration.test.ts +0 -115
- package/src/engine/__tests__/projection.test.ts +0 -191
- package/src/engine/__tests__/qualified-name.test.ts +0 -306
- package/src/engine/__tests__/read-steps-tenant-filter.integration.test.ts +0 -215
- package/src/engine/__tests__/registrar-object-form.test.ts +0 -141
- package/src/engine/__tests__/registry.test.ts +0 -326
- package/src/engine/__tests__/required-surface-keys.test.ts +0 -477
- package/src/engine/__tests__/resolve-config-or-param.test.ts +0 -315
- package/src/engine/__tests__/role-assignment.test.ts +0 -71
- package/src/engine/__tests__/row-action-navigate-tab.test.ts +0 -107
- package/src/engine/__tests__/run-in.test.ts +0 -38
- package/src/engine/__tests__/schema-builder-date-bounds.test.ts +0 -47
- package/src/engine/__tests__/schema-builder.test.ts +0 -1211
- package/src/engine/__tests__/screen-definition-exports.test.ts +0 -102
- package/src/engine/__tests__/screen-filter-ops.test.ts +0 -31
- package/src/engine/__tests__/screen-helpers.test.ts +0 -52
- package/src/engine/__tests__/screen.test.ts +0 -1684
- package/src/engine/__tests__/search-payload-extension.test.ts +0 -288
- package/src/engine/__tests__/soft-delete-cleanup-job-tenant-db.integration.test.ts +0 -82
- package/src/engine/__tests__/soft-delete-cleanup.test.ts +0 -167
- package/src/engine/__tests__/state-machine.test.ts +0 -148
- package/src/engine/__tests__/steps-aggregate-append-event.test.ts +0 -115
- package/src/engine/__tests__/steps-aggregate-create.test.ts +0 -92
- package/src/engine/__tests__/steps-aggregate-update.test.ts +0 -127
- package/src/engine/__tests__/steps-call-feature.test.ts +0 -123
- package/src/engine/__tests__/steps-mail-send.test.ts +0 -137
- package/src/engine/__tests__/steps-read.test.ts +0 -332
- package/src/engine/__tests__/steps-resolver-utils.test.ts +0 -50
- package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +0 -77
- package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +0 -131
- package/src/engine/__tests__/steps-webhook-send.test.ts +0 -143
- package/src/engine/__tests__/steps-workflow.test.ts +0 -353
- package/src/engine/__tests__/store-table.test.ts +0 -247
- package/src/engine/__tests__/system-user.test.ts +0 -57
- package/src/engine/__tests__/tier-resolver-extension.test.ts +0 -69
- package/src/engine/__tests__/toolbar-navigate-tab.test.ts +0 -233
- package/src/engine/__tests__/tree-actions-patterns.test.ts +0 -163
- package/src/engine/__tests__/validate-custom-screen-write-qns.test.ts +0 -43
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +0 -498
- package/src/engine/__tests__/validation-hooks.test.ts +0 -77
- package/src/engine/__tests__/where-rule-lint.test.ts +0 -79
- package/src/engine/__tests__/write-handler-qn-extract.test.ts +0 -50
- package/src/engine/access.ts +0 -27
- package/src/engine/active-membership.ts +0 -70
- package/src/engine/boot-validator/__tests__/access-declarations.test.ts +0 -707
- package/src/engine/boot-validator/__tests__/access-roles.test.ts +0 -84
- package/src/engine/boot-validator/__tests__/action-icon.test.ts +0 -81
- package/src/engine/boot-validator/__tests__/anonymous-rate-limit-required.test.ts +0 -63
- package/src/engine/boot-validator/__tests__/config-deps.test.ts +0 -92
- package/src/engine/boot-validator/__tests__/global-tenancy.test.ts +0 -73
- package/src/engine/boot-validator/__tests__/i18n-keys.test.ts +0 -340
- package/src/engine/boot-validator/__tests__/nav.test.ts +0 -290
- package/src/engine/boot-validator/__tests__/no-all-role-in-handler-access.test.ts +0 -77
- package/src/engine/boot-validator/__tests__/ownership.test.ts +0 -87
- package/src/engine/boot-validator/__tests__/parent-ref.test.ts +0 -161
- package/src/engine/boot-validator/__tests__/rate-limit-disabled-reason.test.ts +0 -45
- package/src/engine/boot-validator/__tests__/record-owned.test.ts +0 -65
- package/src/engine/boot-validator/__tests__/security-baseline.test.ts +0 -107
- package/src/engine/boot-validator/__tests__/transfer-graph.test.ts +0 -138
- package/src/engine/boot-validator/access-declarations.ts +0 -211
- package/src/engine/boot-validator/access-roles.ts +0 -85
- package/src/engine/boot-validator/action-wiring.ts +0 -163
- package/src/engine/boot-validator/api-ext.ts +0 -81
- package/src/engine/boot-validator/boot-check.ts +0 -21
- package/src/engine/boot-validator/config-deps.ts +0 -246
- package/src/engine/boot-validator/custom-screen-write-qns.ts +0 -87
- package/src/engine/boot-validator/detail-screens.ts +0 -49
- package/src/engine/boot-validator/entity-handler.ts +0 -1011
- package/src/engine/boot-validator/entity-list-screens.ts +0 -100
- package/src/engine/boot-validator/gdpr-storage.ts +0 -59
- package/src/engine/boot-validator/global-tenancy.ts +0 -29
- package/src/engine/boot-validator/i18n-keys.ts +0 -101
- package/src/engine/boot-validator/index.ts +0 -301
- package/src/engine/boot-validator/nav.ts +0 -241
- package/src/engine/boot-validator/ownership.ts +0 -352
- package/src/engine/boot-validator/parent-ref.ts +0 -96
- package/src/engine/boot-validator/pii-retention.ts +0 -272
- package/src/engine/boot-validator/projection-list-screens.ts +0 -293
- package/src/engine/boot-validator/query-output-columns.ts +0 -273
- package/src/engine/boot-validator/query-refs.ts +0 -141
- package/src/engine/boot-validator/record-owned.ts +0 -21
- package/src/engine/boot-validator/screens.ts +0 -2604
- package/src/engine/boot-validator/security-baseline.ts +0 -26
- package/src/engine/boot-validator/transfer-graph.ts +0 -135
- package/src/engine/boot-validator/url-prefill-fields.ts +0 -178
- package/src/engine/boot-validator/workspaces.ts +0 -106
- package/src/engine/boot-validator/zod-shape.ts +0 -142
- package/src/engine/boot-validator.ts +0 -8
- package/src/engine/build-app-schema.ts +0 -542
- package/src/engine/build-config-feature-schema.ts +0 -815
- package/src/engine/build-target.ts +0 -99
- package/src/engine/changeset-changes.ts +0 -117
- package/src/engine/codemod/index.ts +0 -15
- package/src/engine/codemod/pipeline-codemod.ts +0 -641
- package/src/engine/config-helpers.ts +0 -217
- package/src/engine/constants.ts +0 -117
- package/src/engine/create-app.ts +0 -177
- package/src/engine/cross-tenant.test.ts +0 -32
- package/src/engine/cross-tenant.ts +0 -22
- package/src/engine/dedupe-features.ts +0 -46
- package/src/engine/define-feature.ts +0 -169
- package/src/engine/define-handler.ts +0 -194
- package/src/engine/define-roles.ts +0 -19
- package/src/engine/define-step.ts +0 -28
- package/src/engine/define-workflow.ts +0 -147
- package/src/engine/effective-features.ts +0 -97
- package/src/engine/embedded-derived.ts +0 -93
- package/src/engine/entity-handlers.ts +0 -782
- package/src/engine/escape-hatch-declaration.ts +0 -7
- package/src/engine/event-helpers.ts +0 -73
- package/src/engine/extension-names.ts +0 -242
- package/src/engine/extension-selector-plugins.ts +0 -24
- package/src/engine/extensions/__tests__/hook-ctx-db-tenant-scoped.test.ts +0 -76
- package/src/engine/extensions/escape-hatch-usage.ts +0 -16
- package/src/engine/extensions/storage-provider.ts +0 -16
- package/src/engine/extensions/tenant-data.ts +0 -34
- package/src/engine/extensions/tenant-resource.ts +0 -40
- package/src/engine/extensions/user-data.ts +0 -181
- package/src/engine/factories.ts +0 -559
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +0 -416
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-name-const/constants.ts +0 -1
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-name-const/feature.ts +0 -8
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-patch-const/constants.ts +0 -2
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-patch-const/feature.ts +0 -8
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +0 -9
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +0 -4
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/alias-reexport.ts +0 -1
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/cycle-a.ts +0 -1
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/cycle-b.ts +0 -1
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/entry.ts +0 -3
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/local-rebind.ts +0 -3
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/origin.ts +0 -1
- package/src/engine/feature-ast/__tests__/fixtures/resolve-import/star-barrel.ts +0 -1
- package/src/engine/feature-ast/__tests__/handler-header-roundtrip.test.ts +0 -669
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +0 -196
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +0 -138
- package/src/engine/feature-ast/__tests__/parse.test.ts +0 -2841
- package/src/engine/feature-ast/__tests__/patch-update.test.ts +0 -718
- package/src/engine/feature-ast/__tests__/patch.test.ts +0 -780
- package/src/engine/feature-ast/__tests__/patcher.test.ts +0 -468
- package/src/engine/feature-ast/__tests__/pattern-change-schema.test.ts +0 -819
- package/src/engine/feature-ast/__tests__/read-optional-access-rule.test.ts +0 -42
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +0 -947
- package/src/engine/feature-ast/__tests__/resolve-import.test.ts +0 -138
- package/src/engine/feature-ast/entity-field-types.ts +0 -41
- package/src/engine/feature-ast/extractors/ai-steps.ts +0 -352
- package/src/engine/feature-ast/extractors/events.ts +0 -349
- package/src/engine/feature-ast/extractors/handlers.ts +0 -459
- package/src/engine/feature-ast/extractors/hooks.ts +0 -331
- package/src/engine/feature-ast/extractors/index.ts +0 -92
- package/src/engine/feature-ast/extractors/jobs-routes.ts +0 -225
- package/src/engine/feature-ast/extractors/projections-screens.ts +0 -269
- package/src/engine/feature-ast/extractors/resolve-import.ts +0 -187
- package/src/engine/feature-ast/extractors/round1.ts +0 -150
- package/src/engine/feature-ast/extractors/round2.ts +0 -276
- package/src/engine/feature-ast/extractors/round3.ts +0 -474
- package/src/engine/feature-ast/extractors/round5.ts +0 -114
- package/src/engine/feature-ast/extractors/round6.ts +0 -32
- package/src/engine/feature-ast/extractors/shared.ts +0 -356
- package/src/engine/feature-ast/index.ts +0 -113
- package/src/engine/feature-ast/parse.ts +0 -615
- package/src/engine/feature-ast/patch.ts +0 -979
- package/src/engine/feature-ast/patcher.ts +0 -546
- package/src/engine/feature-ast/pattern-change-schema.ts +0 -1411
- package/src/engine/feature-ast/patterns.ts +0 -803
- package/src/engine/feature-ast/render.ts +0 -767
- package/src/engine/feature-ast/source-location.ts +0 -45
- package/src/engine/feature-builder-state.ts +0 -174
- package/src/engine/feature-changelog.ts +0 -95
- package/src/engine/feature-config-events-jobs.ts +0 -472
- package/src/engine/feature-entity-handlers.ts +0 -269
- package/src/engine/feature-manifest.ts +0 -173
- package/src/engine/feature-ui-extensions.ts +0 -648
- package/src/engine/field-access.ts +0 -140
- package/src/engine/field-helpers.ts +0 -35
- package/src/engine/handler-helpers.ts +0 -26
- package/src/engine/hook-helpers.ts +0 -16
- package/src/engine/i18n-key.ts +0 -12
- package/src/engine/index.ts +0 -585
- package/src/engine/instruction-fields.ts +0 -11
- package/src/engine/membership-roles.ts +0 -61
- package/src/engine/object-form.ts +0 -27
- package/src/engine/ownership.ts +0 -404
- package/src/engine/parse-ref-target.ts +0 -28
- package/src/engine/pattern-library/__tests__/library.test.ts +0 -399
- package/src/engine/pattern-library/index.ts +0 -24
- package/src/engine/pattern-library/library.ts +0 -140
- package/src/engine/pattern-library/mixed-schemas.ts +0 -588
- package/src/engine/pattern-library/opaque-schemas.ts +0 -124
- package/src/engine/pattern-library/shared-fields.ts +0 -79
- package/src/engine/pattern-library/static-schemas.ts +0 -456
- package/src/engine/pattern-library/types.ts +0 -255
- package/src/engine/personal-data-fields.ts +0 -66
- package/src/engine/pipeline.ts +0 -111
- package/src/engine/project-app-schema-for-roles.ts +0 -950
- package/src/engine/projection-helpers.ts +0 -79
- package/src/engine/qualified-name.ts +0 -165
- package/src/engine/read-claim.ts +0 -31
- package/src/engine/registry-facade.ts +0 -381
- package/src/engine/registry-ingest.ts +0 -479
- package/src/engine/registry-state.ts +0 -410
- package/src/engine/registry-validate.ts +0 -810
- package/src/engine/registry.ts +0 -94
- package/src/engine/resolve-config-or-param.ts +0 -157
- package/src/engine/role-assignment.ts +0 -63
- package/src/engine/run-in.ts +0 -29
- package/src/engine/run-pipeline.ts +0 -162
- package/src/engine/schema-builder.ts +0 -428
- package/src/engine/screen-filter-ops.ts +0 -60
- package/src/engine/screen-helpers.ts +0 -161
- package/src/engine/soft-delete-cleanup.ts +0 -126
- package/src/engine/state-machine.ts +0 -71
- package/src/engine/steps/__tests__/duration-utils.test.ts +0 -20
- package/src/engine/steps/_duration-utils.ts +0 -35
- package/src/engine/steps/_event-match.ts +0 -98
- package/src/engine/steps/_no-return-guard.ts +0 -21
- package/src/engine/steps/_read-source.ts +0 -12
- package/src/engine/steps/_resolver-utils.ts +0 -42
- package/src/engine/steps/_step-dispatch-constants.ts +0 -38
- package/src/engine/steps/aggregate-append-event.ts +0 -56
- package/src/engine/steps/aggregate-create.ts +0 -56
- package/src/engine/steps/aggregate-update.ts +0 -68
- package/src/engine/steps/branch.ts +0 -84
- package/src/engine/steps/call-feature.ts +0 -49
- package/src/engine/steps/compute.ts +0 -41
- package/src/engine/steps/describe-workflow-step-error.ts +0 -6
- package/src/engine/steps/for-each.ts +0 -111
- package/src/engine/steps/mail-send.ts +0 -48
- package/src/engine/steps/read-find-many.ts +0 -57
- package/src/engine/steps/read-find-one.ts +0 -59
- package/src/engine/steps/retry.ts +0 -99
- package/src/engine/steps/return.ts +0 -35
- package/src/engine/steps/unsafe-projection-delete.ts +0 -50
- package/src/engine/steps/unsafe-projection-upsert.ts +0 -88
- package/src/engine/steps/wait-for-event.ts +0 -90
- package/src/engine/steps/wait.ts +0 -69
- package/src/engine/steps/webhook-send.ts +0 -63
- package/src/engine/system-user.ts +0 -38
- package/src/engine/tier-resolver-extension.ts +0 -107
- package/src/engine/types/config.ts +0 -2
- package/src/engine/types/define-handler.ts +0 -2
- package/src/engine/types/entity-handlers.ts +0 -2
- package/src/engine/types/feature.ts +0 -2
- package/src/engine/types/fields.ts +0 -2
- package/src/engine/types/handlers.ts +0 -3
- package/src/engine/types/hooks.ts +0 -2
- package/src/engine/types/index.ts +0 -394
- package/src/engine/types/nav.ts +0 -2
- package/src/engine/types/ownership.ts +0 -2
- package/src/engine/types/projection.ts +0 -2
- package/src/engine/types/screen.ts +0 -3
- package/src/engine/types/step.ts +0 -2
- package/src/engine/types/workspace.ts +0 -2
- package/src/engine/validate-projection-allowlist.ts +0 -191
- package/src/engine/validation.ts +0 -33
- package/src/engine/where-rule-lint.ts +0 -196
- package/src/engine/write-handler-qn-extract.ts +0 -47
- package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +0 -140
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +0 -292
- package/src/entrypoint/__tests__/split-deploy.integration.test.ts +0 -467
- package/src/entrypoint/index.ts +0 -541
- package/src/env/__tests__/compose-env-schema.test.ts +0 -410
- package/src/env/__tests__/dry-run.test.ts +0 -161
- package/src/env/__tests__/parse-env-dry-run.test.ts +0 -40
- package/src/env/_zod-introspect.ts +0 -51
- package/src/env/dry-run.ts +0 -204
- package/src/env/index.ts +0 -435
- package/src/errors/__tests__/classes.test.ts +0 -442
- package/src/errors/__tests__/error-helpers.test.ts +0 -58
- package/src/errors/__tests__/reason-i18n.test.ts +0 -46
- package/src/errors/__tests__/write-failures.test.ts +0 -167
- package/src/errors/classes.ts +0 -311
- package/src/errors/field-issue.ts +0 -8
- package/src/errors/i18n/de.yaml +0 -244
- package/src/errors/i18n/en.yaml +0 -234
- package/src/errors/index.ts +0 -47
- package/src/errors/kumiko-error.ts +0 -67
- package/src/errors/member-resolution.ts +0 -12
- package/src/errors/reasons.ts +0 -102
- package/src/errors/serialize.ts +0 -136
- package/src/errors/to-kumiko-error.ts +0 -8
- package/src/errors/transition-details.ts +0 -30
- package/src/errors/write-error-info.ts +0 -124
- package/src/errors/zod-bridge.ts +0 -62
- package/src/es-ops/README.md +0 -179
- package/src/es-ops/__tests__/context.integration.test.ts +0 -378
- package/src/es-ops/__tests__/runner.integration.test.ts +0 -483
- package/src/es-ops/__tests__/runner.test.ts +0 -202
- package/src/es-ops/context.ts +0 -98
- package/src/es-ops/index.ts +0 -34
- package/src/es-ops/operations-schema.ts +0 -57
- package/src/es-ops/runner.ts +0 -253
- package/src/es-ops/types.ts +0 -124
- package/src/event-store/__tests__/admin-api.integration.test.ts +0 -412
- package/src/event-store/__tests__/backfill-pii.integration.test.ts +0 -880
- package/src/event-store/__tests__/event-attribution.integration.test.ts +0 -236
- package/src/event-store/__tests__/event-store.integration.test.ts +0 -932
- package/src/event-store/__tests__/get-stream-version-perf.integration.test.ts +0 -87
- package/src/event-store/__tests__/perf.integration.test.ts +0 -283
- package/src/event-store/__tests__/provenance-append.integration.test.ts +0 -140
- package/src/event-store/__tests__/row-to-stored-event.test.ts +0 -68
- package/src/event-store/__tests__/snapshot.integration.test.ts +0 -328
- package/src/event-store/__tests__/transfer.integration.test.ts +0 -236
- package/src/event-store/__tests__/unscoped-stream-primitives.guard.test.ts +0 -62
- package/src/event-store/__tests__/upcaster-dead-letter.integration.test.ts +0 -202
- package/src/event-store/__tests__/upcaster.integration.test.ts +0 -491
- package/src/event-store/admin-api.ts +0 -241
- package/src/event-store/archive.ts +0 -83
- package/src/event-store/errors.ts +0 -58
- package/src/event-store/event-store.ts +0 -449
- package/src/event-store/events-schema.ts +0 -89
- package/src/event-store/index.ts +0 -80
- package/src/event-store/provenance-append.ts +0 -67
- package/src/event-store/rebuild-dead-letter.ts +0 -111
- package/src/event-store/row-to-stored-event.ts +0 -34
- package/src/event-store/snapshot.ts +0 -235
- package/src/event-store/transfer.ts +0 -84
- package/src/event-store/types.ts +0 -2
- package/src/event-store/upcaster-dead-letter.ts +0 -120
- package/src/event-store/upcaster.ts +0 -147
- package/src/files/__tests__/build-storage-key.test.ts +0 -28
- package/src/files/__tests__/content-disposition.test.ts +0 -123
- package/src/files/__tests__/file-field-pipeline.integration.test.ts +0 -229
- package/src/files/__tests__/file-handle.test.ts +0 -175
- package/src/files/__tests__/file-ref-entity.test.ts +0 -42
- package/src/files/__tests__/files.integration.test.ts +0 -1749
- package/src/files/__tests__/in-memory-provider.contract.test.ts +0 -4
- package/src/files/__tests__/in-memory-provider.test.ts +0 -94
- package/src/files/__tests__/local-provider.contract.test.ts +0 -14
- package/src/files/__tests__/local-provider.test.ts +0 -31
- package/src/files/__tests__/provider-resolver.test.ts +0 -70
- package/src/files/__tests__/read-stream.test.ts +0 -105
- package/src/files/__tests__/storage-tracking.integration.test.ts +0 -238
- package/src/files/__tests__/storage-tracking.test.ts +0 -46
- package/src/files/__tests__/write-stream.test.ts +0 -250
- package/src/files/__tests__/zip-stream.test.ts +0 -357
- package/src/files/content-disposition.ts +0 -55
- package/src/files/feature.ts +0 -44
- package/src/files/file-handle.ts +0 -83
- package/src/files/file-ref-entity.ts +0 -33
- package/src/files/file-ref-table.ts +0 -13
- package/src/files/file-routes.ts +0 -579
- package/src/files/in-memory-provider.ts +0 -113
- package/src/files/index.ts +0 -57
- package/src/files/local-provider.ts +0 -132
- package/src/files/provider-resolver.ts +0 -176
- package/src/files/storage-tracking.ts +0 -245
- package/src/files/types.ts +0 -336
- package/src/files/zip-stream.ts +0 -251
- package/src/http/index.ts +0 -10
- package/src/i18n/__tests__/i18n.test.ts +0 -113
- package/src/i18n/__tests__/mail-registry.test.ts +0 -54
- package/src/i18n/__tests__/request-locale.test.ts +0 -19
- package/src/i18n/__tests__/required-surface-keys.test.ts +0 -17
- package/src/i18n/index.ts +0 -51
- package/src/i18n/mail-registry.ts +0 -42
- package/src/i18n/request-locale.ts +0 -72
- package/src/i18n/required-surface-keys.ts +0 -459
- package/src/i18n/settings-hub-keys.ts +0 -31
- package/src/jobs/__tests__/job-attempt-context.integration.test.ts +0 -76
- package/src/jobs/__tests__/job-backoff.integration.test.ts +0 -147
- package/src/jobs/__tests__/job-event-trigger.integration.test.ts +0 -175
- package/src/jobs/__tests__/job-last-success.integration.test.ts +0 -135
- package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +0 -147
- package/src/jobs/__tests__/job-public-intake-origin.integration.test.ts +0 -536
- package/src/jobs/__tests__/job-queue-depth.integration.test.ts +0 -82
- package/src/jobs/__tests__/job-retention.integration.test.ts +0 -316
- package/src/jobs/__tests__/job-retry-enqueue-paths.integration.test.ts +0 -309
- package/src/jobs/__tests__/job-runner-boot-timeout.test.ts +0 -55
- package/src/jobs/__tests__/job-systemdb.integration.test.ts +0 -153
- package/src/jobs/__tests__/job-tenant-db.integration.test.ts +0 -162
- package/src/jobs/__tests__/job-write-as.integration.test.ts +0 -251
- package/src/jobs/__tests__/jobs.integration.test.ts +0 -1563
- package/src/jobs/__tests__/scheduler-id.test.ts +0 -30
- package/src/jobs/index.ts +0 -8
- package/src/jobs/job-runner.ts +0 -1390
- package/src/lifecycle/__tests__/create-test-lifecycle.ts +0 -19
- package/src/lifecycle/__tests__/lifecycle-server.integration.test.ts +0 -108
- package/src/lifecycle/__tests__/lifecycle.test.ts +0 -212
- package/src/lifecycle/__tests__/signal-handlers.test.ts +0 -98
- package/src/lifecycle/index.ts +0 -13
- package/src/lifecycle/lifecycle.ts +0 -157
- package/src/lifecycle/signal-handlers.ts +0 -64
- package/src/logging/__tests__/fallback-logger.test.ts +0 -90
- package/src/logging/__tests__/pino-trace-bridge.test.ts +0 -50
- package/src/logging/index.ts +0 -4
- package/src/logging/pino-logger.ts +0 -68
- package/src/logging/utils.ts +0 -50
- package/src/migrations/__tests__/kumiko-drift.integration.test.ts +0 -238
- package/src/migrations/__tests__/kumiko-drift.report.test.ts +0 -105
- package/src/migrations/__tests__/pending-rebuilds.integration.test.ts +0 -412
- package/src/migrations/__tests__/pii-event-backfill.integration.test.ts +0 -457
- package/src/migrations/index.ts +0 -36
- package/src/migrations/kumiko-drift.ts +0 -187
- package/src/migrations/pending-rebuilds.ts +0 -251
- package/src/migrations/pii-event-backfill.ts +0 -422
- package/src/migrations/projection-table-index.ts +0 -23
- package/src/observability/__tests__/console-provider.test.ts +0 -67
- package/src/observability/__tests__/escape-hatch-report.test.ts +0 -149
- package/src/observability/__tests__/metric-validator.test.ts +0 -95
- package/src/observability/__tests__/metrics-handle.test.ts +0 -30
- package/src/observability/__tests__/metrics-wiring.test.ts +0 -61
- package/src/observability/__tests__/noop-provider.test.ts +0 -82
- package/src/observability/__tests__/observability.integration.test.ts +0 -639
- package/src/observability/__tests__/prometheus-meter.test.ts +0 -144
- package/src/observability/__tests__/recording-meter.test.ts +0 -101
- package/src/observability/__tests__/recording-tracer.test.ts +0 -112
- package/src/observability/__tests__/sensitive-filter.test.ts +0 -98
- package/src/observability/console-provider.ts +0 -130
- package/src/observability/context.ts +0 -26
- package/src/observability/escape-hatch-report.ts +0 -129
- package/src/observability/fallback.ts +0 -34
- package/src/observability/ids.ts +0 -25
- package/src/observability/index.ts +0 -88
- package/src/observability/metric-validator.ts +0 -98
- package/src/observability/metrics-handle.ts +0 -105
- package/src/observability/metrics-wiring.ts +0 -32
- package/src/observability/noop-provider.ts +0 -137
- package/src/observability/prometheus-meter.ts +0 -286
- package/src/observability/recording-meter.ts +0 -156
- package/src/observability/recording-tracer.ts +0 -186
- package/src/observability/redis-wrapper.ts +0 -132
- package/src/observability/sensitive-filter.ts +0 -108
- package/src/observability/standard-metrics.ts +0 -295
- package/src/pipeline/__tests__/archive-stream.integration.test.ts +0 -299
- package/src/pipeline/__tests__/auth-claims-resolver.test.ts +0 -279
- package/src/pipeline/__tests__/blind-index-rebuild-guard.integration.test.ts +0 -94
- package/src/pipeline/__tests__/cascade-handler.integration.test.ts +0 -435
- package/src/pipeline/__tests__/cascade-handler.test.ts +0 -67
- package/src/pipeline/__tests__/causation-chain.integration.test.ts +0 -207
- package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +0 -507
- package/src/pipeline/__tests__/ctx-systemdb.integration.test.ts +0 -120
- package/src/pipeline/__tests__/dispatcher-utils.test.ts +0 -132
- package/src/pipeline/__tests__/dispatcher.test.ts +0 -1091
- package/src/pipeline/__tests__/distributed-lock.integration.test.ts +0 -110
- package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +0 -323
- package/src/pipeline/__tests__/escape-hatch-audit.integration.test.ts +0 -254
- package/src/pipeline/__tests__/escape-hatch.integration.test.ts +0 -90
- package/src/pipeline/__tests__/event-consumer-state.integration.test.ts +0 -81
- package/src/pipeline/__tests__/event-dedup.integration.test.ts +0 -153
- package/src/pipeline/__tests__/event-define-event-strict.integration.test.ts +0 -196
- package/src/pipeline/__tests__/event-dispatcher-batch-delivery.test.ts +0 -178
- package/src/pipeline/__tests__/event-dispatcher-batch-handler.integration.test.ts +0 -143
- package/src/pipeline/__tests__/event-dispatcher-commit-order.integration.test.ts +0 -278
- package/src/pipeline/__tests__/event-dispatcher-db-outage.integration.test.ts +0 -224
- package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +0 -127
- package/src/pipeline/__tests__/event-dispatcher-idle.integration.test.ts +0 -94
- package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.test.ts +0 -209
- package/src/pipeline/__tests__/event-dispatcher-multi-instance.integration.test.ts +0 -424
- package/src/pipeline/__tests__/event-dispatcher-pass-failure.integration.test.ts +0 -266
- package/src/pipeline/__tests__/event-dispatcher-per-consumer-turns.integration.test.ts +0 -126
- package/src/pipeline/__tests__/event-dispatcher-pg-listen.integration.test.ts +0 -106
- package/src/pipeline/__tests__/event-dispatcher-rearm.integration.test.ts +0 -263
- package/src/pipeline/__tests__/event-dispatcher-recovery.integration.test.ts +0 -202
- package/src/pipeline/__tests__/event-dispatcher-second-audit.integration.test.ts +0 -282
- package/src/pipeline/__tests__/event-dispatcher-start-from.integration.test.ts +0 -276
- package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +0 -67
- package/src/pipeline/__tests__/event-dispatcher.integration.test.ts +0 -290
- package/src/pipeline/__tests__/event-retention.integration.test.ts +0 -242
- package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +0 -364
- package/src/pipeline/__tests__/hook-systemdb-escape-hatch.integration.test.ts +0 -246
- package/src/pipeline/__tests__/idempotency-transient-failure.integration.test.ts +0 -198
- package/src/pipeline/__tests__/instruction-field-gate.integration.test.ts +0 -213
- package/src/pipeline/__tests__/irreversible-operation-gate.integration.test.ts +0 -249
- package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +0 -235
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +0 -800
- package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +0 -281
- package/src/pipeline/__tests__/member-resolution-read-only.test.ts +0 -79
- package/src/pipeline/__tests__/msp-error-mode.integration.test.ts +0 -149
- package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +0 -231
- package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +0 -510
- package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +0 -313
- package/src/pipeline/__tests__/pending-gap-ranges.test.ts +0 -94
- package/src/pipeline/__tests__/perf-rebuild.integration.test.ts +0 -147
- package/src/pipeline/__tests__/post-query-hook.integration.test.ts +0 -165
- package/src/pipeline/__tests__/projection-rebuild.integration.test.ts +0 -1033
- package/src/pipeline/__tests__/projections-runner-systemscope.integration.test.ts +0 -101
- package/src/pipeline/__tests__/public-intake-runtime-gate.integration.test.ts +0 -684
- package/src/pipeline/__tests__/query-projection.integration.test.ts +0 -357
- package/src/pipeline/__tests__/rate-limit-default.integration.test.ts +0 -118
- package/src/pipeline/__tests__/rate-limit-default.test.ts +0 -117
- package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +0 -275
- package/src/pipeline/__tests__/redis-keys.test.ts +0 -12
- package/src/pipeline/__tests__/redis-pipeline.integration.test.ts +0 -490
- package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +0 -274
- package/src/pipeline/__tests__/system-identity-switch.integration.test.ts +0 -583
- package/src/pipeline/__tests__/system-identity-switch.test.ts +0 -645
- package/src/pipeline/__tests__/tenant-timezone-cache.test.ts +0 -89
- package/src/pipeline/__tests__/try-append-event.integration.test.ts +0 -168
- package/src/pipeline/__tests__/unsafe-raw-escape-hatch.integration.test.ts +0 -103
- package/src/pipeline/active-membership.ts +0 -190
- package/src/pipeline/append-event-core.ts +0 -117
- package/src/pipeline/auth-claims-resolver.ts +0 -103
- package/src/pipeline/cascade-handler.ts +0 -109
- package/src/pipeline/dispatch-batch.ts +0 -269
- package/src/pipeline/dispatch-query.ts +0 -195
- package/src/pipeline/dispatch-shared.ts +0 -1242
- package/src/pipeline/dispatch-stream.ts +0 -129
- package/src/pipeline/dispatch-write.ts +0 -599
- package/src/pipeline/dispatcher-utils.ts +0 -199
- package/src/pipeline/dispatcher.ts +0 -278
- package/src/pipeline/distributed-lock.ts +0 -66
- package/src/pipeline/entity-cache.ts +0 -82
- package/src/pipeline/event-consumer-state.ts +0 -190
- package/src/pipeline/event-dedup.ts +0 -23
- package/src/pipeline/event-dispatcher-admin.ts +0 -320
- package/src/pipeline/event-dispatcher-delivery.ts +0 -499
- package/src/pipeline/event-dispatcher.ts +0 -817
- package/src/pipeline/event-retention.ts +0 -144
- package/src/pipeline/idempotency.ts +0 -158
- package/src/pipeline/index.ts +0 -83
- package/src/pipeline/irreversible-operation-gate.ts +0 -76
- package/src/pipeline/lifecycle-pipeline.ts +0 -421
- package/src/pipeline/member-read-only-transaction.ts +0 -71
- package/src/pipeline/member-reader.ts +0 -110
- package/src/pipeline/msp-rebuild.ts +0 -312
- package/src/pipeline/multi-stream-apply-context.ts +0 -85
- package/src/pipeline/pending-gap-ranges.ts +0 -120
- package/src/pipeline/projection-rebuild.ts +0 -539
- package/src/pipeline/projection-state.ts +0 -60
- package/src/pipeline/projections-runner.ts +0 -66
- package/src/pipeline/redis-keys.ts +0 -11
- package/src/pipeline/system-hooks.ts +0 -695
- package/src/pipeline/system-identity-switch.ts +0 -401
- package/src/pipeline/tenant-timezone-cache.ts +0 -92
- package/src/pipeline/write-origin.ts +0 -128
- package/src/random/__tests__/generate.test.ts +0 -149
- package/src/random/__tests__/words.test.ts +0 -44
- package/src/random/generate.ts +0 -141
- package/src/random/index.ts +0 -8
- package/src/random/words.ts +0 -393
- package/src/rate-limit/__tests__/dispatcher-l3.integration.test.ts +0 -111
- package/src/rate-limit/__tests__/middleware.integration.test.ts +0 -246
- package/src/rate-limit/__tests__/resolver.integration.test.ts +0 -211
- package/src/rate-limit/bucket.ts +0 -36
- package/src/rate-limit/index.ts +0 -14
- package/src/rate-limit/middleware.ts +0 -179
- package/src/rate-limit/resolver.ts +0 -251
- package/src/redis/__tests__/redis-options.test.ts +0 -54
- package/src/redis/client.ts +0 -77
- package/src/redis/index.ts +0 -4
- package/src/redis/pubsub-signal.ts +0 -107
- package/src/schema-cli.ts +0 -510
- package/src/search/__tests__/derive-search-adapter-config.test.ts +0 -65
- package/src/search/__tests__/meilisearch-adapter.integration.test.ts +0 -413
- package/src/search/__tests__/meilisearch-ids.test.ts +0 -30
- package/src/search/__tests__/reindex-entity.integration.test.ts +0 -243
- package/src/search/__tests__/search-adapter.test.ts +0 -271
- package/src/search/__tests__/search-consumer-batch.integration.test.ts +0 -306
- package/src/search/__tests__/search-pii-derived-index.integration.test.ts +0 -208
- package/src/search/derive-search-adapter-config.ts +0 -27
- package/src/search/in-memory-adapter.ts +0 -129
- package/src/search/index.ts +0 -20
- package/src/search/meilisearch-adapter.ts +0 -213
- package/src/search/purge-subject.ts +0 -253
- package/src/search/reindex-entity.ts +0 -190
- package/src/secrets/__tests__/contains-secret.test.ts +0 -108
- package/src/secrets/__tests__/dek-cache.test.ts +0 -211
- package/src/secrets/__tests__/derive-purpose-secret.test.ts +0 -52
- package/src/secrets/__tests__/env-master-key-provider.test.ts +0 -119
- package/src/secrets/__tests__/envelope-cipher.test.ts +0 -59
- package/src/secrets/__tests__/envelope.test.ts +0 -74
- package/src/secrets/__tests__/leak-guard.test.ts +0 -104
- package/src/secrets/__tests__/rotation.test.ts +0 -149
- package/src/secrets/dek-cache.ts +0 -137
- package/src/secrets/derive-purpose-secret.ts +0 -17
- package/src/secrets/env-master-key-provider.ts +0 -162
- package/src/secrets/envelope-cipher.ts +0 -53
- package/src/secrets/envelope.ts +0 -57
- package/src/secrets/index.ts +0 -33
- package/src/secrets/leak-guard.ts +0 -87
- package/src/secrets/rotation.ts +0 -34
- package/src/secrets/stored-envelope.ts +0 -46
- package/src/seeding/__tests__/entity-seed.test.ts +0 -70
- package/src/seeding/entity-seed.ts +0 -31
- package/src/seeding/index.ts +0 -6
- package/src/seeding/types.ts +0 -8
- package/src/stack/__tests__/drain-event-consumers.integration.test.ts +0 -166
- package/src/stack/__tests__/drain-jobs.integration.test.ts +0 -159
- package/src/stack/__tests__/event-collector.test.ts +0 -42
- package/src/stack/__tests__/ownership-boot-guard.integration.test.ts +0 -108
- package/src/stack/__tests__/push-entity-projection-tables-column-sync.integration.test.ts +0 -107
- package/src/stack/__tests__/request-helper.integration.test.ts +0 -140
- package/src/stack/__tests__/request-helper.test.ts +0 -159
- package/src/stack/__tests__/setup-test-stack-entity-tables.integration.test.ts +0 -65
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +0 -185
- package/src/stack/__tests__/setup-test-stack-metrics.integration.test.ts +0 -79
- package/src/stack/db.ts +0 -82
- package/src/stack/drain-event-consumers.ts +0 -74
- package/src/stack/drain-jobs.ts +0 -129
- package/src/stack/event-collector.ts +0 -23
- package/src/stack/index.ts +0 -34
- package/src/stack/push-entity-projection-tables.ts +0 -95
- package/src/stack/redis.ts +0 -55
- package/src/stack/request-helper.ts +0 -298
- package/src/stack/table-helpers.ts +0 -175
- package/src/stack/test-stack.ts +0 -620
- package/src/stack/test-users.ts +0 -37
- package/src/testing/__tests__/db-cleanup.test.ts +0 -40
- package/src/testing/__tests__/e2e-generator.test.ts +0 -349
- package/src/testing/__tests__/ensure-entity-table.integration.test.ts +0 -52
- package/src/testing/__tests__/late-bound.test.ts +0 -32
- package/src/testing/__tests__/multipart-helper.test.ts +0 -49
- package/src/testing/__tests__/real-providers.test.ts +0 -62
- package/src/testing/__tests__/wait-for.test.ts +0 -104
- package/src/testing/access-assertions.ts +0 -15
- package/src/testing/assertions.ts +0 -35
- package/src/testing/boot-validator-fixture.ts +0 -124
- package/src/testing/closed-connection-error.ts +0 -62
- package/src/testing/db-cleanup.ts +0 -45
- package/src/testing/e2e-generator.ts +0 -494
- package/src/testing/expect-error.ts +0 -25
- package/src/testing/file-provider-contract.ts +0 -123
- package/src/testing/handler-context.ts +0 -143
- package/src/testing/http-cookies.ts +0 -52
- package/src/testing/index.ts +0 -60
- package/src/testing/late-bound.ts +0 -41
- package/src/testing/multipart-helper.ts +0 -94
- package/src/testing/mutable-master-key-provider.ts +0 -57
- package/src/testing/observability-recorder.ts +0 -54
- package/src/testing/real-providers.ts +0 -30
- package/src/testing/seed.ts +0 -50
- package/src/testing/shared-entities.ts +0 -65
- package/src/testing/utils.ts +0 -1
- package/src/testing/wait-for.ts +0 -49
- package/src/testing/without-ambient-temporal.ts +0 -14
- package/src/time/__tests__/boot-tz-warning.test.ts +0 -46
- package/src/time/__tests__/geo-tz.test.ts +0 -71
- package/src/time/__tests__/iana.test.ts +0 -35
- package/src/time/__tests__/polyfill-reinstall.test.ts +0 -17
- package/src/time/__tests__/polyfill.test.ts +0 -73
- package/src/time/__tests__/tz-context.test.ts +0 -121
- package/src/time/__tests__/tz-dateline.test.ts +0 -34
- package/src/time/boot-tz-warning.ts +0 -30
- package/src/time/iana.ts +0 -19
- package/src/time/index.ts +0 -23
- package/src/time/legacy-date.ts +0 -18
- package/src/time/polyfill.ts +0 -59
- package/src/time/tz-context.ts +0 -100
- package/src/ui-types/__tests__/plural.test.ts +0 -108
- package/src/ui-types/app-schema.ts +0 -91
- package/src/ui-types/formality.ts +0 -7
- package/src/ui-types/index.ts +0 -159
- package/src/ui-types/list-row-meta.ts +0 -87
- package/src/ui-types/plural.ts +0 -69
- package/src/upgrade-cli.ts +0 -651
- package/src/utils/__tests__/assert.test.ts +0 -17
- package/src/utils/__tests__/case.test.ts +0 -16
- package/src/utils/__tests__/compare.test.ts +0 -20
- package/src/utils/__tests__/env-parse.test.ts +0 -54
- package/src/utils/__tests__/ids.test.ts +0 -52
- package/src/utils/__tests__/is-plain-object.test.ts +0 -16
- package/src/utils/__tests__/parse-string-array-json.test.ts +0 -16
- package/src/utils/__tests__/safe-json-temporal.test.ts +0 -28
- package/src/utils/__tests__/safe-json.test.ts +0 -22
- package/src/utils/__tests__/serialization.test.ts +0 -30
- package/src/utils/assert.ts +0 -18
- package/src/utils/case.ts +0 -6
- package/src/utils/compare.ts +0 -10
- package/src/utils/env-parse.ts +0 -16
- package/src/utils/ids.ts +0 -26
- package/src/utils/index.ts +0 -9
- package/src/utils/is-plain-object.ts +0 -4
- package/src/utils/parse-string-array-json.ts +0 -14
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/serialization.ts +0 -14
- /package/{src/engine/types/event-type-map.ts → dist/engine/types/event-type-map.d.ts} +0 -0
- /package/{src/engine/types/extension-options-map.ts → dist/engine/types/extension-options-map.d.ts} +0 -0
- /package/{src/engine/types/http-route.ts → dist/engine/types/http-route.d.ts} +0 -0
- /package/{src/engine/types/identifiers.ts → dist/engine/types/identifiers.d.ts} +0 -0
- /package/{src/engine/types/relations.ts → dist/engine/types/relations.d.ts} +0 -0
- /package/{src/engine/types/target-ref.ts → dist/engine/types/target-ref.d.ts} +0 -0
- /package/{src/engine/types/tree-node.ts → dist/engine/types/tree-node.d.ts} +0 -0
- /package/{src/logging/types.ts → dist/logging/types.d.ts} +0 -0
- /package/{src/observability/types/index.ts → dist/observability/types/index.d.ts} +0 -0
- /package/{src/observability/types/metric.ts → dist/observability/types/metric.d.ts} +0 -0
- /package/{src/observability/types/provider.ts → dist/observability/types/provider.d.ts} +0 -0
- /package/{src/observability/types/span.ts → dist/observability/types/span.d.ts} +0 -0
- /package/{src/search/types.ts → dist/search/types.d.ts} +0 -0
- /package/{src/secrets/types.ts → dist/secrets/types.js} +0 -0
- /package/{src/time/geo-tz.ts → dist/time/geo-tz.d.ts} +0 -0
|
@@ -1,2604 +0,0 @@
|
|
|
1
|
-
// Screen validation, including the dashboard-panel sub-validators.
|
|
2
|
-
// Dashboard stays here (not a separate dashboard.ts) — validateScreens
|
|
3
|
-
// calls validateDashboardScreen and the dashboard validators call back
|
|
4
|
-
// into validateColumnRendererForm, splitting them would create a
|
|
5
|
-
// same-folder require cycle.
|
|
6
|
-
|
|
7
|
-
import { resolveActionIcon } from "@cosmicdrift/kumiko-types/action-icon";
|
|
8
|
-
import { NO_WIDGET_FIELD_TYPES } from "@cosmicdrift/kumiko-types/fields";
|
|
9
|
-
import type { IconKey } from "@cosmicdrift/kumiko-types/nav-icon";
|
|
10
|
-
import { NAV_ICON_KEYS } from "@cosmicdrift/kumiko-types/nav-icon";
|
|
11
|
-
import { rowMetaFieldNames } from "../../db/table-builder";
|
|
12
|
-
import { LIST_ROW_META_COLUMNS } from "../../ui-types/list-row-meta";
|
|
13
|
-
import { parseRefTarget } from "../parse-ref-target";
|
|
14
|
-
import { isKebabSegment, isValidQn, qualifyEntityName } from "../qualified-name";
|
|
15
|
-
import { getAllowedFilterOps, isFieldFilterable } from "../screen-filter-ops";
|
|
16
|
-
import {
|
|
17
|
-
isExtensionEditSection,
|
|
18
|
-
isWriteFormEditSection,
|
|
19
|
-
normalizeEditField,
|
|
20
|
-
normalizeListColumn,
|
|
21
|
-
resolveNavParentScreen,
|
|
22
|
-
sectionFieldSpecs,
|
|
23
|
-
} from "../screen-helpers";
|
|
24
|
-
import type { EntityDefinition, FeatureDefinition, FieldDefinition } from "../types";
|
|
25
|
-
import { metricField } from "../types";
|
|
26
|
-
import type {
|
|
27
|
-
ActionFormRedirect,
|
|
28
|
-
ActionFormScreenDefinition,
|
|
29
|
-
DashboardCustomPanel,
|
|
30
|
-
DashboardFilterDefinition,
|
|
31
|
-
DashboardPanelDefinition,
|
|
32
|
-
DashboardScreenDefinition,
|
|
33
|
-
DashboardScreenPanel,
|
|
34
|
-
DashboardStatGroupPanel,
|
|
35
|
-
EditFieldSpec,
|
|
36
|
-
EditLayout,
|
|
37
|
-
FieldCondition,
|
|
38
|
-
ListColumnSpec,
|
|
39
|
-
ListFacetSpec,
|
|
40
|
-
RelatedListToolbarAction,
|
|
41
|
-
RowAction,
|
|
42
|
-
RowActionNavigateBase,
|
|
43
|
-
RowFieldExtractor,
|
|
44
|
-
ScreenDefinition,
|
|
45
|
-
SecretMintScreenDefinition,
|
|
46
|
-
ToolbarAction,
|
|
47
|
-
} from "../types/screen";
|
|
48
|
-
|
|
49
|
-
// entityList columns accept exactly the row-meta columns the renderer
|
|
50
|
-
// (computeListViewModel) knows how to type — LIST_ROW_META_COLUMNS, NOT the
|
|
51
|
-
// wider rowMetaFieldNames(softDelete) used below for rowAction payload/visible
|
|
52
|
-
// field refs. Those two checks guard different things: a screen column must
|
|
53
|
-
// be renderable, a payload/visible field reference only needs to exist on the
|
|
54
|
-
// row. Using the wider set here would let a softDelete column like
|
|
55
|
-
// "deletedAt" pass the boot gate and then throw at render-time instead.
|
|
56
|
-
const LIST_ROW_META_COLUMN_NAMES = new Set(Object.keys(LIST_ROW_META_COLUMNS));
|
|
57
|
-
|
|
58
|
-
// entityList and projectionList both allow a rowAction to double as the
|
|
59
|
-
// row-body click target (rowClick: true, fw#1708/#2164) — at most one per
|
|
60
|
-
// screen, or the renderer can't tell which one should fire.
|
|
61
|
-
function validateAtMostOneRowClick(
|
|
62
|
-
featureName: string,
|
|
63
|
-
screenId: string,
|
|
64
|
-
screenType: "entityList" | "projectionList",
|
|
65
|
-
rowActions: readonly RowAction[],
|
|
66
|
-
): void {
|
|
67
|
-
const rowClickActions = rowActions.filter((a) => a.kind === "navigate" && a.rowClick === true);
|
|
68
|
-
if (rowClickActions.length > 1) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has ${rowClickActions.length} ` +
|
|
71
|
-
"rowActions marked rowClick:true — at most one may fire on a row-body click.",
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// A field type in NO_WIDGET_FIELD_TYPES renders read-only on the auto-wired
|
|
77
|
-
// entityEdit path (#1925) — a required field the user can never fill would
|
|
78
|
-
// block every save. Only the statically-resolvable case is caught here: a
|
|
79
|
-
// literal `required: true` (screen-spec override or entity-level default).
|
|
80
|
-
// A dynamic FieldCondition depends on runtime form values and can't be
|
|
81
|
-
// evaluated at boot; buildFormSchema() silently skips presence-checking it.
|
|
82
|
-
function validateNoWidgetRequiredField(
|
|
83
|
-
featureName: string,
|
|
84
|
-
screenId: string,
|
|
85
|
-
entityDef: EntityDefinition,
|
|
86
|
-
fieldSpec: Exclude<EditFieldSpec, string>,
|
|
87
|
-
): void {
|
|
88
|
-
const fieldDef = entityDef.fields[fieldSpec.field];
|
|
89
|
-
// skip: field doesn't exist or its type already has a widget — nothing to validate.
|
|
90
|
-
if (fieldDef === undefined || !NO_WIDGET_FIELD_TYPES.includes(fieldDef.type)) return;
|
|
91
|
-
// Embedded LIST fields (`multiple: true`) get their own EmbeddedListField
|
|
92
|
-
// grid widget (#1838) — only plain (non-list) embedded has no widget.
|
|
93
|
-
const isEmbeddedList = fieldDef.type === "embedded" && fieldDef.multiple === true;
|
|
94
|
-
// skip: list variant has a widget — not the no-widget case this guard targets.
|
|
95
|
-
if (isEmbeddedList) return;
|
|
96
|
-
// skip: already read-only by spec — no fillable widget needed regardless of type.
|
|
97
|
-
if (fieldSpec.readOnly === true) return;
|
|
98
|
-
const entityRequired = "required" in fieldDef && fieldDef.required === true;
|
|
99
|
-
const isStaticallyRequired =
|
|
100
|
-
fieldSpec.required === undefined ? entityRequired : fieldSpec.required === true;
|
|
101
|
-
// skip: not required — a read-only widget-less field is fine to leave empty.
|
|
102
|
-
if (!isStaticallyRequired) return;
|
|
103
|
-
throw new Error(
|
|
104
|
-
`[Feature ${featureName}] Screen "${screenId}" (entityEdit) field "${fieldSpec.field}" is ` +
|
|
105
|
-
`type "${fieldDef.type}", which renders read-only on the auto-wired entityEdit path — a ` +
|
|
106
|
-
`required field the user could never fill would block every save. Set required: false, ` +
|
|
107
|
-
`move the field to a custom-component section, or drop the required constraint.`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function rowFieldExtractorKeys(params: RowFieldExtractor): readonly string[] {
|
|
112
|
-
return "pick" in params ? params.pick : Object.keys(params.map);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Mirrors the renderer's navigate: an explicit entityId, or a same-entity
|
|
116
|
-
// entityEdit target (row["id"] auto-fill), opens UPDATE mode, which ignores params.
|
|
117
|
-
export function readsNavigateParamsAsFormPrefill(
|
|
118
|
-
target: ScreenDefinition,
|
|
119
|
-
explicitEntityId: string | undefined,
|
|
120
|
-
sourceScreenEntity: string | undefined,
|
|
121
|
-
): boolean {
|
|
122
|
-
if (target.type === "actionForm" || target.type === "secretMint") return true;
|
|
123
|
-
if (target.type !== "entityEdit") return false;
|
|
124
|
-
if (explicitEntityId !== undefined) return false;
|
|
125
|
-
return sourceScreenEntity === undefined || target.entity !== sourceScreenEntity;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Tier 2.7e navigate rowAction → target-screen params validity. Shared by
|
|
129
|
-
// entityList and projectionList (framework#1708) — projectionList has no
|
|
130
|
-
// `screen.entity`, so there's no same-entity row["id"] auto-fill case: any
|
|
131
|
-
// entityEdit target without an explicit entityId reaches create there.
|
|
132
|
-
//
|
|
133
|
-
// dashboard targets (framework#1708 follow-up, commit 4e0d6cb26) also read
|
|
134
|
-
// URL search params — but only for the single `filter.id` a dashboard
|
|
135
|
-
// declares (useFilterParams in dashboard-body.tsx seeds its value from
|
|
136
|
-
// `nav.searchParams[filter.id]`). A dashboard with no `filter` has nowhere
|
|
137
|
-
// for the value to land, and a params extractor whose keys don't include
|
|
138
|
-
// the filter's id would silently miss it — both are boot errors instead of
|
|
139
|
-
// a silently-empty dashboard.
|
|
140
|
-
function validateRowActionNavigateParams(
|
|
141
|
-
featureName: string,
|
|
142
|
-
screenId: string,
|
|
143
|
-
screenType: "entityList" | "projectionList" | "projectionDetail" | "entityEdit",
|
|
144
|
-
screenEntity: string | undefined,
|
|
145
|
-
action: RowAction,
|
|
146
|
-
target: { readonly featureName: string; readonly screen: ScreenDefinition } | undefined,
|
|
147
|
-
): void {
|
|
148
|
-
// skip: not a navigate-with-params action — nothing to validate here.
|
|
149
|
-
if (action.kind !== "navigate" || action.params === undefined) return;
|
|
150
|
-
// skip: unresolvable/custom target already reported (or exempt) elsewhere.
|
|
151
|
-
if (target === undefined || target.screen.type === "custom") return;
|
|
152
|
-
// skip: entityList/projectionList targets also read URL search params (Tier
|
|
153
|
-
// 2.7c filter-prefill, see use-list-url-state.ts: `<screenId>.q/.sort/
|
|
154
|
-
// .dir/.page/.f.<field>`), not just actionForm/entityEdit-create.
|
|
155
|
-
if (target.screen.type === "entityList" || target.screen.type === "projectionList") return;
|
|
156
|
-
|
|
157
|
-
if (target.screen.type === "dashboard") {
|
|
158
|
-
const targetDescriptor =
|
|
159
|
-
action.screen !== undefined ? `"${action.screen}"` : `entity "${action.entity}"`;
|
|
160
|
-
const filter = target.screen.filter;
|
|
161
|
-
if (filter === undefined) {
|
|
162
|
-
throw new Error(
|
|
163
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) rowAction "${action.id}" sets ` +
|
|
164
|
-
`params on navigate-target ${targetDescriptor} (dashboard) — target dashboard declares no ` +
|
|
165
|
-
`filter — params would be a no-op. Remove the params extractor, or add a \`filter\` to the ` +
|
|
166
|
-
`target dashboard so it has somewhere to read the value from.`,
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
const extractedKeys = rowFieldExtractorKeys(action.params);
|
|
170
|
-
if (!extractedKeys.includes(filter.id)) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) rowAction "${action.id}" sets ` +
|
|
173
|
-
`params [${extractedKeys.join(", ")}] on navigate-target ${targetDescriptor} (dashboard) whose ` +
|
|
174
|
-
`filter id is "${filter.id}" — none of the extracted keys match, so the filter would stay ` +
|
|
175
|
-
`unset. Fix the params extractor to produce a "${filter.id}" key, or remove it.`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
// skip: filter present and the extractor's keys cover it — valid dashboard deep-link.
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (!readsNavigateParamsAsFormPrefill(target.screen, action.entityId, screenEntity)) {
|
|
183
|
-
const isEntityEditUpdate = target.screen.type === "entityEdit";
|
|
184
|
-
const reason = isEntityEditUpdate
|
|
185
|
-
? `resolves to UPDATE mode (${
|
|
186
|
-
action.entityId !== undefined
|
|
187
|
-
? `explicit entityId "${action.entityId}"`
|
|
188
|
-
: `same entity "${screenEntity}" auto-fills row["id"]`
|
|
189
|
-
})`
|
|
190
|
-
: `screen type "${target.screen.type}"`;
|
|
191
|
-
const targetDescriptor =
|
|
192
|
-
action.screen !== undefined ? `"${action.screen}"` : `entity "${action.entity}"`;
|
|
193
|
-
throw new Error(
|
|
194
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) rowAction "${action.id}" ` +
|
|
195
|
-
`sets params on navigate-target ${targetDescriptor} which ${reason} — only actionForm, ` +
|
|
196
|
-
`secretMint and entityEdit-create targets read URL search params as initial values. Remove ` +
|
|
197
|
-
`the params extractor or retarget to an actionForm / secretMint / cross-entity ` +
|
|
198
|
-
`entityEdit-create screen.`,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Shared by projectionDetail and entityEdit: a tab strip needs something to
|
|
204
|
-
// label each tab with and a stable id per tab — the id anchors the ?tab=
|
|
205
|
-
// param on projectionDetail and the jump-to-erroring-tab logic on entityEdit
|
|
206
|
-
// (fw#3134).
|
|
207
|
-
function validateTabSections(
|
|
208
|
-
featureName: string,
|
|
209
|
-
screenId: string,
|
|
210
|
-
screenType: "projectionDetail" | "entityEdit",
|
|
211
|
-
sections: readonly { readonly id?: string; readonly title?: string }[],
|
|
212
|
-
): void {
|
|
213
|
-
if (sections.length < 2) {
|
|
214
|
-
throw new Error(
|
|
215
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has mode: "tabs" but only ` +
|
|
216
|
-
`${sections.length} section(s) — tabs need at least 2 sections.`,
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
const tabIds = new Set<string>();
|
|
220
|
-
sections.forEach((section, index) => {
|
|
221
|
-
if (section.title === undefined || section.title.trim().length === 0) {
|
|
222
|
-
throw new Error(
|
|
223
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has mode: "tabs" but ` +
|
|
224
|
-
`sections[${index}] has no title — every tab needs a title.`,
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
if (section.id === undefined || section.id.trim().length === 0) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has mode: "tabs" but ` +
|
|
230
|
-
`sections[${index}] ("${section.title}") has no id — every tab needs a stable id.`,
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
if (!isKebabSegment(section.id)) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) sections[${index}] ` +
|
|
236
|
-
`("${section.title}") has id "${section.id}" — must be kebab-case.`,
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
if (tabIds.has(section.id)) {
|
|
240
|
-
throw new Error(
|
|
241
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has duplicate tab id ` +
|
|
242
|
-
`"${section.id}" (sections[${index}]).`,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
tabIds.add(section.id);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Wizard layouts (mode: "wizard") need >= 2 titled sections — a single or
|
|
250
|
-
// untitled step would leave the progress indicator blank, so both fail at
|
|
251
|
-
// boot rather than as a broken step UI.
|
|
252
|
-
function validateWizardLayout(
|
|
253
|
-
featureName: string,
|
|
254
|
-
screenId: string,
|
|
255
|
-
screenType: "entityEdit" | "actionForm" | "configEdit" | "secretMint",
|
|
256
|
-
layout: EditLayout,
|
|
257
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
258
|
-
): void {
|
|
259
|
-
// entityEdit renders tabs like wizard steps: every section stays mounted,
|
|
260
|
-
// submit validates across all of them, and a field error jumps to the tab
|
|
261
|
-
// holding it (fw#3134). The other three have no such jump — actionForm and
|
|
262
|
-
// secretMint are one-shot forms, configEdit fires a write per field — so a
|
|
263
|
-
// required field on a hidden tab would still block their submit silently.
|
|
264
|
-
if (layout.mode === "tabs") {
|
|
265
|
-
if (screenType !== "entityEdit") {
|
|
266
|
-
throw new Error(
|
|
267
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) sets mode: "tabs" — tabs are only ` +
|
|
268
|
-
`supported on projectionDetail and entityEdit. Use mode: "wizard" or "single" instead.`,
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
validateTabSections(featureName, screenId, screenType, layout.sections);
|
|
272
|
-
}
|
|
273
|
-
// "form-draft" is hardcoded because the framework layer must not depend on
|
|
274
|
-
// @cosmicdrift/kumiko-bundled-features — same precedence as the
|
|
275
|
-
// "user-data-rights" check in gdpr-storage.ts.
|
|
276
|
-
if (layout.draft === true) {
|
|
277
|
-
if (layout.mode !== "wizard") {
|
|
278
|
-
throw new Error(
|
|
279
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) sets draft: true but ` +
|
|
280
|
-
`mode is not "wizard" — draft persistence only applies to wizard layouts. Remove ` +
|
|
281
|
-
`draft: true or set mode: "wizard".`,
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
if (!featureMap.has("form-draft")) {
|
|
285
|
-
throw new Error(
|
|
286
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) sets draft: true but the ` +
|
|
287
|
-
`bundled feature "form-draft" is not mounted — every resume would silently lose its ` +
|
|
288
|
-
`values. Add formDraftFeature() from @cosmicdrift/kumiko-bundled-features to the app's ` +
|
|
289
|
-
`feature list.`,
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
// skip: mode omitted/"single" — no wizard constraints apply.
|
|
294
|
-
if (layout.mode !== "wizard") return;
|
|
295
|
-
if (layout.sections.length < 2) {
|
|
296
|
-
throw new Error(
|
|
297
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has mode: "wizard" but only ` +
|
|
298
|
-
`${layout.sections.length} section(s) — a wizard needs at least 2 sections (one per step).`,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
layout.sections.forEach((section, index) => {
|
|
302
|
-
if (section.title === undefined || section.title.trim().length === 0) {
|
|
303
|
-
throw new Error(
|
|
304
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) has mode: "wizard" but ` +
|
|
305
|
-
`sections[${index}] has no title — every wizard step needs a title.`,
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// --- Screen validation ---
|
|
312
|
-
//
|
|
313
|
-
// For every r.screen() declaration check what's locally knowable at boot:
|
|
314
|
-
// - entityList / entityEdit: the referenced entity must exist in the
|
|
315
|
-
// feature (cross-feature entity-refs aren't allowed — a feature owns
|
|
316
|
-
// the screens over its own entities) and every column/field ref must
|
|
317
|
-
// name a real field on that entity
|
|
318
|
-
// - custom: the renderer must at least have one platform component set
|
|
319
|
-
// (react OR native), otherwise the screen is structurally empty
|
|
320
|
-
//
|
|
321
|
-
// Field-level renderer QN strings (cross-feature `component:` references)
|
|
322
|
-
// are NOT validated here — the r.uiComponent registry that would resolve
|
|
323
|
-
// them ships in M4/M5. Until then those are kept opaque on purpose.
|
|
324
|
-
|
|
325
|
-
// every action rendered in a Card title row (screen-level
|
|
326
|
-
// `actions`, section-level `actions`, `emptyState.action`) must draw an
|
|
327
|
-
// icon — declared or id-derived (resolveActionIcon, same resolver the
|
|
328
|
-
// renderer uses). Uncovered here: entityList/projectionList rowActions —
|
|
329
|
-
// those render inside a DataTable row, not a Card title row, so they keep
|
|
330
|
-
// today's "icon optional" behavior.
|
|
331
|
-
//
|
|
332
|
-
// Registration check (not just "resolved is not undefined"): action.icon is
|
|
333
|
-
// typed IconKey, a closed union — but that only protects call sites this
|
|
334
|
-
// repo's own `tsc --build` actually covers (samples and external consumers
|
|
335
|
-
// aren't in that project graph). The renderer's actionIconFor
|
|
336
|
-
// (renderer-web/src/primitives/index.tsx) already falls back to "no icon"
|
|
337
|
-
// for an unregistered key instead of crashing — this check makes the same
|
|
338
|
-
// gap fail loudly at boot instead of silently at render.
|
|
339
|
-
function isRegisteredIcon(icon: IconKey): boolean {
|
|
340
|
-
return (NAV_ICON_KEYS as readonly string[]).includes(icon);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
export function validateActionHasIcon(
|
|
344
|
-
featureName: string,
|
|
345
|
-
screenId: string,
|
|
346
|
-
screenType: "projectionDetail" | "entityEdit",
|
|
347
|
-
context: string,
|
|
348
|
-
action: { readonly id: string; readonly icon?: IconKey },
|
|
349
|
-
): void {
|
|
350
|
-
const resolved = resolveActionIcon(action.id, action.icon);
|
|
351
|
-
if (resolved === undefined || !isRegisteredIcon(resolved)) {
|
|
352
|
-
throw new Error(
|
|
353
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${context} "${action.id}" has no ` +
|
|
354
|
-
`resolvable icon — set icon to a key registered in NAV_ICONS, or use an id that resolves one of ` +
|
|
355
|
-
`its kebab segments via the default icon map (resolveActionIcon).`,
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Tier 2.7e-3: deklarative Feld-Referenzen einer Action gegen die Entity-
|
|
361
|
-
// Felder pinnen — ein Tippfehler in pick/map-Quellfeldern oder
|
|
362
|
-
// visible.field erzeugte sonst still `undefined` im Payload bzw. dauerhaft
|
|
363
|
-
// falsche Sichtbarkeit (gleiche "Typo fällt erst beim Klick"-Klasse wie
|
|
364
|
-
// navigate/handler). Exported for query-output-columns.ts, which reuses it
|
|
365
|
-
// against a projectionDetail's outputSchema-derived record shape instead of
|
|
366
|
-
// an entity's field map (relatedList toolbarActions' visible/params).
|
|
367
|
-
export function validateActionFieldRefs(
|
|
368
|
-
featureName: string,
|
|
369
|
-
screenId: string,
|
|
370
|
-
actionKind: "rowAction" | "toolbarAction",
|
|
371
|
-
actionId: string,
|
|
372
|
-
action: RowAction | ToolbarAction | RelatedListToolbarAction,
|
|
373
|
-
fieldNames: ReadonlySet<string>,
|
|
374
|
-
rowMeta: ReadonlySet<string>,
|
|
375
|
-
): void {
|
|
376
|
-
// ToolbarAction.payload ist ein STATISCHER Record (kein Row-Context) —
|
|
377
|
-
// nur echte pick/map-Extractoren werden gegen die Feldnamen geprüft.
|
|
378
|
-
const isExtractor = (v: unknown): v is RowFieldExtractor =>
|
|
379
|
-
typeof v === "object" && v !== null && ("pick" in v || "map" in v);
|
|
380
|
-
const payload = "payload" in action && isExtractor(action.payload) ? action.payload : undefined;
|
|
381
|
-
const params = "params" in action && isExtractor(action.params) ? action.params : undefined;
|
|
382
|
-
const visible: FieldCondition | undefined = "visible" in action ? action.visible : undefined;
|
|
383
|
-
const entityId: string | undefined = "entityId" in action ? action.entityId : undefined;
|
|
384
|
-
const known = () => [...fieldNames].sort().join(", ") || "(none)";
|
|
385
|
-
const checkExtractor = (label: string, extractor: RowFieldExtractor | undefined): void => {
|
|
386
|
-
// skip: extractor ist ein optionaler Action-Slot — ohne ihn gibt es
|
|
387
|
-
// keine Feld-Referenzen zu validieren.
|
|
388
|
-
if (extractor === undefined) {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
const sources = "pick" in extractor ? extractor.pick : Object.values(extractor.map);
|
|
392
|
-
for (const source of sources) {
|
|
393
|
-
if (rowMeta.has(source)) continue;
|
|
394
|
-
if (!fieldNames.has(source)) {
|
|
395
|
-
throw new Error(
|
|
396
|
-
`[Feature ${featureName}] Screen "${screenId}" ${actionKind} "${actionId}" ` +
|
|
397
|
-
`${label} references unknown field "${source}". Known fields: ${known()}.`,
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
checkExtractor("payload", payload);
|
|
403
|
-
checkExtractor("params", params);
|
|
404
|
-
if (
|
|
405
|
-
visible !== undefined &&
|
|
406
|
-
typeof visible !== "boolean" &&
|
|
407
|
-
!rowMeta.has(visible.field) &&
|
|
408
|
-
!fieldNames.has(visible.field)
|
|
409
|
-
) {
|
|
410
|
-
throw new Error(
|
|
411
|
-
`[Feature ${featureName}] Screen "${screenId}" ${actionKind} "${actionId}" ` +
|
|
412
|
-
`visible.field references unknown field "${visible.field}". Known fields: ${known()}.`,
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
if (entityId !== undefined && entityId !== "id" && !fieldNames.has(entityId)) {
|
|
416
|
-
throw new Error(
|
|
417
|
-
`[Feature ${featureName}] Screen "${screenId}" ${actionKind} "${actionId}" ` +
|
|
418
|
-
`entityId references unknown field "${entityId}". Known fields: ${known()}.`,
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Two features registering the same short screen-id is a silent routing
|
|
424
|
-
// footgun: create-app.tsx's runtime router resolves a bare navigate-target
|
|
425
|
-
// id by scanning features[] and taking the first match — the collision
|
|
426
|
-
// never surfaces as an error, the second feature's screen is just
|
|
427
|
-
// unreachable by that id (and always the same one that loses, in whatever
|
|
428
|
-
// order the app composed its features).
|
|
429
|
-
export function validateScreenShortIdCollisions(
|
|
430
|
-
screensByShortId: ReadonlyMap<
|
|
431
|
-
string,
|
|
432
|
-
ReadonlyArray<{ readonly featureName: string; readonly screen: ScreenDefinition }>
|
|
433
|
-
>,
|
|
434
|
-
): void {
|
|
435
|
-
for (const [shortId, entries] of screensByShortId) {
|
|
436
|
-
const featureNames = new Set(entries.map((e) => e.featureName));
|
|
437
|
-
if (featureNames.size > 1) {
|
|
438
|
-
throw new Error(
|
|
439
|
-
`Screen short-id "${shortId}" is registered by ${featureNames.size} features ` +
|
|
440
|
-
`(${[...featureNames].join(", ")}) — the runtime router resolves a bare navigate-target ` +
|
|
441
|
-
`id by taking the first match, so all but one of these screens would be unreachable by ` +
|
|
442
|
-
`that id. Give each screen a distinct id.`,
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// redirect/cancelTarget accept either a same-feature short id (unchanged
|
|
449
|
-
// behavior, qualified against the owning feature) or a fully-qualified
|
|
450
|
-
// cross-feature screen QN (`<feature>:screen:<id>`) given verbatim — a
|
|
451
|
-
// short id can never itself be a valid QN (QN_SEGMENT forbids colons), so
|
|
452
|
-
// the two forms don't collide.
|
|
453
|
-
export function resolveScreenTargetQn(featureName: string, target: string): string {
|
|
454
|
-
return isValidQn(target) ? target : qualifyEntityName(featureName, "screen", target);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function validateScreenNavTarget(
|
|
458
|
-
featureName: string,
|
|
459
|
-
screenId: string,
|
|
460
|
-
screenKind: string,
|
|
461
|
-
fieldName: string,
|
|
462
|
-
value: string,
|
|
463
|
-
allScreenQns: ReadonlySet<string>,
|
|
464
|
-
screens: FeatureDefinition["screens"],
|
|
465
|
-
): void {
|
|
466
|
-
const candidateQn = resolveScreenTargetQn(featureName, value);
|
|
467
|
-
if (!allScreenQns.has(candidateQn)) {
|
|
468
|
-
throw new Error(
|
|
469
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenKind}) ${fieldName} "${value}" ` +
|
|
470
|
-
`does not resolve to a registered screen (checked "${candidateQn}"). Known screens ` +
|
|
471
|
-
`in this feature: ${[...Object.keys(screens)].sort().join(", ") || "(none)"}.`,
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// kind:"drawer" resolves same-feature only — unlike navigate/redirect,
|
|
477
|
-
// which the runtime router resolves app-wide (see the comment on
|
|
478
|
-
// validateScreens below), the drawer mounts the target inline using this
|
|
479
|
-
// feature's schema, so a cross-feature reference could never actually
|
|
480
|
-
// render. Two distinct failure messages: dangling reference vs. wrong
|
|
481
|
-
// screen type. Shared by ToolbarAction (fw#2225) and RowAction (fw#2710)
|
|
482
|
-
// drawer variants — same target-resolution rule, only the label in the
|
|
483
|
-
// error message ("toolbarAction" vs. "rowAction" vs. "action") differs.
|
|
484
|
-
function validateDrawerTargetAction(
|
|
485
|
-
featureName: string,
|
|
486
|
-
screenId: string,
|
|
487
|
-
screenKind: string,
|
|
488
|
-
actionLabel: "toolbarAction" | "rowAction" | "action",
|
|
489
|
-
action: {
|
|
490
|
-
readonly id: string;
|
|
491
|
-
readonly screen: string;
|
|
492
|
-
readonly params?: RowFieldExtractor;
|
|
493
|
-
},
|
|
494
|
-
screens: FeatureDefinition["screens"],
|
|
495
|
-
): void {
|
|
496
|
-
const target = screens[action.screen];
|
|
497
|
-
if (target === undefined) {
|
|
498
|
-
throw new Error(
|
|
499
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenKind}) ${actionLabel} "${action.id}" ` +
|
|
500
|
-
`drawer-target "${action.screen}" does not resolve to a registered screen in this feature. ` +
|
|
501
|
-
`kind:"drawer" only resolves same-feature screens (unlike kind:"navigate", which can target ` +
|
|
502
|
-
`screens in any feature) — the drawer mounts the target inline using this feature's schema. ` +
|
|
503
|
-
`Known screens in this feature: ${[...Object.keys(screens)].sort().join(", ") || "(none)"}.`,
|
|
504
|
-
);
|
|
505
|
-
}
|
|
506
|
-
// secretMint is intentionally excluded — a one-time secret reveal belongs on
|
|
507
|
-
// its own page, not layered in a Drawer above a list.
|
|
508
|
-
if (target.type !== "actionForm") {
|
|
509
|
-
throw new Error(
|
|
510
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenKind}) ${actionLabel} "${action.id}" ` +
|
|
511
|
-
`drawer-target "${action.screen}" is a "${target.type}" screen, not an actionForm. ` +
|
|
512
|
-
`kind:"drawer" mounts an actionForm inside a Drawer widget — point "screen" at an ` +
|
|
513
|
-
`actionForm screen, or use kind:"navigate" for a full-page target.`,
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
// A prefill key the target form doesn't declare is dropped silently by the
|
|
517
|
-
// renderer (mergeSearchParamsIntoInitial iterates the target's fields), so
|
|
518
|
-
// the typo would only show up as an empty field at click time.
|
|
519
|
-
// skip: no params extractor — nothing to check against the target's fields.
|
|
520
|
-
if (action.params === undefined) return;
|
|
521
|
-
for (const fieldName of rowFieldExtractorKeys(action.params)) {
|
|
522
|
-
if (Object.hasOwn(target.fields, fieldName)) continue;
|
|
523
|
-
throw new Error(
|
|
524
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenKind}) ${actionLabel} "${action.id}" ` +
|
|
525
|
-
`params prefills "${fieldName}", which drawer-target "${action.screen}" does not declare as a ` +
|
|
526
|
-
`field — the renderer would drop it and leave the form empty. Target fields: ` +
|
|
527
|
-
`${Object.keys(target.fields).sort().join(", ") || "(none)"}.`,
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// fw#2228: a navigate rowAction (or projectionDetail header action, which
|
|
533
|
-
// reuses the same RowActionNavigate shape) names its target as either a
|
|
534
|
-
// screen (existing) or an entity (new) — exactly one. Shared by all three
|
|
535
|
-
// call sites so the mutual-exclusivity check and the entity→detailFor
|
|
536
|
-
// resolution don't drift between them (same drift risk
|
|
537
|
-
// validateRowActionNavigateParams above is already shared to avoid).
|
|
538
|
-
// Runtime-loose shape: boot still rejects both/neither for untyped schemas;
|
|
539
|
-
// authors get the exclusive union via RowActionNavigate (#2303).
|
|
540
|
-
type RowActionNavigateRuntime = RowActionNavigateBase & {
|
|
541
|
-
readonly screen?: string;
|
|
542
|
-
readonly entity?: string;
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
// The target can be any screen, so this checks the resolved target's
|
|
546
|
-
// sections, not the host's.
|
|
547
|
-
function validateRowActionNavigateTab(
|
|
548
|
-
featureName: string,
|
|
549
|
-
screenId: string,
|
|
550
|
-
screenType: "entityList" | "projectionList" | "projectionDetail" | "entityEdit",
|
|
551
|
-
actionLabel: string,
|
|
552
|
-
action: RowActionNavigateRuntime,
|
|
553
|
-
target: { readonly featureName: string; readonly screen: ScreenDefinition } | undefined,
|
|
554
|
-
): void {
|
|
555
|
-
// skip: no tab declared — nothing to check against the target's sections
|
|
556
|
-
if (action.tab === undefined) return;
|
|
557
|
-
if (target === undefined) {
|
|
558
|
-
throw new Error(
|
|
559
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
560
|
-
`sets tab "${action.tab}", but its navigate-target could not be resolved to a screen.`,
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
if (target.screen.type !== "projectionDetail" || target.screen.layout.mode !== "tabs") {
|
|
564
|
-
throw new Error(
|
|
565
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
566
|
-
`sets tab "${action.tab}", but navigate-target screen "${target.screen.id}" is not a ` +
|
|
567
|
-
`projectionDetail with layout.mode "tabs" — only those read the tab param.`,
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
const sectionIds = target.screen.layout.sections
|
|
571
|
-
.map((section) => section.id)
|
|
572
|
-
.filter((id): id is string => id !== undefined);
|
|
573
|
-
if (!sectionIds.includes(action.tab)) {
|
|
574
|
-
throw new Error(
|
|
575
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
576
|
-
`navigates to tab "${action.tab}", which is not a section id on target screen ` +
|
|
577
|
-
`"${target.screen.id}". Available: ${sectionIds.join(", ") || "(none)"}.`,
|
|
578
|
-
);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function resolveRowActionNavigateTarget(
|
|
583
|
-
featureName: string,
|
|
584
|
-
screenId: string,
|
|
585
|
-
screenType: "entityList" | "projectionList" | "projectionDetail" | "entityEdit",
|
|
586
|
-
actionLabel: string,
|
|
587
|
-
action: RowActionNavigateRuntime,
|
|
588
|
-
allScreenQns: ReadonlySet<string>,
|
|
589
|
-
navTargetShortIds: ReadonlySet<string>,
|
|
590
|
-
screensByShortId: ReadonlyMap<
|
|
591
|
-
string,
|
|
592
|
-
ReadonlyArray<{ readonly featureName: string; readonly screen: ScreenDefinition }>
|
|
593
|
-
>,
|
|
594
|
-
detailForScreens: ReadonlyMap<
|
|
595
|
-
string,
|
|
596
|
-
{ readonly featureName: string; readonly screen: ScreenDefinition }
|
|
597
|
-
>,
|
|
598
|
-
): { readonly featureName: string; readonly screen: ScreenDefinition } | undefined {
|
|
599
|
-
if (action.entity !== undefined) {
|
|
600
|
-
if (action.screen !== undefined) {
|
|
601
|
-
throw new Error(
|
|
602
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
603
|
-
`sets both "screen" and "entity" — exactly one navigate-target form is allowed.`,
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
|
-
if (screenType !== "entityList" && action.entityId === undefined) {
|
|
607
|
-
// entityList rows are always a real entity record, so row["id"] is a
|
|
608
|
-
// safe implicit default. projectionList/projectionDetail rows come from
|
|
609
|
-
// an arbitrary query projection with no guaranteed "id" field — an
|
|
610
|
-
// entity-target there needs an explicit entityId, or navigation silently
|
|
611
|
-
// opens the detail screen with no entity context at runtime.
|
|
612
|
-
throw new Error(
|
|
613
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
614
|
-
`navigate-target entity "${action.entity}" needs an explicit "entityId" — ${screenType} rows ` +
|
|
615
|
-
`come from a query projection with no guaranteed "id" field.`,
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
const detail = detailForScreens.get(action.entity);
|
|
619
|
-
if (detail === undefined) {
|
|
620
|
-
throw new Error(
|
|
621
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
622
|
-
`navigate-target entity "${action.entity}" has no screen declaring ` +
|
|
623
|
-
`detailFor: "${action.entity}".`,
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
validateRowActionNavigateTab(featureName, screenId, screenType, actionLabel, action, detail);
|
|
627
|
-
return detail;
|
|
628
|
-
}
|
|
629
|
-
if (action.screen === undefined) {
|
|
630
|
-
throw new Error(
|
|
631
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
632
|
-
`sets neither "screen" nor "entity" — exactly one navigate-target form is required.`,
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
const candidateQn = qualifyEntityName(featureName, "screen", action.screen);
|
|
636
|
-
if (!allScreenQns.has(candidateQn) && !navTargetShortIds.has(action.screen)) {
|
|
637
|
-
throw new Error(
|
|
638
|
-
`[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${actionLabel} "${action.id}" ` +
|
|
639
|
-
`navigate-target "${action.screen}" does not resolve to a registered screen in any feature.`,
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
const target = screensByShortId.get(action.screen)?.[0];
|
|
643
|
-
validateRowActionNavigateTab(featureName, screenId, screenType, actionLabel, action, target);
|
|
644
|
-
return target;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// Shared by the mint step's own handler (screen.handler) and its optional
|
|
648
|
-
// confirm step (confirm.handler) — both are a bare write-handler QN with the
|
|
649
|
-
// same registration requirement.
|
|
650
|
-
function validateWriteHandlerRegistered(
|
|
651
|
-
featureName: string,
|
|
652
|
-
screenId: string,
|
|
653
|
-
context: string,
|
|
654
|
-
handler: unknown,
|
|
655
|
-
allWriteHandlerQns: ReadonlySet<string>,
|
|
656
|
-
): void {
|
|
657
|
-
if (!handler || typeof handler !== "string") {
|
|
658
|
-
throw new Error(
|
|
659
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) has empty or non-string handler.`,
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
if (!allWriteHandlerQns.has(handler)) {
|
|
663
|
-
throw new Error(
|
|
664
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) handler "${handler}" ` +
|
|
665
|
-
`is not a registered write-handler. Check the QN spelling (expected ` +
|
|
666
|
-
`"<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Every field entry must carry a `type` discriminator. An author typo
|
|
672
|
-
// (`title: { required: true }` without a type) would otherwise let
|
|
673
|
-
// RenderField silently fall through to the default renderer and submit an
|
|
674
|
-
// empty string — failing at boot is clearer. `type as unknown` because
|
|
675
|
-
// FieldDefinition, as a union, only allows known type strings; here we're
|
|
676
|
-
// checking author code that may have circumvented the type check.
|
|
677
|
-
//
|
|
678
|
-
// Parametrized over (fields, layout) instead of a whole screen so a
|
|
679
|
-
// secretMint's own `confirm` step — a second, independent inline form on the
|
|
680
|
-
// same screen — can reuse the same checks as its `fields`/`layout` pair.
|
|
681
|
-
// `allowEmpty` is only ever true for a secretMint's own mint step (an
|
|
682
|
-
// input-less mint, fw#2838): the secret is server-generated and the mint
|
|
683
|
-
// step is just its submit button — actionForm and the confirm step always
|
|
684
|
-
// require at least one field.
|
|
685
|
-
function validateFormFieldsMap(
|
|
686
|
-
featureName: string,
|
|
687
|
-
screenId: string,
|
|
688
|
-
context: string,
|
|
689
|
-
fields: Readonly<Record<string, FieldDefinition>>,
|
|
690
|
-
allowEmpty: boolean,
|
|
691
|
-
): Set<string> {
|
|
692
|
-
const fieldNames = new Set(Object.keys(fields));
|
|
693
|
-
if (fieldNames.size === 0 && !allowEmpty) {
|
|
694
|
-
throw new Error(
|
|
695
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) has empty fields map — ` +
|
|
696
|
-
`declare at least one field.`,
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
for (const [fname, fdef] of Object.entries(fields)) {
|
|
700
|
-
// @cast-boundary schema-walk — feature-config inspection (Author may circumvent type-check)
|
|
701
|
-
const ftype = (fdef as { type?: unknown }).type;
|
|
702
|
-
if (typeof ftype !== "string" || ftype.length === 0) {
|
|
703
|
-
throw new Error(
|
|
704
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) field "${fname}" has no ` +
|
|
705
|
-
`\`type\` set. Each field must declare a type (e.g. "text", "number", "select").`,
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
if (ftype === "money") {
|
|
709
|
-
validateFormMoneyCurrency(featureName, screenId, context, fname, fdef);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
return fieldNames;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Fail-closed currency-source gate (fw#2839), the runtime half of the
|
|
716
|
-
// narrowed `FormFieldDefinition` — an untyped JS consumer has no compiler to
|
|
717
|
-
// stop it. An inline form screen has no entity, so a money field there can't
|
|
718
|
-
// borrow `entity.defaultCurrency`: without a declared source the renderer
|
|
719
|
-
// seeds a bare `0` and the handler's zod schema rejects the submit. Entity
|
|
720
|
-
// money fields are exempt — create-app already refuses an entity that holds
|
|
721
|
-
// money without a `defaultCurrency`.
|
|
722
|
-
function validateFormMoneyCurrency(
|
|
723
|
-
featureName: string,
|
|
724
|
-
screenId: string,
|
|
725
|
-
context: string,
|
|
726
|
-
fieldName: string,
|
|
727
|
-
fdef: unknown,
|
|
728
|
-
): void {
|
|
729
|
-
const where = `[Feature ${featureName}] Screen "${screenId}" (${context}) money field "${fieldName}"`;
|
|
730
|
-
const validForms =
|
|
731
|
-
`Declare \`currency: { kind: "literal", code: "EUR" }\` for a fixed currency, or ` +
|
|
732
|
-
`\`currency: { kind: "tenant" }\` to take the tenant-settings bundle's per-tenant currency.`;
|
|
733
|
-
// @cast-boundary schema-walk — feature-config inspection (Author may circumvent type-check)
|
|
734
|
-
const currency = (fdef as { currency?: { kind?: unknown; code?: unknown } | null }).currency;
|
|
735
|
-
if (currency === undefined) {
|
|
736
|
-
throw new Error(
|
|
737
|
-
`${where} must declare where its currency comes from — this screen has no entity whose ` +
|
|
738
|
-
`\`defaultCurrency\` it could inherit, so the form would seed a bare \`0\` that the ` +
|
|
739
|
-
`handler's schema rejects. ${validForms}`,
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
if (typeof currency !== "object" || currency === null) {
|
|
743
|
-
throw new Error(`${where} has a non-object \`currency\`. ${validForms}`);
|
|
744
|
-
}
|
|
745
|
-
const kind = currency.kind;
|
|
746
|
-
if (kind === "literal") {
|
|
747
|
-
const code = currency.code;
|
|
748
|
-
if (typeof code !== "string" || code.trim() === "") {
|
|
749
|
-
throw new Error(
|
|
750
|
-
`${where} declares \`currency: { kind: "literal" }\` with an empty or non-string \`code\`. ` +
|
|
751
|
-
`Pass the ISO code, e.g. { kind: "literal", code: "EUR" }.`,
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
} else if (kind !== "tenant") {
|
|
755
|
-
throw new Error(
|
|
756
|
-
`${where} declares an unknown currency kind ${JSON.stringify(kind)}. ${validForms}`,
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// `allowEmptySections` mirrors `validateFormFieldsMap`'s `allowEmpty` — the
|
|
762
|
-
// caller only ever passes true together with an actually-empty fields map
|
|
763
|
-
// (an input-less secretMint declares BOTH `fields: {}` and
|
|
764
|
-
// `layout.sections: []`; fields declared with no layout to show them stays
|
|
765
|
-
// an error).
|
|
766
|
-
// `fields`/`groups` are mutually exclusive on a fields-kind section; shared by
|
|
767
|
-
// every EditLayout-walking screen type instead of four hand-rolled copies.
|
|
768
|
-
function validateFieldsXorGroups(
|
|
769
|
-
errorPrefix: string,
|
|
770
|
-
section: { readonly title?: string; readonly fields: readonly EditFieldSpec[] } & {
|
|
771
|
-
readonly groups?: readonly { readonly fields: readonly EditFieldSpec[] }[];
|
|
772
|
-
},
|
|
773
|
-
): void {
|
|
774
|
-
if (section.fields.length > 0 && section.groups !== undefined) {
|
|
775
|
-
throw new Error(
|
|
776
|
-
`${errorPrefix} section "${section.title}" declares both fields and groups — pass fields: [] ` +
|
|
777
|
-
`when using groups.`,
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
if (section.fields.length === 0 && section.groups === undefined) {
|
|
781
|
-
throw new Error(
|
|
782
|
-
`${errorPrefix} has a section "${section.title}" with zero fields — drop the section or add ` +
|
|
783
|
-
`fields (or groups) to it.`,
|
|
784
|
-
);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
function validateFormLayoutSections(
|
|
789
|
-
featureName: string,
|
|
790
|
-
screenId: string,
|
|
791
|
-
context: string,
|
|
792
|
-
layout: EditLayout,
|
|
793
|
-
fieldNames: ReadonlySet<string>,
|
|
794
|
-
allowEmptySections: boolean,
|
|
795
|
-
): void {
|
|
796
|
-
if (layout.sections.length === 0) {
|
|
797
|
-
// skip: an input-less secretMint declares fields: {} and sections: [] together — no sections to validate.
|
|
798
|
-
if (allowEmptySections) return;
|
|
799
|
-
throw new Error(
|
|
800
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) has an empty sections list — ` +
|
|
801
|
-
`declare at least one section.`,
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
for (const section of layout.sections) {
|
|
805
|
-
if (isExtensionEditSection(section)) {
|
|
806
|
-
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
807
|
-
throw new Error(
|
|
808
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) extension section ` +
|
|
809
|
-
`"${section.title}" has no component — declare a react/native component marker.`,
|
|
810
|
-
);
|
|
811
|
-
}
|
|
812
|
-
continue;
|
|
813
|
-
}
|
|
814
|
-
if (section.kind === "relatedList") {
|
|
815
|
-
throw new Error(
|
|
816
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) relatedList section ` +
|
|
817
|
-
`"${section.title}" is not supported — relatedList is a projectionDetail-only ` +
|
|
818
|
-
`primitive (fw#2166).`,
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
if (isWriteFormEditSection(section)) {
|
|
822
|
-
throw new Error(
|
|
823
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) writeForm section ` +
|
|
824
|
-
`"${section.title}" is not supported — writeForm is a projectionDetail-only primitive.`,
|
|
825
|
-
);
|
|
826
|
-
}
|
|
827
|
-
validateFieldsXorGroups(`[Feature ${featureName}] Screen "${screenId}" (${context})`, section);
|
|
828
|
-
for (const fieldSpec of sectionFieldSpecs(section)) {
|
|
829
|
-
const normalized = normalizeEditField(fieldSpec);
|
|
830
|
-
if (!fieldNames.has(normalized.field)) {
|
|
831
|
-
throw new Error(
|
|
832
|
-
`[Feature ${featureName}] Screen "${screenId}" (${context}) layout references unknown field ` +
|
|
833
|
-
`"${normalized.field}". Known fields: ${[...fieldNames].sort().join(", ")}`,
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
// redirect is either a short screen id (same-feature, e.g. "item-list") or
|
|
841
|
-
// a fully-qualified cross-feature QN (`<feature>:screen:<id>`) — the
|
|
842
|
-
// renderer strips the latter to the short id (lastSegment) when
|
|
843
|
-
// navigating, which the nav-router resolves app-wide (#1946). The object
|
|
844
|
-
// form (fw#2670) carries the same target under `screen` plus the payload
|
|
845
|
-
// field `idFrom`. Shared by actionForm/secretMint (validateInlineFormNavTargets)
|
|
846
|
-
// and entityEdit — same rule, same error message, one place to keep them in sync.
|
|
847
|
-
function validateRedirectTarget(
|
|
848
|
-
feature: FeatureDefinition,
|
|
849
|
-
screenId: string,
|
|
850
|
-
screenKind: "actionForm" | "secretMint" | "entityEdit",
|
|
851
|
-
redirect: string | ActionFormRedirect,
|
|
852
|
-
allScreenQns: ReadonlySet<string>,
|
|
853
|
-
): void {
|
|
854
|
-
const redirectTarget = typeof redirect === "string" ? redirect : redirect.screen;
|
|
855
|
-
if (
|
|
856
|
-
typeof redirect !== "string" &&
|
|
857
|
-
(typeof redirect.idFrom !== "string" || redirect.idFrom.trim() === "")
|
|
858
|
-
) {
|
|
859
|
-
throw new Error(
|
|
860
|
-
`[Feature ${feature.name}] Screen "${screenId}" (${screenKind}) redirect.idFrom is empty or not a string — ` +
|
|
861
|
-
`name the success-payload field carrying the navigation id, or use the plain string ` +
|
|
862
|
-
`redirect form to navigate with the handler's own "id".`,
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
validateScreenNavTarget(
|
|
866
|
-
feature.name,
|
|
867
|
-
screenId,
|
|
868
|
-
screenKind,
|
|
869
|
-
"redirect",
|
|
870
|
-
redirectTarget,
|
|
871
|
-
allScreenQns,
|
|
872
|
-
feature.screens,
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
function validateInlineFormNavTargets(
|
|
877
|
-
feature: FeatureDefinition,
|
|
878
|
-
screenId: string,
|
|
879
|
-
screen: ActionFormScreenDefinition | SecretMintScreenDefinition,
|
|
880
|
-
kind: "actionForm" | "secretMint",
|
|
881
|
-
allScreenQns: ReadonlySet<string>,
|
|
882
|
-
): void {
|
|
883
|
-
if (screen.redirect !== undefined) {
|
|
884
|
-
validateRedirectTarget(feature, screenId, kind, screen.redirect, allScreenQns);
|
|
885
|
-
}
|
|
886
|
-
if (typeof screen.cancelTarget === "string") {
|
|
887
|
-
// Same rule as redirect — `false` (no Cancel button) needs no validation.
|
|
888
|
-
validateScreenNavTarget(
|
|
889
|
-
feature.name,
|
|
890
|
-
screenId,
|
|
891
|
-
kind,
|
|
892
|
-
"cancelTarget",
|
|
893
|
-
screen.cancelTarget,
|
|
894
|
-
allScreenQns,
|
|
895
|
-
feature.screens,
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// actionForm/secretMint have no entity link, only a write-handler QN +
|
|
901
|
-
// inline fields: checks handler registration, field types, layout refs, and nav targets.
|
|
902
|
-
function validateInlineFormScreen(
|
|
903
|
-
feature: FeatureDefinition,
|
|
904
|
-
screenId: string,
|
|
905
|
-
screen: ActionFormScreenDefinition | SecretMintScreenDefinition,
|
|
906
|
-
kind: "actionForm" | "secretMint",
|
|
907
|
-
allWriteHandlerQns: ReadonlySet<string>,
|
|
908
|
-
allScreenQns: ReadonlySet<string>,
|
|
909
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
910
|
-
): void {
|
|
911
|
-
validateWriteHandlerRegistered(feature.name, screenId, kind, screen.handler, allWriteHandlerQns);
|
|
912
|
-
// Only a secretMint's own mint step may skip fields/sections entirely
|
|
913
|
-
// (input-less mint, fw#2838) — actionForm always needs at least one field.
|
|
914
|
-
const allowEmptyMintForm = kind === "secretMint";
|
|
915
|
-
const fieldNames = validateFormFieldsMap(
|
|
916
|
-
feature.name,
|
|
917
|
-
screenId,
|
|
918
|
-
kind,
|
|
919
|
-
screen.fields,
|
|
920
|
-
allowEmptyMintForm,
|
|
921
|
-
);
|
|
922
|
-
validateFormLayoutSections(
|
|
923
|
-
feature.name,
|
|
924
|
-
screenId,
|
|
925
|
-
kind,
|
|
926
|
-
screen.layout,
|
|
927
|
-
fieldNames,
|
|
928
|
-
allowEmptyMintForm && fieldNames.size === 0,
|
|
929
|
-
);
|
|
930
|
-
validateWizardLayout(feature.name, screenId, kind, screen.layout, featureMap);
|
|
931
|
-
validateInlineFormNavTargets(feature, screenId, screen, kind, allScreenQns);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// SecretMintConfirmStep (fw#2838): an optional proof-of-receipt form rendered
|
|
935
|
-
// on the reveal card in place of the bare acknowledge button (TOTP enrollment:
|
|
936
|
-
// scan the code, then enter one). Unlike the mint step, confirm always needs
|
|
937
|
-
// at least one field — a step whose only job is proving receipt with no input
|
|
938
|
-
// would be meaningless — and its layout is a single plain form, never a
|
|
939
|
-
// wizard/tabs flow and never a persisted draft: a one-time reveal-confirm is
|
|
940
|
-
// not a multi-step flow, and persisting a draft of it would re-open the leak
|
|
941
|
-
// fw#2548 closed (a lingering copy of the just-revealed secret in storage).
|
|
942
|
-
function validateSecretMintConfirm(
|
|
943
|
-
feature: FeatureDefinition,
|
|
944
|
-
screenId: string,
|
|
945
|
-
screen: SecretMintScreenDefinition,
|
|
946
|
-
allWriteHandlerQns: ReadonlySet<string>,
|
|
947
|
-
): void {
|
|
948
|
-
const confirm = screen.confirm;
|
|
949
|
-
// skip: the screen declares no confirm step — nothing to validate.
|
|
950
|
-
if (confirm === undefined) return;
|
|
951
|
-
const context = "secretMint confirm";
|
|
952
|
-
validateWriteHandlerRegistered(
|
|
953
|
-
feature.name,
|
|
954
|
-
screenId,
|
|
955
|
-
context,
|
|
956
|
-
confirm.handler,
|
|
957
|
-
allWriteHandlerQns,
|
|
958
|
-
);
|
|
959
|
-
const fieldNames = validateFormFieldsMap(feature.name, screenId, context, confirm.fields, false);
|
|
960
|
-
validateFormLayoutSections(feature.name, screenId, context, confirm.layout, fieldNames, false);
|
|
961
|
-
if (confirm.layout.mode !== undefined && confirm.layout.mode !== "single") {
|
|
962
|
-
throw new Error(
|
|
963
|
-
`[Feature ${feature.name}] Screen "${screenId}" (${context}) sets layout.mode: ` +
|
|
964
|
-
`"${confirm.layout.mode}" — a one-time reveal confirm step must stay a single-step form. ` +
|
|
965
|
-
`Remove mode or set it to "single".`,
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
if (confirm.layout.draft === true) {
|
|
969
|
-
throw new Error(
|
|
970
|
-
`[Feature ${feature.name}] Screen "${screenId}" (${context}) sets layout.draft: true — ` +
|
|
971
|
-
`persisting a draft of the reveal-confirm step would leak the one-time secret into storage, ` +
|
|
972
|
-
`the exact leak fw#2548 closed. Remove draft: true.`,
|
|
973
|
-
);
|
|
974
|
-
}
|
|
975
|
-
// skip: no carry list declared — no carried fields to cross-check against confirm.fields.
|
|
976
|
-
if (confirm.carry === undefined) return;
|
|
977
|
-
for (const carryField of confirm.carry) {
|
|
978
|
-
if (typeof carryField !== "string" || carryField.trim() === "") {
|
|
979
|
-
throw new Error(
|
|
980
|
-
`[Feature ${feature.name}] Screen "${screenId}" (${context}) has an empty or non-string ` +
|
|
981
|
-
`entry in "carry".`,
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
if (fieldNames.has(carryField)) {
|
|
985
|
-
throw new Error(
|
|
986
|
-
`[Feature ${feature.name}] Screen "${screenId}" (${context}) carry field "${carryField}" ` +
|
|
987
|
-
`also names a confirm.fields entry — a carried mint-payload value would silently ` +
|
|
988
|
-
`overwrite the user-entered field of the same name. Rename one of them.`,
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
// nav.screen is a full QN (cross-feature), so a standalone r.nav() pointing
|
|
995
|
-
// at `targetQn` must be found by scanning every feature's nav entries and actions.
|
|
996
|
-
function hasStandaloneNavEntry(
|
|
997
|
-
targetQn: string,
|
|
998
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
999
|
-
): boolean {
|
|
1000
|
-
for (const f of featureMap.values()) {
|
|
1001
|
-
for (const nav of Object.values(f.navs)) {
|
|
1002
|
-
if (nav.screen === targetQn) return true;
|
|
1003
|
-
if (nav.createAction?.screen === targetQn) return true;
|
|
1004
|
-
if (nav.actions?.some((a) => a.screen === targetQn)) return true;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
return false;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// Every screen must resolve nav via `nav`, `r.nav()`, a parent list, or
|
|
1011
|
-
// `dormant: true`. Skipped when the composed set has no nav entries at all.
|
|
1012
|
-
function validateScreenHasNavArea(
|
|
1013
|
-
feature: FeatureDefinition,
|
|
1014
|
-
screenId: string,
|
|
1015
|
-
screen: ScreenDefinition,
|
|
1016
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
1017
|
-
): void {
|
|
1018
|
-
// skip: the screen already declares its own nav entry.
|
|
1019
|
-
if (screen.nav !== undefined) return;
|
|
1020
|
-
// skip: explicitly opted out via `dormant: true`.
|
|
1021
|
-
if (screen.dormant === true) return;
|
|
1022
|
-
const anyNavRegistered = [...featureMap.values()].some((f) => Object.keys(f.navs).length > 0);
|
|
1023
|
-
// skip: no nav entries exist anywhere in the composed set — a feature/
|
|
1024
|
-
// recipe/test fixture booted without an app shell, not an omission.
|
|
1025
|
-
if (!anyNavRegistered) return;
|
|
1026
|
-
const targetQn = qualifyEntityName(feature.name, "screen", screenId);
|
|
1027
|
-
// skip: a standalone r.nav() elsewhere already points at this screen.
|
|
1028
|
-
if (hasStandaloneNavEntry(targetQn, featureMap)) return;
|
|
1029
|
-
const allScreens = [...featureMap.values()].flatMap((f) => Object.values(f.screens));
|
|
1030
|
-
// skip: resolves to a parent list via listScreenId/rowAction-target/same-entity.
|
|
1031
|
-
if (resolveNavParentScreen(allScreens, screen, (s) => s.id) !== undefined) return;
|
|
1032
|
-
throw new Error(
|
|
1033
|
-
`[Feature ${feature.name}] Screen "${targetQn}" has no nav entry and no resolvable list — ` +
|
|
1034
|
-
`declare listScreenId (or a rowAction/toolbarAction navigate target from a list screen), add ` +
|
|
1035
|
-
`a standalone r.nav() pointing at it, or set dormant: true if it's intentionally reachable ` +
|
|
1036
|
-
`only via a direct link/redirect or a consuming app's own r.nav().`,
|
|
1037
|
-
);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
export function validateScreens(
|
|
1041
|
-
feature: FeatureDefinition,
|
|
1042
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
1043
|
-
allWriteHandlerQns: ReadonlySet<string>,
|
|
1044
|
-
allScreenQns: ReadonlySet<string>,
|
|
1045
|
-
allConfigKeyQns: ReadonlySet<string>,
|
|
1046
|
-
screensByShortId: ReadonlyMap<
|
|
1047
|
-
string,
|
|
1048
|
-
ReadonlyArray<{ readonly featureName: string; readonly screen: ScreenDefinition }>
|
|
1049
|
-
>,
|
|
1050
|
-
detailForScreens: ReadonlyMap<
|
|
1051
|
-
string,
|
|
1052
|
-
{ readonly featureName: string; readonly screen: ScreenDefinition }
|
|
1053
|
-
>,
|
|
1054
|
-
): void {
|
|
1055
|
-
// navigate-Targets (rowAction/toolbarAction) dürfen cross-feature zeigen —
|
|
1056
|
-
// der Runtime-Router (create-app) löst eine bare screenId app-weit über ALLE
|
|
1057
|
-
// Features auf (eine deklarative Liste im owning-Feature der Entity navigiert
|
|
1058
|
-
// so zu den Custom-Editoren der Consumer-App). Der Validator spiegelt das:
|
|
1059
|
-
// same-feature ODER irgendein Feature. redirect/cancelTarget akzeptieren
|
|
1060
|
-
// zusätzlich eine voll-qualifizierte Cross-Feature-QN (resolveScreenTargetQn,
|
|
1061
|
-
// #1946) — kurze IDs bleiben same-feature wie zuvor.
|
|
1062
|
-
const navTargetShortIds = screenShortIdsFrom(allScreenQns);
|
|
1063
|
-
for (const [screenId, screen] of Object.entries(feature.screens)) {
|
|
1064
|
-
validateScreenHasNavArea(feature, screenId, screen, featureMap);
|
|
1065
|
-
if (screen.type === "custom") {
|
|
1066
|
-
if (!screen.renderer.react && !screen.renderer.native) {
|
|
1067
|
-
throw new Error(
|
|
1068
|
-
`[Feature ${feature.name}] Screen "${screenId}" has type="custom" but the renderer ` +
|
|
1069
|
-
`declares neither a react nor a native component — at least one platform must be set.`,
|
|
1070
|
-
);
|
|
1071
|
-
}
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
if (screen.type === "projectionList") {
|
|
1076
|
-
// Query-getrieben, keine Entity → nur query + columns prüfen (die
|
|
1077
|
-
// Column-Felder können nicht gegen eine Entity gecheckt werden; sie
|
|
1078
|
-
// werden zur Render-Zeit gegen die Projection-Rows aufgelöst).
|
|
1079
|
-
if (!screen.query || typeof screen.query !== "string") {
|
|
1080
|
-
throw new Error(
|
|
1081
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionList) has empty or non-string query.`,
|
|
1082
|
-
);
|
|
1083
|
-
}
|
|
1084
|
-
if (screen.columns.length === 0) {
|
|
1085
|
-
throw new Error(
|
|
1086
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionList) has an empty columns list — ` +
|
|
1087
|
-
`declare at least one column.`,
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
for (const col of screen.columns) {
|
|
1091
|
-
const normalizedCol = normalizeListColumn(col);
|
|
1092
|
-
validateColumnRendererForm(feature.name, screenId, normalizedCol);
|
|
1093
|
-
if (normalizedCol.refEntity !== undefined) {
|
|
1094
|
-
assertRefTargetRegistered(
|
|
1095
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionList)`,
|
|
1096
|
-
`column "${normalizedCol.field}" (refEntity)`,
|
|
1097
|
-
normalizedCol.refEntity,
|
|
1098
|
-
feature.name,
|
|
1099
|
-
featureMap,
|
|
1100
|
-
);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
// Screen filter (fw#2224) — field existence can't be checked without
|
|
1104
|
-
// an entity (columns aren't a complete field inventory of the
|
|
1105
|
-
// underlying query), so only pin the structure: "in" requires an
|
|
1106
|
-
// array. Field validity is documented in the PR body.
|
|
1107
|
-
if (
|
|
1108
|
-
screen.filter !== undefined &&
|
|
1109
|
-
screen.filter.op === "in" &&
|
|
1110
|
-
!Array.isArray(screen.filter.value)
|
|
1111
|
-
) {
|
|
1112
|
-
throw new Error(
|
|
1113
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionList) filter.op "in" requires ` +
|
|
1114
|
-
`filter.value to be a readonly array.`,
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
if (screen.facets !== undefined) {
|
|
1118
|
-
validateListFacets(
|
|
1119
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionList)`,
|
|
1120
|
-
screen.facets,
|
|
1121
|
-
screen.columns,
|
|
1122
|
-
feature.name,
|
|
1123
|
-
featureMap,
|
|
1124
|
-
);
|
|
1125
|
-
}
|
|
1126
|
-
if (screen.rowActions !== undefined) {
|
|
1127
|
-
for (const action of screen.rowActions) {
|
|
1128
|
-
if (action.kind === "navigate") {
|
|
1129
|
-
const target = resolveRowActionNavigateTarget(
|
|
1130
|
-
feature.name,
|
|
1131
|
-
screenId,
|
|
1132
|
-
"projectionList",
|
|
1133
|
-
"rowAction",
|
|
1134
|
-
action,
|
|
1135
|
-
allScreenQns,
|
|
1136
|
-
navTargetShortIds,
|
|
1137
|
-
screensByShortId,
|
|
1138
|
-
detailForScreens,
|
|
1139
|
-
);
|
|
1140
|
-
validateRowActionNavigateParams(
|
|
1141
|
-
feature.name,
|
|
1142
|
-
screenId,
|
|
1143
|
-
"projectionList",
|
|
1144
|
-
undefined,
|
|
1145
|
-
action,
|
|
1146
|
-
target,
|
|
1147
|
-
);
|
|
1148
|
-
} else if (action.kind === "drawer") {
|
|
1149
|
-
validateDrawerTargetAction(
|
|
1150
|
-
feature.name,
|
|
1151
|
-
screenId,
|
|
1152
|
-
"projectionList",
|
|
1153
|
-
"rowAction",
|
|
1154
|
-
action,
|
|
1155
|
-
feature.screens,
|
|
1156
|
-
);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
validateAtMostOneRowClick(feature.name, screenId, "projectionList", screen.rowActions);
|
|
1160
|
-
}
|
|
1161
|
-
// Only drawer-kind and navigate actions that set tab are validated
|
|
1162
|
-
// here — plain navigate (no tab) and writeHandler toolbarActions on
|
|
1163
|
-
// projectionList have no boot check yet (pre-existing gap).
|
|
1164
|
-
if (screen.toolbarActions !== undefined) {
|
|
1165
|
-
for (const action of screen.toolbarActions) {
|
|
1166
|
-
if (action.kind === "drawer") {
|
|
1167
|
-
validateDrawerTargetAction(
|
|
1168
|
-
feature.name,
|
|
1169
|
-
screenId,
|
|
1170
|
-
"projectionList",
|
|
1171
|
-
"toolbarAction",
|
|
1172
|
-
action,
|
|
1173
|
-
feature.screens,
|
|
1174
|
-
);
|
|
1175
|
-
}
|
|
1176
|
-
if (action.kind === "navigate" && action.tab !== undefined) {
|
|
1177
|
-
resolveRowActionNavigateTarget(
|
|
1178
|
-
feature.name,
|
|
1179
|
-
screenId,
|
|
1180
|
-
"projectionList",
|
|
1181
|
-
"toolbarAction",
|
|
1182
|
-
action,
|
|
1183
|
-
allScreenQns,
|
|
1184
|
-
navTargetShortIds,
|
|
1185
|
-
screensByShortId,
|
|
1186
|
-
detailForScreens,
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
continue;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
if (screen.type === "projectionDetail") {
|
|
1195
|
-
// Query-getrieben wie projectionList, aber Single-Row + Layout statt
|
|
1196
|
-
// Columns. Kein Entity-Check möglich — Felder werden render-seitig
|
|
1197
|
-
// gegen die Query-Response aufgelöst, nicht gegen eine Entity.
|
|
1198
|
-
if (!screen.query || typeof screen.query !== "string") {
|
|
1199
|
-
throw new Error(
|
|
1200
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) has empty or non-string query.`,
|
|
1201
|
-
);
|
|
1202
|
-
}
|
|
1203
|
-
if (screen.singleton === true && screen.idParam !== undefined) {
|
|
1204
|
-
throw new Error(
|
|
1205
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) sets both singleton: true ` +
|
|
1206
|
-
`and idParam — singleton means the server picks the row from the caller's context, so there ` +
|
|
1207
|
-
`is no row id to send under idParam. Remove idParam.`,
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
// singleton has no row id in the path, but the auto-generated "Edit"
|
|
1211
|
-
// action (fw#2166, defaultEditAction in kumiko-screen.tsx) navigates
|
|
1212
|
-
// to the detailFor entity's entityEdit screen using that path id —
|
|
1213
|
-
// under singleton it would silently open that screen in CREATE mode
|
|
1214
|
-
// instead of editing the singleton row. Reject rather than let that
|
|
1215
|
-
// win silently, same as the idParam check above.
|
|
1216
|
-
if (screen.singleton === true && screen.detailFor !== undefined) {
|
|
1217
|
-
throw new Error(
|
|
1218
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) sets both singleton: true ` +
|
|
1219
|
-
`and detailFor — the auto-generated "Edit" action navigates using the path's row id, which a ` +
|
|
1220
|
-
`singleton screen never has. Remove detailFor, or declare an explicit "edit" action instead.`,
|
|
1221
|
-
);
|
|
1222
|
-
}
|
|
1223
|
-
if (screen.layout.sections.length === 0) {
|
|
1224
|
-
throw new Error(
|
|
1225
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) has an empty sections list — ` +
|
|
1226
|
-
`declare at least one section.`,
|
|
1227
|
-
);
|
|
1228
|
-
}
|
|
1229
|
-
if (screen.layout.mode === "tabs") {
|
|
1230
|
-
validateTabSections(feature.name, screenId, "projectionDetail", screen.layout.sections);
|
|
1231
|
-
for (const section of screen.layout.sections) {
|
|
1232
|
-
const isFieldsSection = section.kind === undefined || section.kind === "fields";
|
|
1233
|
-
if (isFieldsSection && section.groups !== undefined && section.actions !== undefined) {
|
|
1234
|
-
throw new Error(
|
|
1235
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.id}" ` +
|
|
1236
|
-
`declares both groups and actions in tabs mode — actions has no single group's Card to sit ` +
|
|
1237
|
-
`in, and an extra title-less Card around the group cards would nest a Card inside a Card. ` +
|
|
1238
|
-
`Move the actions onto one group's title, or drop groups for a flat fields grid.`,
|
|
1239
|
-
);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
if (screen.metrics !== undefined) {
|
|
1244
|
-
for (const metric of screen.metrics) {
|
|
1245
|
-
const field = metricField(metric);
|
|
1246
|
-
const hasOwnLabel = typeof metric !== "string" && metric.label !== undefined;
|
|
1247
|
-
if (!hasOwnLabel && screen.fieldLabels?.[field] === undefined) {
|
|
1248
|
-
throw new Error(
|
|
1249
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) metric "${field}" has ` +
|
|
1250
|
-
`no entry in fieldLabels and no own "label" — every metrics field needs one of the two, ` +
|
|
1251
|
-
`there is no fallback to the raw column name.`,
|
|
1252
|
-
);
|
|
1253
|
-
}
|
|
1254
|
-
const navigate = typeof metric === "string" ? undefined : metric.navigate;
|
|
1255
|
-
if (navigate?.tab !== undefined) {
|
|
1256
|
-
if (navigate.screen === undefined && navigate.entity === undefined) {
|
|
1257
|
-
if (!screen.layout.sections.some((section) => section.id === navigate.tab)) {
|
|
1258
|
-
throw new Error(
|
|
1259
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) metric "${field}" ` +
|
|
1260
|
-
`navigates to tab "${navigate.tab}", which is not a section id on this screen.`,
|
|
1261
|
-
);
|
|
1262
|
-
}
|
|
1263
|
-
} else {
|
|
1264
|
-
resolveRowActionNavigateTarget(
|
|
1265
|
-
feature.name,
|
|
1266
|
-
screenId,
|
|
1267
|
-
"projectionDetail",
|
|
1268
|
-
"metric",
|
|
1269
|
-
{
|
|
1270
|
-
kind: "navigate",
|
|
1271
|
-
id: field,
|
|
1272
|
-
label: field,
|
|
1273
|
-
...(navigate.screen !== undefined ? { screen: navigate.screen } : {}),
|
|
1274
|
-
...(navigate.entity !== undefined ? { entity: navigate.entity } : {}),
|
|
1275
|
-
...(navigate.entityId !== undefined ? { entityId: navigate.entityId } : {}),
|
|
1276
|
-
tab: navigate.tab,
|
|
1277
|
-
},
|
|
1278
|
-
allScreenQns,
|
|
1279
|
-
navTargetShortIds,
|
|
1280
|
-
screensByShortId,
|
|
1281
|
-
detailForScreens,
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
for (const section of screen.layout.sections) {
|
|
1288
|
-
const sectionLabel = section.title ?? section.id ?? "(untitled)";
|
|
1289
|
-
if (section.actions !== undefined) {
|
|
1290
|
-
for (const action of section.actions) {
|
|
1291
|
-
validateActionHasIcon(
|
|
1292
|
-
feature.name,
|
|
1293
|
-
screenId,
|
|
1294
|
-
"projectionDetail",
|
|
1295
|
-
`section "${sectionLabel}" action`,
|
|
1296
|
-
action,
|
|
1297
|
-
);
|
|
1298
|
-
if (action.kind === "navigate" && action.tab !== undefined) {
|
|
1299
|
-
resolveRowActionNavigateTarget(
|
|
1300
|
-
feature.name,
|
|
1301
|
-
screenId,
|
|
1302
|
-
"projectionDetail",
|
|
1303
|
-
`section "${sectionLabel}" action`,
|
|
1304
|
-
action,
|
|
1305
|
-
allScreenQns,
|
|
1306
|
-
navTargetShortIds,
|
|
1307
|
-
screensByShortId,
|
|
1308
|
-
detailForScreens,
|
|
1309
|
-
);
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
if (section.kind === "relatedList" && section.emptyState?.action !== undefined) {
|
|
1314
|
-
validateActionHasIcon(
|
|
1315
|
-
feature.name,
|
|
1316
|
-
screenId,
|
|
1317
|
-
"projectionDetail",
|
|
1318
|
-
`section "${sectionLabel}" emptyState action`,
|
|
1319
|
-
section.emptyState.action,
|
|
1320
|
-
);
|
|
1321
|
-
if (
|
|
1322
|
-
section.emptyState.action.kind === "navigate" &&
|
|
1323
|
-
section.emptyState.action.tab !== undefined
|
|
1324
|
-
) {
|
|
1325
|
-
resolveRowActionNavigateTarget(
|
|
1326
|
-
feature.name,
|
|
1327
|
-
screenId,
|
|
1328
|
-
"projectionDetail",
|
|
1329
|
-
`section "${sectionLabel}" emptyState action`,
|
|
1330
|
-
section.emptyState.action,
|
|
1331
|
-
allScreenQns,
|
|
1332
|
-
navTargetShortIds,
|
|
1333
|
-
screensByShortId,
|
|
1334
|
-
detailForScreens,
|
|
1335
|
-
);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
if (isExtensionEditSection(section)) {
|
|
1339
|
-
// projectionDetail is read-only (no composed form submit) — an
|
|
1340
|
-
// extension that persists through the host's Save button has
|
|
1341
|
-
// nothing to save against. Extensions that persist themselves
|
|
1342
|
-
// (e.g. notes-history NotesSection, via their own dispatcher
|
|
1343
|
-
// writes) are fine (solon#264).
|
|
1344
|
-
if (section.contributesToFormSubmit === true) {
|
|
1345
|
-
throw new Error(
|
|
1346
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) extension section ` +
|
|
1347
|
-
`"${section.title}" sets contributesToFormSubmit: true, but projectionDetail has no ` +
|
|
1348
|
-
`form submit to contribute to (read-only screen). Omit contributesToFormSubmit and ` +
|
|
1349
|
-
`persist through the extension's own dispatcher writes instead.`,
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
1353
|
-
throw new Error(
|
|
1354
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) extension section ` +
|
|
1355
|
-
`"${section.title}" has no component — declare a react/native component marker.`,
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
// The host-derived entity name on projectionDetail is an internal
|
|
1359
|
-
// placeholder (no real entity backs this screen) — a self-
|
|
1360
|
-
// persisting extension needs the real domain entity name declared
|
|
1361
|
-
// explicitly, or it silently reads/writes against the wrong type.
|
|
1362
|
-
if (section.entityName === undefined || section.entityName.trim().length === 0) {
|
|
1363
|
-
throw new Error(
|
|
1364
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) extension section ` +
|
|
1365
|
-
`"${section.title}" has no entityName — projectionDetail has no real entity, so the ` +
|
|
1366
|
-
`extension must declare entityName explicitly (the domain entity it persists against).`,
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
continue;
|
|
1370
|
-
}
|
|
1371
|
-
if (section.kind === "relatedList") {
|
|
1372
|
-
if (!section.query || typeof section.query !== "string") {
|
|
1373
|
-
throw new Error(
|
|
1374
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1375
|
-
`(relatedList) has empty or non-string query.`,
|
|
1376
|
-
);
|
|
1377
|
-
}
|
|
1378
|
-
for (const col of section.columns) {
|
|
1379
|
-
const normalizedCol = normalizeListColumn(col);
|
|
1380
|
-
if (normalizedCol.refEntity !== undefined) {
|
|
1381
|
-
assertRefTargetRegistered(
|
|
1382
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" (relatedList)`,
|
|
1383
|
-
`column "${normalizedCol.field}" (refEntity)`,
|
|
1384
|
-
normalizedCol.refEntity,
|
|
1385
|
-
feature.name,
|
|
1386
|
-
featureMap,
|
|
1387
|
-
);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
if (screen.layout.mode === "wizard") {
|
|
1391
|
-
throw new Error(
|
|
1392
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1393
|
-
`is kind "relatedList" in a wizard layout — a read-only list is not supported in a ` +
|
|
1394
|
-
`stepped form. Remove mode: "wizard" or drop the relatedList section.`,
|
|
1395
|
-
);
|
|
1396
|
-
}
|
|
1397
|
-
if (section.rowClick !== undefined) {
|
|
1398
|
-
const targetEntity = section.rowClick.entity;
|
|
1399
|
-
const hasDetailScreen = [...featureMap.values()].some((f) =>
|
|
1400
|
-
Object.values(f.screens).some((s) => s.detailFor === targetEntity),
|
|
1401
|
-
);
|
|
1402
|
-
if (!hasDetailScreen) {
|
|
1403
|
-
throw new Error(
|
|
1404
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1405
|
-
`(relatedList) rowClick targets entity "${targetEntity}", but no screen declares ` +
|
|
1406
|
-
`detailFor: "${targetEntity}". Add detailFor: "${targetEntity}" to the screen that shows it.`,
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
if (section.rowActions !== undefined) {
|
|
1411
|
-
for (const action of section.rowActions) {
|
|
1412
|
-
if (action.kind === "navigate") {
|
|
1413
|
-
const target = resolveRowActionNavigateTarget(
|
|
1414
|
-
feature.name,
|
|
1415
|
-
screenId,
|
|
1416
|
-
"projectionDetail",
|
|
1417
|
-
"rowAction",
|
|
1418
|
-
action,
|
|
1419
|
-
allScreenQns,
|
|
1420
|
-
navTargetShortIds,
|
|
1421
|
-
screensByShortId,
|
|
1422
|
-
detailForScreens,
|
|
1423
|
-
);
|
|
1424
|
-
validateRowActionNavigateParams(
|
|
1425
|
-
feature.name,
|
|
1426
|
-
screenId,
|
|
1427
|
-
"projectionDetail",
|
|
1428
|
-
undefined,
|
|
1429
|
-
action,
|
|
1430
|
-
target,
|
|
1431
|
-
);
|
|
1432
|
-
} else if (action.kind === "drawer") {
|
|
1433
|
-
validateDrawerTargetAction(
|
|
1434
|
-
feature.name,
|
|
1435
|
-
screenId,
|
|
1436
|
-
"projectionDetail",
|
|
1437
|
-
"rowAction",
|
|
1438
|
-
action,
|
|
1439
|
-
feature.screens,
|
|
1440
|
-
);
|
|
1441
|
-
} else if (!allWriteHandlerQns.has(action.handler)) {
|
|
1442
|
-
throw new Error(
|
|
1443
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section ` +
|
|
1444
|
-
`"${section.title}" (relatedList) rowAction "${action.id}" handler "${action.handler}" ` +
|
|
1445
|
-
`is not a registered write-handler. Check the QN spelling (expected ` +
|
|
1446
|
-
`"<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
1447
|
-
);
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
// section.rowClick (legacy, navigate-only) and a rowActions entry
|
|
1451
|
-
// marked rowClick:true both claim the row-body click — same
|
|
1452
|
-
// at-most-one constraint as entityList/projectionList's
|
|
1453
|
-
// validateAtMostOneRowClick, just spanning two fields instead of one.
|
|
1454
|
-
const rowClickActionCount = section.rowActions.filter(
|
|
1455
|
-
(a) => a.kind === "navigate" && a.rowClick === true,
|
|
1456
|
-
).length;
|
|
1457
|
-
const legacyRowClickCount = section.rowClick !== undefined ? 1 : 0;
|
|
1458
|
-
if (rowClickActionCount + legacyRowClickCount > 1) {
|
|
1459
|
-
throw new Error(
|
|
1460
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1461
|
-
`(relatedList) has both a rowClick and ${rowClickActionCount} rowActions marked ` +
|
|
1462
|
-
`rowClick:true — at most one may fire on a row-body click.`,
|
|
1463
|
-
);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
if (section.defaultSort !== undefined) {
|
|
1467
|
-
const sortField = section.defaultSort.field;
|
|
1468
|
-
const col = section.columns.find((c) => normalizeListColumn(c).field === sortField);
|
|
1469
|
-
if (col === undefined) {
|
|
1470
|
-
throw new Error(
|
|
1471
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1472
|
-
`(relatedList) defaultSort.field "${sortField}" is not a listed column.`,
|
|
1473
|
-
);
|
|
1474
|
-
}
|
|
1475
|
-
if (normalizeListColumn(col).sortable !== true) {
|
|
1476
|
-
throw new Error(
|
|
1477
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1478
|
-
`(relatedList) defaultSort.field "${sortField}" is not sortable. Set sortable: true on ` +
|
|
1479
|
-
`the column or pick another field.`,
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
if (section.facets !== undefined) {
|
|
1484
|
-
validateListFacets(
|
|
1485
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) relatedList section "${section.title}"`,
|
|
1486
|
-
section.facets,
|
|
1487
|
-
section.columns,
|
|
1488
|
-
feature.name,
|
|
1489
|
-
featureMap,
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
// Only drawer-kind and navigate actions that set tab are validated
|
|
1493
|
-
// here — plain navigate (no tab) and writeHandler toolbarActions
|
|
1494
|
-
// have no boot check yet, same gap as above.
|
|
1495
|
-
if (section.toolbarActions !== undefined) {
|
|
1496
|
-
for (const action of section.toolbarActions) {
|
|
1497
|
-
if (action.kind === "drawer") {
|
|
1498
|
-
validateDrawerTargetAction(
|
|
1499
|
-
feature.name,
|
|
1500
|
-
screenId,
|
|
1501
|
-
"projectionDetail",
|
|
1502
|
-
"toolbarAction",
|
|
1503
|
-
action,
|
|
1504
|
-
feature.screens,
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
if (action.kind === "navigate" && action.tab !== undefined) {
|
|
1508
|
-
resolveRowActionNavigateTarget(
|
|
1509
|
-
feature.name,
|
|
1510
|
-
screenId,
|
|
1511
|
-
"projectionDetail",
|
|
1512
|
-
"toolbarAction",
|
|
1513
|
-
action,
|
|
1514
|
-
allScreenQns,
|
|
1515
|
-
navTargetShortIds,
|
|
1516
|
-
screensByShortId,
|
|
1517
|
-
detailForScreens,
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
continue;
|
|
1523
|
-
}
|
|
1524
|
-
if (isWriteFormEditSection(section)) {
|
|
1525
|
-
if (Object.keys(section.fieldDefs).length === 0) {
|
|
1526
|
-
throw new Error(
|
|
1527
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1528
|
-
`(writeForm) has an empty fieldDefs map — declare at least one field type.`,
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
|
-
if (section.fields.length === 0) {
|
|
1532
|
-
throw new Error(
|
|
1533
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1534
|
-
`(writeForm) has zero fields — drop the section or add fields to it.`,
|
|
1535
|
-
);
|
|
1536
|
-
}
|
|
1537
|
-
// kumiko-lint-ignore section-fields-raw writeForm sections carry no groups (EditWriteFormSection)
|
|
1538
|
-
for (const f of section.fields) {
|
|
1539
|
-
const fieldName = normalizeEditField(f).field;
|
|
1540
|
-
if (section.fieldDefs[fieldName] === undefined) {
|
|
1541
|
-
throw new Error(
|
|
1542
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1543
|
-
`(writeForm) field "${fieldName}" has no entry in fieldDefs — every rendered field needs ` +
|
|
1544
|
-
`a type declared there.`,
|
|
1545
|
-
);
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
if (screen.layout.mode === "wizard") {
|
|
1549
|
-
throw new Error(
|
|
1550
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1551
|
-
`is kind "writeForm" in a wizard layout — a self-persisting form is not supported in a ` +
|
|
1552
|
-
`stepped form. Remove mode: "wizard" or drop the writeForm section.`,
|
|
1553
|
-
);
|
|
1554
|
-
}
|
|
1555
|
-
if (!allWriteHandlerQns.has(section.handler)) {
|
|
1556
|
-
throw new Error(
|
|
1557
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.title}" ` +
|
|
1558
|
-
`(writeForm) handler "${section.handler}" is not a registered write-handler. Check the QN ` +
|
|
1559
|
-
`spelling (expected "<feature>:write:<short>") and that the handler is declared via ` +
|
|
1560
|
-
`r.writeHandler(...).`,
|
|
1561
|
-
);
|
|
1562
|
-
}
|
|
1563
|
-
continue;
|
|
1564
|
-
}
|
|
1565
|
-
validateFieldsXorGroups(
|
|
1566
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail)`,
|
|
1567
|
-
section,
|
|
1568
|
-
);
|
|
1569
|
-
for (const fieldSpec of sectionFieldSpecs(section)) {
|
|
1570
|
-
const normalizedField = normalizeEditField(fieldSpec);
|
|
1571
|
-
if (normalizedField.refEntity !== undefined) {
|
|
1572
|
-
assertRefTargetRegistered(
|
|
1573
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail)`,
|
|
1574
|
-
`field "${normalizedField.field}" (refEntity)`,
|
|
1575
|
-
normalizedField.refEntity,
|
|
1576
|
-
feature.name,
|
|
1577
|
-
featureMap,
|
|
1578
|
-
);
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
// Header actions reuse RowAction (the displayed record stands in for
|
|
1583
|
-
// the row), so the same navigate/writeHandler existence checks as
|
|
1584
|
-
// entityList/projectionList apply. `rowClick` is rejected outright —
|
|
1585
|
-
// a detail screen has no row to click.
|
|
1586
|
-
if (screen.actions !== undefined) {
|
|
1587
|
-
for (const action of screen.actions) {
|
|
1588
|
-
validateActionHasIcon(feature.name, screenId, "projectionDetail", "action", action);
|
|
1589
|
-
if (action.kind === "navigate" && action.rowClick === true) {
|
|
1590
|
-
throw new Error(
|
|
1591
|
-
`[Feature ${feature.name}] Screen "${qualifyEntityName(feature.name, "screen", screenId)}" ` +
|
|
1592
|
-
`(projectionDetail) action "${action.id}" sets rowClick: true — there is no row to ` +
|
|
1593
|
-
`click on a detail screen. Remove rowClick.`,
|
|
1594
|
-
);
|
|
1595
|
-
}
|
|
1596
|
-
if (action.kind === "navigate") {
|
|
1597
|
-
const target = resolveRowActionNavigateTarget(
|
|
1598
|
-
feature.name,
|
|
1599
|
-
screenId,
|
|
1600
|
-
"projectionDetail",
|
|
1601
|
-
"action",
|
|
1602
|
-
action,
|
|
1603
|
-
allScreenQns,
|
|
1604
|
-
navTargetShortIds,
|
|
1605
|
-
screensByShortId,
|
|
1606
|
-
detailForScreens,
|
|
1607
|
-
);
|
|
1608
|
-
validateRowActionNavigateParams(
|
|
1609
|
-
feature.name,
|
|
1610
|
-
screenId,
|
|
1611
|
-
"projectionDetail",
|
|
1612
|
-
screen.detailFor,
|
|
1613
|
-
action,
|
|
1614
|
-
target,
|
|
1615
|
-
);
|
|
1616
|
-
} else if (action.kind === "drawer") {
|
|
1617
|
-
validateDrawerTargetAction(
|
|
1618
|
-
feature.name,
|
|
1619
|
-
screenId,
|
|
1620
|
-
"projectionDetail",
|
|
1621
|
-
"action",
|
|
1622
|
-
action,
|
|
1623
|
-
feature.screens,
|
|
1624
|
-
);
|
|
1625
|
-
} else {
|
|
1626
|
-
if (!allWriteHandlerQns.has(action.handler)) {
|
|
1627
|
-
throw new Error(
|
|
1628
|
-
`[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) action "${action.id}" ` +
|
|
1629
|
-
`handler "${action.handler}" is not a registered write-handler. Check the QN spelling ` +
|
|
1630
|
-
`(expected "<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
1631
|
-
);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
if (screen.type === "dashboard") {
|
|
1640
|
-
validateDashboardScreen(feature.name, screenId, screen, featureMap);
|
|
1641
|
-
continue;
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
if (screen.type === "configEdit") {
|
|
1645
|
-
// configEdit: layout/fields wie actionForm validieren, plus
|
|
1646
|
-
// Cross-Check dass jeder qualifizierte Config-Key registriert
|
|
1647
|
-
// ist und der scope mit dem Key matcht.
|
|
1648
|
-
const fieldNames = new Set(Object.keys(screen.fields));
|
|
1649
|
-
if (fieldNames.size === 0) {
|
|
1650
|
-
throw new Error(
|
|
1651
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) has empty fields map — ` +
|
|
1652
|
-
`declare at least one field.`,
|
|
1653
|
-
);
|
|
1654
|
-
}
|
|
1655
|
-
for (const [fname, fdef] of Object.entries(screen.fields)) {
|
|
1656
|
-
// @cast-boundary schema-walk — feature-config inspection
|
|
1657
|
-
const ftype = (fdef as { type?: unknown }).type;
|
|
1658
|
-
if (typeof ftype !== "string" || ftype.length === 0) {
|
|
1659
|
-
throw new Error(
|
|
1660
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) field "${fname}" has no ` +
|
|
1661
|
-
`\`type\` set. Each field must declare a type (e.g. "text", "number", "select").`,
|
|
1662
|
-
);
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
if (screen.layout.sections.length === 0) {
|
|
1666
|
-
throw new Error(
|
|
1667
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) has an empty sections list — ` +
|
|
1668
|
-
`declare at least one section.`,
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
for (const section of screen.layout.sections) {
|
|
1672
|
-
if (isExtensionEditSection(section)) {
|
|
1673
|
-
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
1674
|
-
throw new Error(
|
|
1675
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) extension section ` +
|
|
1676
|
-
`"${section.title}" has no component — declare a react/native component marker.`,
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1679
|
-
continue;
|
|
1680
|
-
}
|
|
1681
|
-
if (section.kind === "relatedList") {
|
|
1682
|
-
throw new Error(
|
|
1683
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) relatedList section ` +
|
|
1684
|
-
`"${section.title}" is not supported — relatedList is a projectionDetail-only ` +
|
|
1685
|
-
`primitive (fw#2166).`,
|
|
1686
|
-
);
|
|
1687
|
-
}
|
|
1688
|
-
if (isWriteFormEditSection(section)) {
|
|
1689
|
-
throw new Error(
|
|
1690
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) writeForm section ` +
|
|
1691
|
-
`"${section.title}" is not supported — writeForm is a projectionDetail-only primitive.`,
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
validateFieldsXorGroups(
|
|
1695
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit)`,
|
|
1696
|
-
section,
|
|
1697
|
-
);
|
|
1698
|
-
for (const fieldSpec of sectionFieldSpecs(section)) {
|
|
1699
|
-
const normalized = normalizeEditField(fieldSpec);
|
|
1700
|
-
if (!fieldNames.has(normalized.field)) {
|
|
1701
|
-
throw new Error(
|
|
1702
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) layout references unknown ` +
|
|
1703
|
-
`field "${normalized.field}". Known fields: ${[...fieldNames].sort().join(", ")}`,
|
|
1704
|
-
);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
validateWizardLayout(feature.name, screenId, "configEdit", screen.layout, featureMap);
|
|
1709
|
-
// configKeys: jeder fieldName muss einen Mapping-Eintrag haben,
|
|
1710
|
-
// jeder qualifizierte Key muss in der Registry existieren.
|
|
1711
|
-
for (const fname of fieldNames) {
|
|
1712
|
-
const qualified = screen.configKeys[fname];
|
|
1713
|
-
if (qualified === undefined) {
|
|
1714
|
-
throw new Error(
|
|
1715
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) field "${fname}" hat ` +
|
|
1716
|
-
`keinen Eintrag in configKeys-Map. Jedes deklarierte Field braucht ein Mapping zu ` +
|
|
1717
|
-
`einem qualifizierten Config-Key (\`<feature>:config:<short>\`).`,
|
|
1718
|
-
);
|
|
1719
|
-
}
|
|
1720
|
-
if (!allConfigKeyQns.has(qualified)) {
|
|
1721
|
-
throw new Error(
|
|
1722
|
-
`[Feature ${feature.name}] Screen "${screenId}" (configEdit) field "${fname}" → ` +
|
|
1723
|
-
`Config-Key "${qualified}" ist in keiner Feature-Registry deklariert. Tippfehler? ` +
|
|
1724
|
-
`Erwartetes Format: "<feature>:config:<short>". Bekannte Keys: ${
|
|
1725
|
-
[...allConfigKeyQns].sort().join(", ") || "(keine)"
|
|
1726
|
-
}`,
|
|
1727
|
-
);
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
continue;
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
if (screen.type === "actionForm") {
|
|
1734
|
-
validateInlineFormScreen(
|
|
1735
|
-
feature,
|
|
1736
|
-
screenId,
|
|
1737
|
-
screen,
|
|
1738
|
-
"actionForm",
|
|
1739
|
-
allWriteHandlerQns,
|
|
1740
|
-
allScreenQns,
|
|
1741
|
-
featureMap,
|
|
1742
|
-
);
|
|
1743
|
-
continue;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
if (screen.type === "secretMint") {
|
|
1747
|
-
validateInlineFormScreen(
|
|
1748
|
-
feature,
|
|
1749
|
-
screenId,
|
|
1750
|
-
screen,
|
|
1751
|
-
"secretMint",
|
|
1752
|
-
allWriteHandlerQns,
|
|
1753
|
-
allScreenQns,
|
|
1754
|
-
featureMap,
|
|
1755
|
-
);
|
|
1756
|
-
if (screen.reveal.fields.length === 0) {
|
|
1757
|
-
throw new Error(
|
|
1758
|
-
`[Feature ${feature.name}] Screen "${screenId}" (secretMint) has an empty reveal.fields ` +
|
|
1759
|
-
`list — declare at least one field to reveal.`,
|
|
1760
|
-
);
|
|
1761
|
-
}
|
|
1762
|
-
const revealFieldNames = new Set<string>();
|
|
1763
|
-
for (const revealField of screen.reveal.fields) {
|
|
1764
|
-
if (typeof revealField.field !== "string" || revealField.field.trim() === "") {
|
|
1765
|
-
throw new Error(
|
|
1766
|
-
`[Feature ${feature.name}] Screen "${screenId}" (secretMint) has a reveal.fields entry ` +
|
|
1767
|
-
`with an empty or non-string "field".`,
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
if (revealFieldNames.has(revealField.field)) {
|
|
1771
|
-
throw new Error(
|
|
1772
|
-
`[Feature ${feature.name}] Screen "${screenId}" (secretMint) reveal.fields has a ` +
|
|
1773
|
-
`duplicate field "${revealField.field}" — each revealed field must be unique.`,
|
|
1774
|
-
);
|
|
1775
|
-
}
|
|
1776
|
-
revealFieldNames.add(revealField.field);
|
|
1777
|
-
if (typeof revealField.label !== "string" || revealField.label.trim() === "") {
|
|
1778
|
-
throw new Error(
|
|
1779
|
-
`[Feature ${feature.name}] Screen "${screenId}" (secretMint) reveal.fields entry ` +
|
|
1780
|
-
`"${revealField.field}" has an empty or non-string "label".`,
|
|
1781
|
-
);
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
validateSecretMintConfirm(feature, screenId, screen, allWriteHandlerQns);
|
|
1785
|
-
continue;
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
// secretsEdit is generator-only (buildConfigFeatureSchema) — never
|
|
1789
|
-
// authored via r.screen, so feature.screens never actually carries one.
|
|
1790
|
-
// Kept here only so this loop's type narrowing stays exhaustive.
|
|
1791
|
-
if (screen.type === "secretsEdit") continue;
|
|
1792
|
-
|
|
1793
|
-
// entityList / entityEdit: entity-refs are feature-local.
|
|
1794
|
-
const entityDef = feature.entities?.[screen.entity];
|
|
1795
|
-
if (!entityDef) {
|
|
1796
|
-
const known =
|
|
1797
|
-
Object.keys(feature.entities ?? {})
|
|
1798
|
-
.sort()
|
|
1799
|
-
.join(", ") || "(none)";
|
|
1800
|
-
const crossFeature = findEntityFeature(screen.entity, featureMap);
|
|
1801
|
-
const hint = crossFeature
|
|
1802
|
-
? ` Entity "${screen.entity}" is owned by feature "${crossFeature}" — cross-feature screen ownership is not supported.`
|
|
1803
|
-
: "";
|
|
1804
|
-
throw new Error(
|
|
1805
|
-
`[Feature ${feature.name}] Screen "${screenId}" references entity "${screen.entity}" ` +
|
|
1806
|
-
`which is not declared in this feature (known: ${known}).${hint}`,
|
|
1807
|
-
);
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
const fieldNames = new Set(Object.keys(entityDef.fields));
|
|
1811
|
-
// List columns may also name a read-time derived field (not a stored
|
|
1812
|
-
// column). Allowed for display; deliberately NOT added to `fieldNames`, so
|
|
1813
|
-
// defaultSort/filter on a derived field still fails — server-side sort over
|
|
1814
|
-
// a non-column is a silent no-op (see DerivedFieldDef).
|
|
1815
|
-
const columnFieldNames =
|
|
1816
|
-
entityDef.derivedFields !== undefined
|
|
1817
|
-
? new Set([...fieldNames, ...Object.keys(entityDef.derivedFields)])
|
|
1818
|
-
: fieldNames;
|
|
1819
|
-
const rowMeta = rowMetaFieldNames(entityDef.softDelete ?? false);
|
|
1820
|
-
if (screen.type === "entityList") {
|
|
1821
|
-
// Empty column list would render as a blank table — almost always the
|
|
1822
|
-
// sign of an in-progress screen the author forgot to fill in. Fail
|
|
1823
|
-
// loud: ui-core's computeListViewModel can't do anything useful with
|
|
1824
|
-
// zero columns either.
|
|
1825
|
-
if (screen.columns.length === 0) {
|
|
1826
|
-
throw new Error(
|
|
1827
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) has an empty columns list — ` +
|
|
1828
|
-
`declare at least one column.`,
|
|
1829
|
-
);
|
|
1830
|
-
}
|
|
1831
|
-
for (const col of screen.columns) {
|
|
1832
|
-
const normalized = normalizeListColumn(col);
|
|
1833
|
-
// A row-meta column (id/tenantId/version/insertedAt/modifiedAt/...) is a
|
|
1834
|
-
// real DB column without being a declared entity field — e.g. a
|
|
1835
|
-
// SystemAdmin cross-tenant list exposing tenantId. Accept it like a
|
|
1836
|
-
// regular field name — but only the columns computeListViewModel can
|
|
1837
|
-
// actually render (LIST_ROW_META_COLUMN_NAMES), not the wider
|
|
1838
|
-
// softDelete-aware rowMeta set used below for payload/visible refs.
|
|
1839
|
-
//
|
|
1840
|
-
// A virtual presentational column (drawn by a columnRenderer component
|
|
1841
|
-
// from the row, e.g. tag chips) needs BOTH a label AND a renderer —
|
|
1842
|
-
// renderer is what actually makes it "virtual" (label alone still
|
|
1843
|
-
// needs list.ts's virtual-branch to have something to draw; without
|
|
1844
|
-
// a renderer the column would render nothing). A column with neither
|
|
1845
|
-
// matching a real field/row-meta column NOR a renderer is a typo worth
|
|
1846
|
-
// failing the boot.
|
|
1847
|
-
if (
|
|
1848
|
-
!columnFieldNames.has(normalized.field) &&
|
|
1849
|
-
!LIST_ROW_META_COLUMN_NAMES.has(normalized.field) &&
|
|
1850
|
-
!(normalized.label !== undefined && normalized.renderer !== undefined)
|
|
1851
|
-
) {
|
|
1852
|
-
throw new Error(
|
|
1853
|
-
buildUnknownFieldMessage(
|
|
1854
|
-
feature.name,
|
|
1855
|
-
screenId,
|
|
1856
|
-
normalized.field,
|
|
1857
|
-
screen.entity,
|
|
1858
|
-
columnFieldNames,
|
|
1859
|
-
),
|
|
1860
|
-
);
|
|
1861
|
-
}
|
|
1862
|
-
validateColumnRendererForm(feature.name, screenId, normalized);
|
|
1863
|
-
}
|
|
1864
|
-
// Pagination/Sort/Search-Validierung: Author-Fehler beim Boot
|
|
1865
|
-
// fangen, damit kein "warum kommt die Liste leer / falsch
|
|
1866
|
-
// sortiert"-Debug-Cycle zur Laufzeit losgeht.
|
|
1867
|
-
if (screen.pageSize !== undefined && screen.pageSize <= 0) {
|
|
1868
|
-
throw new Error(
|
|
1869
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) has pageSize=${screen.pageSize} — ` +
|
|
1870
|
-
`must be a positive integer.`,
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
if (screen.defaultSort !== undefined) {
|
|
1874
|
-
const sortField = screen.defaultSort.field;
|
|
1875
|
-
if (!fieldNames.has(sortField)) {
|
|
1876
|
-
throw new Error(
|
|
1877
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) defaultSort references unknown ` +
|
|
1878
|
-
`field "${sortField}". Known fields: ${[...fieldNames].sort().join(", ")}`,
|
|
1879
|
-
);
|
|
1880
|
-
}
|
|
1881
|
-
// sortable: true Pflicht — verhindert dass das UI auf einer
|
|
1882
|
-
// Spalte sortiert, die Server-Side gar keinen DB-Index hat
|
|
1883
|
-
// oder im Schema absichtlich nicht sortiert werden soll
|
|
1884
|
-
// (Audit-Felder, Computed-Werte). `sortable` lebt heute nur
|
|
1885
|
-
// auf TextFieldDef; "in"-narrow lässt das auch für andere
|
|
1886
|
-
// Field-Types ohne explizites Flag durchfallen, was ok ist:
|
|
1887
|
-
// Number/Date sind natürlich sortierbar, der Author kann sie
|
|
1888
|
-
// im Author-Code als sortable markieren wenn das Field-Type
|
|
1889
|
-
// es trägt (Erweiterung folgt).
|
|
1890
|
-
const fieldDef = entityDef.fields[sortField];
|
|
1891
|
-
const isSortable =
|
|
1892
|
-
fieldDef !== undefined && "sortable" in fieldDef && fieldDef.sortable === true;
|
|
1893
|
-
if (!isSortable) {
|
|
1894
|
-
throw new Error(
|
|
1895
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) defaultSort.field "${sortField}" ` +
|
|
1896
|
-
`is not sortable. Set sortable: true on the field definition or pick another field.`,
|
|
1897
|
-
);
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
// Screen-Filter (Tier 2.7c) — drei Layer Author-Code-Check:
|
|
1901
|
-
// 1) Field existiert auf der Entity (Tippfehler = leere Liste
|
|
1902
|
-
// statt Crash; Boot-Fail ist deutlich besser).
|
|
1903
|
-
// 2) Field hat `filterable: true` (Author opt-in, analog zu
|
|
1904
|
-
// `sortable`). Verhindert dass Audit-/Computed-/encrypted-
|
|
1905
|
-
// Felder unbeabsichtigt filterbar werden.
|
|
1906
|
-
// 3) Op passt zum Field-Type. Lt/gt auf text-Feldern → Boot-
|
|
1907
|
-
// Fail mit Hinweis statt String-Sort-Surprise zur Laufzeit.
|
|
1908
|
-
// Außerdem: "in" verlangt readonly Array.
|
|
1909
|
-
if (screen.filter !== undefined) {
|
|
1910
|
-
const filterField = screen.filter.field;
|
|
1911
|
-
if (!fieldNames.has(filterField)) {
|
|
1912
|
-
throw new Error(
|
|
1913
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) filter references unknown ` +
|
|
1914
|
-
`field "${filterField}". Known fields: ${[...fieldNames].sort().join(", ")}`,
|
|
1915
|
-
);
|
|
1916
|
-
}
|
|
1917
|
-
const fieldDef = entityDef.fields[filterField];
|
|
1918
|
-
if (fieldDef !== undefined && !isFieldFilterable(fieldDef)) {
|
|
1919
|
-
throw new Error(
|
|
1920
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) filter references field ` +
|
|
1921
|
-
`"${filterField}" which is not filterable. Set filterable: true on the field ` +
|
|
1922
|
-
`definition or pick another field.`,
|
|
1923
|
-
);
|
|
1924
|
-
}
|
|
1925
|
-
if (fieldDef !== undefined) {
|
|
1926
|
-
const allowedOps = getAllowedFilterOps(fieldDef);
|
|
1927
|
-
if (!allowedOps.includes(screen.filter.op)) {
|
|
1928
|
-
throw new Error(
|
|
1929
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) filter.op ` +
|
|
1930
|
-
`"${screen.filter.op}" is not allowed on field "${filterField}" ` +
|
|
1931
|
-
`(type "${fieldDef.type}"). Allowed ops: ${allowedOps.join(", ") || "(none)"}.`,
|
|
1932
|
-
);
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
if (screen.filter.op === "in" && !Array.isArray(screen.filter.value)) {
|
|
1936
|
-
throw new Error(
|
|
1937
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) filter.op "in" requires ` +
|
|
1938
|
-
`filter.value to be a readonly array.`,
|
|
1939
|
-
);
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
// Tier 2.7e-1: rowActions pinnen — navigate-target existiert (selbes
|
|
1943
|
-
// Feature), writeHandler-QN ist registriert. Tippfehler fallen sonst
|
|
1944
|
-
// erst beim ersten Klick als "Screen not found" / 404 auf.
|
|
1945
|
-
if (screen.rowActions !== undefined) {
|
|
1946
|
-
for (const action of screen.rowActions) {
|
|
1947
|
-
if (action.kind === "navigate") {
|
|
1948
|
-
const target = resolveRowActionNavigateTarget(
|
|
1949
|
-
feature.name,
|
|
1950
|
-
screenId,
|
|
1951
|
-
"entityList",
|
|
1952
|
-
"rowAction",
|
|
1953
|
-
action,
|
|
1954
|
-
allScreenQns,
|
|
1955
|
-
navTargetShortIds,
|
|
1956
|
-
screensByShortId,
|
|
1957
|
-
detailForScreens,
|
|
1958
|
-
);
|
|
1959
|
-
// The renderer's default-entityId fallback (row["id"]) only fires
|
|
1960
|
-
// for a same-feature entityEdit target — it can't safely guess
|
|
1961
|
-
// the id for a screen owned by a different feature. Cross-feature
|
|
1962
|
-
// + entityEdit therefore MUST set an explicit entityId, or the
|
|
1963
|
-
// edit screen silently opens with no entity context at runtime.
|
|
1964
|
-
// Entity-targets (fw#2228) are exempt: the renderer always
|
|
1965
|
-
// supplies an id for them (explicit entityId, else row["id"]),
|
|
1966
|
-
// regardless of which feature the resolved detailFor screen
|
|
1967
|
-
// belongs to.
|
|
1968
|
-
if (
|
|
1969
|
-
action.screen !== undefined &&
|
|
1970
|
-
target !== undefined &&
|
|
1971
|
-
target.featureName !== feature.name &&
|
|
1972
|
-
target.screen.type === "entityEdit" &&
|
|
1973
|
-
action.entityId === undefined
|
|
1974
|
-
) {
|
|
1975
|
-
throw new Error(
|
|
1976
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) rowAction "${action.id}" ` +
|
|
1977
|
-
`navigates cross-feature to entityEdit screen "${action.screen}" (feature ` +
|
|
1978
|
-
`"${target.featureName}") without an explicit entityId field — the renderer's ` +
|
|
1979
|
-
`same-feature row["id"] fallback does not apply across features. Set entityId to ` +
|
|
1980
|
-
`the row field that names the target entity's id.`,
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
|
-
// params only have a reader in actionForm and entityEdit-CREATE;
|
|
1984
|
-
// on projectionDetail/dashboard/configEdit/entityEdit-update they
|
|
1985
|
-
// are silently ignored at runtime. `custom` is deliberately
|
|
1986
|
-
// exempt: it renders an app-registered component the framework
|
|
1987
|
-
// has no visibility into — the author may read nav.searchParams
|
|
1988
|
-
// directly (real example: publicstatus's MonitorDetailScreen
|
|
1989
|
-
// does exactly that), so flagging it would be a false positive
|
|
1990
|
-
// on working code, not a caught bug.
|
|
1991
|
-
//
|
|
1992
|
-
// Whether an entityEdit target lands in create or update mode is
|
|
1993
|
-
// decided by the same rule the renderer's runNavigate() uses: an
|
|
1994
|
-
// explicit entityId always forces update mode, and (absent an
|
|
1995
|
-
// explicit entityId) a same-entity target gets row["id"] auto-
|
|
1996
|
-
// injected — only a cross-entity target with no explicit
|
|
1997
|
-
// entityId reaches create.
|
|
1998
|
-
validateRowActionNavigateParams(
|
|
1999
|
-
feature.name,
|
|
2000
|
-
screenId,
|
|
2001
|
-
"entityList",
|
|
2002
|
-
screen.entity,
|
|
2003
|
-
action,
|
|
2004
|
-
target,
|
|
2005
|
-
);
|
|
2006
|
-
} else if (action.kind === "drawer") {
|
|
2007
|
-
validateDrawerTargetAction(
|
|
2008
|
-
feature.name,
|
|
2009
|
-
screenId,
|
|
2010
|
-
"entityList",
|
|
2011
|
-
"rowAction",
|
|
2012
|
-
action,
|
|
2013
|
-
feature.screens,
|
|
2014
|
-
);
|
|
2015
|
-
} else {
|
|
2016
|
-
if (!allWriteHandlerQns.has(action.handler)) {
|
|
2017
|
-
throw new Error(
|
|
2018
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) rowAction "${action.id}" ` +
|
|
2019
|
-
`handler "${action.handler}" is not a registered write-handler. Check the QN spelling ` +
|
|
2020
|
-
`(expected "<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
2021
|
-
);
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
validateActionFieldRefs(
|
|
2025
|
-
feature.name,
|
|
2026
|
-
screenId,
|
|
2027
|
-
"rowAction",
|
|
2028
|
-
action.id,
|
|
2029
|
-
action,
|
|
2030
|
-
fieldNames,
|
|
2031
|
-
rowMeta,
|
|
2032
|
-
);
|
|
2033
|
-
}
|
|
2034
|
-
validateAtMostOneRowClick(feature.name, screenId, "entityList", screen.rowActions);
|
|
2035
|
-
}
|
|
2036
|
-
// Tier 2.7e-2: toolbarActions — analog zu rowActions, aber bisher
|
|
2037
|
-
// ohne Validator. Typo'd navigate-targets und unregistrierte
|
|
2038
|
-
// writeHandler-QNs fallen bis hierhin erst beim Klick auf.
|
|
2039
|
-
if (screen.toolbarActions !== undefined) {
|
|
2040
|
-
for (const action of screen.toolbarActions) {
|
|
2041
|
-
if (action.kind === "navigate") {
|
|
2042
|
-
const candidateQn = qualifyEntityName(feature.name, "screen", action.screen);
|
|
2043
|
-
if (!allScreenQns.has(candidateQn) && !navTargetShortIds.has(action.screen)) {
|
|
2044
|
-
throw new Error(
|
|
2045
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) toolbarAction "${action.id}" ` +
|
|
2046
|
-
`navigate-target "${action.screen}" does not resolve to a registered screen in any feature.`,
|
|
2047
|
-
);
|
|
2048
|
-
}
|
|
2049
|
-
if (action.tab !== undefined) {
|
|
2050
|
-
resolveRowActionNavigateTarget(
|
|
2051
|
-
feature.name,
|
|
2052
|
-
screenId,
|
|
2053
|
-
"entityList",
|
|
2054
|
-
"toolbarAction",
|
|
2055
|
-
action,
|
|
2056
|
-
allScreenQns,
|
|
2057
|
-
navTargetShortIds,
|
|
2058
|
-
screensByShortId,
|
|
2059
|
-
detailForScreens,
|
|
2060
|
-
);
|
|
2061
|
-
}
|
|
2062
|
-
} else if (action.kind === "drawer") {
|
|
2063
|
-
validateDrawerTargetAction(
|
|
2064
|
-
feature.name,
|
|
2065
|
-
screenId,
|
|
2066
|
-
"entityList",
|
|
2067
|
-
"toolbarAction",
|
|
2068
|
-
action,
|
|
2069
|
-
feature.screens,
|
|
2070
|
-
);
|
|
2071
|
-
} else {
|
|
2072
|
-
if (!allWriteHandlerQns.has(action.handler)) {
|
|
2073
|
-
throw new Error(
|
|
2074
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityList) toolbarAction "${action.id}" ` +
|
|
2075
|
-
`handler "${action.handler}" is not a registered write-handler. Check the QN spelling ` +
|
|
2076
|
-
`(expected "<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
2077
|
-
);
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
validateActionFieldRefs(
|
|
2081
|
-
feature.name,
|
|
2082
|
-
screenId,
|
|
2083
|
-
"toolbarAction",
|
|
2084
|
-
action.id,
|
|
2085
|
-
action,
|
|
2086
|
-
fieldNames,
|
|
2087
|
-
rowMeta,
|
|
2088
|
-
);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
} else {
|
|
2092
|
-
// Same rationale as the columns check: an entityEdit layout with zero
|
|
2093
|
-
// sections (or sections without any fields) renders as nothing — reject
|
|
2094
|
-
// at boot so the author sees it before the blank form surprises them.
|
|
2095
|
-
if (screen.layout.sections.length === 0) {
|
|
2096
|
-
throw new Error(
|
|
2097
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit) has an empty sections list — ` +
|
|
2098
|
-
`declare at least one section.`,
|
|
2099
|
-
);
|
|
2100
|
-
}
|
|
2101
|
-
for (const section of screen.layout.sections) {
|
|
2102
|
-
if (section.actions !== undefined) {
|
|
2103
|
-
const sectionLabel = section.title ?? section.id ?? "(untitled)";
|
|
2104
|
-
for (const action of section.actions) {
|
|
2105
|
-
validateActionHasIcon(
|
|
2106
|
-
feature.name,
|
|
2107
|
-
screenId,
|
|
2108
|
-
"entityEdit",
|
|
2109
|
-
`section "${sectionLabel}" action`,
|
|
2110
|
-
action,
|
|
2111
|
-
);
|
|
2112
|
-
if (action.kind === "navigate" && action.tab !== undefined) {
|
|
2113
|
-
resolveRowActionNavigateTarget(
|
|
2114
|
-
feature.name,
|
|
2115
|
-
screenId,
|
|
2116
|
-
"entityEdit",
|
|
2117
|
-
`section "${sectionLabel}" action`,
|
|
2118
|
-
action,
|
|
2119
|
-
allScreenQns,
|
|
2120
|
-
navTargetShortIds,
|
|
2121
|
-
screensByShortId,
|
|
2122
|
-
detailForScreens,
|
|
2123
|
-
);
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
if (isExtensionEditSection(section)) {
|
|
2128
|
-
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
2129
|
-
throw new Error(
|
|
2130
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit) extension section ` +
|
|
2131
|
-
`"${section.title}" has no component — declare a react/native component marker.`,
|
|
2132
|
-
);
|
|
2133
|
-
}
|
|
2134
|
-
continue;
|
|
2135
|
-
}
|
|
2136
|
-
if (section.kind === "relatedList") {
|
|
2137
|
-
throw new Error(
|
|
2138
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit) relatedList section ` +
|
|
2139
|
-
`"${section.title}" is not supported — relatedList is a projectionDetail-only ` +
|
|
2140
|
-
`primitive (fw#2166).`,
|
|
2141
|
-
);
|
|
2142
|
-
}
|
|
2143
|
-
if (isWriteFormEditSection(section)) {
|
|
2144
|
-
throw new Error(
|
|
2145
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit) writeForm section ` +
|
|
2146
|
-
`"${section.title}" is not supported — writeForm is a projectionDetail-only primitive.`,
|
|
2147
|
-
);
|
|
2148
|
-
}
|
|
2149
|
-
validateFieldsXorGroups(
|
|
2150
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit)`,
|
|
2151
|
-
section,
|
|
2152
|
-
);
|
|
2153
|
-
for (const fieldSpec of sectionFieldSpecs(section)) {
|
|
2154
|
-
const normalized = normalizeEditField(fieldSpec);
|
|
2155
|
-
if (!fieldNames.has(normalized.field)) {
|
|
2156
|
-
throw new Error(
|
|
2157
|
-
buildUnknownFieldMessage(
|
|
2158
|
-
feature.name,
|
|
2159
|
-
screenId,
|
|
2160
|
-
normalized.field,
|
|
2161
|
-
screen.entity,
|
|
2162
|
-
fieldNames,
|
|
2163
|
-
),
|
|
2164
|
-
);
|
|
2165
|
-
}
|
|
2166
|
-
validateNoWidgetRequiredField(feature.name, screenId, entityDef, normalized);
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
validateWizardLayout(feature.name, screenId, "entityEdit", screen.layout, featureMap);
|
|
2170
|
-
// Header actions reuse RowAction — same shape and same checks as
|
|
2171
|
-
// projectionDetail's `actions` above (the loaded record stands in
|
|
2172
|
-
// for the row). `rowClick` is rejected outright — entityEdit has no
|
|
2173
|
-
// row to click either.
|
|
2174
|
-
if (screen.actions !== undefined) {
|
|
2175
|
-
for (const action of screen.actions) {
|
|
2176
|
-
if (action.kind === "navigate" && action.rowClick === true) {
|
|
2177
|
-
throw new Error(
|
|
2178
|
-
`[Feature ${feature.name}] Screen "${qualifyEntityName(feature.name, "screen", screenId)}" ` +
|
|
2179
|
-
`(entityEdit) action "${action.id}" sets rowClick: true — there is no row to click ` +
|
|
2180
|
-
`on an edit screen. Remove rowClick.`,
|
|
2181
|
-
);
|
|
2182
|
-
}
|
|
2183
|
-
if (action.kind === "navigate") {
|
|
2184
|
-
const target = resolveRowActionNavigateTarget(
|
|
2185
|
-
feature.name,
|
|
2186
|
-
screenId,
|
|
2187
|
-
"entityEdit",
|
|
2188
|
-
"action",
|
|
2189
|
-
action,
|
|
2190
|
-
allScreenQns,
|
|
2191
|
-
navTargetShortIds,
|
|
2192
|
-
screensByShortId,
|
|
2193
|
-
detailForScreens,
|
|
2194
|
-
);
|
|
2195
|
-
validateRowActionNavigateParams(
|
|
2196
|
-
feature.name,
|
|
2197
|
-
screenId,
|
|
2198
|
-
"entityEdit",
|
|
2199
|
-
screen.entity,
|
|
2200
|
-
action,
|
|
2201
|
-
target,
|
|
2202
|
-
);
|
|
2203
|
-
} else if (action.kind === "drawer") {
|
|
2204
|
-
validateDrawerTargetAction(
|
|
2205
|
-
feature.name,
|
|
2206
|
-
screenId,
|
|
2207
|
-
"entityEdit",
|
|
2208
|
-
"action",
|
|
2209
|
-
action,
|
|
2210
|
-
feature.screens,
|
|
2211
|
-
);
|
|
2212
|
-
} else {
|
|
2213
|
-
if (!allWriteHandlerQns.has(action.handler)) {
|
|
2214
|
-
throw new Error(
|
|
2215
|
-
`[Feature ${feature.name}] Screen "${screenId}" (entityEdit) action "${action.id}" ` +
|
|
2216
|
-
`handler "${action.handler}" is not a registered write-handler. Check the QN spelling ` +
|
|
2217
|
-
`(expected "<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`,
|
|
2218
|
-
);
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
if (screen.redirect !== undefined) {
|
|
2224
|
-
validateRedirectTarget(feature, screenId, "entityEdit", screen.redirect, allScreenQns);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
// Panel-getrieben, keine Entity — Struktur-Checks (eindeutige Panel-Ids,
|
|
2231
|
-
// non-empty Queries/Columns/valueField); die Query-Contracts (Stat-Record,
|
|
2232
|
-
// Points-Envelope, Paged-Rows) werden zur Render-Zeit aufgelöst.
|
|
2233
|
-
function validateDashboardScreen(
|
|
2234
|
-
featureName: string,
|
|
2235
|
-
screenId: string,
|
|
2236
|
-
screen: DashboardScreenDefinition,
|
|
2237
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
2238
|
-
): void {
|
|
2239
|
-
if (screen.panels.length === 0) {
|
|
2240
|
-
throw new Error(
|
|
2241
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) has an empty panels list — ` +
|
|
2242
|
-
`declare at least one panel.`,
|
|
2243
|
-
);
|
|
2244
|
-
}
|
|
2245
|
-
const panelIds = new Set<string>();
|
|
2246
|
-
const addPanelId = (id: string, context: string): void => {
|
|
2247
|
-
if (panelIds.has(id)) {
|
|
2248
|
-
throw new Error(
|
|
2249
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) has duplicate panel id "${id}" (${context}).`,
|
|
2250
|
-
);
|
|
2251
|
-
}
|
|
2252
|
-
panelIds.add(id);
|
|
2253
|
-
};
|
|
2254
|
-
|
|
2255
|
-
for (const panel of screen.panels) {
|
|
2256
|
-
addPanelId(panel.id, panel.kind);
|
|
2257
|
-
if (panel.kind === "stat-group") {
|
|
2258
|
-
validateDashboardStatGroupPanel(featureName, screenId, panel, addPanelId);
|
|
2259
|
-
} else if (panel.kind === "custom") {
|
|
2260
|
-
validateDashboardCustomPanel(featureName, screenId, panel);
|
|
2261
|
-
} else if (panel.kind === "screen") {
|
|
2262
|
-
validateDashboardScreenPanel(featureName, screenId, panel, featureMap);
|
|
2263
|
-
} else {
|
|
2264
|
-
validateDashboardQueryPanel(featureName, screenId, panel);
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
if (screen.filter !== undefined) {
|
|
2269
|
-
validateDashboardFilterDefinition(featureName, screenId, screen.filter);
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
const EMBEDDABLE_SCREEN_TYPES: ReadonlySet<ScreenDefinition["type"]> = new Set([
|
|
2274
|
-
"projectionList",
|
|
2275
|
-
"actionForm",
|
|
2276
|
-
"secretMint",
|
|
2277
|
-
"configEdit",
|
|
2278
|
-
"secretsEdit",
|
|
2279
|
-
]);
|
|
2280
|
-
|
|
2281
|
-
function validateDashboardScreenPanel(
|
|
2282
|
-
featureName: string,
|
|
2283
|
-
screenId: string,
|
|
2284
|
-
panel: DashboardScreenPanel,
|
|
2285
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
2286
|
-
): void {
|
|
2287
|
-
const context = `[Feature ${featureName}] Screen "${screenId}" (dashboard) screen-panel "${panel.id}"`;
|
|
2288
|
-
const targetQn = resolveScreenTargetQn(featureName, panel.screen);
|
|
2289
|
-
const [targetFeatureName = "", , targetShortId = ""] = targetQn.split(":");
|
|
2290
|
-
const target = featureMap.get(targetFeatureName)?.screens[targetShortId];
|
|
2291
|
-
if (target === undefined) {
|
|
2292
|
-
throw new Error(
|
|
2293
|
-
`${context} screen "${panel.screen}" does not resolve to a registered screen (checked "${targetQn}"). ` +
|
|
2294
|
-
`Use a same-feature short id or a cross-feature QN "<feature>:screen:<id>".`,
|
|
2295
|
-
);
|
|
2296
|
-
}
|
|
2297
|
-
if (!EMBEDDABLE_SCREEN_TYPES.has(target.type)) {
|
|
2298
|
-
throw new Error(
|
|
2299
|
-
`${context} embeds "${targetQn}" of type "${target.type}", which can't be embedded — allowed: ` +
|
|
2300
|
-
`${[...EMBEDDABLE_SCREEN_TYPES].join(", ")}. Use a custom panel for app components.`,
|
|
2301
|
-
);
|
|
2302
|
-
}
|
|
2303
|
-
const visibleWhen = panel.visibleWhen;
|
|
2304
|
-
if (visibleWhen !== undefined && (visibleWhen.query === "" || visibleWhen.field === "")) {
|
|
2305
|
-
throw new Error(`${context} visibleWhen needs a non-empty query and field.`);
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
function validateDashboardStatGroupPanel(
|
|
2310
|
-
featureName: string,
|
|
2311
|
-
screenId: string,
|
|
2312
|
-
panel: DashboardStatGroupPanel,
|
|
2313
|
-
addPanelId: (id: string, context: string) => void,
|
|
2314
|
-
): void {
|
|
2315
|
-
if (panel.stats.length === 0) {
|
|
2316
|
-
throw new Error(
|
|
2317
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) stat-group "${panel.id}" has an empty stats list.`,
|
|
2318
|
-
);
|
|
2319
|
-
}
|
|
2320
|
-
for (const stat of panel.stats) {
|
|
2321
|
-
addPanelId(stat.id, "stat-group child");
|
|
2322
|
-
if (!stat.query || typeof stat.query !== "string") {
|
|
2323
|
-
throw new Error(
|
|
2324
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) stat-group "${panel.id}" child "${stat.id}" has empty or non-string query.`,
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2327
|
-
if (stat.valueField.length === 0) {
|
|
2328
|
-
throw new Error(
|
|
2329
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) stat-group "${panel.id}" child "${stat.id}" has empty valueField.`,
|
|
2330
|
-
);
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
function validateDashboardCustomPanel(
|
|
2336
|
-
featureName: string,
|
|
2337
|
-
screenId: string,
|
|
2338
|
-
panel: DashboardCustomPanel,
|
|
2339
|
-
): void {
|
|
2340
|
-
if (panel.component.react === undefined && panel.component.native === undefined) {
|
|
2341
|
-
throw new Error(
|
|
2342
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) custom-panel "${panel.id}" has no component — ` +
|
|
2343
|
-
`declare a react/native component marker.`,
|
|
2344
|
-
);
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
function validateDashboardQueryPanel(
|
|
2349
|
-
featureName: string,
|
|
2350
|
-
screenId: string,
|
|
2351
|
-
panel: Exclude<
|
|
2352
|
-
DashboardPanelDefinition,
|
|
2353
|
-
DashboardStatGroupPanel | DashboardCustomPanel | DashboardScreenPanel
|
|
2354
|
-
>,
|
|
2355
|
-
): void {
|
|
2356
|
-
if (!panel.query || typeof panel.query !== "string") {
|
|
2357
|
-
throw new Error(
|
|
2358
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) panel "${panel.id}" has empty or non-string query.`,
|
|
2359
|
-
);
|
|
2360
|
-
}
|
|
2361
|
-
if (panel.kind === "stat" && panel.valueField.length === 0) {
|
|
2362
|
-
throw new Error(
|
|
2363
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) stat-panel "${panel.id}" has empty valueField.`,
|
|
2364
|
-
);
|
|
2365
|
-
}
|
|
2366
|
-
if (panel.kind === "list") {
|
|
2367
|
-
if (panel.columns.length === 0) {
|
|
2368
|
-
throw new Error(
|
|
2369
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) list-panel "${panel.id}" has an empty columns list.`,
|
|
2370
|
-
);
|
|
2371
|
-
}
|
|
2372
|
-
for (const col of panel.columns) {
|
|
2373
|
-
validateColumnRendererForm(featureName, screenId, normalizeListColumn(col));
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
function validateDashboardFilterDefinition(
|
|
2379
|
-
featureName: string,
|
|
2380
|
-
screenId: string,
|
|
2381
|
-
filter: DashboardFilterDefinition,
|
|
2382
|
-
): void {
|
|
2383
|
-
if (filter.id.length === 0) {
|
|
2384
|
-
throw new Error(
|
|
2385
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) filter has an empty id.`,
|
|
2386
|
-
);
|
|
2387
|
-
}
|
|
2388
|
-
if (filter.label.length === 0) {
|
|
2389
|
-
throw new Error(
|
|
2390
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) filter has an empty label.`,
|
|
2391
|
-
);
|
|
2392
|
-
}
|
|
2393
|
-
const hasOptions = filter.options !== undefined;
|
|
2394
|
-
const hasOptionsQuery = filter.optionsQuery !== undefined;
|
|
2395
|
-
if (hasOptions === hasOptionsQuery) {
|
|
2396
|
-
throw new Error(
|
|
2397
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) filter must set exactly one of ` +
|
|
2398
|
-
`options/optionsQuery.`,
|
|
2399
|
-
);
|
|
2400
|
-
}
|
|
2401
|
-
if (hasOptions && (filter.options?.length ?? 0) === 0) {
|
|
2402
|
-
throw new Error(
|
|
2403
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) filter.options is empty — ` +
|
|
2404
|
-
`declare at least one option or use optionsQuery instead.`,
|
|
2405
|
-
);
|
|
2406
|
-
}
|
|
2407
|
-
if (hasOptionsQuery && filter.optionsQuery?.length === 0) {
|
|
2408
|
-
throw new Error(
|
|
2409
|
-
`[Feature ${featureName}] Screen "${screenId}" (dashboard) filter.optionsQuery is empty.`,
|
|
2410
|
-
);
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
// Form-check für ListColumn-Renderer in der PlatformComponent-Form
|
|
2415
|
-
// (`{ react: { __component: "Name" } }`). Der Server kennt die client-
|
|
2416
|
-
// seitige columnRenderers-Map nicht — also nur prüfen ob die Struktur
|
|
2417
|
-
// stimmt: wenn `react` als Object gesetzt ist, MUSS `__component` ein
|
|
2418
|
-
// nicht-leerer String sein. Ein client-seitig ausgelassener Key löst
|
|
2419
|
-
// nur eine Warnung aus, kein Boot-Fail.
|
|
2420
|
-
export function validateColumnRendererForm(
|
|
2421
|
-
featureName: string,
|
|
2422
|
-
screenId: string,
|
|
2423
|
-
column: { readonly field: string; readonly renderer?: unknown },
|
|
2424
|
-
): void {
|
|
2425
|
-
const renderer = column.renderer;
|
|
2426
|
-
// skip: nur die PlatformComponent-Form ({ react: { __component: "..." } })
|
|
2427
|
-
// wird strukturell validiert. Funktions-, String-QN- und null/undefined-
|
|
2428
|
-
// Renderer sind alle gültige andere Formen — kein Form-Fehler.
|
|
2429
|
-
if (renderer === null || typeof renderer !== "object") return;
|
|
2430
|
-
// @cast-boundary schema-walk — feature-config renderer-shape introspection
|
|
2431
|
-
const react = (renderer as { react?: unknown }).react;
|
|
2432
|
-
// skip: kein react-Branch → entweder native-only oder kein
|
|
2433
|
-
// PlatformComponent — beides außerhalb dieses Checks.
|
|
2434
|
-
if (react === undefined || react === null) return;
|
|
2435
|
-
if (typeof react !== "object") {
|
|
2436
|
-
throw new Error(
|
|
2437
|
-
`[Feature ${featureName}] Screen "${screenId}" column "${column.field}" has a renderer with ` +
|
|
2438
|
-
`a non-object \`react\` branch — expected \`{ react: { __component: "Name" } }\`.`,
|
|
2439
|
-
);
|
|
2440
|
-
}
|
|
2441
|
-
// @cast-boundary schema-walk — feature-config react-branch introspection
|
|
2442
|
-
const component = (react as { __component?: unknown }).__component;
|
|
2443
|
-
// skip: ohne __component-Schlüssel ist das keine String-Key-Form
|
|
2444
|
-
// (z.B. ein zukünftiger direkter Component-Ref); nicht unsere Domäne.
|
|
2445
|
-
if (component === undefined) return;
|
|
2446
|
-
if (typeof component !== "string" || component.length === 0) {
|
|
2447
|
-
throw new Error(
|
|
2448
|
-
`[Feature ${featureName}] Screen "${screenId}" column "${column.field}" has a renderer with ` +
|
|
2449
|
-
`\`react.__component\` = ${JSON.stringify(component)} — expected a non-empty string identifying ` +
|
|
2450
|
-
`a client-side columnRenderers entry.`,
|
|
2451
|
-
);
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
|
-
// Shared entity-target resolution for reference facets, refEntity columns
|
|
2456
|
-
// (projectionList/relatedList) and refEntity fields (projectionDetail).
|
|
2457
|
-
function assertRefTargetRegistered(
|
|
2458
|
-
prefix: string,
|
|
2459
|
-
subject: string,
|
|
2460
|
-
refTarget: string,
|
|
2461
|
-
currentFeatureName: string,
|
|
2462
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
2463
|
-
): void {
|
|
2464
|
-
const target = parseRefTarget(refTarget, currentFeatureName);
|
|
2465
|
-
const targetFeature = featureMap.get(target.featureName);
|
|
2466
|
-
if (targetFeature?.entities?.[target.entityName] === undefined) {
|
|
2467
|
-
throw new Error(
|
|
2468
|
-
`${prefix} ${subject} targets entity "${refTarget}", which does not resolve to a ` +
|
|
2469
|
-
`registered entity. Known entities in feature "${target.featureName}": ` +
|
|
2470
|
-
`${
|
|
2471
|
-
Object.keys(targetFeature?.entities ?? {})
|
|
2472
|
-
.sort()
|
|
2473
|
-
.join(", ") || "(none)"
|
|
2474
|
-
}.`,
|
|
2475
|
-
);
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
// Facets (fw#2224) — unlike filter, a field inventory IS available here: the
|
|
2480
|
-
// declared columns. A facet on a field with no column is almost always a
|
|
2481
|
-
// typo (the user never sees the field anywhere), so this is hard-checked
|
|
2482
|
-
// rather than just documented. Shared by projectionList and relatedList
|
|
2483
|
-
// (fw#2740) — `prefix` carries the caller's own screen/section message lead-in.
|
|
2484
|
-
//
|
|
2485
|
-
// `type: "reference"` is exempt from the same-name-column rule (fw akte-
|
|
2486
|
-
// bedienkonzept-2): it filters by an id field (e.g. "propertyId") but
|
|
2487
|
-
// displays a different, human-readable column (e.g. "propertyLabel") — a
|
|
2488
|
-
// column named after the filter field would almost never exist, and
|
|
2489
|
-
// requiring one anyway would force apps to add a column no design calls
|
|
2490
|
-
// for just to satisfy this check. Its own `entity` is mandatory on the type
|
|
2491
|
-
// (TS, not just this validator) and is resolved below — that explicit,
|
|
2492
|
-
// checked declaration is this facet type's field inventory, filling the
|
|
2493
|
-
// same "not just a typo" role the column-name check plays for select/
|
|
2494
|
-
// boolean facets. select/boolean facets keep the column requirement
|
|
2495
|
-
// unchanged: both filter and display the same field, so a facet with no
|
|
2496
|
-
// matching column is still almost always a typo.
|
|
2497
|
-
function validateListFacets(
|
|
2498
|
-
prefix: string,
|
|
2499
|
-
facets: readonly ListFacetSpec[],
|
|
2500
|
-
columns: readonly ListColumnSpec[],
|
|
2501
|
-
currentFeatureName: string,
|
|
2502
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
2503
|
-
): void {
|
|
2504
|
-
const columnFieldNames = new Set(columns.map((col) => normalizeListColumn(col).field));
|
|
2505
|
-
const seenFacetFields = new Set<string>();
|
|
2506
|
-
for (const facet of facets) {
|
|
2507
|
-
if (seenFacetFields.has(facet.field)) {
|
|
2508
|
-
throw new Error(`${prefix} declares facet "${facet.field}" more than once.`);
|
|
2509
|
-
}
|
|
2510
|
-
seenFacetFields.add(facet.field);
|
|
2511
|
-
if (facet.type !== "reference" && !columnFieldNames.has(facet.field)) {
|
|
2512
|
-
throw new Error(
|
|
2513
|
-
`${prefix} facet references field "${facet.field}" which is not a declared column. ` +
|
|
2514
|
-
`Known columns: ${[...columnFieldNames].sort().join(", ")}`,
|
|
2515
|
-
);
|
|
2516
|
-
}
|
|
2517
|
-
if (facet.type === "select" && facet.options.length === 0) {
|
|
2518
|
-
throw new Error(
|
|
2519
|
-
`${prefix} facet "${facet.field}" (type "select") has an empty options list — ` +
|
|
2520
|
-
`declare at least one option.`,
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2523
|
-
if (facet.type === "reference") {
|
|
2524
|
-
assertRefTargetRegistered(
|
|
2525
|
-
prefix,
|
|
2526
|
-
`facet "${facet.field}" (type "reference")`,
|
|
2527
|
-
facet.entity,
|
|
2528
|
-
currentFeatureName,
|
|
2529
|
-
featureMap,
|
|
2530
|
-
);
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
export function findEntityFeature(
|
|
2536
|
-
entityName: string,
|
|
2537
|
-
featureMap: ReadonlyMap<string, FeatureDefinition>,
|
|
2538
|
-
): string | undefined {
|
|
2539
|
-
for (const [name, feature] of featureMap) {
|
|
2540
|
-
if (feature.entities?.[entityName]) return name;
|
|
2541
|
-
}
|
|
2542
|
-
return undefined;
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
export function buildUnknownFieldMessage(
|
|
2546
|
-
featureName: string,
|
|
2547
|
-
screenId: string,
|
|
2548
|
-
fieldName: string,
|
|
2549
|
-
entityName: string,
|
|
2550
|
-
knownFields: ReadonlySet<string>,
|
|
2551
|
-
): string {
|
|
2552
|
-
const known = [...knownFields].sort().join(", ");
|
|
2553
|
-
return (
|
|
2554
|
-
`[Feature ${featureName}] Screen "${screenId}" references field "${fieldName}" ` +
|
|
2555
|
-
`which does not exist on entity "${entityName}" (known: ${known}).`
|
|
2556
|
-
);
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
export function collectScreenQns(features: readonly FeatureDefinition[]): Set<string> {
|
|
2560
|
-
const set = new Set<string>();
|
|
2561
|
-
for (const f of features) {
|
|
2562
|
-
for (const screenId of Object.keys(f.screens)) {
|
|
2563
|
-
set.add(qualifyEntityName(f.name, "screen", screenId));
|
|
2564
|
-
}
|
|
2565
|
-
}
|
|
2566
|
-
return set;
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
// Bare Screen-ids (ohne `<feature>:screen:`-Prefix) aus den qualifizierten
|
|
2570
|
-
// QNs — für die app-weite Auflösung von navigate-Targets (s. validateScreens).
|
|
2571
|
-
// Spiegelt den Runtime-Router, der bare ids feature-übergreifend matcht.
|
|
2572
|
-
export function screenShortIdsFrom(allScreenQns: ReadonlySet<string>): Set<string> {
|
|
2573
|
-
const marker = ":screen:";
|
|
2574
|
-
const set = new Set<string>();
|
|
2575
|
-
for (const qn of allScreenQns) {
|
|
2576
|
-
const at = qn.indexOf(marker);
|
|
2577
|
-
if (at !== -1) set.add(qn.slice(at + marker.length));
|
|
2578
|
-
}
|
|
2579
|
-
return set;
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
// Short screen-id → every {featureName, screen} that registers it. The
|
|
2583
|
-
// runtime router (create-app.tsx) resolves a bare navigate-target short-id by
|
|
2584
|
-
// scanning ALL features and taking the first match — so two features
|
|
2585
|
-
// registering the same short-id is a silent routing footgun (whichever
|
|
2586
|
-
// feature comes first in the app's features[] array always wins, the other
|
|
2587
|
-
// is unreachable by that id) and a prerequisite for the entityId-check below
|
|
2588
|
-
// (which target screen it resolves to must be unambiguous).
|
|
2589
|
-
export function collectScreensByShortId(
|
|
2590
|
-
features: readonly FeatureDefinition[],
|
|
2591
|
-
): Map<string, ReadonlyArray<{ readonly featureName: string; readonly screen: ScreenDefinition }>> {
|
|
2592
|
-
const map = new Map<
|
|
2593
|
-
string,
|
|
2594
|
-
Array<{ readonly featureName: string; readonly screen: ScreenDefinition }>
|
|
2595
|
-
>();
|
|
2596
|
-
for (const f of features) {
|
|
2597
|
-
for (const [screenId, screen] of Object.entries(f.screens)) {
|
|
2598
|
-
const entries = map.get(screenId) ?? [];
|
|
2599
|
-
entries.push({ featureName: f.name, screen });
|
|
2600
|
-
map.set(screenId, entries);
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
return map;
|
|
2604
|
-
}
|