@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
package/src/mod.ts CHANGED
@@ -1,25 +1,31 @@
1
+ import type { FragnoInstantiatedFragment } from "@fragno-dev/core";
2
+
1
3
  import type { DatabaseAdapter } from "./adapters/adapters";
2
- import type { AnySchema } from "./schema/create";
3
- import type { CursorResult } from "./query/cursor";
4
- import { Cursor } from "./query/cursor";
5
- import { dbNow, type DbNow } from "./query/db-now";
6
- import type { FragnoInstantiatedFragment, AnyFragnoInstantiatedFragment } from "@fragno-dev/core";
7
4
  import type {
8
5
  FragnoPublicConfigWithDatabase,
9
6
  ImplicitDatabaseDependencies,
7
+ DatabaseHandlerContext,
10
8
  } from "./db-fragment-definition-builder";
9
+ import { getSchemaVersionFromDatabase } from "./fragments/internal-fragment";
10
+ import { getInternalFragment } from "./internal/adapter-registry";
11
+ import type { CursorResult } from "./query/cursor";
12
+ import { Cursor } from "./query/cursor";
11
13
  import {
12
- getSchemaVersionFromDatabase,
13
- type InternalFragmentInstance,
14
- } from "./fragments/internal-fragment";
15
- import { resolveDatabaseAdapter } from "./util/default-database-adapter";
14
+ dbInterval,
15
+ dbNow,
16
+ type DbInterval,
17
+ type DbIntervalInput,
18
+ type DbNow,
19
+ } from "./query/db-now";
20
+ import type { AnySchema } from "./schema/create";
16
21
 
17
22
  export type { DatabaseAdapter, CursorResult };
18
23
  export { Cursor };
19
- export { dbNow };
20
- export type { DbNow };
24
+ export { dbNow, dbInterval };
25
+ export type { DbNow, DbInterval, DbIntervalInput };
21
26
  export { InMemoryAdapter, type InMemoryAdapterOptions } from "./adapters/in-memory";
22
27
  export { internalSchema } from "./fragments/internal-fragment";
28
+ export { getInternalFragment } from "./internal/adapter-registry";
23
29
 
24
30
  export const fragnoDatabaseFakeSymbol = "$fragno-database" as const;
25
31
  export const fragnoDatabaseLibraryVersion = "0.1" as const;
@@ -82,6 +88,7 @@ export class FragnoDatabase<const T extends AnySchema, TUOWConfig = void> {
82
88
 
83
89
  export {
84
90
  DatabaseFragmentDefinitionBuilder,
91
+ type DbRoundtripGuardConfig,
85
92
  type FragnoPublicConfigWithDatabase,
86
93
  type DatabaseFragmentContext,
87
94
  type DatabaseServiceContext,
@@ -118,8 +125,10 @@ export {
118
125
  HandlerTxBuilder,
119
126
  createServiceTxBuilder,
120
127
  createHandlerTxBuilder,
128
+ serviceCalls,
121
129
  type TxResult,
122
130
  // Builder context types
131
+ type HandlerTxContext,
123
132
  type ServiceBuilderMutateContext,
124
133
  type HandlerBuilderMutateContext,
125
134
  type BuilderTransformContextWithMutate,
@@ -144,6 +153,7 @@ export type {
144
153
  HookContext,
145
154
  HooksMap,
146
155
  HookFn,
156
+ HookHandlerTx,
147
157
  HookPayload,
148
158
  TriggerHookOptions,
149
159
  DurableHooksProcessingOptions,
@@ -151,10 +161,25 @@ export type {
151
161
  StuckHookProcessingEvent,
152
162
  StuckHookProcessingTimeoutMinutes,
153
163
  } from "./hooks/hooks";
154
- export {
155
- createDurableHooksProcessor,
156
- type DurableHooksProcessor,
157
- } from "./hooks/durable-hooks-processor";
164
+ export { defineSyncCommands } from "./sync/commands";
165
+ export type {
166
+ DurableHookRecord,
167
+ DurableHookStatus,
168
+ DurableHooksAccessor,
169
+ DurableHooksService,
170
+ } from "./durable-hooks";
171
+ export { getDurableHooksService } from "./durable-hooks";
172
+ export type {
173
+ SubmitAppliedResponse,
174
+ SubmitConflictReason,
175
+ SubmitConflictResponse,
176
+ SubmitRequest,
177
+ SubmitResponse,
178
+ SyncCommandDefinition,
179
+ SyncCommandHandler,
180
+ SyncCommandRegistry,
181
+ SyncCommandTxFactory,
182
+ } from "./sync/types";
158
183
 
159
184
  export type AnyFragnoInstantiatedDatabaseFragment<TSchema extends AnySchema = AnySchema> =
160
185
  FragnoInstantiatedFragment<
@@ -165,13 +190,10 @@ export type AnyFragnoInstantiatedDatabaseFragment<TSchema extends AnySchema = An
165
190
  any,
166
191
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
167
192
  any,
193
+ DatabaseHandlerContext,
168
194
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
195
  any,
170
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
- any,
172
- FragnoPublicConfigWithDatabase,
173
- // Ensure the fragment has the internal fragment linked
174
- { _fragno_internal: InternalFragmentInstance } & Record<string, AnyFragnoInstantiatedFragment>
196
+ FragnoPublicConfigWithDatabase
175
197
  >;
176
198
 
177
199
  /**
@@ -199,8 +221,8 @@ export type AnyFragnoInstantiatedDatabaseFragment<TSchema extends AnySchema = An
199
221
  export async function migrate<TSchema extends AnySchema>(
200
222
  fragment: AnyFragnoInstantiatedDatabaseFragment<TSchema>,
201
223
  ): Promise<void> {
202
- const { options, deps, linkedFragments } = fragment.$internal;
203
- const adapter = resolveDatabaseAdapter(options, deps.schema);
224
+ const { deps } = fragment.$internal;
225
+ const adapter = deps.databaseAdapter;
204
226
 
205
227
  // Check if adapter supports prepareMigrations
206
228
  if (!adapter.prepareMigrations) {
@@ -213,11 +235,7 @@ export async function migrate<TSchema extends AnySchema>(
213
235
  const namespace = deps.namespace ?? schema.name;
214
236
 
215
237
  // Step 1: Ensure the internal fragment (settings table) is migrated first
216
- const internalFragment = linkedFragments._fragno_internal;
217
-
218
- if (!internalFragment) {
219
- throw new Error("Internal fragment not found. Please ensure the internal fragment is linked.");
220
- }
238
+ const internalFragment = getInternalFragment(adapter);
221
239
 
222
240
  if (!(await adapter.isConnectionHealthy())) {
223
241
  throw new Error(
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+
2
3
  import type { AnySchema, AnyTable } from "../schema/create";
3
4
 
4
5
  export type NamespaceScope = "suffix" | "schema";
@@ -1,7 +1,7 @@
1
+ import { internalSchema } from "../fragments/internal-fragment.schema";
1
2
  import type { MutationOperation } from "../query/unit-of-work/unit-of-work";
2
3
  import type { AnySchema, AnyTable } from "../schema/create";
3
4
  import { FragnoId, FragnoReference } from "../schema/create";
4
- import { internalSchema } from "../fragments/internal-fragment.schema";
5
5
  import type { OutboxRefLookup, OutboxPayload, OutboxMutation } from "./outbox";
6
6
  import { encodeVersionstamp, versionstampToHex } from "./outbox";
7
7
 
@@ -91,7 +91,7 @@ export function buildOutboxPlan(operations: MutationOperation<AnySchema>[]): Out
91
91
  export function finalizeOutboxPayload(plan: OutboxPlan, transactionVersion: bigint): OutboxPayload {
92
92
  const mutations: OutboxMutation[] = plan.drafts.map((draft, index) => {
93
93
  const versionstamp = versionstampToHex(encodeVersionstamp(transactionVersion, index));
94
- return { ...draft, versionstamp } as OutboxMutation;
94
+ return { ...draft, versionstamp };
95
95
  });
96
96
 
97
97
  return {
@@ -1,16 +1,22 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { defineFragment, instantiate } from "@fragno-dev/core";
3
- import { SQLocalKysely } from "sqlocal/kysely";
2
+
4
3
  import { KyselyPGlite } from "kysely-pglite";
5
- import { PGlite } from "@electric-sql/pglite";
4
+ import { SQLocalKysely } from "sqlocal/kysely";
6
5
  import superjson, { type SuperJSONResult } from "superjson";
7
- import { SqlAdapter } from "../adapters/generic-sql/generic-sql-adapter";
6
+
7
+ import { defineFragment, instantiate } from "@fragno-dev/core";
8
+
9
+ import { PGlite } from "@electric-sql/pglite";
10
+
8
11
  import { PGLiteDriverConfig, SQLocalDriverConfig } from "../adapters/generic-sql/driver-config";
9
- import type { SimpleQueryInterface } from "../query/simple-query-interface";
10
- import { withDatabase } from "../with-database";
12
+ import { SqlAdapter } from "../adapters/generic-sql/generic-sql-adapter";
11
13
  import { internalSchema, type InternalFragmentInstance } from "../fragments/internal-fragment";
14
+ import { getInternalFragment } from "../internal/adapter-registry";
15
+ import type { AnyFragnoInstantiatedDatabaseFragment, DatabaseRequestContext } from "../mod";
16
+ import type { AnySchema } from "../schema/create";
12
17
  import { schema, idColumn, column, referenceColumn, FragnoReference } from "../schema/create";
13
- import type { OutboxConfig, OutboxEntry, OutboxPayload } from "./outbox";
18
+ import { withDatabase } from "../with-database";
19
+ import type { OutboxEntry, OutboxPayload } from "./outbox";
14
20
 
15
21
  const outboxSchema = schema("outbox", (s) => {
16
22
  return s
@@ -33,24 +39,36 @@ const outboxSchema = schema("outbox", (s) => {
33
39
  });
34
40
  });
35
41
 
42
+ const alphaSchema = schema("alpha", (s) =>
43
+ s.addTable("alpha_items", (t) =>
44
+ t.addColumn("id", idColumn()).addColumn("name", column("string")),
45
+ ),
46
+ );
47
+
48
+ const betaSchema = schema("beta", (s) =>
49
+ s.addTable("beta_items", (t) =>
50
+ t.addColumn("id", idColumn()).addColumn("title", column("string")),
51
+ ),
52
+ );
53
+
36
54
  const outboxFragmentName = "outbox-test";
37
55
  const outboxFragmentDef = defineFragment(outboxFragmentName)
38
56
  .extend(withDatabase(outboxSchema))
39
57
  .build();
40
58
 
41
59
  type OutboxAdapterConfig =
42
- | { type: "kysely-sqlite"; outbox?: OutboxConfig }
43
- | { type: "kysely-pglite"; outbox?: OutboxConfig };
60
+ | { type: "kysely-sqlite"; outboxEnabled?: boolean }
61
+ | { type: "kysely-pglite"; outboxEnabled?: boolean };
44
62
 
45
63
  type OutboxTestContext = {
46
- db: SimpleQueryInterface<typeof outboxSchema>;
64
+ fragment: AnyFragnoInstantiatedDatabaseFragment<typeof outboxSchema>;
47
65
  internalFragment: InternalFragmentInstance;
48
66
  cleanup: () => Promise<void>;
49
67
  };
50
68
 
51
69
  async function migrateSchema(
52
70
  adapter: SqlAdapter,
53
- schemaToMigrate: typeof outboxSchema | typeof internalSchema,
71
+ schemaToMigrate: AnySchema,
54
72
  namespace: string,
55
73
  ): Promise<void> {
56
74
  const migrations = adapter.prepareMigrations(schemaToMigrate, namespace);
@@ -66,7 +84,6 @@ async function createAdapter(config: OutboxAdapterConfig): Promise<{
66
84
  const adapter = new SqlAdapter({
67
85
  dialect,
68
86
  driverConfig: new SQLocalDriverConfig(),
69
- outbox: config.outbox,
70
87
  });
71
88
 
72
89
  await migrateSchema(adapter, internalSchema, "");
@@ -85,7 +102,6 @@ async function createAdapter(config: OutboxAdapterConfig): Promise<{
85
102
  const adapter = new SqlAdapter({
86
103
  dialect,
87
104
  driverConfig: new PGLiteDriverConfig(),
88
- outbox: config.outbox,
89
105
  });
90
106
 
91
107
  await migrateSchema(adapter, internalSchema, "");
@@ -104,13 +120,15 @@ async function buildOutboxTest(adapterConfig: OutboxAdapterConfig): Promise<Outb
104
120
  const fragment = instantiate(outboxFragmentDef)
105
121
  .withConfig({})
106
122
  .withRoutes([])
107
- .withOptions({ databaseAdapter: adapter })
123
+ .withOptions({
124
+ databaseAdapter: adapter,
125
+ outbox: adapterConfig.outboxEnabled ? { enabled: true } : undefined,
126
+ })
108
127
  .build();
109
- const deps = fragment.$internal.deps as { db: SimpleQueryInterface<typeof outboxSchema> };
110
128
 
111
129
  return {
112
- db: deps.db,
113
- internalFragment: fragment.$internal.linkedFragments._fragno_internal,
130
+ fragment,
131
+ internalFragment: getInternalFragment(adapter),
114
132
  cleanup,
115
133
  };
116
134
  }
@@ -119,7 +137,7 @@ async function listOutbox(
119
137
  internalFragment: InternalFragmentInstance,
120
138
  options?: { afterVersionstamp?: string; limit?: number },
121
139
  ): Promise<OutboxEntry[]> {
122
- return internalFragment.inContext(async function () {
140
+ return internalFragment.inContext(async function (this: DatabaseRequestContext) {
123
141
  return (await this.handlerTx()
124
142
  .withServiceCalls(() => [internalFragment.services.outboxService.list(options)] as const)
125
143
  .transform(({ serviceResult: [result] }) => result)
@@ -127,39 +145,98 @@ async function listOutbox(
127
145
  });
128
146
  }
129
147
 
148
+ async function listOutboxMutations(internalFragment: InternalFragmentInstance): Promise<
149
+ Array<{
150
+ entryVersionstamp: string;
151
+ mutationVersionstamp: string;
152
+ uowId: string;
153
+ schema: string;
154
+ table: string;
155
+ externalId: string;
156
+ op: string;
157
+ }>
158
+ > {
159
+ return internalFragment.inContext(async function (this: DatabaseRequestContext) {
160
+ return await this.handlerTx()
161
+ .retrieve(({ forSchema }) =>
162
+ forSchema(internalSchema).find("fragno_db_outbox_mutations", (b) =>
163
+ b
164
+ .whereIndex("idx_outbox_mutations_entry")
165
+ .orderByIndex("idx_outbox_mutations_entry", "asc"),
166
+ ),
167
+ )
168
+ .transformRetrieve(([result]) => result)
169
+ .execute();
170
+ });
171
+ }
172
+
173
+ async function createUser(
174
+ fragment: AnyFragnoInstantiatedDatabaseFragment<typeof outboxSchema>,
175
+ email: string,
176
+ ) {
177
+ return fragment.inContext(async function (this: DatabaseRequestContext) {
178
+ await this.handlerTx()
179
+ .mutate(({ forSchema }) => forSchema(outboxSchema).create("users", { email }))
180
+ .execute();
181
+
182
+ const user = await this.handlerTx()
183
+ .retrieve(({ forSchema }) =>
184
+ forSchema(outboxSchema).findFirst("users", (b) =>
185
+ b.whereIndex("idx_users_email", (eb) => eb("email", "=", email)),
186
+ ),
187
+ )
188
+ .transformRetrieve(([result]) => result)
189
+ .execute();
190
+
191
+ if (!user) {
192
+ throw new Error("Expected user to be created.");
193
+ }
194
+
195
+ return user.id;
196
+ });
197
+ }
198
+
199
+ async function createPost(
200
+ fragment: AnyFragnoInstantiatedDatabaseFragment<typeof outboxSchema>,
201
+ title: string,
202
+ authorId: FragnoReference,
203
+ ) {
204
+ return fragment.inContext(async function (this: DatabaseRequestContext) {
205
+ return await this.handlerTx()
206
+ .mutate(({ forSchema }) => forSchema(outboxSchema).create("posts", { title, authorId }))
207
+ .transform(({ mutateResult }) => mutateResult)
208
+ .execute();
209
+ });
210
+ }
211
+
130
212
  const adapterConfigs = [{ type: "kysely-sqlite" as const }, { type: "kysely-pglite" as const }];
131
213
 
132
214
  describe("Fragno DB Outbox", () => {
133
215
  it("does not write outbox entries when disabled", async () => {
134
- const { db, internalFragment, cleanup } = await buildOutboxTest({
216
+ const { fragment, internalFragment, cleanup } = await buildOutboxTest({
135
217
  type: "kysely-sqlite",
136
218
  });
137
219
 
138
- await db.create("users", { email: "disabled@example.com" });
220
+ await createUser(fragment, "disabled@example.com");
139
221
 
140
222
  const entries = await listOutbox(internalFragment);
141
223
  expect(entries).toHaveLength(0);
224
+ const mutations = await listOutboxMutations(internalFragment);
225
+ expect(mutations).toHaveLength(0);
142
226
 
143
227
  await cleanup();
144
228
  });
145
229
 
146
230
  it("stores refMap placeholders and lists entries in order", async () => {
147
- const { db, internalFragment, cleanup } = await buildOutboxTest({
231
+ const { fragment, internalFragment, cleanup } = await buildOutboxTest({
148
232
  type: "kysely-sqlite",
149
- outbox: { enabled: true },
233
+ outboxEnabled: true,
150
234
  });
151
235
 
152
- await db.create("users", { email: "alpha@example.com" });
153
- const user = await db.findFirst("users", (b) =>
154
- b.whereIndex("idx_users_email", (eb) => eb("email", "=", "alpha@example.com")),
155
- );
156
- expect(user).not.toBeNull();
157
- expect(user?.id.internalId).toBeDefined();
236
+ const userId = await createUser(fragment, "alpha@example.com");
237
+ expect(userId.internalId).toBeDefined();
158
238
 
159
- await db.create("posts", {
160
- title: "Hello",
161
- authorId: FragnoReference.fromInternal(user!.id.internalId!),
162
- });
239
+ await createPost(fragment, "Hello", FragnoReference.fromInternal(userId.internalId!));
163
240
 
164
241
  const entries = await listOutbox(internalFragment);
165
242
  expect(entries).toHaveLength(2);
@@ -183,10 +260,10 @@ describe("Fragno DB Outbox", () => {
183
260
  authorId: { __fragno_ref: "0.authorId" },
184
261
  });
185
262
  expect(entries[1].refMap).toEqual({
186
- "0.authorId": user!.id.externalId,
263
+ "0.authorId": userId.externalId,
187
264
  });
188
265
 
189
- await internalFragment.inContext(async function () {
266
+ await internalFragment.inContext(async function (this: DatabaseRequestContext) {
190
267
  await this.handlerTx()
191
268
  .withServiceCalls(() => [
192
269
  internalFragment.services.settingsService.set("outbox-test", "noop", "1"),
@@ -200,19 +277,60 @@ describe("Fragno DB Outbox", () => {
200
277
  await cleanup();
201
278
  });
202
279
 
280
+ it("writes mutation log rows for each outbox entry", async () => {
281
+ const { fragment, internalFragment, cleanup } = await buildOutboxTest({
282
+ type: "kysely-sqlite",
283
+ outboxEnabled: true,
284
+ });
285
+
286
+ const userId = await createUser(fragment, "log-alpha@example.com");
287
+ expect(userId.internalId).toBeDefined();
288
+
289
+ await createPost(fragment, "Log", FragnoReference.fromInternal(userId.internalId!));
290
+
291
+ const entries = await listOutbox(internalFragment);
292
+ const mutations = await listOutboxMutations(internalFragment);
293
+
294
+ expect(entries).toHaveLength(2);
295
+ expect(mutations).toHaveLength(2);
296
+
297
+ const entryByVersion = new Map(entries.map((entry) => [entry.versionstamp, entry]));
298
+
299
+ for (const entry of entries) {
300
+ const payload = superjson.deserialize(entry.payload as SuperJSONResult) as OutboxPayload;
301
+ expect(payload.mutations).toHaveLength(1);
302
+ }
303
+
304
+ for (const mutationRow of mutations) {
305
+ const entry = entryByVersion.get(mutationRow.entryVersionstamp);
306
+ expect(entry).toBeDefined();
307
+ const payload = superjson.deserialize(entry!.payload as SuperJSONResult) as OutboxPayload;
308
+ const mutation = payload.mutations[0];
309
+ expect(mutationRow.mutationVersionstamp).toBe(mutation.versionstamp);
310
+ expect(mutationRow.uowId).toBe(entry!.uowId);
311
+ expect(mutationRow.schema).toBe(mutation.schema);
312
+ expect(mutationRow.table).toBe(mutation.table);
313
+ expect(mutationRow.externalId).toBe(mutation.externalId);
314
+ expect(mutationRow.op).toBe(mutation.op);
315
+ }
316
+
317
+ await cleanup();
318
+ });
319
+
203
320
  it("orders outbox entries by commit order across concurrent UOWs", async () => {
204
- const { db, internalFragment, cleanup } = await buildOutboxTest({
321
+ const { fragment, internalFragment, cleanup } = await buildOutboxTest({
205
322
  type: "kysely-sqlite",
206
- outbox: { enabled: true },
323
+ outboxEnabled: true,
207
324
  });
208
325
 
209
- const uow1 = db.createUnitOfWork("uow-1");
210
- const uow2 = db.createUnitOfWork("uow-2");
326
+ const createUow = fragment.$internal.deps.createUnitOfWork;
327
+ const uow1 = createUow();
328
+ const uow2 = createUow();
211
329
  const uow1Id = uow1.idempotencyKey;
212
330
  const uow2Id = uow2.idempotencyKey;
213
331
 
214
- uow1.create("users", { email: "order-1@example.com" });
215
- uow2.create("users", { email: "order-2@example.com" });
332
+ uow1.forSchema(outboxSchema).create("users", { email: "order-1@example.com" });
333
+ uow2.forSchema(outboxSchema).create("users", { email: "order-2@example.com" });
216
334
 
217
335
  const completionOrder: string[] = [];
218
336
  await Promise.all([
@@ -227,24 +345,77 @@ describe("Fragno DB Outbox", () => {
227
345
  await cleanup();
228
346
  });
229
347
 
348
+ it("only writes outbox entries for schemas that opt in", async () => {
349
+ const { dialect } = new SQLocalKysely(":memory:");
350
+ const adapter = new SqlAdapter({
351
+ dialect,
352
+ driverConfig: new SQLocalDriverConfig(),
353
+ });
354
+
355
+ try {
356
+ await migrateSchema(adapter, internalSchema, "");
357
+ await migrateSchema(adapter, alphaSchema, alphaSchema.name);
358
+ await migrateSchema(adapter, betaSchema, betaSchema.name);
359
+
360
+ const alphaDef = defineFragment("alpha-fragment").extend(withDatabase(alphaSchema)).build();
361
+ const betaDef = defineFragment("beta-fragment").extend(withDatabase(betaSchema)).build();
362
+
363
+ const alphaFragment = instantiate(alphaDef)
364
+ .withConfig({})
365
+ .withRoutes([])
366
+ .withOptions({ databaseAdapter: adapter, outbox: { enabled: true } })
367
+ .build();
368
+
369
+ const betaFragment = instantiate(betaDef)
370
+ .withConfig({})
371
+ .withRoutes([])
372
+ .withOptions({ databaseAdapter: adapter })
373
+ .build();
374
+
375
+ await alphaFragment.inContext(async function (this: DatabaseRequestContext) {
376
+ await this.handlerTx()
377
+ .mutate(({ forSchema }) =>
378
+ forSchema(alphaSchema).create("alpha_items", { name: "alpha" }),
379
+ )
380
+ .execute();
381
+ });
382
+
383
+ await betaFragment.inContext(async function (this: DatabaseRequestContext) {
384
+ await this.handlerTx()
385
+ .mutate(({ forSchema }) => forSchema(betaSchema).create("beta_items", { title: "beta" }))
386
+ .execute();
387
+ });
388
+
389
+ const entries = await listOutbox(getInternalFragment(adapter));
390
+ const mutationSchemas = entries
391
+ .map((entry) => superjson.deserialize(entry.payload as SuperJSONResult) as OutboxPayload)
392
+ .flatMap((payload) => payload.mutations.map((mutation) => mutation.schema));
393
+
394
+ expect(mutationSchemas).toContain(alphaSchema.name);
395
+ expect(mutationSchemas).not.toContain(betaSchema.name);
396
+ } finally {
397
+ await adapter.close();
398
+ }
399
+ });
400
+
230
401
  describe.each(adapterConfigs)("adapter opt-in (%s)", (adapterConfig) => {
231
402
  it("writes outbox rows only when enabled", async () => {
232
- const { db, internalFragment, cleanup } = await buildOutboxTest(adapterConfig);
403
+ const { fragment, internalFragment, cleanup } = await buildOutboxTest(adapterConfig);
233
404
 
234
- await db.create("users", { email: "disabled@example.com" });
405
+ await createUser(fragment, "disabled@example.com");
235
406
  const disabledEntries = await listOutbox(internalFragment);
236
407
  expect(disabledEntries).toHaveLength(0);
237
408
  await cleanup();
238
409
 
239
410
  const {
240
- db: enabledDb,
411
+ fragment: enabledFragment,
241
412
  internalFragment: enabledInternal,
242
413
  cleanup: enabledCleanup,
243
414
  } = await buildOutboxTest({
244
415
  ...adapterConfig,
245
- outbox: { enabled: true },
416
+ outboxEnabled: true,
246
417
  });
247
- await enabledDb.create("users", { email: "enabled@example.com" });
418
+ await createUser(enabledFragment, "enabled@example.com");
248
419
  const enabledEntries = await listOutbox(enabledInternal);
249
420
  expect(enabledEntries).toHaveLength(1);
250
421
  await enabledCleanup();
@@ -1,7 +1,9 @@
1
- import type { AnyTable, FragnoId } from "../schema/create";
1
+ import type { MutationOperation } from "../query/unit-of-work/unit-of-work";
2
+ import type { AnySchema, AnyTable, FragnoId } from "../schema/create";
2
3
 
3
4
  export type OutboxConfig = {
4
5
  enabled: boolean;
6
+ shouldInclude?: (operation: MutationOperation<AnySchema>) => boolean;
5
7
  };
6
8
 
7
9
  export type OutboxVersionstampStrategy =
@@ -1,5 +1,5 @@
1
- import type { AnyColumn } from "../schema/create";
2
1
  import { createId } from "../id";
2
+ import type { AnyColumn } from "../schema/create";
3
3
 
4
4
  export type RuntimeDefaultContext = {
5
5
  now?: () => Date;
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
+
2
3
  import { column, idColumn, schema } from "../schema/create";
3
4
  import { createBuilder, createIndexedBuilder } from "./condition-builder";
4
5
 
@@ -101,6 +102,20 @@ describe("ConditionBuilder", () => {
101
102
  });
102
103
  });
103
104
 
105
+ it("should expose db now helper", () => {
106
+ const builder = createBuilder(usersTable.columns);
107
+
108
+ const now = builder.now();
109
+ expect(now).toMatchObject({ tag: "db-now" });
110
+ expect(typeof now.plus).toBe("function");
111
+ expect(now.plus({ seconds: 1 })).toMatchObject({ tag: "db-now", offsetMs: 1000 });
112
+ expect(now.plus({ seconds: 1 }).plus({ minutes: 1 })).toMatchObject({
113
+ tag: "db-now",
114
+ offsetMs: 61_000,
115
+ });
116
+ expect(builder.interval({ minutes: 1 })).toEqual({ tag: "db-interval", ms: 60_000 });
117
+ });
118
+
104
119
  it("should support boolean columns", () => {
105
120
  const postsTable = testSchema.tables.posts;
106
121
  const builder = createBuilder(postsTable.columns);
@@ -1,4 +1,5 @@
1
1
  import type { AnyColumn, FragnoId, IdColumn } from "../schema/create";
2
+ import { dbInterval, dbNow, type DbInterval, type DbIntervalInput, type DbNow } from "./db-now";
2
3
 
3
4
  export type ConditionType = "compare" | "and" | "or" | "not";
4
5
 
@@ -54,6 +55,8 @@ export type ConditionBuilder<Columns extends Record<string, AnyColumn>> = {
54
55
 
55
56
  isNull: (a: keyof Columns) => Condition;
56
57
  isNotNull: (a: keyof Columns) => Condition;
58
+ now: () => DbNow;
59
+ interval: (input: DbIntervalInput) => DbInterval;
57
60
  };
58
61
 
59
62
  // replacement for `like` (Prisma doesn't support `like`)
@@ -117,6 +120,8 @@ export function createBuilder<Columns extends Record<string, AnyColumn>>(
117
120
 
118
121
  builder.isNull = (a) => builder(a, "is", null);
119
122
  builder.isNotNull = (a) => builder(a, "is not", null);
123
+ builder.now = () => dbNow();
124
+ builder.interval = (input) => dbInterval(input);
120
125
  builder.not = (condition) => {
121
126
  if (typeof condition === "boolean") {
122
127
  return !condition;
@@ -249,6 +254,8 @@ export function createIndexedBuilder<Columns extends Record<string, AnyColumn>>(
249
254
 
250
255
  builder.isNull = (a) => builder(a, "is", null);
251
256
  builder.isNotNull = (a) => builder(a, "is not", null);
257
+ builder.now = () => dbNow();
258
+ builder.interval = (input) => dbInterval(input);
252
259
  builder.not = (condition) => {
253
260
  if (typeof condition === "boolean") {
254
261
  return !condition;