@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
@@ -0,0 +1,504 @@
1
+ import type { AnySchema } from "../../schema/create";
2
+ import type { TypedUnitOfWork, IUnitOfWork } from "./unit-of-work";
3
+ import type { HooksMap } from "../../hooks/hooks";
4
+ import { NoRetryPolicy, ExponentialBackoffRetryPolicy, type RetryPolicy } from "./retry-policy";
5
+ import type { FragnoId } from "../../schema/create";
6
+
7
+ /**
8
+ * Error thrown when a Unit of Work execution fails due to optimistic concurrency conflict.
9
+ * This error triggers automatic retry behavior in executeRestrictedUnitOfWork.
10
+ */
11
+ export class ConcurrencyConflictError extends Error {
12
+ constructor(message = "Optimistic concurrency conflict detected") {
13
+ super(message);
14
+ this.name = "ConcurrencyConflictError";
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Type utility that unwraps promises 1 level deep in objects, arrays, or direct promises
20
+ * Handles tuples, arrays, objects, and direct promises
21
+ */
22
+ export type AwaitedPromisesInObject<T> =
23
+ // First check if it's a Promise
24
+ T extends Promise<infer U>
25
+ ? Awaited<U>
26
+ : // Check for arrays with known length (tuples) - preserves tuple structure
27
+ T extends readonly [unknown, ...unknown[]]
28
+ ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> }
29
+ : T extends [unknown, ...unknown[]]
30
+ ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> }
31
+ : // Check for regular arrays (unknown length)
32
+ T extends (infer U)[]
33
+ ? Awaited<U>[]
34
+ : T extends readonly (infer U)[]
35
+ ? readonly Awaited<U>[]
36
+ : // Check for objects
37
+ T extends Record<string, unknown>
38
+ ? {
39
+ [K in keyof T]: T[K] extends Promise<infer U> ? Awaited<U> : T[K];
40
+ }
41
+ : // Otherwise return as-is
42
+ T;
43
+
44
+ /**
45
+ * Await promises in an object 1 level deep
46
+ */
47
+ async function awaitPromisesInObject<T>(obj: T): Promise<AwaitedPromisesInObject<T>> {
48
+ if (obj === null || obj === undefined) {
49
+ return obj as AwaitedPromisesInObject<T>;
50
+ }
51
+
52
+ if (typeof obj !== "object") {
53
+ return obj as AwaitedPromisesInObject<T>;
54
+ }
55
+
56
+ // Check if it's a Promise
57
+ if (obj instanceof Promise) {
58
+ return (await obj) as AwaitedPromisesInObject<T>;
59
+ }
60
+
61
+ // Check if it's an array
62
+ if (Array.isArray(obj)) {
63
+ const awaited = await Promise.all(
64
+ obj.map((item) => (item instanceof Promise ? item : Promise.resolve(item))),
65
+ );
66
+ return awaited as AwaitedPromisesInObject<T>;
67
+ }
68
+
69
+ // It's a plain object - await promises in each property
70
+ const result = {} as T;
71
+ const entries = Object.entries(obj as Record<string, unknown>);
72
+ const awaitedEntries = await Promise.all(
73
+ entries.map(async ([key, value]) => {
74
+ const awaitedValue = value instanceof Promise ? await value : value;
75
+ return [key, awaitedValue] as const;
76
+ }),
77
+ );
78
+
79
+ for (const [key, value] of awaitedEntries) {
80
+ (result as Record<string, unknown>)[key] = value;
81
+ }
82
+
83
+ return result as AwaitedPromisesInObject<T>;
84
+ }
85
+
86
+ /**
87
+ * Result of executing a Unit of Work with retry support
88
+ * Promises in mutationResult are unwrapped 1 level deep
89
+ */
90
+ export type ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult> =
91
+ | {
92
+ success: true;
93
+ results: TRetrievalResults;
94
+ mutationResult: AwaitedPromisesInObject<TMutationResult>;
95
+ createdIds: FragnoId[];
96
+ nonce: string;
97
+ }
98
+ | {
99
+ success: false;
100
+ reason: "conflict";
101
+ }
102
+ | {
103
+ success: false;
104
+ reason: "aborted";
105
+ }
106
+ | {
107
+ success: false;
108
+ reason: "error";
109
+ error: unknown;
110
+ };
111
+
112
+ /**
113
+ * Callbacks for executing a Unit of Work
114
+ */
115
+ export interface ExecuteUnitOfWorkCallbacks<
116
+ TSchema extends AnySchema,
117
+ TRetrievalResults extends unknown[],
118
+ TMutationResult,
119
+ TRawInput,
120
+ > {
121
+ /**
122
+ * Retrieval phase callback - adds retrieval operations to the UOW
123
+ */
124
+ retrieve?: (
125
+ uow: TypedUnitOfWork<TSchema, [], TRawInput>,
126
+ ) => TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;
127
+
128
+ /**
129
+ * Mutation phase callback - receives UOW and retrieval results, adds mutation operations
130
+ */
131
+ mutate?: (
132
+ uow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>,
133
+ results: TRetrievalResults,
134
+ ) => TMutationResult | Promise<TMutationResult>;
135
+
136
+ /**
137
+ * Success callback - invoked after successful execution
138
+ * Promises in mutationResult are already unwrapped 1 level deep
139
+ */
140
+ onSuccess?: (result: {
141
+ results: TRetrievalResults;
142
+ mutationResult: AwaitedPromisesInObject<TMutationResult>;
143
+ createdIds: FragnoId[];
144
+ nonce: string;
145
+ }) => void | Promise<void>;
146
+ }
147
+
148
+ /**
149
+ * Options for executing a Unit of Work
150
+ */
151
+ export interface ExecuteUnitOfWorkOptions<TSchema extends AnySchema, TRawInput> {
152
+ /**
153
+ * Factory function that creates or resets a UOW instance for each attempt
154
+ */
155
+ createUnitOfWork: () => TypedUnitOfWork<TSchema, [], TRawInput>;
156
+
157
+ /**
158
+ * Retry policy for handling optimistic concurrency conflicts
159
+ */
160
+ retryPolicy?: RetryPolicy;
161
+
162
+ /**
163
+ * Abort signal to cancel execution
164
+ */
165
+ signal?: AbortSignal;
166
+ }
167
+
168
+ /**
169
+ * Create a bound version of executeUnitOfWork with a pre-configured UOW factory.
170
+ * This is useful for handler contexts where the factory is already known.
171
+ *
172
+ * @param createUnitOfWork - Factory function that creates a fresh UOW instance
173
+ * @returns A bound executeUnitOfWork function that doesn't require the factory parameter
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const boundExecute = createExecuteUnitOfWork(() => db.createUnitOfWork());
178
+ * const result = await boundExecute({
179
+ * retrieve: (uow) => uow.find("users", (b) => b.whereIndex("primary")),
180
+ * mutate: (uow, [users]) => {
181
+ * uow.update("users", users[0].id, (b) => b.set({ balance: newBalance }));
182
+ * }
183
+ * });
184
+ * ```
185
+ */
186
+ export function createExecuteUnitOfWork<TSchema extends AnySchema, TRawInput>(
187
+ createUnitOfWork: () => TypedUnitOfWork<TSchema, [], TRawInput>,
188
+ ) {
189
+ return async function <TRetrievalResults extends unknown[], TMutationResult = void>(
190
+ callbacks: ExecuteUnitOfWorkCallbacks<TSchema, TRetrievalResults, TMutationResult, TRawInput>,
191
+ options?: Omit<ExecuteUnitOfWorkOptions<TSchema, TRawInput>, "createUnitOfWork">,
192
+ ): Promise<ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult>> {
193
+ return executeUnitOfWork(callbacks, { ...options, createUnitOfWork });
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Execute a Unit of Work with automatic retry support for optimistic concurrency conflicts.
199
+ *
200
+ * This function orchestrates the two-phase execution (retrieval + mutation) with retry logic.
201
+ * It creates fresh UOW instances for each attempt.
202
+ *
203
+ * @param callbacks - Object containing retrieve, mutate, and onSuccess callbacks
204
+ * @param options - Configuration including UOW factory, retry policy, and abort signal
205
+ * @returns Promise resolving to the execution result
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const result = await executeUnitOfWork(
210
+ * {
211
+ * retrieve: (uow) => uow.find("users", (b) => b.whereIndex("primary")),
212
+ * mutate: (uow, [users]) => {
213
+ * const user = users[0];
214
+ * uow.update("users", user.id, (b) => b.set({ balance: newBalance }));
215
+ * },
216
+ * onSuccess: async ({ results, mutationResult }) => {
217
+ * console.log("Update successful!");
218
+ * }
219
+ * },
220
+ * {
221
+ * createUnitOfWork: () => queryEngine.createUnitOfWork(),
222
+ * retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3 })
223
+ * }
224
+ * );
225
+ * ```
226
+ */
227
+ export async function executeUnitOfWork<
228
+ TSchema extends AnySchema,
229
+ TRetrievalResults extends unknown[],
230
+ TMutationResult = void,
231
+ TRawInput = unknown,
232
+ >(
233
+ callbacks: ExecuteUnitOfWorkCallbacks<TSchema, TRetrievalResults, TMutationResult, TRawInput>,
234
+ options: ExecuteUnitOfWorkOptions<TSchema, TRawInput>,
235
+ ): Promise<ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult>> {
236
+ // Validate that at least one of retrieve or mutate is provided
237
+ if (!callbacks.retrieve && !callbacks.mutate) {
238
+ throw new Error("At least one of 'retrieve' or 'mutate' callbacks must be provided");
239
+ }
240
+
241
+ const retryPolicy = options.retryPolicy ?? new NoRetryPolicy();
242
+ const signal = options.signal;
243
+ let attempt = 0;
244
+
245
+ while (true) {
246
+ // Check if aborted before starting attempt
247
+ if (signal?.aborted) {
248
+ return { success: false, reason: "aborted" };
249
+ }
250
+
251
+ try {
252
+ // Create a fresh UOW for this attempt
253
+ const uow = options.createUnitOfWork();
254
+
255
+ // Apply retrieval phase if provided
256
+ let retrievalUow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;
257
+ if (callbacks.retrieve) {
258
+ retrievalUow = callbacks.retrieve(uow);
259
+ } else {
260
+ // No retrieval phase, use empty UOW with type cast
261
+ // This is safe because when there's no retrieve, TRetrievalResults should be []
262
+ retrievalUow = uow as unknown as TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;
263
+ }
264
+
265
+ // Execute retrieval phase
266
+ const results = (await retrievalUow.executeRetrieve()) as TRetrievalResults;
267
+
268
+ // Invoke mutation phase callback if provided
269
+ let mutationResult: TMutationResult;
270
+ if (callbacks.mutate) {
271
+ mutationResult = await callbacks.mutate(retrievalUow, results);
272
+ } else {
273
+ mutationResult = undefined as TMutationResult;
274
+ }
275
+
276
+ // Execute mutation phase
277
+ const { success } = await retrievalUow.executeMutations();
278
+
279
+ if (success) {
280
+ // Success! Get created IDs and nonce, then invoke onSuccess if provided
281
+ const createdIds = retrievalUow.getCreatedIds();
282
+ const nonce = retrievalUow.nonce;
283
+
284
+ // Await promises in mutationResult (1 level deep)
285
+ const awaitedMutationResult = await awaitPromisesInObject(mutationResult);
286
+
287
+ if (callbacks.onSuccess) {
288
+ await callbacks.onSuccess({
289
+ results,
290
+ mutationResult: awaitedMutationResult,
291
+ createdIds,
292
+ nonce,
293
+ });
294
+ }
295
+
296
+ return {
297
+ success: true,
298
+ results,
299
+ mutationResult: awaitedMutationResult,
300
+ createdIds,
301
+ nonce,
302
+ };
303
+ }
304
+
305
+ // Failed - check if we should retry
306
+ // attempt represents the number of attempts completed so far
307
+ if (!retryPolicy.shouldRetry(attempt, undefined, signal)) {
308
+ // No more retries
309
+ return { success: false, reason: "conflict" };
310
+ }
311
+
312
+ // Wait before retrying
313
+ const delayMs = retryPolicy.getDelayMs(attempt);
314
+ if (delayMs > 0) {
315
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
316
+ }
317
+
318
+ // Increment attempt counter for next iteration
319
+ attempt++;
320
+ } catch (error) {
321
+ // An error was thrown during execution
322
+ return { success: false, reason: "error", error };
323
+ }
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Options for executing a Unit of Work with restricted access
329
+ */
330
+ export interface ExecuteRestrictedUnitOfWorkOptions {
331
+ /**
332
+ * Factory function that creates or resets a UOW instance for each attempt
333
+ */
334
+ createUnitOfWork: () => IUnitOfWork;
335
+
336
+ /**
337
+ * Retry policy for handling optimistic concurrency conflicts
338
+ */
339
+ retryPolicy?: RetryPolicy;
340
+
341
+ /**
342
+ * Abort signal to cancel execution
343
+ */
344
+ signal?: AbortSignal;
345
+
346
+ /**
347
+ * Callback invoked before mutations are executed.
348
+ * Use this to add additional mutation operations (e.g., hook event records).
349
+ */
350
+ onBeforeMutate?: (uow: IUnitOfWork) => void;
351
+
352
+ /**
353
+ * Callback invoked after successful mutation phase.
354
+ * Use this for post-mutation processing like hook execution.
355
+ */
356
+ onSuccess?: (uow: IUnitOfWork) => Promise<void>;
357
+ }
358
+
359
+ /**
360
+ * Execute a Unit of Work with explicit phase control and automatic retry support.
361
+ *
362
+ * This function provides an alternative API where users write a single callback that receives
363
+ * a context object with forSchema, executeRetrieve, and executeMutate methods. The user can
364
+ * create schema-specific UOWs via forSchema, then call executeRetrieve() and executeMutate()
365
+ * to execute the retrieval and mutation phases. The entire callback is re-executed on optimistic
366
+ * concurrency conflicts, ensuring retries work properly.
367
+ *
368
+ * @param callback - Async function that receives a context with forSchema, executeRetrieve, executeMutate, nonce, and currentAttempt
369
+ * @param options - Configuration including UOW factory, retry policy, and abort signal
370
+ * @returns Promise resolving to the callback's return value
371
+ * @throws Error if retries are exhausted or callback throws an error
372
+ *
373
+ * @example
374
+ * ```ts
375
+ * const { userId, profileId } = await executeRestrictedUnitOfWork(
376
+ * async ({ forSchema, executeRetrieve, executeMutate, nonce, currentAttempt }) => {
377
+ * const uow = forSchema(schema);
378
+ * const userId = uow.create("users", { name: "John" });
379
+ *
380
+ * // Execute retrieval phase
381
+ * await executeRetrieve();
382
+ *
383
+ * const profileId = uow.create("profiles", { userId });
384
+ *
385
+ * // Execute mutation phase
386
+ * await executeMutate();
387
+ *
388
+ * return { userId, profileId };
389
+ * },
390
+ * {
391
+ * createUnitOfWork: () => db.createUnitOfWork(),
392
+ * retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 5 })
393
+ * }
394
+ * );
395
+ * ```
396
+ */
397
+ export async function executeRestrictedUnitOfWork<TResult, THooks extends HooksMap = {}>(
398
+ callback: (context: {
399
+ forSchema: <S extends AnySchema, H extends HooksMap = THooks>(
400
+ schema: S,
401
+ hooks?: H,
402
+ ) => TypedUnitOfWork<S, [], unknown, H>;
403
+ executeRetrieve: () => Promise<void>;
404
+ executeMutate: () => Promise<void>;
405
+ nonce: string;
406
+ currentAttempt: number;
407
+ }) => Promise<TResult>,
408
+ options: ExecuteRestrictedUnitOfWorkOptions,
409
+ ): Promise<AwaitedPromisesInObject<TResult>> {
410
+ // Default retry policy with small, fast retries for optimistic concurrency
411
+ const retryPolicy =
412
+ options.retryPolicy ??
413
+ new ExponentialBackoffRetryPolicy({
414
+ maxRetries: 5,
415
+ initialDelayMs: 10,
416
+ maxDelayMs: 100,
417
+ });
418
+ const signal = options.signal;
419
+ let attempt = 0;
420
+
421
+ while (true) {
422
+ // Check if aborted before starting attempt
423
+ if (signal?.aborted) {
424
+ throw new Error("Unit of Work execution aborted");
425
+ }
426
+
427
+ try {
428
+ // Create a fresh UOW for this attempt
429
+ const baseUow = options.createUnitOfWork();
430
+
431
+ const context = {
432
+ forSchema: <S extends AnySchema, H extends HooksMap = THooks>(schema: S, hooks?: H) => {
433
+ return baseUow.forSchema(schema, hooks);
434
+ },
435
+ executeRetrieve: async () => {
436
+ await baseUow.executeRetrieve();
437
+ },
438
+ executeMutate: async () => {
439
+ if (baseUow.state === "executed") {
440
+ return;
441
+ }
442
+
443
+ if (baseUow.state === "building-retrieval") {
444
+ await baseUow.executeRetrieve();
445
+ }
446
+
447
+ // Add hook mutations before executing
448
+ if (options.onBeforeMutate) {
449
+ options.onBeforeMutate(baseUow);
450
+ }
451
+
452
+ const result = await baseUow.executeMutations();
453
+ if (!result.success) {
454
+ throw new ConcurrencyConflictError();
455
+ }
456
+
457
+ if (options.onSuccess) {
458
+ await options.onSuccess(baseUow);
459
+ }
460
+ },
461
+ nonce: baseUow.nonce,
462
+ currentAttempt: attempt,
463
+ };
464
+
465
+ // Execute the callback which will call executeRetrieve and executeMutate
466
+ const result = await callback(context);
467
+
468
+ // Await promises in the result object (1 level deep)
469
+ const awaitedResult = await awaitPromisesInObject(result);
470
+
471
+ // Return the awaited result
472
+ return awaitedResult;
473
+ } catch (error) {
474
+ if (signal?.aborted) {
475
+ throw new Error("Unit of Work execution aborted");
476
+ }
477
+
478
+ // Only retry concurrency conflicts, not other errors
479
+ if (!(error instanceof ConcurrencyConflictError)) {
480
+ // Not a concurrency conflict - throw immediately without retry
481
+ throw error;
482
+ }
483
+
484
+ if (!retryPolicy.shouldRetry(attempt, error, signal)) {
485
+ // No more retries - check again if aborted or throw conflict error
486
+ if (signal?.aborted) {
487
+ throw new Error("Unit of Work execution aborted");
488
+ }
489
+ throw new Error("Unit of Work execution failed: optimistic concurrency conflict", {
490
+ cause: error,
491
+ });
492
+ }
493
+
494
+ // Wait before retrying
495
+ const delayMs = retryPolicy.getDelayMs(attempt);
496
+ if (delayMs > 0) {
497
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
498
+ }
499
+
500
+ // Increment attempt counter for next iteration
501
+ attempt++;
502
+ }
503
+ }
504
+ }