@fragno-dev/db 0.2.1 → 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 (362) hide show
  1. package/.turbo/turbo-build.log +206 -140
  2. package/CHANGELOG.md +67 -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 +38 -28
  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 +45 -96
  80. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  81. package/dist/db-fragment-definition-builder.js +121 -99
  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 +172 -9
  92. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  93. package/dist/fragments/internal-fragment.js +193 -74
  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 +47 -4
  106. package/dist/hooks/hooks.d.ts.map +1 -1
  107. package/dist/hooks/hooks.js +106 -39
  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 +17 -10
  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 +351 -100
  165. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  166. package/dist/query/unit-of-work/execute-unit-of-work.js +440 -267
  167. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  168. package/dist/query/unit-of-work/unit-of-work.d.ts +67 -22
  169. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  170. package/dist/query/unit-of-work/unit-of-work.js +110 -13
  171. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  172. package/dist/query/value-decoding.js +8 -5
  173. package/dist/query/value-decoding.js.map +1 -1
  174. package/dist/query/value-encoding.js +29 -9
  175. package/dist/query/value-encoding.js.map +1 -1
  176. package/dist/schema/create.d.ts +40 -14
  177. package/dist/schema/create.d.ts.map +1 -1
  178. package/dist/schema/create.js +82 -42
  179. package/dist/schema/create.js.map +1 -1
  180. package/dist/schema/generate-id.d.ts +20 -0
  181. package/dist/schema/generate-id.d.ts.map +1 -0
  182. package/dist/schema/generate-id.js +28 -0
  183. package/dist/schema/generate-id.js.map +1 -0
  184. package/dist/schema/type-conversion/create-sql-type-mapper.js +3 -2
  185. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  186. package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
  187. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  188. package/dist/schema/validator.d.ts +10 -0
  189. package/dist/schema/validator.d.ts.map +1 -0
  190. package/dist/schema/validator.js +123 -0
  191. package/dist/schema/validator.js.map +1 -0
  192. package/dist/schema-output/drizzle.d.ts +30 -0
  193. package/dist/schema-output/drizzle.d.ts.map +1 -0
  194. package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +82 -56
  195. package/dist/schema-output/drizzle.js.map +1 -0
  196. package/dist/schema-output/prisma.d.ts +17 -0
  197. package/dist/schema-output/prisma.d.ts.map +1 -0
  198. package/dist/schema-output/prisma.js +296 -0
  199. package/dist/schema-output/prisma.js.map +1 -0
  200. package/dist/util/default-database-adapter.js +61 -0
  201. package/dist/util/default-database-adapter.js.map +1 -0
  202. package/dist/with-database.d.ts +1 -1
  203. package/dist/with-database.d.ts.map +1 -1
  204. package/dist/with-database.js +12 -3
  205. package/dist/with-database.js.map +1 -1
  206. package/package.json +43 -28
  207. package/src/adapters/adapters.ts +30 -24
  208. package/src/adapters/drizzle/migrate-drizzle.test.ts +54 -33
  209. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +599 -0
  210. package/src/adapters/drizzle/test-utils.ts +12 -8
  211. package/src/adapters/generic-sql/driver-config.ts +38 -0
  212. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -5
  213. package/src/adapters/generic-sql/generic-sql-adapter.ts +110 -24
  214. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +54 -0
  215. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +231 -3
  216. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +118 -0
  217. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +26 -8
  218. package/src/adapters/generic-sql/migration/dialect/mysql.ts +46 -8
  219. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +25 -7
  220. package/src/adapters/generic-sql/migration/dialect/postgres.ts +8 -4
  221. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +47 -8
  222. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +27 -12
  223. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +128 -39
  224. package/src/adapters/generic-sql/migration/prepared-migrations.ts +15 -8
  225. package/src/adapters/generic-sql/migration/sql-generator.ts +142 -65
  226. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +9 -6
  227. package/src/adapters/generic-sql/query/cursor-utils.test.ts +271 -0
  228. package/src/adapters/generic-sql/query/cursor-utils.ts +41 -6
  229. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +27 -27
  230. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +38 -24
  231. package/src/adapters/generic-sql/query/select-builder.test.ts +15 -11
  232. package/src/adapters/generic-sql/query/select-builder.ts +6 -2
  233. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +52 -2
  234. package/src/adapters/generic-sql/query/sql-query-compiler.ts +50 -15
  235. package/src/adapters/generic-sql/query/where-builder.test.ts +91 -17
  236. package/src/adapters/generic-sql/query/where-builder.ts +90 -38
  237. package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +6 -6
  238. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +806 -0
  239. package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +11 -11
  240. package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +49 -35
  241. package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +48 -32
  242. package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +6 -6
  243. package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
  244. package/src/adapters/generic-sql/uow-decoder.test.ts +1 -1
  245. package/src/adapters/generic-sql/uow-decoder.ts +21 -3
  246. package/src/adapters/generic-sql/uow-encoder.test.ts +33 -2
  247. package/src/adapters/generic-sql/uow-encoder.ts +50 -11
  248. package/src/adapters/in-memory/condition-evaluator.test.ts +193 -0
  249. package/src/adapters/in-memory/condition-evaluator.ts +275 -0
  250. package/src/adapters/in-memory/errors.ts +20 -0
  251. package/src/adapters/in-memory/in-memory-adapter.ts +277 -0
  252. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +296 -0
  253. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +100 -0
  254. package/src/adapters/in-memory/in-memory-uow.ts +1348 -0
  255. package/src/adapters/in-memory/index.ts +3 -0
  256. package/src/adapters/in-memory/options.test.ts +41 -0
  257. package/src/adapters/in-memory/options.ts +87 -0
  258. package/src/adapters/in-memory/reference-resolution.test.ts +50 -0
  259. package/src/adapters/in-memory/reference-resolution.ts +67 -0
  260. package/src/adapters/in-memory/sorted-array-index.test.ts +123 -0
  261. package/src/adapters/in-memory/sorted-array-index.ts +228 -0
  262. package/src/adapters/in-memory/store.test.ts +68 -0
  263. package/src/adapters/in-memory/store.ts +145 -0
  264. package/src/adapters/in-memory/value-comparison.ts +53 -0
  265. package/src/adapters/in-memory/value-normalization.test.ts +57 -0
  266. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1163 -0
  267. package/src/adapters/shared/from-unit-of-work-compiler.ts +3 -1
  268. package/src/adapters/shared/uow-operation-compiler.ts +26 -16
  269. package/src/adapters/sql/index.ts +12 -0
  270. package/src/db-fragment-definition-builder.test.ts +88 -54
  271. package/src/db-fragment-definition-builder.ts +201 -322
  272. package/src/db-fragment-instantiator.test.ts +169 -101
  273. package/src/db-fragment-integration.test.ts +301 -149
  274. package/src/dispatchers/cloudflare-do/index.test.ts +73 -0
  275. package/src/dispatchers/cloudflare-do/index.ts +104 -0
  276. package/src/dispatchers/node/index.test.ts +91 -0
  277. package/src/dispatchers/node/index.ts +87 -0
  278. package/src/fragments/internal-fragment.routes.ts +42 -0
  279. package/src/fragments/internal-fragment.schema.ts +51 -0
  280. package/src/fragments/internal-fragment.test.ts +730 -274
  281. package/src/fragments/internal-fragment.ts +447 -154
  282. package/src/hooks/durable-hooks-processor.test.ts +117 -0
  283. package/src/hooks/durable-hooks-processor.ts +67 -0
  284. package/src/hooks/hooks.test.ts +411 -259
  285. package/src/hooks/hooks.ts +265 -66
  286. package/src/migration-engine/auto-from-schema.test.ts +14 -14
  287. package/src/migration-engine/auto-from-schema.ts +5 -2
  288. package/src/migration-engine/create.test.ts +2 -2
  289. package/src/migration-engine/generation-engine.test.ts +229 -104
  290. package/src/migration-engine/generation-engine.ts +94 -64
  291. package/src/migration-engine/shared.ts +1 -0
  292. package/src/mod.ts +78 -30
  293. package/src/naming/sql-naming.ts +180 -0
  294. package/src/outbox/outbox-builder.ts +241 -0
  295. package/src/outbox/outbox.test.ts +253 -0
  296. package/src/outbox/outbox.ts +137 -0
  297. package/src/query/column-defaults.ts +41 -3
  298. package/src/query/condition-builder.test.ts +3 -3
  299. package/src/query/cursor.test.ts +116 -18
  300. package/src/query/cursor.ts +75 -26
  301. package/src/query/db-now.ts +6 -0
  302. package/src/query/query-type.test.ts +2 -2
  303. package/src/query/serialize/create-sql-serializer.ts +7 -2
  304. package/src/query/serialize/dialect/mysql-serializer.ts +12 -4
  305. package/src/query/serialize/dialect/postgres-serializer.ts +34 -4
  306. package/src/query/serialize/dialect/sqlite-serializer.test.ts +51 -1
  307. package/src/query/serialize/dialect/sqlite-serializer.ts +92 -9
  308. package/src/query/serialize/sql-serializer.ts +4 -4
  309. package/src/query/simple-query-interface.ts +5 -0
  310. package/src/query/unit-of-work/execute-unit-of-work.test.ts +1512 -1458
  311. package/src/query/unit-of-work/execute-unit-of-work.ts +1708 -596
  312. package/src/query/unit-of-work/tx-builder.test.ts +1041 -0
  313. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +32 -32
  314. package/src/query/unit-of-work/unit-of-work-types.test.ts +1 -1
  315. package/src/query/unit-of-work/unit-of-work.test.ts +231 -36
  316. package/src/query/unit-of-work/unit-of-work.ts +229 -31
  317. package/src/query/value-decoding.test.ts +13 -2
  318. package/src/query/value-decoding.ts +17 -4
  319. package/src/query/value-encoding.test.ts +85 -2
  320. package/src/query/value-encoding.ts +56 -6
  321. package/src/schema/create.test.ts +129 -42
  322. package/src/schema/create.ts +187 -47
  323. package/src/schema/generate-id.test.ts +57 -0
  324. package/src/schema/generate-id.ts +38 -0
  325. package/src/schema/serialize.test.ts +14 -2
  326. package/src/schema/type-conversion/create-sql-type-mapper.ts +7 -2
  327. package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
  328. package/src/schema/type-conversion/type-mapping.test.ts +25 -1
  329. package/src/schema/validator.test.ts +197 -0
  330. package/src/schema/validator.ts +231 -0
  331. package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +179 -129
  332. package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +143 -93
  333. package/src/schema-output/prisma.test.ts +536 -0
  334. package/src/schema-output/prisma.ts +573 -0
  335. package/src/util/default-database-adapter.ts +106 -0
  336. package/src/with-database.ts +22 -3
  337. package/tsdown.config.ts +6 -4
  338. package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
  339. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
  340. package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
  341. package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
  342. package/dist/adapters/drizzle/generate.d.ts +0 -30
  343. package/dist/adapters/drizzle/generate.d.ts.map +0 -1
  344. package/dist/adapters/drizzle/generate.js.map +0 -1
  345. package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
  346. package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
  347. package/dist/adapters/kysely/kysely-adapter.js +0 -17
  348. package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
  349. package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
  350. package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
  351. package/dist/adapters/shared/table-name-mapper.js +0 -43
  352. package/dist/adapters/shared/table-name-mapper.js.map +0 -1
  353. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
  354. package/dist/schema-generator/schema-generator.d.ts +0 -15
  355. package/dist/schema-generator/schema-generator.d.ts.map +0 -1
  356. package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
  357. package/src/adapters/kysely/kysely-adapter.ts +0 -27
  358. package/src/adapters/shared/table-name-mapper.ts +0 -50
  359. package/src/schema-generator/schema-generator.ts +0 -12
  360. package/src/shared/config.ts +0 -10
  361. package/src/shared/connection-pool.ts +0 -24
  362. package/src/shared/prisma.ts +0 -45
@@ -0,0 +1,277 @@
1
+ import { RequestContextStorage } from "@fragno-dev/core/internal/request-context-storage";
2
+ import {
3
+ fragnoDatabaseAdapterNameFakeSymbol,
4
+ fragnoDatabaseAdapterVersionFakeSymbol,
5
+ type DatabaseAdapter,
6
+ type DatabaseContextStorage,
7
+ } from "../adapters";
8
+ import type { AnySchema, AnyTable, FragnoId } from "../../schema/create";
9
+ import type { SimpleQueryInterface, TableToUpdateValues } from "../../query/simple-query-interface";
10
+ import {
11
+ resolveInMemoryAdapterOptions,
12
+ type InMemoryAdapterOptions,
13
+ type ResolvedInMemoryAdapterOptions,
14
+ } from "./options";
15
+ import { createInMemoryStore, ensureNamespaceStore } from "./store";
16
+ import {
17
+ createInMemoryUowCompiler,
18
+ createInMemoryUowExecutor,
19
+ InMemoryUowDecoder,
20
+ } from "./in-memory-uow";
21
+ import { UnitOfWork, type UnitOfWorkConfig } from "../../query/unit-of-work/unit-of-work";
22
+ import type { CursorResult } from "../../query/cursor";
23
+ import {
24
+ createNamingResolver,
25
+ suffixNamingStrategy,
26
+ type SqlNamingStrategy,
27
+ } from "../../naming/sql-naming";
28
+
29
+ class UpdateManySpecialBuilder<TTable extends AnyTable> {
30
+ #indexName?: string;
31
+ #condition?: unknown;
32
+ #setValues?: TableToUpdateValues<TTable>;
33
+
34
+ whereIndex(indexName: string, condition?: unknown): this {
35
+ this.#indexName = indexName;
36
+ this.#condition = condition;
37
+ return this;
38
+ }
39
+
40
+ set(values: TableToUpdateValues<TTable>): this {
41
+ this.#setValues = values;
42
+ return this;
43
+ }
44
+
45
+ getConfig() {
46
+ return {
47
+ indexName: this.#indexName,
48
+ condition: this.#condition,
49
+ setValues: this.#setValues,
50
+ };
51
+ }
52
+ }
53
+
54
+ const hasIdField = (record: unknown): record is { id: string | FragnoId } =>
55
+ record !== null && typeof record === "object" && "id" in record;
56
+
57
+ export type InMemoryUowConfig = UnitOfWorkConfig;
58
+
59
+ export class InMemoryAdapter implements DatabaseAdapter<InMemoryUowConfig> {
60
+ readonly options: ResolvedInMemoryAdapterOptions;
61
+ readonly namingStrategy: SqlNamingStrategy;
62
+
63
+ #contextStorage: RequestContextStorage<DatabaseContextStorage>;
64
+ #store = createInMemoryStore();
65
+ #schemaNamespaceMap = new WeakMap<AnySchema, string | null>();
66
+ #schemaByNamespace = new Map<string, { schema: AnySchema; namespace: string | null }>();
67
+
68
+ constructor(options: InMemoryAdapterOptions = {}) {
69
+ this.options = resolveInMemoryAdapterOptions(options);
70
+ this.namingStrategy = options.namingStrategy ?? suffixNamingStrategy;
71
+ this.#contextStorage = new RequestContextStorage();
72
+ }
73
+
74
+ get [fragnoDatabaseAdapterNameFakeSymbol](): string {
75
+ return "in-memory";
76
+ }
77
+
78
+ get [fragnoDatabaseAdapterVersionFakeSymbol](): number {
79
+ return 0;
80
+ }
81
+
82
+ get contextStorage(): RequestContextStorage<DatabaseContextStorage> {
83
+ return this.#contextStorage;
84
+ }
85
+
86
+ async getSchemaVersion(_namespace: string): Promise<string | undefined> {
87
+ return undefined;
88
+ }
89
+
90
+ async isConnectionHealthy(): Promise<boolean> {
91
+ return true;
92
+ }
93
+
94
+ async close(): Promise<void> {
95
+ return;
96
+ }
97
+
98
+ async reset(): Promise<void> {
99
+ this.#store.namespaces.clear();
100
+ for (const [namespaceKey, { schema, namespace }] of this.#schemaByNamespace) {
101
+ const resolver = createNamingResolver(schema, namespace, this.namingStrategy);
102
+ ensureNamespaceStore(this.#store, namespaceKey, schema, resolver);
103
+ }
104
+ }
105
+
106
+ createQueryEngine<T extends AnySchema>(
107
+ schema: T,
108
+ namespace: string | null,
109
+ ): SimpleQueryInterface<T, InMemoryUowConfig> {
110
+ this.#schemaNamespaceMap.set(schema, namespace);
111
+ const namespaceKey = namespace ?? schema.name;
112
+ this.#schemaByNamespace.set(namespaceKey, { schema, namespace });
113
+ const resolverFactory = (schemaForResolver: AnySchema, namespaceForResolver: string | null) =>
114
+ createNamingResolver(schemaForResolver, namespaceForResolver, this.namingStrategy);
115
+ const resolver = resolverFactory(schema, namespace);
116
+ ensureNamespaceStore(this.#store, namespaceKey, schema, resolver);
117
+
118
+ const compiler = createInMemoryUowCompiler();
119
+ const executor = createInMemoryUowExecutor(this.#store, this.options, resolverFactory);
120
+ const decoder = new InMemoryUowDecoder(resolverFactory);
121
+
122
+ const createUow = (opts?: { name?: string; config?: UnitOfWorkConfig }) =>
123
+ new UnitOfWork(
124
+ compiler,
125
+ executor,
126
+ decoder,
127
+ opts?.name,
128
+ opts?.config,
129
+ this.#schemaNamespaceMap,
130
+ ).forSchema(schema);
131
+
132
+ const queryEngine = {
133
+ now: async () => this.options.clock.now(),
134
+ async find(tableName, builderFn) {
135
+ const uow = createUow();
136
+ uow.find(tableName, builderFn);
137
+ const [result]: unknown[][] = await uow.executeRetrieve();
138
+ return result ?? [];
139
+ },
140
+
141
+ async findWithCursor(tableName, builderFn) {
142
+ const uow = createUow().findWithCursor(tableName, builderFn);
143
+ const [result] = await uow.executeRetrieve();
144
+ return result as CursorResult<unknown>;
145
+ },
146
+
147
+ async findFirst(tableName, builderFn) {
148
+ const uow = createUow();
149
+ if (builderFn) {
150
+ uow.find(tableName, (b) => {
151
+ builderFn(b);
152
+ return b.pageSize(1);
153
+ });
154
+ } else {
155
+ uow.find(tableName, (b) => b.whereIndex("primary").pageSize(1));
156
+ }
157
+ const [result]: unknown[][] = await uow.executeRetrieve();
158
+ return result?.[0] ?? null;
159
+ },
160
+
161
+ async create(tableName, values) {
162
+ const uow = createUow();
163
+ uow.create(tableName, values);
164
+ const { success } = await uow.executeMutations();
165
+ if (!success) {
166
+ throw new Error("Failed to create record");
167
+ }
168
+
169
+ const createdId = uow.getCreatedIds()[0];
170
+ if (!createdId) {
171
+ throw new Error("Failed to get created ID");
172
+ }
173
+ return createdId;
174
+ },
175
+
176
+ async createMany(tableName, valuesArray) {
177
+ const uow = createUow();
178
+ for (const values of valuesArray) {
179
+ uow.create(tableName, values);
180
+ }
181
+ const { success } = await uow.executeMutations();
182
+ if (!success) {
183
+ throw new Error("Failed to create records");
184
+ }
185
+ return uow.getCreatedIds();
186
+ },
187
+
188
+ async update(tableName, id, builderFn) {
189
+ const uow = createUow();
190
+ uow.update(tableName, id, builderFn);
191
+ const { success } = await uow.executeMutations();
192
+ if (!success) {
193
+ throw new Error("Failed to update record (version conflict or record not found)");
194
+ }
195
+ },
196
+
197
+ async updateMany(tableName, builderFn) {
198
+ const table = schema.tables[tableName];
199
+ if (!table) {
200
+ throw new Error(`Table ${tableName} not found in schema`);
201
+ }
202
+
203
+ const specialBuilder = new UpdateManySpecialBuilder<typeof table>();
204
+ builderFn(specialBuilder);
205
+ const { indexName, condition, setValues } = specialBuilder.getConfig();
206
+
207
+ if (!indexName) {
208
+ throw new Error("whereIndex() must be called in updateMany");
209
+ }
210
+ if (!setValues) {
211
+ throw new Error("set() must be called in updateMany");
212
+ }
213
+
214
+ const findUow = createUow();
215
+ findUow.find(tableName, (b) => {
216
+ if (condition !== undefined && condition !== null) {
217
+ return b.whereIndex(indexName as never, condition as never);
218
+ }
219
+ return b.whereIndex(indexName as never);
220
+ });
221
+ const [records]: unknown[][] = await findUow.executeRetrieve();
222
+
223
+ if (!records || records.length === 0) {
224
+ return;
225
+ }
226
+
227
+ const updateUow = createUow();
228
+ for (const record of records) {
229
+ if (!hasIdField(record)) {
230
+ throw new Error("Record missing id field");
231
+ }
232
+ updateUow.update(tableName, record.id, (b) => b.set(setValues).check());
233
+ }
234
+ const { success } = await updateUow.executeMutations();
235
+ if (!success) {
236
+ throw new Error("Failed to update records (version conflict)");
237
+ }
238
+ },
239
+
240
+ async delete(tableName, id, builderFn) {
241
+ const uow = createUow();
242
+ uow.delete(tableName, id, builderFn);
243
+ const { success } = await uow.executeMutations();
244
+ if (!success) {
245
+ throw new Error("Failed to delete record (version conflict or record not found)");
246
+ }
247
+ },
248
+
249
+ async deleteMany(tableName, builderFn) {
250
+ const findUow = createUow();
251
+ findUow.find(tableName, builderFn);
252
+ const [records]: unknown[][] = await findUow.executeRetrieve();
253
+
254
+ if (!records || records.length === 0) {
255
+ return;
256
+ }
257
+
258
+ const deleteUow = createUow();
259
+ for (const record of records) {
260
+ if (!hasIdField(record)) {
261
+ throw new Error("Record missing id field");
262
+ }
263
+ deleteUow.delete(tableName, record.id);
264
+ }
265
+ const { success } = await deleteUow.executeMutations();
266
+ if (!success) {
267
+ throw new Error("Failed to delete records (version conflict)");
268
+ }
269
+ },
270
+
271
+ createUnitOfWork(name, config) {
272
+ return createUow({ name, config });
273
+ },
274
+ } as SimpleQueryInterface<T, InMemoryUowConfig>;
275
+ return queryEngine;
276
+ }
277
+ }
@@ -0,0 +1,296 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { AnySchema } from "../../schema/create";
3
+ import { column, idColumn, schema, FragnoId, referenceColumn } from "../../schema/create";
4
+ import { UnitOfWork } from "../../query/unit-of-work/unit-of-work";
5
+ import { createInMemoryStore } from "./store";
6
+ import {
7
+ createInMemoryUowCompiler,
8
+ createInMemoryUowExecutor,
9
+ InMemoryUowDecoder,
10
+ } from "./in-memory-uow";
11
+ import { resolveInMemoryAdapterOptions, type InMemoryAdapterOptions } from "./options";
12
+
13
+ const testSchema = schema("test", (s) =>
14
+ s.addTable("users", (t) => t.addColumn("id", idColumn()).addColumn("name", column("string"))),
15
+ );
16
+
17
+ const fkSchema = schema("fk", (s) =>
18
+ s
19
+ .addTable("users", (t) => t.addColumn("id", idColumn()).addColumn("name", column("string")))
20
+ .addTable("posts", (t) =>
21
+ t
22
+ .addColumn("id", idColumn())
23
+ .addColumn("title", column("string"))
24
+ .addColumn("authorId", referenceColumn()),
25
+ )
26
+ .addReference("author", {
27
+ type: "one",
28
+ from: { table: "posts", column: "authorId" },
29
+ to: { table: "users", column: "id" },
30
+ }),
31
+ );
32
+
33
+ const createTestUowFactory = () => {
34
+ const store = createInMemoryStore();
35
+ const options = resolveInMemoryAdapterOptions({ idSeed: "seed" });
36
+ const compiler = createInMemoryUowCompiler();
37
+ const executor = createInMemoryUowExecutor(store, options);
38
+ const decoder = new InMemoryUowDecoder();
39
+
40
+ return () => new UnitOfWork(compiler, executor, decoder).forSchema(testSchema);
41
+ };
42
+
43
+ const createFkUowFactory = () => {
44
+ const store = createInMemoryStore();
45
+ const options = resolveInMemoryAdapterOptions({ idSeed: "seed" });
46
+ const compiler = createInMemoryUowCompiler();
47
+ const executor = createInMemoryUowExecutor(store, options);
48
+ const decoder = new InMemoryUowDecoder();
49
+
50
+ return () => new UnitOfWork(compiler, executor, decoder).forSchema(fkSchema);
51
+ };
52
+
53
+ const createUowFactoryWithOptions = <TSchema extends AnySchema>(
54
+ testSchemaToUse: TSchema,
55
+ optionsOverrides: InMemoryAdapterOptions = {},
56
+ ) => {
57
+ const store = createInMemoryStore();
58
+ const options = resolveInMemoryAdapterOptions({ idSeed: "seed", ...optionsOverrides });
59
+ const compiler = createInMemoryUowCompiler();
60
+ const executor = createInMemoryUowExecutor(store, options);
61
+ const decoder = new InMemoryUowDecoder();
62
+
63
+ return {
64
+ createUow: () => new UnitOfWork(compiler, executor, decoder).forSchema(testSchemaToUse),
65
+ options,
66
+ store,
67
+ };
68
+ };
69
+
70
+ describe("in-memory uow mutations", () => {
71
+ it("updates rows with version checks and increments version", async () => {
72
+ const createUow = createTestUowFactory();
73
+
74
+ const createMutation = createUow();
75
+ createMutation.create("users", { id: "user-1", name: "Jane" });
76
+ const createResult = await createMutation.executeMutations();
77
+ expect(createResult.success).toBe(true);
78
+
79
+ const createdId = createMutation.getCreatedIds()[0]!;
80
+ expect(createdId.version).toBe(0);
81
+
82
+ const updateMutation = createUow();
83
+ updateMutation.update("users", createdId, (b) => b.set({ name: "June" }).check());
84
+ const updateResult = await updateMutation.executeMutations();
85
+ expect(updateResult.success).toBe(true);
86
+
87
+ const findAfterUpdate = createUow();
88
+ findAfterUpdate.find("users", (b) => b.whereIndex("primary"));
89
+ const rows = (await findAfterUpdate.executeRetrieve()) as unknown[];
90
+ const updatedUser = (rows[0] as { id: FragnoId; name: string }[])[0];
91
+
92
+ expect(updatedUser?.name).toBe("June");
93
+ expect(updatedUser?.id).toBeInstanceOf(FragnoId);
94
+ expect(updatedUser?.id.version).toBe(1);
95
+
96
+ const staleUpdate = createUow();
97
+ staleUpdate.update("users", createdId, (b) => b.set({ name: "Nope" }).check());
98
+ const staleResult = await staleUpdate.executeMutations();
99
+ expect(staleResult.success).toBe(false);
100
+
101
+ const findAfterStale = createUow();
102
+ findAfterStale.find("users", (b) => b.whereIndex("primary"));
103
+ const staleRows = (await findAfterStale.executeRetrieve()) as unknown[];
104
+ const staleUser = (staleRows[0] as { id: FragnoId; name: string }[])[0];
105
+
106
+ expect(staleUser?.name).toBe("June");
107
+ expect(staleUser?.id.version).toBe(1);
108
+ });
109
+
110
+ it("checks and deletes rows with version enforcement", async () => {
111
+ const createUow = createTestUowFactory();
112
+
113
+ const createMutation = createUow();
114
+ createMutation.create("users", { id: "user-2", name: "Ari" });
115
+ const createResult = await createMutation.executeMutations();
116
+ expect(createResult.success).toBe(true);
117
+
118
+ const createdId = createMutation.getCreatedIds()[0]!;
119
+
120
+ const checkMutation = createUow();
121
+ checkMutation.check("users", createdId);
122
+ const checkResult = await checkMutation.executeMutations();
123
+ expect(checkResult.success).toBe(true);
124
+
125
+ const wrongVersionId = new FragnoId({
126
+ externalId: createdId.externalId,
127
+ internalId: createdId.internalId,
128
+ version: createdId.version + 1,
129
+ });
130
+
131
+ const badCheck = createUow();
132
+ badCheck.check("users", wrongVersionId);
133
+ const badCheckResult = await badCheck.executeMutations();
134
+ expect(badCheckResult.success).toBe(false);
135
+
136
+ const badDelete = createUow();
137
+ badDelete.delete("users", wrongVersionId, (b) => b.check());
138
+ const badDeleteResult = await badDelete.executeMutations();
139
+ expect(badDeleteResult.success).toBe(false);
140
+
141
+ const findBeforeDelete = createUow();
142
+ findBeforeDelete.find("users", (b) => b.whereIndex("primary"));
143
+ const beforeDeleteRows = (await findBeforeDelete.executeRetrieve()) as unknown[];
144
+ expect(beforeDeleteRows[0]).toHaveLength(1);
145
+
146
+ const deleteMutation = createUow();
147
+ deleteMutation.delete("users", createdId, (b) => b.check());
148
+ const deleteResult = await deleteMutation.executeMutations();
149
+ expect(deleteResult.success).toBe(true);
150
+
151
+ const findAfterDelete = createUow();
152
+ findAfterDelete.find("users", (b) => b.whereIndex("primary"));
153
+ const afterDeleteRows = (await findAfterDelete.executeRetrieve()) as unknown[];
154
+ expect(afterDeleteRows[0]).toHaveLength(0);
155
+ });
156
+
157
+ it("enforces foreign keys on create and update", async () => {
158
+ const createUow = createFkUowFactory();
159
+
160
+ const createUser = createUow();
161
+ createUser.create("users", { id: "user-1", name: "Ada" });
162
+ const createUserResult = await createUser.executeMutations();
163
+ expect(createUserResult.success).toBe(true);
164
+
165
+ const userId = createUser.getCreatedIds()[0]!;
166
+
167
+ const createPost = createUow();
168
+ createPost.create("posts", {
169
+ id: "post-1",
170
+ title: "Hello",
171
+ authorId: userId,
172
+ });
173
+ const createPostResult = await createPost.executeMutations();
174
+ expect(createPostResult.success).toBe(true);
175
+
176
+ const badCreate = createUow();
177
+ badCreate.create("posts", {
178
+ id: "post-2",
179
+ title: "Nope",
180
+ authorId: "missing-user",
181
+ });
182
+ await expect(badCreate.executeMutations()).rejects.toThrow("Foreign key constraint violation");
183
+
184
+ const postId = createPost.getCreatedIds()[0]!;
185
+ const badUpdate = createUow();
186
+ badUpdate.update("posts", postId, (b) => b.set({ authorId: "missing-user" }));
187
+ await expect(badUpdate.executeMutations()).rejects.toThrow("Foreign key constraint violation");
188
+ });
189
+
190
+ it("enforces foreign keys on delete", async () => {
191
+ const createUow = createFkUowFactory();
192
+
193
+ const createUser = createUow();
194
+ createUser.create("users", { id: "user-2", name: "Sam" });
195
+ const createUserResult = await createUser.executeMutations();
196
+ expect(createUserResult.success).toBe(true);
197
+
198
+ const userId = createUser.getCreatedIds()[0]!;
199
+
200
+ const createPost = createUow();
201
+ createPost.create("posts", {
202
+ id: "post-3",
203
+ title: "Dependent",
204
+ authorId: userId,
205
+ });
206
+ const createPostResult = await createPost.executeMutations();
207
+ expect(createPostResult.success).toBe(true);
208
+
209
+ const deleteUser = createUow();
210
+ deleteUser.delete("users", userId, (b) => b.check());
211
+ await expect(deleteUser.executeMutations()).rejects.toThrow("Foreign key constraint violation");
212
+ });
213
+
214
+ it("uses custom internal id generators when provided", async () => {
215
+ let current = 9n;
216
+ const internalIdGenerator = () => {
217
+ current += 1n;
218
+ return current;
219
+ };
220
+ const { createUow } = createUowFactoryWithOptions(testSchema, { internalIdGenerator });
221
+
222
+ const firstCreate = createUow();
223
+ firstCreate.create("users", { id: "user-1", name: "Ari" });
224
+ const firstResult = await firstCreate.executeMutations();
225
+ expect(firstResult.success).toBe(true);
226
+
227
+ const secondCreate = createUow();
228
+ secondCreate.create("users", { id: "user-2", name: "Bea" });
229
+ const secondResult = await secondCreate.executeMutations();
230
+ expect(secondResult.success).toBe(true);
231
+
232
+ const [firstId, secondId] = [firstCreate.getCreatedIds()[0]!, secondCreate.getCreatedIds()[0]!];
233
+
234
+ expect(firstId.internalId).toBe(10n);
235
+ expect(secondId.internalId).toBe(11n);
236
+ });
237
+
238
+ it("skips foreign key and unique constraints when enforceConstraints is false", async () => {
239
+ const { createUow } = createUowFactoryWithOptions(testSchema, {
240
+ enforceConstraints: false,
241
+ });
242
+ const { createUow: createFkUow } = createUowFactoryWithOptions(fkSchema, {
243
+ enforceConstraints: false,
244
+ });
245
+
246
+ const createUser = createUow();
247
+ createUser.create("users", { id: "dup-id", name: "First" });
248
+ const firstResult = await createUser.executeMutations();
249
+ expect(firstResult.success).toBe(true);
250
+
251
+ const createUserDup = createUow();
252
+ createUserDup.create("users", { id: "dup-id", name: "Second" });
253
+ const secondResult = await createUserDup.executeMutations();
254
+ expect(secondResult.success).toBe(true);
255
+
256
+ const findDupes = createUow();
257
+ findDupes.find("users", (b) => b.whereIndex("primary"));
258
+ const dupeRows = (await findDupes.executeRetrieve()) as unknown[];
259
+ expect(dupeRows[0]).toHaveLength(2);
260
+
261
+ const createPost = createFkUow();
262
+ createPost.create("posts", {
263
+ id: "post-1",
264
+ title: "No author",
265
+ authorId: "missing-user",
266
+ });
267
+ const createPostResult = await createPost.executeMutations();
268
+ expect(createPostResult.success).toBe(true);
269
+
270
+ const createdPostId = createPost.getCreatedIds()[0]!;
271
+ const updatePost = createFkUow();
272
+ updatePost.update("posts", createdPostId, (b) => b.set({ authorId: "still-missing" }));
273
+ const updatePostResult = await updatePost.executeMutations();
274
+ expect(updatePostResult.success).toBe(true);
275
+
276
+ const createUserForDelete = createFkUow();
277
+ createUserForDelete.create("users", { id: "user-3", name: "Sam" });
278
+ const createUserForDeleteResult = await createUserForDelete.executeMutations();
279
+ expect(createUserForDeleteResult.success).toBe(true);
280
+
281
+ const userId = createUserForDelete.getCreatedIds()[0]!;
282
+ const createPostForDelete = createFkUow();
283
+ createPostForDelete.create("posts", {
284
+ id: "post-2",
285
+ title: "Dependent",
286
+ authorId: userId,
287
+ });
288
+ const createPostForDeleteResult = await createPostForDelete.executeMutations();
289
+ expect(createPostForDeleteResult.success).toBe(true);
290
+
291
+ const deleteUser = createFkUow();
292
+ deleteUser.delete("users", userId, (b) => b.check());
293
+ const deleteResult = await deleteUser.executeMutations();
294
+ expect(deleteResult.success).toBe(true);
295
+ });
296
+ });
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { column, idColumn, referenceColumn, schema, type AnySchema } from "../../schema/create";
3
+ import { UnitOfWork, type RetrievalOperation } from "../../query/unit-of-work/unit-of-work";
4
+ import { createInMemoryStore } from "./store";
5
+ import {
6
+ createInMemoryUowCompiler,
7
+ createInMemoryUowExecutor,
8
+ InMemoryUowDecoder,
9
+ } from "./in-memory-uow";
10
+ import { resolveInMemoryAdapterOptions } from "./options";
11
+ import type { CompiledJoin } from "../../query/orm/orm";
12
+
13
+ const joinSchema = schema("join", (s) =>
14
+ s
15
+ .addTable("users", (t) =>
16
+ t
17
+ .addColumn("id", idColumn())
18
+ .addColumn("name", column("string"))
19
+ .addColumn("email", column("string"))
20
+ .createIndex("idx_users_name", ["name"]),
21
+ )
22
+ .addTable("posts", (t) =>
23
+ t
24
+ .addColumn("id", idColumn())
25
+ .addColumn("title", column("string"))
26
+ .addColumn("authorId", referenceColumn()),
27
+ )
28
+ .addReference("author", {
29
+ type: "one",
30
+ from: { table: "posts", column: "authorId" },
31
+ to: { table: "users", column: "id" },
32
+ }),
33
+ );
34
+
35
+ const createHarness = () => {
36
+ const store = createInMemoryStore();
37
+ const options = resolveInMemoryAdapterOptions({ idSeed: "seed" });
38
+ const compiler = createInMemoryUowCompiler();
39
+ const executor = createInMemoryUowExecutor(store, options);
40
+ const decoder = new InMemoryUowDecoder();
41
+
42
+ return {
43
+ createUow: () => new UnitOfWork(compiler, executor, decoder).forSchema(joinSchema),
44
+ executor,
45
+ };
46
+ };
47
+
48
+ describe("in-memory uow retrieval", () => {
49
+ it("throws when joins use orderBy instead of orderByIndex", async () => {
50
+ const { createUow, executor } = createHarness();
51
+
52
+ const createData = createUow();
53
+ createData.create("users", {
54
+ id: "user-1",
55
+ name: "Ada",
56
+ email: "ada@example.com",
57
+ });
58
+ createData.create("posts", {
59
+ id: "post-1",
60
+ title: "Hello",
61
+ authorId: "user-1",
62
+ });
63
+ await createData.executeMutations();
64
+
65
+ const relation = joinSchema.tables.posts.relations.author;
66
+ const join: CompiledJoin = {
67
+ relation,
68
+ options: {
69
+ select: true,
70
+ where: undefined,
71
+ orderBy: [[joinSchema.tables.users.columns.email, "asc"]],
72
+ join: undefined,
73
+ limit: undefined,
74
+ },
75
+ };
76
+
77
+ const op = {
78
+ type: "find",
79
+ schema: joinSchema,
80
+ table: joinSchema.tables.posts,
81
+ indexName: "_primary",
82
+ options: {
83
+ useIndex: "_primary",
84
+ select: true,
85
+ where: undefined,
86
+ orderByIndex: undefined,
87
+ after: undefined,
88
+ before: undefined,
89
+ pageSize: undefined,
90
+ joins: [join],
91
+ },
92
+ } satisfies RetrievalOperation<typeof joinSchema>;
93
+
94
+ const opForExecutor = op as unknown as RetrievalOperation<AnySchema>;
95
+
96
+ await expect(executor.executeRetrievalPhase([opForExecutor])).rejects.toThrow(
97
+ 'In-memory adapter only supports orderByIndex; received orderBy on table "users".',
98
+ );
99
+ });
100
+ });