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
@@ -1,28 +1,216 @@
1
- // base-model.ts
2
1
  import { DateTime } from "luxon";
3
- import _ from "lodash";
4
- import SqlParser from "node-sql-parser";
5
- import chalk from "chalk";
2
+ import { Knex } from "knex";
3
+ import { chunk, groupBy, isObject, omit, set, uniq } from "lodash";
4
+ import { DBPreset, DB } from "./db";
5
+ import { isCustomJoinClause, type SubsetQuery } from "../types/types";
6
+ import type { BaseListParams } from "../utils/model";
6
7
  import inflection from "inflection";
7
- import { BaseListParams } from "../utils/model";
8
- import { DBPreset, DriverSpec, DatabaseDriver } from "./types";
9
- import { SubsetQuery } from "../types/types";
8
+ import chalk from "chalk";
9
+ import { UpsertBuilder } from "./upsert-builder";
10
+ import SqlParser from "node-sql-parser";
10
11
  import { getTableName, getTableNamesFromWhere } from "../utils/sql-parser";
11
- import { DB } from "./db";
12
+ import { PuriWrapper } from "./puri-wrapper";
12
13
 
13
- export abstract class BaseModelClassAbstract<D extends DatabaseDriver> {
14
+ export class BaseModelClass {
14
15
  public modelName: string = "Unknown";
15
16
 
16
- protected abstract applyJoins(
17
- qb: DriverSpec[D]["adapter"],
18
- joins: SubsetQuery["joins"]
19
- ): DriverSpec[D]["adapter"];
20
- protected abstract executeCountQuery(
21
- qb: DriverSpec[D]["queryBuilder"]
22
- ): Promise<number>;
17
+ /* DB 인스턴스 get, destroy */
18
+ getDB(which: DBPreset): Knex {
19
+ return DB.getDB(which);
20
+ }
21
+
22
+ getPuri(which: DBPreset): PuriWrapper {
23
+ // 트랜잭션 컨텍스트에서 트랜잭션 획득
24
+ const trx = DB.getTransactionContext().getTransaction(which);
25
+ if (trx) {
26
+ return trx;
27
+ }
28
+
29
+ // 트랜잭션이 없으면 새로운 PuriWrapper 반환
30
+ const db = this.getDB(which);
31
+ return new PuriWrapper(db, this.getUpsertBuilder());
32
+ }
33
+
34
+ async destroy() {
35
+ return DB.destroy();
36
+ }
37
+
38
+ myNow(timestamp?: number): string {
39
+ const dt: DateTime =
40
+ timestamp === undefined
41
+ ? DateTime.local()
42
+ : DateTime.fromSeconds(timestamp);
43
+ return dt.toFormat("yyyy-MM-dd HH:mm:ss");
44
+ }
45
+
46
+ async getInsertedIds(
47
+ wdb: Knex,
48
+ rows: any[],
49
+ tableName: string,
50
+ unqKeyFields: string[],
51
+ chunkSize: number = 500
52
+ ) {
53
+ if (!wdb) {
54
+ wdb = this.getDB("w");
55
+ }
56
+
57
+ let unqKeys: string[];
58
+ let whereInField: any, selectField: string;
59
+ if (unqKeyFields.length > 1) {
60
+ whereInField = wdb.raw(`CONCAT_WS('_', '${unqKeyFields.join(",")}')`);
61
+ selectField = `${whereInField} as tmpUid`;
62
+ unqKeys = rows.map((row) =>
63
+ unqKeyFields.map((field) => row[field]).join("_")
64
+ );
65
+ } else {
66
+ whereInField = unqKeyFields[0];
67
+ selectField = unqKeyFields[0];
68
+ unqKeys = rows.map((row) => row[unqKeyFields[0]]);
69
+ }
70
+ const chunks = chunk(unqKeys, chunkSize);
71
+
72
+ let resultIds: number[] = [];
73
+ for (let chunk of chunks) {
74
+ const dbRows = await wdb(tableName)
75
+ .select("id", wdb.raw(selectField))
76
+ .whereIn(whereInField, chunk);
77
+ resultIds = resultIds.concat(
78
+ dbRows.map((dbRow: any) => parseInt(dbRow.id))
79
+ );
80
+ }
81
+
82
+ return resultIds;
83
+ }
84
+
85
+ async useLoaders(db: Knex, rows: any[], loaders: SubsetQuery["loaders"]) {
86
+ if (loaders.length === 0) {
87
+ return rows;
88
+ }
89
+
90
+ for (let loader of loaders) {
91
+ let subQ: any;
92
+ let subRows: any[];
93
+ let toCol: string;
94
+
95
+ const fromIds = rows.map((row) => row[loader.manyJoin.idField]);
96
+
97
+ if (loader.manyJoin.through === undefined) {
98
+ // HasMany
99
+ const idColumn = `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`;
100
+ subQ = db(loader.manyJoin.toTable)
101
+ .whereIn(idColumn, fromIds)
102
+ .select([...loader.select, idColumn]);
103
+
104
+ // HasMany에서 OneJoin이 있는 경우
105
+ loader.oneJoins.map((join) => {
106
+ if (join.join == "inner") {
107
+ subQ.innerJoin(
108
+ `${join.table} as ${join.as}`,
109
+ this.getJoinClause(db, join)
110
+ );
111
+ } else if (join.join == "outer") {
112
+ subQ.leftOuterJoin(
113
+ `${join.table} as ${join.as}`,
114
+ this.getJoinClause(db, join)
115
+ );
116
+ }
117
+ });
118
+ toCol = loader.manyJoin.toCol;
119
+ } else {
120
+ // ManyToMany
121
+ const idColumn = `${loader.manyJoin.through.table}.${loader.manyJoin.through.fromCol}`;
122
+ subQ = db(loader.manyJoin.through.table)
123
+ .join(
124
+ loader.manyJoin.toTable,
125
+ `${loader.manyJoin.through.table}.${loader.manyJoin.through.toCol}`,
126
+ `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`
127
+ )
128
+ .whereIn(idColumn, fromIds)
129
+ .select(uniq([...loader.select, idColumn]));
130
+
131
+ // ManyToMany에서 OneJoin이 있는 경우
132
+ loader.oneJoins.map((join) => {
133
+ if (join.join == "inner") {
134
+ subQ.innerJoin(
135
+ `${join.table} as ${join.as}`,
136
+ this.getJoinClause(db, join)
137
+ );
138
+ } else if (join.join == "outer") {
139
+ subQ.leftOuterJoin(
140
+ `${join.table} as ${join.as}`,
141
+ this.getJoinClause(db, join)
142
+ );
143
+ }
144
+ });
145
+ toCol = loader.manyJoin.through.fromCol;
146
+ }
147
+ subRows = await subQ;
148
+
149
+ if (loader.loaders) {
150
+ // 추가 -Many 케이스가 있는 경우 recursion 처리
151
+ subRows = await this.useLoaders(db, subRows, loader.loaders);
152
+ }
153
+
154
+ // 불러온 row들을 참조ID 기준으로 분류 배치
155
+ const subRowGroups = groupBy(subRows, toCol);
156
+ rows = rows.map((row) => {
157
+ row[loader.as] = (subRowGroups[row[loader.manyJoin.idField]] ?? []).map(
158
+ (r) => omit(r, toCol)
159
+ );
160
+ return row;
161
+ });
162
+ }
163
+ return rows;
164
+ }
23
165
 
24
- abstract getDB(which: DBPreset): DriverSpec[D]["core"];
25
- abstract destroy(): Promise<void>;
166
+ hydrate<T>(rows: T[]): T[] {
167
+ return rows.map((row: any) => {
168
+ // nullable relation인 경우 관련된 필드가 전부 null로 생성되는 것 방지하는 코드
169
+ const nestedKeys = Object.keys(row).filter((key) => key.includes("__"));
170
+ const groups = groupBy(nestedKeys, (key) => key.split("__")[0]);
171
+ const nullKeys = Object.keys(groups).filter(
172
+ (key) =>
173
+ groups[key].length > 1 &&
174
+ groups[key].every(
175
+ (field) =>
176
+ row[field] === null ||
177
+ (Array.isArray(row[field]) && row[field].length === 0)
178
+ )
179
+ );
180
+
181
+ const hydrated = Object.keys(row).reduce((r, field) => {
182
+ if (!field.includes("__")) {
183
+ if (Array.isArray(row[field]) && isObject(row[field][0])) {
184
+ r[field] = this.hydrate(row[field]);
185
+ return r;
186
+ } else {
187
+ r[field] = row[field];
188
+ return r;
189
+ }
190
+ }
191
+
192
+ const parts = field.split("__");
193
+ const objPath =
194
+ parts[0] +
195
+ parts
196
+ .slice(1)
197
+ .map((part) => `[${part}]`)
198
+ .join("");
199
+ set(
200
+ r,
201
+ objPath,
202
+ row[field] && Array.isArray(row[field]) && isObject(row[field][0])
203
+ ? this.hydrate(row[field])
204
+ : row[field]
205
+ );
206
+
207
+ return r;
208
+ }, {} as any);
209
+ nullKeys.map((nullKey) => (hydrated[nullKey] = null));
210
+
211
+ return hydrated;
212
+ });
213
+ }
26
214
 
27
215
  async runSubsetQuery<T extends BaseListParams, U extends string>({
28
216
  params,
@@ -38,265 +226,161 @@ export abstract class BaseModelClassAbstract<D extends DatabaseDriver> {
38
226
  params: T;
39
227
  subsetQuery: SubsetQuery;
40
228
  build: (buildParams: {
41
- qb: DriverSpec[D]["queryBuilder"];
42
- db: DriverSpec[D]["core"];
43
- select: SubsetQuery["select"];
229
+ qb: Knex.QueryBuilder;
230
+ db: Knex;
231
+ select: (string | Knex.Raw)[];
44
232
  joins: SubsetQuery["joins"];
45
233
  virtual: string[];
46
- }) => DriverSpec[D]["queryBuilder"];
47
- baseTable?: DriverSpec[D]["table"];
234
+ }) => Knex.QueryBuilder;
235
+ baseTable?: string;
48
236
  debug?: boolean | "list" | "count";
49
- db?: DriverSpec[D]["core"];
237
+ db?: Knex;
50
238
  optimizeCountQuery?: boolean;
51
239
  }): Promise<{
52
240
  rows: any[];
53
- total?: number;
241
+ total?: number | undefined;
54
242
  subsetQuery: SubsetQuery;
55
- qb: DriverSpec[D]["queryBuilder"];
243
+ qb: Knex.QueryBuilder;
56
244
  }> {
57
- const db = _db ?? DB.getDB(subset.startsWith("A") ? "w" : "r");
58
- const dbClient = DB.toClient(db);
245
+ const db = _db ?? this.getDB(subset.startsWith("A") ? "w" : "r");
59
246
  baseTable =
60
247
  baseTable ?? inflection.pluralize(inflection.underscore(this.modelName));
61
248
  const queryMode =
62
249
  params.queryMode ?? (params.id !== undefined ? "list" : "both");
63
250
 
64
251
  const { select, virtual, joins, loaders } = subsetQuery;
65
- const _qb = build({
66
- qb: dbClient.from(baseTable).qb,
252
+ const qb = build({
253
+ qb: db.from(baseTable),
67
254
  db,
68
255
  select,
69
256
  joins,
70
257
  virtual,
71
258
  });
72
- dbClient.qb = _qb;
73
- const qb = dbClient;
74
259
 
260
+ const applyJoinClause = (
261
+ qb: Knex.QueryBuilder,
262
+ joins: SubsetQuery["joins"]
263
+ ) => {
264
+ joins.map((join) => {
265
+ if (join.join == "inner") {
266
+ qb.innerJoin(
267
+ `${join.table} as ${join.as}`,
268
+ this.getJoinClause(db, join)
269
+ );
270
+ } else if (join.join == "outer") {
271
+ qb.leftOuterJoin(
272
+ `${join.table} as ${join.as}`,
273
+ this.getJoinClause(db, join)
274
+ );
275
+ }
276
+ });
277
+ };
278
+
279
+ // countQuery
75
280
  const total = await (async () => {
76
- if (queryMode === "list") return undefined;
281
+ if (queryMode === "list") {
282
+ return undefined;
283
+ }
77
284
 
78
- const clonedQb = qb
79
- .clone()
80
- .clearQueryParts(["order", "offset", "limit"])
81
- .clearSelect()
82
- .select(`${baseTable}.id`);
285
+ const clonedQb = qb.clone().clear("order").clear("offset").clear("limit");
83
286
  const parser = new SqlParser.Parser();
84
287
 
288
+ // optmizeCountQuery가 true인 경우 다른 clause에 영향을 주지 않는 모든 join을 제외함
85
289
  if (optimizeCountQuery) {
86
- const parsedQuery = parser.astify(clonedQb.sql);
290
+ const parsedQuery = parser.astify(clonedQb.toQuery());
87
291
  const tables = getTableNamesFromWhere(parsedQuery);
88
- const needToJoin = _.uniq(
292
+ // where절에 사용되는 테이블의 조인을 위해 사용되는 테이블
293
+ const needToJoin = uniq(
89
294
  tables.flatMap((table) =>
90
295
  table.split("__").map((t) => inflection.pluralize(t))
91
296
  )
92
297
  );
93
-
94
- this.applyJoins(
298
+ applyJoinClause(
95
299
  clonedQb,
96
300
  joins.filter((j) => needToJoin.includes(j.table))
97
301
  );
98
302
  } else {
99
- this.applyJoins(clonedQb, joins);
303
+ applyJoinClause(clonedQb, joins);
100
304
  }
101
305
 
102
- const parsedQuery = parser.astify(clonedQb.sql);
306
+ const parsedQuery = parser.astify(clonedQb.toQuery());
103
307
  const q = Array.isArray(parsedQuery) ? parsedQuery[0] : parsedQuery;
104
-
105
308
  if (q.type !== "select") {
106
309
  throw new Error("Invalid query");
107
310
  }
108
311
 
109
- const countColumn = `${getTableName(q.columns[0].expr)}.${q.columns[0].expr.column}`;
110
- clonedQb.clearSelect().count(countColumn, "total").first();
111
- if (q.distinct) {
112
- clonedQb.distinct(countColumn);
113
- }
114
-
312
+ const countQuery =
313
+ q.distinct !== null
314
+ ? clonedQb
315
+ .clear("select")
316
+ .select(
317
+ db.raw(
318
+ `COUNT(DISTINCT \`${getTableName(q.columns[0].expr)}\`.\`${q.columns[0].expr.column}\`) as total`
319
+ )
320
+ )
321
+ .first()
322
+ : clonedQb.clear("select").count("*", { as: "total" }).first();
323
+ const countRow: { total?: number } = await countQuery;
324
+
325
+ // debug: countQuery
115
326
  if (debug === true || debug === "count") {
116
- console.debug("DEBUG: count query", chalk.blue(clonedQb.sql));
327
+ console.debug(
328
+ "DEBUG: count query",
329
+ chalk.blue(countQuery.toQuery().toString())
330
+ );
117
331
  }
118
332
 
119
- const [{ total }] = await clonedQb.execute();
120
- return total;
333
+ return countRow?.total ?? 0;
121
334
  })();
122
335
 
336
+ // listQuery
123
337
  const rows = await (async () => {
124
- if (queryMode === "count") return [];
338
+ if (queryMode === "count") {
339
+ return [];
340
+ }
125
341
 
126
- let listQb = qb;
342
+ // limit, offset
127
343
  if (params.num !== 0) {
128
- listQb = listQb
129
- .limit(params.num!)
130
- .offset(params.num! * (params.page! - 1));
344
+ qb.limit(params.num!);
345
+ qb.offset(params.num! * (params.page! - 1));
131
346
  }
132
347
 
133
- listQb.select(select);
134
- listQb = this.applyJoins(listQb, joins);
348
+ // select, rows
349
+ const listQuery = qb.clone().select(select);
135
350
 
351
+ // join
352
+ applyJoinClause(listQuery, joins);
353
+
354
+ let rows = await listQuery;
355
+ // debug: listQuery
136
356
  if (debug === true || debug === "list") {
137
- console.debug("DEBUG: list query", chalk.blue(listQb.sql));
357
+ console.debug(
358
+ "DEBUG: list query",
359
+ chalk.blue(listQuery.toQuery().toString())
360
+ );
138
361
  }
139
362
 
140
- let rows = await listQb.execute();
141
- rows = await this.useLoaders(dbClient, rows, loaders);
142
- return this.hydrate(rows);
363
+ rows = await this.useLoaders(db, rows, loaders);
364
+ rows = this.hydrate(rows);
365
+ return rows;
143
366
  })();
144
367
 
145
- return {
146
- rows,
147
- total,
148
- subsetQuery,
149
- qb: dbClient.qb,
150
- };
368
+ return { rows, total, subsetQuery, qb };
151
369
  }
152
370
 
153
- async useLoaders(
154
- db: DriverSpec[D]["adapter"],
155
- rows: any[],
156
- loaders: SubsetQuery["loaders"]
157
- ): Promise<any[]> {
158
- if (loaders.length === 0) return rows;
159
-
160
- for (const loader of loaders) {
161
- const fromIds = rows.map((row) => row[loader.manyJoin.idField]);
162
-
163
- if (!fromIds.length) continue;
164
-
165
- let subRows: any[];
166
- let toCol: string;
167
-
168
- if (loader.manyJoin.through === undefined) {
169
- // HasMany
170
- const { subQ, col } = await this.buildHasManyQuery(db, loader, fromIds);
171
- subRows = await subQ.execute();
172
- toCol = col;
173
- } else {
174
- // ManyToMany
175
- const { subQ, col } = await this.buildManyToManyQuery(
176
- db,
177
- loader,
178
- fromIds
179
- );
180
- subRows = await subQ.execute();
181
- toCol = col;
182
- }
183
-
184
- if (loader.loaders) {
185
- subRows = await this.useLoaders(db, subRows, loader.loaders);
186
- }
187
-
188
- // Group and assign loaded rows
189
- const subRowGroups = _.groupBy(subRows, toCol);
190
- rows = rows.map((row) => {
191
- row[loader.as] = (subRowGroups[row[loader.manyJoin.idField]] ?? []).map(
192
- (r) => _.omit(r, toCol)
193
- );
194
- return row;
195
- });
371
+ getJoinClause(
372
+ db: Knex<any, unknown>,
373
+ join: SubsetQuery["joins"][number]
374
+ ): Knex.Raw<any> {
375
+ if (!isCustomJoinClause(join)) {
376
+ return db.raw(`${join.from} = ${join.to}`);
377
+ } else {
378
+ return db.raw(join.custom);
196
379
  }
197
-
198
- return rows;
199
- }
200
-
201
- protected async buildHasManyQuery(
202
- db: DriverSpec[D]["adapter"],
203
- loader: SubsetQuery["loaders"][number],
204
- fromIds: any[]
205
- ) {
206
- const idColumn = `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`;
207
- let qb = db.from(loader.manyJoin.toTable);
208
-
209
- db.where([idColumn, "in", fromIds]).select([...loader.select, idColumn]);
210
- qb = this.applyJoins(qb, loader.oneJoins);
211
-
212
- return {
213
- subQ: qb,
214
- col: loader.manyJoin.toCol,
215
- };
216
380
  }
217
381
 
218
- protected async buildManyToManyQuery(
219
- db: DriverSpec[D]["adapter"],
220
- loader: SubsetQuery["loaders"][number],
221
- fromIds: any[]
222
- ) {
223
- if (!loader.manyJoin.through)
224
- throw new Error("Through table info missing for many-to-many relation");
225
-
226
- const idColumn = `${loader.manyJoin.through.table}.${loader.manyJoin.through.fromCol}`;
227
- let qb = db.from(loader.manyJoin.through.table);
228
-
229
- const throughTable = loader.manyJoin.through.table;
230
- const targetTable = loader.manyJoin.toTable;
231
-
232
- qb = this.applyJoins(qb, [
233
- {
234
- join: "inner",
235
- table: targetTable,
236
- as: targetTable,
237
- from: `${throughTable}.${loader.manyJoin.through.toCol}`,
238
- to: `${targetTable}.${loader.manyJoin.toCol}`,
239
- },
240
- ]);
241
-
242
- qb.where([idColumn, "in", fromIds]).select([...loader.select, idColumn]);
243
- qb = this.applyJoins(qb, loader.oneJoins);
244
-
245
- return {
246
- subQ: qb,
247
- col: loader.manyJoin.through.fromCol,
248
- };
249
- }
250
-
251
- myNow(timestamp?: number): string {
252
- const dt: DateTime =
253
- timestamp === undefined
254
- ? DateTime.local()
255
- : DateTime.fromSeconds(timestamp);
256
- return dt.toFormat("yyyy-MM-dd HH:mm:ss");
257
- }
258
-
259
- hydrate<T>(rows: T[]): T[] {
260
- return rows.map((row: any) => {
261
- const nestedKeys = Object.keys(row).filter((key) => key.includes("__"));
262
- const groups = _.groupBy(nestedKeys, (key) => key.split("__")[0]);
263
- const nullKeys = Object.keys(groups).filter(
264
- (key) =>
265
- groups[key].length > 1 &&
266
- groups[key].every((field) => row[field] === null)
267
- );
268
-
269
- const hydrated = Object.keys(row).reduce((r, field) => {
270
- if (!field.includes("__")) {
271
- if (Array.isArray(row[field]) && _.isObject(row[field][0])) {
272
- r[field] = this.hydrate(row[field]);
273
- } else {
274
- r[field] = row[field];
275
- }
276
- return r;
277
- }
278
-
279
- const parts = field.split("__");
280
- const objPath =
281
- parts[0] +
282
- parts
283
- .slice(1)
284
- .map((part) => `[${part}]`)
285
- .join("");
286
-
287
- _.set(
288
- r,
289
- objPath,
290
- row[field] && Array.isArray(row[field]) && _.isObject(row[field][0])
291
- ? this.hydrate(row[field])
292
- : row[field]
293
- );
294
-
295
- return r;
296
- }, {} as any);
297
-
298
- nullKeys.forEach((nullKey) => (hydrated[nullKey] = null));
299
- return hydrated;
300
- });
382
+ getUpsertBuilder(): UpsertBuilder {
383
+ return new UpsertBuilder();
301
384
  }
302
385
  }
386
+ export const BaseModel = new BaseModelClass();