@fragno-dev/db 0.2.2 → 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 (587) hide show
  1. package/.turbo/turbo-build.log +404 -175
  2. package/CHANGELOG.md +109 -0
  3. package/README.md +54 -9
  4. package/dist/adapters/adapters.d.ts +23 -21
  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/driver-config.d.ts +16 -1
  8. package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
  9. package/dist/adapters/generic-sql/driver-config.js +23 -1
  10. package/dist/adapters/generic-sql/driver-config.js.map +1 -1
  11. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +24 -9
  12. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
  13. package/dist/adapters/generic-sql/generic-sql-adapter.js +60 -22
  14. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
  15. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +169 -3
  16. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
  17. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -1
  18. package/dist/adapters/generic-sql/migration/dialect/mysql.js +25 -6
  19. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
  20. package/dist/adapters/generic-sql/migration/dialect/postgres.js +7 -6
  21. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
  22. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +193 -16
  23. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
  24. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -1
  25. package/dist/adapters/generic-sql/migration/executor.js +30 -3
  26. package/dist/adapters/generic-sql/migration/executor.js.map +1 -1
  27. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
  28. package/dist/adapters/generic-sql/migration/prepared-migrations.js +9 -9
  29. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
  30. package/dist/adapters/generic-sql/migration/sql-generator.js +75 -52
  31. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
  32. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +7 -6
  33. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
  34. package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
  35. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
  36. package/dist/adapters/generic-sql/query/db-now-sql.js +27 -0
  37. package/dist/adapters/generic-sql/query/db-now-sql.js.map +1 -0
  38. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +32 -21
  39. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
  40. package/dist/adapters/generic-sql/query/select-builder.js +5 -3
  41. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
  42. package/dist/adapters/generic-sql/query/sql-query-compiler.js +49 -18
  43. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
  44. package/dist/adapters/generic-sql/query/where-builder.js +43 -29
  45. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
  46. package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
  47. package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  48. package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
  49. package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
  50. package/dist/adapters/generic-sql/uow-decoder.js +6 -2
  51. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
  52. package/dist/adapters/generic-sql/uow-encoder.js +27 -8
  53. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
  54. package/dist/adapters/in-memory/condition-evaluator.js +135 -0
  55. package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
  56. package/dist/adapters/in-memory/errors.d.ts +13 -0
  57. package/dist/adapters/in-memory/errors.d.ts.map +1 -0
  58. package/dist/adapters/in-memory/errors.js +23 -0
  59. package/dist/adapters/in-memory/errors.js.map +1 -0
  60. package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
  61. package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
  62. package/dist/adapters/in-memory/in-memory-adapter.js +196 -0
  63. package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
  64. package/dist/adapters/in-memory/in-memory-uow.js +871 -0
  65. package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
  66. package/dist/adapters/in-memory/index.d.ts +4 -0
  67. package/dist/adapters/in-memory/index.js +4 -0
  68. package/dist/adapters/in-memory/options.d.ts +30 -0
  69. package/dist/adapters/in-memory/options.d.ts.map +1 -0
  70. package/dist/adapters/in-memory/options.js +62 -0
  71. package/dist/adapters/in-memory/options.js.map +1 -0
  72. package/dist/adapters/in-memory/reference-resolution.js +26 -0
  73. package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
  74. package/dist/adapters/in-memory/sorted-array-index.js +129 -0
  75. package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
  76. package/dist/adapters/in-memory/store.js +71 -0
  77. package/dist/adapters/in-memory/store.js.map +1 -0
  78. package/dist/adapters/in-memory/value-comparison.js +28 -0
  79. package/dist/adapters/in-memory/value-comparison.js.map +1 -0
  80. package/dist/adapters/shared/from-unit-of-work-compiler.js +51 -24
  81. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
  82. package/dist/adapters/shared/uow-operation-compiler.js +11 -11
  83. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
  84. package/dist/adapters/sql/index.d.ts +5 -0
  85. package/dist/adapters/sql/index.js +4 -0
  86. package/dist/browser/adapters/adapters.d.ts +61 -0
  87. package/dist/browser/adapters/adapters.d.ts.map +1 -0
  88. package/dist/browser/adapters/generic-sql/migration/executor.d.ts +15 -0
  89. package/dist/browser/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  90. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  91. package/dist/browser/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  92. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts +11 -0
  93. package/dist/browser/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  94. package/dist/browser/adapters/in-memory/in-memory-adapter.d.ts +5 -0
  95. package/dist/browser/adapters/in-memory/index.d.ts +2 -0
  96. package/dist/browser/adapters/in-memory/options.d.ts +1 -0
  97. package/dist/browser/db-fragment-definition-builder.d.ts +237 -0
  98. package/dist/browser/db-fragment-definition-builder.d.ts.map +1 -0
  99. package/dist/browser/durable-hooks.d.ts +3 -0
  100. package/dist/browser/fragments/internal-fragment.d.ts +317 -0
  101. package/dist/browser/fragments/internal-fragment.d.ts.map +1 -0
  102. package/dist/browser/fragments/internal-fragment.schema.d.ts +1 -0
  103. package/dist/browser/hooks/durable-hooks-logger.d.ts +10 -0
  104. package/dist/browser/hooks/durable-hooks-logger.d.ts.map +1 -0
  105. package/dist/browser/hooks/hooks.d.ts +146 -0
  106. package/dist/browser/hooks/hooks.d.ts.map +1 -0
  107. package/dist/browser/id.js +1 -0
  108. package/dist/browser/internal/adapter-registry.d.ts +4 -0
  109. package/dist/browser/internal/outbox-state.d.ts +2 -0
  110. package/dist/browser/mod.d.ts +15 -0
  111. package/dist/browser/mod.d.ts.map +1 -0
  112. package/dist/browser/mod.js +17 -0
  113. package/dist/browser/mod.js.map +1 -0
  114. package/dist/browser/mod2.d.ts +48 -0
  115. package/dist/browser/mod2.d.ts.map +1 -0
  116. package/dist/browser/naming/sql-naming.d.ts +19 -0
  117. package/dist/browser/naming/sql-naming.d.ts.map +1 -0
  118. package/dist/browser/outbox/outbox.d.ts +21 -0
  119. package/dist/browser/outbox/outbox.d.ts.map +1 -0
  120. package/dist/browser/query/column-defaults.js +1 -0
  121. package/dist/browser/query/condition-builder.d.ts +44 -0
  122. package/dist/browser/query/condition-builder.d.ts.map +1 -0
  123. package/dist/browser/query/condition-builder.js +97 -0
  124. package/dist/browser/query/condition-builder.js.map +1 -0
  125. package/dist/browser/query/cursor.d.ts +105 -0
  126. package/dist/browser/query/cursor.d.ts.map +1 -0
  127. package/dist/browser/query/cursor.js +150 -0
  128. package/dist/browser/query/cursor.js.map +1 -0
  129. package/dist/browser/query/db-now.d.ts +22 -0
  130. package/dist/browser/query/db-now.d.ts.map +1 -0
  131. package/dist/browser/query/db-now.js +33 -0
  132. package/dist/browser/query/db-now.js.map +1 -0
  133. package/dist/browser/query/orm/orm.d.ts +18 -0
  134. package/dist/browser/query/orm/orm.d.ts.map +1 -0
  135. package/dist/browser/query/simple-query-interface.d.ts +108 -0
  136. package/dist/browser/query/simple-query-interface.d.ts.map +1 -0
  137. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts +423 -0
  138. package/dist/browser/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  139. package/dist/browser/query/unit-of-work/execute-unit-of-work.js +507 -0
  140. package/dist/browser/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  141. package/dist/browser/query/unit-of-work/retry-policy.d.ts +23 -0
  142. package/dist/browser/query/unit-of-work/retry-policy.d.ts.map +1 -0
  143. package/dist/browser/query/unit-of-work/retry-policy.js +40 -0
  144. package/dist/browser/query/unit-of-work/retry-policy.js.map +1 -0
  145. package/dist/browser/query/unit-of-work/unit-of-work.d.ts +703 -0
  146. package/dist/browser/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  147. package/dist/browser/query/unit-of-work/unit-of-work.js +1206 -0
  148. package/dist/browser/query/unit-of-work/unit-of-work.js.map +1 -0
  149. package/dist/browser/query/value-encoding.js +38 -0
  150. package/dist/browser/query/value-encoding.js.map +1 -0
  151. package/dist/browser/schema/create.d.ts +326 -0
  152. package/dist/browser/schema/create.d.ts.map +1 -0
  153. package/dist/browser/schema/create.js +89 -0
  154. package/dist/browser/schema/create.js.map +1 -0
  155. package/dist/browser/schema/generate-id.js +28 -0
  156. package/dist/browser/schema/generate-id.js.map +1 -0
  157. package/dist/browser/shared/providers.d.ts +6 -0
  158. package/dist/browser/shared/providers.d.ts.map +1 -0
  159. package/dist/browser/sql-driver/connection/connection-provider.d.ts +13 -0
  160. package/dist/browser/sql-driver/connection/connection-provider.d.ts.map +1 -0
  161. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  162. package/dist/browser/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  163. package/dist/browser/sql-driver/driver/runtime-driver.d.ts +23 -0
  164. package/dist/browser/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  165. package/dist/browser/sql-driver/query-executor/plugin.d.ts +17 -0
  166. package/dist/browser/sql-driver/query-executor/plugin.d.ts.map +1 -0
  167. package/dist/browser/sql-driver/query-executor/query-executor.d.ts +36 -0
  168. package/dist/browser/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  169. package/dist/browser/sql-driver/sql-driver-adapter.d.ts +29 -0
  170. package/dist/browser/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  171. package/dist/browser/sql-driver/sql-driver.d.ts +38 -0
  172. package/dist/browser/sql-driver/sql-driver.d.ts.map +1 -0
  173. package/dist/browser/sync/commands.d.ts +15 -0
  174. package/dist/browser/sync/commands.d.ts.map +1 -0
  175. package/dist/browser/sync/commands.js +27 -0
  176. package/dist/browser/sync/commands.js.map +1 -0
  177. package/dist/browser/sync/types.d.ts +63 -0
  178. package/dist/browser/sync/types.d.ts.map +1 -0
  179. package/dist/browser/util/types.d.ts +8 -0
  180. package/dist/browser/util/types.d.ts.map +1 -0
  181. package/dist/browser/with-database.d.ts +29 -0
  182. package/dist/browser/with-database.d.ts.map +1 -0
  183. package/dist/client.d.ts +4 -0
  184. package/dist/client.js +5 -0
  185. package/dist/db-fragment-definition-builder.d.ts +101 -33
  186. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  187. package/dist/db-fragment-definition-builder.js +450 -60
  188. package/dist/db-fragment-definition-builder.js.map +1 -1
  189. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts +20 -0
  190. package/dist/dispatchers/cloudflare-do/dispatcher.d.ts.map +1 -0
  191. package/dist/dispatchers/cloudflare-do/dispatcher.js +147 -0
  192. package/dist/dispatchers/cloudflare-do/dispatcher.js.map +1 -0
  193. package/dist/dispatchers/cloudflare-do/index.d.ts +11 -0
  194. package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
  195. package/dist/dispatchers/cloudflare-do/index.js +31 -0
  196. package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
  197. package/dist/dispatchers/node/dispatcher.d.ts +14 -0
  198. package/dist/dispatchers/node/dispatcher.d.ts.map +1 -0
  199. package/dist/dispatchers/node/dispatcher.js +80 -0
  200. package/dist/dispatchers/node/dispatcher.js.map +1 -0
  201. package/dist/dispatchers/node/index.d.ts +12 -0
  202. package/dist/dispatchers/node/index.d.ts.map +1 -0
  203. package/dist/dispatchers/node/index.js +27 -0
  204. package/dist/dispatchers/node/index.js.map +1 -0
  205. package/dist/durable-hooks.d.ts +31 -0
  206. package/dist/durable-hooks.d.ts.map +1 -0
  207. package/dist/durable-hooks.js +23 -0
  208. package/dist/durable-hooks.js.map +1 -0
  209. package/dist/fragments/internal-fragment.d.ts +186 -8
  210. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  211. package/dist/fragments/internal-fragment.js +203 -38
  212. package/dist/fragments/internal-fragment.js.map +1 -1
  213. package/dist/fragments/internal-fragment.routes.js +164 -0
  214. package/dist/fragments/internal-fragment.routes.js.map +1 -0
  215. package/dist/fragments/internal-fragment.schema.d.ts +15 -0
  216. package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
  217. package/dist/fragments/internal-fragment.schema.js +39 -0
  218. package/dist/fragments/internal-fragment.schema.js.map +1 -0
  219. package/dist/hooks/durable-hooks-logger.d.ts +10 -0
  220. package/dist/hooks/durable-hooks-logger.d.ts.map +1 -0
  221. package/dist/hooks/durable-hooks-logger.js +75 -0
  222. package/dist/hooks/durable-hooks-logger.js.map +1 -0
  223. package/dist/hooks/durable-hooks-processor.d.ts +1 -0
  224. package/dist/hooks/durable-hooks-processor.js +80 -0
  225. package/dist/hooks/durable-hooks-processor.js.map +1 -0
  226. package/dist/hooks/durable-hooks-runtime.js +44 -0
  227. package/dist/hooks/durable-hooks-runtime.js.map +1 -0
  228. package/dist/hooks/hooks.d.ts +100 -1
  229. package/dist/hooks/hooks.d.ts.map +1 -1
  230. package/dist/hooks/hooks.js +254 -27
  231. package/dist/hooks/hooks.js.map +1 -1
  232. package/dist/id.d.ts +2 -2
  233. package/dist/id.js +2 -2
  234. package/dist/internal/adapter-registry.d.ts +11 -0
  235. package/dist/internal/adapter-registry.d.ts.map +1 -0
  236. package/dist/internal/adapter-registry.js +135 -0
  237. package/dist/internal/adapter-registry.js.map +1 -0
  238. package/dist/internal/outbox-state.d.ts +2 -0
  239. package/dist/internal/outbox-state.js +26 -0
  240. package/dist/internal/outbox-state.js.map +1 -0
  241. package/dist/migration-engine/auto-from-schema.d.ts +33 -0
  242. package/dist/migration-engine/auto-from-schema.d.ts.map +1 -0
  243. package/dist/migration-engine/auto-from-schema.js +223 -37
  244. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  245. package/dist/migration-engine/generation-engine.d.ts +16 -10
  246. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  247. package/dist/migration-engine/generation-engine.js +86 -35
  248. package/dist/migration-engine/generation-engine.js.map +1 -1
  249. package/dist/migration-engine/shared.d.ts +113 -0
  250. package/dist/migration-engine/shared.d.ts.map +1 -0
  251. package/dist/migration-engine/shared.js.map +1 -1
  252. package/dist/mod.d.ts +20 -12
  253. package/dist/mod.d.ts.map +1 -1
  254. package/dist/mod.js +18 -12
  255. package/dist/mod.js.map +1 -1
  256. package/dist/naming/sql-naming.d.ts +19 -0
  257. package/dist/naming/sql-naming.d.ts.map +1 -0
  258. package/dist/naming/sql-naming.js +116 -0
  259. package/dist/naming/sql-naming.js.map +1 -0
  260. package/dist/outbox/outbox-builder.js +156 -0
  261. package/dist/outbox/outbox-builder.js.map +1 -0
  262. package/dist/outbox/outbox.d.ts +54 -0
  263. package/dist/outbox/outbox.d.ts.map +1 -0
  264. package/dist/outbox/outbox.js +37 -0
  265. package/dist/outbox/outbox.js.map +1 -0
  266. package/dist/query/column-defaults.js +20 -4
  267. package/dist/query/column-defaults.js.map +1 -1
  268. package/dist/query/condition-builder.d.ts +7 -1
  269. package/dist/query/condition-builder.d.ts.map +1 -1
  270. package/dist/query/condition-builder.js +5 -1
  271. package/dist/query/condition-builder.js.map +1 -1
  272. package/dist/query/cursor-client.d.ts +105 -0
  273. package/dist/query/cursor-client.d.ts.map +1 -0
  274. package/dist/query/cursor-client.js +165 -0
  275. package/dist/query/cursor-client.js.map +1 -0
  276. package/dist/query/cursor.d.ts +3 -1
  277. package/dist/query/cursor.d.ts.map +1 -1
  278. package/dist/query/cursor.js +51 -14
  279. package/dist/query/cursor.js.map +1 -1
  280. package/dist/query/db-now.d.ts +22 -0
  281. package/dist/query/db-now.d.ts.map +1 -0
  282. package/dist/query/db-now.js +35 -0
  283. package/dist/query/db-now.js.map +1 -0
  284. package/dist/query/orm/orm.js.map +1 -1
  285. package/dist/query/serialize/create-sql-serializer.js +5 -4
  286. package/dist/query/serialize/create-sql-serializer.js.map +1 -1
  287. package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
  288. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
  289. package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
  290. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
  291. package/dist/query/serialize/dialect/sqlite-serializer.js +60 -12
  292. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
  293. package/dist/query/serialize/sql-serializer.js +2 -2
  294. package/dist/query/serialize/sql-serializer.js.map +1 -1
  295. package/dist/query/simple-query-interface.d.ts +13 -4
  296. package/dist/query/simple-query-interface.d.ts.map +1 -1
  297. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +37 -2
  298. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  299. package/dist/query/unit-of-work/execute-unit-of-work.js +50 -24
  300. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  301. package/dist/query/unit-of-work/unit-of-work.d.ts +92 -30
  302. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  303. package/dist/query/unit-of-work/unit-of-work.js +136 -11
  304. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  305. package/dist/query/value-decoding.js +16 -6
  306. package/dist/query/value-decoding.js.map +1 -1
  307. package/dist/query/value-encoding.js +29 -9
  308. package/dist/query/value-encoding.js.map +1 -1
  309. package/dist/schema/create.d.ts +103 -35
  310. package/dist/schema/create.d.ts.map +1 -1
  311. package/dist/schema/create.js +172 -58
  312. package/dist/schema/create.js.map +1 -1
  313. package/dist/schema/generate-id.js +2 -2
  314. package/dist/schema/generate-id.js.map +1 -1
  315. package/dist/schema/type-conversion/create-sql-type-mapper.js +4 -3
  316. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  317. package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
  318. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  319. package/dist/schema/validator.d.ts +10 -0
  320. package/dist/schema/validator.d.ts.map +1 -0
  321. package/dist/schema/validator.js +123 -0
  322. package/dist/schema/validator.js.map +1 -0
  323. package/dist/schema-output/drizzle.d.ts +30 -0
  324. package/dist/schema-output/drizzle.d.ts.map +1 -0
  325. package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +88 -60
  326. package/dist/schema-output/drizzle.js.map +1 -0
  327. package/dist/schema-output/prisma.d.ts +17 -0
  328. package/dist/schema-output/prisma.d.ts.map +1 -0
  329. package/dist/schema-output/prisma.js +307 -0
  330. package/dist/schema-output/prisma.js.map +1 -0
  331. package/dist/sql-driver/dialects/durable-object-dialect.js +3 -9
  332. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -1
  333. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -1
  334. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -1
  335. package/dist/sql-driver/sql-driver-adapter.js.map +1 -1
  336. package/dist/sql-driver/sql.js.map +1 -1
  337. package/dist/sync/commands.d.ts +15 -0
  338. package/dist/sync/commands.d.ts.map +1 -0
  339. package/dist/sync/commands.js +27 -0
  340. package/dist/sync/commands.js.map +1 -0
  341. package/dist/sync/index.d.ts +4 -0
  342. package/dist/sync/index.js +4 -0
  343. package/dist/sync/read-tracking.d.ts +25 -0
  344. package/dist/sync/read-tracking.d.ts.map +1 -0
  345. package/dist/sync/read-tracking.js +148 -0
  346. package/dist/sync/read-tracking.js.map +1 -0
  347. package/dist/sync/submit.js +213 -0
  348. package/dist/sync/submit.js.map +1 -0
  349. package/dist/sync/types.d.ts +63 -0
  350. package/dist/sync/types.d.ts.map +1 -0
  351. package/dist/util/default-database-adapter.js +66 -0
  352. package/dist/util/default-database-adapter.js.map +1 -0
  353. package/dist/with-database.d.ts +3 -6
  354. package/dist/with-database.d.ts.map +1 -1
  355. package/dist/with-database.js +8 -7
  356. package/dist/with-database.js.map +1 -1
  357. package/package.json +62 -55
  358. package/src/adapters/adapters.ts +33 -26
  359. package/src/adapters/drizzle/migrate-drizzle.test.ts +99 -41
  360. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +601 -0
  361. package/src/adapters/drizzle/test-utils.ts +13 -8
  362. package/src/adapters/generic-sql/driver-config.ts +38 -0
  363. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +10 -8
  364. package/src/adapters/generic-sql/generic-sql-adapter.ts +117 -34
  365. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +55 -0
  366. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +297 -3
  367. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +120 -0
  368. package/src/adapters/generic-sql/migration/cold-kysely.ts +1 -0
  369. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +27 -8
  370. package/src/adapters/generic-sql/migration/dialect/mysql.ts +47 -8
  371. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +28 -9
  372. package/src/adapters/generic-sql/migration/dialect/postgres.ts +9 -4
  373. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +839 -8
  374. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +396 -53
  375. package/src/adapters/generic-sql/migration/executor.test.ts +52 -0
  376. package/src/adapters/generic-sql/migration/executor.ts +47 -4
  377. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +238 -46
  378. package/src/adapters/generic-sql/migration/prepared-migrations.ts +21 -13
  379. package/src/adapters/generic-sql/migration/sql-generator.ts +145 -66
  380. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +11 -8
  381. package/src/adapters/generic-sql/query/cursor-utils.test.ts +272 -0
  382. package/src/adapters/generic-sql/query/cursor-utils.ts +42 -7
  383. package/src/adapters/generic-sql/query/db-now-sql.ts +49 -0
  384. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +171 -35
  385. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +53 -40
  386. package/src/adapters/generic-sql/query/select-builder.test.ts +16 -11
  387. package/src/adapters/generic-sql/query/select-builder.ts +7 -3
  388. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +75 -6
  389. package/src/adapters/generic-sql/query/sql-query-compiler.ts +129 -24
  390. package/src/adapters/generic-sql/query/where-builder.test.ts +96 -20
  391. package/src/adapters/generic-sql/query/where-builder.ts +112 -41
  392. package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +11 -20
  393. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +851 -0
  394. package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +18 -15
  395. package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +282 -14
  396. package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +129 -12
  397. package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +9 -7
  398. package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
  399. package/src/adapters/generic-sql/uow-decoder.test.ts +5 -4
  400. package/src/adapters/generic-sql/uow-decoder.ts +23 -5
  401. package/src/adapters/generic-sql/uow-encoder.test.ts +36 -3
  402. package/src/adapters/generic-sql/uow-encoder.ts +48 -13
  403. package/src/adapters/in-memory/condition-evaluator.test.ts +194 -0
  404. package/src/adapters/in-memory/condition-evaluator.ts +280 -0
  405. package/src/adapters/in-memory/errors.ts +20 -0
  406. package/src/adapters/in-memory/in-memory-adapter.ts +388 -0
  407. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +344 -0
  408. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +255 -0
  409. package/src/adapters/in-memory/in-memory-uow.ts +1724 -0
  410. package/src/adapters/in-memory/index.ts +3 -0
  411. package/src/adapters/in-memory/options.test.ts +42 -0
  412. package/src/adapters/in-memory/options.ts +91 -0
  413. package/src/adapters/in-memory/outbox.test.ts +361 -0
  414. package/src/adapters/in-memory/reference-resolution.test.ts +51 -0
  415. package/src/adapters/in-memory/reference-resolution.ts +67 -0
  416. package/src/adapters/in-memory/sorted-array-index.test.ts +124 -0
  417. package/src/adapters/in-memory/sorted-array-index.ts +228 -0
  418. package/src/adapters/in-memory/store.test.ts +69 -0
  419. package/src/adapters/in-memory/store.ts +145 -0
  420. package/src/adapters/in-memory/value-comparison.ts +53 -0
  421. package/src/adapters/in-memory/value-normalization.test.ts +58 -0
  422. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1207 -0
  423. package/src/adapters/shared/from-unit-of-work-compiler.ts +159 -47
  424. package/src/adapters/shared/uow-operation-compiler.ts +28 -18
  425. package/src/adapters/sql/index.ts +12 -0
  426. package/src/browser/mod.ts +64 -0
  427. package/src/client.ts +19 -0
  428. package/src/db-fragment-definition-builder.test.ts +845 -53
  429. package/src/db-fragment-definition-builder.ts +911 -95
  430. package/src/db-fragment-instantiator.test.ts +210 -94
  431. package/src/db-fragment-integration.test.ts +17 -12
  432. package/src/dispatchers/cloudflare-do/dispatcher.ts +204 -0
  433. package/src/dispatchers/cloudflare-do/index.test.ts +206 -0
  434. package/src/dispatchers/cloudflare-do/index.ts +63 -0
  435. package/src/dispatchers/node/dispatcher.ts +112 -0
  436. package/src/dispatchers/node/index.test.ts +120 -0
  437. package/src/dispatchers/node/index.ts +50 -0
  438. package/src/durable-hooks.test.ts +80 -0
  439. package/src/durable-hooks.ts +67 -0
  440. package/src/fragments/internal-fragment.routes.test.ts +570 -0
  441. package/src/fragments/internal-fragment.routes.ts +334 -0
  442. package/src/fragments/internal-fragment.schema.ts +95 -0
  443. package/src/fragments/internal-fragment.test.ts +505 -83
  444. package/src/fragments/internal-fragment.ts +453 -70
  445. package/src/hooks/durable-hooks-logger.ts +126 -0
  446. package/src/hooks/durable-hooks-processor.pglite.test.ts +87 -0
  447. package/src/hooks/durable-hooks-processor.test.ts +282 -0
  448. package/src/hooks/durable-hooks-processor.ts +173 -0
  449. package/src/hooks/durable-hooks-runtime.test.ts +65 -0
  450. package/src/hooks/durable-hooks-runtime.ts +81 -0
  451. package/src/hooks/hooks.test.ts +455 -34
  452. package/src/hooks/hooks.ts +501 -34
  453. package/src/id.test.ts +34 -0
  454. package/src/id.ts +1 -3
  455. package/src/internal/adapter-registry.test.ts +93 -0
  456. package/src/internal/adapter-registry.ts +239 -0
  457. package/src/internal/outbox-state.ts +43 -0
  458. package/src/migration-engine/auto-from-schema.test.ts +107 -14
  459. package/src/migration-engine/auto-from-schema.ts +365 -44
  460. package/src/migration-engine/create.test.ts +4 -3
  461. package/src/migration-engine/create.ts +1 -1
  462. package/src/migration-engine/generation-engine.test.ts +292 -110
  463. package/src/migration-engine/generation-engine.ts +117 -66
  464. package/src/migration-engine/shared.ts +14 -0
  465. package/src/mod.ts +95 -39
  466. package/src/naming/sql-naming.ts +181 -0
  467. package/src/outbox/outbox-builder.ts +241 -0
  468. package/src/outbox/outbox.test.ts +424 -0
  469. package/src/outbox/outbox.ts +139 -0
  470. package/src/query/column-defaults.ts +42 -4
  471. package/src/query/condition-builder.test.ts +18 -3
  472. package/src/query/condition-builder.ts +7 -0
  473. package/src/query/cursor-client.test.ts +70 -0
  474. package/src/query/cursor-client.ts +263 -0
  475. package/src/query/cursor.test.ts +119 -20
  476. package/src/query/cursor.ts +88 -27
  477. package/src/query/db-now.ts +73 -0
  478. package/src/query/orm/orm.ts +2 -2
  479. package/src/query/query-type.test.ts +4 -3
  480. package/src/query/serialize/create-sql-serializer.ts +10 -5
  481. package/src/query/serialize/dialect/mysql-serializer.ts +13 -5
  482. package/src/query/serialize/dialect/postgres-serializer.ts +35 -5
  483. package/src/query/serialize/dialect/sqlite-serializer.test.ts +90 -3
  484. package/src/query/serialize/dialect/sqlite-serializer.ts +108 -12
  485. package/src/query/serialize/sql-serializer.ts +4 -4
  486. package/src/query/simple-query-interface.ts +15 -4
  487. package/src/query/unit-of-work/execute-unit-of-work.test.ts +372 -10
  488. package/src/query/unit-of-work/execute-unit-of-work.ts +87 -27
  489. package/src/query/unit-of-work/retry-policy.test.ts +1 -0
  490. package/src/query/unit-of-work/tx-builder.test.ts +73 -1
  491. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +17 -16
  492. package/src/query/unit-of-work/unit-of-work-types.test.ts +42 -12
  493. package/src/query/unit-of-work/unit-of-work.test.ts +196 -39
  494. package/src/query/unit-of-work/unit-of-work.ts +309 -38
  495. package/src/query/value-decoding.test.ts +63 -4
  496. package/src/query/value-decoding.ts +32 -6
  497. package/src/query/value-encoding.test.ts +86 -2
  498. package/src/query/value-encoding.ts +56 -6
  499. package/src/schema/create.test.ts +293 -47
  500. package/src/schema/create.ts +406 -70
  501. package/src/schema/generate-id.test.ts +3 -2
  502. package/src/schema/generate-id.ts +2 -2
  503. package/src/schema/serialize.test.ts +18 -5
  504. package/src/schema/type-conversion/create-sql-type-mapper.ts +8 -3
  505. package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
  506. package/src/schema/type-conversion/type-mapping.test.ts +26 -1
  507. package/src/schema/validator.test.ts +199 -0
  508. package/src/schema/validator.ts +232 -0
  509. package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +232 -129
  510. package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +155 -99
  511. package/src/schema-output/prisma.test.ts +694 -0
  512. package/src/schema-output/prisma.ts +593 -0
  513. package/src/sql-driver/better-sqlite3.test.ts +5 -3
  514. package/src/sql-driver/dialects/durable-object-dialect.ts +3 -8
  515. package/src/sql-driver/query-executor/default-query-executor.ts +1 -1
  516. package/src/sql-driver/query-executor/query-executor-base.ts +1 -1
  517. package/src/sql-driver/query-executor/query-executor.ts +1 -1
  518. package/src/sql-driver/sql-driver-adapter.ts +2 -2
  519. package/src/sql-driver/sql.ts +2 -1
  520. package/src/sql-driver/sqlocal.test.ts +4 -2
  521. package/src/sync/commands.test.ts +39 -0
  522. package/src/sync/commands.ts +51 -0
  523. package/src/sync/conflict-checker.test.ts +450 -0
  524. package/src/sync/conflict-checker.ts +248 -0
  525. package/src/sync/index.ts +14 -0
  526. package/src/sync/plan.ts +9 -0
  527. package/src/sync/read-tracking.test.ts +177 -0
  528. package/src/sync/read-tracking.ts +287 -0
  529. package/src/sync/submit.test.ts +205 -0
  530. package/src/sync/submit.ts +328 -0
  531. package/src/sync/types.ts +80 -0
  532. package/src/util/default-database-adapter.ts +119 -0
  533. package/src/with-database.ts +20 -31
  534. package/tsconfig.json +1 -1
  535. package/tsdown.config.ts +38 -24
  536. package/vitest.config.ts +1 -0
  537. package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
  538. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
  539. package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
  540. package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
  541. package/dist/adapters/drizzle/generate.d.ts +0 -30
  542. package/dist/adapters/drizzle/generate.d.ts.map +0 -1
  543. package/dist/adapters/drizzle/generate.js.map +0 -1
  544. package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
  545. package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
  546. package/dist/adapters/kysely/kysely-adapter.js +0 -17
  547. package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
  548. package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
  549. package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
  550. package/dist/adapters/shared/table-name-mapper.js +0 -43
  551. package/dist/adapters/shared/table-name-mapper.js.map +0 -1
  552. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js +0 -165
  553. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
  554. package/dist/packages/fragno/dist/api/bind-services.js +0 -20
  555. package/dist/packages/fragno/dist/api/bind-services.js.map +0 -1
  556. package/dist/packages/fragno/dist/api/error.js +0 -48
  557. package/dist/packages/fragno/dist/api/error.js.map +0 -1
  558. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +0 -320
  559. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +0 -1
  560. package/dist/packages/fragno/dist/api/fragment-instantiator.js +0 -525
  561. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +0 -1
  562. package/dist/packages/fragno/dist/api/fragno-response.js +0 -73
  563. package/dist/packages/fragno/dist/api/fragno-response.js.map +0 -1
  564. package/dist/packages/fragno/dist/api/internal/response-stream.js +0 -81
  565. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +0 -1
  566. package/dist/packages/fragno/dist/api/internal/route.js +0 -10
  567. package/dist/packages/fragno/dist/api/internal/route.js.map +0 -1
  568. package/dist/packages/fragno/dist/api/mutable-request-state.js +0 -97
  569. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +0 -1
  570. package/dist/packages/fragno/dist/api/request-context-storage.js +0 -43
  571. package/dist/packages/fragno/dist/api/request-context-storage.js.map +0 -1
  572. package/dist/packages/fragno/dist/api/request-input-context.js +0 -118
  573. package/dist/packages/fragno/dist/api/request-input-context.js.map +0 -1
  574. package/dist/packages/fragno/dist/api/request-middleware.js +0 -83
  575. package/dist/packages/fragno/dist/api/request-middleware.js.map +0 -1
  576. package/dist/packages/fragno/dist/api/request-output-context.js +0 -119
  577. package/dist/packages/fragno/dist/api/request-output-context.js.map +0 -1
  578. package/dist/packages/fragno/dist/api/route.js +0 -17
  579. package/dist/packages/fragno/dist/api/route.js.map +0 -1
  580. package/dist/packages/fragno/dist/internal/symbols.js +0 -10
  581. package/dist/packages/fragno/dist/internal/symbols.js.map +0 -1
  582. package/dist/schema-generator/schema-generator.d.ts +0 -15
  583. package/dist/schema-generator/schema-generator.d.ts.map +0 -1
  584. package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
  585. package/src/adapters/kysely/kysely-adapter.ts +0 -27
  586. package/src/adapters/shared/table-name-mapper.ts +0 -50
  587. package/src/schema-generator/schema-generator.ts +0 -12
@@ -0,0 +1,1207 @@
1
+ import { beforeAll, describe, expect, expectTypeOf, it } from "vitest";
2
+
3
+ import SQLite from "better-sqlite3";
4
+ import { SqliteDialect } from "kysely";
5
+
6
+ import { internalSchema } from "../../fragments/internal-fragment";
7
+ import { generateSchemaArtifacts } from "../../migration-engine/generation-engine";
8
+ import { FragnoDatabase } from "../../mod";
9
+ import { Cursor } from "../../query/cursor";
10
+ import {
11
+ createHandlerTxBuilder,
12
+ createServiceTxBuilder,
13
+ } from "../../query/unit-of-work/execute-unit-of-work";
14
+ import { ExponentialBackoffRetryPolicy } from "../../query/unit-of-work/retry-policy";
15
+ import { column, idColumn, referenceColumn, schema, type FragnoId } from "../../schema/create";
16
+ import { BetterSQLite3DriverConfig } from "../generic-sql/driver-config";
17
+ import { SqlAdapter } from "../generic-sql/generic-sql-adapter";
18
+ import { sqliteStorageDefault, sqliteStoragePrisma } from "../generic-sql/sqlite-storage";
19
+
20
+ describe("SqlAdapter SQLite (prisma profile)", () => {
21
+ const testSchema = schema("test", (s) => {
22
+ return s
23
+ .addTable("users", (t) => {
24
+ return t
25
+ .addColumn("id", idColumn())
26
+ .addColumn("name", column("string"))
27
+ .addColumn("age", column("integer").nullable())
28
+ .createIndex("name_idx", ["name"])
29
+ .createIndex("name_id_idx", ["name", "id"]);
30
+ })
31
+ .addTable("emails", (t) => {
32
+ return t
33
+ .addColumn("id", idColumn())
34
+ .addColumn("user_id", referenceColumn())
35
+ .addColumn("email", column("string"))
36
+ .addColumn("is_primary", column("bool").defaultTo(false))
37
+ .createIndex("unique_email", ["email"], { unique: true })
38
+ .createIndex("user_emails", ["user_id"]);
39
+ })
40
+ .addTable("posts", (t) => {
41
+ return t
42
+ .addColumn("id", idColumn())
43
+ .addColumn("user_id", referenceColumn())
44
+ .addColumn("title", column("string"))
45
+ .addColumn("content", column("string"))
46
+ .createIndex("posts_user_idx", ["user_id"]);
47
+ })
48
+ .addTable("comments", (t) => {
49
+ return t
50
+ .addColumn("id", idColumn())
51
+ .addColumn("post_id", referenceColumn())
52
+ .addColumn("user_id", referenceColumn())
53
+ .addColumn("text", column("string"))
54
+ .createIndex("comments_post_idx", ["post_id"])
55
+ .createIndex("comments_user_idx", ["user_id"]);
56
+ })
57
+ .addTable("events", (t) => {
58
+ return t
59
+ .addColumn("id", idColumn())
60
+ .addColumn("name", column("string"))
61
+ .addColumn(
62
+ "created_at",
63
+ column("timestamp").defaultTo((b) => b.now()),
64
+ )
65
+ .addColumn("happened_on", column("date"))
66
+ .addColumn("payload", column("json").nullable())
67
+ .addColumn("big_score", column("bigint"))
68
+ .createIndex("events_name_idx", ["name"]);
69
+ })
70
+ .addReference("user", {
71
+ type: "one",
72
+ from: { table: "emails", column: "user_id" },
73
+ to: { table: "users", column: "id" },
74
+ })
75
+ .addReference("author", {
76
+ type: "one",
77
+ from: { table: "posts", column: "user_id" },
78
+ to: { table: "users", column: "id" },
79
+ })
80
+ .addReference("post", {
81
+ type: "one",
82
+ from: { table: "comments", column: "post_id" },
83
+ to: { table: "posts", column: "id" },
84
+ })
85
+ .addReference("commenter", {
86
+ type: "one",
87
+ from: { table: "comments", column: "user_id" },
88
+ to: { table: "users", column: "id" },
89
+ });
90
+ });
91
+
92
+ const schema2 = schema("schema2", (s) => {
93
+ return s
94
+ .addTable("products", (t) => {
95
+ return t
96
+ .addColumn("id", idColumn())
97
+ .addColumn("name", column("string"))
98
+ .addColumn("price", column("integer"))
99
+ .createIndex("name_idx", ["name"]);
100
+ })
101
+ .addTable("orders", (t) => {
102
+ return t
103
+ .addColumn("id", idColumn())
104
+ .addColumn("product_id", referenceColumn())
105
+ .addColumn("quantity", column("integer"))
106
+ .createIndex("product_orders_idx", ["product_id"]);
107
+ })
108
+ .addReference("product", {
109
+ type: "one",
110
+ from: { table: "orders", column: "product_id" },
111
+ to: { table: "products", column: "id" },
112
+ });
113
+ });
114
+
115
+ let adapter: SqlAdapter;
116
+ let sqliteDatabase: InstanceType<typeof SQLite>;
117
+
118
+ beforeAll(async () => {
119
+ sqliteDatabase = new SQLite(":memory:");
120
+
121
+ const dialect = new SqliteDialect({
122
+ database: sqliteDatabase,
123
+ });
124
+
125
+ adapter = new SqlAdapter({
126
+ dialect,
127
+ driverConfig: new BetterSQLite3DriverConfig(),
128
+ sqliteProfile: "prisma",
129
+ });
130
+
131
+ {
132
+ const migrations = adapter.prepareMigrations(internalSchema, "");
133
+ await migrations.executeWithDriver(adapter.driver, 0);
134
+ }
135
+
136
+ {
137
+ const migrations = adapter.prepareMigrations(testSchema, "namespace");
138
+ await migrations.executeWithDriver(adapter.driver, 0);
139
+ }
140
+
141
+ {
142
+ const migrations = adapter.prepareMigrations(schema2, "namespace2");
143
+ await migrations.executeWithDriver(adapter.driver, 0);
144
+ }
145
+
146
+ return async () => {
147
+ await adapter.close();
148
+ sqliteDatabase.close();
149
+ };
150
+ }, 12000);
151
+
152
+ it("should use prisma sqlite storage mode", () => {
153
+ expect(adapter.sqliteStorageMode).toBe(sqliteStoragePrisma);
154
+ });
155
+
156
+ it("should default schema output path to fragno.prisma", async () => {
157
+ const fragnoDb = new FragnoDatabase({
158
+ namespace: "namespace",
159
+ schema: testSchema,
160
+ adapter,
161
+ });
162
+
163
+ const [result] = await generateSchemaArtifacts([fragnoDb], { format: "prisma" });
164
+
165
+ expect(result.path).toBe("fragno.prisma");
166
+ });
167
+
168
+ it("should store prisma storage values using sqlite-friendly types", async () => {
169
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
170
+ const happenedOn = new Date("2024-06-18T12:34:56.789Z");
171
+ const bigScore = 12345n;
172
+
173
+ const createUow = queryEngine.createUnitOfWork("create-prisma-storage-event");
174
+ createUow.create("events", {
175
+ name: "Prisma Storage Event",
176
+ happened_on: happenedOn,
177
+ payload: { level: "info", tags: ["sqlite", "prisma"] },
178
+ big_score: bigScore,
179
+ });
180
+ await createUow.executeMutations();
181
+
182
+ const tableName = adapter.namingStrategy.tableName("events", "namespace");
183
+ const row = sqliteDatabase
184
+ .prepare(`SELECT happened_on, big_score FROM ${tableName} WHERE name = ?`)
185
+ .get("Prisma Storage Event") as { happened_on?: unknown; big_score?: unknown } | undefined;
186
+
187
+ expect(typeof row?.happened_on).toBe("string");
188
+ expect(row?.happened_on).toBe(happenedOn.toISOString());
189
+ expect(row?.big_score).not.toBeInstanceOf(Buffer);
190
+ expect(["number", "bigint"]).toContain(typeof row?.big_score);
191
+ });
192
+
193
+ it("should honor explicit sqlite storage overrides", async () => {
194
+ const fragnoDatabase = new SQLite(":memory:");
195
+ const fragnoDialect = new SqliteDialect({ database: fragnoDatabase });
196
+ const fragnoAdapter = new SqlAdapter({
197
+ dialect: fragnoDialect,
198
+ driverConfig: new BetterSQLite3DriverConfig(),
199
+ sqliteStorageMode: sqliteStorageDefault,
200
+ });
201
+
202
+ try {
203
+ const internalMigrations = fragnoAdapter.prepareMigrations(internalSchema, "");
204
+ await internalMigrations.executeWithDriver(fragnoAdapter.driver, 0);
205
+
206
+ const migrations = fragnoAdapter.prepareMigrations(testSchema, "namespace");
207
+ await migrations.executeWithDriver(fragnoAdapter.driver, 0);
208
+
209
+ expect(fragnoAdapter.sqliteStorageMode).toBe(sqliteStorageDefault);
210
+
211
+ const queryEngine = fragnoAdapter.createQueryEngine(testSchema, "namespace");
212
+ const happenedOn = new Date("2024-06-18T12:34:56.789Z");
213
+ const bigScore = 1234567890123n;
214
+
215
+ const createUow = queryEngine.createUnitOfWork("create-fragno-storage-event");
216
+ createUow.create("events", {
217
+ name: "Fragno Storage Event",
218
+ happened_on: happenedOn,
219
+ payload: { level: "info", tags: ["sqlite", "fragno"] },
220
+ big_score: bigScore,
221
+ });
222
+ await createUow.executeMutations();
223
+
224
+ const tableName = fragnoAdapter.namingStrategy.tableName("events", "namespace");
225
+ const row = fragnoDatabase
226
+ .prepare(`SELECT happened_on, big_score FROM ${tableName} WHERE name = ?`)
227
+ .get("Fragno Storage Event") as { happened_on?: number; big_score?: Buffer } | undefined;
228
+
229
+ expect(typeof row?.happened_on).toBe("number");
230
+ expect(row?.happened_on).toBe(happenedOn.getTime());
231
+ expect(row?.big_score).toBeInstanceOf(Buffer);
232
+ expect(row?.big_score?.readBigInt64BE(0)).toBe(bigScore);
233
+ } finally {
234
+ await fragnoAdapter.close();
235
+ fragnoDatabase.close();
236
+ }
237
+ });
238
+
239
+ it("should execute Unit of Work with version checking", async () => {
240
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
241
+
242
+ const createUow = queryEngine.createUnitOfWork("create-users");
243
+ createUow.create("users", {
244
+ name: "Prisma SQLite Alice",
245
+ age: 25,
246
+ });
247
+ createUow.create("users", {
248
+ name: "Prisma SQLite Bob",
249
+ age: 30,
250
+ });
251
+
252
+ expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
253
+ Parameters<typeof createUow.find>[0]
254
+ >();
255
+ expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
256
+ "users" | "emails" | "posts" | "comments" | "events"
257
+ >();
258
+
259
+ const { success: createSuccess } = await createUow.executeMutations();
260
+ expect(createSuccess).toBe(true);
261
+
262
+ const createdIds = createUow.getCreatedIds();
263
+ expect(createdIds).toHaveLength(2);
264
+
265
+ const [createdUsers] = await queryEngine
266
+ .createUnitOfWork("get-created-users")
267
+ .find("users", (b) =>
268
+ b.whereIndex("name_idx", (eb) =>
269
+ eb("name", "in", ["Prisma SQLite Alice", "Prisma SQLite Bob"]),
270
+ ),
271
+ )
272
+ .executeRetrieve();
273
+
274
+ expect(createdUsers).toHaveLength(2);
275
+
276
+ const initialUserId = createdIds[0];
277
+
278
+ const uow = queryEngine
279
+ .createUnitOfWork("update-user-age")
280
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)));
281
+
282
+ const [users] = await uow.executeRetrieve();
283
+
284
+ uow.update("users", initialUserId, (b) => b.set({ age: 26 }).check());
285
+
286
+ const { success } = await uow.executeMutations();
287
+ expect(success).toBe(true);
288
+ expect(users).toHaveLength(1);
289
+ expect(users[0].name).toBe("Prisma SQLite Alice");
290
+
291
+ const [[updatedUser]] = await queryEngine
292
+ .createUnitOfWork("get-updated-user")
293
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
294
+ .executeRetrieve();
295
+
296
+ expect(updatedUser).toMatchObject({
297
+ id: expect.objectContaining({
298
+ externalId: initialUserId.externalId,
299
+ version: 1,
300
+ }),
301
+ age: 26,
302
+ });
303
+
304
+ const uow2 = queryEngine.createUnitOfWork("update-user-stale");
305
+ uow2.update("users", initialUserId, (b) => b.set({ age: 27 }).check());
306
+
307
+ const { success: success2 } = await uow2.executeMutations();
308
+ expect(success2).toBe(false);
309
+
310
+ const [[unchangedUser]] = await queryEngine
311
+ .createUnitOfWork("verify-unchanged")
312
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
313
+ .executeRetrieve();
314
+
315
+ expect(unchangedUser).toMatchObject({
316
+ id: expect.objectContaining({
317
+ version: 1,
318
+ }),
319
+ age: 26,
320
+ });
321
+ });
322
+
323
+ it("should support count operations", async () => {
324
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
325
+
326
+ const createUow = queryEngine.createUnitOfWork("create-count-users");
327
+ createUow.create("users", { name: "Prisma SQLite Count 1", age: 20 });
328
+ createUow.create("users", { name: "Prisma SQLite Count 2", age: 30 });
329
+ createUow.create("users", { name: "Prisma SQLite Count 3", age: 40 });
330
+ await createUow.executeMutations();
331
+
332
+ const [totalCount] = await queryEngine
333
+ .createUnitOfWork("count-all")
334
+ .find("users", (b) => b.whereIndex("primary").selectCount())
335
+ .executeRetrieve();
336
+
337
+ expect(totalCount).toBeGreaterThanOrEqual(3);
338
+ expect(typeof totalCount).toBe("number");
339
+ });
340
+
341
+ it("should support cursor-based pagination", async () => {
342
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
343
+ const prefix = "Prisma SQLite Cursor";
344
+
345
+ const createUow = queryEngine.createUnitOfWork("create-cursor-users");
346
+ createUow.create("users", { name: `${prefix} A`, age: 20 });
347
+ createUow.create("users", { name: `${prefix} B`, age: 30 });
348
+ createUow.create("users", { name: `${prefix} C`, age: 40 });
349
+ createUow.create("users", { name: `${prefix} D`, age: 50 });
350
+ createUow.create("users", { name: `${prefix} E`, age: 60 });
351
+
352
+ await createUow.executeMutations();
353
+
354
+ const [firstPage] = await queryEngine
355
+ .createUnitOfWork("first-page")
356
+ .find("users", (b) =>
357
+ b
358
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
359
+ .orderByIndex("name_idx", "asc")
360
+ .pageSize(2),
361
+ )
362
+ .executeRetrieve();
363
+
364
+ expect(firstPage).toHaveLength(2);
365
+ expect(firstPage.map((u) => u.name)).toEqual([`${prefix} A`, `${prefix} B`]);
366
+
367
+ const lastItem = firstPage[firstPage.length - 1]!;
368
+ const cursor = new Cursor({
369
+ indexName: "name_idx",
370
+ orderDirection: "asc",
371
+ pageSize: 2,
372
+ indexValues: { name: lastItem.name },
373
+ }).encode();
374
+
375
+ const [secondPage] = await queryEngine
376
+ .createUnitOfWork("second-page")
377
+ .find("users", (b) =>
378
+ b
379
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
380
+ .orderByIndex("name_idx", "asc")
381
+ .after(cursor)
382
+ .pageSize(2),
383
+ )
384
+ .executeRetrieve();
385
+
386
+ expect(secondPage).toHaveLength(2);
387
+ expect(secondPage.map((u) => u.name)).toEqual([`${prefix} C`, `${prefix} D`]);
388
+ });
389
+
390
+ it("should support composite cursor pagination", async () => {
391
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
392
+ const nameValue = "Prisma SQLite Composite Cursor";
393
+
394
+ const createUow = queryEngine.createUnitOfWork("create-composite-cursor-users");
395
+ for (let i = 0; i < 5; i += 1) {
396
+ createUow.create("users", { name: nameValue, age: 30 + i });
397
+ }
398
+ await createUow.executeMutations();
399
+
400
+ const all = await queryEngine.find("users", (b) =>
401
+ b
402
+ .whereIndex("name_id_idx", (eb) => eb("name", "=", nameValue))
403
+ .orderByIndex("name_id_idx", "asc"),
404
+ );
405
+
406
+ const firstPage = await queryEngine.findWithCursor("users", (b) =>
407
+ b
408
+ .whereIndex("name_id_idx", (eb) => eb("name", "=", nameValue))
409
+ .orderByIndex("name_id_idx", "asc")
410
+ .pageSize(2),
411
+ );
412
+
413
+ const secondPage = await queryEngine.findWithCursor("users", (b) =>
414
+ b
415
+ .whereIndex("name_id_idx", (eb) => eb("name", "=", nameValue))
416
+ .after(firstPage.cursor!)
417
+ .orderByIndex("name_id_idx", "asc")
418
+ .pageSize(2),
419
+ );
420
+
421
+ const ids = (rows: { id: FragnoId }[]) => rows.map((row) => row.id.externalId);
422
+
423
+ expect(all.length).toBeGreaterThanOrEqual(5);
424
+ expect(firstPage.items).toHaveLength(2);
425
+ expect(firstPage.cursor).toBeInstanceOf(Cursor);
426
+ expect(firstPage.hasNextPage).toBe(true);
427
+ expect(ids(firstPage.items)).toEqual(ids(all).slice(0, 2));
428
+ expect(ids([...firstPage.items, ...secondPage.items])).toEqual(ids(all).slice(0, 4));
429
+ });
430
+
431
+ it("should verify hasNextPage in cursor pagination", async () => {
432
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
433
+ const prefix = "Prisma SQLite HasNextPage";
434
+
435
+ for (let i = 1; i <= 15; i++) {
436
+ await queryEngine.create("users", {
437
+ name: `${prefix} ${i.toString().padStart(2, "0")}`,
438
+ age: 20 + i,
439
+ });
440
+ }
441
+
442
+ const firstPage = await queryEngine.findWithCursor("users", (b) =>
443
+ b
444
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
445
+ .orderByIndex("name_idx", "asc")
446
+ .pageSize(10),
447
+ );
448
+
449
+ expect(firstPage.items).toHaveLength(10);
450
+ expect(firstPage.hasNextPage).toBe(true);
451
+ expect(firstPage.cursor).toBeInstanceOf(Cursor);
452
+
453
+ const secondPage = await queryEngine.findWithCursor("users", (b) =>
454
+ b
455
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
456
+ .after(firstPage.cursor!)
457
+ .orderByIndex("name_idx", "asc")
458
+ .pageSize(10),
459
+ );
460
+
461
+ expect(secondPage.items).toHaveLength(5);
462
+ expect(secondPage.hasNextPage).toBe(false);
463
+ expect(secondPage.cursor).toBeUndefined();
464
+
465
+ const emptyPage = await queryEngine.findWithCursor("users", (b) =>
466
+ b
467
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", "NoMatchPrefix"))
468
+ .orderByIndex("name_idx", "asc")
469
+ .pageSize(10),
470
+ );
471
+
472
+ expect(emptyPage.items).toHaveLength(0);
473
+ expect(emptyPage.hasNextPage).toBe(false);
474
+ expect(emptyPage.cursor).toBeUndefined();
475
+ });
476
+
477
+ it("should support findWithCursor() in Unit of Work", async () => {
478
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
479
+ const prefix = "Prisma SQLite UOW Cursor";
480
+
481
+ for (let i = 1; i <= 6; i++) {
482
+ await queryEngine.create("users", {
483
+ name: `${prefix} ${i}`,
484
+ age: 40 + i,
485
+ });
486
+ }
487
+
488
+ const uow = queryEngine.createUnitOfWork("cursor-test").findWithCursor("users", (b) =>
489
+ b
490
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
491
+ .orderByIndex("name_idx", "asc")
492
+ .pageSize(5),
493
+ );
494
+
495
+ const [result] = await uow.executeRetrieve();
496
+
497
+ expect(result.items).toHaveLength(5);
498
+ expect(typeof result.hasNextPage).toBe("boolean");
499
+ expect(result.cursor).toBeInstanceOf(Cursor);
500
+ });
501
+
502
+ it("should support forSchema for multi-schema queries", async () => {
503
+ const queryEngine1 = adapter.createQueryEngine(testSchema, "namespace");
504
+ const queryEngine2 = adapter.createQueryEngine(schema2, "namespace2");
505
+
506
+ const createUsersUow = queryEngine1.createUnitOfWork("create-users-for-multi-schema");
507
+ createUsersUow.create("users", { name: "Prisma Multi Schema User 1", age: 25 });
508
+ createUsersUow.create("users", { name: "Prisma Multi Schema User 2", age: 30 });
509
+ const { success: usersSuccess } = await createUsersUow.executeMutations();
510
+ expect(usersSuccess).toBe(true);
511
+
512
+ const createProductsUow = queryEngine2.createUnitOfWork("create-products-for-multi-schema");
513
+ createProductsUow.create("products", { name: "Prisma Product A", price: 100 });
514
+ createProductsUow.create("products", { name: "Prisma Product B", price: 200 });
515
+ const { success: productsSuccess } = await createProductsUow.executeMutations();
516
+ expect(productsSuccess).toBe(true);
517
+
518
+ const uow = queryEngine1.createUnitOfWork("multi-schema-query");
519
+
520
+ const view1 = uow
521
+ .forSchema(testSchema)
522
+ .find("users", (b) =>
523
+ b
524
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", "Prisma Multi Schema User"))
525
+ .select(["id", "name"]),
526
+ )
527
+ .find("users", (b) =>
528
+ b
529
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", "Prisma Multi Schema User"))
530
+ .select(["name", "age"]),
531
+ );
532
+
533
+ const view2 = uow
534
+ .forSchema(schema2)
535
+ .find("products", (b) => b.whereIndex("primary").select(["name", "price"]));
536
+
537
+ await uow.executeRetrieve();
538
+
539
+ const [users1, users2] = await view1.retrievalPhase;
540
+ const [user1] = users1;
541
+ expectTypeOf(user1).toMatchObjectType<{ id: FragnoId; name: string }>();
542
+
543
+ const [user2] = users2;
544
+ expectTypeOf(user2).toMatchObjectType<{ name: string; age: number | null }>();
545
+
546
+ const [products] = await view2.retrievalPhase;
547
+ const [product1] = products;
548
+ expectTypeOf(product1).toMatchObjectType<{ name: string; price: number }>();
549
+
550
+ expect(users1).toHaveLength(2);
551
+ expect(users1[0]).toMatchObject({
552
+ id: expect.any(Object),
553
+ name: "Prisma Multi Schema User 1",
554
+ });
555
+ expect(users1[1]).toMatchObject({
556
+ id: expect.any(Object),
557
+ name: "Prisma Multi Schema User 2",
558
+ });
559
+
560
+ expect(users2).toHaveLength(2);
561
+ expect(users2[0]).toMatchObject({
562
+ name: "Prisma Multi Schema User 1",
563
+ age: 25,
564
+ });
565
+ expect(users2[1]).toMatchObject({
566
+ name: "Prisma Multi Schema User 2",
567
+ age: 30,
568
+ });
569
+
570
+ expect(products).toHaveLength(2);
571
+ expect(products[0]).toMatchObject({
572
+ name: "Prisma Product A",
573
+ price: 100,
574
+ });
575
+ expect(products[1]).toMatchObject({
576
+ name: "Prisma Product B",
577
+ price: 200,
578
+ });
579
+ });
580
+
581
+ it("should support joins", async () => {
582
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
583
+
584
+ const createUserUow = queryEngine.createUnitOfWork("create-join-user");
585
+ createUserUow.create("users", { name: "Prisma SQLite Email User", age: 20 });
586
+
587
+ const { success } = await createUserUow.executeMutations();
588
+ expect(success).toBe(true);
589
+
590
+ const [usersResult] = await queryEngine
591
+ .createUnitOfWork("get-created-user")
592
+ .find("users", (b) =>
593
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Email User")),
594
+ )
595
+ .executeRetrieve();
596
+
597
+ expect(usersResult).toHaveLength(1);
598
+ const createdUser = usersResult[0];
599
+ expect(createdUser.name).toBe("Prisma SQLite Email User");
600
+
601
+ const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
602
+ createEmailUow.create("emails", {
603
+ user_id: createdUser.id,
604
+ email: "prisma-sqlite@example.com",
605
+ is_primary: true,
606
+ });
607
+ await createEmailUow.executeMutations();
608
+
609
+ const uow = queryEngine
610
+ .createUnitOfWork("test-joins")
611
+ .find("emails", (b) =>
612
+ b
613
+ .whereIndex("user_emails", (eb) => eb("user_id", "=", createdUser.id))
614
+ .join((jb) => jb.user((builder) => builder.select(["name", "id", "age"]))),
615
+ );
616
+
617
+ const [[email]] = await uow.executeRetrieve();
618
+
619
+ expect(email).toMatchObject({
620
+ id: expect.objectContaining({
621
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
622
+ internalId: expect.any(BigInt),
623
+ }),
624
+ user_id: expect.objectContaining({
625
+ internalId: expect.any(BigInt),
626
+ }),
627
+ email: "prisma-sqlite@example.com",
628
+ is_primary: true,
629
+ user: {
630
+ id: expect.objectContaining({
631
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
632
+ internalId: expect.any(BigInt),
633
+ }),
634
+ name: "Prisma SQLite Email User",
635
+ age: 20,
636
+ },
637
+ });
638
+ });
639
+
640
+ it("should support inserting with external id string", async () => {
641
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
642
+
643
+ const createUserUow = queryEngine.createUnitOfWork("create-user-for-external-id");
644
+ createUserUow.create("users", { name: "Prisma SQLite External ID User", age: 35 });
645
+ await createUserUow.executeMutations();
646
+
647
+ const [[user]] = await queryEngine
648
+ .createUnitOfWork("get-user-for-external-id")
649
+ .find("users", (b) =>
650
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite External ID User")),
651
+ )
652
+ .executeRetrieve();
653
+
654
+ const createEmailUow = queryEngine.createUnitOfWork("create-email-with-external-id");
655
+ createEmailUow.create("emails", {
656
+ user_id: user.id.externalId,
657
+ email: "prisma-sqlite-external-id@example.com",
658
+ is_primary: false,
659
+ });
660
+
661
+ const { success } = await createEmailUow.executeMutations();
662
+ expect(success).toBe(true);
663
+
664
+ const [[email]] = await queryEngine
665
+ .createUnitOfWork("get-email-by-external-id")
666
+ .find("emails", (b) =>
667
+ b
668
+ .whereIndex("unique_email", (eb) =>
669
+ eb("email", "=", "prisma-sqlite-external-id@example.com"),
670
+ )
671
+ .join((jb) => jb.user((builder) => builder.select(["name", "id"]))),
672
+ )
673
+ .executeRetrieve();
674
+
675
+ expect(email).toMatchObject({
676
+ email: "prisma-sqlite-external-id@example.com",
677
+ is_primary: false,
678
+ user_id: expect.objectContaining({
679
+ internalId: user.id.internalId,
680
+ }),
681
+ user: {
682
+ id: expect.objectContaining({
683
+ externalId: user.id.externalId,
684
+ }),
685
+ name: "Prisma SQLite External ID User",
686
+ },
687
+ });
688
+ });
689
+
690
+ it("should create user and post in same transaction using returned ID", async () => {
691
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
692
+
693
+ const uow = queryEngine.createUnitOfWork("create-user-and-post");
694
+ const userId = uow.create("users", {
695
+ name: "Prisma SQLite UOW User",
696
+ age: 35,
697
+ });
698
+
699
+ const postId = uow.create("posts", {
700
+ user_id: userId,
701
+ title: "Prisma SQLite UOW Post",
702
+ content: "This post was created in the same transaction as the user",
703
+ });
704
+
705
+ const { success } = await uow.executeMutations();
706
+ expect(success).toBe(true);
707
+
708
+ const userIdStr = userId.toString();
709
+ const postIdStr = postId.toString();
710
+
711
+ const [[user]] = await queryEngine
712
+ .createUnitOfWork("verify-user")
713
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userIdStr)))
714
+ .executeRetrieve();
715
+
716
+ const [[post]] = await queryEngine
717
+ .createUnitOfWork("verify-post")
718
+ .find("posts", (b) => b.whereIndex("primary", (eb) => eb("id", "=", postIdStr)))
719
+ .executeRetrieve();
720
+
721
+ expect(user.name).toBe("Prisma SQLite UOW User");
722
+ expect(user.age).toBe(35);
723
+ expect(post.title).toBe("Prisma SQLite UOW Post");
724
+ expect(post.content).toBe("This post was created in the same transaction as the user");
725
+ expect(post.user_id.internalId).toBe(user.id.internalId);
726
+ });
727
+
728
+ it("should support complex nested joins (comments -> post -> author)", async () => {
729
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
730
+
731
+ const createAuthorUow = queryEngine.createUnitOfWork("create-author");
732
+ createAuthorUow.create("users", { name: "Prisma SQLite Author", age: 30 });
733
+ await createAuthorUow.executeMutations();
734
+
735
+ const [[author]] = await queryEngine
736
+ .createUnitOfWork("get-author")
737
+ .find("users", (b) =>
738
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Author")),
739
+ )
740
+ .executeRetrieve();
741
+
742
+ const createPostUow = queryEngine.createUnitOfWork("create-post");
743
+ createPostUow.create("posts", {
744
+ user_id: author.id,
745
+ title: "Prisma SQLite Post",
746
+ content: "Nested join content",
747
+ });
748
+ await createPostUow.executeMutations();
749
+
750
+ const [[post]] = await queryEngine
751
+ .createUnitOfWork("get-post")
752
+ .find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", author.id)))
753
+ .executeRetrieve();
754
+
755
+ const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
756
+ createCommenterUow.create("users", { name: "Prisma SQLite Commenter", age: 25 });
757
+ await createCommenterUow.executeMutations();
758
+
759
+ const [[commenter]] = await queryEngine
760
+ .createUnitOfWork("get-commenter")
761
+ .find("users", (b) =>
762
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma SQLite Commenter")),
763
+ )
764
+ .executeRetrieve();
765
+
766
+ const createCommentUow = queryEngine.createUnitOfWork("create-comment");
767
+ createCommentUow.create("comments", {
768
+ post_id: post.id,
769
+ user_id: commenter.id,
770
+ text: "Great Prisma post!",
771
+ });
772
+ await createCommentUow.executeMutations();
773
+
774
+ const uow = queryEngine.createUnitOfWork("test-complex-joins").find("comments", (b) =>
775
+ b.whereIndex("primary").join((jb) =>
776
+ jb
777
+ .post((postBuilder) =>
778
+ postBuilder
779
+ .select(["id", "title", "content"])
780
+ .orderByIndex("primary", "desc")
781
+ .pageSize(1)
782
+ .join((jb2) =>
783
+ jb2.author((authorBuilder) =>
784
+ authorBuilder.select(["id", "name", "age"]).orderByIndex("name_idx", "asc"),
785
+ ),
786
+ ),
787
+ )
788
+ .commenter((commenterBuilder) => commenterBuilder.select(["id", "name"])),
789
+ ),
790
+ );
791
+
792
+ const [[comment]] = await uow.executeRetrieve();
793
+
794
+ expect(comment).toMatchObject({
795
+ id: expect.objectContaining({
796
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
797
+ internalId: expect.any(BigInt),
798
+ }),
799
+ text: "Great Prisma post!",
800
+ post: {
801
+ id: expect.objectContaining({
802
+ externalId: post.id.externalId,
803
+ }),
804
+ title: "Prisma SQLite Post",
805
+ content: "Nested join content",
806
+ author: {
807
+ id: expect.objectContaining({
808
+ externalId: author.id.externalId,
809
+ }),
810
+ name: "Prisma SQLite Author",
811
+ age: 30,
812
+ },
813
+ },
814
+ commenter: {
815
+ id: expect.objectContaining({
816
+ externalId: commenter.id.externalId,
817
+ }),
818
+ name: "Prisma SQLite Commenter",
819
+ },
820
+ });
821
+ });
822
+
823
+ it("should return created IDs from UOW create operations", async () => {
824
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
825
+
826
+ const uow1 = queryEngine.createUnitOfWork("create-multiple-users");
827
+ uow1.create("users", { name: "Prisma ID User 1", age: 30 });
828
+ uow1.create("users", { name: "Prisma ID User 2", age: 35 });
829
+ uow1.create("users", { name: "Prisma ID User 3", age: 40 });
830
+
831
+ const { success: success1 } = await uow1.executeMutations();
832
+ expect(success1).toBe(true);
833
+
834
+ const createdIds1 = uow1.getCreatedIds();
835
+ expect(createdIds1).toMatchObject([
836
+ expect.objectContaining({
837
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
838
+ internalId: expect.any(BigInt),
839
+ }),
840
+ expect.objectContaining({
841
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
842
+ internalId: expect.any(BigInt),
843
+ }),
844
+ expect.objectContaining({
845
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
846
+ internalId: expect.any(BigInt),
847
+ }),
848
+ ]);
849
+
850
+ const externalIds = createdIds1.map((id) => id.externalId);
851
+ expect(new Set(externalIds).size).toBe(3);
852
+
853
+ const user1 = await queryEngine.findFirst("users", (b) =>
854
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[0].externalId)),
855
+ );
856
+ const user2 = await queryEngine.findFirst("users", (b) =>
857
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[1].externalId)),
858
+ );
859
+ const user3 = await queryEngine.findFirst("users", (b) =>
860
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[2].externalId)),
861
+ );
862
+
863
+ expect(user1).toMatchObject({
864
+ id: expect.objectContaining({
865
+ externalId: createdIds1[0].externalId,
866
+ }),
867
+ name: "Prisma ID User 1",
868
+ age: 30,
869
+ });
870
+
871
+ expect(user2).toMatchObject({
872
+ id: expect.objectContaining({
873
+ externalId: createdIds1[1].externalId,
874
+ }),
875
+ name: "Prisma ID User 2",
876
+ age: 35,
877
+ });
878
+
879
+ expect(user3).toMatchObject({
880
+ id: expect.objectContaining({
881
+ externalId: createdIds1[2].externalId,
882
+ }),
883
+ name: "Prisma ID User 3",
884
+ age: 40,
885
+ });
886
+
887
+ const uow2 = queryEngine.createUnitOfWork("mixed-operations");
888
+ uow2.create("users", { name: "Prisma New User", age: 50 });
889
+ uow2.update("users", createdIds1[0], (b) => b.set({ age: 31 }));
890
+ uow2.create("users", { name: "Prisma Another New User", age: 55 });
891
+ uow2.delete("users", createdIds1[2]);
892
+
893
+ const { success: success2 } = await uow2.executeMutations();
894
+ expect(success2).toBe(true);
895
+
896
+ const createdIds2 = uow2.getCreatedIds();
897
+ expect(createdIds2).toHaveLength(2);
898
+ expect(createdIds2[0].externalId).toBeDefined();
899
+ expect(createdIds2[1].externalId).toBeDefined();
900
+
901
+ const customId = "prisma-custom-user-id-12345";
902
+ const uow3 = queryEngine.createUnitOfWork("create-with-custom-id");
903
+ uow3.create("users", { id: customId, name: "Prisma Custom ID User", age: 60 });
904
+
905
+ const { success: success3 } = await uow3.executeMutations();
906
+ expect(success3).toBe(true);
907
+
908
+ const createdIds3 = uow3.getCreatedIds();
909
+ expect(createdIds3).toHaveLength(1);
910
+ expect(createdIds3[0].externalId).toBe(customId);
911
+ expect(createdIds3[0].internalId).toBeDefined();
912
+
913
+ const customIdUser = await queryEngine.findFirst("users", (b) =>
914
+ b.whereIndex("primary", (eb) => eb("id", "=", customId)),
915
+ );
916
+
917
+ expect(customIdUser).toMatchObject({
918
+ id: expect.objectContaining({
919
+ externalId: customId,
920
+ }),
921
+ name: "Prisma Custom ID User",
922
+ age: 60,
923
+ });
924
+ });
925
+
926
+ it("should handle timestamps and timezones correctly", async () => {
927
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
928
+
929
+ const createUow = queryEngine.createUnitOfWork("create-event-for-timestamp");
930
+ createUow.create("events", {
931
+ name: "Timestamp Event SQLite",
932
+ happened_on: new Date("2024-06-18T00:00:00.000Z"),
933
+ payload: { level: "info", tags: ["timestamp", "sqlite"] },
934
+ big_score: 42n,
935
+ });
936
+ await createUow.executeMutations();
937
+
938
+ const [[event]] = await queryEngine
939
+ .createUnitOfWork("get-event-for-timestamp")
940
+ .find("events", (b) =>
941
+ b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Timestamp Event SQLite")),
942
+ )
943
+ .executeRetrieve();
944
+
945
+ expect(event.created_at).toBeInstanceOf(Date);
946
+
947
+ const now = Date.now();
948
+ const createdTime = event.created_at.getTime();
949
+ expect(createdTime).toBeGreaterThan(now - 24 * 60 * 60 * 1000);
950
+ expect(createdTime).toBeLessThan(now + 24 * 60 * 60 * 1000);
951
+
952
+ expect(typeof event.created_at.toISOString).toBe("function");
953
+ expect(typeof event.created_at.getTime).toBe("function");
954
+ expect(typeof event.created_at.getTimezoneOffset).toBe("function");
955
+
956
+ const isoString = event.created_at.toISOString();
957
+ expect(new Date(isoString).getTime()).toBe(event.created_at.getTime());
958
+ });
959
+
960
+ it("should store Date values as UTC ISO strings for sqlite prisma storage", async () => {
961
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
962
+ const happenedOn = new Date("2024-06-18T12:34:56.789Z");
963
+
964
+ const createUow = queryEngine.createUnitOfWork("create-iso-event");
965
+ createUow.create("events", {
966
+ name: "ISO Stored Event",
967
+ happened_on: happenedOn,
968
+ payload: { level: "info", tags: ["sqlite", "iso"] },
969
+ big_score: 7n,
970
+ });
971
+ await createUow.executeMutations();
972
+
973
+ const tableName = adapter.namingStrategy.tableName("events", "namespace");
974
+ const row = sqliteDatabase
975
+ .prepare(`SELECT happened_on FROM ${tableName} WHERE name = ?`)
976
+ .get("ISO Stored Event") as { happened_on?: string } | undefined;
977
+
978
+ expect(row?.happened_on).toBe(happenedOn.toISOString());
979
+ });
980
+
981
+ it("should support handlerTx with retry logic", async () => {
982
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
983
+
984
+ const createUow = queryEngine.createUnitOfWork("create-user-for-execute-uow");
985
+ createUow.create("users", { name: "Prisma Execute UOW User", age: 42 });
986
+ await createUow.executeMutations();
987
+
988
+ const [[user]] = await queryEngine
989
+ .createUnitOfWork("get-user-for-execute-uow")
990
+ .find("users", (b) =>
991
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma Execute UOW User")),
992
+ )
993
+ .executeRetrieve();
994
+
995
+ let currentUow: ReturnType<typeof queryEngine.createUnitOfWork> | null = null;
996
+
997
+ const getUserById = (userId: typeof user.id) => {
998
+ return createServiceTxBuilder(testSchema, currentUow!)
999
+ .retrieve((uow) =>
1000
+ uow.find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userId))),
1001
+ )
1002
+ .transformRetrieve(([users]) => users[0] ?? null)
1003
+ .build();
1004
+ };
1005
+
1006
+ const result = await createHandlerTxBuilder({
1007
+ createUnitOfWork: () => {
1008
+ currentUow = queryEngine.createUnitOfWork("execute-uow-update");
1009
+ return currentUow;
1010
+ },
1011
+ retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3, initialDelayMs: 1 }),
1012
+ })
1013
+ .withServiceCalls(() => [getUserById(user.id)])
1014
+ .mutate(({ forSchema, serviceIntermediateResult: [foundUser] }) => {
1015
+ if (!foundUser) {
1016
+ throw new Error("User not found");
1017
+ }
1018
+ const newAge = foundUser.age! + 1;
1019
+ forSchema(testSchema).update("users", foundUser.id, (b) => b.set({ age: newAge }).check());
1020
+ return { previousAge: foundUser.age, newAge };
1021
+ })
1022
+ .transform(({ mutateResult }) => {
1023
+ expect(mutateResult.newAge).toBe(mutateResult.previousAge! + 1);
1024
+ return mutateResult;
1025
+ })
1026
+ .execute();
1027
+
1028
+ expect(result).toEqual({
1029
+ previousAge: 42,
1030
+ newAge: 43,
1031
+ });
1032
+
1033
+ const updatedUser = await queryEngine.findFirst("users", (b) =>
1034
+ b.whereIndex("primary", (eb) => eb("id", "=", user.id)),
1035
+ );
1036
+
1037
+ expect(updatedUser).toMatchObject({
1038
+ id: expect.objectContaining({
1039
+ externalId: user.id.externalId,
1040
+ version: 1,
1041
+ }),
1042
+ name: "Prisma Execute UOW User",
1043
+ age: 43,
1044
+ });
1045
+ });
1046
+
1047
+ it("should fail check() when version changes", async () => {
1048
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
1049
+
1050
+ const createUserUow = queryEngine.createUnitOfWork("create-user-for-version-conflict");
1051
+ createUserUow.create("users", {
1052
+ name: "Prisma Version Conflict User",
1053
+ age: 40,
1054
+ });
1055
+ await createUserUow.executeMutations();
1056
+
1057
+ const [[user]] = await queryEngine
1058
+ .createUnitOfWork("get-user-for-version-conflict")
1059
+ .find("users", (b) =>
1060
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "Prisma Version Conflict User")),
1061
+ )
1062
+ .executeRetrieve();
1063
+
1064
+ const updateUow = queryEngine.createUnitOfWork("update-user-version");
1065
+ updateUow.update("users", user.id, (b) => b.set({ age: 41 }));
1066
+ await updateUow.executeMutations();
1067
+
1068
+ const uow = queryEngine.createUnitOfWork("check-stale-version");
1069
+ uow.check("users", user.id);
1070
+ uow.create("posts", {
1071
+ user_id: user.id,
1072
+ title: "Prisma Should Not Be Created",
1073
+ content: "Content",
1074
+ });
1075
+
1076
+ const { success } = await uow.executeMutations();
1077
+ expect(success).toBe(false);
1078
+
1079
+ const [posts] = await queryEngine
1080
+ .createUnitOfWork("get-posts-for-version-conflict")
1081
+ .find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
1082
+ .executeRetrieve();
1083
+
1084
+ const conflictPosts = posts.filter((p) => p.title === "Prisma Should Not Be Created");
1085
+ expect(conflictPosts).toHaveLength(0);
1086
+ });
1087
+
1088
+ it("should roundtrip Prisma SQLite DateTime, Date, JSON, and BigInt", async () => {
1089
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
1090
+ const beforeCreate = Date.now();
1091
+ const happenedOn = new Date("2024-06-15T00:00:00.000Z");
1092
+ const payload = { level: "info", tags: ["launch", "sqlite"] };
1093
+ const bigScore = 1234567890123n;
1094
+
1095
+ const createUow = queryEngine.createUnitOfWork("create-event");
1096
+ createUow.create("events", {
1097
+ name: "Launch",
1098
+ happened_on: happenedOn,
1099
+ payload,
1100
+ big_score: bigScore,
1101
+ });
1102
+ await createUow.executeMutations();
1103
+
1104
+ const [[event]] = await queryEngine
1105
+ .createUnitOfWork("get-event")
1106
+ .find("events", (b) => b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Launch")))
1107
+ .executeRetrieve();
1108
+
1109
+ expect(event).toBeDefined();
1110
+ expect(event.name).toBe("Launch");
1111
+ expect(event.payload).toEqual(payload);
1112
+ expect(event.big_score).toBe(bigScore);
1113
+ expect(event.happened_on).toBeInstanceOf(Date);
1114
+ expect(event.happened_on.toISOString()).toBe(happenedOn.toISOString());
1115
+ expect(event.created_at).toBeInstanceOf(Date);
1116
+
1117
+ const createdAtMs = event.created_at.getTime();
1118
+ const afterFetch = Date.now();
1119
+ expect(createdAtMs).toBeGreaterThanOrEqual(beforeCreate - 5 * 60 * 1000);
1120
+ expect(createdAtMs).toBeLessThanOrEqual(afterFetch + 5 * 60 * 1000);
1121
+ });
1122
+
1123
+ it("should parse CURRENT_TIMESTAMP strings as UTC", async () => {
1124
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
1125
+ const tableName = adapter.namingStrategy.tableName("events", "namespace");
1126
+
1127
+ const createUow = queryEngine.createUnitOfWork("create-utc-event");
1128
+ createUow.create("events", {
1129
+ name: "UTC Timestamp",
1130
+ happened_on: new Date("2024-06-15T00:00:00.000Z"),
1131
+ payload: { level: "info", tags: ["sqlite", "utc"] },
1132
+ big_score: 42n,
1133
+ });
1134
+ await createUow.executeMutations();
1135
+
1136
+ sqliteDatabase
1137
+ .prepare(`UPDATE ${tableName} SET created_at = ? WHERE name = ?`)
1138
+ .run("2024-06-15 14:30:00", "UTC Timestamp");
1139
+
1140
+ const [[event]] = await queryEngine
1141
+ .createUnitOfWork("get-utc-event")
1142
+ .find("events", (b) =>
1143
+ b.whereIndex("events_name_idx", (eb) => eb("name", "=", "UTC Timestamp")),
1144
+ )
1145
+ .executeRetrieve();
1146
+
1147
+ expect(event.created_at).toBeInstanceOf(Date);
1148
+ expect(event.created_at.toISOString()).toBe("2024-06-15T14:30:00.000Z");
1149
+ });
1150
+
1151
+ it("should roundtrip BigInt when sqlite returns bigint values", async () => {
1152
+ sqliteDatabase.defaultSafeIntegers(true);
1153
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
1154
+ const safeIntegerLimit = BigInt(Number.MAX_SAFE_INTEGER);
1155
+ const bigScore = safeIntegerLimit + 42n;
1156
+
1157
+ try {
1158
+ const createUow = queryEngine.createUnitOfWork("create-safe-bigint-event");
1159
+ createUow.create("events", {
1160
+ name: "Safe BigInt",
1161
+ happened_on: new Date("2024-06-17T00:00:00.000Z"),
1162
+ payload: { level: "info", tags: ["sqlite", "safe-bigint"] },
1163
+ big_score: bigScore,
1164
+ });
1165
+ await createUow.executeMutations();
1166
+
1167
+ const [[event]] = await queryEngine
1168
+ .createUnitOfWork("get-safe-bigint-event")
1169
+ .find("events", (b) =>
1170
+ b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Safe BigInt")),
1171
+ )
1172
+ .executeRetrieve();
1173
+
1174
+ expect(event.big_score).toBe(bigScore);
1175
+ } finally {
1176
+ sqliteDatabase.defaultSafeIntegers(false);
1177
+ }
1178
+ });
1179
+
1180
+ it("should throw when sqlite returns unsafe BigInt numbers", async () => {
1181
+ sqliteDatabase.defaultSafeIntegers(false);
1182
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
1183
+ const unsafeBigScore = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
1184
+
1185
+ try {
1186
+ const createUow = queryEngine.createUnitOfWork("create-unsafe-event");
1187
+ createUow.create("events", {
1188
+ name: "Unsafe BigInt",
1189
+ happened_on: new Date("2024-06-16T00:00:00.000Z"),
1190
+ payload: { level: "warn", tags: ["sqlite", "unsafe-bigint"] },
1191
+ big_score: unsafeBigScore,
1192
+ });
1193
+ await createUow.executeMutations();
1194
+
1195
+ await expect(
1196
+ queryEngine
1197
+ .createUnitOfWork("get-unsafe-event")
1198
+ .find("events", (b) =>
1199
+ b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Unsafe BigInt")),
1200
+ )
1201
+ .executeRetrieve(),
1202
+ ).rejects.toThrow(/Number\.MAX_SAFE_INTEGER/);
1203
+ } finally {
1204
+ sqliteDatabase.defaultSafeIntegers(false);
1205
+ }
1206
+ });
1207
+ });