@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,32 +1,82 @@
1
+ import { dbNow, isDbNow } from "../query/db-now.js";
2
+ import { DurableHooksLogger } from "./durable-hooks-logger.js";
1
3
  import { ExponentialBackoffRetryPolicy } from "../query/unit-of-work/retry-policy.js";
2
4
 
3
5
  //#region src/hooks/hooks.ts
6
+ const hookStatusValues = [
7
+ "pending",
8
+ "processing",
9
+ "completed",
10
+ "failed"
11
+ ];
12
+ const isHookStatus = (value) => typeof value === "string" && hookStatusValues.includes(value);
13
+ const assertHookStatus = (value) => {
14
+ if (isHookStatus(value)) return value;
15
+ throw new Error(`Invalid hook status: ${String(value)}`);
16
+ };
17
+ const DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;
18
+ function resolveStuckProcessingTimeoutMinutes(value) {
19
+ if (value === false) return false;
20
+ if (typeof value === "number") return value > 0 ? value : false;
21
+ return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;
22
+ }
4
23
  /**
5
24
  * Add hook events as mutation operations to the UOW.
6
25
  * This should be called before executeMutations() so hook records are created
7
26
  * in the same transaction as the user's mutations.
27
+ *
28
+ * Each triggered hook carries its own namespace (set at trigger-time), so this
29
+ * function works regardless of which fragment's handler is executing. When a
30
+ * `config` is provided its `defaultRetryPolicy` is used as fallback.
8
31
  */
9
- function prepareHookMutations(uow, config) {
10
- const { namespace, internalFragment, defaultRetryPolicy } = config;
32
+ function prepareHookMutations(uow, internalFragment, defaultRetryPolicy) {
11
33
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
12
34
  const triggeredHooks = uow.getTriggeredHooks();
13
35
  if (triggeredHooks.length === 0) return;
36
+ const primaryNamespace = triggeredHooks[0]?.namespace;
37
+ DurableHooksLogger.debug("Durable hooks queued", {
38
+ namespace: primaryNamespace,
39
+ fields: () => {
40
+ const hookSummary = /* @__PURE__ */ new Map();
41
+ for (const hook of triggeredHooks) {
42
+ const key = `${hook.namespace}:${hook.hookName}`;
43
+ const summary = hookSummary.get(key);
44
+ if (summary) {
45
+ summary.count += 1;
46
+ continue;
47
+ }
48
+ hookSummary.set(key, {
49
+ namespace: hook.namespace,
50
+ hookName: hook.hookName,
51
+ count: 1
52
+ });
53
+ }
54
+ return {
55
+ hooks: Array.from(hookSummary.values()),
56
+ total: triggeredHooks.length
57
+ };
58
+ }
59
+ });
14
60
  const internalSchema = internalFragment.$internal.deps.schema;
15
61
  const internalUow = uow.forSchema(internalSchema);
16
62
  for (const hook of triggeredHooks) {
17
63
  const maxAttempts = (hook.options?.retryPolicy ?? retryPolicy).shouldRetry(4) ? 5 : 1;
18
- internalUow.create("fragno_hooks", {
19
- namespace,
64
+ const rawProcessAt = hook.options?.processAt ?? null;
65
+ const nextRetryAt = (rawProcessAt === null ? null : isDbNow(rawProcessAt) ? rawProcessAt : new Date(rawProcessAt)) ?? null;
66
+ const hookValues = {
67
+ ...hook.options?.id !== void 0 ? { id: hook.options.id } : {},
68
+ namespace: hook.namespace,
20
69
  hookName: hook.hookName,
21
70
  payload: hook.payload,
22
71
  status: "pending",
23
72
  attempts: 0,
24
73
  maxAttempts,
25
74
  lastAttemptAt: null,
26
- nextRetryAt: null,
75
+ nextRetryAt,
27
76
  error: null,
28
77
  nonce: uow.idempotencyKey
29
- });
78
+ };
79
+ internalUow.create("fragno_hooks", hookValues);
30
80
  }
31
81
  }
32
82
  /**
@@ -35,29 +85,129 @@ function prepareHookMutations(uow, config) {
35
85
  */
36
86
  async function processHooks(config) {
37
87
  const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
88
+ if (!hooks) throw new Error("Hook processor hooks not initialized.");
38
89
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
39
- const pendingEvents = await internalFragment.inContext(async function() {
40
- return await this.handlerTx().withServiceCalls(() => [internalFragment.services.hookService.getPendingHookEvents(namespace)]).transform(({ serviceResult: [events] }) => events).execute();
90
+ const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(config.stuckProcessingTimeoutMinutes);
91
+ const internalSchema = internalFragment.$internal.deps.schema;
92
+ const includeStuckProcessing = stuckProcessingTimeoutMinutes !== false;
93
+ const staleBefore = includeStuckProcessing ? dbNow().plus({ minutes: -stuckProcessingTimeoutMinutes }) : null;
94
+ let claimedEvents = [];
95
+ let stuckEvents = [];
96
+ const result = await internalFragment.inContext(async function() {
97
+ return await this.handlerTx().withServiceCalls(() => {
98
+ return [internalFragment.services.hookService.claimPendingHookEvents(namespace), includeStuckProcessing ? internalFragment.services.hookService.claimStuckProcessingHookEvents(namespace, staleBefore) : void 0];
99
+ }).transform(({ serviceResult: [pendingEvents, stuckResult] }) => ({
100
+ pendingEvents,
101
+ stuckResult
102
+ })).execute();
41
103
  });
42
- if (pendingEvents.length === 0) return;
43
- const processedEvents = await Promise.allSettled(pendingEvents.map(async (event) => {
104
+ const pendingCount = result.pendingEvents.length;
105
+ const stuckClaimedCount = result.stuckResult?.events.length ?? 0;
106
+ const stuckRequeuedCount = result.stuckResult?.stuckEvents.length ?? 0;
107
+ const staleBeforeApprox = includeStuckProcessing ? (/* @__PURE__ */ new Date(Date.now() - stuckProcessingTimeoutMinutes * 6e4)).toISOString() : null;
108
+ DurableHooksLogger.debug("Durable hooks claimed", {
109
+ namespace,
110
+ fields: {
111
+ pending: pendingCount,
112
+ stuck: stuckClaimedCount,
113
+ requeued: stuckRequeuedCount,
114
+ includeStuckProcessing,
115
+ staleBeforeApprox
116
+ }
117
+ });
118
+ claimedEvents = [...result.pendingEvents, ...result.stuckResult?.events ?? []].map((event) => ({
119
+ ...event,
120
+ status: assertHookStatus(event.status)
121
+ }));
122
+ stuckEvents = result.stuckResult?.stuckEvents ?? [];
123
+ if (includeStuckProcessing && stuckEvents.length > 0) try {
124
+ config.onStuckProcessingHooks?.({
125
+ namespace,
126
+ timeoutMinutes: stuckProcessingTimeoutMinutes,
127
+ events: stuckEvents
128
+ });
129
+ } catch (error) {
130
+ DurableHooksLogger.error("Error calling onStuckProcessingHooks", {
131
+ namespace,
132
+ fields: { error: DurableHooksLogger.toErrorMessage(error) }
133
+ });
134
+ }
135
+ if (claimedEvents.length === 0) {
136
+ DurableHooksLogger.debug("Durable hooks idle", { namespace });
137
+ return 0;
138
+ }
139
+ const processedEvents = await Promise.allSettled(claimedEvents.map(async (event) => {
44
140
  const hookFn = hooks[event.hookName];
45
- if (!hookFn) return {
46
- eventId: event.id,
47
- status: "failed",
48
- error: `Hook '${event.hookName}' not found in hooks map`,
49
- attempts: event.attempts,
50
- maxAttempts: event.maxAttempts
51
- };
141
+ if (!hookFn) {
142
+ DurableHooksLogger.error("Hook missing", {
143
+ namespace,
144
+ fields: {
145
+ eventId: event.id,
146
+ hookName: event.hookName,
147
+ attempts: event.attempts,
148
+ maxAttempts: event.maxAttempts,
149
+ createdAt: event.createdAt.toISOString()
150
+ }
151
+ });
152
+ return {
153
+ eventId: event.id,
154
+ status: "failed",
155
+ error: `Hook '${event.hookName}' not found in hooks map`,
156
+ attempts: event.attempts,
157
+ maxAttempts: event.maxAttempts
158
+ };
159
+ }
52
160
  try {
53
- const hookContext = { idempotencyKey: event.idempotencyKey };
161
+ const startedAt = Date.now();
162
+ DurableHooksLogger.debug("Hook start", {
163
+ namespace,
164
+ fields: {
165
+ eventId: event.id,
166
+ hookName: event.hookName,
167
+ status: event.status,
168
+ attempts: event.attempts,
169
+ maxAttempts: event.maxAttempts,
170
+ createdAt: event.createdAt.toISOString(),
171
+ nextRetryAt: event.nextRetryAt ? event.nextRetryAt.toISOString() : null,
172
+ lastAttemptAt: event.lastAttemptAt ? event.lastAttemptAt.toISOString() : null
173
+ }
174
+ });
175
+ const hookContext = {
176
+ idempotencyKey: event.idempotencyKey,
177
+ hookId: event.id,
178
+ hookName: event.hookName,
179
+ status: event.status,
180
+ attempts: event.attempts,
181
+ maxAttempts: event.maxAttempts,
182
+ lastAttemptAt: event.lastAttemptAt,
183
+ nextRetryAt: event.nextRetryAt,
184
+ createdAt: event.createdAt,
185
+ handlerTx: config.handlerTx
186
+ };
54
187
  await hookFn.call(hookContext, event.payload);
188
+ DurableHooksLogger.debug("Hook completed", {
189
+ namespace,
190
+ fields: {
191
+ eventId: event.id,
192
+ hookName: event.hookName,
193
+ ms: Date.now() - startedAt
194
+ }
195
+ });
55
196
  return {
56
197
  eventId: event.id,
57
198
  status: "completed"
58
199
  };
59
200
  } catch (error) {
60
201
  const errorMessage = error instanceof Error ? error.message : String(error);
202
+ DurableHooksLogger.error("Hook failed", {
203
+ namespace,
204
+ fields: {
205
+ eventId: event.id.toJSON(),
206
+ hookName: event.hookName,
207
+ error: errorMessage,
208
+ idempotencyKey: event.idempotencyKey
209
+ }
210
+ });
61
211
  return {
62
212
  eventId: event.id,
63
213
  status: "failed",
@@ -68,22 +218,99 @@ async function processHooks(config) {
68
218
  }
69
219
  }));
70
220
  await internalFragment.inContext(async function() {
71
- await this.handlerTx().withServiceCalls(() => {
72
- const txResults = [];
221
+ await this.handlerTx().mutate(({ forSchema }) => {
222
+ const uow = forSchema(internalSchema);
223
+ const now = dbNow();
73
224
  for (const processedEvent of processedEvents) {
74
- if (processedEvent.status === "rejected") continue;
225
+ if (processedEvent.status === "rejected") {
226
+ DurableHooksLogger.error("Hook processing promise rejected", {
227
+ namespace,
228
+ fields: { error: DurableHooksLogger.toErrorMessage(processedEvent.reason) }
229
+ });
230
+ continue;
231
+ }
75
232
  const { eventId, status } = processedEvent.value;
76
- if (status === "completed") txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
77
- else if (status === "failed") {
78
- const { error, attempts } = processedEvent.value;
79
- txResults.push(internalFragment.services.hookService.markHookFailed(eventId, error, attempts, retryPolicy));
233
+ if (status === "completed") {
234
+ uow.update("fragno_hooks", eventId, (b) => b.set({
235
+ status: "completed",
236
+ lastAttemptAt: now
237
+ }).check());
238
+ continue;
80
239
  }
240
+ const { error, attempts } = processedEvent.value;
241
+ const newAttempts = attempts + 1;
242
+ if (retryPolicy.shouldRetry(newAttempts - 1)) {
243
+ const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
244
+ const nextRetryAt = now.plus({ ms: delayMs });
245
+ uow.update("fragno_hooks", eventId, (b) => b.set({
246
+ status: "pending",
247
+ attempts: newAttempts,
248
+ lastAttemptAt: now,
249
+ nextRetryAt,
250
+ error
251
+ }).check());
252
+ } else uow.update("fragno_hooks", eventId, (b) => b.set({
253
+ status: "failed",
254
+ attempts: newAttempts,
255
+ lastAttemptAt: now,
256
+ error
257
+ }).check());
81
258
  }
82
- return txResults;
83
259
  }).execute();
84
260
  });
261
+ const processedCount = processedEvents.reduce((count, result$1) => count + (result$1.status === "fulfilled" ? 1 : 0), 0);
262
+ const failed = processedEvents.filter((result$1) => result$1.status === "fulfilled" && result$1.value.status === "failed").map((result$1) => {
263
+ if (result$1.status !== "fulfilled") return null;
264
+ return {
265
+ eventId: result$1.value.eventId,
266
+ error: result$1.value.error ?? null
267
+ };
268
+ }).filter(Boolean);
269
+ DurableHooksLogger.debug("Durable hooks processed", {
270
+ namespace,
271
+ fields: {
272
+ processed: processedCount,
273
+ failed: failed.length,
274
+ failures: failed.length > 0 ? failed : null
275
+ }
276
+ });
277
+ return processedCount;
278
+ }
279
+ function createDurableHooksRunner(config) {
280
+ let processing = false;
281
+ let queued = false;
282
+ let currentPromise = null;
283
+ const processDue = async () => {
284
+ if (processing) {
285
+ queued = true;
286
+ return currentPromise ?? Promise.resolve(0);
287
+ }
288
+ processing = true;
289
+ currentPromise = (async () => {
290
+ let totalProcessed = 0;
291
+ try {
292
+ do {
293
+ queued = false;
294
+ totalProcessed += await processHooks(config);
295
+ } while (queued);
296
+ return totalProcessed;
297
+ } finally {
298
+ processing = false;
299
+ queued = false;
300
+ currentPromise = null;
301
+ }
302
+ })();
303
+ return currentPromise;
304
+ };
305
+ const drain = async () => {
306
+ while (true) if (await processDue() === 0) return;
307
+ };
308
+ return {
309
+ processDue,
310
+ drain
311
+ };
85
312
  }
86
313
 
87
314
  //#endregion
88
- export { prepareHookMutations, processHooks };
315
+ export { createDurableHooksRunner, isHookStatus, prepareHookMutations };
89
316
  //# sourceMappingURL=hooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","names":["hookContext: HookContext","txResults: TxResult<void>[]"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { ExponentialBackoffRetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport type { IUnitOfWork } from \"../query/unit-of-work/unit-of-work\";\nimport type { InternalFragmentInstance } from \"../fragments/internal-fragment\";\nimport type { TxResult } from \"../query/unit-of-work/execute-unit-of-work\";\n\n/**\n * Context available in hook functions via `this`.\n * Contains the idempotency key for idempotency and database access.\n */\nexport interface HookContext {\n /**\n * Unique idempotency key for this transaction.\n * Use this for idempotency checks in your hook implementation.\n */\n idempotencyKey: string;\n}\n\n/**\n * A hook function signature.\n * Hooks receive a typed payload and access context via `this`.\n */\nexport type HookFn<TPayload = unknown> = (payload: TPayload) => void | Promise<void>;\n\n/**\n * Map of hook names to hook functions.\n * Used for type-safe hook definitions and triggering.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HooksMap = Record<string, HookFn<any>>;\n\n/**\n * Extract the payload type from a hook function.\n */\nexport type HookPayload<T> = T extends HookFn<infer P> ? P : never;\n\n/**\n * Options for triggering a hook.\n */\nexport interface TriggerHookOptions {\n /**\n * Optional retry policy override for this specific hook trigger.\n * If not provided, uses the default retry policy.\n */\n retryPolicy?: RetryPolicy;\n}\n\n/**\n * Internal representation of a triggered hook.\n * Stored in the Unit of Work before execution.\n */\nexport interface TriggeredHook {\n hookName: string;\n payload: unknown;\n options?: TriggerHookOptions;\n}\n\n/**\n * Configuration for hook processing.\n */\nexport interface HookProcessorConfig {\n hooks: HooksMap;\n namespace: string;\n internalFragment: InternalFragmentInstance;\n defaultRetryPolicy?: RetryPolicy;\n}\n\n/**\n * Add hook events as mutation operations to the UOW.\n * This should be called before executeMutations() so hook records are created\n * in the same transaction as the user's mutations.\n */\nexport function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConfig): void {\n const { namespace, internalFragment, defaultRetryPolicy } = config;\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n\n const triggeredHooks = uow.getTriggeredHooks();\n\n if (triggeredHooks.length === 0) {\n return;\n }\n\n const internalSchema = internalFragment.$internal.deps.schema;\n const internalUow = uow.forSchema(internalSchema);\n\n for (const hook of triggeredHooks) {\n const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;\n const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;\n internalUow.create(\"fragno_hooks\", {\n namespace,\n hookName: hook.hookName,\n payload: hook.payload,\n status: \"pending\",\n attempts: 0,\n maxAttempts,\n lastAttemptAt: null,\n nextRetryAt: null,\n error: null,\n nonce: uow.idempotencyKey,\n });\n }\n}\n\n/**\n * Process pending hook events after the transaction has committed.\n * This should be called in the onSuccess callback after executeMutations().\n */\nexport async function processHooks(config: HookProcessorConfig): Promise<void> {\n const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n\n // Get pending events\n const pendingEvents = await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () => [internalFragment.services.hookService.getPendingHookEvents(namespace)] as const,\n )\n .transform(({ serviceResult: [events] }) => events)\n .execute();\n });\n\n if (pendingEvents.length === 0) {\n return;\n }\n\n // Process events (async work outside transaction)\n const processedEvents = await Promise.allSettled(\n pendingEvents.map(async (event) => {\n const hookFn = hooks[event.hookName];\n if (!hookFn) {\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: `Hook '${event.hookName}' not found in hooks map`,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n\n try {\n const hookContext: HookContext = { idempotencyKey: event.idempotencyKey };\n await hookFn.call(hookContext, event.payload);\n return {\n eventId: event.id,\n status: \"completed\" as const,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: errorMessage,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n }),\n );\n\n // Mark events as completed/failed\n await internalFragment.inContext(async function () {\n await this.handlerTx()\n .withServiceCalls(() => {\n const txResults: TxResult<void>[] = [];\n for (const processedEvent of processedEvents) {\n if (processedEvent.status === \"rejected\") {\n continue;\n }\n\n const { eventId, status } = processedEvent.value;\n\n if (status === \"completed\") {\n txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));\n } else if (status === \"failed\") {\n const { error, attempts } = processedEvent.value;\n txResults.push(\n internalFragment.services.hookService.markHookFailed(\n eventId,\n error,\n attempts,\n retryPolicy,\n ),\n );\n }\n }\n return txResults;\n })\n .execute();\n });\n}\n"],"mappings":";;;;;;;;AAwEA,SAAgB,qBAAqB,KAAkB,QAAmC;CACxF,MAAM,EAAE,WAAW,kBAAkB,uBAAuB;CAC5D,MAAM,cAAc,sBAAsB,IAAI,8BAA8B,EAAE,YAAY,GAAG,CAAC;CAE9F,MAAM,iBAAiB,IAAI,mBAAmB;AAE9C,KAAI,eAAe,WAAW,EAC5B;CAGF,MAAM,iBAAiB,iBAAiB,UAAU,KAAK;CACvD,MAAM,cAAc,IAAI,UAAU,eAAe;AAEjD,MAAK,MAAM,QAAQ,gBAAgB;EAEjC,MAAM,eADkB,KAAK,SAAS,eAAe,aACjB,YAAY,EAAE,GAAG,IAAI;AACzD,cAAY,OAAO,gBAAgB;GACjC;GACA,UAAU,KAAK;GACf,SAAS,KAAK;GACd,QAAQ;GACR,UAAU;GACV;GACA,eAAe;GACf,aAAa;GACb,OAAO;GACP,OAAO,IAAI;GACZ,CAAC;;;;;;;AAQN,eAAsB,aAAa,QAA4C;CAC7E,MAAM,EAAE,OAAO,WAAW,kBAAkB,uBAAuB;CACnE,MAAM,cAAc,sBAAsB,IAAI,8BAA8B,EAAE,YAAY,GAAG,CAAC;CAG9F,MAAM,gBAAgB,MAAM,iBAAiB,UAAU,iBAAkB;AACvE,SAAO,MAAM,KAAK,WAAW,CAC1B,uBACO,CAAC,iBAAiB,SAAS,YAAY,qBAAqB,UAAU,CAAC,CAC9E,CACA,WAAW,EAAE,eAAe,CAAC,cAAc,OAAO,CAClD,SAAS;GACZ;AAEF,KAAI,cAAc,WAAW,EAC3B;CAIF,MAAM,kBAAkB,MAAM,QAAQ,WACpC,cAAc,IAAI,OAAO,UAAU;EACjC,MAAM,SAAS,MAAM,MAAM;AAC3B,MAAI,CAAC,OACH,QAAO;GACL,SAAS,MAAM;GACf,QAAQ;GACR,OAAO,SAAS,MAAM,SAAS;GAC/B,UAAU,MAAM;GAChB,aAAa,MAAM;GACpB;AAGH,MAAI;GACF,MAAMA,cAA2B,EAAE,gBAAgB,MAAM,gBAAgB;AACzE,SAAM,OAAO,KAAK,aAAa,MAAM,QAAQ;AAC7C,UAAO;IACL,SAAS,MAAM;IACf,QAAQ;IACT;WACM,OAAO;GACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,UAAO;IACL,SAAS,MAAM;IACf,QAAQ;IACR,OAAO;IACP,UAAU,MAAM;IAChB,aAAa,MAAM;IACpB;;GAEH,CACH;AAGD,OAAM,iBAAiB,UAAU,iBAAkB;AACjD,QAAM,KAAK,WAAW,CACnB,uBAAuB;GACtB,MAAMC,YAA8B,EAAE;AACtC,QAAK,MAAM,kBAAkB,iBAAiB;AAC5C,QAAI,eAAe,WAAW,WAC5B;IAGF,MAAM,EAAE,SAAS,WAAW,eAAe;AAE3C,QAAI,WAAW,YACb,WAAU,KAAK,iBAAiB,SAAS,YAAY,kBAAkB,QAAQ,CAAC;aACvE,WAAW,UAAU;KAC9B,MAAM,EAAE,OAAO,aAAa,eAAe;AAC3C,eAAU,KACR,iBAAiB,SAAS,YAAY,eACpC,SACA,OACA,UACA,YACD,CACF;;;AAGL,UAAO;IACP,CACD,SAAS;GACZ"}
1
+ {"version":3,"file":"hooks.js","names":["claimedEvents: Array<{\n id: FragnoId;\n hookName: string;\n payload: unknown;\n status: HookStatus;\n attempts: number;\n maxAttempts: number;\n idempotencyKey: string;\n lastAttemptAt: Date | null;\n nextRetryAt: Date | null;\n createdAt: Date;\n }>","stuckEvents: StuckHookProcessingEvent[]","hookContext: HookContext","result","currentPromise: Promise<number> | null"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type { InternalFragmentInstance } from \"../fragments/internal-fragment\";\nimport { dbNow, isDbNow, type DbNow } from \"../query/db-now\";\nimport type {\n ExecuteTxOptions,\n HandlerTxBuilder,\n} from \"../query/unit-of-work/execute-unit-of-work\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { ExponentialBackoffRetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport type { IUnitOfWork } from \"../query/unit-of-work/unit-of-work\";\nimport type { FragnoId } from \"../schema/create\";\nimport { DurableHooksLogger, type DurableHooksLoggerConfig } from \"./durable-hooks-logger\";\n\n/**\n * Context available in hook functions via `this`.\n * Contains the idempotency key for idempotency and database access.\n */\nexport interface HookContext {\n /**\n * Unique idempotency key for this transaction.\n * Use this for idempotency checks in your hook implementation.\n */\n idempotencyKey: string;\n /**\n * Hook event identifier (versioned FragnoId).\n */\n hookId: FragnoId;\n /**\n * Hook name for this event.\n */\n hookName: string;\n /**\n * Current status of the hook event.\n */\n status: HookStatus;\n /**\n * Attempt count for this hook event.\n */\n attempts: number;\n /**\n * Maximum attempts configured for this hook event.\n */\n maxAttempts: number;\n /**\n * Timestamp of the last attempt (if any).\n */\n lastAttemptAt: Date | null;\n /**\n * Next scheduled retry timestamp (if any).\n */\n nextRetryAt: Date | null;\n /**\n * When the hook event was created.\n */\n createdAt: Date;\n /**\n * Create a handler transaction builder to run atomic operations.\n */\n handlerTx: HookHandlerTx;\n}\n\nexport const hookStatusValues = [\"pending\", \"processing\", \"completed\", \"failed\"] as const;\n\nexport type HookStatus = (typeof hookStatusValues)[number];\n\nexport const isHookStatus = (value: unknown): value is HookStatus =>\n typeof value === \"string\" && hookStatusValues.includes(value as HookStatus);\n\nconst assertHookStatus = (value: unknown): HookStatus => {\n if (isHookStatus(value)) {\n return value;\n }\n throw new Error(`Invalid hook status: ${String(value)}`);\n};\n\n/**\n * A hook function signature.\n * Hooks receive a typed payload and access context via `this`.\n */\nexport type HookFn<TPayload = unknown> = (payload: TPayload) => void | Promise<void>;\n\n/**\n * Map of hook names to hook functions.\n * Used for type-safe hook definitions and triggering.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HooksMap = Record<string, HookFn<any>>;\n\n/**\n * Extract the payload type from a hook function.\n */\nexport type HookPayload<T> = T extends HookFn<infer P> ? P : never;\n\n/**\n * Options for triggering a hook.\n */\nexport interface TriggerHookOptions {\n /**\n * Optional retry policy override for this specific hook trigger.\n * If not provided, uses the default retry policy.\n */\n retryPolicy?: RetryPolicy;\n /**\n * Optional hook event ID. When provided, the hook insert will fail if a hook\n * with the same ID already exists.\n */\n id?: string | FragnoId;\n /**\n * Absolute time for the first attempt. If in the future, the hook is\n * scheduled for that time; if in the past, it runs immediately.\n */\n processAt?: Date | DbNow;\n}\n\n/**\n * Internal representation of a triggered hook.\n * Stored in the Unit of Work before execution.\n */\nexport interface TriggeredHook {\n namespace: string;\n hookName: string;\n payload: unknown;\n options?: TriggerHookOptions;\n}\n\nexport type HookNotifySource = \"request\" | \"hook\" | \"alarm\";\n\nexport type HookNotifyContext = {\n source: HookNotifySource;\n route?: string | null;\n waitUntil?: (promise: Promise<unknown>) => void;\n};\n\nexport type HookNotifier = {\n notify: (context: HookNotifyContext) => void | Promise<void>;\n};\n\nexport type DurableHooksRunner = {\n processDue: () => Promise<number>;\n drain: () => Promise<void>;\n};\n\n/**\n * @deprecated Use DurableHooksRunner.\n */\nexport type HookScheduler = {\n schedule: () => Promise<number>;\n drain: () => Promise<void>;\n};\n\n/**\n * Configuration for hook processing.\n */\nexport interface HookProcessorConfig<THooks extends HooksMap = HooksMap> {\n hooks?: THooks;\n namespace: string;\n internalFragment: InternalFragmentInstance;\n handlerTx: HookHandlerTx;\n /**\n * Whether post-mutate notifications should auto-schedule processing.\n * Defaults to true.\n */\n autoSchedule?: boolean;\n /**\n * Internal hook runner used to coordinate processing/drain.\n */\n runner?: DurableHooksRunner;\n /**\n * Post-commit durable hooks notifier.\n */\n notifier?: HookNotifier;\n /**\n * @deprecated Use `runner`.\n */\n scheduler?: HookScheduler;\n defaultRetryPolicy?: RetryPolicy;\n /**\n * Re-queue hooks that have been in `processing` for at least this many minutes.\n * Use `false` to disable stuck-processing recovery entirely.\n * Values <= 0 are treated as `false`.\n *\n * Default: 10 minutes.\n */\n stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;\n /**\n * Called when stuck processing hooks are detected and re-queued.\n * Invoked after the hooks are moved back to `pending`.\n */\n onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;\n}\n\nexport type StuckHookProcessingTimeoutMinutes = number | false;\n\nexport type StuckHookProcessingEvent = {\n id: FragnoId;\n hookName: string;\n attempts: number;\n maxAttempts: number;\n lastAttemptAt: Date | null;\n nextRetryAt: Date | null;\n};\n\nexport type StuckHookProcessingInfo = {\n namespace: string;\n timeoutMinutes: number;\n events: StuckHookProcessingEvent[];\n};\n\nexport type DurableHooksProcessingOptions = {\n /**\n * Re-queue hooks that have been in `processing` for at least this many minutes.\n * Use `false` to disable stuck-processing recovery entirely.\n * Values <= 0 are treated as `false`.\n *\n * Default: 10 minutes.\n */\n stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;\n /**\n * Automatically schedule hook processing after successful mutations.\n * Defaults to true. Set false to disable auto scheduling (manual drain only).\n */\n autoSchedule?: boolean;\n /**\n * Called when stuck processing hooks are detected and re-queued.\n * Invoked after the hooks are moved back to `pending`.\n */\n onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;\n /**\n * Durable hooks logging controls.\n * Defaults: enabled=true, level=\"warn\".\n */\n logging?: DurableHooksLoggerConfig;\n};\n\nexport type HookHandlerTx = (\n execOptions?: Omit<ExecuteTxOptions, \"createUnitOfWork\">,\n) => HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, HooksMap>;\n\nconst DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;\n\nfunction resolveStuckProcessingTimeoutMinutes(\n value: StuckHookProcessingTimeoutMinutes | undefined,\n): number | false {\n if (value === false) {\n return false;\n }\n if (typeof value === \"number\") {\n return value > 0 ? value : false;\n }\n return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;\n}\n\n/**\n * Add hook events as mutation operations to the UOW.\n * This should be called before executeMutations() so hook records are created\n * in the same transaction as the user's mutations.\n *\n * Each triggered hook carries its own namespace (set at trigger-time), so this\n * function works regardless of which fragment's handler is executing. When a\n * `config` is provided its `defaultRetryPolicy` is used as fallback.\n */\nexport function prepareHookMutations(\n uow: Pick<IUnitOfWork, \"getTriggeredHooks\" | \"forSchema\" | \"idempotencyKey\">,\n internalFragment: InternalFragmentInstance,\n defaultRetryPolicy?: RetryPolicy,\n): void {\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n\n const triggeredHooks = uow.getTriggeredHooks();\n\n if (triggeredHooks.length === 0) {\n return;\n }\n\n const primaryNamespace = triggeredHooks[0]?.namespace;\n DurableHooksLogger.debug(\"Durable hooks queued\", {\n namespace: primaryNamespace,\n fields: () => {\n const hookSummary = new Map<string, { namespace: string; hookName: string; count: number }>();\n for (const hook of triggeredHooks) {\n const key = `${hook.namespace}:${hook.hookName}`;\n const summary = hookSummary.get(key);\n if (summary) {\n summary.count += 1;\n continue;\n }\n hookSummary.set(key, {\n namespace: hook.namespace,\n hookName: hook.hookName,\n count: 1,\n });\n }\n const hooks = Array.from(hookSummary.values());\n return {\n hooks,\n total: triggeredHooks.length,\n };\n },\n });\n\n const internalSchema = internalFragment.$internal.deps.schema;\n const internalUow = uow.forSchema(internalSchema);\n\n for (const hook of triggeredHooks) {\n const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;\n const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;\n const rawProcessAt = hook.options?.processAt ?? null;\n const processAt: Date | DbNow | null =\n rawProcessAt === null ? null : isDbNow(rawProcessAt) ? rawProcessAt : new Date(rawProcessAt);\n const nextRetryAt = processAt ?? null;\n const hookValues = {\n ...(hook.options?.id !== undefined ? { id: hook.options.id } : {}),\n namespace: hook.namespace,\n hookName: hook.hookName,\n payload: hook.payload,\n status: \"pending\",\n attempts: 0,\n maxAttempts,\n lastAttemptAt: null,\n nextRetryAt,\n error: null,\n nonce: uow.idempotencyKey,\n };\n\n internalUow.create(\"fragno_hooks\", hookValues);\n }\n}\n\n/**\n * Process pending hook events after the transaction has committed.\n * This should be called in the onSuccess callback after executeMutations().\n */\nexport async function processHooks<THooks extends HooksMap>(\n config: HookProcessorConfig<THooks>,\n): Promise<number> {\n const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;\n if (!hooks) {\n throw new Error(\"Hook processor hooks not initialized.\");\n }\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(\n config.stuckProcessingTimeoutMinutes,\n );\n const internalSchema = internalFragment.$internal.deps.schema;\n const includeStuckProcessing = stuckProcessingTimeoutMinutes !== false;\n const staleBefore = includeStuckProcessing\n ? dbNow().plus({ minutes: -stuckProcessingTimeoutMinutes })\n : null;\n let claimedEvents: Array<{\n id: FragnoId;\n hookName: string;\n payload: unknown;\n status: HookStatus;\n attempts: number;\n maxAttempts: number;\n idempotencyKey: string;\n lastAttemptAt: Date | null;\n nextRetryAt: Date | null;\n createdAt: Date;\n }> = [];\n let stuckEvents: StuckHookProcessingEvent[] = [];\n\n const result = await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(() => {\n const pending = internalFragment.services.hookService.claimPendingHookEvents(namespace);\n const stuck = includeStuckProcessing\n ? internalFragment.services.hookService.claimStuckProcessingHookEvents(\n namespace,\n staleBefore!,\n )\n : undefined;\n return [pending, stuck] as const;\n })\n .transform(({ serviceResult: [pendingEvents, stuckResult] }) => ({\n pendingEvents,\n stuckResult,\n }))\n .execute();\n });\n\n const pendingCount = result.pendingEvents.length;\n const stuckClaimedCount = result.stuckResult?.events.length ?? 0;\n const stuckRequeuedCount = result.stuckResult?.stuckEvents.length ?? 0;\n const staleBeforeApprox = includeStuckProcessing\n ? new Date(Date.now() - stuckProcessingTimeoutMinutes * 60_000).toISOString()\n : null;\n DurableHooksLogger.debug(\"Durable hooks claimed\", {\n namespace,\n fields: {\n pending: pendingCount,\n stuck: stuckClaimedCount,\n requeued: stuckRequeuedCount,\n includeStuckProcessing,\n staleBeforeApprox,\n },\n });\n\n claimedEvents = [...result.pendingEvents, ...(result.stuckResult?.events ?? [])].map((event) => ({\n ...event,\n status: assertHookStatus(event.status),\n }));\n stuckEvents = result.stuckResult?.stuckEvents ?? [];\n\n if (includeStuckProcessing && stuckEvents.length > 0) {\n try {\n config.onStuckProcessingHooks?.({\n namespace,\n timeoutMinutes: stuckProcessingTimeoutMinutes,\n events: stuckEvents,\n });\n } catch (error) {\n DurableHooksLogger.error(\"Error calling onStuckProcessingHooks\", {\n namespace,\n fields: {\n error: DurableHooksLogger.toErrorMessage(error),\n },\n });\n }\n }\n\n if (claimedEvents.length === 0) {\n DurableHooksLogger.debug(\"Durable hooks idle\", {\n namespace,\n });\n return 0;\n }\n\n // Process events (async work outside transaction)\n const processedEvents = await Promise.allSettled(\n claimedEvents.map(async (event) => {\n const hookFn = hooks[event.hookName];\n if (!hookFn) {\n DurableHooksLogger.error(\"Hook missing\", {\n namespace,\n fields: {\n eventId: event.id,\n hookName: event.hookName,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n createdAt: event.createdAt.toISOString(),\n },\n });\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: `Hook '${event.hookName}' not found in hooks map`,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n\n try {\n const startedAt = Date.now();\n DurableHooksLogger.debug(\"Hook start\", {\n namespace,\n fields: {\n eventId: event.id,\n hookName: event.hookName,\n status: event.status,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n createdAt: event.createdAt.toISOString(),\n nextRetryAt: event.nextRetryAt ? event.nextRetryAt.toISOString() : null,\n lastAttemptAt: event.lastAttemptAt ? event.lastAttemptAt.toISOString() : null,\n },\n });\n const hookContext: HookContext = {\n idempotencyKey: event.idempotencyKey,\n hookId: event.id,\n hookName: event.hookName,\n status: event.status,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\n handlerTx: config.handlerTx,\n };\n await hookFn.call(hookContext, event.payload);\n DurableHooksLogger.debug(\"Hook completed\", {\n namespace,\n fields: {\n eventId: event.id,\n hookName: event.hookName,\n ms: Date.now() - startedAt,\n },\n });\n return {\n eventId: event.id,\n status: \"completed\" as const,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n DurableHooksLogger.error(\"Hook failed\", {\n namespace,\n fields: {\n eventId: event.id.toJSON(),\n hookName: event.hookName,\n error: errorMessage,\n idempotencyKey: event.idempotencyKey,\n },\n });\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: errorMessage,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n }),\n );\n\n // Mark events as completed/failed\n await internalFragment.inContext(async function () {\n await this.handlerTx()\n .mutate(({ forSchema }) => {\n const uow = forSchema(internalSchema);\n const now = dbNow();\n\n for (const processedEvent of processedEvents) {\n if (processedEvent.status === \"rejected\") {\n DurableHooksLogger.error(\"Hook processing promise rejected\", {\n namespace,\n fields: {\n error: DurableHooksLogger.toErrorMessage(processedEvent.reason),\n },\n });\n continue;\n }\n\n const { eventId, status } = processedEvent.value;\n\n if (status === \"completed\") {\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"completed\", lastAttemptAt: now }).check(),\n );\n continue;\n }\n\n const { error, attempts } = processedEvent.value;\n const newAttempts = attempts + 1;\n const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);\n\n if (shouldRetry) {\n const delayMs = retryPolicy.getDelayMs(newAttempts - 1);\n const nextRetryAt = now.plus({ ms: delayMs });\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"pending\",\n attempts: newAttempts,\n lastAttemptAt: now,\n nextRetryAt,\n error,\n })\n .check(),\n );\n } else {\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"failed\",\n attempts: newAttempts,\n lastAttemptAt: now,\n error,\n })\n .check(),\n );\n }\n }\n })\n .execute();\n });\n\n const processedCount = processedEvents.reduce(\n (count, result) => count + (result.status === \"fulfilled\" ? 1 : 0),\n 0,\n );\n\n const failed = processedEvents\n .filter((result) => result.status === \"fulfilled\" && result.value.status === \"failed\")\n .map((result) => {\n if (result.status !== \"fulfilled\") {\n return null;\n }\n return {\n eventId: result.value.eventId,\n error: result.value.error ?? null,\n };\n })\n .filter(Boolean);\n\n DurableHooksLogger.debug(\"Durable hooks processed\", {\n namespace,\n fields: {\n processed: processedCount,\n failed: failed.length,\n failures: failed.length > 0 ? failed : null,\n },\n });\n\n return processedCount;\n}\n\nexport function createDurableHooksRunner(config: HookProcessorConfig): DurableHooksRunner {\n let processing = false;\n let queued = false;\n let currentPromise: Promise<number> | null = null;\n\n const processDue = async () => {\n if (processing) {\n queued = true;\n return currentPromise ?? Promise.resolve(0);\n }\n\n processing = true;\n currentPromise = (async () => {\n let totalProcessed = 0;\n try {\n do {\n queued = false;\n totalProcessed += await processHooks(config);\n } while (queued);\n return totalProcessed;\n } finally {\n processing = false;\n queued = false;\n currentPromise = null;\n }\n })();\n\n return currentPromise;\n };\n\n const drain = async () => {\n while (true) {\n const processed = await processDue();\n if (processed === 0) {\n return;\n }\n }\n };\n\n return { processDue, drain };\n}\n\n/**\n * @deprecated Use createDurableHooksRunner.\n */\nexport function createHookScheduler(config: HookProcessorConfig): HookScheduler {\n const runner = createDurableHooksRunner(config);\n return {\n schedule: () => runner.processDue(),\n drain: () => runner.drain(),\n };\n}\n"],"mappings":"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"}
package/dist/id.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { createId } from "@paralleldrive/cuid2";
2
- export { createId };
1
+ import { createId, init } from "@fragno-dev/core";
2
+ export { createId, init };
package/dist/id.js CHANGED
@@ -1,3 +1,3 @@
1
- import { createId } from "@paralleldrive/cuid2";
1
+ import { createId, init } from "@fragno-dev/core";
2
2
 
3
- export { createId };
3
+ export { createId, init };
@@ -0,0 +1,11 @@
1
+ import "../sync/types.js";
2
+ import "./outbox-state.js";
3
+ import { InternalFragmentInstance } from "../fragments/internal-fragment.js";
4
+ import { DatabaseAdapter } from "../adapters/adapters.js";
5
+
6
+ //#region src/internal/adapter-registry.d.ts
7
+
8
+ declare const getInternalFragment: <TUOWConfig>(adapter: DatabaseAdapter<TUOWConfig>) => InternalFragmentInstance;
9
+ //#endregion
10
+ export { getInternalFragment };
11
+ //# sourceMappingURL=adapter-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-registry.d.ts","names":[],"sources":["../../src/internal/adapter-registry.ts"],"sourcesContent":[],"mappings":";;;;;;;cAoOa,2CACF,gBAAgB,gBACxB"}
@@ -0,0 +1,135 @@
1
+ import { getOutboxStateForAdapter } from "./outbox-state.js";
2
+ import { SchemaRegistryCollisionError, internalFragmentDef } from "../fragments/internal-fragment.js";
3
+ import { createInternalFragmentDescribeRoutes, createInternalFragmentOutboxRoutes, createInternalFragmentSyncRoutes } from "../fragments/internal-fragment.routes.js";
4
+ import { instantiate } from "@fragno-dev/core";
5
+
6
+ //#region src/internal/adapter-registry.ts
7
+ const registryByAdapter = /* @__PURE__ */ new WeakMap();
8
+ const toAdapterKey = (adapter) => adapter;
9
+ const isDryRun = () => process.env["FRAGNO_INIT_DRY_RUN"] === "true";
10
+ const getNamespaceKey = (schema) => schema.namespace ?? schema.name;
11
+ const getSyncTargetKey = (fragmentName, schemaName) => `${fragmentName}::${schemaName}`;
12
+ const sortSchemas = (schemas) => schemas.sort((a, b) => {
13
+ const namespaceA = a.namespace ?? "";
14
+ const namespaceB = b.namespace ?? "";
15
+ if (namespaceA !== namespaceB) return namespaceA.localeCompare(namespaceB);
16
+ if (a.name !== b.name) return a.name.localeCompare(b.name);
17
+ return a.version - b.version;
18
+ });
19
+ const sortFragments = (fragments) => fragments.sort((a, b) => a.name.localeCompare(b.name));
20
+ const buildInternalFragment = (adapter, registry) => {
21
+ const routes = [
22
+ createInternalFragmentDescribeRoutes(),
23
+ createInternalFragmentOutboxRoutes(),
24
+ createInternalFragmentSyncRoutes()
25
+ ];
26
+ return instantiate(internalFragmentDef).withConfig({ registry }).withOptions({
27
+ databaseAdapter: adapter,
28
+ databaseNamespace: null
29
+ }).withRoutes(routes).build();
30
+ };
31
+ const createRegistry = (adapter) => {
32
+ const schemas = /* @__PURE__ */ new Map();
33
+ const fragments = /* @__PURE__ */ new Map();
34
+ const outboxState = getOutboxStateForAdapter(adapter);
35
+ const syncCommandTargets = /* @__PURE__ */ new Map();
36
+ let registry;
37
+ registry = {
38
+ internalFragment: void 0,
39
+ schemas,
40
+ fragments,
41
+ outboxState,
42
+ syncCommandTargets,
43
+ registerSchema: (schema, fragment, options) => {
44
+ const namespaceKey = getNamespaceKey(schema);
45
+ const existing = schemas.get(namespaceKey);
46
+ if (existing && (existing.name !== schema.name || existing.namespace !== schema.namespace)) throw new SchemaRegistryCollisionError({
47
+ namespaceKey,
48
+ existing: {
49
+ name: existing.name,
50
+ namespace: existing.namespace
51
+ },
52
+ attempted: {
53
+ name: schema.name,
54
+ namespace: schema.namespace
55
+ }
56
+ });
57
+ const schemaCopy = {
58
+ ...schema,
59
+ tables: [...schema.tables]
60
+ };
61
+ if (existing && existing.name === schemaCopy.name && existing.namespace === schemaCopy.namespace && existing.version === schemaCopy.version && existing.tables.length === schemaCopy.tables.length && existing.tables.every((table, index) => table === schemaCopy.tables[index])) {
62
+ fragments.set(fragment.name, { ...fragment });
63
+ if (options?.outboxEnabled) {
64
+ outboxState.enabledSchemaKeys.add(namespaceKey);
65
+ outboxState.enabledFragments.add(fragment.name);
66
+ outboxState.config.enabled = true;
67
+ }
68
+ return;
69
+ }
70
+ schemas.set(namespaceKey, schemaCopy);
71
+ fragments.set(fragment.name, { ...fragment });
72
+ if (options?.outboxEnabled) {
73
+ outboxState.enabledSchemaKeys.add(namespaceKey);
74
+ outboxState.enabledFragments.add(fragment.name);
75
+ outboxState.config.enabled = true;
76
+ }
77
+ },
78
+ registerSyncCommands: (registration) => {
79
+ const key = getSyncTargetKey(registration.fragmentName, registration.schemaName);
80
+ const existing = syncCommandTargets.get(key);
81
+ if (existing) {
82
+ const commandsMatch = existing.commands.size === registration.commands.size && [...existing.commands.entries()].every(([name, command]) => registration.commands.get(name) === command);
83
+ if (existing.namespace !== registration.namespace || existing.fragmentName !== registration.fragmentName || existing.schemaName !== registration.schemaName || !commandsMatch) throw new Error(`Sync commands for ${registration.fragmentName}/${registration.schemaName} are already registered.`);
84
+ return;
85
+ }
86
+ syncCommandTargets.set(key, {
87
+ fragmentName: registration.fragmentName,
88
+ schemaName: registration.schemaName,
89
+ namespace: registration.namespace,
90
+ commands: new Map(registration.commands)
91
+ });
92
+ },
93
+ resolveSyncTarget: (fragmentName, schemaName) => {
94
+ const key = getSyncTargetKey(fragmentName, schemaName);
95
+ return syncCommandTargets.get(key);
96
+ },
97
+ resolveSyncCommand: (fragmentName, schemaName, commandName) => {
98
+ const target = registry.resolveSyncTarget(fragmentName, schemaName);
99
+ if (!target) return;
100
+ const command = target.commands.get(commandName);
101
+ if (!command) return;
102
+ return {
103
+ command,
104
+ namespace: target.namespace
105
+ };
106
+ },
107
+ listSchemas: () => sortSchemas([...schemas.values()]),
108
+ listFragments: () => sortFragments([...fragments.values()]),
109
+ listOutboxFragments: () => sortFragments([...fragments.values()].filter((fragment) => outboxState.enabledFragments.has(fragment.name))),
110
+ isOutboxEnabled: () => outboxState.config.enabled
111
+ };
112
+ registry.internalFragment = buildInternalFragment(adapter, registry);
113
+ return registry;
114
+ };
115
+ const getRegistryForAdapterSync = (adapter) => {
116
+ const adapterKey = toAdapterKey(adapter);
117
+ const existing = registryByAdapter.get(adapterKey);
118
+ if (existing) return existing;
119
+ if (isDryRun()) throw new Error("Adapter registry is unavailable during dry-run initialization.");
120
+ const registry = createRegistry(adapterKey);
121
+ registryByAdapter.set(adapterKey, registry);
122
+ return registry;
123
+ };
124
+ const getInternalFragment = (adapter) => {
125
+ const adapterKey = toAdapterKey(adapter);
126
+ if (isDryRun()) return instantiate(internalFragmentDef).withOptions({
127
+ databaseAdapter: adapterKey,
128
+ databaseNamespace: null
129
+ }).build();
130
+ return getRegistryForAdapterSync(adapterKey).internalFragment;
131
+ };
132
+
133
+ //#endregion
134
+ export { getInternalFragment, getRegistryForAdapterSync };
135
+ //# sourceMappingURL=adapter-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-registry.js","names":["registry: AdapterRegistry"],"sources":["../../src/internal/adapter-registry.ts"],"sourcesContent":["import { instantiate } from \"@fragno-dev/core\";\n\nimport type { DatabaseAdapter } from \"../adapters/adapters\";\nimport {\n SchemaRegistryCollisionError,\n internalFragmentDef,\n type InternalFragmentInstance,\n} from \"../fragments/internal-fragment\";\nimport {\n createInternalFragmentDescribeRoutes,\n createInternalFragmentOutboxRoutes,\n createInternalFragmentSyncRoutes,\n} from \"../fragments/internal-fragment.routes\";\nimport type { SyncCommandDefinition, SyncCommandTargetRegistration } from \"../sync/types\";\nimport { getOutboxStateForAdapter, type OutboxState } from \"./outbox-state\";\n\nexport type SchemaInfo = {\n name: string;\n namespace: string | null;\n version: number;\n tables: string[];\n};\n\nexport type FragmentMeta = {\n name: string;\n mountRoute: string;\n};\n\nexport type SyncCommandTarget = {\n fragmentName: string;\n schemaName: string;\n namespace: string | null;\n commands: Map<string, SyncCommandDefinition>;\n};\n\nexport type AdapterRegistry = {\n internalFragment: InternalFragmentInstance;\n schemas: Map<string, SchemaInfo>;\n fragments: Map<string, FragmentMeta>;\n outboxState: OutboxState;\n syncCommandTargets: Map<string, SyncCommandTarget>;\n registerSchema: (\n schema: SchemaInfo,\n fragment: FragmentMeta,\n options?: { outboxEnabled?: boolean },\n ) => void;\n registerSyncCommands: (registration: SyncCommandTargetRegistration) => void;\n resolveSyncTarget: (fragmentName: string, schemaName: string) => SyncCommandTarget | undefined;\n resolveSyncCommand: (\n fragmentName: string,\n schemaName: string,\n commandName: string,\n ) => { command: SyncCommandDefinition; namespace: string | null } | undefined;\n listSchemas: () => SchemaInfo[];\n listFragments: () => FragmentMeta[];\n listOutboxFragments: () => FragmentMeta[];\n isOutboxEnabled: () => boolean;\n};\n\ntype AdapterKey = DatabaseAdapter<unknown>;\n\nconst registryByAdapter = new WeakMap<AdapterKey, AdapterRegistry>();\n\nconst toAdapterKey = <TUOWConfig>(adapter: DatabaseAdapter<TUOWConfig>): AdapterKey =>\n adapter as AdapterKey;\n\nconst isDryRun = (): boolean => process.env[\"FRAGNO_INIT_DRY_RUN\"] === \"true\";\n\nconst getNamespaceKey = (schema: SchemaInfo): string => schema.namespace ?? schema.name;\nconst getSyncTargetKey = (fragmentName: string, schemaName: string): string =>\n `${fragmentName}::${schemaName}`;\n\nconst sortSchemas = (schemas: SchemaInfo[]): SchemaInfo[] =>\n schemas.sort((a, b) => {\n const namespaceA = a.namespace ?? \"\";\n const namespaceB = b.namespace ?? \"\";\n if (namespaceA !== namespaceB) {\n return namespaceA.localeCompare(namespaceB);\n }\n if (a.name !== b.name) {\n return a.name.localeCompare(b.name);\n }\n return a.version - b.version;\n });\n\nconst sortFragments = (fragments: FragmentMeta[]): FragmentMeta[] =>\n fragments.sort((a, b) => a.name.localeCompare(b.name));\n\nconst buildInternalFragment = (\n adapter: DatabaseAdapter<unknown>,\n registry: AdapterRegistry,\n): InternalFragmentInstance => {\n const routes = [\n createInternalFragmentDescribeRoutes(),\n createInternalFragmentOutboxRoutes(),\n createInternalFragmentSyncRoutes(),\n ];\n\n return instantiate(internalFragmentDef)\n .withConfig({ registry })\n .withOptions({ databaseAdapter: adapter, databaseNamespace: null })\n .withRoutes(routes)\n .build();\n};\n\nconst createRegistry = (adapter: DatabaseAdapter<unknown>): AdapterRegistry => {\n const schemas = new Map<string, SchemaInfo>();\n const fragments = new Map<string, FragmentMeta>();\n const outboxState = getOutboxStateForAdapter(adapter);\n const syncCommandTargets = new Map<string, SyncCommandTarget>();\n let registry: AdapterRegistry;\n\n registry = {\n internalFragment: undefined as unknown as InternalFragmentInstance,\n schemas,\n fragments,\n outboxState,\n syncCommandTargets,\n registerSchema: (schema, fragment, options) => {\n const namespaceKey = getNamespaceKey(schema);\n const existing = schemas.get(namespaceKey);\n if (existing && (existing.name !== schema.name || existing.namespace !== schema.namespace)) {\n throw new SchemaRegistryCollisionError({\n namespaceKey,\n existing: { name: existing.name, namespace: existing.namespace },\n attempted: { name: schema.name, namespace: schema.namespace },\n });\n }\n const schemaCopy = { ...schema, tables: [...schema.tables] };\n if (\n existing &&\n existing.name === schemaCopy.name &&\n existing.namespace === schemaCopy.namespace &&\n existing.version === schemaCopy.version &&\n existing.tables.length === schemaCopy.tables.length &&\n existing.tables.every((table, index) => table === schemaCopy.tables[index])\n ) {\n fragments.set(fragment.name, { ...fragment });\n if (options?.outboxEnabled) {\n outboxState.enabledSchemaKeys.add(namespaceKey);\n outboxState.enabledFragments.add(fragment.name);\n outboxState.config.enabled = true;\n }\n return;\n }\n schemas.set(namespaceKey, schemaCopy);\n fragments.set(fragment.name, { ...fragment });\n if (options?.outboxEnabled) {\n outboxState.enabledSchemaKeys.add(namespaceKey);\n outboxState.enabledFragments.add(fragment.name);\n outboxState.config.enabled = true;\n }\n },\n registerSyncCommands: (registration) => {\n const key = getSyncTargetKey(registration.fragmentName, registration.schemaName);\n const existing = syncCommandTargets.get(key);\n if (existing) {\n const commandsMatch =\n existing.commands.size === registration.commands.size &&\n [...existing.commands.entries()].every(\n ([name, command]) => registration.commands.get(name) === command,\n );\n if (\n existing.namespace !== registration.namespace ||\n existing.fragmentName !== registration.fragmentName ||\n existing.schemaName !== registration.schemaName ||\n !commandsMatch\n ) {\n throw new Error(\n `Sync commands for ${registration.fragmentName}/${registration.schemaName} are already registered.`,\n );\n }\n return;\n }\n syncCommandTargets.set(key, {\n fragmentName: registration.fragmentName,\n schemaName: registration.schemaName,\n namespace: registration.namespace,\n commands: new Map(registration.commands),\n });\n },\n resolveSyncTarget: (fragmentName, schemaName) => {\n const key = getSyncTargetKey(fragmentName, schemaName);\n return syncCommandTargets.get(key);\n },\n resolveSyncCommand: (fragmentName, schemaName, commandName) => {\n const target = registry.resolveSyncTarget(fragmentName, schemaName);\n if (!target) {\n return undefined;\n }\n const command = target.commands.get(commandName);\n if (!command) {\n return undefined;\n }\n return { command, namespace: target.namespace };\n },\n listSchemas: () => sortSchemas([...schemas.values()]),\n listFragments: () => sortFragments([...fragments.values()]),\n listOutboxFragments: () =>\n sortFragments(\n [...fragments.values()].filter((fragment) =>\n outboxState.enabledFragments.has(fragment.name),\n ),\n ),\n isOutboxEnabled: () => outboxState.config.enabled,\n };\n\n registry.internalFragment = buildInternalFragment(adapter, registry);\n return registry;\n};\n\nexport const getRegistryForAdapterSync = <TUOWConfig>(\n adapter: DatabaseAdapter<TUOWConfig>,\n): AdapterRegistry => {\n const adapterKey = toAdapterKey(adapter);\n const existing = registryByAdapter.get(adapterKey);\n if (existing) {\n return existing;\n }\n if (isDryRun()) {\n throw new Error(\"Adapter registry is unavailable during dry-run initialization.\");\n }\n\n const registry = createRegistry(adapterKey);\n registryByAdapter.set(adapterKey, registry);\n return registry;\n};\n\nexport const getInternalFragment = <TUOWConfig>(\n adapter: DatabaseAdapter<TUOWConfig>,\n): InternalFragmentInstance => {\n const adapterKey = toAdapterKey(adapter);\n if (isDryRun()) {\n return instantiate(internalFragmentDef)\n .withOptions({ databaseAdapter: adapterKey, databaseNamespace: null })\n .build();\n }\n return getRegistryForAdapterSync(adapterKey).internalFragment;\n};\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import "../outbox/outbox.js";
2
+ import "../adapters/adapters.js";
@@ -0,0 +1,26 @@
1
+ //#region src/internal/outbox-state.ts
2
+ const outboxStateByAdapter = /* @__PURE__ */ new WeakMap();
3
+ const getOperationNamespaceKey = (operation) => operation.namespace ?? operation.schema.name;
4
+ const createOutboxState = () => {
5
+ const enabledSchemaKeys = /* @__PURE__ */ new Set();
6
+ return {
7
+ config: {
8
+ enabled: false,
9
+ shouldInclude: (operation) => enabledSchemaKeys.has(getOperationNamespaceKey(operation))
10
+ },
11
+ enabledSchemaKeys,
12
+ enabledFragments: /* @__PURE__ */ new Set()
13
+ };
14
+ };
15
+ const getOutboxStateForAdapter = (adapter) => {
16
+ const existing = outboxStateByAdapter.get(adapter);
17
+ if (existing) return existing;
18
+ const state = createOutboxState();
19
+ outboxStateByAdapter.set(adapter, state);
20
+ return state;
21
+ };
22
+ const getOutboxConfigForAdapter = (adapter) => getOutboxStateForAdapter(adapter).config;
23
+
24
+ //#endregion
25
+ export { getOutboxConfigForAdapter, getOutboxStateForAdapter };
26
+ //# sourceMappingURL=outbox-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-state.js","names":[],"sources":["../../src/internal/outbox-state.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"../adapters/adapters\";\nimport type { OutboxConfig } from \"../outbox/outbox\";\nimport type { MutationOperation } from \"../query/unit-of-work/unit-of-work\";\nimport type { AnySchema } from \"../schema/create\";\n\nexport type OutboxState = {\n config: OutboxConfig;\n enabledSchemaKeys: Set<string>;\n enabledFragments: Set<string>;\n};\n\ntype AdapterKey = DatabaseAdapter<unknown>;\n\nconst outboxStateByAdapter = new WeakMap<AdapterKey, OutboxState>();\n\nconst getOperationNamespaceKey = (operation: MutationOperation<AnySchema>): string =>\n operation.namespace ?? operation.schema.name;\n\nconst createOutboxState = (): OutboxState => {\n const enabledSchemaKeys = new Set<string>();\n const enabledFragments = new Set<string>();\n const config: OutboxConfig = {\n enabled: false,\n shouldInclude: (operation) => enabledSchemaKeys.has(getOperationNamespaceKey(operation)),\n };\n\n return { config, enabledSchemaKeys, enabledFragments };\n};\n\nexport const getOutboxStateForAdapter = (adapter: AdapterKey): OutboxState => {\n const existing = outboxStateByAdapter.get(adapter);\n if (existing) {\n return existing;\n }\n\n const state = createOutboxState();\n outboxStateByAdapter.set(adapter, state);\n return state;\n};\n\nexport const getOutboxConfigForAdapter = <TUOWConfig>(\n adapter: DatabaseAdapter<TUOWConfig>,\n): OutboxConfig => getOutboxStateForAdapter(adapter as AdapterKey).config;\n"],"mappings":";AAaA,MAAM,uCAAuB,IAAI,SAAkC;AAEnE,MAAM,4BAA4B,cAChC,UAAU,aAAa,UAAU,OAAO;AAE1C,MAAM,0BAAuC;CAC3C,MAAM,oCAAoB,IAAI,KAAa;AAO3C,QAAO;EAAE,QALoB;GAC3B,SAAS;GACT,gBAAgB,cAAc,kBAAkB,IAAI,yBAAyB,UAAU,CAAC;GACzF;EAEgB;EAAmB,kCANX,IAAI,KAAa;EAMY;;AAGxD,MAAa,4BAA4B,YAAqC;CAC5E,MAAM,WAAW,qBAAqB,IAAI,QAAQ;AAClD,KAAI,SACF,QAAO;CAGT,MAAM,QAAQ,mBAAmB;AACjC,sBAAqB,IAAI,SAAS,MAAM;AACxC,QAAO;;AAGT,MAAa,6BACX,YACiB,yBAAyB,QAAsB,CAAC"}