@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,7 +1,8 @@
1
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
2
3
  import type { TableColumns } from "../../db/dialect";
3
4
  import { createEventStoreExecutor, type EventStoreExecutor } from "../../db/event-store-executor";
4
- import { buildDrizzleTable } from "../../db/table-builder";
5
+ import { buildEntityTable } from "../../db/table-builder";
5
6
  import { createTenantDb, type TenantDb } from "../../db/tenant-db";
6
7
  import {
7
8
  createEntity,
@@ -11,13 +12,14 @@ import {
11
12
  type Registry,
12
13
  } from "../../engine";
13
14
  import { createEventsTable } from "../../event-store";
14
- import { createTestDb, type TestDb, TestUsers, unsafeCreateEntityTable } from "../../stack";
15
+ import { TestUsers, unsafeCreateEntityTable } from "../../stack";
16
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
15
17
  import { createCascadeDeleteHook } from "../cascade-handler";
16
18
 
17
19
  // biome-ignore lint/suspicious/noExplicitAny: Drizzle dynamic tables
18
20
  type Table = TableColumns<any>;
19
21
 
20
- let testDb: TestDb;
22
+ let testDb: BunTestDb;
21
23
  let tdb: TenantDb;
22
24
  let registry: Registry;
23
25
  let departmentTable: Table;
@@ -77,6 +79,7 @@ const memberEntity = createEntity({
77
79
  });
78
80
 
79
81
  beforeAll(async () => {
82
+ await ensureTemporalPolyfill();
80
83
  testDb = await createTestDb();
81
84
  await createEventsTable(testDb.db);
82
85
  tdb = createTenantDb(testDb.db, admin.tenantId);
@@ -90,14 +93,14 @@ beforeAll(async () => {
90
93
  await unsafeCreateEntityTable(testDb.db, teamEntity);
91
94
  await unsafeCreateEntityTable(testDb.db, memberEntity);
92
95
 
93
- departmentTable = buildDrizzleTable("department", departmentEntity);
94
- userTable = buildDrizzleTable("user", userEntity);
95
- sessionTable = buildDrizzleTable("session", sessionEntity);
96
- groupTable = buildDrizzleTable("group", groupEntity);
97
- userGroupRestrictTable = buildDrizzleTable("user-group-restrict", userGroupRestrictEntity);
98
- userGroupCascadeTable = buildDrizzleTable("user-group-cascade", userGroupCascadeEntity);
99
- teamTable = buildDrizzleTable("team", teamEntity);
100
- memberTable = buildDrizzleTable("member", memberEntity);
96
+ departmentTable = buildEntityTable("department", departmentEntity);
97
+ userTable = buildEntityTable("user", userEntity);
98
+ sessionTable = buildEntityTable("session", sessionEntity);
99
+ groupTable = buildEntityTable("group", groupEntity);
100
+ userGroupRestrictTable = buildEntityTable("user-group-restrict", userGroupRestrictEntity);
101
+ userGroupCascadeTable = buildEntityTable("user-group-cascade", userGroupCascadeEntity);
102
+ teamTable = buildEntityTable("team", teamEntity);
103
+ memberTable = buildEntityTable("member", memberEntity);
101
104
 
102
105
  const feature = defineFeature("cascade", (r) => {
103
106
  r.entity("department", departmentEntity);
@@ -219,10 +222,10 @@ describe("cascade delete: cascade", () => {
219
222
  const user = await userExecutor.create({ name: "Cascade User" }, admin, tdb);
220
223
  if (!user.isSuccess) throw new Error("Setup failed");
221
224
 
222
- await sessionExecutor.create({ userId: user.data.id, token: "abc" }, admin, tdb);
223
- await sessionExecutor.create({ userId: user.data.id, token: "def" }, admin, tdb);
225
+ const s1 = await sessionExecutor.create({ userId: user.data.id, token: "abc" }, admin, tdb);
226
+ const s2 = await sessionExecutor.create({ userId: user.data.id, token: "def" }, admin, tdb);
227
+ if (!s1.isSuccess || !s2.isSuccess) throw new Error("Setup failed");
224
228
 
225
- // Verify sessions exist
226
229
  const before = await sessionExecutor.list({}, admin, tdb);
227
230
  const sessionsBefore = before.rows.filter((r) => r["userId"] === user.data.id);
228
231
  expect(sessionsBefore.length).toBe(2);
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { createEntity, createRegistry, createTextField, defineFeature } from "../../engine";
3
3
 
4
4
  describe("getIncomingRelations", () => {
@@ -15,11 +15,12 @@
15
15
  // The active propagation into cascaded writes is covered by
16
16
  // msp-multi-hop.integration.ts.
17
17
 
18
- import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
18
+ import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
19
19
  import { z } from "zod";
20
20
  import { requestContext } from "../../api/request-context";
21
21
  import { createEventStoreExecutor } from "../../db/event-store-executor";
22
- import { buildDrizzleTable } from "../../db/table-builder";
22
+ import { selectMany } from "../../db/query";
23
+ import { buildEntityTable } from "../../db/table-builder";
23
24
  import { createEntity, createTextField, defineFeature } from "../../engine";
24
25
  import { eventsTable } from "../../event-store";
25
26
  import {
@@ -39,7 +40,7 @@ const orderEntity = createEntity({
39
40
  },
40
41
  });
41
42
 
42
- const orderTable = buildDrizzleTable("causation-order", orderEntity);
43
+ const orderTable = buildEntityTable("causation-order", orderEntity);
43
44
 
44
45
  // MSP-apply observation sink — every apply run pushes its reqCtx snapshot
45
46
  // here so the tests can assert what the event-dispatcher wrapped it with.
@@ -85,7 +86,7 @@ const causationFeature = defineFeature("causation", (r) => {
85
86
  [placed.name]: async (event) => {
86
87
  const ctx = requestContext.get();
87
88
  applyObservations.push({
88
- forEventId: String(event.id),
89
+ forEventId: String(event["id"]),
89
90
  correlationId: ctx?.correlationId,
90
91
  causationId: ctx?.causationId,
91
92
  });
@@ -118,11 +119,9 @@ afterEach(async () => {
118
119
 
119
120
  // --- Helpers ---
120
121
 
121
- type EventRow = typeof eventsTable.$inferSelect;
122
-
123
- async function eventsByType(type: string): Promise<EventRow[]> {
124
- const rows = await stack.db.select().from(eventsTable);
125
- return rows.filter((r) => r.type === type);
122
+ async function eventsByType(type: string) {
123
+ const rows = await selectMany(stack.db, eventsTable);
124
+ return rows.filter((r: Record<string, unknown>) => r["type"] === type);
126
125
  }
127
126
 
128
127
  // --- Tests ---
@@ -133,7 +132,7 @@ describe("Runde 2 — correlationId on root HTTP request", () => {
133
132
 
134
133
  const [placedEvent] = await eventsByType("causation:event:placed");
135
134
  expect(placedEvent).toBeDefined();
136
- const meta = placedEvent?.metadata as {
135
+ const meta = placedEvent?.["metadata"] as {
137
136
  requestId?: string;
138
137
  correlationId?: string;
139
138
  causationId?: string;
@@ -159,10 +158,10 @@ describe("Runde 2 — correlationId on root HTTP request", () => {
159
158
  const crudEvent = (await eventsByType("causation-order.created"))[0];
160
159
  const placedEvent = (await eventsByType("causation:event:placed"))[0];
161
160
 
162
- expect((crudEvent?.metadata as { correlationId?: string })?.correlationId).toBe(
161
+ expect((crudEvent?.["metadata"] as { correlationId?: string })?.correlationId).toBe(
163
162
  "test-chain-abc123",
164
163
  );
165
- expect((placedEvent?.metadata as { correlationId?: string })?.correlationId).toBe(
164
+ expect((placedEvent?.["metadata"] as { correlationId?: string })?.correlationId).toBe(
166
165
  "test-chain-abc123",
167
166
  );
168
167
  });
@@ -192,7 +191,7 @@ describe("Runde 2 — event-dispatcher propagates correlation + causation to MSP
192
191
  // should have seen as causationId.
193
192
  const [placedEvent] = await eventsByType("causation:event:placed");
194
193
  expect(placedEvent).toBeDefined();
195
- const placedId = String(placedEvent?.id);
194
+ const placedId = String(placedEvent?.["id"]);
196
195
 
197
196
  // Observation recorded inside the MSP apply.
198
197
  expect(applyObservations).toHaveLength(1);
@@ -1,7 +1,8 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { createEventStoreExecutor } from "../../db/event-store-executor";
4
- import { buildDrizzleTable } from "../../db/table-builder";
4
+ import { asRawClient, selectMany } from "../../db/query";
5
+ import { buildEntityTable } from "../../db/table-builder";
5
6
  import {
6
7
  access,
7
8
  createEntity,
@@ -27,7 +28,7 @@ const bagEntity = createEntity({
27
28
  counter: createNumberField({ default: 0 }),
28
29
  },
29
30
  });
30
- const bagTable = buildDrizzleTable("bag", bagEntity);
31
+ const bagTable = buildEntityTable("bag", bagEntity);
31
32
 
32
33
  // secret has a system-only read field — proves queryAs(system) reads it,
33
34
  // plain query doesn't.
@@ -41,7 +42,7 @@ const secretEntity = createEntity({
41
42
  }),
42
43
  },
43
44
  });
44
- const secretTable = buildDrizzleTable("secret", secretEntity);
45
+ const secretTable = buildEntityTable("secret", secretEntity);
45
46
 
46
47
  let stack: TestStack;
47
48
  const admin = TestUsers.admin;
@@ -76,7 +77,7 @@ const bridgeFeature = defineFeature("ctxbridge", (r) => {
76
77
  "secret:by-owner",
77
78
  z.object({ owner: z.string() }),
78
79
  async (query, ctx) => {
79
- const rows = await ctx.db.select().from(secretTable);
80
+ const rows = await selectMany(ctx.db, secretTable);
80
81
  return (
81
82
  (rows as Array<Record<string, unknown>>).find((r) => r["owner"] === query.payload.owner) ??
82
83
  null
@@ -154,8 +155,8 @@ afterAll(async () => {
154
155
 
155
156
  beforeEach(async () => {
156
157
  afterCommitLog.length = 0;
157
- await stack.db.delete(bagTable);
158
- await stack.db.delete(secretTable);
158
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${bagTable.tableName}"`);
159
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${secretTable.tableName}"`);
159
160
  // Clear the event-dedup cache — tests re-use entity ids (Postgres sequences
160
161
  // reset, each test sees id=1). Without flushing Redis the second test hits
161
162
  // a dedup hit on the same handler:id:version:phase key and the hook is
@@ -195,8 +196,8 @@ describe("ctx.writeAs shares the outer transaction", () => {
195
196
  expect(body.isSuccess).toBe(false);
196
197
 
197
198
  // Both tables empty — outer bag + inner secret rolled back together
198
- const bags = await stack.db.select().from(bagTable);
199
- const secrets = await stack.db.select().from(secretTable);
199
+ const bags = await selectMany(stack.db, bagTable);
200
+ const secrets = await selectMany(stack.db, secretTable);
200
201
  expect(bags).toHaveLength(0);
201
202
  expect(secrets).toHaveLength(0);
202
203
  });
@@ -209,8 +210,8 @@ describe("ctx.writeAs shares the outer transaction", () => {
209
210
  );
210
211
  expect((await res.json()).isSuccess).toBe(true);
211
212
 
212
- const bags = await stack.db.select().from(bagTable);
213
- const secrets = await stack.db.select().from(secretTable);
213
+ const bags = await selectMany(stack.db, bagTable);
214
+ const secrets = await selectMany(stack.db, secretTable);
214
215
  expect(bags).toHaveLength(1);
215
216
  expect(secrets).toHaveLength(1);
216
217
 
@@ -0,0 +1,107 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createSystemUser } from "../../engine/system-user";
3
+ import { InternalError } from "../../errors";
4
+ import {
5
+ describeShape,
6
+ dispatcherSpanAttributes,
7
+ extractNestedSpecs,
8
+ isFailedWriteResult,
9
+ isLifecycleResult,
10
+ isWriteResultShape,
11
+ prefixValidationPath,
12
+ resolveType,
13
+ wrapToKumiko,
14
+ } from "../dispatcher-utils";
15
+
16
+ describe("isFailedWriteResult", () => {
17
+ test("narrows failed write results", () => {
18
+ const result = { isSuccess: false as const, error: { code: "validation_error" } };
19
+ expect(isFailedWriteResult(result)).toBe(true);
20
+ expect(isFailedWriteResult({ isSuccess: true, data: {} })).toBe(false);
21
+ });
22
+ });
23
+
24
+ describe("isWriteResultShape / isLifecycleResult", () => {
25
+ test("detects write-result envelope", () => {
26
+ expect(isWriteResultShape({ isSuccess: true, data: 1 })).toBe(true);
27
+ expect(isWriteResultShape({ kind: "created" })).toBe(false);
28
+ });
29
+
30
+ test("detects lifecycle results", () => {
31
+ expect(isLifecycleResult({ kind: "deleted" })).toBe(true);
32
+ expect(isLifecycleResult(null)).toBe(false);
33
+ });
34
+ });
35
+
36
+ describe("describeShape", () => {
37
+ test("summarizes unknown values", () => {
38
+ expect(describeShape(null)).toBe("null");
39
+ expect(describeShape("x")).toBe("string");
40
+ expect(describeShape({ a: 1, b: 2 })).toContain("object with keys");
41
+ });
42
+ });
43
+
44
+ describe("dispatcherSpanAttributes", () => {
45
+ test("includes handler, operation, user, tenant, optional feature", () => {
46
+ const user = createSystemUser("tenant-1");
47
+ const attrs = dispatcherSpanAttributes("feat:query:task:list", "query", user, "feat");
48
+ expect(attrs).toMatchObject({
49
+ "kumiko.handler": "feat:query:task:list",
50
+ "kumiko.operation": "query",
51
+ "kumiko.feature": "feat",
52
+ });
53
+ });
54
+ });
55
+
56
+ describe("prefixValidationPath", () => {
57
+ test("prefixes validation field paths", () => {
58
+ const info = {
59
+ code: "validation_error",
60
+ httpStatus: 400,
61
+ i18nKey: "errors.validation.failed",
62
+ message: "Validation failed",
63
+ details: {
64
+ fields: [{ path: "title", code: "too_small", i18nKey: "errors.validation.too_small" }],
65
+ },
66
+ };
67
+ const prefixed = prefixValidationPath(info, "tasks.0");
68
+ const fields = (prefixed.details as { fields: { path: string }[] }).fields;
69
+ expect(fields[0]?.path).toBe("tasks.0.title");
70
+ });
71
+
72
+ test("leaves non-validation errors unchanged", () => {
73
+ const info = {
74
+ code: "not_found",
75
+ httpStatus: 404,
76
+ i18nKey: "errors.notFound",
77
+ message: "missing",
78
+ };
79
+ expect(prefixValidationPath(info, "x")).toBe(info);
80
+ });
81
+ });
82
+
83
+ describe("resolveType", () => {
84
+ test("unwraps HandlerRef objects", () => {
85
+ expect(resolveType({ name: "feat:write:task:create" })).toBe("feat:write:task:create");
86
+ expect(resolveType("feat:query:task:list")).toBe("feat:query:task:list");
87
+ });
88
+ });
89
+
90
+ describe("wrapToKumiko", () => {
91
+ test("passes through KumikoError instances", () => {
92
+ const err = new InternalError();
93
+ expect(wrapToKumiko(err)).toBe(err);
94
+ });
95
+
96
+ test("wraps generic Error as InternalError", () => {
97
+ const wrapped = wrapToKumiko(new TypeError("boom"));
98
+ expect(wrapped.code).toBe("internal_error");
99
+ expect(wrapped.cause).toBeInstanceOf(TypeError);
100
+ });
101
+ });
102
+
103
+ describe("extractNestedSpecs", () => {
104
+ test("returns null for non-create handlers", () => {
105
+ expect(extractNestedSpecs("feat:write:task:update", { tasks: [] }, {} as never)).toBeNull();
106
+ });
107
+ });
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { createEntity, createRegistry, createTextField, defineFeature } from "../../engine";
4
4
  import type { TenantId } from "../../engine/types/identifiers";
@@ -97,7 +97,7 @@ describe("dispatcher.write", () => {
97
97
  const res = await dispatcher.write("alias:write:item:create", { name: "x" }, user);
98
98
  expect(res.isSuccess).toBe(true);
99
99
  expect(captured.fromCtx).toBe(captured.fromEvent);
100
- expect((captured.fromCtx as { id: number }).id).toBe(user.id);
100
+ expect((captured.fromCtx as { id: unknown }).id).toBe(user.id);
101
101
  });
102
102
 
103
103
  test("runs validation hooks", async () => {
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
2
  import { createTestRedis, type TestRedis } from "../../stack";
3
3
  import { createDistributedLock } from "../distributed-lock";
4
4
 
@@ -9,9 +9,7 @@
9
9
  // Without all three pieces wired together (registry opens defineEvent names,
10
10
  // projections-runner fires on appendEvent, dispatcher routes appendEvent to
11
11
  // the aggregate stream), any of the assertions below go red.
12
-
13
- import { eq } from "drizzle-orm";
14
- import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
12
+ import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
15
13
  import { z } from "zod";
16
14
  import {
17
15
  integer as pgInteger,
@@ -20,7 +18,8 @@ import {
20
18
  uuid as pgUuid,
21
19
  } from "../../db/dialect";
22
20
  import { createEventStoreExecutor } from "../../db/event-store-executor";
23
- import { buildDrizzleTable } from "../../db/table-builder";
21
+ import { insertOne, selectMany, updateMany } from "../../db/query";
22
+ import { buildEntityTable } from "../../db/table-builder";
24
23
  import { createEntity, createTextField, defineFeature } from "../../engine";
25
24
  import { loadAggregate } from "../../event-store";
26
25
  import {
@@ -41,7 +40,7 @@ const shipmentEntity = createEntity({
41
40
  },
42
41
  });
43
42
 
44
- const shipmentTable = buildDrizzleTable("domain-shipment", shipmentEntity);
43
+ const shipmentTable = buildEntityTable("domain-shipment", shipmentEntity);
45
44
 
46
45
  // --- Read-model table (fed by the projection below) ---
47
46
 
@@ -69,7 +68,7 @@ const shippingFeature = defineFeature("shipping", (r) => {
69
68
  // Auto CRUD event — fires on shipment create.
70
69
  "domain-shipment.created": async (event, tx) => {
71
70
  const payload = event.payload as { cargo?: string };
72
- await tx.insert(billingTable).values({
71
+ await insertOne(tx, billingTable, {
73
72
  shipmentId: event.aggregateId,
74
73
  tenantId: event.tenantId,
75
74
  cargo: payload.cargo ?? "",
@@ -82,10 +81,12 @@ const shippingFeature = defineFeature("shipping", (r) => {
82
81
  // create-apply just inserted.
83
82
  [shipmentBilled.name]: async (event, tx) => {
84
83
  const payload = event.payload as { cost: number };
85
- await tx
86
- .update(billingTable)
87
- .set({ totalCost: payload.cost, billedMarker: "billed" })
88
- .where(eq(billingTable.shipmentId, event.aggregateId));
84
+ await updateMany(
85
+ tx,
86
+ billingTable,
87
+ { totalCost: payload.cost, billedMarker: "billed" },
88
+ { shipmentId: event.aggregateId },
89
+ );
89
90
  },
90
91
  },
91
92
  });
@@ -196,7 +197,7 @@ describe("Marten gold-standard: domain events → inline projections", () => {
196
197
  admin,
197
198
  );
198
199
 
199
- const rows = await stack.db.select().from(billingTable);
200
+ const rows = await selectMany(stack.db, billingTable);
200
201
  expect(rows).toHaveLength(1);
201
202
  expect(rows[0]?.shipmentId).toBe(data.id);
202
203
  expect(rows[0]?.billedMarker).toBe("pending");
@@ -212,10 +213,7 @@ describe("Marten gold-standard: domain events → inline projections", () => {
212
213
 
213
214
  await stack.http.writeOk("shipping:write:shipment:bill", { id: created.id, cost: 1500 }, admin);
214
215
 
215
- const [row] = await stack.db
216
- .select()
217
- .from(billingTable)
218
- .where(eq(billingTable.shipmentId, created.id));
216
+ const [row] = await selectMany(stack.db, billingTable, { shipmentId: created.id });
219
217
  expect(row).toBeDefined();
220
218
  expect(row?.billedMarker).toBe("billed");
221
219
  expect(row?.totalCost).toBe(1500);
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { createEventStoreExecutor } from "../../db/event-store-executor";
4
4
  import { defineFeature, type SaveContext } from "../../engine";
@@ -10,10 +10,9 @@
10
10
  // it hits the events-table. Mismatches throw ValidationError.
11
11
  // 3. r.defineEvent returns `{ name: qualifiedName, schema }` — callers
12
12
  // pass `def.name` to ctx.appendEvent without building the qn manually.
13
-
14
- import { eq } from "drizzle-orm";
15
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
13
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
16
14
  import { z } from "zod";
15
+ import { selectMany } from "../../db/query";
17
16
  import { defineFeature } from "../../engine";
18
17
  import { eventsTable } from "../../event-store";
19
18
  import {
@@ -146,10 +145,7 @@ describe("E.3 — ctx.appendEvent strict validation", () => {
146
145
  );
147
146
  expect(res.status).toBe(200);
148
147
 
149
- const stored = await stack.db
150
- .select()
151
- .from(eventsTable)
152
- .where(eq(eventsTable.type, welcomeEventName));
148
+ const stored = await selectMany(stack.db, eventsTable, { type: welcomeEventName });
153
149
  expect(stored).toHaveLength(1);
154
150
  expect(stored[0]?.payload).toEqual({ userId, email: "welcome@test.de" });
155
151
  expect(stored[0]?.aggregateType).toBe("user");
@@ -164,7 +160,7 @@ describe("E.3 — ctx.appendEvent strict validation", () => {
164
160
  // Non-Kumiko throw inside a handler → wrapped as InternalError → 500.
165
161
  expect(res.status).toBe(500);
166
162
 
167
- const stored = await stack.db.select().from(eventsTable);
163
+ const stored = await selectMany(stack.db, eventsTable);
168
164
  // TX rolled back: no event landed.
169
165
  expect(stored).toHaveLength(0);
170
166
  });
@@ -175,10 +171,7 @@ describe("E.3 — ctx.appendEvent strict validation", () => {
175
171
  // ValidationError is a first-class Kumiko error → 400.
176
172
  expect(res.status).toBe(400);
177
173
 
178
- const stored = await stack.db
179
- .select()
180
- .from(eventsTable)
181
- .where(eq(eventsTable.type, welcomeEventName));
174
+ const stored = await selectMany(stack.db, eventsTable, { type: welcomeEventName });
182
175
  expect(stored).toHaveLength(0);
183
176
  });
184
177
  });
@@ -193,10 +186,7 @@ describe("E.3 — cross-feature ownership guard", () => {
193
186
  const res = await stack.http.write("emitter:write:emit:foreign-event", { userId }, admin);
194
187
  expect(res.status).toBe(500);
195
188
 
196
- const stored = await stack.db
197
- .select()
198
- .from(eventsTable)
199
- .where(eq(eventsTable.type, foreignEventName));
189
+ const stored = await selectMany(stack.db, eventsTable, { type: foreignEventName });
200
190
  expect(stored).toHaveLength(0);
201
191
  });
202
192
  });
@@ -12,7 +12,7 @@
12
12
  // via payload, not via a wrapped DB handle. Tenant-isolation-via-MSP is
13
13
  // tested in multi-stream-projection.integration.ts.
14
14
 
15
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
15
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
16
16
  import { createEventStoreExecutor } from "../../db/event-store-executor";
17
17
  import { createTenantDb, type TenantDb } from "../../db/tenant-db";
18
18
  import { defineFeature } from "../../engine";
@@ -18,8 +18,9 @@
18
18
  // single-instance runs. Any regression in the locking strategy shows up
19
19
  // here first.
20
20
 
21
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
21
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
22
22
  import { createEventStoreExecutor } from "../../db/event-store-executor";
23
+ import { insertMany } from "../../db/query";
23
24
  import { createTenantDb, type TenantDb } from "../../db/tenant-db";
24
25
  import { defineFeature } from "../../engine";
25
26
  import { eventsTable, type StoredEvent } from "../../event-store";
@@ -91,7 +92,7 @@ async function bulkSeedWidgetCreated(count: number, namePrefix: string): Promise
91
92
  metadata: { userId: admin.id },
92
93
  createdBy: admin.id,
93
94
  }));
94
- await stack.db.insert(eventsTable).values(rows);
95
+ await insertMany(stack.db, eventsTable, rows);
95
96
  }
96
97
 
97
98
  function buildDispatcherWith(consumers: readonly EventConsumer[]): EventDispatcher {
@@ -11,7 +11,7 @@
11
11
  // 2. The dispatcher starts cleanly when pgClient is wired and stops
12
12
  // without leaking the LISTEN connection.
13
13
 
14
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
14
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
15
15
  import { createEventStoreExecutor } from "../../db/event-store-executor";
16
16
  import { createTenantDb, type TenantDb } from "../../db/tenant-db";
17
17
  import { defineFeature } from "../../engine";
@@ -14,7 +14,7 @@
14
14
  // will never succeed (broken payload, removed code).
15
15
  // (list/status are read-only; covered by event-dispatcher wiring tests.)
16
16
 
17
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
17
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
18
18
  import { createEventStoreExecutor } from "../../db/event-store-executor";
19
19
  import { createTenantDb, type TenantDb } from "../../db/tenant-db";
20
20
  import { defineFeature } from "../../engine";
@@ -110,7 +110,7 @@ describe("E.9 — restartConsumer", () => {
110
110
  expect(after.attempts).toBe(0);
111
111
  expect(after.lastError).toBeNull();
112
112
  // Cursor unchanged — next pass retries the SAME failing event.
113
- expect(after.lastProcessedEventId).toBe(cursorBefore);
113
+ expect(after.lastProcessedEventId).toBe(cursorBefore!);
114
114
 
115
115
  // Retry still poisoned (handler still throws) — attempts climbs again.
116
116
  await stack.eventDispatcher?.runOnce();
@@ -14,8 +14,8 @@
14
14
  // moment delivery latency regresses from TCP-round-trip to
15
15
  // pollIntervalMs.
16
16
 
17
- import { eq, sql } from "drizzle-orm";
18
- import { afterEach, beforeAll, describe, expect, test } from "vitest";
17
+ import { afterEach, beforeAll, describe, expect, test } from "bun:test";
18
+ import { asRawClient, insertOne, selectMany, updateMany } from "../../db/query";
19
19
  import { defineFeature } from "../../engine";
20
20
  import { eventsTable } from "../../event-store";
21
21
  import {
@@ -70,7 +70,7 @@ afterEach(async () => {
70
70
  });
71
71
 
72
72
  async function seedOldWidgetEvent(createdAt: Temporal.Instant): Promise<void> {
73
- await stack.db.insert(eventsTable).values({
73
+ await insertOne(stack.db, eventsTable, {
74
74
  aggregateId: generateId(),
75
75
  aggregateType: "widget",
76
76
  tenantId: admin.tenantId,
@@ -94,7 +94,7 @@ describe("Second audit — consumer pre-registration on start()", () => {
94
94
  // every consumer at cursor=0, refuses to prune past them.
95
95
  await stack.eventDispatcher?.start();
96
96
  try {
97
- const rows = await stack.db.select().from(eventConsumerStateTable);
97
+ const rows = await selectMany(stack.db, eventConsumerStateTable);
98
98
  const names = new Set(rows.map((r) => r.name));
99
99
 
100
100
  // The test-stack wires only feature MSP consumers (systemHooks: []),
@@ -138,17 +138,18 @@ describe("Second audit — consumer pre-registration on start()", () => {
138
138
 
139
139
  // Advance the cursor explicitly so we can prove the second start
140
140
  // doesn't clobber it back to 0.
141
- await stack.db
142
- .update(eventConsumerStateTable)
143
- .set({ lastProcessedEventId: 42n })
144
- .where(eq(eventConsumerStateTable.name, "audit:projection:default-scope"));
141
+ await updateMany(
142
+ stack.db,
143
+ eventConsumerStateTable,
144
+ { lastProcessedEventId: 42n },
145
+ { name: "audit:projection:default-scope" },
146
+ );
145
147
 
146
148
  await stack.eventDispatcher?.start();
147
149
  try {
148
- const [row] = await stack.db
149
- .select()
150
- .from(eventConsumerStateTable)
151
- .where(eq(eventConsumerStateTable.name, "audit:projection:default-scope"));
150
+ const [row] = await selectMany(stack.db, eventConsumerStateTable, {
151
+ name: "audit:projection:default-scope",
152
+ });
152
153
  expect(row?.lastProcessedEventId).toBe(42n);
153
154
  } finally {
154
155
  await stack.eventDispatcher?.stop();
@@ -255,13 +256,13 @@ describe("Second audit — LISTEN gauge", () => {
255
256
  // `LISTEN "kumiko_events_new"` and is now idle. pg_terminate_backend
256
257
  // on it closes the TCP; postgres.js's onclose handler re-subscribes
257
258
  // and fires onlisten again.
258
- await recStack.db.execute(
259
- sql`SELECT pg_terminate_backend(pid) FROM pg_stat_activity
259
+ await asRawClient(
260
+ recStack.db,
261
+ ).unsafe(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity
260
262
  WHERE datname = current_database()
261
263
  AND query ILIKE 'listen%'
262
264
  AND state = 'idle'
263
- AND pid <> pg_backend_pid()`,
264
- );
265
+ AND pid <> pg_backend_pid()`);
265
266
 
266
267
  // Wait for the SECOND gauge.set(1) — the reconnect. Generous timeout
267
268
  // because postgres.js's reconnect loop includes backoff.