sonamu 0.4.13 → 0.5.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 (371) hide show
  1. package/.swcrc +15 -0
  2. package/dist/api/base-frame.d.ts +8 -0
  3. package/dist/api/base-frame.d.ts.map +1 -0
  4. package/dist/api/base-frame.js +2 -0
  5. package/dist/api/base-frame.js.map +1 -0
  6. package/dist/api/caster.d.ts +5 -0
  7. package/dist/api/caster.d.ts.map +1 -0
  8. package/dist/api/caster.js +2 -0
  9. package/dist/api/caster.js.map +1 -0
  10. package/dist/api/code-converters.d.ts +23 -0
  11. package/dist/api/code-converters.d.ts.map +1 -0
  12. package/dist/api/code-converters.js +2 -0
  13. package/dist/api/code-converters.js.map +1 -0
  14. package/dist/api/context.d.ts +16 -0
  15. package/dist/api/context.d.ts.map +1 -0
  16. package/dist/api/context.js +2 -0
  17. package/dist/api/context.js.map +1 -0
  18. package/dist/api/decorators.d.ts +50 -0
  19. package/dist/api/decorators.d.ts.map +1 -0
  20. package/dist/api/decorators.js +2 -0
  21. package/dist/api/decorators.js.map +1 -0
  22. package/dist/api/index.d.ts +8 -0
  23. package/dist/api/index.d.ts.map +1 -0
  24. package/dist/api/index.js +2 -0
  25. package/dist/api/index.js.map +1 -0
  26. package/dist/api/sonamu.d.ts +83 -0
  27. package/dist/api/sonamu.d.ts.map +1 -0
  28. package/dist/api/sonamu.js +2 -0
  29. package/dist/api/sonamu.js.map +1 -0
  30. package/dist/api/sonamu.types.d.ts +30 -0
  31. package/dist/api/sonamu.types.d.ts.map +1 -0
  32. package/dist/api/sonamu.types.js +2 -0
  33. package/dist/api/sonamu.types.js.map +1 -0
  34. package/dist/bin/build-config.d.ts +5 -0
  35. package/dist/bin/build-config.d.ts.map +1 -0
  36. package/dist/bin/build-config.js +2 -0
  37. package/dist/bin/build-config.js.map +1 -0
  38. package/dist/bin/cli-wrapper.d.ts +2 -0
  39. package/dist/bin/cli-wrapper.d.ts.map +1 -0
  40. package/dist/bin/cli-wrapper.js +1 -38
  41. package/dist/bin/cli-wrapper.js.map +1 -1
  42. package/dist/bin/cli.d.ts +2 -2
  43. package/dist/bin/cli.d.ts.map +1 -0
  44. package/dist/bin/cli.js +1 -903
  45. package/dist/bin/cli.js.map +1 -1
  46. package/dist/bin/cli.mjs +2 -2
  47. package/dist/{chunk-DMJSNO2L.js → chunk-2WAC2GER.js} +44 -44
  48. package/dist/{chunk-DMJSNO2L.js.map → chunk-2WAC2GER.js.map} +1 -1
  49. package/dist/{chunk-NI37CY4T.mjs → chunk-C3IPIF6O.mjs} +2 -2
  50. package/dist/{chunk-DYFCACHD.js → chunk-EXHKSVTE.js} +7 -7
  51. package/dist/{chunk-QJFHDCBN.mjs → chunk-FCERKIIF.mjs} +2 -2
  52. package/dist/chunk-FCERKIIF.mjs.map +1 -0
  53. package/dist/{chunk-DDJ7T4MA.mjs → chunk-HGIBJYOU.mjs} +2 -2
  54. package/dist/{chunk-NIFOTHBW.mjs → chunk-JKSOJRQA.mjs} +2 -2
  55. package/dist/{chunk-CXAVBVKC.js → chunk-OTKKFP3Y.js} +100 -100
  56. package/dist/{chunk-J6S43O7G.js → chunk-UZ2IY5VE.js} +4 -4
  57. package/dist/database/_batch_update.d.ts +15 -0
  58. package/dist/database/_batch_update.d.ts.map +1 -0
  59. package/dist/database/_batch_update.js +2 -0
  60. package/dist/database/_batch_update.js.map +1 -0
  61. package/dist/database/base-model.d.ts +41 -0
  62. package/dist/database/base-model.d.ts.map +1 -0
  63. package/dist/database/base-model.js +2 -0
  64. package/dist/database/base-model.js.map +1 -0
  65. package/dist/database/code-generator.d.ts +13 -0
  66. package/dist/database/code-generator.d.ts.map +1 -0
  67. package/dist/database/code-generator.js +2 -0
  68. package/dist/database/code-generator.js.map +1 -0
  69. package/dist/database/db.d.ts +40 -0
  70. package/dist/database/db.d.ts.map +1 -0
  71. package/dist/database/db.js +2 -0
  72. package/dist/database/db.js.map +1 -0
  73. package/dist/database/drivers/knex/base-model.js +8 -8
  74. package/dist/database/drivers/knex/base-model.mjs +3 -3
  75. package/dist/database/drivers/kysely/base-model.js +9 -9
  76. package/dist/database/drivers/kysely/base-model.mjs +3 -3
  77. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts +2 -0
  78. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts.map +1 -0
  79. package/dist/database/knex-plugins/knex-on-duplicate-update.js +2 -0
  80. package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +1 -0
  81. package/dist/database/puri-wrapper.d.ts +34 -0
  82. package/dist/database/puri-wrapper.d.ts.map +1 -0
  83. package/dist/database/puri-wrapper.js +2 -0
  84. package/dist/database/puri-wrapper.js.map +1 -0
  85. package/dist/database/puri.d.ts +83 -0
  86. package/dist/database/puri.d.ts.map +1 -0
  87. package/dist/database/puri.js +2 -0
  88. package/dist/database/puri.js.map +1 -0
  89. package/dist/database/puri.types.d.ts +60 -0
  90. package/dist/database/puri.types.d.ts.map +1 -0
  91. package/dist/database/puri.types.js +2 -0
  92. package/dist/database/puri.types.js.map +1 -0
  93. package/dist/database/transaction-context.d.ts +9 -0
  94. package/dist/database/transaction-context.d.ts.map +1 -0
  95. package/dist/database/transaction-context.js +2 -0
  96. package/dist/database/transaction-context.js.map +1 -0
  97. package/dist/database/types.d.ts +39 -0
  98. package/dist/database/types.d.ts.map +1 -0
  99. package/dist/database/types.js +2 -0
  100. package/dist/database/types.js.map +1 -0
  101. package/dist/database/upsert-builder.d.ts +34 -0
  102. package/dist/database/upsert-builder.d.ts.map +1 -0
  103. package/dist/database/upsert-builder.js +2 -0
  104. package/dist/database/upsert-builder.js.map +1 -0
  105. package/dist/entity/entity-manager.d.ts +32 -0
  106. package/dist/entity/entity-manager.d.ts.map +1 -0
  107. package/dist/entity/entity-manager.js +2 -0
  108. package/dist/entity/entity-manager.js.map +1 -0
  109. package/dist/entity/entity-utils.d.ts +61 -0
  110. package/dist/entity/entity-utils.d.ts.map +1 -0
  111. package/dist/entity/entity-utils.js +2 -0
  112. package/dist/entity/entity-utils.js.map +1 -0
  113. package/dist/entity/entity.d.ts +62 -0
  114. package/dist/entity/entity.d.ts.map +1 -0
  115. package/dist/entity/entity.js +2 -0
  116. package/dist/entity/entity.js.map +1 -0
  117. package/dist/entity/migrator.d.ts +135 -0
  118. package/dist/entity/migrator.d.ts.map +1 -0
  119. package/dist/entity/migrator.js +2 -0
  120. package/dist/entity/migrator.js.map +1 -0
  121. package/dist/exceptions/error-handler.d.ts +3 -0
  122. package/dist/exceptions/error-handler.d.ts.map +1 -0
  123. package/dist/exceptions/error-handler.js +2 -0
  124. package/dist/exceptions/error-handler.js.map +1 -0
  125. package/dist/exceptions/so-exceptions.d.ts +48 -0
  126. package/dist/exceptions/so-exceptions.d.ts.map +1 -0
  127. package/dist/exceptions/so-exceptions.js +2 -0
  128. package/dist/exceptions/so-exceptions.js.map +1 -0
  129. package/dist/file-storage/driver.d.ts +45 -0
  130. package/dist/file-storage/driver.d.ts.map +1 -0
  131. package/dist/file-storage/driver.js +2 -0
  132. package/dist/file-storage/driver.js.map +1 -0
  133. package/dist/file-storage/file-storage.d.ts +50 -0
  134. package/dist/file-storage/file-storage.d.ts.map +1 -0
  135. package/dist/file-storage/file-storage.js +2 -0
  136. package/dist/file-storage/file-storage.js.map +1 -0
  137. package/dist/index.d.ts +22 -813
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +1 -433
  140. package/dist/index.js.map +1 -1
  141. package/dist/index.mjs +3 -3
  142. package/dist/migration/code-generation.d.ts +15 -0
  143. package/dist/migration/code-generation.d.ts.map +1 -0
  144. package/dist/migration/code-generation.js +2 -0
  145. package/dist/migration/code-generation.js.map +1 -0
  146. package/dist/migration/migration-set.d.ts +17 -0
  147. package/dist/migration/migration-set.d.ts.map +1 -0
  148. package/dist/migration/migration-set.js +2 -0
  149. package/dist/migration/migration-set.js.map +1 -0
  150. package/dist/migration/migrator.d.ts +130 -0
  151. package/dist/migration/migrator.d.ts.map +1 -0
  152. package/dist/migration/migrator.js +2 -0
  153. package/dist/migration/migrator.js.map +1 -0
  154. package/dist/migration/types.d.ts +52 -0
  155. package/dist/migration/types.d.ts.map +1 -0
  156. package/dist/migration/types.js +2 -0
  157. package/dist/migration/types.js.map +1 -0
  158. package/dist/smd/smd-manager.d.ts +28 -0
  159. package/dist/smd/smd-manager.d.ts.map +1 -0
  160. package/dist/smd/smd-manager.js +2 -0
  161. package/dist/smd/smd-manager.js.map +1 -0
  162. package/dist/smd/smd.d.ts +40 -0
  163. package/dist/smd/smd.d.ts.map +1 -0
  164. package/dist/smd/smd.js +2 -0
  165. package/dist/smd/smd.js.map +1 -0
  166. package/dist/syncer/index.d.ts +2 -0
  167. package/dist/syncer/index.d.ts.map +1 -0
  168. package/dist/syncer/index.js +2 -0
  169. package/dist/syncer/index.js.map +1 -0
  170. package/dist/syncer/syncer.d.ts +127 -0
  171. package/dist/syncer/syncer.d.ts.map +1 -0
  172. package/dist/syncer/syncer.js +2 -0
  173. package/dist/syncer/syncer.js.map +1 -0
  174. package/dist/templates/base-template.d.ts +13 -0
  175. package/dist/templates/base-template.d.ts.map +1 -0
  176. package/dist/templates/base-template.js +2 -0
  177. package/dist/templates/base-template.js.map +1 -0
  178. package/dist/templates/entity.template.d.ts +17 -0
  179. package/dist/templates/entity.template.d.ts.map +1 -0
  180. package/dist/templates/entity.template.js +2 -0
  181. package/dist/templates/entity.template.js.map +1 -0
  182. package/dist/templates/generated.template.d.ts +27 -0
  183. package/dist/templates/generated.template.d.ts.map +1 -0
  184. package/dist/templates/generated.template.js +2 -0
  185. package/dist/templates/generated.template.js.map +1 -0
  186. package/dist/templates/generated_http.template.d.ts +24 -0
  187. package/dist/templates/generated_http.template.d.ts.map +1 -0
  188. package/dist/templates/generated_http.template.js +2 -0
  189. package/dist/templates/generated_http.template.js.map +1 -0
  190. package/dist/templates/generated_sso.template.d.ts +20 -0
  191. package/dist/templates/generated_sso.template.d.ts.map +1 -0
  192. package/dist/templates/generated_sso.template.js +2 -0
  193. package/dist/templates/generated_sso.template.js.map +1 -0
  194. package/dist/templates/index.d.ts +2 -0
  195. package/dist/templates/index.d.ts.map +1 -0
  196. package/dist/templates/index.js +2 -0
  197. package/dist/templates/index.js.map +1 -0
  198. package/dist/templates/init_types.template.d.ts +17 -0
  199. package/dist/templates/init_types.template.d.ts.map +1 -0
  200. package/dist/templates/init_types.template.js +2 -0
  201. package/dist/templates/init_types.template.js.map +1 -0
  202. package/dist/templates/model.template.d.ts +17 -0
  203. package/dist/templates/model.template.d.ts.map +1 -0
  204. package/dist/templates/model.template.js +2 -0
  205. package/dist/templates/model.template.js.map +1 -0
  206. package/dist/templates/model_test.template.d.ts +17 -0
  207. package/dist/templates/model_test.template.d.ts.map +1 -0
  208. package/dist/templates/model_test.template.js +2 -0
  209. package/dist/templates/model_test.template.js.map +1 -0
  210. package/dist/templates/service.template.d.ts +29 -0
  211. package/dist/templates/service.template.d.ts.map +1 -0
  212. package/dist/templates/service.template.js +2 -0
  213. package/dist/templates/service.template.js.map +1 -0
  214. package/dist/templates/view_enums_buttonset.template.d.ts +17 -0
  215. package/dist/templates/view_enums_buttonset.template.d.ts.map +1 -0
  216. package/dist/templates/view_enums_buttonset.template.js +2 -0
  217. package/dist/templates/view_enums_buttonset.template.js.map +1 -0
  218. package/dist/templates/view_enums_dropdown.template.d.ts +18 -0
  219. package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -0
  220. package/dist/templates/view_enums_dropdown.template.js +2 -0
  221. package/dist/templates/view_enums_dropdown.template.js.map +1 -0
  222. package/dist/templates/view_enums_select.template.d.ts +17 -0
  223. package/dist/templates/view_enums_select.template.d.ts.map +1 -0
  224. package/dist/templates/view_enums_select.template.js +2 -0
  225. package/dist/templates/view_enums_select.template.js.map +1 -0
  226. package/dist/templates/view_form.template.d.ts +26 -0
  227. package/dist/templates/view_form.template.d.ts.map +1 -0
  228. package/dist/templates/view_form.template.js +2 -0
  229. package/dist/templates/view_form.template.js.map +1 -0
  230. package/dist/templates/view_id_all_select.template.d.ts +17 -0
  231. package/dist/templates/view_id_all_select.template.d.ts.map +1 -0
  232. package/dist/templates/view_id_all_select.template.js +2 -0
  233. package/dist/templates/view_id_all_select.template.js.map +1 -0
  234. package/dist/templates/view_id_async_select.template.d.ts +17 -0
  235. package/dist/templates/view_id_async_select.template.d.ts.map +1 -0
  236. package/dist/templates/view_id_async_select.template.js +2 -0
  237. package/dist/templates/view_id_async_select.template.js.map +1 -0
  238. package/dist/templates/view_list.template.d.ts +38 -0
  239. package/dist/templates/view_list.template.d.ts.map +1 -0
  240. package/dist/templates/view_list.template.js +2 -0
  241. package/dist/templates/view_list.template.js.map +1 -0
  242. package/dist/templates/view_list_columns.template.d.ts +17 -0
  243. package/dist/templates/view_list_columns.template.d.ts.map +1 -0
  244. package/dist/templates/view_list_columns.template.js +2 -0
  245. package/dist/templates/view_list_columns.template.js.map +1 -0
  246. package/dist/templates/view_search_input.template.d.ts +17 -0
  247. package/dist/templates/view_search_input.template.d.ts.map +1 -0
  248. package/dist/templates/view_search_input.template.js +2 -0
  249. package/dist/templates/view_search_input.template.js.map +1 -0
  250. package/dist/testing/_relation-graph.d.ts +7 -0
  251. package/dist/testing/_relation-graph.d.ts.map +1 -0
  252. package/dist/testing/_relation-graph.js +2 -0
  253. package/dist/testing/_relation-graph.js.map +1 -0
  254. package/dist/testing/fixture-manager.d.ts +35 -0
  255. package/dist/testing/fixture-manager.d.ts.map +1 -0
  256. package/dist/testing/fixture-manager.js +2 -0
  257. package/dist/testing/fixture-manager.js.map +1 -0
  258. package/dist/types/types.d.ts +609 -0
  259. package/dist/types/types.d.ts.map +1 -0
  260. package/dist/types/types.js +2 -0
  261. package/dist/types/types.js.map +1 -0
  262. package/dist/typings/knex.d.js +2 -0
  263. package/dist/typings/knex.d.js.map +1 -0
  264. package/dist/utils/async-utils.d.ts +25 -0
  265. package/dist/utils/async-utils.d.ts.map +1 -0
  266. package/dist/utils/async-utils.js +2 -0
  267. package/dist/utils/async-utils.js.map +1 -0
  268. package/dist/utils/controller.d.ts +9 -0
  269. package/dist/utils/controller.d.ts.map +1 -0
  270. package/dist/utils/controller.js +2 -0
  271. package/dist/utils/controller.js.map +1 -0
  272. package/dist/utils/fs-utils.d.ts +9 -0
  273. package/dist/utils/fs-utils.d.ts.map +1 -0
  274. package/dist/utils/fs-utils.js +2 -0
  275. package/dist/utils/fs-utils.js.map +1 -0
  276. package/dist/utils/lodash-able.d.ts +2 -0
  277. package/dist/utils/lodash-able.d.ts.map +1 -0
  278. package/dist/utils/lodash-able.js +2 -0
  279. package/dist/utils/lodash-able.js.map +1 -0
  280. package/dist/utils/model.d.ts +17 -0
  281. package/dist/utils/model.d.ts.map +1 -0
  282. package/dist/utils/model.js +2 -0
  283. package/dist/utils/model.js.map +1 -0
  284. package/dist/utils/sql-parser.d.ts +4 -0
  285. package/dist/utils/sql-parser.d.ts.map +1 -0
  286. package/dist/utils/sql-parser.js +2 -0
  287. package/dist/utils/sql-parser.js.map +1 -0
  288. package/dist/utils/utils.d.ts +9 -0
  289. package/dist/utils/utils.d.ts.map +1 -0
  290. package/dist/utils/utils.js +2 -0
  291. package/dist/utils/utils.js.map +1 -0
  292. package/dist/utils/zod-error.d.ts +8 -0
  293. package/dist/utils/zod-error.d.ts.map +1 -0
  294. package/dist/utils/zod-error.js +2 -0
  295. package/dist/utils/zod-error.js.map +1 -0
  296. package/nodemon.json +6 -0
  297. package/package.json +29 -44
  298. package/src/api/base-frame.ts +3 -4
  299. package/src/api/caster.ts +22 -23
  300. package/src/api/code-converters.ts +170 -134
  301. package/src/api/context.ts +13 -6
  302. package/src/api/decorators.ts +146 -20
  303. package/src/api/index.ts +2 -0
  304. package/src/api/sonamu.ts +374 -165
  305. package/src/bin/build-config.ts +5 -0
  306. package/src/bin/cli-wrapper.ts +29 -40
  307. package/src/bin/cli.ts +132 -190
  308. package/src/database/_batch_update.ts +10 -15
  309. package/src/database/base-model.ts +300 -216
  310. package/src/database/db.ts +191 -21
  311. package/src/database/{drivers/knex/plugins → knex-plugins}/knex-on-duplicate-update.ts +1 -1
  312. package/src/database/puri-wrapper.ts +129 -0
  313. package/src/database/puri.ts +808 -0
  314. package/src/database/puri.types.ts +222 -0
  315. package/src/database/transaction-context.ts +18 -0
  316. package/src/database/upsert-builder.ts +32 -35
  317. package/src/entity/entity-manager.ts +7 -15
  318. package/src/entity/entity.ts +9 -31
  319. package/src/entity/migrator-/354/235/264/354/202/254/352/260/224/354/226/264/354/232/224.md +1 -0
  320. package/src/file-storage/driver.ts +121 -0
  321. package/src/file-storage/file-storage.ts +100 -0
  322. package/src/index.ts +14 -11
  323. package/src/migration/code-generation.ts +777 -0
  324. package/src/migration/migration-set.ts +453 -0
  325. package/src/migration/migrator.ts +823 -0
  326. package/src/migration/types.ts +53 -0
  327. package/src/shared/web.shared.ts.txt +33 -2
  328. package/src/syncer/syncer.ts +294 -127
  329. package/src/templates/generated.template.ts +13 -1
  330. package/src/templates/generated_http.template.ts +15 -12
  331. package/src/templates/generated_sso.template.ts +50 -2
  332. package/src/templates/model.template.ts +138 -2
  333. package/src/templates/service.template.ts +0 -1
  334. package/src/templates/view_form.template.ts +11 -7
  335. package/src/templates/view_list.template.ts +12 -4
  336. package/src/testing/fixture-manager.ts +229 -174
  337. package/src/types/types.ts +102 -14
  338. package/src/utils/async-utils.ts +64 -0
  339. package/src/utils/fs-utils.ts +17 -0
  340. package/src/utils/model.ts +0 -2
  341. package/src/utils/utils.ts +14 -58
  342. package/src/utils/zod-error.ts +12 -176
  343. package/tsconfig.json +2 -0
  344. package/tsup.config.js +4 -2
  345. package/.pnp.cjs +0 -14363
  346. package/.pnp.loader.mjs +0 -2047
  347. package/.vscode/extensions.json +0 -6
  348. package/.vscode/settings.json +0 -9
  349. package/.yarnrc.yml +0 -5
  350. package/dist/chunk-QJFHDCBN.mjs.map +0 -1
  351. package/src/database/base-model.abstract.ts +0 -97
  352. package/src/database/db.abstract.ts +0 -75
  353. package/src/database/drivers/knex/base-model.ts +0 -55
  354. package/src/database/drivers/knex/client.ts +0 -209
  355. package/src/database/drivers/knex/db.ts +0 -232
  356. package/src/database/drivers/knex/generator.ts +0 -659
  357. package/src/database/drivers/kysely/base-model.ts +0 -89
  358. package/src/database/drivers/kysely/client.ts +0 -309
  359. package/src/database/drivers/kysely/db.ts +0 -238
  360. package/src/database/drivers/kysely/generator.ts +0 -714
  361. package/src/database/types.ts +0 -118
  362. package/src/entity/migrator.ts +0 -1400
  363. package/src/smd/smd-manager.ts +0 -139
  364. package/src/smd/smd.ts +0 -571
  365. package/src/templates/kysely_types.template.ts +0 -205
  366. /package/dist/{chunk-NI37CY4T.mjs.map → chunk-C3IPIF6O.mjs.map} +0 -0
  367. /package/dist/{chunk-DYFCACHD.js.map → chunk-EXHKSVTE.js.map} +0 -0
  368. /package/dist/{chunk-DDJ7T4MA.mjs.map → chunk-HGIBJYOU.mjs.map} +0 -0
  369. /package/dist/{chunk-NIFOTHBW.mjs.map → chunk-JKSOJRQA.mjs.map} +0 -0
  370. /package/dist/{chunk-CXAVBVKC.js.map → chunk-OTKKFP3Y.js.map} +0 -0
  371. /package/dist/{chunk-J6S43O7G.js.map → chunk-UZ2IY5VE.js.map} +0 -0
@@ -0,0 +1,808 @@
1
+ import type { Knex } from "knex";
2
+ import type {
3
+ AvailableColumns,
4
+ ComparisonOperator,
5
+ EmptyRecord,
6
+ Expand,
7
+ ExtractColumnType,
8
+ FulltextColumns,
9
+ InsertData,
10
+ MergeJoined,
11
+ ParseSelectObject,
12
+ ResultAvailableColumns,
13
+ SelectObject,
14
+ SqlFunction,
15
+ WhereCondition,
16
+ } from "./puri.types";
17
+ import chalk from "chalk";
18
+
19
+ // 메인 Puri 클래스
20
+ export class Puri<
21
+ TSchema,
22
+ TTable extends keyof TSchema | string,
23
+ TOriginal = TTable extends keyof TSchema ? TSchema[TTable] : unknown,
24
+ TResult = TTable extends keyof TSchema ? TSchema[TTable] : unknown,
25
+ TJoined = EmptyRecord,
26
+ > {
27
+ private knexQuery: Knex.QueryBuilder;
28
+
29
+ // 생성자 시그니처들
30
+ constructor(
31
+ knex: Knex,
32
+ tableName: TTable extends keyof TSchema ? TTable : unknown
33
+ );
34
+ constructor(
35
+ knex: Knex,
36
+ subquery: Puri<TSchema, any, any, TOriginal, any>,
37
+ alias: TTable extends string ? TTable : never
38
+ );
39
+ constructor(
40
+ private knex: Knex,
41
+ tableNameOrSubquery: any,
42
+ alias?: TTable extends string ? TTable : never
43
+ ) {
44
+ if (typeof tableNameOrSubquery === "string") {
45
+ // 일반 테이블로 시작
46
+ this.knexQuery = knex(tableNameOrSubquery).from(tableNameOrSubquery);
47
+ } else {
48
+ // 서브쿼리로 시작
49
+ this.knexQuery = knex.from(tableNameOrSubquery.raw().as(alias));
50
+ }
51
+ }
52
+
53
+ // Static SQL helper functions
54
+ static count(column: string = "*"): SqlFunction<"number"> {
55
+ return {
56
+ _type: "sql_function",
57
+ _return: "number",
58
+ _sql: `COUNT(${column})`,
59
+ };
60
+ }
61
+
62
+ static sum(column: string): SqlFunction<"number"> {
63
+ return { _type: "sql_function", _return: "number", _sql: `SUM(${column})` };
64
+ }
65
+
66
+ static avg(column: string): SqlFunction<"number"> {
67
+ return { _type: "sql_function", _return: "number", _sql: `AVG(${column})` };
68
+ }
69
+
70
+ static max(column: string): SqlFunction<"number"> {
71
+ return { _type: "sql_function", _return: "number", _sql: `MAX(${column})` };
72
+ }
73
+
74
+ static min(column: string): SqlFunction<"number"> {
75
+ return { _type: "sql_function", _return: "number", _sql: `MIN(${column})` };
76
+ }
77
+
78
+ static concat(...args: string[]): SqlFunction<"string"> {
79
+ return {
80
+ _type: "sql_function",
81
+ _return: "string",
82
+ _sql: `CONCAT(${args.join(", ")})`,
83
+ };
84
+ }
85
+
86
+ static upper(column: string): SqlFunction<"string"> {
87
+ return {
88
+ _type: "sql_function",
89
+ _return: "string",
90
+ _sql: `UPPER(${column})`,
91
+ };
92
+ }
93
+
94
+ static lower(column: string): SqlFunction<"string"> {
95
+ return {
96
+ _type: "sql_function",
97
+ _return: "string",
98
+ _sql: `LOWER(${column})`,
99
+ };
100
+ }
101
+
102
+ // Raw functions
103
+ static rawString(sql: string): SqlFunction<"string"> {
104
+ return { _type: "sql_function", _return: "string", _sql: sql };
105
+ }
106
+
107
+ static rawNumber(sql: string): SqlFunction<"number"> {
108
+ return { _type: "sql_function", _return: "number", _sql: sql };
109
+ }
110
+
111
+ static rawBoolean(sql: string): SqlFunction<"boolean"> {
112
+ return { _type: "sql_function", _return: "boolean", _sql: sql };
113
+ }
114
+
115
+ static rawDate(sql: string): SqlFunction<"date"> {
116
+ return { _type: "sql_function", _return: "date", _sql: sql };
117
+ }
118
+
119
+ // Alias 기반 Select
120
+ select<TSelect extends SelectObject<TSchema, TTable, TOriginal, TJoined>>(
121
+ selectObj: TSelect
122
+ ): Puri<
123
+ TSchema,
124
+ TTable,
125
+ TOriginal,
126
+ ParseSelectObject<TSchema, TTable, TSelect, TOriginal, TJoined>,
127
+ TJoined
128
+ > {
129
+ const selectClauses: (string | Knex.Raw)[] = [];
130
+
131
+ for (const [alias, columnOrFunction] of Object.entries(selectObj)) {
132
+ if (
133
+ typeof columnOrFunction === "object" &&
134
+ columnOrFunction._type === "sql_function"
135
+ ) {
136
+ // SQL 함수인 경우
137
+ selectClauses.push(
138
+ this.knex.raw(`${columnOrFunction._sql} as ${alias}`)
139
+ );
140
+ } else {
141
+ // 일반 컬럼인 경우
142
+ const columnPath = columnOrFunction as string;
143
+ if (alias === columnPath) {
144
+ // alias와 컬럼명이 같으면 alias 생략
145
+ selectClauses.push(columnPath);
146
+ } else {
147
+ // alias 지정
148
+ selectClauses.push(`${columnPath} as ${alias}`);
149
+ }
150
+ }
151
+ }
152
+
153
+ this.knexQuery.select(selectClauses);
154
+ return this as any;
155
+ }
156
+
157
+ // 전체 선택 (편의 메서드)
158
+ selectAll(): Puri<
159
+ TSchema,
160
+ TTable,
161
+ TOriginal,
162
+ TTable extends keyof TSchema
163
+ ? TSchema[TTable] & TJoined
164
+ : TResult & TJoined,
165
+ TJoined
166
+ > {
167
+ this.knexQuery.select("*");
168
+ return this as any;
169
+ }
170
+
171
+ // Where 조건 (조인된 테이블 컬럼도 지원)
172
+ where(
173
+ conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
174
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
175
+ where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
176
+ column: TColumn,
177
+ value: ExtractColumnType<
178
+ TSchema,
179
+ TTable,
180
+ TColumn & string,
181
+ TOriginal,
182
+ TJoined
183
+ >
184
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
185
+ where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
186
+ column: TColumn,
187
+ operator: ComparisonOperator | "like",
188
+ value: ExtractColumnType<
189
+ TSchema,
190
+ TTable,
191
+ TColumn & string,
192
+ TOriginal,
193
+ TJoined
194
+ >
195
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
196
+ where(
197
+ columnOrConditions: any,
198
+ operatorOrValue?: any,
199
+ value?: any
200
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
201
+ if (typeof columnOrConditions === "object") {
202
+ this.knexQuery.where(columnOrConditions);
203
+ } else if (arguments.length === 2) {
204
+ if (operatorOrValue === null) {
205
+ this.knexQuery.whereNull(columnOrConditions);
206
+ return this;
207
+ }
208
+ this.knexQuery.where(columnOrConditions, operatorOrValue);
209
+ } else if (arguments.length === 3) {
210
+ if (value === null) {
211
+ if (operatorOrValue === "!=") {
212
+ this.knexQuery.whereNotNull(columnOrConditions);
213
+ return this;
214
+ } else if (operatorOrValue === "=") {
215
+ this.knexQuery.whereNull(columnOrConditions);
216
+ return this;
217
+ }
218
+ }
219
+ this.knexQuery.where(columnOrConditions, operatorOrValue, value);
220
+ } else {
221
+ this.knexQuery.where(columnOrConditions);
222
+ }
223
+ return this;
224
+ }
225
+
226
+ // WhereIn (조인된 테이블 컬럼도 지원)
227
+ whereIn<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
228
+ column: TColumn,
229
+ values: ExtractColumnType<
230
+ TSchema,
231
+ TTable,
232
+ TColumn & string,
233
+ TOriginal,
234
+ TJoined
235
+ >[]
236
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
237
+ whereIn(
238
+ column: string,
239
+ values: any[]
240
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
241
+ this.knexQuery.whereIn(column, values);
242
+ return this;
243
+ }
244
+
245
+ whereNotIn<
246
+ TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
247
+ >(
248
+ column: TColumn,
249
+ values: ExtractColumnType<
250
+ TSchema,
251
+ TTable,
252
+ TColumn & string,
253
+ TOriginal,
254
+ TJoined
255
+ >[]
256
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
257
+ whereNotIn(
258
+ column: string,
259
+ values: any[]
260
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
261
+ this.knexQuery.whereNotIn(column, values);
262
+ return this;
263
+ }
264
+
265
+ whereMatch<
266
+ TColumn extends FulltextColumns<TSchema, TTable, TOriginal, TJoined>,
267
+ >(column: TColumn, value: string): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
268
+ this.knexQuery.whereRaw(`MATCH (${String(column)}) AGAINST (?)`, [value]);
269
+ return this;
270
+ }
271
+
272
+ // WhereGroup (괄호 그룹핑 지원)
273
+ whereGroup(
274
+ callback: (
275
+ group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
276
+ ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
277
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
278
+ this.knexQuery.where((builder) => {
279
+ const group = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(builder);
280
+ callback(group);
281
+ });
282
+ return this;
283
+ }
284
+
285
+ orWhereGroup(
286
+ callback: (
287
+ group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
288
+ ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
289
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
290
+ this.knexQuery.orWhere((builder) => {
291
+ const group = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(builder);
292
+ callback(group);
293
+ });
294
+ return this;
295
+ }
296
+
297
+ // Join
298
+ join<
299
+ TJoinTable extends keyof TSchema,
300
+ TLColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined & Record<TJoinTable, TSchema[TJoinTable]>>,
301
+ TRColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined & Record<TJoinTable, TSchema[TJoinTable]>>,
302
+ >(
303
+ table: TJoinTable,
304
+ left: TLColumn,
305
+ right: TRColumn,
306
+ ): Puri<
307
+ TSchema,
308
+ TTable,
309
+ TOriginal,
310
+ TResult,
311
+ MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
312
+ >;
313
+ join<TJoinTable extends keyof TSchema>(
314
+ table: TJoinTable,
315
+ joinCallback: (
316
+ joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>
317
+ ) => void
318
+ ): Puri<
319
+ TSchema,
320
+ TTable,
321
+ TOriginal,
322
+ TResult,
323
+ MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
324
+ >;
325
+ join<TSubResult, TAlias extends string>(
326
+ subquery: Puri<TSchema, any, any, TSubResult, any>,
327
+ alias: TAlias,
328
+ left: string,
329
+ right: string
330
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined & Record<TAlias, TSubResult>>;
331
+ join(
332
+ table: string,
333
+ left: string,
334
+ right: string
335
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
336
+ join(
337
+ tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
338
+ ...args: any[]
339
+ ): Puri<TSchema, TTable, TOriginal, TResult, any> {
340
+ if (tableOrSubquery instanceof Puri) {
341
+ // 서브쿼리 조인: join(subquery, alias, left, right)
342
+ const [alias, left, right] = args;
343
+ this.knexQuery.join(tableOrSubquery.raw().as(alias), left, right);
344
+ } else if (
345
+ args.length === 2 &&
346
+ typeof args[0] === "string" &&
347
+ typeof args[1] === "string"
348
+ ) {
349
+ const [left, right] = args;
350
+ this.knexQuery.join(tableOrSubquery as string, left, right);
351
+ } else if (args.length === 1 && typeof args[0] === "function") {
352
+ const joinCallback = args[0];
353
+ this.knexQuery.join(tableOrSubquery as string, (joinClause) => {
354
+ joinCallback(new JoinClauseGroup(joinClause));
355
+ });
356
+ } else {
357
+ throw new Error("Invalid arguments");
358
+ }
359
+ return this as any;
360
+ }
361
+
362
+ leftJoin<
363
+ TJoinTable extends keyof TSchema,
364
+ TLColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined & Record<TJoinTable, TSchema[TJoinTable]>>,
365
+ TRColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined & Record<TJoinTable, TSchema[TJoinTable]>>,
366
+ >(
367
+ table: TJoinTable,
368
+ left: TLColumn,
369
+ right: TRColumn,
370
+ ): Puri<
371
+ TSchema,
372
+ TTable,
373
+ TOriginal,
374
+ TResult,
375
+ TJoined & Record<TJoinTable, Partial<TSchema[TJoinTable]>>
376
+ >;
377
+ leftJoin<TSubResult, TAlias extends string>(
378
+ subquery: Puri<TSchema, any, any, TSubResult, any>,
379
+ alias: TAlias,
380
+ left: string,
381
+ right: string
382
+ ): Puri<
383
+ TSchema,
384
+ TTable,
385
+ TOriginal,
386
+ TResult,
387
+ TJoined & Record<TAlias, Partial<TSubResult>>
388
+ >;
389
+ leftJoin(
390
+ table: string,
391
+ left: string,
392
+ right: string
393
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
394
+ leftJoin(
395
+ tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
396
+ ...args: any[]
397
+ ): Puri<TSchema, TTable, TOriginal, TResult, any> {
398
+ if (tableOrSubquery instanceof Puri) {
399
+ // 서브쿼리 조인: leftJoin(subquery, alias, left, right)
400
+ const [alias, left, right] = args;
401
+ this.knexQuery.leftJoin(tableOrSubquery.raw().as(alias), left, right);
402
+ } else {
403
+ const [left, right] = args;
404
+ this.knexQuery.leftJoin(tableOrSubquery as string, left, right);
405
+ }
406
+ return this as any;
407
+ }
408
+
409
+ // OrderBy
410
+ orderBy<TColumn extends ResultAvailableColumns<TSchema, TTable, TOriginal, TResult, TJoined>>(
411
+ column: TColumn,
412
+ direction: "asc" | "desc"
413
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
414
+ orderBy(
415
+ column: string,
416
+ direction: "asc" | "desc" = "asc"
417
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
418
+ this.knexQuery.orderBy(column, direction);
419
+ return this;
420
+ }
421
+
422
+ // 기본 쿼리 메서드들
423
+ limit(count: number): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
424
+ this.knexQuery.limit(count);
425
+ return this;
426
+ }
427
+
428
+ offset(count: number): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
429
+ this.knexQuery.offset(count);
430
+ return this;
431
+ }
432
+
433
+ // Group by (조인된 테이블 컬럼도 지원)
434
+ groupBy<TColumns extends ResultAvailableColumns<TSchema, TTable, TOriginal, TResult, TJoined>>(
435
+ ...columns: TColumns[]
436
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
437
+ groupBy(...columns: string[]): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
438
+ this.knexQuery.groupBy(...(columns as string[]));
439
+ return this;
440
+ }
441
+
442
+ having(condition: string): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
443
+ having<TColumn extends ResultAvailableColumns<TSchema, TTable, TOriginal, TResult, TJoined>>(
444
+ condition: TColumn,
445
+ operator: ComparisonOperator,
446
+ value: any
447
+ ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
448
+ having(...conditions: string[]): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
449
+ this.knexQuery.having(...(conditions as [string, string, string]));
450
+ return this;
451
+ }
452
+ // 실행 메서드들 - thenable 구현
453
+ then<TResult1, TResult2 = never>(
454
+ onfulfilled?:
455
+ | ((
456
+ value: Expand<TResult>[]
457
+ ) => Expand<TResult1> | PromiseLike<Expand<TResult1>>)
458
+ | null,
459
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
460
+ ): Promise<Expand<TResult1> | TResult2> {
461
+ return this.knexQuery.then(onfulfilled as any, onrejected);
462
+ }
463
+
464
+ catch<TResult2 = never>(
465
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
466
+ ): Promise<Expand<TResult> | TResult2> {
467
+ return this.knexQuery.catch(onrejected);
468
+ }
469
+
470
+ finally(onfinally?: (() => void) | null): Promise<Expand<TResult>> {
471
+ return this.knexQuery.finally(onfinally);
472
+ }
473
+
474
+ // 안전한 실행 메서드들
475
+ async first(): Promise<Expand<TResult> | undefined> {
476
+ return this.knexQuery.first() as Promise<Expand<TResult> | undefined>;
477
+ }
478
+
479
+ async firstOrFail(): Promise<TResult> {
480
+ const result = await this.knexQuery.first();
481
+ if (!result) {
482
+ throw new Error("No results found");
483
+ }
484
+ return result as TResult;
485
+ }
486
+
487
+ async at(index: number): Promise<Expand<TResult> | undefined> {
488
+ const results = await this;
489
+ return results[index] as Expand<TResult> | undefined;
490
+ }
491
+
492
+ async assertAt(index: number): Promise<Expand<TResult>> {
493
+ const results = await this;
494
+ const result = results[index];
495
+ if (result === undefined) {
496
+ throw new Error(`No result found at index ${index}`);
497
+ }
498
+ return result;
499
+ }
500
+
501
+ // Insert/Update/Delete
502
+ // TODO(Haze, 251030): InsertData<T>에서 nullable type을 제대로 처리하지 못하는 것 같음.
503
+ async insert(
504
+ data: TTable extends keyof TSchema ? InsertData<TSchema[TTable]> : unknown
505
+ ): Promise<number[]> {
506
+ return this.knexQuery.insert(data);
507
+ }
508
+
509
+ async update(
510
+ data: Partial<TTable extends keyof TSchema ? TSchema[TTable] : unknown>
511
+ ): Promise<number> {
512
+ return this.knexQuery.update(data);
513
+ }
514
+
515
+ async delete(): Promise<number> {
516
+ return this.knexQuery.delete();
517
+ }
518
+
519
+ toQuery(): string {
520
+ return this.knexQuery.toQuery();
521
+ }
522
+
523
+ debug(): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
524
+ console.log(
525
+ `${chalk.cyan("[Puri Debug]")} ${chalk.yellow(this.formatSQL(this.toQuery()))}`
526
+ );
527
+ return this;
528
+ }
529
+
530
+ formatSQL(unformatted: string): string {
531
+ // SQL 예약어 목록
532
+ const keywords = [
533
+ "SELECT",
534
+ "FROM",
535
+ "WHERE",
536
+ "INSERT",
537
+ "INTO",
538
+ "VALUES",
539
+ "UPDATE",
540
+ "DELETE",
541
+ "CREATE",
542
+ "TABLE",
543
+ "ALTER",
544
+ "DROP",
545
+ "JOIN",
546
+ "ON",
547
+ "INNER",
548
+ "LEFT",
549
+ "RIGHT",
550
+ "FULL",
551
+ "OUTER",
552
+ "GROUP",
553
+ "BY",
554
+ "ORDER",
555
+ "HAVING",
556
+ "DISTINCT",
557
+ "LIMIT",
558
+ "OFFSET",
559
+ "AS",
560
+ "AND",
561
+ "OR",
562
+ "NOT",
563
+ "IN",
564
+ "LIKE",
565
+ "IS",
566
+ "NULL",
567
+ "CASE",
568
+ "WHEN",
569
+ "THEN",
570
+ "ELSE",
571
+ "END",
572
+ "UNION",
573
+ "ALL",
574
+ "EXISTS",
575
+ "BETWEEN",
576
+ ];
577
+
578
+ let formatted = unformatted;
579
+
580
+ // 예약어를 대문자로 변환
581
+ keywords.forEach((keyword) => {
582
+ const regex = new RegExp(`\\b${keyword}\\b`, "gi");
583
+ formatted = formatted.replace(regex, keyword.toUpperCase());
584
+ });
585
+
586
+ // 주요 절 앞에 줄바꿈 추가
587
+ const majorClauses = [
588
+ "SELECT",
589
+ "FROM",
590
+ "WHERE",
591
+ "GROUP BY",
592
+ "ORDER BY",
593
+ "HAVING",
594
+ "LIMIT",
595
+ "UNION",
596
+ ];
597
+ majorClauses.forEach((clause) => {
598
+ const regex = new RegExp(`\\s+(${clause})\\s+`, "gi");
599
+ formatted = formatted.replace(regex, `\n${clause.toUpperCase()} `);
600
+ });
601
+
602
+ // JOIN 절 처리
603
+ formatted = formatted.replace(
604
+ /\s+((?:INNER|LEFT|RIGHT|FULL OUTER)\s+)?JOIN\s+/gi,
605
+ "\n$1JOIN "
606
+ );
607
+
608
+ // AND, OR 조건 처리
609
+ formatted = formatted.replace(/\s+(AND|OR)\s+/gi, "\n $1 ");
610
+
611
+ // 괄호 처리 및 들여쓰기
612
+ const lines = formatted.split("\n");
613
+ const indentedLines = [];
614
+ let indentLevel = 0;
615
+
616
+ for (let line of lines) {
617
+ const trimmedLine = line.trim();
618
+ if (!trimmedLine) continue;
619
+
620
+ // 닫는 괄호가 있으면 들여쓰기 레벨 감소
621
+ const closingParens = (trimmedLine.match(/\)/g) || []).length;
622
+ const openingParens = (trimmedLine.match(/\(/g) || []).length;
623
+
624
+ if (closingParens > 0 && openingParens === 0) {
625
+ indentLevel = Math.max(0, indentLevel - closingParens);
626
+ }
627
+
628
+ // 현재 들여쓰기 적용
629
+ const indent = " ".repeat(indentLevel);
630
+ indentedLines.push(indent + trimmedLine);
631
+
632
+ // 여는 괄호가 있으면 들여쓰기 레벨 증가
633
+ if (openingParens > closingParens) {
634
+ indentLevel += openingParens - closingParens;
635
+ }
636
+ }
637
+
638
+ return indentedLines.join("\n").trim();
639
+ }
640
+
641
+ raw(): Knex.QueryBuilder {
642
+ return this.knexQuery;
643
+ }
644
+ }
645
+
646
+ // 11. Database 클래스
647
+ class WhereGroup<
648
+ TSchema,
649
+ TTable extends keyof TSchema | string,
650
+ TOriginal = any,
651
+ TJoined = EmptyRecord,
652
+ > {
653
+ constructor(private builder: Knex.QueryBuilder) {}
654
+
655
+ where(
656
+ conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
657
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
658
+ where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
659
+ column: TColumn,
660
+ value: ExtractColumnType<
661
+ TSchema,
662
+ TTable,
663
+ TColumn & string,
664
+ TOriginal,
665
+ TJoined
666
+ >
667
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
668
+ where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
669
+ column: TColumn,
670
+ operator: ComparisonOperator | "like",
671
+ value: ExtractColumnType<
672
+ TSchema,
673
+ TTable,
674
+ TColumn & string,
675
+ TOriginal,
676
+ TJoined
677
+ >
678
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
679
+ where(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
680
+ where(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
681
+ this.builder.where(args[0], ...args.slice(1));
682
+ return this;
683
+ }
684
+
685
+ orWhere(
686
+ conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
687
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
688
+ orWhere<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
689
+ column: TColumn,
690
+ value: ExtractColumnType<
691
+ TSchema,
692
+ TTable,
693
+ TColumn & string,
694
+ TOriginal,
695
+ TJoined
696
+ >
697
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
698
+ orWhere<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
699
+ column: TColumn,
700
+ operator: ComparisonOperator | "like",
701
+ value: ExtractColumnType<
702
+ TSchema,
703
+ TTable,
704
+ TColumn & string,
705
+ TOriginal,
706
+ TJoined
707
+ >
708
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
709
+ orWhere(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
710
+ orWhere(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
711
+ this.builder.orWhere(args[0], ...args.slice(1));
712
+ return this;
713
+ }
714
+
715
+ whereIn<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
716
+ column: TColumn,
717
+ values: ExtractColumnType<
718
+ TSchema,
719
+ TTable,
720
+ TColumn & string,
721
+ TOriginal,
722
+ TJoined
723
+ >[]
724
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
725
+ whereIn(
726
+ column: string,
727
+ values: any[]
728
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
729
+ this.builder.whereIn(column, values);
730
+ return this;
731
+ }
732
+
733
+ orWhereIn<
734
+ TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
735
+ >(
736
+ column: TColumn,
737
+ values: ExtractColumnType<
738
+ TSchema,
739
+ TTable,
740
+ TColumn & string,
741
+ TOriginal,
742
+ TJoined
743
+ >[]
744
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
745
+ orWhereIn(
746
+ column: string,
747
+ values: any[]
748
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
749
+ this.builder.orWhereIn(column, values);
750
+ return this;
751
+ }
752
+
753
+ // 중첩 그룹 지원
754
+ whereGroup(
755
+ callback: (
756
+ group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
757
+ ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
758
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
759
+ this.builder.where((subBuilder) => {
760
+ const subGroup = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
761
+ subBuilder
762
+ );
763
+ callback(subGroup);
764
+ });
765
+ return this;
766
+ }
767
+
768
+ orWhereGroup(
769
+ callback: (
770
+ group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
771
+ ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
772
+ ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
773
+ this.builder.orWhere((subBuilder) => {
774
+ const subGroup = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
775
+ subBuilder
776
+ );
777
+ callback(subGroup);
778
+ });
779
+ return this;
780
+ }
781
+ }
782
+
783
+ export class JoinClauseGroup<
784
+ TSchema,
785
+ TTable extends keyof TSchema | string,
786
+ TOriginal = any,
787
+ TJoined = EmptyRecord,
788
+ > {
789
+ constructor(private callback: Knex.JoinClause) {}
790
+
791
+ on(
792
+ callback: (joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>) => void
793
+ ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
794
+ on(column: string, value: any): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
795
+ on(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
796
+ this.callback.on(...(args as [string, string]));
797
+ return this;
798
+ }
799
+
800
+ orOn(
801
+ callback: (joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>) => void
802
+ ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
803
+ orOn(column: string, value: any): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
804
+ orOn(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
805
+ this.callback.orOn(...(args as [string, string]));
806
+ return this;
807
+ }
808
+ }