@cosmicdrift/kumiko-framework 0.13.0 → 0.15.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 (314) hide show
  1. package/package.json +7 -7
  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/readiness.ts +2 -2
  24. package/src/auth/__tests__/roles.test.ts +2 -2
  25. package/src/bun-db/__tests__/PATTERN.md +73 -0
  26. package/src/bun-db/__tests__/_helpers.ts +103 -0
  27. package/src/bun-db/__tests__/batch-methods.integration.test.ts +143 -0
  28. package/src/bun-db/__tests__/batch-methods.test.ts +20 -0
  29. package/src/bun-db/__tests__/bun-test-db.ts +19 -0
  30. package/src/bun-db/__tests__/bun-test-stack.ts +6 -0
  31. package/src/bun-db/__tests__/column-types.integration.test.ts +132 -0
  32. package/src/bun-db/__tests__/compound-types.integration.test.ts +134 -0
  33. package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +235 -0
  34. package/src/bun-db/__tests__/smoke.integration.test.ts +43 -0
  35. package/src/bun-db/__tests__/sql-methods.integration.test.ts +231 -0
  36. package/src/bun-db/__tests__/where-patterns.integration.test.ts +185 -0
  37. package/src/bun-db/connection.ts +84 -0
  38. package/src/bun-db/index.ts +31 -0
  39. package/src/bun-db/query.ts +845 -0
  40. package/src/compliance/__tests__/duration-spec.test.ts +1 -1
  41. package/src/compliance/__tests__/profiles.test.ts +1 -1
  42. package/src/compliance/__tests__/sub-processors.test.ts +1 -1
  43. package/src/db/__tests__/{apply-entity-event-tenant.integration.ts → apply-entity-event-tenant.integration.test.ts} +13 -11
  44. package/src/db/__tests__/big-int-field.test.ts +15 -14
  45. package/src/db/__tests__/column-ddl.integration.test.ts +113 -0
  46. package/src/db/__tests__/compound-types.test.ts +1 -1
  47. package/src/db/__tests__/{config-seed.integration.ts → config-seed.integration.test.ts} +32 -27
  48. package/src/db/__tests__/connection-options.test.ts +1 -1
  49. package/src/db/__tests__/dialect-instant.test.ts +1 -1
  50. package/src/db/__tests__/encryption.test.ts +1 -1
  51. package/src/db/__tests__/{drizzle-table-types.test.ts → entity-table-types.test.ts} +16 -16
  52. package/src/db/__tests__/{event-store-executor-list.integration.ts → event-store-executor-list.integration.test.ts} +12 -7
  53. package/src/db/__tests__/{event-store-executor.integration.ts → event-store-executor.integration.test.ts} +19 -12
  54. package/src/db/__tests__/{implicit-projection-equivalence.integration.ts → implicit-projection-equivalence.integration.test.ts} +35 -29
  55. package/src/db/__tests__/located-timestamp.test.ts +1 -1
  56. package/src/db/__tests__/money.test.ts +1 -1
  57. package/src/db/__tests__/{multi-row-insert.integration.ts → multi-row-insert.integration.test.ts} +18 -11
  58. package/src/db/__tests__/parse-auto-verb.test.ts +1 -1
  59. package/src/db/__tests__/{required-not-null-migration-safety.integration.ts → required-not-null-migration-safety.integration.test.ts} +28 -24
  60. package/src/db/__tests__/{schema-migration.integration.ts → schema-migration.integration.test.ts} +32 -28
  61. package/src/db/__tests__/sql-inventory.test.ts +56 -0
  62. package/src/db/__tests__/table-builder-indexes.test.ts +30 -11
  63. package/src/db/__tests__/table-builder-required.test.ts +20 -22
  64. package/src/db/__tests__/{tenant-db.integration.ts → tenant-db.integration.test.ts} +106 -144
  65. package/src/db/__tests__/{unique-violation-mapping.integration.ts → unique-violation-mapping.integration.test.ts} +13 -8
  66. package/src/db/api.ts +46 -0
  67. package/src/db/apply-entity-event.ts +45 -36
  68. package/src/db/assert-exists-in.ts +5 -16
  69. package/src/db/bun-provider.ts +37 -0
  70. package/src/db/config-seed.ts +4 -4
  71. package/src/db/connection.ts +14 -57
  72. package/src/db/cursor.ts +5 -56
  73. package/src/db/dialect.ts +472 -99
  74. package/src/db/eagerload.ts +5 -12
  75. package/src/db/entity-table-meta.ts +390 -0
  76. package/src/db/event-store-executor.ts +158 -100
  77. package/src/db/index.ts +33 -5
  78. package/src/db/migrate-generator.ts +350 -0
  79. package/src/db/migrate-runner.ts +206 -0
  80. package/src/db/postgres-provider.ts +25 -0
  81. package/src/db/queries/entity-read.ts +15 -0
  82. package/src/db/queries/es-ops.ts +17 -0
  83. package/src/db/queries/event-consumer.ts +170 -0
  84. package/src/db/queries/event-store-admin.ts +127 -0
  85. package/src/db/queries/event-store.ts +155 -0
  86. package/src/db/queries/projection-rebuild.ts +59 -0
  87. package/src/db/queries/raw-sql.ts +15 -0
  88. package/src/db/queries/schema-drift.ts +35 -0
  89. package/src/db/queries/seed-context.ts +58 -0
  90. package/src/db/queries/table-ops.ts +11 -0
  91. package/src/db/queries/test-stack.ts +56 -0
  92. package/src/db/query-api.ts +22 -0
  93. package/src/db/query.ts +30 -0
  94. package/src/db/reference-data.ts +19 -22
  95. package/src/db/render-ddl.ts +57 -0
  96. package/src/db/row-helpers.ts +3 -52
  97. package/src/db/schema-inspection.ts +17 -4
  98. package/src/db/sql-inventory.ts +208 -0
  99. package/src/db/table-builder.ts +48 -40
  100. package/src/db/tenant-db.ts +105 -326
  101. package/src/engine/__tests__/auth-claims-registrar.test.ts +1 -1
  102. package/src/engine/__tests__/boot-validator-api-exposure.test.ts +3 -3
  103. package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +1 -1
  104. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +5 -5
  105. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +3 -3
  106. package/src/engine/__tests__/boot-validator.test.ts +4 -3
  107. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  108. package/src/engine/__tests__/build-target.test.ts +1 -1
  109. package/src/engine/__tests__/claim-keys.test.ts +1 -1
  110. package/src/engine/__tests__/codemod-pipeline.test.ts +3 -3
  111. package/src/engine/__tests__/config-helpers.test.ts +1 -1
  112. package/src/engine/__tests__/effective-features.test.ts +1 -1
  113. package/src/engine/__tests__/engine.test.ts +1 -1
  114. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  115. package/src/engine/__tests__/event-helpers.test.ts +3 -3
  116. package/src/engine/__tests__/extends-registrar.test.ts +4 -4
  117. package/src/engine/__tests__/factories-long-text.test.ts +1 -1
  118. package/src/engine/__tests__/factories-time.test.ts +1 -1
  119. package/src/engine/__tests__/field-predicates.test.ts +1 -1
  120. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  121. package/src/engine/__tests__/identifiers.test.ts +1 -1
  122. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  123. package/src/engine/__tests__/nav.test.ts +1 -1
  124. package/src/engine/__tests__/ownership.test.ts +10 -11
  125. package/src/engine/__tests__/parse-ref-target.test.ts +1 -1
  126. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  127. package/src/engine/__tests__/{pipeline-handler.integration.ts → pipeline-handler.integration.test.ts} +38 -52
  128. package/src/engine/__tests__/{pipeline-observability.integration.ts → pipeline-observability.integration.test.ts} +1 -1
  129. package/src/engine/__tests__/{pipeline-performance.integration.ts → pipeline-performance.integration.test.ts} +1 -1
  130. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  131. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  132. package/src/engine/__tests__/projection-helpers.test.ts +25 -17
  133. package/src/engine/__tests__/projection.test.ts +4 -4
  134. package/src/engine/__tests__/qualified-name.test.ts +1 -1
  135. package/src/engine/__tests__/raw-table.test.ts +9 -8
  136. package/src/engine/__tests__/resolve-config-or-param.test.ts +5 -5
  137. package/src/engine/__tests__/run-in.test.ts +1 -1
  138. package/src/engine/__tests__/schema-builder.test.ts +1 -1
  139. package/src/engine/__tests__/screen.test.ts +1 -1
  140. package/src/engine/__tests__/search-payload-extension.test.ts +3 -3
  141. package/src/engine/__tests__/state-machine.test.ts +1 -1
  142. package/src/engine/__tests__/steps-aggregate-append-event.test.ts +7 -7
  143. package/src/engine/__tests__/steps-aggregate-create.test.ts +4 -4
  144. package/src/engine/__tests__/steps-aggregate-update.test.ts +3 -3
  145. package/src/engine/__tests__/steps-call-feature.test.ts +5 -5
  146. package/src/engine/__tests__/steps-mail-send.test.ts +7 -7
  147. package/src/engine/__tests__/steps-read.test.ts +34 -40
  148. package/src/engine/__tests__/steps-resolver-utils.test.ts +6 -6
  149. package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +24 -19
  150. package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +28 -17
  151. package/src/engine/__tests__/steps-webhook-send.test.ts +6 -6
  152. package/src/engine/__tests__/steps-workflow.test.ts +7 -7
  153. package/src/engine/__tests__/system-user.test.ts +1 -1
  154. package/src/engine/__tests__/validate-projection-allowlist.test.ts +4 -5
  155. package/src/engine/__tests__/validation-hooks.test.ts +1 -1
  156. package/src/engine/__tests__/visual-tree-patterns.test.ts +1 -1
  157. package/src/engine/boot-validator/entity-handler.ts +3 -3
  158. package/src/engine/boot-validator/ownership.ts +1 -1
  159. package/src/engine/define-feature.ts +1 -2
  160. package/src/engine/entity-handlers.ts +5 -5
  161. package/src/engine/factories.ts +1 -1
  162. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +1 -1
  163. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -1
  164. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +2 -2
  165. package/src/engine/feature-ast/__tests__/parse.test.ts +1 -1
  166. package/src/engine/feature-ast/__tests__/patch.test.ts +1 -1
  167. package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
  168. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +1 -1
  169. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +1 -1
  170. package/src/engine/ownership.ts +113 -41
  171. package/src/engine/pattern-library/__tests__/library.test.ts +2 -2
  172. package/src/engine/projection-helpers.ts +2 -11
  173. package/src/engine/registry.ts +2 -2
  174. package/src/engine/steps/read-find-many.ts +13 -13
  175. package/src/engine/steps/read-find-one.ts +7 -9
  176. package/src/engine/steps/unsafe-projection-delete.ts +4 -5
  177. package/src/engine/steps/unsafe-projection-upsert.ts +63 -31
  178. package/src/engine/types/feature.ts +7 -2
  179. package/src/engine/types/fields.ts +4 -5
  180. package/src/engine/types/step.ts +10 -10
  181. package/src/engine/validate-projection-allowlist.ts +23 -3
  182. package/src/entrypoint/__tests__/{entrypoint-job-wiring.integration.ts → entrypoint-job-wiring.integration.test.ts} +4 -3
  183. package/src/entrypoint/__tests__/{split-deploy.integration.ts → split-deploy.integration.test.ts} +4 -3
  184. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  185. package/src/env/__tests__/dry-run.test.ts +1 -1
  186. package/src/errors/__tests__/classes.test.ts +1 -1
  187. package/src/errors/__tests__/write-failures.test.ts +1 -1
  188. package/src/es-ops/__tests__/{context.integration.ts → context.integration.test.ts} +43 -29
  189. package/src/es-ops/__tests__/{runner.integration.ts → runner.integration.test.ts} +25 -23
  190. package/src/es-ops/__tests__/runner.test.ts +29 -19
  191. package/src/es-ops/context.ts +9 -43
  192. package/src/es-ops/operations-schema.ts +2 -2
  193. package/src/es-ops/runner.ts +12 -26
  194. package/src/event-store/__tests__/{admin-api.integration.ts → admin-api.integration.test.ts} +71 -45
  195. package/src/event-store/__tests__/{event-store.integration.ts → event-store.integration.test.ts} +7 -5
  196. package/src/event-store/__tests__/{get-stream-version-perf.integration.ts → get-stream-version-perf.integration.test.ts} +5 -3
  197. package/src/event-store/__tests__/{perf.integration.ts → perf.integration.test.ts} +24 -16
  198. package/src/event-store/__tests__/{snapshot.integration.ts → snapshot.integration.test.ts} +34 -28
  199. package/src/event-store/__tests__/{upcaster-dead-letter.integration.ts → upcaster-dead-letter.integration.test.ts} +11 -12
  200. package/src/event-store/__tests__/{upcaster.integration.ts → upcaster.integration.test.ts} +19 -32
  201. package/src/event-store/admin-api.ts +55 -83
  202. package/src/event-store/archive.ts +15 -39
  203. package/src/event-store/event-store.ts +92 -86
  204. package/src/event-store/events-schema.ts +2 -1
  205. package/src/event-store/index.ts +1 -0
  206. package/src/event-store/snapshot.ts +26 -24
  207. package/src/event-store/upcaster-dead-letter.ts +19 -18
  208. package/src/files/__tests__/content-disposition.test.ts +1 -1
  209. package/src/files/__tests__/{file-field-pipeline.integration.ts → file-field-pipeline.integration.test.ts} +8 -5
  210. package/src/files/__tests__/file-handle.test.ts +1 -1
  211. package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +32 -17
  212. package/src/files/__tests__/read-stream.test.ts +1 -1
  213. package/src/files/__tests__/{storage-tracking.integration.ts → storage-tracking.integration.test.ts} +26 -30
  214. package/src/files/__tests__/write-stream.test.ts +1 -1
  215. package/src/files/__tests__/zip-stream.test.ts +1 -1
  216. package/src/files/file-ref-table.ts +2 -2
  217. package/src/files/file-routes.ts +7 -9
  218. package/src/files/storage-tracking.ts +9 -17
  219. package/src/i18n/__tests__/i18n.test.ts +1 -1
  220. package/src/jobs/__tests__/{job-event-trigger.integration.ts → job-event-trigger.integration.test.ts} +6 -3
  221. package/src/jobs/__tests__/{job-multi-trigger.integration.ts → job-multi-trigger.integration.test.ts} +6 -3
  222. package/src/jobs/__tests__/{jobs.integration.ts → jobs.integration.test.ts} +5 -7
  223. package/src/lifecycle/__tests__/{lifecycle-server.integration.ts → lifecycle-server.integration.test.ts} +1 -1
  224. package/src/lifecycle/__tests__/lifecycle.test.ts +6 -6
  225. package/src/lifecycle/__tests__/signal-handlers.test.ts +6 -6
  226. package/src/logging/__tests__/pino-trace-bridge.test.ts +1 -1
  227. package/src/migrations/__tests__/compare-snapshots.test.ts +1 -1
  228. package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts} +34 -26
  229. package/src/migrations/__tests__/{detect-projections-to-rebuild.integration.ts → detect-projections-to-rebuild.integration.test.ts} +1 -1
  230. package/src/migrations/__tests__/rebuild-marker.test.ts +1 -1
  231. package/src/migrations/projection-detection.ts +12 -1
  232. package/src/migrations/schema-drift.ts +7 -23
  233. package/src/observability/__tests__/console-provider.test.ts +1 -1
  234. package/src/observability/__tests__/metric-validator.test.ts +1 -1
  235. package/src/observability/__tests__/noop-provider.test.ts +1 -1
  236. package/src/observability/__tests__/{observability.integration.ts → observability.integration.test.ts} +5 -8
  237. package/src/observability/__tests__/prometheus-meter.test.ts +1 -1
  238. package/src/observability/__tests__/recording-meter.test.ts +1 -1
  239. package/src/observability/__tests__/recording-tracer.test.ts +1 -1
  240. package/src/observability/__tests__/sensitive-filter.test.ts +1 -1
  241. package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts} +3 -3
  242. package/src/pipeline/__tests__/auth-claims-resolver.test.ts +9 -9
  243. package/src/pipeline/__tests__/{cascade-handler.integration.ts → cascade-handler.integration.test.ts} +18 -15
  244. package/src/pipeline/__tests__/cascade-handler.test.ts +1 -1
  245. package/src/pipeline/__tests__/{causation-chain.integration.ts → causation-chain.integration.test.ts} +12 -13
  246. package/src/pipeline/__tests__/{ctx-bridge.integration.ts → ctx-bridge.integration.test.ts} +12 -11
  247. package/src/pipeline/__tests__/dispatcher.test.ts +2 -2
  248. package/src/pipeline/__tests__/{distributed-lock.integration.ts → distributed-lock.integration.test.ts} +1 -1
  249. package/src/pipeline/__tests__/{domain-events-projections.integration.ts → domain-events-projections.integration.test.ts} +13 -15
  250. package/src/pipeline/__tests__/{event-dedup.integration.ts → event-dedup.integration.test.ts} +1 -1
  251. package/src/pipeline/__tests__/{event-define-event-strict.integration.ts → event-define-event-strict.integration.test.ts} +6 -16
  252. package/src/pipeline/__tests__/{event-dispatcher-lifecycle.integration.ts → event-dispatcher-lifecycle.integration.test.ts} +1 -1
  253. package/src/pipeline/__tests__/{event-dispatcher-multi-instance.integration.ts → event-dispatcher-multi-instance.integration.test.ts} +3 -2
  254. package/src/pipeline/__tests__/{event-dispatcher-pg-listen.integration.ts → event-dispatcher-pg-listen.integration.test.ts} +1 -1
  255. package/src/pipeline/__tests__/{event-dispatcher-recovery.integration.ts → event-dispatcher-recovery.integration.test.ts} +2 -2
  256. package/src/pipeline/__tests__/{event-dispatcher-second-audit.integration.ts → event-dispatcher-second-audit.integration.test.ts} +17 -16
  257. package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +14 -12
  258. package/src/pipeline/__tests__/{event-dispatcher.integration.ts → event-dispatcher.integration.test.ts} +8 -15
  259. package/src/pipeline/__tests__/{event-retention.integration.ts → event-retention.integration.test.ts} +28 -25
  260. package/src/pipeline/__tests__/{fetch-for-writing.integration.ts → fetch-for-writing.integration.test.ts} +6 -6
  261. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +4 -4
  262. package/src/pipeline/__tests__/{load-aggregate-query.integration.ts → load-aggregate-query.integration.test.ts} +9 -5
  263. package/src/pipeline/__tests__/{msp-error-mode.integration.ts → msp-error-mode.integration.test.ts} +1 -1
  264. package/src/pipeline/__tests__/{msp-multi-hop.integration.ts → msp-multi-hop.integration.test.ts} +9 -8
  265. package/src/pipeline/__tests__/{msp-rebuild.integration.ts → msp-rebuild.integration.test.ts} +47 -55
  266. package/src/pipeline/__tests__/{multi-stream-projection.integration.ts → multi-stream-projection.integration.test.ts} +19 -53
  267. package/src/pipeline/__tests__/{perf-rebuild.integration.ts → perf-rebuild.integration.test.ts} +36 -34
  268. package/src/pipeline/__tests__/{post-query-hook.integration.ts → post-query-hook.integration.test.ts} +1 -1
  269. package/src/pipeline/__tests__/{projection-rebuild.integration.ts → projection-rebuild.integration.test.ts} +21 -30
  270. package/src/pipeline/__tests__/{query-projection.integration.ts → query-projection.integration.test.ts} +6 -5
  271. package/src/pipeline/__tests__/{redis-pipeline.integration.ts → redis-pipeline.integration.test.ts} +3 -1
  272. package/src/pipeline/cascade-handler.ts +13 -21
  273. package/src/pipeline/dispatcher.ts +43 -48
  274. package/src/pipeline/event-consumer-state.ts +11 -2
  275. package/src/pipeline/event-dispatcher.ts +86 -146
  276. package/src/pipeline/event-retention.ts +14 -24
  277. package/src/pipeline/msp-rebuild.ts +54 -78
  278. package/src/pipeline/projection-rebuild.ts +65 -67
  279. package/src/pipeline/projection-state.ts +2 -2
  280. package/src/random/__tests__/generate.test.ts +13 -13
  281. package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts} +1 -1
  282. package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts} +1 -1
  283. package/src/rate-limit/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +1 -1
  284. package/src/redis/__tests__/redis-options.test.ts +1 -1
  285. package/src/search/__tests__/{meilisearch-adapter.integration.ts → meilisearch-adapter.integration.test.ts} +1 -1
  286. package/src/search/__tests__/search-adapter.test.ts +1 -1
  287. package/src/secrets/__tests__/dek-cache.test.ts +1 -3
  288. package/src/secrets/__tests__/env-master-key-provider.test.ts +1 -1
  289. package/src/secrets/__tests__/envelope.test.ts +1 -1
  290. package/src/secrets/__tests__/leak-guard.test.ts +1 -1
  291. package/src/secrets/__tests__/rotation.test.ts +1 -1
  292. package/src/stack/db.ts +25 -48
  293. package/src/stack/push-entity-projection-tables.ts +2 -4
  294. package/src/stack/table-helpers.ts +98 -61
  295. package/src/stack/test-stack.ts +8 -7
  296. package/src/testing/__tests__/db-cleanup.test.ts +40 -0
  297. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  298. package/src/testing/__tests__/{ensure-entity-table.integration.ts → ensure-entity-table.integration.test.ts} +7 -14
  299. package/src/testing/db-cleanup.ts +44 -0
  300. package/src/testing/expect-error.ts +1 -1
  301. package/src/testing/index.ts +2 -0
  302. package/src/testing/multipart-helper.ts +94 -0
  303. package/src/testing/shared-entities.ts +5 -5
  304. package/src/time/__tests__/polyfill.test.ts +1 -1
  305. package/src/time/__tests__/tz-context.test.ts +1 -1
  306. package/src/utils/__tests__/assert.test.ts +1 -1
  307. package/src/utils/__tests__/env-parse.test.ts +1 -1
  308. package/CHANGELOG.md +0 -472
  309. package/src/db/__tests__/cursor.test.ts +0 -41
  310. package/src/db/__tests__/db-helpers.test.ts +0 -369
  311. package/src/db/__tests__/drizzle-helpers.integration.ts +0 -186
  312. package/src/db/__tests__/row-helpers.test.ts +0 -59
  313. package/src/engine/steps/_drizzle-boundary.ts +0 -19
  314. package/src/files/__tests__/file-field-column.integration.ts +0 -103
@@ -9,8 +9,8 @@
9
9
  // (see TS-typing notes in the design doc). M.1 uses unsafeAppendEvent
10
10
  // semantics under the hood for r.step.aggregate.appendEvent.
11
11
 
12
- import type { SQL, Table } from "drizzle-orm";
13
12
  import type { EventStoreExecutor } from "../../db/event-store-executor";
13
+ import type { WhereObject } from "../../db/query";
14
14
  import type { KumikoEventTypeMap } from "./event-type-map";
15
15
  import type { HandlerContext, WriteEvent, WriteResult } from "./handlers";
16
16
  import type { SaveContext } from "./hooks";
@@ -200,31 +200,31 @@ export type StepNamespace = {
200
200
  // and NOT registered as an aggregate-table via r.entity. See
201
201
  // step-vocabulary.md "Was unsafeProjection.* überspringt".
202
202
  readonly unsafeProjectionUpsert: (args: {
203
- readonly table: Table;
203
+ readonly table: unknown;
204
204
  readonly on: readonly string[];
205
205
  readonly row: StepResolver<Record<string, unknown>>;
206
206
  }) => StepInstance;
207
207
  // Sibling: delete row(s) from a read-side projection table. Same
208
208
  // boot-validation contract as unsafeProjectionUpsert.
209
209
  readonly unsafeProjectionDelete: (args: {
210
- readonly table: Table;
211
- readonly where: StepResolver<SQL>;
210
+ readonly table: unknown;
211
+ readonly where: StepResolver<WhereObject>;
212
212
  }) => StepInstance;
213
- // Read sub-namespace — thin wrapper on ctx.db.select(). Caller-owned
214
- // tenant-filter (does NOT auto-inject like ctx.queryProjection does).
213
+ // Read sub-namespace — thin wrapper on selectMany/fetchOne (bun-db).
214
+ // Caller-owned tenant-filter (does NOT auto-inject like ctx.queryProjection does).
215
215
  readonly read: {
216
216
  readonly findOne: (
217
217
  name: string,
218
218
  opts: {
219
- readonly table: Table;
220
- readonly where: StepResolver<SQL | undefined>;
219
+ readonly table: unknown;
220
+ readonly where: StepResolver<WhereObject | undefined>;
221
221
  },
222
222
  ) => StepInstance;
223
223
  readonly findMany: (
224
224
  name: string,
225
225
  opts: {
226
- readonly table: Table;
227
- readonly where?: StepResolver<SQL | undefined>;
226
+ readonly table: unknown;
227
+ readonly where?: StepResolver<WhereObject | undefined>;
228
228
  readonly limit?: number;
229
229
  },
230
230
  ) => StepInstance;
@@ -23,7 +23,6 @@
23
23
  // caught by this validator. A future lint-rule will enforce the contract
24
24
  // statically; today it lives in this comment + the StepBuilder doc.
25
25
 
26
- import { getTableName, type Table } from "drizzle-orm";
27
26
  import { getStep } from "./define-step";
28
27
  import { buildPipelineSteps } from "./pipeline";
29
28
  import type { FeatureDefinition, SessionUser, TenantId, WriteEvent } from "./types";
@@ -56,7 +55,28 @@ function* walkAllSteps(steps: readonly StepInstance[]): Generator<StepInstance,
56
55
  }
57
56
  }
58
57
 
59
- type UnsafeProjectionStepArgs = { readonly table: Table };
58
+ // @cast-boundary drizzle-bridge reads table name from drizzle Symbol
59
+ // without importing drizzle-orm (bun-db pattern, see bun-db/query.ts).
60
+ const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
61
+
62
+ function resolveTableNameFromStep(table: unknown): string {
63
+ if (typeof table === "object" && table !== null) {
64
+ // EntityTableMeta discriminator
65
+ if (
66
+ "source" in table &&
67
+ "tableName" in table &&
68
+ typeof (table as Record<string, unknown>)["tableName"] === "string"
69
+ ) {
70
+ return (table as Record<string, unknown>)["tableName"] as string;
71
+ }
72
+ // drizzle pgTable
73
+ const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
74
+ if (typeof name === "string") return name;
75
+ }
76
+ throw new Error(`validate-projection-allowlist: cannot resolve table name from ${String(table)}`);
77
+ }
78
+
79
+ type UnsafeProjectionStepArgs = { readonly table: unknown };
60
80
 
61
81
  const DUMMY_USER: SessionUser = {
62
82
  id: "00000000-0000-0000-0000-000000000000",
@@ -137,7 +157,7 @@ export function validateProjectionAllowlist(features: readonly FeatureDefinition
137
157
  `without a \`table\` argument.`,
138
158
  );
139
159
  }
140
- const tableName = getTableName(stepArgs.table);
160
+ const tableName = resolveTableNameFromStep(stepArgs.table);
141
161
 
142
162
  const aggregateOwner = aggregateTables.get(tableName);
143
163
  if (aggregateOwner) {
@@ -12,12 +12,13 @@
12
12
  // so a future refactor that drops the merge fails here instead of silently
13
13
  // regressing to the Welle-2.5 state.
14
14
 
15
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
15
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
16
16
  import { z } from "zod";
17
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
17
18
  import { createRegistry, defineFeature } from "../../engine";
18
19
  import { createArchivedStreamsTable, createEventsTable } from "../../event-store";
19
20
  import { createEventConsumerStateTable } from "../../pipeline";
20
- import { createTestDb, createTestRedis, type TestDb, type TestRedis, TestUsers } from "../../stack";
21
+ import { createTestRedis, type TestRedis, TestUsers } from "../../stack";
21
22
  import { waitFor } from "../../testing";
22
23
  import { createAllInOneEntrypoint } from "../index";
23
24
 
@@ -72,7 +73,7 @@ const mixedLaneFeature = defineFeature("mixed", (r) => {
72
73
  const JWT = "entrypoint-wiring-test-secret-must-be-32-chars!";
73
74
  const adminUser = TestUsers.admin;
74
75
 
75
- let testDb: TestDb;
76
+ let testDb: BunTestDb;
76
77
  let testRedis: TestRedis;
77
78
 
78
79
  beforeAll(async () => {
@@ -11,12 +11,13 @@
11
11
  // guard — buildServer always wires an SSE consumer so this only
12
12
  // fires with systemConsumers explicitly disabled).
13
13
 
14
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
14
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
15
15
  import { z } from "zod";
16
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
16
17
  import { createRegistry, defineFeature } from "../../engine";
17
18
  import { createArchivedStreamsTable, createEventsTable } from "../../event-store";
18
19
  import { createEventConsumerStateTable } from "../../pipeline";
19
- import { createTestDb, createTestRedis, type TestDb, type TestRedis } from "../../stack";
20
+ import { createTestRedis, type TestRedis } from "../../stack";
20
21
  import { createAllInOneEntrypoint, createApiEntrypoint, createWorkerEntrypoint } from "../index";
21
22
 
22
23
  const splitFeature = defineFeature("split", (r) => {
@@ -38,7 +39,7 @@ function uniquePrefix(label: string): string {
38
39
  return `${label}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
39
40
  }
40
41
 
41
- let testDb: TestDb;
42
+ let testDb: BunTestDb;
42
43
  let testRedis: TestRedis;
43
44
 
44
45
  beforeAll(async () => {
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { defineFeature } from "../../engine/define-feature";
4
4
  import { camelCase, composeEnvSchema, KumikoBootError, parseEnv, pulumiConfigKey } from "../index";
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { z } from "zod";
3
3
  import { defineFeature } from "../../engine/define-feature";
4
4
  import { renderDryRun } from "../dry-run";
@@ -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 {
4
4
  AccessDeniedError,
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { failNotFound, failTransition, failUnprocessable } from "../write-error-info";
3
3
 
4
4
  describe("failNotFound", () => {
@@ -10,25 +10,27 @@
10
10
  // Feature in den Tests zu schwer wäre — wir testen nur den Read-Helper-
11
11
  // Layer, nicht die volle Event-Store-Pipeline.
12
12
 
13
+ import { afterAll, beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
13
14
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
14
15
  import { tmpdir } from "node:os";
15
16
  import { join } from "node:path";
16
- import { sql } from "drizzle-orm";
17
- import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
18
- import { createTestDb, type TestDb } from "../../stack";
17
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
18
+ import { asRawClient, selectMany } from "../../db/query";
19
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
19
20
  import { createSeedMigrationContext } from "../context";
20
21
  import { createEsOperationsTable, esOperationsTable } from "../operations-schema";
21
22
  import { runPendingSeedMigrations } from "../runner";
22
23
 
23
- let testDb: TestDb;
24
+ let testDb: BunTestDb;
24
25
 
25
26
  beforeAll(async () => {
27
+ await ensureTemporalPolyfill();
26
28
  testDb = await createTestDb();
27
29
  await createEsOperationsTable(testDb.db);
28
30
 
29
31
  // Minimal-Schema-Stubs für die 3 Read-Tabellen die context.ts liest.
30
32
  // Spalten matchen production (siehe Sysadmin-Stream-Tenant-Bug Memory).
31
- await testDb.db.execute(sql`
33
+ await asRawClient(testDb.db).unsafe(`
32
34
  CREATE TABLE IF NOT EXISTS read_users (
33
35
  id uuid PRIMARY KEY,
34
36
  email text NOT NULL,
@@ -54,7 +56,7 @@ afterAll(async () => {
54
56
  });
55
57
 
56
58
  beforeEach(async () => {
57
- await testDb.db.execute(sql`
59
+ await asRawClient(testDb.db).unsafe(`
58
60
  TRUNCATE kumiko_es_operations, kumiko_events, read_users, read_tenant_memberships, read_tenants
59
61
  RESTART IDENTITY CASCADE
60
62
  `);
@@ -70,26 +72,32 @@ async function insertMembershipWithEvent(args: {
70
72
  readonly streamTenantId: string;
71
73
  readonly roles: string;
72
74
  }): Promise<void> {
73
- await testDb.db.execute(sql`
75
+ await asRawClient(testDb.db).unsafe(
76
+ `
74
77
  INSERT INTO read_tenant_memberships (id, user_id, tenant_id, roles)
75
- VALUES (${args.id}::uuid, ${args.userId}, ${args.payloadTenantId}::uuid, ${args.roles})
76
- `);
77
- await testDb.db.execute(sql`
78
+ VALUES ($1::uuid, $2, $3::uuid, $4)
79
+ `,
80
+ [args.id, args.userId, args.payloadTenantId, args.roles],
81
+ );
82
+ await asRawClient(testDb.db).unsafe(
83
+ `
78
84
  INSERT INTO kumiko_events
79
85
  (aggregate_id, aggregate_type, tenant_id, version, type, payload, metadata, created_by)
80
86
  VALUES
81
- (${args.id}::uuid, 'tenant-membership', ${args.streamTenantId}::uuid, 1,
87
+ ($1::uuid, 'tenant-membership', $2::uuid, 1,
82
88
  'tenant-membership.created', '{}'::jsonb, '{"userId":"system"}'::jsonb, 'system')
83
- `);
89
+ `,
90
+ [args.id, args.streamTenantId],
91
+ );
84
92
  }
85
93
 
86
94
  function makeMockDispatcher() {
87
95
  return {
88
- write: vi.fn(async () => ({ isSuccess: true as const, data: {} })),
89
- query: vi.fn(),
90
- command: vi.fn(),
91
- batch: vi.fn(),
92
- resolveAuthClaims: vi.fn(),
96
+ write: mock(async () => ({ isSuccess: true as const, data: {} })),
97
+ query: mock(),
98
+ command: mock(),
99
+ batch: mock(),
100
+ resolveAuthClaims: mock(),
93
101
  };
94
102
  }
95
103
 
@@ -105,10 +113,13 @@ describe("SeedMigrationContext.findUserByEmail (integration)", () => {
105
113
  test("liest existing user-row korrekt + maps tenant_id → tenantId", async () => {
106
114
  const userId = "01900000-0000-7000-8000-000000000001";
107
115
  const tenantId = "00000000-0000-4000-8000-000000000099";
108
- await testDb.db.execute(sql`
116
+ await asRawClient(testDb.db).unsafe(
117
+ `
109
118
  INSERT INTO read_users (id, email, tenant_id)
110
- VALUES (${userId}::uuid, 'admin@example.com', ${tenantId}::uuid)
111
- `);
119
+ VALUES ($1::uuid, 'admin@example.com', $2::uuid)
120
+ `,
121
+ [userId, tenantId],
122
+ );
112
123
 
113
124
  const ctx = createSeedMigrationContext({
114
125
  dispatcher: makeMockDispatcher() as never,
@@ -231,11 +242,14 @@ describe("SeedMigrationContext.findMembershipsOfUser (integration)", () => {
231
242
  // statt einer mit fehlendem stream-tenant zu arbeiten und schwer
232
243
  // diagnostizierbare version_conflict-Errors zu produzieren.
233
244
  const userId = "01900000-0000-7000-8000-000000000003";
234
- await testDb.db.execute(sql`
245
+ await asRawClient(testDb.db).unsafe(
246
+ `
235
247
  INSERT INTO read_tenant_memberships (id, user_id, tenant_id, roles) VALUES
236
- ('00000000-0000-4000-8000-0000000000d1'::uuid, ${userId},
248
+ ('00000000-0000-4000-8000-0000000000d1'::uuid, $1,
237
249
  '00000000-0000-4000-8000-000000000005'::uuid, '["Admin"]')
238
- `);
250
+ `,
251
+ [userId],
252
+ );
239
253
  const ctx = createSeedMigrationContext({
240
254
  dispatcher: makeMockDispatcher() as never,
241
255
  dbRunner: testDb.db,
@@ -247,7 +261,7 @@ describe("SeedMigrationContext.findMembershipsOfUser (integration)", () => {
247
261
 
248
262
  describe("SeedMigrationContext.findTenants (integration)", () => {
249
263
  test("returnt alle Tenants sortiert nach inserted_at", async () => {
250
- await testDb.db.execute(sql`
264
+ await asRawClient(testDb.db).unsafe(`
251
265
  INSERT INTO read_tenants (id, name, tenant_key, inserted_at) VALUES
252
266
  ('00000000-0000-4000-8000-000000000002'::uuid, 'Beta', 'beta', '2026-01-02'),
253
267
  ('00000000-0000-4000-8000-000000000001'::uuid, 'Alpha', 'alpha', '2026-01-01')
@@ -296,7 +310,7 @@ describe("runPendingSeedMigrations: skippable + env-flag (integration)", () => {
296
310
 
297
311
  // Kritisch: KEIN Marker — beim nächsten Boot ohne env-flag würde
298
312
  // der Seed dann tatsächlich laufen.
299
- const markers = await testDb.db.select().from(esOperationsTable);
313
+ const markers = await selectMany(testDb.db, esOperationsTable);
300
314
  expect(markers).toHaveLength(0);
301
315
  } finally {
302
316
  delete process.env[envKey];
@@ -329,7 +343,7 @@ describe("runPendingSeedMigrations: skippable + env-flag (integration)", () => {
329
343
  expect(r.appliedIds).toEqual(["2026-05-20-skippable-but-no-flag"]);
330
344
  expect(r.skippedIds).toEqual([]);
331
345
 
332
- const markers = await testDb.db.select().from(esOperationsTable);
346
+ const markers = await selectMany(testDb.db, esOperationsTable);
333
347
  expect(markers).toHaveLength(1);
334
348
  } finally {
335
349
  rmSync(dir, { recursive: true, force: true });
@@ -341,7 +355,7 @@ describe("runPendingSeedMigrations: skippable + env-flag (integration)", () => {
341
355
 
342
356
  describe("SeedMigrationContext.db (escape-hatch, integration)", () => {
343
357
  test("ctx.db kann für eigene Lookups genutzt werden (read-only)", async () => {
344
- await testDb.db.execute(sql`
358
+ await asRawClient(testDb.db).unsafe(`
345
359
  INSERT INTO read_tenants (id, name, tenant_key) VALUES
346
360
  ('00000000-0000-4000-8000-000000000007'::uuid, 'Lucky', 'lucky')
347
361
  `);
@@ -349,8 +363,8 @@ describe("SeedMigrationContext.db (escape-hatch, integration)", () => {
349
363
  dispatcher: makeMockDispatcher() as never,
350
364
  dbRunner: testDb.db,
351
365
  });
352
- const rows = (await ctx.db.execute(
353
- sql`SELECT name FROM read_tenants WHERE tenant_key = 'lucky'`,
366
+ const rows = (await asRawClient(ctx.db).unsafe(
367
+ `SELECT name FROM read_tenants WHERE tenant_key = 'lucky'`,
354
368
  )) as unknown as readonly { name: string }[];
355
369
  expect(rows[0]?.name).toBe("Lucky");
356
370
  });
@@ -8,19 +8,21 @@
8
8
  // Heavy lifting (mock-dispatcher, in-memory-applied-set) liegt in
9
9
  // runner.test.ts. Hier nur DB-Round-Trip-Wahrheit.
10
10
 
11
+ import { afterAll, beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
11
12
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
12
13
  import { tmpdir } from "node:os";
13
14
  import { join } from "node:path";
14
- import { sql } from "drizzle-orm";
15
- import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
16
- import { createTestDb, type TestDb } from "../../stack";
15
+ import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
16
+ import { asRawClient, insertOne, selectMany } from "../../db/query";
17
+ import { ensureTemporalPolyfill } from "../../time/polyfill";
17
18
  import { createSeedMigrationContext } from "../context";
18
19
  import { createEsOperationsTable, esOperationsTable } from "../operations-schema";
19
20
  import { runPendingSeedMigrations } from "../runner";
20
21
 
21
- let testDb: TestDb;
22
+ let testDb: BunTestDb;
22
23
 
23
24
  beforeAll(async () => {
25
+ await ensureTemporalPolyfill();
24
26
  testDb = await createTestDb();
25
27
  await createEsOperationsTable(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_es_operations RESTART IDENTITY`);
35
+ await asRawClient(testDb.db).unsafe(`TRUNCATE kumiko_es_operations RESTART IDENTITY`);
34
36
  });
35
37
 
36
38
  function makeTempSeedsDir(files: readonly { name: string; content: string }[]): string {
@@ -42,14 +44,14 @@ function makeTempSeedsDir(files: readonly { name: string; content: string }[]):
42
44
  function makeMockDispatcher() {
43
45
  const calls: Array<{ qn: string; payload: unknown }> = [];
44
46
  return {
45
- write: vi.fn(async (qn: string, payload: unknown) => {
47
+ write: mock(async (qn: string, payload: unknown) => {
46
48
  calls.push({ qn, payload });
47
49
  return { isSuccess: true as const, data: {} };
48
50
  }),
49
- query: vi.fn(),
50
- command: vi.fn(),
51
- batch: vi.fn(),
52
- resolveAuthClaims: vi.fn(),
51
+ query: mock(),
52
+ command: mock(),
53
+ batch: mock(),
54
+ resolveAuthClaims: mock(),
53
55
  calls,
54
56
  };
55
57
  }
@@ -82,7 +84,7 @@ describe("runPendingSeedMigrations (integration)", () => {
82
84
  expect(r1.appliedIds).toEqual(["2026-05-20-noop"]);
83
85
 
84
86
  // Marker landed
85
- const markers1 = await testDb.db.select().from(esOperationsTable);
87
+ const markers1 = await selectMany(testDb.db, esOperationsTable);
86
88
  expect(markers1).toHaveLength(1);
87
89
  expect(markers1[0]?.id).toBe("2026-05-20-noop");
88
90
  expect(markers1[0]?.operationType).toBe("seed-migration");
@@ -98,7 +100,7 @@ describe("runPendingSeedMigrations (integration)", () => {
98
100
  logger: () => {},
99
101
  });
100
102
  expect(r2.appliedIds).toEqual([]);
101
- const markers2 = await testDb.db.select().from(esOperationsTable);
103
+ const markers2 = await selectMany(testDb.db, esOperationsTable);
102
104
  expect(markers2).toHaveLength(1);
103
105
  } finally {
104
106
  rmSync(dir, { recursive: true, force: true });
@@ -130,7 +132,7 @@ describe("runPendingSeedMigrations (integration)", () => {
130
132
  }),
131
133
  ).rejects.toThrow(/boom/);
132
134
 
133
- const markers = await testDb.db.select().from(esOperationsTable);
135
+ const markers = await selectMany(testDb.db, esOperationsTable);
134
136
  expect(markers).toHaveLength(0);
135
137
  } finally {
136
138
  rmSync(dir, { recursive: true, force: true });
@@ -192,14 +194,14 @@ describe("runPendingSeedMigrations (integration)", () => {
192
194
  ]);
193
195
  try {
194
196
  const dispatcher = {
195
- write: vi.fn(async () => ({
197
+ write: mock(async () => ({
196
198
  isSuccess: false as const,
197
199
  error: { code: "version_conflict", message: "stream changed" },
198
200
  })),
199
- query: vi.fn(),
200
- command: vi.fn(),
201
- batch: vi.fn(),
202
- resolveAuthClaims: vi.fn(),
201
+ query: mock(),
202
+ command: mock(),
203
+ batch: mock(),
204
+ resolveAuthClaims: mock(),
203
205
  };
204
206
 
205
207
  await expect(
@@ -214,7 +216,7 @@ describe("runPendingSeedMigrations (integration)", () => {
214
216
  ).rejects.toThrow(/version_conflict/);
215
217
 
216
218
  // Kein Marker — bei nächstem Boot würde der Seed retried
217
- const markers = await testDb.db.select().from(esOperationsTable);
219
+ const markers = await selectMany(testDb.db, esOperationsTable);
218
220
  expect(markers).toHaveLength(0);
219
221
  } finally {
220
222
  rmSync(dir, { recursive: true, force: true });
@@ -264,7 +266,7 @@ describe("runPendingSeedMigrations (integration)", () => {
264
266
  expect(dispatcher.write).toHaveBeenCalledTimes(1);
265
267
  // Marker NICHT gesetzt — retry beim nächsten Boot wird die Migration
266
268
  // nochmal ausführen. Wenn der Write nicht idempotent ist → Duplikat.
267
- const markers = await testDb.db.select().from(esOperationsTable);
269
+ const markers = await selectMany(testDb.db, esOperationsTable);
268
270
  expect(markers).toHaveLength(0);
269
271
  } finally {
270
272
  rmSync(dir, { recursive: true, force: true });
@@ -293,7 +295,7 @@ describe("runPendingSeedMigrations (integration)", () => {
293
295
  ]);
294
296
  try {
295
297
  // Pre-seed marker als wäre ein parallel-Pod schon durch
296
- await testDb.db.insert(esOperationsTable).values({
298
+ await insertOne(testDb.db, esOperationsTable, {
297
299
  id: "2026-05-20-race",
298
300
  operationType: "seed-migration",
299
301
  durationMs: 42,
@@ -318,7 +320,7 @@ describe("runPendingSeedMigrations (integration)", () => {
318
320
  });
319
321
 
320
322
  expect(dispatcher.write).not.toHaveBeenCalled();
321
- const markers = await testDb.db.select().from(esOperationsTable);
323
+ const markers = await selectMany(testDb.db, esOperationsTable);
322
324
  expect(markers).toHaveLength(1); // nur der pre-seeded
323
325
  } finally {
324
326
  rmSync(dir, { recursive: true, force: true });
@@ -354,7 +356,7 @@ describe("runPendingSeedMigrations (integration)", () => {
354
356
  ).rejects.toThrow(/stop here/);
355
357
 
356
358
  // Nur first hat marker — fails warf, never wurde nie attempted
357
- const markers = await testDb.db.select().from(esOperationsTable);
359
+ const markers = await selectMany(testDb.db, esOperationsTable);
358
360
  expect(markers.map((m) => m.id)).toEqual(["2026-05-19-first"]);
359
361
  } finally {
360
362
  rmSync(dir, { recursive: true, force: true });
@@ -2,10 +2,10 @@
2
2
  // dispatcher-call) testen wir gegen Postgres in der integration-test.
3
3
  // Hier nur die pure-logic-Pfade die kein echtes DB brauchen.
4
4
 
5
+ import { describe, expect, test } from "bun:test";
5
6
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
6
7
  import { tmpdir } from "node:os";
7
8
  import { join } from "node:path";
8
- import { describe, expect, test } from "vitest";
9
9
  import { runPendingSeedMigrations } from "../runner";
10
10
 
11
11
  function makeTempSeedsDir(files: readonly { name: string; content: string }[]): string {
@@ -14,31 +14,41 @@ function makeTempSeedsDir(files: readonly { name: string; content: string }[]):
14
14
  return dir;
15
15
  }
16
16
 
17
- // Minimal DB-Stub — Runner ruft transaction() + select() + insert() +
18
- // execute() auf. execute() liefert ein leeres array für den
19
- // re-check-inside-lock (= "nicht applied, weiter mit Run").
17
+ // Minimal DB-Stub — Runner uses bun-db helpers (selectMany, insertOne) +
18
+ // asRawClient(tx).unsafe(...) for the advisory lock + re-check + insert.
19
+ // Returns parsed rows based on the SQL text shape:
20
+ // SELECT * FROM "kumiko_es_operations" ... → applied-set rows
21
+ // SELECT pg_advisory_xact_lock(...) → ignored
22
+ // SELECT 1 FROM "kumiko_es_operations" ... → empty (= not applied)
23
+ // INSERT INTO "kumiko_es_operations" ... → record + add to applied
20
24
  function makeStubDb(initialApplied: readonly string[] = []) {
21
25
  const inserts: Array<Record<string, unknown>> = [];
22
26
  const applied = new Set(initialApplied);
27
+ const unsafe = async (
28
+ sqlText: string,
29
+ params?: readonly unknown[],
30
+ ): Promise<readonly unknown[]> => {
31
+ if (/SELECT \* FROM "kumiko_es_operations"/.test(sqlText)) {
32
+ return Array.from(applied).map((id) => ({
33
+ id,
34
+ operationType: "seed-migration",
35
+ }));
36
+ }
37
+ if (/INSERT INTO "kumiko_es_operations"/.test(sqlText)) {
38
+ const id = String(params?.[0]);
39
+ inserts.push({ id });
40
+ applied.add(id);
41
+ return [{ id }];
42
+ }
43
+ // pg_advisory_xact_lock + re-check both yield empty → "not applied, run".
44
+ return [];
45
+ };
23
46
  const db = {
47
+ unsafe,
48
+ begin: async (cb: (tx: unknown) => Promise<unknown>) => cb(db),
24
49
  transaction: async (cb: (tx: unknown) => Promise<void>) => {
25
50
  await cb(db);
26
51
  },
27
- select: () => ({
28
- from: () => ({
29
- where: async () => Array.from(applied).map((id) => ({ id })),
30
- }),
31
- }),
32
- insert: () => ({
33
- values: async (row: Record<string, unknown>) => {
34
- inserts.push(row);
35
- if (typeof row["id"] === "string") applied.add(row["id"]);
36
- },
37
- }),
38
- // execute: für pg_advisory_xact_lock + re-check. Leere Liste = "nicht
39
- // applied im Inner-Lock-Scope, weiter mit Run". applied-set check via
40
- // select() oben wird sowieso schon angewendet.
41
- execute: async (_q: unknown) => [],
42
52
  };
43
53
  return { db, inserts, applied };
44
54
  }
@@ -7,8 +7,12 @@
7
7
  // config-seed.ts:40). Events haben createdBy = SYSTEM_TENANT_ID-User
8
8
  // → audit-fähig.
9
9
 
10
- import { sql } from "drizzle-orm";
11
10
  import type { DbRunner } from "../db";
11
+ import {
12
+ selectAllTenants,
13
+ selectMembershipsOfUser,
14
+ selectUserByEmail,
15
+ } from "../db/queries/seed-context";
12
16
  import { createSystemUser, SYSTEM_TENANT_ID } from "../engine";
13
17
  import type { Dispatcher } from "../pipeline/dispatcher";
14
18
  import type { SeedMembershipRow, SeedMigrationContext, SeedTenantRow } from "./types";
@@ -59,46 +63,13 @@ export function createSeedMigrationContext(
59
63
  },
60
64
 
61
65
  findUserByEmail: async (email) => {
62
- // Direct DB-Read via read_users-Projection (gleicher Pfad wie
63
- // UserQueries.findForAuth aber ohne Dispatcher-Roundtrip; Seeds
64
- // greifen oft 1-N Lookups → direkt schneller).
65
- // @cast-boundary db-row — drizzle execute(sql) returns row-array
66
- // direkt (kein { rows }-Wrapper); column-types vom SQL-Cast oben
67
- const rows = (await args.dbRunner.execute(
68
- sql`SELECT id::text AS id, email, tenant_id::text AS tenant_id
69
- FROM read_users
70
- WHERE email = ${email}
71
- LIMIT 1`,
72
- )) as unknown as readonly { id: string; email: string; tenant_id: string }[];
73
- const row = rows[0];
66
+ const row = await selectUserByEmail(args.dbRunner, email);
74
67
  if (!row) return null;
75
- return { id: row.id, email: row.email, tenantId: row.tenant_id };
68
+ return { id: row.id, email: row.email, tenantId: row.tenantId };
76
69
  },
77
70
 
78
71
  findMembershipsOfUser: async (userId) => {
79
- // INNER JOIN auf kumiko_events um den stream-tenant (events.tenant_id
80
- // der v1-Row) neben dem payload-tenant (memberships.tenant_id) zu
81
- // liefern. Die beiden divergieren wenn das Aggregate von einem
82
- // Executor mit fremder tenantId angelegt wurde (seedTenantMembership
83
- // by=systemAdmin) — typischer publicstatus-Driver-Use-Case.
84
- // INNER (nicht LEFT): kein v1-Event bei vorhandener Read-Row wäre
85
- // Data-Drift, kein legitimer Zustand für Seed-Migrations.
86
- // @cast-boundary db-row — roles ist JSON-string in der text-Spalte
87
- // (Memory: tenant-membership.created payload "[\"User\"]"), wird unten geparst
88
- const rows = (await args.dbRunner.execute(
89
- sql`SELECT m.user_id::text AS user_id,
90
- m.tenant_id::text AS tenant_id,
91
- e.tenant_id::text AS stream_tenant_id,
92
- m.roles
93
- FROM read_tenant_memberships m
94
- JOIN kumiko_events e ON e.aggregate_id = m.id AND e.version = 1
95
- WHERE m.user_id = ${userId}`,
96
- )) as unknown as readonly {
97
- user_id: string;
98
- tenant_id: string;
99
- stream_tenant_id: string;
100
- roles: string;
101
- }[];
72
+ const rows = await selectMembershipsOfUser(args.dbRunner, userId);
102
73
  return rows.map(
103
74
  (r): SeedMembershipRow => ({
104
75
  userId: r.user_id,
@@ -110,12 +81,7 @@ export function createSeedMigrationContext(
110
81
  },
111
82
 
112
83
  findTenants: async () => {
113
- // @cast-boundary db-row
114
- const rows = (await args.dbRunner.execute(
115
- sql`SELECT id::text AS id, name, tenant_key
116
- FROM read_tenants
117
- ORDER BY inserted_at`,
118
- )) as unknown as readonly { id: string; name: string; tenant_key: string }[];
84
+ const rows = await selectAllTenants(args.dbRunner);
119
85
  return rows.map((r): SeedTenantRow => ({ id: r.id, name: r.name, tenantKey: r.tenant_key }));
120
86
  },
121
87
 
@@ -10,9 +10,9 @@
10
10
  // bunx kumiko ops seed:status → operation_type = "seed-migration"
11
11
  // bunx kumiko ops projection:status → operation_type = "projection-rebuild"
12
12
 
13
- import { sql } from "drizzle-orm";
13
+ // sql now comes from native dialect
14
14
  import { type DbConnection, tableExists } from "../db";
15
- import { index, integer, table as pgTable, text, timestamp } from "../db/dialect";
15
+ import { index, integer, table as pgTable, sql, text, timestamp } from "../db/dialect";
16
16
  import { unsafePushTables } from "../stack";
17
17
 
18
18
  export type EsOperationType = "seed-migration";