@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.
Files changed (375) hide show
  1. package/AGENTS.md +9 -1
  2. package/agents/examples/anonymous-access.md +1 -1
  3. package/agents/examples/apex-landing.md +204 -0
  4. package/agents/examples/api-worker-split.md +64 -0
  5. package/agents/examples/app-config.md +14 -0
  6. package/agents/examples/auth-mfa.md +115 -0
  7. package/agents/examples/env-composition.md +28 -1
  8. package/agents/examples/golden-path.md +13 -1
  9. package/agents/examples/legal-pages.md +1 -1
  10. package/agents/examples/lifecycle-hooks.md +55 -2
  11. package/agents/examples/list-table-options.md +466 -0
  12. package/agents/examples/pdf-audit-export.md +4 -2
  13. package/agents/examples/prod-boot-options.md +16 -0
  14. package/agents/examples/rate-limiting.md +48 -0
  15. package/agents/examples/record-detail-layout.md +692 -0
  16. package/agents/examples/seed-migration.md +212 -0
  17. package/agents/examples/state-machine.md +3 -2
  18. package/agents/examples/tenant-isolation.md +73 -1
  19. package/agents/examples/wizard-form.md +941 -0
  20. package/agents/examples/writeform-section.md +548 -0
  21. package/agents/examples.md +1 -0
  22. package/agents/features.md +19 -2
  23. package/agents/widgets.md +2 -0
  24. package/dist/api/api-constants.d.ts +1 -0
  25. package/dist/api/api-constants.js +2 -0
  26. package/dist/api/auth-routes-shared.d.ts +2 -0
  27. package/dist/api/auth-routes-shared.js +5 -0
  28. package/dist/api/client-error-route.js +10 -5
  29. package/dist/api/entity-read-access.d.ts +2 -1
  30. package/dist/api/entity-read-access.js +14 -0
  31. package/dist/api/export/export-cells-reference.d.ts +9 -0
  32. package/dist/api/export/export-cells-reference.js +21 -0
  33. package/dist/api/export/export-cells.d.ts +11 -0
  34. package/dist/api/export/export-cells.js +136 -0
  35. package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
  36. package/dist/api/export/export-ciphertext-guard.js +27 -0
  37. package/dist/api/export/export-columns.d.ts +29 -0
  38. package/dist/api/export/export-columns.js +85 -0
  39. package/dist/api/export/export-errors.d.ts +4 -0
  40. package/dist/api/export/export-errors.js +14 -0
  41. package/dist/api/export/export-route.d.ts +4 -0
  42. package/dist/api/export/export-route.js +74 -0
  43. package/dist/api/export/export-rows.d.ts +13 -0
  44. package/dist/api/export/export-rows.js +62 -0
  45. package/dist/api/export/export-screen.d.ts +12 -0
  46. package/dist/api/export/export-screen.js +47 -0
  47. package/dist/api/export/export-xlsx-writer.d.ts +4 -0
  48. package/dist/api/export/export-xlsx-writer.js +21 -0
  49. package/dist/api/extra-route.d.ts +4 -0
  50. package/dist/api/index.d.ts +3 -1
  51. package/dist/api/index.js +3 -1
  52. package/dist/api/pii-leak-guard.d.ts +3 -0
  53. package/dist/api/pii-leak-guard.js +3 -2
  54. package/dist/api/request-context.d.ts +1 -1
  55. package/dist/api/request-context.js +1 -1
  56. package/dist/api/routes.d.ts +16 -0
  57. package/dist/api/routes.js +2 -2
  58. package/dist/api/server-boot-checks.d.ts +10 -0
  59. package/dist/api/server-boot-checks.js +157 -0
  60. package/dist/api/server-event-dispatcher.d.ts +26 -0
  61. package/dist/api/server-event-dispatcher.js +208 -0
  62. package/dist/api/server-extra-routes.d.ts +11 -0
  63. package/dist/api/server-extra-routes.js +206 -0
  64. package/dist/api/server-middleware.d.ts +20 -0
  65. package/dist/api/server-middleware.js +211 -0
  66. package/dist/api/server-routes.d.ts +19 -0
  67. package/dist/api/server-routes.js +299 -0
  68. package/dist/api/server-websocket-routes.d.ts +4 -0
  69. package/dist/api/server-websocket-routes.js +108 -0
  70. package/dist/api/server-wiring.d.ts +32 -0
  71. package/dist/api/server-wiring.js +238 -0
  72. package/dist/api/server.d.ts +10 -15
  73. package/dist/api/server.js +48 -1251
  74. package/dist/api/session-cookies.js +4 -4
  75. package/dist/api/sse-route.d.ts +2 -1
  76. package/dist/api/sse-route.js +27 -13
  77. package/dist/config/index.d.ts +3 -1
  78. package/dist/crypto/owner-transition-rekey.d.ts +5 -1
  79. package/dist/crypto/owner-transition-rekey.js +1 -1
  80. package/dist/crypto/pg-kms-adapter.d.ts +1 -0
  81. package/dist/crypto/pg-kms-adapter.js +12 -18
  82. package/dist/crypto/pii-field-encryption.d.ts +5 -1
  83. package/dist/crypto/pii-field-encryption.js +3 -1
  84. package/dist/db/aggregate-sql.d.ts +23 -0
  85. package/dist/db/aggregate-sql.js +165 -0
  86. package/dist/db/closed-connection.d.ts +9 -0
  87. package/dist/db/closed-connection.js +88 -0
  88. package/dist/db/connection.d.ts +4 -5
  89. package/dist/db/connection.js +29 -20
  90. package/dist/db/dialect.js +1 -1
  91. package/dist/db/distinct-on.d.ts +1 -1
  92. package/dist/db/driver-row-coercion.d.ts +2 -0
  93. package/dist/db/driver-row-coercion.js +186 -0
  94. package/dist/db/driver-value-preparation.d.ts +16 -0
  95. package/dist/db/driver-value-preparation.js +101 -0
  96. package/dist/db/entity-field-encryption.d.ts +1 -0
  97. package/dist/db/entity-field-encryption.js +1 -1
  98. package/dist/db/entity-table-meta.js +103 -110
  99. package/dist/db/event-store-executor-context.d.ts +2 -1
  100. package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
  101. package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
  102. package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
  103. package/dist/db/event-store-executor-read-aggregate.js +143 -0
  104. package/dist/db/event-store-executor-read-filter.d.ts +31 -0
  105. package/dist/db/event-store-executor-read-filter.js +206 -0
  106. package/dist/db/event-store-executor-read-paging.d.ts +14 -0
  107. package/dist/db/event-store-executor-read-paging.js +99 -0
  108. package/dist/db/event-store-executor-read-rows.d.ts +6 -0
  109. package/dist/db/event-store-executor-read-rows.js +32 -0
  110. package/dist/db/event-store-executor-read-search.d.ts +28 -0
  111. package/dist/db/event-store-executor-read-search.js +221 -0
  112. package/dist/db/event-store-executor-read-sort.d.ts +41 -0
  113. package/dist/db/event-store-executor-read-sort.js +130 -0
  114. package/dist/db/event-store-executor-read-where.d.ts +24 -0
  115. package/dist/db/event-store-executor-read-where.js +81 -0
  116. package/dist/db/event-store-executor-read.d.ts +2 -9
  117. package/dist/db/event-store-executor-read.js +48 -701
  118. package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
  119. package/dist/db/event-store-executor-write-atomic.js +41 -0
  120. package/dist/db/event-store-executor-write-steps.d.ts +17 -0
  121. package/dist/db/event-store-executor-write-steps.js +95 -0
  122. package/dist/db/event-store-executor-write.js +405 -509
  123. package/dist/db/event-store-executor.js +2 -1
  124. package/dist/db/executor-managed-tables.js +1 -1
  125. package/dist/db/index.d.ts +7 -3
  126. package/dist/db/index.js +6 -2
  127. package/dist/db/list-aggregate-labels.d.ts +14 -0
  128. package/dist/db/list-aggregate-labels.js +26 -0
  129. package/dist/db/migrate-generator.js +71 -86
  130. package/dist/db/migrate-runner.js +7 -4
  131. package/dist/db/pg-error.js +26 -21
  132. package/dist/db/queries/backfill-pii.js +3 -6
  133. package/dist/db/queries/database-now.js +2 -1
  134. package/dist/db/queries/shadow-swap.js +2 -3
  135. package/dist/db/query.d.ts +16 -15
  136. package/dist/db/query.js +76 -804
  137. package/dist/db/reference-data.js +19 -2
  138. package/dist/db/statement-timeout.js +2 -1
  139. package/dist/db/table-builder.js +113 -166
  140. package/dist/db/table-info.d.ts +10 -0
  141. package/dist/db/table-info.js +45 -0
  142. package/dist/db/temporal-value-guards.d.ts +6 -0
  143. package/dist/db/temporal-value-guards.js +15 -0
  144. package/dist/db/tenant-db-runner.d.ts +1 -0
  145. package/dist/db/tenant-db-runner.js +3 -0
  146. package/dist/db/tenant-db-upsert.d.ts +5 -0
  147. package/dist/db/tenant-db-upsert.js +91 -0
  148. package/dist/db/tenant-db.js +50 -10
  149. package/dist/db/where-clause.d.ts +6 -0
  150. package/dist/db/where-clause.js +236 -0
  151. package/dist/derivatives/delete-stored-file.js +2 -1
  152. package/dist/engine/boot-validator/boot-check.js +1 -1
  153. package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
  154. package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
  155. package/dist/engine/boot-validator/detail-screens.js +2 -2
  156. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  157. package/dist/engine/boot-validator/entity-handler.js +17 -0
  158. package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
  159. package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
  160. package/dist/engine/boot-validator/form-derivations.js +35 -1
  161. package/dist/engine/boot-validator/i18n-keys.js +5 -3
  162. package/dist/engine/boot-validator/index.js +2 -0
  163. package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
  164. package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
  165. package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
  166. package/dist/engine/boot-validator/list-column-layout.js +41 -0
  167. package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
  168. package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
  169. package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
  170. package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
  171. package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
  172. package/dist/engine/boot-validator/list-table-export-options.js +27 -0
  173. package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
  174. package/dist/engine/boot-validator/list-table-options.js +77 -0
  175. package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
  176. package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
  177. package/dist/engine/boot-validator/pii-retention.js +233 -155
  178. package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
  179. package/dist/engine/boot-validator/projection-list-screens.js +3 -13
  180. package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
  181. package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
  182. package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
  183. package/dist/engine/boot-validator/screens-actions.js +8 -1
  184. package/dist/engine/boot-validator/screens-dashboard.js +2 -5
  185. package/dist/engine/boot-validator/screens-forms.js +2 -6
  186. package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
  187. package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
  188. package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
  189. package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
  190. package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
  191. package/dist/engine/boot-validator/screens-kind-entity-list.js +235 -0
  192. package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
  193. package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
  194. package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
  195. package/dist/engine/boot-validator/screens-kind-projection-list.js +56 -0
  196. package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
  197. package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
  198. package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
  199. package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
  200. package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
  201. package/dist/engine/boot-validator/screens-lists.js +24 -11
  202. package/dist/engine/boot-validator/screens.d.ts +0 -2
  203. package/dist/engine/boot-validator/screens.js +99 -860
  204. package/dist/engine/build-app-schema.d.ts +2 -1
  205. package/dist/engine/build-app-schema.js +65 -97
  206. package/dist/engine/build-config-feature-schema.js +6 -1
  207. package/dist/engine/define-feature.js +13 -2
  208. package/dist/engine/define-handler.js +1 -0
  209. package/dist/engine/entity-handlers.d.ts +13 -0
  210. package/dist/engine/entity-handlers.js +10 -21
  211. package/dist/engine/entity-list-runtime-options.d.ts +8 -0
  212. package/dist/engine/entity-list-runtime-options.js +18 -0
  213. package/dist/engine/feature-ast/extractors/handlers.js +1 -0
  214. package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
  215. package/dist/engine/feature-ast/extractors/index.js +1 -1
  216. package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
  217. package/dist/engine/feature-ast/extractors/round1.js +6 -0
  218. package/dist/engine/feature-ast/extractors/round2.js +25 -6
  219. package/dist/engine/feature-ast/index.d.ts +1 -1
  220. package/dist/engine/feature-ast/parse.js +3 -1
  221. package/dist/engine/feature-ast/patch.d.ts +2 -0
  222. package/dist/engine/feature-ast/patch.js +5 -182
  223. package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -245
  224. package/dist/engine/feature-ast/pattern-change-schema.js +10 -163
  225. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
  226. package/dist/engine/feature-ast/pattern-id-matchers.js +211 -0
  227. package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
  228. package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
  229. package/dist/engine/feature-ast/patterns.d.ts +14 -1
  230. package/dist/engine/feature-ast/patterns.js +44 -51
  231. package/dist/engine/feature-ast/render.js +52 -87
  232. package/dist/engine/feature-builder-state.d.ts +1 -0
  233. package/dist/engine/feature-builder-state.js +1 -0
  234. package/dist/engine/feature-entity-handlers.js +3 -0
  235. package/dist/engine/feature-manifest.d.ts +1 -0
  236. package/dist/engine/feature-manifest.js +1 -0
  237. package/dist/engine/field-access.d.ts +1 -0
  238. package/dist/engine/field-access.js +12 -0
  239. package/dist/engine/form-derivations.js +18 -7
  240. package/dist/engine/handler-field-references.js +1 -1
  241. package/dist/engine/index.d.ts +2 -2
  242. package/dist/engine/index.js +1 -1
  243. package/dist/engine/pattern-library/library.js +2 -1
  244. package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
  245. package/dist/engine/pattern-library/static-schemas.js +9 -0
  246. package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
  247. package/dist/engine/project-app-schema-for-roles.js +6 -4
  248. package/dist/engine/registry-ingest.js +1 -1
  249. package/dist/engine/schema-builder.js +228 -212
  250. package/dist/engine/screen-filter-ops.js +2 -0
  251. package/dist/engine/screen-helpers.js +1 -1
  252. package/dist/engine/screen-kind.d.ts +7 -0
  253. package/dist/engine/screen-kind.js +1 -0
  254. package/dist/engine/strip-disabled-features-from-app-schema.js +58 -17
  255. package/dist/engine/types/feature.d.ts +1 -0
  256. package/dist/engine/types/index.d.ts +4 -3
  257. package/dist/engine/types/screen.d.ts +1 -1
  258. package/dist/engine/types/screen.js +1 -1
  259. package/dist/engine/validate-projection-allowlist.js +2 -1
  260. package/dist/entrypoint/index.d.ts +6 -1
  261. package/dist/entrypoint/index.js +10 -1
  262. package/dist/errors/docs-url.d.ts +1 -1
  263. package/dist/errors/docs-url.js +22 -2
  264. package/dist/es-ops/context.js +1 -1
  265. package/dist/event-store/admin-api.js +1 -1
  266. package/dist/event-store/event-store.d.ts +1 -0
  267. package/dist/event-store/event-store.js +17 -8
  268. package/dist/event-store/provenance-append.js +1 -1
  269. package/dist/files/bind-pending-file-refs.js +3 -3
  270. package/dist/files/create-file-ref-from-bytes.js +7 -1
  271. package/dist/files/create-file-ref.d.ts +3 -0
  272. package/dist/files/create-file-ref.js +11 -2
  273. package/dist/files/file-handle.js +2 -3
  274. package/dist/files/file-routes.d.ts +2 -0
  275. package/dist/files/file-routes.js +40 -10
  276. package/dist/files/provider-resolver.d.ts +1 -0
  277. package/dist/files/provider-resolver.js +1 -1
  278. package/dist/files/record-write-check.d.ts +6 -0
  279. package/dist/files/record-write-check.js +13 -0
  280. package/dist/files/resolve-content-type.js +43 -0
  281. package/dist/files/upload-ledger.d.ts +2 -2
  282. package/dist/files/upload-ledger.js +11 -4
  283. package/dist/i18n/required-surface-keys.js +189 -208
  284. package/dist/internal/stack-support.d.ts +2 -1
  285. package/dist/internal/stack-support.js +1 -1
  286. package/dist/internal/testing.js +1 -1
  287. package/dist/jobs/job-runner-context.d.ts +32 -0
  288. package/dist/jobs/job-runner-context.js +305 -0
  289. package/dist/jobs/job-runner-execution.d.ts +7 -0
  290. package/dist/jobs/job-runner-execution.js +160 -0
  291. package/dist/jobs/job-runner-gates.d.ts +19 -0
  292. package/dist/jobs/job-runner-gates.js +198 -0
  293. package/dist/jobs/job-runner-helpers.d.ts +17 -3
  294. package/dist/jobs/job-runner-helpers.js +61 -2
  295. package/dist/jobs/job-runner-scope.d.ts +44 -0
  296. package/dist/jobs/job-runner-scope.js +102 -0
  297. package/dist/jobs/job-runner-types.d.ts +12 -0
  298. package/dist/jobs/job-runner.d.ts +0 -1
  299. package/dist/jobs/job-runner.js +50 -657
  300. package/dist/observability/escape-hatch-report.d.ts +3 -0
  301. package/dist/observability/escape-hatch-report.js +21 -3
  302. package/dist/pipeline/after-rollback-sink.d.ts +6 -0
  303. package/dist/pipeline/after-rollback-sink.js +32 -0
  304. package/dist/pipeline/cascade-handler.js +165 -138
  305. package/dist/pipeline/dispatch-batch.js +8 -1
  306. package/dist/pipeline/dispatch-query.js +10 -3
  307. package/dist/pipeline/dispatch-shared.d.ts +13 -3
  308. package/dist/pipeline/dispatch-shared.js +62 -710
  309. package/dist/pipeline/dispatch-write.js +296 -189
  310. package/dist/pipeline/event-dispatcher-admin.js +2 -1
  311. package/dist/pipeline/event-dispatcher-delivery.js +6 -6
  312. package/dist/pipeline/event-dispatcher.d.ts +1 -0
  313. package/dist/pipeline/event-dispatcher.js +6 -9
  314. package/dist/pipeline/handler-context-auth.d.ts +9 -0
  315. package/dist/pipeline/handler-context-auth.js +54 -0
  316. package/dist/pipeline/handler-context-config.d.ts +14 -0
  317. package/dist/pipeline/handler-context-config.js +113 -0
  318. package/dist/pipeline/handler-context-db.d.ts +24 -0
  319. package/dist/pipeline/handler-context-db.js +149 -0
  320. package/dist/pipeline/handler-context-dispatch.d.ts +23 -0
  321. package/dist/pipeline/handler-context-dispatch.js +101 -0
  322. package/dist/pipeline/handler-context-events.d.ts +41 -0
  323. package/dist/pipeline/handler-context-events.js +239 -0
  324. package/dist/pipeline/handler-context-notify.d.ts +12 -0
  325. package/dist/pipeline/handler-context-notify.js +55 -0
  326. package/dist/pipeline/handler-context-observability.d.ts +7 -0
  327. package/dist/pipeline/handler-context-observability.js +21 -0
  328. package/dist/pipeline/handler-context-scope.d.ts +48 -0
  329. package/dist/pipeline/handler-context-scope.js +88 -0
  330. package/dist/pipeline/notify-scope.js +12 -1
  331. package/dist/pipeline/pre-handler-gates.js +20 -4
  332. package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
  333. package/dist/pipeline/pre-transaction-reservations.js +9 -3
  334. package/dist/pipeline/projection-rebuild.js +2 -1
  335. package/dist/pipeline/system-hooks.d.ts +3 -1
  336. package/dist/pipeline/system-hooks.js +55 -6
  337. package/dist/pipeline/write-result-masking.js +13 -10
  338. package/dist/schema-cli/apply-command.d.ts +2 -0
  339. package/dist/schema-cli/apply-command.js +97 -0
  340. package/dist/schema-cli/baseline-command.d.ts +2 -0
  341. package/dist/schema-cli/baseline-command.js +33 -0
  342. package/dist/schema-cli/generate-command.d.ts +2 -0
  343. package/dist/schema-cli/generate-command.js +90 -0
  344. package/dist/schema-cli/shared.d.ts +39 -0
  345. package/dist/schema-cli/shared.js +95 -0
  346. package/dist/schema-cli/status-command.d.ts +2 -0
  347. package/dist/schema-cli/status-command.js +73 -0
  348. package/dist/schema-cli/validate-boot-input.d.ts +5 -0
  349. package/dist/schema-cli/validate-boot-input.js +130 -0
  350. package/dist/schema-cli/validate-command.d.ts +2 -0
  351. package/dist/schema-cli/validate-command.js +130 -0
  352. package/dist/schema-cli.d.ts +2 -18
  353. package/dist/schema-cli.js +47 -604
  354. package/dist/search/meilisearch-adapter.js +2 -1
  355. package/dist/search/reindex-entity.js +15 -5
  356. package/dist/ui-types/index.d.ts +2 -2
  357. package/dist/ui-types/index.js +1 -1
  358. package/dist/upgrade-changelog.d.ts +2 -0
  359. package/dist/upgrade-changelog.js +14 -0
  360. package/dist/upgrade-cli.d.ts +3 -12
  361. package/dist/upgrade-cli.js +171 -197
  362. package/dist/upgrade-marker-parse.d.ts +32 -0
  363. package/dist/upgrade-marker-parse.js +73 -0
  364. package/package.json +9 -6
  365. package/src/changes.json +555 -0
  366. package/src/scripts/codemod/codemod-support.ts +1 -1
  367. package/src/scripts/codemod/db-factory.ts +1 -1
  368. package/src/scripts/codemod/feature-layout.ts +10 -2
  369. package/src/scripts/codemod/object-form.ts +41 -4
  370. package/src/scripts/codemod/package-merge.ts +58 -8
  371. package/src/scripts/codemod/pii-personal-migration.ts +175 -145
  372. package/src/scripts/codemod/testing-injection.ts +122 -42
  373. package/src/scripts/codemod/testing-move.ts +29 -7
  374. package/dist/engine/provider-plugin-context.d.ts +0 -3
  375. 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 { isRowClickAction, rejectRedirectOnListRowAction, resolveRowActionNavigateTarget, validateClientActionName, validateDrawerTargetAction, validateRowActionNavigateParams, } from "./screens-actions.js";
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
- if (section.rowActions !== undefined)
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
- const { featureName, screenId, screenType } = ctx;
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, actionLabel);
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 if (!ctx.allWriteHandlerQns.has(action.handler)) {
133
- throw new Error(`[Feature ${featureName}] ${ctx.where} ` +
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<{