@cosmicdrift/kumiko-framework 0.387.0 → 0.389.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 +9 -1
- 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/golden-path.md +13 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/pdf-audit-export.md +4 -2
- 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/state-machine.md +3 -2
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/examples.md +1 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +2 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-routes-shared.d.ts +2 -0
- package/dist/api/auth-routes-shared.js +5 -0
- package/dist/api/client-error-route.js +10 -5
- package/dist/api/entity-read-access.d.ts +2 -1
- package/dist/api/entity-read-access.js +14 -0
- package/dist/api/export/export-cells-reference.d.ts +9 -0
- package/dist/api/export/export-cells-reference.js +21 -0
- package/dist/api/export/export-cells.d.ts +11 -0
- package/dist/api/export/export-cells.js +136 -0
- package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
- package/dist/api/export/export-ciphertext-guard.js +27 -0
- package/dist/api/export/export-columns.d.ts +29 -0
- package/dist/api/export/export-columns.js +85 -0
- package/dist/api/export/export-errors.d.ts +4 -0
- package/dist/api/export/export-errors.js +14 -0
- package/dist/api/export/export-route.d.ts +4 -0
- package/dist/api/export/export-route.js +74 -0
- package/dist/api/export/export-rows.d.ts +13 -0
- package/dist/api/export/export-rows.js +62 -0
- package/dist/api/export/export-screen.d.ts +12 -0
- package/dist/api/export/export-screen.js +47 -0
- package/dist/api/export/export-xlsx-writer.d.ts +4 -0
- package/dist/api/export/export-xlsx-writer.js +21 -0
- package/dist/api/extra-route.d.ts +4 -0
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/pii-leak-guard.d.ts +3 -0
- package/dist/api/pii-leak-guard.js +3 -2
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/routes.d.ts +16 -0
- 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 +206 -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 +299 -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 +238 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1251
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-route.d.ts +2 -1
- package/dist/api/sse-route.js +27 -13
- package/dist/config/index.d.ts +3 -1
- 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.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +12 -18
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +3 -1
- package/dist/db/aggregate-sql.d.ts +23 -0
- package/dist/db/aggregate-sql.js +165 -0
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +88 -0
- package/dist/db/connection.d.ts +4 -5
- package/dist/db/connection.js +29 -20
- package/dist/db/dialect.js +1 -1
- 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 +101 -0
- 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.js +103 -110
- package/dist/db/event-store-executor-context.d.ts +2 -1
- package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
- package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +143 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +206 -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 +48 -701
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +41 -0
- package/dist/db/event-store-executor-write-steps.d.ts +17 -0
- package/dist/db/event-store-executor-write-steps.js +95 -0
- package/dist/db/event-store-executor-write.js +405 -509
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.js +1 -1
- package/dist/db/index.d.ts +7 -3
- package/dist/db/index.js +6 -2
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/migrate-generator.js +71 -86
- package/dist/db/migrate-runner.js +7 -4
- package/dist/db/pg-error.js +26 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/shadow-swap.js +2 -3
- package/dist/db/query.d.ts +16 -15
- package/dist/db/query.js +76 -804
- package/dist/db/reference-data.js +19 -2
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/table-builder.js +113 -166
- 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-upsert.d.ts +5 -0
- package/dist/db/tenant-db-upsert.js +91 -0
- package/dist/db/tenant-db.js +50 -10
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +2 -1
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
- package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
- package/dist/engine/boot-validator/detail-screens.js +2 -2
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +17 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/i18n-keys.js +5 -3
- package/dist/engine/boot-validator/index.js +2 -0
- package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
- package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
- package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
- package/dist/engine/boot-validator/list-column-layout.js +41 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
- package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-export-options.js +27 -0
- package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
- package/dist/engine/boot-validator/list-table-options.js +77 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
- package/dist/engine/boot-validator/pii-retention.js +233 -155
- package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
- package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +8 -1
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +2 -6
- 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 +235 -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 +56 -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 +24 -11
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +99 -860
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +65 -97
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/define-feature.js +13 -2
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +10 -21
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/index.js +1 -1
- package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
- package/dist/engine/feature-ast/extractors/round1.js +6 -0
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/index.d.ts +1 -1
- package/dist/engine/feature-ast/parse.js +3 -1
- package/dist/engine/feature-ast/patch.d.ts +2 -0
- package/dist/engine/feature-ast/patch.js +5 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -245
- package/dist/engine/feature-ast/pattern-change-schema.js +10 -163
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +211 -0
- package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
- package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
- package/dist/engine/feature-ast/patterns.d.ts +14 -1
- package/dist/engine/feature-ast/patterns.js +44 -51
- package/dist/engine/feature-ast/render.js +52 -87
- package/dist/engine/feature-builder-state.d.ts +1 -0
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-entity-handlers.js +3 -0
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +1 -0
- package/dist/engine/field-access.d.ts +1 -0
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/engine/pattern-library/library.js +2 -1
- package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
- package/dist/engine/pattern-library/static-schemas.js +9 -0
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +6 -4
- package/dist/engine/registry-ingest.js +1 -1
- package/dist/engine/schema-builder.js +228 -212
- package/dist/engine/screen-filter-ops.js +2 -0
- package/dist/engine/screen-helpers.js +1 -1
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +58 -17
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +4 -3
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/engine/validate-projection-allowlist.js +2 -1
- package/dist/entrypoint/index.d.ts +6 -1
- package/dist/entrypoint/index.js +10 -1
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/es-ops/context.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +17 -8
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +7 -1
- package/dist/files/create-file-ref.d.ts +3 -0
- package/dist/files/create-file-ref.js +11 -2
- package/dist/files/file-handle.js +2 -3
- package/dist/files/file-routes.d.ts +2 -0
- package/dist/files/file-routes.js +40 -10
- package/dist/files/provider-resolver.d.ts +1 -0
- package/dist/files/provider-resolver.js +1 -1
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +11 -4
- package/dist/i18n/required-surface-keys.js +189 -208
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/internal/testing.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 +61 -2
- 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 +50 -657
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +21 -3
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-batch.js +8 -1
- package/dist/pipeline/dispatch-query.js +10 -3
- package/dist/pipeline/dispatch-shared.d.ts +13 -3
- package/dist/pipeline/dispatch-shared.js +62 -710
- package/dist/pipeline/dispatch-write.js +296 -189
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +6 -6
- package/dist/pipeline/event-dispatcher.d.ts +1 -0
- package/dist/pipeline/event-dispatcher.js +6 -9
- 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 +23 -0
- package/dist/pipeline/handler-context-dispatch.js +101 -0
- package/dist/pipeline/handler-context-events.d.ts +41 -0
- package/dist/pipeline/handler-context-events.js +239 -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/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 +2 -1
- package/dist/pipeline/system-hooks.d.ts +3 -1
- package/dist/pipeline/system-hooks.js +55 -6
- package/dist/pipeline/write-result-masking.js +13 -10
- 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 -604
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/search/reindex-entity.js +15 -5
- package/dist/ui-types/index.d.ts +2 -2
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-changelog.d.ts +2 -0
- package/dist/upgrade-changelog.js +14 -0
- package/dist/upgrade-cli.d.ts +3 -12
- package/dist/upgrade-cli.js +171 -197
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/package.json +9 -6
- package/src/changes.json +555 -0
- package/src/scripts/codemod/codemod-support.ts +1 -1
- package/src/scripts/codemod/db-factory.ts +1 -1
- package/src/scripts/codemod/feature-layout.ts +10 -2
- 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 +175 -145
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- package/dist/engine/provider-plugin-context.js +0 -28
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { normalizeListColumn } from "../screen-helpers.js";
|
|
2
|
+
import { validateListBulkActions } from "./list-bulk-actions.js";
|
|
3
|
+
import { validateListTableOptions } from "./list-table-options.js";
|
|
4
|
+
import { assertWriteHandlerRegistered, resolveRowActionNavigateTarget, validateAtMostOneRowClick, validateDrawerTargetAction, } from "./screens-actions.js";
|
|
5
|
+
import { listActionContext, rejectEmptyColumns, rejectMissingQuery, rejectNonArrayInFilter, validateListColumnRendering, } from "./screens-kind-shared.js";
|
|
6
|
+
import { validateListActionTargets, validateListFacets } from "./screens-lists.js";
|
|
7
|
+
function validateProjectionListRowActions(ctx, screenId, rowActions) {
|
|
8
|
+
const listContext = listActionContext(ctx, screenId, "projectionList", undefined, `Screen "${screenId}" (projectionList)`);
|
|
9
|
+
for (const action of rowActions) {
|
|
10
|
+
validateListActionTargets(listContext, "rowAction", action);
|
|
11
|
+
}
|
|
12
|
+
validateAtMostOneRowClick(ctx.feature.name, screenId, "projectionList", rowActions);
|
|
13
|
+
}
|
|
14
|
+
// Plain navigate toolbarActions (no tab) have no boot check yet.
|
|
15
|
+
function validateProjectionListToolbarActions(ctx, screenId, toolbarActions) {
|
|
16
|
+
const featureName = ctx.feature.name;
|
|
17
|
+
for (const action of toolbarActions) {
|
|
18
|
+
if (action.kind === "drawer") {
|
|
19
|
+
validateDrawerTargetAction(featureName, screenId, "projectionList", "toolbarAction", action, ctx.feature.screens);
|
|
20
|
+
}
|
|
21
|
+
else if (action.kind === "writeHandler") {
|
|
22
|
+
assertWriteHandlerRegistered(featureName, `Screen "${screenId}" (projectionList) toolbarAction "${action.id}"`, action.handler, ctx.allWriteHandlerQns);
|
|
23
|
+
}
|
|
24
|
+
if (action.kind === "navigate" && action.tab !== undefined) {
|
|
25
|
+
resolveRowActionNavigateTarget(featureName, screenId, "projectionList", "toolbarAction", action, ctx.allScreenQns, ctx.navTargetShortIds, ctx.screensByShortId, ctx.detailForScreens);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Query-driven, no entity, so only query + columns are checked (the column
|
|
30
|
+
// fields cannot be checked against an entity; they resolve against the
|
|
31
|
+
// projection rows at render time).
|
|
32
|
+
export function validateProjectionListScreen(ctx, screenId, screen) {
|
|
33
|
+
const featureName = ctx.feature.name;
|
|
34
|
+
rejectMissingQuery(featureName, screenId, "projectionList", screen.query);
|
|
35
|
+
rejectEmptyColumns(featureName, screenId, "projectionList", screen.columns.length);
|
|
36
|
+
for (const col of screen.columns) {
|
|
37
|
+
validateListColumnRendering(ctx, screenId, "projectionList", normalizeListColumn(col));
|
|
38
|
+
}
|
|
39
|
+
validateListTableOptions(`[Feature ${featureName}] Screen "${screenId}" (projectionList)`, screen.table, screen.columns, true);
|
|
40
|
+
// Screen filter: field existence can't be checked without an entity (columns
|
|
41
|
+
// aren't a complete field inventory of the underlying query), so only the
|
|
42
|
+
// structure is pinned: "in" requires an array.
|
|
43
|
+
if (screen.filter !== undefined) {
|
|
44
|
+
rejectNonArrayInFilter(featureName, screenId, "projectionList", screen.filter);
|
|
45
|
+
}
|
|
46
|
+
if (screen.facets !== undefined) {
|
|
47
|
+
validateListFacets(`[Feature ${featureName}] Screen "${screenId}" (projectionList)`, screen.facets, screen.columns, featureName, ctx.featureMap);
|
|
48
|
+
}
|
|
49
|
+
if (screen.rowActions !== undefined) {
|
|
50
|
+
validateProjectionListRowActions(ctx, screenId, screen.rowActions);
|
|
51
|
+
}
|
|
52
|
+
validateListBulkActions(`[Feature ${featureName}] Screen "${screenId}" (projectionList)`, screen.bulkActions, screen.rowActions);
|
|
53
|
+
if (screen.toolbarActions !== undefined) {
|
|
54
|
+
validateProjectionListToolbarActions(ctx, screenId, screen.toolbarActions);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SecretMintScreenDefinition } from "../types/screen.ts";
|
|
2
|
+
import type { ScreenValidationContext } from "./screens-kind-shared.ts";
|
|
3
|
+
export declare function validateSecretMintScreen(ctx: ScreenValidationContext, screenId: string, screen: SecretMintScreenDefinition): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { validateInlineFormScreen, validateSecretMintConfirm } from "./screens-forms.js";
|
|
2
|
+
function validateSecretMintRevealFields(featureName, screenId, screen) {
|
|
3
|
+
if (screen.reveal.fields.length === 0) {
|
|
4
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (secretMint) has an empty reveal.fields ` +
|
|
5
|
+
`list — declare at least one field to reveal.`);
|
|
6
|
+
}
|
|
7
|
+
const revealFieldNames = new Set();
|
|
8
|
+
for (const revealField of screen.reveal.fields) {
|
|
9
|
+
if (typeof revealField.field !== "string" || revealField.field.trim() === "") {
|
|
10
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (secretMint) has a reveal.fields entry ` +
|
|
11
|
+
`with an empty or non-string "field". Set field to a non-empty string.`);
|
|
12
|
+
}
|
|
13
|
+
if (revealFieldNames.has(revealField.field)) {
|
|
14
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (secretMint) reveal.fields has a ` +
|
|
15
|
+
`duplicate field "${revealField.field}" — each revealed field must be unique.`);
|
|
16
|
+
}
|
|
17
|
+
revealFieldNames.add(revealField.field);
|
|
18
|
+
if (typeof revealField.label !== "string" || revealField.label.trim() === "") {
|
|
19
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (secretMint) reveal.fields entry ` +
|
|
20
|
+
`"${revealField.field}" has an empty or non-string "label". Set label to a non-empty string.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function validateSecretMintScreen(ctx, screenId, screen) {
|
|
25
|
+
validateInlineFormScreen(ctx.feature, screenId, screen, "secretMint", ctx.allWriteHandlerQns, ctx, ctx.featureMap);
|
|
26
|
+
validateSecretMintRevealFields(ctx.feature.name, screenId, screen);
|
|
27
|
+
validateSecretMintConfirm(ctx.feature, screenId, screen, ctx.allWriteHandlerQns);
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type normalizeListColumn } from "../screen-helpers.ts";
|
|
2
|
+
import type { ConfigKeyDefinition, EntityDefinition, FeatureDefinition } from "../types/index.ts";
|
|
3
|
+
import type { EditExtensionSection, EditFieldsSection, EditSectionSpec, RowAction } from "../types/screen.ts";
|
|
4
|
+
import type { NavigateSourceScreenType, NavigateTargetLookups } from "./screens-actions.ts";
|
|
5
|
+
import { type resolveRowActionNavigateTarget } from "./screens-actions.ts";
|
|
6
|
+
import type { ListActionTargetContext } from "./screens-lists.ts";
|
|
7
|
+
export type ScreenValidationContext = NavigateTargetLookups & {
|
|
8
|
+
readonly feature: FeatureDefinition;
|
|
9
|
+
readonly featureMap: ReadonlyMap<string, FeatureDefinition>;
|
|
10
|
+
readonly allWriteHandlerQns: ReadonlySet<string>;
|
|
11
|
+
readonly configKeyDefsByQn: ReadonlyMap<string, ConfigKeyDefinition>;
|
|
12
|
+
};
|
|
13
|
+
export declare function findEntityFeature(entityName: string, featureMap: ReadonlyMap<string, FeatureDefinition>): string | undefined;
|
|
14
|
+
export declare function buildUnknownFieldMessage(featureName: string, screenId: string, fieldName: string, entityName: string, knownFields: ReadonlySet<string>): string;
|
|
15
|
+
export declare function listActionContext(ctx: ScreenValidationContext, screenId: string, screenType: NavigateSourceScreenType, screenEntity: string | undefined, where: string): ListActionTargetContext;
|
|
16
|
+
export declare function resolveScreenEntity(ctx: ScreenValidationContext, screenId: string, entityName: string): EntityDefinition;
|
|
17
|
+
export declare function rejectMissingQuery(featureName: string, screenId: string, kind: "projectionList" | "projectionDetail", query: unknown): void;
|
|
18
|
+
export declare function rejectEmptyColumns(featureName: string, screenId: string, kind: "projectionList" | "entityList", columnCount: number): void;
|
|
19
|
+
export declare function validateListColumnRendering(ctx: ScreenValidationContext, screenId: string, kind: "projectionList" | "entityList", column: ReturnType<typeof normalizeListColumn>): void;
|
|
20
|
+
export declare function rejectNonArrayInFilter(featureName: string, screenId: string, kind: "projectionList" | "entityList", filter: {
|
|
21
|
+
readonly op: string;
|
|
22
|
+
readonly value?: unknown;
|
|
23
|
+
}): void;
|
|
24
|
+
export declare function rejectEmptySections(featureName: string, screenId: string, kind: "projectionDetail" | "configEdit" | "entityEdit", sectionCount: number): void;
|
|
25
|
+
export declare function assertExtensionSectionHasComponent(featureName: string, screenId: string, kind: "projectionDetail" | "configEdit" | "entityEdit", section: EditExtensionSection): void;
|
|
26
|
+
export declare function rejectProjectionDetailOnlySection(featureName: string, screenId: string, kind: "configEdit" | "entityEdit", section: Exclude<EditSectionSpec, EditExtensionSection>): asserts section is EditFieldsSection;
|
|
27
|
+
export declare function editSectionLabel(section: EditSectionSpec): string;
|
|
28
|
+
export declare function validateRecordSectionActions(ctx: ScreenValidationContext, screenId: string, kind: "projectionDetail" | "entityEdit", section: EditSectionSpec): void;
|
|
29
|
+
export declare function validateRecordHeaderAction(ctx: ScreenValidationContext, screenId: string, kind: "projectionDetail" | "entityEdit", screenEntity: string | undefined, action: RowAction): void;
|
|
30
|
+
type NavigateLink = {
|
|
31
|
+
readonly screen?: string;
|
|
32
|
+
readonly entity?: string;
|
|
33
|
+
readonly entityId?: string;
|
|
34
|
+
readonly tab?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function navigateActionFromLink(id: string, navigate: NavigateLink): Parameters<typeof resolveRowActionNavigateTarget>[4];
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Checks shared by several screen kinds; each helper takes the kind it reports under.
|
|
2
|
+
// Dependencies point one way: screens-kind-*.ts -> here ->
|
|
3
|
+
// screens-dashboard/forms/lists -> screens-actions.
|
|
4
|
+
import { qualifyEntityName } from "../qualified-name.js";
|
|
5
|
+
import { isWriteFormEditSection } from "../screen-helpers.js";
|
|
6
|
+
import { isRowClickAction, validateActionHasIcon, validateNavigateActionTab, validateRecordActionRedirect, } from "./screens-actions.js";
|
|
7
|
+
import { assertRefTargetRegistered, rejectInertColumnSortable, validateColumnRendererForm, validateRowActionTarget, } from "./screens-lists.js";
|
|
8
|
+
export function findEntityFeature(entityName, featureMap) {
|
|
9
|
+
for (const [name, feature] of featureMap) {
|
|
10
|
+
if (feature.entities?.[entityName])
|
|
11
|
+
return name;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
export function buildUnknownFieldMessage(featureName, screenId, fieldName, entityName, knownFields) {
|
|
16
|
+
const known = [...knownFields].sort().join(", ");
|
|
17
|
+
return (`[Feature ${featureName}] Screen "${screenId}" references field "${fieldName}" ` +
|
|
18
|
+
`which does not exist on entity "${entityName}" (known: ${known}).`);
|
|
19
|
+
}
|
|
20
|
+
export function listActionContext(ctx, screenId, screenType, screenEntity, where) {
|
|
21
|
+
return {
|
|
22
|
+
featureName: ctx.feature.name,
|
|
23
|
+
screenId,
|
|
24
|
+
screenType,
|
|
25
|
+
screenEntity,
|
|
26
|
+
where,
|
|
27
|
+
screens: ctx.feature.screens,
|
|
28
|
+
allWriteHandlerQns: ctx.allWriteHandlerQns,
|
|
29
|
+
allScreenQns: ctx.allScreenQns,
|
|
30
|
+
navTargetShortIds: ctx.navTargetShortIds,
|
|
31
|
+
screensByShortId: ctx.screensByShortId,
|
|
32
|
+
detailForScreens: ctx.detailForScreens,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// entityList / entityEdit: entity-refs are feature-local.
|
|
36
|
+
export function resolveScreenEntity(ctx, screenId, entityName) {
|
|
37
|
+
const entityDef = ctx.feature.entities?.[entityName];
|
|
38
|
+
if (entityDef)
|
|
39
|
+
return entityDef;
|
|
40
|
+
const known = Object.keys(ctx.feature.entities ?? {})
|
|
41
|
+
.sort()
|
|
42
|
+
.join(", ") || "(none)";
|
|
43
|
+
const crossFeature = findEntityFeature(entityName, ctx.featureMap);
|
|
44
|
+
const hint = crossFeature
|
|
45
|
+
? ` Entity "${entityName}" is owned by feature "${crossFeature}" — cross-feature screen ownership is not supported.`
|
|
46
|
+
: "";
|
|
47
|
+
throw new Error(`[Feature ${ctx.feature.name}] Screen "${screenId}" references entity "${entityName}" ` +
|
|
48
|
+
`which is not declared in this feature (known: ${known}).${hint}`);
|
|
49
|
+
}
|
|
50
|
+
export function rejectMissingQuery(featureName, screenId, kind, query) {
|
|
51
|
+
if (!query || typeof query !== "string") {
|
|
52
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has empty or non-string query. Set query to a registered query name.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// An empty column list would render a blank table: almost always an
|
|
56
|
+
// in-progress screen the author forgot to fill in.
|
|
57
|
+
export function rejectEmptyColumns(featureName, screenId, kind, columnCount) {
|
|
58
|
+
if (columnCount === 0) {
|
|
59
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has an empty columns list — ` +
|
|
60
|
+
`declare at least one column.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function validateListColumnRendering(ctx, screenId, kind, column) {
|
|
64
|
+
const featureName = ctx.feature.name;
|
|
65
|
+
validateColumnRendererForm(featureName, screenId, column);
|
|
66
|
+
rejectInertColumnSortable(featureName, screenId, kind, column);
|
|
67
|
+
if (column.refEntity !== undefined) {
|
|
68
|
+
assertRefTargetRegistered(`[Feature ${featureName}] Screen "${screenId}" (${kind})`, `column "${column.field}" (refEntity)`, column.refEntity, featureName, ctx.featureMap);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function rejectNonArrayInFilter(featureName, screenId, kind, filter) {
|
|
72
|
+
if (filter.op === "in" && !Array.isArray(filter.value)) {
|
|
73
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) filter.op "in" requires ` +
|
|
74
|
+
`filter.value to be a readonly array. Pass an array as filter.value.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// A layout with zero sections renders as nothing: reject at boot so the
|
|
78
|
+
// author sees it before the blank form surprises them.
|
|
79
|
+
export function rejectEmptySections(featureName, screenId, kind, sectionCount) {
|
|
80
|
+
if (sectionCount === 0) {
|
|
81
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has an empty sections list — ` +
|
|
82
|
+
`declare at least one section.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function assertExtensionSectionHasComponent(featureName, screenId, kind, section) {
|
|
86
|
+
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
87
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) extension section ` +
|
|
88
|
+
`"${section.title}" has no component — declare a react/native component marker.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// relatedList and writeForm sections exist on projectionDetail only; what
|
|
92
|
+
// remains afterwards is a plain fields/groups section.
|
|
93
|
+
export function rejectProjectionDetailOnlySection(featureName, screenId, kind, section) {
|
|
94
|
+
if (section.kind === "relatedList") {
|
|
95
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) relatedList section ` +
|
|
96
|
+
`"${section.title}" is not supported — relatedList is a projectionDetail-only ` +
|
|
97
|
+
`primitive (fw#2166). Move the section to a projectionDetail screen or remove it.`);
|
|
98
|
+
}
|
|
99
|
+
if (isWriteFormEditSection(section)) {
|
|
100
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) writeForm section ` +
|
|
101
|
+
`"${section.title}" is not supported — writeForm is a projectionDetail-only primitive. Move the section to a projectionDetail screen or remove it.`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function editSectionLabel(section) {
|
|
105
|
+
return section.title ?? section.id ?? "(untitled)";
|
|
106
|
+
}
|
|
107
|
+
export function validateRecordSectionActions(ctx, screenId, kind, section) {
|
|
108
|
+
const actionContext = `section "${editSectionLabel(section)}" action`;
|
|
109
|
+
for (const action of section.actions ?? []) {
|
|
110
|
+
validateActionHasIcon(ctx.feature.name, screenId, kind, actionContext, action);
|
|
111
|
+
validateRecordActionRedirect(ctx.feature, screenId, kind, action, ctx);
|
|
112
|
+
validateNavigateActionTab(ctx.feature.name, screenId, kind, actionContext, action, ctx);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const NO_ROW_SUBJECT = {
|
|
116
|
+
projectionDetail: "a detail screen",
|
|
117
|
+
entityEdit: "an edit screen",
|
|
118
|
+
};
|
|
119
|
+
// Header actions reuse RowAction (the displayed record stands in for the row),
|
|
120
|
+
// so the same navigate/writeHandler existence checks as list rowActions apply.
|
|
121
|
+
// `rowClick` is rejected outright: there is no row to click.
|
|
122
|
+
export function validateRecordHeaderAction(ctx, screenId, kind, screenEntity, action) {
|
|
123
|
+
const featureName = ctx.feature.name;
|
|
124
|
+
validateRecordActionRedirect(ctx.feature, screenId, kind, action, ctx);
|
|
125
|
+
if (isRowClickAction(action)) {
|
|
126
|
+
throw new Error(`[Feature ${featureName}] Screen "${qualifyEntityName(featureName, "screen", screenId)}" ` +
|
|
127
|
+
`(${kind}) action "${action.id}" sets rowClick: true — there is no row to ` +
|
|
128
|
+
`click on ${NO_ROW_SUBJECT[kind]}. Remove rowClick.`);
|
|
129
|
+
}
|
|
130
|
+
validateRowActionTarget(listActionContext(ctx, screenId, kind, screenEntity, `Screen "${screenId}" (${kind})`), "action", "rowAction", action);
|
|
131
|
+
}
|
|
132
|
+
export function navigateActionFromLink(id, navigate) {
|
|
133
|
+
return {
|
|
134
|
+
kind: "navigate",
|
|
135
|
+
id,
|
|
136
|
+
label: id,
|
|
137
|
+
...(navigate.screen !== undefined ? { screen: navigate.screen } : {}),
|
|
138
|
+
...(navigate.entity !== undefined ? { entity: navigate.entity } : {}),
|
|
139
|
+
...(navigate.entityId !== undefined ? { entityId: navigate.entityId } : {}),
|
|
140
|
+
...(navigate.tab !== undefined ? { tab: navigate.tab } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -24,7 +24,7 @@ type RelatedListSectionValidation = {
|
|
|
24
24
|
}>;
|
|
25
25
|
};
|
|
26
26
|
export declare function validateRelatedListSection(args: RelatedListSectionValidation): void;
|
|
27
|
-
type ListActionTargetContext = NavigateTargetLookups & {
|
|
27
|
+
export type ListActionTargetContext = NavigateTargetLookups & {
|
|
28
28
|
readonly featureName: string;
|
|
29
29
|
readonly screenId: string;
|
|
30
30
|
readonly screenType: NavigateSourceScreenType;
|
|
@@ -34,6 +34,7 @@ type ListActionTargetContext = NavigateTargetLookups & {
|
|
|
34
34
|
readonly allWriteHandlerQns: ReadonlySet<string>;
|
|
35
35
|
};
|
|
36
36
|
export declare function validateListActionTargets(ctx: ListActionTargetContext, actionLabel: "rowAction" | "emptyState action", action: RowAction): void;
|
|
37
|
+
export declare function validateRowActionTarget(ctx: ListActionTargetContext, actionLabel: "rowAction" | "emptyState action" | "action", paramsLabel: "rowAction" | "emptyState action", action: RowAction): void;
|
|
37
38
|
export declare function rejectInertColumnSortable(featureName: string, screenId: string, screenType: "entityList" | "projectionList", column: {
|
|
38
39
|
readonly field: string;
|
|
39
40
|
readonly sortable?: boolean;
|
|
@@ -2,7 +2,10 @@ import { REFERENCE_LOOKUP_SOURCES } from "../../ui-types/list-row-meta.js";
|
|
|
2
2
|
import { parseRefTarget } from "../parse-ref-target.js";
|
|
3
3
|
import { qualifyEntityName } from "../qualified-name.js";
|
|
4
4
|
import { explicitListScreenId, normalizeListColumn, resolveNavParentScreen, } from "../screen-helpers.js";
|
|
5
|
-
import {
|
|
5
|
+
import { validateListBulkActions } from "./list-bulk-actions.js";
|
|
6
|
+
import { validateListTableOptions } from "./list-table-options.js";
|
|
7
|
+
import { validateRelatedListGroupingRules } from "./related-list-table-rules.js";
|
|
8
|
+
import { assertWriteHandlerRegistered, isRowClickAction, rejectRedirectOnListRowAction, resolveRowActionNavigateTarget, validateClientActionName, validateDrawerTargetAction, validateRowActionNavigateParams, } from "./screens-actions.js";
|
|
6
9
|
// validateScreenHasNavArea runs once per screen; the index is built once per
|
|
7
10
|
// featureMap (immutable during boot) instead of re-scanning every feature each call.
|
|
8
11
|
const navAreaIndexByFeatureMap = new WeakMap();
|
|
@@ -70,6 +73,14 @@ export function validateScreenHasNavArea(feature, screenId, screen, featureMap)
|
|
|
70
73
|
}
|
|
71
74
|
// Shared by projectionDetail relatedList sections and entityList.expandableRow:
|
|
72
75
|
// both render a relatedList whose rows come from an arbitrary query.
|
|
76
|
+
function validateRelatedListTableAndBulk(args) {
|
|
77
|
+
const { featureName, where, section } = args;
|
|
78
|
+
validateRelatedListGroupingRules(`[Feature ${featureName}] ${where}`, section, args.screenType === "entityList expandableRow");
|
|
79
|
+
validateListTableOptions(`[Feature ${featureName}] ${where}`, section.table, section.columns);
|
|
80
|
+
if (section.rowActions !== undefined)
|
|
81
|
+
validateRelatedListRowActions(args, section.rowActions);
|
|
82
|
+
validateListBulkActions(`[Feature ${featureName}] ${where}`, section.bulkActions, section.rowActions);
|
|
83
|
+
}
|
|
73
84
|
export function validateRelatedListSection(args) {
|
|
74
85
|
const { featureName, featureMap, where, section } = args;
|
|
75
86
|
if (!section.query || typeof section.query !== "string") {
|
|
@@ -93,8 +104,7 @@ export function validateRelatedListSection(args) {
|
|
|
93
104
|
`detailFor: "${targetEntity}". Add detailFor: "${targetEntity}" to the screen that shows it.`);
|
|
94
105
|
}
|
|
95
106
|
}
|
|
96
|
-
|
|
97
|
-
validateRelatedListRowActions(args, section.rowActions);
|
|
107
|
+
validateRelatedListTableAndBulk(args);
|
|
98
108
|
if (section.defaultSort !== undefined)
|
|
99
109
|
validateRelatedListDefaultSort(args, section.defaultSort);
|
|
100
110
|
if (section.facets !== undefined) {
|
|
@@ -115,13 +125,19 @@ function rejectEmptyStateActionParams(ctx, actionLabel, action) {
|
|
|
115
125
|
// Target checks shared by list/related-list rowActions and emptyState.action:
|
|
116
126
|
// navigate target + params, drawer target, write-handler registration.
|
|
117
127
|
export function validateListActionTargets(ctx, actionLabel, action) {
|
|
118
|
-
|
|
119
|
-
rejectRedirectOnListRowAction(featureName, screenId, screenType, action);
|
|
128
|
+
rejectRedirectOnListRowAction(ctx.featureName, ctx.screenId, ctx.screenType, action);
|
|
120
129
|
if (actionLabel === "emptyState action")
|
|
121
130
|
rejectEmptyStateActionParams(ctx, actionLabel, action);
|
|
131
|
+
validateRowActionTarget(ctx, actionLabel, actionLabel, action);
|
|
132
|
+
}
|
|
133
|
+
// Header actions have always reported navigate-params errors under the
|
|
134
|
+
// "rowAction" label, which validateRowActionNavigateParams only knows in list
|
|
135
|
+
// labels — hence paramsLabel apart from actionLabel.
|
|
136
|
+
export function validateRowActionTarget(ctx, actionLabel, paramsLabel, action) {
|
|
137
|
+
const { featureName, screenId, screenType } = ctx;
|
|
122
138
|
if (action.kind === "navigate") {
|
|
123
139
|
const target = resolveRowActionNavigateTarget(featureName, screenId, screenType, actionLabel, action, ctx.allScreenQns, ctx.navTargetShortIds, ctx.screensByShortId, ctx.detailForScreens);
|
|
124
|
-
validateRowActionNavigateParams(featureName, screenId, screenType, ctx.screenEntity, action, target,
|
|
140
|
+
validateRowActionNavigateParams(featureName, screenId, screenType, ctx.screenEntity, action, target, paramsLabel);
|
|
125
141
|
}
|
|
126
142
|
else if (action.kind === "drawer") {
|
|
127
143
|
validateDrawerTargetAction(featureName, screenId, screenType, actionLabel, action, ctx.screens);
|
|
@@ -129,11 +145,8 @@ export function validateListActionTargets(ctx, actionLabel, action) {
|
|
|
129
145
|
else if (action.kind === "clientAction") {
|
|
130
146
|
validateClientActionName(featureName, screenId, screenType, actionLabel, action);
|
|
131
147
|
}
|
|
132
|
-
else
|
|
133
|
-
|
|
134
|
-
`${actionLabel} "${action.id}" handler "${action.handler}" ` +
|
|
135
|
-
`is not a registered write-handler. Check the QN spelling (expected ` +
|
|
136
|
-
`"<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`);
|
|
148
|
+
else {
|
|
149
|
+
assertWriteHandlerRegistered(featureName, `${ctx.where} ${actionLabel} "${action.id}"`, action.handler, ctx.allWriteHandlerQns);
|
|
137
150
|
}
|
|
138
151
|
}
|
|
139
152
|
function validateRelatedListRowActions(args, rowActions) {
|
|
@@ -10,8 +10,6 @@ export declare function validateScreens(feature: FeatureDefinition, featureMap:
|
|
|
10
10
|
readonly featureName: string;
|
|
11
11
|
readonly screen: ScreenDefinition;
|
|
12
12
|
}>): void;
|
|
13
|
-
export declare function findEntityFeature(entityName: string, featureMap: ReadonlyMap<string, FeatureDefinition>): string | undefined;
|
|
14
|
-
export declare function buildUnknownFieldMessage(featureName: string, screenId: string, fieldName: string, entityName: string, knownFields: ReadonlySet<string>): string;
|
|
15
13
|
export declare function collectScreenQns(features: readonly FeatureDefinition[]): Set<string>;
|
|
16
14
|
export declare function screenShortIdsFrom(allScreenQns: ReadonlySet<string>): Set<string>;
|
|
17
15
|
export declare function collectScreensByShortId(features: readonly FeatureDefinition[]): Map<string, ReadonlyArray<{
|