@cosmicdrift/kumiko-framework 0.14.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 +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/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 -474
  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
@@ -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";
package/src/stack/db.ts CHANGED
@@ -1,5 +1,10 @@
1
- import { drizzle } from "drizzle-orm/postgres-js";
2
- import postgres from "postgres";
1
+ // Test-DB Factory: CREATE DATABASE → connect → createEventsTable.
2
+ // Provider-agnostic via createConnection (DB_PROVIDER env).
3
+ // postgres-js = default. DB_PROVIDER=bun = Bun.SQL (experimentell).
4
+
5
+ import { createConnection } from "../db/api";
6
+ import { createDatabase, databaseExists, dropDatabaseIfExists } from "../db/queries/test-stack";
7
+ import { ensureTemporalPolyfill } from "../time/polyfill";
3
8
  import { generateId } from "../utils";
4
9
 
5
10
  function requireEnv(name: string): string {
@@ -13,88 +18,60 @@ function requireEnv(name: string): string {
13
18
  }
14
19
 
15
20
  export type TestDb = {
16
- db: ReturnType<typeof drizzle>;
17
- client: ReturnType<typeof postgres>;
21
+ db: unknown;
22
+ client: unknown;
18
23
  dbName: string;
19
24
  cleanup: () => Promise<void>;
20
25
  };
21
26
 
22
27
  export type CreateTestDbOptions = {
23
- /** Override TEST_DATABASE_URL. Rare — mostly for tests that want a
24
- * non-default Postgres (e.g. a read-replica probe). */
25
28
  readonly baseUrl?: string;
26
- /** Use a specific DB name instead of the default
27
- * `kumiko_test_<8chars>`. Combined with `persistent: true`, lets a
28
- * dev server keep state across restarts. Must be a legal Postgres
29
- * identifier — the caller is responsible for matching the usual
30
- * [a-z_0-9]+ shape. */
31
29
  readonly dbName?: string;
32
- /** When true, cleanup() is a no-op and the DB survives. Also
33
- * changes CREATE DATABASE to IF-NOT-EXISTS semantics so restarts
34
- * reuse the same storage. Default false (test contract: fresh DB
35
- * per call, dropped on cleanup). */
36
30
  readonly persistent?: boolean;
37
31
  };
38
32
 
39
33
  /**
40
- * Accepts a baseUrl string (legacy shorthand used by most tests) OR an
41
- * options object. The string form is kept because thousands of tests
42
- * call `createTestDb()` with no args; only dev-server and niche tests
43
- * need the options form.
34
+ * Provider-agnostische Test-DB. createConnection liest DB_PROVIDER.
35
+ * Für Bun.SQL: DB_PROVIDER=bun setzen (experimentell siehe db/bun-provider.ts).
44
36
  */
45
37
  export async function createTestDb(arg?: string | CreateTestDbOptions): Promise<TestDb> {
38
+ await ensureTemporalPolyfill();
46
39
  const opts: CreateTestDbOptions = typeof arg === "string" ? { baseUrl: arg } : (arg ?? {});
47
40
  const url = opts.baseUrl ?? requireEnv("TEST_DATABASE_URL");
48
- // slice(-8) — the last 8 hex chars of a UUIDv7 are pure random (the
49
- // front 48 bits are a timestamp, which would collide across workers
50
- // that start within the same millisecond).
51
41
  const dbName = opts.dbName ?? `kumiko_test_${generateId().slice(-8)}`;
52
42
  const adminUrl = url.replace(/\/[^/]+$/, "/postgres");
53
43
 
54
- const adminClient = postgres(adminUrl);
44
+ const admin = await createConnection(adminUrl, { maxConnections: 1 });
55
45
  try {
56
46
  if (opts.persistent) {
57
- // Postgres has no CREATE DATABASE IF NOT EXISTS; emulate with a
58
- // catalog probe so restarts are idempotent.
59
- const existing = await adminClient<{ exists: boolean }[]>`
60
- SELECT EXISTS (SELECT 1 FROM pg_database WHERE datname = ${dbName}) AS exists
61
- `;
62
- if (!existing[0]?.exists) {
63
- await adminClient.unsafe(`CREATE DATABASE "${dbName}"`);
47
+ if (!(await databaseExists(admin.db, dbName))) {
48
+ await createDatabase(admin.db, dbName);
64
49
  }
65
50
  } else {
66
- await adminClient.unsafe(`CREATE DATABASE "${dbName}"`);
51
+ await createDatabase(admin.db, dbName);
67
52
  }
68
53
  } finally {
69
- await adminClient.end();
54
+ await admin.close();
70
55
  }
71
56
 
72
57
  const testUrl = url.replace(/\/[^/]+$/, `/${dbName}`);
73
- const client = postgres(testUrl);
74
- const db = drizzle(client);
58
+ const conn = await createConnection(testUrl);
75
59
 
76
- // Every ES-entity writes events; auto-create the events table so tests that
77
- // go straight to createTestDb (not setupTestStack) also work out of the box.
78
- // In persistent mode this is idempotent: createEventsTable emits IF NOT
79
- // EXISTS so a second boot is a no-op.
80
60
  const { createEventsTable } = await import("../event-store");
81
- await createEventsTable(db);
61
+ await createEventsTable(conn.db);
82
62
 
83
63
  return {
84
- db,
85
- client,
64
+ db: conn.db,
65
+ client: conn.client,
86
66
  dbName,
87
67
  cleanup: async () => {
88
- await client.end();
89
- // Persistent mode: dev-server owns the DB lifecycle — don't drop
90
- // on process exit. `yarn kumiko clean-test-dbs` is the escape
91
- // hatch when you really want to start over.
68
+ await conn.close();
92
69
  if (!opts.persistent) {
93
- const admin = postgres(adminUrl);
70
+ const admin2 = await createConnection(adminUrl, { maxConnections: 1 });
94
71
  try {
95
- await admin.unsafe(`DROP DATABASE IF EXISTS "${dbName}"`);
72
+ await dropDatabaseIfExists(admin2.db, dbName);
96
73
  } finally {
97
- await admin.end();
74
+ await admin2.close();
98
75
  }
99
76
  }
100
77
  },
@@ -1,4 +1,3 @@
1
- import { getTableName } from "drizzle-orm";
2
1
  import { tableExists } from "../db/schema-inspection";
3
2
  import type { Registry } from "../engine/types";
4
3
  import { unsafePushTables } from "./table-helpers";
@@ -35,9 +34,8 @@ export async function pushEntityProjectionTables(
35
34
  if (!proj.isImplicit) continue;
36
35
  if (seen.has(proj.table)) continue;
37
36
  seen.add(proj.table);
38
- // @cast-boundary drizzle-bridge ProjectionTable + PgTable both round-trip
39
- // through getTableName at runtime; the type system can't unify them.
40
- const physical = getTableName(proj.table as Parameters<typeof getTableName>[0]);
37
+ const tableRec = proj.table as unknown as Record<symbol, unknown>;
38
+ const physical = tableRec[Symbol.for("kumiko:schema:Name")] as string;
41
39
  if (await tableExists(stack.db, `public.${physical}`)) {
42
40
  logInfo(`[kumiko-stack] table ${physical} already exists — skipping create`);
43
41
  continue;