@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,137 @@
1
+ import type { AnyTable, FragnoId } from "../schema/create";
2
+
3
+ export type OutboxConfig = {
4
+ enabled: boolean;
5
+ };
6
+
7
+ export type OutboxVersionstampStrategy =
8
+ | "update-returning"
9
+ | "insert-on-conflict-returning"
10
+ | "insert-on-duplicate-last-insert-id";
11
+
12
+ export type OutboxPayload = {
13
+ version: 1;
14
+ mutations: OutboxMutation[];
15
+ };
16
+
17
+ export type OutboxMutation =
18
+ | {
19
+ op: "create";
20
+ schema: string;
21
+ namespace?: string;
22
+ table: string;
23
+ externalId: string;
24
+ versionstamp: string;
25
+ values: Record<string, unknown>;
26
+ }
27
+ | {
28
+ op: "update";
29
+ schema: string;
30
+ namespace?: string;
31
+ table: string;
32
+ externalId: string;
33
+ versionstamp: string;
34
+ set: Record<string, unknown>;
35
+ checkVersion?: number;
36
+ }
37
+ | {
38
+ op: "delete";
39
+ schema: string;
40
+ namespace?: string;
41
+ table: string;
42
+ externalId: string;
43
+ versionstamp: string;
44
+ checkVersion?: number;
45
+ };
46
+
47
+ export type OutboxRefMap = Record<string, string>;
48
+
49
+ export type OutboxPayloadSerialized = {
50
+ json: unknown;
51
+ meta?: Record<string, unknown>;
52
+ };
53
+
54
+ export type OutboxEntry = {
55
+ id: FragnoId;
56
+ versionstamp: string;
57
+ uowId: string;
58
+ payload: OutboxPayloadSerialized;
59
+ refMap?: OutboxRefMap;
60
+ createdAt: Date;
61
+ };
62
+
63
+ export type OutboxRefLookup = {
64
+ key: string;
65
+ internalId: bigint | number;
66
+ table: AnyTable;
67
+ namespace?: string;
68
+ };
69
+
70
+ export function encodeVersionstamp(transactionVersion: bigint, userVersion: number): Uint8Array {
71
+ if (userVersion < 0 || userVersion > 0xffff) {
72
+ throw new Error(`Invalid outbox user version: ${userVersion}`);
73
+ }
74
+
75
+ const txBytes = bigintToBytes(transactionVersion, 10);
76
+ const userBytes = new Uint8Array(2);
77
+ userBytes[0] = (userVersion >> 8) & 0xff;
78
+ userBytes[1] = userVersion & 0xff;
79
+
80
+ const combined = new Uint8Array(12);
81
+ combined.set(txBytes, 0);
82
+ combined.set(userBytes, 10);
83
+
84
+ return combined;
85
+ }
86
+
87
+ export function versionstampToHex(bytes: Uint8Array): string {
88
+ let hex = "";
89
+ for (const byte of bytes) {
90
+ hex += byte.toString(16).padStart(2, "0");
91
+ }
92
+ return hex;
93
+ }
94
+
95
+ export function hexToVersionstamp(hex: string): Uint8Array {
96
+ if (hex.length % 2 !== 0) {
97
+ throw new Error(`Invalid versionstamp hex length: ${hex.length}`);
98
+ }
99
+
100
+ const bytes = new Uint8Array(hex.length / 2);
101
+ for (let i = 0; i < hex.length; i += 2) {
102
+ bytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
103
+ }
104
+ return bytes;
105
+ }
106
+
107
+ export function parseOutboxVersionValue(value: unknown): bigint {
108
+ if (typeof value === "bigint") {
109
+ return value;
110
+ }
111
+
112
+ if (typeof value === "number") {
113
+ return BigInt(value);
114
+ }
115
+
116
+ if (typeof value === "string") {
117
+ return BigInt(value);
118
+ }
119
+
120
+ throw new Error(`Invalid outbox version value: ${String(value)}`);
121
+ }
122
+
123
+ function bigintToBytes(value: bigint, length: number): Uint8Array {
124
+ const bytes = new Uint8Array(length);
125
+ let remaining = value;
126
+
127
+ for (let i = length - 1; i >= 0; i -= 1) {
128
+ bytes[i] = Number(remaining & 0xffn);
129
+ remaining >>= 8n;
130
+ }
131
+
132
+ if (remaining !== 0n) {
133
+ throw new Error(`Outbox version ${value} exceeds ${length * 8} bits`);
134
+ }
135
+
136
+ return bytes;
137
+ }
@@ -1,6 +1,11 @@
1
1
  import type { AnyColumn } from "../schema/create";
2
2
  import { createId } from "../id";
3
3
 
4
+ export type RuntimeDefaultContext = {
5
+ now?: () => Date;
6
+ createId?: () => string;
7
+ };
8
+
4
9
  /**
5
10
  * Generate a runtime default value for a column that has defaultTo$()
6
11
  *
@@ -12,7 +17,10 @@ import { createId } from "../id";
12
17
  *
13
18
  * @internal
14
19
  */
15
- export function generateRuntimeDefault(column: AnyColumn): unknown {
20
+ export function generateRuntimeDefault(
21
+ column: AnyColumn,
22
+ context: RuntimeDefaultContext = {},
23
+ ): unknown {
16
24
  // Check if column has a default value configuration
17
25
  if (!column.default) {
18
26
  return undefined;
@@ -34,11 +42,11 @@ export function generateRuntimeDefault(column: AnyColumn): unknown {
34
42
  const runtime = column.default.runtime;
35
43
 
36
44
  if (runtime === "cuid") {
37
- return createId();
45
+ return (context.createId ?? createId)();
38
46
  }
39
47
 
40
48
  if (runtime === "now") {
41
- return new Date();
49
+ return (context.now ?? (() => new Date()))();
42
50
  }
43
51
 
44
52
  if (typeof runtime === "function") {
@@ -47,3 +55,33 @@ export function generateRuntimeDefault(column: AnyColumn): unknown {
47
55
 
48
56
  return undefined;
49
57
  }
58
+
59
+ /**
60
+ * Generate a fallback value for database-level defaults.
61
+ *
62
+ * This is used by adapters that cannot rely on database DEFAULT constraints,
63
+ * such as the in-memory adapter.
64
+ *
65
+ * @internal
66
+ */
67
+ export function generateDatabaseDefault(
68
+ column: AnyColumn,
69
+ context: RuntimeDefaultContext = {},
70
+ ): unknown {
71
+ if (!column.default) {
72
+ return undefined;
73
+ }
74
+
75
+ if ("value" in column.default) {
76
+ return column.default.value;
77
+ }
78
+
79
+ if ("dbSpecial" in column.default) {
80
+ if (column.default.dbSpecial === "now") {
81
+ return (context.now ?? (() => new Date()))();
82
+ }
83
+ return undefined;
84
+ }
85
+
86
+ return undefined;
87
+ }
@@ -3,7 +3,7 @@ import { column, idColumn, schema } from "../schema/create";
3
3
  import { createBuilder, createIndexedBuilder } from "./condition-builder";
4
4
 
5
5
  describe("ConditionBuilder", () => {
6
- const testSchema = schema((s) =>
6
+ const testSchema = schema("test", (s) =>
7
7
  s
8
8
  .addTable("users", (t) =>
9
9
  t
@@ -11,7 +11,7 @@ describe("ConditionBuilder", () => {
11
11
  .addColumn("email", column("string"))
12
12
  .addColumn("name", column("string"))
13
13
  .addColumn("age", column("integer").nullable())
14
- .createIndex("_primary", ["id"], { unique: true })
14
+ .createIndex("idx_users_primary", ["id"], { unique: true })
15
15
  .createIndex("idx_email", ["email"], { unique: true })
16
16
  .createIndex("idx_name_age", ["name", "age"]),
17
17
  )
@@ -21,7 +21,7 @@ describe("ConditionBuilder", () => {
21
21
  .addColumn("title", column("string"))
22
22
  .addColumn("content", column("string"))
23
23
  .addColumn("published", column("bool"))
24
- .createIndex("_primary", ["id"], { unique: true })
24
+ .createIndex("idx_posts_primary", ["id"], { unique: true })
25
25
  .createIndex("idx_published", ["published"]),
26
26
  ),
27
27
  );
@@ -92,6 +92,39 @@ describe("Cursor utilities", () => {
92
92
  const decoded = decodeCursor(encoded);
93
93
  expect(decoded.indexValues).toEqual({});
94
94
  });
95
+
96
+ it("should throw when index values contain undefined", () => {
97
+ const cursor = new Cursor({
98
+ indexName: "_primary",
99
+ orderDirection: "asc",
100
+ pageSize: 10,
101
+ indexValues: { id: undefined },
102
+ });
103
+
104
+ expect(() => cursor.encode()).toThrow(/undefined/i);
105
+ });
106
+
107
+ it("should throw when index values contain BigInt", () => {
108
+ const cursor = new Cursor({
109
+ indexName: "_primary",
110
+ orderDirection: "asc",
111
+ pageSize: 10,
112
+ indexValues: { id: 1n },
113
+ });
114
+
115
+ expect(() => cursor.encode()).toThrow(/bigint/i);
116
+ });
117
+
118
+ it("should throw when index values contain non-finite numbers", () => {
119
+ const cursor = new Cursor({
120
+ indexName: "_primary",
121
+ orderDirection: "asc",
122
+ pageSize: 10,
123
+ indexValues: { id: Number.NaN },
124
+ });
125
+
126
+ expect(() => cursor.encode()).toThrow(/finite/i);
127
+ });
95
128
  });
96
129
 
97
130
  describe("decodeCursor error handling", () => {
@@ -145,11 +178,58 @@ describe("Cursor utilities", () => {
145
178
  const encoded = Buffer.from(JSON.stringify(invalidData), "utf-8").toString("base64");
146
179
  expect(() => decodeCursor(encoded)).toThrow(/invalid cursor/i);
147
180
  });
181
+
182
+ it("should throw error for array indexValues", () => {
183
+ const invalidData = {
184
+ v: 1,
185
+ indexValues: [],
186
+ indexName: "test",
187
+ orderDirection: "asc",
188
+ pageSize: 10,
189
+ };
190
+ const encoded = Buffer.from(JSON.stringify(invalidData), "utf-8").toString("base64");
191
+ expect(() => decodeCursor(encoded)).toThrow(/invalid cursor/i);
192
+ });
193
+
194
+ it("should throw error for non-string indexName", () => {
195
+ const invalidData = {
196
+ v: 1,
197
+ indexValues: { id: "test" },
198
+ indexName: 123,
199
+ orderDirection: "asc",
200
+ pageSize: 10,
201
+ };
202
+ const encoded = Buffer.from(JSON.stringify(invalidData), "utf-8").toString("base64");
203
+ expect(() => decodeCursor(encoded)).toThrow(/invalid cursor/i);
204
+ });
205
+
206
+ it("should throw error for invalid pageSize values", () => {
207
+ const invalidData = {
208
+ v: 1,
209
+ indexValues: { id: "test" },
210
+ indexName: "test",
211
+ orderDirection: "asc",
212
+ pageSize: 0,
213
+ };
214
+ const encoded = Buffer.from(JSON.stringify(invalidData), "utf-8").toString("base64");
215
+ expect(() => decodeCursor(encoded)).toThrow(/invalid cursor/i);
216
+ });
217
+
218
+ it("should throw error for missing cursor version", () => {
219
+ const invalidData = {
220
+ indexValues: { id: "test" },
221
+ indexName: "test",
222
+ orderDirection: "asc",
223
+ pageSize: 10,
224
+ };
225
+ const encoded = Buffer.from(JSON.stringify(invalidData), "utf-8").toString("base64");
226
+ expect(() => decodeCursor(encoded)).toThrow(/unsupported cursor version/i);
227
+ });
148
228
  });
149
229
 
150
230
  describe("createCursorFromRecord", () => {
151
231
  it("should create a cursor from a record with single column index", () => {
152
- const testSchema = schema((s) =>
232
+ const testSchema = schema("test", (s) =>
153
233
  s.addTable("users", (t) =>
154
234
  t.addColumn("id", idColumn()).addColumn("name", column("string")),
155
235
  ),
@@ -172,7 +252,7 @@ describe("Cursor utilities", () => {
172
252
  });
173
253
 
174
254
  it("should create a cursor from a record with multi-column index", () => {
175
- const testSchema = schema((s) =>
255
+ const testSchema = schema("test", (s) =>
176
256
  s.addTable("posts", (t) =>
177
257
  t
178
258
  .addColumn("id", idColumn())
@@ -207,8 +287,31 @@ describe("Cursor utilities", () => {
207
287
  });
208
288
  });
209
289
 
290
+ it("should throw when record is missing index columns", () => {
291
+ const testSchema = schema("test", (s) =>
292
+ s.addTable("posts", (t) =>
293
+ t
294
+ .addColumn("id", idColumn())
295
+ .addColumn("createdAt", column("integer"))
296
+ .createIndex("created", ["createdAt"]),
297
+ ),
298
+ );
299
+
300
+ const table = testSchema.tables.posts;
301
+ const record = { id: "post123" };
302
+ const indexColumns = table.indexes.created.columns;
303
+
304
+ expect(() =>
305
+ createCursorFromRecord(record, indexColumns, {
306
+ indexName: "created",
307
+ orderDirection: "asc",
308
+ pageSize: 10,
309
+ }),
310
+ ).toThrow(/missing value/i);
311
+ });
312
+
210
313
  it("should only include columns that are in the index", () => {
211
- const testSchema = schema((s) =>
314
+ const testSchema = schema("test", (s) =>
212
315
  s.addTable("users", (t) =>
213
316
  t
214
317
  .addColumn("id", idColumn())
@@ -235,7 +338,7 @@ describe("Cursor utilities", () => {
235
338
 
236
339
  describe("serializeCursorValues", () => {
237
340
  it("should serialize cursor values for database queries", () => {
238
- const testSchema = schema((s) =>
341
+ const testSchema = schema("test", (s) =>
239
342
  s.addTable("users", (t) =>
240
343
  t.addColumn("id", idColumn()).addColumn("age", column("integer")),
241
344
  ),
@@ -260,8 +363,8 @@ describe("Cursor utilities", () => {
260
363
  expect(serialized).toHaveProperty("age", 25);
261
364
  });
262
365
 
263
- it("should handle missing values in cursor data", () => {
264
- const testSchema = schema((s) =>
366
+ it("should throw when cursor data is missing index values", () => {
367
+ const testSchema = schema("test", (s) =>
265
368
  s.addTable("users", (t) =>
266
369
  t.addColumn("id", idColumn()).addColumn("name", column("string")),
267
370
  ),
@@ -276,20 +379,15 @@ describe("Cursor utilities", () => {
276
379
  });
277
380
 
278
381
  const indexColumns = [table.columns.id, table.columns.name];
279
- const serialized = serializeCursorValues(
280
- cursor,
281
- indexColumns,
282
- new NodePostgresDriverConfig(),
283
- );
284
-
285
- expect(serialized).toHaveProperty("id", "user123");
286
- expect(serialized).not.toHaveProperty("name");
382
+ expect(() =>
383
+ serializeCursorValues(cursor, indexColumns, new NodePostgresDriverConfig()),
384
+ ).toThrow(/missing values/i);
287
385
  });
288
386
  });
289
387
 
290
388
  describe("round-trip integration", () => {
291
389
  it("should successfully round-trip cursor data through encode/decode", () => {
292
- const testSchema = schema((s) =>
390
+ const testSchema = schema("test", (s) =>
293
391
  s.addTable("posts", (t) =>
294
392
  t
295
393
  .addColumn("id", idColumn())
@@ -332,7 +430,7 @@ describe("Cursor utilities", () => {
332
430
  });
333
431
 
334
432
  it("should handle Date objects in cursors for PostgreSQL", () => {
335
- const testSchema = schema((s) =>
433
+ const testSchema = schema("test", (s) =>
336
434
  s.addTable("posts", (t) =>
337
435
  t
338
436
  .addColumn("id", idColumn())
@@ -379,7 +477,7 @@ describe("Cursor utilities", () => {
379
477
  });
380
478
 
381
479
  it("should handle Date objects in cursors for SQLite", () => {
382
- const testSchema = schema((s) =>
480
+ const testSchema = schema("test", (s) =>
383
481
  s.addTable("posts", (t) =>
384
482
  t
385
483
  .addColumn("id", idColumn())
@@ -421,7 +519,7 @@ describe("Cursor utilities", () => {
421
519
  });
422
520
 
423
521
  it("should handle Date objects in cursors for MySQL", () => {
424
- const testSchema = schema((s) =>
522
+ const testSchema = schema("test", (s) =>
425
523
  s.addTable("posts", (t) =>
426
524
  t
427
525
  .addColumn("id", idColumn())
@@ -2,6 +2,7 @@ import type { AnyColumn } from "../schema/create";
2
2
  import { createSQLSerializer } from "./serialize/create-sql-serializer";
3
3
  import { resolveFragnoIdValue } from "./value-encoding";
4
4
  import type { DriverConfig } from "../adapters/generic-sql/driver-config";
5
+ import type { SQLiteStorageMode } from "../adapters/generic-sql/sqlite-storage";
5
6
 
6
7
  /**
7
8
  * Cursor object containing all information needed for pagination
@@ -56,6 +57,7 @@ export class Cursor {
56
57
  * Encode cursor to an opaque base64 string (safe to send to client)
57
58
  */
58
59
  encode(): string {
60
+ assertSerializableIndexValues(this.#indexValues);
59
61
  const data: CursorData = {
60
62
  v: 1,
61
63
  indexName: this.#indexName,
@@ -100,7 +102,12 @@ export interface CursorData {
100
102
  * Encode cursor data to a base64 string (internal)
101
103
  */
102
104
  function encodeCursorData(data: CursorData): string {
103
- const json = JSON.stringify(data);
105
+ let json: string;
106
+ try {
107
+ json = JSON.stringify(data);
108
+ } catch (error) {
109
+ throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
110
+ }
104
111
  // Use Buffer in Node.js or btoa in browsers
105
112
  if (typeof Buffer !== "undefined") {
106
113
  return Buffer.from(json, "utf-8").toString("base64");
@@ -129,32 +136,38 @@ export function decodeCursor(cursor: string): Cursor {
129
136
  json = atob(cursor);
130
137
  }
131
138
  const data = JSON.parse(json);
139
+ const record = data as Record<string, unknown>;
132
140
 
133
141
  // Validate structure
134
142
  if (
135
- !data ||
136
- typeof data !== "object" ||
137
- !data.indexValues ||
138
- typeof data.indexValues !== "object" ||
139
- typeof data.pageSize !== "number" ||
140
- !data.indexName ||
141
- !data.orderDirection ||
142
- (data.orderDirection !== "asc" && data.orderDirection !== "desc")
143
+ !isPlainObject(data) ||
144
+ !isPlainObject(record["indexValues"]) ||
145
+ typeof record["indexName"] !== "string" ||
146
+ record["indexName"].length === 0 ||
147
+ typeof record["orderDirection"] !== "string" ||
148
+ (record["orderDirection"] !== "asc" && record["orderDirection"] !== "desc") ||
149
+ typeof record["pageSize"] !== "number" ||
150
+ !Number.isFinite(record["pageSize"]) ||
151
+ !Number.isInteger(record["pageSize"]) ||
152
+ record["pageSize"] <= 0
143
153
  ) {
144
154
  throw new Error("Invalid cursor structure");
145
155
  }
146
156
 
147
157
  // Only support v1
148
- const version = typeof data.v === "number" ? data.v : 0;
158
+ if (typeof record["v"] !== "number") {
159
+ throw new Error("Unsupported cursor version: missing. Only v1 is supported.");
160
+ }
161
+ const version = record["v"];
149
162
  if (version !== 1) {
150
163
  throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);
151
164
  }
152
165
 
153
166
  return new Cursor({
154
- indexName: data.indexName,
155
- orderDirection: data.orderDirection,
156
- pageSize: data.pageSize,
157
- indexValues: data.indexValues,
167
+ indexName: record["indexName"],
168
+ orderDirection: record["orderDirection"],
169
+ pageSize: record["pageSize"],
170
+ indexValues: record["indexValues"],
158
171
  });
159
172
  } catch (error) {
160
173
  throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : "malformed data"}`);
@@ -194,7 +207,11 @@ export function createCursorFromRecord(
194
207
  const indexValues: Record<string, unknown> = {};
195
208
 
196
209
  for (const col of indexColumns) {
197
- indexValues[col.ormName] = record[col.ormName];
210
+ const value = record[col.name];
211
+ if (value === undefined) {
212
+ throw new Error(`Record is missing value for index column "${col.name}".`);
213
+ }
214
+ indexValues[col.name] = value;
198
215
  }
199
216
 
200
217
  return new Cursor({
@@ -218,6 +235,7 @@ export function createCursorFromRecord(
218
235
  * @param cursor - The cursor object
219
236
  * @param indexColumns - The columns that make up the index
220
237
  * @param driverConfig - The driver configuration
238
+ * @param sqliteStorageMode - Optional SQLite storage mode override
221
239
  * @returns Serialized values ready for database queries
222
240
  *
223
241
  * @example
@@ -233,23 +251,54 @@ export function serializeCursorValues(
233
251
  cursor: Cursor,
234
252
  indexColumns: AnyColumn[],
235
253
  driverConfig: DriverConfig,
254
+ sqliteStorageMode?: SQLiteStorageMode,
236
255
  ): Record<string, unknown> {
237
- const serializer = createSQLSerializer(driverConfig);
256
+ const serializer = createSQLSerializer(driverConfig, sqliteStorageMode);
238
257
  const serialized: Record<string, unknown> = {};
258
+ const missingColumns: string[] = [];
239
259
 
240
260
  for (const col of indexColumns) {
241
- const value = cursor.indexValues[col.ormName];
242
- if (value !== undefined) {
243
- // First deserialize from JSON format to application format
244
- // (e.g., "2025-11-07T09:36:57.959Z" string → Date object)
245
- const deserialized = serializer.deserialize(value, col);
246
- // Resolve FragnoId/FragnoReference to primitive values (if present)
247
- const resolved = resolveFragnoIdValue(deserialized, col);
248
- // Then serialize to database format
249
- // (e.g., Date → database driver format)
250
- serialized[col.ormName] = serializer.serialize(resolved, col);
261
+ const value = cursor.indexValues[col.name];
262
+ if (value === undefined) {
263
+ missingColumns.push(col.name);
264
+ continue;
251
265
  }
266
+ // First deserialize from JSON format to application format
267
+ // (e.g., "2025-11-07T09:36:57.959Z" string → Date object)
268
+ const deserialized = serializer.deserialize(value, col);
269
+ // Resolve FragnoId/FragnoReference to primitive values (if present)
270
+ const resolved = resolveFragnoIdValue(deserialized, col);
271
+ // Then serialize to database format
272
+ // (e.g., Date → database driver format)
273
+ serialized[col.name] = serializer.serialize(resolved, col);
274
+ }
275
+
276
+ if (missingColumns.length > 0) {
277
+ const suffix = cursor.indexName ? ` for index "${cursor.indexName}"` : "";
278
+ const columns = missingColumns.map((name) => `"${name}"`).join(", ");
279
+ const plural = missingColumns.length === 1 ? "" : "s";
280
+ throw new Error(`Cursor is missing values for index column${plural} ${columns}${suffix}.`);
252
281
  }
253
282
 
254
283
  return serialized;
255
284
  }
285
+
286
+ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
287
+ typeof value === "object" && value !== null && !Array.isArray(value);
288
+
289
+ const assertSerializableIndexValues = (values: Record<string, unknown>): void => {
290
+ for (const [key, value] of Object.entries(values)) {
291
+ if (value === undefined) {
292
+ throw new Error(`Cursor index value "${key}" is undefined.`);
293
+ }
294
+ if (typeof value === "number" && !Number.isFinite(value)) {
295
+ throw new Error(`Cursor index value "${key}" must be a finite number.`);
296
+ }
297
+ if (typeof value === "bigint") {
298
+ throw new Error(`Cursor index value "${key}" must not be a BigInt.`);
299
+ }
300
+ if (typeof value === "function" || typeof value === "symbol") {
301
+ throw new Error(`Cursor index value "${key}" is not JSON-serializable.`);
302
+ }
303
+ }
304
+ };
@@ -0,0 +1,6 @@
1
+ export type DbNow = { tag: "db-now" };
2
+
3
+ export const dbNow = (): DbNow => ({ tag: "db-now" });
4
+
5
+ export const isDbNow = (value: unknown): value is DbNow =>
6
+ typeof value === "object" && value !== null && (value as { tag?: string }).tag === "db-now";
@@ -13,7 +13,7 @@ import type { ConditionBuilder } from "./condition-builder";
13
13
 
14
14
  describe("query type tests", () => {
15
15
  // Create test schema
16
- const _testSchema = schema((s) => {
16
+ const _testSchema = schema("_test", (s) => {
17
17
  return s
18
18
  .addTable("users", (t) => {
19
19
  return t
@@ -337,7 +337,7 @@ describe("query type tests", () => {
337
337
  });
338
338
 
339
339
  describe("join", () => {
340
- const userSchema = schema((s) => {
340
+ const userSchema = schema("user", (s) => {
341
341
  return s
342
342
  .addTable("users", (t) => {
343
343
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -1,4 +1,5 @@
1
1
  import type { DriverConfig } from "../../adapters/generic-sql/driver-config";
2
+ import type { SQLiteStorageMode } from "../../adapters/generic-sql/sqlite-storage";
2
3
  import { SQLSerializer } from "./sql-serializer";
3
4
  import { SQLiteSerializer } from "./dialect/sqlite-serializer";
4
5
  import { PostgreSQLSerializer } from "./dialect/postgres-serializer";
@@ -14,9 +15,13 @@ export { SQLSerializer } from "./sql-serializer";
14
15
  * (PostgreSQL, MySQL, or SQLite).
15
16
  *
16
17
  * @param driverConfig - The driver configuration
18
+ * @param sqliteStorageMode - Optional SQLite storage mode override
17
19
  * @returns Dialect-specific SQLSerializer instance
18
20
  */
19
- export function createSQLSerializer(driverConfig: DriverConfig): SQLSerializer {
21
+ export function createSQLSerializer(
22
+ driverConfig: DriverConfig,
23
+ sqliteStorageMode?: SQLiteStorageMode,
24
+ ): SQLSerializer {
20
25
  // TODO: The serializers are pretty lenient in what they accept (lost of typeof checks), it may
21
26
  // be beneficial to implement serializers per DriverConfig, and be less lenient.
22
27
  switch (driverConfig.databaseType) {
@@ -25,7 +30,7 @@ export function createSQLSerializer(driverConfig: DriverConfig): SQLSerializer {
25
30
  case "mysql":
26
31
  return new MySQLSerializer(driverConfig);
27
32
  case "sqlite":
28
- return new SQLiteSerializer(driverConfig);
33
+ return new SQLiteSerializer(driverConfig, sqliteStorageMode);
29
34
  default: {
30
35
  const exhaustiveCheck: never = driverConfig.databaseType;
31
36
  throw new Error(`Unsupported database type: ${exhaustiveCheck}`);