@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,806 @@
1
+ import { PGlite } from "@electric-sql/pglite";
2
+ import { KyselyPGlite } from "kysely-pglite";
3
+ import { beforeAll, describe, expect, expectTypeOf, it } from "vitest";
4
+ import { SqlAdapter } from "./generic-sql-adapter";
5
+ import { column, idColumn, referenceColumn, schema } from "../../schema/create";
6
+ import { Cursor } from "../../query/cursor";
7
+ import { PGLiteDriverConfig } from "./driver-config";
8
+ import { internalSchema } from "../../fragments/internal-fragment";
9
+ import type { CompiledQuery } from "../../sql-driver/sql-driver";
10
+
11
+ describe("SqlAdapter PGLite", () => {
12
+ const testSchema = schema("test", (s) => {
13
+ return s
14
+ .addTable("users", (t) => {
15
+ return t
16
+ .addColumn("id", idColumn())
17
+ .addColumn("name", column("string"))
18
+ .addColumn("age", column("integer").nullable())
19
+ .createIndex("name_idx", ["name"]);
20
+ })
21
+ .addTable("emails", (t) => {
22
+ return t
23
+ .addColumn("id", idColumn())
24
+ .addColumn("user_id", referenceColumn())
25
+ .addColumn("email", column("string"))
26
+ .addColumn("is_primary", column("bool").defaultTo(false))
27
+ .createIndex("unique_email", ["email"], { unique: true })
28
+ .createIndex("user_emails", ["user_id"]);
29
+ })
30
+ .addTable("posts", (t) => {
31
+ return t
32
+ .addColumn("id", idColumn())
33
+ .addColumn("user_id", referenceColumn())
34
+ .addColumn("title", column("string"))
35
+ .addColumn("content", column("string"))
36
+ .createIndex("posts_user_idx", ["user_id"]);
37
+ })
38
+ .addTable("comments", (t) => {
39
+ return t
40
+ .addColumn("id", idColumn())
41
+ .addColumn("post_id", referenceColumn())
42
+ .addColumn("user_id", referenceColumn())
43
+ .addColumn("text", column("string"))
44
+ .createIndex("comments_post_idx", ["post_id"])
45
+ .createIndex("comments_user_idx", ["user_id"]);
46
+ })
47
+ .addTable("events", (t) => {
48
+ return t
49
+ .addColumn("id", idColumn())
50
+ .addColumn("name", column("string"))
51
+ .addColumn(
52
+ "created_at",
53
+ column("timestamp").defaultTo((b) => b.now()),
54
+ )
55
+ .addColumn("happened_on", column("date"))
56
+ .addColumn("payload", column("json").nullable())
57
+ .addColumn("big_score", column("bigint"))
58
+ .createIndex("events_name_idx", ["name"]);
59
+ })
60
+ .addReference("user", {
61
+ type: "one",
62
+ from: { table: "emails", column: "user_id" },
63
+ to: { table: "users", column: "id" },
64
+ })
65
+ .addReference("author", {
66
+ type: "one",
67
+ from: { table: "posts", column: "user_id" },
68
+ to: { table: "users", column: "id" },
69
+ })
70
+ .addReference("post", {
71
+ type: "one",
72
+ from: { table: "comments", column: "post_id" },
73
+ to: { table: "posts", column: "id" },
74
+ })
75
+ .addReference("commenter", {
76
+ type: "one",
77
+ from: { table: "comments", column: "user_id" },
78
+ to: { table: "users", column: "id" },
79
+ });
80
+ });
81
+
82
+ let adapter: SqlAdapter;
83
+
84
+ beforeAll(async () => {
85
+ const pgliteDatabase = new PGlite();
86
+
87
+ const { dialect } = new KyselyPGlite(pgliteDatabase);
88
+
89
+ adapter = new SqlAdapter({
90
+ dialect,
91
+ driverConfig: new PGLiteDriverConfig(),
92
+ });
93
+
94
+ {
95
+ const migrations = adapter.prepareMigrations(internalSchema, "");
96
+ await migrations.executeWithDriver(adapter.driver, 0);
97
+ }
98
+
99
+ {
100
+ const migrations = adapter.prepareMigrations(testSchema, "namespace");
101
+ await migrations.executeWithDriver(adapter.driver, 0);
102
+ }
103
+
104
+ return async () => {
105
+ await adapter.close();
106
+ };
107
+ }, 12000);
108
+
109
+ it("should execute Unit of Work with version checking", async () => {
110
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
111
+
112
+ const createUow = queryEngine.createUnitOfWork("create-users");
113
+ createUow.create("users", {
114
+ name: "SqlAdapter PGLite Alice",
115
+ age: 25,
116
+ });
117
+ createUow.create("users", {
118
+ name: "SqlAdapter PGLite Bob",
119
+ age: 30,
120
+ });
121
+
122
+ expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
123
+ Parameters<typeof createUow.find>[0]
124
+ >();
125
+ expectTypeOf<keyof typeof testSchema.tables>().toEqualTypeOf<
126
+ "users" | "emails" | "posts" | "comments" | "events"
127
+ >();
128
+
129
+ const { success: createSuccess } = await createUow.executeMutations();
130
+ expect(createSuccess).toBe(true);
131
+
132
+ const createdIds = createUow.getCreatedIds();
133
+ expect(createdIds).toHaveLength(2);
134
+
135
+ const initialUserId = createdIds[0];
136
+
137
+ const uow = queryEngine
138
+ .createUnitOfWork("update-user-age")
139
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)));
140
+
141
+ const [users] = await uow.executeRetrieve();
142
+
143
+ uow.update("users", initialUserId, (b) => b.set({ age: 26 }).check());
144
+
145
+ const { success } = await uow.executeMutations();
146
+ expect(success).toBe(true);
147
+ expect(users).toHaveLength(1);
148
+ expect(users[0].name).toBe("SqlAdapter PGLite Alice");
149
+
150
+ const [[updatedUser]] = await queryEngine
151
+ .createUnitOfWork("get-updated-user")
152
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
153
+ .executeRetrieve();
154
+
155
+ expect(updatedUser).toMatchObject({
156
+ id: expect.objectContaining({
157
+ externalId: initialUserId.externalId,
158
+ version: 1,
159
+ }),
160
+ age: 26,
161
+ });
162
+
163
+ const uow2 = queryEngine.createUnitOfWork("update-user-stale");
164
+ uow2.update("users", initialUserId, (b) => b.set({ age: 27 }).check());
165
+
166
+ const { success: success2 } = await uow2.executeMutations();
167
+ expect(success2).toBe(false);
168
+
169
+ const [[unchangedUser]] = await queryEngine
170
+ .createUnitOfWork("verify-unchanged")
171
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", initialUserId)))
172
+ .executeRetrieve();
173
+
174
+ expect(unchangedUser).toMatchObject({
175
+ id: expect.objectContaining({
176
+ version: 1,
177
+ }),
178
+ age: 26,
179
+ });
180
+ });
181
+
182
+ it("should support count operations", async () => {
183
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
184
+
185
+ const createUow = queryEngine.createUnitOfWork("create-users-count");
186
+ createUow.create("users", { name: "SqlAdapter PGLite Count 1", age: 21 });
187
+ createUow.create("users", { name: "SqlAdapter PGLite Count 2", age: 31 });
188
+ createUow.create("users", { name: "SqlAdapter PGLite Count 3", age: 41 });
189
+ await createUow.executeMutations();
190
+
191
+ const [totalCount] = await queryEngine
192
+ .createUnitOfWork("count-all")
193
+ .find("users", (b) => b.whereIndex("primary").selectCount())
194
+ .executeRetrieve();
195
+
196
+ expect(totalCount).toBeGreaterThanOrEqual(3);
197
+ expect(typeof totalCount).toBe("number");
198
+ });
199
+
200
+ it("should support cursor-based pagination", async () => {
201
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
202
+ const prefix = "SqlAdapter PGLite Manual Cursor";
203
+
204
+ const createUow = queryEngine.createUnitOfWork("create-cursor-users");
205
+ createUow.create("users", { name: `${prefix} A`, age: 20 });
206
+ createUow.create("users", { name: `${prefix} B`, age: 30 });
207
+ createUow.create("users", { name: `${prefix} C`, age: 40 });
208
+ createUow.create("users", { name: `${prefix} D`, age: 50 });
209
+ createUow.create("users", { name: `${prefix} E`, age: 60 });
210
+
211
+ await createUow.executeMutations();
212
+
213
+ const [firstPage] = await queryEngine
214
+ .createUnitOfWork("first-page")
215
+ .find("users", (b) =>
216
+ b
217
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
218
+ .orderByIndex("name_idx", "asc")
219
+ .pageSize(2),
220
+ )
221
+ .executeRetrieve();
222
+
223
+ expect(firstPage).toHaveLength(2);
224
+ expect(firstPage.map((u) => u.name)).toEqual([`${prefix} A`, `${prefix} B`]);
225
+
226
+ const lastItem = firstPage[firstPage.length - 1]!;
227
+ const cursor = new Cursor({
228
+ indexName: "name_idx",
229
+ orderDirection: "asc",
230
+ pageSize: 2,
231
+ indexValues: { name: lastItem.name },
232
+ }).encode();
233
+
234
+ const [secondPage] = await queryEngine
235
+ .createUnitOfWork("second-page")
236
+ .find("users", (b) =>
237
+ b
238
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
239
+ .orderByIndex("name_idx", "asc")
240
+ .after(cursor)
241
+ .pageSize(2),
242
+ )
243
+ .executeRetrieve();
244
+
245
+ expect(secondPage).toHaveLength(2);
246
+ expect(secondPage.map((u) => u.name)).toEqual([`${prefix} C`, `${prefix} D`]);
247
+ });
248
+
249
+ it("should support cursor-based pagination with findWithCursor()", async () => {
250
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
251
+ const prefix = "SqlAdapter PGLite Cursor";
252
+
253
+ for (let i = 1; i <= 12; i++) {
254
+ await queryEngine.create("users", {
255
+ name: `${prefix} ${i.toString().padStart(2, "0")}`,
256
+ age: 20 + i,
257
+ });
258
+ }
259
+
260
+ const firstPage = await queryEngine.findWithCursor("users", (b) =>
261
+ b
262
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
263
+ .orderByIndex("name_idx", "asc")
264
+ .pageSize(5),
265
+ );
266
+
267
+ expect(firstPage.items).toHaveLength(5);
268
+ expect(firstPage.hasNextPage).toBe(true);
269
+ expect(firstPage.cursor).toBeInstanceOf(Cursor);
270
+
271
+ const secondPage = await queryEngine.findWithCursor("users", (b) =>
272
+ b
273
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
274
+ .after(firstPage.cursor!)
275
+ .orderByIndex("name_idx", "asc")
276
+ .pageSize(5),
277
+ );
278
+
279
+ expect(secondPage.items).toHaveLength(5);
280
+ expect(secondPage.hasNextPage).toBe(true);
281
+ expect(secondPage.cursor).toBeInstanceOf(Cursor);
282
+
283
+ const thirdPage = await queryEngine.findWithCursor("users", (b) =>
284
+ b
285
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
286
+ .after(secondPage.cursor!)
287
+ .orderByIndex("name_idx", "asc")
288
+ .pageSize(5),
289
+ );
290
+
291
+ expect(thirdPage.items).toHaveLength(2);
292
+ expect(thirdPage.hasNextPage).toBe(false);
293
+ expect(thirdPage.cursor).toBeUndefined();
294
+
295
+ const emptyPage = await queryEngine.findWithCursor("users", (b) =>
296
+ b
297
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", "NoMatchPrefix"))
298
+ .orderByIndex("name_idx", "asc")
299
+ .pageSize(5),
300
+ );
301
+
302
+ expect(emptyPage.items).toHaveLength(0);
303
+ expect(emptyPage.hasNextPage).toBe(false);
304
+ expect(emptyPage.cursor).toBeUndefined();
305
+ });
306
+
307
+ it("should support findWithCursor() in Unit of Work", async () => {
308
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
309
+ const prefix = "SqlAdapter PGLite UOW Cursor";
310
+
311
+ for (let i = 1; i <= 6; i++) {
312
+ await queryEngine.create("users", {
313
+ name: `${prefix} ${i}`,
314
+ age: 40 + i,
315
+ });
316
+ }
317
+
318
+ const uow = queryEngine.createUnitOfWork("cursor-test").findWithCursor("users", (b) =>
319
+ b
320
+ .whereIndex("name_idx", (eb) => eb("name", "starts with", prefix))
321
+ .orderByIndex("name_idx", "asc")
322
+ .pageSize(5),
323
+ );
324
+
325
+ const [result] = await uow.executeRetrieve();
326
+
327
+ expect(result.items).toHaveLength(5);
328
+ expect(typeof result.hasNextPage).toBe("boolean");
329
+ expect(result.cursor).toBeInstanceOf(Cursor);
330
+ });
331
+
332
+ it("should support joins", async () => {
333
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
334
+ const queries: CompiledQuery[] = [];
335
+
336
+ const createUow = queryEngine.createUnitOfWork("create-join-user");
337
+ createUow.create("users", { name: "SqlAdapter PGLite Email User", age: 20 });
338
+ const { success } = await createUow.executeMutations();
339
+ expect(success).toBe(true);
340
+
341
+ const [usersResult] = await queryEngine
342
+ .createUnitOfWork("get-created-user")
343
+ .find("users", (b) =>
344
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "SqlAdapter PGLite Email User")),
345
+ )
346
+ .executeRetrieve();
347
+
348
+ expect(usersResult).toHaveLength(1);
349
+ const createdUser = usersResult[0];
350
+ expect(createdUser.name).toBe("SqlAdapter PGLite Email User");
351
+
352
+ const createEmailUow = queryEngine.createUnitOfWork("create-test-email");
353
+ createEmailUow.create("emails", {
354
+ user_id: createdUser.id,
355
+ email: "prisma-pglite@example.com",
356
+ is_primary: true,
357
+ });
358
+ await createEmailUow.executeMutations();
359
+
360
+ const uow = queryEngine
361
+ .createUnitOfWork("test-joins", { onQuery: (query) => queries.push(query) })
362
+ .find("emails", (b) =>
363
+ b
364
+ .whereIndex("user_emails", (eb) => eb("user_id", "=", createdUser.id))
365
+ .join((jb) => jb.user((builder) => builder.select(["name", "id", "age"]))),
366
+ );
367
+
368
+ const [[email]] = await uow.executeRetrieve();
369
+
370
+ const [query] = queries;
371
+ expect(query.sql).toMatchInlineSnapshot(
372
+ `"select "user"."name" as "user:name", "user"."id" as "user:id", "user"."age" as "user:age", "user"."_internalId" as "user:_internalId", "user"."_version" as "user:_version", "namespace"."emails"."id" as "id", "namespace"."emails"."user_id" as "user_id", "namespace"."emails"."email" as "email", "namespace"."emails"."is_primary" as "is_primary", "namespace"."emails"."_internalId" as "_internalId", "namespace"."emails"."_version" as "_version" from "namespace"."emails" left join "namespace"."users" as "user" on "namespace"."emails"."user_id" = "user"."_internalId" where "namespace"."emails"."user_id" = $1"`,
373
+ );
374
+
375
+ expect(email).toMatchObject({
376
+ id: expect.objectContaining({
377
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
378
+ internalId: expect.any(BigInt),
379
+ }),
380
+ user_id: expect.objectContaining({
381
+ internalId: expect.any(BigInt),
382
+ }),
383
+ email: "prisma-pglite@example.com",
384
+ is_primary: true,
385
+ user: {
386
+ id: expect.objectContaining({
387
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
388
+ internalId: expect.any(BigInt),
389
+ }),
390
+ name: "SqlAdapter PGLite Email User",
391
+ age: 20,
392
+ },
393
+ });
394
+ });
395
+
396
+ it("should support inserting with external id string", async () => {
397
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
398
+
399
+ const createUserUow = queryEngine.createUnitOfWork("create-user-for-external-id");
400
+ createUserUow.create("users", { name: "SqlAdapter PGLite External ID User", age: 35 });
401
+ await createUserUow.executeMutations();
402
+
403
+ const [[user]] = await queryEngine
404
+ .createUnitOfWork("get-user-for-external-id")
405
+ .find("users", (b) =>
406
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "SqlAdapter PGLite External ID User")),
407
+ )
408
+ .executeRetrieve();
409
+
410
+ const createEmailUow = queryEngine.createUnitOfWork("create-email-with-external-id");
411
+ createEmailUow.create("emails", {
412
+ user_id: user.id.externalId,
413
+ email: "prisma-pglite-external-id@example.com",
414
+ is_primary: false,
415
+ });
416
+
417
+ const { success } = await createEmailUow.executeMutations();
418
+ expect(success).toBe(true);
419
+
420
+ const [[email]] = await queryEngine
421
+ .createUnitOfWork("get-email-by-external-id")
422
+ .find("emails", (b) =>
423
+ b
424
+ .whereIndex("unique_email", (eb) =>
425
+ eb("email", "=", "prisma-pglite-external-id@example.com"),
426
+ )
427
+ .join((jb) => jb.user((builder) => builder.select(["name", "id"]))),
428
+ )
429
+ .executeRetrieve();
430
+
431
+ expect(email).toMatchObject({
432
+ email: "prisma-pglite-external-id@example.com",
433
+ is_primary: false,
434
+ user_id: expect.objectContaining({
435
+ internalId: user.id.internalId,
436
+ }),
437
+ user: {
438
+ id: expect.objectContaining({
439
+ externalId: user.id.externalId,
440
+ }),
441
+ name: "SqlAdapter PGLite External ID User",
442
+ },
443
+ });
444
+ });
445
+
446
+ it("should create user and post in same transaction using returned ID", async () => {
447
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
448
+
449
+ const uow = queryEngine.createUnitOfWork("create-user-and-post");
450
+ const userId = uow.create("users", {
451
+ name: "SqlAdapter PGLite UOW User",
452
+ age: 35,
453
+ });
454
+
455
+ const postId = uow.create("posts", {
456
+ user_id: userId,
457
+ title: "SqlAdapter PGLite UOW Post",
458
+ content: "This post was created in the same transaction as the user",
459
+ });
460
+
461
+ const { success } = await uow.executeMutations();
462
+ expect(success).toBe(true);
463
+
464
+ const userIdStr = userId.toString();
465
+ const postIdStr = postId.toString();
466
+
467
+ const [[user]] = await queryEngine
468
+ .createUnitOfWork("verify-user")
469
+ .find("users", (b) => b.whereIndex("primary", (eb) => eb("id", "=", userIdStr)))
470
+ .executeRetrieve();
471
+
472
+ const [[post]] = await queryEngine
473
+ .createUnitOfWork("verify-post")
474
+ .find("posts", (b) => b.whereIndex("primary", (eb) => eb("id", "=", postIdStr)))
475
+ .executeRetrieve();
476
+
477
+ expect(user.name).toBe("SqlAdapter PGLite UOW User");
478
+ expect(user.age).toBe(35);
479
+ expect(post.title).toBe("SqlAdapter PGLite UOW Post");
480
+ expect(post.content).toBe("This post was created in the same transaction as the user");
481
+ expect(post.user_id.internalId).toBe(user.id.internalId);
482
+ });
483
+
484
+ it("should support complex nested joins (comments -> post -> author)", async () => {
485
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
486
+ const queries: CompiledQuery[] = [];
487
+
488
+ const createAuthorUow = queryEngine.createUnitOfWork("create-author");
489
+ createAuthorUow.create("users", { name: "SqlAdapter PGLite Author", age: 30 });
490
+ await createAuthorUow.executeMutations();
491
+
492
+ const [[author]] = await queryEngine
493
+ .createUnitOfWork("get-author")
494
+ .find("users", (b) =>
495
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "SqlAdapter PGLite Author")),
496
+ )
497
+ .executeRetrieve();
498
+
499
+ const createPostUow = queryEngine.createUnitOfWork("create-post");
500
+ createPostUow.create("posts", {
501
+ user_id: author.id,
502
+ title: "SqlAdapter PGLite Post",
503
+ content: "Nested join content",
504
+ });
505
+ await createPostUow.executeMutations();
506
+
507
+ const [[post]] = await queryEngine
508
+ .createUnitOfWork("get-post")
509
+ .find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", author.id)))
510
+ .executeRetrieve();
511
+
512
+ const createCommenterUow = queryEngine.createUnitOfWork("create-commenter");
513
+ createCommenterUow.create("users", { name: "SqlAdapter PGLite Commenter", age: 25 });
514
+ await createCommenterUow.executeMutations();
515
+
516
+ const [[commenter]] = await queryEngine
517
+ .createUnitOfWork("get-commenter")
518
+ .find("users", (b) =>
519
+ b.whereIndex("name_idx", (eb) => eb("name", "=", "SqlAdapter PGLite Commenter")),
520
+ )
521
+ .executeRetrieve();
522
+
523
+ const createCommentUow = queryEngine.createUnitOfWork("create-comment");
524
+ createCommentUow.create("comments", {
525
+ post_id: post.id,
526
+ user_id: commenter.id,
527
+ text: "Great Prisma post!",
528
+ });
529
+ await createCommentUow.executeMutations();
530
+
531
+ const uow = queryEngine
532
+ .createUnitOfWork("test-complex-joins", { onQuery: (query) => queries.push(query) })
533
+ .find("comments", (b) =>
534
+ b.whereIndex("primary").join((jb) =>
535
+ jb
536
+ .post((postBuilder) =>
537
+ postBuilder
538
+ .select(["id", "title", "content"])
539
+ .orderByIndex("primary", "desc")
540
+ .pageSize(1)
541
+ .join((jb2) =>
542
+ jb2.author((authorBuilder) =>
543
+ authorBuilder.select(["id", "name", "age"]).orderByIndex("name_idx", "asc"),
544
+ ),
545
+ ),
546
+ )
547
+ .commenter((commenterBuilder) => commenterBuilder.select(["id", "name"])),
548
+ ),
549
+ );
550
+
551
+ const [[comment]] = await uow.executeRetrieve();
552
+
553
+ expect(comment).toMatchObject({
554
+ id: expect.objectContaining({
555
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
556
+ internalId: expect.any(BigInt),
557
+ }),
558
+ text: "Great Prisma post!",
559
+ post: {
560
+ id: expect.objectContaining({
561
+ externalId: post.id.externalId,
562
+ }),
563
+ title: "SqlAdapter PGLite Post",
564
+ content: "Nested join content",
565
+ author: {
566
+ id: expect.objectContaining({
567
+ externalId: author.id.externalId,
568
+ }),
569
+ name: "SqlAdapter PGLite Author",
570
+ age: 30,
571
+ },
572
+ },
573
+ commenter: {
574
+ id: expect.objectContaining({
575
+ externalId: commenter.id.externalId,
576
+ }),
577
+ name: "SqlAdapter PGLite Commenter",
578
+ },
579
+ });
580
+
581
+ const [query] = queries;
582
+ expect(query.sql).toMatchInlineSnapshot(
583
+ `"select "post"."id" as "post:id", "post"."title" as "post:title", "post"."content" as "post:content", "post"."_internalId" as "post:_internalId", "post"."_version" as "post:_version", "post_author"."id" as "post:author:id", "post_author"."name" as "post:author:name", "post_author"."age" as "post:author:age", "post_author"."_internalId" as "post:author:_internalId", "post_author"."_version" as "post:author:_version", "commenter"."id" as "commenter:id", "commenter"."name" as "commenter:name", "commenter"."_internalId" as "commenter:_internalId", "commenter"."_version" as "commenter:_version", "namespace"."comments"."id" as "id", "namespace"."comments"."post_id" as "post_id", "namespace"."comments"."user_id" as "user_id", "namespace"."comments"."text" as "text", "namespace"."comments"."_internalId" as "_internalId", "namespace"."comments"."_version" as "_version" from "namespace"."comments" left join "namespace"."posts" as "post" on "namespace"."comments"."post_id" = "post"."_internalId" left join "namespace"."users" as "post_author" on "post"."user_id" = "post_author"."_internalId" left join "namespace"."users" as "commenter" on "namespace"."comments"."user_id" = "commenter"."_internalId""`,
584
+ );
585
+ });
586
+
587
+ it("should return created IDs from UOW create operations", async () => {
588
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
589
+
590
+ const uow1 = queryEngine.createUnitOfWork("create-multiple-users");
591
+ uow1.create("users", { name: "SqlAdapter PGLite ID User 1", age: 30 });
592
+ uow1.create("users", { name: "SqlAdapter PGLite ID User 2", age: 35 });
593
+ uow1.create("users", { name: "SqlAdapter PGLite ID User 3", age: 40 });
594
+
595
+ const { success: success1 } = await uow1.executeMutations();
596
+ expect(success1).toBe(true);
597
+
598
+ const createdIds1 = uow1.getCreatedIds();
599
+ expect(createdIds1).toMatchObject([
600
+ expect.objectContaining({
601
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
602
+ internalId: expect.any(BigInt),
603
+ }),
604
+ expect.objectContaining({
605
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
606
+ internalId: expect.any(BigInt),
607
+ }),
608
+ expect.objectContaining({
609
+ externalId: expect.stringMatching(/^[a-z0-9]{20,}$/),
610
+ internalId: expect.any(BigInt),
611
+ }),
612
+ ]);
613
+
614
+ const externalIds = createdIds1.map((id) => id.externalId);
615
+ expect(new Set(externalIds).size).toBe(3);
616
+
617
+ const user1 = await queryEngine.findFirst("users", (b) =>
618
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[0].externalId)),
619
+ );
620
+ const user2 = await queryEngine.findFirst("users", (b) =>
621
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[1].externalId)),
622
+ );
623
+ const user3 = await queryEngine.findFirst("users", (b) =>
624
+ b.whereIndex("primary", (eb) => eb("id", "=", createdIds1[2].externalId)),
625
+ );
626
+
627
+ expect(user1).toMatchObject({
628
+ id: expect.objectContaining({
629
+ externalId: createdIds1[0].externalId,
630
+ }),
631
+ name: "SqlAdapter PGLite ID User 1",
632
+ age: 30,
633
+ });
634
+
635
+ expect(user2).toMatchObject({
636
+ id: expect.objectContaining({
637
+ externalId: createdIds1[1].externalId,
638
+ }),
639
+ name: "SqlAdapter PGLite ID User 2",
640
+ age: 35,
641
+ });
642
+
643
+ expect(user3).toMatchObject({
644
+ id: expect.objectContaining({
645
+ externalId: createdIds1[2].externalId,
646
+ }),
647
+ name: "SqlAdapter PGLite ID User 3",
648
+ age: 40,
649
+ });
650
+
651
+ const uow2 = queryEngine.createUnitOfWork("mixed-operations");
652
+ uow2.create("users", { name: "SqlAdapter PGLite New User", age: 50 });
653
+ uow2.update("users", createdIds1[0], (b) => b.set({ age: 31 }));
654
+ uow2.create("users", { name: "SqlAdapter PGLite Another New User", age: 55 });
655
+ uow2.delete("users", createdIds1[2]);
656
+
657
+ const { success: success2 } = await uow2.executeMutations();
658
+ expect(success2).toBe(true);
659
+
660
+ const createdIds2 = uow2.getCreatedIds();
661
+ expect(createdIds2).toHaveLength(2);
662
+ expect(createdIds2[0].externalId).toBeDefined();
663
+ expect(createdIds2[1].externalId).toBeDefined();
664
+
665
+ const customId = "pglite-custom-user-id-1";
666
+ const uow3 = queryEngine.createUnitOfWork("create-with-custom-id");
667
+ uow3.create("users", { id: customId, name: "SqlAdapter PGLite Custom ID User", age: 60 });
668
+
669
+ const { success: success3 } = await uow3.executeMutations();
670
+ expect(success3).toBe(true);
671
+
672
+ const createdIds3 = uow3.getCreatedIds();
673
+ expect(createdIds3).toHaveLength(1);
674
+ expect(createdIds3[0].externalId).toBe(customId);
675
+ expect(createdIds3[0].internalId).toBeDefined();
676
+
677
+ const customIdUser = await queryEngine.findFirst("users", (b) =>
678
+ b.whereIndex("primary", (eb) => eb("id", "=", customId)),
679
+ );
680
+
681
+ expect(customIdUser).toMatchObject({
682
+ id: expect.objectContaining({
683
+ externalId: customId,
684
+ }),
685
+ name: "SqlAdapter PGLite Custom ID User",
686
+ age: 60,
687
+ });
688
+ });
689
+
690
+ it("should handle timestamps and timezones correctly", async () => {
691
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
692
+
693
+ const createUow = queryEngine.createUnitOfWork("create-event-for-timestamp");
694
+ createUow.create("events", {
695
+ name: "Timestamp Event PGLite",
696
+ happened_on: new Date("2024-06-18T00:00:00.000Z"),
697
+ payload: { level: "info", tags: ["timestamp", "pglite"] },
698
+ big_score: 42n,
699
+ });
700
+ await createUow.executeMutations();
701
+
702
+ const [[event]] = await queryEngine
703
+ .createUnitOfWork("get-event-for-timestamp")
704
+ .find("events", (b) =>
705
+ b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Timestamp Event PGLite")),
706
+ )
707
+ .executeRetrieve();
708
+
709
+ expect(event.created_at).toBeInstanceOf(Date);
710
+
711
+ const now = Date.now();
712
+ const createdTime = event.created_at.getTime();
713
+ expect(createdTime).toBeGreaterThan(now - 24 * 60 * 60 * 1000);
714
+ expect(createdTime).toBeLessThan(now + 24 * 60 * 60 * 1000);
715
+
716
+ expect(typeof event.created_at.toISOString).toBe("function");
717
+ expect(typeof event.created_at.getTime).toBe("function");
718
+ expect(typeof event.created_at.getTimezoneOffset).toBe("function");
719
+
720
+ const isoString = event.created_at.toISOString();
721
+ expect(new Date(isoString).getTime()).toBe(event.created_at.getTime());
722
+ });
723
+
724
+ it("should fail check() when version changes", async () => {
725
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
726
+
727
+ const createUserUow = queryEngine.createUnitOfWork("create-user-for-version-conflict");
728
+ createUserUow.create("users", {
729
+ name: "SqlAdapter PGLite Version Conflict User",
730
+ age: 40,
731
+ });
732
+ await createUserUow.executeMutations();
733
+
734
+ const [[user]] = await queryEngine
735
+ .createUnitOfWork("get-user-for-version-conflict")
736
+ .find("users", (b) =>
737
+ b.whereIndex("name_idx", (eb) =>
738
+ eb("name", "=", "SqlAdapter PGLite Version Conflict User"),
739
+ ),
740
+ )
741
+ .executeRetrieve();
742
+
743
+ const updateUow = queryEngine.createUnitOfWork("update-user-version");
744
+ updateUow.update("users", user.id, (b) => b.set({ age: 41 }));
745
+ await updateUow.executeMutations();
746
+
747
+ const uow = queryEngine.createUnitOfWork("check-stale-version");
748
+ uow.check("users", user.id);
749
+ uow.create("posts", {
750
+ user_id: user.id,
751
+ title: "SqlAdapter PGLite Should Not Be Created",
752
+ content: "Content",
753
+ });
754
+
755
+ const { success } = await uow.executeMutations();
756
+ expect(success).toBe(false);
757
+
758
+ const [posts] = await queryEngine
759
+ .createUnitOfWork("get-posts-for-version-conflict")
760
+ .find("posts", (b) => b.whereIndex("posts_user_idx", (eb) => eb("user_id", "=", user.id)))
761
+ .executeRetrieve();
762
+
763
+ const conflictPosts = posts.filter(
764
+ (p) => p.title === "SqlAdapter PGLite Should Not Be Created",
765
+ );
766
+ expect(conflictPosts).toHaveLength(0);
767
+ });
768
+
769
+ it("should roundtrip DateTime, Date, JSON, and BigInt", async () => {
770
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
771
+ const beforeCreate = Date.now();
772
+ const happenedOn = new Date("2024-06-15T00:00:00.000Z");
773
+ const payload = { level: "info", tags: ["launch", "pglite"] };
774
+ const bigScore = 9876543210123n;
775
+
776
+ const createUow = queryEngine.createUnitOfWork("create-event");
777
+ createUow.create("events", {
778
+ name: "Launch",
779
+ happened_on: happenedOn,
780
+ payload,
781
+ big_score: bigScore,
782
+ });
783
+ await createUow.executeMutations();
784
+
785
+ const [[event]] = await queryEngine
786
+ .createUnitOfWork("get-event")
787
+ .find("events", (b) => b.whereIndex("events_name_idx", (eb) => eb("name", "=", "Launch")))
788
+ .executeRetrieve();
789
+
790
+ expect(event).toBeDefined();
791
+ expect(event.name).toBe("Launch");
792
+ expect(event.payload).toEqual(payload);
793
+ expect(event.big_score).toBe(bigScore);
794
+ expect(event.happened_on).toBeInstanceOf(Date);
795
+ expect(event.happened_on.toISOString().slice(0, 10)).toBe(
796
+ happenedOn.toISOString().slice(0, 10),
797
+ );
798
+ expect(event.created_at).toBeInstanceOf(Date);
799
+
800
+ const createdAtMs = event.created_at.getTime();
801
+ const afterFetch = Date.now();
802
+ expect(createdAtMs).toBeGreaterThan(0);
803
+ expect(Math.abs(createdAtMs - beforeCreate)).toBeLessThan(24 * 60 * 60 * 1000);
804
+ expect(Math.abs(createdAtMs - afterFetch)).toBeLessThan(24 * 60 * 60 * 1000);
805
+ });
806
+ });