@fragno-dev/db 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/.turbo/turbo-build.log +206 -140
  2. package/CHANGELOG.md +67 -0
  3. package/README.md +30 -9
  4. package/dist/adapters/adapters.d.ts +23 -21
  5. package/dist/adapters/adapters.d.ts.map +1 -1
  6. package/dist/adapters/adapters.js.map +1 -1
  7. package/dist/adapters/generic-sql/driver-config.d.ts +16 -1
  8. package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
  9. package/dist/adapters/generic-sql/driver-config.js +23 -1
  10. package/dist/adapters/generic-sql/driver-config.js.map +1 -1
  11. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +27 -9
  12. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
  13. package/dist/adapters/generic-sql/generic-sql-adapter.js +55 -16
  14. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
  15. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +129 -3
  16. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
  17. package/dist/adapters/generic-sql/migration/dialect/mysql.js +24 -5
  18. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
  19. package/dist/adapters/generic-sql/migration/dialect/postgres.js +6 -5
  20. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
  21. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +21 -10
  22. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
  23. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
  24. package/dist/adapters/generic-sql/migration/prepared-migrations.js +8 -8
  25. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
  26. package/dist/adapters/generic-sql/migration/sql-generator.js +74 -51
  27. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
  28. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +6 -5
  29. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
  30. package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
  31. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
  32. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +25 -17
  33. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
  34. package/dist/adapters/generic-sql/query/select-builder.js +5 -3
  35. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
  36. package/dist/adapters/generic-sql/query/sql-query-compiler.js +15 -12
  37. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
  38. package/dist/adapters/generic-sql/query/where-builder.js +38 -28
  39. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
  40. package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
  41. package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
  42. package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
  43. package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
  44. package/dist/adapters/generic-sql/uow-decoder.js +7 -3
  45. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
  46. package/dist/adapters/generic-sql/uow-encoder.js +28 -8
  47. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
  48. package/dist/adapters/in-memory/condition-evaluator.js +131 -0
  49. package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
  50. package/dist/adapters/in-memory/errors.d.ts +13 -0
  51. package/dist/adapters/in-memory/errors.d.ts.map +1 -0
  52. package/dist/adapters/in-memory/errors.js +23 -0
  53. package/dist/adapters/in-memory/errors.js.map +1 -0
  54. package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
  55. package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
  56. package/dist/adapters/in-memory/in-memory-adapter.js +176 -0
  57. package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
  58. package/dist/adapters/in-memory/in-memory-uow.js +648 -0
  59. package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
  60. package/dist/adapters/in-memory/index.d.ts +4 -0
  61. package/dist/adapters/in-memory/index.js +4 -0
  62. package/dist/adapters/in-memory/options.d.ts +28 -0
  63. package/dist/adapters/in-memory/options.d.ts.map +1 -0
  64. package/dist/adapters/in-memory/options.js +61 -0
  65. package/dist/adapters/in-memory/options.js.map +1 -0
  66. package/dist/adapters/in-memory/reference-resolution.js +26 -0
  67. package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
  68. package/dist/adapters/in-memory/sorted-array-index.js +129 -0
  69. package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
  70. package/dist/adapters/in-memory/store.js +71 -0
  71. package/dist/adapters/in-memory/store.js.map +1 -0
  72. package/dist/adapters/in-memory/value-comparison.js +28 -0
  73. package/dist/adapters/in-memory/value-comparison.js.map +1 -0
  74. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
  75. package/dist/adapters/shared/uow-operation-compiler.js +11 -11
  76. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
  77. package/dist/adapters/sql/index.d.ts +5 -0
  78. package/dist/adapters/sql/index.js +4 -0
  79. package/dist/db-fragment-definition-builder.d.ts +45 -96
  80. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  81. package/dist/db-fragment-definition-builder.js +121 -99
  82. package/dist/db-fragment-definition-builder.js.map +1 -1
  83. package/dist/dispatchers/cloudflare-do/index.d.ts +26 -0
  84. package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
  85. package/dist/dispatchers/cloudflare-do/index.js +63 -0
  86. package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
  87. package/dist/dispatchers/node/index.d.ts +17 -0
  88. package/dist/dispatchers/node/index.d.ts.map +1 -0
  89. package/dist/dispatchers/node/index.js +59 -0
  90. package/dist/dispatchers/node/index.js.map +1 -0
  91. package/dist/fragments/internal-fragment.d.ts +172 -9
  92. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  93. package/dist/fragments/internal-fragment.js +193 -74
  94. package/dist/fragments/internal-fragment.js.map +1 -1
  95. package/dist/fragments/internal-fragment.routes.js +29 -0
  96. package/dist/fragments/internal-fragment.routes.js.map +1 -0
  97. package/dist/fragments/internal-fragment.schema.d.ts +9 -0
  98. package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
  99. package/dist/fragments/internal-fragment.schema.js +22 -0
  100. package/dist/fragments/internal-fragment.schema.js.map +1 -0
  101. package/dist/hooks/durable-hooks-processor.d.ts +14 -0
  102. package/dist/hooks/durable-hooks-processor.d.ts.map +1 -0
  103. package/dist/hooks/durable-hooks-processor.js +32 -0
  104. package/dist/hooks/durable-hooks-processor.js.map +1 -0
  105. package/dist/hooks/hooks.d.ts +47 -4
  106. package/dist/hooks/hooks.d.ts.map +1 -1
  107. package/dist/hooks/hooks.js +106 -39
  108. package/dist/hooks/hooks.js.map +1 -1
  109. package/dist/migration-engine/auto-from-schema.js +14 -11
  110. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  111. package/dist/migration-engine/generation-engine.d.ts +16 -10
  112. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  113. package/dist/migration-engine/generation-engine.js +72 -33
  114. package/dist/migration-engine/generation-engine.js.map +1 -1
  115. package/dist/migration-engine/shared.js.map +1 -1
  116. package/dist/mod.d.ts +17 -10
  117. package/dist/mod.d.ts.map +1 -1
  118. package/dist/mod.js +14 -8
  119. package/dist/mod.js.map +1 -1
  120. package/dist/naming/sql-naming.d.ts +19 -0
  121. package/dist/naming/sql-naming.d.ts.map +1 -0
  122. package/dist/naming/sql-naming.js +116 -0
  123. package/dist/naming/sql-naming.js.map +1 -0
  124. package/dist/node_modules/.pnpm/{rou3@0.7.10 → rou3@0.7.12}/node_modules/rou3/dist/index.js +8 -5
  125. package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +1 -0
  126. package/dist/outbox/outbox-builder.js +156 -0
  127. package/dist/outbox/outbox-builder.js.map +1 -0
  128. package/dist/outbox/outbox.d.ts +52 -0
  129. package/dist/outbox/outbox.d.ts.map +1 -0
  130. package/dist/outbox/outbox.js +37 -0
  131. package/dist/outbox/outbox.js.map +1 -0
  132. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +3 -2
  133. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -1
  134. package/dist/packages/fragno/dist/api/fragment-instantiator.js +164 -20
  135. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -1
  136. package/dist/packages/fragno/dist/api/request-input-context.js +67 -0
  137. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -1
  138. package/dist/packages/fragno/dist/api/route.js +14 -1
  139. package/dist/packages/fragno/dist/api/route.js.map +1 -1
  140. package/dist/packages/fragno/dist/internal/trace-context.js +12 -0
  141. package/dist/packages/fragno/dist/internal/trace-context.js.map +1 -0
  142. package/dist/query/column-defaults.js +20 -4
  143. package/dist/query/column-defaults.js.map +1 -1
  144. package/dist/query/cursor.d.ts +3 -1
  145. package/dist/query/cursor.d.ts.map +1 -1
  146. package/dist/query/cursor.js +45 -14
  147. package/dist/query/cursor.js.map +1 -1
  148. package/dist/query/db-now.d.ts +8 -0
  149. package/dist/query/db-now.d.ts.map +1 -0
  150. package/dist/query/db-now.js +7 -0
  151. package/dist/query/db-now.js.map +1 -0
  152. package/dist/query/serialize/create-sql-serializer.js +3 -2
  153. package/dist/query/serialize/create-sql-serializer.js.map +1 -1
  154. package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
  155. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
  156. package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
  157. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
  158. package/dist/query/serialize/dialect/sqlite-serializer.js +55 -11
  159. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
  160. package/dist/query/serialize/sql-serializer.js +2 -2
  161. package/dist/query/serialize/sql-serializer.js.map +1 -1
  162. package/dist/query/simple-query-interface.d.ts +6 -1
  163. package/dist/query/simple-query-interface.d.ts.map +1 -1
  164. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +351 -100
  165. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  166. package/dist/query/unit-of-work/execute-unit-of-work.js +440 -267
  167. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  168. package/dist/query/unit-of-work/unit-of-work.d.ts +67 -22
  169. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  170. package/dist/query/unit-of-work/unit-of-work.js +110 -13
  171. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  172. package/dist/query/value-decoding.js +8 -5
  173. package/dist/query/value-decoding.js.map +1 -1
  174. package/dist/query/value-encoding.js +29 -9
  175. package/dist/query/value-encoding.js.map +1 -1
  176. package/dist/schema/create.d.ts +40 -14
  177. package/dist/schema/create.d.ts.map +1 -1
  178. package/dist/schema/create.js +82 -42
  179. package/dist/schema/create.js.map +1 -1
  180. package/dist/schema/generate-id.d.ts +20 -0
  181. package/dist/schema/generate-id.d.ts.map +1 -0
  182. package/dist/schema/generate-id.js +28 -0
  183. package/dist/schema/generate-id.js.map +1 -0
  184. package/dist/schema/type-conversion/create-sql-type-mapper.js +3 -2
  185. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
  186. package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
  187. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
  188. package/dist/schema/validator.d.ts +10 -0
  189. package/dist/schema/validator.d.ts.map +1 -0
  190. package/dist/schema/validator.js +123 -0
  191. package/dist/schema/validator.js.map +1 -0
  192. package/dist/schema-output/drizzle.d.ts +30 -0
  193. package/dist/schema-output/drizzle.d.ts.map +1 -0
  194. package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +82 -56
  195. package/dist/schema-output/drizzle.js.map +1 -0
  196. package/dist/schema-output/prisma.d.ts +17 -0
  197. package/dist/schema-output/prisma.d.ts.map +1 -0
  198. package/dist/schema-output/prisma.js +296 -0
  199. package/dist/schema-output/prisma.js.map +1 -0
  200. package/dist/util/default-database-adapter.js +61 -0
  201. package/dist/util/default-database-adapter.js.map +1 -0
  202. package/dist/with-database.d.ts +1 -1
  203. package/dist/with-database.d.ts.map +1 -1
  204. package/dist/with-database.js +12 -3
  205. package/dist/with-database.js.map +1 -1
  206. package/package.json +43 -28
  207. package/src/adapters/adapters.ts +30 -24
  208. package/src/adapters/drizzle/migrate-drizzle.test.ts +54 -33
  209. package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +599 -0
  210. package/src/adapters/drizzle/test-utils.ts +12 -8
  211. package/src/adapters/generic-sql/driver-config.ts +38 -0
  212. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -5
  213. package/src/adapters/generic-sql/generic-sql-adapter.ts +110 -24
  214. package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +54 -0
  215. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +231 -3
  216. package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +118 -0
  217. package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +26 -8
  218. package/src/adapters/generic-sql/migration/dialect/mysql.ts +46 -8
  219. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +25 -7
  220. package/src/adapters/generic-sql/migration/dialect/postgres.ts +8 -4
  221. package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +47 -8
  222. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +27 -12
  223. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +128 -39
  224. package/src/adapters/generic-sql/migration/prepared-migrations.ts +15 -8
  225. package/src/adapters/generic-sql/migration/sql-generator.ts +142 -65
  226. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +9 -6
  227. package/src/adapters/generic-sql/query/cursor-utils.test.ts +271 -0
  228. package/src/adapters/generic-sql/query/cursor-utils.ts +41 -6
  229. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +27 -27
  230. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +38 -24
  231. package/src/adapters/generic-sql/query/select-builder.test.ts +15 -11
  232. package/src/adapters/generic-sql/query/select-builder.ts +6 -2
  233. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +52 -2
  234. package/src/adapters/generic-sql/query/sql-query-compiler.ts +50 -15
  235. package/src/adapters/generic-sql/query/where-builder.test.ts +91 -17
  236. package/src/adapters/generic-sql/query/where-builder.ts +90 -38
  237. package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +6 -6
  238. package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +806 -0
  239. package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +11 -11
  240. package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +49 -35
  241. package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +48 -32
  242. package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +6 -6
  243. package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
  244. package/src/adapters/generic-sql/uow-decoder.test.ts +1 -1
  245. package/src/adapters/generic-sql/uow-decoder.ts +21 -3
  246. package/src/adapters/generic-sql/uow-encoder.test.ts +33 -2
  247. package/src/adapters/generic-sql/uow-encoder.ts +50 -11
  248. package/src/adapters/in-memory/condition-evaluator.test.ts +193 -0
  249. package/src/adapters/in-memory/condition-evaluator.ts +275 -0
  250. package/src/adapters/in-memory/errors.ts +20 -0
  251. package/src/adapters/in-memory/in-memory-adapter.ts +277 -0
  252. package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +296 -0
  253. package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +100 -0
  254. package/src/adapters/in-memory/in-memory-uow.ts +1348 -0
  255. package/src/adapters/in-memory/index.ts +3 -0
  256. package/src/adapters/in-memory/options.test.ts +41 -0
  257. package/src/adapters/in-memory/options.ts +87 -0
  258. package/src/adapters/in-memory/reference-resolution.test.ts +50 -0
  259. package/src/adapters/in-memory/reference-resolution.ts +67 -0
  260. package/src/adapters/in-memory/sorted-array-index.test.ts +123 -0
  261. package/src/adapters/in-memory/sorted-array-index.ts +228 -0
  262. package/src/adapters/in-memory/store.test.ts +68 -0
  263. package/src/adapters/in-memory/store.ts +145 -0
  264. package/src/adapters/in-memory/value-comparison.ts +53 -0
  265. package/src/adapters/in-memory/value-normalization.test.ts +57 -0
  266. package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1163 -0
  267. package/src/adapters/shared/from-unit-of-work-compiler.ts +3 -1
  268. package/src/adapters/shared/uow-operation-compiler.ts +26 -16
  269. package/src/adapters/sql/index.ts +12 -0
  270. package/src/db-fragment-definition-builder.test.ts +88 -54
  271. package/src/db-fragment-definition-builder.ts +201 -322
  272. package/src/db-fragment-instantiator.test.ts +169 -101
  273. package/src/db-fragment-integration.test.ts +301 -149
  274. package/src/dispatchers/cloudflare-do/index.test.ts +73 -0
  275. package/src/dispatchers/cloudflare-do/index.ts +104 -0
  276. package/src/dispatchers/node/index.test.ts +91 -0
  277. package/src/dispatchers/node/index.ts +87 -0
  278. package/src/fragments/internal-fragment.routes.ts +42 -0
  279. package/src/fragments/internal-fragment.schema.ts +51 -0
  280. package/src/fragments/internal-fragment.test.ts +730 -274
  281. package/src/fragments/internal-fragment.ts +447 -154
  282. package/src/hooks/durable-hooks-processor.test.ts +117 -0
  283. package/src/hooks/durable-hooks-processor.ts +67 -0
  284. package/src/hooks/hooks.test.ts +411 -259
  285. package/src/hooks/hooks.ts +265 -66
  286. package/src/migration-engine/auto-from-schema.test.ts +14 -14
  287. package/src/migration-engine/auto-from-schema.ts +5 -2
  288. package/src/migration-engine/create.test.ts +2 -2
  289. package/src/migration-engine/generation-engine.test.ts +229 -104
  290. package/src/migration-engine/generation-engine.ts +94 -64
  291. package/src/migration-engine/shared.ts +1 -0
  292. package/src/mod.ts +78 -30
  293. package/src/naming/sql-naming.ts +180 -0
  294. package/src/outbox/outbox-builder.ts +241 -0
  295. package/src/outbox/outbox.test.ts +253 -0
  296. package/src/outbox/outbox.ts +137 -0
  297. package/src/query/column-defaults.ts +41 -3
  298. package/src/query/condition-builder.test.ts +3 -3
  299. package/src/query/cursor.test.ts +116 -18
  300. package/src/query/cursor.ts +75 -26
  301. package/src/query/db-now.ts +6 -0
  302. package/src/query/query-type.test.ts +2 -2
  303. package/src/query/serialize/create-sql-serializer.ts +7 -2
  304. package/src/query/serialize/dialect/mysql-serializer.ts +12 -4
  305. package/src/query/serialize/dialect/postgres-serializer.ts +34 -4
  306. package/src/query/serialize/dialect/sqlite-serializer.test.ts +51 -1
  307. package/src/query/serialize/dialect/sqlite-serializer.ts +92 -9
  308. package/src/query/serialize/sql-serializer.ts +4 -4
  309. package/src/query/simple-query-interface.ts +5 -0
  310. package/src/query/unit-of-work/execute-unit-of-work.test.ts +1512 -1458
  311. package/src/query/unit-of-work/execute-unit-of-work.ts +1708 -596
  312. package/src/query/unit-of-work/tx-builder.test.ts +1041 -0
  313. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +32 -32
  314. package/src/query/unit-of-work/unit-of-work-types.test.ts +1 -1
  315. package/src/query/unit-of-work/unit-of-work.test.ts +231 -36
  316. package/src/query/unit-of-work/unit-of-work.ts +229 -31
  317. package/src/query/value-decoding.test.ts +13 -2
  318. package/src/query/value-decoding.ts +17 -4
  319. package/src/query/value-encoding.test.ts +85 -2
  320. package/src/query/value-encoding.ts +56 -6
  321. package/src/schema/create.test.ts +129 -42
  322. package/src/schema/create.ts +187 -47
  323. package/src/schema/generate-id.test.ts +57 -0
  324. package/src/schema/generate-id.ts +38 -0
  325. package/src/schema/serialize.test.ts +14 -2
  326. package/src/schema/type-conversion/create-sql-type-mapper.ts +7 -2
  327. package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
  328. package/src/schema/type-conversion/type-mapping.test.ts +25 -1
  329. package/src/schema/validator.test.ts +197 -0
  330. package/src/schema/validator.ts +231 -0
  331. package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +179 -129
  332. package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +143 -93
  333. package/src/schema-output/prisma.test.ts +536 -0
  334. package/src/schema-output/prisma.ts +573 -0
  335. package/src/util/default-database-adapter.ts +106 -0
  336. package/src/with-database.ts +22 -3
  337. package/tsdown.config.ts +6 -4
  338. package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
  339. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
  340. package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
  341. package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
  342. package/dist/adapters/drizzle/generate.d.ts +0 -30
  343. package/dist/adapters/drizzle/generate.d.ts.map +0 -1
  344. package/dist/adapters/drizzle/generate.js.map +0 -1
  345. package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
  346. package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
  347. package/dist/adapters/kysely/kysely-adapter.js +0 -17
  348. package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
  349. package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
  350. package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
  351. package/dist/adapters/shared/table-name-mapper.js +0 -43
  352. package/dist/adapters/shared/table-name-mapper.js.map +0 -1
  353. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
  354. package/dist/schema-generator/schema-generator.d.ts +0 -15
  355. package/dist/schema-generator/schema-generator.d.ts.map +0 -1
  356. package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
  357. package/src/adapters/kysely/kysely-adapter.ts +0 -27
  358. package/src/adapters/shared/table-name-mapper.ts +0 -50
  359. package/src/schema-generator/schema-generator.ts +0 -12
  360. package/src/shared/config.ts +0 -10
  361. package/src/shared/connection-pool.ts +0 -24
  362. package/src/shared/prisma.ts +0 -45
@@ -1,18 +1,28 @@
1
+ import type {
2
+ ExecuteTxOptions,
3
+ HandlerTxBuilder,
4
+ } from "../query/unit-of-work/execute-unit-of-work";
1
5
  import type { RetryPolicy } from "../query/unit-of-work/retry-policy";
2
6
  import { ExponentialBackoffRetryPolicy } from "../query/unit-of-work/retry-policy";
3
7
  import type { IUnitOfWork } from "../query/unit-of-work/unit-of-work";
4
8
  import type { InternalFragmentInstance } from "../fragments/internal-fragment";
9
+ import type { TxResult } from "../query/unit-of-work/execute-unit-of-work";
10
+ import type { FragnoId } from "../schema/create";
5
11
 
6
12
  /**
7
13
  * Context available in hook functions via `this`.
8
- * Contains the nonce for idempotency and database access.
14
+ * Contains the idempotency key for idempotency and database access.
9
15
  */
10
16
  export interface HookContext {
11
17
  /**
12
- * Unique nonce for this transaction.
18
+ * Unique idempotency key for this transaction.
13
19
  * Use this for idempotency checks in your hook implementation.
14
20
  */
15
- nonce: string;
21
+ idempotencyKey: string;
22
+ /**
23
+ * Create a handler transaction builder to run atomic operations.
24
+ */
25
+ handlerTx: HookHandlerTx;
16
26
  }
17
27
 
18
28
  /**
@@ -42,6 +52,11 @@ export interface TriggerHookOptions {
42
52
  * If not provided, uses the default retry policy.
43
53
  */
44
54
  retryPolicy?: RetryPolicy;
55
+ /**
56
+ * Absolute time for the first attempt. If in the future, the hook is
57
+ * scheduled for that time; if in the past, it runs immediately.
58
+ */
59
+ processAt?: Date;
45
60
  }
46
61
 
47
62
  /**
@@ -54,14 +69,88 @@ export interface TriggeredHook {
54
69
  options?: TriggerHookOptions;
55
70
  }
56
71
 
72
+ export type HookScheduler = {
73
+ schedule: () => Promise<number>;
74
+ drain: () => Promise<void>;
75
+ };
76
+
57
77
  /**
58
78
  * Configuration for hook processing.
59
79
  */
60
- export interface HookProcessorConfig {
61
- hooks: HooksMap;
80
+ export interface HookProcessorConfig<THooks extends HooksMap = HooksMap> {
81
+ hooks: THooks;
62
82
  namespace: string;
63
83
  internalFragment: InternalFragmentInstance;
84
+ handlerTx: HookHandlerTx;
85
+ /**
86
+ * Internal hook scheduler used to coordinate processing/drain.
87
+ */
88
+ scheduler?: HookScheduler;
64
89
  defaultRetryPolicy?: RetryPolicy;
90
+ /**
91
+ * Re-queue hooks that have been in `processing` for at least this many minutes.
92
+ * Use `false` to disable stuck-processing recovery entirely.
93
+ * Values <= 0 are treated as `false`.
94
+ *
95
+ * Default: 10 minutes.
96
+ */
97
+ stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
98
+ /**
99
+ * Called when stuck processing hooks are detected and re-queued.
100
+ * Invoked after the hooks are moved back to `pending`.
101
+ */
102
+ onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
103
+ }
104
+
105
+ export type StuckHookProcessingTimeoutMinutes = number | false;
106
+
107
+ export type StuckHookProcessingEvent = {
108
+ id: FragnoId;
109
+ hookName: string;
110
+ attempts: number;
111
+ maxAttempts: number;
112
+ lastAttemptAt: Date | null;
113
+ nextRetryAt: Date | null;
114
+ };
115
+
116
+ export type StuckHookProcessingInfo = {
117
+ namespace: string;
118
+ timeoutMinutes: number;
119
+ events: StuckHookProcessingEvent[];
120
+ };
121
+
122
+ export type DurableHooksProcessingOptions = {
123
+ /**
124
+ * Re-queue hooks that have been in `processing` for at least this many minutes.
125
+ * Use `false` to disable stuck-processing recovery entirely.
126
+ * Values <= 0 are treated as `false`.
127
+ *
128
+ * Default: 10 minutes.
129
+ */
130
+ stuckProcessingTimeoutMinutes?: StuckHookProcessingTimeoutMinutes;
131
+ /**
132
+ * Called when stuck processing hooks are detected and re-queued.
133
+ * Invoked after the hooks are moved back to `pending`.
134
+ */
135
+ onStuckProcessingHooks?: (info: StuckHookProcessingInfo) => void;
136
+ };
137
+
138
+ export type HookHandlerTx = (
139
+ execOptions?: Omit<ExecuteTxOptions, "createUnitOfWork">,
140
+ ) => HandlerTxBuilder<readonly [], [], [], unknown, unknown, false, false, false, false, HooksMap>;
141
+
142
+ const DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES = 10;
143
+
144
+ function resolveStuckProcessingTimeoutMinutes(
145
+ value: StuckHookProcessingTimeoutMinutes | undefined,
146
+ ): number | false {
147
+ if (value === false) {
148
+ return false;
149
+ }
150
+ if (typeof value === "number") {
151
+ return value > 0 ? value : false;
152
+ }
153
+ return DEFAULT_STUCK_PROCESSING_TIMEOUT_MINUTES;
65
154
  }
66
155
 
67
156
  /**
@@ -69,7 +158,10 @@ export interface HookProcessorConfig {
69
158
  * This should be called before executeMutations() so hook records are created
70
159
  * in the same transaction as the user's mutations.
71
160
  */
72
- export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConfig): void {
161
+ export function prepareHookMutations<THooks extends HooksMap>(
162
+ uow: IUnitOfWork,
163
+ config: HookProcessorConfig<THooks>,
164
+ ): void {
73
165
  const { namespace, internalFragment, defaultRetryPolicy } = config;
74
166
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
75
167
 
@@ -85,6 +177,8 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
85
177
  for (const hook of triggeredHooks) {
86
178
  const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;
87
179
  const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;
180
+ const processAt = hook.options?.processAt ? new Date(hook.options.processAt) : null;
181
+ const nextRetryAt = processAt ?? null;
88
182
  internalUow.create("fragno_hooks", {
89
183
  namespace,
90
184
  hookName: hook.hookName,
@@ -93,9 +187,9 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
93
187
  attempts: 0,
94
188
  maxAttempts,
95
189
  lastAttemptAt: null,
96
- nextRetryAt: null,
190
+ nextRetryAt,
97
191
  error: null,
98
- nonce: uow.nonce,
192
+ nonce: uow.idempotencyKey,
99
193
  });
100
194
  }
101
195
  }
@@ -104,76 +198,181 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
104
198
  * Process pending hook events after the transaction has committed.
105
199
  * This should be called in the onSuccess callback after executeMutations().
106
200
  */
107
- export async function processHooks(config: HookProcessorConfig): Promise<void> {
201
+ export async function processHooks<THooks extends HooksMap>(
202
+ config: HookProcessorConfig<THooks>,
203
+ ): Promise<number> {
108
204
  const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
109
205
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
206
+ const stuckProcessingTimeoutMinutes = resolveStuckProcessingTimeoutMinutes(
207
+ config.stuckProcessingTimeoutMinutes,
208
+ );
209
+ const getDbNow = async () => {
210
+ const services = internalFragment.services as { getDbNow?: () => Promise<Date> };
211
+ if (services.getDbNow) {
212
+ return services.getDbNow();
213
+ }
214
+ return new Date();
215
+ };
216
+ const dbNow = await getDbNow();
110
217
 
111
- await internalFragment.inContext(async function () {
112
- return await this.uow(async ({ executeRetrieve, executeMutate }) => {
113
- const pendingEventsPromise =
114
- internalFragment.services.hookService.getPendingHookEvents(namespace);
115
- await executeRetrieve();
218
+ if (stuckProcessingTimeoutMinutes !== false) {
219
+ const staleBefore = new Date(dbNow.getTime() - stuckProcessingTimeoutMinutes * 60_000);
220
+ const stuckEvents = await internalFragment.inContext(async function () {
221
+ return await this.handlerTx()
222
+ .withServiceCalls(
223
+ () =>
224
+ [
225
+ internalFragment.services.hookService.requeueStuckProcessingHooks(
226
+ namespace,
227
+ staleBefore,
228
+ ),
229
+ ] as const,
230
+ )
231
+ .transform(({ serviceResult: [events] }) => events)
232
+ .execute();
233
+ });
116
234
 
117
- const pendingEvents = await pendingEventsPromise;
235
+ if (stuckEvents.length > 0) {
236
+ try {
237
+ config.onStuckProcessingHooks?.({
238
+ namespace,
239
+ timeoutMinutes: stuckProcessingTimeoutMinutes,
240
+ events: stuckEvents,
241
+ });
242
+ } catch (error) {
243
+ console.error("Error calling onStuckProcessingHooks", error);
244
+ }
245
+ }
246
+ }
118
247
 
119
- if (pendingEvents.length === 0) {
120
- return;
248
+ // Claim pending events in the same transaction to avoid double-processing.
249
+ const pendingEvents = await internalFragment.inContext(async function () {
250
+ return await this.handlerTx()
251
+ .withServiceCalls(
252
+ () => [internalFragment.services.hookService.claimPendingHookEvents(namespace)] as const,
253
+ )
254
+ .transform(({ serviceResult: [events] }) => events)
255
+ .execute();
256
+ });
257
+
258
+ if (pendingEvents.length === 0) {
259
+ return 0;
260
+ }
261
+
262
+ // Process events (async work outside transaction)
263
+ const processedEvents = await Promise.allSettled(
264
+ pendingEvents.map(async (event) => {
265
+ const hookFn = hooks[event.hookName];
266
+ if (!hookFn) {
267
+ return {
268
+ eventId: event.id,
269
+ status: "failed" as const,
270
+ error: `Hook '${event.hookName}' not found in hooks map`,
271
+ attempts: event.attempts,
272
+ maxAttempts: event.maxAttempts,
273
+ };
121
274
  }
122
275
 
123
- const processedEvents = await Promise.allSettled(
124
- pendingEvents.map(async (event) => {
125
- const hookFn = hooks[event.hookName];
126
- if (!hookFn) {
127
- return {
128
- eventId: event.id,
129
- status: "failed" as const,
130
- error: `Hook '${event.hookName}' not found in hooks map`,
131
- attempts: event.attempts,
132
- maxAttempts: event.maxAttempts,
133
- };
134
- }
276
+ try {
277
+ const hookContext: HookContext = {
278
+ idempotencyKey: event.idempotencyKey,
279
+ handlerTx: config.handlerTx,
280
+ };
281
+ await hookFn.call(hookContext, event.payload);
282
+ return {
283
+ eventId: event.id,
284
+ status: "completed" as const,
285
+ };
286
+ } catch (error) {
287
+ const errorMessage = error instanceof Error ? error.message : String(error);
288
+ return {
289
+ eventId: event.id,
290
+ status: "failed" as const,
291
+ error: errorMessage,
292
+ attempts: event.attempts,
293
+ maxAttempts: event.maxAttempts,
294
+ };
295
+ }
296
+ }),
297
+ );
135
298
 
136
- try {
137
- const hookContext: HookContext = { nonce: event.nonce };
138
- await hookFn.call(hookContext, event.payload);
139
- return {
140
- eventId: event.id,
141
- status: "completed" as const,
142
- };
143
- } catch (error) {
144
- const errorMessage = error instanceof Error ? error.message : String(error);
145
- return {
146
- eventId: event.id,
147
- status: "failed" as const,
148
- error: errorMessage,
149
- attempts: event.attempts,
150
- maxAttempts: event.maxAttempts,
151
- };
299
+ // Mark events as completed/failed
300
+ await internalFragment.inContext(async function () {
301
+ await this.handlerTx()
302
+ .withServiceCalls(() => {
303
+ const txResults: TxResult<void>[] = [];
304
+ for (const processedEvent of processedEvents) {
305
+ if (processedEvent.status === "rejected") {
306
+ continue;
152
307
  }
153
- }),
154
- );
155
308
 
156
- for (const processedEvent of processedEvents) {
157
- if (processedEvent.status === "rejected") {
158
- continue;
159
- }
309
+ const { eventId, status } = processedEvent.value;
160
310
 
161
- const { eventId, status } = processedEvent.value;
162
-
163
- if (status === "completed") {
164
- internalFragment.services.hookService.markHookCompleted(eventId);
165
- } else if (status === "failed") {
166
- const { error, attempts } = processedEvent.value;
167
- internalFragment.services.hookService.markHookFailed(
168
- eventId,
169
- error,
170
- attempts,
171
- retryPolicy,
172
- );
311
+ if (status === "completed") {
312
+ txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
313
+ } else if (status === "failed") {
314
+ const { error, attempts } = processedEvent.value;
315
+ txResults.push(
316
+ internalFragment.services.hookService.markHookFailed(
317
+ eventId,
318
+ error,
319
+ attempts,
320
+ retryPolicy,
321
+ dbNow,
322
+ ),
323
+ );
324
+ }
173
325
  }
326
+ return txResults;
327
+ })
328
+ .execute();
329
+ });
330
+
331
+ const processedCount = processedEvents.reduce(
332
+ (count, result) => count + (result.status === "fulfilled" ? 1 : 0),
333
+ 0,
334
+ );
335
+
336
+ return processedCount;
337
+ }
338
+
339
+ export function createHookScheduler(config: HookProcessorConfig): HookScheduler {
340
+ let processing = false;
341
+ let queued = false;
342
+ let currentPromise: Promise<number> | null = null;
343
+
344
+ const schedule = async () => {
345
+ if (processing) {
346
+ queued = true;
347
+ return currentPromise ?? Promise.resolve(0);
348
+ }
349
+
350
+ processing = true;
351
+ currentPromise = (async () => {
352
+ let lastCount = 0;
353
+ try {
354
+ do {
355
+ queued = false;
356
+ lastCount = await processHooks(config);
357
+ } while (queued);
358
+ return lastCount;
359
+ } finally {
360
+ processing = false;
361
+ queued = false;
174
362
  }
363
+ })();
175
364
 
176
- await executeMutate();
177
- });
178
- });
365
+ return currentPromise;
366
+ };
367
+
368
+ const drain = async () => {
369
+ while (true) {
370
+ const processed = await schedule();
371
+ if (processed === 0) {
372
+ return;
373
+ }
374
+ }
375
+ };
376
+
377
+ return { schedule, drain };
179
378
  }
@@ -4,7 +4,7 @@ import { generateMigrationFromSchema } from "./auto-from-schema";
4
4
 
5
5
  describe("generateMigrationFromSchema", () => {
6
6
  it("should generate create-table operation for new tables", () => {
7
- const mySchema = schema((s) => {
7
+ const mySchema = schema("my", (s) => {
8
8
  return s
9
9
  .addTable("users", (t) => {
10
10
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -35,7 +35,7 @@ describe("generateMigrationFromSchema", () => {
35
35
  });
36
36
 
37
37
  it("should generate multiple table operations in sequence", () => {
38
- const mySchema = schema((s) => {
38
+ const mySchema = schema("my", (s) => {
39
39
  return s
40
40
  .addTable("users", (t) => {
41
41
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -79,7 +79,7 @@ describe("generateMigrationFromSchema", () => {
79
79
  });
80
80
 
81
81
  it("should generate add-foreign-key operation for new foreign keys", () => {
82
- const mySchema = schema((s) => {
82
+ const mySchema = schema("my", (s) => {
83
83
  return s
84
84
  .addTable("users", (t) => {
85
85
  return t.addColumn("id", idColumn());
@@ -108,7 +108,7 @@ describe("generateMigrationFromSchema", () => {
108
108
  const fkOp = operations[0];
109
109
  if (fkOp.type === "add-foreign-key") {
110
110
  expect(fkOp.value).toMatchObject({
111
- name: "posts_users_author_fk",
111
+ name: "author",
112
112
  referencedTable: "users",
113
113
  columns: ["authorId"],
114
114
  referencedColumns: ["_internalId"],
@@ -117,7 +117,7 @@ describe("generateMigrationFromSchema", () => {
117
117
  });
118
118
 
119
119
  it("should generate add-index operation for indexes added via alterTable", () => {
120
- const mySchema = schema((s) => {
120
+ const mySchema = schema("my", (s) => {
121
121
  return s
122
122
  .addTable("users", (t) => {
123
123
  return t.addColumn("id", idColumn()).addColumn("email", column("string"));
@@ -141,7 +141,7 @@ describe("generateMigrationFromSchema", () => {
141
141
  });
142
142
 
143
143
  it("should generate mixed operations for tables and foreign keys", () => {
144
- const mySchema = schema((s) => {
144
+ const mySchema = schema("my", (s) => {
145
145
  return s
146
146
  .addTable("users", (t) => {
147
147
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -166,7 +166,7 @@ describe("generateMigrationFromSchema", () => {
166
166
  });
167
167
 
168
168
  it("should generate mixed operations for tables, indexes, and foreign keys", () => {
169
- const mySchema = schema((s) => {
169
+ const mySchema = schema("my", (s) => {
170
170
  return s
171
171
  .addTable("users", (t) => {
172
172
  return t.addColumn("id", idColumn()).addColumn("email", column("string"));
@@ -195,7 +195,7 @@ describe("generateMigrationFromSchema", () => {
195
195
  });
196
196
 
197
197
  it("should generate no operations when version range is empty", () => {
198
- const mySchema = schema((s) => {
198
+ const mySchema = schema("my", (s) => {
199
199
  return s.addTable("users", (t) => {
200
200
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
201
201
  });
@@ -207,7 +207,7 @@ describe("generateMigrationFromSchema", () => {
207
207
  });
208
208
 
209
209
  it("should throw error when fromVersion exceeds schema version", () => {
210
- const mySchema = schema((s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
210
+ const mySchema = schema("my", (s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
211
211
 
212
212
  expect(() => {
213
213
  generateMigrationFromSchema(mySchema, 999, 1000);
@@ -215,7 +215,7 @@ describe("generateMigrationFromSchema", () => {
215
215
  });
216
216
 
217
217
  it("should throw error when toVersion exceeds schema version", () => {
218
- const mySchema = schema((s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
218
+ const mySchema = schema("my", (s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
219
219
 
220
220
  expect(() => {
221
221
  generateMigrationFromSchema(mySchema, 0, 999);
@@ -223,7 +223,7 @@ describe("generateMigrationFromSchema", () => {
223
223
  });
224
224
 
225
225
  it("should throw error when trying to migrate backwards", () => {
226
- const mySchema = schema((s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
226
+ const mySchema = schema("my", (s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
227
227
 
228
228
  expect(() => {
229
229
  generateMigrationFromSchema(mySchema, 1, 0);
@@ -231,7 +231,7 @@ describe("generateMigrationFromSchema", () => {
231
231
  });
232
232
 
233
233
  it("should throw error for negative fromVersion", () => {
234
- const mySchema = schema((s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
234
+ const mySchema = schema("my", (s) => s.addTable("users", (t) => t.addColumn("id", idColumn())));
235
235
 
236
236
  expect(() => {
237
237
  generateMigrationFromSchema(mySchema, -1, 1);
@@ -239,7 +239,7 @@ describe("generateMigrationFromSchema", () => {
239
239
  });
240
240
 
241
241
  it("should only create constraints for references, not for referenceColumns", () => {
242
- const mySchema = schema((s) => {
242
+ const mySchema = schema("my", (s) => {
243
243
  return s
244
244
  .addTable("posts", (t) => {
245
245
  return t
@@ -276,7 +276,7 @@ describe("generateMigrationFromSchema", () => {
276
276
  });
277
277
 
278
278
  it("should not create duplicate foreign key constraints", () => {
279
- const mySchema = schema((s) => {
279
+ const mySchema = schema("my", (s) => {
280
280
  return s
281
281
  .addTable("users", (t) => t.addColumn("id", idColumn()))
282
282
  .addTable("posts", (t) =>
@@ -16,7 +16,7 @@ import type { MigrationOperation, ColumnInfo } from "./shared";
16
16
  *
17
17
  * @example
18
18
  * ```ts
19
- * const mySchema = schema(s => s
19
+ * const mySchema = schema("my", s => s
20
20
  * .addTable("users", t => t.addColumn("id", idColumn())) // version 1
21
21
  * .addTable("posts", t => t.addColumn("id", idColumn())) // version 2
22
22
  * );
@@ -156,11 +156,14 @@ export function generateMigrationFromSchema(
156
156
  }
157
157
  }
158
158
  } else if (op.type === "add-reference") {
159
+ if (!op.referenceName || op.referenceName.trim().length === 0) {
160
+ throw new Error(`referenceName is required for add-reference on ${op.tableName}`);
161
+ }
159
162
  migrationOperations.push({
160
163
  type: "add-foreign-key",
161
164
  table: op.tableName,
162
165
  value: {
163
- name: `${op.tableName}_${op.config.to.table}_${op.referenceName}_fk`,
166
+ name: op.referenceName,
164
167
  columns: [op.config.from.column],
165
168
  referencedTable: op.config.to.table,
166
169
  referencedColumns: [op.config.to.column],
@@ -4,7 +4,7 @@ import { schema, idColumn, column, referenceColumn } from "../schema/create";
4
4
  import type { MigrationOperation } from "./shared";
5
5
 
6
6
  describe("createMigrator", () => {
7
- const testSchema = schema((s) => {
7
+ const testSchema = schema("test", (s) => {
8
8
  return s
9
9
  .addTable("users", (t) => {
10
10
  return t.addColumn("id", idColumn()).addColumn("name", column("string"));
@@ -296,7 +296,7 @@ describe("createMigrator", () => {
296
296
 
297
297
  describe("with foreign keys", () => {
298
298
  it("should handle schema with foreign keys", async () => {
299
- const fkSchema = schema((s) => {
299
+ const fkSchema = schema("fk", (s) => {
300
300
  return s
301
301
  .addTable("users", (t) => {
302
302
  return t.addColumn("id", idColumn());