@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
@@ -161,7 +161,7 @@ export type RetrievalOperation<
161
161
  | {
162
162
  type: "find";
163
163
  schema: TSchema;
164
- namespace?: string;
164
+ namespace?: string | null;
165
165
  table: TTable;
166
166
  indexName: string;
167
167
  options: FindOptions<TTable, SelectClause<TTable>>;
@@ -171,7 +171,7 @@ export type RetrievalOperation<
171
171
  | {
172
172
  type: "count";
173
173
  schema: TSchema;
174
- namespace?: string;
174
+ namespace?: string | null;
175
175
  table: TTable;
176
176
  indexName: string;
177
177
  options: Pick<FindOptions<TTable>, "where" | "useIndex">;
@@ -187,7 +187,7 @@ export type MutationOperation<
187
187
  | {
188
188
  type: "update";
189
189
  schema: TSchema;
190
- namespace?: string;
190
+ namespace?: string | null;
191
191
  table: TTable["name"];
192
192
  id: FragnoId | string;
193
193
  checkVersion: boolean;
@@ -196,7 +196,7 @@ export type MutationOperation<
196
196
  | {
197
197
  type: "create";
198
198
  schema: TSchema;
199
- namespace?: string;
199
+ namespace?: string | null;
200
200
  table: TTable["name"];
201
201
  values: TableToInsertValues<TTable>;
202
202
  generatedExternalId: string;
@@ -204,7 +204,7 @@ export type MutationOperation<
204
204
  | {
205
205
  type: "delete";
206
206
  schema: TSchema;
207
- namespace?: string;
207
+ namespace?: string | null;
208
208
  table: TTable["name"];
209
209
  id: FragnoId | string;
210
210
  checkVersion: boolean;
@@ -212,7 +212,7 @@ export type MutationOperation<
212
212
  | {
213
213
  type: "check";
214
214
  schema: TSchema;
215
- namespace?: string;
215
+ namespace?: string | null;
216
216
  table: TTable["name"];
217
217
  id: FragnoId;
218
218
  };
@@ -222,6 +222,14 @@ export type MutationOperation<
222
222
  */
223
223
  export interface CompiledMutation<TOutput> {
224
224
  query: TOutput;
225
+ /**
226
+ * Original mutation operation for execution metadata (e.g., outbox payloads).
227
+ */
228
+ operation?: MutationOperation<AnySchema>;
229
+ /**
230
+ * Idempotency key for the Unit of Work that produced this mutation.
231
+ */
232
+ uowId?: string;
225
233
  /**
226
234
  * The type of mutation operation (create, update, delete, or check).
227
235
  */
@@ -832,7 +840,7 @@ export function buildJoinIndexed<TTable extends AnyTable, TJoinOut>(
832
840
 
833
841
  builder[name] = (builderFn?: (b: JoinFindBuilder<AnyTable>) => JoinFindBuilder<AnyTable>) => {
834
842
  // Create join builder for this relation's table
835
- const joinBuilder = new JoinFindBuilder(relation.table.ormName, relation.table);
843
+ const joinBuilder = new JoinFindBuilder(relation.table.name, relation.table);
836
844
  if (builderFn) {
837
845
  builderFn(joinBuilder);
838
846
  }
@@ -933,7 +941,7 @@ export interface IUnitOfWork {
933
941
  ): TypedUnitOfWork<TOtherSchema, [], any, TOtherHooks>;
934
942
 
935
943
  // Schema registration (for cross-fragment operations like hooks)
936
- registerSchema(schema: AnySchema, namespace: string): void;
944
+ registerSchema(schema: AnySchema, namespace: string | null): void;
937
945
 
938
946
  // Hook triggering (schema-agnostic, string-based hook names)
939
947
  triggerHook(hookName: string, payload: unknown, options?: TriggerHookOptions): void;
@@ -951,11 +959,50 @@ export interface IUnitOfWork {
951
959
  export interface IUnitOfWorkRestricted
952
960
  extends Omit<IUnitOfWork, "executeRetrieve" | "executeMutations"> {}
953
961
 
962
+ export type UOWInstrumentationPhase =
963
+ | "beforeRetrieve"
964
+ | "afterRetrieve"
965
+ | "beforeMutate"
966
+ | "afterMutate";
967
+
968
+ export type UOWInstrumentationInjection =
969
+ | { type: "conflict"; reason?: string }
970
+ | { type: "error"; error: Error };
971
+
972
+ export type UOWInstrumentationContext = {
973
+ phase: UOWInstrumentationPhase;
974
+ uowName?: string;
975
+ idempotencyKey: string;
976
+ retrievalOpsCount: number;
977
+ mutationOpsCount: number;
978
+ uow: IUnitOfWork;
979
+ };
980
+
981
+ export type UOWInstrumentation = {
982
+ beforeRetrieve?: (
983
+ ctx: UOWInstrumentationContext,
984
+ ) => void | Promise<void> | UOWInstrumentationInjection;
985
+ afterRetrieve?: (
986
+ ctx: UOWInstrumentationContext,
987
+ ) => void | Promise<void> | UOWInstrumentationInjection;
988
+ beforeMutate?: (
989
+ ctx: UOWInstrumentationContext,
990
+ ) => void | Promise<void> | UOWInstrumentationInjection;
991
+ afterMutate?: (
992
+ ctx: UOWInstrumentationContext,
993
+ ) => void | Promise<void> | UOWInstrumentationInjection;
994
+ };
995
+
996
+ export type UOWInstrumentationFinalizer = {
997
+ afterRetrieve?: (ctx: UOWInstrumentationContext) => void | Promise<void>;
998
+ afterMutate?: (ctx: UOWInstrumentationContext) => void | Promise<void>;
999
+ };
1000
+
954
1001
  export function createUnitOfWork(
955
1002
  compiler: UOWCompiler<unknown>,
956
1003
  executor: UOWExecutor<unknown, unknown>,
957
1004
  decoder: UOWDecoder<unknown>,
958
- schemaNamespaceMap?: WeakMap<AnySchema, string>,
1005
+ schemaNamespaceMap?: WeakMap<AnySchema, string | null>,
959
1006
  name?: string,
960
1007
  ): UnitOfWork {
961
1008
  return new UnitOfWork(compiler, executor, decoder, name, undefined, schemaNamespaceMap);
@@ -965,6 +1012,16 @@ export interface UnitOfWorkConfig {
965
1012
  dryRun?: boolean;
966
1013
  onQuery?: (query: unknown) => void;
967
1014
  idempotencyKey?: string;
1015
+ instrumentation?: UOWInstrumentation;
1016
+ instrumentationFinalizer?: UOWInstrumentationFinalizer;
1017
+ }
1018
+
1019
+ function isUowInstrumentationInjection(value: unknown): value is UOWInstrumentationInjection {
1020
+ if (!value || typeof value !== "object") {
1021
+ return false;
1022
+ }
1023
+ const injection = value as UOWInstrumentationInjection;
1024
+ return injection.type === "conflict" || injection.type === "error";
968
1025
  }
969
1026
 
970
1027
  /**
@@ -1207,7 +1264,7 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1207
1264
  #compiler: UOWCompiler<unknown>;
1208
1265
  #executor: UOWExecutor<unknown, TRawInput>;
1209
1266
  #decoder: UOWDecoder<TRawInput>;
1210
- #schemaNamespaceMap: WeakMap<AnySchema, string>;
1267
+ #schemaNamespaceMap: WeakMap<AnySchema, string | null>;
1211
1268
 
1212
1269
  #retrievalResults?: unknown[];
1213
1270
  #createdInternalIds: (bigint | null)[] = [];
@@ -1232,7 +1289,7 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1232
1289
  decoder: UOWDecoder<TRawInput>,
1233
1290
  name?: string,
1234
1291
  config?: UnitOfWorkConfig,
1235
- schemaNamespaceMap?: WeakMap<AnySchema, string>,
1292
+ schemaNamespaceMap?: WeakMap<AnySchema, string | null>,
1236
1293
  ) {
1237
1294
  this.#compiler = compiler;
1238
1295
  this.#executor = executor;
@@ -1243,12 +1300,67 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1243
1300
  this.#idempotencyKey = config?.idempotencyKey ?? crypto.randomUUID();
1244
1301
  }
1245
1302
 
1303
+ #createInstrumentationContext(phase: UOWInstrumentationPhase): UOWInstrumentationContext {
1304
+ return {
1305
+ phase,
1306
+ uowName: this.#name,
1307
+ idempotencyKey: this.#idempotencyKey,
1308
+ retrievalOpsCount: this.#retrievalOps.length,
1309
+ mutationOpsCount: this.#mutationOps.length,
1310
+ uow: this,
1311
+ };
1312
+ }
1313
+
1314
+ async #runInstrumentation(
1315
+ phase: UOWInstrumentationPhase,
1316
+ ): Promise<UOWInstrumentationInjection | null> {
1317
+ const hook = this.#config?.instrumentation?.[phase];
1318
+ if (!hook) {
1319
+ return null;
1320
+ }
1321
+
1322
+ const result = await hook(this.#createInstrumentationContext(phase));
1323
+ if (isUowInstrumentationInjection(result)) {
1324
+ return result;
1325
+ }
1326
+
1327
+ return null;
1328
+ }
1329
+
1330
+ async #runInstrumentationFinalizer(phase: "afterRetrieve" | "afterMutate"): Promise<void> {
1331
+ const hook = this.#config?.instrumentationFinalizer?.[phase];
1332
+ if (!hook) {
1333
+ return;
1334
+ }
1335
+
1336
+ await hook(this.#createInstrumentationContext(phase));
1337
+ }
1338
+
1339
+ #handleRetrieveInjection(injection: UOWInstrumentationInjection): never {
1340
+ if (injection.type === "error") {
1341
+ throw injection.error;
1342
+ }
1343
+
1344
+ throw new Error(injection.reason ?? "Injected conflict");
1345
+ }
1346
+
1347
+ #handleMutationInjection(injection: UOWInstrumentationInjection): { success: false } | never {
1348
+ if (injection.type === "error") {
1349
+ throw injection.error;
1350
+ }
1351
+
1352
+ this.#state = "executed";
1353
+ this.#createdInternalIds.length = 0;
1354
+ this.#mutationPhaseDeferred.resolve();
1355
+ return { success: false };
1356
+ }
1357
+
1246
1358
  /**
1247
1359
  * Register a schema with its namespace for cross-fragment operations.
1248
1360
  * This is used for internal fragments like hooks that need to create
1249
1361
  * records in a different schema during the same transaction.
1250
1362
  */
1251
- registerSchema(schema: AnySchema, namespace: string): void {
1363
+ registerSchema(schema: AnySchema, namespace: string | null): void {
1252
1364
  this.#schemaNamespaceMap.set(schema, namespace);
1253
1365
  }
1254
1366
 
@@ -1426,13 +1538,25 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1426
1538
  );
1427
1539
  }
1428
1540
 
1541
+ let afterRan = false;
1542
+ let failed = false;
1429
1543
  try {
1430
1544
  // Wait for all children to signal readiness
1431
1545
  await this.#coordinator.retrievalReadinessPromise;
1432
1546
 
1547
+ const beforeInjection = await this.#runInstrumentation("beforeRetrieve");
1548
+ if (beforeInjection) {
1549
+ return this.#handleRetrieveInjection(beforeInjection);
1550
+ }
1551
+
1433
1552
  if (this.#retrievalOps.length === 0) {
1434
1553
  this.#state = "building-mutation";
1435
1554
  const emptyResults: unknown[] = [];
1555
+ afterRan = true;
1556
+ const afterInjection = await this.#runInstrumentation("afterRetrieve");
1557
+ if (afterInjection) {
1558
+ return this.#handleRetrieveInjection(afterInjection);
1559
+ }
1436
1560
  this.#retrievalPhaseDeferred.resolve(emptyResults);
1437
1561
  return emptyResults;
1438
1562
  }
@@ -1450,6 +1574,11 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1450
1574
  if (this.#config?.dryRun) {
1451
1575
  this.#state = "executed";
1452
1576
  const emptyResults: unknown[] = [];
1577
+ afterRan = true;
1578
+ const afterInjection = await this.#runInstrumentation("afterRetrieve");
1579
+ if (afterInjection) {
1580
+ return this.#handleRetrieveInjection(afterInjection);
1581
+ }
1453
1582
  this.#retrievalPhaseDeferred.resolve(emptyResults);
1454
1583
  return emptyResults;
1455
1584
  }
@@ -1462,12 +1591,33 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1462
1591
  this.#retrievalResults = results;
1463
1592
  this.#state = "building-mutation";
1464
1593
 
1594
+ afterRan = true;
1595
+ const afterInjection = await this.#runInstrumentation("afterRetrieve");
1596
+ if (afterInjection) {
1597
+ return this.#handleRetrieveInjection(afterInjection);
1598
+ }
1599
+
1465
1600
  this.#retrievalPhaseDeferred.resolve(this.#retrievalResults);
1466
1601
 
1467
1602
  return this.#retrievalResults;
1468
1603
  } catch (error) {
1469
1604
  this.#retrievalError = error instanceof Error ? error : new Error(String(error));
1605
+ failed = true;
1470
1606
  throw error;
1607
+ } finally {
1608
+ try {
1609
+ await this.#runInstrumentationFinalizer("afterRetrieve");
1610
+ } catch {
1611
+ // Ignore finalizer errors when unwinding failures.
1612
+ }
1613
+
1614
+ if (!afterRan && failed) {
1615
+ try {
1616
+ await this.#runInstrumentation("afterRetrieve");
1617
+ } catch {
1618
+ // Ignore after-retrieve hook errors when unwinding failures.
1619
+ }
1620
+ }
1471
1621
  }
1472
1622
  }
1473
1623
 
@@ -1484,15 +1634,23 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1484
1634
  throw new Error(`Cannot execute mutations from state ${this.#state}.`);
1485
1635
  }
1486
1636
 
1637
+ let afterRan = false;
1638
+ let failed = false;
1487
1639
  try {
1488
1640
  // Wait for all children to signal readiness
1489
1641
  await this.#coordinator.mutationReadinessPromise;
1490
1642
 
1643
+ const beforeInjection = await this.#runInstrumentation("beforeMutate");
1644
+ if (beforeInjection) {
1645
+ return this.#handleMutationInjection(beforeInjection);
1646
+ }
1647
+
1491
1648
  // Compile mutation operations using single compiler
1492
1649
  const mutationBatch: CompiledMutation<unknown>[] = [];
1493
1650
  for (const op of this.#mutationOps) {
1494
1651
  const compiled = this.#compiler.compileMutationOperation(op);
1495
1652
  if (compiled !== null) {
1653
+ compiled.uowId = this.#idempotencyKey;
1496
1654
  this.#config?.onQuery?.(compiled);
1497
1655
  mutationBatch.push(compiled);
1498
1656
  }
@@ -1500,6 +1658,11 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1500
1658
 
1501
1659
  if (this.#config?.dryRun) {
1502
1660
  this.#state = "executed";
1661
+ afterRan = true;
1662
+ const afterInjection = await this.#runInstrumentation("afterMutate");
1663
+ if (afterInjection) {
1664
+ return this.#handleMutationInjection(afterInjection);
1665
+ }
1503
1666
  this.#mutationPhaseDeferred.resolve();
1504
1667
  return {
1505
1668
  success: true,
@@ -1516,6 +1679,12 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1516
1679
  this.#createdInternalIds.push(...result.createdInternalIds);
1517
1680
  }
1518
1681
 
1682
+ afterRan = true;
1683
+ const afterInjection = await this.#runInstrumentation("afterMutate");
1684
+ if (afterInjection) {
1685
+ return this.#handleMutationInjection(afterInjection);
1686
+ }
1687
+
1519
1688
  // Resolve the mutation phase promise to unblock waiting service methods
1520
1689
  this.#mutationPhaseDeferred.resolve();
1521
1690
 
@@ -1524,7 +1693,22 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1524
1693
  };
1525
1694
  } catch (error) {
1526
1695
  this.#mutationError = error instanceof Error ? error : new Error(String(error));
1696
+ failed = true;
1527
1697
  throw error;
1698
+ } finally {
1699
+ try {
1700
+ await this.#runInstrumentationFinalizer("afterMutate");
1701
+ } catch {
1702
+ // Ignore finalizer errors when unwinding failures.
1703
+ }
1704
+
1705
+ if (!afterRan && failed) {
1706
+ try {
1707
+ await this.#runInstrumentation("afterMutate");
1708
+ } catch {
1709
+ // Ignore after-mutate hook errors when unwinding failures.
1710
+ }
1711
+ }
1528
1712
  }
1529
1713
  }
1530
1714
 
@@ -1623,6 +1807,7 @@ export class UnitOfWork<const TRawInput = unknown> implements IUnitOfWork {
1623
1807
  for (const op of this.#mutationOps) {
1624
1808
  const compiled = compiler.compileMutationOperation(op);
1625
1809
  if (compiled !== null) {
1810
+ compiled.uowId = this.#idempotencyKey;
1626
1811
  mutationBatch.push(compiled);
1627
1812
  }
1628
1813
  }
@@ -1648,12 +1833,12 @@ export class TypedUnitOfWork<
1648
1833
  > implements IUnitOfWork
1649
1834
  {
1650
1835
  #schema: TSchema;
1651
- #namespace?: string;
1836
+ #namespace?: string | null;
1652
1837
  #uow: UnitOfWork<TRawInput>;
1653
1838
  #operationIndices: number[] = [];
1654
1839
  #cachedRetrievalPhase?: Promise<TRetrievalResults>;
1655
1840
 
1656
- constructor(schema: TSchema, namespace: string | undefined, uow: UnitOfWork<TRawInput>) {
1841
+ constructor(schema: TSchema, namespace: string | null | undefined, uow: UnitOfWork<TRawInput>) {
1657
1842
  this.#schema = schema;
1658
1843
  this.#namespace = namespace;
1659
1844
  this.#uow = uow;
@@ -1746,7 +1931,7 @@ export class TypedUnitOfWork<
1746
1931
  return this.#uow.forSchema<TOtherSchema, TOtherHooks>(schema, hooks);
1747
1932
  }
1748
1933
 
1749
- registerSchema(schema: AnySchema, namespace: string): void {
1934
+ registerSchema(schema: AnySchema, namespace: string | null): void {
1750
1935
  this.#uow.registerSchema(schema, namespace);
1751
1936
  }
1752
1937
 
@@ -1989,7 +2174,7 @@ export class TypedUnitOfWork<
1989
2174
  let updatedValues = values;
1990
2175
 
1991
2176
  // Check if ID value is provided in values
1992
- const providedIdValue = (values as Record<string, unknown>)[idColumn.ormName];
2177
+ const providedIdValue = (values as Record<string, unknown>)[idColumn.name];
1993
2178
 
1994
2179
  if (providedIdValue !== undefined) {
1995
2180
  // Extract string from FragnoId or use string directly
@@ -2007,7 +2192,7 @@ export class TypedUnitOfWork<
2007
2192
  const generated = idColumn.generateDefaultValue();
2008
2193
  if (generated === undefined) {
2009
2194
  throw new Error(
2010
- `No ID value provided and ID column ${idColumn.ormName} has no default generator`,
2195
+ `No ID value provided and ID column ${idColumn.name} has no default generator`,
2011
2196
  );
2012
2197
  }
2013
2198
  externalId = generated as string;
@@ -2015,7 +2200,7 @@ export class TypedUnitOfWork<
2015
2200
  // Add the generated ID to values so it's used in the insert
2016
2201
  updatedValues = {
2017
2202
  ...values,
2018
- [idColumn.ormName]: externalId,
2203
+ [idColumn.name]: externalId,
2019
2204
  } as TableToInsertValues<TSchema["tables"][TableName]>;
2020
2205
  }
2021
2206
 
@@ -4,11 +4,12 @@ import { decodeResult } from "./value-decoding";
4
4
  import {
5
5
  SQLocalDriverConfig,
6
6
  NodePostgresDriverConfig,
7
+ PGLiteDriverConfig,
7
8
  MySQL2DriverConfig,
8
9
  } from "../adapters/generic-sql/driver-config";
9
10
 
10
11
  describe("decodeResult", () => {
11
- const testSchema = schema((s) => {
12
+ const testSchema = schema("test", (s) => {
12
13
  return s
13
14
  .addTable("users", (t) => {
14
15
  return t
@@ -40,6 +41,7 @@ describe("decodeResult", () => {
40
41
 
41
42
  const sqliteConfig = new SQLocalDriverConfig();
42
43
  const postgresqlConfig = new NodePostgresDriverConfig();
44
+ const pgliteConfig = new PGLiteDriverConfig();
43
45
  const mysqlConfig = new MySQL2DriverConfig();
44
46
 
45
47
  describe("basic decoding", () => {
@@ -105,6 +107,15 @@ describe("decodeResult", () => {
105
107
 
106
108
  expect(result["createdAt"]).toBe(date);
107
109
  });
110
+
111
+ it("should normalize Date values from pglite timestamp parsing", () => {
112
+ const localDate = new Date("2024-01-15T10:30:00");
113
+ const expected = new Date(localDate.getTime() - localDate.getTimezoneOffset() * 60_000);
114
+ const result = decodeResult({ createdAt: localDate }, usersTable, pgliteConfig);
115
+
116
+ assert.instanceOf(result["createdAt"], Date);
117
+ expect((result["createdAt"] as Date).toISOString()).toBe(expected.toISOString());
118
+ });
108
119
  });
109
120
 
110
121
  describe("nullable columns", () => {
@@ -206,7 +217,7 @@ describe("decodeResult", () => {
206
217
  });
207
218
 
208
219
  it("should handle multiple relations in same result", () => {
209
- const schemaWithMultipleRelations = schema((s) => {
220
+ const schemaWithMultipleRelations = schema("schemawithmultiplerelations", (s) => {
210
221
  return s
211
222
  .addTable("users", (t) => {
212
223
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -2,6 +2,8 @@ import type { AnyTable } from "../schema/create";
2
2
  import { createSQLSerializer } from "./serialize/create-sql-serializer";
3
3
  import { FragnoId, FragnoReference } from "../schema/create";
4
4
  import type { DriverConfig } from "../adapters/generic-sql/driver-config";
5
+ import type { SQLiteStorageMode } from "../adapters/generic-sql/sqlite-storage";
6
+ import type { NamingResolver } from "../naming/sql-naming";
5
7
 
6
8
  /**
7
9
  * Decodes a database result record to application format.
@@ -15,6 +17,7 @@ import type { DriverConfig } from "../adapters/generic-sql/driver-config";
15
17
  * @param result - The raw database result record
16
18
  * @param table - The table schema definition containing column and relation information
17
19
  * @param driverConfig - The driver configuration containing database type information
20
+ * @param sqliteStorageMode - Optional SQLite storage mode override
18
21
  * @returns A record in application format with deserialized values
19
22
  *
20
23
  * @example
@@ -31,11 +34,14 @@ export function decodeResult(
31
34
  result: Record<string, unknown>,
32
35
  table: AnyTable,
33
36
  driverConfig: DriverConfig,
37
+ sqliteStorageMode?: SQLiteStorageMode,
38
+ resolver?: NamingResolver,
34
39
  ): Record<string, unknown> {
35
- const serializer = createSQLSerializer(driverConfig);
40
+ const serializer = createSQLSerializer(driverConfig, sqliteStorageMode);
36
41
  const output: Record<string, unknown> = {};
37
42
  // First pass: collect all column values
38
43
  const columnValues: Record<string, unknown> = {};
44
+ const columnMap = resolver ? resolver.getColumnNameMap(table) : undefined;
39
45
 
40
46
  // Collect all relation data (including nested) keyed by relation name
41
47
  const relationData: Record<string, Record<string, unknown>> = {};
@@ -46,13 +52,14 @@ export function decodeResult(
46
52
 
47
53
  // Direct column (no colon)
48
54
  if (colonIndex === -1) {
49
- const col = table.columns[k];
55
+ const logicalName = columnMap?.[k] ?? k;
56
+ const col = table.columns[logicalName];
50
57
  if (!col) {
51
58
  continue;
52
59
  }
53
60
 
54
61
  // Store all column values (including hidden ones for FragnoId creation)
55
- columnValues[k] = serializer.deserialize(value, col);
62
+ columnValues[logicalName] = serializer.deserialize(value, col);
56
63
  continue;
57
64
  }
58
65
 
@@ -78,7 +85,13 @@ export function decodeResult(
78
85
  }
79
86
 
80
87
  // Recursively decode the relation data
81
- output[relationName] = decodeResult(relationData[relationName], relation.table, driverConfig);
88
+ output[relationName] = decodeResult(
89
+ relationData[relationName],
90
+ relation.table,
91
+ driverConfig,
92
+ sqliteStorageMode,
93
+ resolver,
94
+ );
82
95
  }
83
96
 
84
97
  // Second pass: create output with FragnoId objects where appropriate
@@ -7,10 +7,15 @@ import {
7
7
  schema,
8
8
  referenceColumn,
9
9
  } from "../schema/create";
10
- import { resolveFragnoIdValue, encodeValues, ReferenceSubquery } from "./value-encoding";
10
+ import {
11
+ resolveFragnoIdValue,
12
+ encodeValues,
13
+ encodeValuesWithDbDefaults,
14
+ ReferenceSubquery,
15
+ } from "./value-encoding";
11
16
 
12
17
  describe("encodeValues", () => {
13
- const testSchema = schema((s) => {
18
+ const testSchema = schema("test", (s) => {
14
19
  return s
15
20
  .addTable("users", (t) => {
16
21
  return t
@@ -128,6 +133,84 @@ describe("encodeValues", () => {
128
133
 
129
134
  expect(result["viewCount"]).toBe(100);
130
135
  });
136
+
137
+ it("should use injected runtime defaults when provided", () => {
138
+ const testDate = new Date("2024-02-01T00:00:00Z");
139
+ const testSchemaWithDefaults = schema("testschemawithdefaults", (s) =>
140
+ s.addTable("entries", (t) =>
141
+ t.addColumn("id", idColumn()).addColumn(
142
+ "createdAt",
143
+ column("timestamp").defaultTo$((b) => b.now()),
144
+ ),
145
+ ),
146
+ );
147
+ const entriesTable = testSchemaWithDefaults.tables.entries;
148
+
149
+ const result = encodeValues({}, entriesTable, true, {
150
+ createId: () => "entry_1",
151
+ now: () => testDate,
152
+ });
153
+
154
+ expect(result["id"]).toBe("entry_1");
155
+ expect(result["createdAt"]).toBe(testDate);
156
+ });
157
+ });
158
+
159
+ describe("database defaults in memory", () => {
160
+ it("should apply static and dbSpecial defaults when requested", () => {
161
+ const testDate = new Date("2024-03-01T00:00:00Z");
162
+ const testSchemaWithDbDefaults = schema("testschemawithdbdefaults", (s) =>
163
+ s.addTable("posts", (t) =>
164
+ t
165
+ .addColumn("id", idColumn())
166
+ .addColumn("title", column("string"))
167
+ .addColumn("status", column("string").defaultTo("draft"))
168
+ .addColumn(
169
+ "createdAt",
170
+ column("timestamp").defaultTo((b) => b.now()),
171
+ ),
172
+ ),
173
+ );
174
+ const postsTable = testSchemaWithDbDefaults.tables.posts;
175
+
176
+ const result = encodeValuesWithDbDefaults({ title: "Hello" }, postsTable, {
177
+ createId: () => "post_1",
178
+ now: () => testDate,
179
+ });
180
+
181
+ expect(result).toEqual({
182
+ id: "post_1",
183
+ title: "Hello",
184
+ _version: 0,
185
+ status: "draft",
186
+ createdAt: testDate,
187
+ });
188
+ });
189
+
190
+ it("should not override explicit values when applying db defaults", () => {
191
+ const testSchemaWithDbDefaults = schema("testschemawithdbdefaults", (s) =>
192
+ s.addTable("posts", (t) =>
193
+ t
194
+ .addColumn("id", idColumn())
195
+ .addColumn("title", column("string"))
196
+ .addColumn("status", column("string").defaultTo("draft")),
197
+ ),
198
+ );
199
+ const postsTable = testSchemaWithDbDefaults.tables.posts;
200
+
201
+ const result = encodeValuesWithDbDefaults(
202
+ { title: "Hello", status: "published" },
203
+ postsTable,
204
+ { createId: () => "post_2" },
205
+ );
206
+
207
+ expect(result).toEqual({
208
+ id: "post_2",
209
+ title: "Hello",
210
+ _version: 0,
211
+ status: "published",
212
+ });
213
+ });
131
214
  });
132
215
 
133
216
  describe("complete record encoding", () => {