@fragno-dev/db 0.2.2 → 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 (587) hide show
  1. package/.turbo/turbo-build.log +404 -175
  2. package/CHANGELOG.md +109 -0
  3. package/README.md +54 -9
  4. package/dist/adapters/adapters.d.ts +23 -21
  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/driver-config.d.ts +16 -1
  8. package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
  9. package/dist/adapters/generic-sql/driver-config.js +23 -1
  10. package/dist/adapters/generic-sql/driver-config.js.map +1 -1
  11. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +24 -9
  12. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
  13. package/dist/adapters/generic-sql/generic-sql-adapter.js +60 -22
  14. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
  15. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +169 -3
  16. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
  17. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
  18. package/dist/adapters/generic-sql/migration/dialect/mysql.js +25 -6
  19. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
  20. package/dist/adapters/generic-sql/migration/dialect/postgres.js +7 -6
  21. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
  22. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +193 -16
  23. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
  24. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
  25. package/dist/adapters/generic-sql/migration/executor.js +30 -3
  26. package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
  27. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
  28. package/dist/adapters/generic-sql/migration/prepared-migrations.js +9 -9
  29. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
  30. package/dist/adapters/generic-sql/migration/sql-generator.js +75 -52
  31. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
  32. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +7 -6
  33. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
  34. package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
  35. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
  36. package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
  37. package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
  38. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +32 -21
  39. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
  40. package/dist/adapters/generic-sql/query/select-builder.js +5 -3
  41. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
  42. package/dist/adapters/generic-sql/query/sql-query-compiler.js +49 -18
  43. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
  44. package/dist/adapters/generic-sql/query/where-builder.js +43 -29
  45. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
  46. package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
  47. package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  48. package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
  49. package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
  50. package/dist/adapters/generic-sql/uow-decoder.js +6 -2
  51. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
  52. package/dist/adapters/generic-sql/uow-encoder.js +27 -8
  53. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
  54. package/dist/adapters/in-memory/condition-evaluator.js +135 -0
  55. package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
  56. package/dist/adapters/in-memory/errors.d.ts +13 -0
  57. package/dist/adapters/in-memory/errors.d.ts.map +1 -0
  58. package/dist/adapters/in-memory/errors.js +23 -0
  59. package/dist/adapters/in-memory/errors.js.map +1 -0
  60. package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
  61. package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
  62. package/dist/adapters/in-memory/in-memory-adapter.js +196 -0
  63. package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
  64. package/dist/adapters/in-memory/in-memory-uow.js +871 -0
  65. package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
  66. package/dist/adapters/in-memory/index.d.ts +4 -0
  67. package/dist/adapters/in-memory/index.js +4 -0
  68. package/dist/adapters/in-memory/options.d.ts +30 -0
  69. package/dist/adapters/in-memory/options.d.ts.map +1 -0
  70. package/dist/adapters/in-memory/options.js +62 -0
  71. package/dist/adapters/in-memory/options.js.map +1 -0
  72. package/dist/adapters/in-memory/reference-resolution.js +26 -0
  73. package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
  74. package/dist/adapters/in-memory/sorted-array-index.js +129 -0
  75. package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
  76. package/dist/adapters/in-memory/store.js +71 -0
  77. package/dist/adapters/in-memory/store.js.map +1 -0
  78. package/dist/adapters/in-memory/value-comparison.js +28 -0
  79. package/dist/adapters/in-memory/value-comparison.js.map +1 -0
  80. package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
  81. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
  82. package/dist/adapters/shared/uow-operation-compiler.js +11 -11
  83. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
  84. package/dist/adapters/sql/index.d.ts +5 -0
  85. package/dist/adapters/sql/index.js +4 -0
  86. package/dist/browser/adapters/adapters.d.ts +61 -0
  87. package/dist/browser/adapters/adapters.d.ts.map +1 -0
  88. package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
  89. package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  90. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  91. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  92. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
  93. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  94. package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
  95. package/dist/browser/adapters/in-memory/index.d.ts +2 -0
  96. package/dist/browser/adapters/in-memory/options.d.ts +1 -0
  97. package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
  98. package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
  99. package/dist/browser/durable-hooks.d.ts +3 -0
  100. package/dist/browser/fragments/internal-fragment.d.ts +317 -0
  101. package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
  102. package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
  103. package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
  104. package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
  105. package/dist/browser/hooks/hooks.d.ts +146 -0
  106. package/dist/browser/hooks/hooks.d.ts.map +1 -0
  107. package/dist/browser/id.js +1 -0
  108. package/dist/browser/internal/adapter-registry.d.ts +4 -0
  109. package/dist/browser/internal/outbox-state.d.ts +2 -0
  110. package/dist/browser/mod.d.ts +15 -0
  111. package/dist/browser/mod.d.ts.map +1 -0
  112. package/dist/browser/mod.js +17 -0
  113. package/dist/browser/mod.js.map +1 -0
  114. package/dist/browser/mod2.d.ts +48 -0
  115. package/dist/browser/mod2.d.ts.map +1 -0
  116. package/dist/browser/naming/sql-naming.d.ts +19 -0
  117. package/dist/browser/naming/sql-naming.d.ts.map +1 -0
  118. package/dist/browser/outbox/outbox.d.ts +21 -0
  119. package/dist/browser/outbox/outbox.d.ts.map +1 -0
  120. package/dist/browser/query/column-defaults.js +1 -0
  121. package/dist/browser/query/condition-builder.d.ts +44 -0
  122. package/dist/browser/query/condition-builder.d.ts.map +1 -0
  123. package/dist/browser/query/condition-builder.js +97 -0
  124. package/dist/browser/query/condition-builder.js.map +1 -0
  125. package/dist/browser/query/cursor.d.ts +105 -0
  126. package/dist/browser/query/cursor.d.ts.map +1 -0
  127. package/dist/browser/query/cursor.js +150 -0
  128. package/dist/browser/query/cursor.js.map +1 -0
  129. package/dist/browser/query/db-now.d.ts +22 -0
  130. package/dist/browser/query/db-now.d.ts.map +1 -0
  131. package/dist/browser/query/db-now.js +33 -0
  132. package/dist/browser/query/db-now.js.map +1 -0
  133. package/dist/browser/query/orm/orm.d.ts +18 -0
  134. package/dist/browser/query/orm/orm.d.ts.map +1 -0
  135. package/dist/browser/query/simple-query-interface.d.ts +108 -0
  136. package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
  137. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
  138. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  139. package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
  140. package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  141. package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
  142. package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
  143. package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
  144. package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
  145. package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
  146. package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  147. package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
  148. package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
  149. package/dist/browser/query/value-encoding.js +38 -0
  150. package/dist/browser/query/value-encoding.js.map +1 -0
  151. package/dist/browser/schema/create.d.ts +326 -0
  152. package/dist/browser/schema/create.d.ts.map +1 -0
  153. package/dist/browser/schema/create.js +89 -0
  154. package/dist/browser/schema/create.js.map +1 -0
  155. package/dist/browser/schema/generate-id.js +28 -0
  156. package/dist/browser/schema/generate-id.js.map +1 -0
  157. package/dist/browser/shared/providers.d.ts +6 -0
  158. package/dist/browser/shared/providers.d.ts.map +1 -0
  159. package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
  160. package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
  161. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  162. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  163. package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
  164. package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  165. package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
  166. package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
  167. package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
  168. package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  169. package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
  170. package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  171. package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
  172. package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
  173. package/dist/browser/sync/commands.d.ts +15 -0
  174. package/dist/browser/sync/commands.d.ts.map +1 -0
  175. package/dist/browser/sync/commands.js +27 -0
  176. package/dist/browser/sync/commands.js.map +1 -0
  177. package/dist/browser/sync/types.d.ts +63 -0
  178. package/dist/browser/sync/types.d.ts.map +1 -0
  179. package/dist/browser/util/types.d.ts +8 -0
  180. package/dist/browser/util/types.d.ts.map +1 -0
  181. package/dist/browser/with-database.d.ts +29 -0
  182. package/dist/browser/with-database.d.ts.map +1 -0
  183. package/dist/client.d.ts +4 -0
  184. package/dist/client.js +5 -0
  185. package/dist/db-fragment-definition-builder.d.ts +101 -33
  186. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  187. package/dist/db-fragment-definition-builder.js +450 -60
  188. package/dist/db-fragment-definition-builder.js.map +1 -1
  189. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
  190. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
  191. package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
  192. package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
  193. package/dist/dispatchers/cloudflare-do/index.d.ts +11 -0
  194. package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
  195. package/dist/dispatchers/cloudflare-do/index.js +31 -0
  196. package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
  197. package/dist/dispatchers/node/dispatcher.d.ts +14 -0
  198. package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
  199. package/dist/dispatchers/node/dispatcher.js +80 -0
  200. package/dist/dispatchers/node/dispatcher.js.map +1 -0
  201. package/dist/dispatchers/node/index.d.ts +12 -0
  202. package/dist/dispatchers/node/index.d.ts.map +1 -0
  203. package/dist/dispatchers/node/index.js +27 -0
  204. package/dist/dispatchers/node/index.js.map +1 -0
  205. package/dist/durable-hooks.d.ts +31 -0
  206. package/dist/durable-hooks.d.ts.map +1 -0
  207. package/dist/durable-hooks.js +23 -0
  208. package/dist/durable-hooks.js.map +1 -0
  209. package/dist/fragments/internal-fragment.d.ts +186 -8
  210. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  211. package/dist/fragments/internal-fragment.js +203 -38
  212. package/dist/fragments/internal-fragment.js.map +1 -1
  213. package/dist/fragments/internal-fragment.routes.js +164 -0
  214. package/dist/fragments/internal-fragment.routes.js.map +1 -0
  215. package/dist/fragments/internal-fragment.schema.d.ts +15 -0
  216. package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
  217. package/dist/fragments/internal-fragment.schema.js +39 -0
  218. package/dist/fragments/internal-fragment.schema.js.map +1 -0
  219. package/dist/hooks/durable-hooks-logger.d.ts +10 -0
  220. package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
  221. package/dist/hooks/durable-hooks-logger.js +75 -0
  222. package/dist/hooks/durable-hooks-logger.js.map +1 -0
  223. package/dist/hooks/durable-hooks-processor.d.ts +1 -0
  224. package/dist/hooks/durable-hooks-processor.js +80 -0
  225. package/dist/hooks/durable-hooks-processor.js.map +1 -0
  226. package/dist/hooks/durable-hooks-runtime.js +44 -0
  227. package/dist/hooks/durable-hooks-runtime.js.map +1 -0
  228. package/dist/hooks/hooks.d.ts +100 -1
  229. package/dist/hooks/hooks.d.ts.map +1 -1
  230. package/dist/hooks/hooks.js +254 -27
  231. package/dist/hooks/hooks.js.map +1 -1
  232. package/dist/id.d.ts +2 -2
  233. package/dist/id.js +2 -2
  234. package/dist/internal/adapter-registry.d.ts +11 -0
  235. package/dist/internal/adapter-registry.d.ts.map +1 -0
  236. package/dist/internal/adapter-registry.js +135 -0
  237. package/dist/internal/adapter-registry.js.map +1 -0
  238. package/dist/internal/outbox-state.d.ts +2 -0
  239. package/dist/internal/outbox-state.js +26 -0
  240. package/dist/internal/outbox-state.js.map +1 -0
  241. package/dist/migration-engine/auto-from-schema.d.ts +33 -0
  242. package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
  243. package/dist/migration-engine/auto-from-schema.js +223 -37
  244. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  245. package/dist/migration-engine/generation-engine.d.ts +16 -10
  246. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  247. package/dist/migration-engine/generation-engine.js +86 -35
  248. package/dist/migration-engine/generation-engine.js.map +1 -1
  249. package/dist/migration-engine/shared.d.ts +113 -0
  250. package/dist/migration-engine/shared.d.ts.map +1 -0
  251. package/dist/migration-engine/shared.js.map +1 -1
  252. package/dist/mod.d.ts +20 -12
  253. package/dist/mod.d.ts.map +1 -1
  254. package/dist/mod.js +18 -12
  255. package/dist/mod.js.map +1 -1
  256. package/dist/naming/sql-naming.d.ts +19 -0
  257. package/dist/naming/sql-naming.d.ts.map +1 -0
  258. package/dist/naming/sql-naming.js +116 -0
  259. package/dist/naming/sql-naming.js.map +1 -0
  260. package/dist/outbox/outbox-builder.js +156 -0
  261. package/dist/outbox/outbox-builder.js.map +1 -0
  262. package/dist/outbox/outbox.d.ts +54 -0
  263. package/dist/outbox/outbox.d.ts.map +1 -0
  264. package/dist/outbox/outbox.js +37 -0
  265. package/dist/outbox/outbox.js.map +1 -0
  266. package/dist/query/column-defaults.js +20 -4
  267. package/dist/query/column-defaults.js.map +1 -1
  268. package/dist/query/condition-builder.d.ts +7 -1
  269. package/dist/query/condition-builder.d.ts.map +1 -1
  270. package/dist/query/condition-builder.js +5 -1
  271. package/dist/query/condition-builder.js.map +1 -1
  272. package/dist/query/cursor-client.d.ts +105 -0
  273. package/dist/query/cursor-client.d.ts.map +1 -0
  274. package/dist/query/cursor-client.js +165 -0
  275. package/dist/query/cursor-client.js.map +1 -0
  276. package/dist/query/cursor.d.ts +3 -1
  277. package/dist/query/cursor.d.ts.map +1 -1
  278. package/dist/query/cursor.js +51 -14
  279. package/dist/query/cursor.js.map +1 -1
  280. package/dist/query/db-now.d.ts +22 -0
  281. package/dist/query/db-now.d.ts.map +1 -0
  282. package/dist/query/db-now.js +35 -0
  283. package/dist/query/db-now.js.map +1 -0
  284. package/dist/query/orm/orm.js.map +1 -1
  285. package/dist/query/serialize/create-sql-serializer.js +5 -4
  286. package/dist/query/serialize/create-sql-serializer.js.map +1 -1
  287. package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
  288. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
  289. package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
  290. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
  291. package/dist/query/serialize/dialect/sqlite-serializer.js +60 -12
  292. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
  293. package/dist/query/serialize/sql-serializer.js +2 -2
  294. package/dist/query/serialize/sql-serializer.js.map +1 -1
  295. package/dist/query/simple-query-interface.d.ts +13 -4
  296. package/dist/query/simple-query-interface.d.ts.map +1 -1
  297. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
  298. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  299. package/dist/query/unit-of-work/execute-unit-of-work.js +50 -24
  300. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  301. package/dist/query/unit-of-work/unit-of-work.d.ts +92 -30
  302. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  303. package/dist/query/unit-of-work/unit-of-work.js +136 -11
  304. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  305. package/dist/query/value-decoding.js +16 -6
  306. package/dist/query/value-decoding.js.map +1 -1
  307. package/dist/query/value-encoding.js +29 -9
  308. package/dist/query/value-encoding.js.map +1 -1
  309. package/dist/schema/create.d.ts +103 -35
  310. package/dist/schema/create.d.ts.map +1 -1
  311. package/dist/schema/create.js +172 -58
  312. package/dist/schema/create.js.map +1 -1
  313. package/dist/schema/generate-id.js +2 -2
  314. package/dist/schema/generate-id.js.map +1 -1
  315. package/dist/schema/type-conversion/create-sql-type-mapper.js +4 -3
  316. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  317. package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
  318. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  319. package/dist/schema/validator.d.ts +10 -0
  320. package/dist/schema/validator.d.ts.map +1 -0
  321. package/dist/schema/validator.js +123 -0
  322. package/dist/schema/validator.js.map +1 -0
  323. package/dist/schema-output/drizzle.d.ts +30 -0
  324. package/dist/schema-output/drizzle.d.ts.map +1 -0
  325. package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +88 -60
  326. package/dist/schema-output/drizzle.js.map +1 -0
  327. package/dist/schema-output/prisma.d.ts +17 -0
  328. package/dist/schema-output/prisma.d.ts.map +1 -0
  329. package/dist/schema-output/prisma.js +307 -0
  330. package/dist/schema-output/prisma.js.map +1 -0
  331. package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
  332. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
  333. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
  334. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
  335. package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
  336. package/dist/sql-driver/sql.js.map +1 -1
  337. package/dist/sync/commands.d.ts +15 -0
  338. package/dist/sync/commands.d.ts.map +1 -0
  339. package/dist/sync/commands.js +27 -0
  340. package/dist/sync/commands.js.map +1 -0
  341. package/dist/sync/index.d.ts +4 -0
  342. package/dist/sync/index.js +4 -0
  343. package/dist/sync/read-tracking.d.ts +25 -0
  344. package/dist/sync/read-tracking.d.ts.map +1 -0
  345. package/dist/sync/read-tracking.js +148 -0
  346. package/dist/sync/read-tracking.js.map +1 -0
  347. package/dist/sync/submit.js +213 -0
  348. package/dist/sync/submit.js.map +1 -0
  349. package/dist/sync/types.d.ts +63 -0
  350. package/dist/sync/types.d.ts.map +1 -0
  351. package/dist/util/default-database-adapter.js +66 -0
  352. package/dist/util/default-database-adapter.js.map +1 -0
  353. package/dist/with-database.d.ts +3 -6
  354. package/dist/with-database.d.ts.map +1 -1
  355. package/dist/with-database.js +8 -7
  356. package/dist/with-database.js.map +1 -1
  357. package/package.json +62 -55
  358. package/src/adapters/adapters.ts +33 -26
  359. package/src/adapters/drizzle/migrate-drizzle.test.ts +99 -41
  360. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +601 -0
  361. package/src/adapters/drizzle/test-utils.ts +13 -8
  362. package/src/adapters/generic-sql/driver-config.ts +38 -0
  363. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +10 -8
  364. package/src/adapters/generic-sql/generic-sql-adapter.ts +117 -34
  365. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +55 -0
  366. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +297 -3
  367. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +120 -0
  368. package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
  369. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +27 -8
  370. package/src/adapters/generic-sql/migration/dialect/mysql.ts +47 -8
  371. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +28 -9
  372. package/src/adapters/generic-sql/migration/dialect/postgres.ts +9 -4
  373. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +839 -8
  374. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +396 -53
  375. package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
  376. package/src/adapters/generic-sql/migration/executor.ts +47 -4
  377. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +238 -46
  378. package/src/adapters/generic-sql/migration/prepared-migrations.ts +21 -13
  379. package/src/adapters/generic-sql/migration/sql-generator.ts +145 -66
  380. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +11 -8
  381. package/src/adapters/generic-sql/query/cursor-utils.test.ts +272 -0
  382. package/src/adapters/generic-sql/query/cursor-utils.ts +42 -7
  383. package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
  384. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +171 -35
  385. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +53 -40
  386. package/src/adapters/generic-sql/query/select-builder.test.ts +16 -11
  387. package/src/adapters/generic-sql/query/select-builder.ts +7 -3
  388. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +75 -6
  389. package/src/adapters/generic-sql/query/sql-query-compiler.ts +129 -24
  390. package/src/adapters/generic-sql/query/where-builder.test.ts +96 -20
  391. package/src/adapters/generic-sql/query/where-builder.ts +112 -41
  392. package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +11 -20
  393. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +851 -0
  394. package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +18 -15
  395. package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +282 -14
  396. package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +129 -12
  397. package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +9 -7
  398. package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
  399. package/src/adapters/generic-sql/uow-decoder.test.ts +5 -4
  400. package/src/adapters/generic-sql/uow-decoder.ts +23 -5
  401. package/src/adapters/generic-sql/uow-encoder.test.ts +36 -3
  402. package/src/adapters/generic-sql/uow-encoder.ts +48 -13
  403. package/src/adapters/in-memory/condition-evaluator.test.ts +194 -0
  404. package/src/adapters/in-memory/condition-evaluator.ts +280 -0
  405. package/src/adapters/in-memory/errors.ts +20 -0
  406. package/src/adapters/in-memory/in-memory-adapter.ts +388 -0
  407. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +344 -0
  408. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +255 -0
  409. package/src/adapters/in-memory/in-memory-uow.ts +1724 -0
  410. package/src/adapters/in-memory/index.ts +3 -0
  411. package/src/adapters/in-memory/options.test.ts +42 -0
  412. package/src/adapters/in-memory/options.ts +91 -0
  413. package/src/adapters/in-memory/outbox.test.ts +361 -0
  414. package/src/adapters/in-memory/reference-resolution.test.ts +51 -0
  415. package/src/adapters/in-memory/reference-resolution.ts +67 -0
  416. package/src/adapters/in-memory/sorted-array-index.test.ts +124 -0
  417. package/src/adapters/in-memory/sorted-array-index.ts +228 -0
  418. package/src/adapters/in-memory/store.test.ts +69 -0
  419. package/src/adapters/in-memory/store.ts +145 -0
  420. package/src/adapters/in-memory/value-comparison.ts +53 -0
  421. package/src/adapters/in-memory/value-normalization.test.ts +58 -0
  422. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1207 -0
  423. package/src/adapters/shared/from-unit-of-work-compiler.ts +159 -47
  424. package/src/adapters/shared/uow-operation-compiler.ts +28 -18
  425. package/src/adapters/sql/index.ts +12 -0
  426. package/src/browser/mod.ts +64 -0
  427. package/src/client.ts +19 -0
  428. package/src/db-fragment-definition-builder.test.ts +845 -53
  429. package/src/db-fragment-definition-builder.ts +911 -95
  430. package/src/db-fragment-instantiator.test.ts +210 -94
  431. package/src/db-fragment-integration.test.ts +17 -12
  432. package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
  433. package/src/dispatchers/cloudflare-do/index.test.ts +206 -0
  434. package/src/dispatchers/cloudflare-do/index.ts +63 -0
  435. package/src/dispatchers/node/dispatcher.ts +112 -0
  436. package/src/dispatchers/node/index.test.ts +120 -0
  437. package/src/dispatchers/node/index.ts +50 -0
  438. package/src/durable-hooks.test.ts +80 -0
  439. package/src/durable-hooks.ts +67 -0
  440. package/src/fragments/internal-fragment.routes.test.ts +570 -0
  441. package/src/fragments/internal-fragment.routes.ts +334 -0
  442. package/src/fragments/internal-fragment.schema.ts +95 -0
  443. package/src/fragments/internal-fragment.test.ts +505 -83
  444. package/src/fragments/internal-fragment.ts +453 -70
  445. package/src/hooks/durable-hooks-logger.ts +126 -0
  446. package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
  447. package/src/hooks/durable-hooks-processor.test.ts +282 -0
  448. package/src/hooks/durable-hooks-processor.ts +173 -0
  449. package/src/hooks/durable-hooks-runtime.test.ts +65 -0
  450. package/src/hooks/durable-hooks-runtime.ts +81 -0
  451. package/src/hooks/hooks.test.ts +455 -34
  452. package/src/hooks/hooks.ts +501 -34
  453. package/src/id.test.ts +34 -0
  454. package/src/id.ts +1 -3
  455. package/src/internal/adapter-registry.test.ts +93 -0
  456. package/src/internal/adapter-registry.ts +239 -0
  457. package/src/internal/outbox-state.ts +43 -0
  458. package/src/migration-engine/auto-from-schema.test.ts +107 -14
  459. package/src/migration-engine/auto-from-schema.ts +365 -44
  460. package/src/migration-engine/create.test.ts +4 -3
  461. package/src/migration-engine/create.ts +1 -1
  462. package/src/migration-engine/generation-engine.test.ts +292 -110
  463. package/src/migration-engine/generation-engine.ts +117 -66
  464. package/src/migration-engine/shared.ts +14 -0
  465. package/src/mod.ts +95 -39
  466. package/src/naming/sql-naming.ts +181 -0
  467. package/src/outbox/outbox-builder.ts +241 -0
  468. package/src/outbox/outbox.test.ts +424 -0
  469. package/src/outbox/outbox.ts +139 -0
  470. package/src/query/column-defaults.ts +42 -4
  471. package/src/query/condition-builder.test.ts +18 -3
  472. package/src/query/condition-builder.ts +7 -0
  473. package/src/query/cursor-client.test.ts +70 -0
  474. package/src/query/cursor-client.ts +263 -0
  475. package/src/query/cursor.test.ts +119 -20
  476. package/src/query/cursor.ts +88 -27
  477. package/src/query/db-now.ts +73 -0
  478. package/src/query/orm/orm.ts +2 -2
  479. package/src/query/query-type.test.ts +4 -3
  480. package/src/query/serialize/create-sql-serializer.ts +10 -5
  481. package/src/query/serialize/dialect/mysql-serializer.ts +13 -5
  482. package/src/query/serialize/dialect/postgres-serializer.ts +35 -5
  483. package/src/query/serialize/dialect/sqlite-serializer.test.ts +90 -3
  484. package/src/query/serialize/dialect/sqlite-serializer.ts +108 -12
  485. package/src/query/serialize/sql-serializer.ts +4 -4
  486. package/src/query/simple-query-interface.ts +15 -4
  487. package/src/query/unit-of-work/execute-unit-of-work.test.ts +372 -10
  488. package/src/query/unit-of-work/execute-unit-of-work.ts +87 -27
  489. package/src/query/unit-of-work/retry-policy.test.ts +1 -0
  490. package/src/query/unit-of-work/tx-builder.test.ts +73 -1
  491. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +17 -16
  492. package/src/query/unit-of-work/unit-of-work-types.test.ts +42 -12
  493. package/src/query/unit-of-work/unit-of-work.test.ts +196 -39
  494. package/src/query/unit-of-work/unit-of-work.ts +309 -38
  495. package/src/query/value-decoding.test.ts +63 -4
  496. package/src/query/value-decoding.ts +32 -6
  497. package/src/query/value-encoding.test.ts +86 -2
  498. package/src/query/value-encoding.ts +56 -6
  499. package/src/schema/create.test.ts +293 -47
  500. package/src/schema/create.ts +406 -70
  501. package/src/schema/generate-id.test.ts +3 -2
  502. package/src/schema/generate-id.ts +2 -2
  503. package/src/schema/serialize.test.ts +18 -5
  504. package/src/schema/type-conversion/create-sql-type-mapper.ts +8 -3
  505. package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
  506. package/src/schema/type-conversion/type-mapping.test.ts +26 -1
  507. package/src/schema/validator.test.ts +199 -0
  508. package/src/schema/validator.ts +232 -0
  509. package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +232 -129
  510. package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +155 -99
  511. package/src/schema-output/prisma.test.ts +694 -0
  512. package/src/schema-output/prisma.ts +593 -0
  513. package/src/sql-driver/better-sqlite3.test.ts +5 -3
  514. package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
  515. package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
  516. package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
  517. package/src/sql-driver/query-executor/query-executor.ts +1 -1
  518. package/src/sql-driver/sql-driver-adapter.ts +2 -2
  519. package/src/sql-driver/sql.ts +2 -1
  520. package/src/sql-driver/sqlocal.test.ts +4 -2
  521. package/src/sync/commands.test.ts +39 -0
  522. package/src/sync/commands.ts +51 -0
  523. package/src/sync/conflict-checker.test.ts +450 -0
  524. package/src/sync/conflict-checker.ts +248 -0
  525. package/src/sync/index.ts +14 -0
  526. package/src/sync/plan.ts +9 -0
  527. package/src/sync/read-tracking.test.ts +177 -0
  528. package/src/sync/read-tracking.ts +287 -0
  529. package/src/sync/submit.test.ts +205 -0
  530. package/src/sync/submit.ts +328 -0
  531. package/src/sync/types.ts +80 -0
  532. package/src/util/default-database-adapter.ts +119 -0
  533. package/src/with-database.ts +20 -31
  534. package/tsconfig.json +1 -1
  535. package/tsdown.config.ts +38 -24
  536. package/vitest.config.ts +1 -0
  537. package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
  538. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
  539. package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
  540. package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
  541. package/dist/adapters/drizzle/generate.d.ts +0 -30
  542. package/dist/adapters/drizzle/generate.d.ts.map +0 -1
  543. package/dist/adapters/drizzle/generate.js.map +0 -1
  544. package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
  545. package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
  546. package/dist/adapters/kysely/kysely-adapter.js +0 -17
  547. package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
  548. package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
  549. package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
  550. package/dist/adapters/shared/table-name-mapper.js +0 -43
  551. package/dist/adapters/shared/table-name-mapper.js.map +0 -1
  552. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js +0 -165
  553. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
  554. package/dist/packages/fragno/dist/api/bind-services.js +0 -20
  555. package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
  556. package/dist/packages/fragno/dist/api/error.js +0 -48
  557. package/dist/packages/fragno/dist/api/error.js.map +0 -1
  558. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -320
  559. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
  560. package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -525
  561. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
  562. package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
  563. package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
  564. package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
  565. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
  566. package/dist/packages/fragno/dist/api/internal/route.js +0 -10
  567. package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
  568. package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
  569. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
  570. package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
  571. package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
  572. package/dist/packages/fragno/dist/api/request-input-context.js +0 -118
  573. package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
  574. package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
  575. package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
  576. package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
  577. package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
  578. package/dist/packages/fragno/dist/api/route.js +0 -17
  579. package/dist/packages/fragno/dist/api/route.js.map +0 -1
  580. package/dist/packages/fragno/dist/internal/symbols.js +0 -10
  581. package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
  582. package/dist/schema-generator/schema-generator.d.ts +0 -15
  583. package/dist/schema-generator/schema-generator.d.ts.map +0 -1
  584. package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
  585. package/src/adapters/kysely/kysely-adapter.ts +0 -27
  586. package/src/adapters/shared/table-name-mapper.ts +0 -50
  587. package/src/schema-generator/schema-generator.ts +0 -12
@@ -1,17 +1,37 @@
1
1
  import { describe, it, expect, vi, expectTypeOf } from "vitest";
2
- import { defineFragment } from "@fragno-dev/core";
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ import { RequestContextStorage } from "@fragno-dev/core/internal/request-context-storage";
8
+ import SQLite from "better-sqlite3";
9
+ import { SqliteDialect } from "kysely";
10
+
11
+ import { defineFragment, instantiate } from "@fragno-dev/core";
12
+
13
+ import type { DatabaseAdapter, DatabaseContextStorage } from "./adapters/adapters";
14
+ import { BetterSQLite3DriverConfig } from "./adapters/generic-sql/driver-config";
15
+ import { SqlAdapter } from "./adapters/generic-sql/generic-sql-adapter";
3
16
  import {
4
17
  DatabaseFragmentDefinitionBuilder,
5
18
  type ImplicitDatabaseDependencies,
6
19
  } from "./db-fragment-definition-builder";
7
- import { withDatabase } from "./with-database";
8
- import { schema, column, idColumn } from "./schema/create";
20
+ import { internalSchema } from "./fragments/internal-fragment";
21
+ import { getDurableHooksRuntimeByToken } from "./hooks/durable-hooks-runtime";
22
+ import type { HookFn } from "./hooks/hooks";
23
+ import * as hooks from "./hooks/hooks";
24
+ import { getInternalFragment, getRegistryForAdapterSync } from "./internal/adapter-registry";
25
+ import { suffixNamingStrategy, sanitizeNamespace } from "./naming/sql-naming";
9
26
  import type { SimpleQueryInterface } from "./query/simple-query-interface";
10
- import type { DatabaseAdapter } from "./adapters/adapters";
11
27
  import * as executeUnitOfWork from "./query/unit-of-work/execute-unit-of-work";
28
+ import type { IUnitOfWork } from "./query/unit-of-work/unit-of-work";
29
+ import { schema, column, idColumn } from "./schema/create";
30
+ import { defineSyncCommands } from "./sync/commands";
31
+ import { withDatabase } from "./with-database";
12
32
 
13
33
  // Create a test schema
14
- const testSchema = schema((s) => {
34
+ const testSchema = schema("test", (s) => {
15
35
  return s.addTable("users", (t) => {
16
36
  return t
17
37
  .addColumn("id", idColumn())
@@ -24,18 +44,25 @@ type TestSchema = typeof testSchema;
24
44
 
25
45
  // Mock database adapter
26
46
  function createMockAdapter(): DatabaseAdapter {
47
+ const createMockUow = () => ({
48
+ forSchema: vi.fn(),
49
+ executeRetrieve: vi.fn(),
50
+ executeMutations: vi.fn(),
51
+ registerSchema: vi.fn(),
52
+ reset: vi.fn(),
53
+ });
54
+
27
55
  const mockdb = {
28
- createUnitOfWork: vi.fn(() => ({
29
- forSchema: vi.fn(),
30
- executeRetrieve: vi.fn(),
31
- executeMutations: vi.fn(),
32
- })),
56
+ createUnitOfWork: vi.fn(() => createMockUow()),
57
+ createBaseUnitOfWork: vi.fn(() => createMockUow()),
33
58
  } as unknown as SimpleQueryInterface<TestSchema>;
34
59
 
35
60
  return {
36
61
  createQueryEngine: vi.fn(() => mockdb),
37
- migrate: vi.fn(),
62
+ getSchemaVersion: vi.fn(async () => undefined),
38
63
  close: vi.fn(),
64
+ contextStorage: new RequestContextStorage(),
65
+ namingStrategy: suffixNamingStrategy,
39
66
  } as unknown as DatabaseAdapter;
40
67
  }
41
68
 
@@ -78,8 +105,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
78
105
 
79
106
  const definition = defineFragment("test-frag")
80
107
  .extend(withDatabase(testSchema))
81
- .withDependencies(({ db }) => {
82
- expect(db).toBeDefined();
108
+ .withDependencies(({ databaseAdapter }) => {
109
+ expect(databaseAdapter).toBeDefined();
83
110
  return {
84
111
  customDep: "value",
85
112
  };
@@ -92,7 +119,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
92
119
  });
93
120
 
94
121
  expect(deps.customDep).toBe("value");
95
- expect(deps.db).toBeDefined();
122
+ expect(deps.databaseAdapter).toBeDefined();
123
+ expect(deps.createUnitOfWork).toBeDefined();
96
124
  expect(deps.schema).toBeDefined();
97
125
  });
98
126
 
@@ -112,7 +140,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
112
140
  });
113
141
 
114
142
  expect(deps.apiKey).toBe("key123");
115
- expect(deps.db).toBeDefined();
143
+ expect(deps.databaseAdapter).toBeDefined();
144
+ expect(deps.createUnitOfWork).toBeDefined();
116
145
  expect(deps.schema).toBeDefined();
117
146
  });
118
147
 
@@ -122,7 +151,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
122
151
  const definition = defineFragment("test-frag")
123
152
  .extend(withDatabase(testSchema))
124
153
  .providesService("users", ({ deps }) => {
125
- expect(deps.db).toBeDefined();
154
+ expect(deps.databaseAdapter).toBeDefined();
126
155
  return {
127
156
  list: () => "users list",
128
157
  };
@@ -155,7 +184,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
155
184
  }))
156
185
  .extend(withDatabase(testSchema))
157
186
  .providesService("users", ({ deps }) => {
158
- expect(deps.db).toBeDefined();
187
+ expect(deps.databaseAdapter).toBeDefined();
159
188
  return {
160
189
  list: () => "users list",
161
190
  };
@@ -229,7 +258,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
229
258
  const definition = defineFragment("test-frag")
230
259
  .extend(withDatabase(testSchema))
231
260
  .providesBaseService(({ deps }) => {
232
- expect(deps.db).toBeDefined();
261
+ expect(deps.databaseAdapter).toBeDefined();
233
262
  return {
234
263
  healthCheck: () => "healthy",
235
264
  };
@@ -262,7 +291,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
262
291
  }))
263
292
  .extend(withDatabase(testSchema))
264
293
  .providesBaseService(({ deps }) => {
265
- expect(deps.db).toBeDefined();
294
+ expect(deps.databaseAdapter).toBeDefined();
266
295
  return {
267
296
  status: () => "ok",
268
297
  };
@@ -297,8 +326,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
297
326
 
298
327
  const definition = defineFragment("test-frag")
299
328
  .extend(withDatabase(testSchema))
300
- .withDependencies(({ db }) => {
301
- expect(db).toBeDefined();
329
+ .withDependencies(({ databaseAdapter }) => {
330
+ expect(databaseAdapter).toBeDefined();
302
331
  return {
303
332
  apiKey: "secret",
304
333
  };
@@ -313,7 +342,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
313
342
  },
314
343
  }))
315
344
  .providesService("data", ({ deps }) => {
316
- expect(deps.db).toBeDefined();
345
+ expect(deps.databaseAdapter).toBeDefined();
317
346
  return {
318
347
  fetch: () => `Fetching with ${deps.apiKey}`,
319
348
  };
@@ -366,15 +395,15 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
366
395
  baseConfig: "config1",
367
396
  }))
368
397
  .extend(withDatabase(testSchema))
369
- .withDependencies(({ db }) => {
370
- expect(db).toBeDefined();
398
+ .withDependencies(({ databaseAdapter }) => {
399
+ expect(databaseAdapter).toBeDefined();
371
400
  // Second withDependencies replaces the first one
372
401
  return {
373
402
  enhancedConfig: "enhanced",
374
403
  };
375
404
  })
376
405
  .providesService("combined", ({ deps }) => {
377
- expect(deps.db).toBeDefined();
406
+ expect(deps.databaseAdapter).toBeDefined();
378
407
  return {
379
408
  getConfig: () => deps.enhancedConfig,
380
409
  };
@@ -389,7 +418,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
389
418
  // Only the second withDependencies is used (plus implicit deps)
390
419
  expect(deps).not.toHaveProperty("baseConfig");
391
420
  expect(deps.enhancedConfig).toBe("enhanced");
392
- expect(deps.db).toBeDefined();
421
+ expect(deps.databaseAdapter).toBeDefined();
393
422
 
394
423
  const combinedService = definition.namedServices!.combined({
395
424
  config: {},
@@ -419,9 +448,9 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
419
448
  };
420
449
  })
421
450
  .extend(withDatabase(testSchema))
422
- .withDependencies(({ config, db }) => {
451
+ .withDependencies(({ config, databaseAdapter }) => {
423
452
  expectTypeOf(config).toExtend<MyConfig>();
424
- expect(db).toBeDefined();
453
+ expect(databaseAdapter).toBeDefined();
425
454
  // Second withDependencies replaces the first, so combine them here
426
455
  return {
427
456
  connectionTimeout: config.timeout,
@@ -437,7 +466,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
437
466
 
438
467
  expect(deps.connectionTimeout).toBe(5000);
439
468
  expect(deps.dbConnectionString).toBe("postgres://localhost");
440
- expect(deps.db).toBeDefined();
469
+ expect(deps.databaseAdapter).toBeDefined();
441
470
  });
442
471
 
443
472
  it("recommended pattern: extend first then configure", () => {
@@ -454,8 +483,8 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
454
483
  // Best practice: .extend() early, then add all your features
455
484
  const definition = defineFragment("best-practice")
456
485
  .extend(withDatabase(testSchema))
457
- .withDependencies(({ db }) => {
458
- expect(db).toBeDefined();
486
+ .withDependencies(({ databaseAdapter }) => {
487
+ expect(databaseAdapter).toBeDefined();
459
488
  return {
460
489
  apiKey: "secret",
461
490
  timeout: 5000,
@@ -465,7 +494,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
465
494
  .usesOptionalService<"auth", AuthService>("auth")
466
495
  .providesBaseService(({ deps }) => ({
467
496
  healthCheck: () => {
468
- expect(deps.db).toBeDefined();
497
+ expect(deps.databaseAdapter).toBeDefined();
469
498
  return `OK (timeout: ${deps.timeout})`;
470
499
  },
471
500
  }))
@@ -488,7 +517,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
488
517
  // Verify all dependencies including implicit ones
489
518
  expect(deps.apiKey).toBe("secret");
490
519
  expect(deps.timeout).toBe(5000);
491
- expect(deps.db).toBeDefined();
520
+ expect(deps.databaseAdapter).toBeDefined();
492
521
  expect(deps.schema).toBeDefined();
493
522
  expect(deps.createUnitOfWork).toBeDefined();
494
523
 
@@ -528,8 +557,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
528
557
  const mockAdapter = createMockAdapter();
529
558
 
530
559
  const definition = withDatabase(testSchema)(defineFragment("db-frag"))
531
- .withDependencies(({ db, databaseAdapter }) => {
532
- expect(db).toBeDefined();
560
+ .withDependencies(({ databaseAdapter }) => {
533
561
  expect(databaseAdapter).toBeDefined();
534
562
  return {
535
563
  customDep: "test",
@@ -546,7 +574,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
546
574
  });
547
575
 
548
576
  // Should have implicit deps
549
- expect(deps).toHaveProperty("db");
577
+ expect(deps).toHaveProperty("databaseAdapter");
550
578
  expect(deps).toHaveProperty("schema");
551
579
  expect(deps).toHaveProperty("createUnitOfWork");
552
580
  expect(deps).toHaveProperty("customDep");
@@ -567,7 +595,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
567
595
  });
568
596
 
569
597
  // Check implicit dependencies structure
570
- expect(deps.db).toBeDefined();
598
+ expect(deps.databaseAdapter).toBeDefined();
571
599
  expect(deps.schema).toBeDefined();
572
600
  expect(typeof deps.createUnitOfWork).toBe("function");
573
601
  });
@@ -581,7 +609,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
581
609
  .withDependencies(() => ({ apiKey: "key" }))
582
610
  .providesBaseService(({ deps, defineService }) => {
583
611
  expect(deps.apiKey).toBe("key");
584
- expect(deps.db).toBeDefined();
612
+ expect(deps.databaseAdapter).toBeDefined();
585
613
  expect(defineService).toBeDefined();
586
614
 
587
615
  return defineService({
@@ -678,6 +706,43 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
678
706
  });
679
707
  });
680
708
 
709
+ describe("withSyncCommands", () => {
710
+ it("registers sync commands in adapter registry", async () => {
711
+ const sqlite = new SQLite(":memory:");
712
+ const adapter = new SqlAdapter({
713
+ dialect: new SqliteDialect({ database: sqlite }),
714
+ driverConfig: new BetterSQLite3DriverConfig(),
715
+ });
716
+
717
+ const registry = getRegistryForAdapterSync(adapter);
718
+ const syncCommands = defineSyncCommands({ schema: testSchema }).create(
719
+ ({ defineCommand }) => [
720
+ defineCommand({
721
+ name: "ping",
722
+ handler: async () => undefined,
723
+ }),
724
+ ],
725
+ );
726
+
727
+ const definition = defineFragment("sync-frag")
728
+ .extend(withDatabase(testSchema))
729
+ .withSyncCommands(syncCommands)
730
+ .build();
731
+
732
+ definition.dependencies!({
733
+ config: {},
734
+ options: { databaseAdapter: adapter },
735
+ });
736
+
737
+ const resolved = registry.resolveSyncCommand("sync-frag", testSchema.name, "ping");
738
+ expect(resolved?.command).toBe(syncCommands.commands.get("ping"));
739
+ expect(resolved?.namespace).toBe(sanitizeNamespace(testSchema.name));
740
+
741
+ await adapter.close();
742
+ sqlite.close();
743
+ });
744
+ });
745
+
681
746
  describe("createRequestStorage and createThisContext", () => {
682
747
  it("should create request storage with UnitOfWork", () => {
683
748
  const mockAdapter = createMockAdapter();
@@ -735,10 +800,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
735
800
 
736
801
  const mockAdapter = createMockAdapter();
737
802
 
738
- const definition = withDatabase(
739
- testSchema,
740
- "my-namespace",
741
- )(defineFragment<Config>("complex-db-frag"))
803
+ const definition = withDatabase(testSchema)(defineFragment<Config>("complex-db-frag"))
742
804
  .withDependencies(({ config }) => ({
743
805
  connectionString: config.dbConnectionString,
744
806
  debug: config.debug,
@@ -806,17 +868,29 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
806
868
  });
807
869
  });
808
870
 
809
- describe("error handling", () => {
810
- it("should throw error if database adapter is missing", () => {
871
+ describe("default adapter", () => {
872
+ it("should default to sqlite adapter when database adapter is missing", () => {
873
+ const previous = process.env["FRAGNO_DATA_DIR"];
874
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "fragno-db-default-"));
875
+ process.env["FRAGNO_DATA_DIR"] = dataDir;
876
+
811
877
  const definition = withDatabase(testSchema)(defineFragment("db-frag")).build();
812
878
 
813
- expect(() => {
814
- definition.dependencies!({
879
+ try {
880
+ const deps = definition.dependencies!({
815
881
  config: {},
816
- // @ts-expect-error No databaseAdapter - intentionally invalid for runtime error test
817
882
  options: {},
818
883
  });
819
- }).toThrow("Database fragment requires a database adapter");
884
+
885
+ expect(deps.databaseAdapter).toBeDefined();
886
+ expect(deps.createUnitOfWork).toBeDefined();
887
+ } finally {
888
+ if (previous === undefined) {
889
+ delete process.env["FRAGNO_DATA_DIR"];
890
+ } else {
891
+ process.env["FRAGNO_DATA_DIR"] = previous;
892
+ }
893
+ }
820
894
  });
821
895
  });
822
896
 
@@ -842,7 +916,7 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
842
916
  >();
843
917
 
844
918
  expect(deps.customDep).toBe("test");
845
- expect(deps.db).toBeDefined();
919
+ expect(deps.databaseAdapter).toBeDefined();
846
920
  });
847
921
  });
848
922
 
@@ -870,21 +944,31 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
870
944
  getStore: () => ({
871
945
  uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
872
946
  }),
873
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
874
- } as any;
947
+ } as unknown as RequestContextStorage<DatabaseContextStorage>;
875
948
 
876
949
  // Spy on createServiceTxBuilder
877
950
  const createServiceTxBuilderSpy = vi.spyOn(executeUnitOfWork, "createServiceTxBuilder");
878
951
 
952
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
953
+ const deps = {} as any;
954
+
879
955
  // Get the contexts which includes serviceTx
880
956
  const contexts = definition.createThisContext!({
881
957
  config: {},
882
958
  options: { databaseAdapter: mockAdapter },
883
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
884
- deps: {} as any,
959
+ deps,
885
960
  storage: mockStorage,
886
961
  });
887
962
 
963
+ // Initialize hooks with services so serviceTx receives the hooks map.
964
+ definition.internalDataFactory?.({
965
+ config: {},
966
+ options: { databaseAdapter: mockAdapter },
967
+ deps,
968
+ services: {} as Record<string, never>,
969
+ serviceDeps: {} as Record<string, never>,
970
+ });
971
+
888
972
  // Call serviceTx - this should pass hooks to createServiceTxBuilder
889
973
  contexts.serviceContext.serviceTx(testSchema);
890
974
 
@@ -900,4 +984,712 @@ describe("DatabaseFragmentDefinitionBuilder", () => {
900
984
  createServiceTxBuilderSpy.mockRestore();
901
985
  });
902
986
  });
987
+
988
+ describe("provideHooks services access", () => {
989
+ it("should provide bound services to hooks factory", () => {
990
+ const mockAdapter = createMockAdapter();
991
+ let observed: string | null = null;
992
+
993
+ type TestHooks = {
994
+ onPing: HookFn;
995
+ };
996
+
997
+ const definition = defineFragment("db-frag-hooks-services")
998
+ .extend(withDatabase(testSchema))
999
+ .providesBaseService(() => ({
1000
+ ping: () => "pong",
1001
+ }))
1002
+ .provideHooks<TestHooks>(({ defineHook, services }) => {
1003
+ observed = services.ping();
1004
+ return {
1005
+ onPing: defineHook(async function () {
1006
+ // no-op
1007
+ }),
1008
+ };
1009
+ })
1010
+ .build();
1011
+
1012
+ const deps = definition.dependencies!({
1013
+ config: {},
1014
+ options: { databaseAdapter: mockAdapter },
1015
+ });
1016
+
1017
+ const internalData = definition.internalDataFactory?.({
1018
+ config: {},
1019
+ options: { databaseAdapter: mockAdapter },
1020
+ deps,
1021
+ services: { ping: () => "pong" },
1022
+ serviceDeps: {},
1023
+ }) as { durableHooksToken?: object } | undefined;
1024
+
1025
+ expect(observed).toBe("pong");
1026
+ expect(internalData?.durableHooksToken).toBeDefined();
1027
+ const runtime = internalData?.durableHooksToken
1028
+ ? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
1029
+ : undefined;
1030
+ expect(runtime?.config.hooks).toBeDefined();
1031
+ expect(runtime?.config.hooks).toHaveProperty("onPing");
1032
+ });
1033
+ });
1034
+
1035
+ describe("durable hooks waitUntil forwarding", () => {
1036
+ it("forwards request waitUntil to notifier context", async () => {
1037
+ const mockAdapter = createMockAdapter();
1038
+ const requestSourceSymbol = Symbol.for("fragno-request-source");
1039
+ const requestRouteSymbol = Symbol.for("fragno-request-route");
1040
+ const requestWaitUntilSymbol = Symbol.for("fragno-request-wait-until");
1041
+ const notifySpy = vi.fn().mockResolvedValue(undefined);
1042
+ const waitUntilSpy = vi.fn();
1043
+
1044
+ type TestHooks = {
1045
+ onPing: HookFn<{ value: string }>;
1046
+ };
1047
+
1048
+ const definition = defineFragment("db-frag-hooks-waituntil")
1049
+ .extend(withDatabase(testSchema))
1050
+ .provideHooks<TestHooks>(({ defineHook }) => ({
1051
+ onPing: defineHook(async function () {
1052
+ // no-op
1053
+ }),
1054
+ }))
1055
+ .build();
1056
+
1057
+ const deps = definition.dependencies!({
1058
+ config: {},
1059
+ options: { databaseAdapter: mockAdapter },
1060
+ });
1061
+
1062
+ const internalData = definition.internalDataFactory?.({
1063
+ config: {},
1064
+ options: { databaseAdapter: mockAdapter },
1065
+ deps,
1066
+ services: {} as Record<string, never>,
1067
+ serviceDeps: {} as Record<string, never>,
1068
+ }) as { durableHooksToken?: object } | undefined;
1069
+
1070
+ const runtime = internalData?.durableHooksToken
1071
+ ? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
1072
+ : undefined;
1073
+ if (!runtime) {
1074
+ throw new Error("Durable hooks runtime missing");
1075
+ }
1076
+ runtime.config.notifier = {
1077
+ notify: notifySpy,
1078
+ };
1079
+
1080
+ const createHandlerTxBuilderSpy = vi
1081
+ .spyOn(executeUnitOfWork, "createHandlerTxBuilder")
1082
+ .mockReturnValue(
1083
+ {} as unknown as ReturnType<typeof executeUnitOfWork.createHandlerTxBuilder>,
1084
+ );
1085
+
1086
+ const mockStorage = {
1087
+ getStore: () => ({
1088
+ uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
1089
+ [requestSourceSymbol]: "route",
1090
+ [requestRouteSymbol]: { method: "POST", path: "/users" },
1091
+ [requestWaitUntilSymbol]: waitUntilSpy,
1092
+ }),
1093
+ } as unknown as RequestContextStorage<DatabaseContextStorage>;
1094
+
1095
+ const contexts = definition.createThisContext!({
1096
+ config: {},
1097
+ options: { databaseAdapter: mockAdapter },
1098
+ deps,
1099
+ storage: mockStorage,
1100
+ });
1101
+
1102
+ contexts.handlerContext.handlerTx();
1103
+ const callArgs = createHandlerTxBuilderSpy.mock.calls[0]?.[0];
1104
+ const mockUow = {
1105
+ getTriggeredHooks: () => [
1106
+ {
1107
+ namespace: runtime.config.namespace,
1108
+ hookName: "onPing",
1109
+ payload: { value: "ping" },
1110
+ },
1111
+ ],
1112
+ } as unknown as IUnitOfWork;
1113
+ await callArgs?.onAfterMutate?.(mockUow);
1114
+
1115
+ expect(notifySpy).toHaveBeenCalledTimes(1);
1116
+ const notifyContext = notifySpy.mock.calls[0]?.[0] as
1117
+ | { route?: string; source?: string; waitUntil?: unknown }
1118
+ | undefined;
1119
+ expect(notifyContext).toMatchObject({
1120
+ source: "request",
1121
+ route: "POST /users",
1122
+ });
1123
+ expect(notifyContext?.waitUntil).toBe(waitUntilSpy);
1124
+ expect(waitUntilSpy).toHaveBeenCalledTimes(1);
1125
+
1126
+ createHandlerTxBuilderSpy.mockRestore();
1127
+ });
1128
+
1129
+ it("forwards inherited request waitUntil for hook mutations", async () => {
1130
+ const mockAdapter = createMockAdapter();
1131
+ const requestWaitUntilSymbol = Symbol.for("fragno-request-wait-until");
1132
+ const notifySpy = vi.fn().mockResolvedValue(undefined);
1133
+ const waitUntilSpy = vi.fn();
1134
+
1135
+ type TestHooks = {
1136
+ onPing: HookFn<{ value: string }>;
1137
+ };
1138
+
1139
+ const definition = defineFragment("db-frag-hooks-hook-waituntil")
1140
+ .extend(withDatabase(testSchema))
1141
+ .provideHooks<TestHooks>(({ defineHook }) => ({
1142
+ onPing: defineHook(async function () {
1143
+ // no-op
1144
+ }),
1145
+ }))
1146
+ .build();
1147
+
1148
+ const deps = definition.dependencies!({
1149
+ config: {},
1150
+ options: { databaseAdapter: mockAdapter },
1151
+ });
1152
+
1153
+ const internalData = definition.internalDataFactory?.({
1154
+ config: {},
1155
+ options: { databaseAdapter: mockAdapter },
1156
+ deps,
1157
+ services: {} as Record<string, never>,
1158
+ serviceDeps: {} as Record<string, never>,
1159
+ }) as { durableHooksToken?: object } | undefined;
1160
+
1161
+ const runtime = internalData?.durableHooksToken
1162
+ ? getDurableHooksRuntimeByToken(internalData.durableHooksToken)
1163
+ : undefined;
1164
+ if (!runtime) {
1165
+ throw new Error("Durable hooks runtime missing");
1166
+ }
1167
+ runtime.config.notifier = {
1168
+ notify: notifySpy,
1169
+ };
1170
+
1171
+ const createHandlerTxBuilderSpy = vi
1172
+ .spyOn(executeUnitOfWork, "createHandlerTxBuilder")
1173
+ .mockImplementation((options) => {
1174
+ return {
1175
+ execute: vi.fn(async () => {
1176
+ const mockUow = {
1177
+ getTriggeredHooks: () => [
1178
+ {
1179
+ namespace: runtime.config.namespace,
1180
+ hookName: "onPing",
1181
+ payload: { value: "ping" },
1182
+ },
1183
+ ],
1184
+ } as unknown as IUnitOfWork;
1185
+ await options.onAfterMutate?.(mockUow);
1186
+ }),
1187
+ } as unknown as ReturnType<typeof executeUnitOfWork.createHandlerTxBuilder>;
1188
+ });
1189
+
1190
+ try {
1191
+ const storage = mockAdapter.contextStorage as RequestContextStorage<DatabaseContextStorage>;
1192
+ await storage.runWithInitializer(
1193
+ () =>
1194
+ ({
1195
+ uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
1196
+ [requestWaitUntilSymbol]: waitUntilSpy,
1197
+ }) as DatabaseContextStorage,
1198
+ async () => {
1199
+ await runtime.config.handlerTx().execute();
1200
+ },
1201
+ );
1202
+
1203
+ expect(notifySpy).toHaveBeenCalledTimes(1);
1204
+ const notifyContext = notifySpy.mock.calls[0]?.[0] as
1205
+ | { source?: string; waitUntil?: unknown }
1206
+ | undefined;
1207
+ expect(notifyContext?.source).toBe("hook");
1208
+ expect(notifyContext?.waitUntil).toBe(waitUntilSpy);
1209
+ expect(waitUntilSpy).toHaveBeenCalledTimes(1);
1210
+ } finally {
1211
+ createHandlerTxBuilderSpy.mockRestore();
1212
+ }
1213
+ });
1214
+ });
1215
+
1216
+ describe("durable hooks cross-namespace scheduling", () => {
1217
+ it("should schedule hooks triggered in another namespace when mutation runs inside a hook", async () => {
1218
+ const sqlite = new SQLite(":memory:");
1219
+ const adapter = new SqlAdapter({
1220
+ dialect: new SqliteDialect({ database: sqlite }),
1221
+ driverConfig: new BetterSQLite3DriverConfig(),
1222
+ });
1223
+
1224
+ const schemaA = schema("hooks_alpha", (s) =>
1225
+ s.addTable("items", (t) =>
1226
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1227
+ ),
1228
+ );
1229
+ const schemaB = schema("hooks_beta", (s) =>
1230
+ s.addTable("items", (t) =>
1231
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1232
+ ),
1233
+ );
1234
+
1235
+ type HooksA = {
1236
+ onAlpha: (payload: { value: string }) => void;
1237
+ };
1238
+ type HooksB = {
1239
+ onBeta: (payload: { value: string }) => void;
1240
+ };
1241
+
1242
+ const hooksB: HooksB = {
1243
+ onBeta: () => {},
1244
+ };
1245
+
1246
+ const namespaceA = sanitizeNamespace(schemaA.name);
1247
+ const namespaceB = sanitizeNamespace(schemaB.name);
1248
+
1249
+ const fragmentADef = defineFragment("frag-hooks-a")
1250
+ .extend(withDatabase(schemaA))
1251
+ .provideHooks<HooksA>(({ defineHook }) => ({
1252
+ onAlpha: defineHook(async function () {
1253
+ await this.handlerTx({
1254
+ onBeforeMutate: (uow) => {
1255
+ uow.registerSchema(schemaB, namespaceB);
1256
+ },
1257
+ })
1258
+ .mutate(({ forSchema }) => {
1259
+ const other = forSchema(schemaB, hooksB);
1260
+ other.triggerHook("onBeta", { value: "beta" });
1261
+ })
1262
+ .execute();
1263
+ }),
1264
+ }))
1265
+ .build();
1266
+
1267
+ const fragmentBDef = defineFragment("frag-hooks-b")
1268
+ .extend(withDatabase(schemaB))
1269
+ .provideHooks<HooksB>(({ defineHook }) => ({
1270
+ onBeta: defineHook(async function () {
1271
+ // no-op
1272
+ }),
1273
+ }))
1274
+ .build();
1275
+
1276
+ try {
1277
+ const internalMigrations = adapter.prepareMigrations(internalSchema, null);
1278
+ await internalMigrations.executeWithDriver(adapter.driver, 0);
1279
+
1280
+ const migrationsA = adapter.prepareMigrations(schemaA, namespaceA);
1281
+ await migrationsA.executeWithDriver(adapter.driver, 0);
1282
+
1283
+ const migrationsB = adapter.prepareMigrations(schemaB, namespaceB);
1284
+ await migrationsB.executeWithDriver(adapter.driver, 0);
1285
+
1286
+ const fragmentA = instantiate(fragmentADef)
1287
+ .withConfig({})
1288
+ .withOptions({ databaseAdapter: adapter })
1289
+ .build();
1290
+ const fragmentB = instantiate(fragmentBDef)
1291
+ .withConfig({})
1292
+ .withOptions({ databaseAdapter: adapter })
1293
+ .build();
1294
+
1295
+ const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
1296
+ const tokenB = (fragmentB.$internal as { durableHooksToken?: object }).durableHooksToken;
1297
+
1298
+ expect(tokenA).toBeDefined();
1299
+ expect(tokenB).toBeDefined();
1300
+
1301
+ const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
1302
+ const runtimeB = tokenB ? getDurableHooksRuntimeByToken(tokenB) : undefined;
1303
+
1304
+ if (!runtimeA || !runtimeB) {
1305
+ throw new Error("Durable hooks runtime missing");
1306
+ }
1307
+
1308
+ const notifySpy = vi.fn();
1309
+ runtimeB.config.notifier = {
1310
+ notify: notifySpy,
1311
+ };
1312
+
1313
+ const internalFragment = getInternalFragment(adapter);
1314
+ await internalFragment.inContext(async function () {
1315
+ await this.handlerTx()
1316
+ .mutate(({ forSchema }) => {
1317
+ const uow = forSchema(internalSchema);
1318
+ uow.create("fragno_hooks", {
1319
+ namespace: namespaceA,
1320
+ hookName: "onAlpha",
1321
+ payload: { value: "alpha" },
1322
+ status: "pending",
1323
+ attempts: 0,
1324
+ maxAttempts: 1,
1325
+ lastAttemptAt: null,
1326
+ nextRetryAt: null,
1327
+ error: null,
1328
+ nonce: "test-nonce",
1329
+ });
1330
+ })
1331
+ .execute();
1332
+ });
1333
+
1334
+ const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
1335
+ runtimeA.config.runner = runnerA;
1336
+ await runnerA.processDue();
1337
+
1338
+ expect(notifySpy).toHaveBeenCalled();
1339
+ } finally {
1340
+ await adapter.close();
1341
+ sqlite.close();
1342
+ }
1343
+ });
1344
+
1345
+ it("should skip cross-namespace notify when target namespace has autoSchedule=false", async () => {
1346
+ const sqlite = new SQLite(":memory:");
1347
+ const adapter = new SqlAdapter({
1348
+ dialect: new SqliteDialect({ database: sqlite }),
1349
+ driverConfig: new BetterSQLite3DriverConfig(),
1350
+ });
1351
+
1352
+ const schemaA = schema("hooks_alpha_autoschedule", (s) =>
1353
+ s.addTable("items", (t) =>
1354
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1355
+ ),
1356
+ );
1357
+ const schemaB = schema("hooks_beta_autoschedule", (s) =>
1358
+ s.addTable("items", (t) =>
1359
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1360
+ ),
1361
+ );
1362
+
1363
+ type HooksA = {
1364
+ onAlpha: (payload: { value: string }) => void;
1365
+ };
1366
+ type HooksB = {
1367
+ onBeta: (payload: { value: string }) => void;
1368
+ };
1369
+
1370
+ const hooksB: HooksB = {
1371
+ onBeta: () => {},
1372
+ };
1373
+
1374
+ const namespaceA = sanitizeNamespace(schemaA.name);
1375
+ const namespaceB = sanitizeNamespace(schemaB.name);
1376
+
1377
+ const fragmentADef = defineFragment("frag-hooks-autoschedule-a")
1378
+ .extend(withDatabase(schemaA))
1379
+ .provideHooks<HooksA>(({ defineHook }) => ({
1380
+ onAlpha: defineHook(async function () {
1381
+ await this.handlerTx({
1382
+ onBeforeMutate: (uow) => {
1383
+ uow.registerSchema(schemaB, namespaceB);
1384
+ },
1385
+ })
1386
+ .mutate(({ forSchema }) => {
1387
+ const other = forSchema(schemaB, hooksB);
1388
+ other.triggerHook("onBeta", { value: "beta" });
1389
+ })
1390
+ .execute();
1391
+ }),
1392
+ }))
1393
+ .build();
1394
+
1395
+ const fragmentBDef = defineFragment("frag-hooks-autoschedule-b")
1396
+ .extend(withDatabase(schemaB))
1397
+ .provideHooks<HooksB>(({ defineHook }) => ({
1398
+ onBeta: defineHook(async function () {
1399
+ // no-op
1400
+ }),
1401
+ }))
1402
+ .build();
1403
+
1404
+ try {
1405
+ const internalMigrations = adapter.prepareMigrations(internalSchema, null);
1406
+ await internalMigrations.executeWithDriver(adapter.driver, 0);
1407
+
1408
+ const migrationsA = adapter.prepareMigrations(schemaA, namespaceA);
1409
+ await migrationsA.executeWithDriver(adapter.driver, 0);
1410
+
1411
+ const migrationsB = adapter.prepareMigrations(schemaB, namespaceB);
1412
+ await migrationsB.executeWithDriver(adapter.driver, 0);
1413
+
1414
+ const fragmentA = instantiate(fragmentADef)
1415
+ .withConfig({})
1416
+ .withOptions({ databaseAdapter: adapter })
1417
+ .build();
1418
+ const fragmentB = instantiate(fragmentBDef)
1419
+ .withConfig({})
1420
+ .withOptions({ databaseAdapter: adapter, durableHooks: { autoSchedule: false } })
1421
+ .build();
1422
+
1423
+ const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
1424
+ const tokenB = (fragmentB.$internal as { durableHooksToken?: object }).durableHooksToken;
1425
+
1426
+ expect(tokenA).toBeDefined();
1427
+ expect(tokenB).toBeDefined();
1428
+
1429
+ const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
1430
+ const runtimeB = tokenB ? getDurableHooksRuntimeByToken(tokenB) : undefined;
1431
+
1432
+ if (!runtimeA || !runtimeB) {
1433
+ throw new Error("Durable hooks runtime missing");
1434
+ }
1435
+
1436
+ const notifySpy = vi.fn();
1437
+ runtimeB.config.notifier = {
1438
+ notify: notifySpy,
1439
+ };
1440
+
1441
+ const internalFragment = getInternalFragment(adapter);
1442
+ await internalFragment.inContext(async function () {
1443
+ await this.handlerTx()
1444
+ .mutate(({ forSchema }) => {
1445
+ const uow = forSchema(internalSchema);
1446
+ uow.create("fragno_hooks", {
1447
+ namespace: namespaceA,
1448
+ hookName: "onAlpha",
1449
+ payload: { value: "alpha" },
1450
+ status: "pending",
1451
+ attempts: 0,
1452
+ maxAttempts: 1,
1453
+ lastAttemptAt: null,
1454
+ nextRetryAt: null,
1455
+ error: null,
1456
+ nonce: "auto-schedule-test-nonce",
1457
+ });
1458
+ })
1459
+ .execute();
1460
+ });
1461
+
1462
+ const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
1463
+ runtimeA.config.runner = runnerA;
1464
+ await runnerA.processDue();
1465
+
1466
+ expect(notifySpy).not.toHaveBeenCalled();
1467
+
1468
+ const queuedHooks = await internalFragment.inContext(async function () {
1469
+ return await this.handlerTx()
1470
+ .withServiceCalls(
1471
+ () =>
1472
+ [internalFragment.services.hookService.getHooksByNamespace(namespaceB)] as const,
1473
+ )
1474
+ .transform(({ serviceResult: [result] }) => result)
1475
+ .execute();
1476
+ });
1477
+
1478
+ const pendingBetaHooks = queuedHooks.filter(
1479
+ (hook) => hook.status === "pending" && hook.hookName === "onBeta",
1480
+ );
1481
+ expect(pendingBetaHooks.length).toBeGreaterThan(0);
1482
+ } finally {
1483
+ await adapter.close();
1484
+ sqlite.close();
1485
+ }
1486
+ });
1487
+
1488
+ it("should notify the matching runtime instance when namespace has multiple runtimes", async () => {
1489
+ const sqliteA = new SQLite(":memory:");
1490
+ const sqliteB = new SQLite(":memory:");
1491
+ const adapterA = new SqlAdapter({
1492
+ dialect: new SqliteDialect({ database: sqliteA }),
1493
+ driverConfig: new BetterSQLite3DriverConfig(),
1494
+ });
1495
+ const adapterB = new SqlAdapter({
1496
+ dialect: new SqliteDialect({ database: sqliteB }),
1497
+ driverConfig: new BetterSQLite3DriverConfig(),
1498
+ });
1499
+
1500
+ const unique = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1501
+ const schemaA = schema(`hooks_alpha_instance_${unique}`, (s) =>
1502
+ s.addTable("items", (t) =>
1503
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1504
+ ),
1505
+ );
1506
+ const schemaB = schema(`hooks_shared_${unique}`, (s) =>
1507
+ s.addTable("items", (t) =>
1508
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
1509
+ ),
1510
+ );
1511
+
1512
+ type HooksA = {
1513
+ onAlpha: (payload: { value: string }) => void;
1514
+ };
1515
+ type HooksB = {
1516
+ onBeta: (payload: { value: string }) => void;
1517
+ };
1518
+
1519
+ const hooksB: HooksB = {
1520
+ onBeta: () => {},
1521
+ };
1522
+
1523
+ const namespaceA = sanitizeNamespace(schemaA.name);
1524
+ const namespaceB = sanitizeNamespace(schemaB.name);
1525
+
1526
+ const fragmentADef = defineFragment("frag-hooks-instance-a")
1527
+ .extend(withDatabase(schemaA))
1528
+ .provideHooks<HooksA>(({ defineHook }) => ({
1529
+ onAlpha: defineHook(async function () {
1530
+ await this.handlerTx({
1531
+ onBeforeMutate: (uow) => {
1532
+ uow.registerSchema(schemaB, namespaceB);
1533
+ },
1534
+ })
1535
+ .mutate(({ forSchema }) => {
1536
+ const other = forSchema(schemaB, hooksB);
1537
+ other.triggerHook("onBeta", { value: "beta" });
1538
+ })
1539
+ .execute();
1540
+ }),
1541
+ }))
1542
+ .build();
1543
+
1544
+ const fragmentBDef = defineFragment("frag-hooks-shared")
1545
+ .extend(withDatabase(schemaB))
1546
+ .provideHooks<HooksB>(({ defineHook }) => ({
1547
+ onBeta: defineHook(async function () {
1548
+ // no-op
1549
+ }),
1550
+ }))
1551
+ .build();
1552
+
1553
+ try {
1554
+ const internalMigrationsA = adapterA.prepareMigrations(internalSchema, null);
1555
+ await internalMigrationsA.executeWithDriver(adapterA.driver, 0);
1556
+ const schemaAMigrations = adapterA.prepareMigrations(schemaA, namespaceA);
1557
+ await schemaAMigrations.executeWithDriver(adapterA.driver, 0);
1558
+ const schemaBMigrationsA = adapterA.prepareMigrations(schemaB, namespaceB);
1559
+ await schemaBMigrationsA.executeWithDriver(adapterA.driver, 0);
1560
+
1561
+ const internalMigrationsB = adapterB.prepareMigrations(internalSchema, null);
1562
+ await internalMigrationsB.executeWithDriver(adapterB.driver, 0);
1563
+ const schemaBMigrationsB = adapterB.prepareMigrations(schemaB, namespaceB);
1564
+ await schemaBMigrationsB.executeWithDriver(adapterB.driver, 0);
1565
+
1566
+ const fragmentA = instantiate(fragmentADef)
1567
+ .withConfig({})
1568
+ .withOptions({ databaseAdapter: adapterA })
1569
+ .build();
1570
+ const fragmentBForAdapterA = instantiate(fragmentBDef)
1571
+ .withConfig({})
1572
+ .withOptions({ databaseAdapter: adapterA })
1573
+ .build();
1574
+ const fragmentBForAdapterB = instantiate(fragmentBDef)
1575
+ .withConfig({})
1576
+ .withOptions({ databaseAdapter: adapterB })
1577
+ .build();
1578
+
1579
+ const tokenA = (fragmentA.$internal as { durableHooksToken?: object }).durableHooksToken;
1580
+ const tokenBForAdapterA = (fragmentBForAdapterA.$internal as { durableHooksToken?: object })
1581
+ .durableHooksToken;
1582
+ const tokenBForAdapterB = (fragmentBForAdapterB.$internal as { durableHooksToken?: object })
1583
+ .durableHooksToken;
1584
+
1585
+ expect(tokenA).toBeDefined();
1586
+ expect(tokenBForAdapterA).toBeDefined();
1587
+ expect(tokenBForAdapterB).toBeDefined();
1588
+
1589
+ const runtimeA = tokenA ? getDurableHooksRuntimeByToken(tokenA) : undefined;
1590
+ const runtimeBForAdapterA = tokenBForAdapterA
1591
+ ? getDurableHooksRuntimeByToken(tokenBForAdapterA)
1592
+ : undefined;
1593
+ const runtimeBForAdapterB = tokenBForAdapterB
1594
+ ? getDurableHooksRuntimeByToken(tokenBForAdapterB)
1595
+ : undefined;
1596
+
1597
+ if (!runtimeA || !runtimeBForAdapterA || !runtimeBForAdapterB) {
1598
+ throw new Error("Durable hooks runtime missing");
1599
+ }
1600
+
1601
+ const notifySpyForAdapterA = vi.fn();
1602
+ const notifySpyForAdapterB = vi.fn();
1603
+ runtimeBForAdapterA.config.notifier = {
1604
+ notify: notifySpyForAdapterA,
1605
+ };
1606
+ runtimeBForAdapterB.config.notifier = {
1607
+ notify: notifySpyForAdapterB,
1608
+ };
1609
+
1610
+ const internalFragment = getInternalFragment(adapterA);
1611
+ await internalFragment.inContext(async function () {
1612
+ await this.handlerTx()
1613
+ .mutate(({ forSchema }) => {
1614
+ const uow = forSchema(internalSchema);
1615
+ uow.create("fragno_hooks", {
1616
+ namespace: namespaceA,
1617
+ hookName: "onAlpha",
1618
+ payload: { value: "alpha" },
1619
+ status: "pending",
1620
+ attempts: 0,
1621
+ maxAttempts: 1,
1622
+ lastAttemptAt: null,
1623
+ nextRetryAt: null,
1624
+ error: null,
1625
+ nonce: "instance-test-nonce",
1626
+ });
1627
+ })
1628
+ .execute();
1629
+ });
1630
+
1631
+ const runnerA = runtimeA.config.runner ?? hooks.createDurableHooksRunner(runtimeA.config);
1632
+ runtimeA.config.runner = runnerA;
1633
+ await runnerA.processDue();
1634
+
1635
+ expect(notifySpyForAdapterA).toHaveBeenCalled();
1636
+ expect(notifySpyForAdapterB).not.toHaveBeenCalled();
1637
+ } finally {
1638
+ await adapterA.close();
1639
+ await adapterB.close();
1640
+ sqliteA.close();
1641
+ sqliteB.close();
1642
+ }
1643
+ });
1644
+ });
1645
+
1646
+ describe("handlerTx plan mode", () => {
1647
+ it("should suppress hook mutations in plan mode", () => {
1648
+ const mockAdapter = createMockAdapter();
1649
+
1650
+ type TestHooks = {
1651
+ onUserCreated: (payload: { email: string }) => void;
1652
+ };
1653
+
1654
+ const definition = withDatabase(testSchema)(defineFragment("db-frag-plan-mode"))
1655
+ .provideHooks<TestHooks>(({ defineHook }) => ({
1656
+ onUserCreated: defineHook(function () {
1657
+ // no-op
1658
+ }),
1659
+ }))
1660
+ .build();
1661
+
1662
+ const mockStorage = {
1663
+ getStore: () => ({
1664
+ uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
1665
+ }),
1666
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1667
+ } as any;
1668
+
1669
+ const createHandlerTxBuilderSpy = vi
1670
+ .spyOn(executeUnitOfWork, "createHandlerTxBuilder")
1671
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1672
+ .mockReturnValue({} as any);
1673
+
1674
+ const prepareHookMutationsSpy = vi.spyOn(hooks, "prepareHookMutations");
1675
+
1676
+ const contexts = definition.createThisContext!({
1677
+ config: {},
1678
+ options: { databaseAdapter: mockAdapter },
1679
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1680
+ deps: {} as any,
1681
+ storage: mockStorage,
1682
+ });
1683
+
1684
+ contexts.handlerContext.handlerTx({ planMode: true });
1685
+
1686
+ const callArgs = createHandlerTxBuilderSpy.mock.calls[0]?.[0];
1687
+ callArgs?.onBeforeMutate?.({} as unknown as IUnitOfWork);
1688
+
1689
+ expect(prepareHookMutationsSpy).not.toHaveBeenCalled();
1690
+
1691
+ prepareHookMutationsSpy.mockRestore();
1692
+ createHandlerTxBuilderSpy.mockRestore();
1693
+ });
1694
+ });
903
1695
  });