@cosmicdrift/kumiko-framework 0.14.0 → 0.16.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 (342) hide show
  1. package/package.json +6 -6
  2. package/src/__tests__/{anonymous-access.integration.ts → anonymous-access.integration.test.ts} +12 -9
  3. package/src/__tests__/{error-contract.integration.ts → error-contract.integration.test.ts} +5 -4
  4. package/src/__tests__/{field-access.integration.ts → field-access.integration.test.ts} +3 -3
  5. package/src/__tests__/{full-stack.integration.ts → full-stack.integration.test.ts} +7 -16
  6. package/src/__tests__/{ownership.integration.ts → ownership.integration.test.ts} +3 -2
  7. package/src/__tests__/{raw-table.integration.ts → raw-table.integration.test.ts} +18 -30
  8. package/src/__tests__/{reference-data.integration.ts → reference-data.integration.test.ts} +24 -11
  9. package/src/__tests__/{transition-guard.integration.ts → transition-guard.integration.test.ts} +12 -10
  10. package/src/api/__tests__/api.test.ts +1 -1
  11. package/src/api/__tests__/auth-middleware-transport.test.ts +1 -1
  12. package/src/api/__tests__/auth-routes-cookie.test.ts +1 -1
  13. package/src/api/__tests__/{batch.integration.ts → batch.integration.test.ts} +30 -30
  14. package/src/api/__tests__/body-limit.test.ts +1 -1
  15. package/src/api/__tests__/csrf-middleware.test.ts +1 -1
  16. package/src/api/__tests__/{dispatcher-live.integration.ts → dispatcher-live.integration.test.ts} +10 -9
  17. package/src/api/__tests__/metrics-endpoint.test.ts +1 -1
  18. package/src/api/__tests__/{nested-write.integration.ts → nested-write.integration.test.ts} +13 -16
  19. package/src/api/__tests__/readiness.test.ts +1 -1
  20. package/src/api/__tests__/request-id-middleware.test.ts +1 -1
  21. package/src/api/__tests__/sse-broker.test.ts +12 -12
  22. package/src/api/__tests__/sse-route.test.ts +1 -1
  23. package/src/api/auth-routes.ts +2 -5
  24. package/src/api/readiness.ts +2 -2
  25. package/src/auth/__tests__/roles.test.ts +2 -2
  26. package/src/bun-db/__tests__/PATTERN.md +73 -0
  27. package/src/bun-db/__tests__/_helpers.ts +103 -0
  28. package/src/bun-db/__tests__/batch-methods.integration.test.ts +143 -0
  29. package/src/bun-db/__tests__/batch-methods.test.ts +20 -0
  30. package/src/bun-db/__tests__/bun-test-db.ts +19 -0
  31. package/src/bun-db/__tests__/bun-test-stack.ts +6 -0
  32. package/src/bun-db/__tests__/column-types.integration.test.ts +132 -0
  33. package/src/bun-db/__tests__/compound-types.integration.test.ts +134 -0
  34. package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +235 -0
  35. package/src/bun-db/__tests__/smoke.integration.test.ts +43 -0
  36. package/src/bun-db/__tests__/sql-methods.integration.test.ts +231 -0
  37. package/src/bun-db/__tests__/where-patterns.integration.test.ts +185 -0
  38. package/src/bun-db/connection.ts +84 -0
  39. package/src/bun-db/index.ts +31 -0
  40. package/src/bun-db/query.ts +842 -0
  41. package/src/compliance/__tests__/duration-spec.test.ts +1 -1
  42. package/src/compliance/__tests__/profiles.test.ts +1 -1
  43. package/src/compliance/__tests__/sub-processors.test.ts +1 -1
  44. package/src/compliance/profiles.ts +1 -4
  45. package/src/db/__tests__/{apply-entity-event-tenant.integration.ts → apply-entity-event-tenant.integration.test.ts} +13 -11
  46. package/src/db/__tests__/big-int-field.test.ts +15 -14
  47. package/src/db/__tests__/column-ddl.integration.test.ts +113 -0
  48. package/src/db/__tests__/compound-types.test.ts +1 -1
  49. package/src/db/__tests__/{config-seed.integration.ts → config-seed.integration.test.ts} +32 -27
  50. package/src/db/__tests__/connection-options.test.ts +1 -1
  51. package/src/db/__tests__/cursor.test.ts +8 -32
  52. package/src/db/__tests__/dialect-instant.test.ts +1 -1
  53. package/src/db/__tests__/encryption.test.ts +1 -1
  54. package/src/db/__tests__/{drizzle-table-types.test.ts → entity-table-types.test.ts} +16 -16
  55. package/src/db/__tests__/{event-store-executor-list.integration.ts → event-store-executor-list.integration.test.ts} +12 -7
  56. package/src/db/__tests__/{event-store-executor.integration.ts → event-store-executor.integration.test.ts} +19 -12
  57. package/src/db/__tests__/{implicit-projection-equivalence.integration.ts → implicit-projection-equivalence.integration.test.ts} +35 -29
  58. package/src/db/__tests__/located-timestamp.test.ts +1 -1
  59. package/src/db/__tests__/migrate-generator.test.ts +71 -0
  60. package/src/db/__tests__/migrate-runner.test.ts +19 -0
  61. package/src/db/__tests__/money.test.ts +1 -1
  62. package/src/db/__tests__/{multi-row-insert.integration.ts → multi-row-insert.integration.test.ts} +18 -11
  63. package/src/db/__tests__/parse-auto-verb.test.ts +1 -1
  64. package/src/db/__tests__/pg-error.test.ts +43 -0
  65. package/src/db/__tests__/{required-not-null-migration-safety.integration.ts → required-not-null-migration-safety.integration.test.ts} +28 -24
  66. package/src/db/__tests__/{schema-migration.integration.ts → schema-migration.integration.test.ts} +32 -28
  67. package/src/db/__tests__/sql-inventory.test.ts +56 -0
  68. package/src/db/__tests__/table-builder-indexes.test.ts +30 -11
  69. package/src/db/__tests__/table-builder-required.test.ts +20 -22
  70. package/src/db/__tests__/{tenant-db.integration.ts → tenant-db.integration.test.ts} +106 -144
  71. package/src/db/__tests__/{unique-violation-mapping.integration.ts → unique-violation-mapping.integration.test.ts} +13 -8
  72. package/src/db/api.ts +46 -0
  73. package/src/db/apply-entity-event.ts +45 -36
  74. package/src/db/assert-exists-in.ts +5 -16
  75. package/src/db/bun-provider.ts +37 -0
  76. package/src/db/config-seed.ts +4 -4
  77. package/src/db/connection.ts +14 -57
  78. package/src/db/cursor.ts +5 -56
  79. package/src/db/dialect.ts +472 -99
  80. package/src/db/eagerload.ts +5 -12
  81. package/src/db/entity-table-meta.ts +390 -0
  82. package/src/db/event-store-executor.ts +158 -100
  83. package/src/db/index.ts +33 -5
  84. package/src/db/migrate-generator.ts +350 -0
  85. package/src/db/migrate-runner.ts +206 -0
  86. package/src/db/postgres-provider.ts +25 -0
  87. package/src/db/queries/entity-read.ts +15 -0
  88. package/src/db/queries/es-ops.ts +17 -0
  89. package/src/db/queries/event-consumer.ts +170 -0
  90. package/src/db/queries/event-store-admin.ts +127 -0
  91. package/src/db/queries/event-store.ts +155 -0
  92. package/src/db/queries/projection-rebuild.ts +59 -0
  93. package/src/db/queries/raw-sql.ts +15 -0
  94. package/src/db/queries/schema-drift.ts +35 -0
  95. package/src/db/queries/seed-context.ts +58 -0
  96. package/src/db/queries/table-ops.ts +11 -0
  97. package/src/db/queries/test-stack.ts +56 -0
  98. package/src/db/query-api.ts +22 -0
  99. package/src/db/query.ts +30 -0
  100. package/src/db/reference-data.ts +19 -22
  101. package/src/db/render-ddl.ts +57 -0
  102. package/src/db/row-helpers.ts +3 -52
  103. package/src/db/schema-inspection.ts +17 -4
  104. package/src/db/sql-inventory.ts +208 -0
  105. package/src/db/table-builder.ts +54 -46
  106. package/src/db/tenant-db.ts +105 -326
  107. package/src/engine/__tests__/auth-claims-registrar.test.ts +1 -1
  108. package/src/engine/__tests__/boot-validator-api-exposure.test.ts +3 -3
  109. package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +1 -1
  110. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +5 -5
  111. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +3 -3
  112. package/src/engine/__tests__/boot-validator.test.ts +4 -3
  113. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  114. package/src/engine/__tests__/build-target.test.ts +1 -1
  115. package/src/engine/__tests__/claim-keys.test.ts +1 -1
  116. package/src/engine/__tests__/codemod-pipeline.test.ts +3 -3
  117. package/src/engine/__tests__/config-helpers.test.ts +1 -1
  118. package/src/engine/__tests__/duration-utils.test.ts +16 -0
  119. package/src/engine/__tests__/effective-features.test.ts +1 -1
  120. package/src/engine/__tests__/engine.test.ts +1 -1
  121. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  122. package/src/engine/__tests__/event-helpers.test.ts +3 -3
  123. package/src/engine/__tests__/extends-registrar.test.ts +4 -4
  124. package/src/engine/__tests__/factories-long-text.test.ts +1 -1
  125. package/src/engine/__tests__/factories-time.test.ts +1 -1
  126. package/src/engine/__tests__/field-access.test.ts +38 -0
  127. package/src/engine/__tests__/field-predicates.test.ts +1 -1
  128. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  129. package/src/engine/__tests__/identifiers.test.ts +1 -1
  130. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  131. package/src/engine/__tests__/nav.test.ts +1 -1
  132. package/src/engine/__tests__/no-return-guard.test.ts +17 -0
  133. package/src/engine/__tests__/ownership.test.ts +10 -11
  134. package/src/engine/__tests__/parse-ref-target.test.ts +1 -1
  135. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  136. package/src/engine/__tests__/{pipeline-handler.integration.ts → pipeline-handler.integration.test.ts} +38 -52
  137. package/src/engine/__tests__/{pipeline-observability.integration.ts → pipeline-observability.integration.test.ts} +1 -1
  138. package/src/engine/__tests__/{pipeline-performance.integration.ts → pipeline-performance.integration.test.ts} +1 -1
  139. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  140. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  141. package/src/engine/__tests__/projection-helpers.test.ts +25 -17
  142. package/src/engine/__tests__/projection.test.ts +4 -4
  143. package/src/engine/__tests__/qualified-name.test.ts +1 -1
  144. package/src/engine/__tests__/raw-table.test.ts +9 -8
  145. package/src/engine/__tests__/resolve-config-or-param.test.ts +5 -5
  146. package/src/engine/__tests__/run-in.test.ts +1 -1
  147. package/src/engine/__tests__/schema-builder.test.ts +1 -1
  148. package/src/engine/__tests__/screen.test.ts +1 -1
  149. package/src/engine/__tests__/search-payload-extension.test.ts +3 -3
  150. package/src/engine/__tests__/state-machine.test.ts +1 -1
  151. package/src/engine/__tests__/steps-aggregate-append-event.test.ts +7 -7
  152. package/src/engine/__tests__/steps-aggregate-create.test.ts +4 -4
  153. package/src/engine/__tests__/steps-aggregate-update.test.ts +3 -3
  154. package/src/engine/__tests__/steps-call-feature.test.ts +5 -5
  155. package/src/engine/__tests__/steps-mail-send.test.ts +7 -7
  156. package/src/engine/__tests__/steps-read.test.ts +34 -40
  157. package/src/engine/__tests__/steps-resolver-utils.test.ts +6 -6
  158. package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +24 -19
  159. package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +28 -17
  160. package/src/engine/__tests__/steps-webhook-send.test.ts +6 -6
  161. package/src/engine/__tests__/steps-workflow.test.ts +7 -7
  162. package/src/engine/__tests__/system-user.test.ts +1 -1
  163. package/src/engine/__tests__/unmanaged-table.test.ts +98 -0
  164. package/src/engine/__tests__/validate-projection-allowlist.test.ts +4 -5
  165. package/src/engine/__tests__/validation-hooks.test.ts +1 -1
  166. package/src/engine/__tests__/visual-tree-patterns.test.ts +1 -1
  167. package/src/engine/boot-validator/entity-handler.ts +3 -3
  168. package/src/engine/boot-validator/ownership.ts +1 -1
  169. package/src/engine/define-feature.ts +37 -2
  170. package/src/engine/entity-handlers.ts +5 -5
  171. package/src/engine/factories.ts +1 -1
  172. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +1 -1
  173. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -1
  174. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +2 -2
  175. package/src/engine/feature-ast/__tests__/parse.test.ts +1 -1
  176. package/src/engine/feature-ast/__tests__/patch.test.ts +1 -1
  177. package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
  178. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +1 -1
  179. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +1 -1
  180. package/src/engine/feature-ast/extractors/shared.ts +2 -3
  181. package/src/engine/ownership.ts +113 -41
  182. package/src/engine/pattern-library/__tests__/library.test.ts +2 -2
  183. package/src/engine/projection-helpers.ts +2 -11
  184. package/src/engine/registry.ts +21 -2
  185. package/src/engine/steps/read-find-many.ts +13 -13
  186. package/src/engine/steps/read-find-one.ts +7 -9
  187. package/src/engine/steps/unsafe-projection-delete.ts +4 -5
  188. package/src/engine/steps/unsafe-projection-upsert.ts +63 -31
  189. package/src/engine/types/feature.ts +47 -2
  190. package/src/engine/types/fields.ts +4 -5
  191. package/src/engine/types/index.ts +2 -0
  192. package/src/engine/types/step.ts +10 -10
  193. package/src/engine/validate-projection-allowlist.ts +23 -3
  194. package/src/entrypoint/__tests__/{entrypoint-job-wiring.integration.ts → entrypoint-job-wiring.integration.test.ts} +4 -3
  195. package/src/entrypoint/__tests__/{split-deploy.integration.ts → split-deploy.integration.test.ts} +4 -3
  196. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  197. package/src/env/__tests__/dry-run.test.ts +1 -1
  198. package/src/errors/__tests__/classes.test.ts +1 -1
  199. package/src/errors/__tests__/error-helpers.test.ts +44 -0
  200. package/src/errors/__tests__/field-issue-compat.test.ts +16 -0
  201. package/src/errors/__tests__/write-failures.test.ts +1 -1
  202. package/src/errors/classes.ts +5 -19
  203. package/src/errors/field-issue.ts +11 -0
  204. package/src/errors/index.ts +1 -0
  205. package/src/errors/zod-bridge.ts +3 -2
  206. package/src/es-ops/__tests__/{context.integration.ts → context.integration.test.ts} +43 -29
  207. package/src/es-ops/__tests__/{runner.integration.ts → runner.integration.test.ts} +25 -23
  208. package/src/es-ops/__tests__/runner.test.ts +29 -19
  209. package/src/es-ops/context.ts +11 -56
  210. package/src/es-ops/operations-schema.ts +2 -2
  211. package/src/es-ops/runner.ts +12 -26
  212. package/src/event-store/__tests__/{admin-api.integration.ts → admin-api.integration.test.ts} +71 -45
  213. package/src/event-store/__tests__/{event-store.integration.ts → event-store.integration.test.ts} +7 -5
  214. package/src/event-store/__tests__/{get-stream-version-perf.integration.ts → get-stream-version-perf.integration.test.ts} +5 -3
  215. package/src/event-store/__tests__/{perf.integration.ts → perf.integration.test.ts} +24 -16
  216. package/src/event-store/__tests__/{snapshot.integration.ts → snapshot.integration.test.ts} +34 -28
  217. package/src/event-store/__tests__/{upcaster-dead-letter.integration.ts → upcaster-dead-letter.integration.test.ts} +11 -12
  218. package/src/event-store/__tests__/{upcaster.integration.ts → upcaster.integration.test.ts} +19 -32
  219. package/src/event-store/admin-api.ts +55 -83
  220. package/src/event-store/archive.ts +15 -39
  221. package/src/event-store/event-store.ts +92 -86
  222. package/src/event-store/events-schema.ts +2 -1
  223. package/src/event-store/index.ts +1 -0
  224. package/src/event-store/snapshot.ts +26 -24
  225. package/src/event-store/upcaster-dead-letter.ts +19 -18
  226. package/src/files/__tests__/content-disposition.test.ts +1 -1
  227. package/src/files/__tests__/{file-field-pipeline.integration.ts → file-field-pipeline.integration.test.ts} +8 -5
  228. package/src/files/__tests__/file-handle.test.ts +1 -1
  229. package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +32 -17
  230. package/src/files/__tests__/read-stream.test.ts +1 -1
  231. package/src/files/__tests__/{storage-tracking.integration.ts → storage-tracking.integration.test.ts} +26 -30
  232. package/src/files/__tests__/write-stream.test.ts +1 -1
  233. package/src/files/__tests__/zip-stream.test.ts +1 -1
  234. package/src/files/file-ref-table.ts +2 -2
  235. package/src/files/file-routes.ts +7 -9
  236. package/src/files/storage-tracking.ts +9 -17
  237. package/src/i18n/__tests__/i18n.test.ts +1 -1
  238. package/src/jobs/__tests__/{job-event-trigger.integration.ts → job-event-trigger.integration.test.ts} +6 -3
  239. package/src/jobs/__tests__/{job-multi-trigger.integration.ts → job-multi-trigger.integration.test.ts} +6 -3
  240. package/src/jobs/__tests__/{jobs.integration.ts → jobs.integration.test.ts} +5 -7
  241. package/src/lifecycle/__tests__/{lifecycle-server.integration.ts → lifecycle-server.integration.test.ts} +1 -1
  242. package/src/lifecycle/__tests__/lifecycle.test.ts +6 -6
  243. package/src/lifecycle/__tests__/signal-handlers.test.ts +6 -6
  244. package/src/logging/__tests__/pino-trace-bridge.test.ts +1 -1
  245. package/src/migrations/__tests__/compare-snapshots.test.ts +1 -1
  246. package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts} +34 -26
  247. package/src/migrations/__tests__/{detect-projections-to-rebuild.integration.ts → detect-projections-to-rebuild.integration.test.ts} +1 -1
  248. package/src/migrations/__tests__/rebuild-marker.test.ts +1 -1
  249. package/src/migrations/projection-detection.ts +12 -1
  250. package/src/migrations/schema-drift.ts +7 -23
  251. package/src/observability/__tests__/console-provider.test.ts +1 -1
  252. package/src/observability/__tests__/metric-validator.test.ts +1 -1
  253. package/src/observability/__tests__/noop-provider.test.ts +1 -1
  254. package/src/observability/__tests__/{observability.integration.ts → observability.integration.test.ts} +5 -8
  255. package/src/observability/__tests__/prometheus-meter.test.ts +1 -1
  256. package/src/observability/__tests__/recording-meter.test.ts +1 -1
  257. package/src/observability/__tests__/recording-tracer.test.ts +1 -1
  258. package/src/observability/__tests__/sensitive-filter.test.ts +1 -1
  259. package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts} +3 -3
  260. package/src/pipeline/__tests__/auth-claims-resolver.test.ts +9 -9
  261. package/src/pipeline/__tests__/{cascade-handler.integration.ts → cascade-handler.integration.test.ts} +18 -15
  262. package/src/pipeline/__tests__/cascade-handler.test.ts +1 -1
  263. package/src/pipeline/__tests__/{causation-chain.integration.ts → causation-chain.integration.test.ts} +12 -13
  264. package/src/pipeline/__tests__/{ctx-bridge.integration.ts → ctx-bridge.integration.test.ts} +12 -11
  265. package/src/pipeline/__tests__/dispatcher-utils.test.ts +107 -0
  266. package/src/pipeline/__tests__/dispatcher.test.ts +2 -2
  267. package/src/pipeline/__tests__/{distributed-lock.integration.ts → distributed-lock.integration.test.ts} +1 -1
  268. package/src/pipeline/__tests__/{domain-events-projections.integration.ts → domain-events-projections.integration.test.ts} +13 -15
  269. package/src/pipeline/__tests__/{event-dedup.integration.ts → event-dedup.integration.test.ts} +1 -1
  270. package/src/pipeline/__tests__/{event-define-event-strict.integration.ts → event-define-event-strict.integration.test.ts} +6 -16
  271. package/src/pipeline/__tests__/{event-dispatcher-lifecycle.integration.ts → event-dispatcher-lifecycle.integration.test.ts} +1 -1
  272. package/src/pipeline/__tests__/{event-dispatcher-multi-instance.integration.ts → event-dispatcher-multi-instance.integration.test.ts} +3 -2
  273. package/src/pipeline/__tests__/{event-dispatcher-pg-listen.integration.ts → event-dispatcher-pg-listen.integration.test.ts} +1 -1
  274. package/src/pipeline/__tests__/{event-dispatcher-recovery.integration.ts → event-dispatcher-recovery.integration.test.ts} +2 -2
  275. package/src/pipeline/__tests__/{event-dispatcher-second-audit.integration.ts → event-dispatcher-second-audit.integration.test.ts} +17 -16
  276. package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +14 -12
  277. package/src/pipeline/__tests__/{event-dispatcher.integration.ts → event-dispatcher.integration.test.ts} +8 -15
  278. package/src/pipeline/__tests__/{event-retention.integration.ts → event-retention.integration.test.ts} +28 -25
  279. package/src/pipeline/__tests__/{fetch-for-writing.integration.ts → fetch-for-writing.integration.test.ts} +6 -6
  280. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +4 -4
  281. package/src/pipeline/__tests__/{load-aggregate-query.integration.ts → load-aggregate-query.integration.test.ts} +9 -5
  282. package/src/pipeline/__tests__/{msp-error-mode.integration.ts → msp-error-mode.integration.test.ts} +1 -1
  283. package/src/pipeline/__tests__/{msp-multi-hop.integration.ts → msp-multi-hop.integration.test.ts} +9 -8
  284. package/src/pipeline/__tests__/{msp-rebuild.integration.ts → msp-rebuild.integration.test.ts} +47 -55
  285. package/src/pipeline/__tests__/{multi-stream-projection.integration.ts → multi-stream-projection.integration.test.ts} +19 -53
  286. package/src/pipeline/__tests__/{perf-rebuild.integration.ts → perf-rebuild.integration.test.ts} +36 -34
  287. package/src/pipeline/__tests__/{post-query-hook.integration.ts → post-query-hook.integration.test.ts} +1 -1
  288. package/src/pipeline/__tests__/{projection-rebuild.integration.ts → projection-rebuild.integration.test.ts} +21 -30
  289. package/src/pipeline/__tests__/{query-projection.integration.ts → query-projection.integration.test.ts} +6 -5
  290. package/src/pipeline/__tests__/redis-keys.test.ts +12 -0
  291. package/src/pipeline/__tests__/{redis-pipeline.integration.ts → redis-pipeline.integration.test.ts} +3 -1
  292. package/src/pipeline/cascade-handler.ts +13 -21
  293. package/src/pipeline/dispatcher-utils.ts +8 -7
  294. package/src/pipeline/dispatcher.ts +43 -48
  295. package/src/pipeline/event-consumer-state.ts +11 -2
  296. package/src/pipeline/event-dispatcher.ts +86 -146
  297. package/src/pipeline/event-retention.ts +14 -24
  298. package/src/pipeline/msp-rebuild.ts +54 -78
  299. package/src/pipeline/projection-rebuild.ts +65 -67
  300. package/src/pipeline/projection-state.ts +2 -2
  301. package/src/random/__tests__/generate.test.ts +13 -13
  302. package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts} +1 -1
  303. package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts} +1 -1
  304. package/src/rate-limit/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +1 -1
  305. package/src/redis/__tests__/redis-options.test.ts +1 -1
  306. package/src/search/__tests__/{meilisearch-adapter.integration.ts → meilisearch-adapter.integration.test.ts} +1 -1
  307. package/src/search/__tests__/search-adapter.test.ts +1 -1
  308. package/src/secrets/__tests__/dek-cache.test.ts +1 -3
  309. package/src/secrets/__tests__/env-master-key-provider.test.ts +1 -1
  310. package/src/secrets/__tests__/envelope.test.ts +1 -1
  311. package/src/secrets/__tests__/leak-guard.test.ts +1 -1
  312. package/src/secrets/__tests__/rotation.test.ts +1 -1
  313. package/src/stack/db.ts +25 -48
  314. package/src/stack/push-entity-projection-tables.ts +2 -4
  315. package/src/stack/table-helpers.ts +98 -61
  316. package/src/stack/test-stack.ts +10 -9
  317. package/src/testing/__tests__/db-cleanup.test.ts +40 -0
  318. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  319. package/src/testing/__tests__/{ensure-entity-table.integration.ts → ensure-entity-table.integration.test.ts} +7 -14
  320. package/src/testing/db-cleanup.ts +44 -0
  321. package/src/testing/expect-error.ts +1 -1
  322. package/src/testing/index.ts +2 -0
  323. package/src/testing/multipart-helper.ts +94 -0
  324. package/src/testing/shared-entities.ts +5 -5
  325. package/src/time/__tests__/polyfill.test.ts +1 -1
  326. package/src/time/__tests__/tz-context.test.ts +1 -1
  327. package/src/utils/__tests__/assert.test.ts +1 -1
  328. package/src/utils/__tests__/case.test.ts +16 -0
  329. package/src/utils/__tests__/env-parse.test.ts +1 -1
  330. package/src/utils/__tests__/is-plain-object.test.ts +16 -0
  331. package/src/utils/__tests__/parse-string-array-json.test.ts +16 -0
  332. package/src/utils/__tests__/safe-json.test.ts +22 -0
  333. package/src/utils/case.ts +6 -0
  334. package/src/utils/index.ts +3 -0
  335. package/src/utils/is-plain-object.ts +4 -0
  336. package/src/utils/parse-string-array-json.ts +14 -0
  337. package/CHANGELOG.md +0 -474
  338. package/src/db/__tests__/db-helpers.test.ts +0 -369
  339. package/src/db/__tests__/drizzle-helpers.integration.ts +0 -186
  340. package/src/db/__tests__/row-helpers.test.ts +0 -59
  341. package/src/engine/steps/_drizzle-boundary.ts +0 -19
  342. package/src/files/__tests__/file-field-column.integration.ts +0 -103
@@ -1,5 +1,5 @@
1
- import { eq } from "drizzle-orm";
2
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { asRawClient, insertOne, selectMany } from "../../db/query";
3
3
  import { createBooleanField, createEntity, createTextField } from "../../engine";
4
4
  import {
5
5
  createTestDb,
@@ -10,7 +10,7 @@ import {
10
10
  unsafePushTables,
11
11
  } from "../../stack";
12
12
  import { table as pgTable, serial, text, timestamp } from "../dialect";
13
- import { buildDrizzleTable } from "../table-builder";
13
+ import { buildEntityTable } from "../table-builder";
14
14
  import { createTenantDb } from "../tenant-db";
15
15
 
16
16
  // --- Entity table (has tenantId via buildBaseColumns) ---
@@ -25,7 +25,7 @@ const entity = createEntity({
25
25
  softDelete: true,
26
26
  });
27
27
 
28
- const table = buildDrizzleTable("tenantDbItem", entity);
28
+ const table = buildEntityTable("tenantDbItem", entity);
29
29
 
30
30
  // --- System table (no tenantId — like job_runs) ---
31
31
 
@@ -58,19 +58,16 @@ describe("scoped mode (default)", () => {
58
58
  test("auto-injects tenantId into values", async () => {
59
59
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
60
60
 
61
- const rows = await tdb.insert(table).values({ name: "Item 1" }).returning();
62
- expect(rows[0]?.["tenantId"]).toBe(testTenantId(1));
63
- expect(rows[0]?.["name"]).toBe("Item 1");
61
+ const row = await tdb.insertOne(table, { name: "Item 1" });
62
+ expect(row?.["tenantId"]).toBe(testTenantId(1));
63
+ expect(row?.["name"]).toBe("Item 1");
64
64
  });
65
65
 
66
66
  test("cannot override tenantId via values", async () => {
67
67
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
68
68
 
69
- const rows = await tdb
70
- .insert(table)
71
- .values({ name: "Sneaky", tenantId: testTenantId(999) })
72
- .returning();
73
- expect(rows[0]?.["tenantId"]).toBe(testTenantId(1));
69
+ const row = await tdb.insertOne(table, { name: "Sneaky", tenantId: testTenantId(999) });
70
+ expect(row?.["tenantId"]).toBe(testTenantId(1));
74
71
  });
75
72
  });
76
73
 
@@ -79,11 +76,11 @@ describe("scoped mode (default)", () => {
79
76
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
80
77
  const tdb2 = createTenantDb(testDb.db, tenant2.tenantId);
81
78
 
82
- await tdb1.insert(table).values({ name: "T1 Scoped" }).returning();
83
- await tdb2.insert(table).values({ name: "T2 Scoped" }).returning();
79
+ await tdb1.insertOne(table, { name: "T1 Scoped" });
80
+ await tdb2.insertOne(table, { name: "T2 Scoped" });
84
81
 
85
- const rows1 = await tdb1.select().from(table);
86
- const rows2 = await tdb2.select().from(table);
82
+ const rows1 = await tdb1.selectMany(table);
83
+ const rows2 = await tdb2.selectMany(table);
87
84
 
88
85
  expect(rows1.every((r) => r!["tenantId"] === testTenantId(1))).toBe(true);
89
86
  expect(rows2.every((r) => r!["tenantId"] === testTenantId(2))).toBe(true);
@@ -92,10 +89,10 @@ describe("scoped mode (default)", () => {
92
89
  test("additional where conditions combine with tenant filter", async () => {
93
90
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
94
91
 
95
- await tdb.insert(table).values({ name: "findme", status: "active" }).returning();
96
- await tdb.insert(table).values({ name: "notme", status: "draft" }).returning();
92
+ await tdb.insertOne(table, { name: "findme", status: "active" });
93
+ await tdb.insertOne(table, { name: "notme", status: "draft" });
97
94
 
98
- const rows = await tdb.select().from(table).where(eq(table["status"], "active"));
95
+ const rows = await tdb.selectMany(table, { status: "active" });
99
96
 
100
97
  expect(rows.length).toBeGreaterThanOrEqual(1);
101
98
  expect(
@@ -106,12 +103,12 @@ describe("scoped mode (default)", () => {
106
103
  test("select with columns", async () => {
107
104
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
108
105
 
109
- await tdb.insert(table).values({ name: "ColSelect" }).returning();
106
+ await tdb.insertOne(table, { name: "ColSelect" });
110
107
 
111
- const rows = await tdb
112
- .select({ id: table["id"], name: table["name"] })
113
- .from(table)
114
- .where(eq(table["name"], "ColSelect"));
108
+ const rows = await asRawClient(tdb).unsafe<Record<string, unknown>>(
109
+ `SELECT id, name FROM "tenant_db_items" WHERE name = $1 LIMIT 10`,
110
+ ["ColSelect"],
111
+ );
115
112
 
116
113
  expect(rows.length).toBeGreaterThanOrEqual(1);
117
114
  const row = rows[0]!;
@@ -124,13 +121,10 @@ describe("scoped mode (default)", () => {
124
121
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
125
122
 
126
123
  for (let i = 0; i < 5; i++) {
127
- await tdb
128
- .insert(table)
129
- .values({ name: `Limit${i}` })
130
- .returning();
124
+ await tdb.insertOne(table, { name: `Limit${i}` });
131
125
  }
132
126
 
133
- const rows = await tdb.select().from(table).limit(2);
127
+ const rows = await tdb.selectMany(table, {}, { limit: 2 });
134
128
  expect(rows).toHaveLength(2);
135
129
  });
136
130
  });
@@ -140,22 +134,14 @@ describe("scoped mode (default)", () => {
140
134
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
141
135
  const tdb2 = createTenantDb(testDb.db, tenant2.tenantId);
142
136
 
143
- const [row] = await tdb1.insert(table).values({ name: "T1 Update" }).returning();
137
+ const row = await tdb1.insertOne(table, { name: "T1 Update" });
144
138
  const id = row!["id"] as string;
145
139
 
146
- const result = await tdb2
147
- .update(table)
148
- .set({ name: "Hacked" })
149
- .where(eq(table["id"], id))
150
- .returning();
140
+ const result = await tdb2.updateMany(table, { name: "Hacked" }, { id: id });
151
141
 
152
142
  expect(result).toHaveLength(0);
153
143
 
154
- const [updated] = await tdb1
155
- .update(table)
156
- .set({ name: "Updated" })
157
- .where(eq(table["id"], id))
158
- .returning();
144
+ const [updated] = await tdb1.updateMany(table, { name: "Updated" }, { id: id });
159
145
 
160
146
  expect(updated!["name"]).toBe("Updated");
161
147
  });
@@ -163,12 +149,12 @@ describe("scoped mode (default)", () => {
163
149
  test("update without returning", async () => {
164
150
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
165
151
 
166
- const [row] = await tdb.insert(table).values({ name: "NoReturn" }).returning();
152
+ const row = await tdb.insertOne(table, { name: "NoReturn" });
167
153
  const id = row!["id"] as string;
168
154
 
169
- await tdb.update(table).set({ name: "NoReturnUpdated" }).where(eq(table["id"], id));
155
+ await tdb.updateMany(table, { name: "NoReturnUpdated" }, { id: id });
170
156
 
171
- const [updated] = await tdb.select().from(table).where(eq(table["id"], id));
157
+ const [updated] = await tdb.selectMany(table, { id: id });
172
158
  expect(updated!["name"]).toBe("NoReturnUpdated");
173
159
  });
174
160
  });
@@ -178,12 +164,12 @@ describe("scoped mode (default)", () => {
178
164
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
179
165
  const tdb2 = createTenantDb(testDb.db, tenant2.tenantId);
180
166
 
181
- const [row] = await tdb1.insert(table).values({ name: "T1 Delete" }).returning();
167
+ const row = await tdb1.insertOne(table, { name: "T1 Delete" });
182
168
  const id = row!["id"] as string;
183
169
 
184
- await tdb2.delete(table).where(eq(table["id"], id));
170
+ await tdb2.deleteMany(table, { id: id });
185
171
 
186
- const rows = await tdb1.select().from(table).where(eq(table["id"], id));
172
+ const rows = await tdb1.selectMany(table, { id: id });
187
173
  expect(rows).toHaveLength(1);
188
174
  });
189
175
  });
@@ -193,21 +179,17 @@ describe("scoped mode (default)", () => {
193
179
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
194
180
  const tdb2 = createTenantDb(testDb.db, tenant2.tenantId);
195
181
 
196
- const [created] = await tdb1.insert(table).values({ name: "Secret" }).returning();
182
+ const created = await tdb1.insertOne(table, { name: "Secret" });
197
183
  const id = created!["id"] as string;
198
184
 
199
- const seen = await tdb2.select().from(table).where(eq(table["id"], id));
185
+ const seen = await tdb2.selectMany(table, { id: id });
200
186
  expect(seen).toHaveLength(0);
201
187
 
202
- const updated = await tdb2
203
- .update(table)
204
- .set({ name: "Hacked" })
205
- .where(eq(table["id"], id))
206
- .returning();
188
+ const updated = await tdb2.updateMany(table, { name: "Hacked" }, { id: id });
207
189
  expect(updated).toHaveLength(0);
208
190
 
209
- await tdb2.delete(table).where(eq(table["id"], id));
210
- const stillThere = await tdb1.select().from(table).where(eq(table["id"], id));
191
+ await tdb2.deleteMany(table, { id: id });
192
+ const stillThere = await tdb1.selectMany(table, { id: id });
211
193
  expect(stillThere).toHaveLength(1);
212
194
  });
213
195
  });
@@ -215,7 +197,7 @@ describe("scoped mode (default)", () => {
215
197
  describe("reference data (tenantId = 0)", () => {
216
198
  test("scoped select includes rows with tenantId = 0", async () => {
217
199
  // Seed reference data with tenantId = 0 (like seedReferenceData does)
218
- await testDb.db.insert(table).values({
200
+ await insertOne(testDb.db, table, {
219
201
  name: "GlobalRef",
220
202
  status: "ref",
221
203
  tenantId: "00000000-0000-4000-8000-000000000000",
@@ -227,15 +209,15 @@ describe("scoped mode (default)", () => {
227
209
  const tdb2 = createTenantDb(testDb.db, tenant2.tenantId);
228
210
 
229
211
  // Both tenants can see the global reference row
230
- const rows1 = await tdb1.select().from(table).where(eq(table["name"], "GlobalRef"));
212
+ const rows1 = await tdb1.selectMany(table, { name: "GlobalRef" });
231
213
  expect(rows1).toHaveLength(1);
232
214
 
233
- const rows2 = await tdb2.select().from(table).where(eq(table["name"], "GlobalRef"));
215
+ const rows2 = await tdb2.selectMany(table, { name: "GlobalRef" });
234
216
  expect(rows2).toHaveLength(1);
235
217
  });
236
218
 
237
219
  test("scoped update does NOT affect tenantId = 0 rows", async () => {
238
- await testDb.db.insert(table).values({
220
+ await insertOne(testDb.db, table, {
239
221
  name: "RefNoUpdate",
240
222
  status: "ref",
241
223
  tenantId: "00000000-0000-4000-8000-000000000000",
@@ -245,25 +227,18 @@ describe("scoped mode (default)", () => {
245
227
 
246
228
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
247
229
 
248
- const result = await tdb1
249
- .update(table)
250
- .set({ name: "Hacked" })
251
- .where(eq(table["name"], "RefNoUpdate"))
252
- .returning();
230
+ const result = await tdb1.updateMany(table, { name: "Hacked" }, { name: "RefNoUpdate" });
253
231
 
254
232
  // Writes from a tenant scope must never touch reference rows (tenantId = 0).
255
233
  // Reading them is fine, modifying them is a cross-tenant integrity bug.
256
234
  expect(result).toHaveLength(0);
257
235
 
258
- const [untouched] = await testDb.db
259
- .select()
260
- .from(table)
261
- .where(eq(table["name"], "RefNoUpdate"));
236
+ const [untouched] = await selectMany(testDb.db, table, { name: "RefNoUpdate" });
262
237
  expect(untouched!["name"]).toBe("RefNoUpdate");
263
238
  });
264
239
 
265
240
  test("scoped delete does NOT affect tenantId = 0 rows", async () => {
266
- await testDb.db.insert(table).values({
241
+ await insertOne(testDb.db, table, {
267
242
  name: "RefNoDelete",
268
243
  status: "ref",
269
244
  tenantId: "00000000-0000-4000-8000-000000000000",
@@ -272,12 +247,9 @@ describe("scoped mode (default)", () => {
272
247
  });
273
248
 
274
249
  const tdb1 = createTenantDb(testDb.db, tenant1.tenantId);
275
- await tdb1.delete(table).where(eq(table["name"], "RefNoDelete"));
250
+ await tdb1.deleteMany(table, { name: "RefNoDelete" });
276
251
 
277
- const [stillThere] = await testDb.db
278
- .select()
279
- .from(table)
280
- .where(eq(table["name"], "RefNoDelete"));
252
+ const [stillThere] = await selectMany(testDb.db, table, { name: "RefNoDelete" });
281
253
  expect(stillThere).toBeDefined();
282
254
  });
283
255
  });
@@ -292,11 +264,11 @@ describe("system mode (r.systemScope())", () => {
292
264
  const scoped1 = createTenantDb(testDb.db, tenant1.tenantId);
293
265
  const scoped2 = createTenantDb(testDb.db, tenant2.tenantId);
294
266
 
295
- await scoped1.insert(table).values({ name: "System-T1" }).returning();
296
- await scoped2.insert(table).values({ name: "System-T2" }).returning();
267
+ await scoped1.insertOne(table, { name: "System-T1" });
268
+ await scoped2.insertOne(table, { name: "System-T2" });
297
269
 
298
270
  const systemDb = createTenantDb(testDb.db, tenant1.tenantId, "system");
299
- const rows = await systemDb.select().from(table);
271
+ const rows = await systemDb.selectMany(table);
300
272
 
301
273
  const tenantIds = new Set(rows.map((r) => r!["tenantId"]));
302
274
  // Must see rows from at least 2 different tenants
@@ -307,14 +279,14 @@ describe("system mode (r.systemScope())", () => {
307
279
  const systemDb = createTenantDb(testDb.db, tenant1.tenantId, "system");
308
280
 
309
281
  // Without explicit tenantId — uses the default (tenant1)
310
- const [defaultRow] = await systemDb.insert(table).values({ name: "SystemDefault" }).returning();
282
+ const defaultRow = await systemDb.insertOne(table, { name: "SystemDefault" });
311
283
  expect(defaultRow!["tenantId"]).toBe(testTenantId(1));
312
284
 
313
285
  // With explicit tenantId — handler's value wins
314
- const [overrideRow] = await systemDb
315
- .insert(table)
316
- .values({ name: "SystemOverride", tenantId: testTenantId(99) })
317
- .returning();
286
+ const overrideRow = await systemDb.insertOne(table, {
287
+ name: "SystemOverride",
288
+ tenantId: testTenantId(99),
289
+ });
318
290
  expect(overrideRow!["tenantId"]).toBe(testTenantId(99));
319
291
  });
320
292
 
@@ -326,55 +298,47 @@ describe("system mode (r.systemScope())", () => {
326
298
 
327
299
  // In scoped mode, tenantId: 77 would be overridden to 1
328
300
  const scopedDb = createTenantDb(testDb.db, tenant1.tenantId);
329
- const [scopedRow] = await scopedDb
330
- .insert(table)
331
- .values({ name: "ScopedForce", tenantId: testTenantId(77) })
332
- .returning();
301
+ const scopedRow = await scopedDb.insertOne(table, {
302
+ name: "ScopedForce",
303
+ tenantId: testTenantId(77),
304
+ });
333
305
  expect(scopedRow!["tenantId"]).toBe(testTenantId(1)); // forced
334
306
 
335
307
  // In unscoped mode, explicit tenantId wins
336
- const [unscopedRow] = await systemDb
337
- .insert(table)
338
- .values({ name: "SystemExplicit", tenantId: testTenantId(77) })
339
- .returning();
308
+ const unscopedRow = await systemDb.insertOne(table, {
309
+ name: "SystemExplicit",
310
+ tenantId: testTenantId(77),
311
+ });
340
312
  expect(unscopedRow!["tenantId"]).toBe(testTenantId(77)); // handler wins
341
313
  });
342
314
 
343
315
  test("update affects rows from any tenant", async () => {
344
316
  const scoped2 = createTenantDb(testDb.db, tenant2.tenantId);
345
- const [row] = await scoped2.insert(table).values({ name: "T2-System-Upd" }).returning();
317
+ const row = await scoped2.insertOne(table, { name: "T2-System-Upd" });
346
318
  const id = row!["id"] as string;
347
319
 
348
320
  // Scoped tenant 1 cannot update tenant 2's row
349
321
  const scoped1 = createTenantDb(testDb.db, tenant1.tenantId);
350
- const scopedResult = await scoped1
351
- .update(table)
352
- .set({ name: "ScopedFail" })
353
- .where(eq(table["id"], id))
354
- .returning();
322
+ const scopedResult = await scoped1.updateMany(table, { name: "ScopedFail" }, { id: id });
355
323
  expect(scopedResult).toHaveLength(0);
356
324
 
357
325
  // Unscoped CAN update tenant 2's row
358
326
  const systemDb = createTenantDb(testDb.db, tenant1.tenantId, "system");
359
- const [updated] = await systemDb
360
- .update(table)
361
- .set({ name: "SystemWin" })
362
- .where(eq(table["id"], id))
363
- .returning();
364
- expect(updated!["name"]).toBe("SystemWin");
327
+ const updated = await systemDb.updateMany(table, { name: "SystemWin" }, { id: id });
328
+ expect(updated[0]!["name"]).toBe("SystemWin");
365
329
  });
366
330
 
367
331
  test("delete affects rows from any tenant", async () => {
368
332
  const scoped2 = createTenantDb(testDb.db, tenant2.tenantId);
369
- const [row] = await scoped2.insert(table).values({ name: "T2-System-Del" }).returning();
333
+ const row = await scoped2.insertOne(table, { name: "T2-System-Del" });
370
334
  const id = row!["id"] as string;
371
335
 
372
336
  // Unscoped can delete tenant 2's row from tenant 1 context
373
337
  const systemDb = createTenantDb(testDb.db, tenant1.tenantId, "system");
374
- await systemDb.delete(table).where(eq(table["id"], id));
338
+ await systemDb.deleteMany(table, { id: id });
375
339
 
376
340
  // Verify it's gone
377
- const remaining = await scoped2.select().from(table).where(eq(table["id"], id));
341
+ const remaining = await scoped2.selectMany(table, { id: id });
378
342
  expect(remaining).toHaveLength(0);
379
343
  });
380
344
  });
@@ -386,18 +350,18 @@ describe("system mode (r.systemScope())", () => {
386
350
  describe("tables without tenantId column", () => {
387
351
  test("select returns all rows (no tenant filter)", async () => {
388
352
  // Insert two rows via raw db
389
- await testDb.db.insert(systemTable).values({ label: "System-A" });
390
- await testDb.db.insert(systemTable).values({ label: "System-B" });
353
+ await insertOne(testDb.db, systemTable, { label: "System-A" });
354
+ await insertOne(testDb.db, systemTable, { label: "System-B" });
391
355
 
392
356
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
393
- const rows = await tdb.select().from(systemTable);
357
+ const rows = await tdb.selectMany(systemTable);
394
358
  expect(rows.length).toBeGreaterThanOrEqual(2);
395
359
  });
396
360
 
397
361
  test("insert does not inject tenantId", async () => {
398
362
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
399
363
 
400
- const [row] = await tdb.insert(systemTable).values({ label: "NoTenantInjection" }).returning();
364
+ const row = await tdb.insertOne(systemTable, { label: "NoTenantInjection" });
401
365
  const data = row!;
402
366
  expect(data["label"]).toBe("NoTenantInjection");
403
367
  // No tenantId column at all — should not be in the result
@@ -407,33 +371,33 @@ describe("tables without tenantId column", () => {
407
371
  test("select with where works without tenant filter", async () => {
408
372
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
409
373
 
410
- await tdb.insert(systemTable).values({ label: "FindThis" }).returning();
374
+ await tdb.insertOne(systemTable, { label: "FindThis" });
411
375
 
412
- const rows = await tdb.select().from(systemTable).where(eq(systemTable["label"], "FindThis"));
376
+ const rows = await tdb.selectMany(systemTable, { label: "FindThis" });
413
377
  expect(rows).toHaveLength(1);
414
378
  });
415
379
 
416
380
  test("update works without tenant filter", async () => {
417
381
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
418
382
 
419
- const [row] = await tdb.insert(systemTable).values({ label: "BeforeUpd" }).returning();
383
+ const row = await tdb.insertOne(systemTable, { label: "BeforeUpd" });
420
384
  const id = row!["id"] as number;
421
385
 
422
- await tdb.update(systemTable).set({ label: "AfterUpd" }).where(eq(systemTable["id"], id));
386
+ await tdb.updateMany(systemTable, { label: "AfterUpd" }, { id: id });
423
387
 
424
- const [updated] = await tdb.select().from(systemTable).where(eq(systemTable["id"], id));
388
+ const [updated] = await tdb.selectMany(systemTable, { id: id });
425
389
  expect(updated!["label"]).toBe("AfterUpd");
426
390
  });
427
391
 
428
392
  test("delete works without tenant filter", async () => {
429
393
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
430
394
 
431
- const [row] = await tdb.insert(systemTable).values({ label: "ToDelete" }).returning();
395
+ const row = await tdb.insertOne(systemTable, { label: "ToDelete" });
432
396
  const id = row!["id"] as number;
433
397
 
434
- await tdb.delete(systemTable).where(eq(systemTable["id"], id));
398
+ await tdb.deleteMany(systemTable, { id: id });
435
399
 
436
- const remaining = await tdb.select().from(systemTable).where(eq(systemTable["id"], id));
400
+ const remaining = await tdb.selectMany(systemTable, { id: id });
437
401
  expect(remaining).toHaveLength(0);
438
402
  });
439
403
  });
@@ -460,41 +424,41 @@ describe("tenantId property", () => {
460
424
  describe("mass-update guard", () => {
461
425
  test(".set().returning() without .where() rejects with a clear error", async () => {
462
426
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
463
- await tdb.insert(table).values({ name: "MassUpdateVictim1" }).returning();
464
- await tdb.insert(table).values({ name: "MassUpdateVictim2" }).returning();
427
+ await tdb.insertOne(table, { name: "MassUpdateVictim1" });
428
+ await tdb.insertOne(table, { name: "MassUpdateVictim2" });
465
429
 
466
- await expect(tdb.update(table).set({ name: "Wiped" }).returning()).rejects.toThrow(
467
- /without \.where\(\) would mass-update/,
430
+ // biome-ignore lint/suspicious/noExplicitAny: test cast
431
+ await expect((tdb as any).updateMany(table, { name: "Wiped" })).rejects.toThrow(
432
+ /without where would mass-update/,
468
433
  );
469
434
 
470
435
  // Rows must be untouched — the rejection happened before any SQL ran.
471
- const untouched = await tdb.select().from(table);
436
+ const untouched = await tdb.selectMany(table);
472
437
  const touched = untouched.filter((r) => r["name"] === "Wiped");
473
438
  expect(touched).toHaveLength(0);
474
439
  });
475
440
 
476
441
  test(".set() awaited without .where() rejects too", async () => {
477
442
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
478
- await tdb.insert(table).values({ name: "AwaitGuardVictim" }).returning();
443
+ await tdb.insertOne(table, { name: "AwaitGuardVictim" });
479
444
 
480
- const promise = tdb.update(table).set({ name: "WipedByAwait" }) as unknown as Promise<void>;
481
- await expect(promise).rejects.toThrow(/awaited without \.where\(\) would mass-update/);
445
+ // biome-ignore lint/suspicious/noExplicitAny: test cast
446
+ const promise = (tdb as any).updateMany(table, {
447
+ name: "WipedByAwait",
448
+ }) as unknown as Promise<void>;
449
+ await expect(promise).rejects.toThrow(/without where would mass-update/);
482
450
 
483
- const untouched = await tdb.select().from(table);
451
+ const untouched = await tdb.selectMany(table);
484
452
  const touched = untouched.filter((r) => r["name"] === "WipedByAwait");
485
453
  expect(touched).toHaveLength(0);
486
454
  });
487
455
 
488
456
  test(".set().where(...).returning() still works (guard only triggers on missing where)", async () => {
489
457
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
490
- const [row] = await tdb.insert(table).values({ name: "HappyPath" }).returning();
458
+ const row = await tdb.insertOne(table, { name: "HappyPath" });
491
459
  const id = row!["id"] as string;
492
460
 
493
- const updated = await tdb
494
- .update(table)
495
- .set({ name: "HappyPathUpdated" })
496
- .where(eq(table["id"], id))
497
- .returning();
461
+ const updated = await tdb.updateMany(table, { name: "HappyPathUpdated" }, { id: id });
498
462
  expect(updated[0]!["name"]).toBe("HappyPathUpdated");
499
463
  });
500
464
  });
@@ -514,7 +478,7 @@ describe("pre-flight signal cancellation", () => {
514
478
 
515
479
  let thrown: unknown;
516
480
  try {
517
- await tdb.select().from(table);
481
+ await tdb.selectMany(table);
518
482
  } catch (e) {
519
483
  thrown = e;
520
484
  }
@@ -535,7 +499,7 @@ describe("pre-flight signal cancellation", () => {
535
499
 
536
500
  let insertThrown: unknown;
537
501
  try {
538
- await tdb.insert(table).values({ name: "x" }).returning();
502
+ await tdb.insertOne(table, { name: "x" });
539
503
  } catch (e) {
540
504
  insertThrown = e;
541
505
  }
@@ -543,10 +507,7 @@ describe("pre-flight signal cancellation", () => {
543
507
 
544
508
  let updateThrown: unknown;
545
509
  try {
546
- await tdb
547
- .update(table)
548
- .set({ name: "y" })
549
- .where(eq(table["id"], "00000000-0000-0000-0000-000000000001"));
510
+ await tdb.updateMany(table, { name: "y" }, { id: "00000000-0000-0000-0000-000000000001" });
550
511
  } catch (e) {
551
512
  updateThrown = e;
552
513
  }
@@ -554,7 +515,7 @@ describe("pre-flight signal cancellation", () => {
554
515
 
555
516
  let deleteThrown: unknown;
556
517
  try {
557
- await tdb.delete(table).where(eq(table["id"], "00000000-0000-0000-0000-000000000001"));
518
+ await tdb.deleteMany(table, { id: "00000000-0000-0000-0000-000000000001" });
558
519
  } catch (e) {
559
520
  deleteThrown = e;
560
521
  }
@@ -576,14 +537,14 @@ describe("pre-flight signal cancellation", () => {
576
537
  controller.signal,
577
538
  );
578
539
 
579
- const [first] = await tdb.insert(table).values({ name: "preflight-first" }).returning();
540
+ const first = await tdb.insertOne(table, { name: "preflight-first" });
580
541
  expect(first).toBeDefined();
581
542
 
582
543
  controller.abort();
583
544
 
584
545
  let secondThrown: unknown;
585
546
  try {
586
- await tdb.insert(table).values({ name: "preflight-second" }).returning();
547
+ await tdb.insertOne(table, { name: "preflight-second" });
587
548
  } catch (e) {
588
549
  secondThrown = e;
589
550
  }
@@ -592,7 +553,7 @@ describe("pre-flight signal cancellation", () => {
592
553
  // Proves the first row was actually committed and the second never
593
554
  // made it — the abort prevented future work, didn't roll back done
594
555
  // work.
595
- const rows = await testDb.db.select().from(table);
556
+ const rows = await selectMany(testDb.db, table);
596
557
  const names = rows.map((r) => r["name"] as string);
597
558
  expect(names).toContain("preflight-first");
598
559
  expect(names).not.toContain("preflight-second");
@@ -600,7 +561,8 @@ describe("pre-flight signal cancellation", () => {
600
561
 
601
562
  test("no signal passed: queries run normally (signal is opt-in)", async () => {
602
563
  const tdb = createTenantDb(testDb.db, tenant1.tenantId);
603
- const result = await tdb.insert(table).values({ name: "no-signal" }).returning();
604
- expect(result).toHaveLength(1);
564
+ const result = await tdb.insertOne(table, { name: "no-signal" });
565
+ expect(result).toBeDefined();
566
+ expect(result?.["name"]).toBe("no-signal");
605
567
  });
606
568
  });
@@ -13,13 +13,15 @@
13
13
  // also nicht. Erst die projection-INSERT verletzt den Index. Ohne F8:
14
14
  // 500. Mit F8: writeFailure(UniqueViolationError) → 409.
15
15
 
16
- import { sql } from "drizzle-orm";
17
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
16
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
17
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
18
+ import { asRawClient, selectMany } from "../../db/query";
18
19
  import { createEntity, createTextField } from "../../engine";
19
20
  import { createEventsTable } from "../../event-store";
20
- import { createTestDb, type TestDb, TestUsers, unsafeCreateEntityTable } from "../../stack";
21
+ import { TestUsers, unsafeCreateEntityTable } from "../../stack";
22
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
21
23
  import { createEventStoreExecutor } from "../event-store-executor";
22
- import { buildDrizzleTable } from "../table-builder";
24
+ import { buildEntityTable } from "../table-builder";
23
25
  import { createTenantDb, type TenantDb } from "../tenant-db";
24
26
 
25
27
  const userEntity = createEntity({
@@ -39,14 +41,15 @@ const userEntity = createEntity({
39
41
  { columns: ["tenantId", "email"], unique: true, name: "read_unique_users_tenant_email_uniq" },
40
42
  ],
41
43
  });
42
- const table = buildDrizzleTable("unique-user", userEntity);
44
+ const table = buildEntityTable("unique-user", userEntity);
43
45
  const exec = createEventStoreExecutor(table, userEntity, { entityName: "unique-user" });
44
46
 
45
- let testDb: TestDb;
47
+ let testDb: BunTestDb;
46
48
  let tdb: TenantDb;
47
49
  const admin = TestUsers.admin;
48
50
 
49
51
  beforeAll(async () => {
52
+ await ensureTemporalPolyfill();
50
53
  testDb = await createTestDb();
51
54
  await unsafeCreateEntityTable(testDb.db, userEntity, "unique-user");
52
55
  await createEventsTable(testDb.db);
@@ -58,7 +61,9 @@ afterAll(async () => {
58
61
  });
59
62
 
60
63
  beforeEach(async () => {
61
- await testDb.db.execute(sql`TRUNCATE kumiko_events, read_unique_users RESTART IDENTITY CASCADE`);
64
+ await asRawClient(testDb.db).unsafe(
65
+ `TRUNCATE kumiko_events, read_unique_users RESTART IDENTITY CASCADE`,
66
+ );
62
67
  });
63
68
 
64
69
  // =============================================================================
@@ -98,7 +103,7 @@ describe("F8 — entity-level unique-violation auf create", () => {
98
103
  tdb,
99
104
  );
100
105
  expect(second.isSuccess).toBe(false);
101
- const rows = await testDb.db.select().from(table);
106
+ const rows = await selectMany(testDb.db, table);
102
107
  expect(rows).toHaveLength(1);
103
108
  expect((rows[0] as { displayName: string }).displayName).toBe("Bob 1");
104
109
  });