@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
@@ -0,0 +1,450 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
2
+
3
+ import Database from "better-sqlite3";
4
+ import { SqliteDialect } from "kysely";
5
+
6
+ import { BetterSQLite3DriverConfig } from "../adapters/generic-sql/driver-config";
7
+ import { buildFindOptions } from "../query/orm/orm";
8
+ import { schema, column, idColumn, referenceColumn } from "../schema/create";
9
+ import { sql } from "../sql-driver/sql";
10
+ import { SqlDriverAdapter } from "../sql-driver/sql-driver-adapter";
11
+ import { checkConflicts } from "./conflict-checker";
12
+
13
+ const testSchema = schema("test", (s) => {
14
+ return s
15
+ .addTable("users", (t) => {
16
+ return t.addColumn("id", idColumn()).addColumn("name", column("string"));
17
+ })
18
+ .addTable("posts", (t) => {
19
+ return t
20
+ .addColumn("id", idColumn())
21
+ .addColumn("title", column("string"))
22
+ .addColumn("userId", referenceColumn());
23
+ })
24
+ .addReference("author", {
25
+ type: "one",
26
+ from: { table: "posts", column: "userId" },
27
+ to: { table: "users", column: "id" },
28
+ });
29
+ });
30
+
31
+ const baseVersionstamp = "000000000000000000000001";
32
+ const nextVersionstamp = "000000000000000000000002";
33
+
34
+ const createDriver = () => {
35
+ const database = new Database(":memory:");
36
+ const dialect = new SqliteDialect({ database });
37
+ const driver = new SqlDriverAdapter(dialect);
38
+
39
+ return { database, dialect, driver };
40
+ };
41
+
42
+ const setupTables = async (driver: SqlDriverAdapter, dialect: SqliteDialect) => {
43
+ await driver.executeQuery(
44
+ sql`CREATE TABLE users (
45
+ id text,
46
+ name text,
47
+ _internalId integer,
48
+ _version integer
49
+ );`.compile(dialect),
50
+ );
51
+
52
+ await driver.executeQuery(
53
+ sql`CREATE TABLE posts (
54
+ id text,
55
+ title text,
56
+ userId integer,
57
+ _internalId integer,
58
+ _version integer
59
+ );`.compile(dialect),
60
+ );
61
+
62
+ await driver.executeQuery(
63
+ sql`CREATE TABLE fragno_db_outbox_mutations (
64
+ id text,
65
+ entryVersionstamp text,
66
+ mutationVersionstamp text,
67
+ uowId text,
68
+ schema text,
69
+ "table" text,
70
+ externalId text,
71
+ op text
72
+ );`.compile(dialect),
73
+ );
74
+ };
75
+
76
+ describe("checkConflicts", () => {
77
+ let database: InstanceType<typeof Database>;
78
+ let dialect: SqliteDialect;
79
+ let driver: SqlDriverAdapter;
80
+ const insertMutation = async ({
81
+ id,
82
+ schema: schemaName,
83
+ table,
84
+ externalId,
85
+ entryVersionstamp = nextVersionstamp,
86
+ }: {
87
+ id: string;
88
+ schema: string;
89
+ table: string;
90
+ externalId: string;
91
+ entryVersionstamp?: string;
92
+ }) => {
93
+ await driver.executeQuery(
94
+ sql`INSERT INTO fragno_db_outbox_mutations (
95
+ id, entryVersionstamp, mutationVersionstamp, uowId, schema, "table", externalId, op
96
+ ) VALUES (
97
+ ${id}, ${entryVersionstamp}, ${entryVersionstamp}, ${`uow_${id}`}, ${schemaName}, ${table}, ${externalId}, ${"update"}
98
+ );`.compile(dialect),
99
+ );
100
+ };
101
+
102
+ beforeEach(async () => {
103
+ const setup = createDriver();
104
+ database = setup.database;
105
+ dialect = setup.dialect;
106
+ driver = setup.driver;
107
+ await setupTables(driver, dialect);
108
+ });
109
+
110
+ afterEach(async () => {
111
+ await driver.destroy();
112
+ database.close();
113
+ });
114
+
115
+ test("detects conflicts from read keys", async () => {
116
+ await driver.executeQuery(
117
+ sql`INSERT INTO users (id, name, _internalId, _version)
118
+ VALUES (${"u1"}, ${"Ava"}, 1, 0);`.compile(dialect),
119
+ );
120
+
121
+ await insertMutation({
122
+ id: "m1",
123
+ schema: "",
124
+ table: "users",
125
+ externalId: "u1",
126
+ });
127
+
128
+ const hasConflict = await checkConflicts(
129
+ {
130
+ baseVersionstamp,
131
+ readKeys: [{ schema: "", table: "users", externalId: "u1" }],
132
+ writeKeys: [],
133
+ readScopes: [],
134
+ },
135
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
136
+ );
137
+
138
+ expect(hasConflict).toBe(true);
139
+ });
140
+
141
+ test("detects conflicts from read scopes with joins", async () => {
142
+ await driver.executeQuery(
143
+ sql`INSERT INTO users (id, name, _internalId, _version)
144
+ VALUES (${"u1"}, ${"Ava"}, 1, 0);`.compile(dialect),
145
+ );
146
+
147
+ await driver.executeQuery(
148
+ sql`INSERT INTO posts (id, title, userId, _internalId, _version)
149
+ VALUES (${"p1"}, ${"Hello"}, 1, 10, 0);`.compile(dialect),
150
+ );
151
+
152
+ await insertMutation({
153
+ id: "m2",
154
+ schema: "",
155
+ table: "users",
156
+ externalId: "u1",
157
+ });
158
+
159
+ const options = buildFindOptions(testSchema.tables.posts, {
160
+ where: (eb) => eb("title", "=", "Hello"),
161
+ join: (jb) => {
162
+ jb["author"]();
163
+ },
164
+ });
165
+
166
+ if (!options) {
167
+ throw new Error("Expected join options to compile.");
168
+ }
169
+
170
+ const hasConflict = await checkConflicts(
171
+ {
172
+ baseVersionstamp,
173
+ readKeys: [],
174
+ writeKeys: [],
175
+ readScopes: [
176
+ {
177
+ schema: "",
178
+ table: testSchema.tables.posts,
179
+ indexName: "primary",
180
+ condition: options.where,
181
+ joins: options.join,
182
+ },
183
+ ],
184
+ },
185
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
186
+ );
187
+
188
+ expect(hasConflict).toBe(true);
189
+ });
190
+
191
+ test("honors unknownReadStrategy ignore", async () => {
192
+ await insertMutation({
193
+ id: "m3",
194
+ schema: "",
195
+ table: "users",
196
+ externalId: "u1",
197
+ });
198
+
199
+ const hasConflict = await checkConflicts(
200
+ {
201
+ baseVersionstamp,
202
+ readKeys: [],
203
+ writeKeys: [],
204
+ readScopes: [],
205
+ unknownReads: [{ schema: "", table: "users" }],
206
+ unknownReadStrategy: "ignore",
207
+ },
208
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
209
+ );
210
+
211
+ expect(hasConflict).toBe(false);
212
+ });
213
+
214
+ test("ignores empty externalIds in read/write keys", async () => {
215
+ await insertMutation({
216
+ id: "m4",
217
+ schema: "",
218
+ table: "users",
219
+ externalId: "u1",
220
+ });
221
+
222
+ const hasConflict = await checkConflicts(
223
+ {
224
+ baseVersionstamp,
225
+ readKeys: [{ schema: "", table: "users", externalId: " " }],
226
+ writeKeys: [{ schema: "", table: "users", externalId: "" }],
227
+ readScopes: [],
228
+ },
229
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
230
+ );
231
+
232
+ expect(hasConflict).toBe(false);
233
+ });
234
+
235
+ test("detects conflicts from write keys", async () => {
236
+ await insertMutation({
237
+ id: "m5",
238
+ schema: "",
239
+ table: "users",
240
+ externalId: "u1",
241
+ });
242
+
243
+ const hasConflict = await checkConflicts(
244
+ {
245
+ baseVersionstamp,
246
+ readKeys: [],
247
+ writeKeys: [{ schema: "", table: "users", externalId: "u1" }],
248
+ readScopes: [],
249
+ },
250
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
251
+ );
252
+
253
+ expect(hasConflict).toBe(true);
254
+ });
255
+
256
+ test("skips mutations at the base versionstamp", async () => {
257
+ await insertMutation({
258
+ id: "m6",
259
+ schema: "",
260
+ table: "users",
261
+ externalId: "u1",
262
+ entryVersionstamp: baseVersionstamp,
263
+ });
264
+
265
+ const hasConflict = await checkConflicts(
266
+ {
267
+ baseVersionstamp,
268
+ readKeys: [{ schema: "", table: "users", externalId: "u1" }],
269
+ writeKeys: [],
270
+ readScopes: [],
271
+ },
272
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
273
+ );
274
+
275
+ expect(hasConflict).toBe(false);
276
+ });
277
+
278
+ test("defaults unknown read strategy to conflict", async () => {
279
+ await insertMutation({
280
+ id: "m7",
281
+ schema: "",
282
+ table: "users",
283
+ externalId: "u1",
284
+ });
285
+
286
+ const hasConflict = await checkConflicts(
287
+ {
288
+ baseVersionstamp,
289
+ readKeys: [],
290
+ writeKeys: [],
291
+ readScopes: [],
292
+ unknownReads: [{ schema: "", table: "posts" }],
293
+ },
294
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
295
+ );
296
+
297
+ expect(hasConflict).toBe(true);
298
+ });
299
+
300
+ test("unknownReadStrategy table only checks matching tables", async () => {
301
+ await insertMutation({
302
+ id: "m8",
303
+ schema: "",
304
+ table: "users",
305
+ externalId: "u1",
306
+ });
307
+
308
+ const shouldSkip = await checkConflicts(
309
+ {
310
+ baseVersionstamp,
311
+ readKeys: [],
312
+ writeKeys: [],
313
+ readScopes: [],
314
+ unknownReads: [{ schema: "", table: "posts" }],
315
+ unknownReadStrategy: "table",
316
+ },
317
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
318
+ );
319
+
320
+ const shouldConflict = await checkConflicts(
321
+ {
322
+ baseVersionstamp,
323
+ readKeys: [],
324
+ writeKeys: [],
325
+ readScopes: [],
326
+ unknownReads: [{ schema: "", table: "users" }],
327
+ unknownReadStrategy: "table",
328
+ },
329
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
330
+ );
331
+
332
+ expect(shouldSkip).toBe(false);
333
+ expect(shouldConflict).toBe(true);
334
+ });
335
+
336
+ test("read scopes without matching rows do not conflict", async () => {
337
+ await driver.executeQuery(
338
+ sql`INSERT INTO posts (id, title, userId, _internalId, _version)
339
+ VALUES (${"p1"}, ${"Hello"}, 1, 10, 0);`.compile(dialect),
340
+ );
341
+
342
+ await insertMutation({
343
+ id: "m9",
344
+ schema: "",
345
+ table: "posts",
346
+ externalId: "p1",
347
+ });
348
+
349
+ const options = buildFindOptions(testSchema.tables.posts, {
350
+ where: (eb) => eb("title", "=", "Missing"),
351
+ });
352
+
353
+ if (!options) {
354
+ throw new Error("Expected options to compile.");
355
+ }
356
+
357
+ const hasConflict = await checkConflicts(
358
+ {
359
+ baseVersionstamp,
360
+ readKeys: [],
361
+ writeKeys: [],
362
+ readScopes: [
363
+ {
364
+ schema: "",
365
+ table: testSchema.tables.posts,
366
+ indexName: "primary",
367
+ condition: options.where,
368
+ joins: options.join,
369
+ },
370
+ ],
371
+ },
372
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
373
+ );
374
+
375
+ expect(hasConflict).toBe(false);
376
+ });
377
+
378
+ test("returns conflict when any read scope matches", async () => {
379
+ await driver.executeQuery(
380
+ sql`INSERT INTO posts (id, title, userId, _internalId, _version)
381
+ VALUES (${"p1"}, ${"Hello"}, 1, 10, 0);`.compile(dialect),
382
+ );
383
+
384
+ await insertMutation({
385
+ id: "m10",
386
+ schema: "",
387
+ table: "posts",
388
+ externalId: "p1",
389
+ });
390
+
391
+ const noMatch = buildFindOptions(testSchema.tables.posts, {
392
+ where: (eb) => eb("title", "=", "Missing"),
393
+ });
394
+ const match = buildFindOptions(testSchema.tables.posts, {
395
+ where: (eb) => eb("title", "=", "Hello"),
396
+ });
397
+
398
+ if (!noMatch || !match) {
399
+ throw new Error("Expected options to compile.");
400
+ }
401
+
402
+ const hasConflict = await checkConflicts(
403
+ {
404
+ baseVersionstamp,
405
+ readKeys: [],
406
+ writeKeys: [],
407
+ readScopes: [
408
+ {
409
+ schema: "",
410
+ table: testSchema.tables.posts,
411
+ indexName: "primary",
412
+ condition: noMatch.where,
413
+ joins: noMatch.join,
414
+ },
415
+ {
416
+ schema: "",
417
+ table: testSchema.tables.posts,
418
+ indexName: "primary",
419
+ condition: match.where,
420
+ joins: match.join,
421
+ },
422
+ ],
423
+ },
424
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
425
+ );
426
+
427
+ expect(hasConflict).toBe(true);
428
+ });
429
+
430
+ test("does not conflict on schema mismatch", async () => {
431
+ await insertMutation({
432
+ id: "m11",
433
+ schema: "other",
434
+ table: "users",
435
+ externalId: "u1",
436
+ });
437
+
438
+ const hasConflict = await checkConflicts(
439
+ {
440
+ baseVersionstamp,
441
+ readKeys: [{ schema: "", table: "users", externalId: "u1" }],
442
+ writeKeys: [],
443
+ readScopes: [],
444
+ },
445
+ { driver, driverConfig: new BetterSQLite3DriverConfig() },
446
+ );
447
+
448
+ expect(hasConflict).toBe(false);
449
+ });
450
+ });
@@ -0,0 +1,248 @@
1
+ import { sql } from "kysely";
2
+
3
+ import type { DriverConfig } from "../adapters/generic-sql/driver-config";
4
+ import { createColdKysely } from "../adapters/generic-sql/migration/cold-kysely";
5
+ import { createSQLQueryCompiler } from "../adapters/generic-sql/query/create-sql-query-compiler";
6
+ import type { SQLiteStorageMode } from "../adapters/generic-sql/sqlite-storage";
7
+ import type { NamingResolver } from "../naming/sql-naming";
8
+ import type { Condition } from "../query/condition-builder";
9
+ import type { CompiledJoin } from "../query/orm/orm";
10
+ import type { AnyTable } from "../schema/create";
11
+ import type { SqlDriverAdapter } from "../sql-driver/sql-driver-adapter";
12
+
13
+ export type ConflictKey = {
14
+ schema: string;
15
+ table: string;
16
+ externalId: string;
17
+ };
18
+
19
+ export type ConflictReadScope = {
20
+ schema: string;
21
+ table: AnyTable;
22
+ indexName: string;
23
+ condition?: Condition;
24
+ joins?: CompiledJoin[];
25
+ };
26
+
27
+ export type UnknownRead = {
28
+ schema: string;
29
+ table: string;
30
+ };
31
+
32
+ export type UnknownReadStrategy = "conflict" | "table" | "ignore";
33
+
34
+ export type ConflictCheckInput = {
35
+ baseVersionstamp?: string;
36
+ readKeys: ConflictKey[];
37
+ writeKeys: ConflictKey[];
38
+ readScopes: ConflictReadScope[];
39
+ unknownReads?: UnknownRead[];
40
+ unknownReadStrategy?: UnknownReadStrategy;
41
+ };
42
+
43
+ export type ConflictCheckRuntime = {
44
+ driver: SqlDriverAdapter;
45
+ driverConfig: DriverConfig;
46
+ sqliteStorageMode?: SQLiteStorageMode;
47
+ resolver?: NamingResolver;
48
+ };
49
+
50
+ const OUTBOX_MUTATIONS_TABLE = "fragno_db_outbox_mutations" as const;
51
+
52
+ const normalizeKeys = (keys: ConflictKey[]): ConflictKey[] =>
53
+ keys.filter((key) => key.externalId.trim().length > 0);
54
+
55
+ const groupKeys = (keys: ConflictKey[]) => {
56
+ const map = new Map<string, { schema: string; table: string; externalIds: string[] }>();
57
+ for (const key of keys) {
58
+ const groupKey = `${key.schema}::${key.table}`;
59
+ const existing = map.get(groupKey);
60
+ if (existing) {
61
+ existing.externalIds.push(key.externalId);
62
+ continue;
63
+ }
64
+ map.set(groupKey, {
65
+ schema: key.schema,
66
+ table: key.table,
67
+ externalIds: [key.externalId],
68
+ });
69
+ }
70
+ return Array.from(map.values());
71
+ };
72
+
73
+ const groupTables = (tables: UnknownRead[]) => {
74
+ const map = new Map<string, UnknownRead>();
75
+ for (const entry of tables) {
76
+ const key = `${entry.schema}::${entry.table}`;
77
+ if (!map.has(key)) {
78
+ map.set(key, entry);
79
+ }
80
+ }
81
+ return Array.from(map.values());
82
+ };
83
+
84
+ const hasKeyConflicts = async (
85
+ runtime: ConflictCheckRuntime,
86
+ baseVersionstamp: string | undefined,
87
+ readKeys: ConflictKey[],
88
+ writeKeys: ConflictKey[],
89
+ ): Promise<boolean> => {
90
+ const combinedKeys = normalizeKeys([...readKeys, ...writeKeys]);
91
+ if (combinedKeys.length === 0) {
92
+ return false;
93
+ }
94
+
95
+ const grouped = groupKeys(combinedKeys);
96
+ const db = createColdKysely(runtime.driverConfig.databaseType);
97
+ let query = db.selectFrom(OUTBOX_MUTATIONS_TABLE).select(sql<number>`1`.as("exists"));
98
+
99
+ if (baseVersionstamp) {
100
+ query = query.where("entryVersionstamp", ">", baseVersionstamp);
101
+ }
102
+
103
+ query = query.where((eb) =>
104
+ eb.or(
105
+ grouped.map((group) =>
106
+ eb.and([
107
+ eb("schema", "=", group.schema),
108
+ eb("table", "=", group.table),
109
+ eb("externalId", "in", group.externalIds),
110
+ ]),
111
+ ),
112
+ ),
113
+ );
114
+
115
+ query = query.limit(1);
116
+
117
+ const result = await runtime.driver.executeQuery(query.compile());
118
+ return result.rows.length > 0;
119
+ };
120
+
121
+ const hasUnknownReadConflicts = async (
122
+ runtime: ConflictCheckRuntime,
123
+ baseVersionstamp: string | undefined,
124
+ unknownReads: UnknownRead[],
125
+ strategy: UnknownReadStrategy,
126
+ ): Promise<boolean> => {
127
+ if (unknownReads.length === 0 || strategy === "ignore") {
128
+ return false;
129
+ }
130
+
131
+ const db = createColdKysely(runtime.driverConfig.databaseType);
132
+
133
+ if (strategy === "conflict") {
134
+ let query = db.selectFrom(OUTBOX_MUTATIONS_TABLE).select(sql<number>`1`.as("exists"));
135
+ if (baseVersionstamp) {
136
+ query = query.where("entryVersionstamp", ">", baseVersionstamp);
137
+ }
138
+ query = query.limit(1);
139
+ const result = await runtime.driver.executeQuery(query.compile());
140
+ return result.rows.length > 0;
141
+ }
142
+
143
+ const grouped = groupTables(unknownReads);
144
+ let query = db.selectFrom(OUTBOX_MUTATIONS_TABLE).select(sql<number>`1`.as("exists"));
145
+
146
+ if (baseVersionstamp) {
147
+ query = query.where("entryVersionstamp", ">", baseVersionstamp);
148
+ }
149
+
150
+ query = query.where((eb) =>
151
+ eb.or(
152
+ grouped.map((group) =>
153
+ eb.and([eb("schema", "=", group.schema), eb("table", "=", group.table)]),
154
+ ),
155
+ ),
156
+ );
157
+
158
+ query = query.limit(1);
159
+
160
+ const result = await runtime.driver.executeQuery(query.compile());
161
+ return result.rows.length > 0;
162
+ };
163
+
164
+ const hasScopeConflicts = async (
165
+ runtime: ConflictCheckRuntime,
166
+ baseVersionstamp: string | undefined,
167
+ scope: ConflictReadScope,
168
+ ): Promise<boolean> => {
169
+ const db = createColdKysely(runtime.driverConfig.databaseType);
170
+ const compiler = createSQLQueryCompiler(
171
+ db,
172
+ runtime.driverConfig,
173
+ runtime.sqliteStorageMode,
174
+ runtime.resolver,
175
+ );
176
+
177
+ const { query: baseQuery, aliases } = compiler.buildJoinQuery(scope.table, {
178
+ where: scope.condition,
179
+ join: scope.joins,
180
+ });
181
+
182
+ let query = baseQuery.select(sql<number>`1`.as("exists"));
183
+ const mutationAliases: string[] = [];
184
+
185
+ for (const [index, alias] of aliases.entries()) {
186
+ const mutationAlias = `m_${index}`;
187
+ mutationAliases.push(mutationAlias);
188
+
189
+ const idColumn = alias.table.getIdColumn();
190
+ const columnName = runtime.resolver
191
+ ? runtime.resolver.getColumnName(alias.table.name, idColumn.name)
192
+ : idColumn.name;
193
+
194
+ query = query.leftJoin(`${OUTBOX_MUTATIONS_TABLE} as ${mutationAlias}`, (join) => {
195
+ let on = join
196
+ .on(`${mutationAlias}.schema`, "=", scope.schema)
197
+ .on(`${mutationAlias}.table`, "=", alias.table.name)
198
+ .onRef(`${mutationAlias}.externalId`, "=", `${alias.alias}.${columnName}`);
199
+
200
+ if (baseVersionstamp) {
201
+ on = on.on(`${mutationAlias}.entryVersionstamp`, ">", baseVersionstamp);
202
+ }
203
+
204
+ return on;
205
+ });
206
+ }
207
+
208
+ if (mutationAliases.length > 0) {
209
+ query = query.where((eb) =>
210
+ eb.or(mutationAliases.map((alias) => eb(`${alias}.entryVersionstamp`, "is not", null))),
211
+ );
212
+ }
213
+
214
+ query = query.limit(1);
215
+
216
+ const result = await runtime.driver.executeQuery(query.compile());
217
+ return result.rows.length > 0;
218
+ };
219
+
220
+ export const checkConflicts = async (
221
+ input: ConflictCheckInput,
222
+ runtime: ConflictCheckRuntime,
223
+ ): Promise<boolean> => {
224
+ const unknownReadStrategy = input.unknownReadStrategy ?? "conflict";
225
+
226
+ if (await hasKeyConflicts(runtime, input.baseVersionstamp, input.readKeys, input.writeKeys)) {
227
+ return true;
228
+ }
229
+
230
+ for (const scope of input.readScopes) {
231
+ if (await hasScopeConflicts(runtime, input.baseVersionstamp, scope)) {
232
+ return true;
233
+ }
234
+ }
235
+
236
+ if (
237
+ await hasUnknownReadConflicts(
238
+ runtime,
239
+ input.baseVersionstamp,
240
+ input.unknownReads ?? [],
241
+ unknownReadStrategy,
242
+ )
243
+ ) {
244
+ return true;
245
+ }
246
+
247
+ return false;
248
+ };
@@ -0,0 +1,14 @@
1
+ export { defineSyncCommands } from "./commands";
2
+ export {
3
+ collectReadKeys,
4
+ collectReadScopes,
5
+ collectWriteKeys,
6
+ stripReadTrackingResults,
7
+ } from "./read-tracking";
8
+ export type { ReadKey, ReadScope } from "./read-tracking";
9
+ export type {
10
+ SyncCommandDefinition,
11
+ SyncCommandHandler,
12
+ SyncCommandRegistry,
13
+ SyncCommandTxFactory,
14
+ } from "./types";