@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
@@ -9,15 +9,35 @@
9
9
  // - Batch: single INSERT with multi-VALUES; atomic rollback on any
10
10
  // failure; predecessor pre-flight per aggregate in the batch.
11
11
 
12
- import { sql } from "drizzle-orm";
13
- import { drizzle } from "drizzle-orm/postgres-js";
14
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
12
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
13
+ import type { DbConnection, PgClient } from "../../db/connection";
14
+ import { asRawClient, selectMany } from "../../db/query";
15
15
  import { createTestDb, type TestDb } from "../../stack";
16
16
  import { generateId as uuid } from "../../utils";
17
17
  import { appendRaw, appendRawBatch, type RawEventToAppend } from "../admin-api";
18
18
  import { VersionConflictError } from "../errors";
19
19
  import { append, loadAggregate } from "../event-store";
20
- import { createEventsTable } from "../events-schema";
20
+ import { createEventsTable, eventsTable } from "../events-schema";
21
+
22
+ // Test-only spy: wrap a DbConnection's `.unsafe()` to capture the SQL
23
+ // string of every query the framework runs. Used to assert batching
24
+ // behaviour (single multi-VALUES INSERT vs N statements).
25
+ function spyQueries(db: DbConnection): { db: DbConnection; queries: string[] } {
26
+ const queries: string[] = [];
27
+ const wrapped = new Proxy(db, {
28
+ get(target, prop, receiver) {
29
+ if (prop === "unsafe") {
30
+ return (sql: string, params?: readonly unknown[]) => {
31
+ queries.push(sql);
32
+ // biome-ignore lint/suspicious/noExplicitAny: postgres-js .unsafe signature variance
33
+ return (target as any).unsafe(sql, params);
34
+ };
35
+ }
36
+ return Reflect.get(target, prop, receiver);
37
+ },
38
+ });
39
+ return { db: wrapped, queries };
40
+ }
21
41
 
22
42
  let testDb: TestDb;
23
43
 
@@ -35,7 +55,7 @@ afterAll(async () => {
35
55
  });
36
56
 
37
57
  beforeEach(async () => {
38
- await testDb.db.execute(sql`TRUNCATE kumiko_events RESTART IDENTITY`);
58
+ await asRawClient(testDb.db).unsafe(`TRUNCATE kumiko_events RESTART IDENTITY`);
39
59
  });
40
60
 
41
61
  function makeEvent(partial: Partial<RawEventToAppend> = {}): RawEventToAppend {
@@ -86,9 +106,12 @@ describe("appendRaw — single event", () => {
86
106
  }),
87
107
  );
88
108
 
89
- const rows = await testDb.db.execute<{ created_by: string }>(sql`
90
- SELECT created_by FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid
91
- `);
109
+ const rows = await asRawClient(testDb.db).unsafe<{ created_by: string }>(
110
+ `
111
+ SELECT created_by FROM kumiko_events WHERE aggregate_id = $1::uuid
112
+ `,
113
+ [aggregateId],
114
+ );
92
115
  expect(rows[0]?.created_by).toBe(legacyUser);
93
116
  expect(rows[0]?.created_by).not.toBe(userMigration);
94
117
  });
@@ -113,12 +136,7 @@ describe("appendRaw — single event", () => {
113
136
  }),
114
137
  );
115
138
 
116
- const rows = await testDb.db.execute<{
117
- payload: Record<string, unknown>;
118
- metadata: Record<string, unknown>;
119
- }>(sql`
120
- SELECT payload, metadata FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid
121
- `);
139
+ const rows = await selectMany(testDb.db, eventsTable, { aggregateId });
122
140
  expect(rows[0]?.payload).toEqual(payload);
123
141
  expect(rows[0]?.metadata).toEqual(metadata);
124
142
  });
@@ -128,9 +146,12 @@ describe("appendRaw — single event", () => {
128
146
  // client; the callback is invoked per NOTIFY payload. The resolved value
129
147
  // is a meta-object with an `unlisten` method, not a plain function.
130
148
  const notifications: string[] = [];
131
- const subscription = await testDb.client.listen("kumiko_events_new", (payload) => {
132
- notifications.push(payload);
133
- });
149
+ const subscription = await (testDb.client as PgClient).listen(
150
+ "kumiko_events_new",
151
+ (payload: string) => {
152
+ notifications.push(payload);
153
+ },
154
+ );
134
155
 
135
156
  try {
136
157
  // appendRaw path — MUST NOT fire.
@@ -176,9 +197,12 @@ describe("appendRaw — single event", () => {
176
197
  ).rejects.toBeInstanceOf(VersionConflictError);
177
198
 
178
199
  // Sanity: no row landed.
179
- const rows = await testDb.db.execute<{ c: number }>(sql`
180
- SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid
181
- `);
200
+ const rows = await asRawClient(testDb.db).unsafe<{ c: number }>(
201
+ `
202
+ SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = $1::uuid
203
+ `,
204
+ [aggregateId],
205
+ );
182
206
  expect(rows[0]?.c).toBe(0);
183
207
  });
184
208
 
@@ -188,21 +212,19 @@ describe("appendRaw — single event", () => {
188
212
  await appendRaw(testDb.db, makeEvent({ aggregateId, expectedVersion: 1 }));
189
213
  await appendRaw(testDb.db, makeEvent({ aggregateId, expectedVersion: 2 }));
190
214
 
191
- const rows = await testDb.db.execute<{ version: number }>(sql`
192
- SELECT version FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid ORDER BY version
193
- `);
215
+ const rows = await asRawClient(testDb.db).unsafe<{ version: number }>(
216
+ `
217
+ SELECT version FROM kumiko_events WHERE aggregate_id = $1::uuid ORDER BY version
218
+ `,
219
+ [aggregateId],
220
+ );
194
221
  expect(rows.map((r) => r.version)).toEqual([1, 2, 3]);
195
222
  });
196
223
  });
197
224
 
198
225
  describe("appendRawBatch — multi-event", () => {
199
226
  test("writes all events in a single INSERT statement (query-log spy)", async () => {
200
- const queries: string[] = [];
201
- const loggedDb = drizzle(testDb.client, {
202
- logger: {
203
- logQuery: (q) => queries.push(q),
204
- },
205
- });
227
+ const { db: loggedDb, queries } = spyQueries(testDb.db);
206
228
 
207
229
  const aggregateId = uuid();
208
230
  const events: readonly RawEventToAppend[] = [
@@ -217,9 +239,12 @@ describe("appendRawBatch — multi-event", () => {
217
239
  expect(inserts).toHaveLength(1);
218
240
 
219
241
  // All three events persisted with ascending versions.
220
- const rows = await testDb.db.execute<{ version: number; type: string }>(sql`
221
- SELECT version, type FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid ORDER BY version
222
- `);
242
+ const rows = await asRawClient(testDb.db).unsafe<{ version: number; type: string }>(
243
+ `
244
+ SELECT version, type FROM kumiko_events WHERE aggregate_id = $1::uuid ORDER BY version
245
+ `,
246
+ [aggregateId],
247
+ );
223
248
  expect(rows.map((r) => ({ v: r.version, t: r.type }))).toEqual([
224
249
  { v: 1, t: "legacy.order.created" },
225
250
  { v: 2, t: "legacy.order.accepted" },
@@ -263,7 +288,7 @@ describe("appendRawBatch — multi-event", () => {
263
288
  await expect(appendRawBatch(testDb.db, batch)).rejects.toBeInstanceOf(VersionConflictError);
264
289
 
265
290
  // Only the seed event survived — multi-VALUES INSERT is atomic.
266
- const rows = await testDb.db.execute<{ c: number }>(sql`
291
+ const rows = await asRawClient(testDb.db).unsafe<{ c: number }>(`
267
292
  SELECT count(*)::int as c FROM kumiko_events
268
293
  `);
269
294
  expect(rows[0]?.c).toBe(1);
@@ -280,9 +305,12 @@ describe("appendRawBatch — multi-event", () => {
280
305
  ]),
281
306
  ).rejects.toBeInstanceOf(VersionConflictError);
282
307
 
283
- const rows = await testDb.db.execute<{ c: number }>(sql`
284
- SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid
285
- `);
308
+ const rows = await asRawClient(testDb.db).unsafe<{ c: number }>(
309
+ `
310
+ SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = $1::uuid
311
+ `,
312
+ [aggregateId],
313
+ );
286
314
  expect(rows[0]?.c).toBe(0);
287
315
  });
288
316
 
@@ -301,9 +329,12 @@ describe("appendRawBatch — multi-event", () => {
301
329
  ).rejects.toBeInstanceOf(VersionConflictError);
302
330
 
303
331
  // Zero events persisted — the whole batch is rejected before the INSERT.
304
- const rows = await testDb.db.execute<{ c: number }>(sql`
305
- SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = ${aggregateId}::uuid
306
- `);
332
+ const rows = await asRawClient(testDb.db).unsafe<{ c: number }>(
333
+ `
334
+ SELECT count(*)::int as c FROM kumiko_events WHERE aggregate_id = $1::uuid
335
+ `,
336
+ [aggregateId],
337
+ );
307
338
  expect(rows[0]?.c).toBe(0);
308
339
  });
309
340
 
@@ -326,12 +357,7 @@ describe("appendRawBatch — multi-event", () => {
326
357
  });
327
358
 
328
359
  test("empty array is a no-op — no query, no throw", async () => {
329
- const queries: string[] = [];
330
- const loggedDb = drizzle(testDb.client, {
331
- logger: {
332
- logQuery: (q) => queries.push(q),
333
- },
334
- });
360
+ const { db: loggedDb, queries } = spyQueries(testDb.db);
335
361
 
336
362
  await appendRawBatch(loggedDb, []);
337
363
  expect(queries).toHaveLength(0);
@@ -1,6 +1,7 @@
1
- import { sql } from "drizzle-orm";
2
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
3
- import { createTestDb, type TestDb } from "../../stack";
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
3
+ import { asRawClient } from "../../db/query";
4
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
4
5
  import { generateId as uuid } from "../../utils";
5
6
  import {
6
7
  append,
@@ -14,13 +15,14 @@ import {
14
15
  VersionConflictError,
15
16
  } from "../index";
16
17
 
17
- let testDb: TestDb;
18
+ let testDb: BunTestDb;
18
19
 
19
20
  const tenantA = uuid();
20
21
  const tenantB = uuid();
21
22
  const userA = uuid();
22
23
 
23
24
  beforeAll(async () => {
25
+ await ensureTemporalPolyfill();
24
26
  testDb = await createTestDb();
25
27
  await createEventsTable(testDb.db);
26
28
  });
@@ -30,7 +32,7 @@ afterAll(async () => {
30
32
  });
31
33
 
32
34
  beforeEach(async () => {
33
- await testDb.db.execute(sql`TRUNCATE kumiko_events RESTART IDENTITY`);
35
+ await asRawClient(testDb.db).unsafe(`TRUNCATE kumiko_events RESTART IDENTITY`);
34
36
  });
35
37
 
36
38
  describe("event-store: append + load", () => {
@@ -9,17 +9,19 @@
9
9
  // Not a strict SLA test — the threshold is generous enough to survive CI
10
10
  // noise but tight enough that an index-miss regression would fail loudly.
11
11
 
12
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
12
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
13
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
13
14
  import type { TenantId } from "../../engine/types";
14
- import { createTestDb, type TestDb } from "../../stack";
15
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
15
16
  import { generateId as uuid } from "../../utils";
16
17
  import { append, createEventsTable, getStreamVersion } from "../index";
17
18
 
18
- let testDb: TestDb;
19
+ let testDb: BunTestDb;
19
20
  const tenantId: TenantId = uuid();
20
21
  const userId = uuid();
21
22
 
22
23
  beforeAll(async () => {
24
+ await ensureTemporalPolyfill();
23
25
  testDb = await createTestDb();
24
26
  await createEventsTable(testDb.db);
25
27
  });
@@ -12,10 +12,11 @@
12
12
  // latency, single-node PG. Production deploys are slower; these numbers
13
13
  // are the ceiling. Red test = framework regression, no slack tolerated.
14
14
 
15
- import { sql } from "drizzle-orm";
16
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
15
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
16
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
17
+ import { asRawClient } from "../../db/query";
17
18
  import type { TenantId } from "../../engine/types";
18
- import { createTestDb, type TestDb } from "../../stack";
19
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
19
20
  import { generateId as uuid } from "../../utils";
20
21
  import {
21
22
  append,
@@ -25,11 +26,12 @@ import {
25
26
  saveSnapshot,
26
27
  } from "../index";
27
28
 
28
- let testDb: TestDb;
29
+ let testDb: BunTestDb;
29
30
  const tenantId = uuid() as TenantId;
30
31
  const userId = uuid();
31
32
 
32
33
  beforeAll(async () => {
34
+ await ensureTemporalPolyfill();
33
35
  testDb = await createTestDb();
34
36
  await createEventsTable(testDb.db);
35
37
  });
@@ -39,8 +41,8 @@ afterAll(async () => {
39
41
  });
40
42
 
41
43
  beforeEach(async () => {
42
- await testDb.db.execute(
43
- sql`TRUNCATE kumiko_events, kumiko_snapshots, kumiko_archived_streams RESTART IDENTITY CASCADE`,
44
+ await asRawClient(testDb.db).unsafe(
45
+ `TRUNCATE kumiko_events, kumiko_snapshots, kumiko_archived_streams RESTART IDENTITY CASCADE`,
44
46
  );
45
47
  });
46
48
 
@@ -196,21 +198,27 @@ describe("event-store performance — Gate A", () => {
196
198
  // loadAggregateWithSnapshot performance on a finished stream, not
197
199
  // the seed phase.
198
200
  const aggregateId = uuid();
199
- await testDb.db.execute(sql`
201
+ await asRawClient(testDb.db).unsafe(
202
+ `
200
203
  INSERT INTO kumiko_events (aggregate_id, aggregate_type, tenant_id, version, type, payload, metadata, created_by)
201
- SELECT ${aggregateId}::uuid, 'task', ${tenantId}::uuid, 1, 'task.created',
204
+ SELECT $1::uuid, 'task', $2::uuid, 1, 'task.created',
202
205
  jsonb_build_object('title', 'v1'),
203
- jsonb_build_object('userId', ${userId}::text),
204
- ${userId}::text;
205
- `);
206
- await testDb.db.execute(sql`
206
+ jsonb_build_object('userId', $3::text),
207
+ $4::text;
208
+ `,
209
+ [aggregateId, tenantId, userId, userId],
210
+ );
211
+ await asRawClient(testDb.db).unsafe(
212
+ `
207
213
  INSERT INTO kumiko_events (aggregate_id, aggregate_type, tenant_id, version, type, payload, metadata, created_by)
208
- SELECT ${aggregateId}::uuid, 'task', ${tenantId}::uuid, gs.v, 'task.updated',
214
+ SELECT $1::uuid, 'task', $2::uuid, gs.v, 'task.updated',
209
215
  jsonb_build_object('title', 'v' || gs.v),
210
- jsonb_build_object('userId', ${userId}::text),
211
- ${userId}::text
216
+ jsonb_build_object('userId', $3::text),
217
+ $4::text
212
218
  FROM generate_series(2, 1000) gs(v);
213
- `);
219
+ `,
220
+ [aggregateId, tenantId, userId, userId],
221
+ );
214
222
 
215
223
  // Snapshot @ version 900 — typische Policy: snapshot every N events
216
224
  await saveSnapshot(testDb.db, {
@@ -9,10 +9,15 @@
9
9
  // in < 50ms (typical asOf/reducer rehydrate budget). Same gate the
10
10
  // spike proved on raw SQL, now enforced on the framework path.
11
11
 
12
- import { sql } from "drizzle-orm";
13
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
12
+ // Bun.SQL-only setup. KEIN postgres-js, KEIN createTestDb.
13
+ // Event-store functions expect DbRunner (= postgres-js) but Bun.SQL
14
+ // ist strukturell kompatibel (beide haben .unsafe()/.begin()) — kein Cast nötig.
15
+
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 } from "../../db/query";
14
19
  import type { TenantId } from "../../engine/types";
15
- import { createTestDb, type TestDb } from "../../stack";
20
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
16
21
  import { generateId as uuid } from "../../utils";
17
22
  import {
18
23
  append,
@@ -25,7 +30,7 @@ import {
25
30
  saveSnapshot,
26
31
  } from "../index";
27
32
 
28
- let testDb: TestDb;
33
+ let bun: BunTestDb;
29
34
  const tenant = uuid() as TenantId;
30
35
  const userId = uuid();
31
36
 
@@ -49,17 +54,18 @@ const reducer: SnapshotReducer<CounterState> = (state, event) => {
49
54
  };
50
55
 
51
56
  beforeAll(async () => {
52
- testDb = await createTestDb();
53
- await createEventsTable(testDb.db);
57
+ await ensureTemporalPolyfill();
58
+ bun = await createTestDb();
59
+ await createEventsTable(bun.db);
54
60
  });
55
61
 
56
62
  afterAll(async () => {
57
- await testDb.cleanup();
63
+ await bun.cleanup();
58
64
  });
59
65
 
60
66
  beforeEach(async () => {
61
- await testDb.db.execute(
62
- sql`TRUNCATE kumiko_events, kumiko_snapshots, kumiko_archived_streams RESTART IDENTITY`,
67
+ await asRawClient(bun.db).unsafe(
68
+ `TRUNCATE kumiko_events, kumiko_snapshots, kumiko_archived_streams RESTART IDENTITY`,
63
69
  );
64
70
  });
65
71
 
@@ -67,7 +73,7 @@ beforeEach(async () => {
67
73
  async function seedAggregate(eventCount: number): Promise<string> {
68
74
  const aggId = uuid();
69
75
  for (let i = 0; i < eventCount; i++) {
70
- await append(testDb.db, {
76
+ await append(bun.db, {
71
77
  aggregateId: aggId,
72
78
  aggregateType: "counter",
73
79
  tenantId: tenant,
@@ -85,7 +91,7 @@ async function seedAggregate(eventCount: number): Promise<string> {
85
91
  // raw SQL) keeps the invariant honest: if loadAggregate semantics drift,
86
92
  // this helper shifts with them.
87
93
  async function loadFullState(aggregateId: string): Promise<CounterState> {
88
- const events = await loadAggregate(testDb.db, aggregateId, tenant);
94
+ const events = await loadAggregate(bun.db, aggregateId, tenant);
89
95
  let state: CounterState = initial;
90
96
  for (const event of events) {
91
97
  state = reducer(state, event);
@@ -96,7 +102,7 @@ async function loadFullState(aggregateId: string): Promise<CounterState> {
96
102
  describe("snapshot-store — round-trip", () => {
97
103
  test("saveSnapshot + loadLatestSnapshot roundtrip the state", async () => {
98
104
  const aggId = uuid();
99
- await saveSnapshot(testDb.db, {
105
+ await saveSnapshot(bun.db, {
100
106
  aggregateId: aggId,
101
107
  tenantId: tenant,
102
108
  aggregateType: "counter",
@@ -104,7 +110,7 @@ describe("snapshot-store — round-trip", () => {
104
110
  state: { count: 100, label: "checkpoint" },
105
111
  });
106
112
 
107
- const loaded = await loadLatestSnapshot<CounterState>(testDb.db, aggId, tenant);
113
+ const loaded = await loadLatestSnapshot<CounterState>(bun.db, aggId, tenant);
108
114
  expect(loaded).not.toBeNull();
109
115
  expect(loaded?.version).toBe(42);
110
116
  expect(loaded?.state).toEqual({ count: 100, label: "checkpoint" });
@@ -113,28 +119,28 @@ describe("snapshot-store — round-trip", () => {
113
119
 
114
120
  test("saveSnapshot is idempotent — re-snapshotting the same version upserts", async () => {
115
121
  const aggId = uuid();
116
- await saveSnapshot(testDb.db, {
122
+ await saveSnapshot(bun.db, {
117
123
  aggregateId: aggId,
118
124
  tenantId: tenant,
119
125
  aggregateType: "counter",
120
126
  version: 10,
121
127
  state: { count: 10, label: "v1" },
122
128
  });
123
- await saveSnapshot(testDb.db, {
129
+ await saveSnapshot(bun.db, {
124
130
  aggregateId: aggId,
125
131
  tenantId: tenant,
126
132
  aggregateType: "counter",
127
133
  version: 10,
128
134
  state: { count: 10, label: "v2-updated" },
129
135
  });
130
- const loaded = await loadLatestSnapshot<CounterState>(testDb.db, aggId, tenant);
136
+ const loaded = await loadLatestSnapshot<CounterState>(bun.db, aggId, tenant);
131
137
  expect(loaded?.state.label).toBe("v2-updated");
132
138
  });
133
139
 
134
140
  test("loadLatestSnapshot picks the highest version when multiple exist", async () => {
135
141
  const aggId = uuid();
136
142
  for (const v of [5, 50, 20, 100, 75]) {
137
- await saveSnapshot(testDb.db, {
143
+ await saveSnapshot(bun.db, {
138
144
  aggregateId: aggId,
139
145
  tenantId: tenant,
140
146
  aggregateType: "counter",
@@ -142,7 +148,7 @@ describe("snapshot-store — round-trip", () => {
142
148
  state: { count: v * 2, label: `at-${v}` },
143
149
  });
144
150
  }
145
- const loaded = await loadLatestSnapshot<CounterState>(testDb.db, aggId, tenant);
151
+ const loaded = await loadLatestSnapshot<CounterState>(bun.db, aggId, tenant);
146
152
  expect(loaded?.version).toBe(100);
147
153
  expect(loaded?.state.label).toBe("at-100");
148
154
  });
@@ -155,7 +161,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
155
161
  // Snapshot MUST reflect that truth; otherwise the "snapshot + deltas =
156
162
  // full replay" invariant is meaningless.
157
163
  const partial = { count: 40, label: "snap-at-40" };
158
- await saveSnapshot(testDb.db, {
164
+ await saveSnapshot(bun.db, {
159
165
  aggregateId: aggId,
160
166
  tenantId: tenant,
161
167
  aggregateType: "counter",
@@ -165,7 +171,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
165
171
 
166
172
  const full = await loadFullState(aggId);
167
173
  const snapBased = await loadAggregateWithSnapshot<CounterState>(
168
- testDb.db,
174
+ bun.db,
169
175
  aggId,
170
176
  tenant,
171
177
  reducer,
@@ -181,7 +187,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
181
187
  const aggId = await seedAggregate(20);
182
188
  const full = await loadFullState(aggId);
183
189
  const snapBased = await loadAggregateWithSnapshot<CounterState>(
184
- testDb.db,
190
+ bun.db,
185
191
  aggId,
186
192
  tenant,
187
193
  reducer,
@@ -198,14 +204,14 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
198
204
  // presence. Otherwise a snapshot would silently survive a GDPR-style
199
205
  // archival and leak state that the event log hid.
200
206
  const aggId = await seedAggregate(10);
201
- await saveSnapshot(testDb.db, {
207
+ await saveSnapshot(bun.db, {
202
208
  aggregateId: aggId,
203
209
  tenantId: tenant,
204
210
  aggregateType: "counter",
205
211
  version: 10,
206
212
  state: { count: 10, label: "pre-archive" },
207
213
  });
208
- await archiveStream(testDb.db, {
214
+ await archiveStream(bun.db, {
209
215
  tenantId: tenant,
210
216
  aggregateId: aggId,
211
217
  aggregateType: "counter",
@@ -213,7 +219,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
213
219
  });
214
220
 
215
221
  const snapBased = await loadAggregateWithSnapshot<CounterState>(
216
- testDb.db,
222
+ bun.db,
217
223
  aggId,
218
224
  tenant,
219
225
  reducer,
@@ -225,7 +231,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
225
231
 
226
232
  // includeArchived opt-in surfaces the snapshot + deltas for ops tooling.
227
233
  const archived = await loadAggregateWithSnapshot<CounterState>(
228
- testDb.db,
234
+ bun.db,
229
235
  aggId,
230
236
  tenant,
231
237
  reducer,
@@ -238,7 +244,7 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
238
244
 
239
245
  test("1000-event aggregate with snapshot at v900 loads in under 50ms", async () => {
240
246
  const aggId = await seedAggregate(1000);
241
- await saveSnapshot(testDb.db, {
247
+ await saveSnapshot(bun.db, {
242
248
  aggregateId: aggId,
243
249
  tenantId: tenant,
244
250
  aggregateType: "counter",
@@ -247,11 +253,11 @@ describe("snapshot-store — loadAggregateWithSnapshot", () => {
247
253
  });
248
254
 
249
255
  // Warm cache
250
- await loadAggregateWithSnapshot<CounterState>(testDb.db, aggId, tenant, reducer, initial);
256
+ await loadAggregateWithSnapshot<CounterState>(bun.db, aggId, tenant, reducer, initial);
251
257
 
252
258
  const start = performance.now();
253
259
  const snapBased = await loadAggregateWithSnapshot<CounterState>(
254
- testDb.db,
260
+ bun.db,
255
261
  aggId,
256
262
  tenant,
257
263
  reducer,
@@ -6,9 +6,10 @@
6
6
  // - die throw-Policy bleibt unverändert (Regression-Guard)
7
7
  // - listDeadLetters filtert per eventType
8
8
 
9
- import { eq, sql } from "drizzle-orm";
10
- import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
11
- import { createTestDb, type TestDb } from "../../stack";
9
+ import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
10
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
11
+ import { asRawClient, insertOne, selectMany } from "../../db/query";
12
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
12
13
  import type { StoredEvent } from "../event-store";
13
14
  import { createEventsTable, eventsTable } from "../events-schema";
14
15
  import { type EventUpcasters, makeUpcastCtx, upcastStoredEvents } from "../upcaster";
@@ -18,7 +19,7 @@ import {
18
19
  upcasterDeadLetterTable,
19
20
  } from "../upcaster-dead-letter";
20
21
 
21
- let testDb: TestDb;
22
+ let testDb: BunTestDb;
22
23
 
23
24
  const TENANT_ID = "00000000-0000-4000-8000-0000000000aa";
24
25
 
@@ -78,6 +79,7 @@ const passthroughUpcasters: EventUpcasters = new Map([
78
79
  ]);
79
80
 
80
81
  beforeAll(async () => {
82
+ await ensureTemporalPolyfill();
81
83
  testDb = await createTestDb();
82
84
  await createEventsTable(testDb.db);
83
85
  await createUpcasterDeadLetterTable(testDb.db);
@@ -88,8 +90,8 @@ afterAll(async () => {
88
90
  });
89
91
 
90
92
  afterEach(async () => {
91
- await testDb.db.delete(upcasterDeadLetterTable);
92
- await testDb.db.delete(eventsTable);
93
+ await asRawClient(testDb.db).unsafe(`DELETE FROM "${upcasterDeadLetterTable.tableName}"`);
94
+ await asRawClient(testDb.db).unsafe(`DELETE FROM "${eventsTable.tableName}"`);
93
95
  });
94
96
 
95
97
  describe("upcaster error-policy: throw (default)", () => {
@@ -165,7 +167,7 @@ describe("upcaster error-policy: quarantine", () => {
165
167
  );
166
168
 
167
169
  // Drop one directly to add noise of a different type.
168
- await testDb.db.insert(upcasterDeadLetterTable).values({
170
+ await insertOne(testDb.db, upcasterDeadLetterTable, {
169
171
  eventId: "99",
170
172
  tenantId: TENANT_ID,
171
173
  aggregateId: "other",
@@ -195,10 +197,7 @@ describe("upcaster error-policy: quarantine", () => {
195
197
  errorPolicy: "quarantine",
196
198
  });
197
199
 
198
- const rows = await testDb.db
199
- .select({ c: sql<number>`count(*)::int` })
200
- .from(upcasterDeadLetterTable)
201
- .where(eq(upcasterDeadLetterTable.eventId, "30"));
202
- expect(rows[0]?.c).toBe(2);
200
+ const rows = await selectMany(testDb.db, upcasterDeadLetterTable, { eventId: "30" });
201
+ expect(rows).toHaveLength(2);
203
202
  });
204
203
  });