@cosmicdrift/kumiko-framework 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/package.json +6 -6
  2. package/src/__tests__/{anonymous-access.integration.ts → anonymous-access.integration.test.ts} +12 -9
  3. package/src/__tests__/{error-contract.integration.ts → error-contract.integration.test.ts} +5 -4
  4. package/src/__tests__/{field-access.integration.ts → field-access.integration.test.ts} +3 -3
  5. package/src/__tests__/{full-stack.integration.ts → full-stack.integration.test.ts} +7 -16
  6. package/src/__tests__/{ownership.integration.ts → ownership.integration.test.ts} +3 -2
  7. package/src/__tests__/{raw-table.integration.ts → raw-table.integration.test.ts} +18 -30
  8. package/src/__tests__/{reference-data.integration.ts → reference-data.integration.test.ts} +24 -11
  9. package/src/__tests__/{transition-guard.integration.ts → transition-guard.integration.test.ts} +12 -10
  10. package/src/api/__tests__/api.test.ts +1 -1
  11. package/src/api/__tests__/auth-middleware-transport.test.ts +1 -1
  12. package/src/api/__tests__/auth-routes-cookie.test.ts +1 -1
  13. package/src/api/__tests__/{batch.integration.ts → batch.integration.test.ts} +30 -30
  14. package/src/api/__tests__/body-limit.test.ts +1 -1
  15. package/src/api/__tests__/csrf-middleware.test.ts +1 -1
  16. package/src/api/__tests__/{dispatcher-live.integration.ts → dispatcher-live.integration.test.ts} +10 -9
  17. package/src/api/__tests__/metrics-endpoint.test.ts +1 -1
  18. package/src/api/__tests__/{nested-write.integration.ts → nested-write.integration.test.ts} +13 -16
  19. package/src/api/__tests__/readiness.test.ts +1 -1
  20. package/src/api/__tests__/request-id-middleware.test.ts +1 -1
  21. package/src/api/__tests__/sse-broker.test.ts +12 -12
  22. package/src/api/__tests__/sse-route.test.ts +1 -1
  23. package/src/api/auth-routes.ts +2 -5
  24. package/src/api/readiness.ts +2 -2
  25. package/src/auth/__tests__/roles.test.ts +2 -2
  26. package/src/bun-db/__tests__/PATTERN.md +73 -0
  27. package/src/bun-db/__tests__/_helpers.ts +103 -0
  28. package/src/bun-db/__tests__/batch-methods.integration.test.ts +143 -0
  29. package/src/bun-db/__tests__/batch-methods.test.ts +20 -0
  30. package/src/bun-db/__tests__/bun-test-db.ts +19 -0
  31. package/src/bun-db/__tests__/bun-test-stack.ts +6 -0
  32. package/src/bun-db/__tests__/column-types.integration.test.ts +132 -0
  33. package/src/bun-db/__tests__/compound-types.integration.test.ts +134 -0
  34. package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +235 -0
  35. package/src/bun-db/__tests__/smoke.integration.test.ts +43 -0
  36. package/src/bun-db/__tests__/sql-methods.integration.test.ts +231 -0
  37. package/src/bun-db/__tests__/where-patterns.integration.test.ts +185 -0
  38. package/src/bun-db/connection.ts +84 -0
  39. package/src/bun-db/index.ts +31 -0
  40. package/src/bun-db/query.ts +842 -0
  41. package/src/compliance/__tests__/duration-spec.test.ts +1 -1
  42. package/src/compliance/__tests__/profiles.test.ts +1 -1
  43. package/src/compliance/__tests__/sub-processors.test.ts +1 -1
  44. package/src/compliance/profiles.ts +1 -4
  45. package/src/db/__tests__/{apply-entity-event-tenant.integration.ts → apply-entity-event-tenant.integration.test.ts} +13 -11
  46. package/src/db/__tests__/big-int-field.test.ts +15 -14
  47. package/src/db/__tests__/column-ddl.integration.test.ts +113 -0
  48. package/src/db/__tests__/compound-types.test.ts +1 -1
  49. package/src/db/__tests__/{config-seed.integration.ts → config-seed.integration.test.ts} +32 -27
  50. package/src/db/__tests__/connection-options.test.ts +1 -1
  51. package/src/db/__tests__/cursor.test.ts +8 -32
  52. package/src/db/__tests__/dialect-instant.test.ts +1 -1
  53. package/src/db/__tests__/encryption.test.ts +1 -1
  54. package/src/db/__tests__/{drizzle-table-types.test.ts → entity-table-types.test.ts} +16 -16
  55. package/src/db/__tests__/{event-store-executor-list.integration.ts → event-store-executor-list.integration.test.ts} +12 -7
  56. package/src/db/__tests__/{event-store-executor.integration.ts → event-store-executor.integration.test.ts} +19 -12
  57. package/src/db/__tests__/{implicit-projection-equivalence.integration.ts → implicit-projection-equivalence.integration.test.ts} +35 -29
  58. package/src/db/__tests__/located-timestamp.test.ts +1 -1
  59. package/src/db/__tests__/migrate-generator.test.ts +71 -0
  60. package/src/db/__tests__/migrate-runner.test.ts +19 -0
  61. package/src/db/__tests__/money.test.ts +1 -1
  62. package/src/db/__tests__/{multi-row-insert.integration.ts → multi-row-insert.integration.test.ts} +18 -11
  63. package/src/db/__tests__/parse-auto-verb.test.ts +1 -1
  64. package/src/db/__tests__/pg-error.test.ts +43 -0
  65. package/src/db/__tests__/{required-not-null-migration-safety.integration.ts → required-not-null-migration-safety.integration.test.ts} +28 -24
  66. package/src/db/__tests__/{schema-migration.integration.ts → schema-migration.integration.test.ts} +32 -28
  67. package/src/db/__tests__/sql-inventory.test.ts +56 -0
  68. package/src/db/__tests__/table-builder-indexes.test.ts +30 -11
  69. package/src/db/__tests__/table-builder-required.test.ts +20 -22
  70. package/src/db/__tests__/{tenant-db.integration.ts → tenant-db.integration.test.ts} +106 -144
  71. package/src/db/__tests__/{unique-violation-mapping.integration.ts → unique-violation-mapping.integration.test.ts} +13 -8
  72. package/src/db/api.ts +46 -0
  73. package/src/db/apply-entity-event.ts +45 -36
  74. package/src/db/assert-exists-in.ts +5 -16
  75. package/src/db/bun-provider.ts +37 -0
  76. package/src/db/config-seed.ts +4 -4
  77. package/src/db/connection.ts +14 -57
  78. package/src/db/cursor.ts +5 -56
  79. package/src/db/dialect.ts +472 -99
  80. package/src/db/eagerload.ts +5 -12
  81. package/src/db/entity-table-meta.ts +390 -0
  82. package/src/db/event-store-executor.ts +158 -100
  83. package/src/db/index.ts +33 -5
  84. package/src/db/migrate-generator.ts +350 -0
  85. package/src/db/migrate-runner.ts +206 -0
  86. package/src/db/postgres-provider.ts +25 -0
  87. package/src/db/queries/entity-read.ts +15 -0
  88. package/src/db/queries/es-ops.ts +17 -0
  89. package/src/db/queries/event-consumer.ts +170 -0
  90. package/src/db/queries/event-store-admin.ts +127 -0
  91. package/src/db/queries/event-store.ts +155 -0
  92. package/src/db/queries/projection-rebuild.ts +59 -0
  93. package/src/db/queries/raw-sql.ts +15 -0
  94. package/src/db/queries/schema-drift.ts +35 -0
  95. package/src/db/queries/seed-context.ts +58 -0
  96. package/src/db/queries/table-ops.ts +11 -0
  97. package/src/db/queries/test-stack.ts +56 -0
  98. package/src/db/query-api.ts +22 -0
  99. package/src/db/query.ts +30 -0
  100. package/src/db/reference-data.ts +19 -22
  101. package/src/db/render-ddl.ts +57 -0
  102. package/src/db/row-helpers.ts +3 -52
  103. package/src/db/schema-inspection.ts +17 -4
  104. package/src/db/sql-inventory.ts +208 -0
  105. package/src/db/table-builder.ts +54 -46
  106. package/src/db/tenant-db.ts +105 -326
  107. package/src/engine/__tests__/auth-claims-registrar.test.ts +1 -1
  108. package/src/engine/__tests__/boot-validator-api-exposure.test.ts +3 -3
  109. package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +1 -1
  110. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +5 -5
  111. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +3 -3
  112. package/src/engine/__tests__/boot-validator.test.ts +4 -3
  113. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  114. package/src/engine/__tests__/build-target.test.ts +1 -1
  115. package/src/engine/__tests__/claim-keys.test.ts +1 -1
  116. package/src/engine/__tests__/codemod-pipeline.test.ts +3 -3
  117. package/src/engine/__tests__/config-helpers.test.ts +1 -1
  118. package/src/engine/__tests__/duration-utils.test.ts +16 -0
  119. package/src/engine/__tests__/effective-features.test.ts +1 -1
  120. package/src/engine/__tests__/engine.test.ts +1 -1
  121. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  122. package/src/engine/__tests__/event-helpers.test.ts +3 -3
  123. package/src/engine/__tests__/extends-registrar.test.ts +4 -4
  124. package/src/engine/__tests__/factories-long-text.test.ts +1 -1
  125. package/src/engine/__tests__/factories-time.test.ts +1 -1
  126. package/src/engine/__tests__/field-access.test.ts +38 -0
  127. package/src/engine/__tests__/field-predicates.test.ts +1 -1
  128. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  129. package/src/engine/__tests__/identifiers.test.ts +1 -1
  130. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  131. package/src/engine/__tests__/nav.test.ts +1 -1
  132. package/src/engine/__tests__/no-return-guard.test.ts +17 -0
  133. package/src/engine/__tests__/ownership.test.ts +10 -11
  134. package/src/engine/__tests__/parse-ref-target.test.ts +1 -1
  135. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  136. package/src/engine/__tests__/{pipeline-handler.integration.ts → pipeline-handler.integration.test.ts} +38 -52
  137. package/src/engine/__tests__/{pipeline-observability.integration.ts → pipeline-observability.integration.test.ts} +1 -1
  138. package/src/engine/__tests__/{pipeline-performance.integration.ts → pipeline-performance.integration.test.ts} +1 -1
  139. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  140. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  141. package/src/engine/__tests__/projection-helpers.test.ts +25 -17
  142. package/src/engine/__tests__/projection.test.ts +4 -4
  143. package/src/engine/__tests__/qualified-name.test.ts +1 -1
  144. package/src/engine/__tests__/raw-table.test.ts +9 -8
  145. package/src/engine/__tests__/resolve-config-or-param.test.ts +5 -5
  146. package/src/engine/__tests__/run-in.test.ts +1 -1
  147. package/src/engine/__tests__/schema-builder.test.ts +1 -1
  148. package/src/engine/__tests__/screen.test.ts +1 -1
  149. package/src/engine/__tests__/search-payload-extension.test.ts +3 -3
  150. package/src/engine/__tests__/state-machine.test.ts +1 -1
  151. package/src/engine/__tests__/steps-aggregate-append-event.test.ts +7 -7
  152. package/src/engine/__tests__/steps-aggregate-create.test.ts +4 -4
  153. package/src/engine/__tests__/steps-aggregate-update.test.ts +3 -3
  154. package/src/engine/__tests__/steps-call-feature.test.ts +5 -5
  155. package/src/engine/__tests__/steps-mail-send.test.ts +7 -7
  156. package/src/engine/__tests__/steps-read.test.ts +34 -40
  157. package/src/engine/__tests__/steps-resolver-utils.test.ts +6 -6
  158. package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +24 -19
  159. package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +28 -17
  160. package/src/engine/__tests__/steps-webhook-send.test.ts +6 -6
  161. package/src/engine/__tests__/steps-workflow.test.ts +7 -7
  162. package/src/engine/__tests__/system-user.test.ts +1 -1
  163. package/src/engine/__tests__/unmanaged-table.test.ts +98 -0
  164. package/src/engine/__tests__/validate-projection-allowlist.test.ts +4 -5
  165. package/src/engine/__tests__/validation-hooks.test.ts +1 -1
  166. package/src/engine/__tests__/visual-tree-patterns.test.ts +1 -1
  167. package/src/engine/boot-validator/entity-handler.ts +3 -3
  168. package/src/engine/boot-validator/ownership.ts +1 -1
  169. package/src/engine/define-feature.ts +37 -2
  170. package/src/engine/entity-handlers.ts +5 -5
  171. package/src/engine/factories.ts +1 -1
  172. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +1 -1
  173. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -1
  174. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +2 -2
  175. package/src/engine/feature-ast/__tests__/parse.test.ts +1 -1
  176. package/src/engine/feature-ast/__tests__/patch.test.ts +1 -1
  177. package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
  178. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +1 -1
  179. package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +1 -1
  180. package/src/engine/feature-ast/extractors/shared.ts +2 -3
  181. package/src/engine/ownership.ts +113 -41
  182. package/src/engine/pattern-library/__tests__/library.test.ts +2 -2
  183. package/src/engine/projection-helpers.ts +2 -11
  184. package/src/engine/registry.ts +21 -2
  185. package/src/engine/steps/read-find-many.ts +13 -13
  186. package/src/engine/steps/read-find-one.ts +7 -9
  187. package/src/engine/steps/unsafe-projection-delete.ts +4 -5
  188. package/src/engine/steps/unsafe-projection-upsert.ts +63 -31
  189. package/src/engine/types/feature.ts +47 -2
  190. package/src/engine/types/fields.ts +4 -5
  191. package/src/engine/types/index.ts +2 -0
  192. package/src/engine/types/step.ts +10 -10
  193. package/src/engine/validate-projection-allowlist.ts +23 -3
  194. package/src/entrypoint/__tests__/{entrypoint-job-wiring.integration.ts → entrypoint-job-wiring.integration.test.ts} +4 -3
  195. package/src/entrypoint/__tests__/{split-deploy.integration.ts → split-deploy.integration.test.ts} +4 -3
  196. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  197. package/src/env/__tests__/dry-run.test.ts +1 -1
  198. package/src/errors/__tests__/classes.test.ts +1 -1
  199. package/src/errors/__tests__/error-helpers.test.ts +44 -0
  200. package/src/errors/__tests__/field-issue-compat.test.ts +16 -0
  201. package/src/errors/__tests__/write-failures.test.ts +1 -1
  202. package/src/errors/classes.ts +5 -19
  203. package/src/errors/field-issue.ts +11 -0
  204. package/src/errors/index.ts +1 -0
  205. package/src/errors/zod-bridge.ts +3 -2
  206. package/src/es-ops/__tests__/{context.integration.ts → context.integration.test.ts} +43 -29
  207. package/src/es-ops/__tests__/{runner.integration.ts → runner.integration.test.ts} +25 -23
  208. package/src/es-ops/__tests__/runner.test.ts +29 -19
  209. package/src/es-ops/context.ts +11 -56
  210. package/src/es-ops/operations-schema.ts +2 -2
  211. package/src/es-ops/runner.ts +12 -26
  212. package/src/event-store/__tests__/{admin-api.integration.ts → admin-api.integration.test.ts} +71 -45
  213. package/src/event-store/__tests__/{event-store.integration.ts → event-store.integration.test.ts} +7 -5
  214. package/src/event-store/__tests__/{get-stream-version-perf.integration.ts → get-stream-version-perf.integration.test.ts} +5 -3
  215. package/src/event-store/__tests__/{perf.integration.ts → perf.integration.test.ts} +24 -16
  216. package/src/event-store/__tests__/{snapshot.integration.ts → snapshot.integration.test.ts} +34 -28
  217. package/src/event-store/__tests__/{upcaster-dead-letter.integration.ts → upcaster-dead-letter.integration.test.ts} +11 -12
  218. package/src/event-store/__tests__/{upcaster.integration.ts → upcaster.integration.test.ts} +19 -32
  219. package/src/event-store/admin-api.ts +55 -83
  220. package/src/event-store/archive.ts +15 -39
  221. package/src/event-store/event-store.ts +92 -86
  222. package/src/event-store/events-schema.ts +2 -1
  223. package/src/event-store/index.ts +1 -0
  224. package/src/event-store/snapshot.ts +26 -24
  225. package/src/event-store/upcaster-dead-letter.ts +19 -18
  226. package/src/files/__tests__/content-disposition.test.ts +1 -1
  227. package/src/files/__tests__/{file-field-pipeline.integration.ts → file-field-pipeline.integration.test.ts} +8 -5
  228. package/src/files/__tests__/file-handle.test.ts +1 -1
  229. package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +32 -17
  230. package/src/files/__tests__/read-stream.test.ts +1 -1
  231. package/src/files/__tests__/{storage-tracking.integration.ts → storage-tracking.integration.test.ts} +26 -30
  232. package/src/files/__tests__/write-stream.test.ts +1 -1
  233. package/src/files/__tests__/zip-stream.test.ts +1 -1
  234. package/src/files/file-ref-table.ts +2 -2
  235. package/src/files/file-routes.ts +7 -9
  236. package/src/files/storage-tracking.ts +9 -17
  237. package/src/i18n/__tests__/i18n.test.ts +1 -1
  238. package/src/jobs/__tests__/{job-event-trigger.integration.ts → job-event-trigger.integration.test.ts} +6 -3
  239. package/src/jobs/__tests__/{job-multi-trigger.integration.ts → job-multi-trigger.integration.test.ts} +6 -3
  240. package/src/jobs/__tests__/{jobs.integration.ts → jobs.integration.test.ts} +5 -7
  241. package/src/lifecycle/__tests__/{lifecycle-server.integration.ts → lifecycle-server.integration.test.ts} +1 -1
  242. package/src/lifecycle/__tests__/lifecycle.test.ts +6 -6
  243. package/src/lifecycle/__tests__/signal-handlers.test.ts +6 -6
  244. package/src/logging/__tests__/pino-trace-bridge.test.ts +1 -1
  245. package/src/migrations/__tests__/compare-snapshots.test.ts +1 -1
  246. package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts} +34 -26
  247. package/src/migrations/__tests__/{detect-projections-to-rebuild.integration.ts → detect-projections-to-rebuild.integration.test.ts} +1 -1
  248. package/src/migrations/__tests__/rebuild-marker.test.ts +1 -1
  249. package/src/migrations/projection-detection.ts +12 -1
  250. package/src/migrations/schema-drift.ts +7 -23
  251. package/src/observability/__tests__/console-provider.test.ts +1 -1
  252. package/src/observability/__tests__/metric-validator.test.ts +1 -1
  253. package/src/observability/__tests__/noop-provider.test.ts +1 -1
  254. package/src/observability/__tests__/{observability.integration.ts → observability.integration.test.ts} +5 -8
  255. package/src/observability/__tests__/prometheus-meter.test.ts +1 -1
  256. package/src/observability/__tests__/recording-meter.test.ts +1 -1
  257. package/src/observability/__tests__/recording-tracer.test.ts +1 -1
  258. package/src/observability/__tests__/sensitive-filter.test.ts +1 -1
  259. package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts} +3 -3
  260. package/src/pipeline/__tests__/auth-claims-resolver.test.ts +9 -9
  261. package/src/pipeline/__tests__/{cascade-handler.integration.ts → cascade-handler.integration.test.ts} +18 -15
  262. package/src/pipeline/__tests__/cascade-handler.test.ts +1 -1
  263. package/src/pipeline/__tests__/{causation-chain.integration.ts → causation-chain.integration.test.ts} +12 -13
  264. package/src/pipeline/__tests__/{ctx-bridge.integration.ts → ctx-bridge.integration.test.ts} +12 -11
  265. package/src/pipeline/__tests__/dispatcher-utils.test.ts +107 -0
  266. package/src/pipeline/__tests__/dispatcher.test.ts +2 -2
  267. package/src/pipeline/__tests__/{distributed-lock.integration.ts → distributed-lock.integration.test.ts} +1 -1
  268. package/src/pipeline/__tests__/{domain-events-projections.integration.ts → domain-events-projections.integration.test.ts} +13 -15
  269. package/src/pipeline/__tests__/{event-dedup.integration.ts → event-dedup.integration.test.ts} +1 -1
  270. package/src/pipeline/__tests__/{event-define-event-strict.integration.ts → event-define-event-strict.integration.test.ts} +6 -16
  271. package/src/pipeline/__tests__/{event-dispatcher-lifecycle.integration.ts → event-dispatcher-lifecycle.integration.test.ts} +1 -1
  272. package/src/pipeline/__tests__/{event-dispatcher-multi-instance.integration.ts → event-dispatcher-multi-instance.integration.test.ts} +3 -2
  273. package/src/pipeline/__tests__/{event-dispatcher-pg-listen.integration.ts → event-dispatcher-pg-listen.integration.test.ts} +1 -1
  274. package/src/pipeline/__tests__/{event-dispatcher-recovery.integration.ts → event-dispatcher-recovery.integration.test.ts} +2 -2
  275. package/src/pipeline/__tests__/{event-dispatcher-second-audit.integration.ts → event-dispatcher-second-audit.integration.test.ts} +17 -16
  276. package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +14 -12
  277. package/src/pipeline/__tests__/{event-dispatcher.integration.ts → event-dispatcher.integration.test.ts} +8 -15
  278. package/src/pipeline/__tests__/{event-retention.integration.ts → event-retention.integration.test.ts} +28 -25
  279. package/src/pipeline/__tests__/{fetch-for-writing.integration.ts → fetch-for-writing.integration.test.ts} +6 -6
  280. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +4 -4
  281. package/src/pipeline/__tests__/{load-aggregate-query.integration.ts → load-aggregate-query.integration.test.ts} +9 -5
  282. package/src/pipeline/__tests__/{msp-error-mode.integration.ts → msp-error-mode.integration.test.ts} +1 -1
  283. package/src/pipeline/__tests__/{msp-multi-hop.integration.ts → msp-multi-hop.integration.test.ts} +9 -8
  284. package/src/pipeline/__tests__/{msp-rebuild.integration.ts → msp-rebuild.integration.test.ts} +47 -55
  285. package/src/pipeline/__tests__/{multi-stream-projection.integration.ts → multi-stream-projection.integration.test.ts} +19 -53
  286. package/src/pipeline/__tests__/{perf-rebuild.integration.ts → perf-rebuild.integration.test.ts} +36 -34
  287. package/src/pipeline/__tests__/{post-query-hook.integration.ts → post-query-hook.integration.test.ts} +1 -1
  288. package/src/pipeline/__tests__/{projection-rebuild.integration.ts → projection-rebuild.integration.test.ts} +21 -30
  289. package/src/pipeline/__tests__/{query-projection.integration.ts → query-projection.integration.test.ts} +6 -5
  290. package/src/pipeline/__tests__/redis-keys.test.ts +12 -0
  291. package/src/pipeline/__tests__/{redis-pipeline.integration.ts → redis-pipeline.integration.test.ts} +3 -1
  292. package/src/pipeline/cascade-handler.ts +13 -21
  293. package/src/pipeline/dispatcher-utils.ts +8 -7
  294. package/src/pipeline/dispatcher.ts +43 -48
  295. package/src/pipeline/event-consumer-state.ts +11 -2
  296. package/src/pipeline/event-dispatcher.ts +86 -146
  297. package/src/pipeline/event-retention.ts +14 -24
  298. package/src/pipeline/msp-rebuild.ts +54 -78
  299. package/src/pipeline/projection-rebuild.ts +65 -67
  300. package/src/pipeline/projection-state.ts +2 -2
  301. package/src/random/__tests__/generate.test.ts +13 -13
  302. package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts} +1 -1
  303. package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts} +1 -1
  304. package/src/rate-limit/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +1 -1
  305. package/src/redis/__tests__/redis-options.test.ts +1 -1
  306. package/src/search/__tests__/{meilisearch-adapter.integration.ts → meilisearch-adapter.integration.test.ts} +1 -1
  307. package/src/search/__tests__/search-adapter.test.ts +1 -1
  308. package/src/secrets/__tests__/dek-cache.test.ts +1 -3
  309. package/src/secrets/__tests__/env-master-key-provider.test.ts +1 -1
  310. package/src/secrets/__tests__/envelope.test.ts +1 -1
  311. package/src/secrets/__tests__/leak-guard.test.ts +1 -1
  312. package/src/secrets/__tests__/rotation.test.ts +1 -1
  313. package/src/stack/db.ts +25 -48
  314. package/src/stack/push-entity-projection-tables.ts +2 -4
  315. package/src/stack/table-helpers.ts +98 -61
  316. package/src/stack/test-stack.ts +10 -9
  317. package/src/testing/__tests__/db-cleanup.test.ts +40 -0
  318. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  319. package/src/testing/__tests__/{ensure-entity-table.integration.ts → ensure-entity-table.integration.test.ts} +7 -14
  320. package/src/testing/db-cleanup.ts +44 -0
  321. package/src/testing/expect-error.ts +1 -1
  322. package/src/testing/index.ts +2 -0
  323. package/src/testing/multipart-helper.ts +94 -0
  324. package/src/testing/shared-entities.ts +5 -5
  325. package/src/time/__tests__/polyfill.test.ts +1 -1
  326. package/src/time/__tests__/tz-context.test.ts +1 -1
  327. package/src/utils/__tests__/assert.test.ts +1 -1
  328. package/src/utils/__tests__/case.test.ts +16 -0
  329. package/src/utils/__tests__/env-parse.test.ts +1 -1
  330. package/src/utils/__tests__/is-plain-object.test.ts +16 -0
  331. package/src/utils/__tests__/parse-string-array-json.test.ts +16 -0
  332. package/src/utils/__tests__/safe-json.test.ts +22 -0
  333. package/src/utils/case.ts +6 -0
  334. package/src/utils/index.ts +3 -0
  335. package/src/utils/is-plain-object.ts +4 -0
  336. package/src/utils/parse-string-array-json.ts +14 -0
  337. package/CHANGELOG.md +0 -474
  338. package/src/db/__tests__/db-helpers.test.ts +0 -369
  339. package/src/db/__tests__/drizzle-helpers.integration.ts +0 -186
  340. package/src/db/__tests__/row-helpers.test.ts +0 -59
  341. package/src/engine/steps/_drizzle-boundary.ts +0 -19
  342. package/src/files/__tests__/file-field-column.integration.ts +0 -103
@@ -1,5 +1,6 @@
1
- import { and, getTableName, inArray, lt, sql } from "drizzle-orm";
2
1
  import type { DbConnection } from "../db/connection";
2
+ import { lockEventConsumersShareMode } from "../db/queries/event-consumer";
3
+ import { deleteMany, selectMany, transaction } from "../db/query";
3
4
  import { eventsTable } from "../event-store";
4
5
  import { eventConsumerStateTable } from "./event-consumer-state";
5
6
 
@@ -83,7 +84,7 @@ export async function pruneEvents(
83
84
  const aggregateTypes = options.aggregateTypes;
84
85
  const dryRun = options.dryRun === true;
85
86
 
86
- return db.transaction(async (tx) => {
87
+ return transaction(db, async (tx) => {
87
88
  // Serialise against consumer-bootstrap INSERTs. Without this, the race
88
89
  // is: prune reads consumers (snapshot misses a consumer bootstrapping
89
90
  // in a parallel tx) → consumer commits its row with
@@ -96,21 +97,13 @@ export async function pruneEvents(
96
97
  // (cursor advances) do too, but prune is measured in milliseconds and
97
98
  // pausing cursor advances for that window is cheap insurance against
98
99
  // a silent data-loss bug.
99
- //
100
- // Drizzle can't express LOCK TABLE — drop to raw SQL with the table
101
- // name identifier so a future table-rename is caught at compile time.
102
- await tx.execute(sql.raw(`LOCK TABLE ${getTableName(eventConsumerStateTable)} IN SHARE MODE`));
100
+ await lockEventConsumersShareMode(tx);
103
101
 
104
102
  // Step 1 — collect candidate event ids.
105
- const candidates = await tx
106
- .select({ id: eventsTable.id })
107
- .from(eventsTable)
108
- .where(
109
- and(
110
- inArray(eventsTable.aggregateType, [...aggregateTypes]),
111
- lt(eventsTable.createdAt, cutoff),
112
- ),
113
- );
103
+ const candidates = await selectMany<{ id: bigint }>(tx, eventsTable, {
104
+ aggregateType: [...aggregateTypes],
105
+ createdAt: { lt: cutoff },
106
+ });
114
107
 
115
108
  if (candidates.length === 0) {
116
109
  return { deletedCount: 0, cutoff, aggregateTypes, dryRun };
@@ -127,10 +120,10 @@ export async function pruneEvents(
127
120
  // The SHARE lock above guarantees this SELECT sees a complete view:
128
121
  // no new consumer can INSERT a fresh-cursor row between here and the
129
122
  // DELETE below.
130
- const activeConsumers = await tx
131
- .select()
132
- .from(eventConsumerStateTable)
133
- .where(sql`${eventConsumerStateTable.status} <> 'disabled'`);
123
+ const activeConsumers = await selectMany<{
124
+ name: string;
125
+ lastProcessedEventId: bigint;
126
+ }>(tx, eventConsumerStateTable, { status: { ne: "disabled" } });
134
127
 
135
128
  for (const consumer of activeConsumers) {
136
129
  if (consumer.lastProcessedEventId < maxCandidateId) {
@@ -144,11 +137,8 @@ export async function pruneEvents(
144
137
 
145
138
  // Step 3 — actual delete, bounded to the candidate set.
146
139
  const candidateIds = candidates.map((c) => c.id);
147
- const deleted = await tx
148
- .delete(eventsTable)
149
- .where(inArray(eventsTable.id, candidateIds))
150
- .returning({ id: eventsTable.id });
140
+ await deleteMany(tx, eventsTable, { id: candidateIds });
151
141
 
152
- return { deletedCount: deleted.length, cutoff, aggregateTypes, dryRun: false };
142
+ return { deletedCount: candidateIds.length, cutoff, aggregateTypes, dryRun: false };
153
143
  });
154
144
  }
@@ -1,5 +1,12 @@
1
- import { and, asc, eq, getTableName, inArray, sql } from "drizzle-orm";
2
- import type { DbConnection, DbRunner } from "../db/connection";
1
+ import type { DbConnection, DbRunner, DbTx } from "../db/connection";
2
+ import {
3
+ markConsumerRebuildFailed,
4
+ resetConsumerForMspRebuild,
5
+ selectConsumerForUpdate,
6
+ updateConsumerRebuildCursor,
7
+ } from "../db/queries/event-consumer";
8
+ import { truncateTable } from "../db/queries/table-ops";
9
+ import { selectMany } from "../db/query";
3
10
  import type { Registry, TenantId } from "../engine/types";
4
11
  import { InternalError } from "../errors";
5
12
  import { eventsTable, type StoredEvent, upcastStoredEvent } from "../event-store";
@@ -7,7 +14,7 @@ import { loadAggregate, loadAggregateAsOf } from "../event-store/event-store";
7
14
  import { upcastStoredEvents } from "../event-store/upcaster";
8
15
  import { emitProjectionRebuild } from "../observability/standard-metrics";
9
16
  import type { Meter } from "../observability/types/metric";
10
- import { eventConsumerStateTable, SHARED_INSTANCE_SENTINEL } from "./event-consumer-state";
17
+ import { SHARED_INSTANCE_SENTINEL } from "./event-consumer-state";
11
18
  import type { MultiStreamApplyContext } from "./multi-stream-apply-context";
12
19
  import type { RebuildResult } from "./projection-rebuild";
13
20
 
@@ -101,57 +108,35 @@ export async function rebuildMultiStreamProjection(
101
108
  let lastProcessedEventId = 0n;
102
109
 
103
110
  try {
104
- await db.transaction(async (tx) => {
105
- // Upsert + lock the consumer row. Rebuild always targets the
106
- // SHARED-delivery shard: per-instance MSPs are side-effect-only (no
107
- // table, so the guard above refuses them anyway), and rebuild's
108
- // purpose is to rematerialize one persistent read-model, not fan
109
- // out a local cache reset across instances. The FOR UPDATE on the
110
- // next SELECT is what blocks concurrent rebuilds of the same MSP;
111
- // live dispatcher passes use SKIP LOCKED on this row and will bail
112
- // silently while we hold it.
113
- await tx
114
- .insert(eventConsumerStateTable)
115
- .values({
116
- name: mspName,
117
- instanceId: SHARED_INSTANCE_SENTINEL,
118
- lastProcessedEventId: 0n,
119
- status: "idle",
120
- })
121
- .onConflictDoUpdate({
122
- target: [eventConsumerStateTable.name, eventConsumerStateTable.instanceId],
123
- set: {
124
- lastProcessedEventId: 0n,
125
- status: "idle",
126
- attempts: 0,
127
- lastError: null,
128
- updatedAt: sql`now()`,
129
- },
130
- });
131
- await tx
132
- .select()
133
- .from(eventConsumerStateTable)
134
- .where(
135
- and(
136
- eq(eventConsumerStateTable.name, mspName),
137
- eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
138
- ),
139
- )
140
- .for("update");
111
+ await db.begin(async (tx: DbTx) => {
112
+ await resetConsumerForMspRebuild(tx, mspName, SHARED_INSTANCE_SENTINEL);
113
+ await selectConsumerForUpdate(tx, mspName, SHARED_INSTANCE_SENTINEL);
141
114
 
142
- // msp.table is narrowed by the upfront guard; the assertion here is
143
- // for TS inside the async closure (narrowing doesn't cross the
144
- // transaction boundary).
145
- const tableName = getTableName(msp.table as NonNullable<typeof msp.table>); // @cast-boundary db-operator
146
- await tx.execute(sql.raw(`TRUNCATE TABLE ${quoteIdent(tableName)}`));
115
+ const mspTable = msp.table as NonNullable<typeof msp.table>;
116
+ const tableName = getTableName(mspTable);
117
+ await truncateTable(tx, tableName);
147
118
 
148
119
  const subscribedTypes = Object.keys(msp.apply);
149
120
  if (subscribedTypes.length > 0) {
150
- const events = (await tx
151
- .select()
152
- .from(eventsTable)
153
- .where(inArray(eventsTable.type, subscribedTypes))
154
- .orderBy(asc(eventsTable.id))) as ReadonlyArray<typeof eventsTable.$inferSelect>; // @cast-boundary db-row
121
+ type EventRow = {
122
+ id: bigint;
123
+ aggregateId: string;
124
+ aggregateType: string;
125
+ tenantId: TenantId;
126
+ version: number;
127
+ type: string;
128
+ eventVersion: number;
129
+ payload: Record<string, unknown>;
130
+ metadata: import("../event-store/event-store").EventMetadata;
131
+ createdAt: Temporal.Instant;
132
+ createdBy: string;
133
+ };
134
+ const events = await selectMany<EventRow>(
135
+ tx,
136
+ eventsTable,
137
+ { type: [...subscribedTypes] },
138
+ { orderBy: { col: "id", direction: "asc" } },
139
+ );
155
140
 
156
141
  const upcasters = registry.getEventUpcasters();
157
142
  for (const row of events) {
@@ -170,44 +155,27 @@ export async function rebuildMultiStreamProjection(
170
155
  };
171
156
  const storedEvent = await upcastStoredEvent(raw, upcasters, {
172
157
  db: tx,
173
- tenantId: row.tenantId as TenantId, // @cast-boundary db-row
158
+ tenantId: row.tenantId,
174
159
  });
175
160
  const applyFn = msp.apply[row.type];
176
161
  if (!applyFn) continue;
177
- const rebuildCtx = createRebuildCtx(registry, tx, row.tenantId as TenantId); // @cast-boundary db-row
162
+ const rebuildCtx = createRebuildCtx(registry, tx, row.tenantId);
178
163
  await applyFn(storedEvent, tx, rebuildCtx);
179
164
  eventsProcessed++;
180
165
  lastProcessedEventId = row.id;
181
166
  }
182
167
  }
183
168
 
184
- await tx
185
- .update(eventConsumerStateTable)
186
- .set({
187
- lastProcessedEventId,
188
- status: "idle",
189
- attempts: 0,
190
- lastError: null,
191
- updatedAt: sql`now()`,
192
- })
193
- .where(
194
- and(
195
- eq(eventConsumerStateTable.name, mspName),
196
- eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
197
- ),
198
- );
169
+ await updateConsumerRebuildCursor(
170
+ tx,
171
+ mspName,
172
+ SHARED_INSTANCE_SENTINEL,
173
+ lastProcessedEventId,
174
+ );
199
175
  });
200
176
  } catch (e) {
201
177
  const message = e instanceof Error ? e.message : String(e);
202
- await db
203
- .update(eventConsumerStateTable)
204
- .set({ status: "dead", lastError: message, updatedAt: sql`now()` })
205
- .where(
206
- and(
207
- eq(eventConsumerStateTable.name, mspName),
208
- eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
209
- ),
210
- );
178
+ await markConsumerRebuildFailed(db, mspName, SHARED_INSTANCE_SENTINEL, message);
211
179
  if (deps.meter) {
212
180
  emitProjectionRebuild(
213
181
  deps.meter,
@@ -237,6 +205,14 @@ export async function rebuildMultiStreamProjection(
237
205
  return result;
238
206
  }
239
207
 
240
- function quoteIdent(name: string): string {
241
- return `"${name.replace(/"/g, '""')}"`;
208
+ const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
209
+ function getTableName(table: unknown): string {
210
+ if (typeof table !== "object" || table === null) {
211
+ throw new InternalError({ message: "msp-rebuild: msp.table is not a pgTable object" });
212
+ }
213
+ const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
214
+ if (typeof name !== "string") {
215
+ throw new InternalError({ message: "msp-rebuild: msp.table missing drizzle name symbol" });
216
+ }
217
+ return name;
242
218
  }
@@ -1,5 +1,12 @@
1
- import { asc, eq, getTableName, inArray, sql } from "drizzle-orm";
2
- import type { DbConnection } from "../db/connection";
1
+ import type { DbConnection, DbTx } from "../db/connection";
2
+ import {
3
+ finalizeProjectionRebuild,
4
+ markProjectionRebuildFailed,
5
+ markProjectionRebuilding,
6
+ selectEventsForProjectionRebuild,
7
+ } from "../db/queries/projection-rebuild";
8
+ import { truncateTable } from "../db/queries/table-ops";
9
+ import { coerceRow, extractTableInfo, selectMany } from "../db/query";
3
10
  import type { Registry, TenantId } from "../engine/types";
4
11
  import {
5
12
  eventsTable,
@@ -86,53 +93,43 @@ export async function rebuildProjection(
86
93
  let lastProcessedEventId = 0n;
87
94
 
88
95
  try {
89
- await db.transaction(async (tx) => {
90
- // Lock the state row. Use upsert so a never-rebuilt projection also
91
- // gets a row. FOR UPDATE would need the row to exist — upsert-first
92
- // keeps it idempotent.
93
- await tx
94
- .insert(projectionStateTable)
95
- .values({ name: projectionName, status: "rebuilding" })
96
- .onConflictDoUpdate({
97
- target: projectionStateTable.name,
98
- set: {
99
- status: "rebuilding",
100
- lastError: null,
101
- updatedAt: sql`now()`,
102
- },
103
- });
96
+ await db.begin(async (tx: DbTx) => {
97
+ await markProjectionRebuilding(tx, projectionName);
104
98
 
105
- // Wipe the projection table. drizzle-orm's public API doesn't expose
106
- // TRUNCATE, so we issue raw SQL — but `getTableName()` is the public
107
- // accessor for the table's registered name, avoiding Symbol.for()
108
- // internal lookups. The identifier is still quoted defensively.
109
99
  const tableName = getTableName(projection.table);
110
- await tx.execute(sql.raw(`TRUNCATE TABLE ${quoteIdent(tableName)}`));
100
+ await truncateTable(tx, tableName);
111
101
 
112
102
  // Stream events in chronological order for every source. The event
113
- // type filter (inArray(type, validTypes)) prunes events the projection
114
- // doesn't care about early — important when a single source has more
115
- // event types than the projection subscribes to.
103
+ // type filter prunes events the projection doesn't care about early.
116
104
  const subscribed = Object.keys(projection.apply);
117
105
  if (subscribed.length === 0) {
118
106
  // nothing to replay, just mark idle — projection exists but doesn't
119
107
  // subscribe to any event types on its sources yet.
120
108
  } else {
121
- const events = (await tx
122
- .select()
123
- .from(eventsTable)
124
- .where(
125
- sql`${inArray(eventsTable.aggregateType, sources)} AND ${inArray(
126
- eventsTable.type,
127
- subscribed,
128
- )}`,
129
- )
130
- .orderBy(asc(eventsTable.id))) as ReadonlyArray<typeof eventsTable.$inferSelect>; // @cast-boundary db-row
109
+ type EventRow = {
110
+ id: bigint;
111
+ aggregateId: string;
112
+ aggregateType: string;
113
+ tenantId: string;
114
+ version: number;
115
+ type: string;
116
+ eventVersion: number;
117
+ payload: Record<string, unknown>;
118
+ metadata: import("../event-store/event-store").EventMetadata;
119
+ createdAt: Temporal.Instant;
120
+ createdBy: string;
121
+ };
122
+ const sourcesList = [...sources];
123
+ const subscribedList = [...subscribed];
124
+ const rawEvents = await selectEventsForProjectionRebuild(tx, sourcesList, subscribedList);
125
+ const events = rawEvents.map((r) => {
126
+ const info = extractTableInfo(eventsTable);
127
+ return coerceRow(r, info) as EventRow;
128
+ });
131
129
 
132
130
  // Upcasters run at read time: older stored payloads get walked
133
131
  // through the registered r.eventMigration chain until their shape
134
- // matches the current event version. An apply() written against the
135
- // v3 shape stays oblivious to v1 payloads still on disk.
132
+ // matches the current event version.
136
133
  const upcasters = registry.getEventUpcasters();
137
134
  for (const row of events) {
138
135
  deps.signal?.throwIfAborted();
@@ -163,30 +160,13 @@ export async function rebuildProjection(
163
160
  }
164
161
  }
165
162
 
166
- // Finalize state row.
167
- await tx
168
- .update(projectionStateTable)
169
- .set({
170
- lastProcessedEventId,
171
- status: "idle",
172
- lastRebuildAt: sql`now()`,
173
- lastError: null,
174
- updatedAt: sql`now()`,
175
- })
176
- .where(eq(projectionStateTable.name, projectionName));
163
+ await finalizeProjectionRebuild(tx, projectionName, lastProcessedEventId);
177
164
  });
178
165
  } catch (e) {
179
166
  // Outer catch: TX has been rolled back by Postgres already. Record the
180
- // failure in a SEPARATE write so ops can see what happened — the
181
- // rolled-back status change is gone, so we write failed+error now.
167
+ // failure in a SEPARATE write so ops can see what happened.
182
168
  const message = e instanceof Error ? e.message : String(e);
183
- await db
184
- .insert(projectionStateTable)
185
- .values({ name: projectionName, status: "failed", lastError: message })
186
- .onConflictDoUpdate({
187
- target: projectionStateTable.name,
188
- set: { status: "failed", lastError: message, updatedAt: sql`now()` },
189
- });
169
+ await markProjectionRebuildFailed(db, projectionName, message);
190
170
  // Failure metric: duration until throw, 0 events "delivered" (the replayed
191
171
  // rows were rolled back — counting them would overstate live delivery).
192
172
  // success=false label distinguishes these in Prom dashboards.
@@ -219,11 +199,16 @@ export async function rebuildProjection(
219
199
  return result;
220
200
  }
221
201
 
222
- // Identifier quoting for raw TRUNCATE. Drizzle doesn't expose a safe helper
223
- // for table-name interpolation in raw SQL; double-quote + escape double-quote
224
- // matches Postgres identifier rules.
225
- function quoteIdent(name: string): string {
226
- return `"${name.replace(/"/g, '""')}"`;
202
+ const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
203
+ function getTableName(table: unknown): string {
204
+ if (typeof table !== "object" || table === null) {
205
+ throw new Error("projection-rebuild: projection.table is not a pgTable object");
206
+ }
207
+ const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
208
+ if (typeof name !== "string") {
209
+ throw new Error("projection-rebuild: projection.table missing drizzle name symbol");
210
+ }
211
+ return name;
227
212
  }
228
213
 
229
214
  // Read-only status for one projection. Returns null if the projection was
@@ -239,10 +224,15 @@ export async function getProjectionState(
239
224
  readonly lastError: string | null;
240
225
  readonly updatedAt: Temporal.Instant;
241
226
  } | null> {
242
- const [row] = await db
243
- .select()
244
- .from(projectionStateTable)
245
- .where(eq(projectionStateTable.name, projectionName));
227
+ type Row = {
228
+ readonly name: string;
229
+ readonly status: string;
230
+ readonly lastProcessedEventId: bigint;
231
+ readonly lastRebuildAt: Temporal.Instant | null;
232
+ readonly lastError: string | null;
233
+ readonly updatedAt: Temporal.Instant;
234
+ };
235
+ const [row] = await selectMany<Row>(db, projectionStateTable, { name: projectionName });
246
236
  if (!row) return null;
247
237
  return {
248
238
  name: row.name,
@@ -277,8 +267,16 @@ export async function listProjectionsWithState(
277
267
  readonly lastError: string | null;
278
268
  }>
279
269
  > {
270
+ type StateRow = {
271
+ name: string;
272
+ status: string;
273
+ lastProcessedEventId: bigint;
274
+ lastRebuildAt: Temporal.Instant | null;
275
+ lastError: string | null;
276
+ updatedAt: Temporal.Instant;
277
+ };
280
278
  const projections = registry.getAllProjections();
281
- const stateRows = await db.select().from(projectionStateTable);
279
+ const stateRows = await selectMany<StateRow>(db, projectionStateTable);
282
280
  const stateByName = new Map(stateRows.map((r) => [r.name, r]));
283
281
 
284
282
  return [...projections.values()]
@@ -1,6 +1,6 @@
1
- import { sql } from "drizzle-orm";
1
+ // sql now comes from native dialect
2
2
  import type { DbConnection } from "../db/connection";
3
- import { bigint, index, instant, table as pgTable, text } from "../db/dialect";
3
+ import { bigint, index, instant, table as pgTable, sql, text } from "../db/dialect";
4
4
  import { tableExists } from "../db/schema-inspection";
5
5
  import { unsafePushTables } from "../stack";
6
6
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import {
3
3
  ADJECTIVES,
4
4
  generateAdjNounName,
@@ -12,16 +12,16 @@ describe("generateAdjNounName", () => {
12
12
  const name = generateAdjNounName();
13
13
  const [adj, noun, ...rest] = name.split("-");
14
14
  expect(rest).toEqual([]);
15
- expect(ADJECTIVES).toContain(adj);
16
- expect(NOUNS).toContain(noun);
15
+ expect(ADJECTIVES).toContain(adj!);
16
+ expect(NOUNS).toContain(noun!);
17
17
  });
18
18
 
19
19
  test("custom separator", () => {
20
20
  const name = generateAdjNounName({ separator: "_" });
21
21
  expect(name).toMatch(/^[a-z]+_[a-z]+$/);
22
22
  const [adj, noun] = name.split("_");
23
- expect(ADJECTIVES).toContain(adj);
24
- expect(NOUNS).toContain(noun);
23
+ expect(ADJECTIVES).toContain(adj!);
24
+ expect(NOUNS).toContain(noun!);
25
25
  });
26
26
 
27
27
  test("mit suffix: <adj>-<noun>-<suffix>", () => {
@@ -29,11 +29,11 @@ describe("generateAdjNounName", () => {
29
29
  const parts = name.split("-");
30
30
  expect(parts).toHaveLength(3);
31
31
  const [adj, noun, suffix] = parts;
32
- expect(ADJECTIVES).toContain(adj);
33
- expect(NOUNS).toContain(noun);
34
- expect(suffix).toMatch(/^[a-z2-9]{3}$/);
32
+ expect(ADJECTIVES).toContain(adj!);
33
+ expect(NOUNS).toContain(noun!);
34
+ expect(suffix!).toMatch(/^[a-z2-9]{3}$/);
35
35
  // No-confusable: keine 0/1/o/l/i
36
- expect(suffix).not.toMatch(/[01oli]/);
36
+ expect(suffix!).not.toMatch(/[01oli]/);
37
37
  });
38
38
 
39
39
  test("custom adjectives + nouns Wahl", () => {
@@ -94,7 +94,7 @@ describe("generateUniqueName", () => {
94
94
  },
95
95
  });
96
96
  expect(seen).toHaveLength(1);
97
- expect(name).toBe(seen[0]);
97
+ expect(name).toBe(seen[0]!);
98
98
  // Clean = nur 2 Teile (adj + noun), kein suffix
99
99
  expect(name.split("-")).toHaveLength(2);
100
100
  });
@@ -112,11 +112,11 @@ describe("generateUniqueName", () => {
112
112
  expect(tried).toHaveLength(4);
113
113
  // Erste 3 ohne suffix
114
114
  for (let i = 0; i < 3; i++) {
115
- expect(tried[i]?.split("-")).toHaveLength(2);
115
+ expect(tried[i]!.split("-")).toHaveLength(2);
116
116
  }
117
117
  // 4. Versuch hat suffix
118
- expect(tried[3]?.split("-")).toHaveLength(3);
119
- expect(name).toBe(tried[3]);
118
+ expect(tried[3]!.split("-")).toHaveLength(3);
119
+ expect(name).toBe(tried[3]!);
120
120
  });
121
121
 
122
122
  test("wirft wenn maxTotalAttempts erschöpft", async () => {
@@ -1,5 +1,5 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
1
2
  import { defineFeature, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
2
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
3
3
  import { z } from "zod";
4
4
  import { createTestUser, setupTestStack, type TestStack, TestUsers } from "../../stack";
5
5
 
@@ -1,5 +1,5 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
1
2
  import { Hono } from "hono";
2
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
3
3
  import { createTestRedis, type TestRedis } from "../../stack";
4
4
  import { authEndpointRateLimit, globalIpRateLimit } from "../middleware";
5
5
  import { createRateLimitResolver, type RateLimitResolver } from "../resolver";
@@ -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 { RateLimitError } from "../../errors";
3
3
  import { createTestRedis, type TestRedis } from "../../stack";
4
4
  import {
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { redisClientOptionsFromEnv } from "../index";
3
3
 
4
4
  describe("redisClientOptionsFromEnv", () => {
@@ -1,7 +1,7 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
3
  import { generateId as uuid } from "@cosmicdrift/kumiko-framework/utils";
3
4
  import { Meilisearch } from "meilisearch";
4
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
5
5
  import { createMeilisearchAdapter } from "../meilisearch-adapter";
6
6
  import type { SearchAdapter } from "../types";
7
7
 
@@ -1,4 +1,4 @@
1
- import { beforeEach, describe, expect, test } from "vitest";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
  import { createInMemorySearchAdapter } from "../in-memory-adapter";
3
3
  import type { SearchAdapter } from "../types";
4
4
 
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import { randomBytes } from "node:crypto";
2
- import { describe, expect, test, vi } from "vitest";
3
3
  import { createDekCache } from "../dek-cache";
4
4
  import type { MasterKeyProvider } from "../types";
5
5
 
@@ -153,8 +153,6 @@ describe("DekCache", () => {
153
153
  t = 5 * 60 * 1000 + 1; // just past 5 min
154
154
  await cache.unwrapDek(Buffer.from("x"), 1, provider);
155
155
  expect(provider.unwrapCallCount()).toBe(2);
156
- // avoid vitest unused-import warning
157
- void vi;
158
156
  });
159
157
 
160
158
  test("LRU: evicts oldest when maxEntries is reached", async () => {
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import { randomBytes } from "node:crypto";
2
- import { describe, expect, test } from "vitest";
3
3
  import { createEnvMasterKeyProvider } from "../env-master-key-provider";
4
4
 
5
5
  function env(vars: Record<string, string>): Record<string, string> {
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import { randomBytes } from "node:crypto";
2
- import { describe, expect, test } from "vitest";
3
3
  import { createEnvMasterKeyProvider } from "../env-master-key-provider";
4
4
  import { decryptValue, encryptValue } from "../envelope";
5
5
 
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { assertNoSecretLeak } from "../leak-guard";
3
3
  import { createSecret } from "../types";
4
4
 
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import { randomBytes } from "node:crypto";
2
- import { describe, expect, test } from "vitest";
3
3
  import { createEnvMasterKeyProvider } from "../env-master-key-provider";
4
4
  import { decryptValue, encryptValue } from "../envelope";
5
5
  import { rewrapDek } from "../rotation";