@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,8 +1,14 @@
1
+ import type { InternalFragmentInstance } from "../fragments/internal-fragment";
2
+ import { dbNow, isDbNow, type DbNow } from "../query/db-now";
3
+ import type {
4
+ ExecuteTxOptions,
5
+ HandlerTxBuilder,
6
+ } from "../query/unit-of-work/execute-unit-of-work";
1
7
  import type { RetryPolicy } from "../query/unit-of-work/retry-policy";
2
8
  import { ExponentialBackoffRetryPolicy } from "../query/unit-of-work/retry-policy";
3
9
  import type { IUnitOfWork } from "../query/unit-of-work/unit-of-work";
4
- import type { InternalFragmentInstance } from "../fragments/internal-fragment";
5
- import type { TxResult } from "../query/unit-of-work/execute-unit-of-work";
10
+ import type { FragnoId } from "../schema/create";
11
+ import { DurableHooksLogger, type DurableHooksLoggerConfig } from "./durable-hooks-logger";
6
12
 
7
13
  /**
8
14
  * Context available in hook functions via `this`.
@@ -14,8 +20,58 @@ export interface HookContext {
14
20
  * Use this for idempotency checks in your hook implementation.
15
21
  */
16
22
  idempotencyKey: string;
23
+ /**
24
+ * Hook event identifier (versioned FragnoId).
25
+ */
26
+ hookId: FragnoId;
27
+ /**
28
+ * Hook name for this event.
29
+ */
30
+ hookName: string;
31
+ /**
32
+ * Current status of the hook event.
33
+ */
34
+ status: HookStatus;
35
+ /**
36
+ * Attempt count for this hook event.
37
+ */
38
+ attempts: number;
39
+ /**
40
+ * Maximum attempts configured for this hook event.
41
+ */
42
+ maxAttempts: number;
43
+ /**
44
+ * Timestamp of the last attempt (if any).
45
+ */
46
+ lastAttemptAt: Date | null;
47
+ /**
48
+ * Next scheduled retry timestamp (if any).
49
+ */
50
+ nextRetryAt: Date | null;
51
+ /**
52
+ * When the hook event was created.
53
+ */
54
+ createdAt: Date;
55
+ /**
56
+ * Create a handler transaction builder to run atomic operations.
57
+ */
58
+ handlerTx: HookHandlerTx;
17
59
  }
18
60
 
61
+ export const hookStatusValues = ["pending", "processing", "completed", "failed"] as const;
62
+
63
+ export type HookStatus = (typeof hookStatusValues)[number];
64
+
65
+ export const isHookStatus = (value: unknown): value is HookStatus =>
66
+ typeof value === "string" && hookStatusValues.includes(value as HookStatus);
67
+
68
+ const assertHookStatus = (value: unknown): HookStatus => {
69
+ if (isHookStatus(value)) {
70
+ return value;
71
+ }
72
+ throw new Error(`Invalid hook status: ${String(value)}`);
73
+ };
74
+
19
75
  /**
20
76
  * A hook function signature.
21
77
  * Hooks receive a typed payload and access context via `this`.
@@ -43,6 +99,16 @@ export interface TriggerHookOptions {
43
99
  * If not provided, uses the default retry policy.
44
100
  */
45
101
  retryPolicy?: RetryPolicy;
102
+ /**
103
+ * Optional hook event ID. When provided, the hook insert will fail if a hook
104
+ * with the same ID already exists.
105
+ */
106
+ id?: string | FragnoId;
107
+ /**
108
+ * Absolute time for the first attempt. If in the future, the hook is
109
+ * scheduled for that time; if in the past, it runs immediately.
110
+ */
111
+ processAt?: Date | DbNow;
46
112
  }
47
113
 
48
114
  /**
@@ -50,28 +116,153 @@ export interface TriggerHookOptions {
50
116
  * Stored in the Unit of Work before execution.
51
117
  */
52
118
  export interface TriggeredHook {
119
+ namespace: string;
53
120
  hookName: string;
54
121
  payload: unknown;
55
122
  options?: TriggerHookOptions;
56
123
  }
57
124
 
125
+ export type HookNotifySource = "request" | "hook" | "alarm";
126
+
127
+ export type HookNotifyContext = {
128
+ source: HookNotifySource;
129
+ route?: string | null;
130
+ waitUntil?: (promise: Promise<unknown>) => void;
131
+ };
132
+
133
+ export type HookNotifier = {
134
+ notify: (context: HookNotifyContext) => void | Promise<void>;
135
+ };
136
+
137
+ export type DurableHooksRunner = {
138
+ processDue: () => Promise<number>;
139
+ drain: () => Promise<void>;
140
+ };
141
+
142
+ /**
143
+ * @deprecated Use DurableHooksRunner.
144
+ */
145
+ export type HookScheduler = {
146
+ schedule: () => Promise<number>;
147
+ drain: () => Promise<void>;
148
+ };
149
+
58
150
  /**
59
151
  * Configuration for hook processing.
60
152
  */
61
- export interface HookProcessorConfig {
62
- hooks: HooksMap;
153
+ export interface HookProcessorConfig<THooks extends HooksMap = HooksMap> {
154
+ hooks?: THooks;
63
155
  namespace: string;
64
156
  internalFragment: InternalFragmentInstance;
157
+ handlerTx: HookHandlerTx;
158
+ /**
159
+ * Whether post-mutate notifications should auto-schedule processing.
160
+ * Defaults to true.
161
+ */
162
+ autoSchedule?: boolean;
163
+ /**
164
+ * Internal hook runner used to coordinate processing/drain.
165
+ */
166
+ runner?: DurableHooksRunner;
167
+ /**
168
+ * Post-commit durable hooks notifier.
169
+ */
170
+ notifier?: HookNotifier;
171
+ /**
172
+ * @deprecated Use `runner`.
173
+ */
174
+ scheduler?: HookScheduler;
65
175
  defaultRetryPolicy?: RetryPolicy;
176
+ /**
177
+ * Re-queue hooks that have been in `processing` for at least this many minutes.
178
+ * Use `false` to disable stuck-processing recovery entirely.
179
+ * Values <= 0 are treated as `false`.
180
+ *
181
+ * Default: 10 minutes.
182
+ */
183
+ stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
184
+ /**
185
+ * Called when stuck processing hooks are detected and re-queued.
186
+ * Invoked after the hooks are moved back to `pending`.
187
+ */
188
+ onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
189
+ }
190
+
191
+ export type StuckHookProcessingTimeoutMinutes = number | false;
192
+
193
+ export type StuckHookProcessingEvent = {
194
+ id: FragnoId;
195
+ hookName: string;
196
+ attempts: number;
197
+ maxAttempts: number;
198
+ lastAttemptAt: Date | null;
199
+ nextRetryAt: Date | null;
200
+ };
201
+
202
+ export type StuckHookProcessingInfo = {
203
+ namespace: string;
204
+ timeoutMinutes: number;
205
+ events: StuckHookProcessingEvent[];
206
+ };
207
+
208
+ export type DurableHooksProcessingOptions = {
209
+ /**
210
+ * Re-queue hooks that have been in `processing` for at least this many minutes.
211
+ * Use `false` to disable stuck-processing recovery entirely.
212
+ * Values <= 0 are treated as `false`.
213
+ *
214
+ * Default: 10 minutes.
215
+ */
216
+ stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
217
+ /**
218
+ * Automatically schedule hook processing after successful mutations.
219
+ * Defaults to true. Set false to disable auto scheduling (manual drain only).
220
+ */
221
+ autoSchedule?: boolean;
222
+ /**
223
+ * Called when stuck processing hooks are detected and re-queued.
224
+ * Invoked after the hooks are moved back to `pending`.
225
+ */
226
+ onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
227
+ /**
228
+ * Durable hooks logging controls.
229
+ * Defaults: enabled=true, level="warn".
230
+ */
231
+ logging?: DurableHooksLoggerConfig;
232
+ };
233
+
234
+ export type HookHandlerTx = (
235
+ execOptions?: Omit<ExecuteTxOptions, "createUnitOfWork">,
236
+ ) => HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, HooksMap>;
237
+
238
+ const DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;
239
+
240
+ function resolveStuckProcessingTimeoutMinutes(
241
+ value: StuckHookProcessingTimeoutMinutes | undefined,
242
+ ): number | false {
243
+ if (value === false) {
244
+ return false;
245
+ }
246
+ if (typeof value === "number") {
247
+ return value > 0 ? value : false;
248
+ }
249
+ return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;
66
250
  }
67
251
 
68
252
  /**
69
253
  * Add hook events as mutation operations to the UOW.
70
254
  * This should be called before executeMutations() so hook records are created
71
255
  * in the same transaction as the user's mutations.
256
+ *
257
+ * Each triggered hook carries its own namespace (set at trigger-time), so this
258
+ * function works regardless of which fragment's handler is executing. When a
259
+ * `config` is provided its `defaultRetryPolicy` is used as fallback.
72
260
  */
73
- export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConfig): void {
74
- const { namespace, internalFragment, defaultRetryPolicy } = config;
261
+ export function prepareHookMutations(
262
+ uow: Pick<IUnitOfWork, "getTriggeredHooks" | "forSchema" | "idempotencyKey">,
263
+ internalFragment: InternalFragmentInstance,
264
+ defaultRetryPolicy?: RetryPolicy,
265
+ ): void {
75
266
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
76
267
 
77
268
  const triggeredHooks = uow.getTriggeredHooks();
@@ -80,24 +271,57 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
80
271
  return;
81
272
  }
82
273
 
274
+ const primaryNamespace = triggeredHooks[0]?.namespace;
275
+ DurableHooksLogger.debug("Durable hooks queued", {
276
+ namespace: primaryNamespace,
277
+ fields: () => {
278
+ const hookSummary = new Map<string, { namespace: string; hookName: string; count: number }>();
279
+ for (const hook of triggeredHooks) {
280
+ const key = `${hook.namespace}:${hook.hookName}`;
281
+ const summary = hookSummary.get(key);
282
+ if (summary) {
283
+ summary.count += 1;
284
+ continue;
285
+ }
286
+ hookSummary.set(key, {
287
+ namespace: hook.namespace,
288
+ hookName: hook.hookName,
289
+ count: 1,
290
+ });
291
+ }
292
+ const hooks = Array.from(hookSummary.values());
293
+ return {
294
+ hooks,
295
+ total: triggeredHooks.length,
296
+ };
297
+ },
298
+ });
299
+
83
300
  const internalSchema = internalFragment.$internal.deps.schema;
84
301
  const internalUow = uow.forSchema(internalSchema);
85
302
 
86
303
  for (const hook of triggeredHooks) {
87
304
  const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;
88
305
  const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;
89
- internalUow.create("fragno_hooks", {
90
- namespace,
306
+ const rawProcessAt = hook.options?.processAt ?? null;
307
+ const processAt: Date | DbNow | null =
308
+ rawProcessAt === null ? null : isDbNow(rawProcessAt) ? rawProcessAt : new Date(rawProcessAt);
309
+ const nextRetryAt = processAt ?? null;
310
+ const hookValues = {
311
+ ...(hook.options?.id !== undefined ? { id: hook.options.id } : {}),
312
+ namespace: hook.namespace,
91
313
  hookName: hook.hookName,
92
314
  payload: hook.payload,
93
315
  status: "pending",
94
316
  attempts: 0,
95
317
  maxAttempts,
96
318
  lastAttemptAt: null,
97
- nextRetryAt: null,
319
+ nextRetryAt,
98
320
  error: null,
99
321
  nonce: uow.idempotencyKey,
100
- });
322
+ };
323
+
324
+ internalUow.create("fragno_hooks", hookValues);
101
325
  }
102
326
  }
103
327
 
@@ -105,29 +329,117 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
105
329
  * Process pending hook events after the transaction has committed.
106
330
  * This should be called in the onSuccess callback after executeMutations().
107
331
  */
108
- export async function processHooks(config: HookProcessorConfig): Promise<void> {
332
+ export async function processHooks<THooks extends HooksMap>(
333
+ config: HookProcessorConfig<THooks>,
334
+ ): Promise<number> {
109
335
  const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
336
+ if (!hooks) {
337
+ throw new Error("Hook processor hooks not initialized.");
338
+ }
110
339
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
340
+ const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(
341
+ config.stuckProcessingTimeoutMinutes,
342
+ );
343
+ const internalSchema = internalFragment.$internal.deps.schema;
344
+ const includeStuckProcessing = stuckProcessingTimeoutMinutes !== false;
345
+ const staleBefore = includeStuckProcessing
346
+ ? dbNow().plus({ minutes: -stuckProcessingTimeoutMinutes })
347
+ : null;
348
+ let claimedEvents: Array<{
349
+ id: FragnoId;
350
+ hookName: string;
351
+ payload: unknown;
352
+ status: HookStatus;
353
+ attempts: number;
354
+ maxAttempts: number;
355
+ idempotencyKey: string;
356
+ lastAttemptAt: Date | null;
357
+ nextRetryAt: Date | null;
358
+ createdAt: Date;
359
+ }> = [];
360
+ let stuckEvents: StuckHookProcessingEvent[] = [];
111
361
 
112
- // Get pending events
113
- const pendingEvents = await internalFragment.inContext(async function () {
362
+ const result = await internalFragment.inContext(async function () {
114
363
  return await this.handlerTx()
115
- .withServiceCalls(
116
- () => [internalFragment.services.hookService.getPendingHookEvents(namespace)] as const,
117
- )
118
- .transform(({ serviceResult: [events] }) => events)
364
+ .withServiceCalls(() => {
365
+ const pending = internalFragment.services.hookService.claimPendingHookEvents(namespace);
366
+ const stuck = includeStuckProcessing
367
+ ? internalFragment.services.hookService.claimStuckProcessingHookEvents(
368
+ namespace,
369
+ staleBefore!,
370
+ )
371
+ : undefined;
372
+ return [pending, stuck] as const;
373
+ })
374
+ .transform(({ serviceResult: [pendingEvents, stuckResult] }) => ({
375
+ pendingEvents,
376
+ stuckResult,
377
+ }))
119
378
  .execute();
120
379
  });
121
380
 
122
- if (pendingEvents.length === 0) {
123
- return;
381
+ const pendingCount = result.pendingEvents.length;
382
+ const stuckClaimedCount = result.stuckResult?.events.length ?? 0;
383
+ const stuckRequeuedCount = result.stuckResult?.stuckEvents.length ?? 0;
384
+ const staleBeforeApprox = includeStuckProcessing
385
+ ? new Date(Date.now() - stuckProcessingTimeoutMinutes * 60_000).toISOString()
386
+ : null;
387
+ DurableHooksLogger.debug("Durable hooks claimed", {
388
+ namespace,
389
+ fields: {
390
+ pending: pendingCount,
391
+ stuck: stuckClaimedCount,
392
+ requeued: stuckRequeuedCount,
393
+ includeStuckProcessing,
394
+ staleBeforeApprox,
395
+ },
396
+ });
397
+
398
+ claimedEvents = [...result.pendingEvents, ...(result.stuckResult?.events ?? [])].map((event) => ({
399
+ ...event,
400
+ status: assertHookStatus(event.status),
401
+ }));
402
+ stuckEvents = result.stuckResult?.stuckEvents ?? [];
403
+
404
+ if (includeStuckProcessing && stuckEvents.length > 0) {
405
+ try {
406
+ config.onStuckProcessingHooks?.({
407
+ namespace,
408
+ timeoutMinutes: stuckProcessingTimeoutMinutes,
409
+ events: stuckEvents,
410
+ });
411
+ } catch (error) {
412
+ DurableHooksLogger.error("Error calling onStuckProcessingHooks", {
413
+ namespace,
414
+ fields: {
415
+ error: DurableHooksLogger.toErrorMessage(error),
416
+ },
417
+ });
418
+ }
419
+ }
420
+
421
+ if (claimedEvents.length === 0) {
422
+ DurableHooksLogger.debug("Durable hooks idle", {
423
+ namespace,
424
+ });
425
+ return 0;
124
426
  }
125
427
 
126
428
  // Process events (async work outside transaction)
127
429
  const processedEvents = await Promise.allSettled(
128
- pendingEvents.map(async (event) => {
430
+ claimedEvents.map(async (event) => {
129
431
  const hookFn = hooks[event.hookName];
130
432
  if (!hookFn) {
433
+ DurableHooksLogger.error("Hook missing", {
434
+ namespace,
435
+ fields: {
436
+ eventId: event.id,
437
+ hookName: event.hookName,
438
+ attempts: event.attempts,
439
+ maxAttempts: event.maxAttempts,
440
+ createdAt: event.createdAt.toISOString(),
441
+ },
442
+ });
131
443
  return {
132
444
  eventId: event.id,
133
445
  status: "failed" as const,
@@ -138,14 +450,56 @@ export async function processHooks(config: HookProcessorConfig): Promise<void> {
138
450
  }
139
451
 
140
452
  try {
141
- const hookContext: HookContext = { idempotencyKey: event.idempotencyKey };
453
+ const startedAt = Date.now();
454
+ DurableHooksLogger.debug("Hook start", {
455
+ namespace,
456
+ fields: {
457
+ eventId: event.id,
458
+ hookName: event.hookName,
459
+ status: event.status,
460
+ attempts: event.attempts,
461
+ maxAttempts: event.maxAttempts,
462
+ createdAt: event.createdAt.toISOString(),
463
+ nextRetryAt: event.nextRetryAt ? event.nextRetryAt.toISOString() : null,
464
+ lastAttemptAt: event.lastAttemptAt ? event.lastAttemptAt.toISOString() : null,
465
+ },
466
+ });
467
+ const hookContext: HookContext = {
468
+ idempotencyKey: event.idempotencyKey,
469
+ hookId: event.id,
470
+ hookName: event.hookName,
471
+ status: event.status,
472
+ attempts: event.attempts,
473
+ maxAttempts: event.maxAttempts,
474
+ lastAttemptAt: event.lastAttemptAt,
475
+ nextRetryAt: event.nextRetryAt,
476
+ createdAt: event.createdAt,
477
+ handlerTx: config.handlerTx,
478
+ };
142
479
  await hookFn.call(hookContext, event.payload);
480
+ DurableHooksLogger.debug("Hook completed", {
481
+ namespace,
482
+ fields: {
483
+ eventId: event.id,
484
+ hookName: event.hookName,
485
+ ms: Date.now() - startedAt,
486
+ },
487
+ });
143
488
  return {
144
489
  eventId: event.id,
145
490
  status: "completed" as const,
146
491
  };
147
492
  } catch (error) {
148
493
  const errorMessage = error instanceof Error ? error.message : String(error);
494
+ DurableHooksLogger.error("Hook failed", {
495
+ namespace,
496
+ fields: {
497
+ eventId: event.id.toJSON(),
498
+ hookName: event.hookName,
499
+ error: errorMessage,
500
+ idempotencyKey: event.idempotencyKey,
501
+ },
502
+ });
149
503
  return {
150
504
  eventId: event.id,
151
505
  status: "failed" as const,
@@ -160,31 +514,144 @@ export async function processHooks(config: HookProcessorConfig): Promise<void> {
160
514
  // Mark events as completed/failed
161
515
  await internalFragment.inContext(async function () {
162
516
  await this.handlerTx()
163
- .withServiceCalls(() => {
164
- const txResults: TxResult<void>[] = [];
517
+ .mutate(({ forSchema }) => {
518
+ const uow = forSchema(internalSchema);
519
+ const now = dbNow();
520
+
165
521
  for (const processedEvent of processedEvents) {
166
522
  if (processedEvent.status === "rejected") {
523
+ DurableHooksLogger.error("Hook processing promise rejected", {
524
+ namespace,
525
+ fields: {
526
+ error: DurableHooksLogger.toErrorMessage(processedEvent.reason),
527
+ },
528
+ });
167
529
  continue;
168
530
  }
169
531
 
170
532
  const { eventId, status } = processedEvent.value;
171
533
 
172
534
  if (status === "completed") {
173
- txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
174
- } else if (status === "failed") {
175
- const { error, attempts } = processedEvent.value;
176
- txResults.push(
177
- internalFragment.services.hookService.markHookFailed(
178
- eventId,
179
- error,
180
- attempts,
181
- retryPolicy,
182
- ),
535
+ uow.update("fragno_hooks", eventId, (b) =>
536
+ b.set({ status: "completed", lastAttemptAt: now }).check(),
537
+ );
538
+ continue;
539
+ }
540
+
541
+ const { error, attempts } = processedEvent.value;
542
+ const newAttempts = attempts + 1;
543
+ const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
544
+
545
+ if (shouldRetry) {
546
+ const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
547
+ const nextRetryAt = now.plus({ ms: delayMs });
548
+ uow.update("fragno_hooks", eventId, (b) =>
549
+ b
550
+ .set({
551
+ status: "pending",
552
+ attempts: newAttempts,
553
+ lastAttemptAt: now,
554
+ nextRetryAt,
555
+ error,
556
+ })
557
+ .check(),
558
+ );
559
+ } else {
560
+ uow.update("fragno_hooks", eventId, (b) =>
561
+ b
562
+ .set({
563
+ status: "failed",
564
+ attempts: newAttempts,
565
+ lastAttemptAt: now,
566
+ error,
567
+ })
568
+ .check(),
183
569
  );
184
570
  }
185
571
  }
186
- return txResults;
187
572
  })
188
573
  .execute();
189
574
  });
575
+
576
+ const processedCount = processedEvents.reduce(
577
+ (count, result) => count + (result.status === "fulfilled" ? 1 : 0),
578
+ 0,
579
+ );
580
+
581
+ const failed = processedEvents
582
+ .filter((result) => result.status === "fulfilled" && result.value.status === "failed")
583
+ .map((result) => {
584
+ if (result.status !== "fulfilled") {
585
+ return null;
586
+ }
587
+ return {
588
+ eventId: result.value.eventId,
589
+ error: result.value.error ?? null,
590
+ };
591
+ })
592
+ .filter(Boolean);
593
+
594
+ DurableHooksLogger.debug("Durable hooks processed", {
595
+ namespace,
596
+ fields: {
597
+ processed: processedCount,
598
+ failed: failed.length,
599
+ failures: failed.length > 0 ? failed : null,
600
+ },
601
+ });
602
+
603
+ return processedCount;
604
+ }
605
+
606
+ export function createDurableHooksRunner(config: HookProcessorConfig): DurableHooksRunner {
607
+ let processing = false;
608
+ let queued = false;
609
+ let currentPromise: Promise<number> | null = null;
610
+
611
+ const processDue = async () => {
612
+ if (processing) {
613
+ queued = true;
614
+ return currentPromise ?? Promise.resolve(0);
615
+ }
616
+
617
+ processing = true;
618
+ currentPromise = (async () => {
619
+ let totalProcessed = 0;
620
+ try {
621
+ do {
622
+ queued = false;
623
+ totalProcessed += await processHooks(config);
624
+ } while (queued);
625
+ return totalProcessed;
626
+ } finally {
627
+ processing = false;
628
+ queued = false;
629
+ currentPromise = null;
630
+ }
631
+ })();
632
+
633
+ return currentPromise;
634
+ };
635
+
636
+ const drain = async () => {
637
+ while (true) {
638
+ const processed = await processDue();
639
+ if (processed === 0) {
640
+ return;
641
+ }
642
+ }
643
+ };
644
+
645
+ return { processDue, drain };
646
+ }
647
+
648
+ /**
649
+ * @deprecated Use createDurableHooksRunner.
650
+ */
651
+ export function createHookScheduler(config: HookProcessorConfig): HookScheduler {
652
+ const runner = createDurableHooksRunner(config);
653
+ return {
654
+ schedule: () => runner.processDue(),
655
+ drain: () => runner.drain(),
656
+ };
190
657
  }
package/src/id.test.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+
3
+ import { createId, init } from "./id";
4
+
5
+ const idPattern = /^[a-z][0-9a-z]*$/;
6
+
7
+ const createCounter = (start = 0) => {
8
+ let value = start;
9
+ return () => value++;
10
+ };
11
+
12
+ describe("db id", () => {
13
+ afterEach(() => {
14
+ vi.useRealTimers();
15
+ vi.restoreAllMocks();
16
+ });
17
+
18
+ it("re-exports the shared createId implementation", () => {
19
+ const id = createId();
20
+
21
+ expect(id).toHaveLength(24);
22
+ expect(id).toMatch(idPattern);
23
+ });
24
+
25
+ it("re-exports init for deterministic generators", () => {
26
+ vi.useFakeTimers();
27
+ vi.setSystemTime(new Date("2020-01-01T00:00:00.000Z"));
28
+
29
+ const first = init({ random: () => 0, counter: createCounter(0), fingerprint: "fingerprint" });
30
+ const second = init({ random: () => 0, counter: createCounter(0), fingerprint: "fingerprint" });
31
+
32
+ expect(first()).toBe(second());
33
+ });
34
+ });
package/src/id.ts CHANGED
@@ -1,3 +1 @@
1
- import { createId } from "@paralleldrive/cuid2";
2
-
3
- export { createId };
1
+ export { createId, init } from "@fragno-dev/core";