@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,27 @@
1
+ //#region src/sync/commands.ts
2
+ const createRegistry = (schemaName, commandList) => {
3
+ const commands = /* @__PURE__ */ new Map();
4
+ for (const command of commandList) {
5
+ if (!command || typeof command !== "object") throw new Error("Sync commands must be objects.");
6
+ const name = command.name;
7
+ if (typeof name !== "string" || name.trim().length === 0) throw new Error("Sync command name must be a non-empty string.");
8
+ const existing = commands.get(name);
9
+ if (existing && existing !== command) throw new Error(`Sync command "${name}" is already defined for schema "${schemaName}".`);
10
+ commands.set(name, command);
11
+ }
12
+ return {
13
+ schemaName,
14
+ commands,
15
+ getCommand: (name) => commands.get(name)
16
+ };
17
+ };
18
+ const defineSyncCommands = (options) => {
19
+ const schemaName = options.schema.name;
20
+ return { create: (factory) => {
21
+ return createRegistry(schemaName, factory({ defineCommand: (definition) => definition }));
22
+ } };
23
+ };
24
+
25
+ //#endregion
26
+ export { defineSyncCommands };
27
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","names":[],"sources":["../../src/sync/commands.ts"],"sourcesContent":["import type { AnySchema } from \"../schema/create\";\nimport type { SyncCommandDefinition, SyncCommandRegistry } from \"./types\";\n\ntype DefineSyncCommandsContext = {\n defineCommand: <TInput, TContext>(\n definition: SyncCommandDefinition<TInput, TContext>,\n ) => SyncCommandDefinition<TInput, TContext>;\n};\n\nconst createRegistry = (\n schemaName: string,\n commandList: readonly SyncCommandDefinition[],\n): SyncCommandRegistry => {\n const commands = new Map<string, SyncCommandDefinition>();\n\n for (const command of commandList) {\n if (!command || typeof command !== \"object\") {\n throw new Error(\"Sync commands must be objects.\");\n }\n\n const name = command.name;\n if (typeof name !== \"string\" || name.trim().length === 0) {\n throw new Error(\"Sync command name must be a non-empty string.\");\n }\n\n const existing = commands.get(name);\n if (existing && existing !== command) {\n throw new Error(`Sync command \"${name}\" is already defined for schema \"${schemaName}\".`);\n }\n\n commands.set(name, command);\n }\n\n return {\n schemaName,\n commands,\n getCommand: (name) => commands.get(name),\n };\n};\n\nexport const defineSyncCommands = <TSchema extends AnySchema>(options: { schema: TSchema }) => {\n const schemaName = options.schema.name;\n return {\n create: <const TCommands extends readonly SyncCommandDefinition[]>(\n factory: (context: DefineSyncCommandsContext) => TCommands,\n ): SyncCommandRegistry => {\n const commands = factory({ defineCommand: (definition) => definition });\n return createRegistry(schemaName, commands);\n },\n };\n};\n"],"mappings":";AASA,MAAM,kBACJ,YACA,gBACwB;CACxB,MAAM,2BAAW,IAAI,KAAoC;AAEzD,MAAK,MAAM,WAAW,aAAa;AACjC,MAAI,CAAC,WAAW,OAAO,YAAY,SACjC,OAAM,IAAI,MAAM,iCAAiC;EAGnD,MAAM,OAAO,QAAQ;AACrB,MAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAC,WAAW,EACrD,OAAM,IAAI,MAAM,gDAAgD;EAGlE,MAAM,WAAW,SAAS,IAAI,KAAK;AACnC,MAAI,YAAY,aAAa,QAC3B,OAAM,IAAI,MAAM,iBAAiB,KAAK,mCAAmC,WAAW,IAAI;AAG1F,WAAS,IAAI,MAAM,QAAQ;;AAG7B,QAAO;EACL;EACA;EACA,aAAa,SAAS,SAAS,IAAI,KAAK;EACzC;;AAGH,MAAa,sBAAiD,YAAiC;CAC7F,MAAM,aAAa,QAAQ,OAAO;AAClC,QAAO,EACL,SACE,YACwB;AAExB,SAAO,eAAe,YADL,QAAQ,EAAE,gBAAgB,eAAe,YAAY,CAAC,CAC5B;IAE9C"}
@@ -0,0 +1,4 @@
1
+ import { SyncCommandDefinition, SyncCommandHandler, SyncCommandRegistry, SyncCommandTxFactory } from "./types.js";
2
+ import { defineSyncCommands } from "./commands.js";
3
+ import { ReadKey, ReadScope, collectReadKeys, collectReadScopes, collectWriteKeys, stripReadTrackingResults } from "./read-tracking.js";
4
+ export { type ReadKey, type ReadScope, type SyncCommandDefinition, type SyncCommandHandler, type SyncCommandRegistry, type SyncCommandTxFactory, collectReadKeys, collectReadScopes, collectWriteKeys, defineSyncCommands, stripReadTrackingResults };
@@ -0,0 +1,4 @@
1
+ import { defineSyncCommands } from "./commands.js";
2
+ import { collectReadKeys, collectReadScopes, collectWriteKeys, stripReadTrackingResults } from "./read-tracking.js";
3
+
4
+ export { collectReadKeys, collectReadScopes, collectWriteKeys, defineSyncCommands, stripReadTrackingResults };
@@ -0,0 +1,25 @@
1
+ import { AnySchema, AnyTable } from "../schema/create.js";
2
+ import { Condition } from "../query/condition-builder.js";
3
+ import { CompiledJoin } from "../query/orm/orm.js";
4
+ import { MutationOperation, RetrievalOperation } from "../query/unit-of-work/unit-of-work.js";
5
+
6
+ //#region src/sync/read-tracking.d.ts
7
+ type ReadKey = {
8
+ schema: string;
9
+ table: string;
10
+ externalId: string;
11
+ };
12
+ type ReadScope = {
13
+ schema: string;
14
+ table: AnyTable;
15
+ indexName: string;
16
+ condition?: Condition;
17
+ joins?: CompiledJoin[];
18
+ };
19
+ declare const collectReadScopes: (operations: ReadonlyArray<RetrievalOperation<AnySchema>>) => ReadScope[];
20
+ declare const collectReadKeys: (operations: ReadonlyArray<RetrievalOperation<AnySchema>>, results: unknown[]) => ReadKey[];
21
+ declare const collectWriteKeys: (operations: ReadonlyArray<MutationOperation<AnySchema>>) => ReadKey[];
22
+ declare const stripReadTrackingResults: (operations: ReadonlyArray<RetrievalOperation<AnySchema>>, results: unknown[]) => void;
23
+ //#endregion
24
+ export { ReadKey, ReadScope, collectReadKeys, collectReadScopes, collectWriteKeys, stripReadTrackingResults };
25
+ //# sourceMappingURL=read-tracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-tracking.d.ts","names":[],"sources":["../../src/sync/read-tracking.ts"],"sourcesContent":[],"mappings":";;;;;;KASY,OAAA;;EAAA,KAAA,EAAA,MAAO;EAMP,UAAA,EAAS,MAAA;CAEZ;AAEK,KAJF,SAAA,GAIE;EACJ,MAAA,EAAA,MAAA;EAAY,KAAA,EAHb,QAGa;EA6HT,SAAA,EAAA,MAAA;EACkC,SAAA,CAAA,EA/HjC,SA+HiC;EAAnB,KAAA,CAAA,EA9HlB,YA8HkB,EAAA;CAAd;AACX,cAFU,iBAEV,EAAA,CAAA,UAAA,EADW,aACX,CADyB,kBACzB,CAD4C,SAC5C,CAAA,CAAA,EAAA,GAAA,SAAA,EAAA;AAAS,cA8CC,eA9CD,EAAA,CAAA,UAAA,EA+CE,aA/CF,CA+CgB,kBA/ChB,CA+CmC,SA/CnC,CAAA,CAAA,EAAA,OAAA,EAAA,OAAA,EAAA,EAAA,GAiDT,OAjDS,EAAA;AA8CC,cA+BA,gBAFZ,EAAA,CAAA,UAAA,EAGa,aAHb,CAG2B,iBAH3B,CAG6C,SAH7C,CAAA,CAAA,EAAA,GAIE,OAJF,EAAA;AA5B8C,cAgElC,wBAhEkC,EAAA,CAAA,UAAA,EAiEjC,aAjEiC,CAiEnB,kBAjEmB,CAiEA,SAjEA,CAAA,CAAA,EAAA,OAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA"}
@@ -0,0 +1,148 @@
1
+ import { buildCondition } from "../query/condition-builder.js";
2
+ import { FragnoId } from "../schema/create.js";
3
+
4
+ //#region src/sync/read-tracking.ts
5
+ const isCursorResult = (value) => {
6
+ if (!value || typeof value !== "object") return false;
7
+ return Array.isArray(value.items);
8
+ };
9
+ const getExternalId = (value) => {
10
+ if (typeof value === "string") return value;
11
+ if (value instanceof FragnoId) return value.externalId;
12
+ if (value && typeof value === "object") {
13
+ const candidate = value.externalId;
14
+ if (typeof candidate === "string") return candidate;
15
+ }
16
+ };
17
+ const collectKeysFromRecord = (record, table, joins, schemaName, output) => {
18
+ if (!record || typeof record !== "object") return;
19
+ const idKey = table.getIdColumn().name;
20
+ const externalId = getExternalId(record[idKey]);
21
+ if (externalId !== void 0) output.push({
22
+ schema: schemaName,
23
+ table: table.name,
24
+ externalId
25
+ });
26
+ if (!joins || joins.length === 0) return;
27
+ for (const join of joins) {
28
+ const { relation, options } = join;
29
+ if (options === false) continue;
30
+ const relationValue = record[relation.name];
31
+ if (relationValue === null || relationValue === void 0) continue;
32
+ if (Array.isArray(relationValue)) {
33
+ for (const item of relationValue) collectKeysFromRecord(item, relation.table, options.join, schemaName, output);
34
+ continue;
35
+ }
36
+ collectKeysFromRecord(relationValue, relation.table, options.join, schemaName, output);
37
+ }
38
+ };
39
+ const shouldStripId = (select, table) => {
40
+ if (!select || select === true) return false;
41
+ const idKey = table.getIdColumn().name;
42
+ return !select.includes(idKey);
43
+ };
44
+ const stripKeysFromRecord = (record, table, joins, stripId) => {
45
+ if (!record || typeof record !== "object") return;
46
+ if (stripId) {
47
+ const idKey = table.getIdColumn().name;
48
+ delete record[idKey];
49
+ }
50
+ if (!joins || joins.length === 0) return;
51
+ for (const join of joins) {
52
+ const { relation, options } = join;
53
+ if (options === false) continue;
54
+ const relationValue = record[relation.name];
55
+ if (relationValue === null || relationValue === void 0) continue;
56
+ const stripRelationId = shouldStripId(options.select, relation.table);
57
+ if (Array.isArray(relationValue)) {
58
+ for (const item of relationValue) stripKeysFromRecord(item, relation.table, options.join, stripRelationId);
59
+ continue;
60
+ }
61
+ stripKeysFromRecord(relationValue, relation.table, options.join, stripRelationId);
62
+ }
63
+ };
64
+ const collectReadScopes = (operations) => {
65
+ const scopes = [];
66
+ for (const op of operations) {
67
+ const schemaName = op.namespace ?? "";
68
+ if (op.type === "count") {
69
+ const condition = op.options.where ? buildCondition(op.table.columns, op.options.where) : void 0;
70
+ if (condition === false) continue;
71
+ scopes.push({
72
+ schema: schemaName,
73
+ table: op.table,
74
+ indexName: op.indexName,
75
+ condition: condition === true ? void 0 : condition
76
+ });
77
+ continue;
78
+ }
79
+ if (op.type === "find") {
80
+ const condition = op.options.where ? buildCondition(op.table.columns, op.options.where) : void 0;
81
+ if (condition === false) continue;
82
+ scopes.push({
83
+ schema: schemaName,
84
+ table: op.table,
85
+ indexName: op.indexName,
86
+ condition: condition === true ? void 0 : condition,
87
+ joins: op.options.joins
88
+ });
89
+ }
90
+ }
91
+ return scopes;
92
+ };
93
+ const collectReadKeys = (operations, results) => {
94
+ const keys = [];
95
+ for (const [index, op] of operations.entries()) {
96
+ if (op.type !== "find") continue;
97
+ const schemaName = op.namespace ?? "";
98
+ const result = results[index];
99
+ let records = [];
100
+ if (op.withCursor && isCursorResult(result)) records = result.items;
101
+ else if (Array.isArray(result)) records = result;
102
+ else if (result !== null && result !== void 0) records = [result];
103
+ for (const record of records) collectKeysFromRecord(record, op.table, op.options.joins, schemaName, keys);
104
+ }
105
+ return keys;
106
+ };
107
+ const collectWriteKeys = (operations) => {
108
+ const keys = [];
109
+ for (const op of operations) {
110
+ if (op.type === "check") continue;
111
+ const schemaName = op.namespace ?? "";
112
+ if (op.type === "create") {
113
+ keys.push({
114
+ schema: schemaName,
115
+ table: op.table,
116
+ externalId: op.generatedExternalId
117
+ });
118
+ continue;
119
+ }
120
+ const externalId = getExternalId(op.id);
121
+ if (externalId !== void 0) keys.push({
122
+ schema: schemaName,
123
+ table: op.table,
124
+ externalId
125
+ });
126
+ }
127
+ return keys;
128
+ };
129
+ const stripReadTrackingResults = (operations, results) => {
130
+ for (const [index, op] of operations.entries()) {
131
+ if (op.type !== "find" || !op.readTracking) continue;
132
+ const result = results[index];
133
+ const stripId = shouldStripId(op.options.select, op.table);
134
+ if (op.withCursor && isCursorResult(result)) {
135
+ for (const record of result.items) stripKeysFromRecord(record, op.table, op.options.joins, stripId);
136
+ continue;
137
+ }
138
+ if (Array.isArray(result)) {
139
+ for (const record of result) stripKeysFromRecord(record, op.table, op.options.joins, stripId);
140
+ continue;
141
+ }
142
+ stripKeysFromRecord(result, op.table, op.options.joins, stripId);
143
+ }
144
+ };
145
+
146
+ //#endregion
147
+ export { collectReadKeys, collectReadScopes, collectWriteKeys, stripReadTrackingResults };
148
+ //# sourceMappingURL=read-tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-tracking.js","names":["scopes: ReadScope[]","keys: ReadKey[]","records: unknown[]"],"sources":["../../src/sync/read-tracking.ts"],"sourcesContent":["import type { Condition } from \"../query/condition-builder\";\nimport { buildCondition } from \"../query/condition-builder\";\nimport type { CursorResult } from \"../query/cursor\";\nimport type { CompiledJoin } from \"../query/orm/orm\";\nimport type { AnySelectClause } from \"../query/simple-query-interface\";\nimport type { MutationOperation, RetrievalOperation } from \"../query/unit-of-work/unit-of-work\";\nimport type { AnySchema, AnyTable } from \"../schema/create\";\nimport { FragnoId } from \"../schema/create\";\n\nexport type ReadKey = {\n schema: string;\n table: string;\n externalId: string;\n};\n\nexport type ReadScope = {\n schema: string;\n table: AnyTable;\n indexName: string;\n condition?: Condition;\n joins?: CompiledJoin[];\n};\n\nconst isCursorResult = (value: unknown): value is CursorResult<unknown> => {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n return Array.isArray((value as CursorResult<unknown>).items);\n};\n\nconst getExternalId = (value: unknown): string | undefined => {\n if (typeof value === \"string\") {\n return value;\n }\n\n if (value instanceof FragnoId) {\n return value.externalId;\n }\n\n if (value && typeof value === \"object\") {\n const candidate = (value as { externalId?: unknown }).externalId;\n if (typeof candidate === \"string\") {\n return candidate;\n }\n }\n\n return undefined;\n};\n\nconst collectKeysFromRecord = (\n record: unknown,\n table: AnyTable,\n joins: CompiledJoin[] | undefined,\n schemaName: string,\n output: ReadKey[],\n): void => {\n if (!record || typeof record !== \"object\") {\n return;\n }\n\n const idKey = table.getIdColumn().name;\n const externalId = getExternalId((record as Record<string, unknown>)[idKey]);\n if (externalId !== undefined) {\n output.push({ schema: schemaName, table: table.name, externalId });\n }\n\n if (!joins || joins.length === 0) {\n return;\n }\n\n for (const join of joins) {\n const { relation, options } = join;\n if (options === false) {\n continue;\n }\n\n const relationValue = (record as Record<string, unknown>)[relation.name];\n if (relationValue === null || relationValue === undefined) {\n continue;\n }\n\n if (Array.isArray(relationValue)) {\n for (const item of relationValue) {\n collectKeysFromRecord(item, relation.table, options.join, schemaName, output);\n }\n continue;\n }\n\n collectKeysFromRecord(relationValue, relation.table, options.join, schemaName, output);\n }\n};\n\nconst shouldStripId = (select: AnySelectClause | undefined, table: AnyTable): boolean => {\n if (!select || select === true) {\n return false;\n }\n\n const idKey = table.getIdColumn().name;\n return !select.includes(idKey);\n};\n\nconst stripKeysFromRecord = (\n record: unknown,\n table: AnyTable,\n joins: CompiledJoin[] | undefined,\n stripId: boolean,\n): void => {\n if (!record || typeof record !== \"object\") {\n return;\n }\n\n if (stripId) {\n const idKey = table.getIdColumn().name;\n delete (record as Record<string, unknown>)[idKey];\n }\n\n if (!joins || joins.length === 0) {\n return;\n }\n\n for (const join of joins) {\n const { relation, options } = join;\n if (options === false) {\n continue;\n }\n\n const relationValue = (record as Record<string, unknown>)[relation.name];\n if (relationValue === null || relationValue === undefined) {\n continue;\n }\n\n const stripRelationId = shouldStripId(options.select, relation.table);\n\n if (Array.isArray(relationValue)) {\n for (const item of relationValue) {\n stripKeysFromRecord(item, relation.table, options.join, stripRelationId);\n }\n continue;\n }\n\n stripKeysFromRecord(relationValue, relation.table, options.join, stripRelationId);\n }\n};\n\nexport const collectReadScopes = (\n operations: ReadonlyArray<RetrievalOperation<AnySchema>>,\n): ReadScope[] => {\n const scopes: ReadScope[] = [];\n\n for (const op of operations) {\n const schemaName = op.namespace ?? \"\";\n\n if (op.type === \"count\") {\n const condition = op.options.where\n ? buildCondition(op.table.columns, op.options.where)\n : undefined;\n\n if (condition === false) {\n continue;\n }\n\n scopes.push({\n schema: schemaName,\n table: op.table,\n indexName: op.indexName,\n condition: condition === true ? undefined : condition,\n });\n continue;\n }\n\n if (op.type === \"find\") {\n const condition = op.options.where\n ? buildCondition(op.table.columns, op.options.where)\n : undefined;\n\n if (condition === false) {\n continue;\n }\n\n scopes.push({\n schema: schemaName,\n table: op.table,\n indexName: op.indexName,\n condition: condition === true ? undefined : condition,\n joins: op.options.joins,\n });\n }\n }\n\n return scopes;\n};\n\nexport const collectReadKeys = (\n operations: ReadonlyArray<RetrievalOperation<AnySchema>>,\n results: unknown[],\n): ReadKey[] => {\n const keys: ReadKey[] = [];\n\n for (const [index, op] of operations.entries()) {\n if (op.type !== \"find\") {\n continue;\n }\n\n const schemaName = op.namespace ?? \"\";\n const result = results[index];\n\n let records: unknown[] = [];\n if (op.withCursor && isCursorResult(result)) {\n records = result.items;\n } else if (Array.isArray(result)) {\n records = result;\n } else if (result !== null && result !== undefined) {\n records = [result];\n }\n\n for (const record of records) {\n collectKeysFromRecord(record, op.table, op.options.joins, schemaName, keys);\n }\n }\n\n return keys;\n};\n\nexport const collectWriteKeys = (\n operations: ReadonlyArray<MutationOperation<AnySchema>>,\n): ReadKey[] => {\n const keys: ReadKey[] = [];\n\n for (const op of operations) {\n if (op.type === \"check\") {\n continue;\n }\n\n const schemaName = op.namespace ?? \"\";\n\n if (op.type === \"create\") {\n keys.push({\n schema: schemaName,\n table: op.table,\n externalId: op.generatedExternalId,\n });\n continue;\n }\n\n const externalId = getExternalId(op.id);\n if (externalId !== undefined) {\n keys.push({\n schema: schemaName,\n table: op.table,\n externalId,\n });\n }\n }\n\n return keys;\n};\n\nexport const stripReadTrackingResults = (\n operations: ReadonlyArray<RetrievalOperation<AnySchema>>,\n results: unknown[],\n): void => {\n for (const [index, op] of operations.entries()) {\n if (op.type !== \"find\" || !op.readTracking) {\n continue;\n }\n\n const result = results[index];\n const stripId = shouldStripId(op.options.select, op.table);\n\n if (op.withCursor && isCursorResult(result)) {\n for (const record of result.items) {\n stripKeysFromRecord(record, op.table, op.options.joins, stripId);\n }\n continue;\n }\n\n if (Array.isArray(result)) {\n for (const record of result) {\n stripKeysFromRecord(record, op.table, op.options.joins, stripId);\n }\n continue;\n }\n\n stripKeysFromRecord(result, op.table, op.options.joins, stripId);\n }\n};\n"],"mappings":"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"}
@@ -0,0 +1,213 @@
1
+ import { ConcurrencyConflictError } from "../query/unit-of-work/execute-unit-of-work.js";
2
+
3
+ //#region src/sync/submit.ts
4
+ const DEFAULT_MAX_COMMANDS_PER_SUBMIT = 100;
5
+ const DEFAULT_MAX_UNSEEN_MUTATIONS = 1e4;
6
+ const VALID_CONFLICT_RESOLUTION = new Set(["server", "disabled"]);
7
+ const VERSIONSTAMP_REGEX = /^[0-9a-f]{24}$/i;
8
+ const isObject = (value) => value !== null && typeof value === "object";
9
+ const isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
10
+ const normalizeVersionstamp = (value) => value ? value.toLowerCase() : void 0;
11
+ const isValidVersionstamp = (value) => VERSIONSTAMP_REGEX.test(value);
12
+ const buildError = (code, message, statusCode = 400) => ({
13
+ status: "error",
14
+ statusCode,
15
+ body: { error: {
16
+ code,
17
+ message
18
+ } }
19
+ });
20
+ const withEntries = async (runtime, baseVersionstamp) => {
21
+ const entries = await runtime.listOutboxEntries(baseVersionstamp);
22
+ return {
23
+ entries,
24
+ lastVersionstamp: entries.length > 0 ? entries[entries.length - 1]?.versionstamp : void 0
25
+ };
26
+ };
27
+ const submitSyncRequest = async (raw, runtime) => {
28
+ if (!isObject(raw)) return buildError("INVALID_REQUEST", "Request body must be an object.");
29
+ const requestId = raw["requestId"];
30
+ if (!isNonEmptyString(requestId)) return buildError("INVALID_REQUEST", "requestId is required.");
31
+ const adapterIdentity = raw["adapterIdentity"];
32
+ if (!isNonEmptyString(adapterIdentity)) return buildError("INVALID_REQUEST", "adapterIdentity is required.");
33
+ const conflictResolutionStrategy = raw["conflictResolutionStrategy"];
34
+ if (!isNonEmptyString(conflictResolutionStrategy) || !VALID_CONFLICT_RESOLUTION.has(conflictResolutionStrategy)) return buildError("INVALID_REQUEST", "conflictResolutionStrategy must be \"server\" or \"disabled\".");
35
+ const baseVersionstamp = raw["baseVersionstamp"];
36
+ if (baseVersionstamp !== void 0 && !isNonEmptyString(baseVersionstamp)) return buildError("INVALID_REQUEST", "baseVersionstamp must be a non-empty string.");
37
+ const normalizedBaseVersionstamp = baseVersionstamp !== void 0 ? normalizeVersionstamp(baseVersionstamp) : void 0;
38
+ if (normalizedBaseVersionstamp && !isValidVersionstamp(normalizedBaseVersionstamp)) return buildError("INVALID_VERSIONSTAMP", "baseVersionstamp must be 24 hex characters.");
39
+ const commands = raw["commands"];
40
+ if (!Array.isArray(commands)) return buildError("INVALID_REQUEST", "commands must be an array.");
41
+ if (await runtime.getAdapterIdentity() !== adapterIdentity) return buildError("ADAPTER_IDENTITY_MISMATCH", "adapterIdentity does not match this adapter.", 409);
42
+ const existing = await runtime.getSyncRequest(requestId);
43
+ if (existing) {
44
+ const { entries: entries$1, lastVersionstamp: lastVersionstamp$1 } = await withEntries(runtime, normalizedBaseVersionstamp);
45
+ return {
46
+ status: "ok",
47
+ response: {
48
+ status: "conflict",
49
+ requestId,
50
+ confirmedCommandIds: existing.confirmedCommandIds ?? [],
51
+ conflictCommandId: existing.conflictCommandId ?? void 0,
52
+ lastVersionstamp: lastVersionstamp$1 ?? existing.lastVersionstamp ?? void 0,
53
+ entries: entries$1,
54
+ reason: "already_handled"
55
+ }
56
+ };
57
+ }
58
+ const maxCommands = runtime.maxCommandsPerSubmit ?? DEFAULT_MAX_COMMANDS_PER_SUBMIT;
59
+ const maxUnseen = runtime.maxUnseenMutations ?? DEFAULT_MAX_UNSEEN_MUTATIONS;
60
+ if (commands.length === 0) {
61
+ const { entries: entries$1, lastVersionstamp: lastVersionstamp$1 } = await withEntries(runtime, normalizedBaseVersionstamp);
62
+ const response$1 = {
63
+ status: "conflict",
64
+ requestId,
65
+ confirmedCommandIds: [],
66
+ entries: entries$1,
67
+ lastVersionstamp: lastVersionstamp$1,
68
+ reason: "no_commands"
69
+ };
70
+ await runtime.storeSyncRequest({
71
+ requestId,
72
+ status: "conflict",
73
+ confirmedCommandIds: [],
74
+ baseVersionstamp: normalizedBaseVersionstamp,
75
+ lastVersionstamp: lastVersionstamp$1
76
+ });
77
+ return {
78
+ status: "ok",
79
+ response: response$1
80
+ };
81
+ }
82
+ if (commands.length > maxCommands) {
83
+ const { entries: entries$1, lastVersionstamp: lastVersionstamp$1 } = await withEntries(runtime, normalizedBaseVersionstamp);
84
+ const response$1 = {
85
+ status: "conflict",
86
+ requestId,
87
+ confirmedCommandIds: [],
88
+ entries: entries$1,
89
+ lastVersionstamp: lastVersionstamp$1,
90
+ reason: "limit_exceeded"
91
+ };
92
+ await runtime.storeSyncRequest({
93
+ requestId,
94
+ status: "conflict",
95
+ confirmedCommandIds: [],
96
+ baseVersionstamp: normalizedBaseVersionstamp,
97
+ lastVersionstamp: lastVersionstamp$1
98
+ });
99
+ return {
100
+ status: "ok",
101
+ response: response$1
102
+ };
103
+ }
104
+ if (await runtime.countOutboxMutations(normalizedBaseVersionstamp) > maxUnseen) {
105
+ const { entries: entries$1, lastVersionstamp: lastVersionstamp$1 } = await withEntries(runtime, normalizedBaseVersionstamp);
106
+ const response$1 = {
107
+ status: "conflict",
108
+ requestId,
109
+ confirmedCommandIds: [],
110
+ entries: entries$1,
111
+ lastVersionstamp: lastVersionstamp$1,
112
+ reason: "client_far_behind"
113
+ };
114
+ await runtime.storeSyncRequest({
115
+ requestId,
116
+ status: "conflict",
117
+ confirmedCommandIds: [],
118
+ baseVersionstamp: normalizedBaseVersionstamp,
119
+ lastVersionstamp: lastVersionstamp$1
120
+ });
121
+ return {
122
+ status: "ok",
123
+ response: response$1
124
+ };
125
+ }
126
+ const confirmedCommandIds = [];
127
+ let conflictCommandId;
128
+ let conflictReason;
129
+ for (const command of commands) {
130
+ if (!isObject(command)) return buildError("INVALID_COMMAND", "Command entries must be objects.");
131
+ const commandId = command["id"];
132
+ const commandName = command["name"];
133
+ const target = command["target"];
134
+ if (!isNonEmptyString(commandId) || !isNonEmptyString(commandName) || !isObject(target)) return buildError("INVALID_COMMAND", "Command entries must include id, name, and target.");
135
+ const fragmentName = target["fragment"];
136
+ const schemaName = target["schema"];
137
+ if (!isNonEmptyString(fragmentName) || !isNonEmptyString(schemaName)) return buildError("INVALID_COMMAND", "Command target must include fragment and schema.");
138
+ const resolved = runtime.resolveCommand(fragmentName, schemaName, commandName);
139
+ if (!resolved) return buildError("UNKNOWN_COMMAND", "Command target or name was not recognized.");
140
+ const ctx = runtime.createCommandContext(resolved.command);
141
+ try {
142
+ await runtime.executeCommand(resolved.command, command["input"], ctx);
143
+ confirmedCommandIds.push(commandId);
144
+ } catch (error) {
145
+ if (error instanceof ConcurrencyConflictError) {
146
+ conflictCommandId = commandId;
147
+ conflictReason = "write_congestion";
148
+ break;
149
+ }
150
+ await runtime.storeSyncRequest({
151
+ requestId,
152
+ status: "conflict",
153
+ confirmedCommandIds,
154
+ conflictCommandId: commandId,
155
+ baseVersionstamp: normalizedBaseVersionstamp
156
+ });
157
+ return {
158
+ status: "error",
159
+ statusCode: 500,
160
+ body: { error: {
161
+ code: "SYNC_COMMAND_FAILED",
162
+ message: error instanceof Error ? error.message : "Sync command failed."
163
+ } }
164
+ };
165
+ }
166
+ }
167
+ const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);
168
+ if (conflictReason) {
169
+ const response$1 = {
170
+ status: "conflict",
171
+ requestId,
172
+ confirmedCommandIds,
173
+ conflictCommandId,
174
+ entries,
175
+ lastVersionstamp,
176
+ reason: conflictReason
177
+ };
178
+ await runtime.storeSyncRequest({
179
+ requestId,
180
+ status: "conflict",
181
+ confirmedCommandIds,
182
+ conflictCommandId,
183
+ baseVersionstamp: normalizedBaseVersionstamp,
184
+ lastVersionstamp
185
+ });
186
+ return {
187
+ status: "ok",
188
+ response: response$1
189
+ };
190
+ }
191
+ const response = {
192
+ status: "applied",
193
+ requestId,
194
+ confirmedCommandIds,
195
+ entries,
196
+ lastVersionstamp
197
+ };
198
+ await runtime.storeSyncRequest({
199
+ requestId,
200
+ status: "applied",
201
+ confirmedCommandIds,
202
+ baseVersionstamp: normalizedBaseVersionstamp,
203
+ lastVersionstamp
204
+ });
205
+ return {
206
+ status: "ok",
207
+ response
208
+ };
209
+ };
210
+
211
+ //#endregion
212
+ export { submitSyncRequest };
213
+ //# sourceMappingURL=submit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submit.js","names":["lastVersionstamp","response: SubmitResponse","confirmedCommandIds: string[]","conflictCommandId: string | undefined","conflictReason: SubmitConflictReason | undefined"],"sources":["../../src/sync/submit.ts"],"sourcesContent":["import type { OutboxEntry } from \"../outbox/outbox\";\nimport { ConcurrencyConflictError } from \"../query/unit-of-work/execute-unit-of-work\";\nimport type { SubmitResponse, SubmitConflictReason, SyncCommandDefinition } from \"./types\";\n\nexport const DEFAULT_MAX_COMMANDS_PER_SUBMIT = 100;\nexport const DEFAULT_MAX_UNSEEN_MUTATIONS = 10_000;\n\nexport type SyncRequestRecord = {\n requestId: string;\n status: \"applied\" | \"conflict\";\n confirmedCommandIds: string[];\n conflictCommandId?: string;\n baseVersionstamp?: string;\n lastVersionstamp?: string;\n};\n\nexport type SyncCommandResolution = {\n command: SyncCommandDefinition;\n namespace: string | null;\n};\n\nexport type SyncSubmitRuntime = {\n getAdapterIdentity: () => Promise<string>;\n listOutboxEntries: (afterVersionstamp?: string) => Promise<OutboxEntry[]>;\n countOutboxMutations: (afterVersionstamp?: string) => Promise<number>;\n getSyncRequest: (requestId: string) => Promise<SyncRequestRecord | undefined>;\n storeSyncRequest: (record: SyncRequestRecord) => Promise<void>;\n resolveCommand: (\n fragment: string,\n schema: string,\n name: string,\n ) => SyncCommandResolution | undefined;\n createCommandContext: (command: SyncCommandDefinition) => unknown;\n executeCommand: (command: SyncCommandDefinition, input: unknown, ctx: unknown) => Promise<void>;\n maxCommandsPerSubmit?: number;\n maxUnseenMutations?: number;\n};\n\nexport type SyncSubmitError = {\n status: \"error\";\n statusCode: number;\n body: {\n error: {\n code: string;\n message: string;\n detail?: string;\n };\n };\n};\n\nexport type SyncSubmitResult =\n | {\n status: \"ok\";\n response: SubmitResponse;\n }\n | SyncSubmitError;\n\nconst VALID_CONFLICT_RESOLUTION = new Set([\"server\", \"disabled\"] as const);\nconst VERSIONSTAMP_REGEX = /^[0-9a-f]{24}$/i;\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\";\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst normalizeVersionstamp = (value: string | undefined): string | undefined =>\n value ? value.toLowerCase() : undefined;\n\nconst isValidVersionstamp = (value: string): boolean => VERSIONSTAMP_REGEX.test(value);\n\nconst buildError = (code: string, message: string, statusCode = 400): SyncSubmitError => ({\n status: \"error\",\n statusCode,\n body: {\n error: {\n code,\n message,\n },\n },\n});\n\nconst withEntries = async (\n runtime: SyncSubmitRuntime,\n baseVersionstamp: string | undefined,\n): Promise<{ entries: OutboxEntry[]; lastVersionstamp?: string }> => {\n const entries = await runtime.listOutboxEntries(baseVersionstamp);\n const lastVersionstamp =\n entries.length > 0 ? entries[entries.length - 1]?.versionstamp : undefined;\n return { entries, lastVersionstamp };\n};\n\nexport const submitSyncRequest = async (\n raw: unknown,\n runtime: SyncSubmitRuntime,\n): Promise<SyncSubmitResult> => {\n if (!isObject(raw)) {\n return buildError(\"INVALID_REQUEST\", \"Request body must be an object.\");\n }\n\n const requestId = raw[\"requestId\"];\n if (!isNonEmptyString(requestId)) {\n return buildError(\"INVALID_REQUEST\", \"requestId is required.\");\n }\n\n const adapterIdentity = raw[\"adapterIdentity\"];\n if (!isNonEmptyString(adapterIdentity)) {\n return buildError(\"INVALID_REQUEST\", \"adapterIdentity is required.\");\n }\n\n const conflictResolutionStrategy = raw[\"conflictResolutionStrategy\"];\n if (\n !isNonEmptyString(conflictResolutionStrategy) ||\n !VALID_CONFLICT_RESOLUTION.has(conflictResolutionStrategy as \"server\" | \"disabled\")\n ) {\n return buildError(\n \"INVALID_REQUEST\",\n 'conflictResolutionStrategy must be \"server\" or \"disabled\".',\n );\n }\n\n const baseVersionstamp = raw[\"baseVersionstamp\"];\n if (baseVersionstamp !== undefined && !isNonEmptyString(baseVersionstamp)) {\n return buildError(\"INVALID_REQUEST\", \"baseVersionstamp must be a non-empty string.\");\n }\n\n const normalizedBaseVersionstamp =\n baseVersionstamp !== undefined ? normalizeVersionstamp(baseVersionstamp) : undefined;\n if (normalizedBaseVersionstamp && !isValidVersionstamp(normalizedBaseVersionstamp)) {\n return buildError(\"INVALID_VERSIONSTAMP\", \"baseVersionstamp must be 24 hex characters.\");\n }\n\n const commands = raw[\"commands\"];\n if (!Array.isArray(commands)) {\n return buildError(\"INVALID_REQUEST\", \"commands must be an array.\");\n }\n\n const expectedAdapterIdentity = await runtime.getAdapterIdentity();\n if (expectedAdapterIdentity !== adapterIdentity) {\n return buildError(\n \"ADAPTER_IDENTITY_MISMATCH\",\n \"adapterIdentity does not match this adapter.\",\n 409,\n );\n }\n\n const existing = await runtime.getSyncRequest(requestId);\n if (existing) {\n const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);\n const response: SubmitResponse = {\n status: \"conflict\",\n requestId,\n confirmedCommandIds: existing.confirmedCommandIds ?? [],\n conflictCommandId: existing.conflictCommandId ?? undefined,\n lastVersionstamp: lastVersionstamp ?? existing.lastVersionstamp ?? undefined,\n entries,\n reason: \"already_handled\",\n };\n return { status: \"ok\", response };\n }\n\n const maxCommands = runtime.maxCommandsPerSubmit ?? DEFAULT_MAX_COMMANDS_PER_SUBMIT;\n const maxUnseen = runtime.maxUnseenMutations ?? DEFAULT_MAX_UNSEEN_MUTATIONS;\n\n if (commands.length === 0) {\n const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);\n const response: SubmitResponse = {\n status: \"conflict\",\n requestId,\n confirmedCommandIds: [],\n entries,\n lastVersionstamp,\n reason: \"no_commands\",\n };\n await runtime.storeSyncRequest({\n requestId,\n status: \"conflict\",\n confirmedCommandIds: [],\n baseVersionstamp: normalizedBaseVersionstamp,\n lastVersionstamp,\n });\n return { status: \"ok\", response };\n }\n\n if (commands.length > maxCommands) {\n const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);\n const response: SubmitResponse = {\n status: \"conflict\",\n requestId,\n confirmedCommandIds: [],\n entries,\n lastVersionstamp,\n reason: \"limit_exceeded\",\n };\n await runtime.storeSyncRequest({\n requestId,\n status: \"conflict\",\n confirmedCommandIds: [],\n baseVersionstamp: normalizedBaseVersionstamp,\n lastVersionstamp,\n });\n return { status: \"ok\", response };\n }\n\n const unseenCount = await runtime.countOutboxMutations(normalizedBaseVersionstamp);\n if (unseenCount > maxUnseen) {\n const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);\n const response: SubmitResponse = {\n status: \"conflict\",\n requestId,\n confirmedCommandIds: [],\n entries,\n lastVersionstamp,\n reason: \"client_far_behind\",\n };\n await runtime.storeSyncRequest({\n requestId,\n status: \"conflict\",\n confirmedCommandIds: [],\n baseVersionstamp: normalizedBaseVersionstamp,\n lastVersionstamp,\n });\n return { status: \"ok\", response };\n }\n\n const confirmedCommandIds: string[] = [];\n let conflictCommandId: string | undefined;\n let conflictReason: SubmitConflictReason | undefined;\n\n for (const command of commands) {\n if (!isObject(command)) {\n return buildError(\"INVALID_COMMAND\", \"Command entries must be objects.\");\n }\n\n const commandId = command[\"id\"];\n const commandName = command[\"name\"];\n const target = command[\"target\"];\n\n if (!isNonEmptyString(commandId) || !isNonEmptyString(commandName) || !isObject(target)) {\n return buildError(\"INVALID_COMMAND\", \"Command entries must include id, name, and target.\");\n }\n\n const fragmentName = target[\"fragment\"];\n const schemaName = target[\"schema\"];\n\n if (!isNonEmptyString(fragmentName) || !isNonEmptyString(schemaName)) {\n return buildError(\"INVALID_COMMAND\", \"Command target must include fragment and schema.\");\n }\n\n const resolved = runtime.resolveCommand(fragmentName, schemaName, commandName);\n if (!resolved) {\n return buildError(\"UNKNOWN_COMMAND\", \"Command target or name was not recognized.\");\n }\n\n const ctx = runtime.createCommandContext(resolved.command);\n\n try {\n await runtime.executeCommand(resolved.command, command[\"input\"], ctx);\n confirmedCommandIds.push(commandId);\n } catch (error) {\n if (error instanceof ConcurrencyConflictError) {\n conflictCommandId = commandId;\n conflictReason = \"write_congestion\";\n break;\n }\n await runtime.storeSyncRequest({\n requestId,\n status: \"conflict\",\n confirmedCommandIds,\n conflictCommandId: commandId,\n baseVersionstamp: normalizedBaseVersionstamp,\n });\n return {\n status: \"error\",\n statusCode: 500,\n body: {\n error: {\n code: \"SYNC_COMMAND_FAILED\",\n message: error instanceof Error ? error.message : \"Sync command failed.\",\n },\n },\n };\n }\n }\n\n const { entries, lastVersionstamp } = await withEntries(runtime, normalizedBaseVersionstamp);\n\n if (conflictReason) {\n const response: SubmitResponse = {\n status: \"conflict\",\n requestId,\n confirmedCommandIds,\n conflictCommandId,\n entries,\n lastVersionstamp,\n reason: conflictReason,\n };\n\n await runtime.storeSyncRequest({\n requestId,\n status: \"conflict\",\n confirmedCommandIds,\n conflictCommandId,\n baseVersionstamp: normalizedBaseVersionstamp,\n lastVersionstamp,\n });\n\n return { status: \"ok\", response };\n }\n\n const response: SubmitResponse = {\n status: \"applied\",\n requestId,\n confirmedCommandIds,\n entries,\n lastVersionstamp,\n };\n\n await runtime.storeSyncRequest({\n requestId,\n status: \"applied\",\n confirmedCommandIds,\n baseVersionstamp: normalizedBaseVersionstamp,\n lastVersionstamp,\n });\n\n return { status: \"ok\", response };\n};\n"],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { ExecuteTxOptions, HandlerTxBuilder } from "../query/unit-of-work/execute-unit-of-work.js";
2
+ import { OutboxEntry } from "../outbox/outbox.js";
3
+ import { RequestThisContext } from "@fragno-dev/core";
4
+
5
+ //#region src/sync/types.d.ts
6
+ type SubmitRequest = {
7
+ baseVersionstamp?: string;
8
+ requestId: string;
9
+ conflictResolutionStrategy: "server" | "disabled";
10
+ adapterIdentity: string;
11
+ commands: Array<{
12
+ id: string;
13
+ name: string;
14
+ target: {
15
+ fragment: string;
16
+ schema: string;
17
+ };
18
+ input: unknown;
19
+ }>;
20
+ };
21
+ type SubmitConflictReason = "conflict" | "write_congestion" | "client_far_behind" | "no_commands" | "already_handled" | "limit_exceeded";
22
+ type SubmitAppliedResponse = {
23
+ status: "applied";
24
+ requestId: string;
25
+ confirmedCommandIds: string[];
26
+ lastVersionstamp?: string;
27
+ entries: OutboxEntry[];
28
+ };
29
+ type SubmitConflictResponse = {
30
+ status: "conflict";
31
+ requestId: string;
32
+ confirmedCommandIds: string[];
33
+ conflictCommandId?: string;
34
+ lastVersionstamp?: string;
35
+ entries: OutboxEntry[];
36
+ reason: SubmitConflictReason;
37
+ };
38
+ type SubmitResponse = SubmitAppliedResponse | SubmitConflictResponse;
39
+ type SyncCommandTxFactory = (options?: Omit<ExecuteTxOptions, "createUnitOfWork">) => HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, {}>;
40
+ type SyncCommandHandler<TInput = unknown, TContext = unknown> = (args: {
41
+ input: TInput;
42
+ tx: SyncCommandTxFactory;
43
+ ctx: TContext;
44
+ }) => Promise<unknown>;
45
+ type SyncCommandDefinition<TInput = unknown, TContext = unknown> = {
46
+ name: string;
47
+ handler: SyncCommandHandler<TInput, TContext>;
48
+ createServerContext?: (requestContext: RequestThisContext) => TContext;
49
+ };
50
+ type SyncCommandRegistry = {
51
+ schemaName: string;
52
+ commands: Map<string, SyncCommandDefinition>;
53
+ getCommand: (name: string) => SyncCommandDefinition | undefined;
54
+ };
55
+ type SyncCommandTargetRegistration = {
56
+ fragmentName: string;
57
+ schemaName: string;
58
+ namespace: string | null;
59
+ commands: Map<string, SyncCommandDefinition>;
60
+ };
61
+ //#endregion
62
+ export { SubmitAppliedResponse, SubmitConflictReason, SubmitConflictResponse, SubmitRequest, SubmitResponse, SyncCommandDefinition, SyncCommandHandler, SyncCommandRegistry, SyncCommandTargetRegistration, SyncCommandTxFactory };
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/sync/types.ts"],"sourcesContent":[],"mappings":";;;;;KAQY,aAAA;;EAAA,SAAA,EAAA,MAAa;EAgBb,0BAAoB,EAAA,QAAA,GAAA,UAAA;EAQpB,eAAA,EAAA,MAAA;EAQA,QAAA,EA3BA,KA2BA,CAAA;IAUA,EAAA,EAAA,MAAA;IAEA,IAAA,EAAA,MAAA;IACK,MAAA,EAAA;MAAL,QAAA,EAAA,MAAA;MACP,MAAA,EAAA,MAAA;IAAgB,CAAA;IAET,KAAA,EAAA,OAAA;EACH,CAAA,CAAA;CACH;AACC,KAnCK,oBAAA,GAmCL,UAAA,GAAA,kBAAA,GAAA,mBAAA,GAAA,aAAA,GAAA,iBAAA,GAAA,gBAAA;AACD,KA5BM,qBAAA,GA4BN;EAAO,MAAA,EAAA,SAAA;EAED,SAAA,EAAA,MAAA;EAEkB,mBAAA,EAAA,MAAA,EAAA;EAAQ,gBAAA,CAAA,EAAA,MAAA;EAA3B,OAAA,EA3BA,WA2BA,EAAA;CAC8B;AAAuB,KAzBpD,sBAAA,GAyBoD;EAAQ,MAAA,EAAA,UAAA;EAG5D,SAAA,EAAA,MAAA;EAEY,mBAAA,EAAA,MAAA,EAAA;EAAZ,iBAAA,CAAA,EAAA,MAAA;EACoB,gBAAA,CAAA,EAAA,MAAA;EAAqB,OAAA,EAzB1C,WAyB0C,EAAA;EAGzC,MAAA,EA3BF,oBA2BE;;KAxBA,cAAA,GAAiB,wBAAwB;KAEzC,oBAAA,cACA,KAAK,0CACZ;KAEO;SACH;MACH;OACC;MACD;KAEM;;WAED,mBAAmB,QAAQ;yCACG,uBAAuB;;KAGpD,mBAAA;;YAEA,YAAY;gCACQ;;KAGpB,6BAAA;;;;YAIA,YAAY"}
@@ -1,11 +1,15 @@
1
- import { SqlAdapter } from "../adapters/generic-sql/generic-sql-adapter.js";
2
1
  import { BetterSQLite3DriverConfig } from "../adapters/generic-sql/driver-config.js";
2
+ import { SqlAdapter } from "../adapters/generic-sql/generic-sql-adapter.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { SqliteDialect } from "kysely";
7
7
 
8
8
  //#region src/util/default-database-adapter.ts
9
+ const isCloudflareWorkers = () => {
10
+ if (typeof globalThis === "undefined") return false;
11
+ return globalThis.navigator?.userAgent === "Cloudflare-Workers";
12
+ };
9
13
  const createNodeRequire = () => {
10
14
  try {
11
15
  const metaUrl = typeof import.meta !== "undefined" ? import.meta.url : void 0;
@@ -16,6 +20,7 @@ const createNodeRequire = () => {
16
20
  }
17
21
  };
18
22
  const loadBetterSqlite3 = () => {
23
+ if (isCloudflareWorkers()) return null;
19
24
  const requireFn = createNodeRequire();
20
25
  if (!requireFn) return null;
21
26
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"default-database-adapter.js","names":[],"sources":["../../src/util/default-database-adapter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { SqliteDialect } from \"kysely\";\nimport { SqlAdapter } from \"../adapters/generic-sql/generic-sql-adapter\";\nimport { BetterSQLite3DriverConfig } from \"../adapters/generic-sql/driver-config\";\nimport type { DatabaseAdapter } from \"../adapters/adapters\";\nimport type { AnySchema } from \"../schema/create\";\ntype DatabaseAdapterConfig = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n databaseAdapter?: DatabaseAdapter<any>;\n databaseNamespace?: string | null;\n};\n\ntype BetterSqlite3Constructor = typeof import(\"better-sqlite3\");\n\nconst createNodeRequire = (): NodeRequire | null => {\n try {\n const metaUrl = typeof import.meta !== \"undefined\" ? import.meta.url : undefined;\n if (!metaUrl || typeof metaUrl !== \"string\") {\n return null;\n }\n return createRequire(metaUrl);\n } catch {\n return null;\n }\n};\n\nconst loadBetterSqlite3 = (): BetterSqlite3Constructor | null => {\n const requireFn = createNodeRequire();\n if (!requireFn) {\n return null;\n }\n try {\n const module = requireFn(\"better-sqlite3\");\n return (module.default ?? module) as BetterSqlite3Constructor;\n } catch {\n return null;\n }\n};\n\nconst betterSqlite3Constructor = loadBetterSqlite3();\n\nconst defaultDataDir = (): string => {\n const configured = process.env[\"FRAGNO_DATA_DIR\"];\n if (configured && configured.trim().length > 0) {\n return configured;\n }\n return path.join(process.env[\"HOME\"] ?? process.cwd(), \".fragno\");\n};\n\nconst sanitizeFileSegment = (name: string): string => {\n const sanitized = name.replace(/[^a-z0-9-]/gi, \"_\");\n return sanitized.length > 0 ? sanitized : \"fragno\";\n};\n\nconst resolveSqliteDatabasePath = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n): string => {\n const baseName =\n typeof options.databaseNamespace === \"string\" && options.databaseNamespace.length > 0\n ? options.databaseNamespace\n : schema.name;\n const fileName = `${sanitizeFileSegment(baseName)}.sqlite`;\n return path.join(defaultDataDir(), fileName);\n};\n\nconst createDefaultSqliteAdapter = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): DatabaseAdapter<any> | null => {\n if (!betterSqlite3Constructor) {\n return null;\n }\n\n const dbPath = resolveSqliteDatabasePath(options, schema);\n fs.mkdirSync(path.dirname(dbPath), { recursive: true });\n\n const dialect = new SqliteDialect({\n database: new betterSqlite3Constructor(dbPath),\n });\n const driverConfig = new BetterSQLite3DriverConfig();\n return new SqlAdapter({ dialect, driverConfig });\n};\n\nexport const resolveDatabaseAdapter = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): DatabaseAdapter<any> => {\n if (options.databaseAdapter) {\n return options.databaseAdapter;\n }\n\n const defaultAdapter = createDefaultSqliteAdapter(options, schema);\n if (!defaultAdapter) {\n throw new Error(\n \"Database fragment requires options.databaseAdapter, or install better-sqlite3 to use the default SQLite adapter.\",\n );\n }\n\n options.databaseAdapter = defaultAdapter;\n return defaultAdapter;\n};\n"],"mappings":";;;;;;;;AAgBA,MAAM,0BAA8C;AAClD,KAAI;EACF,MAAM,UAAU,OAAO,OAAO,SAAS,cAAc,OAAO,KAAK,MAAM;AACvE,MAAI,CAAC,WAAW,OAAO,YAAY,SACjC,QAAO;AAET,SAAO,cAAc,QAAQ;SACvB;AACN,SAAO;;;AAIX,MAAM,0BAA2D;CAC/D,MAAM,YAAY,mBAAmB;AACrC,KAAI,CAAC,UACH,QAAO;AAET,KAAI;EACF,MAAM,SAAS,UAAU,iBAAiB;AAC1C,SAAQ,OAAO,WAAW;SACpB;AACN,SAAO;;;AAIX,MAAM,2BAA2B,mBAAmB;AAEpD,MAAM,uBAA+B;CACnC,MAAM,aAAa,QAAQ,IAAI;AAC/B,KAAI,cAAc,WAAW,MAAM,CAAC,SAAS,EAC3C,QAAO;AAET,QAAO,KAAK,KAAK,QAAQ,IAAI,WAAW,QAAQ,KAAK,EAAE,UAAU;;AAGnE,MAAM,uBAAuB,SAAyB;CACpD,MAAM,YAAY,KAAK,QAAQ,gBAAgB,IAAI;AACnD,QAAO,UAAU,SAAS,IAAI,YAAY;;AAG5C,MAAM,6BACJ,SACA,WACW;CAKX,MAAM,WAAW,GAAG,oBAHlB,OAAO,QAAQ,sBAAsB,YAAY,QAAQ,kBAAkB,SAAS,IAChF,QAAQ,oBACR,OAAO,KACoC,CAAC;AAClD,QAAO,KAAK,KAAK,gBAAgB,EAAE,SAAS;;AAG9C,MAAM,8BACJ,SACA,WAEgC;AAChC,KAAI,CAAC,yBACH,QAAO;CAGT,MAAM,SAAS,0BAA0B,SAAS,OAAO;AACzD,IAAG,UAAU,KAAK,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AAMvD,QAAO,IAAI,WAAW;EAAE,SAJR,IAAI,cAAc,EAChC,UAAU,IAAI,yBAAyB,OAAO,EAC/C,CAAC;EAE+B,cADZ,IAAI,2BAA2B;EACL,CAAC;;AAGlD,MAAa,0BACX,SACA,WAEyB;AACzB,KAAI,QAAQ,gBACV,QAAO,QAAQ;CAGjB,MAAM,iBAAiB,2BAA2B,SAAS,OAAO;AAClE,KAAI,CAAC,eACH,OAAM,IAAI,MACR,mHACD;AAGH,SAAQ,kBAAkB;AAC1B,QAAO"}
1
+ {"version":3,"file":"default-database-adapter.js","names":[],"sources":["../../src/util/default-database-adapter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nimport { SqliteDialect } from \"kysely\";\n\nimport type { DatabaseAdapter } from \"../adapters/adapters\";\nimport { BetterSQLite3DriverConfig } from \"../adapters/generic-sql/driver-config\";\nimport { SqlAdapter } from \"../adapters/generic-sql/generic-sql-adapter\";\nimport type { AnySchema } from \"../schema/create\";\ntype DatabaseAdapterConfig = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n databaseAdapter?: DatabaseAdapter<any>;\n databaseNamespace?: string | null;\n};\n\ntype BetterSqlite3Constructor = typeof import(\"better-sqlite3\");\n\nconst isCloudflareWorkers = (): boolean => {\n if (typeof globalThis === \"undefined\") {\n return false;\n }\n const navigatorRef = (globalThis as { navigator?: { userAgent?: string } }).navigator;\n return navigatorRef?.userAgent === \"Cloudflare-Workers\";\n};\n\nconst createNodeRequire = (): NodeRequire | null => {\n try {\n const metaUrl = typeof import.meta !== \"undefined\" ? import.meta.url : undefined;\n if (!metaUrl || typeof metaUrl !== \"string\") {\n return null;\n }\n return createRequire(metaUrl);\n } catch {\n return null;\n }\n};\n\nconst loadBetterSqlite3 = (): BetterSqlite3Constructor | null => {\n if (isCloudflareWorkers()) {\n return null;\n }\n const requireFn = createNodeRequire();\n if (!requireFn) {\n return null;\n }\n try {\n const module = requireFn(\"better-sqlite3\");\n return (module.default ?? module) as BetterSqlite3Constructor;\n } catch {\n return null;\n }\n};\n\nconst betterSqlite3Constructor = loadBetterSqlite3();\n\nconst defaultDataDir = (): string => {\n const configured = process.env[\"FRAGNO_DATA_DIR\"];\n if (configured && configured.trim().length > 0) {\n return configured;\n }\n return path.join(process.env[\"HOME\"] ?? process.cwd(), \".fragno\");\n};\n\nconst sanitizeFileSegment = (name: string): string => {\n const sanitized = name.replace(/[^a-z0-9-]/gi, \"_\");\n return sanitized.length > 0 ? sanitized : \"fragno\";\n};\n\nconst resolveSqliteDatabasePath = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n): string => {\n const baseName =\n typeof options.databaseNamespace === \"string\" && options.databaseNamespace.length > 0\n ? options.databaseNamespace\n : schema.name;\n const fileName = `${sanitizeFileSegment(baseName)}.sqlite`;\n return path.join(defaultDataDir(), fileName);\n};\n\nconst createDefaultSqliteAdapter = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): DatabaseAdapter<any> | null => {\n if (!betterSqlite3Constructor) {\n return null;\n }\n\n const dbPath = resolveSqliteDatabasePath(options, schema);\n fs.mkdirSync(path.dirname(dbPath), { recursive: true });\n\n const dialect = new SqliteDialect({\n database: new betterSqlite3Constructor(dbPath),\n });\n const driverConfig = new BetterSQLite3DriverConfig();\n return new SqlAdapter({ dialect, driverConfig });\n};\n\nexport const resolveDatabaseAdapter = <TSchema extends AnySchema>(\n options: DatabaseAdapterConfig,\n schema: TSchema,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): DatabaseAdapter<any> => {\n if (options.databaseAdapter) {\n return options.databaseAdapter;\n }\n\n const defaultAdapter = createDefaultSqliteAdapter(options, schema);\n if (!defaultAdapter) {\n throw new Error(\n \"Database fragment requires options.databaseAdapter, or install better-sqlite3 to use the default SQLite adapter.\",\n );\n }\n\n options.databaseAdapter = defaultAdapter;\n return defaultAdapter;\n};\n"],"mappings":";;;;;;;;AAkBA,MAAM,4BAAqC;AACzC,KAAI,OAAO,eAAe,YACxB,QAAO;AAGT,QADsB,WAAsD,WACvD,cAAc;;AAGrC,MAAM,0BAA8C;AAClD,KAAI;EACF,MAAM,UAAU,OAAO,OAAO,SAAS,cAAc,OAAO,KAAK,MAAM;AACvE,MAAI,CAAC,WAAW,OAAO,YAAY,SACjC,QAAO;AAET,SAAO,cAAc,QAAQ;SACvB;AACN,SAAO;;;AAIX,MAAM,0BAA2D;AAC/D,KAAI,qBAAqB,CACvB,QAAO;CAET,MAAM,YAAY,mBAAmB;AACrC,KAAI,CAAC,UACH,QAAO;AAET,KAAI;EACF,MAAM,SAAS,UAAU,iBAAiB;AAC1C,SAAQ,OAAO,WAAW;SACpB;AACN,SAAO;;;AAIX,MAAM,2BAA2B,mBAAmB;AAEpD,MAAM,uBAA+B;CACnC,MAAM,aAAa,QAAQ,IAAI;AAC/B,KAAI,cAAc,WAAW,MAAM,CAAC,SAAS,EAC3C,QAAO;AAET,QAAO,KAAK,KAAK,QAAQ,IAAI,WAAW,QAAQ,KAAK,EAAE,UAAU;;AAGnE,MAAM,uBAAuB,SAAyB;CACpD,MAAM,YAAY,KAAK,QAAQ,gBAAgB,IAAI;AACnD,QAAO,UAAU,SAAS,IAAI,YAAY;;AAG5C,MAAM,6BACJ,SACA,WACW;CAKX,MAAM,WAAW,GAAG,oBAHlB,OAAO,QAAQ,sBAAsB,YAAY,QAAQ,kBAAkB,SAAS,IAChF,QAAQ,oBACR,OAAO,KACoC,CAAC;AAClD,QAAO,KAAK,KAAK,gBAAgB,EAAE,SAAS;;AAG9C,MAAM,8BACJ,SACA,WAEgC;AAChC,KAAI,CAAC,yBACH,QAAO;CAGT,MAAM,SAAS,0BAA0B,SAAS,OAAO;AACzD,IAAG,UAAU,KAAK,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AAMvD,QAAO,IAAI,WAAW;EAAE,SAJR,IAAI,cAAc,EAChC,UAAU,IAAI,yBAAyB,OAAO,EAC/C,CAAC;EAE+B,cADZ,IAAI,2BAA2B;EACL,CAAC;;AAGlD,MAAa,0BACX,SACA,WAEyB;AACzB,KAAI,QAAQ,gBACV,QAAO,QAAQ;CAGjB,MAAM,iBAAiB,2BAA2B,SAAS,OAAO;AAClE,KAAI,CAAC,eACH,OAAM,IAAI,MACR,mHACD;AAGH,SAAQ,kBAAkB;AAC1B,QAAO"}
@@ -1,14 +1,13 @@
1
1
  import { AnySchema } from "./schema/create.js";
2
2
  import { DatabaseFragmentDefinitionBuilder, DatabaseHandlerContext, DatabaseServiceContext, ImplicitDatabaseDependencies } from "./db-fragment-definition-builder.js";
3
- import { InternalFragmentInstance } from "./fragments/internal-fragment.js";
4
3
  import { HooksMap } from "./hooks/hooks.js";
5
- import { AnyFragnoInstantiatedFragment, FragmentDefinitionBuilder, FragnoPublicConfig, RequestThisContext } from "@fragno-dev/core";
4
+ import { AnyRouteOrFactory, FragmentDefinitionBuilder, FragnoPublicConfig, RequestThisContext } from "@fragno-dev/core";
6
5
 
7
6
  //#region src/with-database.d.ts
8
7
 
9
8
  /**
10
9
  * Helper to add database support to a fragment builder.
11
- * Automatically links the internal fragment and adds ImplicitDatabaseDependencies to the TDeps type.
10
+ * Registers the schema with the adapter registry and adds ImplicitDatabaseDependencies to the TDeps type.
12
11
  *
13
12
  * @example
14
13
  * ```typescript
@@ -24,9 +23,7 @@ import { AnyFragnoInstantiatedFragment, FragmentDefinitionBuilder, FragnoPublicC
24
23
  * .build();
25
24
  * ```
26
25
  */
27
- declare function withDatabase<TSchema extends AnySchema>(schema: TSchema): <TConfig, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, TLinkedFragments extends Record<string, AnyFragnoInstantiatedFragment>>(builder: FragmentDefinitionBuilder<TConfig, FragnoPublicConfig, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage, TLinkedFragments>) => DatabaseFragmentDefinitionBuilder<TSchema, TConfig, TDeps & ImplicitDatabaseDependencies<TSchema>, TBaseServices, TServices, TServiceDeps, TPrivateServices, HooksMap, DatabaseServiceContext<HooksMap>, DatabaseHandlerContext, TLinkedFragments & {
28
- _fragno_internal: InternalFragmentInstance;
29
- }>;
26
+ declare function withDatabase<TSchema extends AnySchema>(schema: TSchema): <TConfig, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, TInternalRoutes extends readonly AnyRouteOrFactory[]>(builder: FragmentDefinitionBuilder<TConfig, FragnoPublicConfig, TDeps, TBaseServices, TServices, TServiceDeps, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage, TInternalRoutes>) => DatabaseFragmentDefinitionBuilder<TSchema, TConfig, TDeps & ImplicitDatabaseDependencies<TSchema>, TBaseServices, TServices, TServiceDeps, TPrivateServices, HooksMap, DatabaseServiceContext<HooksMap>, DatabaseHandlerContext, TInternalRoutes>;
30
27
  //#endregion
31
28
  export { withDatabase };
32
29
  //# sourceMappingURL=with-database.d.ts.map