@fragno-dev/db 0.3.0 → 0.4.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 (516) hide show
  1. package/.turbo/turbo-build.log +327 -160
  2. package/CHANGELOG.md +74 -0
  3. package/README.md +24 -0
  4. package/dist/adapters/adapters.d.ts +1 -1
  5. package/dist/adapters/adapters.d.ts.map +1 -1
  6. package/dist/adapters/adapters.js.map +1 -1
  7. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
  8. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
  9. package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
  10. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
  11. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
  12. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
  13. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
  14. package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
  15. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
  16. package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
  17. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
  18. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
  19. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
  20. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
  21. package/dist/adapters/generic-sql/migration/executor.js +30 -3
  22. package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
  23. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
  24. package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
  25. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
  26. package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
  27. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
  28. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
  29. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
  30. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
  31. package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
  32. package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
  33. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
  34. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
  35. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
  36. package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
  37. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
  38. package/dist/adapters/generic-sql/query/where-builder.js +24 -20
  39. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
  40. package/dist/adapters/generic-sql/uow-decoder.js +1 -1
  41. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
  42. package/dist/adapters/generic-sql/uow-encoder.js +8 -9
  43. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
  44. package/dist/adapters/in-memory/condition-evaluator.js +10 -6
  45. package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
  46. package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
  47. package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
  48. package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
  49. package/dist/adapters/in-memory/in-memory-uow.js +236 -13
  50. package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
  51. package/dist/adapters/in-memory/options.d.ts +2 -0
  52. package/dist/adapters/in-memory/options.d.ts.map +1 -1
  53. package/dist/adapters/in-memory/options.js +3 -2
  54. package/dist/adapters/in-memory/options.js.map +1 -1
  55. package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
  56. package/dist/adapters/in-memory/store.js +1 -1
  57. package/dist/adapters/in-memory/store.js.map +1 -1
  58. package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
  59. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
  60. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
  61. package/dist/browser/adapters/adapters.d.ts +61 -0
  62. package/dist/browser/adapters/adapters.d.ts.map +1 -0
  63. package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
  64. package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  65. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  66. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  67. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
  68. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  69. package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
  70. package/dist/browser/adapters/in-memory/index.d.ts +2 -0
  71. package/dist/browser/adapters/in-memory/options.d.ts +1 -0
  72. package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
  73. package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
  74. package/dist/browser/durable-hooks.d.ts +3 -0
  75. package/dist/browser/fragments/internal-fragment.d.ts +317 -0
  76. package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
  77. package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
  78. package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
  79. package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
  80. package/dist/browser/hooks/hooks.d.ts +146 -0
  81. package/dist/browser/hooks/hooks.d.ts.map +1 -0
  82. package/dist/browser/id.js +1 -0
  83. package/dist/browser/internal/adapter-registry.d.ts +4 -0
  84. package/dist/browser/internal/outbox-state.d.ts +2 -0
  85. package/dist/browser/mod.d.ts +15 -0
  86. package/dist/browser/mod.d.ts.map +1 -0
  87. package/dist/browser/mod.js +17 -0
  88. package/dist/browser/mod.js.map +1 -0
  89. package/dist/browser/mod2.d.ts +48 -0
  90. package/dist/browser/mod2.d.ts.map +1 -0
  91. package/dist/browser/naming/sql-naming.d.ts +19 -0
  92. package/dist/browser/naming/sql-naming.d.ts.map +1 -0
  93. package/dist/browser/outbox/outbox.d.ts +21 -0
  94. package/dist/browser/outbox/outbox.d.ts.map +1 -0
  95. package/dist/browser/query/column-defaults.js +1 -0
  96. package/dist/browser/query/condition-builder.d.ts +44 -0
  97. package/dist/browser/query/condition-builder.d.ts.map +1 -0
  98. package/dist/browser/query/condition-builder.js +97 -0
  99. package/dist/browser/query/condition-builder.js.map +1 -0
  100. package/dist/browser/query/cursor.d.ts +105 -0
  101. package/dist/browser/query/cursor.d.ts.map +1 -0
  102. package/dist/browser/query/cursor.js +150 -0
  103. package/dist/browser/query/cursor.js.map +1 -0
  104. package/dist/browser/query/db-now.d.ts +22 -0
  105. package/dist/browser/query/db-now.d.ts.map +1 -0
  106. package/dist/browser/query/db-now.js +33 -0
  107. package/dist/browser/query/db-now.js.map +1 -0
  108. package/dist/browser/query/orm/orm.d.ts +18 -0
  109. package/dist/browser/query/orm/orm.d.ts.map +1 -0
  110. package/dist/browser/query/simple-query-interface.d.ts +108 -0
  111. package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
  112. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
  113. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  114. package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
  115. package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  116. package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
  117. package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
  118. package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
  119. package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
  120. package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
  121. package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  122. package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
  123. package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
  124. package/dist/browser/query/value-encoding.js +38 -0
  125. package/dist/browser/query/value-encoding.js.map +1 -0
  126. package/dist/browser/schema/create.d.ts +326 -0
  127. package/dist/browser/schema/create.d.ts.map +1 -0
  128. package/dist/browser/schema/create.js +89 -0
  129. package/dist/browser/schema/create.js.map +1 -0
  130. package/dist/browser/schema/generate-id.js +28 -0
  131. package/dist/browser/schema/generate-id.js.map +1 -0
  132. package/dist/browser/shared/providers.d.ts +6 -0
  133. package/dist/browser/shared/providers.d.ts.map +1 -0
  134. package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
  135. package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
  136. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  137. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  138. package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
  139. package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  140. package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
  141. package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
  142. package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
  143. package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  144. package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
  145. package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  146. package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
  147. package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
  148. package/dist/browser/sync/commands.d.ts +15 -0
  149. package/dist/browser/sync/commands.d.ts.map +1 -0
  150. package/dist/browser/sync/commands.js +27 -0
  151. package/dist/browser/sync/commands.js.map +1 -0
  152. package/dist/browser/sync/types.d.ts +63 -0
  153. package/dist/browser/sync/types.d.ts.map +1 -0
  154. package/dist/browser/util/types.d.ts +8 -0
  155. package/dist/browser/util/types.d.ts.map +1 -0
  156. package/dist/browser/with-database.d.ts +29 -0
  157. package/dist/browser/with-database.d.ts.map +1 -0
  158. package/dist/client.d.ts +4 -0
  159. package/dist/client.js +5 -0
  160. package/dist/db-fragment-definition-builder.d.ts +85 -28
  161. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  162. package/dist/db-fragment-definition-builder.js +374 -46
  163. package/dist/db-fragment-definition-builder.js.map +1 -1
  164. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
  165. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
  166. package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
  167. package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
  168. package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
  169. package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
  170. package/dist/dispatchers/cloudflare-do/index.js +23 -55
  171. package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
  172. package/dist/dispatchers/node/dispatcher.d.ts +14 -0
  173. package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
  174. package/dist/dispatchers/node/dispatcher.js +80 -0
  175. package/dist/dispatchers/node/dispatcher.js.map +1 -0
  176. package/dist/dispatchers/node/index.d.ts +5 -10
  177. package/dist/dispatchers/node/index.d.ts.map +1 -1
  178. package/dist/dispatchers/node/index.js +21 -53
  179. package/dist/dispatchers/node/index.js.map +1 -1
  180. package/dist/durable-hooks.d.ts +31 -0
  181. package/dist/durable-hooks.d.ts.map +1 -0
  182. package/dist/durable-hooks.js +23 -0
  183. package/dist/durable-hooks.js.map +1 -0
  184. package/dist/fragments/internal-fragment.d.ts +128 -27
  185. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  186. package/dist/fragments/internal-fragment.js +125 -78
  187. package/dist/fragments/internal-fragment.js.map +1 -1
  188. package/dist/fragments/internal-fragment.routes.js +138 -3
  189. package/dist/fragments/internal-fragment.routes.js.map +1 -1
  190. package/dist/fragments/internal-fragment.schema.d.ts +7 -1
  191. package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
  192. package/dist/fragments/internal-fragment.schema.js +18 -1
  193. package/dist/fragments/internal-fragment.schema.js.map +1 -1
  194. package/dist/hooks/durable-hooks-logger.d.ts +10 -0
  195. package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
  196. package/dist/hooks/durable-hooks-logger.js +75 -0
  197. package/dist/hooks/durable-hooks-logger.js.map +1 -0
  198. package/dist/hooks/durable-hooks-processor.d.ts +1 -14
  199. package/dist/hooks/durable-hooks-processor.js +58 -10
  200. package/dist/hooks/durable-hooks-processor.js.map +1 -1
  201. package/dist/hooks/durable-hooks-runtime.js +44 -0
  202. package/dist/hooks/durable-hooks-runtime.js.map +1 -0
  203. package/dist/hooks/hooks.d.ts +60 -2
  204. package/dist/hooks/hooks.d.ts.map +1 -1
  205. package/dist/hooks/hooks.js +214 -53
  206. package/dist/hooks/hooks.js.map +1 -1
  207. package/dist/id.d.ts +2 -2
  208. package/dist/id.js +2 -2
  209. package/dist/internal/adapter-registry.d.ts +11 -0
  210. package/dist/internal/adapter-registry.d.ts.map +1 -0
  211. package/dist/internal/adapter-registry.js +135 -0
  212. package/dist/internal/adapter-registry.js.map +1 -0
  213. package/dist/internal/outbox-state.d.ts +2 -0
  214. package/dist/internal/outbox-state.js +26 -0
  215. package/dist/internal/outbox-state.js.map +1 -0
  216. package/dist/migration-engine/auto-from-schema.d.ts +33 -0
  217. package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
  218. package/dist/migration-engine/auto-from-schema.js +210 -27
  219. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  220. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  221. package/dist/migration-engine/generation-engine.js +17 -5
  222. package/dist/migration-engine/generation-engine.js.map +1 -1
  223. package/dist/migration-engine/shared.d.ts +113 -0
  224. package/dist/migration-engine/shared.d.ts.map +1 -0
  225. package/dist/migration-engine/shared.js.map +1 -1
  226. package/dist/mod.d.ts +12 -11
  227. package/dist/mod.d.ts.map +1 -1
  228. package/dist/mod.js +10 -10
  229. package/dist/mod.js.map +1 -1
  230. package/dist/naming/sql-naming.d.ts.map +1 -1
  231. package/dist/naming/sql-naming.js.map +1 -1
  232. package/dist/outbox/outbox-builder.js.map +1 -1
  233. package/dist/outbox/outbox.d.ts +3 -1
  234. package/dist/outbox/outbox.d.ts.map +1 -1
  235. package/dist/outbox/outbox.js.map +1 -1
  236. package/dist/query/column-defaults.js.map +1 -1
  237. package/dist/query/condition-builder.d.ts +7 -1
  238. package/dist/query/condition-builder.d.ts.map +1 -1
  239. package/dist/query/condition-builder.js +5 -1
  240. package/dist/query/condition-builder.js.map +1 -1
  241. package/dist/query/cursor-client.d.ts +105 -0
  242. package/dist/query/cursor-client.d.ts.map +1 -0
  243. package/dist/query/cursor-client.js +165 -0
  244. package/dist/query/cursor-client.js.map +1 -0
  245. package/dist/query/cursor.d.ts.map +1 -1
  246. package/dist/query/cursor.js +7 -1
  247. package/dist/query/cursor.js.map +1 -1
  248. package/dist/query/db-now.d.ts +15 -1
  249. package/dist/query/db-now.d.ts.map +1 -1
  250. package/dist/query/db-now.js +30 -2
  251. package/dist/query/db-now.js.map +1 -1
  252. package/dist/query/orm/orm.js.map +1 -1
  253. package/dist/query/serialize/create-sql-serializer.js +2 -2
  254. package/dist/query/serialize/create-sql-serializer.js.map +1 -1
  255. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
  256. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
  257. package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
  258. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
  259. package/dist/query/simple-query-interface.d.ts +7 -3
  260. package/dist/query/simple-query-interface.d.ts.map +1 -1
  261. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
  262. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  263. package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
  264. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  265. package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
  266. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  267. package/dist/query/unit-of-work/unit-of-work.js +50 -6
  268. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  269. package/dist/query/value-decoding.js +8 -1
  270. package/dist/query/value-decoding.js.map +1 -1
  271. package/dist/query/value-encoding.js.map +1 -1
  272. package/dist/schema/create.d.ts +69 -25
  273. package/dist/schema/create.d.ts.map +1 -1
  274. package/dist/schema/create.js +91 -16
  275. package/dist/schema/create.js.map +1 -1
  276. package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
  277. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  278. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  279. package/dist/schema/validator.d.ts.map +1 -1
  280. package/dist/schema/validator.js.map +1 -1
  281. package/dist/schema-output/drizzle.d.ts.map +1 -1
  282. package/dist/schema-output/drizzle.js +8 -6
  283. package/dist/schema-output/drizzle.js.map +1 -1
  284. package/dist/schema-output/prisma.d.ts.map +1 -1
  285. package/dist/schema-output/prisma.js +21 -10
  286. package/dist/schema-output/prisma.js.map +1 -1
  287. package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
  288. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
  289. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
  290. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
  291. package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
  292. package/dist/sql-driver/sql.js.map +1 -1
  293. package/dist/sync/commands.d.ts +15 -0
  294. package/dist/sync/commands.d.ts.map +1 -0
  295. package/dist/sync/commands.js +27 -0
  296. package/dist/sync/commands.js.map +1 -0
  297. package/dist/sync/index.d.ts +4 -0
  298. package/dist/sync/index.js +4 -0
  299. package/dist/sync/read-tracking.d.ts +25 -0
  300. package/dist/sync/read-tracking.d.ts.map +1 -0
  301. package/dist/sync/read-tracking.js +148 -0
  302. package/dist/sync/read-tracking.js.map +1 -0
  303. package/dist/sync/submit.js +213 -0
  304. package/dist/sync/submit.js.map +1 -0
  305. package/dist/sync/types.d.ts +63 -0
  306. package/dist/sync/types.d.ts.map +1 -0
  307. package/dist/util/default-database-adapter.js +6 -1
  308. package/dist/util/default-database-adapter.js.map +1 -1
  309. package/dist/with-database.d.ts +3 -6
  310. package/dist/with-database.d.ts.map +1 -1
  311. package/dist/with-database.js +7 -15
  312. package/dist/with-database.js.map +1 -1
  313. package/package.json +33 -41
  314. package/src/adapters/adapters.ts +5 -4
  315. package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
  316. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
  317. package/src/adapters/drizzle/test-utils.ts +2 -1
  318. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
  319. package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
  320. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
  321. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
  322. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
  323. package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
  324. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
  325. package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
  326. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
  327. package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
  328. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
  329. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
  330. package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
  331. package/src/adapters/generic-sql/migration/executor.ts +47 -4
  332. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
  333. package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
  334. package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
  335. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
  336. package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
  337. package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
  338. package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
  339. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
  340. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
  341. package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
  342. package/src/adapters/generic-sql/query/select-builder.ts +2 -2
  343. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
  344. package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
  345. package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
  346. package/src/adapters/generic-sql/query/where-builder.ts +48 -29
  347. package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
  348. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
  349. package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
  350. package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
  351. package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
  352. package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
  353. package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
  354. package/src/adapters/generic-sql/uow-decoder.ts +3 -3
  355. package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
  356. package/src/adapters/generic-sql/uow-encoder.ts +14 -18
  357. package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
  358. package/src/adapters/in-memory/condition-evaluator.ts +9 -4
  359. package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
  360. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
  361. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
  362. package/src/adapters/in-memory/in-memory-uow.ts +402 -26
  363. package/src/adapters/in-memory/options.test.ts +1 -0
  364. package/src/adapters/in-memory/options.ts +5 -1
  365. package/src/adapters/in-memory/outbox.test.ts +361 -0
  366. package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
  367. package/src/adapters/in-memory/reference-resolution.ts +2 -2
  368. package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
  369. package/src/adapters/in-memory/store.test.ts +1 -0
  370. package/src/adapters/in-memory/store.ts +3 -3
  371. package/src/adapters/in-memory/value-normalization.test.ts +1 -0
  372. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
  373. package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
  374. package/src/adapters/shared/uow-operation-compiler.ts +3 -3
  375. package/src/browser/mod.ts +64 -0
  376. package/src/client.ts +19 -0
  377. package/src/db-fragment-definition-builder.test.ts +821 -47
  378. package/src/db-fragment-definition-builder.ts +857 -110
  379. package/src/db-fragment-instantiator.test.ts +114 -90
  380. package/src/db-fragment-integration.test.ts +9 -6
  381. package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
  382. package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
  383. package/src/dispatchers/cloudflare-do/index.ts +49 -90
  384. package/src/dispatchers/node/dispatcher.ts +112 -0
  385. package/src/dispatchers/node/index.test.ts +43 -14
  386. package/src/dispatchers/node/index.ts +38 -75
  387. package/src/durable-hooks.test.ts +80 -0
  388. package/src/durable-hooks.ts +67 -0
  389. package/src/fragments/internal-fragment.routes.test.ts +570 -0
  390. package/src/fragments/internal-fragment.routes.ts +297 -5
  391. package/src/fragments/internal-fragment.schema.ts +45 -1
  392. package/src/fragments/internal-fragment.test.ts +223 -251
  393. package/src/fragments/internal-fragment.ts +278 -154
  394. package/src/hooks/durable-hooks-logger.ts +126 -0
  395. package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
  396. package/src/hooks/durable-hooks-processor.test.ts +179 -14
  397. package/src/hooks/durable-hooks-processor.ts +120 -14
  398. package/src/hooks/durable-hooks-runtime.test.ts +65 -0
  399. package/src/hooks/durable-hooks-runtime.ts +81 -0
  400. package/src/hooks/hooks.test.ts +314 -53
  401. package/src/hooks/hooks.ts +360 -81
  402. package/src/id.test.ts +34 -0
  403. package/src/id.ts +1 -3
  404. package/src/internal/adapter-registry.test.ts +93 -0
  405. package/src/internal/adapter-registry.ts +239 -0
  406. package/src/internal/outbox-state.ts +43 -0
  407. package/src/migration-engine/auto-from-schema.test.ts +93 -0
  408. package/src/migration-engine/auto-from-schema.ts +360 -42
  409. package/src/migration-engine/create.test.ts +2 -1
  410. package/src/migration-engine/create.ts +1 -1
  411. package/src/migration-engine/generation-engine.test.ts +66 -9
  412. package/src/migration-engine/generation-engine.ts +31 -10
  413. package/src/migration-engine/shared.ts +13 -0
  414. package/src/mod.ts +45 -27
  415. package/src/naming/sql-naming.ts +1 -0
  416. package/src/outbox/outbox-builder.ts +2 -2
  417. package/src/outbox/outbox.test.ts +216 -45
  418. package/src/outbox/outbox.ts +3 -1
  419. package/src/query/column-defaults.ts +1 -1
  420. package/src/query/condition-builder.test.ts +15 -0
  421. package/src/query/condition-builder.ts +7 -0
  422. package/src/query/cursor-client.test.ts +70 -0
  423. package/src/query/cursor-client.ts +263 -0
  424. package/src/query/cursor.test.ts +3 -2
  425. package/src/query/cursor.ts +15 -3
  426. package/src/query/db-now.ts +69 -2
  427. package/src/query/orm/orm.ts +2 -2
  428. package/src/query/query-type.test.ts +2 -1
  429. package/src/query/serialize/create-sql-serializer.ts +3 -3
  430. package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
  431. package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
  432. package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
  433. package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
  434. package/src/query/simple-query-interface.ts +10 -4
  435. package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
  436. package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
  437. package/src/query/unit-of-work/retry-policy.test.ts +1 -0
  438. package/src/query/unit-of-work/tx-builder.test.ts +73 -1
  439. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
  440. package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
  441. package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
  442. package/src/query/unit-of-work/unit-of-work.ts +105 -19
  443. package/src/query/value-decoding.test.ts +50 -2
  444. package/src/query/value-decoding.ts +17 -4
  445. package/src/query/value-encoding.test.ts +1 -0
  446. package/src/query/value-encoding.ts +1 -1
  447. package/src/schema/create.test.ts +164 -5
  448. package/src/schema/create.ts +222 -24
  449. package/src/schema/generate-id.test.ts +1 -0
  450. package/src/schema/serialize.test.ts +4 -3
  451. package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
  452. package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
  453. package/src/schema/type-conversion/type-mapping.test.ts +2 -1
  454. package/src/schema/validator.test.ts +4 -2
  455. package/src/schema/validator.ts +1 -0
  456. package/src/schema-output/drizzle.test.ts +72 -19
  457. package/src/schema-output/drizzle.ts +24 -18
  458. package/src/schema-output/prisma.test.ts +172 -14
  459. package/src/schema-output/prisma.ts +34 -14
  460. package/src/sql-driver/better-sqlite3.test.ts +5 -3
  461. package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
  462. package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
  463. package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
  464. package/src/sql-driver/query-executor/query-executor.ts +1 -1
  465. package/src/sql-driver/sql-driver-adapter.ts +2 -2
  466. package/src/sql-driver/sql.ts +2 -1
  467. package/src/sql-driver/sqlocal.test.ts +4 -2
  468. package/src/sync/commands.test.ts +39 -0
  469. package/src/sync/commands.ts +51 -0
  470. package/src/sync/conflict-checker.test.ts +450 -0
  471. package/src/sync/conflict-checker.ts +248 -0
  472. package/src/sync/index.ts +14 -0
  473. package/src/sync/plan.ts +9 -0
  474. package/src/sync/read-tracking.test.ts +177 -0
  475. package/src/sync/read-tracking.ts +287 -0
  476. package/src/sync/submit.test.ts +205 -0
  477. package/src/sync/submit.ts +328 -0
  478. package/src/sync/types.ts +80 -0
  479. package/src/util/default-database-adapter.ts +15 -2
  480. package/src/with-database.ts +20 -50
  481. package/tsconfig.json +1 -1
  482. package/tsdown.config.ts +38 -26
  483. package/vitest.config.ts +1 -0
  484. package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
  485. package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
  486. package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
  487. package/dist/packages/fragno/dist/api/bind-services.js +0 -20
  488. package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
  489. package/dist/packages/fragno/dist/api/error.js +0 -48
  490. package/dist/packages/fragno/dist/api/error.js.map +0 -1
  491. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
  492. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
  493. package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
  494. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
  495. package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
  496. package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
  497. package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
  498. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
  499. package/dist/packages/fragno/dist/api/internal/route.js +0 -10
  500. package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
  501. package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
  502. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
  503. package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
  504. package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
  505. package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
  506. package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
  507. package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
  508. package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
  509. package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
  510. package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
  511. package/dist/packages/fragno/dist/api/route.js +0 -30
  512. package/dist/packages/fragno/dist/api/route.js.map +0 -1
  513. package/dist/packages/fragno/dist/internal/symbols.js +0 -10
  514. package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
  515. package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
  516. package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
@@ -1,24 +1,30 @@
1
- import { describe, it, expect, expectTypeOf } from "vitest";
1
+ import { describe, it, expect, expectTypeOf, vi } from "vitest";
2
+
3
+ import { InMemoryAdapter } from "../../adapters/in-memory/in-memory-adapter";
4
+ import type { InternalFragmentInstance } from "../../fragments/internal-fragment";
5
+ import { internalSchema } from "../../fragments/internal-fragment.schema";
6
+ import { prepareHookMutations } from "../../hooks/hooks";
2
7
  import { schema, idColumn, FragnoId } from "../../schema/create";
3
- import {
4
- createUnitOfWork,
5
- type IUnitOfWork,
6
- type UOWCompiler,
7
- type UOWDecoder,
8
- type UOWExecutor,
9
- } from "./unit-of-work";
10
8
  import {
11
9
  createServiceTxBuilder,
12
10
  createHandlerTxBuilder,
13
11
  isTxResult,
12
+ serviceCalls,
14
13
  ConcurrencyConflictError,
15
14
  } from "./execute-unit-of-work";
15
+ import type { AwaitedPromisesInObject, TxResult } from "./execute-unit-of-work";
16
16
  import {
17
17
  ExponentialBackoffRetryPolicy,
18
18
  LinearBackoffRetryPolicy,
19
19
  NoRetryPolicy,
20
20
  } from "./retry-policy";
21
- import type { AwaitedPromisesInObject, TxResult } from "./execute-unit-of-work";
21
+ import {
22
+ createUnitOfWork,
23
+ type IUnitOfWork,
24
+ type UOWCompiler,
25
+ type UOWDecoder,
26
+ type UOWExecutor,
27
+ } from "./unit-of-work";
22
28
 
23
29
  const testSchema = schema("test", (s) =>
24
30
  s.addTable("users", (t) =>
@@ -31,6 +37,42 @@ const testSchema = schema("test", (s) =>
31
37
  ),
32
38
  );
33
39
 
40
+ function setupCrossSchemaHookTest() {
41
+ const adapter = new InMemoryAdapter();
42
+ const schemaA = schema("alpha", (s) =>
43
+ s.addTable("items", (t) => t.addColumn("id", idColumn()).addColumn("label", "string")),
44
+ );
45
+ const schemaB = schema("beta", (s) =>
46
+ s.addTable("items", (t) => t.addColumn("id", idColumn()).addColumn("label", "string")),
47
+ );
48
+ const namespaceA = "alpha";
49
+ const namespaceB = "beta";
50
+
51
+ const queryA = adapter.createQueryEngine(schemaA, namespaceA);
52
+ const queryB = adapter.createQueryEngine(schemaB, namespaceB);
53
+ const internalQuery = adapter.createQueryEngine(internalSchema, null);
54
+
55
+ const internalFragment = {
56
+ $internal: {
57
+ deps: {
58
+ schema: internalSchema,
59
+ namespace: null,
60
+ },
61
+ },
62
+ } as InternalFragmentInstance;
63
+
64
+ return {
65
+ schemaA,
66
+ schemaB,
67
+ namespaceA,
68
+ namespaceB,
69
+ queryA,
70
+ queryB,
71
+ internalQuery,
72
+ internalFragment,
73
+ };
74
+ }
75
+
34
76
  describe("AwaitedPromisesInObject type tests", () => {
35
77
  it("should unwrap promises in objects", () => {
36
78
  type Input = { a: Promise<string>; b: number };
@@ -390,6 +432,36 @@ describe("Unified Tx API", () => {
390
432
  expect(users[1].name).toBe("Bob");
391
433
  });
392
434
 
435
+ it("should call onAfterRetrieve with full results", async () => {
436
+ const compiler = createMockCompiler();
437
+ const mockUsers = [
438
+ {
439
+ id: FragnoId.fromExternal("1", 1),
440
+ email: "alice@example.com",
441
+ name: "Alice",
442
+ balance: 100,
443
+ },
444
+ ];
445
+ const executor: UOWExecutor<unknown, unknown> = {
446
+ executeRetrievalPhase: async () => [mockUsers],
447
+ executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
448
+ };
449
+ const decoder = createMockDecoder();
450
+ const onAfterRetrieve = vi.fn();
451
+
452
+ await createHandlerTxBuilder({
453
+ createUnitOfWork: () => createUnitOfWork(compiler, executor, decoder),
454
+ onAfterRetrieve,
455
+ })
456
+ .retrieve(({ forSchema }) =>
457
+ forSchema(testSchema).find("users", (b) => b.whereIndex("idx_email")),
458
+ )
459
+ .execute();
460
+
461
+ expect(onAfterRetrieve).toHaveBeenCalledOnce();
462
+ expect(onAfterRetrieve.mock.calls[0]?.[1]).toEqual([mockUsers]);
463
+ });
464
+
393
465
  it("should execute a simple mutate-only transaction", async () => {
394
466
  const compiler = createMockCompiler();
395
467
  const executor: UOWExecutor<unknown, unknown> = {
@@ -773,6 +845,72 @@ describe("Unified Tx API", () => {
773
845
  expect(result.finalDepUserId).toBeInstanceOf(FragnoId);
774
846
  });
775
847
 
848
+ it("should preserve tuple inference for dynamic serviceCalls using helper", async () => {
849
+ const compiler = createMockCompiler();
850
+ const executor: UOWExecutor<unknown, unknown> = {
851
+ executeRetrievalPhase: async () => [],
852
+ executeMutationPhase: async () => ({ success: true, createdInternalIds: [BigInt(1)] }),
853
+ };
854
+ const decoder = createMockDecoder();
855
+
856
+ let currentUow: IUnitOfWork | null = null;
857
+
858
+ type UserData = { id: FragnoId; email: string };
859
+ type Stats = { count: number };
860
+ type HistoryEntry = { page: number };
861
+
862
+ const getUser = () =>
863
+ createServiceTxBuilder(testSchema, currentUow!)
864
+ .mutate(({ uow }): UserData => {
865
+ const userId = uow.create("users", {
866
+ email: "user@example.com",
867
+ name: "User",
868
+ balance: 0,
869
+ });
870
+ return { id: userId, email: "user@example.com" };
871
+ })
872
+ .build();
873
+
874
+ const getStats = () =>
875
+ createServiceTxBuilder(testSchema, currentUow!)
876
+ .mutate((): Stats => ({ count: 42 }))
877
+ .build();
878
+
879
+ const listHistory = (page: number) =>
880
+ createServiceTxBuilder(testSchema, currentUow!)
881
+ .mutate((): HistoryEntry => ({ page }))
882
+ .build();
883
+
884
+ const result = await createHandlerTxBuilder({
885
+ createUnitOfWork: () => {
886
+ currentUow = createUnitOfWork(compiler, executor, decoder);
887
+ return currentUow;
888
+ },
889
+ })
890
+ .withServiceCalls(() => {
891
+ const historyCalls = Array.from({ length: 2 }, (_, idx) => listHistory(idx + 1));
892
+ return serviceCalls(getUser(), getStats(), ...historyCalls);
893
+ })
894
+ .mutate(({ serviceIntermediateResult }) => {
895
+ const [user, stats, ...history] = serviceIntermediateResult;
896
+
897
+ expectTypeOf(user).toEqualTypeOf<UserData>();
898
+ expectTypeOf(stats).toEqualTypeOf<Stats>();
899
+ expectTypeOf(history).items.toEqualTypeOf<HistoryEntry>();
900
+
901
+ return {
902
+ userId: user.id,
903
+ count: stats.count,
904
+ pages: history.map((entry) => entry.page),
905
+ };
906
+ })
907
+ .execute();
908
+
909
+ expect(result.count).toBe(42);
910
+ expect(result.pages).toEqual([1, 2]);
911
+ expect(result.userId).toBeInstanceOf(FragnoId);
912
+ });
913
+
776
914
  it("should retry on concurrency conflict", async () => {
777
915
  const compiler = createMockCompiler();
778
916
  let mutationAttempts = 0;
@@ -911,6 +1049,88 @@ describe("Unified Tx API", () => {
911
1049
  ).rejects.toThrow("Transaction execution aborted");
912
1050
  });
913
1051
 
1052
+ it("should propagate error when mutate callback rejects a duplicate via retrieve guard", async () => {
1053
+ const compiler = createMockCompiler();
1054
+ const existingUser = {
1055
+ id: FragnoId.fromExternal("existing-1", 1),
1056
+ email: "alice@example.com",
1057
+ name: "Alice",
1058
+ balance: 100,
1059
+ };
1060
+ const executor: UOWExecutor<unknown, unknown> = {
1061
+ executeRetrievalPhase: async () => [[existingUser]],
1062
+ executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
1063
+ };
1064
+ const decoder = createMockDecoder();
1065
+
1066
+ let currentUow: IUnitOfWork | null = null;
1067
+
1068
+ const createIfNotExists = (email: string) => {
1069
+ return createServiceTxBuilder(testSchema, currentUow!)
1070
+ .retrieve((uow) =>
1071
+ uow.find("users", (b) => b.whereIndex("idx_email", (eb) => eb("email", "=", email))),
1072
+ )
1073
+ .mutate(({ uow, retrieveResult: [users] }) => {
1074
+ if (users.length > 0) {
1075
+ throw new Error("ALREADY_EXISTS");
1076
+ }
1077
+ const id = uow.create("users", { email, name: "New", balance: 0 });
1078
+ return { id };
1079
+ })
1080
+ .build();
1081
+ };
1082
+
1083
+ await expect(
1084
+ createHandlerTxBuilder({
1085
+ createUnitOfWork: () => {
1086
+ currentUow = createUnitOfWork(compiler, executor, decoder);
1087
+ return currentUow;
1088
+ },
1089
+ })
1090
+ .withServiceCalls(() => [createIfNotExists("alice@example.com")])
1091
+ .transform(({ serviceResult: [result] }) => result)
1092
+ .execute(),
1093
+ ).rejects.toThrow("ALREADY_EXISTS");
1094
+ });
1095
+
1096
+ it("should succeed when retrieve guard finds no existing record", async () => {
1097
+ const compiler = createMockCompiler();
1098
+ const executor: UOWExecutor<unknown, unknown> = {
1099
+ executeRetrievalPhase: async () => [[]],
1100
+ executeMutationPhase: async () => ({ success: true, createdInternalIds: [BigInt(1)] }),
1101
+ };
1102
+ const decoder = createMockDecoder();
1103
+
1104
+ let currentUow: IUnitOfWork | null = null;
1105
+
1106
+ const createIfNotExists = (email: string) => {
1107
+ return createServiceTxBuilder(testSchema, currentUow!)
1108
+ .retrieve((uow) =>
1109
+ uow.find("users", (b) => b.whereIndex("idx_email", (eb) => eb("email", "=", email))),
1110
+ )
1111
+ .mutate(({ uow, retrieveResult: [users] }) => {
1112
+ if (users.length > 0) {
1113
+ throw new Error("ALREADY_EXISTS");
1114
+ }
1115
+ const id = uow.create("users", { email, name: "New", balance: 0 });
1116
+ return { id };
1117
+ })
1118
+ .build();
1119
+ };
1120
+
1121
+ const result = await createHandlerTxBuilder({
1122
+ createUnitOfWork: () => {
1123
+ currentUow = createUnitOfWork(compiler, executor, decoder);
1124
+ return currentUow;
1125
+ },
1126
+ })
1127
+ .withServiceCalls(() => [createIfNotExists("new@example.com")])
1128
+ .transform(({ serviceResult: [result] }) => result)
1129
+ .execute();
1130
+
1131
+ expect(result.id).toBeInstanceOf(FragnoId);
1132
+ });
1133
+
914
1134
  it("should pass serviceResult to transform callback with final results", async () => {
915
1135
  const compiler = createMockCompiler();
916
1136
  const mockUser = {
@@ -1547,6 +1767,7 @@ describe("Unified Tx API", () => {
1547
1767
  await createHandlerTxBuilder({
1548
1768
  createUnitOfWork: () => {
1549
1769
  currentUow = createUnitOfWork(compiler, executor, decoder);
1770
+ currentUow.registerSchema(testSchema, "test");
1550
1771
  return currentUow;
1551
1772
  },
1552
1773
  })
@@ -1616,6 +1837,7 @@ describe("Unified Tx API", () => {
1616
1837
  await createHandlerTxBuilder({
1617
1838
  createUnitOfWork: () => {
1618
1839
  currentUow = createUnitOfWork(compiler, executor, decoder);
1840
+ currentUow.registerSchema(testSchema, "test");
1619
1841
  return currentUow;
1620
1842
  },
1621
1843
  })
@@ -1631,6 +1853,122 @@ describe("Unified Tx API", () => {
1631
1853
  payload: { userId: expect.any(String) },
1632
1854
  });
1633
1855
  });
1856
+
1857
+ it("should create hook records when handler uses schema A and service uses schema B", async () => {
1858
+ const { schemaA, schemaB, namespaceA, namespaceB, queryA, internalQuery, internalFragment } =
1859
+ setupCrossSchemaHookTest();
1860
+
1861
+ type HooksA = {
1862
+ onAlpha: (payload: { value: string }) => void;
1863
+ };
1864
+ type HooksB = {
1865
+ onBeta: (payload: { value: string }) => void;
1866
+ };
1867
+
1868
+ const hooksA: HooksA = {
1869
+ onAlpha: (_payload) => {},
1870
+ };
1871
+ const hooksB: HooksB = {
1872
+ onBeta: (_payload) => {},
1873
+ };
1874
+
1875
+ let currentUow: IUnitOfWork | null = null;
1876
+
1877
+ const serviceInSchemaB = (value: string) =>
1878
+ createServiceTxBuilder(schemaB, currentUow!, hooksB)
1879
+ .mutate(({ uow }) => {
1880
+ uow.create("items", { label: value });
1881
+ uow.triggerHook("onBeta", { value });
1882
+ })
1883
+ .build();
1884
+
1885
+ await createHandlerTxBuilder({
1886
+ createUnitOfWork: () => {
1887
+ currentUow = queryA.createBaseUnitOfWork();
1888
+ currentUow.registerSchema(schemaA, namespaceA);
1889
+ currentUow.registerSchema(schemaB, namespaceB);
1890
+ currentUow.registerSchema(internalSchema, null);
1891
+ return currentUow;
1892
+ },
1893
+ onBeforeMutate: (uow) => {
1894
+ prepareHookMutations(uow, internalFragment);
1895
+ },
1896
+ })
1897
+ .mutate(({ forSchema }) => {
1898
+ const uow = forSchema(schemaA, hooksA);
1899
+ uow.create("items", { label: "alpha" });
1900
+ uow.triggerHook("onAlpha", { value: "alpha" });
1901
+ })
1902
+ .withServiceCalls(() => [serviceInSchemaB("beta")] as const)
1903
+ .execute();
1904
+
1905
+ const hooks = await internalQuery.find("fragno_hooks");
1906
+ expect(hooks).toHaveLength(2);
1907
+ expect(hooks).toEqual(
1908
+ expect.arrayContaining([
1909
+ expect.objectContaining({ hookName: "onAlpha", namespace: namespaceA }),
1910
+ expect.objectContaining({ hookName: "onBeta", namespace: namespaceB }),
1911
+ ]),
1912
+ );
1913
+ });
1914
+
1915
+ it("should create hook records when handler uses schema B and service uses schema A", async () => {
1916
+ const { schemaA, schemaB, namespaceA, namespaceB, queryB, internalQuery, internalFragment } =
1917
+ setupCrossSchemaHookTest();
1918
+
1919
+ type HooksA = {
1920
+ onAlpha: (payload: { value: string }) => void;
1921
+ };
1922
+ type HooksB = {
1923
+ onBeta: (payload: { value: string }) => void;
1924
+ };
1925
+
1926
+ const hooksA: HooksA = {
1927
+ onAlpha: (_payload) => {},
1928
+ };
1929
+ const hooksB: HooksB = {
1930
+ onBeta: (_payload) => {},
1931
+ };
1932
+
1933
+ let currentUow: IUnitOfWork | null = null;
1934
+
1935
+ const serviceInSchemaA = (value: string) =>
1936
+ createServiceTxBuilder(schemaA, currentUow!, hooksA)
1937
+ .mutate(({ uow }) => {
1938
+ uow.create("items", { label: value });
1939
+ uow.triggerHook("onAlpha", { value });
1940
+ })
1941
+ .build();
1942
+
1943
+ await createHandlerTxBuilder({
1944
+ createUnitOfWork: () => {
1945
+ currentUow = queryB.createBaseUnitOfWork();
1946
+ currentUow.registerSchema(schemaA, namespaceA);
1947
+ currentUow.registerSchema(schemaB, namespaceB);
1948
+ currentUow.registerSchema(internalSchema, null);
1949
+ return currentUow;
1950
+ },
1951
+ onBeforeMutate: (uow) => {
1952
+ prepareHookMutations(uow, internalFragment);
1953
+ },
1954
+ })
1955
+ .mutate(({ forSchema }) => {
1956
+ const uow = forSchema(schemaB, hooksB);
1957
+ uow.create("items", { label: "beta" });
1958
+ uow.triggerHook("onBeta", { value: "beta" });
1959
+ })
1960
+ .withServiceCalls(() => [serviceInSchemaA("alpha")] as const)
1961
+ .execute();
1962
+
1963
+ const hooks = await internalQuery.find("fragno_hooks");
1964
+ expect(hooks).toHaveLength(2);
1965
+ expect(hooks).toEqual(
1966
+ expect.arrayContaining([
1967
+ expect.objectContaining({ hookName: "onAlpha", namespace: namespaceA }),
1968
+ expect.objectContaining({ hookName: "onBeta", namespace: namespaceB }),
1969
+ ]),
1970
+ );
1971
+ });
1634
1972
  });
1635
1973
 
1636
1974
  describe("error handling in createServiceTx", () => {
@@ -1,7 +1,7 @@
1
- import type { AnySchema } from "../../schema/create";
2
- import type { TypedUnitOfWork, IUnitOfWork } from "./unit-of-work";
3
1
  import type { HooksMap } from "../../hooks/hooks";
2
+ import type { AnySchema } from "../../schema/create";
4
3
  import { ExponentialBackoffRetryPolicy, NoRetryPolicy, type RetryPolicy } from "./retry-policy";
4
+ import type { TypedUnitOfWork, IUnitOfWork } from "./unit-of-work";
5
5
 
6
6
  /**
7
7
  * Symbol to identify TxResult objects
@@ -58,6 +58,26 @@ export type ExtractServiceFinalResults<T extends readonly unknown[]> = {
58
58
  [K in keyof T]: ExtractTxFinalResult<T[K]>;
59
59
  };
60
60
 
61
+ /**
62
+ * Helper to preserve tuple inference for service calls, especially with spreads.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const historyCalls = Array.from({ length: 3 }, () => svc.listHistory(...));
67
+ * return serviceCalls(
68
+ * svc.getInstanceStatus(...),
69
+ * svc.getInstanceRunNumber(...),
70
+ * ...historyCalls,
71
+ * );
72
+ * ```
73
+ */
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ export function serviceCalls<const T extends readonly (TxResult<any, any> | undefined)[]>(
76
+ ...calls: T
77
+ ): T {
78
+ return calls;
79
+ }
80
+
61
81
  /**
62
82
  * Context passed to mutate callback for service methods
63
83
  */
@@ -491,6 +511,12 @@ export interface ExecuteTxOptions {
491
511
  */
492
512
  signal?: AbortSignal;
493
513
 
514
+ /**
515
+ * Callback invoked after retrieval phase completes.
516
+ * Use this to inspect retrieval operations and results (e.g., read tracking).
517
+ */
518
+ onAfterRetrieve?: (uow: IUnitOfWork, results: unknown[]) => void | Promise<void>;
519
+
494
520
  /**
495
521
  * Callback invoked before mutations are executed.
496
522
  * Use this to add additional mutation operations (e.g., hook event records).
@@ -502,6 +528,11 @@ export interface ExecuteTxOptions {
502
528
  * Use this for post-mutation processing like hook execution.
503
529
  */
504
530
  onAfterMutate?: (uow: IUnitOfWork) => Promise<void>;
531
+
532
+ /**
533
+ * Plan mode suppresses hook execution while still running serviceTx logic.
534
+ */
535
+ planMode?: boolean;
505
536
  }
506
537
 
507
538
  /**
@@ -738,6 +769,10 @@ async function executeTx(
738
769
  try {
739
770
  // Create a fresh UOW for this attempt
740
771
  const baseUow = options.createUnitOfWork();
772
+ if (options.onAfterRetrieve) {
773
+ const readTrackingUow = baseUow as { enableReadTracking?: () => void };
774
+ readTrackingUow.enableReadTracking?.();
775
+ }
741
776
 
742
777
  // Create handler context
743
778
  const context: HandlerTxContext<THooks> = {
@@ -777,7 +812,10 @@ async function executeTx(
777
812
  });
778
813
  }
779
814
 
780
- await baseUow.executeRetrieve();
815
+ const allRetrieveResults = await baseUow.executeRetrieve();
816
+ if (options.onAfterRetrieve) {
817
+ await options.onAfterRetrieve(baseUow, allRetrieveResults);
818
+ }
781
819
 
782
820
  // Get retrieve results from TypedUnitOfWork's retrievalPhase or default to empty array
783
821
  const retrieveResult: TRetrieveResults = typedUowFromRetrieve
@@ -840,7 +878,7 @@ async function executeTx(
840
878
  mutateResult = callbacks.mutate(mutateCtx);
841
879
  }
842
880
 
843
- if (options.onBeforeMutate) {
881
+ if (!options.planMode && options.onBeforeMutate) {
844
882
  options.onBeforeMutate(baseUow);
845
883
  }
846
884
  const result = await baseUow.executeMutations();
@@ -883,7 +921,7 @@ async function executeTx(
883
921
  finalResult = serviceFinalResults;
884
922
  }
885
923
 
886
- if (options.onAfterMutate) {
924
+ if (!options.planMode && options.onAfterMutate) {
887
925
  await options.onAfterMutate(baseUow);
888
926
  }
889
927
 
@@ -1555,6 +1593,7 @@ interface HandlerTxBuilderState<
1555
1593
  > {
1556
1594
  options: ExecuteTxOptions;
1557
1595
  hooks?: THooks;
1596
+ executeWrapper?: <T>(run: () => Promise<T>) => Promise<T>;
1558
1597
  withServiceCallsFn?: () => TServiceCalls;
1559
1598
  retrieveFn?: (context: {
1560
1599
  forSchema: <S extends AnySchema, H extends HooksMap = THooks>(
@@ -1891,22 +1930,24 @@ export class HandlerTxBuilder<
1891
1930
  : undefined,
1892
1931
  };
1893
1932
 
1894
- // Use the existing executeTx implementation
1895
- return executeTx(callbacks as Parameters<typeof executeTx>[0], state.options) as Promise<
1896
- AwaitedPromisesInObject<
1897
- InferBuilderResultType<
1898
- TRetrieveResults,
1899
- TRetrieveSuccessResult,
1900
- TServiceCalls,
1901
- TMutateResult,
1902
- TTransformResult,
1903
- HasTransform,
1904
- HasMutate,
1905
- HasTransformRetrieve,
1906
- HasRetrieve
1933
+ const run = () =>
1934
+ executeTx(callbacks as Parameters<typeof executeTx>[0], state.options) as Promise<
1935
+ AwaitedPromisesInObject<
1936
+ InferBuilderResultType<
1937
+ TRetrieveResults,
1938
+ TRetrieveSuccessResult,
1939
+ TServiceCalls,
1940
+ TMutateResult,
1941
+ TTransformResult,
1942
+ HasTransform,
1943
+ HasMutate,
1944
+ HasTransformRetrieve,
1945
+ HasRetrieve
1946
+ >
1907
1947
  >
1908
- >
1909
- >;
1948
+ >;
1949
+
1950
+ return state.executeWrapper ? state.executeWrapper(run) : run();
1910
1951
  }
1911
1952
  }
1912
1953
 
@@ -1916,9 +1957,11 @@ export class HandlerTxBuilder<
1916
1957
  export function createHandlerTxBuilder<THooks extends HooksMap = {}>(
1917
1958
  options: ExecuteTxOptions,
1918
1959
  hooks?: THooks,
1960
+ executeWrapper?: <T>(run: () => Promise<T>) => Promise<T>,
1919
1961
  ): HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, THooks> {
1920
1962
  return new HandlerTxBuilder({
1921
1963
  options,
1922
1964
  hooks,
1965
+ executeWrapper,
1923
1966
  });
1924
1967
  }
@@ -1,4 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
+
2
3
  import {
3
4
  ExponentialBackoffRetryPolicy,
4
5
  LinearBackoffRetryPolicy,
@@ -7,12 +7,13 @@
7
7
  * not the runtime behavior (which is covered by execute-unit-of-work.test.ts).
8
8
  */
9
9
  import { describe, expectTypeOf, it } from "vitest";
10
+
10
11
  import type { AnySchema } from "../../schema/create";
11
- import type { TypedUnitOfWork } from "./unit-of-work";
12
12
  import {
13
13
  ServiceTxBuilder,
14
14
  HandlerTxBuilder,
15
15
  type TxResult,
16
+ serviceCalls,
16
17
  type ServiceBuilderMutateContext,
17
18
  type HandlerBuilderMutateContext,
18
19
  type BuilderTransformContextWithMutate,
@@ -21,6 +22,7 @@ import {
21
22
  type ExtractServiceFinalResults,
22
23
  type AwaitedPromisesInObject,
23
24
  } from "./execute-unit-of-work";
25
+ import type { TypedUnitOfWork } from "./unit-of-work";
24
26
 
25
27
  // =============================================================================
26
28
  // Helper types for extracting TxResult type parameters
@@ -552,6 +554,76 @@ describe("HandlerTxBuilder type inference", () => {
552
554
  readonly [{ user: { id: string } }]
553
555
  >();
554
556
  });
557
+
558
+ it("widens serviceIntermediateResult when serviceCalls is a non-tuple array", () => {
559
+ type Status = { status: string };
560
+ type HistoryPage = { runNumber: number };
561
+ type StatusCall = TxResult<Status, Status>;
562
+ type RunNumberCall = TxResult<number, number>;
563
+ type HistoryCall = TxResult<HistoryPage, HistoryPage>;
564
+
565
+ // When serviceCalls are built dynamically (e.g. Array.from + spread),
566
+ // the tuple shape is lost and results widen to a union array.
567
+ type ServiceCalls = readonly (StatusCall | RunNumberCall | HistoryCall)[];
568
+
569
+ type Builder = HandlerTxBuilder<
570
+ ServiceCalls,
571
+ [],
572
+ [],
573
+ unknown,
574
+ unknown,
575
+ false,
576
+ false,
577
+ true, // HasMutate
578
+ false,
579
+ {}
580
+ >;
581
+
582
+ type MutateParam = Parameters<Builder["mutate"]>[0];
583
+ type MutateCtx = Parameters<MutateParam>[0];
584
+
585
+ expectTypeOf<MutateCtx["serviceIntermediateResult"]>().toEqualTypeOf<
586
+ readonly (Status | number | HistoryPage)[]
587
+ >();
588
+ });
589
+
590
+ it("preserves tuple inference for dynamic serviceCalls when using helper", () => {
591
+ type Status = { status: string };
592
+ type HistoryPage = { runNumber: number };
593
+ type StatusCall = TxResult<Status, Status>;
594
+ type RunNumberCall = TxResult<number, number>;
595
+ type HistoryCall = TxResult<HistoryPage, HistoryPage>;
596
+
597
+ const statusCall = null as unknown as StatusCall;
598
+ const runNumberCall = null as unknown as RunNumberCall;
599
+ const historyCalls = [] as HistoryCall[];
600
+
601
+ const calls = serviceCalls(statusCall, runNumberCall, ...historyCalls);
602
+
603
+ expectTypeOf<typeof calls>().toEqualTypeOf<
604
+ readonly [StatusCall, RunNumberCall, ...HistoryCall[]]
605
+ >();
606
+
607
+ type Builder = HandlerTxBuilder<
608
+ typeof calls,
609
+ [],
610
+ [],
611
+ unknown,
612
+ unknown,
613
+ false,
614
+ false,
615
+ true, // HasMutate
616
+ false,
617
+ {}
618
+ >;
619
+
620
+ type MutateParam = Parameters<Builder["mutate"]>[0];
621
+ type MutateCtx = Parameters<MutateParam>[0];
622
+
623
+ expectTypeOf<MutateCtx["serviceIntermediateResult"]>().toEqualTypeOf<
624
+ readonly [Status, number, ...HistoryPage[]]
625
+ >();
626
+ });
555
627
  });
556
628
 
557
629
  describe("retrieve + transformRetrieve flow", () => {