@fragno-dev/db 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. package/.turbo/turbo-build.log +327 -160
  2. package/CHANGELOG.md +74 -0
  3. package/README.md +24 -0
  4. package/dist/adapters/adapters.d.ts +1 -1
  5. package/dist/adapters/adapters.d.ts.map +1 -1
  6. package/dist/adapters/adapters.js.map +1 -1
  7. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +0 -3
  8. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
  9. package/dist/adapters/generic-sql/generic-sql-adapter.js +11 -12
  10. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
  11. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +46 -6
  12. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
  13. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
  14. package/dist/adapters/generic-sql/migration/dialect/mysql.js +1 -1
  15. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
  16. package/dist/adapters/generic-sql/migration/dialect/postgres.js +1 -1
  17. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
  18. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +185 -19
  19. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
  20. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
  21. package/dist/adapters/generic-sql/migration/executor.js +30 -3
  22. package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
  23. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
  24. package/dist/adapters/generic-sql/migration/prepared-migrations.js +3 -3
  25. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
  26. package/dist/adapters/generic-sql/migration/sql-generator.js +1 -1
  27. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
  28. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +1 -1
  29. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
  30. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
  31. package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
  32. package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
  33. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +9 -6
  34. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
  35. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
  36. package/dist/adapters/generic-sql/query/sql-query-compiler.js +37 -9
  37. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
  38. package/dist/adapters/generic-sql/query/where-builder.js +24 -20
  39. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
  40. package/dist/adapters/generic-sql/uow-decoder.js +1 -1
  41. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
  42. package/dist/adapters/generic-sql/uow-encoder.js +8 -9
  43. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
  44. package/dist/adapters/in-memory/condition-evaluator.js +10 -6
  45. package/dist/adapters/in-memory/condition-evaluator.js.map +1 -1
  46. package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -1
  47. package/dist/adapters/in-memory/in-memory-adapter.js +45 -25
  48. package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -1
  49. package/dist/adapters/in-memory/in-memory-uow.js +236 -13
  50. package/dist/adapters/in-memory/in-memory-uow.js.map +1 -1
  51. package/dist/adapters/in-memory/options.d.ts +2 -0
  52. package/dist/adapters/in-memory/options.d.ts.map +1 -1
  53. package/dist/adapters/in-memory/options.js +3 -2
  54. package/dist/adapters/in-memory/options.js.map +1 -1
  55. package/dist/adapters/in-memory/reference-resolution.js.map +1 -1
  56. package/dist/adapters/in-memory/store.js +1 -1
  57. package/dist/adapters/in-memory/store.js.map +1 -1
  58. package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
  59. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
  60. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
  61. package/dist/browser/adapters/adapters.d.ts +61 -0
  62. package/dist/browser/adapters/adapters.d.ts.map +1 -0
  63. package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
  64. package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  65. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  66. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  67. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
  68. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  69. package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
  70. package/dist/browser/adapters/in-memory/index.d.ts +2 -0
  71. package/dist/browser/adapters/in-memory/options.d.ts +1 -0
  72. package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
  73. package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
  74. package/dist/browser/durable-hooks.d.ts +3 -0
  75. package/dist/browser/fragments/internal-fragment.d.ts +317 -0
  76. package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
  77. package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
  78. package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
  79. package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
  80. package/dist/browser/hooks/hooks.d.ts +146 -0
  81. package/dist/browser/hooks/hooks.d.ts.map +1 -0
  82. package/dist/browser/id.js +1 -0
  83. package/dist/browser/internal/adapter-registry.d.ts +4 -0
  84. package/dist/browser/internal/outbox-state.d.ts +2 -0
  85. package/dist/browser/mod.d.ts +15 -0
  86. package/dist/browser/mod.d.ts.map +1 -0
  87. package/dist/browser/mod.js +17 -0
  88. package/dist/browser/mod.js.map +1 -0
  89. package/dist/browser/mod2.d.ts +48 -0
  90. package/dist/browser/mod2.d.ts.map +1 -0
  91. package/dist/browser/naming/sql-naming.d.ts +19 -0
  92. package/dist/browser/naming/sql-naming.d.ts.map +1 -0
  93. package/dist/browser/outbox/outbox.d.ts +21 -0
  94. package/dist/browser/outbox/outbox.d.ts.map +1 -0
  95. package/dist/browser/query/column-defaults.js +1 -0
  96. package/dist/browser/query/condition-builder.d.ts +44 -0
  97. package/dist/browser/query/condition-builder.d.ts.map +1 -0
  98. package/dist/browser/query/condition-builder.js +97 -0
  99. package/dist/browser/query/condition-builder.js.map +1 -0
  100. package/dist/browser/query/cursor.d.ts +105 -0
  101. package/dist/browser/query/cursor.d.ts.map +1 -0
  102. package/dist/browser/query/cursor.js +150 -0
  103. package/dist/browser/query/cursor.js.map +1 -0
  104. package/dist/browser/query/db-now.d.ts +22 -0
  105. package/dist/browser/query/db-now.d.ts.map +1 -0
  106. package/dist/browser/query/db-now.js +33 -0
  107. package/dist/browser/query/db-now.js.map +1 -0
  108. package/dist/browser/query/orm/orm.d.ts +18 -0
  109. package/dist/browser/query/orm/orm.d.ts.map +1 -0
  110. package/dist/browser/query/simple-query-interface.d.ts +108 -0
  111. package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
  112. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
  113. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  114. package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
  115. package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  116. package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
  117. package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
  118. package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
  119. package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
  120. package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
  121. package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  122. package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
  123. package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
  124. package/dist/browser/query/value-encoding.js +38 -0
  125. package/dist/browser/query/value-encoding.js.map +1 -0
  126. package/dist/browser/schema/create.d.ts +326 -0
  127. package/dist/browser/schema/create.d.ts.map +1 -0
  128. package/dist/browser/schema/create.js +89 -0
  129. package/dist/browser/schema/create.js.map +1 -0
  130. package/dist/browser/schema/generate-id.js +28 -0
  131. package/dist/browser/schema/generate-id.js.map +1 -0
  132. package/dist/browser/shared/providers.d.ts +6 -0
  133. package/dist/browser/shared/providers.d.ts.map +1 -0
  134. package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
  135. package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
  136. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  137. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  138. package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
  139. package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  140. package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
  141. package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
  142. package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
  143. package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  144. package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
  145. package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  146. package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
  147. package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
  148. package/dist/browser/sync/commands.d.ts +15 -0
  149. package/dist/browser/sync/commands.d.ts.map +1 -0
  150. package/dist/browser/sync/commands.js +27 -0
  151. package/dist/browser/sync/commands.js.map +1 -0
  152. package/dist/browser/sync/types.d.ts +63 -0
  153. package/dist/browser/sync/types.d.ts.map +1 -0
  154. package/dist/browser/util/types.d.ts +8 -0
  155. package/dist/browser/util/types.d.ts.map +1 -0
  156. package/dist/browser/with-database.d.ts +29 -0
  157. package/dist/browser/with-database.d.ts.map +1 -0
  158. package/dist/client.d.ts +4 -0
  159. package/dist/client.js +5 -0
  160. package/dist/db-fragment-definition-builder.d.ts +85 -28
  161. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  162. package/dist/db-fragment-definition-builder.js +374 -46
  163. package/dist/db-fragment-definition-builder.js.map +1 -1
  164. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
  165. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
  166. package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
  167. package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
  168. package/dist/dispatchers/cloudflare-do/index.d.ts +5 -20
  169. package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -1
  170. package/dist/dispatchers/cloudflare-do/index.js +23 -55
  171. package/dist/dispatchers/cloudflare-do/index.js.map +1 -1
  172. package/dist/dispatchers/node/dispatcher.d.ts +14 -0
  173. package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
  174. package/dist/dispatchers/node/dispatcher.js +80 -0
  175. package/dist/dispatchers/node/dispatcher.js.map +1 -0
  176. package/dist/dispatchers/node/index.d.ts +5 -10
  177. package/dist/dispatchers/node/index.d.ts.map +1 -1
  178. package/dist/dispatchers/node/index.js +21 -53
  179. package/dist/dispatchers/node/index.js.map +1 -1
  180. package/dist/durable-hooks.d.ts +31 -0
  181. package/dist/durable-hooks.d.ts.map +1 -0
  182. package/dist/durable-hooks.js +23 -0
  183. package/dist/durable-hooks.js.map +1 -0
  184. package/dist/fragments/internal-fragment.d.ts +128 -27
  185. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  186. package/dist/fragments/internal-fragment.js +125 -78
  187. package/dist/fragments/internal-fragment.js.map +1 -1
  188. package/dist/fragments/internal-fragment.routes.js +138 -3
  189. package/dist/fragments/internal-fragment.routes.js.map +1 -1
  190. package/dist/fragments/internal-fragment.schema.d.ts +7 -1
  191. package/dist/fragments/internal-fragment.schema.d.ts.map +1 -1
  192. package/dist/fragments/internal-fragment.schema.js +18 -1
  193. package/dist/fragments/internal-fragment.schema.js.map +1 -1
  194. package/dist/hooks/durable-hooks-logger.d.ts +10 -0
  195. package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
  196. package/dist/hooks/durable-hooks-logger.js +75 -0
  197. package/dist/hooks/durable-hooks-logger.js.map +1 -0
  198. package/dist/hooks/durable-hooks-processor.d.ts +1 -14
  199. package/dist/hooks/durable-hooks-processor.js +58 -10
  200. package/dist/hooks/durable-hooks-processor.js.map +1 -1
  201. package/dist/hooks/durable-hooks-runtime.js +44 -0
  202. package/dist/hooks/durable-hooks-runtime.js.map +1 -0
  203. package/dist/hooks/hooks.d.ts +60 -2
  204. package/dist/hooks/hooks.d.ts.map +1 -1
  205. package/dist/hooks/hooks.js +214 -53
  206. package/dist/hooks/hooks.js.map +1 -1
  207. package/dist/id.d.ts +2 -2
  208. package/dist/id.js +2 -2
  209. package/dist/internal/adapter-registry.d.ts +11 -0
  210. package/dist/internal/adapter-registry.d.ts.map +1 -0
  211. package/dist/internal/adapter-registry.js +135 -0
  212. package/dist/internal/adapter-registry.js.map +1 -0
  213. package/dist/internal/outbox-state.d.ts +2 -0
  214. package/dist/internal/outbox-state.js +26 -0
  215. package/dist/internal/outbox-state.js.map +1 -0
  216. package/dist/migration-engine/auto-from-schema.d.ts +33 -0
  217. package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
  218. package/dist/migration-engine/auto-from-schema.js +210 -27
  219. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  220. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  221. package/dist/migration-engine/generation-engine.js +17 -5
  222. package/dist/migration-engine/generation-engine.js.map +1 -1
  223. package/dist/migration-engine/shared.d.ts +113 -0
  224. package/dist/migration-engine/shared.d.ts.map +1 -0
  225. package/dist/migration-engine/shared.js.map +1 -1
  226. package/dist/mod.d.ts +12 -11
  227. package/dist/mod.d.ts.map +1 -1
  228. package/dist/mod.js +10 -10
  229. package/dist/mod.js.map +1 -1
  230. package/dist/naming/sql-naming.d.ts.map +1 -1
  231. package/dist/naming/sql-naming.js.map +1 -1
  232. package/dist/outbox/outbox-builder.js.map +1 -1
  233. package/dist/outbox/outbox.d.ts +3 -1
  234. package/dist/outbox/outbox.d.ts.map +1 -1
  235. package/dist/outbox/outbox.js.map +1 -1
  236. package/dist/query/column-defaults.js.map +1 -1
  237. package/dist/query/condition-builder.d.ts +7 -1
  238. package/dist/query/condition-builder.d.ts.map +1 -1
  239. package/dist/query/condition-builder.js +5 -1
  240. package/dist/query/condition-builder.js.map +1 -1
  241. package/dist/query/cursor-client.d.ts +105 -0
  242. package/dist/query/cursor-client.d.ts.map +1 -0
  243. package/dist/query/cursor-client.js +165 -0
  244. package/dist/query/cursor-client.js.map +1 -0
  245. package/dist/query/cursor.d.ts.map +1 -1
  246. package/dist/query/cursor.js +7 -1
  247. package/dist/query/cursor.js.map +1 -1
  248. package/dist/query/db-now.d.ts +15 -1
  249. package/dist/query/db-now.d.ts.map +1 -1
  250. package/dist/query/db-now.js +30 -2
  251. package/dist/query/db-now.js.map +1 -1
  252. package/dist/query/orm/orm.js.map +1 -1
  253. package/dist/query/serialize/create-sql-serializer.js +2 -2
  254. package/dist/query/serialize/create-sql-serializer.js.map +1 -1
  255. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
  256. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
  257. package/dist/query/serialize/dialect/sqlite-serializer.js +6 -2
  258. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
  259. package/dist/query/simple-query-interface.d.ts +7 -3
  260. package/dist/query/simple-query-interface.d.ts.map +1 -1
  261. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
  262. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  263. package/dist/query/unit-of-work/execute-unit-of-work.js +39 -18
  264. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  265. package/dist/query/unit-of-work/unit-of-work.d.ts +42 -16
  266. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  267. package/dist/query/unit-of-work/unit-of-work.js +50 -6
  268. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  269. package/dist/query/value-decoding.js +8 -1
  270. package/dist/query/value-decoding.js.map +1 -1
  271. package/dist/query/value-encoding.js.map +1 -1
  272. package/dist/schema/create.d.ts +69 -25
  273. package/dist/schema/create.d.ts.map +1 -1
  274. package/dist/schema/create.js +91 -16
  275. package/dist/schema/create.js.map +1 -1
  276. package/dist/schema/type-conversion/create-sql-type-mapper.js +1 -1
  277. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  278. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  279. package/dist/schema/validator.d.ts.map +1 -1
  280. package/dist/schema/validator.js.map +1 -1
  281. package/dist/schema-output/drizzle.d.ts.map +1 -1
  282. package/dist/schema-output/drizzle.js +8 -6
  283. package/dist/schema-output/drizzle.js.map +1 -1
  284. package/dist/schema-output/prisma.d.ts.map +1 -1
  285. package/dist/schema-output/prisma.js +21 -10
  286. package/dist/schema-output/prisma.js.map +1 -1
  287. package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
  288. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
  289. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
  290. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
  291. package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
  292. package/dist/sql-driver/sql.js.map +1 -1
  293. package/dist/sync/commands.d.ts +15 -0
  294. package/dist/sync/commands.d.ts.map +1 -0
  295. package/dist/sync/commands.js +27 -0
  296. package/dist/sync/commands.js.map +1 -0
  297. package/dist/sync/index.d.ts +4 -0
  298. package/dist/sync/index.js +4 -0
  299. package/dist/sync/read-tracking.d.ts +25 -0
  300. package/dist/sync/read-tracking.d.ts.map +1 -0
  301. package/dist/sync/read-tracking.js +148 -0
  302. package/dist/sync/read-tracking.js.map +1 -0
  303. package/dist/sync/submit.js +213 -0
  304. package/dist/sync/submit.js.map +1 -0
  305. package/dist/sync/types.d.ts +63 -0
  306. package/dist/sync/types.d.ts.map +1 -0
  307. package/dist/util/default-database-adapter.js +6 -1
  308. package/dist/util/default-database-adapter.js.map +1 -1
  309. package/dist/with-database.d.ts +3 -6
  310. package/dist/with-database.d.ts.map +1 -1
  311. package/dist/with-database.js +7 -15
  312. package/dist/with-database.js.map +1 -1
  313. package/package.json +33 -41
  314. package/src/adapters/adapters.ts +5 -4
  315. package/src/adapters/drizzle/migrate-drizzle.test.ts +46 -9
  316. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +5 -3
  317. package/src/adapters/drizzle/test-utils.ts +2 -1
  318. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -3
  319. package/src/adapters/generic-sql/generic-sql-adapter.ts +21 -24
  320. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +1 -0
  321. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +81 -15
  322. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +4 -2
  323. package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
  324. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +3 -2
  325. package/src/adapters/generic-sql/migration/dialect/mysql.ts +1 -0
  326. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +5 -4
  327. package/src/adapters/generic-sql/migration/dialect/postgres.ts +2 -1
  328. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +795 -3
  329. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +385 -57
  330. package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
  331. package/src/adapters/generic-sql/migration/executor.ts +47 -4
  332. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +117 -14
  333. package/src/adapters/generic-sql/migration/prepared-migrations.ts +9 -8
  334. package/src/adapters/generic-sql/migration/sql-generator.ts +5 -3
  335. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +3 -3
  336. package/src/adapters/generic-sql/query/cursor-utils.test.ts +3 -2
  337. package/src/adapters/generic-sql/query/cursor-utils.ts +1 -1
  338. package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
  339. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +144 -8
  340. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +16 -17
  341. package/src/adapters/generic-sql/query/select-builder.test.ts +1 -0
  342. package/src/adapters/generic-sql/query/select-builder.ts +2 -2
  343. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +24 -5
  344. package/src/adapters/generic-sql/query/sql-query-compiler.ts +83 -13
  345. package/src/adapters/generic-sql/query/where-builder.test.ts +7 -5
  346. package/src/adapters/generic-sql/query/where-builder.ts +48 -29
  347. package/src/adapters/generic-sql/sql-adapter-pglite-migrations.test.ts +6 -15
  348. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +52 -7
  349. package/src/adapters/generic-sql/sql-adapter-pglite-queries.test.ts +9 -6
  350. package/src/adapters/generic-sql/sql-adapter-sqlite3-driver.test.ts +273 -5
  351. package/src/adapters/generic-sql/sql-adapter-sqlite3-uow.test.ts +123 -6
  352. package/src/adapters/generic-sql/sql-adapter-sqlocal.test.ts +4 -2
  353. package/src/adapters/generic-sql/uow-decoder.test.ts +4 -3
  354. package/src/adapters/generic-sql/uow-decoder.ts +3 -3
  355. package/src/adapters/generic-sql/uow-encoder.test.ts +4 -2
  356. package/src/adapters/generic-sql/uow-encoder.ts +14 -18
  357. package/src/adapters/in-memory/condition-evaluator.test.ts +2 -1
  358. package/src/adapters/in-memory/condition-evaluator.ts +9 -4
  359. package/src/adapters/in-memory/in-memory-adapter.ts +155 -44
  360. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +50 -2
  361. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +158 -3
  362. package/src/adapters/in-memory/in-memory-uow.ts +402 -26
  363. package/src/adapters/in-memory/options.test.ts +1 -0
  364. package/src/adapters/in-memory/options.ts +5 -1
  365. package/src/adapters/in-memory/outbox.test.ts +361 -0
  366. package/src/adapters/in-memory/reference-resolution.test.ts +3 -2
  367. package/src/adapters/in-memory/reference-resolution.ts +2 -2
  368. package/src/adapters/in-memory/sorted-array-index.test.ts +1 -0
  369. package/src/adapters/in-memory/store.test.ts +1 -0
  370. package/src/adapters/in-memory/store.ts +3 -3
  371. package/src/adapters/in-memory/value-normalization.test.ts +1 -0
  372. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +51 -7
  373. package/src/adapters/shared/from-unit-of-work-compiler.ts +156 -46
  374. package/src/adapters/shared/uow-operation-compiler.ts +3 -3
  375. package/src/browser/mod.ts +64 -0
  376. package/src/client.ts +19 -0
  377. package/src/db-fragment-definition-builder.test.ts +821 -47
  378. package/src/db-fragment-definition-builder.ts +857 -110
  379. package/src/db-fragment-instantiator.test.ts +114 -90
  380. package/src/db-fragment-integration.test.ts +9 -6
  381. package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
  382. package/src/dispatchers/cloudflare-do/index.test.ts +145 -12
  383. package/src/dispatchers/cloudflare-do/index.ts +49 -90
  384. package/src/dispatchers/node/dispatcher.ts +112 -0
  385. package/src/dispatchers/node/index.test.ts +43 -14
  386. package/src/dispatchers/node/index.ts +38 -75
  387. package/src/durable-hooks.test.ts +80 -0
  388. package/src/durable-hooks.ts +67 -0
  389. package/src/fragments/internal-fragment.routes.test.ts +570 -0
  390. package/src/fragments/internal-fragment.routes.ts +297 -5
  391. package/src/fragments/internal-fragment.schema.ts +45 -1
  392. package/src/fragments/internal-fragment.test.ts +223 -251
  393. package/src/fragments/internal-fragment.ts +278 -154
  394. package/src/hooks/durable-hooks-logger.ts +126 -0
  395. package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
  396. package/src/hooks/durable-hooks-processor.test.ts +179 -14
  397. package/src/hooks/durable-hooks-processor.ts +120 -14
  398. package/src/hooks/durable-hooks-runtime.test.ts +65 -0
  399. package/src/hooks/durable-hooks-runtime.ts +81 -0
  400. package/src/hooks/hooks.test.ts +314 -53
  401. package/src/hooks/hooks.ts +360 -81
  402. package/src/id.test.ts +34 -0
  403. package/src/id.ts +1 -3
  404. package/src/internal/adapter-registry.test.ts +93 -0
  405. package/src/internal/adapter-registry.ts +239 -0
  406. package/src/internal/outbox-state.ts +43 -0
  407. package/src/migration-engine/auto-from-schema.test.ts +93 -0
  408. package/src/migration-engine/auto-from-schema.ts +360 -42
  409. package/src/migration-engine/create.test.ts +2 -1
  410. package/src/migration-engine/create.ts +1 -1
  411. package/src/migration-engine/generation-engine.test.ts +66 -9
  412. package/src/migration-engine/generation-engine.ts +31 -10
  413. package/src/migration-engine/shared.ts +13 -0
  414. package/src/mod.ts +45 -27
  415. package/src/naming/sql-naming.ts +1 -0
  416. package/src/outbox/outbox-builder.ts +2 -2
  417. package/src/outbox/outbox.test.ts +216 -45
  418. package/src/outbox/outbox.ts +3 -1
  419. package/src/query/column-defaults.ts +1 -1
  420. package/src/query/condition-builder.test.ts +15 -0
  421. package/src/query/condition-builder.ts +7 -0
  422. package/src/query/cursor-client.test.ts +70 -0
  423. package/src/query/cursor-client.ts +263 -0
  424. package/src/query/cursor.test.ts +3 -2
  425. package/src/query/cursor.ts +15 -3
  426. package/src/query/db-now.ts +69 -2
  427. package/src/query/orm/orm.ts +2 -2
  428. package/src/query/query-type.test.ts +2 -1
  429. package/src/query/serialize/create-sql-serializer.ts +3 -3
  430. package/src/query/serialize/dialect/mysql-serializer.ts +1 -1
  431. package/src/query/serialize/dialect/postgres-serializer.ts +1 -1
  432. package/src/query/serialize/dialect/sqlite-serializer.test.ts +39 -2
  433. package/src/query/serialize/dialect/sqlite-serializer.ts +18 -5
  434. package/src/query/simple-query-interface.ts +10 -4
  435. package/src/query/unit-of-work/execute-unit-of-work.test.ts +347 -9
  436. package/src/query/unit-of-work/execute-unit-of-work.ts +63 -20
  437. package/src/query/unit-of-work/retry-policy.test.ts +1 -0
  438. package/src/query/unit-of-work/tx-builder.test.ts +73 -1
  439. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +5 -4
  440. package/src/query/unit-of-work/unit-of-work-types.test.ts +41 -11
  441. package/src/query/unit-of-work/unit-of-work.test.ts +28 -2
  442. package/src/query/unit-of-work/unit-of-work.ts +105 -19
  443. package/src/query/value-decoding.test.ts +50 -2
  444. package/src/query/value-decoding.ts +17 -4
  445. package/src/query/value-encoding.test.ts +1 -0
  446. package/src/query/value-encoding.ts +1 -1
  447. package/src/schema/create.test.ts +164 -5
  448. package/src/schema/create.ts +222 -24
  449. package/src/schema/generate-id.test.ts +1 -0
  450. package/src/schema/serialize.test.ts +4 -3
  451. package/src/schema/type-conversion/create-sql-type-mapper.ts +1 -1
  452. package/src/schema/type-conversion/dialect/sqlite.ts +2 -2
  453. package/src/schema/type-conversion/type-mapping.test.ts +2 -1
  454. package/src/schema/validator.test.ts +4 -2
  455. package/src/schema/validator.ts +1 -0
  456. package/src/schema-output/drizzle.test.ts +72 -19
  457. package/src/schema-output/drizzle.ts +24 -18
  458. package/src/schema-output/prisma.test.ts +172 -14
  459. package/src/schema-output/prisma.ts +34 -14
  460. package/src/sql-driver/better-sqlite3.test.ts +5 -3
  461. package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
  462. package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
  463. package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
  464. package/src/sql-driver/query-executor/query-executor.ts +1 -1
  465. package/src/sql-driver/sql-driver-adapter.ts +2 -2
  466. package/src/sql-driver/sql.ts +2 -1
  467. package/src/sql-driver/sqlocal.test.ts +4 -2
  468. package/src/sync/commands.test.ts +39 -0
  469. package/src/sync/commands.ts +51 -0
  470. package/src/sync/conflict-checker.test.ts +450 -0
  471. package/src/sync/conflict-checker.ts +248 -0
  472. package/src/sync/index.ts +14 -0
  473. package/src/sync/plan.ts +9 -0
  474. package/src/sync/read-tracking.test.ts +177 -0
  475. package/src/sync/read-tracking.ts +287 -0
  476. package/src/sync/submit.test.ts +205 -0
  477. package/src/sync/submit.ts +328 -0
  478. package/src/sync/types.ts +80 -0
  479. package/src/util/default-database-adapter.ts +15 -2
  480. package/src/with-database.ts +20 -50
  481. package/tsconfig.json +1 -1
  482. package/tsdown.config.ts +38 -26
  483. package/vitest.config.ts +1 -0
  484. package/dist/hooks/durable-hooks-processor.d.ts.map +0 -1
  485. package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js +0 -168
  486. package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +0 -1
  487. package/dist/packages/fragno/dist/api/bind-services.js +0 -20
  488. package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
  489. package/dist/packages/fragno/dist/api/error.js +0 -48
  490. package/dist/packages/fragno/dist/api/error.js.map +0 -1
  491. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -321
  492. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
  493. package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -669
  494. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
  495. package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
  496. package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
  497. package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
  498. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
  499. package/dist/packages/fragno/dist/api/internal/route.js +0 -10
  500. package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
  501. package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
  502. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
  503. package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
  504. package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
  505. package/dist/packages/fragno/dist/api/request-input-context.js +0 -185
  506. package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
  507. package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
  508. package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
  509. package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
  510. package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
  511. package/dist/packages/fragno/dist/api/route.js +0 -30
  512. package/dist/packages/fragno/dist/api/route.js.map +0 -1
  513. package/dist/packages/fragno/dist/internal/symbols.js +0 -10
  514. package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
  515. package/dist/packages/fragno/dist/internal/trace-context.js +0 -12
  516. package/dist/packages/fragno/dist/internal/trace-context.js.map +0 -1
@@ -1,6 +1,19 @@
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
+ };
4
17
  const DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;
5
18
  function resolveStuckProcessingTimeoutMinutes(value) {
6
19
  if (value === false) return false;
@@ -11,19 +24,48 @@ function resolveStuckProcessingTimeoutMinutes(value) {
11
24
  * Add hook events as mutation operations to the UOW.
12
25
  * This should be called before executeMutations() so hook records are created
13
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.
14
31
  */
15
- function prepareHookMutations(uow, config) {
16
- const { namespace, internalFragment, defaultRetryPolicy } = config;
32
+ function prepareHookMutations(uow, internalFragment, defaultRetryPolicy) {
17
33
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
18
34
  const triggeredHooks = uow.getTriggeredHooks();
19
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
+ });
20
60
  const internalSchema = internalFragment.$internal.deps.schema;
21
61
  const internalUow = uow.forSchema(internalSchema);
22
62
  for (const hook of triggeredHooks) {
23
63
  const maxAttempts = (hook.options?.retryPolicy ?? retryPolicy).shouldRetry(4) ? 5 : 1;
24
- const nextRetryAt = (hook.options?.processAt ? new Date(hook.options.processAt) : null) ?? null;
25
- internalUow.create("fragno_hooks", {
26
- 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,
27
69
  hookName: hook.hookName,
28
70
  payload: hook.payload,
29
71
  status: "pending",
@@ -33,7 +75,8 @@ function prepareHookMutations(uow, config) {
33
75
  nextRetryAt,
34
76
  error: null,
35
77
  nonce: uow.idempotencyKey
36
- });
78
+ };
79
+ internalUow.create("fragno_hooks", hookValues);
37
80
  }
38
81
  }
39
82
  /**
@@ -42,54 +85,129 @@ function prepareHookMutations(uow, config) {
42
85
  */
43
86
  async function processHooks(config) {
44
87
  const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
88
+ if (!hooks) throw new Error("Hook processor hooks not initialized.");
45
89
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
46
90
  const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(config.stuckProcessingTimeoutMinutes);
47
- const getDbNow = async () => {
48
- const services = internalFragment.services;
49
- if (services.getDbNow) return services.getDbNow();
50
- return /* @__PURE__ */ new Date();
51
- };
52
- const dbNow = await getDbNow();
53
- if (stuckProcessingTimeoutMinutes !== false) {
54
- const staleBefore = /* @__PURE__ */ new Date(dbNow.getTime() - stuckProcessingTimeoutMinutes * 6e4);
55
- const stuckEvents = await internalFragment.inContext(async function() {
56
- return await this.handlerTx().withServiceCalls(() => [internalFragment.services.hookService.requeueStuckProcessingHooks(namespace, staleBefore)]).transform(({ serviceResult: [events] }) => events).execute();
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();
103
+ });
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) }
57
133
  });
58
- if (stuckEvents.length > 0) try {
59
- config.onStuckProcessingHooks?.({
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) => {
140
+ const hookFn = hooks[event.hookName];
141
+ if (!hookFn) {
142
+ DurableHooksLogger.error("Hook missing", {
60
143
  namespace,
61
- timeoutMinutes: stuckProcessingTimeoutMinutes,
62
- events: stuckEvents
144
+ fields: {
145
+ eventId: event.id,
146
+ hookName: event.hookName,
147
+ attempts: event.attempts,
148
+ maxAttempts: event.maxAttempts,
149
+ createdAt: event.createdAt.toISOString()
150
+ }
63
151
  });
64
- } catch (error) {
65
- console.error("Error calling onStuckProcessingHooks", error);
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
+ };
66
159
  }
67
- }
68
- const pendingEvents = await internalFragment.inContext(async function() {
69
- return await this.handlerTx().withServiceCalls(() => [internalFragment.services.hookService.claimPendingHookEvents(namespace)]).transform(({ serviceResult: [events] }) => events).execute();
70
- });
71
- if (pendingEvents.length === 0) return 0;
72
- const processedEvents = await Promise.allSettled(pendingEvents.map(async (event) => {
73
- const hookFn = hooks[event.hookName];
74
- if (!hookFn) return {
75
- eventId: event.id,
76
- status: "failed",
77
- error: `Hook '${event.hookName}' not found in hooks map`,
78
- attempts: event.attempts,
79
- maxAttempts: event.maxAttempts
80
- };
81
160
  try {
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
+ });
82
175
  const hookContext = {
83
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,
84
185
  handlerTx: config.handlerTx
85
186
  };
86
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
+ });
87
196
  return {
88
197
  eventId: event.id,
89
198
  status: "completed"
90
199
  };
91
200
  } catch (error) {
92
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
+ });
93
211
  return {
94
212
  eventId: event.id,
95
213
  status: "failed",
@@ -100,56 +218,99 @@ async function processHooks(config) {
100
218
  }
101
219
  }));
102
220
  await internalFragment.inContext(async function() {
103
- await this.handlerTx().withServiceCalls(() => {
104
- const txResults = [];
221
+ await this.handlerTx().mutate(({ forSchema }) => {
222
+ const uow = forSchema(internalSchema);
223
+ const now = dbNow();
105
224
  for (const processedEvent of processedEvents) {
106
- 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
+ }
107
232
  const { eventId, status } = processedEvent.value;
108
- if (status === "completed") txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
109
- else if (status === "failed") {
110
- const { error, attempts } = processedEvent.value;
111
- txResults.push(internalFragment.services.hookService.markHookFailed(eventId, error, attempts, retryPolicy, dbNow));
233
+ if (status === "completed") {
234
+ uow.update("fragno_hooks", eventId, (b) => b.set({
235
+ status: "completed",
236
+ lastAttemptAt: now
237
+ }).check());
238
+ continue;
112
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());
113
258
  }
114
- return txResults;
115
259
  }).execute();
116
260
  });
117
- return processedEvents.reduce((count, result) => count + (result.status === "fulfilled" ? 1 : 0), 0);
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;
118
278
  }
119
- function createHookScheduler(config) {
279
+ function createDurableHooksRunner(config) {
120
280
  let processing = false;
121
281
  let queued = false;
122
282
  let currentPromise = null;
123
- const schedule = async () => {
283
+ const processDue = async () => {
124
284
  if (processing) {
125
285
  queued = true;
126
286
  return currentPromise ?? Promise.resolve(0);
127
287
  }
128
288
  processing = true;
129
289
  currentPromise = (async () => {
130
- let lastCount = 0;
290
+ let totalProcessed = 0;
131
291
  try {
132
292
  do {
133
293
  queued = false;
134
- lastCount = await processHooks(config);
294
+ totalProcessed += await processHooks(config);
135
295
  } while (queued);
136
- return lastCount;
296
+ return totalProcessed;
137
297
  } finally {
138
298
  processing = false;
139
299
  queued = false;
300
+ currentPromise = null;
140
301
  }
141
302
  })();
142
303
  return currentPromise;
143
304
  };
144
305
  const drain = async () => {
145
- while (true) if (await schedule() === 0) return;
306
+ while (true) if (await processDue() === 0) return;
146
307
  };
147
308
  return {
148
- schedule,
309
+ processDue,
149
310
  drain
150
311
  };
151
312
  }
152
313
 
153
314
  //#endregion
154
- export { createHookScheduler, prepareHookMutations };
315
+ export { createDurableHooksRunner, isHookStatus, prepareHookMutations };
155
316
  //# sourceMappingURL=hooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","names":["hookContext: HookContext","txResults: TxResult<void>[]","currentPromise: Promise<number> | null"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import 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 { InternalFragmentInstance } from \"../fragments/internal-fragment\";\nimport type { TxResult } from \"../query/unit-of-work/execute-unit-of-work\";\nimport type { FragnoId } from \"../schema/create\";\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 * Create a handler transaction builder to run atomic operations.\n */\n handlerTx: HookHandlerTx;\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 * 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;\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\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 * Internal hook scheduler used to coordinate processing/drain.\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 * 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 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 */\nexport function prepareHookMutations<THooks extends HooksMap>(\n uow: IUnitOfWork,\n config: HookProcessorConfig<THooks>,\n): 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 const processAt = hook.options?.processAt ? new Date(hook.options.processAt) : null;\n const nextRetryAt = processAt ?? null;\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,\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<THooks extends HooksMap>(\n config: HookProcessorConfig<THooks>,\n): Promise<number> {\n const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(\n config.stuckProcessingTimeoutMinutes,\n );\n const getDbNow = async () => {\n const services = internalFragment.services as { getDbNow?: () => Promise<Date> };\n if (services.getDbNow) {\n return services.getDbNow();\n }\n return new Date();\n };\n const dbNow = await getDbNow();\n\n if (stuckProcessingTimeoutMinutes !== false) {\n const staleBefore = new Date(dbNow.getTime() - stuckProcessingTimeoutMinutes * 60_000);\n const stuckEvents = await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () =>\n [\n internalFragment.services.hookService.requeueStuckProcessingHooks(\n namespace,\n staleBefore,\n ),\n ] as const,\n )\n .transform(({ serviceResult: [events] }) => events)\n .execute();\n });\n\n if (stuckEvents.length > 0) {\n try {\n config.onStuckProcessingHooks?.({\n namespace,\n timeoutMinutes: stuckProcessingTimeoutMinutes,\n events: stuckEvents,\n });\n } catch (error) {\n console.error(\"Error calling onStuckProcessingHooks\", error);\n }\n }\n }\n\n // Claim pending events in the same transaction to avoid double-processing.\n const pendingEvents = await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () => [internalFragment.services.hookService.claimPendingHookEvents(namespace)] as const,\n )\n .transform(({ serviceResult: [events] }) => events)\n .execute();\n });\n\n if (pendingEvents.length === 0) {\n return 0;\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 = {\n idempotencyKey: event.idempotencyKey,\n handlerTx: config.handlerTx,\n };\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 dbNow,\n ),\n );\n }\n }\n return txResults;\n })\n .execute();\n });\n\n const processedCount = processedEvents.reduce(\n (count, result) => count + (result.status === \"fulfilled\" ? 1 : 0),\n 0,\n );\n\n return processedCount;\n}\n\nexport function createHookScheduler(config: HookProcessorConfig): HookScheduler {\n let processing = false;\n let queued = false;\n let currentPromise: Promise<number> | null = null;\n\n const schedule = async () => {\n if (processing) {\n queued = true;\n return currentPromise ?? Promise.resolve(0);\n }\n\n processing = true;\n currentPromise = (async () => {\n let lastCount = 0;\n try {\n do {\n queued = false;\n lastCount = await processHooks(config);\n } while (queued);\n return lastCount;\n } finally {\n processing = false;\n queued = false;\n }\n })();\n\n return currentPromise;\n };\n\n const drain = async () => {\n while (true) {\n const processed = await schedule();\n if (processed === 0) {\n return;\n }\n }\n };\n\n return { schedule, drain };\n}\n"],"mappings":"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"}
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";