@fragno-dev/db 0.2.2 → 0.3.0

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