@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
@@ -1,5 +1,6 @@
1
- import type { AppSchema } from "../ui-types/index.ts";
1
+ import type { AppSchema, WorkspaceSchema } from "../ui-types/index.ts";
2
2
  import type { Registry } from "./types/feature.ts";
3
+ export declare function workspaceOwnerOf(definition: WorkspaceSchema["definition"]): string | undefined;
3
4
  export type BuildAppSchemaOptions = {
4
5
  /** Dev-server authoring hints (Settings-Hub placement). Default off — only
5
6
  * `createKumikoServer` opts in; prod boot + unit tests stay silent. */
@@ -31,6 +31,12 @@ import { isEncryptedAtRest } from "./config-helpers.js";
31
31
  import { isListQueryWithoutSearchableFields } from "./entity-list-searchability.js";
32
32
  import { QnTypes, qualifyEntityName } from "./qualified-name.js";
33
33
  const log = createFallbackLogger("kumiko");
34
+ // Workspace ids are only unique per feature, and the schema carries the short id. The owner stays
35
+ // off the serialized schema; the tenant-level feature strip reads it from here by object identity.
36
+ const workspaceOwnerByDefinition = new WeakMap();
37
+ export function workspaceOwnerOf(definition) {
38
+ return workspaceOwnerByDefinition.get(definition);
39
+ }
34
40
  export function buildAppSchema(registry, options = {}) {
35
41
  const features = [];
36
42
  const urlPrefillFieldsByScreenQn = collectUrlPrefillFieldsByScreenQn([
@@ -74,8 +80,10 @@ export function buildAppSchema(registry, options = {}) {
74
80
  for (const [featureName, feature] of registry.features) {
75
81
  for (const [shortId, definition] of Object.entries(feature.workspaces)) {
76
82
  const qualified = `${featureName}:workspace:${shortId}`;
83
+ const projectedDefinition = { ...definition, id: shortId };
84
+ workspaceOwnerByDefinition.set(projectedDefinition, featureName);
77
85
  workspaces.push({
78
- definition: { ...definition, id: shortId },
86
+ definition: projectedDefinition,
79
87
  navMembers: registry.getWorkspaceNavs(qualified),
80
88
  });
81
89
  }
@@ -111,7 +119,7 @@ export function buildAppSchema(registry, options = {}) {
111
119
  // instead so a leaked function renderer is actually caught.
112
120
  const offender = findNonJsonSafePath(schema, "schema");
113
121
  if (offender !== null) {
114
- log.error(`buildAppSchema: Output ist nicht JSON-safe — nicht-serialisierbarer Wert bei "${offender}" (Funktions-Renderer oder Klassen-Instanz im Schema?).`, { offender });
122
+ log.error(`buildAppSchema: output is not JSON-safe, non-serializable value at "${offender}" (a function renderer or class instance in the schema?).`, { offender });
115
123
  }
116
124
  }
117
125
  return schema;
@@ -446,136 +454,96 @@ function assertValidScreenMultilineRows(featureName, shortId, screen) {
446
454
  check(screen.confirm.fields);
447
455
  }
448
456
  }
449
- // Per-field whitelist. Every forwarded value must be JSON-safe (literal, or
450
- // an array/plain-object built only from JSON-safe values) — even though the
451
- // FieldDefinition types only type `default` as a literal, the sample-authoring
452
- // pattern `as unknown as EntityDefinition` has already let function-valued
453
- // defaults slip through. This whitelist is defense-in-depth that catches them
454
- // BEFORE JSON.stringify silently drops them in the browser-injection pipeline.
455
- // The same check gates the structured properties below (`multiline`,
456
- // `derived`, `totals`, `totalsMatch`) — a top-level `Array.isArray`/plain-
457
- // object check alone would let a function survive one level deep.
458
- //
459
- // Cast at the exit `as EntityFieldDefinition`: type-system-wise our out-map only
460
- // satisfies the discriminated union with an unnarrowed `type` string — the
461
- // cast bridges the variant inference TS can't recompute from a generic
462
- // Record.
463
- // kumiko-lint-ignore complexity-budget flat per-property whitelist walk, one independent `if` per FieldDefinition property — same shape as schema-builder.ts's field→zod switch
464
- function projectField(fieldDef) {
465
- const def = fieldDef; // @cast-boundary schema-walk
466
- const out = {};
467
- if (typeof def["type"] === "string")
468
- out["type"] = def["type"];
469
- if (typeof def["required"] === "boolean")
470
- out["required"] = def["required"];
471
- if (typeof def["sortable"] === "boolean")
472
- out["sortable"] = def["sortable"];
457
+ const isStringValue = (value) => typeof value === "string";
458
+ const isBooleanValue = (value) => typeof value === "boolean";
459
+ const isNumberValue = (value) => typeof value === "number";
460
+ const isTrueValue = (value) => value === true;
461
+ const isNumberOrStringValue = (value) => typeof value === "number" || typeof value === "string";
462
+ const isBooleanOrJsonSafeObject = (value) => typeof value === "boolean" || isJsonSafeObject(value);
463
+ const isUnitSpec = (value) => typeof value === "string" || (isPlainObject(value) && typeof value["field"] === "string");
464
+ // Forwarded in this order, so the client schema keeps its key order.
465
+ const FORWARDED_FIELD_PROPERTIES = [
466
+ ["type", isStringValue],
467
+ ["required", isBooleanValue],
468
+ ["sortable", isBooleanValue],
473
469
  // filterable controls the faceted filter dropdowns in the renderer (select/
474
470
  // boolean), so it must be included in the client schema.
475
- if (typeof def["filterable"] === "boolean")
476
- out["filterable"] = def["filterable"];
477
- if (typeof def["searchable"] === "boolean")
478
- out["searchable"] = def["searchable"];
479
- if (isJsonSafeValue(def["default"]))
480
- out["default"] = def["default"];
471
+ ["filterable", isBooleanValue],
472
+ ["searchable", isBooleanValue],
473
+ ["default", isJsonSafeValue],
481
474
  // Select: the options list is plain JSON, so forward it.
482
- if (Array.isArray(def["options"]))
483
- out["options"] = def["options"];
484
- if (isJsonSafeObject(def["optionTones"]))
485
- out["optionTones"] = def["optionTones"];
475
+ ["options", Array.isArray],
476
+ ["optionTones", isJsonSafeObject],
486
477
  // Reference: entity target, labelField, and multiple are required by the
487
478
  // renderer. ReferenceInput builds the options query from refEntity/refFeature
488
479
  // and resolves the label through labelField; without these properties, the
489
480
  // dropdown is empty (the QN becomes `<feature>:query::list` → 404).
490
- if (typeof def["entity"] === "string")
491
- out["entity"] = def["entity"];
492
- if (typeof def["labelField"] === "string")
493
- out["labelField"] = def["labelField"];
481
+ ["entity", isStringValue],
482
+ ["labelField", isStringValue],
494
483
  // Author-supplied picker source — without it the client falls back to the list handler.
495
- if (typeof def["optionsQuery"] === "string")
496
- out["optionsQuery"] = def["optionsQuery"];
484
+ ["optionsQuery", isStringValue],
497
485
  // Select: tier/tenant availability of the static options, loaded by the renderer.
498
- if (typeof def["optionsAvailabilityQuery"] === "string")
499
- out["optionsAvailabilityQuery"] = def["optionsAvailabilityQuery"];
486
+ ["optionsAvailabilityQuery", isStringValue],
500
487
  // Select: options only offered while a sibling-field condition holds; the renderer filters, the write path enforces.
501
- if (Array.isArray(def["conditionalOptions"]))
502
- out["conditionalOptions"] = def["conditionalOptions"];
503
- if (typeof def["multiple"] === "boolean")
504
- out["multiple"] = def["multiple"];
488
+ ["conditionalOptions", Array.isArray],
489
+ ["multiple", isBooleanValue],
505
490
  // MultiSelect: display picks checkboxes vs. combobox in the renderer,
506
491
  // columns/maxRows size the checkbox grid — without these the renderer
507
492
  // always falls back to the combobox. Select: display picks
508
493
  // "radio" | "dropdown" the same way.
509
- if (typeof def["display"] === "string")
510
- out["display"] = def["display"];
511
- if (typeof def["columns"] === "number")
512
- out["columns"] = def["columns"];
513
- if (typeof def["maxRows"] === "number")
514
- out["maxRows"] = def["maxRows"];
494
+ ["display", isStringValue],
495
+ ["columns", isNumberValue],
496
+ ["maxRows", isNumberValue],
515
497
  // text/longText: textarea row count — DefaultInput renders a single-line
516
498
  // input otherwise.
517
- if (typeof def["multiline"] === "boolean" || isJsonSafeObject(def["multiline"]))
518
- out["multiline"] = def["multiline"];
499
+ ["multiline", isBooleanOrJsonSafeObject],
519
500
  // number: Zod write-boundary bounds. date/timestamp/locatedTimestamp:
520
501
  // ISO-string picker bounds — same keys, different literal type.
521
- if (typeof def["min"] === "number" || typeof def["min"] === "string")
522
- out["min"] = def["min"];
523
- if (typeof def["max"] === "number" || typeof def["max"] === "string")
524
- out["max"] = def["max"];
502
+ ["min", isNumberOrStringValue],
503
+ ["max", isNumberOrStringValue],
525
504
  // date/timestamp/locatedTimestamp: display/parsing locale override.
526
- if (typeof def["locale"] === "string")
527
- out["locale"] = def["locale"];
505
+ ["locale", isStringValue],
528
506
  // image/images: which camera a mobile capture opens.
529
- if (typeof def["capture"] === "string")
530
- out["capture"] = def["capture"];
507
+ ["capture", isStringValue],
531
508
  // number: display-only suffix, static or read from a sibling field of the row.
532
- const unit = def["unit"];
533
- if (typeof unit === "string" || (isPlainObject(unit) && typeof unit["field"] === "string"))
534
- out["unit"] = unit;
509
+ ["unit", isUnitSpec],
535
510
  // number/decimal/bigInt: `false` drops thousands separators (e.g. a year).
536
- if (typeof def["grouping"] === "boolean")
537
- out["grouping"] = def["grouping"];
511
+ ["grouping", isBooleanValue],
538
512
  // text: "password" masks the input and blocks URL prefill in the renderer.
539
- if (typeof def["format"] === "string")
540
- out["format"] = def["format"];
513
+ ["format", isStringValue],
541
514
  // Write-response redaction stays server-side; the renderer needs the flag to
542
515
  // refuse any prefill (URL or handoff) into the field.
543
- if (def["sensitive"] === true)
544
- out["sensitive"] = true;
516
+ ["sensitive", isTrueValue],
545
517
  // Without it the renderer shows a plain text input for a field whose value never leaves the server.
546
- if (def["writeOnly"] === true)
547
- out["writeOnly"] = true;
518
+ ["writeOnly", isTrueValue],
548
519
  // timestamp: the edit view-model derives its wall-clock input mode from this.
549
- if (typeof def["locatedBy"] === "string")
550
- out["locatedBy"] = def["locatedBy"];
520
+ ["locatedBy", isStringValue],
551
521
  // file/image/images: upload picker constraints; the first variant key picks
552
522
  // the preview variant.
553
- if (isJsonSafeArray(def["accept"]))
554
- out["accept"] = def["accept"];
555
- if (typeof def["maxSize"] === "string")
556
- out["maxSize"] = def["maxSize"];
557
- if (isJsonSafeObject(def["variants"]))
558
- out["variants"] = def["variants"];
523
+ ["accept", isJsonSafeArray],
524
+ ["maxSize", isStringValue],
525
+ ["variants", isJsonSafeObject],
559
526
  // decimal, incl. embedded sub-fields: rounding of derived embedded-list cells.
560
- if (typeof def["scale"] === "number")
561
- out["scale"] = def["scale"];
527
+ ["scale", isNumberValue],
562
528
  // money: declared currency source (MoneyCurrencySource) — without
563
529
  // it the renderer can't tell a "currency: { kind: 'tenant' }" field apart
564
530
  // from an undeclared one and always falls back to entity.defaultCurrency.
565
- if (isJsonSafeObject(def["currency"]))
566
- out["currency"] = def["currency"];
531
+ ["currency", isJsonSafeObject],
567
532
  // embedded lists: row-count bounds, computed cells, totals row, and the
568
533
  // sibling-money-field totals check.
569
- if (typeof def["minItems"] === "number")
570
- out["minItems"] = def["minItems"];
571
- if (typeof def["maxItems"] === "number")
572
- out["maxItems"] = def["maxItems"];
573
- if (isJsonSafeObject(def["derived"]))
574
- out["derived"] = def["derived"];
575
- if (isJsonSafeArray(def["totals"]))
576
- out["totals"] = def["totals"];
577
- if (isJsonSafeObject(def["totalsMatch"]))
578
- out["totalsMatch"] = def["totalsMatch"];
534
+ ["minItems", isNumberValue],
535
+ ["maxItems", isNumberValue],
536
+ ["derived", isJsonSafeObject],
537
+ ["totals", isJsonSafeArray],
538
+ ["totalsMatch", isJsonSafeObject],
539
+ ];
540
+ function projectField(fieldDef) {
541
+ const def = fieldDef; // @cast-boundary schema-walk
542
+ const out = {};
543
+ for (const [key, isForwarded] of FORWARDED_FIELD_PROPERTIES) {
544
+ if (isForwarded(def[key]))
545
+ out[key] = def[key];
546
+ }
579
547
  // embedded lists: the sub-field map itself — without it the client-side
580
548
  // edit view-model iterates `undefined` and the screen crashes.
581
549
  // Recursive per sub-field, NOT a blanket isJsonSafeValue gate: a single
@@ -656,8 +656,13 @@ function buildScreen(shortId, scope, feature, keys, access, hub, options = {}) {
656
656
  // feature's group (100% of today's apps — byte-identical output). Only a
657
657
  // cross-feature `group` (feature !== ownerFeature) needs the owner prefix,
658
658
  // to keep two features sharing a group from clobbering the same shortKey.
659
+ // A selector's option labels hang on its field id in one global key space
660
+ // (`config:entity:__config-edit__:field:<id>:option:<value>`), so two owners
661
+ // whose selector is both named `provider` need distinct ids.
659
662
  const fieldId = options.fieldIdOf ??
660
- ((k) => (k.feature === k.ownerFeature ? k.shortKey : qualifiedFieldId(k)));
663
+ ((k) => k.feature === k.ownerFeature && !hub.selectorOptions.has(k.qn)
664
+ ? k.shortKey
665
+ : qualifiedFieldId(k));
661
666
  const seenFieldIds = new Map();
662
667
  for (const k of keys) {
663
668
  const id = fieldId(k);
@@ -48,9 +48,16 @@ export function defineFeature(name, setup, settings) {
48
48
  },
49
49
  requires: (() => {
50
50
  const fn = (...args) => {
51
- if (!args.every(isObjectArgument))
51
+ const [first, ...rest] = args;
52
+ if (rest.length > 0 || !isObjectArgument(first)) {
52
53
  throwObjectArgumentRequired(name, "requires");
53
- state.requires.push(...resolveFeatureNamesArgs(args));
54
+ }
55
+ // Untyped callers can pass `{ feature: [...] }`; without this the object would be
56
+ // registered as a dependency name.
57
+ if (!Array.isArray(first.features) || !first.features.every((f) => typeof f === "string")) {
58
+ throw new Error(`[Feature ${name}] r.requires() needs { features: string[] }`);
59
+ }
60
+ state.requires.push(...first.features);
54
61
  };
55
62
  return Object.assign(fn, {
56
63
  projection: (tableName) => {
@@ -70,6 +77,9 @@ export function defineFeature(name, setup, settings) {
70
77
  }
71
78
  state.toggleableDefault = options.default;
72
79
  },
80
+ adoptsBundledLists() {
81
+ state.adoptsBundledLists = true;
82
+ },
73
83
  uiHints(hints) {
74
84
  if (state.uiHints !== undefined) {
75
85
  throw new Error(`[Feature ${name}] r.uiHints() called twice — UI hints are declared once`);
@@ -91,6 +101,7 @@ export function defineFeature(name, setup, settings) {
91
101
  requiredProjections: state.requiredProjections,
92
102
  requiredSteps: state.requiredSteps,
93
103
  ...(state.toggleableDefault !== undefined && { toggleableDefault: state.toggleableDefault }),
104
+ ...(state.adoptsBundledLists && { adoptsBundledLists: true }),
94
105
  ...(state.uiHints !== undefined && { uiHints: state.uiHints }),
95
106
  entities: state.entities,
96
107
  entityTables: state.entityTables,
@@ -57,6 +57,7 @@ export function defineWriteHandler(def,
57
57
  ...(def.unsafeSkipTransitionGuard && { unsafeSkipTransitionGuard: true }),
58
58
  ...(def.rateLimit && { rateLimit: def.rateLimit }),
59
59
  ...(def.additionalRateLimits && { additionalRateLimits: def.additionalRateLimits }),
60
+ ...(def.rateLimitCost && { rateLimitCost: def.rateLimitCost }),
60
61
  ...(def.escapeHatch && { escapeHatch: def.escapeHatch }),
61
62
  ...pickPreTransactionPhases(def),
62
63
  ...(def.tenantlessAnonymous && { tenantlessAnonymous: true }),
@@ -44,6 +44,19 @@ export declare const entityListSchema: z.ZodObject<{
44
44
  }>;
45
45
  value: z.ZodUnknown;
46
46
  }, z.core.$strip>>>;
47
+ aggregate: z.ZodOptional<z.ZodObject<{
48
+ groupBy: z.ZodOptional<z.ZodString>;
49
+ measures: z.ZodDefault<z.ZodArray<z.ZodObject<{
50
+ field: z.ZodString;
51
+ kind: z.ZodEnum<{
52
+ avg: "avg";
53
+ count: "count";
54
+ max: "max";
55
+ min: "min";
56
+ sum: "sum";
57
+ }>;
58
+ }, z.core.$strip>>>;
59
+ }, z.core.$strip>>;
47
60
  }, z.core.$strip>;
48
61
  export declare function defineEntityWriteHandler(name: string, entity: EntityDefinition, options: EntityWriteHandlerOptions): WriteHandlerDef;
49
62
  export declare function defineEntityQueryHandler(name: string, entity: EntityDefinition, options: EntityQueryHandlerOptions): QueryHandlerDef;
@@ -1,13 +1,16 @@
1
1
  import { ENTITY_CONVENTION_QUERY_BRAND } from "@cosmicdrift/kumiko-types/handlers";
2
+ import { listAggregatePayloadSchema, } from "@cosmicdrift/kumiko-types/list-aggregate-types";
2
3
  import * as z from "zod";
3
4
  import { collectReferenceFields, enrichRowWithReferences, enrichWithReferences, } from "../db/eagerload.js";
4
5
  import { createEventStoreExecutor } from "../db/event-store-executor.js";
6
+ import { answerListAggregate } from "../db/list-aggregate-labels.js";
5
7
  import { buildEntityTable } from "../db/table-builder.js";
6
8
  import { acknowledgeConventionCrossTenant, reportConventionCrossTenantUse, } from "../db/tenant-db.js";
7
9
  import { InternalError } from "../errors/index.js";
8
10
  import { isSystemIdentity } from "../pipeline/system-identity-switch.js";
9
11
  import { assertUnreachable } from "../utils/index.js";
10
12
  import { PAGED_QUERY_HANDLER_BRAND } from "./define-handler.js";
13
+ import { entityListRuntime } from "./entity-list-runtime-options.js";
11
14
  import { assertReason } from "./escape-hatch-reason.js";
12
15
  import { dropEmptyWriteOnlyValues } from "./field-access.js";
13
16
  import { instructionFieldNames } from "./instruction-fields.js";
@@ -105,6 +108,7 @@ export const entityListSchema = z.object({
105
108
  value: z.unknown(),
106
109
  }))
107
110
  .optional(),
111
+ aggregate: listAggregatePayloadSchema.optional(),
108
112
  });
109
113
  function parseHandlerName(name, validVerbs) {
110
114
  const colonIdx = name.indexOf(":");
@@ -390,28 +394,13 @@ export function defineEntityQueryHandler(name, entity, options) {
390
394
  // runtime override supplies it.
391
395
  const listPayload = query.payload; // @cast-boundary engine-payload
392
396
  const db = dbFor(ctx);
393
- const result = await executor.list(listPayload, query.user, db, {
394
- ...(ctx.searchAdapter !== undefined && { searchAdapter: ctx.searchAdapter }),
395
- ...(ctx.includeDeleted === true && { includeDeleted: true }),
396
- // Registry access only exists at request time, so the
397
- // searchable-reference cache + entity resolver travel through the
398
- // same runtime-override extension point as searchAdapter above.
399
- referenceSearch: {
400
- fields: ctx.registry.getSearchableReferences(entityName),
401
- resolveEntity: (name) => ctx.registry.getEntity(name),
402
- },
403
- referenceSort: {
404
- fields: ctx.registry.getSortableReferences(entityName),
405
- resolveEntity: (name) => ctx.registry.getEntity(name),
406
- },
407
- parentVisibility: { entities: ctx.registry.getAllEntities() },
408
- });
409
- const eagerLoadViewer = {
410
- user: query.user,
411
- parentVisibility: { entities: ctx.registry.getAllEntities() },
412
- };
397
+ const { runtimeOptions, eagerLoadViewer, resolveEntity } = entityListRuntime(ctx, entityName, query.user);
398
+ if (listPayload.aggregate !== undefined) {
399
+ return answerListAggregate(executor, { ...listPayload, aggregate: listPayload.aggregate }, query.user, db, runtimeOptions, { entity, resolveEntity, viewer: eagerLoadViewer });
400
+ }
401
+ const result = await executor.list(listPayload, query.user, db, runtimeOptions);
413
402
  const enrichedRows = hasRefFields
414
- ? await enrichWithReferences(result.rows, entity, (name) => ctx.registry.getEntity(name), db, eagerLoadViewer)
403
+ ? await enrichWithReferences(result.rows, entity, resolveEntity, db, eagerLoadViewer)
415
404
  : result.rows;
416
405
  return { ...result, rows: augmentDerivedFields(enrichedRows, entity) };
417
406
  };
@@ -0,0 +1,8 @@
1
+ import type { ListRuntimeOptions } from "@cosmicdrift/kumiko-types/event-store-executor-types";
2
+ import type { EagerLoadEntityResolver, EagerLoadViewer } from "../db/eagerload.ts";
3
+ import type { HandlerContext, SessionUser } from "./types/index.ts";
4
+ export declare function entityListRuntime(ctx: HandlerContext, entityName: string, user: SessionUser): {
5
+ readonly runtimeOptions: ListRuntimeOptions;
6
+ readonly eagerLoadViewer: EagerLoadViewer;
7
+ readonly resolveEntity: EagerLoadEntityResolver;
8
+ };
@@ -0,0 +1,18 @@
1
+ // Registry access only exists at request time, so the searchable/sortable-reference
2
+ // caches, the entity resolver and the join-row read gate travel through the
3
+ // runtime-override extension point of the executor verbs.
4
+ export function entityListRuntime(ctx, entityName, user) {
5
+ const parentVisibility = { entities: ctx.registry.getAllEntities() };
6
+ const resolveEntity = (name) => ctx.registry.getEntity(name);
7
+ return {
8
+ runtimeOptions: {
9
+ ...(ctx.searchAdapter !== undefined && { searchAdapter: ctx.searchAdapter }),
10
+ ...(ctx.includeDeleted === true && { includeDeleted: true }),
11
+ referenceSearch: { fields: ctx.registry.getSearchableReferences(entityName), resolveEntity },
12
+ referenceSort: { fields: ctx.registry.getSortableReferences(entityName), resolveEntity },
13
+ parentVisibility,
14
+ },
15
+ eagerLoadViewer: { user, parentVisibility },
16
+ resolveEntity,
17
+ };
18
+ }
@@ -33,6 +33,7 @@ const WRITE_HANDLER_KEY_KINDS = {
33
33
  unsafeSkipTransitionGuard: "modeled",
34
34
  rateLimit: "modeled",
35
35
  additionalRateLimits: "modeled",
36
+ rateLimitCost: "opaque",
36
37
  escapeHatch: "modeled",
37
38
  tenantlessAnonymous: "opaque",
38
39
  reserveBeforeTransaction: "opaque",
@@ -5,7 +5,7 @@ export { extractAuthClaims, extractHook, isHookType, readOptionalAccessRule, rea
5
5
  export { extractHttpRoute, extractJob, isHttpRouteMethod, } from "./jobs-routes.ts";
6
6
  export { collectScreenOpaqueProps, extractMultiStreamProjection, extractProjection, extractScreen, readApplyBodies, readScreenStatic, } from "./projections-screens.ts";
7
7
  export { findImportBindingForLocalName, resolveModuleFile, } from "./resolve-import.ts";
8
- export { extractDescribe, extractOptionalRequires, extractReadsConfig, extractRequires, extractSystemScope, extractToggleable, extractUiHints, } from "./round1.ts";
8
+ export { extractAdoptsBundledLists, extractDescribe, extractOptionalRequires, extractReadsConfig, extractRequires, extractSystemScope, extractToggleable, extractUiHints, } from "./round1.ts";
9
9
  export { extractEntity, extractNav, extractRelation, extractWorkspace, } from "./round2.ts";
10
10
  export { extractClaimKey, extractConfig, extractMetric, extractReferenceData, extractSecret, extractSecretNamespace, extractTranslations, extractUseExtension, isClaimKeyType, type NamedOptionsResult, readNamedOptions, } from "./round3.ts";
11
11
  export { extractBootCheck, extractEnvSchema, extractExposesApi, extractExtendsRegistrar, extractStoreTable, extractUsesApi, } from "./round5.ts";
@@ -5,7 +5,7 @@ export { extractAuthClaims, extractHook, isHookType, readOptionalAccessRule, rea
5
5
  export { extractHttpRoute, extractJob, isHttpRouteMethod, } from "./jobs-routes.js";
6
6
  export { collectScreenOpaqueProps, extractMultiStreamProjection, extractProjection, extractScreen, readApplyBodies, readScreenStatic, } from "./projections-screens.js";
7
7
  export { findImportBindingForLocalName, resolveModuleFile, } from "./resolve-import.js";
8
- export { extractDescribe, extractOptionalRequires, extractReadsConfig, extractRequires, extractSystemScope, extractToggleable, extractUiHints, } from "./round1.js";
8
+ export { extractAdoptsBundledLists, extractDescribe, extractOptionalRequires, extractReadsConfig, extractRequires, extractSystemScope, extractToggleable, extractUiHints, } from "./round1.js";
9
9
  export { extractEntity, extractNav, extractRelation, extractWorkspace, } from "./round2.js";
10
10
  export { extractClaimKey, extractConfig, extractMetric, extractReferenceData, extractSecret, extractSecretNamespace, extractTranslations, extractUseExtension, isClaimKeyType, readNamedOptions, } from "./round3.js";
11
11
  export { extractBootCheck, extractEnvSchema, extractExposesApi, extractExtendsRegistrar, extractStoreTable, extractUsesApi, } from "./round5.js";
@@ -1,10 +1,11 @@
1
1
  import type { CallExpression, SourceFile } from "ts-morph";
2
- import type { DescribePattern, OptionalRequiresPattern, ReadsConfigPattern, RequiresPattern, SystemScopePattern, ToggleablePattern, UiHintsPattern } from "../patterns.ts";
2
+ import type { AdoptsBundledListsPattern, DescribePattern, OptionalRequiresPattern, ReadsConfigPattern, RequiresPattern, SystemScopePattern, ToggleablePattern, UiHintsPattern } from "../patterns.ts";
3
3
  import { type ExtractOutput } from "./shared.ts";
4
4
  export declare function extractRequires(call: CallExpression, sourceFile: SourceFile): ExtractOutput<RequiresPattern>;
5
5
  export declare function extractOptionalRequires(call: CallExpression, sourceFile: SourceFile): ExtractOutput<OptionalRequiresPattern>;
6
6
  export declare function extractReadsConfig(call: CallExpression, sourceFile: SourceFile): ExtractOutput<ReadsConfigPattern>;
7
7
  export declare function extractSystemScope(call: CallExpression, sourceFile: SourceFile): ExtractOutput<SystemScopePattern>;
8
+ export declare function extractAdoptsBundledLists(call: CallExpression, sourceFile: SourceFile): ExtractOutput<AdoptsBundledListsPattern>;
8
9
  export declare function extractUiHints(call: CallExpression, sourceFile: SourceFile): ExtractOutput<UiHintsPattern>;
9
10
  export declare function extractDescribe(call: CallExpression, sourceFile: SourceFile): ExtractOutput<DescribePattern>;
10
11
  export declare function extractToggleable(call: CallExpression, sourceFile: SourceFile): ExtractOutput<ToggleablePattern>;
@@ -39,6 +39,12 @@ export function extractSystemScope(call, sourceFile) {
39
39
  source: sourceLocationFromNode(call, sourceFile),
40
40
  });
41
41
  }
42
+ export function extractAdoptsBundledLists(call, sourceFile) {
43
+ return ok({
44
+ kind: "adoptsBundledLists",
45
+ source: sourceLocationFromNode(call, sourceFile),
46
+ });
47
+ }
42
48
  export function extractUiHints(call, sourceFile) {
43
49
  return ok({
44
50
  kind: "uiHints",
@@ -2,19 +2,36 @@ import { SyntaxKind } from "ts-morph";
2
2
  import { describeUnknownFieldType, findUnknownEntityFieldTypes } from "../entity-field-types.js";
3
3
  import { sourceLocationFromNode } from "../source-location.js";
4
4
  import { fail, isPlainObject, isRawRefSentinel, ok, readDataLiteralNode, readNameLiteral, readNameOrRef, } from "./shared.js";
5
+ function readBackingTableRef(property) {
6
+ const initializer = property.asKind(SyntaxKind.PropertyAssignment)?.getInitializer() ??
7
+ property.asKind(SyntaxKind.ShorthandPropertyAssignment)?.getNameNode();
8
+ const reference = initializer ? readDataLiteralNode(initializer) : undefined;
9
+ return isRawRefSentinel(reference) ? reference : undefined;
10
+ }
5
11
  // `{ name: "x", ...xEntity }` — the definition is a reference the parser cannot
6
12
  // inline, so it is kept as a raw-ref sentinel and re-rendered as the same spread.
7
13
  // `backingTable` is runtime wiring (a table identifier), not part of the
8
- // definition data, so it does not take part in the shape check.
14
+ // definition data, so it is carried next to the definition and skipped by the shape check.
9
15
  function readSpreadOnlyDefinition(obj) {
10
- const properties = obj
11
- .getProperties()
12
- .filter((property) => property.asKind(SyntaxKind.PropertyAssignment)?.getName() !== "backingTable");
16
+ const isBackingTable = (property) => {
17
+ const name = property.asKind(SyntaxKind.PropertyAssignment)?.getName() ??
18
+ property.asKind(SyntaxKind.ShorthandPropertyAssignment)?.getName();
19
+ return name === "backingTable";
20
+ };
21
+ const allProperties = obj.getProperties();
22
+ const properties = allProperties.filter((property) => !isBackingTable(property));
13
23
  const spread = properties.length === 2 ? properties[1]?.asKind(SyntaxKind.SpreadAssignment) : undefined;
14
24
  if (!spread)
15
25
  return undefined;
16
26
  const reference = readDataLiteralNode(spread.getExpression());
17
- return isRawRefSentinel(reference) ? reference : undefined;
27
+ if (!isRawRefSentinel(reference))
28
+ return undefined;
29
+ const backingTableProperty = allProperties.find(isBackingTable);
30
+ const backingTable = backingTableProperty ? readBackingTableRef(backingTableProperty) : undefined;
31
+ // A backingTable that is not a plain reference cannot be re-rendered; fall back to the data reader.
32
+ if (backingTableProperty && !backingTable)
33
+ return undefined;
34
+ return { definition: reference, backingTable };
18
35
  }
19
36
  export function extractEntity(call, sourceFile) {
20
37
  const args = call.getArguments();
@@ -32,7 +49,8 @@ export function extractEntity(call, sourceFile) {
32
49
  if (!nameInit) {
33
50
  return fail("entity", sourceLocationFromNode(call, sourceFile), "object form requires a string-literal `name` property");
34
51
  }
35
- const definition = readSpreadOnlyDefinition(obj) ?? readDataLiteralNode(obj);
52
+ const spreadOnly = readSpreadOnlyDefinition(obj);
53
+ const definition = spreadOnly?.definition ?? readDataLiteralNode(obj);
36
54
  if (!isPlainObject(definition)) {
37
55
  return fail("entity", sourceLocationFromNode(call, sourceFile), "definition could not be read as a plain object (contains functions or identifiers)");
38
56
  }
@@ -49,6 +67,7 @@ export function extractEntity(call, sourceFile) {
49
67
  // Field-type membership is checked above; the cast is the extractor's
50
68
  // existing narrowing contract (readDataLiteralNode returns unknown).
51
69
  definition: defWithoutName,
70
+ ...(spreadOnly?.backingTable && { backingTable: spreadOnly.backingTable }),
52
71
  });
53
72
  }
54
73
  const entityName = readNameLiteral(first);
@@ -8,7 +8,7 @@ export type { AddAuthClaimsArgs, AddClaimKeyArgs, AddConfigArgs, AddDefineEventA
8
8
  export { createFeaturePatcher } from "./patcher.ts";
9
9
  export type { PatternChangeIssue, PatternChangesParseResult } from "./pattern-change-schema.ts";
10
10
  export { parsePatternChanges } from "./pattern-change-schema.ts";
11
- export type { AuthClaimsPattern, ClaimKeyPattern, ConfigPattern, DefineEventPattern, Editability, EntityPattern, ExtendsRegistrarPattern, FeaturePattern, FeaturePatternKind, HookPattern, HttpRoutePattern, JobPattern, MetricPattern, MultiStreamProjectionPattern, NavPattern, NotificationPattern, OptionalRequiresPattern, ProjectionPattern, QueryHandlerPattern, ReadsConfigPattern, ReferenceDataPattern, RelationPattern, RequiresPattern, ScreenPattern, SecretNamespacePattern, SecretPattern, StreamHandlerPattern, SystemScopePattern, ToggleablePattern, TranslationsPattern, UnknownPattern, UseExtensionPattern, WorkspacePattern, WriteHandlerPattern, } from "./patterns.ts";
11
+ export type { AdoptsBundledListsPattern, AuthClaimsPattern, ClaimKeyPattern, ConfigPattern, DefineEventPattern, Editability, EntityPattern, ExtendsRegistrarPattern, FeaturePattern, FeaturePatternKind, HookPattern, HttpRoutePattern, JobPattern, MetricPattern, MultiStreamProjectionPattern, NavPattern, NotificationPattern, OptionalRequiresPattern, ProjectionPattern, QueryHandlerPattern, ReadsConfigPattern, ReferenceDataPattern, RelationPattern, RequiresPattern, ScreenPattern, SecretNamespacePattern, SecretPattern, StreamHandlerPattern, SystemScopePattern, ToggleablePattern, TranslationsPattern, UnknownPattern, UseExtensionPattern, WorkspacePattern, WriteHandlerPattern, } from "./patterns.ts";
12
12
  export { getEditability } from "./patterns.ts";
13
13
  export type { RenderFeatureFileInput } from "./render.ts";
14
14
  export { FEATURE_FILE_VERSION, renderFeatureFile, renderPattern, renderValue, VERSION_HEADER, } from "./render.ts";
@@ -21,7 +21,7 @@
21
21
  // Per-pattern extractors fill in iteratively — each iteration adds
22
22
  // one extractor + a focused test.
23
23
  import { Project, SyntaxKind } from "ts-morph";
24
- import { extractAiClassify, extractAiExtract, extractAiGenerate, extractAuthClaims, extractBootCheck, extractClaimKey, extractConfig, extractDefineEvent, extractDescribe, extractEntity, extractEnvSchema, extractExposesApi, extractExtendsRegistrar, extractHook, extractHttpRoute, extractJob, extractMetric, extractMultiStreamProjection, extractNav, extractNotification, extractOptionalRequires, extractProjection, extractQueryHandler, extractReadsConfig, extractReferenceData, extractRelation, extractRequires, extractScreen, extractSecret, extractSecretNamespace, extractStoreTable, extractStreamHandler, extractSystemScope, extractToggleable, extractTranslations, extractTreeActions, extractUiHints, extractUseExtension, extractUsesApi, extractWorkspace, extractWriteHandler, findFunctionLiteral, findImportBindingForLocalName, readObjectPropertyInitializer, resolveModuleFile, resolveSameFileObjectLiteral, } from "./extractors/index.js";
24
+ import { extractAdoptsBundledLists, extractAiClassify, extractAiExtract, extractAiGenerate, extractAuthClaims, extractBootCheck, extractClaimKey, extractConfig, extractDefineEvent, extractDescribe, extractEntity, extractEnvSchema, extractExposesApi, extractExtendsRegistrar, extractHook, extractHttpRoute, extractJob, extractMetric, extractMultiStreamProjection, extractNav, extractNotification, extractOptionalRequires, extractProjection, extractQueryHandler, extractReadsConfig, extractReferenceData, extractRelation, extractRequires, extractScreen, extractSecret, extractSecretNamespace, extractStoreTable, extractStreamHandler, extractSystemScope, extractToggleable, extractTranslations, extractTreeActions, extractUiHints, extractUseExtension, extractUsesApi, extractWorkspace, extractWriteHandler, findFunctionLiteral, findImportBindingForLocalName, readObjectPropertyInitializer, resolveModuleFile, resolveSameFileObjectLiteral, } from "./extractors/index.js";
25
25
  import { sourceLocationFromNode } from "./source-location.js";
26
26
  /**
27
27
  * Parse the given feature file and return all recognised r.* calls
@@ -397,6 +397,8 @@ function dispatchExtractor(methodName, call, sourceFile) {
397
397
  return extractDescribe(call, sourceFile);
398
398
  case "uiHints":
399
399
  return extractUiHints(call, sourceFile);
400
+ case "adoptsBundledLists":
401
+ return extractAdoptsBundledLists(call, sourceFile);
400
402
  // Object-literal-based static patterns
401
403
  case "entity":
402
404
  return extractEntity(call, sourceFile);
@@ -102,6 +102,8 @@ export type PatternId = {
102
102
  readonly kind: "describe";
103
103
  } | {
104
104
  readonly kind: "uiHints";
105
+ } | {
106
+ readonly kind: "adoptsBundledLists";
105
107
  } | {
106
108
  readonly kind: "config";
107
109
  } | {