@fragno-dev/db 0.1.15 → 0.2.1

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 (402) hide show
  1. package/.turbo/turbo-build.log +242 -179
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +123 -8
  4. package/dist/adapters/adapters.d.ts +5 -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 -21
  8. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js +7 -54
  10. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  11. package/dist/adapters/drizzle/generate.d.ts +3 -0
  12. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  13. package/dist/adapters/drizzle/generate.js +36 -28
  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 -18
  68. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  69. package/dist/adapters/kysely/kysely-adapter.js +6 -165
  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} +47 -61
  72. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -0
  73. package/dist/adapters/{drizzle/shared.d.ts → shared/table-name-mapper.d.ts} +2 -4
  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 +107 -19
  80. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  81. package/dist/db-fragment-definition-builder.js +134 -20
  82. package/dist/db-fragment-definition-builder.js.map +1 -1
  83. package/dist/fragments/internal-fragment.d.ts +39 -5
  84. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  85. package/dist/fragments/internal-fragment.js +82 -10
  86. package/dist/fragments/internal-fragment.js.map +1 -1
  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 +23 -61
  94. package/dist/migration-engine/generation-engine.js.map +1 -1
  95. package/dist/mod.d.ts +34 -10
  96. package/dist/mod.d.ts.map +1 -1
  97. package/dist/mod.js +47 -16
  98. package/dist/mod.js.map +1 -1
  99. package/dist/node_modules/.pnpm/{rou3@0.7.8 → rou3@0.7.10}/node_modules/rou3/dist/index.js +1 -1
  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/fragment-instantiator.js +69 -31
  102. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -1
  103. package/dist/query/column-defaults.js +27 -0
  104. package/dist/query/column-defaults.js.map +1 -0
  105. package/dist/query/cursor.d.ts +4 -4
  106. package/dist/query/cursor.d.ts.map +1 -1
  107. package/dist/query/cursor.js +8 -6
  108. package/dist/query/cursor.js.map +1 -1
  109. package/dist/query/orm/orm.d.ts +1 -1
  110. package/dist/query/orm/orm.js.map +1 -1
  111. package/dist/query/serialize/create-sql-serializer.js +30 -0
  112. package/dist/query/serialize/create-sql-serializer.js.map +1 -0
  113. package/dist/query/serialize/dialect/mysql-serializer.js +87 -0
  114. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -0
  115. package/dist/query/serialize/dialect/postgres-serializer.js +80 -0
  116. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -0
  117. package/dist/query/serialize/dialect/sqlite-serializer.js +93 -0
  118. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -0
  119. package/dist/query/serialize/sql-serializer.js +67 -0
  120. package/dist/query/serialize/sql-serializer.js.map +1 -0
  121. package/dist/query/{query.d.ts → simple-query-interface.d.ts} +5 -5
  122. package/dist/query/simple-query-interface.d.ts.map +1 -0
  123. package/dist/query/{execute-unit-of-work.d.ts → unit-of-work/execute-unit-of-work.d.ts} +50 -4
  124. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  125. package/dist/query/{execute-unit-of-work.js → unit-of-work/execute-unit-of-work.js} +150 -5
  126. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  127. package/dist/query/{retry-policy.d.ts → unit-of-work/retry-policy.d.ts} +1 -1
  128. package/dist/query/unit-of-work/retry-policy.d.ts.map +1 -0
  129. package/dist/query/{retry-policy.js → unit-of-work/retry-policy.js} +1 -1
  130. package/dist/query/unit-of-work/retry-policy.js.map +1 -0
  131. package/dist/query/{unit-of-work.d.ts → unit-of-work/unit-of-work.d.ts} +69 -21
  132. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  133. package/dist/query/{unit-of-work.js → unit-of-work/unit-of-work.js} +83 -22
  134. package/dist/query/unit-of-work/unit-of-work.js.map +1 -0
  135. package/dist/query/value-decoding.js +71 -0
  136. package/dist/query/value-decoding.js.map +1 -0
  137. package/dist/query/value-encoding.js +124 -0
  138. package/dist/query/value-encoding.js.map +1 -0
  139. package/dist/schema/type-conversion/create-sql-type-mapper.js +29 -0
  140. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -0
  141. package/dist/schema/type-conversion/dialect/mysql.js +57 -0
  142. package/dist/schema/type-conversion/dialect/mysql.js.map +1 -0
  143. package/dist/schema/type-conversion/dialect/postgres.js +56 -0
  144. package/dist/schema/type-conversion/dialect/postgres.js.map +1 -0
  145. package/dist/schema/type-conversion/dialect/sqlite.js +52 -0
  146. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -0
  147. package/dist/schema/type-conversion/type-mapping.js +63 -0
  148. package/dist/schema/type-conversion/type-mapping.js.map +1 -0
  149. package/dist/sql-driver/connection/connection-provider.d.ts +13 -0
  150. package/dist/sql-driver/connection/connection-provider.d.ts.map +1 -0
  151. package/dist/sql-driver/connection/connection-provider.js +19 -0
  152. package/dist/sql-driver/connection/connection-provider.js.map +1 -0
  153. package/dist/sql-driver/connection/single-connection-provider.js +23 -0
  154. package/dist/sql-driver/connection/single-connection-provider.js.map +1 -0
  155. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  156. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  157. package/dist/sql-driver/dialects/dialects.d.ts +2 -0
  158. package/dist/sql-driver/dialects/dialects.js +3 -0
  159. package/dist/sql-driver/dialects/durable-object-dialect.d.ts +72 -0
  160. package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -0
  161. package/dist/sql-driver/dialects/durable-object-dialect.js +130 -0
  162. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -0
  163. package/dist/sql-driver/driver/runtime-driver.d.ts +23 -0
  164. package/dist/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  165. package/dist/sql-driver/driver/runtime-driver.js +56 -0
  166. package/dist/sql-driver/driver/runtime-driver.js.map +1 -0
  167. package/dist/sql-driver/query-executor/default-query-executor.js +26 -0
  168. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -0
  169. package/dist/sql-driver/query-executor/plugin.d.ts +17 -0
  170. package/dist/sql-driver/query-executor/plugin.d.ts.map +1 -0
  171. package/dist/sql-driver/query-executor/query-executor-base.js +25 -0
  172. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -0
  173. package/dist/sql-driver/query-executor/query-executor.d.ts +36 -0
  174. package/dist/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  175. package/dist/sql-driver/sql-driver-adapter.d.ts +29 -0
  176. package/dist/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  177. package/dist/sql-driver/sql-driver-adapter.js +68 -0
  178. package/dist/sql-driver/sql-driver-adapter.js.map +1 -0
  179. package/dist/sql-driver/sql-driver.d.ts +38 -0
  180. package/dist/sql-driver/sql-driver.d.ts.map +1 -0
  181. package/dist/sql-driver/sql-driver.js +1 -0
  182. package/dist/sql-driver/sql.js +50 -0
  183. package/dist/sql-driver/sql.js.map +1 -0
  184. package/dist/with-database.d.ts +6 -2
  185. package/dist/with-database.d.ts.map +1 -1
  186. package/dist/with-database.js +1 -1
  187. package/dist/with-database.js.map +1 -1
  188. package/package.json +39 -12
  189. package/src/adapters/adapters.ts +8 -5
  190. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +61 -169
  191. package/src/adapters/drizzle/{drizzle-adapter-sqlite.test.ts → drizzle-adapter-sqlite3.test.ts} +31 -55
  192. package/src/adapters/drizzle/drizzle-adapter.ts +15 -107
  193. package/src/adapters/drizzle/generate.test.ts +2 -2
  194. package/src/adapters/drizzle/generate.ts +78 -34
  195. package/src/adapters/drizzle/migrate-drizzle.test.ts +19 -0
  196. package/src/adapters/drizzle/shared.ts +0 -34
  197. package/src/adapters/drizzle/test-utils.ts +3 -3
  198. package/src/adapters/generic-sql/README.md +14 -0
  199. package/src/adapters/generic-sql/driver-config.ts +144 -0
  200. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +50 -0
  201. package/src/adapters/generic-sql/generic-sql-adapter.ts +146 -0
  202. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +130 -0
  203. package/src/adapters/generic-sql/migration/cold-kysely.ts +55 -0
  204. package/src/adapters/{kysely/migration/execute-mysql.test.ts → generic-sql/migration/dialect/mysql.test.ts} +342 -484
  205. package/src/adapters/generic-sql/migration/dialect/mysql.ts +104 -0
  206. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +1008 -0
  207. package/src/adapters/generic-sql/migration/dialect/postgres.ts +113 -0
  208. package/src/adapters/{kysely/migration/execute-sqlite.test.ts → generic-sql/migration/dialect/sqlite.test.ts} +307 -510
  209. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +189 -0
  210. package/src/adapters/generic-sql/migration/executor.ts +33 -0
  211. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +661 -0
  212. package/src/adapters/generic-sql/migration/prepared-migrations.ts +214 -0
  213. package/src/adapters/generic-sql/migration/sql-generator.ts +413 -0
  214. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +36 -0
  215. package/src/adapters/generic-sql/query/cursor-utils.ts +56 -0
  216. package/src/adapters/generic-sql/query/dialect/mysql.ts +34 -0
  217. package/src/adapters/generic-sql/query/dialect/postgres.ts +32 -0
  218. package/src/adapters/generic-sql/query/dialect/sqlite.ts +32 -0
  219. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +1568 -0
  220. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +314 -0
  221. package/src/adapters/generic-sql/query/select-builder.test.ts +256 -0
  222. package/src/adapters/generic-sql/query/select-builder.ts +137 -0
  223. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +195 -0
  224. package/src/adapters/generic-sql/query/sql-query-compiler.ts +367 -0
  225. package/src/adapters/generic-sql/query/where-builder.test.ts +744 -0
  226. package/src/adapters/generic-sql/query/where-builder.ts +211 -0
  227. package/src/adapters/generic-sql/result-interpreter.ts +102 -0
  228. package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +899 -0
  229. package/src/adapters/generic-sql/uow-decoder.test.ts +399 -0
  230. package/src/adapters/generic-sql/uow-decoder.ts +152 -0
  231. package/src/adapters/generic-sql/uow-encoder.test.ts +183 -0
  232. package/src/adapters/generic-sql/uow-encoder.ts +131 -0
  233. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +26 -76
  234. package/src/adapters/kysely/{kysely-adapter-sqlite.test.ts → kysely-adapter-sqlocal.test.ts} +76 -17
  235. package/src/adapters/kysely/kysely-adapter.ts +10 -250
  236. package/src/adapters/{drizzle/drizzle-query.ts → shared/from-unit-of-work-compiler.ts} +110 -104
  237. package/src/adapters/shared/table-name-mapper.ts +50 -0
  238. package/src/adapters/shared/uow-operation-compiler.ts +211 -0
  239. package/src/db-fragment-definition-builder.test.ts +2 -2
  240. package/src/db-fragment-definition-builder.ts +461 -50
  241. package/src/db-fragment-instantiator.test.ts +78 -2
  242. package/src/db-fragment-integration.test.ts +14 -16
  243. package/src/fragments/internal-fragment.test.ts +434 -45
  244. package/src/fragments/internal-fragment.ts +183 -20
  245. package/src/hooks/hooks.test.ts +587 -0
  246. package/src/hooks/hooks.ts +179 -0
  247. package/src/migration-engine/generation-engine.test.ts +44 -54
  248. package/src/migration-engine/generation-engine.ts +48 -94
  249. package/src/mod.ts +117 -29
  250. package/src/query/column-defaults.ts +49 -0
  251. package/src/query/cursor.test.ts +31 -6
  252. package/src/query/cursor.ts +11 -7
  253. package/src/query/orm/orm.ts +1 -1
  254. package/src/query/query-type.test.ts +9 -9
  255. package/src/query/serialize/create-sql-serializer.ts +34 -0
  256. package/src/query/serialize/dialect/mysql-serializer.ts +142 -0
  257. package/src/query/serialize/dialect/postgres-serializer.ts +129 -0
  258. package/src/query/serialize/dialect/sqlite-serializer.test.ts +251 -0
  259. package/src/query/serialize/dialect/sqlite-serializer.ts +156 -0
  260. package/src/query/serialize/sql-serializer.ts +143 -0
  261. package/src/query/{query.ts → simple-query-interface.ts} +2 -2
  262. package/src/query/{execute-unit-of-work.test.ts → unit-of-work/execute-unit-of-work.test.ts} +571 -17
  263. package/src/query/{execute-unit-of-work.ts → unit-of-work/execute-unit-of-work.ts} +361 -12
  264. package/src/query/{unit-of-work-coordinator.test.ts → unit-of-work/unit-of-work-coordinator.test.ts} +290 -44
  265. package/src/query/{unit-of-work-types.test.ts → unit-of-work/unit-of-work-types.test.ts} +5 -3
  266. package/src/query/{unit-of-work.test.ts → unit-of-work/unit-of-work.test.ts} +100 -9
  267. package/src/query/{unit-of-work.ts → unit-of-work/unit-of-work.ts} +174 -49
  268. package/src/query/{result-transform.test.ts → value-decoding.test.ts} +45 -427
  269. package/src/query/value-decoding.ts +113 -0
  270. package/src/query/value-encoding.test.ts +390 -0
  271. package/src/query/value-encoding.ts +168 -0
  272. package/src/schema/create.test.ts +5 -1
  273. package/src/schema/serialize.test.ts +165 -407
  274. package/src/schema/type-conversion/create-sql-type-mapper.ts +28 -0
  275. package/src/schema/type-conversion/dialect/mysql.ts +64 -0
  276. package/src/schema/type-conversion/dialect/postgres.ts +62 -0
  277. package/src/schema/type-conversion/dialect/sqlite.ts +63 -0
  278. package/src/schema/type-conversion/type-mapping.test.ts +137 -0
  279. package/src/schema/type-conversion/type-mapping.ts +153 -0
  280. package/src/shared/connection-pool.ts +5 -5
  281. package/src/sql-driver/better-sqlite3.test.ts +126 -0
  282. package/src/sql-driver/connection/connection-provider.ts +27 -0
  283. package/src/sql-driver/connection/single-connection-provider.ts +42 -0
  284. package/src/sql-driver/dialect-adapter/dialect-adapter.ts +9 -0
  285. package/src/sql-driver/dialect-adapter/sqlite-dialect-adapter.ts +7 -0
  286. package/src/sql-driver/dialects/dialects.ts +1 -0
  287. package/src/sql-driver/dialects/durable-object-dialect.ts +260 -0
  288. package/src/sql-driver/driver/runtime-driver.ts +91 -0
  289. package/src/sql-driver/query-executor/default-query-executor.ts +38 -0
  290. package/src/sql-driver/query-executor/plugin.ts +22 -0
  291. package/src/sql-driver/query-executor/query-executor-base.ts +53 -0
  292. package/src/sql-driver/query-executor/query-executor.ts +44 -0
  293. package/src/sql-driver/sql-driver-adapter.ts +96 -0
  294. package/src/sql-driver/sql-driver.ts +53 -0
  295. package/src/sql-driver/sql.ts +57 -0
  296. package/src/sql-driver/sqlocal.test.ts +117 -0
  297. package/src/with-database.ts +35 -23
  298. package/tsdown.config.ts +7 -2
  299. package/dist/adapters/drizzle/drizzle-connection-pool.js +0 -40
  300. package/dist/adapters/drizzle/drizzle-connection-pool.js.map +0 -1
  301. package/dist/adapters/drizzle/drizzle-query.d.ts +0 -23
  302. package/dist/adapters/drizzle/drizzle-query.d.ts.map +0 -1
  303. package/dist/adapters/drizzle/drizzle-query.js.map +0 -1
  304. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -10
  305. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +0 -1
  306. package/dist/adapters/drizzle/drizzle-uow-compiler.js +0 -334
  307. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +0 -1
  308. package/dist/adapters/drizzle/drizzle-uow-decoder.js +0 -123
  309. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +0 -1
  310. package/dist/adapters/drizzle/drizzle-uow-executor.js +0 -160
  311. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +0 -1
  312. package/dist/adapters/drizzle/join-column-utils.js +0 -28
  313. package/dist/adapters/drizzle/join-column-utils.js.map +0 -1
  314. package/dist/adapters/drizzle/shared.d.ts.map +0 -1
  315. package/dist/adapters/drizzle/shared.js +0 -35
  316. package/dist/adapters/drizzle/shared.js.map +0 -1
  317. package/dist/adapters/kysely/kysely-connection-pool.js +0 -41
  318. package/dist/adapters/kysely/kysely-connection-pool.js.map +0 -1
  319. package/dist/adapters/kysely/kysely-query-builder.js +0 -321
  320. package/dist/adapters/kysely/kysely-query-builder.js.map +0 -1
  321. package/dist/adapters/kysely/kysely-query-compiler.js +0 -67
  322. package/dist/adapters/kysely/kysely-query-compiler.js.map +0 -1
  323. package/dist/adapters/kysely/kysely-query.d.ts +0 -23
  324. package/dist/adapters/kysely/kysely-query.d.ts.map +0 -1
  325. package/dist/adapters/kysely/kysely-query.js +0 -230
  326. package/dist/adapters/kysely/kysely-query.js.map +0 -1
  327. package/dist/adapters/kysely/kysely-shared.d.ts +0 -14
  328. package/dist/adapters/kysely/kysely-shared.d.ts.map +0 -1
  329. package/dist/adapters/kysely/kysely-shared.js +0 -33
  330. package/dist/adapters/kysely/kysely-shared.js.map +0 -1
  331. package/dist/adapters/kysely/kysely-uow-compiler.js +0 -193
  332. package/dist/adapters/kysely/kysely-uow-compiler.js.map +0 -1
  333. package/dist/adapters/kysely/kysely-uow-executor.js +0 -93
  334. package/dist/adapters/kysely/kysely-uow-executor.js.map +0 -1
  335. package/dist/adapters/kysely/migration/execute-base.js +0 -128
  336. package/dist/adapters/kysely/migration/execute-base.js.map +0 -1
  337. package/dist/adapters/kysely/migration/execute-factory.js +0 -34
  338. package/dist/adapters/kysely/migration/execute-factory.js.map +0 -1
  339. package/dist/adapters/kysely/migration/execute-mssql.js +0 -112
  340. package/dist/adapters/kysely/migration/execute-mssql.js.map +0 -1
  341. package/dist/adapters/kysely/migration/execute-mysql.js +0 -93
  342. package/dist/adapters/kysely/migration/execute-mysql.js.map +0 -1
  343. package/dist/adapters/kysely/migration/execute-postgres.js +0 -104
  344. package/dist/adapters/kysely/migration/execute-postgres.js.map +0 -1
  345. package/dist/adapters/kysely/migration/execute-sqlite.js +0 -123
  346. package/dist/adapters/kysely/migration/execute-sqlite.js.map +0 -1
  347. package/dist/adapters/kysely/migration/execute.js +0 -34
  348. package/dist/adapters/kysely/migration/execute.js.map +0 -1
  349. package/dist/migration-engine/create.d.ts +0 -37
  350. package/dist/migration-engine/create.d.ts.map +0 -1
  351. package/dist/migration-engine/create.js +0 -58
  352. package/dist/migration-engine/create.js.map +0 -1
  353. package/dist/migration-engine/shared.d.ts +0 -112
  354. package/dist/migration-engine/shared.d.ts.map +0 -1
  355. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +0 -1
  356. package/dist/query/execute-unit-of-work.d.ts.map +0 -1
  357. package/dist/query/execute-unit-of-work.js.map +0 -1
  358. package/dist/query/query.d.ts.map +0 -1
  359. package/dist/query/result-transform.js +0 -170
  360. package/dist/query/result-transform.js.map +0 -1
  361. package/dist/query/retry-policy.d.ts.map +0 -1
  362. package/dist/query/retry-policy.js.map +0 -1
  363. package/dist/query/unit-of-work.d.ts.map +0 -1
  364. package/dist/query/unit-of-work.js.map +0 -1
  365. package/dist/schema/serialize.js +0 -111
  366. package/dist/schema/serialize.js.map +0 -1
  367. package/src/adapters/drizzle/drizzle-adapter.test.ts +0 -122
  368. package/src/adapters/drizzle/drizzle-connection-pool.ts +0 -66
  369. package/src/adapters/drizzle/drizzle-query.test.ts +0 -499
  370. package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +0 -1442
  371. package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +0 -1414
  372. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +0 -1400
  373. package/src/adapters/drizzle/drizzle-uow-compiler.ts +0 -677
  374. package/src/adapters/drizzle/drizzle-uow-decoder.ts +0 -228
  375. package/src/adapters/drizzle/drizzle-uow-executor.ts +0 -309
  376. package/src/adapters/drizzle/join-column-utils.test.ts +0 -79
  377. package/src/adapters/drizzle/join-column-utils.ts +0 -39
  378. package/src/adapters/kysely/kysely-connection-pool.ts +0 -70
  379. package/src/adapters/kysely/kysely-query-builder.test.ts +0 -1344
  380. package/src/adapters/kysely/kysely-query-builder.ts +0 -666
  381. package/src/adapters/kysely/kysely-query-compiler.ts +0 -127
  382. package/src/adapters/kysely/kysely-query.test.ts +0 -498
  383. package/src/adapters/kysely/kysely-query.ts +0 -399
  384. package/src/adapters/kysely/kysely-shared.ts +0 -57
  385. package/src/adapters/kysely/kysely-uow-compiler.test.ts +0 -986
  386. package/src/adapters/kysely/kysely-uow-compiler.ts +0 -350
  387. package/src/adapters/kysely/kysely-uow-executor.ts +0 -164
  388. package/src/adapters/kysely/kysely-uow-joins.test.ts +0 -794
  389. package/src/adapters/kysely/migration/execute-base.ts +0 -256
  390. package/src/adapters/kysely/migration/execute-factory.ts +0 -53
  391. package/src/adapters/kysely/migration/execute-mssql.ts +0 -250
  392. package/src/adapters/kysely/migration/execute-mysql.ts +0 -211
  393. package/src/adapters/kysely/migration/execute-postgres.test.ts +0 -2657
  394. package/src/adapters/kysely/migration/execute-postgres.ts +0 -234
  395. package/src/adapters/kysely/migration/execute-sqlite.ts +0 -247
  396. package/src/adapters/kysely/migration/execute.ts +0 -50
  397. package/src/adapters/kysely/migration/kysely-migrator.test.ts +0 -261
  398. package/src/query/result-transform.ts +0 -274
  399. package/src/schema/serialize.ts +0 -407
  400. /package/dist/query/{query.js → simple-query-interface.js} +0 -0
  401. /package/src/query/{retry-policy.test.ts → unit-of-work/retry-policy.test.ts} +0 -0
  402. /package/src/query/{retry-policy.ts → unit-of-work/retry-policy.ts} +0 -0
@@ -1,9 +1,13 @@
1
1
  import type { AnySchema } from "./schema/create";
2
- import type { AbstractQuery } from "./query/query";
2
+ import type { SimpleQueryInterface } from "./query/simple-query-interface";
3
3
  import type { DatabaseAdapter } from "./adapters/adapters";
4
- import type { IUnitOfWork } from "./query/unit-of-work";
5
- import { TypedUnitOfWork, UnitOfWork } from "./query/unit-of-work";
6
- import type { RequestThisContext, FragnoPublicConfig } from "@fragno-dev/core";
4
+ import type { IUnitOfWork } from "./query/unit-of-work/unit-of-work";
5
+ import { TypedUnitOfWork, UnitOfWork } from "./query/unit-of-work/unit-of-work";
6
+ import type {
7
+ RequestThisContext,
8
+ FragnoPublicConfig,
9
+ AnyFragnoInstantiatedFragment,
10
+ } from "@fragno-dev/core";
7
11
  import {
8
12
  FragmentDefinitionBuilder,
9
13
  type FragmentDefinition,
@@ -11,9 +15,22 @@ import {
11
15
  } from "@fragno-dev/core";
12
16
  import {
13
17
  executeRestrictedUnitOfWork,
18
+ executeTxArray,
19
+ executeTxCallbacks,
20
+ executeServiceTx,
14
21
  type AwaitedPromisesInObject,
15
22
  type ExecuteRestrictedUnitOfWorkOptions,
16
- } from "./query/execute-unit-of-work";
23
+ type HandlerTxCallbacks,
24
+ type ServiceTxCallbacks,
25
+ } from "./query/unit-of-work/execute-unit-of-work";
26
+ import {
27
+ prepareHookMutations,
28
+ processHooks,
29
+ type HooksMap,
30
+ type HookFn,
31
+ type HookContext,
32
+ } from "./hooks/hooks";
33
+ import type { InternalFragmentInstance } from "./fragments/internal-fragment";
17
34
 
18
35
  /**
19
36
  * Extended FragnoPublicConfig that includes a database adapter.
@@ -32,7 +49,7 @@ export type ImplicitDatabaseDependencies<TSchema extends AnySchema> = {
32
49
  /**
33
50
  * Database query engine for the fragment's schema.
34
51
  */
35
- db: AbstractQuery<TSchema>;
52
+ db: SimpleQueryInterface<TSchema>;
36
53
  /**
37
54
  * The schema definition for this fragment.
38
55
  */
@@ -50,19 +67,43 @@ export type ImplicitDatabaseDependencies<TSchema extends AnySchema> = {
50
67
  /**
51
68
  * Service context for database fragments - provides restricted UOW access without execute methods.
52
69
  */
53
- export type DatabaseServiceContext = RequestThisContext & {
70
+ export type DatabaseServiceContext<THooks extends HooksMap> = RequestThisContext & {
54
71
  /**
55
72
  * Get a typed, restricted Unit of Work for the given schema.
56
73
  * @param schema - Schema to get a typed view for
57
74
  * @returns TypedUnitOfWork (restricted version without execute methods)
58
75
  */
59
- uow<TSchema extends AnySchema>(schema: TSchema): TypedUnitOfWork<TSchema, [], unknown>;
76
+ uow<TSchema extends AnySchema>(schema: TSchema): TypedUnitOfWork<TSchema, [], unknown, THooks>;
77
+
78
+ /**
79
+ * Execute a transaction with two-phase callbacks (retrieve + mutate).
80
+ *
81
+ * @param schema - Schema to use for the transaction
82
+ * @param callbacks - Object containing retrieve and mutate callbacks
83
+ * @returns Promise resolving to the mutation result with promises awaited 1 level deep
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * return this.tx(schema, {
88
+ * retrieve: (uow) => uow.findFirst("users", ...),
89
+ * mutate: async (uow, [user]) => {
90
+ * await validateUser(user);
91
+ * uow.update("users", user.id, ...).check();
92
+ * return { ok: true };
93
+ * }
94
+ * });
95
+ * ```
96
+ */
97
+ tx<TSchema extends AnySchema, TRetrievalResults extends unknown[], TMutationResult = void>(
98
+ schema: TSchema,
99
+ callbacks: ServiceTxCallbacks<TSchema, TRetrievalResults, TMutationResult, THooks>,
100
+ ): Promise<AwaitedPromisesInObject<TMutationResult>>;
60
101
  };
61
102
 
62
103
  /**
63
104
  * Handler context for database fragments - provides UOW execution with automatic retry support.
64
105
  */
65
- export type DatabaseHandlerContext = RequestThisContext & {
106
+ export type DatabaseHandlerContext<THooks extends HooksMap = {}> = RequestThisContext & {
66
107
  /**
67
108
  * Execute a Unit of Work with explicit phase control and automatic retry support.
68
109
  * This method provides an API where users call forSchema to create a schema-specific
@@ -96,7 +137,10 @@ export type DatabaseHandlerContext = RequestThisContext & {
96
137
  */
97
138
  uow<TResult>(
98
139
  callback: (context: {
99
- forSchema: <S extends AnySchema>(schema: S) => TypedUnitOfWork<S, [], unknown>;
140
+ forSchema: <TSchema extends AnySchema, H extends HooksMap = THooks>(
141
+ schema: TSchema,
142
+ hooks?: H,
143
+ ) => TypedUnitOfWork<TSchema, [], unknown, H>;
100
144
  executeRetrieve: () => Promise<void>;
101
145
  executeMutate: () => Promise<void>;
102
146
  nonce: string;
@@ -104,6 +148,49 @@ export type DatabaseHandlerContext = RequestThisContext & {
104
148
  }) => Promise<TResult> | TResult,
105
149
  options?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
106
150
  ): Promise<AwaitedPromisesInObject<TResult>>;
151
+
152
+ /**
153
+ * Execute a transaction with automatic retry support.
154
+ * Provides two overloads: array syntax (common case) and callback syntax (advanced).
155
+ *
156
+ * Array syntax - pass a factory function that returns service calls:
157
+ * ```ts
158
+ * const [transfer, notify] = await this.tx(
159
+ * () => [
160
+ * services.transfer({ from, to, amount }),
161
+ * services.notify({ userId, message })
162
+ * ]
163
+ * );
164
+ * ```
165
+ *
166
+ * Callback syntax - for handlers that need direct UOW access:
167
+ * ```ts
168
+ * const result = await this.tx({
169
+ * retrieve: ({ forSchema }) => {
170
+ * const uow = forSchema(schema);
171
+ * uow.find("users", ...);
172
+ * return services.transfer({ ... });
173
+ * },
174
+ * mutate: ({ forSchema }, transferPromise) => {
175
+ * const uow = forSchema(schema);
176
+ * uow.create("auditLog", { ... });
177
+ * return transferPromise;
178
+ * }
179
+ * });
180
+ * ```
181
+ *
182
+ * Note: Handler callbacks are synchronous only to prevent accidentally awaiting services in wrong place.
183
+ */
184
+ // Overload 1: Array syntax (common case) - accepts a factory
185
+ tx<T extends readonly unknown[]>(
186
+ servicesFactory: () => readonly [...{ [K in keyof T]: Promise<T[K]> }],
187
+ options?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
188
+ ): Promise<{ [K in keyof T]: T[K] }>;
189
+ // Overload 2: Callback syntax (advanced, sync callbacks only)
190
+ tx<TRetrieveResult, TMutationResult>(
191
+ callbacks: HandlerTxCallbacks<TRetrieveResult, TMutationResult, THooks>,
192
+ options?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
193
+ ): Promise<AwaitedPromisesInObject<TMutationResult>>;
107
194
  };
108
195
 
109
196
  /**
@@ -117,7 +204,7 @@ export type DatabaseFragmentContext<TSchema extends AnySchema> = {
117
204
  /**
118
205
  * ORM query engine for the fragment's schema.
119
206
  */
120
- db: AbstractQuery<TSchema>;
207
+ db: SimpleQueryInterface<TSchema>;
121
208
  };
122
209
 
123
210
  /**
@@ -163,8 +250,10 @@ export class DatabaseFragmentDefinitionBuilder<
163
250
  TServices,
164
251
  TServiceDependencies,
165
252
  TPrivateServices,
253
+ THooks extends HooksMap = {},
166
254
  TServiceThisContext extends RequestThisContext = DatabaseHandlerContext,
167
255
  THandlerThisContext extends RequestThisContext = DatabaseHandlerContext,
256
+ TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment> = {},
168
257
  > {
169
258
  // Store the base builder - we'll replace its storage and context setup when building
170
259
  #baseBuilder: FragmentDefinitionBuilder<
@@ -177,10 +266,12 @@ export class DatabaseFragmentDefinitionBuilder<
177
266
  TPrivateServices,
178
267
  TServiceThisContext,
179
268
  THandlerThisContext,
180
- DatabaseRequestStorage
269
+ DatabaseRequestStorage,
270
+ TLinkedFragments
181
271
  >;
182
272
  #schema: TSchema;
183
273
  #namespace: string;
274
+ #hooksFactory?: (context: { config: TConfig; options: FragnoPublicConfigWithDatabase }) => THooks;
184
275
 
185
276
  constructor(
186
277
  baseBuilder: FragmentDefinitionBuilder<
@@ -193,14 +284,20 @@ export class DatabaseFragmentDefinitionBuilder<
193
284
  TPrivateServices,
194
285
  TServiceThisContext,
195
286
  THandlerThisContext,
196
- DatabaseRequestStorage
287
+ DatabaseRequestStorage,
288
+ TLinkedFragments
197
289
  >,
198
290
  schema: TSchema,
199
291
  namespace?: string,
292
+ hooksFactory?: (context: {
293
+ config: TConfig;
294
+ options: FragnoPublicConfigWithDatabase;
295
+ }) => THooks,
200
296
  ) {
201
297
  this.#baseBuilder = baseBuilder;
202
298
  this.#schema = schema;
203
- this.#namespace = namespace ?? baseBuilder.name + "-db";
299
+ this.#namespace = namespace ?? baseBuilder.name;
300
+ this.#hooksFactory = hooksFactory;
204
301
  }
205
302
 
206
303
  /**
@@ -211,7 +308,7 @@ export class DatabaseFragmentDefinitionBuilder<
211
308
  fn: (context: {
212
309
  config: TConfig;
213
310
  options: FragnoPublicConfigWithDatabase;
214
- db: AbstractQuery<TSchema>;
311
+ db: SimpleQueryInterface<TSchema>;
215
312
  databaseAdapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
216
313
  }) => TNewDeps,
217
314
  ): DatabaseFragmentDefinitionBuilder<
@@ -222,8 +319,10 @@ export class DatabaseFragmentDefinitionBuilder<
222
319
  {},
223
320
  TServiceDependencies,
224
321
  {},
322
+ THooks,
225
323
  TServiceThisContext,
226
- THandlerThisContext
324
+ THandlerThisContext,
325
+ TLinkedFragments
227
326
  > {
228
327
  // Wrap user function to inject DB context
229
328
  const wrappedFn = (context: { config: TConfig; options: FragnoPublicConfigWithDatabase }) => {
@@ -255,8 +354,12 @@ export class DatabaseFragmentDefinitionBuilder<
255
354
  // Create new base builder with wrapped function
256
355
  const newBaseBuilder = this.#baseBuilder.withDependencies(wrappedFn);
257
356
 
258
- // Return new database builder with updated base
259
- return new DatabaseFragmentDefinitionBuilder(newBaseBuilder, this.#schema, this.#namespace);
357
+ return new DatabaseFragmentDefinitionBuilder(
358
+ newBaseBuilder,
359
+ this.#schema,
360
+ this.#namespace,
361
+ this.#hooksFactory,
362
+ );
260
363
  }
261
364
 
262
365
  providesBaseService<TNewService>(
@@ -277,12 +380,19 @@ export class DatabaseFragmentDefinitionBuilder<
277
380
  TServices,
278
381
  TServiceDependencies,
279
382
  TPrivateServices,
383
+ THooks,
280
384
  TServiceThisContext,
281
- THandlerThisContext
385
+ THandlerThisContext,
386
+ TLinkedFragments
282
387
  > {
283
388
  const newBaseBuilder = this.#baseBuilder.providesBaseService<TNewService>(fn);
284
389
 
285
- return new DatabaseFragmentDefinitionBuilder(newBaseBuilder, this.#schema, this.#namespace);
390
+ return new DatabaseFragmentDefinitionBuilder(
391
+ newBaseBuilder,
392
+ this.#schema,
393
+ this.#namespace,
394
+ this.#hooksFactory,
395
+ );
286
396
  }
287
397
 
288
398
  providesService<TServiceName extends string, TService>(
@@ -304,15 +414,147 @@ export class DatabaseFragmentDefinitionBuilder<
304
414
  TServices & { [K in TServiceName]: TService },
305
415
  TServiceDependencies,
306
416
  TPrivateServices,
417
+ THooks,
307
418
  TServiceThisContext,
308
- THandlerThisContext
419
+ THandlerThisContext,
420
+ TLinkedFragments
309
421
  > {
310
422
  const newBaseBuilder = this.#baseBuilder.providesService<TServiceName, TService>(
311
423
  serviceName,
312
424
  fn,
313
425
  );
314
426
 
315
- return new DatabaseFragmentDefinitionBuilder(newBaseBuilder, this.#schema, this.#namespace);
427
+ return new DatabaseFragmentDefinitionBuilder(
428
+ newBaseBuilder,
429
+ this.#schema,
430
+ this.#namespace,
431
+ this.#hooksFactory,
432
+ );
433
+ }
434
+
435
+ /**
436
+ * Provide a private service that is only accessible to the fragment author.
437
+ * Private services are NOT exposed on the fragment instance, but can be used
438
+ * when defining other services (baseServices, namedServices, and other privateServices).
439
+ * Private services are instantiated in order, so earlier private services are available
440
+ * to later ones.
441
+ */
442
+ providesPrivateService<TServiceName extends string, TService>(
443
+ serviceName: TServiceName,
444
+ fn: ServiceConstructorFn<
445
+ TConfig,
446
+ FragnoPublicConfigWithDatabase,
447
+ TDeps,
448
+ TServiceDependencies,
449
+ TPrivateServices,
450
+ TService,
451
+ TServiceThisContext
452
+ >,
453
+ ): DatabaseFragmentDefinitionBuilder<
454
+ TSchema,
455
+ TConfig,
456
+ TDeps,
457
+ TBaseServices,
458
+ TServices,
459
+ TServiceDependencies,
460
+ TPrivateServices & { [K in TServiceName]: TService },
461
+ THooks,
462
+ TServiceThisContext,
463
+ THandlerThisContext,
464
+ TLinkedFragments
465
+ > {
466
+ const newBaseBuilder = this.#baseBuilder.providesPrivateService<TServiceName, TService>(
467
+ serviceName,
468
+ fn,
469
+ );
470
+
471
+ return new DatabaseFragmentDefinitionBuilder(
472
+ newBaseBuilder,
473
+ this.#schema,
474
+ this.#namespace,
475
+ this.#hooksFactory,
476
+ );
477
+ }
478
+
479
+ /**
480
+ * Define durable hooks for this fragment.
481
+ * Hooks are automatically persisted and retried on failure.
482
+ *
483
+ * @param fn - Function that receives defineHook helper and returns a hooks map
484
+ * @returns Builder with hooks type set
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * .provideHooks(({ defineHook, config }) => ({
489
+ * onSubscribe: defineHook(async function (payload: { email: string }) {
490
+ * // 'this' context available (HookServiceContext with nonce)
491
+ * await config.onSubscribe?.(payload.email);
492
+ * }),
493
+ * }))
494
+ * ```
495
+ */
496
+ provideHooks<TNewHooks extends HooksMap>(
497
+ fn: (context: {
498
+ config: TConfig;
499
+ options: FragnoPublicConfigWithDatabase;
500
+ defineHook: <TPayload>(
501
+ hook: (this: HookContext, payload: TPayload) => void | Promise<void>,
502
+ ) => HookFn<TPayload>;
503
+ }) => TNewHooks,
504
+ ): DatabaseFragmentDefinitionBuilder<
505
+ TSchema,
506
+ TConfig,
507
+ TDeps,
508
+ TBaseServices,
509
+ TServices,
510
+ TServiceDependencies,
511
+ TPrivateServices,
512
+ TNewHooks,
513
+ DatabaseServiceContext<TNewHooks>,
514
+ THandlerThisContext,
515
+ TLinkedFragments
516
+ > {
517
+ const defineHook = <TPayload>(
518
+ hook: (this: HookContext, payload: TPayload) => void | Promise<void>,
519
+ ): HookFn<TPayload> => {
520
+ return hook;
521
+ };
522
+
523
+ // Store the hooks factory - it will be called in build() with config/options
524
+ const hooksFactory = (context: {
525
+ config: TConfig;
526
+ options: FragnoPublicConfigWithDatabase;
527
+ }) => {
528
+ return fn({
529
+ config: context.config,
530
+ options: context.options,
531
+ defineHook,
532
+ });
533
+ };
534
+
535
+ // Create new builder with hooks factory stored
536
+ // Cast is safe: we're only changing THooks and TServiceThisContext type parameters
537
+ const newBuilder = new DatabaseFragmentDefinitionBuilder(
538
+ this.#baseBuilder,
539
+ this.#schema,
540
+ this.#namespace,
541
+ ) as unknown as DatabaseFragmentDefinitionBuilder<
542
+ TSchema,
543
+ TConfig,
544
+ TDeps,
545
+ TBaseServices,
546
+ TServices,
547
+ TServiceDependencies,
548
+ TPrivateServices,
549
+ TNewHooks,
550
+ DatabaseServiceContext<TNewHooks>,
551
+ THandlerThisContext,
552
+ TLinkedFragments
553
+ >;
554
+
555
+ newBuilder.#hooksFactory = hooksFactory;
556
+
557
+ return newBuilder;
316
558
  }
317
559
 
318
560
  /**
@@ -329,12 +571,19 @@ export class DatabaseFragmentDefinitionBuilder<
329
571
  TServices,
330
572
  TServiceDependencies & { [K in TServiceName]: TService },
331
573
  TPrivateServices,
574
+ THooks,
332
575
  TServiceThisContext,
333
- THandlerThisContext
576
+ THandlerThisContext,
577
+ TLinkedFragments
334
578
  > {
335
579
  const newBaseBuilder = this.#baseBuilder.usesService<TServiceName, TService>(serviceName);
336
580
 
337
- return new DatabaseFragmentDefinitionBuilder(newBaseBuilder, this.#schema, this.#namespace);
581
+ return new DatabaseFragmentDefinitionBuilder(
582
+ newBaseBuilder,
583
+ this.#schema,
584
+ this.#namespace,
585
+ this.#hooksFactory,
586
+ );
338
587
  }
339
588
 
340
589
  /**
@@ -351,14 +600,21 @@ export class DatabaseFragmentDefinitionBuilder<
351
600
  TServices,
352
601
  TServiceDependencies & { [K in TServiceName]: TService | undefined },
353
602
  TPrivateServices,
603
+ THooks,
354
604
  TServiceThisContext,
355
- THandlerThisContext
605
+ THandlerThisContext,
606
+ TLinkedFragments
356
607
  > {
357
608
  const newBaseBuilder = this.#baseBuilder.usesOptionalService<TServiceName, TService>(
358
609
  serviceName,
359
610
  );
360
611
 
361
- return new DatabaseFragmentDefinitionBuilder(newBaseBuilder, this.#schema, this.#namespace);
612
+ return new DatabaseFragmentDefinitionBuilder(
613
+ newBaseBuilder,
614
+ this.#schema,
615
+ this.#namespace,
616
+ this.#hooksFactory,
617
+ );
362
618
  }
363
619
 
364
620
  /**
@@ -374,18 +630,38 @@ export class DatabaseFragmentDefinitionBuilder<
374
630
  TServices,
375
631
  TServiceDependencies,
376
632
  TPrivateServices,
377
- DatabaseServiceContext,
378
- DatabaseHandlerContext,
379
- DatabaseRequestStorage
633
+ DatabaseServiceContext<THooks>,
634
+ DatabaseHandlerContext<THooks>,
635
+ DatabaseRequestStorage,
636
+ TLinkedFragments
380
637
  > {
638
+ const baseDef = this.#baseBuilder.build();
639
+
381
640
  // Ensure dependencies callback always exists for database fragments
382
641
  // If no user dependencies were defined, create a minimal one that only adds implicit deps
383
642
  const dependencies = (context: {
384
643
  config: TConfig;
385
644
  options: FragnoPublicConfigWithDatabase;
386
645
  }): TDeps => {
387
- const baseDef = this.#baseBuilder.build();
388
- const userDeps = baseDef.dependencies?.(context);
646
+ // In dry run mode, allow user deps to fail gracefully.
647
+ // This is critical for database fragments because the CLI needs access to the schema
648
+ // even when user dependencies (like API clients) can't be initialized.
649
+ // Without this, if user deps fail, we'd lose the implicit database dependencies
650
+ // (including schema), and the CLI couldn't extract schema information.
651
+ let userDeps;
652
+ try {
653
+ userDeps = baseDef.dependencies?.(context);
654
+ } catch (error) {
655
+ if (process.env["FRAGNO_INIT_DRY_RUN"] === "true") {
656
+ console.warn(
657
+ "Warning: Failed to initialize user dependencies in dry run mode:",
658
+ error instanceof Error ? error.message : String(error),
659
+ );
660
+ userDeps = {};
661
+ } else {
662
+ throw error;
663
+ }
664
+ }
389
665
 
390
666
  const { db } = createDatabaseContext(context.options, this.#schema, this.#namespace);
391
667
 
@@ -423,15 +699,29 @@ export class DatabaseFragmentDefinitionBuilder<
423
699
  },
424
700
  );
425
701
 
426
- // Services get restricted context (no execute methods), handlers get execution context
702
+ // Get the internal fragment factory from linked fragments (added by withDatabase)
703
+ // Cast is safe: withDatabase() guarantees this fragment exists and has the correct type
704
+ const internalFragmentFactory = baseDef.linkedFragments?.["_fragno_internal"] as (context: {
705
+ config: TConfig;
706
+ options: FragnoPublicConfigWithDatabase;
707
+ }) => InternalFragmentInstance;
708
+
427
709
  const builderWithContext = builderWithStorage.withThisContext<
428
- DatabaseServiceContext,
429
- DatabaseHandlerContext
430
- >(({ storage }) => {
431
- // Service context - forSchema method to get restricted typed UOW
710
+ DatabaseServiceContext<THooks>,
711
+ DatabaseHandlerContext<THooks>
712
+ >(({ storage, config, options }) => {
713
+ // Create hooks config if hooks factory is defined
714
+ const hooksConfig = this.#hooksFactory
715
+ ? {
716
+ hooks: this.#hooksFactory({ config, options }),
717
+ namespace: this.#namespace,
718
+ internalFragment: internalFragmentFactory({ config, options }),
719
+ }
720
+ : undefined;
721
+
432
722
  function forSchema<TSchema extends AnySchema>(
433
723
  schema: TSchema,
434
- ): TypedUnitOfWork<TSchema, [], unknown> {
724
+ ): TypedUnitOfWork<TSchema, [], unknown, THooks> {
435
725
  const uow = storage.getStore()?.uow;
436
726
  if (!uow) {
437
727
  throw new Error(
@@ -439,24 +729,44 @@ export class DatabaseFragmentDefinitionBuilder<
439
729
  );
440
730
  }
441
731
 
442
- // Return typed view of restricted UOW
443
- return uow.restrict().forSchema(schema);
732
+ return uow.restrict().forSchema<TSchema, THooks>(schema);
444
733
  }
445
734
 
446
- const serviceContext: DatabaseServiceContext = {
735
+ const serviceTx = async <
736
+ TSchema extends AnySchema,
737
+ TRetrievalResults extends unknown[],
738
+ TMutationResult = void,
739
+ >(
740
+ schema: TSchema,
741
+ callbacks: ServiceTxCallbacks<TSchema, TRetrievalResults, TMutationResult, THooks>,
742
+ ): Promise<AwaitedPromisesInObject<TMutationResult>> => {
743
+ const uow = storage.getStore()?.uow;
744
+ if (!uow) {
745
+ throw new Error(
746
+ "No UnitOfWork in context. Service must be called within a route handler OR using `withUnitOfWork`.",
747
+ );
748
+ }
749
+
750
+ return executeServiceTx(schema, callbacks, uow);
751
+ };
752
+
753
+ const serviceContext: DatabaseServiceContext<THooks> = {
447
754
  uow: forSchema,
755
+ tx: serviceTx,
448
756
  };
449
757
 
450
- // Handler context - only executeRestrictedUnitOfWork
451
758
  async function uow<TResult>(
452
759
  callback: (context: {
453
- forSchema: <S extends AnySchema>(schema: S) => TypedUnitOfWork<S, [], unknown>;
760
+ forSchema: <S extends AnySchema, H extends HooksMap = THooks>(
761
+ schema: S,
762
+ hooks?: H,
763
+ ) => TypedUnitOfWork<S, [], unknown, H>;
454
764
  executeRetrieve: () => Promise<void>;
455
765
  executeMutate: () => Promise<void>;
456
766
  nonce: string;
457
767
  currentAttempt: number;
458
768
  }) => Promise<TResult> | TResult,
459
- options?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
769
+ execOptions?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
460
770
  ): Promise<AwaitedPromisesInObject<TResult>> {
461
771
  const currentStorage = storage.getStore();
462
772
  if (!currentStorage) {
@@ -465,30 +775,131 @@ export class DatabaseFragmentDefinitionBuilder<
465
775
  );
466
776
  }
467
777
 
468
- // Wrap callback to ensure it always returns a Promise
469
778
  const wrappedCallback = async (context: {
470
- forSchema: <S extends AnySchema>(schema: S) => TypedUnitOfWork<S, [], unknown>;
779
+ forSchema: <S extends AnySchema, H extends HooksMap = THooks>(
780
+ schema: S,
781
+ hooks?: H,
782
+ ) => TypedUnitOfWork<S, [], unknown, H>;
471
783
  executeRetrieve: () => Promise<void>;
472
784
  executeMutate: () => Promise<void>;
473
785
  nonce: string;
474
786
  currentAttempt: number;
475
787
  }): Promise<TResult> => {
476
- return await callback(context);
788
+ return callback(context);
477
789
  };
478
790
 
479
- // Use the UOW from storage - reset it before each attempt for retry support
480
- // Cast is safe because IUnitOfWork is actually implemented by UnitOfWork
791
+ const userOnBeforeMutate = execOptions?.onBeforeMutate;
792
+ const userOnSuccess = execOptions?.onSuccess;
793
+
481
794
  return executeRestrictedUnitOfWork(wrappedCallback, {
482
- ...options,
795
+ ...execOptions,
483
796
  createUnitOfWork: () => {
484
797
  currentStorage.uow.reset();
798
+ if (hooksConfig) {
799
+ currentStorage.uow.registerSchema(
800
+ hooksConfig.internalFragment.$internal.deps.schema,
801
+ hooksConfig.internalFragment.$internal.deps.namespace,
802
+ );
803
+ }
804
+ // Safe cast: currentStorage.uow is always a UnitOfWork instance
485
805
  return currentStorage.uow as UnitOfWork;
486
806
  },
807
+ onBeforeMutate: (uow) => {
808
+ if (hooksConfig) {
809
+ prepareHookMutations(uow, hooksConfig);
810
+ }
811
+ if (userOnBeforeMutate) {
812
+ userOnBeforeMutate(uow);
813
+ }
814
+ },
815
+ onSuccess: async (uow) => {
816
+ if (hooksConfig) {
817
+ await processHooks(hooksConfig);
818
+ }
819
+ if (userOnSuccess) {
820
+ await userOnSuccess(uow);
821
+ }
822
+ },
487
823
  });
488
824
  }
489
825
 
490
- const handlerContext: DatabaseHandlerContext = {
826
+ // Handler tx method with two overloads
827
+ function handlerTx<T extends readonly unknown[]>(
828
+ servicesFactory: () => readonly [...{ [K in keyof T]: Promise<T[K]> }],
829
+ execOptions?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
830
+ ): Promise<{ [K in keyof T]: T[K] }>;
831
+ function handlerTx<TRetrieveResult, TMutationResult>(
832
+ callbacks: HandlerTxCallbacks<TRetrieveResult, TMutationResult, THooks>,
833
+ execOptions?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
834
+ ): Promise<AwaitedPromisesInObject<TMutationResult>>;
835
+ async function handlerTx<T extends readonly unknown[], TRetrieveResult, TMutationResult>(
836
+ factoryOrCallbacks:
837
+ | (() => readonly [...{ [K in keyof T]: Promise<T[K]> }])
838
+ | HandlerTxCallbacks<TRetrieveResult, TMutationResult, THooks>,
839
+ execOptions?: Omit<ExecuteRestrictedUnitOfWorkOptions, "createUnitOfWork">,
840
+ ): Promise<{ [K in keyof T]: T[K] } | AwaitedPromisesInObject<TMutationResult>> {
841
+ const currentStorage = storage.getStore();
842
+ if (!currentStorage) {
843
+ throw new Error(
844
+ "No storage in context. Handler must be called within a request context.",
845
+ );
846
+ }
847
+
848
+ const userOnBeforeMutate = execOptions?.onBeforeMutate;
849
+ const userOnSuccess = execOptions?.onSuccess;
850
+
851
+ const createUow = () => {
852
+ currentStorage.uow.reset();
853
+
854
+ if (hooksConfig) {
855
+ currentStorage.uow.registerSchema(
856
+ hooksConfig.internalFragment.$internal.deps.schema,
857
+ hooksConfig.internalFragment.$internal.deps.namespace,
858
+ );
859
+ }
860
+ // Safe cast: currentStorage.uow is always a UnitOfWork instance
861
+ return currentStorage.uow as UnitOfWork;
862
+ };
863
+
864
+ const options: ExecuteRestrictedUnitOfWorkOptions = {
865
+ ...execOptions,
866
+ createUnitOfWork: createUow,
867
+ onBeforeMutate: (uow) => {
868
+ if (hooksConfig) {
869
+ prepareHookMutations(uow, hooksConfig);
870
+ }
871
+ if (userOnBeforeMutate) {
872
+ userOnBeforeMutate(uow);
873
+ }
874
+ },
875
+ onSuccess: async (uow) => {
876
+ if (hooksConfig) {
877
+ await processHooks(hooksConfig);
878
+ }
879
+ if (userOnSuccess) {
880
+ await userOnSuccess(uow);
881
+ }
882
+ },
883
+ };
884
+
885
+ // Check if it's a function (array syntax factory) or callbacks object (callback syntax)
886
+ if (typeof factoryOrCallbacks === "function") {
887
+ // Array syntax - factoryOrCallbacks is a factory function
888
+ return executeTxArray(
889
+ factoryOrCallbacks as () => readonly [...{ [K in keyof T]: Promise<T[K]> }],
890
+ options,
891
+ ) as Promise<{ [K in keyof T]: T[K] }>;
892
+ } else {
893
+ return executeTxCallbacks(
894
+ factoryOrCallbacks as HandlerTxCallbacks<TRetrieveResult, TMutationResult, THooks>,
895
+ options,
896
+ );
897
+ }
898
+ }
899
+
900
+ const handlerContext: DatabaseHandlerContext<THooks> = {
491
901
  uow,
902
+ tx: handlerTx,
492
903
  };
493
904
 
494
905
  return { serviceContext, handlerContext };