@fragno-dev/db 0.1.14 → 0.2.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 (445) hide show
  1. package/.turbo/turbo-build.log +242 -139
  2. package/CHANGELOG.md +47 -0
  3. package/README.md +123 -8
  4. package/dist/adapters/adapters.d.ts +19 -5
  5. package/dist/adapters/adapters.d.ts.map +1 -1
  6. package/dist/adapters/adapters.js.map +1 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts +6 -19
  8. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js +7 -47
  10. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  11. package/dist/adapters/drizzle/generate.d.ts +7 -1
  12. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  13. package/dist/adapters/drizzle/generate.js +46 -45
  14. package/dist/adapters/drizzle/generate.js.map +1 -1
  15. package/dist/adapters/generic-sql/driver-config.d.ts +74 -0
  16. package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -0
  17. package/dist/adapters/generic-sql/driver-config.js +94 -0
  18. package/dist/adapters/generic-sql/driver-config.js.map +1 -0
  19. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +43 -0
  20. package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -0
  21. package/dist/adapters/generic-sql/generic-sql-adapter.js +87 -0
  22. package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -0
  23. package/dist/adapters/generic-sql/generic-sql-uow-executor.js +67 -0
  24. package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -0
  25. package/dist/adapters/generic-sql/migration/cold-kysely.js +33 -0
  26. package/dist/adapters/generic-sql/migration/cold-kysely.js.map +1 -0
  27. package/dist/adapters/generic-sql/migration/dialect/mysql.js +60 -0
  28. package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -0
  29. package/dist/adapters/generic-sql/migration/dialect/postgres.js +59 -0
  30. package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -0
  31. package/dist/adapters/generic-sql/migration/dialect/sqlite.js +96 -0
  32. package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -0
  33. package/dist/adapters/generic-sql/migration/executor.d.ts +15 -0
  34. package/dist/adapters/generic-sql/migration/executor.d.ts.map +1 -0
  35. package/dist/adapters/generic-sql/migration/executor.js +18 -0
  36. package/dist/adapters/generic-sql/migration/executor.js.map +1 -0
  37. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts +66 -0
  38. package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -0
  39. package/dist/adapters/generic-sql/migration/prepared-migrations.js +68 -0
  40. package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -0
  41. package/dist/adapters/generic-sql/migration/sql-generator.js +212 -0
  42. package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -0
  43. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +32 -0
  44. package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -0
  45. package/dist/adapters/generic-sql/query/cursor-utils.js +37 -0
  46. package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -0
  47. package/dist/adapters/generic-sql/query/dialect/mysql.js +33 -0
  48. package/dist/adapters/generic-sql/query/dialect/mysql.js.map +1 -0
  49. package/dist/adapters/generic-sql/query/dialect/postgres.js +32 -0
  50. package/dist/adapters/generic-sql/query/dialect/postgres.js.map +1 -0
  51. package/dist/adapters/generic-sql/query/dialect/sqlite.js +32 -0
  52. package/dist/adapters/generic-sql/query/dialect/sqlite.js.map +1 -0
  53. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +152 -0
  54. package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -0
  55. package/dist/adapters/generic-sql/query/select-builder.js +69 -0
  56. package/dist/adapters/generic-sql/query/select-builder.js.map +1 -0
  57. package/dist/adapters/generic-sql/query/sql-query-compiler.js +145 -0
  58. package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -0
  59. package/dist/adapters/generic-sql/query/where-builder.js +129 -0
  60. package/dist/adapters/generic-sql/query/where-builder.js.map +1 -0
  61. package/dist/adapters/generic-sql/result-interpreter.js +74 -0
  62. package/dist/adapters/generic-sql/result-interpreter.js.map +1 -0
  63. package/dist/adapters/generic-sql/uow-decoder.js +105 -0
  64. package/dist/adapters/generic-sql/uow-decoder.js.map +1 -0
  65. package/dist/adapters/generic-sql/uow-encoder.js +93 -0
  66. package/dist/adapters/generic-sql/uow-encoder.js.map +1 -0
  67. package/dist/adapters/kysely/kysely-adapter.d.ts +5 -16
  68. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  69. package/dist/adapters/kysely/kysely-adapter.js +6 -159
  70. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  71. package/dist/adapters/{drizzle/drizzle-query.js → shared/from-unit-of-work-compiler.js} +48 -62
  72. package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -0
  73. package/dist/adapters/{kysely/kysely-shared.d.ts → shared/table-name-mapper.d.ts} +3 -2
  74. package/dist/adapters/shared/table-name-mapper.d.ts.map +1 -0
  75. package/dist/adapters/shared/table-name-mapper.js +43 -0
  76. package/dist/adapters/shared/table-name-mapper.js.map +1 -0
  77. package/dist/adapters/shared/uow-operation-compiler.js +105 -0
  78. package/dist/adapters/shared/uow-operation-compiler.js.map +1 -0
  79. package/dist/db-fragment-definition-builder.d.ts +186 -0
  80. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  81. package/dist/db-fragment-definition-builder.js +207 -0
  82. package/dist/db-fragment-definition-builder.js.map +1 -0
  83. package/dist/fragments/internal-fragment.d.ts +53 -0
  84. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  85. package/dist/fragments/internal-fragment.js +111 -0
  86. package/dist/fragments/internal-fragment.js.map +1 -0
  87. package/dist/hooks/hooks.d.ts +51 -0
  88. package/dist/hooks/hooks.d.ts.map +1 -0
  89. package/dist/hooks/hooks.js +88 -0
  90. package/dist/hooks/hooks.js.map +1 -0
  91. package/dist/migration-engine/generation-engine.d.ts +0 -2
  92. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  93. package/dist/migration-engine/generation-engine.js +38 -56
  94. package/dist/migration-engine/generation-engine.js.map +1 -1
  95. package/dist/mod.d.ts +35 -23
  96. package/dist/mod.d.ts.map +1 -1
  97. package/dist/mod.js +48 -45
  98. package/dist/mod.js.map +1 -1
  99. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js +165 -0
  100. package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +1 -0
  101. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  102. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  103. package/dist/packages/fragno/dist/api/error.js +48 -0
  104. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  105. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  106. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  107. package/dist/packages/fragno/dist/api/fragment-instantiator.js +525 -0
  108. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  109. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  110. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  111. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  112. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  113. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  114. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  115. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  116. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  117. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  118. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  119. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  120. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  121. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  122. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  123. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  124. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  125. package/dist/packages/fragno/dist/api/route.js +17 -0
  126. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  127. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  128. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  129. package/dist/query/column-defaults.js +27 -0
  130. package/dist/query/column-defaults.js.map +1 -0
  131. package/dist/query/cursor.d.ts +14 -6
  132. package/dist/query/cursor.d.ts.map +1 -1
  133. package/dist/query/cursor.js +16 -7
  134. package/dist/query/cursor.js.map +1 -1
  135. package/dist/query/orm/orm.d.ts +1 -1
  136. package/dist/query/orm/orm.js.map +1 -1
  137. package/dist/query/serialize/create-sql-serializer.js +30 -0
  138. package/dist/query/serialize/create-sql-serializer.js.map +1 -0
  139. package/dist/query/serialize/dialect/mysql-serializer.js +87 -0
  140. package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -0
  141. package/dist/query/serialize/dialect/postgres-serializer.js +80 -0
  142. package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -0
  143. package/dist/query/serialize/dialect/sqlite-serializer.js +93 -0
  144. package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -0
  145. package/dist/query/serialize/sql-serializer.js +67 -0
  146. package/dist/query/serialize/sql-serializer.js.map +1 -0
  147. package/dist/query/{query.d.ts → simple-query-interface.d.ts} +6 -6
  148. package/dist/query/simple-query-interface.d.ts.map +1 -0
  149. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +133 -0
  150. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -0
  151. package/dist/query/unit-of-work/execute-unit-of-work.js +197 -0
  152. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -0
  153. package/dist/query/unit-of-work/retry-policy.d.ts +88 -0
  154. package/dist/query/unit-of-work/retry-policy.d.ts.map +1 -0
  155. package/dist/query/unit-of-work/retry-policy.js +61 -0
  156. package/dist/query/unit-of-work/retry-policy.js.map +1 -0
  157. package/dist/query/{unit-of-work.d.ts → unit-of-work/unit-of-work.d.ts} +145 -58
  158. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -0
  159. package/dist/query/{unit-of-work.js → unit-of-work/unit-of-work.js} +435 -198
  160. package/dist/query/unit-of-work/unit-of-work.js.map +1 -0
  161. package/dist/query/value-decoding.js +71 -0
  162. package/dist/query/value-decoding.js.map +1 -0
  163. package/dist/query/value-encoding.js +124 -0
  164. package/dist/query/value-encoding.js.map +1 -0
  165. package/dist/schema/create.d.ts +3 -0
  166. package/dist/schema/create.d.ts.map +1 -1
  167. package/dist/schema/create.js +4 -0
  168. package/dist/schema/create.js.map +1 -1
  169. package/dist/schema/type-conversion/create-sql-type-mapper.js +29 -0
  170. package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -0
  171. package/dist/schema/type-conversion/dialect/mysql.js +57 -0
  172. package/dist/schema/type-conversion/dialect/mysql.js.map +1 -0
  173. package/dist/schema/type-conversion/dialect/postgres.js +56 -0
  174. package/dist/schema/type-conversion/dialect/postgres.js.map +1 -0
  175. package/dist/schema/type-conversion/dialect/sqlite.js +52 -0
  176. package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -0
  177. package/dist/schema/type-conversion/type-mapping.js +63 -0
  178. package/dist/schema/type-conversion/type-mapping.js.map +1 -0
  179. package/dist/sql-driver/connection/connection-provider.d.ts +13 -0
  180. package/dist/sql-driver/connection/connection-provider.d.ts.map +1 -0
  181. package/dist/sql-driver/connection/connection-provider.js +19 -0
  182. package/dist/sql-driver/connection/connection-provider.js.map +1 -0
  183. package/dist/sql-driver/connection/single-connection-provider.js +23 -0
  184. package/dist/sql-driver/connection/single-connection-provider.js.map +1 -0
  185. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts +7 -0
  186. package/dist/sql-driver/dialect-adapter/dialect-adapter.d.ts.map +1 -0
  187. package/dist/sql-driver/dialects/dialects.d.ts +2 -0
  188. package/dist/sql-driver/dialects/dialects.js +3 -0
  189. package/dist/sql-driver/dialects/durable-object-dialect.d.ts +72 -0
  190. package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -0
  191. package/dist/sql-driver/dialects/durable-object-dialect.js +130 -0
  192. package/dist/sql-driver/dialects/durable-object-dialect.js.map +1 -0
  193. package/dist/sql-driver/driver/runtime-driver.d.ts +23 -0
  194. package/dist/sql-driver/driver/runtime-driver.d.ts.map +1 -0
  195. package/dist/sql-driver/driver/runtime-driver.js +56 -0
  196. package/dist/sql-driver/driver/runtime-driver.js.map +1 -0
  197. package/dist/sql-driver/query-executor/default-query-executor.js +26 -0
  198. package/dist/sql-driver/query-executor/default-query-executor.js.map +1 -0
  199. package/dist/sql-driver/query-executor/plugin.d.ts +17 -0
  200. package/dist/sql-driver/query-executor/plugin.d.ts.map +1 -0
  201. package/dist/sql-driver/query-executor/query-executor-base.js +25 -0
  202. package/dist/sql-driver/query-executor/query-executor-base.js.map +1 -0
  203. package/dist/sql-driver/query-executor/query-executor.d.ts +36 -0
  204. package/dist/sql-driver/query-executor/query-executor.d.ts.map +1 -0
  205. package/dist/sql-driver/sql-driver-adapter.d.ts +29 -0
  206. package/dist/sql-driver/sql-driver-adapter.d.ts.map +1 -0
  207. package/dist/sql-driver/sql-driver-adapter.js +68 -0
  208. package/dist/sql-driver/sql-driver-adapter.js.map +1 -0
  209. package/dist/sql-driver/sql-driver.d.ts +38 -0
  210. package/dist/sql-driver/sql-driver.d.ts.map +1 -0
  211. package/dist/sql-driver/sql-driver.js +1 -0
  212. package/dist/sql-driver/sql.js +50 -0
  213. package/dist/sql-driver/sql.js.map +1 -0
  214. package/dist/with-database.d.ts +32 -0
  215. package/dist/with-database.d.ts.map +1 -0
  216. package/dist/with-database.js +34 -0
  217. package/dist/with-database.js.map +1 -0
  218. package/package.json +43 -9
  219. package/src/adapters/adapters.ts +23 -4
  220. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +140 -185
  221. package/src/adapters/drizzle/{drizzle-adapter-sqlite.test.ts → drizzle-adapter-sqlite3.test.ts} +187 -55
  222. package/src/adapters/drizzle/drizzle-adapter.ts +14 -93
  223. package/src/adapters/drizzle/generate.test.ts +102 -269
  224. package/src/adapters/drizzle/generate.ts +89 -63
  225. package/src/adapters/drizzle/migrate-drizzle.test.ts +19 -0
  226. package/src/adapters/drizzle/shared.ts +0 -34
  227. package/src/adapters/drizzle/test-utils.ts +36 -5
  228. package/src/adapters/generic-sql/README.md +14 -0
  229. package/src/adapters/generic-sql/driver-config.ts +144 -0
  230. package/src/adapters/generic-sql/generic-sql-adapter.test.ts +50 -0
  231. package/src/adapters/generic-sql/generic-sql-adapter.ts +146 -0
  232. package/src/adapters/generic-sql/generic-sql-uow-executor.ts +130 -0
  233. package/src/adapters/generic-sql/migration/cold-kysely.ts +55 -0
  234. package/src/adapters/{kysely/migration/execute-mysql.test.ts → generic-sql/migration/dialect/mysql.test.ts} +342 -484
  235. package/src/adapters/generic-sql/migration/dialect/mysql.ts +104 -0
  236. package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +1008 -0
  237. package/src/adapters/generic-sql/migration/dialect/postgres.ts +113 -0
  238. package/src/adapters/{kysely/migration/execute-sqlite.test.ts → generic-sql/migration/dialect/sqlite.test.ts} +307 -510
  239. package/src/adapters/generic-sql/migration/dialect/sqlite.ts +189 -0
  240. package/src/adapters/generic-sql/migration/executor.ts +33 -0
  241. package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +661 -0
  242. package/src/adapters/generic-sql/migration/prepared-migrations.ts +214 -0
  243. package/src/adapters/generic-sql/migration/sql-generator.ts +413 -0
  244. package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +36 -0
  245. package/src/adapters/generic-sql/query/cursor-utils.ts +56 -0
  246. package/src/adapters/generic-sql/query/dialect/mysql.ts +34 -0
  247. package/src/adapters/generic-sql/query/dialect/postgres.ts +32 -0
  248. package/src/adapters/generic-sql/query/dialect/sqlite.ts +32 -0
  249. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +1568 -0
  250. package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +314 -0
  251. package/src/adapters/generic-sql/query/select-builder.test.ts +256 -0
  252. package/src/adapters/generic-sql/query/select-builder.ts +137 -0
  253. package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +195 -0
  254. package/src/adapters/generic-sql/query/sql-query-compiler.ts +367 -0
  255. package/src/adapters/generic-sql/query/where-builder.test.ts +744 -0
  256. package/src/adapters/generic-sql/query/where-builder.ts +211 -0
  257. package/src/adapters/generic-sql/result-interpreter.ts +102 -0
  258. package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +899 -0
  259. package/src/adapters/generic-sql/uow-decoder.test.ts +399 -0
  260. package/src/adapters/generic-sql/uow-decoder.ts +152 -0
  261. package/src/adapters/generic-sql/uow-encoder.test.ts +183 -0
  262. package/src/adapters/generic-sql/uow-encoder.ts +131 -0
  263. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +90 -96
  264. package/src/adapters/kysely/kysely-adapter-sqlocal.test.ts +215 -0
  265. package/src/adapters/kysely/kysely-adapter.ts +10 -242
  266. package/src/adapters/{drizzle/drizzle-query.ts → shared/from-unit-of-work-compiler.ts} +111 -106
  267. package/src/adapters/shared/table-name-mapper.ts +50 -0
  268. package/src/adapters/shared/uow-operation-compiler.ts +211 -0
  269. package/src/db-fragment-definition-builder.test.ts +887 -0
  270. package/src/db-fragment-definition-builder.ts +737 -0
  271. package/src/db-fragment-instantiator.test.ts +543 -0
  272. package/src/db-fragment-integration.test.ts +406 -0
  273. package/src/fragments/internal-fragment.test.ts +549 -0
  274. package/src/fragments/internal-fragment.ts +249 -0
  275. package/src/hooks/hooks.test.ts +575 -0
  276. package/src/hooks/hooks.ts +179 -0
  277. package/src/migration-engine/generation-engine.test.ts +60 -27
  278. package/src/migration-engine/generation-engine.ts +99 -92
  279. package/src/mod.ts +139 -78
  280. package/src/query/column-defaults.ts +49 -0
  281. package/src/query/cursor.test.ts +147 -3
  282. package/src/query/cursor.ts +25 -8
  283. package/src/query/orm/orm.ts +1 -1
  284. package/src/query/query-type.test.ts +9 -9
  285. package/src/query/serialize/create-sql-serializer.ts +34 -0
  286. package/src/query/serialize/dialect/mysql-serializer.ts +142 -0
  287. package/src/query/serialize/dialect/postgres-serializer.ts +129 -0
  288. package/src/query/serialize/dialect/sqlite-serializer.test.ts +251 -0
  289. package/src/query/serialize/dialect/sqlite-serializer.ts +156 -0
  290. package/src/query/serialize/sql-serializer.ts +143 -0
  291. package/src/query/{query.ts → simple-query-interface.ts} +4 -4
  292. package/src/query/unit-of-work/execute-unit-of-work.test.ts +1310 -0
  293. package/src/query/unit-of-work/execute-unit-of-work.ts +504 -0
  294. package/src/query/unit-of-work/retry-policy.test.ts +217 -0
  295. package/src/query/unit-of-work/retry-policy.ts +141 -0
  296. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +831 -0
  297. package/src/query/{unit-of-work-types.test.ts → unit-of-work/unit-of-work-types.test.ts} +7 -5
  298. package/src/query/unit-of-work/unit-of-work.test.ts +1716 -0
  299. package/src/query/{unit-of-work.ts → unit-of-work/unit-of-work.ts} +716 -420
  300. package/src/query/{result-transform.test.ts → value-decoding.test.ts} +45 -298
  301. package/src/query/value-decoding.ts +113 -0
  302. package/src/query/value-encoding.test.ts +390 -0
  303. package/src/query/value-encoding.ts +168 -0
  304. package/src/schema/create.test.ts +5 -1
  305. package/src/schema/create.ts +5 -0
  306. package/src/schema/serialize.test.ts +165 -407
  307. package/src/schema/type-conversion/create-sql-type-mapper.ts +28 -0
  308. package/src/schema/type-conversion/dialect/mysql.ts +64 -0
  309. package/src/schema/type-conversion/dialect/postgres.ts +62 -0
  310. package/src/schema/type-conversion/dialect/sqlite.ts +63 -0
  311. package/src/schema/type-conversion/type-mapping.test.ts +137 -0
  312. package/src/schema/type-conversion/type-mapping.ts +153 -0
  313. package/src/shared/connection-pool.ts +5 -5
  314. package/src/sql-driver/better-sqlite3.test.ts +126 -0
  315. package/src/sql-driver/connection/connection-provider.ts +27 -0
  316. package/src/sql-driver/connection/single-connection-provider.ts +42 -0
  317. package/src/sql-driver/dialect-adapter/dialect-adapter.ts +9 -0
  318. package/src/sql-driver/dialect-adapter/sqlite-dialect-adapter.ts +7 -0
  319. package/src/sql-driver/dialects/dialects.ts +1 -0
  320. package/src/sql-driver/dialects/durable-object-dialect.ts +260 -0
  321. package/src/sql-driver/driver/runtime-driver.ts +91 -0
  322. package/src/sql-driver/query-executor/default-query-executor.ts +38 -0
  323. package/src/sql-driver/query-executor/plugin.ts +22 -0
  324. package/src/sql-driver/query-executor/query-executor-base.ts +53 -0
  325. package/src/sql-driver/query-executor/query-executor.ts +44 -0
  326. package/src/sql-driver/sql-driver-adapter.ts +96 -0
  327. package/src/sql-driver/sql-driver.ts +53 -0
  328. package/src/sql-driver/sql.ts +57 -0
  329. package/src/sql-driver/sqlocal.test.ts +117 -0
  330. package/src/with-database.ts +152 -0
  331. package/tsdown.config.ts +8 -2
  332. package/dist/adapters/drizzle/drizzle-connection-pool.js +0 -40
  333. package/dist/adapters/drizzle/drizzle-connection-pool.js.map +0 -1
  334. package/dist/adapters/drizzle/drizzle-query.d.ts +0 -23
  335. package/dist/adapters/drizzle/drizzle-query.d.ts.map +0 -1
  336. package/dist/adapters/drizzle/drizzle-query.js.map +0 -1
  337. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -10
  338. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +0 -1
  339. package/dist/adapters/drizzle/drizzle-uow-compiler.js +0 -315
  340. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +0 -1
  341. package/dist/adapters/drizzle/drizzle-uow-decoder.js +0 -116
  342. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +0 -1
  343. package/dist/adapters/drizzle/drizzle-uow-executor.js +0 -149
  344. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +0 -1
  345. package/dist/adapters/drizzle/join-column-utils.js +0 -28
  346. package/dist/adapters/drizzle/join-column-utils.js.map +0 -1
  347. package/dist/adapters/drizzle/shared.d.ts +0 -14
  348. package/dist/adapters/drizzle/shared.d.ts.map +0 -1
  349. package/dist/adapters/drizzle/shared.js +0 -35
  350. package/dist/adapters/drizzle/shared.js.map +0 -1
  351. package/dist/adapters/kysely/kysely-connection-pool.js +0 -41
  352. package/dist/adapters/kysely/kysely-connection-pool.js.map +0 -1
  353. package/dist/adapters/kysely/kysely-query-builder.js +0 -321
  354. package/dist/adapters/kysely/kysely-query-builder.js.map +0 -1
  355. package/dist/adapters/kysely/kysely-query-compiler.js +0 -66
  356. package/dist/adapters/kysely/kysely-query-compiler.js.map +0 -1
  357. package/dist/adapters/kysely/kysely-query.d.ts +0 -22
  358. package/dist/adapters/kysely/kysely-query.d.ts.map +0 -1
  359. package/dist/adapters/kysely/kysely-query.js +0 -223
  360. package/dist/adapters/kysely/kysely-query.js.map +0 -1
  361. package/dist/adapters/kysely/kysely-shared.d.ts.map +0 -1
  362. package/dist/adapters/kysely/kysely-shared.js +0 -18
  363. package/dist/adapters/kysely/kysely-shared.js.map +0 -1
  364. package/dist/adapters/kysely/kysely-uow-compiler.js +0 -170
  365. package/dist/adapters/kysely/kysely-uow-compiler.js.map +0 -1
  366. package/dist/adapters/kysely/kysely-uow-executor.js +0 -89
  367. package/dist/adapters/kysely/kysely-uow-executor.js.map +0 -1
  368. package/dist/adapters/kysely/migration/execute-base.js +0 -128
  369. package/dist/adapters/kysely/migration/execute-base.js.map +0 -1
  370. package/dist/adapters/kysely/migration/execute-factory.js +0 -34
  371. package/dist/adapters/kysely/migration/execute-factory.js.map +0 -1
  372. package/dist/adapters/kysely/migration/execute-mssql.js +0 -112
  373. package/dist/adapters/kysely/migration/execute-mssql.js.map +0 -1
  374. package/dist/adapters/kysely/migration/execute-mysql.js +0 -93
  375. package/dist/adapters/kysely/migration/execute-mysql.js.map +0 -1
  376. package/dist/adapters/kysely/migration/execute-postgres.js +0 -104
  377. package/dist/adapters/kysely/migration/execute-postgres.js.map +0 -1
  378. package/dist/adapters/kysely/migration/execute-sqlite.js +0 -123
  379. package/dist/adapters/kysely/migration/execute-sqlite.js.map +0 -1
  380. package/dist/adapters/kysely/migration/execute.js +0 -34
  381. package/dist/adapters/kysely/migration/execute.js.map +0 -1
  382. package/dist/bind-services.d.ts +0 -7
  383. package/dist/bind-services.d.ts.map +0 -1
  384. package/dist/bind-services.js +0 -14
  385. package/dist/bind-services.js.map +0 -1
  386. package/dist/fragment.d.ts +0 -173
  387. package/dist/fragment.d.ts.map +0 -1
  388. package/dist/fragment.js +0 -191
  389. package/dist/fragment.js.map +0 -1
  390. package/dist/migration-engine/create.d.ts +0 -37
  391. package/dist/migration-engine/create.d.ts.map +0 -1
  392. package/dist/migration-engine/create.js +0 -58
  393. package/dist/migration-engine/create.js.map +0 -1
  394. package/dist/migration-engine/shared.d.ts +0 -112
  395. package/dist/migration-engine/shared.d.ts.map +0 -1
  396. package/dist/query/query.d.ts.map +0 -1
  397. package/dist/query/result-transform.js +0 -168
  398. package/dist/query/result-transform.js.map +0 -1
  399. package/dist/query/unit-of-work.d.ts.map +0 -1
  400. package/dist/query/unit-of-work.js.map +0 -1
  401. package/dist/schema/serialize.js +0 -106
  402. package/dist/schema/serialize.js.map +0 -1
  403. package/dist/shared/settings-schema.js +0 -36
  404. package/dist/shared/settings-schema.js.map +0 -1
  405. package/src/adapters/drizzle/drizzle-adapter.test.ts +0 -170
  406. package/src/adapters/drizzle/drizzle-connection-pool.ts +0 -66
  407. package/src/adapters/drizzle/drizzle-query.test.ts +0 -499
  408. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +0 -1383
  409. package/src/adapters/drizzle/drizzle-uow-compiler.ts +0 -636
  410. package/src/adapters/drizzle/drizzle-uow-decoder.ts +0 -218
  411. package/src/adapters/drizzle/drizzle-uow-executor.ts +0 -276
  412. package/src/adapters/drizzle/join-column-utils.test.ts +0 -79
  413. package/src/adapters/drizzle/join-column-utils.ts +0 -39
  414. package/src/adapters/kysely/kysely-connection-pool.ts +0 -70
  415. package/src/adapters/kysely/kysely-query-builder.test.ts +0 -1344
  416. package/src/adapters/kysely/kysely-query-builder.ts +0 -666
  417. package/src/adapters/kysely/kysely-query-compiler.ts +0 -132
  418. package/src/adapters/kysely/kysely-query.test.ts +0 -498
  419. package/src/adapters/kysely/kysely-query.ts +0 -390
  420. package/src/adapters/kysely/kysely-shared.ts +0 -23
  421. package/src/adapters/kysely/kysely-uow-compiler.test.ts +0 -998
  422. package/src/adapters/kysely/kysely-uow-compiler.ts +0 -318
  423. package/src/adapters/kysely/kysely-uow-executor.ts +0 -145
  424. package/src/adapters/kysely/kysely-uow-joins.test.ts +0 -811
  425. package/src/adapters/kysely/migration/execute-base.ts +0 -256
  426. package/src/adapters/kysely/migration/execute-factory.ts +0 -53
  427. package/src/adapters/kysely/migration/execute-mssql.ts +0 -250
  428. package/src/adapters/kysely/migration/execute-mysql.ts +0 -211
  429. package/src/adapters/kysely/migration/execute-postgres.test.ts +0 -2657
  430. package/src/adapters/kysely/migration/execute-postgres.ts +0 -234
  431. package/src/adapters/kysely/migration/execute-sqlite.ts +0 -247
  432. package/src/adapters/kysely/migration/execute.ts +0 -50
  433. package/src/adapters/kysely/migration/kysely-migrator.test.ts +0 -261
  434. package/src/bind-services.test.ts +0 -214
  435. package/src/bind-services.ts +0 -37
  436. package/src/db-fragment.test.ts +0 -800
  437. package/src/fragment.ts +0 -727
  438. package/src/query/result-transform.ts +0 -271
  439. package/src/query/unit-of-work-multi-schema.test.ts +0 -64
  440. package/src/query/unit-of-work.test.ts +0 -943
  441. package/src/schema/serialize.ts +0 -396
  442. package/src/shared/settings-schema.ts +0 -61
  443. package/src/uow-context-integration.test.ts +0 -102
  444. package/src/uow-context.test.ts +0 -182
  445. /package/dist/query/{query.js → simple-query-interface.js} +0 -0
@@ -0,0 +1,887 @@
1
+ import { describe, it, expect, vi, expectTypeOf } from "vitest";
2
+ import { defineFragment } from "@fragno-dev/core";
3
+ import {
4
+ DatabaseFragmentDefinitionBuilder,
5
+ type ImplicitDatabaseDependencies,
6
+ } from "./db-fragment-definition-builder";
7
+ import { withDatabase } from "./with-database";
8
+ import { schema, column, idColumn } from "./schema/create";
9
+ import type { SimpleQueryInterface } from "./query/simple-query-interface";
10
+ import type { DatabaseAdapter } from "./adapters/adapters";
11
+
12
+ // Create a test schema
13
+ const testSchema = schema((s) => {
14
+ return s.addTable("users", (t) => {
15
+ return t
16
+ .addColumn("id", idColumn())
17
+ .addColumn("name", column("string"))
18
+ .addColumn("email", column("string"));
19
+ });
20
+ });
21
+
22
+ type TestSchema = typeof testSchema;
23
+
24
+ // Mock database adapter
25
+ function createMockAdapter(): DatabaseAdapter {
26
+ const mockdb = {
27
+ createUnitOfWork: vi.fn(() => ({
28
+ forSchema: vi.fn(),
29
+ executeRetrieve: vi.fn(),
30
+ executeMutations: vi.fn(),
31
+ })),
32
+ } as unknown as SimpleQueryInterface<TestSchema>;
33
+
34
+ return {
35
+ createQueryEngine: vi.fn(() => mockdb),
36
+ migrate: vi.fn(),
37
+ close: vi.fn(),
38
+ } as unknown as DatabaseAdapter;
39
+ }
40
+
41
+ describe("DatabaseFragmentDefinitionBuilder", () => {
42
+ describe("withDatabase helper", () => {
43
+ it("should create a database fragment builder", () => {
44
+ const baseBuilder = defineFragment("test-db-fragment");
45
+ const dbBuilder = withDatabase(testSchema)(baseBuilder);
46
+
47
+ expect(dbBuilder).toBeInstanceOf(DatabaseFragmentDefinitionBuilder);
48
+ });
49
+
50
+ it("should work with .extend() API", () => {
51
+ const dbBuilder = defineFragment("test-db-fragment").extend(withDatabase(testSchema));
52
+
53
+ expect(dbBuilder).toBeInstanceOf(DatabaseFragmentDefinitionBuilder);
54
+ });
55
+
56
+ it("should build with database-specific features", () => {
57
+ const _definition = defineFragment("test-db-fragment")
58
+ .withDependencies(() => ({ apiKey: "key" }))
59
+ .build();
60
+
61
+ // Convert to database builder
62
+ const dbDefinition = withDatabase(testSchema)(defineFragment("test-db-fragment"))
63
+ .withDependencies(() => ({
64
+ apiKey: "key",
65
+ customDep: "value",
66
+ }))
67
+ .build();
68
+
69
+ expect(dbDefinition.dependencies).toBeDefined();
70
+ expect(dbDefinition.createThisContext).toBeDefined();
71
+ });
72
+ });
73
+
74
+ describe("extend() API ordering", () => {
75
+ it("should extend before withDependencies", () => {
76
+ const mockAdapter = createMockAdapter();
77
+
78
+ const definition = defineFragment("test-frag")
79
+ .extend(withDatabase(testSchema))
80
+ .withDependencies(({ db }) => {
81
+ expect(db).toBeDefined();
82
+ return {
83
+ customDep: "value",
84
+ };
85
+ })
86
+ .build();
87
+
88
+ const deps = definition.dependencies!({
89
+ config: {},
90
+ options: { databaseAdapter: mockAdapter },
91
+ });
92
+
93
+ expect(deps.customDep).toBe("value");
94
+ expect(deps.db).toBeDefined();
95
+ expect(deps.schema).toBeDefined();
96
+ });
97
+
98
+ it("should extend after withDependencies", () => {
99
+ const mockAdapter = createMockAdapter();
100
+
101
+ const definition = defineFragment("test-frag")
102
+ .withDependencies(() => ({
103
+ apiKey: "key123",
104
+ }))
105
+ .extend(withDatabase(testSchema))
106
+ .build();
107
+
108
+ const deps = definition.dependencies!({
109
+ config: {},
110
+ options: { databaseAdapter: mockAdapter },
111
+ });
112
+
113
+ expect(deps.apiKey).toBe("key123");
114
+ expect(deps.db).toBeDefined();
115
+ expect(deps.schema).toBeDefined();
116
+ });
117
+
118
+ it("should extend before providesService", () => {
119
+ const mockAdapter = createMockAdapter();
120
+
121
+ const definition = defineFragment("test-frag")
122
+ .extend(withDatabase(testSchema))
123
+ .providesService("users", ({ deps }) => {
124
+ expect(deps.db).toBeDefined();
125
+ return {
126
+ list: () => "users list",
127
+ };
128
+ })
129
+ .build();
130
+
131
+ const deps = definition.dependencies!({
132
+ config: {},
133
+ options: { databaseAdapter: mockAdapter },
134
+ });
135
+
136
+ const userService = definition.namedServices!.users({
137
+ config: {},
138
+ options: { databaseAdapter: mockAdapter },
139
+ deps,
140
+ serviceDeps: {},
141
+ privateServices: {},
142
+ defineService: (svc) => svc,
143
+ });
144
+
145
+ expect(userService.list()).toBe("users list");
146
+ });
147
+
148
+ it("should extend after providesService", () => {
149
+ const mockAdapter = createMockAdapter();
150
+
151
+ const definition = defineFragment("test-frag")
152
+ .providesService("basic", () => ({
153
+ ping: () => "pong",
154
+ }))
155
+ .extend(withDatabase(testSchema))
156
+ .providesService("users", ({ deps }) => {
157
+ expect(deps.db).toBeDefined();
158
+ return {
159
+ list: () => "users list",
160
+ };
161
+ })
162
+ .build();
163
+
164
+ const deps = definition.dependencies!({
165
+ config: {},
166
+ options: { databaseAdapter: mockAdapter },
167
+ });
168
+
169
+ const basicService = definition.namedServices!.basic({
170
+ config: {},
171
+ options: { databaseAdapter: mockAdapter },
172
+ deps,
173
+ serviceDeps: {},
174
+ privateServices: {},
175
+ defineService: (svc) => svc,
176
+ });
177
+
178
+ const userService = definition.namedServices!.users({
179
+ config: {},
180
+ options: { databaseAdapter: mockAdapter },
181
+ deps,
182
+ serviceDeps: {},
183
+ privateServices: {},
184
+ defineService: (svc) => svc,
185
+ });
186
+
187
+ expect(basicService.ping()).toBe("pong");
188
+ expect(userService.list()).toBe("users list");
189
+ });
190
+
191
+ it("should extend before usesService", () => {
192
+ interface EmailService {
193
+ send: (to: string) => void;
194
+ }
195
+
196
+ const definition = defineFragment("test-frag")
197
+ .extend(withDatabase(testSchema))
198
+ .usesService<"email", EmailService>("email")
199
+ .build();
200
+
201
+ expect(definition.serviceDependencies).toBeDefined();
202
+ expect(definition.serviceDependencies!.email).toEqual({
203
+ name: "email",
204
+ required: true,
205
+ });
206
+ });
207
+
208
+ it("should extend after usesService", () => {
209
+ interface LogService {
210
+ log: (msg: string) => void;
211
+ }
212
+
213
+ const definition = defineFragment("test-frag")
214
+ .usesService<"logger", LogService>("logger")
215
+ .extend(withDatabase(testSchema))
216
+ .build();
217
+
218
+ expect(definition.serviceDependencies).toBeDefined();
219
+ expect(definition.serviceDependencies!.logger).toEqual({
220
+ name: "logger",
221
+ required: true,
222
+ });
223
+ });
224
+
225
+ it("should extend before providesBaseService", () => {
226
+ const mockAdapter = createMockAdapter();
227
+
228
+ const definition = defineFragment("test-frag")
229
+ .extend(withDatabase(testSchema))
230
+ .providesBaseService(({ deps }) => {
231
+ expect(deps.db).toBeDefined();
232
+ return {
233
+ healthCheck: () => "healthy",
234
+ };
235
+ })
236
+ .build();
237
+
238
+ const deps = definition.dependencies!({
239
+ config: {},
240
+ options: { databaseAdapter: mockAdapter },
241
+ });
242
+
243
+ const services = definition.baseServices!({
244
+ config: {},
245
+ options: { databaseAdapter: mockAdapter },
246
+ deps,
247
+ serviceDeps: {},
248
+ privateServices: {},
249
+ defineService: (svc) => svc,
250
+ });
251
+
252
+ expect(services.healthCheck()).toBe("healthy");
253
+ });
254
+
255
+ it("should extend after providesBaseService replaces previous base service", () => {
256
+ const mockAdapter = createMockAdapter();
257
+
258
+ const definition = defineFragment("test-frag")
259
+ .providesBaseService(() => ({
260
+ ping: () => "pong",
261
+ }))
262
+ .extend(withDatabase(testSchema))
263
+ .providesBaseService(({ deps }) => {
264
+ expect(deps.db).toBeDefined();
265
+ return {
266
+ status: () => "ok",
267
+ };
268
+ })
269
+ .build();
270
+
271
+ const deps = definition.dependencies!({
272
+ config: {},
273
+ options: { databaseAdapter: mockAdapter },
274
+ });
275
+
276
+ const services = definition.baseServices!({
277
+ config: {},
278
+ options: { databaseAdapter: mockAdapter },
279
+ deps,
280
+ serviceDeps: {},
281
+ privateServices: {},
282
+ defineService: (svc) => svc,
283
+ });
284
+
285
+ // Second providesBaseService replaces the first one
286
+ expect(services.status()).toBe("ok");
287
+ expect(services).not.toHaveProperty("ping");
288
+ });
289
+
290
+ it("should chain multiple operations after extend", () => {
291
+ const mockAdapter = createMockAdapter();
292
+
293
+ interface LogService {
294
+ log: (msg: string) => void;
295
+ }
296
+
297
+ const definition = defineFragment("test-frag")
298
+ .extend(withDatabase(testSchema))
299
+ .withDependencies(({ db }) => {
300
+ expect(db).toBeDefined();
301
+ return {
302
+ apiKey: "secret",
303
+ };
304
+ })
305
+ .usesOptionalService<"logger", LogService>("logger")
306
+ .providesBaseService(({ deps, serviceDeps }) => ({
307
+ init: () => {
308
+ if (serviceDeps.logger) {
309
+ serviceDeps.logger.log(`Initialized with key: ${deps.apiKey}`);
310
+ }
311
+ return "initialized";
312
+ },
313
+ }))
314
+ .providesService("data", ({ deps }) => {
315
+ expect(deps.db).toBeDefined();
316
+ return {
317
+ fetch: () => `Fetching with ${deps.apiKey}`,
318
+ };
319
+ })
320
+ .build();
321
+
322
+ const logs: string[] = [];
323
+ const deps = definition.dependencies!({
324
+ config: {},
325
+ options: { databaseAdapter: mockAdapter },
326
+ });
327
+
328
+ const services = definition.baseServices!({
329
+ config: {},
330
+ options: { databaseAdapter: mockAdapter },
331
+ deps,
332
+ serviceDeps: {
333
+ logger: {
334
+ log: (msg) => logs.push(msg),
335
+ },
336
+ },
337
+ privateServices: {},
338
+ defineService: (svc) => svc,
339
+ });
340
+
341
+ expect(services.init()).toBe("initialized");
342
+ expect(logs).toContain("Initialized with key: secret");
343
+
344
+ const dataService = definition.namedServices!.data({
345
+ config: {},
346
+ options: { databaseAdapter: mockAdapter },
347
+ deps,
348
+ serviceDeps: {
349
+ logger: {
350
+ log: (msg) => logs.push(msg),
351
+ },
352
+ },
353
+ privateServices: {},
354
+ defineService: (svc) => svc,
355
+ });
356
+
357
+ expect(dataService.fetch()).toBe("Fetching with secret");
358
+ });
359
+
360
+ it("should support complex ordering: deps -> extend -> deps replaces previous", () => {
361
+ const mockAdapter = createMockAdapter();
362
+
363
+ const definition = defineFragment("test-frag")
364
+ .withDependencies(() => ({
365
+ baseConfig: "config1",
366
+ }))
367
+ .extend(withDatabase(testSchema))
368
+ .withDependencies(({ db }) => {
369
+ expect(db).toBeDefined();
370
+ // Second withDependencies replaces the first one
371
+ return {
372
+ enhancedConfig: "enhanced",
373
+ };
374
+ })
375
+ .providesService("combined", ({ deps }) => {
376
+ expect(deps.db).toBeDefined();
377
+ return {
378
+ getConfig: () => deps.enhancedConfig,
379
+ };
380
+ })
381
+ .build();
382
+
383
+ const deps = definition.dependencies!({
384
+ config: {},
385
+ options: { databaseAdapter: mockAdapter },
386
+ });
387
+
388
+ // Only the second withDependencies is used (plus implicit deps)
389
+ expect(deps).not.toHaveProperty("baseConfig");
390
+ expect(deps.enhancedConfig).toBe("enhanced");
391
+ expect(deps.db).toBeDefined();
392
+
393
+ const combinedService = definition.namedServices!.combined({
394
+ config: {},
395
+ options: { databaseAdapter: mockAdapter },
396
+ deps,
397
+ serviceDeps: {},
398
+ privateServices: {},
399
+ defineService: (svc) => svc,
400
+ });
401
+
402
+ expect(combinedService.getConfig()).toBe("enhanced");
403
+ });
404
+
405
+ it("should preserve type safety with typed config across extend", () => {
406
+ interface MyConfig {
407
+ dbUrl: string;
408
+ timeout: number;
409
+ }
410
+
411
+ const mockAdapter = createMockAdapter();
412
+
413
+ const definition = defineFragment<MyConfig>("test-frag")
414
+ .withDependencies(({ config }) => {
415
+ expectTypeOf(config).toExtend<MyConfig>();
416
+ return {
417
+ connectionTimeout: config.timeout,
418
+ };
419
+ })
420
+ .extend(withDatabase(testSchema))
421
+ .withDependencies(({ config, db }) => {
422
+ expectTypeOf(config).toExtend<MyConfig>();
423
+ expect(db).toBeDefined();
424
+ // Second withDependencies replaces the first, so combine them here
425
+ return {
426
+ connectionTimeout: config.timeout,
427
+ dbConnectionString: config.dbUrl,
428
+ };
429
+ })
430
+ .build();
431
+
432
+ const deps = definition.dependencies!({
433
+ config: { dbUrl: "postgres://localhost", timeout: 5000 },
434
+ options: { databaseAdapter: mockAdapter },
435
+ });
436
+
437
+ expect(deps.connectionTimeout).toBe(5000);
438
+ expect(deps.dbConnectionString).toBe("postgres://localhost");
439
+ expect(deps.db).toBeDefined();
440
+ });
441
+
442
+ it("recommended pattern: extend first then configure", () => {
443
+ const mockAdapter = createMockAdapter();
444
+
445
+ interface LogService {
446
+ log: (msg: string) => void;
447
+ }
448
+
449
+ interface AuthService {
450
+ checkAuth: (token: string) => boolean;
451
+ }
452
+
453
+ // Best practice: .extend() early, then add all your features
454
+ const definition = defineFragment("best-practice")
455
+ .extend(withDatabase(testSchema))
456
+ .withDependencies(({ db }) => {
457
+ expect(db).toBeDefined();
458
+ return {
459
+ apiKey: "secret",
460
+ timeout: 5000,
461
+ };
462
+ })
463
+ .usesService<"logger", LogService>("logger")
464
+ .usesOptionalService<"auth", AuthService>("auth")
465
+ .providesBaseService(({ deps }) => ({
466
+ healthCheck: () => {
467
+ expect(deps.db).toBeDefined();
468
+ return `OK (timeout: ${deps.timeout})`;
469
+ },
470
+ }))
471
+ .providesService("users", ({ deps, serviceDeps }) => ({
472
+ list: () => {
473
+ if (serviceDeps.logger) {
474
+ serviceDeps.logger.log("Listing users");
475
+ }
476
+ return `Users (key: ${deps.apiKey})`;
477
+ },
478
+ }))
479
+ .build();
480
+
481
+ const logs: string[] = [];
482
+ const deps = definition.dependencies!({
483
+ config: {},
484
+ options: { databaseAdapter: mockAdapter },
485
+ });
486
+
487
+ // Verify all dependencies including implicit ones
488
+ expect(deps.apiKey).toBe("secret");
489
+ expect(deps.timeout).toBe(5000);
490
+ expect(deps.db).toBeDefined();
491
+ expect(deps.schema).toBeDefined();
492
+ expect(deps.createUnitOfWork).toBeDefined();
493
+
494
+ const baseServices = definition.baseServices!({
495
+ config: {},
496
+ options: { databaseAdapter: mockAdapter },
497
+ deps,
498
+ serviceDeps: {
499
+ logger: { log: (msg) => logs.push(msg) },
500
+ auth: undefined, // Optional service
501
+ },
502
+ privateServices: {},
503
+ defineService: (svc) => svc,
504
+ });
505
+
506
+ expect(baseServices.healthCheck()).toBe("OK (timeout: 5000)");
507
+
508
+ const userService = definition.namedServices!.users({
509
+ config: {},
510
+ options: { databaseAdapter: mockAdapter },
511
+ deps,
512
+ serviceDeps: {
513
+ logger: { log: (msg) => logs.push(msg) },
514
+ auth: undefined, // Optional service
515
+ },
516
+ privateServices: {},
517
+ defineService: (svc) => svc,
518
+ });
519
+
520
+ expect(userService.list()).toBe("Users (key: secret)");
521
+ expect(logs).toContain("Listing users");
522
+ });
523
+ });
524
+
525
+ describe("withDependencies", () => {
526
+ it("should inject database context into dependencies", () => {
527
+ const mockAdapter = createMockAdapter();
528
+
529
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
530
+ .withDependencies(({ db, databaseAdapter }) => {
531
+ expect(db).toBeDefined();
532
+ expect(databaseAdapter).toBeDefined();
533
+ return {
534
+ customDep: "test",
535
+ };
536
+ })
537
+ .build();
538
+
539
+ expect(definition.dependencies).toBeDefined();
540
+
541
+ // Call dependencies with mock adapter
542
+ const deps = definition.dependencies!({
543
+ config: {},
544
+ options: { databaseAdapter: mockAdapter },
545
+ });
546
+
547
+ // Should have implicit deps
548
+ expect(deps).toHaveProperty("db");
549
+ expect(deps).toHaveProperty("schema");
550
+ expect(deps).toHaveProperty("createUnitOfWork");
551
+ expect(deps).toHaveProperty("customDep");
552
+ expect(deps.customDep).toBe("test");
553
+ });
554
+
555
+ it("should provide implicit database dependencies", () => {
556
+ const mockAdapter = createMockAdapter();
557
+
558
+ const definition = withDatabase(testSchema)(defineFragment("db-frag")).build();
559
+
560
+ // Even without explicit dependencies, should add implicit ones
561
+ expect(definition.dependencies).toBeDefined();
562
+
563
+ const deps = definition.dependencies!({
564
+ config: {},
565
+ options: { databaseAdapter: mockAdapter },
566
+ });
567
+
568
+ // Check implicit dependencies structure
569
+ expect(deps.db).toBeDefined();
570
+ expect(deps.schema).toBeDefined();
571
+ expect(typeof deps.createUnitOfWork).toBe("function");
572
+ });
573
+ });
574
+
575
+ describe("providesBaseService", () => {
576
+ it("should inject database context into base services", () => {
577
+ const mockAdapter = createMockAdapter();
578
+
579
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
580
+ .withDependencies(() => ({ apiKey: "key" }))
581
+ .providesBaseService(({ deps, defineService }) => {
582
+ expect(deps.apiKey).toBe("key");
583
+ expect(deps.db).toBeDefined();
584
+ expect(defineService).toBeDefined();
585
+
586
+ return defineService({
587
+ getUsers: function () {
588
+ return "users";
589
+ },
590
+ });
591
+ })
592
+ .build();
593
+
594
+ expect(definition.baseServices).toBeDefined();
595
+
596
+ // Get implicit deps first
597
+ const deps = definition.dependencies!({
598
+ config: {},
599
+ options: { databaseAdapter: mockAdapter },
600
+ });
601
+
602
+ const services = definition.baseServices!({
603
+ config: {},
604
+ options: { databaseAdapter: mockAdapter },
605
+ deps,
606
+ serviceDeps: {},
607
+ privateServices: {},
608
+ defineService: (svc) => svc,
609
+ });
610
+
611
+ expect(services.getUsers()).toBe("users");
612
+ });
613
+ });
614
+
615
+ describe("providesService", () => {
616
+ it("should inject database context into named services", () => {
617
+ const mockAdapter = createMockAdapter();
618
+
619
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
620
+ .withDependencies(() => ({ apiKey: "key" }))
621
+ .providesService("users", ({ deps }) => ({
622
+ list: () => `Listing users with ${deps.apiKey}`,
623
+ create: (name: string) => `Creating user ${name}`,
624
+ }))
625
+ .build();
626
+
627
+ expect(definition.namedServices).toBeDefined();
628
+ expect(definition.namedServices!.users).toBeDefined();
629
+
630
+ // Get implicit deps
631
+ const deps = definition.dependencies!({
632
+ config: {},
633
+ options: { databaseAdapter: mockAdapter },
634
+ });
635
+
636
+ const userService = definition.namedServices!.users({
637
+ config: {},
638
+ options: { databaseAdapter: mockAdapter },
639
+ deps,
640
+ serviceDeps: {},
641
+ privateServices: {},
642
+ defineService: (svc) => svc,
643
+ });
644
+
645
+ expect(userService.list()).toBe("Listing users with key");
646
+ expect(userService.create("John")).toBe("Creating user John");
647
+ });
648
+ });
649
+
650
+ describe("usesService", () => {
651
+ it("should delegate service dependencies to base builder", () => {
652
+ interface EmailService {
653
+ send: (to: string) => void;
654
+ }
655
+
656
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
657
+ .usesService<"email", EmailService>("email")
658
+ .build();
659
+
660
+ expect(definition.serviceDependencies).toBeDefined();
661
+ expect(definition.serviceDependencies!.email).toEqual({
662
+ name: "email",
663
+ required: true,
664
+ });
665
+ });
666
+
667
+ it("should support optional service dependencies", () => {
668
+ interface LogService {
669
+ log: (msg: string) => void;
670
+ }
671
+
672
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
673
+ .usesOptionalService<"logger", LogService>("logger")
674
+ .build();
675
+
676
+ expect(definition.serviceDependencies!.logger.required).toBe(false);
677
+ });
678
+ });
679
+
680
+ describe("createRequestStorage and createThisContext", () => {
681
+ it("should create request storage with UnitOfWork", () => {
682
+ const mockAdapter = createMockAdapter();
683
+
684
+ const definition = withDatabase(testSchema)(defineFragment("db-frag")).build();
685
+
686
+ expect(definition.createRequestStorage).toBeDefined();
687
+ expect(definition.createThisContext).toBeDefined();
688
+
689
+ // Create storage
690
+ const storage = definition.createRequestStorage!({
691
+ config: {},
692
+ options: { databaseAdapter: mockAdapter },
693
+ deps: {} as any, // eslint-disable-line @typescript-eslint/no-explicit-any
694
+ });
695
+
696
+ expect(storage).toBeDefined();
697
+ expect(storage.uow).toBeDefined();
698
+ });
699
+
700
+ it("should provide DatabaseServiceContext with forSchema and DatabaseHandlerContext with executeRestrictedUnitOfWork", () => {
701
+ const mockAdapter = createMockAdapter();
702
+
703
+ const definition = withDatabase(testSchema)(defineFragment("db-frag")).build();
704
+
705
+ // Create a mock storage
706
+ const mockStorage = {
707
+ getStore: () => ({
708
+ uow: mockAdapter.createQueryEngine(testSchema, "test").createUnitOfWork(),
709
+ }),
710
+ } as any; // eslint-disable-line @typescript-eslint/no-explicit-any
711
+
712
+ const contexts = definition.createThisContext!({
713
+ config: {},
714
+ options: { databaseAdapter: mockAdapter },
715
+ deps: {} as any, // eslint-disable-line @typescript-eslint/no-explicit-any
716
+ storage: mockStorage,
717
+ });
718
+
719
+ expect(typeof contexts.serviceContext.uow).toBe("function");
720
+ expect(typeof contexts.handlerContext.uow).toBe("function");
721
+ });
722
+ });
723
+
724
+ describe("complex database fragment", () => {
725
+ it("should support full database fragment definition", () => {
726
+ interface Config {
727
+ dbConnectionString: string;
728
+ debug: boolean;
729
+ }
730
+
731
+ interface LogService {
732
+ log: (msg: string) => void;
733
+ }
734
+
735
+ const mockAdapter = createMockAdapter();
736
+
737
+ const definition = withDatabase(
738
+ testSchema,
739
+ "my-namespace",
740
+ )(defineFragment<Config>("complex-db-frag"))
741
+ .withDependencies(({ config }) => ({
742
+ connectionString: config.dbConnectionString,
743
+ debug: config.debug,
744
+ }))
745
+ .usesOptionalService<"logger", LogService>("logger")
746
+ .providesBaseService(({ serviceDeps }) => ({
747
+ healthCheck: () => {
748
+ if (serviceDeps.logger) {
749
+ serviceDeps.logger.log("Health check");
750
+ }
751
+ return "OK";
752
+ },
753
+ }))
754
+ .providesService("users", () => ({
755
+ getAll: () => "all users",
756
+ create: (name: string) => `Created user ${name}`,
757
+ }))
758
+ .build();
759
+
760
+ expect(definition.name).toBe("complex-db-frag");
761
+ expect(definition.dependencies).toBeDefined();
762
+ expect(definition.baseServices).toBeDefined();
763
+ expect(definition.namedServices).toBeDefined();
764
+ expect(definition.serviceDependencies).toBeDefined();
765
+ expect(definition.createThisContext).toBeDefined();
766
+
767
+ // Test execution
768
+ const logs: string[] = [];
769
+ const deps = definition.dependencies!({
770
+ config: { dbConnectionString: "postgres://localhost", debug: true },
771
+ options: { databaseAdapter: mockAdapter },
772
+ });
773
+
774
+ const services = definition.baseServices!({
775
+ config: { dbConnectionString: "postgres://localhost", debug: true },
776
+ options: { databaseAdapter: mockAdapter },
777
+ deps,
778
+ serviceDeps: {
779
+ logger: {
780
+ log: (msg) => logs.push(msg),
781
+ },
782
+ },
783
+ privateServices: {},
784
+ defineService: (svc) => svc,
785
+ });
786
+
787
+ expect(services.healthCheck()).toBe("OK");
788
+ expect(logs).toContain("Health check");
789
+
790
+ const userService = definition.namedServices!.users({
791
+ config: { dbConnectionString: "postgres://localhost", debug: true },
792
+ options: { databaseAdapter: mockAdapter },
793
+ deps,
794
+ serviceDeps: {
795
+ logger: {
796
+ log: (msg) => logs.push(msg),
797
+ },
798
+ },
799
+ privateServices: {},
800
+ defineService: (svc) => svc,
801
+ });
802
+
803
+ expect(userService.getAll()).toBe("all users");
804
+ expect(userService.create("Alice")).toBe("Created user Alice");
805
+ });
806
+ });
807
+
808
+ describe("error handling", () => {
809
+ it("should throw error if database adapter is missing", () => {
810
+ const definition = withDatabase(testSchema)(defineFragment("db-frag")).build();
811
+
812
+ expect(() => {
813
+ definition.dependencies!({
814
+ config: {},
815
+ // @ts-expect-error No databaseAdapter - intentionally invalid for runtime error test
816
+ options: {},
817
+ });
818
+ }).toThrow("Database fragment requires a database adapter");
819
+ });
820
+ });
821
+
822
+ describe("type inference", () => {
823
+ it("should correctly infer implicit dependencies", () => {
824
+ const mockAdapter = createMockAdapter();
825
+
826
+ const definition = withDatabase(testSchema)(defineFragment("db-frag"))
827
+ .withDependencies(() => ({
828
+ customDep: "test",
829
+ }))
830
+ .build();
831
+
832
+ const deps = definition.dependencies!({
833
+ config: {},
834
+ options: { databaseAdapter: mockAdapter },
835
+ });
836
+
837
+ expectTypeOf(deps).toExtend<
838
+ {
839
+ customDep: string;
840
+ } & ImplicitDatabaseDependencies<TestSchema>
841
+ >();
842
+
843
+ expect(deps.customDep).toBe("test");
844
+ expect(deps.db).toBeDefined();
845
+ });
846
+ });
847
+
848
+ // describe("defineService", () => {
849
+ // it("getUnitOfWork should be available in defineService", () => {
850
+ // const mockAdapter = createMockAdapter();
851
+
852
+ // const definition = withDatabase(testSchema)(defineFragment("db-frag"))
853
+ // .withDependencies(() => ({ apiKey: "key" }))
854
+ // .providesBaseService(({ deps, defineService }) => {
855
+ // expect(deps.apiKey).toBe("key");
856
+ // expect(deps.db).toBeDefined();
857
+ // expect(defineService).toBeDefined();
858
+
859
+ // return defineService({
860
+ // getUsers: function () {
861
+ // return typeof this.getUnitOfWork;
862
+ // },
863
+ // });
864
+ // })
865
+ // .build();
866
+
867
+ // expect(definition.baseServices).toBeDefined();
868
+
869
+ // // Get implicit deps first
870
+ // const deps = definition.dependencies!({
871
+ // config: {},
872
+ // options: { databaseAdapter: mockAdapter },
873
+ // });
874
+
875
+ // const services = definition.baseServices!({
876
+ // config: {},
877
+ // options: { databaseAdapter: mockAdapter },
878
+ // deps,
879
+ // serviceDeps: {},
880
+ // privateServices: {},
881
+ // defineService: (svc) => svc,
882
+ // });
883
+
884
+ // expect(services.getUsers()).toBe("function");
885
+ // });
886
+ // });
887
+ });