@fragno-dev/db 0.1.14 → 0.2.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 (445) hide show
  1. package/.turbo/turbo-build.log +242 -139
  2. package/CHANGELOG.md +47 -0
  3. package/README.md +123 -8
  4. package/dist/adapters/adapters.d.ts +19 -5
  5. package/dist/adapters/adapters.d.ts.map +1 -1
  6. package/dist/adapters/adapters.js.map +1 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts +6 -19
  8. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js +7 -47
  10. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  11. package/dist/adapters/drizzle/generate.d.ts +7 -1
  12. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  13. package/dist/adapters/drizzle/generate.js +46 -45
  14. package/dist/adapters/drizzle/generate.js.map +1 -1
  15. package/dist/adapters/generic-sql/driver-config.d.ts +74 -0
  16. package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -0
  17. package/dist/adapters/generic-sql/driver-config.js +94 -0
  18. package/dist/adapters/generic-sql/driver-config.js.map +1 -0
  19. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +43 -0
  20. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -0
  21. package/dist/adapters/generic-sql/generic-sql-adapter.js +87 -0
  22. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -0
  23. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +67 -0
  24. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -0
  25. package/dist/adapters/generic-sql/migration/cold-kysely.js +33 -0
  26. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -0
  27. package/dist/adapters/generic-sql/migration/dialect/mysql.js +60 -0
  28. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -0
  29. package/dist/adapters/generic-sql/migration/dialect/postgres.js +59 -0
  30. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -0
  31. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +96 -0
  32. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -0
  33. package/dist/adapters/generic-sql/migration/executor.d.ts +15 -0
  34. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  35. package/dist/adapters/generic-sql/migration/executor.js +18 -0
  36. package/dist/adapters/generic-sql/migration/executor.js.map +1 -0
  37. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  38. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  39. package/dist/adapters/generic-sql/migration/prepared-migrations.js +68 -0
  40. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -0
  41. package/dist/adapters/generic-sql/migration/sql-generator.js +212 -0
  42. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -0
  43. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +32 -0
  44. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -0
  45. package/dist/adapters/generic-sql/query/cursor-utils.js +37 -0
  46. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -0
  47. package/dist/adapters/generic-sql/query/dialect/mysql.js +33 -0
  48. package/dist/adapters/generic-sql/query/dialect/mysql.js.map +1 -0
  49. package/dist/adapters/generic-sql/query/dialect/postgres.js +32 -0
  50. package/dist/adapters/generic-sql/query/dialect/postgres.js.map +1 -0
  51. package/dist/adapters/generic-sql/query/dialect/sqlite.js +32 -0
  52. package/dist/adapters/generic-sql/query/dialect/sqlite.js.map +1 -0
  53. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +152 -0
  54. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -0
  55. package/dist/adapters/generic-sql/query/select-builder.js +69 -0
  56. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -0
  57. package/dist/adapters/generic-sql/query/sql-query-compiler.js +145 -0
  58. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -0
  59. package/dist/adapters/generic-sql/query/where-builder.js +129 -0
  60. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -0
  61. package/dist/adapters/generic-sql/result-interpreter.js +74 -0
  62. package/dist/adapters/generic-sql/result-interpreter.js.map +1 -0
  63. package/dist/adapters/generic-sql/uow-decoder.js +105 -0
  64. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -0
  65. package/dist/adapters/generic-sql/uow-encoder.js +93 -0
  66. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -0
  67. package/dist/adapters/kysely/kysely-adapter.d.ts +5 -16
  68. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  69. package/dist/adapters/kysely/kysely-adapter.js +6 -159
  70. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  71. package/dist/adapters/{drizzle/drizzle-query.js → shared/from-unit-of-work-compiler.js} +48 -62
  72. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -0
  73. package/dist/adapters/{kysely/kysely-shared.d.ts → shared/table-name-mapper.d.ts} +3 -2
  74. package/dist/adapters/shared/table-name-mapper.d.ts.map +1 -0
  75. package/dist/adapters/shared/table-name-mapper.js +43 -0
  76. package/dist/adapters/shared/table-name-mapper.js.map +1 -0
  77. package/dist/adapters/shared/uow-operation-compiler.js +105 -0
  78. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -0
  79. package/dist/db-fragment-definition-builder.d.ts +186 -0
  80. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  81. package/dist/db-fragment-definition-builder.js +207 -0
  82. package/dist/db-fragment-definition-builder.js.map +1 -0
  83. package/dist/fragments/internal-fragment.d.ts +53 -0
  84. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  85. package/dist/fragments/internal-fragment.js +111 -0
  86. package/dist/fragments/internal-fragment.js.map +1 -0
  87. package/dist/hooks/hooks.d.ts +51 -0
  88. package/dist/hooks/hooks.d.ts.map +1 -0
  89. package/dist/hooks/hooks.js +88 -0
  90. package/dist/hooks/hooks.js.map +1 -0
  91. package/dist/migration-engine/generation-engine.d.ts +0 -2
  92. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  93. package/dist/migration-engine/generation-engine.js +38 -56
  94. package/dist/migration-engine/generation-engine.js.map +1 -1
  95. package/dist/mod.d.ts +35 -23
  96. package/dist/mod.d.ts.map +1 -1
  97. package/dist/mod.js +48 -45
  98. package/dist/mod.js.map +1 -1
  99. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js +165 -0
  100. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +1 -0
  101. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  102. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  103. package/dist/packages/fragno/dist/api/error.js +48 -0
  104. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  105. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  106. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  107. package/dist/packages/fragno/dist/api/fragment-instantiator.js +525 -0
  108. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  109. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  110. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  111. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  112. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  113. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  114. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  115. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  116. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  117. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  118. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  119. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  120. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  121. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  122. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  123. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  124. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  125. package/dist/packages/fragno/dist/api/route.js +17 -0
  126. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  127. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  128. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  129. package/dist/query/column-defaults.js +27 -0
  130. package/dist/query/column-defaults.js.map +1 -0
  131. package/dist/query/cursor.d.ts +14 -6
  132. package/dist/query/cursor.d.ts.map +1 -1
  133. package/dist/query/cursor.js +16 -7
  134. package/dist/query/cursor.js.map +1 -1
  135. package/dist/query/orm/orm.d.ts +1 -1
  136. package/dist/query/orm/orm.js.map +1 -1
  137. package/dist/query/serialize/create-sql-serializer.js +30 -0
  138. package/dist/query/serialize/create-sql-serializer.js.map +1 -0
  139. package/dist/query/serialize/dialect/mysql-serializer.js +87 -0
  140. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -0
  141. package/dist/query/serialize/dialect/postgres-serializer.js +80 -0
  142. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -0
  143. package/dist/query/serialize/dialect/sqlite-serializer.js +93 -0
  144. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -0
  145. package/dist/query/serialize/sql-serializer.js +67 -0
  146. package/dist/query/serialize/sql-serializer.js.map +1 -0
  147. package/dist/query/{query.d.ts → simple-query-interface.d.ts} +6 -6
  148. package/dist/query/simple-query-interface.d.ts.map +1 -0
  149. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +133 -0
  150. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  151. package/dist/query/unit-of-work/execute-unit-of-work.js +197 -0
  152. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  153. package/dist/query/unit-of-work/retry-policy.d.ts +88 -0
  154. package/dist/query/unit-of-work/retry-policy.d.ts.map +1 -0
  155. package/dist/query/unit-of-work/retry-policy.js +61 -0
  156. package/dist/query/unit-of-work/retry-policy.js.map +1 -0
  157. package/dist/query/{unit-of-work.d.ts → unit-of-work/unit-of-work.d.ts} +145 -58
  158. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  159. package/dist/query/{unit-of-work.js → unit-of-work/unit-of-work.js} +435 -198
  160. package/dist/query/unit-of-work/unit-of-work.js.map +1 -0
  161. package/dist/query/value-decoding.js +71 -0
  162. package/dist/query/value-decoding.js.map +1 -0
  163. package/dist/query/value-encoding.js +124 -0
  164. package/dist/query/value-encoding.js.map +1 -0
  165. package/dist/schema/create.d.ts +3 -0
  166. package/dist/schema/create.d.ts.map +1 -1
  167. package/dist/schema/create.js +4 -0
  168. package/dist/schema/create.js.map +1 -1
  169. package/dist/schema/type-conversion/create-sql-type-mapper.js +29 -0
  170. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -0
  171. package/dist/schema/type-conversion/dialect/mysql.js +57 -0
  172. package/dist/schema/type-conversion/dialect/mysql.js.map +1 -0
  173. package/dist/schema/type-conversion/dialect/postgres.js +56 -0
  174. package/dist/schema/type-conversion/dialect/postgres.js.map +1 -0
  175. package/dist/schema/type-conversion/dialect/sqlite.js +52 -0
  176. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -0
  177. package/dist/schema/type-conversion/type-mapping.js +63 -0
  178. package/dist/schema/type-conversion/type-mapping.js.map +1 -0
  179. package/dist/sql-driver/connection/connection-provider.d.ts +13 -0
  180. package/dist/sql-driver/connection/connection-provider.d.ts.map +1 -0
  181. package/dist/sql-driver/connection/connection-provider.js +19 -0
  182. package/dist/sql-driver/connection/connection-provider.js.map +1 -0
  183. package/dist/sql-driver/connection/single-connection-provider.js +23 -0
  184. package/dist/sql-driver/connection/single-connection-provider.js.map +1 -0
  185. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  186. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  187. package/dist/sql-driver/dialects/dialects.d.ts +2 -0
  188. package/dist/sql-driver/dialects/dialects.js +3 -0
  189. package/dist/sql-driver/dialects/durable-object-dialect.d.ts +72 -0
  190. package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -0
  191. package/dist/sql-driver/dialects/durable-object-dialect.js +130 -0
  192. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -0
  193. package/dist/sql-driver/driver/runtime-driver.d.ts +23 -0
  194. package/dist/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  195. package/dist/sql-driver/driver/runtime-driver.js +56 -0
  196. package/dist/sql-driver/driver/runtime-driver.js.map +1 -0
  197. package/dist/sql-driver/query-executor/default-query-executor.js +26 -0
  198. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -0
  199. package/dist/sql-driver/query-executor/plugin.d.ts +17 -0
  200. package/dist/sql-driver/query-executor/plugin.d.ts.map +1 -0
  201. package/dist/sql-driver/query-executor/query-executor-base.js +25 -0
  202. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -0
  203. package/dist/sql-driver/query-executor/query-executor.d.ts +36 -0
  204. package/dist/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  205. package/dist/sql-driver/sql-driver-adapter.d.ts +29 -0
  206. package/dist/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  207. package/dist/sql-driver/sql-driver-adapter.js +68 -0
  208. package/dist/sql-driver/sql-driver-adapter.js.map +1 -0
  209. package/dist/sql-driver/sql-driver.d.ts +38 -0
  210. package/dist/sql-driver/sql-driver.d.ts.map +1 -0
  211. package/dist/sql-driver/sql-driver.js +1 -0
  212. package/dist/sql-driver/sql.js +50 -0
  213. package/dist/sql-driver/sql.js.map +1 -0
  214. package/dist/with-database.d.ts +32 -0
  215. package/dist/with-database.d.ts.map +1 -0
  216. package/dist/with-database.js +34 -0
  217. package/dist/with-database.js.map +1 -0
  218. package/package.json +43 -9
  219. package/src/adapters/adapters.ts +23 -4
  220. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +140 -185
  221. package/src/adapters/drizzle/{drizzle-adapter-sqlite.test.ts → drizzle-adapter-sqlite3.test.ts} +187 -55
  222. package/src/adapters/drizzle/drizzle-adapter.ts +14 -93
  223. package/src/adapters/drizzle/generate.test.ts +102 -269
  224. package/src/adapters/drizzle/generate.ts +89 -63
  225. package/src/adapters/drizzle/migrate-drizzle.test.ts +19 -0
  226. package/src/adapters/drizzle/shared.ts +0 -34
  227. package/src/adapters/drizzle/test-utils.ts +36 -5
  228. package/src/adapters/generic-sql/README.md +14 -0
  229. package/src/adapters/generic-sql/driver-config.ts +144 -0
  230. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +50 -0
  231. package/src/adapters/generic-sql/generic-sql-adapter.ts +146 -0
  232. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +130 -0
  233. package/src/adapters/generic-sql/migration/cold-kysely.ts +55 -0
  234. package/src/adapters/{kysely/migration/execute-mysql.test.ts → generic-sql/migration/dialect/mysql.test.ts} +342 -484
  235. package/src/adapters/generic-sql/migration/dialect/mysql.ts +104 -0
  236. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +1008 -0
  237. package/src/adapters/generic-sql/migration/dialect/postgres.ts +113 -0
  238. package/src/adapters/{kysely/migration/execute-sqlite.test.ts → generic-sql/migration/dialect/sqlite.test.ts} +307 -510
  239. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +189 -0
  240. package/src/adapters/generic-sql/migration/executor.ts +33 -0
  241. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +661 -0
  242. package/src/adapters/generic-sql/migration/prepared-migrations.ts +214 -0
  243. package/src/adapters/generic-sql/migration/sql-generator.ts +413 -0
  244. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +36 -0
  245. package/src/adapters/generic-sql/query/cursor-utils.ts +56 -0
  246. package/src/adapters/generic-sql/query/dialect/mysql.ts +34 -0
  247. package/src/adapters/generic-sql/query/dialect/postgres.ts +32 -0
  248. package/src/adapters/generic-sql/query/dialect/sqlite.ts +32 -0
  249. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +1568 -0
  250. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +314 -0
  251. package/src/adapters/generic-sql/query/select-builder.test.ts +256 -0
  252. package/src/adapters/generic-sql/query/select-builder.ts +137 -0
  253. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +195 -0
  254. package/src/adapters/generic-sql/query/sql-query-compiler.ts +367 -0
  255. package/src/adapters/generic-sql/query/where-builder.test.ts +744 -0
  256. package/src/adapters/generic-sql/query/where-builder.ts +211 -0
  257. package/src/adapters/generic-sql/result-interpreter.ts +102 -0
  258. package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +899 -0
  259. package/src/adapters/generic-sql/uow-decoder.test.ts +399 -0
  260. package/src/adapters/generic-sql/uow-decoder.ts +152 -0
  261. package/src/adapters/generic-sql/uow-encoder.test.ts +183 -0
  262. package/src/adapters/generic-sql/uow-encoder.ts +131 -0
  263. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +90 -96
  264. package/src/adapters/kysely/kysely-adapter-sqlocal.test.ts +215 -0
  265. package/src/adapters/kysely/kysely-adapter.ts +10 -242
  266. package/src/adapters/{drizzle/drizzle-query.ts → shared/from-unit-of-work-compiler.ts} +111 -106
  267. package/src/adapters/shared/table-name-mapper.ts +50 -0
  268. package/src/adapters/shared/uow-operation-compiler.ts +211 -0
  269. package/src/db-fragment-definition-builder.test.ts +887 -0
  270. package/src/db-fragment-definition-builder.ts +737 -0
  271. package/src/db-fragment-instantiator.test.ts +543 -0
  272. package/src/db-fragment-integration.test.ts +406 -0
  273. package/src/fragments/internal-fragment.test.ts +549 -0
  274. package/src/fragments/internal-fragment.ts +249 -0
  275. package/src/hooks/hooks.test.ts +575 -0
  276. package/src/hooks/hooks.ts +179 -0
  277. package/src/migration-engine/generation-engine.test.ts +60 -27
  278. package/src/migration-engine/generation-engine.ts +99 -92
  279. package/src/mod.ts +139 -78
  280. package/src/query/column-defaults.ts +49 -0
  281. package/src/query/cursor.test.ts +147 -3
  282. package/src/query/cursor.ts +25 -8
  283. package/src/query/orm/orm.ts +1 -1
  284. package/src/query/query-type.test.ts +9 -9
  285. package/src/query/serialize/create-sql-serializer.ts +34 -0
  286. package/src/query/serialize/dialect/mysql-serializer.ts +142 -0
  287. package/src/query/serialize/dialect/postgres-serializer.ts +129 -0
  288. package/src/query/serialize/dialect/sqlite-serializer.test.ts +251 -0
  289. package/src/query/serialize/dialect/sqlite-serializer.ts +156 -0
  290. package/src/query/serialize/sql-serializer.ts +143 -0
  291. package/src/query/{query.ts → simple-query-interface.ts} +4 -4
  292. package/src/query/unit-of-work/execute-unit-of-work.test.ts +1310 -0
  293. package/src/query/unit-of-work/execute-unit-of-work.ts +504 -0
  294. package/src/query/unit-of-work/retry-policy.test.ts +217 -0
  295. package/src/query/unit-of-work/retry-policy.ts +141 -0
  296. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +831 -0
  297. package/src/query/{unit-of-work-types.test.ts → unit-of-work/unit-of-work-types.test.ts} +7 -5
  298. package/src/query/unit-of-work/unit-of-work.test.ts +1716 -0
  299. package/src/query/{unit-of-work.ts → unit-of-work/unit-of-work.ts} +716 -420
  300. package/src/query/{result-transform.test.ts → value-decoding.test.ts} +45 -298
  301. package/src/query/value-decoding.ts +113 -0
  302. package/src/query/value-encoding.test.ts +390 -0
  303. package/src/query/value-encoding.ts +168 -0
  304. package/src/schema/create.test.ts +5 -1
  305. package/src/schema/create.ts +5 -0
  306. package/src/schema/serialize.test.ts +165 -407
  307. package/src/schema/type-conversion/create-sql-type-mapper.ts +28 -0
  308. package/src/schema/type-conversion/dialect/mysql.ts +64 -0
  309. package/src/schema/type-conversion/dialect/postgres.ts +62 -0
  310. package/src/schema/type-conversion/dialect/sqlite.ts +63 -0
  311. package/src/schema/type-conversion/type-mapping.test.ts +137 -0
  312. package/src/schema/type-conversion/type-mapping.ts +153 -0
  313. package/src/shared/connection-pool.ts +5 -5
  314. package/src/sql-driver/better-sqlite3.test.ts +126 -0
  315. package/src/sql-driver/connection/connection-provider.ts +27 -0
  316. package/src/sql-driver/connection/single-connection-provider.ts +42 -0
  317. package/src/sql-driver/dialect-adapter/dialect-adapter.ts +9 -0
  318. package/src/sql-driver/dialect-adapter/sqlite-dialect-adapter.ts +7 -0
  319. package/src/sql-driver/dialects/dialects.ts +1 -0
  320. package/src/sql-driver/dialects/durable-object-dialect.ts +260 -0
  321. package/src/sql-driver/driver/runtime-driver.ts +91 -0
  322. package/src/sql-driver/query-executor/default-query-executor.ts +38 -0
  323. package/src/sql-driver/query-executor/plugin.ts +22 -0
  324. package/src/sql-driver/query-executor/query-executor-base.ts +53 -0
  325. package/src/sql-driver/query-executor/query-executor.ts +44 -0
  326. package/src/sql-driver/sql-driver-adapter.ts +96 -0
  327. package/src/sql-driver/sql-driver.ts +53 -0
  328. package/src/sql-driver/sql.ts +57 -0
  329. package/src/sql-driver/sqlocal.test.ts +117 -0
  330. package/src/with-database.ts +152 -0
  331. package/tsdown.config.ts +8 -2
  332. package/dist/adapters/drizzle/drizzle-connection-pool.js +0 -40
  333. package/dist/adapters/drizzle/drizzle-connection-pool.js.map +0 -1
  334. package/dist/adapters/drizzle/drizzle-query.d.ts +0 -23
  335. package/dist/adapters/drizzle/drizzle-query.d.ts.map +0 -1
  336. package/dist/adapters/drizzle/drizzle-query.js.map +0 -1
  337. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -10
  338. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +0 -1
  339. package/dist/adapters/drizzle/drizzle-uow-compiler.js +0 -315
  340. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +0 -1
  341. package/dist/adapters/drizzle/drizzle-uow-decoder.js +0 -116
  342. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +0 -1
  343. package/dist/adapters/drizzle/drizzle-uow-executor.js +0 -149
  344. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +0 -1
  345. package/dist/adapters/drizzle/join-column-utils.js +0 -28
  346. package/dist/adapters/drizzle/join-column-utils.js.map +0 -1
  347. package/dist/adapters/drizzle/shared.d.ts +0 -14
  348. package/dist/adapters/drizzle/shared.d.ts.map +0 -1
  349. package/dist/adapters/drizzle/shared.js +0 -35
  350. package/dist/adapters/drizzle/shared.js.map +0 -1
  351. package/dist/adapters/kysely/kysely-connection-pool.js +0 -41
  352. package/dist/adapters/kysely/kysely-connection-pool.js.map +0 -1
  353. package/dist/adapters/kysely/kysely-query-builder.js +0 -321
  354. package/dist/adapters/kysely/kysely-query-builder.js.map +0 -1
  355. package/dist/adapters/kysely/kysely-query-compiler.js +0 -66
  356. package/dist/adapters/kysely/kysely-query-compiler.js.map +0 -1
  357. package/dist/adapters/kysely/kysely-query.d.ts +0 -22
  358. package/dist/adapters/kysely/kysely-query.d.ts.map +0 -1
  359. package/dist/adapters/kysely/kysely-query.js +0 -223
  360. package/dist/adapters/kysely/kysely-query.js.map +0 -1
  361. package/dist/adapters/kysely/kysely-shared.d.ts.map +0 -1
  362. package/dist/adapters/kysely/kysely-shared.js +0 -18
  363. package/dist/adapters/kysely/kysely-shared.js.map +0 -1
  364. package/dist/adapters/kysely/kysely-uow-compiler.js +0 -170
  365. package/dist/adapters/kysely/kysely-uow-compiler.js.map +0 -1
  366. package/dist/adapters/kysely/kysely-uow-executor.js +0 -89
  367. package/dist/adapters/kysely/kysely-uow-executor.js.map +0 -1
  368. package/dist/adapters/kysely/migration/execute-base.js +0 -128
  369. package/dist/adapters/kysely/migration/execute-base.js.map +0 -1
  370. package/dist/adapters/kysely/migration/execute-factory.js +0 -34
  371. package/dist/adapters/kysely/migration/execute-factory.js.map +0 -1
  372. package/dist/adapters/kysely/migration/execute-mssql.js +0 -112
  373. package/dist/adapters/kysely/migration/execute-mssql.js.map +0 -1
  374. package/dist/adapters/kysely/migration/execute-mysql.js +0 -93
  375. package/dist/adapters/kysely/migration/execute-mysql.js.map +0 -1
  376. package/dist/adapters/kysely/migration/execute-postgres.js +0 -104
  377. package/dist/adapters/kysely/migration/execute-postgres.js.map +0 -1
  378. package/dist/adapters/kysely/migration/execute-sqlite.js +0 -123
  379. package/dist/adapters/kysely/migration/execute-sqlite.js.map +0 -1
  380. package/dist/adapters/kysely/migration/execute.js +0 -34
  381. package/dist/adapters/kysely/migration/execute.js.map +0 -1
  382. package/dist/bind-services.d.ts +0 -7
  383. package/dist/bind-services.d.ts.map +0 -1
  384. package/dist/bind-services.js +0 -14
  385. package/dist/bind-services.js.map +0 -1
  386. package/dist/fragment.d.ts +0 -173
  387. package/dist/fragment.d.ts.map +0 -1
  388. package/dist/fragment.js +0 -191
  389. package/dist/fragment.js.map +0 -1
  390. package/dist/migration-engine/create.d.ts +0 -37
  391. package/dist/migration-engine/create.d.ts.map +0 -1
  392. package/dist/migration-engine/create.js +0 -58
  393. package/dist/migration-engine/create.js.map +0 -1
  394. package/dist/migration-engine/shared.d.ts +0 -112
  395. package/dist/migration-engine/shared.d.ts.map +0 -1
  396. package/dist/query/query.d.ts.map +0 -1
  397. package/dist/query/result-transform.js +0 -168
  398. package/dist/query/result-transform.js.map +0 -1
  399. package/dist/query/unit-of-work.d.ts.map +0 -1
  400. package/dist/query/unit-of-work.js.map +0 -1
  401. package/dist/schema/serialize.js +0 -106
  402. package/dist/schema/serialize.js.map +0 -1
  403. package/dist/shared/settings-schema.js +0 -36
  404. package/dist/shared/settings-schema.js.map +0 -1
  405. package/src/adapters/drizzle/drizzle-adapter.test.ts +0 -170
  406. package/src/adapters/drizzle/drizzle-connection-pool.ts +0 -66
  407. package/src/adapters/drizzle/drizzle-query.test.ts +0 -499
  408. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +0 -1383
  409. package/src/adapters/drizzle/drizzle-uow-compiler.ts +0 -636
  410. package/src/adapters/drizzle/drizzle-uow-decoder.ts +0 -218
  411. package/src/adapters/drizzle/drizzle-uow-executor.ts +0 -276
  412. package/src/adapters/drizzle/join-column-utils.test.ts +0 -79
  413. package/src/adapters/drizzle/join-column-utils.ts +0 -39
  414. package/src/adapters/kysely/kysely-connection-pool.ts +0 -70
  415. package/src/adapters/kysely/kysely-query-builder.test.ts +0 -1344
  416. package/src/adapters/kysely/kysely-query-builder.ts +0 -666
  417. package/src/adapters/kysely/kysely-query-compiler.ts +0 -132
  418. package/src/adapters/kysely/kysely-query.test.ts +0 -498
  419. package/src/adapters/kysely/kysely-query.ts +0 -390
  420. package/src/adapters/kysely/kysely-shared.ts +0 -23
  421. package/src/adapters/kysely/kysely-uow-compiler.test.ts +0 -998
  422. package/src/adapters/kysely/kysely-uow-compiler.ts +0 -318
  423. package/src/adapters/kysely/kysely-uow-executor.ts +0 -145
  424. package/src/adapters/kysely/kysely-uow-joins.test.ts +0 -811
  425. package/src/adapters/kysely/migration/execute-base.ts +0 -256
  426. package/src/adapters/kysely/migration/execute-factory.ts +0 -53
  427. package/src/adapters/kysely/migration/execute-mssql.ts +0 -250
  428. package/src/adapters/kysely/migration/execute-mysql.ts +0 -211
  429. package/src/adapters/kysely/migration/execute-postgres.test.ts +0 -2657
  430. package/src/adapters/kysely/migration/execute-postgres.ts +0 -234
  431. package/src/adapters/kysely/migration/execute-sqlite.ts +0 -247
  432. package/src/adapters/kysely/migration/execute.ts +0 -50
  433. package/src/adapters/kysely/migration/kysely-migrator.test.ts +0 -261
  434. package/src/bind-services.test.ts +0 -214
  435. package/src/bind-services.ts +0 -37
  436. package/src/db-fragment.test.ts +0 -800
  437. package/src/fragment.ts +0 -727
  438. package/src/query/result-transform.ts +0 -271
  439. package/src/query/unit-of-work-multi-schema.test.ts +0 -64
  440. package/src/query/unit-of-work.test.ts +0 -943
  441. package/src/schema/serialize.ts +0 -396
  442. package/src/shared/settings-schema.ts +0 -61
  443. package/src/uow-context-integration.test.ts +0 -102
  444. package/src/uow-context.test.ts +0 -182
  445. /package/dist/query/{query.js → simple-query-interface.js} +0 -0
@@ -1,998 +0,0 @@
1
- import { Kysely, PostgresDialect } from "kysely";
2
- import { assert, beforeAll, describe, expect, it } from "vitest";
3
- import { column, FragnoId, idColumn, referenceColumn, schema } from "../../schema/create";
4
- import { UnitOfWork, type UOWDecoder } from "../../query/unit-of-work";
5
- import { createKyselyUOWCompiler } from "./kysely-uow-compiler";
6
- import type { ConnectionPool } from "../../shared/connection-pool";
7
- import { createKyselyConnectionPool } from "./kysely-connection-pool";
8
- import { Cursor } from "../../query/cursor";
9
-
10
- describe("kysely-uow-compiler", () => {
11
- const testSchema = schema((s) => {
12
- return s
13
- .addTable("users", (t) => {
14
- return t
15
- .addColumn("id", idColumn())
16
- .addColumn("name", column("string"))
17
- .addColumn("email", column("string"))
18
- .addColumn("age", column("integer").nullable())
19
- .addColumn("invitedBy", referenceColumn().nullable())
20
- .createIndex("idx_email", ["email"], { unique: true })
21
- .createIndex("idx_name", ["name"])
22
- .createIndex("idx_age", ["age"]);
23
- })
24
- .addTable("posts", (t) => {
25
- return t
26
- .addColumn("id", idColumn())
27
- .addColumn("title", column("string"))
28
- .addColumn("content", column("string"))
29
- .addColumn("userId", referenceColumn())
30
- .addColumn("viewCount", column("integer").defaultTo(0))
31
- .createIndex("idx_user", ["userId"])
32
- .createIndex("idx_title", ["title"]);
33
- })
34
- .addTable("comments", (t) => {
35
- return t
36
- .addColumn("id", idColumn())
37
- .addColumn("content", column("string"))
38
- .addColumn("postId", referenceColumn())
39
- .addColumn("authorId", referenceColumn())
40
- .createIndex("idx_post", ["postId"])
41
- .createIndex("idx_author", ["authorId"]);
42
- })
43
- .addTable("tags", (t) => {
44
- return t
45
- .addColumn("id", idColumn())
46
- .addColumn("name", column("string"))
47
- .createIndex("idx_name", ["name"]);
48
- })
49
- .addTable("post_tags", (t) => {
50
- return t
51
- .addColumn("id", idColumn())
52
- .addColumn("postId", referenceColumn())
53
- .addColumn("tagId", referenceColumn())
54
- .createIndex("idx_post", ["postId"])
55
- .createIndex("idx_tag", ["tagId"]);
56
- })
57
- .addReference("author", {
58
- type: "one",
59
- from: { table: "posts", column: "userId" },
60
- to: { table: "users", column: "id" },
61
- })
62
- .addReference("inviter", {
63
- type: "one",
64
- from: { table: "users", column: "invitedBy" },
65
- to: { table: "users", column: "id" },
66
- })
67
- .addReference("post", {
68
- type: "one",
69
- from: { table: "comments", column: "postId" },
70
- to: { table: "posts", column: "id" },
71
- })
72
- .addReference("author", {
73
- type: "one",
74
- from: { table: "comments", column: "authorId" },
75
- to: { table: "users", column: "id" },
76
- })
77
- .addReference("post", {
78
- type: "one",
79
- from: { table: "post_tags", column: "postId" },
80
- to: { table: "posts", column: "id" },
81
- })
82
- .addReference("tag", {
83
- type: "one",
84
- from: { table: "post_tags", column: "tagId" },
85
- to: { table: "tags", column: "id" },
86
- });
87
- });
88
-
89
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
- let kysely: Kysely<any>;
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
- let pool: ConnectionPool<Kysely<any>>;
93
-
94
- beforeAll(() => {
95
- // Create a mock Kysely instance (we won't execute queries, just compile them)
96
- // We need a minimal pool that won't actually connect
97
- const mockPool = {
98
- connect: () => Promise.reject(new Error("Mock pool - no actual connections")),
99
- end: () => Promise.resolve(),
100
- on: () => {},
101
- };
102
-
103
- kysely = new Kysely({
104
- dialect: new PostgresDialect({
105
- // Safe: we're only compiling queries, not executing them
106
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
- pool: mockPool as any,
108
- }),
109
- });
110
-
111
- // Wrap in connection pool
112
- pool = createKyselyConnectionPool(kysely);
113
- });
114
-
115
- // Helper to create UnitOfWork for testing
116
- function createTestUOW(name?: string) {
117
- const mockCompiler = createKyselyUOWCompiler(pool, "postgresql");
118
- const mockExecutor = {
119
- executeRetrievalPhase: async () => [],
120
- executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
121
- };
122
- const mockDecoder: UOWDecoder = (rawResults, operations) => {
123
- if (rawResults.length !== operations.length) {
124
- throw new Error("rawResults and ops must have the same length");
125
- }
126
- return rawResults;
127
- };
128
- // Pass undefined for decoder since we're only testing compilation, not execution
129
- return new UnitOfWork(testSchema, mockCompiler, mockExecutor, mockDecoder, name);
130
- }
131
-
132
- describe("compileRetrievalOperation", () => {
133
- it("should compile find operation with where clause", () => {
134
- const uow = createTestUOW();
135
- uow.find("users", (b) =>
136
- b.whereIndex("idx_email", (eb) => eb("email", "=", "test@example.com")),
137
- );
138
-
139
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
140
- const compiled = uow.compile(compiler);
141
-
142
- expect(compiled.retrievalBatch).toHaveLength(1);
143
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
144
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."email" = $1"`,
145
- );
146
- expect(compiled.retrievalBatch[0].parameters).toEqual(["test@example.com"]);
147
- });
148
-
149
- it("should compile find operation with select clause", () => {
150
- const uow = createTestUOW();
151
- uow.find("users", (b) =>
152
- b.whereIndex("idx_name", (eb) => eb("name", "=", "Alice")).select(["id", "name"]),
153
- );
154
-
155
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
156
- const compiled = uow.compile(compiler);
157
-
158
- expect(compiled.retrievalBatch).toHaveLength(1);
159
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
160
- `"select "users"."id" as "id", "users"."name" as "name", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."name" = $1"`,
161
- );
162
- expect(compiled.retrievalBatch[0].parameters).toEqual(["Alice"]);
163
- });
164
-
165
- it("should compile find operation with pageSize", () => {
166
- const uow = createTestUOW();
167
- uow.find("users", (b) => b.whereIndex("primary").pageSize(10));
168
-
169
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
170
- const compiled = uow.compile(compiler);
171
-
172
- expect(compiled.retrievalBatch).toHaveLength(1);
173
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
174
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" limit $1"`,
175
- );
176
- expect(compiled.retrievalBatch[0].parameters).toEqual([10]);
177
- });
178
-
179
- it("should compile find operation with orderByIndex on primary index", () => {
180
- const uow = createTestUOW();
181
- uow.find("users", (b) => b.whereIndex("primary").orderByIndex("primary", "desc"));
182
-
183
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
184
- const compiled = uow.compile(compiler);
185
-
186
- expect(compiled.retrievalBatch).toHaveLength(1);
187
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
188
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" order by "users"."id" desc"`,
189
- );
190
- });
191
-
192
- it("should compile find operation with orderByIndex", () => {
193
- const uow = createTestUOW();
194
- uow.find("users", (b) => b.whereIndex("idx_name").orderByIndex("idx_name", "desc"));
195
-
196
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
197
- const compiled = uow.compile(compiler);
198
-
199
- expect(compiled.retrievalBatch).toHaveLength(1);
200
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
201
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" order by "users"."name" desc"`,
202
- );
203
- });
204
-
205
- it("should compile multiple find operations", () => {
206
- const uow = createTestUOW();
207
- uow.find("users", (b) =>
208
- b.whereIndex("idx_email", (eb) => eb("email", "=", "user1@example.com")),
209
- );
210
- uow.find("posts", (b) => b.whereIndex("idx_title", (eb) => eb("title", "contains", "test")));
211
-
212
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
213
- const compiled = uow.compile(compiler);
214
-
215
- expect(compiled.retrievalBatch).toHaveLength(2);
216
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
217
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."email" = $1"`,
218
- );
219
- expect(compiled.retrievalBatch[1].sql).toMatchInlineSnapshot(
220
- `"select "posts"."id" as "id", "posts"."title" as "title", "posts"."content" as "content", "posts"."userId" as "userId", "posts"."viewCount" as "viewCount", "posts"."_internalId" as "_internalId", "posts"."_version" as "_version" from "posts" where "posts"."title" like $1"`,
221
- );
222
- });
223
-
224
- it("should compile find operation with selectCount", () => {
225
- const uow = createTestUOW();
226
- uow.find("users", (b) => {
227
- b.whereIndex("primary").selectCount();
228
- return b;
229
- });
230
-
231
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
232
- const compiled = uow.compile(compiler);
233
-
234
- expect(compiled.retrievalBatch).toHaveLength(1);
235
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
236
- `"select count(*) as "count" from "users""`,
237
- );
238
- });
239
-
240
- it("should compile find operation with selectCount and where clause", () => {
241
- const uow = createTestUOW();
242
- uow.find("users", (b) => b.whereIndex("idx_age", (eb) => eb("age", ">", 25)).selectCount());
243
-
244
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
245
- const compiled = uow.compile(compiler);
246
-
247
- expect(compiled.retrievalBatch).toHaveLength(1);
248
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
249
- `"select count(*) as "count" from "users" where "users"."age" > $1"`,
250
- );
251
- expect(compiled.retrievalBatch[0].parameters).toEqual([25]);
252
- });
253
-
254
- it("should compile find operation with cursor pagination using after", () => {
255
- const uow = createTestUOW();
256
- const cursor = new Cursor({
257
- indexName: "idx_name",
258
- orderDirection: "asc",
259
- pageSize: 10,
260
- indexValues: { name: "Alice" },
261
- });
262
- uow.find("users", (b) =>
263
- b.whereIndex("idx_name").orderByIndex("idx_name", "asc").after(cursor).pageSize(10),
264
- );
265
-
266
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
267
- const compiled = uow.compile(compiler);
268
-
269
- expect(compiled.retrievalBatch).toHaveLength(1);
270
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
271
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."name" > $1 order by "users"."name" asc limit $2"`,
272
- );
273
- expect(compiled.retrievalBatch[0].parameters).toEqual(["Alice", 10]);
274
- });
275
-
276
- it("should compile find operation with cursor pagination using before", () => {
277
- const uow = createTestUOW();
278
- const cursor = new Cursor({
279
- indexName: "idx_name",
280
- orderDirection: "desc",
281
- pageSize: 10,
282
- indexValues: { name: "Bob" },
283
- });
284
- uow.find("users", (b) =>
285
- b.whereIndex("idx_name").orderByIndex("idx_name", "desc").before(cursor).pageSize(10),
286
- );
287
-
288
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
289
- const compiled = uow.compile(compiler);
290
-
291
- expect(compiled.retrievalBatch).toHaveLength(1);
292
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
293
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."name" > $1 order by "users"."name" desc limit $2"`,
294
- );
295
- expect(compiled.retrievalBatch[0].parameters).toEqual(["Bob", 10]);
296
- });
297
-
298
- it("should compile find operation with cursor pagination and additional where conditions", () => {
299
- const uow = createTestUOW();
300
- const cursor = new Cursor({
301
- indexName: "idx_name",
302
- orderDirection: "asc",
303
- pageSize: 5,
304
- indexValues: { name: "Alice" },
305
- });
306
- uow.find("users", (b) =>
307
- b
308
- .whereIndex("idx_name", (eb) => eb("name", "starts with", "John"))
309
- .orderByIndex("idx_name", "asc")
310
- .after(cursor)
311
- .pageSize(5),
312
- );
313
-
314
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
315
- const compiled = uow.compile(compiler);
316
-
317
- expect(compiled.retrievalBatch).toHaveLength(1);
318
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
319
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where ("users"."name" like $1 and "users"."name" > $2) order by "users"."name" asc limit $3"`,
320
- );
321
- expect(compiled.retrievalBatch[0].parameters).toEqual(["John%", "Alice", 5]);
322
- });
323
- });
324
-
325
- describe("compileMutationOperation", () => {
326
- it("should compile create operation", () => {
327
- const uow = createTestUOW();
328
- uow.create("users", {
329
- name: "John Doe",
330
- email: "john@example.com",
331
- age: 30,
332
- });
333
-
334
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
335
- const compiled = uow.compile(compiler);
336
- const [batch] = compiled.mutationBatch;
337
- assert(batch);
338
- expect(batch.expectedAffectedRows).toBeNull();
339
- expect(batch.query.sql).toMatchInlineSnapshot(
340
- `"insert into "users" ("id", "name", "email", "age") values ($1, $2, $3, $4) returning "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version""`,
341
- );
342
- expect(batch.query.parameters).toMatchObject([
343
- expect.any(String),
344
- "John Doe",
345
- "john@example.com",
346
- 30,
347
- ]);
348
- });
349
-
350
- it("should compile update operation with ID", () => {
351
- const uow = createTestUOW();
352
- const userId = FragnoId.fromExternal("user123", 0);
353
- uow.update("users", userId, (b) =>
354
- b.set({
355
- name: "Jane Doe",
356
- age: 25,
357
- }),
358
- );
359
-
360
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
361
- const compiled = uow.compile(compiler);
362
- const [batch] = compiled.mutationBatch;
363
- assert(batch);
364
- expect(batch.expectedAffectedRows).toBeNull();
365
- expect(batch.query.sql).toMatchInlineSnapshot(
366
- `"update "users" set "name" = $1, "age" = $2, "_version" = COALESCE(_version, 0) + 1 where "users"."id" = $3"`,
367
- );
368
- expect(batch.query.parameters).toMatchObject(["Jane Doe", 25, "user123"]);
369
- });
370
-
371
- it("should compile update operation with version check", () => {
372
- const uow = createTestUOW();
373
- const userId = FragnoId.fromExternal("user123", 5);
374
- uow.update("users", userId, (b) => b.set({ age: 18 }).check());
375
-
376
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
377
- const compiled = uow.compile(compiler);
378
- const [batch] = compiled.mutationBatch;
379
- assert(batch);
380
- expect(batch.expectedAffectedRows).toBe(1);
381
- expect(batch.query.sql).toMatchInlineSnapshot(
382
- `"update "users" set "age" = $1, "_version" = COALESCE(_version, 0) + 1 where ("users"."id" = $2 and "users"."_version" = $3)"`,
383
- );
384
- expect(batch.query.parameters).toMatchObject([18, "user123", 5]);
385
- });
386
-
387
- it("should compile delete operation with ID", () => {
388
- const uow = createTestUOW();
389
- const userId = FragnoId.fromExternal("user123", 0);
390
- uow.delete("users", userId);
391
-
392
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
393
- const compiled = uow.compile(compiler);
394
- const [batch] = compiled.mutationBatch;
395
- assert(batch);
396
- expect(batch.expectedAffectedRows).toBeNull();
397
- expect(batch.query.sql).toMatchInlineSnapshot(
398
- `"delete from "users" where "users"."id" = $1"`,
399
- );
400
- expect(batch.query.parameters).toMatchObject(["user123"]);
401
- });
402
-
403
- it("should compile delete operation with version check", () => {
404
- const uow = createTestUOW();
405
- const userId = FragnoId.fromExternal("user123", 3);
406
- uow.delete("users", userId, (b) => b.check());
407
-
408
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
409
- const compiled = uow.compile(compiler);
410
- const [batch] = compiled.mutationBatch;
411
- assert(batch);
412
- expect(batch.expectedAffectedRows).toBe(1);
413
- expect(batch.query.sql).toMatchInlineSnapshot(
414
- `"delete from "users" where ("users"."id" = $1 and "users"."_version" = $2)"`,
415
- );
416
- expect(batch.query.parameters).toMatchObject(["user123", 3]);
417
- });
418
-
419
- it("should compile update operation with string ID", () => {
420
- const uow = createTestUOW();
421
- uow.update("users", "user123", (b) =>
422
- b.set({
423
- name: "Jane Doe",
424
- age: 25,
425
- }),
426
- );
427
-
428
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
429
- const compiled = uow.compile(compiler);
430
- const [batch] = compiled.mutationBatch;
431
- assert(batch);
432
- expect(batch.expectedAffectedRows).toBeNull();
433
- expect(batch.query.sql).toMatchInlineSnapshot(
434
- `"update "users" set "name" = $1, "age" = $2, "_version" = COALESCE(_version, 0) + 1 where "users"."id" = $3"`,
435
- );
436
- expect(batch.query.parameters).toMatchObject(["Jane Doe", 25, "user123"]);
437
- });
438
-
439
- it("should compile delete operation with string ID", () => {
440
- const uow = createTestUOW();
441
- uow.delete("users", "user123");
442
-
443
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
444
- const compiled = uow.compile(compiler);
445
- const [batch] = compiled.mutationBatch;
446
- assert(batch);
447
- expect(batch.expectedAffectedRows).toBeNull();
448
- expect(batch.query.sql).toMatchInlineSnapshot(
449
- `"delete from "users" where "users"."id" = $1"`,
450
- );
451
- expect(batch.query.parameters).toMatchObject(["user123"]);
452
- });
453
-
454
- it("should throw when trying to check() with string ID on update", () => {
455
- const uow = createTestUOW();
456
- expect(() => {
457
- uow.update("users", "user123", (b) => b.set({ name: "Jane" }).check());
458
- }).toThrow(
459
- 'Cannot use check() with a string ID on table "users". Version checking requires a FragnoId with version information.',
460
- );
461
- });
462
-
463
- it("should throw when trying to check() with string ID on delete", () => {
464
- const uow = createTestUOW();
465
- expect(() => {
466
- uow.delete("users", "user123", (b) => b.check());
467
- }).toThrow(
468
- 'Cannot use check() with a string ID on table "users". Version checking requires a FragnoId with version information.',
469
- );
470
- });
471
-
472
- it("should compile multiple mutation operations", () => {
473
- const uow = createTestUOW();
474
- uow.create("users", {
475
- name: "Alice",
476
- email: "alice@example.com",
477
- });
478
- const postId = FragnoId.fromExternal("post123", 0);
479
- uow.update("posts", postId, (b) => b.set({ viewCount: 10 }));
480
- const userId = FragnoId.fromExternal("user456", 0);
481
- uow.delete("posts", userId);
482
-
483
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
484
- const compiled = uow.compile(compiler);
485
- const [createBatch, updateBatch, deleteBatch] = compiled.mutationBatch;
486
-
487
- expect(compiled.mutationBatch).toHaveLength(3);
488
-
489
- assert(createBatch);
490
- expect(createBatch.query.sql).toMatchInlineSnapshot(
491
- `"insert into "users" ("id", "name", "email") values ($1, $2, $3) returning "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version""`,
492
- );
493
- expect(createBatch.query.parameters).toMatchObject([
494
- expect.any(String),
495
- "Alice",
496
- "alice@example.com",
497
- ]);
498
-
499
- assert(updateBatch);
500
- expect(updateBatch.query.sql).toMatchInlineSnapshot(
501
- `"update "posts" set "viewCount" = $1, "_version" = COALESCE(_version, 0) + 1 where "posts"."id" = $2"`,
502
- );
503
-
504
- assert(deleteBatch);
505
- expect(deleteBatch.query.sql).toMatchInlineSnapshot(
506
- `"delete from "posts" where "posts"."id" = $1"`,
507
- );
508
- });
509
- });
510
-
511
- describe("complete UOW workflow", () => {
512
- it("should compile retrieval and mutation phases together", () => {
513
- const uow = createTestUOW("update-user-balance");
514
-
515
- // Retrieval phase
516
- uow.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", "user123")));
517
-
518
- // Mutation phase
519
- const userId = FragnoId.fromExternal("user123", 3);
520
- uow.update("users", userId, (b) => b.set({ age: 31 }).check());
521
-
522
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
523
- const compiled = uow.compile(compiler);
524
-
525
- expect(compiled.name).toBe("update-user-balance");
526
- expect(compiled.retrievalBatch).toHaveLength(1);
527
- expect(compiled.mutationBatch).toHaveLength(1);
528
-
529
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
530
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where "users"."id" = $1"`,
531
- );
532
-
533
- // Update should include version check in WHERE clause
534
- const [batch] = compiled.mutationBatch;
535
- assert(batch);
536
- expect(batch.expectedAffectedRows).toBe(1);
537
- expect(batch.query.sql).toMatchInlineSnapshot(
538
- `"update "users" set "age" = $1, "_version" = COALESCE(_version, 0) + 1 where ("users"."id" = $2 and "users"."_version" = $3)"`,
539
- );
540
- });
541
-
542
- it("should handle complex where conditions", () => {
543
- const uow = createTestUOW();
544
- uow.find("users", (b) =>
545
- b.whereIndex("idx_email", (eb) =>
546
- eb.and(
547
- eb("email", "contains", "@example.com"),
548
- // @ts-expect-error - name is not indexed
549
- eb.or(eb("name", "=", "Alice"), eb("name", "=", "Bob")),
550
- ),
551
- ),
552
- );
553
-
554
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
555
- const compiled = uow.compile(compiler);
556
-
557
- expect(compiled.retrievalBatch).toHaveLength(1);
558
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
559
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users" where ("users"."email" like $1 and ("users"."name" = $2 or "users"."name" = $3))"`,
560
- );
561
- expect(compiled.retrievalBatch[0].parameters).toEqual(["%@example.com%", "Alice", "Bob"]);
562
- });
563
-
564
- it("should return null for operations with always-false conditions", () => {
565
- const uow = createTestUOW();
566
- uow.find("users", (b) => b.whereIndex("primary", () => false));
567
-
568
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
569
- const compiled = uow.compile(compiler);
570
-
571
- // When condition is false, the operation should return null and not be added to batch
572
- expect(compiled.retrievalBatch).toHaveLength(0);
573
- });
574
-
575
- it("should handle always-true conditions", () => {
576
- const uow = createTestUOW();
577
- uow.find("users", (b) => b.whereIndex("primary", () => true));
578
-
579
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
580
- const compiled = uow.compile(compiler);
581
-
582
- expect(compiled.retrievalBatch).toHaveLength(1);
583
- expect(compiled.retrievalBatch[0].sql).toMatchInlineSnapshot(
584
- `"select "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version" from "users""`,
585
- );
586
- });
587
- });
588
-
589
- describe("version checking", () => {
590
- it("should embed version check in update WHERE clause", () => {
591
- const uow = createTestUOW();
592
-
593
- const userId = FragnoId.fromExternal("user123", 5);
594
- uow.update("users", userId, (b) => b.set({ age: 31 }).check());
595
-
596
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
597
- const compiled = uow.compile(compiler);
598
- const [batch] = compiled.mutationBatch;
599
- assert(batch);
600
- expect(batch.expectedAffectedRows).toBe(1);
601
- expect(batch.query.sql).toMatchInlineSnapshot(
602
- `"update "users" set "age" = $1, "_version" = COALESCE(_version, 0) + 1 where ("users"."id" = $2 and "users"."_version" = $3)"`,
603
- );
604
- expect(batch.query.parameters).toMatchObject([31, "user123", 5]);
605
- });
606
-
607
- it("should embed version check in delete WHERE clause", () => {
608
- const uow = createTestUOW();
609
-
610
- const userId = FragnoId.fromExternal("user456", 3);
611
- uow.delete("users", userId, (b) => b.check());
612
-
613
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
614
- const compiled = uow.compile(compiler);
615
- const [batch] = compiled.mutationBatch;
616
- assert(batch);
617
- expect(batch.expectedAffectedRows).toBe(1);
618
- expect(batch.query.sql).toMatchInlineSnapshot(
619
- `"delete from "users" where ("users"."id" = $1 and "users"."_version" = $2)"`,
620
- );
621
- expect(batch.query.parameters).toMatchObject(["user456", 3]);
622
- });
623
-
624
- it("should handle version checks on different tables", () => {
625
- const uow = createTestUOW();
626
-
627
- const userId = FragnoId.fromExternal("user1", 2);
628
- const postId = FragnoId.fromExternal("post1", 1);
629
-
630
- uow.update("users", userId, (b) => b.set({ age: 30 }).check());
631
- uow.update("posts", postId, (b) => b.set({ viewCount: 100 }).check());
632
-
633
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
634
- const compiled = uow.compile(compiler);
635
- const [userBatch, postBatch] = compiled.mutationBatch;
636
-
637
- expect(compiled.mutationBatch).toHaveLength(2);
638
-
639
- assert(userBatch);
640
- expect(userBatch.expectedAffectedRows).toBe(1);
641
- expect(userBatch.query.sql).toMatchInlineSnapshot(
642
- `"update "users" set "age" = $1, "_version" = COALESCE(_version, 0) + 1 where ("users"."id" = $2 and "users"."_version" = $3)"`,
643
- );
644
- expect(userBatch.query.parameters).toMatchObject([30, "user1", 2]);
645
-
646
- assert(postBatch);
647
- expect(postBatch.expectedAffectedRows).toBe(1);
648
- expect(postBatch.query.sql).toMatchInlineSnapshot(
649
- `"update "posts" set "viewCount" = $1, "_version" = COALESCE(_version, 0) + 1 where ("posts"."id" = $2 and "posts"."_version" = $3)"`,
650
- );
651
- expect(postBatch.query.parameters).toMatchObject([100, "post1", 1]);
652
- });
653
-
654
- it("should not affect updates without version checks", () => {
655
- const uow = createTestUOW();
656
-
657
- const userId = FragnoId.fromExternal("user1", 0);
658
- uow.update("users", userId, (b) => b.set({ age: 25 }));
659
-
660
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
661
- const compiled = uow.compile(compiler);
662
- const [batch] = compiled.mutationBatch;
663
- assert(batch);
664
- expect(batch.expectedAffectedRows).toBeNull();
665
- // Should be normal update without version check
666
- expect(batch.query.sql).toMatchInlineSnapshot(
667
- `"update "users" set "age" = $1, "_version" = COALESCE(_version, 0) + 1 where "users"."id" = $2"`,
668
- );
669
- });
670
- });
671
-
672
- describe("join operations", () => {
673
- it("should compile find operation with basic join", () => {
674
- const uow = createTestUOW();
675
- uow.find("posts", (b) =>
676
- b.whereIndex("primary").join((jb) => jb.author((ab) => ab.select(["name", "email"]))),
677
- );
678
-
679
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
680
- const compiled = uow.compile(compiler);
681
-
682
- expect(compiled.retrievalBatch).toHaveLength(1);
683
- const query = compiled.retrievalBatch[0];
684
- assert(query);
685
- expect(query.sql).toContain("left join");
686
- expect(query.sql).toContain('"users" as "author"');
687
- expect(query.sql).toContain('"author"."name"');
688
- expect(query.sql).toContain('"author"."email"');
689
- });
690
-
691
- it("should compile join with whereIndex filtering", () => {
692
- const uow = createTestUOW();
693
- uow.find("posts", (b) =>
694
- b
695
- .whereIndex("primary")
696
- .join((jb) =>
697
- jb.author((ab) =>
698
- ab.select(["name"]).whereIndex("idx_name", (eb) => eb("name", "=", "Alice")),
699
- ),
700
- ),
701
- );
702
-
703
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
704
- const compiled = uow.compile(compiler);
705
-
706
- expect(compiled.retrievalBatch).toHaveLength(1);
707
- const query = compiled.retrievalBatch[0];
708
- assert(query);
709
- expect(query.sql).toContain("left join");
710
- expect(query.sql).toContain('"users"."name" = $1');
711
- expect(query.parameters).toContain("Alice");
712
- });
713
-
714
- it("should compile join with orderByIndex", () => {
715
- const uow = createTestUOW();
716
- uow.find("posts", (b) =>
717
- b
718
- .whereIndex("primary")
719
- .join((jb) => jb.author((ab) => ab.select(["name"]).orderByIndex("idx_name", "desc"))),
720
- );
721
-
722
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
723
- const compiled = uow.compile(compiler);
724
-
725
- expect(compiled.retrievalBatch).toHaveLength(1);
726
- const query = compiled.retrievalBatch[0];
727
- assert(query);
728
- expect(query.sql).toContain("left join");
729
- expect(query.sql).toContain('"users" as "author"');
730
- });
731
-
732
- it("should compile join with pageSize", () => {
733
- const uow = createTestUOW();
734
- uow.find("posts", (b) =>
735
- b.whereIndex("primary").join((jb) => jb.author((ab) => ab.select(["name"]).pageSize(5))),
736
- );
737
-
738
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
739
- const compiled = uow.compile(compiler);
740
-
741
- expect(compiled.retrievalBatch).toHaveLength(1);
742
- const query = compiled.retrievalBatch[0];
743
- assert(query);
744
- expect(query.sql).toContain("left join");
745
- });
746
-
747
- it("should compile nested joins", () => {
748
- const uow = createTestUOW();
749
- uow.find("posts", (b) =>
750
- b.whereIndex("primary").join((jb) =>
751
- jb.author((ab) =>
752
- // FIXME: inviter should be strongly typed
753
- ab.select(["name"]).join((jb2) => {
754
- // type Prettify<T> = {
755
- // [K in keyof T]: T[K];
756
- // } & {};
757
-
758
- // type InviterFn = Prettify<(typeof jb2)["inviter"]>;
759
- // expectTypeOf<InviterFn>().toEqualTypeOf<{ [x: string]: any }>();
760
- // type BuilderKeys = Prettify<keyof typeof jb2>;
761
- // expectTypeOf<BuilderKeys>().toEqualTypeOf<string | symbol | number>();
762
-
763
- return jb2["inviter"]((ib) => ib.select(["name"]));
764
- }),
765
- ),
766
- ),
767
- );
768
-
769
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
770
- const compiled = uow.compile(compiler);
771
-
772
- expect(compiled.retrievalBatch).toHaveLength(1);
773
- const query = compiled.retrievalBatch[0];
774
- assert(query);
775
- expect(query.sql).toMatchInlineSnapshot(
776
- `"select "author"."name" as "author:name", "author"."_internalId" as "author:_internalId", "author"."_version" as "author:_version", "author_inviter"."name" as "author:inviter:name", "author_inviter"."_internalId" as "author:inviter:_internalId", "author_inviter"."_version" as "author:inviter:_version", "posts"."id" as "id", "posts"."title" as "title", "posts"."content" as "content", "posts"."userId" as "userId", "posts"."viewCount" as "viewCount", "posts"."_internalId" as "_internalId", "posts"."_version" as "_version" from "posts" left join "users" as "author" on "posts"."userId" = "author"."_internalId" left join "users" as "author_inviter" on "author"."invitedBy" = "author_inviter"."_internalId""`,
777
- );
778
- });
779
-
780
- it("should compile multiple joins", () => {
781
- const uow = createTestUOW();
782
- uow.find("comments", (b) =>
783
- b
784
- .whereIndex("primary")
785
- .join((jb) => jb.post((pb) => pb.select(["title"])).author((ab) => ab.select(["name"]))),
786
- );
787
-
788
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
789
- const compiled = uow.compile(compiler);
790
-
791
- expect(compiled.retrievalBatch).toHaveLength(1);
792
- const query = compiled.retrievalBatch[0];
793
- assert(query);
794
- expect(query.sql).toContain('"posts" as "post"');
795
- expect(query.sql).toContain('"users" as "author"');
796
- expect(query.sql).toContain('"post"."title"');
797
- expect(query.sql).toContain('"author"."name"');
798
- });
799
-
800
- it("should compile self-referencing join", () => {
801
- const uow = createTestUOW();
802
- uow.find("users", (b) =>
803
- b.whereIndex("primary").join((jb) => jb.inviter((ib) => ib.select(["name", "email"]))),
804
- );
805
-
806
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
807
- const compiled = uow.compile(compiler);
808
-
809
- expect(compiled.retrievalBatch).toHaveLength(1);
810
- const query = compiled.retrievalBatch[0];
811
- assert(query);
812
- expect(query.sql).toContain('"users" as "inviter"');
813
- expect(query.sql).toContain('"inviter"."name"');
814
- expect(query.sql).toContain('"inviter"."email"');
815
- });
816
-
817
- it("should compile join with all builder features combined", () => {
818
- const uow = createTestUOW();
819
- uow.find("posts", (b) =>
820
- b
821
- .whereIndex("idx_title", (eb) => eb("title", "contains", "test"))
822
- .select(["id", "title"])
823
- .orderByIndex("idx_title", "asc")
824
- .pageSize(10)
825
- .join((jb) =>
826
- jb.author((ab) =>
827
- ab
828
- .select(["name", "email"])
829
- .whereIndex("idx_name", (eb) => eb("name", "starts with", "A"))
830
- .orderByIndex("idx_name", "desc")
831
- .pageSize(5),
832
- ),
833
- ),
834
- );
835
-
836
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
837
- const compiled = uow.compile(compiler);
838
-
839
- expect(compiled.retrievalBatch).toHaveLength(1);
840
- const query = compiled.retrievalBatch[0];
841
- assert(query);
842
- // Main query features
843
- expect(query.sql).toContain('"posts"."title" like');
844
- expect(query.sql).toContain("order by");
845
- expect(query.sql).toContain("limit");
846
- // Join features
847
- expect(query.sql).toContain('"users" as "author"');
848
- expect(query.sql).toContain('"users"."name" like');
849
- });
850
-
851
- it("should compile many-to-many join through junction table", () => {
852
- const uow = createTestUOW();
853
- uow.find("post_tags", (b) =>
854
- b
855
- .whereIndex("primary")
856
- .join((jb) => jb.post((pb) => pb.select(["title"])).tag((tb) => tb.select(["name"]))),
857
- );
858
-
859
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
860
- const compiled = uow.compile(compiler);
861
-
862
- expect(compiled.retrievalBatch).toHaveLength(1);
863
- const query = compiled.retrievalBatch[0];
864
- assert(query);
865
- expect(query.sql).toMatchInlineSnapshot(
866
- `"select "post"."title" as "post:title", "post"."_internalId" as "post:_internalId", "post"."_version" as "post:_version", "tag"."name" as "tag:name", "tag"."_internalId" as "tag:_internalId", "tag"."_version" as "tag:_version", "post_tags"."id" as "id", "post_tags"."postId" as "postId", "post_tags"."tagId" as "tagId", "post_tags"."_internalId" as "_internalId", "post_tags"."_version" as "_version" from "post_tags" left join "posts" as "post" on "post_tags"."postId" = "post"."_internalId" left join "tags" as "tag" on "post_tags"."tagId" = "tag"."_internalId""`,
867
- );
868
- });
869
-
870
- it("should compile nested many-to-many join (post_tags -> post -> author)", () => {
871
- const uow = createTestUOW();
872
- uow.find("post_tags", (b) =>
873
- b
874
- .whereIndex("primary")
875
- .join((jb) =>
876
- jb.post((pb) =>
877
- pb
878
- .select(["title"])
879
- .join((jb2) => jb2["author"]((ab) => ab.select(["name", "email"]))),
880
- ),
881
- ),
882
- );
883
-
884
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
885
- const compiled = uow.compile(compiler);
886
-
887
- expect(compiled.retrievalBatch).toHaveLength(1);
888
- const query = compiled.retrievalBatch[0];
889
- assert(query);
890
- expect(query.sql).toMatchInlineSnapshot(
891
- `"select "post"."title" as "post:title", "post"."_internalId" as "post:_internalId", "post"."_version" as "post:_version", "post_author"."name" as "post:author:name", "post_author"."email" as "post:author:email", "post_author"."_internalId" as "post:author:_internalId", "post_author"."_version" as "post:author:_version", "post_tags"."id" as "id", "post_tags"."postId" as "postId", "post_tags"."tagId" as "tagId", "post_tags"."_internalId" as "_internalId", "post_tags"."_version" as "_version" from "post_tags" left join "posts" as "post" on "post_tags"."postId" = "post"."_internalId" left join "users" as "post_author" on "post"."userId" = "post_author"."_internalId""`,
892
- );
893
- });
894
- });
895
-
896
- describe("edge cases", () => {
897
- it("should handle UOW with no operations", () => {
898
- const uow = createTestUOW();
899
-
900
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
901
- const compiled = uow.compile(compiler);
902
-
903
- expect(compiled.retrievalBatch).toHaveLength(0);
904
- expect(compiled.mutationBatch).toHaveLength(0);
905
- });
906
-
907
- it("should handle UOW with only retrieval operations", () => {
908
- const uow = createTestUOW();
909
- uow.find("users");
910
-
911
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
912
- const compiled = uow.compile(compiler);
913
-
914
- expect(compiled.retrievalBatch).toHaveLength(1);
915
- expect(compiled.mutationBatch).toHaveLength(0);
916
- });
917
-
918
- it("should handle UOW with only mutation operations", () => {
919
- const uow = createTestUOW();
920
- uow.create("users", {
921
- name: "Test User",
922
- email: "test@example.com",
923
- });
924
-
925
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
926
- const compiled = uow.compile(compiler);
927
-
928
- expect(compiled.retrievalBatch).toHaveLength(0);
929
- expect(compiled.mutationBatch).toHaveLength(1);
930
- });
931
- });
932
-
933
- describe("create and use ID in same UOW", () => {
934
- it("should support creating and using ID in same UOW", () => {
935
- const uow = createTestUOW("create-user-and-post");
936
-
937
- // Create user and capture the returned ID
938
- const userId = uow.create("users", {
939
- name: "John Doe",
940
- email: "john@example.com",
941
- age: 30,
942
- });
943
-
944
- // Use the returned FragnoId directly to create a post
945
- // The compiler should extract externalId and generate a subquery
946
- uow.create("posts", {
947
- userId: userId,
948
- title: "My First Post",
949
- content: "This is my first post",
950
- });
951
-
952
- const compiler = createKyselyUOWCompiler(pool, "postgresql");
953
- const compiled = uow.compile(compiler);
954
-
955
- // Should have no retrieval operations
956
- expect(compiled.retrievalBatch).toHaveLength(0);
957
-
958
- // Should have 2 mutation operations (create user, create post)
959
- expect(compiled.mutationBatch).toHaveLength(2);
960
-
961
- const [userCreate, postCreate] = compiled.mutationBatch;
962
- assert(userCreate);
963
- assert(postCreate);
964
-
965
- // Verify user create SQL
966
- expect(userCreate.query.sql).toMatchInlineSnapshot(
967
- `"insert into "users" ("id", "name", "email", "age") values ($1, $2, $3, $4) returning "users"."id" as "id", "users"."name" as "name", "users"."email" as "email", "users"."age" as "age", "users"."invitedBy" as "invitedBy", "users"."_internalId" as "_internalId", "users"."_version" as "_version""`,
968
- );
969
- expect(userCreate.query.parameters).toMatchObject([
970
- userId.externalId, // The generated ID
971
- "John Doe",
972
- "john@example.com",
973
- 30,
974
- ]);
975
- expect(userCreate.expectedAffectedRows).toBeNull();
976
-
977
- // Verify post create SQL - FragnoId generates subquery to lookup internal ID
978
- expect(postCreate.query.sql).toMatchInlineSnapshot(
979
- `"insert into "posts" ("id", "title", "content", "userId") values ($1, $2, $3, (select "_internalId" from "users" where "id" = $4 limit $5)) returning "posts"."id" as "id", "posts"."title" as "title", "posts"."content" as "content", "posts"."userId" as "userId", "posts"."viewCount" as "viewCount", "posts"."_internalId" as "_internalId", "posts"."_version" as "_version""`,
980
- );
981
- expect(postCreate.query.parameters).toMatchObject([
982
- expect.any(String), // generated post ID
983
- "My First Post",
984
- "This is my first post",
985
- userId.externalId, // FragnoId's externalId is used in the subquery
986
- 1, // limit parameter
987
- ]);
988
- expect(postCreate.expectedAffectedRows).toBeNull();
989
-
990
- // Verify the returned FragnoId has the expected structure
991
- expect(userId).toMatchObject({
992
- externalId: expect.any(String),
993
- version: 0,
994
- internalId: undefined,
995
- });
996
- });
997
- });
998
- });