@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,14 +1,37 @@
1
- import { FragmentDefinitionBuilder } from "../packages/fragno/dist/api/fragment-definition-builder.js";
1
+ import { dbNow } from "../query/db-now.js";
2
2
  import { FragnoId } from "../schema/create.js";
3
+ import { isHookStatus } from "../hooks/hooks.js";
3
4
  import { SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME, internalSchema } from "./internal-fragment.schema.js";
4
- import { dbNow } from "../query/db-now.js";
5
5
  import { DatabaseFragmentDefinitionBuilder } from "../db-fragment-definition-builder.js";
6
+ import { FragmentDefinitionBuilder } from "@fragno-dev/core";
6
7
 
7
8
  //#region src/fragments/internal-fragment.ts
8
- const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDefinitionBuilder("$fragno-internal-fragment"), internalSchema).providesBaseService(({ deps }) => ({ getDbNow: async () => {
9
- if (deps.db.now) return deps.db.now();
10
- return /* @__PURE__ */ new Date();
11
- } })).providesService("settingsService", ({ defineService }) => {
9
+ var SchemaRegistryCollisionError = class extends Error {
10
+ code = "SCHEMA_REGISTRY_COLLISION";
11
+ namespaceKey;
12
+ existing;
13
+ attempted;
14
+ constructor({ namespaceKey, existing, attempted }) {
15
+ super(`Schema namespace "${namespaceKey}" is already owned by "${existing.name}" (${existing.namespace ?? "null"}).`);
16
+ this.name = "SchemaRegistryCollisionError";
17
+ this.namespaceKey = namespaceKey;
18
+ this.existing = existing;
19
+ this.attempted = attempted;
20
+ }
21
+ };
22
+ const INTERNAL_SCHEMA_MIN_VERSION = 4;
23
+ if (internalSchema.version < INTERNAL_SCHEMA_MIN_VERSION) internalSchema.version = INTERNAL_SCHEMA_MIN_VERSION;
24
+ const describeHookStatusSource = (event) => `fragno_hooks id=${event.id} hook=${event.hookName}`;
25
+ const coerceHookStatus = (status, context) => {
26
+ if (isHookStatus(status)) return status;
27
+ throw new Error(`Invalid hook status from database (${context}): ${status}`);
28
+ };
29
+ const DEFAULT_HOOKS_PAGE_SIZE = 50;
30
+ const resolveHookPageSize = (pageSize) => {
31
+ if (typeof pageSize !== "number" || !Number.isInteger(pageSize) || pageSize <= 0) return DEFAULT_HOOKS_PAGE_SIZE;
32
+ return pageSize;
33
+ };
34
+ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDefinitionBuilder("$fragno-internal-fragment"), internalSchema).providesService("settingsService", ({ defineService }) => {
12
35
  return defineService({
13
36
  get(namespace, key) {
14
37
  const fullKey = `${namespace}.${key}`;
@@ -24,6 +47,16 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
24
47
  });
25
48
  }).build();
26
49
  },
50
+ setIfMissing(namespace, key, value) {
51
+ const fullKey = `${namespace}.${key}`;
52
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst(SETTINGS_TABLE_NAME, (b) => b.whereIndex("unique_key", (eb) => eb("key", "=", fullKey)))).transformRetrieve(([result]) => result).mutate(({ uow, retrieveResult }) => {
53
+ if (retrieveResult) return;
54
+ uow.create(SETTINGS_TABLE_NAME, {
55
+ key: fullKey,
56
+ value
57
+ });
58
+ }).build();
59
+ },
27
60
  delete(id) {
28
61
  return this.serviceTx(internalSchema).mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id)).build();
29
62
  }
@@ -37,8 +70,12 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
37
70
  id: event.id,
38
71
  hookName: event.hookName,
39
72
  payload: event.payload,
73
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
40
74
  attempts: event.attempts,
41
75
  maxAttempts: event.maxAttempts,
76
+ lastAttemptAt: event.lastAttemptAt,
77
+ nextRetryAt: event.nextRetryAt,
78
+ createdAt: event.createdAt,
42
79
  idempotencyKey: event.nonce
43
80
  }));
44
81
  }).build();
@@ -50,8 +87,12 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
50
87
  id: event.id,
51
88
  hookName: event.hookName,
52
89
  payload: event.payload,
90
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
53
91
  attempts: event.attempts,
54
92
  maxAttempts: event.maxAttempts,
93
+ lastAttemptAt: event.lastAttemptAt,
94
+ nextRetryAt: event.nextRetryAt,
95
+ createdAt: event.createdAt,
55
96
  idempotencyKey: event.nonce
56
97
  }));
57
98
  }).mutate(({ uow, retrieveResult }) => {
@@ -62,6 +103,7 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
62
103
  }).check());
63
104
  }).transform(({ retrieveResult }) => retrieveResult.map((event) => ({
64
105
  ...event,
106
+ status: "processing",
65
107
  id: new FragnoId({
66
108
  externalId: event.id.externalId,
67
109
  internalId: event.id.internalId,
@@ -69,104 +111,89 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
69
111
  })
70
112
  }))).build();
71
113
  },
72
- requeueStuckProcessingHooks(namespace, staleBefore) {
73
- return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"))))).transformRetrieve(([events]) => {
74
- return events.filter((event) => {
75
- if (!event.lastAttemptAt) return true;
76
- return event.lastAttemptAt <= staleBefore;
77
- }).map((event) => ({
114
+ claimStuckProcessingHookEvents(namespace, staleBefore) {
115
+ const now = dbNow();
116
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"), eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)))))).transformRetrieve(([events]) => {
117
+ return events.map((event) => ({
78
118
  id: event.id,
79
119
  hookName: event.hookName,
120
+ payload: event.payload,
121
+ status: coerceHookStatus(event.status, describeHookStatusSource(event)),
80
122
  attempts: event.attempts,
81
123
  maxAttempts: event.maxAttempts,
124
+ idempotencyKey: event.nonce,
82
125
  lastAttemptAt: event.lastAttemptAt,
83
- nextRetryAt: event.nextRetryAt
126
+ nextRetryAt: event.nextRetryAt,
127
+ createdAt: event.createdAt
84
128
  }));
85
129
  }).mutate(({ uow, retrieveResult }) => {
130
+ if (retrieveResult.length === 0) return;
86
131
  for (const event of retrieveResult) uow.update("fragno_hooks", event.id, (b) => b.set({
87
- status: "pending",
132
+ status: "processing",
133
+ lastAttemptAt: now,
88
134
  nextRetryAt: null
89
135
  }).check());
90
- }).transform(({ retrieveResult }) => retrieveResult).build();
91
- },
92
- getNextProcessingStaleAt(namespace, timeoutMinutes, now) {
93
- return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "processing"))))).transformRetrieve(([events]) => {
94
- if (events.length === 0) return null;
95
- const baseNow = now ?? /* @__PURE__ */ new Date();
96
- const nowMs = baseNow.getTime();
97
- const timeoutMs = timeoutMinutes * 6e4;
98
- let earliestStaleAt = null;
99
- for (const event of events) {
100
- if (!event.lastAttemptAt) return baseNow;
101
- const staleAtMs = event.lastAttemptAt.getTime() + timeoutMs;
102
- if (staleAtMs <= nowMs) return baseNow;
103
- const staleAt = new Date(staleAtMs);
104
- if (!earliestStaleAt || staleAt < earliestStaleAt) earliestStaleAt = staleAt;
105
- }
106
- return earliestStaleAt;
136
+ }).transform(({ retrieveResult }) => {
137
+ return {
138
+ events: retrieveResult.map((event) => ({
139
+ ...event,
140
+ id: new FragnoId({
141
+ externalId: event.id.externalId,
142
+ internalId: event.id.internalId,
143
+ version: event.id.version + 1
144
+ })
145
+ })),
146
+ stuckEvents: retrieveResult.map((event) => ({
147
+ id: event.id,
148
+ hookName: event.hookName,
149
+ attempts: event.attempts,
150
+ maxAttempts: event.maxAttempts,
151
+ lastAttemptAt: event.lastAttemptAt,
152
+ nextRetryAt: event.nextRetryAt
153
+ }))
154
+ };
107
155
  }).build();
108
156
  },
109
- getNextHookWakeAt(namespace, timeoutMinutes, now) {
110
- const baseNow = now ?? /* @__PURE__ */ new Date();
111
- const includeProcessing = typeof timeoutMinutes === "number" && timeoutMinutes > 0;
112
- const timeoutMs = includeProcessing ? timeoutMinutes * 6e4 : 0;
113
- return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => {
114
- if (includeProcessing) return eb.and(eb("namespace", "=", namespace), eb.or(eb("status", "=", "pending"), eb("status", "=", "processing")));
115
- return eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"));
116
- }).select([
117
- "status",
118
- "nextRetryAt",
119
- "lastAttemptAt"
120
- ]))).transformRetrieve(([events]) => {
121
- if (events.length === 0) return null;
122
- const nowMs = baseNow.getTime();
123
- let earliestPendingAt = null;
124
- let earliestStaleAt = null;
125
- for (const event of events) {
126
- if (event.status === "pending") {
127
- const nextRetryAt = event.nextRetryAt;
128
- if (!nextRetryAt || nextRetryAt.getTime() <= nowMs) return baseNow;
129
- if (!earliestPendingAt || nextRetryAt < earliestPendingAt) earliestPendingAt = nextRetryAt;
130
- continue;
131
- }
132
- if (!includeProcessing || event.status !== "processing") continue;
133
- const lastAttemptAt = event.lastAttemptAt;
134
- if (!lastAttemptAt) return baseNow;
135
- const staleAtMs = lastAttemptAt.getTime() + timeoutMs;
136
- if (staleAtMs <= nowMs) return baseNow;
137
- const staleAt = new Date(staleAtMs);
138
- if (!earliestStaleAt || staleAt < earliestStaleAt) earliestStaleAt = staleAt;
157
+ getNextHookWakeAt(namespace, timeoutMinutes) {
158
+ const timeoutMinutesValue = typeof timeoutMinutes === "number" && timeoutMinutes > 0 ? timeoutMinutes : 0;
159
+ const includeProcessing = timeoutMinutesValue > 0;
160
+ const now = dbNow();
161
+ const timeoutMs = timeoutMinutesValue * 6e4;
162
+ const processingStatus = includeProcessing ? "processing" : "__disabled__";
163
+ const staleBefore = now.plus({ minutes: -timeoutMinutesValue });
164
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.forSchema(internalSchema).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"), eb.or(eb.isNull("nextRetryAt"), eb("nextRetryAt", "<=", now)))).pageSize(1)).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", "pending"), eb.isNotNull("nextRetryAt"), eb("nextRetryAt", ">", now))).orderByIndex("idx_namespace_status_retry", "asc").pageSize(1).select(["nextRetryAt"])).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", processingStatus), eb.or(eb.isNull("lastAttemptAt"), eb("lastAttemptAt", "<=", staleBefore)))).pageSize(1)).find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_last_attempt", (eb) => eb.and(eb("namespace", "=", namespace), eb("status", "=", processingStatus), eb.isNotNull("lastAttemptAt"), eb("lastAttemptAt", ">", staleBefore))).orderByIndex("idx_namespace_status_last_attempt", "asc").pageSize(1).select(["lastAttemptAt"]))).transformRetrieve(([pendingImmediate, pendingNext, processingImmediate, processingNext]) => {
165
+ const hasProcessingImmediate = includeProcessing && processingImmediate.length > 0;
166
+ if (pendingImmediate.length > 0 || hasProcessingImmediate) return /* @__PURE__ */ new Date();
167
+ const pendingNextAt = pendingNext[0]?.nextRetryAt ?? null;
168
+ let processingNextAt = null;
169
+ if (includeProcessing) {
170
+ const lastAttemptAt = processingNext[0]?.lastAttemptAt;
171
+ if (lastAttemptAt) processingNextAt = new Date(lastAttemptAt.getTime() + timeoutMs);
139
172
  }
140
- if (!earliestPendingAt) return earliestStaleAt ?? null;
141
- if (!earliestStaleAt) return earliestPendingAt;
142
- return earliestPendingAt <= earliestStaleAt ? earliestPendingAt : earliestStaleAt;
173
+ if (!pendingNextAt) return processingNextAt ?? null;
174
+ if (!processingNextAt) return pendingNextAt;
175
+ return pendingNextAt <= processingNextAt ? pendingNextAt : processingNextAt;
143
176
  }).build();
144
177
  },
145
- markHookCompleted(eventId) {
146
- return this.serviceTx(internalSchema).mutate(({ uow }) => uow.update("fragno_hooks", eventId, (b) => b.set({
147
- status: "completed",
148
- lastAttemptAt: dbNow()
149
- }).check())).build();
150
- },
151
- markHookFailed(eventId, error, attempts, retryPolicy, now) {
178
+ markHookFailed(eventId, error, attempts, retryPolicy) {
152
179
  const newAttempts = attempts + 1;
153
180
  const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);
181
+ const now = dbNow();
154
182
  return this.serviceTx(internalSchema).mutate(({ uow }) => {
155
183
  if (shouldRetry) {
156
184
  const delayMs = retryPolicy.getDelayMs(newAttempts - 1);
157
- const baseNow = now ?? /* @__PURE__ */ new Date();
158
- const nextRetryAt = new Date(baseNow.getTime() + delayMs);
185
+ const nextRetryAt = now.plus({ ms: delayMs });
159
186
  uow.update("fragno_hooks", eventId, (b) => b.set({
160
187
  status: "pending",
161
188
  attempts: newAttempts,
162
- lastAttemptAt: dbNow(),
189
+ lastAttemptAt: now,
163
190
  nextRetryAt,
164
191
  error
165
192
  }).check());
166
193
  } else uow.update("fragno_hooks", eventId, (b) => b.set({
167
194
  status: "failed",
168
195
  attempts: newAttempts,
169
- lastAttemptAt: dbNow(),
196
+ lastAttemptAt: now,
170
197
  error
171
198
  }).check());
172
199
  }).build();
@@ -178,10 +205,30 @@ const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(new FragmentDe
178
205
  }).check())).build();
179
206
  },
180
207
  getHookById(eventId) {
181
- return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst("fragno_hooks", (b) => b.whereIndex("primary", (eb) => eb("id", "=", eventId)))).transformRetrieve(([result]) => result ?? void 0).build();
208
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.findFirst("fragno_hooks", (b) => b.whereIndex("primary", (eb) => eb("id", "=", eventId)))).transformRetrieve(([result]) => result ? {
209
+ ...result,
210
+ status: coerceHookStatus(result.status, describeHookStatusSource(result))
211
+ } : void 0).build();
212
+ },
213
+ getHooksByNamespacePage(namespace, options = {}) {
214
+ const pageSize = resolveHookPageSize(options.pageSize);
215
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.findWithCursor("fragno_hooks", (b) => {
216
+ const query = b.whereIndex("idx_namespace_created_at", (eb) => eb("namespace", "=", namespace)).orderByIndex("idx_namespace_created_at", "desc").pageSize(pageSize);
217
+ return options.cursor ? query.after(options.cursor) : query;
218
+ })).transformRetrieve(([page]) => ({
219
+ items: page.items.map((event) => ({
220
+ ...event,
221
+ status: coerceHookStatus(event.status, describeHookStatusSource(event))
222
+ })),
223
+ cursor: page.cursor,
224
+ hasNextPage: page.hasNextPage
225
+ })).build();
182
226
  },
183
227
  getHooksByNamespace(namespace) {
184
- return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb("namespace", "=", namespace)))).transformRetrieve(([events]) => events).build();
228
+ return this.serviceTx(internalSchema).retrieve((uow) => uow.find("fragno_hooks", (b) => b.whereIndex("idx_namespace_status_retry", (eb) => eb("namespace", "=", namespace)))).transformRetrieve(([events]) => events.map((event) => ({
229
+ ...event,
230
+ status: coerceHookStatus(event.status, describeHookStatusSource(event))
231
+ }))).build();
185
232
  }
186
233
  });
187
234
  }).providesService("outboxService", ({ defineService }) => {
@@ -226,5 +273,5 @@ async function getSchemaVersionFromDatabase(fragment, namespace) {
226
273
  }
227
274
 
228
275
  //#endregion
229
- export { getSchemaVersionFromDatabase, internalFragmentDef };
276
+ export { SchemaRegistryCollisionError, getSchemaVersionFromDatabase, internalFragmentDef };
230
277
  //# sourceMappingURL=internal-fragment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-fragment.js","names":["earliestStaleAt: Date | null","earliestPendingAt: Date | null"],"sources":["../../src/fragments/internal-fragment.ts"],"sourcesContent":["import { FragmentDefinitionBuilder } from \"@fragno-dev/core\";\nimport type { InstantiatedFragmentFromDefinition } from \"@fragno-dev/core\";\nimport {\n DatabaseFragmentDefinitionBuilder,\n type DatabaseHandlerContext,\n type DatabaseRequestStorage,\n type DatabaseServiceContext,\n type FragnoPublicConfigWithDatabase,\n type ImplicitDatabaseDependencies,\n} from \"../db-fragment-definition-builder\";\nimport { FragnoId } from \"../schema/create\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { dbNow } from \"../query/db-now\";\nimport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\nexport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\n// This uses DatabaseFragmentDefinitionBuilder directly\n// to avoid circular dependency (it doesn't need to link to itself)\nexport const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(\n new FragmentDefinitionBuilder<\n {},\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies<typeof internalSchema>,\n {},\n {},\n {},\n {},\n DatabaseServiceContext<{}>,\n DatabaseHandlerContext,\n DatabaseRequestStorage\n >(\"$fragno-internal-fragment\"),\n internalSchema,\n)\n .providesBaseService(({ deps }) => ({\n getDbNow: async () => {\n if (deps.db.now) {\n return deps.db.now();\n }\n return new Date();\n },\n }))\n .providesService(\"settingsService\", ({ defineService }) => {\n return defineService({\n /**\n * Get a setting by namespace and key.\n */\n get(namespace: string, key: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(\n ([result]): { id: FragnoId; key: string; value: string } | undefined =>\n result ?? undefined,\n )\n .build();\n },\n\n /**\n * Set a setting value by namespace and key.\n */\n set(namespace: string, key: string, value: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(([result]) => result)\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult) {\n uow.update(SETTINGS_TABLE_NAME, retrieveResult.id, (b) => b.set({ value }).check());\n } else {\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\n });\n }\n })\n .build();\n },\n\n /**\n * Delete a setting by ID.\n */\n delete(id: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id))\n .build();\n },\n });\n })\n .providesService(\"hookService\", ({ defineService }) => {\n return defineService({\n /**\n * Get pending hook events for processing.\n * Returns all pending events for the given namespace that are ready to be processed.\n */\n getPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload as unknown,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n }));\n })\n .build();\n },\n\n /**\n * Claim pending hook events for processing.\n * Returns ready events and marks them as processing in the same transaction.\n */\n claimPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult.length === 0) {\n return;\n }\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: now }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) =>\n retrieveResult.map((event) => ({\n ...event,\n id: new FragnoId({\n externalId: event.id.externalId,\n internalId: event.id.internalId,\n version: event.id.version + 1,\n }),\n })),\n )\n .build();\n },\n\n /**\n * Re-queue hook events that have been stuck in processing for too long.\n */\n requeueStuckProcessingHooks(namespace: string, staleBefore: Date) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"processing\")),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n const stuck = events.filter((event) => {\n if (!event.lastAttemptAt) {\n return true;\n }\n return event.lastAttemptAt <= staleBefore;\n });\n\n return stuck.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"pending\", nextRetryAt: null }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) => retrieveResult)\n .build();\n },\n\n /**\n * Get the next time a processing hook becomes stale.\n */\n getNextProcessingStaleAt(namespace: string, timeoutMinutes: number, now?: Date) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"processing\")),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n if (events.length === 0) {\n return null;\n }\n\n const baseNow = now ?? new Date();\n const nowMs = baseNow.getTime();\n const timeoutMs = timeoutMinutes * 60_000;\n let earliestStaleAt: Date | null = null;\n\n for (const event of events) {\n if (!event.lastAttemptAt) {\n return baseNow;\n }\n\n const staleAtMs = event.lastAttemptAt.getTime() + timeoutMs;\n if (staleAtMs <= nowMs) {\n return baseNow;\n }\n\n const staleAt = new Date(staleAtMs);\n if (!earliestStaleAt || staleAt < earliestStaleAt) {\n earliestStaleAt = staleAt;\n }\n }\n\n return earliestStaleAt;\n })\n .build();\n },\n\n /**\n * Get the earliest pending hook wake time for a namespace.\n * Optionally considers processing hooks becoming stale when timeoutMinutes is provided.\n */\n getNextHookWakeAt(namespace: string, timeoutMinutes?: number | false, now?: Date) {\n const baseNow = now ?? new Date();\n const includeProcessing = typeof timeoutMinutes === \"number\" && timeoutMinutes > 0;\n const timeoutMs = includeProcessing ? timeoutMinutes * 60_000 : 0;\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_retry\", (eb) => {\n if (includeProcessing) {\n return eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb.or(eb(\"status\", \"=\", \"pending\"), eb(\"status\", \"=\", \"processing\")),\n );\n }\n return eb.and(eb(\"namespace\", \"=\", namespace), eb(\"status\", \"=\", \"pending\"));\n })\n .select([\"status\", \"nextRetryAt\", \"lastAttemptAt\"]),\n ),\n )\n .transformRetrieve(([events]) => {\n if (events.length === 0) {\n return null;\n }\n\n const nowMs = baseNow.getTime();\n let earliestPendingAt: Date | null = null;\n let earliestStaleAt: Date | null = null;\n\n for (const event of events) {\n if (event.status === \"pending\") {\n const nextRetryAt = event.nextRetryAt;\n if (!nextRetryAt || nextRetryAt.getTime() <= nowMs) {\n return baseNow;\n }\n if (!earliestPendingAt || nextRetryAt < earliestPendingAt) {\n earliestPendingAt = nextRetryAt;\n }\n continue;\n }\n\n if (!includeProcessing || event.status !== \"processing\") {\n continue;\n }\n\n const lastAttemptAt = event.lastAttemptAt;\n if (!lastAttemptAt) {\n return baseNow;\n }\n\n const staleAtMs = lastAttemptAt.getTime() + timeoutMs;\n if (staleAtMs <= nowMs) {\n return baseNow;\n }\n\n const staleAt = new Date(staleAtMs);\n if (!earliestStaleAt || staleAt < earliestStaleAt) {\n earliestStaleAt = staleAt;\n }\n }\n\n if (!earliestPendingAt) {\n return earliestStaleAt ?? null;\n }\n if (!earliestStaleAt) {\n return earliestPendingAt;\n }\n return earliestPendingAt <= earliestStaleAt ? earliestPendingAt : earliestStaleAt;\n })\n .build();\n },\n\n /**\n * Mark a hook event as completed.\n */\n markHookCompleted(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"completed\", lastAttemptAt: dbNow() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Mark a hook event as failed and schedule next retry.\n */\n markHookFailed(\n eventId: FragnoId,\n error: string,\n attempts: number,\n retryPolicy: RetryPolicy,\n now?: Date,\n ) {\n const newAttempts = attempts + 1;\n const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);\n\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => {\n if (shouldRetry) {\n const delayMs = retryPolicy.getDelayMs(newAttempts - 1);\n const baseNow = now ?? new Date();\n const nextRetryAt = new Date(baseNow.getTime() + delayMs);\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b\n .set({\n status: \"pending\",\n attempts: newAttempts,\n lastAttemptAt: dbNow(),\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: dbNow(),\n error,\n })\n .check(),\n );\n }\n })\n .build();\n },\n\n /**\n * Mark a hook event as processing (to prevent concurrent execution).\n */\n markHookProcessing(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: dbNow() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Get a hook event by ID (for testing/verification purposes).\n */\n getHookById(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(\"fragno_hooks\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", eventId)),\n ),\n )\n .transformRetrieve(([result]) => result ?? undefined)\n .build();\n },\n\n /**\n * Get all hook events for a namespace (for testing/verification purposes).\n */\n getHooksByNamespace(namespace: string) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) => eb(\"namespace\", \"=\", namespace)),\n ),\n )\n .transformRetrieve(([events]) => events)\n .build();\n },\n });\n })\n .providesService(\"outboxService\", ({ defineService }) => {\n return defineService({\n /**\n * List outbox entries ordered by versionstamp (ascending).\n */\n list({ afterVersionstamp, limit }: { afterVersionstamp?: string; limit?: number } = {}) {\n const afterValue = afterVersionstamp?.toLowerCase();\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_db_outbox\", (b) => {\n let builder = afterValue\n ? b.whereIndex(\"idx_outbox_versionstamp\", (eb) =>\n eb(\"versionstamp\", \">\", afterValue),\n )\n : b.whereIndex(\"idx_outbox_versionstamp\");\n\n builder = builder.orderByIndex(\"idx_outbox_versionstamp\", \"asc\");\n if (limit !== undefined) {\n builder = builder.pageSize(limit);\n }\n return builder;\n }),\n )\n .transformRetrieve(([entries]) =>\n entries.map((entry) => ({\n id: entry.id,\n versionstamp: entry.versionstamp,\n uowId: entry.uowId,\n payload: entry.payload,\n refMap: entry.refMap ?? undefined,\n createdAt: entry.createdAt,\n })),\n )\n .build();\n },\n });\n })\n .build();\n\n/**\n * Type representing an instantiated internal fragment.\n * This is the fragment that manages Fragno's internal settings table.\n */\nexport type InternalFragmentInstance = InstantiatedFragmentFromDefinition<\n typeof internalFragmentDef\n>;\n\nexport async function getSchemaVersionFromDatabase(\n fragment: InternalFragmentInstance,\n namespace: string,\n): Promise<number> {\n try {\n const readSchemaVersion = async (targetNamespace: string): Promise<number | undefined> => {\n const setting = await fragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () =>\n [fragment.services.settingsService.get(targetNamespace, \"schema_version\")] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n });\n if (!setting) {\n return undefined;\n }\n const parsed = parseInt(setting.value, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n };\n\n const primary = await readSchemaVersion(namespace);\n if (primary !== undefined) {\n return primary;\n }\n\n // Back-compat: some installs stored internal schema version under a different namespace.\n // Check the alternate key (empty string ↔ schema name) so we find the version either way.\n const legacyNamespace =\n namespace === \"\" ? internalSchema.name : namespace === internalSchema.name ? \"\" : null;\n if (legacyNamespace !== null) {\n const legacy = await readSchemaVersion(legacyNamespace);\n if (legacy !== undefined) {\n return legacy;\n }\n }\n\n return 0;\n } catch {\n return 0;\n }\n}\n"],"mappings":";;;;;;;AA2BA,MAAa,sBAAsB,IAAI,kCACrC,IAAI,0BAWF,4BAA4B,EAC9B,eACD,CACE,qBAAqB,EAAE,YAAY,EAClC,UAAU,YAAY;AACpB,KAAI,KAAK,GAAG,IACV,QAAO,KAAK,GAAG,KAAK;AAEtB,wBAAO,IAAI,MAAM;GAEpB,EAAE,CACF,gBAAgB,oBAAoB,EAAE,oBAAoB;AACzD,QAAO,cAAc;EAInB,IAAI,WAAmB,KAAa;GAClC,MAAM,UAAU,GAAG,UAAU,GAAG;AAChC,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,UAAU,sBAAsB,MAClC,EAAE,WAAW,eAAe,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC,CAC5D,CACF,CACA,mBACE,CAAC,YACA,UAAU,OACb,CACA,OAAO;;EAMZ,IAAI,WAAmB,KAAa,OAAe;GACjD,MAAM,UAAU,GAAG,UAAU,GAAG;AAChC,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,UAAU,sBAAsB,MAClC,EAAE,WAAW,eAAe,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC,CAC5D,CACF,CACA,mBAAmB,CAAC,YAAY,OAAO,CACvC,QAAQ,EAAE,KAAK,qBAAqB;AACnC,QAAI,eACF,KAAI,OAAO,qBAAqB,eAAe,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC;QAEnF,KAAI,OAAO,qBAAqB;KAC9B,KAAK;KACL;KACD,CAAC;KAEJ,CACD,OAAO;;EAMZ,OAAO,IAAc;AACnB,UAAO,KAAK,UAAU,eAAe,CAClC,QAAQ,EAAE,UAAU,IAAI,OAAO,qBAAqB,GAAG,CAAC,CACxD,OAAO;;EAEb,CAAC;EACF,CACD,gBAAgB,gBAAgB,EAAE,oBAAoB;AACrD,QAAO,cAAc;EAKnB,qBAAqB,WAAmB;GACtC,MAAM,MAAM,OAAO;AACnB,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EAAE,WAAW,+BAA+B,OAC1C,GAAG,IACD,GAAG,aAAa,KAAK,UAAU,EAC/B,GAAG,UAAU,KAAK,UAAU,EAC5B,GAAG,GAAG,GAAG,OAAO,cAAc,EAAE,GAAG,eAAe,MAAM,IAAI,CAAC,CAC9D,CACF,CACF,CACF,CACA,mBAAmB,CAAC,YAAY;AAC/B,WAAO,OAAO,KAAK,WAAW;KAC5B,IAAI,MAAM;KACV,UAAU,MAAM;KAChB,SAAS,MAAM;KACf,UAAU,MAAM;KAChB,aAAa,MAAM;KACnB,gBAAgB,MAAM;KACvB,EAAE;KACH,CACD,OAAO;;EAOZ,uBAAuB,WAAmB;GACxC,MAAM,MAAM,OAAO;AACnB,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EAAE,WAAW,+BAA+B,OAC1C,GAAG,IACD,GAAG,aAAa,KAAK,UAAU,EAC/B,GAAG,UAAU,KAAK,UAAU,EAC5B,GAAG,GAAG,GAAG,OAAO,cAAc,EAAE,GAAG,eAAe,MAAM,IAAI,CAAC,CAC9D,CACF,CACF,CACF,CACA,mBAAmB,CAAC,YAAY;AAC/B,WAAO,OAAO,KAAK,WAAW;KAC5B,IAAI,MAAM;KACV,UAAU,MAAM;KAChB,SAAS,MAAM;KACf,UAAU,MAAM;KAChB,aAAa,MAAM;KACnB,gBAAgB,MAAM;KACvB,EAAE;KACH,CACD,QAAQ,EAAE,KAAK,qBAAqB;AACnC,QAAI,eAAe,WAAW,EAC5B;AAEF,SAAK,MAAM,SAAS,eAClB,KAAI,OAAO,gBAAgB,MAAM,KAAK,MACpC,EAAE,IAAI;KAAE,QAAQ;KAAc,eAAe;KAAK,CAAC,CAAC,OAAO,CAC5D;KAEH,CACD,WAAW,EAAE,qBACZ,eAAe,KAAK,WAAW;IAC7B,GAAG;IACH,IAAI,IAAI,SAAS;KACf,YAAY,MAAM,GAAG;KACrB,YAAY,MAAM,GAAG;KACrB,SAAS,MAAM,GAAG,UAAU;KAC7B,CAAC;IACH,EAAE,CACJ,CACA,OAAO;;EAMZ,4BAA4B,WAAmB,aAAmB;AAChE,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EAAE,WAAW,+BAA+B,OAC1C,GAAG,IAAI,GAAG,aAAa,KAAK,UAAU,EAAE,GAAG,UAAU,KAAK,aAAa,CAAC,CACzE,CACF,CACF,CACA,mBAAmB,CAAC,YAAY;AAQ/B,WAPc,OAAO,QAAQ,UAAU;AACrC,SAAI,CAAC,MAAM,cACT,QAAO;AAET,YAAO,MAAM,iBAAiB;MAC9B,CAEW,KAAK,WAAW;KAC3B,IAAI,MAAM;KACV,UAAU,MAAM;KAChB,UAAU,MAAM;KAChB,aAAa,MAAM;KACnB,eAAe,MAAM;KACrB,aAAa,MAAM;KACpB,EAAE;KACH,CACD,QAAQ,EAAE,KAAK,qBAAqB;AACnC,SAAK,MAAM,SAAS,eAClB,KAAI,OAAO,gBAAgB,MAAM,KAAK,MACpC,EAAE,IAAI;KAAE,QAAQ;KAAW,aAAa;KAAM,CAAC,CAAC,OAAO,CACxD;KAEH,CACD,WAAW,EAAE,qBAAqB,eAAe,CACjD,OAAO;;EAMZ,yBAAyB,WAAmB,gBAAwB,KAAY;AAC9E,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EAAE,WAAW,+BAA+B,OAC1C,GAAG,IAAI,GAAG,aAAa,KAAK,UAAU,EAAE,GAAG,UAAU,KAAK,aAAa,CAAC,CACzE,CACF,CACF,CACA,mBAAmB,CAAC,YAAY;AAC/B,QAAI,OAAO,WAAW,EACpB,QAAO;IAGT,MAAM,UAAU,uBAAO,IAAI,MAAM;IACjC,MAAM,QAAQ,QAAQ,SAAS;IAC/B,MAAM,YAAY,iBAAiB;IACnC,IAAIA,kBAA+B;AAEnC,SAAK,MAAM,SAAS,QAAQ;AAC1B,SAAI,CAAC,MAAM,cACT,QAAO;KAGT,MAAM,YAAY,MAAM,cAAc,SAAS,GAAG;AAClD,SAAI,aAAa,MACf,QAAO;KAGT,MAAM,UAAU,IAAI,KAAK,UAAU;AACnC,SAAI,CAAC,mBAAmB,UAAU,gBAChC,mBAAkB;;AAItB,WAAO;KACP,CACD,OAAO;;EAOZ,kBAAkB,WAAmB,gBAAiC,KAAY;GAChF,MAAM,UAAU,uBAAO,IAAI,MAAM;GACjC,MAAM,oBAAoB,OAAO,mBAAmB,YAAY,iBAAiB;GACjF,MAAM,YAAY,oBAAoB,iBAAiB,MAAS;AAEhE,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EACG,WAAW,+BAA+B,OAAO;AAChD,QAAI,kBACF,QAAO,GAAG,IACR,GAAG,aAAa,KAAK,UAAU,EAC/B,GAAG,GAAG,GAAG,UAAU,KAAK,UAAU,EAAE,GAAG,UAAU,KAAK,aAAa,CAAC,CACrE;AAEH,WAAO,GAAG,IAAI,GAAG,aAAa,KAAK,UAAU,EAAE,GAAG,UAAU,KAAK,UAAU,CAAC;KAC5E,CACD,OAAO;IAAC;IAAU;IAAe;IAAgB,CAAC,CACtD,CACF,CACA,mBAAmB,CAAC,YAAY;AAC/B,QAAI,OAAO,WAAW,EACpB,QAAO;IAGT,MAAM,QAAQ,QAAQ,SAAS;IAC/B,IAAIC,oBAAiC;IACrC,IAAID,kBAA+B;AAEnC,SAAK,MAAM,SAAS,QAAQ;AAC1B,SAAI,MAAM,WAAW,WAAW;MAC9B,MAAM,cAAc,MAAM;AAC1B,UAAI,CAAC,eAAe,YAAY,SAAS,IAAI,MAC3C,QAAO;AAET,UAAI,CAAC,qBAAqB,cAAc,kBACtC,qBAAoB;AAEtB;;AAGF,SAAI,CAAC,qBAAqB,MAAM,WAAW,aACzC;KAGF,MAAM,gBAAgB,MAAM;AAC5B,SAAI,CAAC,cACH,QAAO;KAGT,MAAM,YAAY,cAAc,SAAS,GAAG;AAC5C,SAAI,aAAa,MACf,QAAO;KAGT,MAAM,UAAU,IAAI,KAAK,UAAU;AACnC,SAAI,CAAC,mBAAmB,UAAU,gBAChC,mBAAkB;;AAItB,QAAI,CAAC,kBACH,QAAO,mBAAmB;AAE5B,QAAI,CAAC,gBACH,QAAO;AAET,WAAO,qBAAqB,kBAAkB,oBAAoB;KAClE,CACD,OAAO;;EAMZ,kBAAkB,SAAmB;AACnC,UAAO,KAAK,UAAU,eAAe,CAClC,QAAQ,EAAE,UACT,IAAI,OAAO,gBAAgB,UAAU,MACnC,EAAE,IAAI;IAAE,QAAQ;IAAa,eAAe,OAAO;IAAE,CAAC,CAAC,OAAO,CAC/D,CACF,CACA,OAAO;;EAMZ,eACE,SACA,OACA,UACA,aACA,KACA;GACA,MAAM,cAAc,WAAW;GAC/B,MAAM,cAAc,YAAY,YAAY,cAAc,EAAE;AAE5D,UAAO,KAAK,UAAU,eAAe,CAClC,QAAQ,EAAE,UAAU;AACnB,QAAI,aAAa;KACf,MAAM,UAAU,YAAY,WAAW,cAAc,EAAE;KACvD,MAAM,UAAU,uBAAO,IAAI,MAAM;KACjC,MAAM,cAAc,IAAI,KAAK,QAAQ,SAAS,GAAG,QAAQ;AACzD,SAAI,OAAO,gBAAgB,UAAU,MACnC,EACG,IAAI;MACH,QAAQ;MACR,UAAU;MACV,eAAe,OAAO;MACtB;MACA;MACD,CAAC,CACD,OAAO,CACX;UAED,KAAI,OAAO,gBAAgB,UAAU,MACnC,EACG,IAAI;KACH,QAAQ;KACR,UAAU;KACV,eAAe,OAAO;KACtB;KACD,CAAC,CACD,OAAO,CACX;KAEH,CACD,OAAO;;EAMZ,mBAAmB,SAAmB;AACpC,UAAO,KAAK,UAAU,eAAe,CAClC,QAAQ,EAAE,UACT,IAAI,OAAO,gBAAgB,UAAU,MACnC,EAAE,IAAI;IAAE,QAAQ;IAAc,eAAe,OAAO;IAAE,CAAC,CAAC,OAAO,CAChE,CACF,CACA,OAAO;;EAMZ,YAAY,SAAmB;AAC7B,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,UAAU,iBAAiB,MAC7B,EAAE,WAAW,YAAY,OAAO,GAAG,MAAM,KAAK,QAAQ,CAAC,CACxD,CACF,CACA,mBAAmB,CAAC,YAAY,UAAU,OAAU,CACpD,OAAO;;EAMZ,oBAAoB,WAAmB;AACrC,UAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,iBAAiB,MACxB,EAAE,WAAW,+BAA+B,OAAO,GAAG,aAAa,KAAK,UAAU,CAAC,CACpF,CACF,CACA,mBAAmB,CAAC,YAAY,OAAO,CACvC,OAAO;;EAEb,CAAC;EACF,CACD,gBAAgB,kBAAkB,EAAE,oBAAoB;AACvD,QAAO,cAAc,EAInB,KAAK,EAAE,mBAAmB,UAA0D,EAAE,EAAE;EACtF,MAAM,aAAa,mBAAmB,aAAa;AAEnD,SAAO,KAAK,UAAU,eAAe,CAClC,UAAU,QACT,IAAI,KAAK,qBAAqB,MAAM;GAClC,IAAI,UAAU,aACV,EAAE,WAAW,4BAA4B,OACvC,GAAG,gBAAgB,KAAK,WAAW,CACpC,GACD,EAAE,WAAW,0BAA0B;AAE3C,aAAU,QAAQ,aAAa,2BAA2B,MAAM;AAChE,OAAI,UAAU,OACZ,WAAU,QAAQ,SAAS,MAAM;AAEnC,UAAO;IACP,CACH,CACA,mBAAmB,CAAC,aACnB,QAAQ,KAAK,WAAW;GACtB,IAAI,MAAM;GACV,cAAc,MAAM;GACpB,OAAO,MAAM;GACb,SAAS,MAAM;GACf,QAAQ,MAAM,UAAU;GACxB,WAAW,MAAM;GAClB,EAAE,CACJ,CACA,OAAO;IAEb,CAAC;EACF,CACD,OAAO;AAUV,eAAsB,6BACpB,UACA,WACiB;AACjB,KAAI;EACF,MAAM,oBAAoB,OAAO,oBAAyD;GACxF,MAAM,UAAU,MAAM,SAAS,UAAU,iBAAkB;AACzD,WAAO,MAAM,KAAK,WAAW,CAC1B,uBAEG,CAAC,SAAS,SAAS,gBAAgB,IAAI,iBAAiB,iBAAiB,CAAC,CAC7E,CACA,WAAW,EAAE,eAAe,CAAC,cAAc,OAAO,CAClD,SAAS;KACZ;AACF,OAAI,CAAC,QACH;GAEF,MAAM,SAAS,SAAS,QAAQ,OAAO,GAAG;AAC1C,UAAO,OAAO,MAAM,OAAO,GAAG,SAAY;;EAG5C,MAAM,UAAU,MAAM,kBAAkB,UAAU;AAClD,MAAI,YAAY,OACd,QAAO;EAKT,MAAM,kBACJ,cAAc,KAAK,eAAe,OAAO,cAAc,eAAe,OAAO,KAAK;AACpF,MAAI,oBAAoB,MAAM;GAC5B,MAAM,SAAS,MAAM,kBAAkB,gBAAgB;AACvD,OAAI,WAAW,OACb,QAAO;;AAIX,SAAO;SACD;AACN,SAAO"}
1
+ {"version":3,"file":"internal-fragment.js","names":["processingNextAt: Date | null"],"sources":["../../src/fragments/internal-fragment.ts"],"sourcesContent":["import { FragmentDefinitionBuilder } from \"@fragno-dev/core\";\nimport type { InstantiatedFragmentFromDefinition } from \"@fragno-dev/core\";\n\nimport {\n DatabaseFragmentDefinitionBuilder,\n type DatabaseHandlerContext,\n type DatabaseRequestStorage,\n type DatabaseServiceContext,\n type FragnoPublicConfigWithDatabase,\n type ImplicitDatabaseDependencies,\n} from \"../db-fragment-definition-builder\";\nimport { isHookStatus, type HookStatus } from \"../hooks/hooks\";\nimport type { Cursor } from \"../query/cursor\";\nimport { dbNow, type DbNow } from \"../query/db-now\";\nimport type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { FragnoId } from \"../schema/create\";\nimport {\n internalSchema,\n SETTINGS_NAMESPACE,\n SETTINGS_TABLE_NAME,\n} from \"./internal-fragment.schema\";\n\ntype AdapterRegistry = {\n listSchemas: () => Array<{\n name: string;\n namespace: string | null;\n version: number;\n tables: string[];\n }>;\n listOutboxFragments: () => Array<{ name: string; mountRoute: string }>;\n isOutboxEnabled: () => boolean;\n resolveSyncCommand: (\n fragmentName: string,\n schemaName: string,\n commandName: string,\n ) => { command: unknown; namespace: string | null } | undefined;\n};\n\nexport class SchemaRegistryCollisionError extends Error {\n readonly code = \"SCHEMA_REGISTRY_COLLISION\" as const;\n readonly namespaceKey: string;\n readonly existing: { name: string; namespace: string | null };\n readonly attempted: { name: string; namespace: string | null };\n\n constructor({\n namespaceKey,\n existing,\n attempted,\n }: {\n namespaceKey: string;\n existing: { name: string; namespace: string | null };\n attempted: { name: string; namespace: string | null };\n }) {\n super(\n `Schema namespace \"${namespaceKey}\" is already owned by \"${existing.name}\" (${existing.namespace ?? \"null\"}).`,\n );\n this.name = \"SchemaRegistryCollisionError\";\n this.namespaceKey = namespaceKey;\n this.existing = existing;\n this.attempted = attempted;\n }\n}\n\nexport type InternalFragmentConfig = {\n registry?: AdapterRegistry;\n};\n\nexport { internalSchema, SETTINGS_NAMESPACE, SETTINGS_TABLE_NAME };\n\nconst INTERNAL_SCHEMA_MIN_VERSION = 4;\nif (internalSchema.version < INTERNAL_SCHEMA_MIN_VERSION) {\n // Keep the internal schema version monotonic after removing fragno_db_schemas.\n internalSchema.version = INTERNAL_SCHEMA_MIN_VERSION;\n}\n\nconst describeHookStatusSource = (event: { id: FragnoId; hookName: string }) =>\n `fragno_hooks id=${event.id} hook=${event.hookName}`;\n\nconst coerceHookStatus = (status: string, context: string): HookStatus => {\n if (isHookStatus(status)) {\n return status;\n }\n throw new Error(`Invalid hook status from database (${context}): ${status}`);\n};\n\nconst DEFAULT_HOOKS_PAGE_SIZE = 50;\n\nconst resolveHookPageSize = (pageSize?: number): number => {\n if (typeof pageSize !== \"number\" || !Number.isInteger(pageSize) || pageSize <= 0) {\n return DEFAULT_HOOKS_PAGE_SIZE;\n }\n return pageSize;\n};\n\n// This uses DatabaseFragmentDefinitionBuilder directly\n// to avoid circular dependency (it doesn't need to link to itself)\nexport const internalFragmentDef = new DatabaseFragmentDefinitionBuilder(\n new FragmentDefinitionBuilder<\n InternalFragmentConfig,\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies<typeof internalSchema>,\n {},\n {},\n {},\n {},\n DatabaseServiceContext<{}>,\n DatabaseHandlerContext,\n DatabaseRequestStorage\n >(\"$fragno-internal-fragment\"),\n internalSchema,\n)\n .providesService(\"settingsService\", ({ defineService }) => {\n return defineService({\n /**\n * Get a setting by namespace and key.\n */\n get(namespace: string, key: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(\n ([result]): { id: FragnoId; key: string; value: string } | undefined =>\n result ?? undefined,\n )\n .build();\n },\n\n /**\n * Set a setting value by namespace and key.\n */\n set(namespace: string, key: string, value: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(([result]) => result)\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult) {\n uow.update(SETTINGS_TABLE_NAME, retrieveResult.id, (b) => b.set({ value }).check());\n } else {\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\n });\n }\n })\n .build();\n },\n\n /**\n * Set a setting value only if it does not already exist.\n */\n setIfMissing(namespace: string, key: string, value: string) {\n const fullKey = `${namespace}.${key}`;\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(SETTINGS_TABLE_NAME, (b) =>\n b.whereIndex(\"unique_key\", (eb) => eb(\"key\", \"=\", fullKey)),\n ),\n )\n .transformRetrieve(([result]) => result)\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult) {\n return;\n }\n uow.create(SETTINGS_TABLE_NAME, {\n key: fullKey,\n value,\n });\n })\n .build();\n },\n\n /**\n * Delete a setting by ID.\n */\n delete(id: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => uow.delete(SETTINGS_TABLE_NAME, id))\n .build();\n },\n });\n })\n .providesService(\"hookService\", ({ defineService }) => {\n return defineService({\n /**\n * Get pending hook events for processing.\n * Returns all pending events for the given namespace that are ready to be processed.\n */\n getPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload as unknown,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\n idempotencyKey: event.nonce,\n }));\n })\n .build();\n },\n\n /**\n * Claim pending hook events for processing.\n * Returns ready events and marks them as processing in the same transaction.\n */\n claimPendingHookEvents(namespace: string) {\n const now = dbNow();\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\n idempotencyKey: event.nonce,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult.length === 0) {\n return;\n }\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: now }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) =>\n retrieveResult.map((event) => ({\n ...event,\n status: \"processing\" as const,\n id: new FragnoId({\n externalId: event.id.externalId,\n internalId: event.id.internalId,\n version: event.id.version + 1,\n }),\n })),\n )\n .build();\n },\n\n /**\n * Claim stale processing hook events for processing.\n * Returns ready events and marks them as processing in the same transaction.\n */\n claimStuckProcessingHookEvents(namespace: string, staleBefore: DbNow) {\n const now = dbNow();\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"processing\"),\n eb.or(eb.isNull(\"lastAttemptAt\"), eb(\"lastAttemptAt\", \"<=\", staleBefore)),\n ),\n ),\n ),\n )\n .transformRetrieve(([events]) => {\n return events.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n payload: event.payload as unknown,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n idempotencyKey: event.nonce,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n createdAt: event.createdAt,\n }));\n })\n .mutate(({ uow, retrieveResult }) => {\n if (retrieveResult.length === 0) {\n return;\n }\n\n for (const event of retrieveResult) {\n uow.update(\"fragno_hooks\", event.id, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: now, nextRetryAt: null }).check(),\n );\n }\n })\n .transform(({ retrieveResult }) => {\n return {\n events: retrieveResult.map((event) => ({\n ...event,\n id: new FragnoId({\n externalId: event.id.externalId,\n internalId: event.id.internalId,\n version: event.id.version + 1,\n }),\n })),\n stuckEvents: retrieveResult.map((event) => ({\n id: event.id,\n hookName: event.hookName,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n lastAttemptAt: event.lastAttemptAt,\n nextRetryAt: event.nextRetryAt,\n })),\n };\n })\n .build();\n },\n\n /**\n * Get the earliest pending hook wake time for a namespace.\n * Optionally considers processing hooks becoming stale when timeoutMinutes is provided.\n */\n getNextHookWakeAt(namespace: string, timeoutMinutes?: number | false) {\n const timeoutMinutesValue =\n typeof timeoutMinutes === \"number\" && timeoutMinutes > 0 ? timeoutMinutes : 0;\n const includeProcessing = timeoutMinutesValue > 0;\n const now = dbNow();\n const timeoutMs = timeoutMinutesValue * 60_000;\n // Sentinel to keep query shape stable when processing checks are disabled.\n const processingStatus = includeProcessing ? \"processing\" : \"__disabled__\";\n const staleBefore = now.plus({ minutes: -timeoutMinutesValue });\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow\n .forSchema(internalSchema)\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.or(eb.isNull(\"nextRetryAt\"), eb(\"nextRetryAt\", \"<=\", now)),\n ),\n )\n .pageSize(1),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_retry\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", \"pending\"),\n eb.isNotNull(\"nextRetryAt\"),\n eb(\"nextRetryAt\", \">\", now),\n ),\n )\n .orderByIndex(\"idx_namespace_status_retry\", \"asc\")\n .pageSize(1)\n .select([\"nextRetryAt\"]),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", processingStatus),\n eb.or(eb.isNull(\"lastAttemptAt\"), eb(\"lastAttemptAt\", \"<=\", staleBefore)),\n ),\n )\n .pageSize(1),\n )\n .find(\"fragno_hooks\", (b) =>\n b\n .whereIndex(\"idx_namespace_status_last_attempt\", (eb) =>\n eb.and(\n eb(\"namespace\", \"=\", namespace),\n eb(\"status\", \"=\", processingStatus),\n eb.isNotNull(\"lastAttemptAt\"),\n eb(\"lastAttemptAt\", \">\", staleBefore),\n ),\n )\n .orderByIndex(\"idx_namespace_status_last_attempt\", \"asc\")\n .pageSize(1)\n .select([\"lastAttemptAt\"]),\n ),\n )\n .transformRetrieve(\n ([pendingImmediate, pendingNext, processingImmediate, processingNext]) => {\n const hasProcessingImmediate = includeProcessing && processingImmediate.length > 0;\n\n if (pendingImmediate.length > 0 || hasProcessingImmediate) {\n return new Date();\n }\n\n const pendingNextAt = pendingNext[0]?.nextRetryAt ?? null;\n let processingNextAt: Date | null = null;\n\n if (includeProcessing) {\n const lastAttemptAt = processingNext[0]?.lastAttemptAt;\n if (lastAttemptAt) {\n processingNextAt = new Date(lastAttemptAt.getTime() + timeoutMs);\n }\n }\n\n if (!pendingNextAt) {\n return processingNextAt ?? null;\n }\n if (!processingNextAt) {\n return pendingNextAt;\n }\n return pendingNextAt <= processingNextAt ? pendingNextAt : processingNextAt;\n },\n )\n .build();\n },\n\n /**\n * Mark a hook event as failed and schedule next retry.\n */\n markHookFailed(eventId: FragnoId, error: string, attempts: number, retryPolicy: RetryPolicy) {\n const newAttempts = attempts + 1;\n const shouldRetry = retryPolicy.shouldRetry(newAttempts - 1);\n const now = dbNow();\n\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) => {\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 .build();\n },\n\n /**\n * Mark a hook event as processing (to prevent concurrent execution).\n */\n markHookProcessing(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .mutate(({ uow }) =>\n uow.update(\"fragno_hooks\", eventId, (b) =>\n b.set({ status: \"processing\", lastAttemptAt: dbNow() }).check(),\n ),\n )\n .build();\n },\n\n /**\n * Get a hook event by ID (for testing/verification purposes).\n */\n getHookById(eventId: FragnoId) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findFirst(\"fragno_hooks\", (b) =>\n b.whereIndex(\"primary\", (eb) => eb(\"id\", \"=\", eventId)),\n ),\n )\n .transformRetrieve(([result]) =>\n result\n ? {\n ...result,\n status: coerceHookStatus(result.status, describeHookStatusSource(result)),\n }\n : undefined,\n )\n .build();\n },\n\n /**\n * Get hook events for a namespace in newest-first order with pagination.\n */\n getHooksByNamespacePage(\n namespace: string,\n options: { cursor?: Cursor | string; pageSize?: number } = {},\n ) {\n const pageSize = resolveHookPageSize(options.pageSize);\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.findWithCursor(\"fragno_hooks\", (b) => {\n const query = b\n .whereIndex(\"idx_namespace_created_at\", (eb) => eb(\"namespace\", \"=\", namespace))\n .orderByIndex(\"idx_namespace_created_at\", \"desc\")\n .pageSize(pageSize);\n\n return options.cursor ? query.after(options.cursor) : query;\n }),\n )\n .transformRetrieve(([page]) => ({\n items: page.items.map((event) => ({\n ...event,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n })),\n cursor: page.cursor,\n hasNextPage: page.hasNextPage,\n }))\n .build();\n },\n\n /**\n * Get all hook events for a namespace (for testing/verification purposes).\n */\n getHooksByNamespace(namespace: string) {\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_hooks\", (b) =>\n b.whereIndex(\"idx_namespace_status_retry\", (eb) => eb(\"namespace\", \"=\", namespace)),\n ),\n )\n .transformRetrieve(([events]) =>\n events.map((event) => ({\n ...event,\n status: coerceHookStatus(event.status, describeHookStatusSource(event)),\n })),\n )\n .build();\n },\n });\n })\n .providesService(\"outboxService\", ({ defineService }) => {\n return defineService({\n /**\n * List outbox entries ordered by versionstamp (ascending).\n */\n list({ afterVersionstamp, limit }: { afterVersionstamp?: string; limit?: number } = {}) {\n const afterValue = afterVersionstamp?.toLowerCase();\n\n return this.serviceTx(internalSchema)\n .retrieve((uow) =>\n uow.find(\"fragno_db_outbox\", (b) => {\n let builder = afterValue\n ? b.whereIndex(\"idx_outbox_versionstamp\", (eb) =>\n eb(\"versionstamp\", \">\", afterValue),\n )\n : b.whereIndex(\"idx_outbox_versionstamp\");\n\n builder = builder.orderByIndex(\"idx_outbox_versionstamp\", \"asc\");\n if (limit !== undefined) {\n builder = builder.pageSize(limit);\n }\n return builder;\n }),\n )\n .transformRetrieve(([entries]) =>\n entries.map((entry) => ({\n id: entry.id,\n versionstamp: entry.versionstamp,\n uowId: entry.uowId,\n payload: entry.payload,\n refMap: entry.refMap ?? undefined,\n createdAt: entry.createdAt,\n })),\n )\n .build();\n },\n });\n })\n .build();\n\n/**\n * Type representing an instantiated internal fragment.\n * This is the fragment that manages Fragno's internal settings table.\n */\nexport type InternalFragmentInstance = InstantiatedFragmentFromDefinition<\n typeof internalFragmentDef\n>;\n\nexport async function getSchemaVersionFromDatabase(\n fragment: InternalFragmentInstance,\n namespace: string,\n): Promise<number> {\n try {\n const readSchemaVersion = async (targetNamespace: string): Promise<number | undefined> => {\n const setting = await fragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () =>\n [fragment.services.settingsService.get(targetNamespace, \"schema_version\")] as const,\n )\n .transform(({ serviceResult: [result] }) => result)\n .execute();\n });\n if (!setting) {\n return undefined;\n }\n const parsed = parseInt(setting.value, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n };\n\n const primary = await readSchemaVersion(namespace);\n if (primary !== undefined) {\n return primary;\n }\n\n // Back-compat: some installs stored internal schema version under a different namespace.\n // Check the alternate key (empty string ↔ schema name) so we find the version either way.\n const legacyNamespace =\n namespace === \"\" ? internalSchema.name : namespace === internalSchema.name ? \"\" : null;\n if (legacyNamespace !== null) {\n const legacy = await readSchemaVersion(legacyNamespace);\n if (legacy !== undefined) {\n return legacy;\n }\n }\n\n return 0;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,81 @@
1
- import { defineRoutes } from "../packages/fragno/dist/api/route.js";
1
+ import { SETTINGS_NAMESPACE, internalSchema } from "./internal-fragment.schema.js";
2
2
  import { internalFragmentDef } from "./internal-fragment.js";
3
+ import { submitSyncRequest } from "../sync/submit.js";
4
+ import { defineRoutes } from "@fragno-dev/core";
3
5
 
4
6
  //#region src/fragments/internal-fragment.routes.ts
5
- const internalFragmentRoutes = defineRoutes(internalFragmentDef).create(({ defineRoute, services }) => [defineRoute({
7
+ const ADAPTER_IDENTITY_KEY = "adapter_identity";
8
+ const passthroughInputSchema = { "~standard": {
9
+ version: 1,
10
+ vendor: "fragno",
11
+ validate: async (value) => ({ value })
12
+ } };
13
+ const getOrCreateAdapterIdentity = async (handlerTx, services) => {
14
+ const readIdentity = async () => handlerTx().withServiceCalls(() => [services.settingsService.get(SETTINGS_NAMESPACE, ADAPTER_IDENTITY_KEY)]).transform(({ serviceResult: [result] }) => result?.value).execute();
15
+ try {
16
+ const existingIdentity = await readIdentity();
17
+ if (existingIdentity) return {
18
+ ok: true,
19
+ value: existingIdentity
20
+ };
21
+ const adapterIdentity = crypto.randomUUID();
22
+ try {
23
+ await handlerTx().withServiceCalls(() => [services.settingsService.setIfMissing(SETTINGS_NAMESPACE, ADAPTER_IDENTITY_KEY, adapterIdentity)]).execute();
24
+ } catch (error) {
25
+ const recoveredIdentity = await readIdentity();
26
+ if (recoveredIdentity) return {
27
+ ok: true,
28
+ value: recoveredIdentity
29
+ };
30
+ throw error;
31
+ }
32
+ return {
33
+ ok: true,
34
+ value: await readIdentity() ?? adapterIdentity
35
+ };
36
+ } catch (error) {
37
+ return {
38
+ ok: false,
39
+ error: { error: {
40
+ code: "SETTINGS_UNAVAILABLE",
41
+ message: "Internal settings table is not available.",
42
+ detail: error instanceof Error ? error.message : void 0
43
+ } }
44
+ };
45
+ }
46
+ };
47
+ const createInternalFragmentDescribeRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, config, services }) => [defineRoute({
48
+ method: "GET",
49
+ path: "/",
50
+ handler: async function(_input, { json }) {
51
+ const registry = config.registry;
52
+ if (!registry) return json({ error: {
53
+ code: "REGISTRY_UNAVAILABLE",
54
+ message: "Adapter registry is not configured."
55
+ } }, { status: 500 });
56
+ const adapterIdentityResult = await getOrCreateAdapterIdentity(() => this.handlerTx(), services);
57
+ if (!adapterIdentityResult.ok) return json(adapterIdentityResult.error, { status: 500 });
58
+ const outboxEnabled = registry.isOutboxEnabled();
59
+ return json({
60
+ adapterIdentity: adapterIdentityResult.value,
61
+ fragments: outboxEnabled ? registry.listOutboxFragments() : [],
62
+ schemas: registry.listSchemas(),
63
+ routes: {
64
+ internal: "/_internal",
65
+ outbox: outboxEnabled ? "/_internal/outbox" : void 0
66
+ }
67
+ });
68
+ }
69
+ })]);
70
+ const createInternalFragmentOutboxRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, services, config }) => [defineRoute({
6
71
  method: "GET",
7
72
  path: "/outbox",
8
73
  handler: async function(input, { json }) {
74
+ const registry = config.registry;
75
+ if (!registry || !registry.isOutboxEnabled()) return json({ error: {
76
+ code: "OUTBOX_UNAVAILABLE",
77
+ message: "Outbox is not enabled for this adapter."
78
+ } }, { status: 404 });
9
79
  const afterVersionstamp = input.query.get("afterVersionstamp") ?? void 0;
10
80
  const limitValue = input.query.get("limit");
11
81
  let limit;
@@ -23,7 +93,72 @@ const internalFragmentRoutes = defineRoutes(internalFragmentDef).create(({ defin
23
93
  })]).transform(({ serviceResult: [result] }) => result).execute());
24
94
  }
25
95
  })]);
96
+ const createInternalFragmentSyncRoutes = () => defineRoutes(internalFragmentDef).create(({ defineRoute, services, config }) => [defineRoute({
97
+ method: "POST",
98
+ path: "/sync",
99
+ inputSchema: passthroughInputSchema,
100
+ handler: async function(input, { json }) {
101
+ const registry = config.registry;
102
+ if (!registry || !registry.isOutboxEnabled()) return json({ error: {
103
+ code: "SYNC_UNAVAILABLE",
104
+ message: "Sync is not enabled for this adapter."
105
+ } }, { status: 404 });
106
+ const adapterIdentityResult = await getOrCreateAdapterIdentity(() => this.handlerTx(), services);
107
+ if (!adapterIdentityResult.ok) return json(adapterIdentityResult.error, { status: 500 });
108
+ const result = await submitSyncRequest(await input.input?.valid(), {
109
+ getAdapterIdentity: async () => adapterIdentityResult.value,
110
+ listOutboxEntries: async (afterVersionstamp) => await this.handlerTx().withServiceCalls(() => [services.outboxService.list({
111
+ afterVersionstamp,
112
+ limit: void 0
113
+ })]).transform(({ serviceResult: [entries] }) => entries).execute(),
114
+ countOutboxMutations: async (afterVersionstamp) => {
115
+ return await this.handlerTx().retrieve(({ forSchema }) => {
116
+ return afterVersionstamp ? forSchema(internalSchema).find("fragno_db_outbox_mutations", (b) => b.whereIndex("idx_outbox_mutations_entry", (eb) => eb("entryVersionstamp", ">", afterVersionstamp)).selectCount()) : forSchema(internalSchema).find("fragno_db_outbox_mutations", (b) => b.whereIndex("idx_outbox_mutations_entry").selectCount());
117
+ }).transformRetrieve(([result$1]) => typeof result$1 === "number" ? result$1 : 0).execute();
118
+ },
119
+ getSyncRequest: async (requestId) => await this.handlerTx().retrieve(({ forSchema }) => forSchema(internalSchema).findFirst("fragno_db_sync_requests", (b) => b.whereIndex("idx_sync_request_id", (eb) => eb("requestId", "=", requestId)))).transformRetrieve(([result$1]) => {
120
+ if (!result$1) return;
121
+ const confirmed = Array.isArray(result$1.confirmedCommandIds) ? result$1.confirmedCommandIds : [];
122
+ const status = result$1.status === "applied" ? "applied" : "conflict";
123
+ return {
124
+ requestId: result$1.requestId,
125
+ status,
126
+ confirmedCommandIds: confirmed,
127
+ conflictCommandId: result$1.conflictCommandId ?? void 0,
128
+ baseVersionstamp: result$1.baseVersionstamp ?? void 0,
129
+ lastVersionstamp: result$1.lastVersionstamp ?? void 0
130
+ };
131
+ }).execute(),
132
+ storeSyncRequest: async (record) => {
133
+ await this.handlerTx().mutate(({ forSchema }) => {
134
+ forSchema(internalSchema).create("fragno_db_sync_requests", {
135
+ requestId: record.requestId,
136
+ status: record.status,
137
+ confirmedCommandIds: record.confirmedCommandIds,
138
+ conflictCommandId: record.conflictCommandId ?? null,
139
+ baseVersionstamp: record.baseVersionstamp ?? null,
140
+ lastVersionstamp: record.lastVersionstamp ?? null
141
+ });
142
+ }).execute();
143
+ },
144
+ resolveCommand: (fragment, schema, name) => registry.resolveSyncCommand(fragment, schema, name),
145
+ createCommandContext: (command) => command.createServerContext?.(this) ?? { mode: "server" },
146
+ executeCommand: async (command, inputPayload, ctx) => {
147
+ await command.handler({
148
+ input: inputPayload,
149
+ ctx,
150
+ tx: (options) => this.handlerTx(options)
151
+ });
152
+ }
153
+ });
154
+ if (result.status === "error") {
155
+ const statusCode = result.statusCode;
156
+ return json(result.body, { status: statusCode });
157
+ }
158
+ return json(result.response);
159
+ }
160
+ })]);
26
161
 
27
162
  //#endregion
28
- export { internalFragmentRoutes };
163
+ export { createInternalFragmentDescribeRoutes, createInternalFragmentOutboxRoutes, createInternalFragmentSyncRoutes };
29
164
  //# sourceMappingURL=internal-fragment.routes.js.map