@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,5 +1,6 @@
1
1
  import { FragmentDefinitionBuilder } from "@fragno-dev/core";
2
2
  import type { InstantiatedFragmentFromDefinition } from "@fragno-dev/core";
3
+
3
4
  import {
4
5
  DatabaseFragmentDefinitionBuilder,
5
6
  type DatabaseHandlerContext,
@@ -8,26 +9,94 @@ import {
8
9
  type FragnoPublicConfigWithDatabase,
9
10
  type ImplicitDatabaseDependencies,
10
11
  } from "../db-fragment-definition-builder";
11
- import { FragnoId } from "../schema/create";
12
+ import { isHookStatus, type HookStatus } from "../hooks/hooks";
13
+ import type { Cursor } from "../query/cursor";
14
+ import { dbNow, type DbNow } from "../query/db-now";
12
15
  import type { RetryPolicy } from "../query/unit-of-work/retry-policy";
13
- import { dbNow } from "../query/db-now";
16
+ import { FragnoId } from "../schema/create";
14
17
  import {
15
18
  internalSchema,
16
19
  SETTINGS_NAMESPACE,
17
20
  SETTINGS_TABLE_NAME,
18
21
  } from "./internal-fragment.schema";
19
22
 
20
- export {
21
- internalSchema,
22
- SETTINGS_NAMESPACE,
23
- SETTINGS_TABLE_NAME,
24
- } from "./internal-fragment.schema";
23
+ type AdapterRegistry = {
24
+ listSchemas: () => Array<{
25
+ name: string;
26
+ namespace: string | null;
27
+ version: number;
28
+ tables: string[];
29
+ }>;
30
+ listOutboxFragments: () => Array<{ name: string; mountRoute: string }>;
31
+ isOutboxEnabled: () => boolean;
32
+ resolveSyncCommand: (
33
+ fragmentName: string,
34
+ schemaName: string,
35
+ commandName: string,
36
+ ) => { command: unknown; namespace: string | null } | undefined;
37
+ };
38
+
39
+ export class SchemaRegistryCollisionError extends Error {
40
+ readonly code = "SCHEMA_REGISTRY_COLLISION" as const;
41
+ readonly namespaceKey: string;
42
+ readonly existing: { name: string; namespace: string | null };
43
+ readonly attempted: { name: string; namespace: string | null };
44
+
45
+ constructor({
46
+ namespaceKey,
47
+ existing,
48
+ attempted,
49
+ }: {
50
+ namespaceKey: string;
51
+ existing: { name: string; namespace: string | null };
52
+ attempted: { name: string; namespace: string | null };
53
+ }) {
54
+ super(
55
+ `Schema namespace "${namespaceKey}" is already owned by "${existing.name}" (${existing.namespace ?? "null"}).`,
56
+ );
57
+ this.name = "SchemaRegistryCollisionError";
58
+ this.namespaceKey = namespaceKey;
59
+ this.existing = existing;
60
+ this.attempted = attempted;
61
+ }
62
+ }
63
+
64
+ export type InternalFragmentConfig = {
65
+ registry?: AdapterRegistry;
66
+ };
67
+
68
+ export { internalSchema, SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME };
69
+
70
+ const INTERNAL_SCHEMA_MIN_VERSION = 4;
71
+ if (internalSchema.version < INTERNAL_SCHEMA_MIN_VERSION) {
72
+ // Keep the internal schema version monotonic after removing fragno_db_schemas.
73
+ internalSchema.version = INTERNAL_SCHEMA_MIN_VERSION;
74
+ }
75
+
76
+ const describeHookStatusSource = (event: { id: FragnoId; hookName: string }) =>
77
+ `fragno_hooks id=${event.id} hook=${event.hookName}`;
78
+
79
+ const coerceHookStatus = (status: string, context: string): HookStatus => {
80
+ if (isHookStatus(status)) {
81
+ return status;
82
+ }
83
+ throw new Error(`Invalid hook status from database (${context}): ${status}`);
84
+ };
85
+
86
+ const DEFAULT_HOOKS_PAGE_SIZE = 50;
87
+
88
+ const resolveHookPageSize = (pageSize?: number): number => {
89
+ if (typeof pageSize !== "number" || !Number.isInteger(pageSize) || pageSize <= 0) {
90
+ return DEFAULT_HOOKS_PAGE_SIZE;
91
+ }
92
+ return pageSize;
93
+ };
25
94
 
26
95
  // This uses DatabaseFragmentDefinitionBuilder directly
27
96
  // to avoid circular dependency (it doesn't need to link to itself)
28
97
  export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
29
98
  new FragmentDefinitionBuilder<
30
- {},
99
+ InternalFragmentConfig,
31
100
  FragnoPublicConfigWithDatabase,
32
101
  ImplicitDatabaseDependencies<typeof internalSchema>,
33
102
  {},
@@ -40,14 +109,6 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
40
109
  >("$fragno-internal-fragment"),
41
110
  internalSchema,
42
111
  )
43
- .providesBaseService(({ deps }) => ({
44
- getDbNow: async () => {
45
- if (deps.db.now) {
46
- return deps.db.now();
47
- }
48
- return new Date();
49
- },
50
- }))
51
112
  .providesService("settingsService", ({ defineService }) => {
52
113
  return defineService({
53
114
  /**
@@ -93,6 +154,30 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
93
154
  .build();
94
155
  },
95
156
 
157
+ /**
158
+ * Set a setting value only if it does not already exist.
159
+ */
160
+ setIfMissing(namespace: string, key: string, value: string) {
161
+ const fullKey = `${namespace}.${key}`;
162
+ return this.serviceTx(internalSchema)
163
+ .retrieve((uow) =>
164
+ uow.findFirst(SETTINGS_TABLE_NAME, (b) =>
165
+ b.whereIndex("unique_key", (eb) => eb("key", "=", fullKey)),
166
+ ),
167
+ )
168
+ .transformRetrieve(([result]) => result)
169
+ .mutate(({ uow, retrieveResult }) => {
170
+ if (retrieveResult) {
171
+ return;
172
+ }
173
+ uow.create(SETTINGS_TABLE_NAME, {
174
+ key: fullKey,
175
+ value,
176
+ });
177
+ })
178
+ .build();
179
+ },
180
+
96
181
  /**
97
182
  * Delete a setting by ID.
98
183
  */
@@ -128,8 +213,12 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
128
213
  id: event.id,
129
214
  hookName: event.hookName,
130
215
  payload: event.payload as unknown,
216
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
131
217
  attempts: event.attempts,
132
218
  maxAttempts: event.maxAttempts,
219
+ lastAttemptAt: event.lastAttemptAt,
220
+ nextRetryAt: event.nextRetryAt,
221
+ createdAt: event.createdAt,
133
222
  idempotencyKey: event.nonce,
134
223
  }));
135
224
  })
@@ -159,8 +248,12 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
159
248
  id: event.id,
160
249
  hookName: event.hookName,
161
250
  payload: event.payload,
251
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
162
252
  attempts: event.attempts,
163
253
  maxAttempts: event.maxAttempts,
254
+ lastAttemptAt: event.lastAttemptAt,
255
+ nextRetryAt: event.nextRetryAt,
256
+ createdAt: event.createdAt,
164
257
  idempotencyKey: event.nonce,
165
258
  }));
166
259
  })
@@ -177,6 +270,7 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
177
270
  .transform(({ retrieveResult }) =>
178
271
  retrieveResult.map((event) => ({
179
272
  ...event,
273
+ status: "processing" as const,
180
274
  id: new FragnoId({
181
275
  externalId: event.id.externalId,
182
276
  internalId: event.id.internalId,
@@ -188,84 +282,68 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
188
282
  },
189
283
 
190
284
  /**
191
- * Re-queue hook events that have been stuck in processing for too long.
285
+ * Claim stale processing hook events for processing.
286
+ * Returns ready events and marks them as processing in the same transaction.
192
287
  */
193
- requeueStuckProcessingHooks(namespace: string, staleBefore: Date) {
288
+ claimStuckProcessingHookEvents(namespace: string, staleBefore: DbNow) {
289
+ const now = dbNow();
290
+
194
291
  return this.serviceTx(internalSchema)
195
292
  .retrieve((uow) =>
196
293
  uow.find("fragno_hooks", (b) =>
197
- b.whereIndex("idx_namespace_status_retry", (eb) =>
198
- eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing")),
294
+ b.whereIndex("idx_namespace_status_last_attempt", (eb) =>
295
+ eb.and(
296
+ eb("namespace", "=", namespace),
297
+ eb("status", "=", "processing"),
298
+ eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)),
299
+ ),
199
300
  ),
200
301
  ),
201
302
  )
202
303
  .transformRetrieve(([events]) => {
203
- const stuck = events.filter((event) => {
204
- if (!event.lastAttemptAt) {
205
- return true;
206
- }
207
- return event.lastAttemptAt <= staleBefore;
208
- });
209
-
210
- return stuck.map((event) => ({
304
+ return events.map((event) => ({
211
305
  id: event.id,
212
306
  hookName: event.hookName,
307
+ payload: event.payload as unknown,
308
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
213
309
  attempts: event.attempts,
214
310
  maxAttempts: event.maxAttempts,
311
+ idempotencyKey: event.nonce,
215
312
  lastAttemptAt: event.lastAttemptAt,
216
313
  nextRetryAt: event.nextRetryAt,
314
+ createdAt: event.createdAt,
217
315
  }));
218
316
  })
219
317
  .mutate(({ uow, retrieveResult }) => {
318
+ if (retrieveResult.length === 0) {
319
+ return;
320
+ }
321
+
220
322
  for (const event of retrieveResult) {
221
323
  uow.update("fragno_hooks", event.id, (b) =>
222
- b.set({ status: "pending", nextRetryAt: null }).check(),
324
+ b.set({ status: "processing", lastAttemptAt: now, nextRetryAt: null }).check(),
223
325
  );
224
326
  }
225
327
  })
226
- .transform(({ retrieveResult }) => retrieveResult)
227
- .build();
228
- },
229
-
230
- /**
231
- * Get the next time a processing hook becomes stale.
232
- */
233
- getNextProcessingStaleAt(namespace: string, timeoutMinutes: number, now?: Date) {
234
- return this.serviceTx(internalSchema)
235
- .retrieve((uow) =>
236
- uow.find("fragno_hooks", (b) =>
237
- b.whereIndex("idx_namespace_status_retry", (eb) =>
238
- eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing")),
239
- ),
240
- ),
241
- )
242
- .transformRetrieve(([events]) => {
243
- if (events.length === 0) {
244
- return null;
245
- }
246
-
247
- const baseNow = now ?? new Date();
248
- const nowMs = baseNow.getTime();
249
- const timeoutMs = timeoutMinutes * 60_000;
250
- let earliestStaleAt: Date | null = null;
251
-
252
- for (const event of events) {
253
- if (!event.lastAttemptAt) {
254
- return baseNow;
255
- }
256
-
257
- const staleAtMs = event.lastAttemptAt.getTime() + timeoutMs;
258
- if (staleAtMs <= nowMs) {
259
- return baseNow;
260
- }
261
-
262
- const staleAt = new Date(staleAtMs);
263
- if (!earliestStaleAt || staleAt < earliestStaleAt) {
264
- earliestStaleAt = staleAt;
265
- }
266
- }
267
-
268
- return earliestStaleAt;
328
+ .transform(({ retrieveResult }) => {
329
+ return {
330
+ events: retrieveResult.map((event) => ({
331
+ ...event,
332
+ id: new FragnoId({
333
+ externalId: event.id.externalId,
334
+ internalId: event.id.internalId,
335
+ version: event.id.version + 1,
336
+ }),
337
+ })),
338
+ stuckEvents: retrieveResult.map((event) => ({
339
+ id: event.id,
340
+ hookName: event.hookName,
341
+ attempts: event.attempts,
342
+ maxAttempts: event.maxAttempts,
343
+ lastAttemptAt: event.lastAttemptAt,
344
+ nextRetryAt: event.nextRetryAt,
345
+ })),
346
+ };
269
347
  })
270
348
  .build();
271
349
  },
@@ -274,88 +352,97 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
274
352
  * Get the earliest pending hook wake time for a namespace.
275
353
  * Optionally considers processing hooks becoming stale when timeoutMinutes is provided.
276
354
  */
277
- getNextHookWakeAt(namespace: string, timeoutMinutes?: number | false, now?: Date) {
278
- const baseNow = now ?? new Date();
279
- const includeProcessing = typeof timeoutMinutes === "number" && timeoutMinutes > 0;
280
- const timeoutMs = includeProcessing ? timeoutMinutes * 60_000 : 0;
355
+ getNextHookWakeAt(namespace: string, timeoutMinutes?: number | false) {
356
+ const timeoutMinutesValue =
357
+ typeof timeoutMinutes === "number" && timeoutMinutes > 0 ? timeoutMinutes : 0;
358
+ const includeProcessing = timeoutMinutesValue > 0;
359
+ const now = dbNow();
360
+ const timeoutMs = timeoutMinutesValue * 60_000;
361
+ // Sentinel to keep query shape stable when processing checks are disabled.
362
+ const processingStatus = includeProcessing ? "processing" : "__disabled__";
363
+ const staleBefore = now.plus({ minutes: -timeoutMinutesValue });
281
364
 
282
365
  return this.serviceTx(internalSchema)
283
366
  .retrieve((uow) =>
284
- uow.find("fragno_hooks", (b) =>
285
- b
286
- .whereIndex("idx_namespace_status_retry", (eb) => {
287
- if (includeProcessing) {
288
- return eb.and(
367
+ uow
368
+ .forSchema(internalSchema)
369
+ .find("fragno_hooks", (b) =>
370
+ b
371
+ .whereIndex("idx_namespace_status_retry", (eb) =>
372
+ eb.and(
289
373
  eb("namespace", "=", namespace),
290
- eb.or(eb("status", "=", "pending"), eb("status", "=", "processing")),
291
- );
292
- }
293
- return eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"));
294
- })
295
- .select(["status", "nextRetryAt", "lastAttemptAt"]),
296
- ),
374
+ eb("status", "=", "pending"),
375
+ eb.or(eb.isNull("nextRetryAt"), eb("nextRetryAt", "<=", now)),
376
+ ),
377
+ )
378
+ .pageSize(1),
379
+ )
380
+ .find("fragno_hooks", (b) =>
381
+ b
382
+ .whereIndex("idx_namespace_status_retry", (eb) =>
383
+ eb.and(
384
+ eb("namespace", "=", namespace),
385
+ eb("status", "=", "pending"),
386
+ eb.isNotNull("nextRetryAt"),
387
+ eb("nextRetryAt", ">", now),
388
+ ),
389
+ )
390
+ .orderByIndex("idx_namespace_status_retry", "asc")
391
+ .pageSize(1)
392
+ .select(["nextRetryAt"]),
393
+ )
394
+ .find("fragno_hooks", (b) =>
395
+ b
396
+ .whereIndex("idx_namespace_status_last_attempt", (eb) =>
397
+ eb.and(
398
+ eb("namespace", "=", namespace),
399
+ eb("status", "=", processingStatus),
400
+ eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)),
401
+ ),
402
+ )
403
+ .pageSize(1),
404
+ )
405
+ .find("fragno_hooks", (b) =>
406
+ b
407
+ .whereIndex("idx_namespace_status_last_attempt", (eb) =>
408
+ eb.and(
409
+ eb("namespace", "=", namespace),
410
+ eb("status", "=", processingStatus),
411
+ eb.isNotNull("lastAttemptAt"),
412
+ eb("lastAttemptAt", ">", staleBefore),
413
+ ),
414
+ )
415
+ .orderByIndex("idx_namespace_status_last_attempt", "asc")
416
+ .pageSize(1)
417
+ .select(["lastAttemptAt"]),
418
+ ),
297
419
  )
298
- .transformRetrieve(([events]) => {
299
- if (events.length === 0) {
300
- return null;
301
- }
302
-
303
- const nowMs = baseNow.getTime();
304
- let earliestPendingAt: Date | null = null;
305
- let earliestStaleAt: Date | null = null;
420
+ .transformRetrieve(
421
+ ([pendingImmediate, pendingNext, processingImmediate, processingNext]) => {
422
+ const hasProcessingImmediate = includeProcessing && processingImmediate.length > 0;
306
423
 
307
- for (const event of events) {
308
- if (event.status === "pending") {
309
- const nextRetryAt = event.nextRetryAt;
310
- if (!nextRetryAt || nextRetryAt.getTime() <= nowMs) {
311
- return baseNow;
312
- }
313
- if (!earliestPendingAt || nextRetryAt < earliestPendingAt) {
314
- earliestPendingAt = nextRetryAt;
315
- }
316
- continue;
424
+ if (pendingImmediate.length > 0 || hasProcessingImmediate) {
425
+ return new Date();
317
426
  }
318
427
 
319
- if (!includeProcessing || event.status !== "processing") {
320
- continue;
321
- }
428
+ const pendingNextAt = pendingNext[0]?.nextRetryAt ?? null;
429
+ let processingNextAt: Date | null = null;
322
430
 
323
- const lastAttemptAt = event.lastAttemptAt;
324
- if (!lastAttemptAt) {
325
- return baseNow;
431
+ if (includeProcessing) {
432
+ const lastAttemptAt = processingNext[0]?.lastAttemptAt;
433
+ if (lastAttemptAt) {
434
+ processingNextAt = new Date(lastAttemptAt.getTime() + timeoutMs);
435
+ }
326
436
  }
327
437
 
328
- const staleAtMs = lastAttemptAt.getTime() + timeoutMs;
329
- if (staleAtMs <= nowMs) {
330
- return baseNow;
438
+ if (!pendingNextAt) {
439
+ return processingNextAt ?? null;
331
440
  }
332
-
333
- const staleAt = new Date(staleAtMs);
334
- if (!earliestStaleAt || staleAt < earliestStaleAt) {
335
- earliestStaleAt = staleAt;
441
+ if (!processingNextAt) {
442
+ return pendingNextAt;
336
443
  }
337
- }
338
-
339
- if (!earliestPendingAt) {
340
- return earliestStaleAt ?? null;
341
- }
342
- if (!earliestStaleAt) {
343
- return earliestPendingAt;
344
- }
345
- return earliestPendingAt <= earliestStaleAt ? earliestPendingAt : earliestStaleAt;
346
- })
347
- .build();
348
- },
349
-
350
- /**
351
- * Mark a hook event as completed.
352
- */
353
- markHookCompleted(eventId: FragnoId) {
354
- return this.serviceTx(internalSchema)
355
- .mutate(({ uow }) =>
356
- uow.update("fragno_hooks", eventId, (b) =>
357
- b.set({ status: "completed", lastAttemptAt: dbNow() }).check(),
358
- ),
444
+ return pendingNextAt <= processingNextAt ? pendingNextAt : processingNextAt;
445
+ },
359
446
  )
360
447
  .build();
361
448
  },
@@ -363,28 +450,22 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
363
450
  /**
364
451
  * Mark a hook event as failed and schedule next retry.
365
452
  */
366
- markHookFailed(
367
- eventId: FragnoId,
368
- error: string,
369
- attempts: number,
370
- retryPolicy: RetryPolicy,
371
- now?: Date,
372
- ) {
453
+ markHookFailed(eventId: FragnoId, error: string, attempts: number, retryPolicy: RetryPolicy) {
373
454
  const newAttempts = attempts + 1;
374
455
  const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
456
+ const now = dbNow();
375
457
 
376
458
  return this.serviceTx(internalSchema)
377
459
  .mutate(({ uow }) => {
378
460
  if (shouldRetry) {
379
461
  const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
380
- const baseNow = now ?? new Date();
381
- const nextRetryAt = new Date(baseNow.getTime() + delayMs);
462
+ const nextRetryAt = now.plus({ ms: delayMs });
382
463
  uow.update("fragno_hooks", eventId, (b) =>
383
464
  b
384
465
  .set({
385
466
  status: "pending",
386
467
  attempts: newAttempts,
387
- lastAttemptAt: dbNow(),
468
+ lastAttemptAt: now,
388
469
  nextRetryAt,
389
470
  error,
390
471
  })
@@ -396,7 +477,7 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
396
477
  .set({
397
478
  status: "failed",
398
479
  attempts: newAttempts,
399
- lastAttemptAt: dbNow(),
480
+ lastAttemptAt: now,
400
481
  error,
401
482
  })
402
483
  .check(),
@@ -429,7 +510,45 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
429
510
  b.whereIndex("primary", (eb) => eb("id", "=", eventId)),
430
511
  ),
431
512
  )
432
- .transformRetrieve(([result]) => result ?? undefined)
513
+ .transformRetrieve(([result]) =>
514
+ result
515
+ ? {
516
+ ...result,
517
+ status: coerceHookStatus(result.status, describeHookStatusSource(result)),
518
+ }
519
+ : undefined,
520
+ )
521
+ .build();
522
+ },
523
+
524
+ /**
525
+ * Get hook events for a namespace in newest-first order with pagination.
526
+ */
527
+ getHooksByNamespacePage(
528
+ namespace: string,
529
+ options: { cursor?: Cursor | string; pageSize?: number } = {},
530
+ ) {
531
+ const pageSize = resolveHookPageSize(options.pageSize);
532
+
533
+ return this.serviceTx(internalSchema)
534
+ .retrieve((uow) =>
535
+ uow.findWithCursor("fragno_hooks", (b) => {
536
+ const query = b
537
+ .whereIndex("idx_namespace_created_at", (eb) => eb("namespace", "=", namespace))
538
+ .orderByIndex("idx_namespace_created_at", "desc")
539
+ .pageSize(pageSize);
540
+
541
+ return options.cursor ? query.after(options.cursor) : query;
542
+ }),
543
+ )
544
+ .transformRetrieve(([page]) => ({
545
+ items: page.items.map((event) => ({
546
+ ...event,
547
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
548
+ })),
549
+ cursor: page.cursor,
550
+ hasNextPage: page.hasNextPage,
551
+ }))
433
552
  .build();
434
553
  },
435
554
 
@@ -443,7 +562,12 @@ export const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(
443
562
  b.whereIndex("idx_namespace_status_retry", (eb) => eb("namespace", "=", namespace)),
444
563
  ),
445
564
  )
446
- .transformRetrieve(([events]) => events)
565
+ .transformRetrieve(([events]) =>
566
+ events.map((event) => ({
567
+ ...event,
568
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
569
+ })),
570
+ )
447
571
  .build();
448
572
  },
449
573
  });