@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,7 +1,6 @@
1
- import postgres from "postgres";
2
1
  import { readPositiveIntEnv } from "../utils/env-parse.js";
3
2
  // A DB outage can leave an in-flight query referenced on a dead connection,
4
- // so a plain end() (no timeout = wait for queries to finish) hangs forever —
3
+ // so a plain close() (no timeout = wait for queries to finish) hangs forever —
5
4
  // close() must always bound how long shutdown waits.
6
5
  export const DEFAULT_DB_CLOSE_TIMEOUT_SECONDS = 5;
7
6
  // Keyed by the pool so withoutStatementTimeout can open a sibling with identical connect options.
@@ -11,20 +10,28 @@ export function openUnboundedSiblingPool(db) {
11
10
  return undefined;
12
11
  return unboundedSiblingFactories.get(db)?.();
13
12
  }
13
+ // Bun.SQL treats `true` and a TLS object without rejectUnauthorized as sslmode=require, which
14
+ // skips certificate checks; postgres-js verified in both cases, so keep that unless turned off.
15
+ function verifiedTls(ssl) {
16
+ if (ssl === true)
17
+ return "verify-full";
18
+ if (typeof ssl === "object" && !(ssl instanceof Blob) && ssl.rejectUnauthorized === undefined) {
19
+ return { ...ssl, rejectUnauthorized: true };
20
+ }
21
+ return ssl;
22
+ }
14
23
  export function createDbConnection({ url, ...options }) {
15
- const pgOptions = {};
24
+ const sqlOptions = {};
16
25
  if (options.maxConnections !== undefined)
17
- pgOptions.max = options.maxConnections;
26
+ sqlOptions.max = options.maxConnections;
18
27
  if (options.idleTimeoutSeconds !== undefined) {
19
- pgOptions.idle_timeout = options.idleTimeoutSeconds;
28
+ sqlOptions.idleTimeout = options.idleTimeoutSeconds;
20
29
  }
21
30
  if (options.connectTimeoutSeconds !== undefined) {
22
- pgOptions.connect_timeout = options.connectTimeoutSeconds;
31
+ sqlOptions.connectionTimeout = options.connectTimeoutSeconds;
23
32
  }
24
33
  if (options.ssl !== undefined)
25
- pgOptions.ssl = options.ssl;
26
- if (options.onNotice !== undefined)
27
- pgOptions.onnotice = options.onNotice;
34
+ sqlOptions.tls = verifiedTls(options.ssl);
28
35
  const serverParameters = {};
29
36
  if (options.statementTimeoutMs !== undefined) {
30
37
  serverParameters["statement_timeout"] = options.statementTimeoutMs;
@@ -36,48 +43,50 @@ export function createDbConnection({ url, ...options }) {
36
43
  if (options.lockTimeoutMs !== undefined)
37
44
  serverParameters["lock_timeout"] = options.lockTimeoutMs;
38
45
  if (Object.keys(serverParameters).length > 0)
39
- pgOptions.connection = serverParameters;
40
- const client = postgres(url, pgOptions);
46
+ sqlOptions.connection = serverParameters;
47
+ // Bun.SQL opens all `max` connections on the first query, so a pool always costs `max` backends.
48
+ const client = new Bun.SQL(url, sqlOptions);
41
49
  // A run that never settles (a caller gave up on it via its own timeout) never reaches the
42
50
  // sibling's own close; close() ends it so shutdown and DROP DATABASE are not blocked by it.
43
51
  const openSiblings = new Set();
44
52
  unboundedSiblingFactories.set(client, () => {
45
53
  // Two connections are enough for lock-in-tx plus DDL; the cap keeps parallel rebuilds from exhausting max_connections.
46
- const sibling = postgres(url, {
47
- ...pgOptions,
54
+ const sibling = new Bun.SQL(url, {
55
+ ...sqlOptions,
48
56
  max: 4,
49
- connection: { ...pgOptions.connection, statement_timeout: 0 },
57
+ connection: { ...sqlOptions.connection, statement_timeout: 0 },
50
58
  });
51
59
  openSiblings.add(sibling);
52
60
  return {
53
61
  client: sibling,
54
62
  close: async (timeoutSeconds) => {
55
63
  openSiblings.delete(sibling);
56
- await sibling.end({ timeout: timeoutSeconds });
64
+ await sibling.close({ timeout: timeoutSeconds });
57
65
  },
58
66
  };
59
67
  });
60
68
  return {
61
69
  db: client,
62
70
  client,
71
+ listenClient: client,
63
72
  close: async () => {
64
73
  const timeout = options.closeTimeoutSeconds ?? DEFAULT_DB_CLOSE_TIMEOUT_SECONDS;
65
74
  unboundedSiblingFactories.delete(client);
66
75
  const pools = [client, ...openSiblings];
67
76
  openSiblings.clear();
68
- const results = await Promise.allSettled(pools.map((pool) => pool.end({ timeout })));
77
+ const results = await Promise.allSettled(pools.map((pool) => pool.close({ timeout })));
69
78
  const failure = results.find((result) => result.status === "rejected");
70
79
  if (failure !== undefined)
71
80
  throw failure.reason;
72
81
  },
73
82
  };
74
83
  }
75
- // DB_PROVIDER=bun selected the removed Bun.SQL driver; ignoring it would boot
76
- // postgres-js under a config that still claims Bun.SQL.
84
+ // DB_PROVIDER once selected between drivers; a config that still sets it is stale,
85
+ // so fail loudly instead of silently ignoring it.
77
86
  function assertNoRemovedDbProvider(env) {
78
87
  const provider = env["DB_PROVIDER"];
79
- if (provider !== undefined && provider !== "" && provider !== "postgres") {
80
- throw new Error(`DB_PROVIDER="${provider}" is no longer supported: postgres-js is the only driver. Remove DB_PROVIDER from the environment.`);
88
+ if (provider !== undefined && provider !== "") {
89
+ throw new Error(`DB_PROVIDER="${provider}" is no longer supported: Bun.SQL is the only driver. Remove DB_PROVIDER from the environment.`);
81
90
  }
82
91
  }
83
92
  export function dbConnectionOptionsFromEnv(env = process.env) {
@@ -251,7 +251,7 @@ export function primaryKey(opts) {
251
251
  // Limits: no nested SqlExpression composition (drizzle's recursive
252
252
  // `sql\`${other}\``) — schema-files use single-level expressions only.
253
253
  // Unforgeable via JSON — a client-supplied jsonb value can fake `kind:
254
- // "sql-expr"` but can never carry a Symbol, so isSqlExpression() (db/query.ts)
254
+ // "sql-expr"` but can never carry a Symbol, so isSqlExpression() (db/driver-value-preparation.ts)
255
255
  // can't be tricked into treating request data as a raw SQL literal. The same
256
256
  // brand gate is enforced here in the schema DSL (sql`...` interpolation +
257
257
  // literalDefault) and in entity-table-meta's sqlExpressionText — a duck-typed
@@ -1,5 +1,5 @@
1
1
  import type { OrderByClause } from "@cosmicdrift/kumiko-types/where-clause-types";
2
- import type { TableInfo } from "./query.ts";
2
+ import type { TableInfo } from "./table-info.ts";
3
3
  type DistinctOnClause = {
4
4
  /** Physical column names, unquoted. */
5
5
  readonly columns: readonly string[];
@@ -0,0 +1,2 @@
1
+ import type { TableInfo } from "./table-info.ts";
2
+ export declare function coerceDriverRow(row: Record<string, unknown>, info: TableInfo): Record<string, unknown>;
@@ -0,0 +1,186 @@
1
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
+ import { createFallbackLogger } from "../logging/index.js";
3
+ import { parseJsonSafe } from "../utils/safe-json.js";
4
+ import { isTemporalInstant, isTemporalPlainDate } from "./temporal-value-guards.js";
5
+ // --- Value coercion at the driver boundary ------------------------------
6
+ //
7
+ // The driver returns timestamptz as JS Date (or ISO string depending on
8
+ // driver config). Framework contract says timestamptz surfaces as
9
+ // Temporal.Instant — without coercion every read hands callers a Date and
10
+ // downstream code that does .epochMilliseconds / .add(...) crashes.
11
+ //
12
+ // Symmetric on write: Temporal.Instant doesn't bind directly as a driver
13
+ // param — convert to ISO string when the column pgType is timestamptz.
14
+ // Postgres bigint → JS: safe integers become `number` (matches createBigIntField /
15
+ // drizzle mode:"number"); values outside ±2^53 stay `bigint` (money cents, raw
16
+ // unmanaged columns). Uses BigInt equality check so 9007199254740993n never
17
+ // silently rounds to a safe integer.
18
+ function coerceBigIntFromDriver(value) {
19
+ let bi;
20
+ if (typeof value === "string") {
21
+ try {
22
+ bi = BigInt(value);
23
+ }
24
+ catch {
25
+ return value;
26
+ }
27
+ }
28
+ else {
29
+ bi = value;
30
+ }
31
+ const min = BigInt(Number.MIN_SAFE_INTEGER);
32
+ const max = BigInt(Number.MAX_SAFE_INTEGER);
33
+ if (bi >= min && bi <= max) {
34
+ const n = Number(bi);
35
+ if (BigInt(n) === bi)
36
+ return n;
37
+ }
38
+ return bi;
39
+ }
40
+ function instantFromDriver(value) {
41
+ if (value === null || value === undefined)
42
+ return null;
43
+ if (isTemporalInstant(value))
44
+ return value;
45
+ // Number coercion via +date — equivalent to .getTime() without a Date-API
46
+ // method (guard-no-date-api). Date → epoch milliseconds, then to Temporal.Instant.
47
+ if (value instanceof Date)
48
+ return Temporal.Instant.fromEpochMilliseconds(+value);
49
+ if (typeof value === "string") {
50
+ try {
51
+ return Temporal.Instant.from(value);
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ if (typeof value === "number")
58
+ return Temporal.Instant.fromEpochMilliseconds(value);
59
+ return null;
60
+ }
61
+ // Verified empirically: a `date`
62
+ // column comes back as a native JS Date, but ALWAYS anchored at UTC
63
+ // midnight regardless of process TZ — epochMilliseconds is stable across
64
+ // zones, only the local wall-clock read of that Date would drift the day
65
+ // (e.g. America/Los_Angeles reads back the 14th for a stored 15th). Routing
66
+ // through Instant→ZonedDateTimeISO("UTC")→PlainDate reads the calendar day
67
+ // back deterministically without ever touching a local Date getter — the
68
+ // same +value idiom instantFromDriver uses (guard-no-date-api: no
69
+ // `.getTime()`), just anchored to a fixed zone instead of process-local.
70
+ // A real `date` column always arrives as a UTC-midnight Date. Anything else means the column is
71
+ // still timestamptz (declared `date` before its ALTER ran); the UTC anchor then silently shifts
72
+ // the day, so say so once per column instead of returning the wrong day quietly.
73
+ const warnedNonMidnightDateColumns = new Set();
74
+ const MS_PER_DAY = 86_400_000;
75
+ const dateColumnDriftLog = createFallbackLogger("bun-sql");
76
+ function warnIfNotDateColumn(epochMs, column) {
77
+ // skip: midnight value is a correct date column, and an already-warned column is deduped
78
+ if (epochMs % MS_PER_DAY === 0 || warnedNonMidnightDateColumns.has(column))
79
+ return;
80
+ warnedNonMidnightDateColumns.add(column);
81
+ dateColumnDriftLog.warn(`column "${column}" is declared as date but returned a non-midnight timestamp; it is probably still timestamptz. Run the ALTER ... TYPE date migration, otherwise days can shift.`);
82
+ }
83
+ function plainDateFromDriver(value, column) {
84
+ if (value === null || value === undefined)
85
+ return null;
86
+ if (isTemporalPlainDate(value))
87
+ return value;
88
+ if (typeof value === "string") {
89
+ const isoDay = /^\d{4}-\d{2}-\d{2}/.exec(value)?.[0];
90
+ if (isoDay === undefined)
91
+ return null;
92
+ try {
93
+ return Temporal.PlainDate.from(isoDay);
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ if (value instanceof Date) {
100
+ warnIfNotDateColumn(+value, column);
101
+ return Temporal.Instant.fromEpochMilliseconds(+value).toZonedDateTimeISO("UTC").toPlainDate();
102
+ }
103
+ return null;
104
+ }
105
+ function coerceTimestamptz(value) {
106
+ const instant = instantFromDriver(value);
107
+ return instant !== null ? instant : value;
108
+ }
109
+ function coerceDate(value, column) {
110
+ const day = plainDateFromDriver(value, column);
111
+ return day !== null ? day : value;
112
+ }
113
+ function coerceJsonb(value) {
114
+ return typeof value === "string" ? parseJsonSafe(value, value) : value;
115
+ }
116
+ function coerceBigIntColumn(value, column, info) {
117
+ if (typeof value !== "string" && typeof value !== "bigint")
118
+ return value;
119
+ if (info.bigintJsModeOf(column) === "number")
120
+ return coerceBigIntFromDriver(value);
121
+ if (typeof value === "string") {
122
+ try {
123
+ return BigInt(value);
124
+ }
125
+ catch {
126
+ // leave as string on parse error
127
+ return value;
128
+ }
129
+ }
130
+ return value;
131
+ }
132
+ function numberUnlessNaN(value) {
133
+ if (typeof value !== "string" && typeof value !== "bigint")
134
+ return value;
135
+ const n = Number(value);
136
+ return Number.isNaN(n) ? value : n;
137
+ }
138
+ // Keyed by the driver's pgType string, not PgType: legacy tables report int4/smallint/int2.
139
+ const DRIVER_VALUE_COERCERS = new Map([
140
+ ["timestamptz", coerceTimestamptz],
141
+ ["timestamptz(3)", coerceTimestamptz],
142
+ ["date", coerceDate],
143
+ ["jsonb", coerceJsonb],
144
+ ["bigint", coerceBigIntColumn],
145
+ ["bigserial", coerceBigIntColumn],
146
+ // Drivers may return int4 as bigint or numeric string.
147
+ // Drizzle coerced to number for numberField columns — match that.
148
+ ["integer", numberUnlessNaN],
149
+ ["int4", numberUnlessNaN],
150
+ ["smallint", numberUnlessNaN],
151
+ ["int2", numberUnlessNaN],
152
+ ]);
153
+ // pg returns numeric(p,s) as a string to preserve precision; the
154
+ // decimal-field contract surfaces it as JS number (safe ≤ 2^53). A
155
+ // non-numeric string would be DB corruption — leave it untouched so it
156
+ // surfaces loud downstream rather than becoming a silent NaN.
157
+ function driverValueCoercerFor(pgType) {
158
+ if (pgType === undefined)
159
+ return undefined;
160
+ const coercer = DRIVER_VALUE_COERCERS.get(pgType);
161
+ if (coercer)
162
+ return coercer;
163
+ return pgType.startsWith("numeric") ? numberUnlessNaN : undefined;
164
+ }
165
+ export function coerceDriverRow(row, info) {
166
+ const out = {};
167
+ let changed = false;
168
+ for (const key of Object.keys(row)) {
169
+ // Blind-index columns are pure lookup artifacts — the
170
+ // deterministic HMAC must never leak into caller/API responses.
171
+ if ((key.endsWith("_bidx") || key.endsWith("Bidx")) && info.hasColumn(key)) {
172
+ changed = true;
173
+ continue;
174
+ }
175
+ const value = row[key];
176
+ const coercer = driverValueCoercerFor(info.pgTypeOf(key));
177
+ const coerced = coercer ? coercer(value, key, info) : value;
178
+ const fieldName = info.fieldOf(key);
179
+ if (fieldName !== key)
180
+ changed = true;
181
+ if (coerced !== value)
182
+ changed = true;
183
+ out[fieldName] = coerced;
184
+ }
185
+ return changed ? out : row;
186
+ }
@@ -0,0 +1,16 @@
1
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
+ export type PreparedValue = {
3
+ readonly kind: "param";
4
+ readonly sql: string;
5
+ readonly bound: unknown;
6
+ } | {
7
+ readonly kind: "literal";
8
+ readonly literal: string;
9
+ };
10
+ export declare function isSqlExpression(v: unknown): v is {
11
+ kind: "sql-expr";
12
+ text: string;
13
+ };
14
+ export declare function prepareJsonbValue(value: unknown): PreparedValue | undefined;
15
+ export declare function instantToTimestamptzParam(instant: Temporal.Instant): string;
16
+ export declare function prepareValue(value: unknown, pgType: string | undefined): PreparedValue;
@@ -0,0 +1,101 @@
1
+ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
2
+ import { isPiiCiphertext } from "../crypto/pii-ciphertext-format.js";
3
+ import { SQL_EXPR_BRAND } from "./dialect.js";
4
+ import { isTemporalInstant, isTemporalPlainDate, isTemporalPlainDateTimeLike, } from "./temporal-value-guards.js";
5
+ // Checks the brand Symbol, not the `kind` string — a client-supplied jsonb
6
+ // value can fake `kind: "sql-expr"` over JSON but can never carry a Symbol,
7
+ // so request data can't be smuggled in as a raw SQL literal.
8
+ export function isSqlExpression(v) {
9
+ return typeof v === "object" && v !== null && SQL_EXPR_BRAND in v;
10
+ }
11
+ // A `date` column takes a plain "yyyy-mm-dd" string (or PlainDate.toString())
12
+ // as-is — no Instant detour. Postgres parses a DATE literal without ever
13
+ // consulting the session timezone, so this is TZ-independent by construction
14
+ // (verified empirically — see plainDateFromDriver above for the read side).
15
+ // Compat cushion: code (this field used to be an Instant) may still
16
+ // hand a Temporal.Instant into a where-filter or write on a `date` column —
17
+ // same UTC anchor as plainDateFromDriver's read side, so a filter built with
18
+ // `Temporal.Now.instant()` keeps working instead of binding a raw Instant the
19
+ // driver can't serialize.
20
+ function prepareDateValue(value) {
21
+ if (isTemporalPlainDate(value)) {
22
+ return { kind: "param", sql: "", bound: value.toString() };
23
+ }
24
+ if (isTemporalPlainDateTimeLike(value)) {
25
+ return { kind: "param", sql: "", bound: value.toPlainDate().toString() };
26
+ }
27
+ if (isTemporalInstant(value)) {
28
+ const day = value.toZonedDateTimeISO("UTC").toPlainDate();
29
+ return { kind: "param", sql: "", bound: day.toString() };
30
+ }
31
+ return undefined;
32
+ }
33
+ // structured values bind directly with an ::jsonb cast; JSON.stringify first
34
+ // would produce a JSON-string scalar instead of the structured value.
35
+ export function prepareJsonbValue(value) {
36
+ // Bun.SQL types JS numbers as int4/float8 params, which Postgres refuses for a jsonb column.
37
+ // Non-finite numbers fall through so Postgres rejects them instead of storing JSON null.
38
+ if (typeof value === "boolean" || (typeof value === "number" && Number.isFinite(value))) {
39
+ return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
40
+ }
41
+ if (typeof value !== "object" || isTemporalInstant(value)) {
42
+ return undefined;
43
+ }
44
+ if (!Array.isArray(value)) {
45
+ return { kind: "param", sql: "::jsonb", bound: value };
46
+ }
47
+ // All-boolean arrays are inferred as boolean[] by the driver; route via text::jsonb.
48
+ if (value.length > 0 && value.every((entry) => typeof entry === "boolean")) {
49
+ return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
50
+ }
51
+ return { kind: "param", sql: "::jsonb", bound: value };
52
+ }
53
+ const INTEGER_PG_TYPES = new Set([
54
+ "integer",
55
+ "int4",
56
+ "smallint",
57
+ "int2",
58
+ "bigint",
59
+ "int8",
60
+ "serial",
61
+ "bigserial",
62
+ ]);
63
+ // Postgres rounds sub-millisecond params into timestamptz(3); truncating to ms here keeps the
64
+ // stored instant identical across drivers and equal to what a Date param would store.
65
+ export function instantToTimestamptzParam(instant) {
66
+ return Temporal.Instant.fromEpochMilliseconds(instant.epochMilliseconds).toString();
67
+ }
68
+ function prepareDriverNeutralScalar(value, pgType) {
69
+ if ((pgType === "timestamptz" || pgType === "timestamptz(3)") && isTemporalInstant(value)) {
70
+ return { kind: "param", sql: "", bound: instantToTimestamptzParam(value) };
71
+ }
72
+ // Sent as text, so Postgres rejects 3.5 for an integer column instead of rounding it.
73
+ if (typeof value === "number" && pgType !== undefined && INTEGER_PG_TYPES.has(pgType)) {
74
+ return { kind: "param", sql: "", bound: String(value) };
75
+ }
76
+ return undefined;
77
+ }
78
+ export function prepareValue(value, pgType) {
79
+ if (isSqlExpression(value)) {
80
+ return { kind: "literal", literal: value.text };
81
+ }
82
+ // A PII-encrypted jsonb field is a JSON string scalar; a raw string bound to
83
+ // ::jsonb would be parsed as JSON text (or double-encoded).
84
+ if (pgType === "jsonb" && isPiiCiphertext(value)) {
85
+ return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
86
+ }
87
+ if (pgType === "jsonb" && value !== null) {
88
+ const prepared = prepareJsonbValue(value);
89
+ if (prepared)
90
+ return prepared;
91
+ }
92
+ const scalar = prepareDriverNeutralScalar(value, pgType);
93
+ if (scalar)
94
+ return scalar;
95
+ if (pgType === "date") {
96
+ const prepared = prepareDateValue(value);
97
+ if (prepared)
98
+ return prepared;
99
+ }
100
+ return { kind: "param", sql: "", bound: value };
101
+ }
@@ -3,6 +3,7 @@ import { type Logger } from "../logging/index.ts";
3
3
  import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
4
4
  export declare function collectEncryptedFieldNames(entity: EntityDefinition): ReadonlySet<string>;
5
5
  export declare function isSensitiveLabelField(entity: EntityDefinition, field: string): boolean;
6
+ export declare function holdsCiphertextOrSecret(entity: EntityDefinition, field: string): boolean;
6
7
  export declare function isListFilterAllowed(entity: EntityDefinition, field: string, filter: {
7
8
  readonly op: string;
8
9
  readonly value: unknown;
@@ -24,7 +24,7 @@ export function isSensitiveLabelField(entity, field) {
24
24
  }
25
25
  // A `sensitive` (find: "secret") field is never comparable either, even while its value is still
26
26
  // legacy plaintext.
27
- function holdsCiphertextOrSecret(entity, field) {
27
+ export function holdsCiphertextOrSecret(entity, field) {
28
28
  const def = entity.fields[field];
29
29
  if (def === undefined)
30
30
  return false;