@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,49 @@
1
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { isKebabSegment } from "../qualified-name.js";
3
+ import { LIST_BULK_ACTION_KEYS } from "../types/screen.js";
4
+ function validateBulkActionTarget(where, bulkId, target, rowActions) {
5
+ if (typeof target !== "string" || target === "") {
6
+ throw new Error(`${where} bulk action "${bulkId}" needs rowAction: the id of a row action.`);
7
+ }
8
+ const rowAction = rowActions.find((action) => action.id === target);
9
+ if (rowAction === undefined) {
10
+ const known = rowActions.map((action) => action.id).join(", ") || "(none)";
11
+ throw new Error(`${where} bulk action "${bulkId}" points at rowAction "${target}", which is not declared ` +
12
+ `in rowActions. Declared row actions: ${known}.`);
13
+ }
14
+ if (rowAction.kind !== undefined && rowAction.kind !== "writeHandler") {
15
+ throw new Error(`${where} bulk action "${bulkId}" points at rowAction "${target}" of kind "${rowAction.kind}". ` +
16
+ `Bulk actions run one server write per row, so only writeHandler row actions are allowed.`);
17
+ }
18
+ }
19
+ /** `where` is the full message prefix, e.g. `[Feature x] Screen "y" (entityList)`. */
20
+ export function validateListBulkActions(where, bulkActions, rowActions) {
21
+ // skip: no bulk actions declared
22
+ if (bulkActions === undefined)
23
+ return;
24
+ if (!Array.isArray(bulkActions) || bulkActions.length === 0) {
25
+ throw new Error(`${where} bulkActions must be a non-empty array like [{ id: "archive", rowAction: "archive" }].`);
26
+ }
27
+ const seen = new Set();
28
+ for (const entry of bulkActions) {
29
+ if (!isPlainObject(entry)) {
30
+ throw new Error(`${where} bulkActions entries must be objects with id and rowAction.`);
31
+ }
32
+ const id = entry["id"];
33
+ if (typeof id !== "string" || !isKebabSegment(id)) {
34
+ throw new Error(`${where} bulk action id ${JSON.stringify(id)} must be a kebab-case string.`);
35
+ }
36
+ if (seen.has(id))
37
+ throw new Error(`${where} declares bulk action "${id}" twice.`);
38
+ seen.add(id);
39
+ const unknown = Object.keys(entry).filter((key) => !isOneOfBulkKeys(key));
40
+ if (unknown.length > 0) {
41
+ throw new Error(`${where} bulk action "${id}" has unknown key(s) ${unknown.map((k) => `"${k}"`).join(", ")}. ` +
42
+ `Allowed keys: ${LIST_BULK_ACTION_KEYS.join(", ")}.`);
43
+ }
44
+ validateBulkActionTarget(where, id, entry["rowAction"], rowActions ?? []);
45
+ }
46
+ }
47
+ function isOneOfBulkKeys(key) {
48
+ return LIST_BULK_ACTION_KEYS.some((allowed) => allowed === key);
49
+ }
@@ -0,0 +1,8 @@
1
+ import { type ListColumnSpec } from "../types/screen.ts";
2
+ type ColumnLayoutFlags = {
3
+ readonly pinning: boolean;
4
+ readonly resizing: boolean;
5
+ };
6
+ /** Checks `pinned`, `width` and `minWidth` of every column against the table's opt-in flags. */
7
+ export declare function validateColumnLayout(where: string, flags: ColumnLayoutFlags, columns: readonly ListColumnSpec[]): void;
8
+ export {};
@@ -0,0 +1,41 @@
1
+ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { normalizeListColumn } from "../screen-helpers.js";
3
+ import { LIST_COLUMN_PINS } from "../types/screen.js";
4
+ function isPositiveFiniteNumber(value) {
5
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
6
+ }
7
+ function validatePixelWidth(where, field, key, value) {
8
+ if (!isPositiveFiniteNumber(value)) {
9
+ throw new Error(`${where} column "${field}" has ${key} ${JSON.stringify(value)} — ` +
10
+ `must be a finite number of pixels greater than 0.`);
11
+ }
12
+ }
13
+ /** Checks `pinned`, `width` and `minWidth` of every column against the table's opt-in flags. */
14
+ export function validateColumnLayout(where, flags, columns) {
15
+ for (const col of columns.map(normalizeListColumn)) {
16
+ if (col.pinned !== undefined) {
17
+ if (!isOneOf(LIST_COLUMN_PINS, col.pinned)) {
18
+ throw new Error(`${where} column "${col.field}" has pinned ${JSON.stringify(col.pinned)} — ` +
19
+ `must be one of ${LIST_COLUMN_PINS.map((pin) => `"${pin}"`).join(", ")}.`);
20
+ }
21
+ if (!flags.pinning) {
22
+ throw new Error(`${where} column "${col.field}" is pinned but table.pinning is not enabled, ` +
23
+ `so the pin would have no effect. Set table: { pinning: true } or remove "pinned".`);
24
+ }
25
+ }
26
+ for (const key of ["width", "minWidth"]) {
27
+ const value = col[key];
28
+ if (value === undefined)
29
+ continue;
30
+ validatePixelWidth(where, col.field, key, value);
31
+ if (!flags.resizing) {
32
+ throw new Error(`${where} column "${col.field}" sets ${key} but table.resizing is not enabled, ` +
33
+ `so it would have no effect. Set table: { resizing: true } or remove "${key}".`);
34
+ }
35
+ }
36
+ if (col.width !== undefined && col.minWidth !== undefined && col.minWidth > col.width) {
37
+ throw new Error(`${where} column "${col.field}" has minWidth ${col.minWidth} greater than width ${col.width}. ` +
38
+ `Lower minWidth or raise width.`);
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,24 @@
1
+ import type { EntityDefinition, FieldDefinition } from "../types/index.ts";
2
+ import { type ListColumnSpec, type ListTableGrouping, type ListTableTotal } from "../types/screen.ts";
3
+ type FieldType = FieldDefinition["type"];
4
+ export type AggregateFieldInfo = {
5
+ readonly type: FieldType;
6
+ readonly multiple: boolean;
7
+ readonly secret: boolean;
8
+ };
9
+ export declare function groupingFieldsOf(grouping: {
10
+ readonly field?: unknown;
11
+ readonly fields?: unknown;
12
+ }): string[];
13
+ /** Structure and column membership; field types are checked where the entity is known. */
14
+ export declare function validateListTableAggregateShape(where: string, table: Readonly<Record<string, unknown>>, columns: readonly ListColumnSpec[]): void;
15
+ /**
16
+ * `lookup` answers undefined for a field it does not know. With `strict` (an entity
17
+ * stands behind the list) that is an error; otherwise the field is left to the server.
18
+ */
19
+ export declare function validateListTableAggregateFieldTypes(where: string, table: {
20
+ readonly grouping?: ListTableGrouping;
21
+ readonly totals?: readonly ListTableTotal[];
22
+ }, lookup: (field: string) => AggregateFieldInfo | undefined, strict: boolean): void;
23
+ export declare function entityAggregateFieldLookup(entity: EntityDefinition): (field: string) => AggregateFieldInfo | undefined;
24
+ export {};
@@ -0,0 +1,145 @@
1
+ import { LIST_AGGREGATE_MEASURE_KINDS } from "@cosmicdrift/kumiko-types/list-aggregate-types";
2
+ import { isOneOf, isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
3
+ import { isListAggregateGroupable, isListAggregateMeasureSupported, } from "../../db/aggregate-sql.js";
4
+ import { holdsCiphertextOrSecret } from "../../db/entity-field-encryption.js";
5
+ import { normalizeListColumn } from "../screen-helpers.js";
6
+ import { LIST_TABLE_GROUPING_KEYS, LIST_TABLE_TOTAL_KEYS, } from "../types/screen.js";
7
+ import { rejectUnknownKeys } from "./list-table-reject-unknown-keys.js";
8
+ function isStringArray(value) {
9
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
10
+ }
11
+ function isStringRecord(value) {
12
+ return isPlainObject(value) && Object.values(value).every((entry) => typeof entry === "string");
13
+ }
14
+ function isGroupKeyScalar(value) {
15
+ return value === null || ["string", "number", "boolean"].includes(typeof value);
16
+ }
17
+ function requireScreenColumn(where, path, field, columnFields) {
18
+ // skip: the field is a column, nothing to report
19
+ if (columnFields.has(field))
20
+ return;
21
+ throw new Error(`${where} ${path} "${field}" is not a column of this screen. ` +
22
+ `Columns: ${[...columnFields].join(", ")}.`);
23
+ }
24
+ const GROUPING_VALUE_CHECKS = [
25
+ { key: "field", isValid: (v) => typeof v === "string", expected: "must be a string" },
26
+ { key: "fields", isValid: isStringArray, expected: "must be an array of field names" },
27
+ { key: "userChoice", isValid: (v) => typeof v === "boolean", expected: "must be a boolean" },
28
+ { key: "label", isValid: (v) => typeof v === "string", expected: "must be an i18n key" },
29
+ { key: "labels", isValid: isStringRecord, expected: "must map group keys to i18n keys" },
30
+ {
31
+ key: "collapsedWhen",
32
+ isValid: isGroupKeyScalar,
33
+ expected: "must be a string, number, boolean or null",
34
+ },
35
+ ];
36
+ function validateGroupingValueTypes(where, grouping) {
37
+ for (const check of GROUPING_VALUE_CHECKS) {
38
+ const value = grouping[check.key];
39
+ if (value !== undefined && !check.isValid(value)) {
40
+ throw new Error(`${where} table.grouping.${check.key} ${check.expected}.`);
41
+ }
42
+ }
43
+ }
44
+ function validateGroupingFieldChoice(where, grouping) {
45
+ const { field, fields, userChoice } = grouping;
46
+ const offered = isStringArray(fields) ? fields : [];
47
+ if (field === undefined && (userChoice !== true || offered.length === 0)) {
48
+ throw new Error(`${where} table.grouping has no field. Set field, or userChoice: true with at least one entry in fields.`);
49
+ }
50
+ if (fields !== undefined && userChoice !== true) {
51
+ throw new Error(`${where} table.grouping.fields needs userChoice: true, or the user could never pick them.`);
52
+ }
53
+ }
54
+ function validateGroupingShape(where, grouping, columnFields) {
55
+ if (!isPlainObject(grouping)) {
56
+ throw new Error(`${where} table.grouping must be an object like { field: "status" }.`);
57
+ }
58
+ rejectUnknownKeys(where, "table.grouping", grouping, LIST_TABLE_GROUPING_KEYS);
59
+ validateGroupingValueTypes(where, grouping);
60
+ validateGroupingFieldChoice(where, grouping);
61
+ for (const name of groupingFieldsOf(grouping)) {
62
+ requireScreenColumn(where, "table.grouping field", name, columnFields);
63
+ }
64
+ }
65
+ function validateTotalsShape(where, totals, columnFields) {
66
+ if (!Array.isArray(totals) || totals.length === 0) {
67
+ throw new Error(`${where} table.totals must be a non-empty array like [{ field: "amount", kind: "sum" }].`);
68
+ }
69
+ for (const [i, entry] of totals.entries()) {
70
+ if (!isPlainObject(entry))
71
+ throw new Error(`${where} table.totals[${i}] must be an object.`);
72
+ rejectUnknownKeys(where, `table.totals[${i}]`, entry, LIST_TABLE_TOTAL_KEYS);
73
+ if (typeof entry["field"] !== "string") {
74
+ throw new Error(`${where} table.totals[${i}].field must be a field name.`);
75
+ }
76
+ if (!isOneOf(LIST_AGGREGATE_MEASURE_KINDS, entry["kind"])) {
77
+ throw new Error(`${where} table.totals[${i}].kind must be one of ${LIST_AGGREGATE_MEASURE_KINDS.join(", ")}.`);
78
+ }
79
+ requireScreenColumn(where, `table.totals[${i}].field`, entry["field"], columnFields);
80
+ }
81
+ }
82
+ export function groupingFieldsOf(grouping) {
83
+ const named = typeof grouping.field === "string" ? [grouping.field] : [];
84
+ const offered = isStringArray(grouping.fields) ? grouping.fields : [];
85
+ return [...new Set([...named, ...offered])];
86
+ }
87
+ /** Structure and column membership; field types are checked where the entity is known. */
88
+ export function validateListTableAggregateShape(where, table, columns) {
89
+ const columnFields = new Set(columns.map((col) => normalizeListColumn(col).field));
90
+ if (table["grouping"] !== undefined)
91
+ validateGroupingShape(where, table["grouping"], columnFields);
92
+ if (table["totals"] !== undefined)
93
+ validateTotalsShape(where, table["totals"], columnFields);
94
+ }
95
+ function rejectSecret(where, path, field, info) {
96
+ // skip: only secret fields are rejected here
97
+ if (!info.secret)
98
+ return;
99
+ throw new Error(`${where} ${path} "${field}" is encrypted, personal or secret and cannot be grouped or summed.`);
100
+ }
101
+ /**
102
+ * `lookup` answers undefined for a field it does not know. With `strict` (an entity
103
+ * stands behind the list) that is an error; otherwise the field is left to the server.
104
+ */
105
+ export function validateListTableAggregateFieldTypes(where, table, lookup, strict) {
106
+ const resolve = (path, field) => {
107
+ const info = lookup(field);
108
+ if (info === undefined && strict) {
109
+ throw new Error(`${where} ${path} "${field}" is not a field of the entity behind this list.`);
110
+ }
111
+ return info;
112
+ };
113
+ for (const field of groupingFieldsOf(table.grouping ?? {})) {
114
+ const info = resolve("table.grouping field", field);
115
+ if (info === undefined)
116
+ continue;
117
+ rejectSecret(where, "table.grouping field", field, info);
118
+ if (!isListAggregateGroupable(info.type, info.multiple)) {
119
+ throw new Error(`${where} table.grouping field "${field}" (${info.type}) cannot be grouped by.`);
120
+ }
121
+ }
122
+ for (const [i, total] of (table.totals ?? []).entries()) {
123
+ const path = `table.totals[${i}]`;
124
+ const info = resolve(`${path}.field`, total.field);
125
+ if (info === undefined)
126
+ continue;
127
+ rejectSecret(where, `${path}.field`, total.field, info);
128
+ if (!isListAggregateMeasureSupported(total.kind, info.type)) {
129
+ throw new Error(`${where} ${path}: "${total.kind}" is not supported on ${info.type} field "${total.field}". ` +
130
+ `sum and avg need number, decimal, money or bigInt; min and max also take date and timestamp.`);
131
+ }
132
+ }
133
+ }
134
+ export function entityAggregateFieldLookup(entity) {
135
+ return (field) => {
136
+ const def = Object.hasOwn(entity.fields, field) ? entity.fields[field] : undefined;
137
+ if (def === undefined)
138
+ return undefined;
139
+ return {
140
+ type: def.type,
141
+ multiple: def.type === "reference" && def.multiple === true,
142
+ secret: holdsCiphertextOrSecret(entity, field),
143
+ };
144
+ };
145
+ }
@@ -0,0 +1,10 @@
1
+ import type { FeatureDefinition, QueryHandlerDef } from "../types/index.ts";
2
+ import type { ListColumnSpec, ListTableOptions } from "../types/screen.ts";
3
+ export declare function validateListTableAggregateQuery(args: {
4
+ readonly prefix: string;
5
+ readonly query: string;
6
+ readonly table?: ListTableOptions | undefined;
7
+ readonly columns: readonly ListColumnSpec[];
8
+ readonly handler: QueryHandlerDef | undefined;
9
+ readonly featureMap: ReadonlyMap<string, FeatureDefinition>;
10
+ }): void;
@@ -0,0 +1,31 @@
1
+ import { resolveEntityBehindListQuery } from "../entity-list-searchability.js";
2
+ import { normalizeListColumn } from "../screen-helpers.js";
3
+ import { entityAggregateFieldLookup, validateListTableAggregateFieldTypes, } from "./list-table-aggregate-options.js";
4
+ import { findEntity } from "./parent-ref.js";
5
+ import { getZodObjectShape } from "./zod-shape.js";
6
+ function columnValueTypeLookup(columns) {
7
+ const valueTypes = new Map(columns
8
+ .map(normalizeListColumn)
9
+ .flatMap((col) => (col.valueType === undefined ? [] : [[col.field, col.valueType]])));
10
+ return (field) => {
11
+ const type = valueTypes.get(field);
12
+ return type === undefined ? undefined : { type, multiple: false, secret: false };
13
+ };
14
+ }
15
+ // Same pattern as searchable vs search: grouping and totals send `aggregate`,
16
+ // which a query without it in its Zod schema would reject on every load.
17
+ export function validateListTableAggregateQuery(args) {
18
+ const { prefix, query, table, handler } = args;
19
+ // skip: neither grouping nor totals declared, nothing travels via aggregate
20
+ if (table?.grouping === undefined && table?.totals === undefined)
21
+ return;
22
+ const shape = getZodObjectShape(handler?.schema);
23
+ if (shape === undefined || !("aggregate" in shape)) {
24
+ throw new Error(`${prefix}: table.grouping/table.totals need query "${query}" to accept "aggregate" — ` +
25
+ `reuse entityListSchema (or listAggregatePayloadSchema) in the handler's schema and answer it ` +
26
+ `with answerListAggregate, or remove grouping and totals.`);
27
+ }
28
+ const entity = resolveEntityBehindListQuery(query, handler, (name) => findEntity(args.featureMap, name));
29
+ const lookup = entity === undefined ? columnValueTypeLookup(args.columns) : entityAggregateFieldLookup(entity);
30
+ validateListTableAggregateFieldTypes(prefix, table, lookup, entity !== undefined);
31
+ }
@@ -0,0 +1 @@
1
+ export declare function validateExportOptions(where: string, value: unknown, allowed: boolean): void;
@@ -0,0 +1,27 @@
1
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { LIST_TABLE_EXPORT_KEYS, MAX_EXPORT_MAX_ROWS } from "../types/screen.js";
3
+ import { rejectUnknownKeys } from "./list-table-reject-unknown-keys.js";
4
+ export function validateExportOptions(where, value, allowed) {
5
+ if (!allowed) {
6
+ throw new Error(`${where} table.export is only available on entityList and projectionList screens. Remove it here.`);
7
+ }
8
+ if (!isPlainObject(value)) {
9
+ throw new Error(`${where} table.export must be an object like { xlsx: true, maxRows: 5000 }.`);
10
+ }
11
+ rejectUnknownKeys(where, "table.export", value, LIST_TABLE_EXPORT_KEYS);
12
+ if (value["xlsx"] !== true) {
13
+ throw new Error(`${where} table.export.xlsx must be true (the only export format).`);
14
+ }
15
+ if (value["maxRows"] !== undefined)
16
+ validateMaxRows(where, value["maxRows"]);
17
+ }
18
+ function validateMaxRows(where, maxRows) {
19
+ if (typeof maxRows !== "number" || !Number.isInteger(maxRows) || maxRows < 1) {
20
+ throw new Error(`${where} table.export.maxRows is ${JSON.stringify(maxRows)} — must be an integer >= 1. ` +
21
+ `Set a positive row limit or remove maxRows to use the default.`);
22
+ }
23
+ if (maxRows > MAX_EXPORT_MAX_ROWS) {
24
+ throw new Error(`${where} table.export.maxRows is ${maxRows} — must be at most ${MAX_EXPORT_MAX_ROWS} ` +
25
+ `(the workbook is built in memory). Lower maxRows.`);
26
+ }
27
+ }
@@ -0,0 +1,3 @@
1
+ import { type ListColumnSpec } from "../types/screen.ts";
2
+ /** `where` is the full message prefix, e.g. `[Feature x] Screen "y" (entityList)`. */
3
+ export declare function validateListTableOptions(where: string, table: unknown, columns: readonly ListColumnSpec[], exportAllowed?: boolean): void;
@@ -0,0 +1,77 @@
1
+ import { isOneOf, isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { normalizeListColumn } from "../screen-helpers.js";
3
+ import { LIST_TABLE_DENSITIES, LIST_TABLE_DENSITY_KEYS, LIST_TABLE_OPTION_KEYS, } from "../types/screen.js";
4
+ import { validateColumnLayout } from "./list-column-layout.js";
5
+ import { validateListTableAggregateShape } from "./list-table-aggregate-options.js";
6
+ import { validateExportOptions } from "./list-table-export-options.js";
7
+ import { rejectUnknownKeys } from "./list-table-reject-unknown-keys.js";
8
+ const BOOLEAN_OPTION_KEYS = [
9
+ "rememberState",
10
+ "resetAll",
11
+ "columnPicker",
12
+ "pinning",
13
+ "resizing",
14
+ "keyboardNavigation",
15
+ ];
16
+ function validateDensity(where, density) {
17
+ if (!isPlainObject(density)) {
18
+ throw new Error(`${where} table.density must be an object like { default: "compact", userChoice: true }.`);
19
+ }
20
+ rejectUnknownKeys(where, "table.density", density, LIST_TABLE_DENSITY_KEYS);
21
+ if (Object.keys(density).length === 0) {
22
+ throw new Error(`${where} table.density is empty. Set default (${LIST_TABLE_DENSITIES.join(" | ")}) ` +
23
+ `and/or userChoice, or remove table.density.`);
24
+ }
25
+ if (density["default"] !== undefined && !isOneOf(LIST_TABLE_DENSITIES, density["default"])) {
26
+ throw new Error(`${where} table.density.default is ${JSON.stringify(density["default"])} — ` +
27
+ `must be one of ${LIST_TABLE_DENSITIES.map((d) => `"${d}"`).join(", ")}.`);
28
+ }
29
+ if (density["userChoice"] !== undefined && typeof density["userChoice"] !== "boolean") {
30
+ throw new Error(`${where} table.density.userChoice must be a boolean.`);
31
+ }
32
+ }
33
+ function validateHiddenColumns(where, columnPickerOn, columns) {
34
+ const normalized = columns.map(normalizeListColumn);
35
+ for (const col of normalized) {
36
+ if (col.hidden !== undefined && typeof col.hidden !== "boolean") {
37
+ throw new Error(`${where} column "${col.field}" has a non-boolean "hidden".`);
38
+ }
39
+ }
40
+ const hidden = normalized.filter((col) => col.hidden === true);
41
+ // skip: no hidden columns, nothing to validate
42
+ if (hidden.length === 0)
43
+ return;
44
+ if (!columnPickerOn) {
45
+ throw new Error(`${where} column "${hidden[0]?.field}" is hidden but table.columnPicker is not enabled, ` +
46
+ `so the user could never show it. Set table: { columnPicker: true } or remove "hidden".`);
47
+ }
48
+ if (hidden.length === normalized.length) {
49
+ throw new Error(`${where} hides every column. At least one column must start visible — remove "hidden" from one.`);
50
+ }
51
+ }
52
+ /** `where` is the full message prefix, e.g. `[Feature x] Screen "y" (entityList)`. */
53
+ export function validateListTableOptions(where, table, columns, exportAllowed = false) {
54
+ if (table === undefined) {
55
+ validateHiddenColumns(where, false, columns);
56
+ validateColumnLayout(where, { pinning: false, resizing: false }, columns);
57
+ // skip: no table block: nothing else to check
58
+ return;
59
+ }
60
+ if (!isPlainObject(table)) {
61
+ throw new Error(`${where} table must be an object like { rememberState: true }. ` +
62
+ `Allowed keys: ${LIST_TABLE_OPTION_KEYS.join(", ")}.`);
63
+ }
64
+ rejectUnknownKeys(where, "table", table, LIST_TABLE_OPTION_KEYS);
65
+ for (const key of BOOLEAN_OPTION_KEYS) {
66
+ if (table[key] !== undefined && typeof table[key] !== "boolean") {
67
+ throw new Error(`${where} table.${key} must be a boolean, got ${typeof table[key]}.`);
68
+ }
69
+ }
70
+ if (table["density"] !== undefined)
71
+ validateDensity(where, table["density"]);
72
+ if (table["export"] !== undefined)
73
+ validateExportOptions(where, table["export"], exportAllowed);
74
+ validateListTableAggregateShape(where, table, columns);
75
+ validateHiddenColumns(where, table["columnPicker"] === true, columns);
76
+ validateColumnLayout(where, { pinning: table["pinning"] === true, resizing: table["resizing"] === true }, columns);
77
+ }
@@ -0,0 +1 @@
1
+ export declare function rejectUnknownKeys(where: string, block: string, value: Record<string, unknown>, allowed: readonly string[]): void;
@@ -0,0 +1,7 @@
1
+ export function rejectUnknownKeys(where, block, value, allowed) {
2
+ const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
3
+ if (unknown.length > 0) {
4
+ throw new Error(`${where} ${block} has unknown key(s) ${unknown.map((k) => `"${k}"`).join(", ")}. ` +
5
+ `Allowed keys: ${allowed.join(", ")}.`);
6
+ }
7
+ }