@cosmicdrift/kumiko-framework 0.386.0 → 0.388.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -1
- package/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +40 -39
- package/dist/api/api-constants.js +2 -2
- package/dist/api/auth-middleware.js +7 -7
- package/dist/api/auth-rate-limiters.js +7 -3
- package/dist/api/auth-routes-mfa.js +1 -1
- package/dist/api/auth-routes-session.js +1 -1
- package/dist/api/auth-routes-shared.js +7 -3
- package/dist/api/auth-routes-signup.js +1 -1
- package/dist/api/client-error-route.js +8 -2
- package/dist/api/client-ip.js +3 -1
- package/dist/api/extra-route.js +1 -1
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/pii-leak-guard.js +4 -2
- package/dist/api/readiness.js +5 -5
- package/dist/api/redis-sse-broker.js +8 -6
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/routes.js +2 -2
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +203 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +293 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +236 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1250
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +31 -27
- package/dist/auth/index.js +4 -4
- package/dist/auth/roles.d.ts +4 -4
- package/dist/auth/roles.js +24 -24
- package/dist/compliance/duration-spec.js +18 -20
- package/dist/compliance/index.js +2 -3
- package/dist/compliance/override-schema.d.ts +4 -4
- package/dist/compliance/override-schema.js +19 -22
- package/dist/compliance/profiles.d.ts +26 -29
- package/dist/compliance/profiles.js +62 -69
- package/dist/compliance/sub-processors.d.ts +28 -30
- package/dist/compliance/sub-processors.js +17 -21
- package/dist/consumer-cli.js +4 -4
- package/dist/crypto/blind-index.js +18 -17
- package/dist/crypto/ciphertext-pattern.js +1 -1
- package/dist/crypto/event-pii.js +3 -3
- package/dist/crypto/kek-source.d.ts +1 -1
- package/dist/crypto/kek-source.js +3 -2
- package/dist/crypto/kms-adapter.d.ts +1 -1
- package/dist/crypto/kms-adapter.js +4 -6
- package/dist/crypto/kms-wiring.d.ts +1 -2
- package/dist/crypto/kms-wiring.js +2 -2
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +7 -5
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- package/dist/db/aggregate-sql.d.ts +21 -0
- package/dist/db/aggregate-sql.js +155 -0
- package/dist/db/apply-entity-event.d.ts +6 -6
- package/dist/db/apply-entity-event.js +55 -55
- package/dist/db/blind-index-cleanup.js +7 -7
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +95 -0
- package/dist/db/collect-table-metas.js +10 -11
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +12 -12
- package/dist/db/cursor.js +3 -4
- package/dist/db/dialect.d.ts +2 -2
- package/dist/db/dialect.js +3 -3
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +99 -0
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +105 -112
- package/dist/db/event-store-executor-context.d.ts +3 -2
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +112 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +203 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +50 -704
- package/dist/db/event-store-executor-write-steps.d.ts +20 -0
- package/dist/db/event-store-executor-write-steps.js +100 -0
- package/dist/db/event-store-executor-write.js +401 -510
- package/dist/db/index.d.ts +6 -2
- package/dist/db/index.js +5 -1
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/located-timestamp.d.ts +11 -11
- package/dist/db/located-timestamp.js +29 -30
- package/dist/db/migrate-generator.d.ts +1 -1
- package/dist/db/migrate-generator.js +97 -105
- package/dist/db/migrate-runner.js +29 -26
- package/dist/db/money.d.ts +6 -6
- package/dist/db/money.js +29 -27
- package/dist/db/queries/backfill-pii.js +5 -5
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/es-ops.d.ts +1 -1
- package/dist/db/queries/es-ops.js +1 -1
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/projection-rebuild.js +3 -3
- package/dist/db/queries/raw-sql.d.ts +2 -2
- package/dist/db/queries/raw-sql.js +2 -2
- package/dist/db/queries/shadow-swap.js +19 -19
- package/dist/db/query.d.ts +3 -16
- package/dist/db/query.js +8 -761
- package/dist/db/rebuild-marker.js +20 -21
- package/dist/db/reference-data.js +6 -1
- package/dist/db/replay-migration-sql.js +2 -2
- package/dist/db/schema-inspection.js +4 -5
- package/dist/db/table-builder.d.ts +1 -2
- package/dist/db/table-builder.js +137 -190
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +48 -11
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +6 -2
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-roles.js +4 -3
- package/dist/engine/boot-validator/api-ext.js +14 -14
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +4 -4
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +77 -61
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +6 -4
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +22 -20
- package/dist/engine/boot-validator/nav.js +5 -5
- package/dist/engine/boot-validator/ownership.js +12 -9
- package/dist/engine/boot-validator/parent-ref.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +258 -187
- package/dist/engine/boot-validator/projection-list-screens.js +7 -7
- package/dist/engine/boot-validator/query-refs.js +1 -1
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +23 -17
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +12 -17
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +26 -24
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +102 -869
- package/dist/engine/boot-validator/security-baseline.js +3 -2
- package/dist/engine/boot-validator/transfer-graph.js +2 -2
- package/dist/engine/boot-validator/workspaces.js +3 -3
- package/dist/engine/bridge-stub.js +3 -4
- package/dist/engine/build-app-schema.js +130 -170
- package/dist/engine/build-config-feature-schema.js +50 -50
- package/dist/engine/build-target.js +20 -23
- package/dist/engine/codemod/pipeline-codemod.js +20 -13
- package/dist/engine/config-helpers.js +4 -4
- package/dist/engine/constants.js +2 -2
- package/dist/engine/create-app.js +2 -2
- package/dist/engine/define-feature.js +21 -13
- package/dist/engine/define-handler.js +13 -11
- package/dist/engine/define-step.js +1 -1
- package/dist/engine/define-workflow.d.ts +5 -5
- package/dist/engine/define-workflow.js +6 -6
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +22 -34
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/event-def.js +2 -2
- package/dist/engine/extension-names.d.ts +16 -17
- package/dist/engine/extension-names.js +37 -38
- package/dist/engine/extensions/escape-hatch-usage.js +1 -1
- package/dist/engine/extensions/tenant-data.js +2 -2
- package/dist/engine/extensions/user-data.d.ts +23 -24
- package/dist/engine/factories.d.ts +14 -13
- package/dist/engine/factories.js +38 -37
- package/dist/engine/feature-ast/entity-field-types.js +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +2 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
- package/dist/engine/feature-ast/extractors/shared.js +2 -3
- package/dist/engine/feature-ast/parse.js +13 -13
- package/dist/engine/feature-ast/patch.js +4 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +2 -1
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
- package/dist/engine/feature-ast/patterns.d.ts +11 -2
- package/dist/engine/feature-ast/patterns.js +43 -51
- package/dist/engine/feature-ast/render.js +50 -89
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +12 -5
- package/dist/engine/feature-manifest.d.ts +9 -9
- package/dist/engine/feature-manifest.js +6 -7
- package/dist/engine/feature-ui-extensions.d.ts +3 -3
- package/dist/engine/feature-ui-extensions.js +30 -22
- package/dist/engine/field-access.js +5 -6
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -4
- package/dist/engine/object-form.d.ts +1 -0
- package/dist/engine/object-form.js +14 -4
- package/dist/engine/ownership.js +3 -3
- package/dist/engine/parse-ref-target.js +5 -7
- package/dist/engine/pattern-library/index.js +2 -2
- package/dist/engine/pattern-library/library.js +2 -2
- package/dist/engine/pattern-library/types.js +3 -3
- package/dist/engine/pipeline.js +2 -2
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +7 -5
- package/dist/engine/projection-helpers.js +9 -9
- package/dist/engine/qualified-name.js +2 -2
- package/dist/engine/registry-facade.js +1 -1
- package/dist/engine/registry-ingest.js +13 -13
- package/dist/engine/registry-state.js +17 -19
- package/dist/engine/registry-validate.js +12 -12
- package/dist/engine/run-in.js +2 -2
- package/dist/engine/run-pipeline.js +7 -0
- package/dist/engine/schema-builder.js +251 -233
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/state-machine.d.ts +12 -12
- package/dist/engine/state-machine.js +7 -7
- package/dist/engine/steps/_resolver-utils.js +4 -3
- package/dist/engine/steps/for-each.js +4 -4
- package/dist/engine/steps/unsafe-projection-delete.js +1 -1
- package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
- package/dist/engine/steps/wait-for-event.js +4 -4
- package/dist/engine/steps/workflow-system-events.js +3 -3
- package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
- package/dist/engine/tier-resolver-extension.d.ts +29 -27
- package/dist/engine/tier-resolver-extension.js +26 -26
- package/dist/engine/transfer-adjacency.js +2 -2
- package/dist/engine/validate-projection-allowlist.js +9 -28
- package/dist/engine/walk-step-instances.d.ts +2 -0
- package/dist/engine/walk-step-instances.js +38 -0
- package/dist/engine/where-rule-lint.js +1 -1
- package/dist/engine/write-handler-qn-extract.js +1 -1
- package/dist/entrypoint/index.d.ts +4 -1
- package/dist/entrypoint/index.js +10 -4
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/errors/transition-details.js +7 -7
- package/dist/errors/write-error-info.d.ts +6 -6
- package/dist/errors/write-error-info.js +6 -6
- package/dist/es-ops/context.d.ts +9 -9
- package/dist/es-ops/context.js +29 -29
- package/dist/es-ops/index.js +7 -9
- package/dist/es-ops/operations-schema.js +4 -10
- package/dist/es-ops/runner.d.ts +11 -11
- package/dist/es-ops/runner.js +49 -53
- package/dist/es-ops/types.d.ts +55 -55
- package/dist/es-ops/types.js +11 -11
- package/dist/event-store/admin-api.js +7 -8
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +12 -6
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +4 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +7 -9
- package/dist/files/file-ref-entity.js +15 -15
- package/dist/files/file-ref-table.js +7 -8
- package/dist/files/file-routes.d.ts +7 -0
- package/dist/files/file-routes.js +22 -13
- package/dist/files/in-memory-provider.js +8 -9
- package/dist/files/local-provider.js +18 -25
- package/dist/files/provider-resolver.d.ts +7 -7
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/storage-tracking.js +2 -2
- package/dist/files/upload-ledger.js +7 -0
- package/dist/files/zip-stream.d.ts +12 -19
- package/dist/files/zip-stream.js +44 -63
- package/dist/i18n/required-surface-keys.d.ts +1 -1
- package/dist/i18n/required-surface-keys.js +188 -212
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +69 -4
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +57 -669
- package/dist/lifecycle/lifecycle.js +3 -5
- package/dist/lifecycle/signal-handlers.js +5 -4
- package/dist/logging/utils.d.ts +2 -1
- package/dist/logging/utils.js +38 -10
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/kumiko-drift.js +14 -13
- package/dist/migrations/pending-rebuilds.d.ts +21 -20
- package/dist/migrations/pending-rebuilds.js +27 -27
- package/dist/migrations/projection-table-index.d.ts +3 -3
- package/dist/migrations/projection-table-index.js +8 -8
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +12 -1
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.js +3 -3
- package/dist/pipeline/append-event-core.js +11 -2
- package/dist/pipeline/cascade-handler.d.ts +1 -1
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +20 -13
- package/dist/pipeline/dispatch-shared.d.ts +14 -3
- package/dist/pipeline/dispatch-shared.js +67 -721
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +297 -190
- package/dist/pipeline/dispatcher-utils.d.ts +1 -1
- package/dist/pipeline/dispatcher-utils.js +1 -1
- package/dist/pipeline/event-consumer-state.js +2 -2
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +4 -6
- package/dist/pipeline/event-dispatcher.js +17 -28
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
- package/dist/pipeline/handler-context-dispatch.js +89 -0
- package/dist/pipeline/handler-context-events.d.ts +43 -0
- package/dist/pipeline/handler-context-events.js +237 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/irreversible-operation-gate.js +1 -1
- package/dist/pipeline/lifecycle-pipeline.js +9 -9
- package/dist/pipeline/member-read-only-transaction.js +1 -3
- package/dist/pipeline/msp-rebuild.js +1 -1
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +15 -13
- package/dist/pipeline/system-hooks.js +39 -41
- package/dist/pipeline/system-identity-switch.js +1 -3
- package/dist/pipeline/tenant-timezone-cache.js +1 -1
- package/dist/random/generate.d.ts +29 -29
- package/dist/random/generate.js +30 -30
- package/dist/random/words.js +20 -20
- package/dist/rate-limit/middleware.js +9 -5
- package/dist/redis/client.js +2 -2
- package/dist/redis/pubsub-signal.js +14 -17
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -510
- package/dist/search/index.js +4 -4
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/search/purge-subject.js +4 -5
- package/dist/search/reindex-entity.js +6 -9
- package/dist/time/boot-tz-warning.d.ts +2 -2
- package/dist/time/boot-tz-warning.js +8 -8
- package/dist/time/iana.js +10 -10
- package/dist/time/index.js +9 -9
- package/dist/time/legacy-date.d.ts +2 -2
- package/dist/time/legacy-date.js +6 -8
- package/dist/time/polyfill.js +1 -1
- package/dist/time/tz-context.d.ts +1 -1
- package/dist/time/tz-context.js +1 -1
- package/dist/ui-types/app-schema.js +7 -7
- package/dist/ui-types/list-row-meta.js +4 -4
- package/dist/upgrade-cli.js +159 -130
- package/dist/utils/compare.js +1 -1
- package/package.json +5 -5
- package/src/changes.json +358 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
- package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +192 -153
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createFallbackLogger } from "../../logging/utils.js";
|
|
2
|
+
const log = createFallbackLogger("kumiko:boot");
|
|
1
3
|
export const SECURITY_BASELINE_FEATURE_NAMES = [
|
|
2
4
|
"sessions",
|
|
3
5
|
"crypto-shredding",
|
|
@@ -13,6 +15,5 @@ export function warnOnMissingSecurityBaseline(features, nodeEnv = process.env["N
|
|
|
13
15
|
// skip: every baseline feature is mounted
|
|
14
16
|
if (missing.length === 0)
|
|
15
17
|
return;
|
|
16
|
-
|
|
17
|
-
console.warn(`[kumiko:boot] NODE_ENV=production but the security baseline is incomplete — missing feature(s): ${missing.join(", ")}. Mount securityBaselineFeatures() from @cosmicdrift/kumiko-bundled-features/presets (or the missing features individually).`);
|
|
18
|
+
log.warn(`NODE_ENV=production but the security baseline is incomplete — missing feature(s): ${missing.join(", ")}. Mount securityBaselineFeatures() from @cosmicdrift/kumiko-bundled-features/presets (or the missing features individually).`);
|
|
18
19
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { parseRefTargetEntityName } from "../parse-ref-target.js";
|
|
2
2
|
import { findOverDeepTransferChain, MAX_TRANSFER_DEPTH, resolveTransferAdjacency, } from "../transfer-adjacency.js";
|
|
3
|
-
// --- Transfer-graph boot validation
|
|
3
|
+
// --- Transfer-graph boot validation ---
|
|
4
4
|
//
|
|
5
5
|
// tenant-handover walks an entity graph across the tenant boundary along two
|
|
6
6
|
// declared edge kinds: `parentRef` and plain `reference` fields. Two shapes
|
|
7
7
|
// cannot be walked, and both have to fail loudly here rather than silently
|
|
8
8
|
// leaving rows behind in the source tenant — the partial move is exactly what
|
|
9
|
-
//
|
|
9
|
+
// this validation exists to end.
|
|
10
10
|
//
|
|
11
11
|
// Scoped to entities declaring `transferable: true`, so a consumer that has
|
|
12
12
|
// one of these shapes but never hands that entity over is unaffected.
|
|
@@ -33,9 +33,9 @@ export function validateWorkspaces(feature, allNavQns) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
const SETTINGS_HUB_AUDIENCE_NAV_QN_SET = new Set(SETTINGS_HUB_AUDIENCE_NAV_QNS);
|
|
36
|
-
//
|
|
37
|
-
// or `r.nav({ workspaces })` never pass an explicit navAllowlist
|
|
38
|
-
//
|
|
36
|
+
// Apps that assign navs to workspaces via `r.workspace({ nav })`
|
|
37
|
+
// or `r.nav({ workspaces })` never pass an explicit navAllowlist — without
|
|
38
|
+
// this, warnOnUnreachableNavScreens never runs for them. Both
|
|
39
39
|
// `WorkspaceDefinition.nav` and `NavDefinition.workspaces` already hold
|
|
40
40
|
// fully-qualified QNs (validateWorkspaces/validateNavs compare them
|
|
41
41
|
// directly against allNavQns/allWorkspaceQns), so no re-qualification is
|
|
@@ -12,7 +12,7 @@ import { createAnonymousUser } from "./system-user.js";
|
|
|
12
12
|
// anything downstream accidentally reaches for them.
|
|
13
13
|
//
|
|
14
14
|
// Use: `{ db, registry, ...bridgeStub() }`
|
|
15
|
-
const notAvailable = (what) => async () => {
|
|
15
|
+
const notAvailable = (what) => async (..._args) => {
|
|
16
16
|
throw new Error(`ctx.${what} not available in this context — use the dispatcher, not a stubbed handler context`);
|
|
17
17
|
};
|
|
18
18
|
// Noop observability — hand back the shared fallback tracer so ctx.tracer has
|
|
@@ -24,11 +24,10 @@ export function bridgeStub(opts) {
|
|
|
24
24
|
// anonymous default values when no user is passed explicitly. Test code
|
|
25
25
|
// that cares about identity passes its own SessionUser here and gets it
|
|
26
26
|
// back on ctx.
|
|
27
|
-
const stubUser = opts?.user ??
|
|
28
|
-
createAnonymousUser("00000000-0000-0000-0000-000000000000"); // @cast-boundary engine-bridge
|
|
27
|
+
const stubUser = opts?.user ?? createAnonymousUser("00000000-0000-0000-0000-000000000000");
|
|
29
28
|
return {
|
|
30
29
|
user: stubUser,
|
|
31
|
-
query: notAvailable("query"),
|
|
30
|
+
query: notAvailable("query"),
|
|
32
31
|
queryAs: notAvailable("queryAs"),
|
|
33
32
|
write: notAvailable("write"),
|
|
34
33
|
writeAs: notAvailable("writeAs"),
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Builds the client-safe AppSchema from a server registry for the
|
|
2
|
+
// browser renderer pipeline. The dev server can therefore drop its entire
|
|
3
|
+
// hand-written `clientSchema` mirror and simply send the resolved AppSchema
|
|
4
|
+
// during boot.
|
|
5
5
|
//
|
|
6
|
-
// JSON
|
|
7
|
-
// JSON.stringify
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// JSON safety: project explicitly onto a whitelist instead of round-tripping
|
|
7
|
+
// through JSON.stringify. Functions would be silently dropped, and Zod schemas
|
|
8
|
+
// (v4 has .toJSON, but emits a _zod envelope) would appear as strange ghost
|
|
9
|
+
// properties. This is an intentional contract: everything read by the browser
|
|
10
|
+
// renderer pipeline must appear here. New fields needed by the browser must be
|
|
11
|
+
// added explicitly.
|
|
12
12
|
//
|
|
13
|
-
//
|
|
13
|
+
// Currently projected:
|
|
14
14
|
// Entity: { table?, fields: { type, required?, sortable?, default? } }
|
|
15
|
-
// Screen: verbatim (ScreenDefinition
|
|
16
|
-
// custom
|
|
17
|
-
//
|
|
18
|
-
// Nav: verbatim (NavDefinition
|
|
19
|
-
// Workspace: verbatim definition + getWorkspaceNavs()
|
|
15
|
+
// Screen: verbatim (ScreenDefinition is JSON-safe by design —
|
|
16
|
+
// custom screens have no functions; layout/columns/etc. are
|
|
17
|
+
// plain literals)
|
|
18
|
+
// Nav: verbatim (NavDefinition contains only strings + literals)
|
|
19
|
+
// Workspace: verbatim definition + getWorkspaceNavs() from the registry
|
|
20
20
|
// Translations: verbatim feature.translations (raw r.translations keys,
|
|
21
21
|
// unfiltered — see FeatureSchema.translations doc)
|
|
22
22
|
//
|
|
@@ -24,11 +24,13 @@
|
|
|
24
24
|
// The schema route strips features the caller's tenant has switched off
|
|
25
25
|
// (r.toggleable) via stripDisabledFeaturesFromAppSchema.
|
|
26
26
|
import { ZodObject } from "zod";
|
|
27
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
27
28
|
import { collectUrlPrefillFieldsByScreenQn } from "./boot-validator/url-prefill-fields.js";
|
|
28
29
|
import { buildConfigFeatureSchema, SETTINGS_HUB_FEATURE, } from "./build-config-feature-schema.js";
|
|
29
30
|
import { isEncryptedAtRest } from "./config-helpers.js";
|
|
30
31
|
import { isListQueryWithoutSearchableFields } from "./entity-list-searchability.js";
|
|
31
32
|
import { QnTypes, qualifyEntityName } from "./qualified-name.js";
|
|
33
|
+
const log = createFallbackLogger("kumiko");
|
|
32
34
|
export function buildAppSchema(registry, options = {}) {
|
|
33
35
|
const features = [];
|
|
34
36
|
const urlPrefillFieldsByScreenQn = collectUrlPrefillFieldsByScreenQn([
|
|
@@ -52,7 +54,7 @@ export function buildAppSchema(registry, options = {}) {
|
|
|
52
54
|
screens: projectScreens(featureName, feature.screens, registry, urlPrefillFieldsByScreenQn, encryptedConfigKeyQns),
|
|
53
55
|
...(navs.length > 0 && { navs }),
|
|
54
56
|
...(contentCollections.length > 0 && { contentCollections }),
|
|
55
|
-
//
|
|
57
|
+
// Verbatim r.translations({keys}) — see FeatureSchema.translations
|
|
56
58
|
// doc for why this must NOT go through registry.getAllTranslations()
|
|
57
59
|
// (double-prefixes features that already qualify their own keys).
|
|
58
60
|
...(Object.keys(feature.translations ?? {}).length > 0 && {
|
|
@@ -62,12 +64,12 @@ export function buildAppSchema(registry, options = {}) {
|
|
|
62
64
|
};
|
|
63
65
|
features.push(featureSchema);
|
|
64
66
|
}
|
|
65
|
-
// Workspaces: getAllWorkspaces()
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
67
|
+
// Workspaces: getAllWorkspaces() returns QUALIFIED ids (the Registry writes
|
|
68
|
+
// them during store overwrite). The browser renderer expects short ids
|
|
69
|
+
// (matching the URL segment and navigate({ workspaceId })). We therefore
|
|
70
|
+
// read directly from `feature.workspaces`, where the ids are still in the
|
|
71
|
+
// author's short form, and obtain the pre-resolved navMembers from the
|
|
72
|
+
// registry.
|
|
71
73
|
let workspaces = [];
|
|
72
74
|
for (const [featureName, feature] of registry.features) {
|
|
73
75
|
for (const [shortId, definition] of Object.entries(feature.workspaces)) {
|
|
@@ -78,16 +80,16 @@ export function buildAppSchema(registry, options = {}) {
|
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
|
-
// Self-
|
|
82
|
-
//
|
|
83
|
-
//
|
|
83
|
+
// Self-populating Settings Hub: screens/navs (plus one workspace) are
|
|
84
|
+
// derived from the declared config keys with `mask` and attached here,
|
|
85
|
+
// so the app author does not need manual r.screen/r.nav declarations.
|
|
84
86
|
const appHadWorkspaces = workspaces.length > 0;
|
|
85
87
|
const generated = buildConfigFeatureSchema(registry);
|
|
86
88
|
if (generated.screens.length > 0) {
|
|
87
89
|
mergeSettingsHubIntoConfigFeature(features, generated);
|
|
88
|
-
// Flip
|
|
89
|
-
//
|
|
90
|
-
//
|
|
90
|
+
// Flip protection: applies only to apps that already use workspaces. For an
|
|
91
|
+
// app without workspaces, app.workspaces remains undefined, so the renderer
|
|
92
|
+
// shows all navs without filtering, including the Hub navs.
|
|
91
93
|
if (appHadWorkspaces) {
|
|
92
94
|
const placed = placeSettingsHub(workspaces, generated);
|
|
93
95
|
workspaces = placed.workspaces;
|
|
@@ -109,16 +111,16 @@ export function buildAppSchema(registry, options = {}) {
|
|
|
109
111
|
// instead so a leaked function renderer is actually caught.
|
|
110
112
|
const offender = findNonJsonSafePath(schema, "schema");
|
|
111
113
|
if (offender !== null) {
|
|
112
|
-
|
|
113
|
-
console.error(`[kumiko] buildAppSchema: Output ist nicht JSON-safe — nicht-serialisierbarer Wert bei "${offender}" (Funktions-Renderer oder Klassen-Instanz im Schema?).`, schema);
|
|
114
|
+
log.error(`buildAppSchema: output is not JSON-safe, non-serializable value at "${offender}" (a function renderer or class instance in the schema?).`, { offender });
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
return schema;
|
|
117
118
|
}
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// bundled
|
|
121
|
-
//
|
|
119
|
+
// Attaches the generated Hub screens/navs to the config FeatureSchema
|
|
120
|
+
// (qualified as config:screen:* / config:nav:*). If it does not yet exist
|
|
121
|
+
// (because the config bundled feature is not mounted), it is created. Using
|
|
122
|
+
// find-or-create instead of an unconditional push prevents a second
|
|
123
|
+
// FeatureSchema with the same featureName.
|
|
122
124
|
function mergeSettingsHubIntoConfigFeature(features, generated) {
|
|
123
125
|
const existing = features.find((f) => f.featureName === SETTINGS_HUB_FEATURE);
|
|
124
126
|
if (existing === undefined) {
|
|
@@ -202,8 +204,7 @@ function warnUnplacedAudiences(unplaced) {
|
|
|
202
204
|
// skip: every audience placed — nothing to warn about
|
|
203
205
|
if (unplaced.length === 0)
|
|
204
206
|
return;
|
|
205
|
-
|
|
206
|
-
console.warn(`[kumiko] Settings-Hub: ${unplaced.join(", ")} nicht in einer App-Workspace platziert — ` +
|
|
207
|
+
log.warn(`Settings-Hub: ${unplaced.join(", ")} nicht in einer App-Workspace platziert — ` +
|
|
207
208
|
`erscheint im Standalone-"Einstellungen"-Tab. Referenziere ` +
|
|
208
209
|
`${unplaced.map((id) => `${SETTINGS_HUB_FEATURE}:nav:${id}`).join(", ")} ` +
|
|
209
210
|
`in einer r.workspace.nav, um die Gruppe inline zu zeigen.`);
|
|
@@ -212,8 +213,7 @@ function warnDanglingAudienceRefs(dangling) {
|
|
|
212
213
|
// skip: no dangling refs — nothing to warn about
|
|
213
214
|
if (dangling.length === 0)
|
|
214
215
|
return;
|
|
215
|
-
|
|
216
|
-
console.warn(`[kumiko] Settings-Hub: ${dangling
|
|
216
|
+
log.warn(`Settings-Hub: ${dangling
|
|
217
217
|
.map((id) => `${SETTINGS_HUB_FEATURE}:nav:${id}`)
|
|
218
218
|
.join(", ")} in einer Workspace referenziert, aber nie generiert — ` +
|
|
219
219
|
`keine Config-Keys für diesen Scope registriert. Tippfehler oder ` +
|
|
@@ -260,8 +260,8 @@ export function findNonJsonSafePath(value, path) {
|
|
|
260
260
|
}
|
|
261
261
|
// projectionList screens don't declare searchable/sortable/paginated as
|
|
262
262
|
// authoring intent — they're derived here from the bound query handler's
|
|
263
|
-
// Zod schema, the source of truth for what parameters it actually accepts
|
|
264
|
-
//
|
|
263
|
+
// Zod schema, the source of truth for what parameters it actually accepts.
|
|
264
|
+
// A hand-written `searchable: true` still wins over the derived
|
|
265
265
|
// default (screen.searchable ?? derived); the boot-validator (3a) rejects
|
|
266
266
|
// one that contradicts the schema.
|
|
267
267
|
function projectScreens(featureName, screens, registry, urlPrefillFieldsByScreenQn, encryptedConfigKeyQns) {
|
|
@@ -372,30 +372,30 @@ function projectEntities(entities) {
|
|
|
372
372
|
}
|
|
373
373
|
return out;
|
|
374
374
|
}
|
|
375
|
-
// EntityDefinition
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
375
|
+
// EntityDefinition is a discriminated union of field types, and we know the
|
|
376
|
+
// JSON-safe properties for each type. Rather than handling every variant
|
|
377
|
+
// separately, walk the field map and filter it through the whitelist. Values
|
|
378
|
+
// that do not pass through include server-only runtime values such as
|
|
379
|
+
// ZodValidate functions, computed functions, and default functions.
|
|
380
380
|
function projectEntity(entityName, entity) {
|
|
381
381
|
const fieldsOut = {};
|
|
382
382
|
for (const [fieldName, fieldDef] of Object.entries(entity.fields)) {
|
|
383
383
|
assertValidMultilineRows(`Entity "${entityName}"`, fieldName, fieldDef);
|
|
384
384
|
fieldsOut[fieldName] = projectField(fieldDef);
|
|
385
385
|
}
|
|
386
|
-
// derivedFields
|
|
387
|
-
// derive
|
|
388
|
-
// `entity.derivedFields[field].valueType
|
|
389
|
-
// "references unknown field".
|
|
390
|
-
//
|
|
386
|
+
// derivedFields MUST be included in the client schema (metadata only, not the
|
|
387
|
+
// derive function): computeListViewModel resolves an entityList column via
|
|
388
|
+
// `entity.derivedFields[field].valueType`; without the entry it throws
|
|
389
|
+
// "references unknown field". The executor has already attached the value
|
|
390
|
+
// server-side to the row; the client only needs valueType for rendering.
|
|
391
391
|
const derivedOut = {};
|
|
392
392
|
for (const [name, derivedDef] of Object.entries(entity.derivedFields ?? {})) {
|
|
393
393
|
derivedOut[name] = projectDerivedField(derivedDef);
|
|
394
394
|
}
|
|
395
|
-
// EntityDefinition
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
395
|
+
// EntityDefinition accepts idType/access/searchWeight as optional, but we omit
|
|
396
|
+
// them because the browser renderer does not read them. `table` is included
|
|
397
|
+
// in case apps reference `entity.table` directly.
|
|
398
|
+
// No cast is needed: all omitted fields are optional (`?`).
|
|
399
399
|
return {
|
|
400
400
|
fields: fieldsOut,
|
|
401
401
|
// @cast-boundary schema-walk: EntityDefinition.derivedFields is typed for
|
|
@@ -413,10 +413,10 @@ function projectEntity(entityName, entity) {
|
|
|
413
413
|
...(entity.lockedWhen !== undefined && { lockedWhen: entity.lockedWhen }),
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
|
-
//
|
|
417
|
-
// (
|
|
418
|
-
// ClientDerivedFieldDef
|
|
419
|
-
//
|
|
416
|
+
// Only valueType is forwarded — the derive function is server-only and is not
|
|
417
|
+
// JSON-safe (it would otherwise trigger the output-walk guard). Return
|
|
418
|
+
// ClientDerivedFieldDef honestly instead of pretending to return a
|
|
419
|
+
// DerivedFieldDef without its derive function.
|
|
420
420
|
function projectDerivedField(derivedDef) {
|
|
421
421
|
return { valueType: derivedDef.valueType };
|
|
422
422
|
}
|
|
@@ -446,138 +446,98 @@ function assertValidScreenMultilineRows(featureName, shortId, screen) {
|
|
|
446
446
|
check(screen.confirm.fields);
|
|
447
447
|
}
|
|
448
448
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (typeof def["searchable"] === "boolean")
|
|
478
|
-
out["searchable"] = def["searchable"];
|
|
479
|
-
if (isJsonSafeValue(def["default"]))
|
|
480
|
-
out["default"] = def["default"];
|
|
481
|
-
// Select: options-Liste ist plain JSON, durchschicken.
|
|
482
|
-
if (Array.isArray(def["options"]))
|
|
483
|
-
out["options"] = def["options"];
|
|
484
|
-
if (isJsonSafeObject(def["optionTones"]))
|
|
485
|
-
out["optionTones"] = def["optionTones"];
|
|
486
|
-
// Reference: entity-Target + labelField + multiple müssen zum Renderer.
|
|
487
|
-
// Der ReferenceInput baut die Options-Query aus refEntity/refFeature und
|
|
488
|
-
// resolved das Label über labelField — ohne diese Properties fällt das
|
|
489
|
-
// Dropdown leer aus (QN wird `<feature>:query::list` → 404).
|
|
490
|
-
if (typeof def["entity"] === "string")
|
|
491
|
-
out["entity"] = def["entity"];
|
|
492
|
-
if (typeof def["labelField"] === "string")
|
|
493
|
-
out["labelField"] = def["labelField"];
|
|
494
|
-
// fw#2780: author-supplied picker source — without it the client falls back to the list handler.
|
|
495
|
-
if (typeof def["optionsQuery"] === "string")
|
|
496
|
-
out["optionsQuery"] = def["optionsQuery"];
|
|
449
|
+
const isStringValue = (value) => typeof value === "string";
|
|
450
|
+
const isBooleanValue = (value) => typeof value === "boolean";
|
|
451
|
+
const isNumberValue = (value) => typeof value === "number";
|
|
452
|
+
const isTrueValue = (value) => value === true;
|
|
453
|
+
const isNumberOrStringValue = (value) => typeof value === "number" || typeof value === "string";
|
|
454
|
+
const isBooleanOrJsonSafeObject = (value) => typeof value === "boolean" || isJsonSafeObject(value);
|
|
455
|
+
const isUnitSpec = (value) => typeof value === "string" || (isPlainObject(value) && typeof value["field"] === "string");
|
|
456
|
+
// Forwarded in this order, so the client schema keeps its key order.
|
|
457
|
+
const FORWARDED_FIELD_PROPERTIES = [
|
|
458
|
+
["type", isStringValue],
|
|
459
|
+
["required", isBooleanValue],
|
|
460
|
+
["sortable", isBooleanValue],
|
|
461
|
+
// filterable controls the faceted filter dropdowns in the renderer (select/
|
|
462
|
+
// boolean), so it must be included in the client schema.
|
|
463
|
+
["filterable", isBooleanValue],
|
|
464
|
+
["searchable", isBooleanValue],
|
|
465
|
+
["default", isJsonSafeValue],
|
|
466
|
+
// Select: the options list is plain JSON, so forward it.
|
|
467
|
+
["options", Array.isArray],
|
|
468
|
+
["optionTones", isJsonSafeObject],
|
|
469
|
+
// Reference: entity target, labelField, and multiple are required by the
|
|
470
|
+
// renderer. ReferenceInput builds the options query from refEntity/refFeature
|
|
471
|
+
// and resolves the label through labelField; without these properties, the
|
|
472
|
+
// dropdown is empty (the QN becomes `<feature>:query::list` → 404).
|
|
473
|
+
["entity", isStringValue],
|
|
474
|
+
["labelField", isStringValue],
|
|
475
|
+
// Author-supplied picker source — without it the client falls back to the list handler.
|
|
476
|
+
["optionsQuery", isStringValue],
|
|
497
477
|
// Select: tier/tenant availability of the static options, loaded by the renderer.
|
|
498
|
-
|
|
499
|
-
out["optionsAvailabilityQuery"] = def["optionsAvailabilityQuery"];
|
|
478
|
+
["optionsAvailabilityQuery", isStringValue],
|
|
500
479
|
// Select: options only offered while a sibling-field condition holds; the renderer filters, the write path enforces.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (typeof def["multiple"] === "boolean")
|
|
504
|
-
out["multiple"] = def["multiple"];
|
|
480
|
+
["conditionalOptions", Array.isArray],
|
|
481
|
+
["multiple", isBooleanValue],
|
|
505
482
|
// MultiSelect: display picks checkboxes vs. combobox in the renderer,
|
|
506
483
|
// columns/maxRows size the checkbox grid — without these the renderer
|
|
507
|
-
// always falls back to the combobox
|
|
484
|
+
// always falls back to the combobox. Select: display picks
|
|
508
485
|
// "radio" | "dropdown" the same way.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
out["columns"] = def["columns"];
|
|
513
|
-
if (typeof def["maxRows"] === "number")
|
|
514
|
-
out["maxRows"] = def["maxRows"];
|
|
486
|
+
["display", isStringValue],
|
|
487
|
+
["columns", isNumberValue],
|
|
488
|
+
["maxRows", isNumberValue],
|
|
515
489
|
// text/longText: textarea row count — DefaultInput renders a single-line
|
|
516
|
-
// input otherwise
|
|
517
|
-
|
|
518
|
-
out["multiline"] = def["multiline"];
|
|
490
|
+
// input otherwise.
|
|
491
|
+
["multiline", isBooleanOrJsonSafeObject],
|
|
519
492
|
// number: Zod write-boundary bounds. date/timestamp/locatedTimestamp:
|
|
520
|
-
// ISO-string picker bounds — same keys, different literal type
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
out["locale"] = def["locale"];
|
|
528
|
-
// image/images: which camera a mobile capture opens (fw#2497).
|
|
529
|
-
if (typeof def["capture"] === "string")
|
|
530
|
-
out["capture"] = def["capture"];
|
|
493
|
+
// ISO-string picker bounds — same keys, different literal type.
|
|
494
|
+
["min", isNumberOrStringValue],
|
|
495
|
+
["max", isNumberOrStringValue],
|
|
496
|
+
// date/timestamp/locatedTimestamp: display/parsing locale override.
|
|
497
|
+
["locale", isStringValue],
|
|
498
|
+
// image/images: which camera a mobile capture opens.
|
|
499
|
+
["capture", isStringValue],
|
|
531
500
|
// number: display-only suffix, static or read from a sibling field of the row.
|
|
532
|
-
|
|
533
|
-
if (typeof unit === "string" || (isPlainObject(unit) && typeof unit["field"] === "string"))
|
|
534
|
-
out["unit"] = unit;
|
|
501
|
+
["unit", isUnitSpec],
|
|
535
502
|
// number/decimal/bigInt: `false` drops thousands separators (e.g. a year).
|
|
536
|
-
|
|
537
|
-
out["grouping"] = def["grouping"];
|
|
503
|
+
["grouping", isBooleanValue],
|
|
538
504
|
// text: "password" masks the input and blocks URL prefill in the renderer.
|
|
539
|
-
|
|
540
|
-
out["format"] = def["format"];
|
|
505
|
+
["format", isStringValue],
|
|
541
506
|
// Write-response redaction stays server-side; the renderer needs the flag to
|
|
542
507
|
// refuse any prefill (URL or handoff) into the field.
|
|
543
|
-
|
|
544
|
-
out["sensitive"] = true;
|
|
508
|
+
["sensitive", isTrueValue],
|
|
545
509
|
// Without it the renderer shows a plain text input for a field whose value never leaves the server.
|
|
546
|
-
|
|
547
|
-
out["writeOnly"] = true;
|
|
510
|
+
["writeOnly", isTrueValue],
|
|
548
511
|
// timestamp: the edit view-model derives its wall-clock input mode from this.
|
|
549
|
-
|
|
550
|
-
out["locatedBy"] = def["locatedBy"];
|
|
512
|
+
["locatedBy", isStringValue],
|
|
551
513
|
// file/image/images: upload picker constraints; the first variant key picks
|
|
552
514
|
// the preview variant.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
out["maxSize"] = def["maxSize"];
|
|
557
|
-
if (isJsonSafeObject(def["variants"]))
|
|
558
|
-
out["variants"] = def["variants"];
|
|
515
|
+
["accept", isJsonSafeArray],
|
|
516
|
+
["maxSize", isStringValue],
|
|
517
|
+
["variants", isJsonSafeObject],
|
|
559
518
|
// decimal, incl. embedded sub-fields: rounding of derived embedded-list cells.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
// money: declared currency source (MoneyCurrencySource, fw#2933) — without
|
|
519
|
+
["scale", isNumberValue],
|
|
520
|
+
// money: declared currency source (MoneyCurrencySource) — without
|
|
563
521
|
// it the renderer can't tell a "currency: { kind: 'tenant' }" field apart
|
|
564
522
|
// from an undeclared one and always falls back to entity.defaultCurrency.
|
|
565
|
-
|
|
566
|
-
out["currency"] = def["currency"];
|
|
523
|
+
["currency", isJsonSafeObject],
|
|
567
524
|
// embedded lists: row-count bounds, computed cells, totals row, and the
|
|
568
|
-
// sibling-money-field totals check
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
525
|
+
// sibling-money-field totals check.
|
|
526
|
+
["minItems", isNumberValue],
|
|
527
|
+
["maxItems", isNumberValue],
|
|
528
|
+
["derived", isJsonSafeObject],
|
|
529
|
+
["totals", isJsonSafeArray],
|
|
530
|
+
["totalsMatch", isJsonSafeObject],
|
|
531
|
+
];
|
|
532
|
+
function projectField(fieldDef) {
|
|
533
|
+
const def = fieldDef; // @cast-boundary schema-walk
|
|
534
|
+
const out = {};
|
|
535
|
+
for (const [key, isForwarded] of FORWARDED_FIELD_PROPERTIES) {
|
|
536
|
+
if (isForwarded(def[key]))
|
|
537
|
+
out[key] = def[key];
|
|
538
|
+
}
|
|
579
539
|
// embedded lists: the sub-field map itself — without it the client-side
|
|
580
|
-
// edit view-model iterates `undefined` and the screen crashes
|
|
540
|
+
// edit view-model iterates `undefined` and the screen crashes.
|
|
581
541
|
// Recursive per sub-field, NOT a blanket isJsonSafeValue gate: a single
|
|
582
542
|
// sub-field carrying a validator fn would otherwise drop the whole map,
|
|
583
543
|
// and a blanket copy would ship server internals.
|