sonamu 0.6.0 → 0.7.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 (406) hide show
  1. package/.swcrc.project-default +18 -0
  2. package/bin/cli.js +24 -0
  3. package/dist/ai/agents/agent.d.ts +11 -0
  4. package/dist/ai/agents/agent.d.ts.map +1 -0
  5. package/dist/ai/agents/agent.js +65 -0
  6. package/dist/ai/agents/index.d.ts +3 -0
  7. package/dist/ai/agents/index.d.ts.map +1 -0
  8. package/dist/ai/agents/index.js +4 -0
  9. package/dist/ai/agents/types.d.ts +43 -0
  10. package/dist/ai/agents/types.d.ts.map +1 -0
  11. package/dist/ai/agents/types.js +3 -0
  12. package/dist/ai/index.d.ts +2 -0
  13. package/dist/ai/index.d.ts.map +1 -0
  14. package/dist/ai/index.js +3 -0
  15. package/dist/ai/providers/rtzr/api.d.ts +22 -0
  16. package/dist/ai/providers/rtzr/api.d.ts.map +1 -0
  17. package/dist/ai/providers/rtzr/api.js +28 -0
  18. package/dist/ai/providers/rtzr/error.d.ts +18 -0
  19. package/dist/ai/providers/rtzr/error.d.ts.map +1 -0
  20. package/dist/ai/providers/rtzr/error.js +29 -0
  21. package/dist/ai/providers/rtzr/index.d.ts +5 -0
  22. package/dist/ai/providers/rtzr/index.d.ts.map +1 -0
  23. package/dist/ai/providers/rtzr/index.js +6 -0
  24. package/dist/ai/providers/rtzr/model.d.ts +52 -0
  25. package/dist/ai/providers/rtzr/model.d.ts.map +1 -0
  26. package/dist/ai/providers/rtzr/model.js +137 -0
  27. package/dist/ai/providers/rtzr/options.d.ts +7 -0
  28. package/dist/ai/providers/rtzr/options.d.ts.map +1 -0
  29. package/dist/ai/providers/rtzr/options.js +47 -0
  30. package/dist/ai/providers/rtzr/provider.d.ts +18 -0
  31. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -0
  32. package/dist/ai/providers/rtzr/provider.js +54 -0
  33. package/dist/ai/providers/rtzr/utils.d.ts +19 -0
  34. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -0
  35. package/dist/ai/providers/rtzr/utils.js +88 -0
  36. package/dist/api/base-frame.d.ts +2 -2
  37. package/dist/api/base-frame.d.ts.map +1 -1
  38. package/dist/api/base-frame.js +2 -1
  39. package/dist/api/caster.d.ts.map +1 -1
  40. package/dist/api/caster.js +6 -1
  41. package/dist/api/code-converters.d.ts +58 -14
  42. package/dist/api/code-converters.d.ts.map +1 -1
  43. package/dist/api/code-converters.js +178 -409
  44. package/dist/api/config.d.ts +27 -13
  45. package/dist/api/config.d.ts.map +1 -1
  46. package/dist/api/config.js +19 -26
  47. package/dist/api/context.d.ts +4 -3
  48. package/dist/api/context.d.ts.map +1 -1
  49. package/dist/api/context.js +1 -1
  50. package/dist/api/decorators.d.ts +20 -6
  51. package/dist/api/decorators.d.ts.map +1 -1
  52. package/dist/api/decorators.js +111 -18
  53. package/dist/api/index.d.ts +2 -2
  54. package/dist/api/index.d.ts.map +1 -1
  55. package/dist/api/index.js +3 -3
  56. package/dist/api/sonamu.d.ts +7 -7
  57. package/dist/api/sonamu.d.ts.map +1 -1
  58. package/dist/api/sonamu.js +83 -51
  59. package/dist/api/validator.d.ts +6 -0
  60. package/dist/api/validator.d.ts.map +1 -0
  61. package/dist/api/validator.js +81 -0
  62. package/dist/bin/build-config.d.ts +5 -1
  63. package/dist/bin/build-config.d.ts.map +1 -1
  64. package/dist/bin/build-config.js +5 -2
  65. package/dist/bin/cli.js +165 -64
  66. package/dist/bin/loader-register.d.ts +2 -0
  67. package/dist/bin/loader-register.d.ts.map +1 -0
  68. package/dist/bin/loader-register.js +34 -0
  69. package/dist/database/_batch_update.d.ts +5 -3
  70. package/dist/database/_batch_update.d.ts.map +1 -1
  71. package/dist/database/_batch_update.js +30 -13
  72. package/dist/database/base-model.d.ts +96 -10
  73. package/dist/database/base-model.d.ts.map +1 -1
  74. package/dist/database/base-model.js +232 -89
  75. package/dist/database/base-model.types.d.ts +93 -0
  76. package/dist/database/base-model.types.d.ts.map +1 -0
  77. package/dist/database/base-model.types.js +10 -0
  78. package/dist/database/code-generator.d.ts +1 -1
  79. package/dist/database/code-generator.d.ts.map +1 -1
  80. package/dist/database/code-generator.js +11 -10
  81. package/dist/database/db.d.ts +5 -6
  82. package/dist/database/db.d.ts.map +1 -1
  83. package/dist/database/db.js +22 -25
  84. package/dist/database/puri-subset.test-d.js +81 -0
  85. package/dist/database/puri-subset.types.d.ts +123 -0
  86. package/dist/database/puri-subset.types.d.ts.map +1 -0
  87. package/dist/database/puri-subset.types.js +16 -0
  88. package/dist/database/puri-wrapper.d.ts +13 -11
  89. package/dist/database/puri-wrapper.d.ts.map +1 -1
  90. package/dist/database/puri-wrapper.js +2 -2
  91. package/dist/database/puri.d.ts +25 -14
  92. package/dist/database/puri.d.ts.map +1 -1
  93. package/dist/database/puri.js +83 -21
  94. package/dist/database/puri.types.d.ts +21 -7
  95. package/dist/database/puri.types.d.ts.map +1 -1
  96. package/dist/database/puri.types.js +4 -1
  97. package/dist/database/transaction-context.d.ts +1 -1
  98. package/dist/database/transaction-context.d.ts.map +1 -1
  99. package/dist/database/transaction-context.js +1 -1
  100. package/dist/database/upsert-builder.d.ts +9 -3
  101. package/dist/database/upsert-builder.d.ts.map +1 -1
  102. package/dist/database/upsert-builder.js +228 -78
  103. package/dist/entity/entity-manager.d.ts +165 -2
  104. package/dist/entity/entity-manager.d.ts.map +1 -1
  105. package/dist/entity/entity-manager.js +26 -10
  106. package/dist/entity/entity.d.ts +5 -3
  107. package/dist/entity/entity.d.ts.map +1 -1
  108. package/dist/entity/entity.js +153 -54
  109. package/dist/exceptions/error-handler.d.ts +1 -1
  110. package/dist/exceptions/error-handler.d.ts.map +1 -1
  111. package/dist/exceptions/error-handler.js +1 -1
  112. package/dist/exceptions/so-exceptions.d.ts +1 -1
  113. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  114. package/dist/exceptions/so-exceptions.js +1 -1
  115. package/dist/file-storage/driver.d.ts +1 -1
  116. package/dist/file-storage/driver.d.ts.map +1 -1
  117. package/dist/file-storage/driver.js +1 -1
  118. package/dist/file-storage/file-storage.js +2 -2
  119. package/dist/index.d.ts +18 -11
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +19 -13
  122. package/dist/migration/code-generation.d.ts +1 -1
  123. package/dist/migration/code-generation.d.ts.map +1 -1
  124. package/dist/migration/code-generation.js +123 -67
  125. package/dist/migration/migration-set.d.ts +2 -10
  126. package/dist/migration/migration-set.d.ts.map +1 -1
  127. package/dist/migration/migration-set.js +67 -218
  128. package/dist/migration/migrator.d.ts +24 -73
  129. package/dist/migration/migrator.d.ts.map +1 -1
  130. package/dist/migration/migrator.js +121 -301
  131. package/dist/migration/postgresql-schema-reader.d.ts +51 -0
  132. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -0
  133. package/dist/migration/postgresql-schema-reader.js +245 -0
  134. package/dist/migration/types.d.ts +6 -38
  135. package/dist/migration/types.d.ts.map +1 -1
  136. package/dist/migration/types.js +1 -1
  137. package/dist/naite/messaging-types.d.ts +43 -0
  138. package/dist/naite/messaging-types.d.ts.map +1 -0
  139. package/dist/naite/messaging-types.js +7 -0
  140. package/dist/naite/naite-reporter.d.ts +41 -0
  141. package/dist/naite/naite-reporter.d.ts.map +1 -0
  142. package/dist/naite/naite-reporter.js +102 -0
  143. package/dist/naite/naite.d.ts +91 -8
  144. package/dist/naite/naite.d.ts.map +1 -1
  145. package/dist/naite/naite.js +285 -41
  146. package/dist/stream/sse.d.ts +2 -2
  147. package/dist/stream/sse.d.ts.map +1 -1
  148. package/dist/stream/sse.js +1 -1
  149. package/dist/syncer/api-parser.d.ts +3 -13
  150. package/dist/syncer/api-parser.d.ts.map +1 -1
  151. package/dist/syncer/api-parser.js +67 -56
  152. package/dist/syncer/checksum.d.ts +2 -2
  153. package/dist/syncer/checksum.d.ts.map +1 -1
  154. package/dist/syncer/checksum.js +11 -11
  155. package/dist/syncer/code-generator.d.ts +3 -3
  156. package/dist/syncer/code-generator.d.ts.map +1 -1
  157. package/dist/syncer/code-generator.js +37 -17
  158. package/dist/syncer/entity-operations.d.ts +2 -2
  159. package/dist/syncer/entity-operations.d.ts.map +1 -1
  160. package/dist/syncer/entity-operations.js +9 -8
  161. package/dist/syncer/file-patterns.d.ts +1 -1
  162. package/dist/syncer/file-patterns.d.ts.map +1 -1
  163. package/dist/syncer/file-patterns.js +1 -1
  164. package/dist/syncer/index.d.ts +4 -4
  165. package/dist/syncer/index.d.ts.map +1 -1
  166. package/dist/syncer/index.js +5 -5
  167. package/dist/syncer/module-loader.d.ts +4 -4
  168. package/dist/syncer/module-loader.d.ts.map +1 -1
  169. package/dist/syncer/module-loader.js +17 -12
  170. package/dist/syncer/syncer.d.ts +31 -24
  171. package/dist/syncer/syncer.d.ts.map +1 -1
  172. package/dist/syncer/syncer.js +92 -45
  173. package/dist/template/entity-converter.d.ts +1 -1
  174. package/dist/template/entity-converter.d.ts.map +1 -1
  175. package/dist/template/entity-converter.js +15 -8
  176. package/dist/template/helpers.d.ts +2 -2
  177. package/dist/template/helpers.d.ts.map +1 -1
  178. package/dist/template/helpers.js +3 -3
  179. package/dist/template/implementations/entity.template.d.ts +2 -2
  180. package/dist/template/implementations/entity.template.d.ts.map +1 -1
  181. package/dist/template/implementations/entity.template.js +4 -5
  182. package/dist/template/implementations/generated.template.d.ts +2 -3
  183. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  184. package/dist/template/implementations/generated.template.js +46 -29
  185. package/dist/template/implementations/generated_http.template.d.ts +2 -3
  186. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  187. package/dist/template/implementations/generated_http.template.js +9 -9
  188. package/dist/template/implementations/generated_sso.template.d.ts +3 -4
  189. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  190. package/dist/template/implementations/generated_sso.template.js +54 -25
  191. package/dist/template/implementations/init_types.template.d.ts +2 -2
  192. package/dist/template/implementations/init_types.template.d.ts.map +1 -1
  193. package/dist/template/implementations/init_types.template.js +2 -2
  194. package/dist/template/implementations/model.template.d.ts +2 -2
  195. package/dist/template/implementations/model.template.d.ts.map +1 -1
  196. package/dist/template/implementations/model.template.js +47 -37
  197. package/dist/template/implementations/model_test.template.d.ts +2 -2
  198. package/dist/template/implementations/model_test.template.d.ts.map +1 -1
  199. package/dist/template/implementations/model_test.template.js +2 -2
  200. package/dist/template/implementations/service.template.d.ts +4 -4
  201. package/dist/template/implementations/service.template.d.ts.map +1 -1
  202. package/dist/template/implementations/service.template.js +24 -16
  203. package/dist/template/implementations/view_enums_buttonset.template.d.ts +2 -2
  204. package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -1
  205. package/dist/template/implementations/view_enums_buttonset.template.js +1 -1
  206. package/dist/template/implementations/view_enums_dropdown.template.d.ts +2 -2
  207. package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -1
  208. package/dist/template/implementations/view_enums_dropdown.template.js +2 -2
  209. package/dist/template/implementations/view_enums_select.template.d.ts +2 -2
  210. package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -1
  211. package/dist/template/implementations/view_enums_select.template.js +2 -2
  212. package/dist/template/implementations/view_form.template.d.ts +2 -2
  213. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  214. package/dist/template/implementations/view_form.template.js +4 -4
  215. package/dist/template/implementations/view_id_all_select.template.d.ts +2 -2
  216. package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -1
  217. package/dist/template/implementations/view_id_all_select.template.js +1 -1
  218. package/dist/template/implementations/view_id_async_select.template.d.ts +2 -2
  219. package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -1
  220. package/dist/template/implementations/view_id_async_select.template.js +1 -1
  221. package/dist/template/implementations/view_list.template.d.ts +2 -2
  222. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  223. package/dist/template/implementations/view_list.template.js +29 -19
  224. package/dist/template/implementations/view_list_columns.template.d.ts +3 -3
  225. package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -1
  226. package/dist/template/implementations/view_list_columns.template.js +1 -1
  227. package/dist/template/implementations/view_search_input.template.d.ts +2 -2
  228. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  229. package/dist/template/implementations/view_search_input.template.js +1 -1
  230. package/dist/template/index.d.ts +4 -2
  231. package/dist/template/index.d.ts.map +1 -1
  232. package/dist/template/index.js +5 -3
  233. package/dist/template/template-manager.d.ts +56 -0
  234. package/dist/template/template-manager.d.ts.map +1 -0
  235. package/dist/template/template-manager.js +125 -0
  236. package/dist/template/template-types.d.ts +16 -0
  237. package/dist/template/template-types.d.ts.map +1 -0
  238. package/dist/template/template-types.js +7 -0
  239. package/dist/template/template.d.ts +12 -2
  240. package/dist/template/template.d.ts.map +1 -1
  241. package/dist/template/template.js +19 -6
  242. package/dist/template/zod-converter.d.ts +40 -7
  243. package/dist/template/zod-converter.d.ts.map +1 -1
  244. package/dist/template/zod-converter.js +341 -58
  245. package/dist/testing/_relation-graph.d.ts +1 -1
  246. package/dist/testing/_relation-graph.d.ts.map +1 -1
  247. package/dist/testing/_relation-graph.js +12 -3
  248. package/dist/testing/fixture-manager.d.ts +42 -11
  249. package/dist/testing/fixture-manager.d.ts.map +1 -1
  250. package/dist/testing/fixture-manager.js +338 -236
  251. package/dist/types/types.d.ts +709 -104
  252. package/dist/types/types.d.ts.map +1 -1
  253. package/dist/types/types.js +309 -52
  254. package/dist/typings/knex.d.js +2 -2
  255. package/dist/utils/async-utils.d.ts.map +1 -1
  256. package/dist/utils/async-utils.js +3 -3
  257. package/dist/utils/console-util.js +1 -1
  258. package/dist/utils/controller.d.ts +1 -0
  259. package/dist/utils/controller.d.ts.map +1 -1
  260. package/dist/utils/controller.js +4 -1
  261. package/dist/utils/esm-utils.d.ts +0 -6
  262. package/dist/utils/esm-utils.d.ts.map +1 -1
  263. package/dist/utils/esm-utils.js +2 -9
  264. package/dist/utils/formatter.d.ts +3 -0
  265. package/dist/utils/formatter.d.ts.map +1 -0
  266. package/dist/utils/formatter.js +110 -0
  267. package/dist/utils/fs-utils.d.ts +1 -1
  268. package/dist/utils/fs-utils.d.ts.map +1 -1
  269. package/dist/utils/fs-utils.js +1 -1
  270. package/dist/utils/lodash-able.d.ts.map +1 -1
  271. package/dist/utils/lodash-able.js +1 -1
  272. package/dist/utils/object-utils.d.ts +44 -0
  273. package/dist/utils/object-utils.d.ts.map +1 -0
  274. package/dist/utils/object-utils.js +191 -0
  275. package/dist/utils/path-utils.d.ts +1 -1
  276. package/dist/utils/path-utils.d.ts.map +1 -1
  277. package/dist/utils/path-utils.js +3 -3
  278. package/dist/utils/process-utils.js +1 -1
  279. package/dist/utils/sql-parser.d.ts +5 -1
  280. package/dist/utils/sql-parser.d.ts.map +1 -1
  281. package/dist/utils/sql-parser.js +14 -3
  282. package/dist/utils/type-utils.d.ts +23 -0
  283. package/dist/utils/type-utils.d.ts.map +1 -0
  284. package/dist/utils/type-utils.js +45 -0
  285. package/dist/utils/utils.d.ts +7 -1
  286. package/dist/utils/utils.d.ts.map +1 -1
  287. package/dist/utils/utils.js +44 -5
  288. package/dist/utils/zod-error.d.ts +1 -1
  289. package/dist/utils/zod-error.d.ts.map +1 -1
  290. package/dist/utils/zod-error.js +1 -1
  291. package/package.json +54 -29
  292. package/src/ai/agents/agent.ts +87 -0
  293. package/src/ai/agents/index.ts +2 -0
  294. package/src/ai/agents/types.ts +47 -0
  295. package/src/ai/index.ts +1 -0
  296. package/src/ai/providers/rtzr/api.ts +37 -0
  297. package/src/ai/providers/rtzr/error.ts +34 -0
  298. package/src/ai/providers/rtzr/index.ts +4 -0
  299. package/src/ai/providers/rtzr/model.ts +201 -0
  300. package/src/ai/providers/rtzr/options.ts +49 -0
  301. package/src/ai/providers/rtzr/provider.ts +91 -0
  302. package/src/ai/providers/rtzr/utils.ts +127 -0
  303. package/src/api/base-frame.ts +4 -2
  304. package/src/api/caster.ts +17 -23
  305. package/src/api/code-converters.ts +176 -533
  306. package/src/api/config.ts +39 -56
  307. package/src/api/context.ts +7 -18
  308. package/src/api/decorators.ts +175 -46
  309. package/src/api/index.ts +2 -2
  310. package/src/api/sonamu.ts +133 -124
  311. package/src/api/validator.ts +83 -0
  312. package/src/bin/build-config.ts +7 -1
  313. package/src/bin/cli.ts +192 -110
  314. package/src/bin/loader-register.ts +38 -0
  315. package/src/database/_batch_update.ts +46 -31
  316. package/src/database/base-model.ts +390 -182
  317. package/src/database/base-model.types.ts +155 -0
  318. package/src/database/code-generator.ts +13 -32
  319. package/src/database/db.ts +36 -50
  320. package/src/database/puri-subset.test-d.ts +471 -0
  321. package/src/database/puri-subset.types.ts +195 -0
  322. package/src/database/puri-wrapper.ts +58 -67
  323. package/src/database/puri.ts +182 -126
  324. package/src/database/puri.types.ts +64 -31
  325. package/src/database/transaction-context.ts +1 -1
  326. package/src/database/upsert-builder.ts +262 -132
  327. package/src/entity/entity-manager.ts +36 -28
  328. package/src/entity/entity.ts +330 -249
  329. package/src/exceptions/error-handler.ts +3 -3
  330. package/src/exceptions/so-exceptions.ts +11 -11
  331. package/src/file-storage/driver.ts +5 -5
  332. package/src/file-storage/file-storage.ts +2 -2
  333. package/src/index.ts +18 -12
  334. package/src/migration/code-generation.ts +185 -172
  335. package/src/migration/migration-set.ts +80 -293
  336. package/src/migration/migrator.ts +182 -425
  337. package/src/migration/mysql-schema-reader.ts.txt +272 -0
  338. package/src/migration/postgresql-schema-reader.ts +310 -0
  339. package/src/migration/types.ts +6 -39
  340. package/src/naite/messaging-types.ts +51 -0
  341. package/src/naite/naite-reporter.ts +128 -0
  342. package/src/naite/naite.ts +378 -33
  343. package/src/shared/web.shared.ts.txt +20 -24
  344. package/src/stream/sse.ts +5 -5
  345. package/src/syncer/api-parser.ts +52 -69
  346. package/src/syncer/checksum.ts +25 -37
  347. package/src/syncer/code-generator.ts +58 -62
  348. package/src/syncer/entity-operations.ts +12 -15
  349. package/src/syncer/file-patterns.ts +2 -2
  350. package/src/syncer/index.ts +4 -4
  351. package/src/syncer/module-loader.ts +28 -25
  352. package/src/syncer/syncer.ts +155 -162
  353. package/src/template/entity-converter.ts +18 -27
  354. package/src/template/helpers.ts +8 -11
  355. package/src/template/implementations/entity.template.ts +6 -6
  356. package/src/template/implementations/generated.template.ts +99 -99
  357. package/src/template/implementations/generated_http.template.ts +21 -54
  358. package/src/template/implementations/generated_sso.template.ts +78 -65
  359. package/src/template/implementations/init_types.template.ts +4 -6
  360. package/src/template/implementations/model.template.ts +47 -38
  361. package/src/template/implementations/model_test.template.ts +3 -3
  362. package/src/template/implementations/service.template.ts +56 -80
  363. package/src/template/implementations/view_enums_buttonset.template.ts +2 -2
  364. package/src/template/implementations/view_enums_dropdown.template.ts +4 -4
  365. package/src/template/implementations/view_enums_select.template.ts +3 -3
  366. package/src/template/implementations/view_form.template.ts +34 -75
  367. package/src/template/implementations/view_id_all_select.template.ts +2 -2
  368. package/src/template/implementations/view_id_async_select.template.ts +9 -23
  369. package/src/template/implementations/view_list.template.ts +54 -95
  370. package/src/template/implementations/view_list_columns.template.ts +4 -10
  371. package/src/template/implementations/view_search_input.template.ts +2 -2
  372. package/src/template/index.ts +4 -2
  373. package/src/template/template-manager.ts +166 -0
  374. package/src/template/template-types.ts +16 -0
  375. package/src/template/template.ts +29 -10
  376. package/src/template/zod-converter.ts +407 -101
  377. package/src/testing/_relation-graph.ts +18 -11
  378. package/src/testing/fixture-manager.ts +468 -362
  379. package/src/types/types.ts +516 -248
  380. package/src/typings/knex.d.ts +7 -9
  381. package/src/utils/async-utils.ts +8 -12
  382. package/src/utils/console-util.ts +1 -1
  383. package/src/utils/controller.ts +3 -0
  384. package/src/utils/esm-utils.ts +8 -18
  385. package/src/utils/formatter.ts +109 -0
  386. package/src/utils/fs-utils.ts +1 -1
  387. package/src/utils/lodash-able.ts +1 -4
  388. package/src/utils/object-utils.ts +217 -0
  389. package/src/utils/path-utils.ts +3 -6
  390. package/src/utils/process-utils.ts +1 -1
  391. package/src/utils/sql-parser.ts +23 -5
  392. package/src/utils/type-utils.ts +83 -0
  393. package/src/utils/utils.ts +58 -9
  394. package/src/utils/zod-error.ts +3 -3
  395. package/dist/bin/cli-wrapper.d.ts +0 -3
  396. package/dist/bin/cli-wrapper.d.ts.map +0 -1
  397. package/dist/bin/cli-wrapper.js +0 -72
  398. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts +0 -2
  399. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts.map +0 -1
  400. package/dist/database/knex-plugins/knex-on-duplicate-update.js +0 -39
  401. package/dist/entity/entity-utils.d.ts +0 -61
  402. package/dist/entity/entity-utils.d.ts.map +0 -1
  403. package/dist/entity/entity-utils.js +0 -210
  404. package/src/bin/cli-wrapper.ts +0 -82
  405. package/src/database/knex-plugins/knex-on-duplicate-update.ts +0 -45
  406. package/src/entity/entity-utils.ts +0 -291
@@ -1,78 +1,29 @@
1
- import * as _ from "lodash-es";
2
- import knex, { Knex } from "knex";
1
+ import assert from "assert";
3
2
  import chalk from "chalk";
4
- import { DateTime } from "luxon";
5
3
  import { mkdir, readdir, unlink, writeFile } from "fs/promises";
6
- import { exists } from "../utils/fs-utils";
7
- import prompts from "prompts";
8
- import { execSync } from "child_process";
4
+ import knex, { type Knex } from "knex";
9
5
  import path from "path";
10
- import { GenMigrationCode, MigrationSet } from "../types/types";
11
- import { EntityManager } from "../entity/entity-manager";
6
+ import { group, sum, unique } from "radashi";
12
7
  import { Sonamu } from "../api";
8
+ import { DB, type SonamuDBConfig } from "../database/db";
9
+ import { EntityManager } from "../entity/entity-manager";
13
10
  import { ServiceUnavailableException } from "../exceptions/so-exceptions";
14
- import { SonamuDBConfig } from "../database/db";
15
- import { generateCreateCode, generateAlterCode } from "./code-generation";
16
- import { MigrationStatus, MigrationCode, ConnString } from "./types";
17
- import { getMigrationSetFromDB } from "./migration-set";
11
+ import { Naite } from "../naite/naite";
12
+ import type { GenMigrationCode, MigrationSet } from "../types/types";
13
+ import { isTest } from "../utils/controller";
14
+ import { exists } from "../utils/fs-utils";
15
+ import { generateAlterCode, generateCreateCode } from "./code-generation";
18
16
  import { getMigrationSetFromEntity } from "./migration-set";
17
+ import { PostgreSQLSchemaReader } from "./postgresql-schema-reader";
18
+ import type { ConnString, MigrationCode, MigrationStatus } from "./types";
19
19
 
20
- type MigratorMode = "dev" | "deploy";
21
- export type MigratorOptions = {
22
- readonly mode: MigratorMode;
23
- };
20
+ export type MigrationResult = {
21
+ connKey: string;
22
+ batchNo: number;
23
+ applied: string[];
24
+ }[];
24
25
 
25
26
  export class Migrator {
26
- targets: {
27
- compare?: Knex;
28
- pending: Knex;
29
- shadow: Knex;
30
- apply: Knex[];
31
- };
32
-
33
- constructor(private readonly options: MigratorOptions) {
34
- const { dbConfig } = Sonamu;
35
-
36
- if (this.options.mode === "dev") {
37
- const devDB = knex(dbConfig.development_master);
38
- const testDB = knex(dbConfig.test);
39
- const fixtureLocalDB = knex(dbConfig.fixture_local);
40
-
41
- const applyDBs = [devDB, testDB, fixtureLocalDB];
42
- if (
43
- (dbConfig.fixture_local.connection as Knex.MySql2ConnectionConfig)
44
- .host !==
45
- (dbConfig.fixture_remote.connection as Knex.MySql2ConnectionConfig)
46
- .host ||
47
- (dbConfig.fixture_local.connection as Knex.MySql2ConnectionConfig)
48
- .database !==
49
- (dbConfig.fixture_remote.connection as Knex.MySql2ConnectionConfig)
50
- .database
51
- ) {
52
- const fixtureRemoteDB = knex(dbConfig.fixture_remote);
53
- applyDBs.push(fixtureRemoteDB);
54
- }
55
-
56
- this.targets = {
57
- compare: devDB,
58
- pending: devDB,
59
- shadow: testDB,
60
- apply: applyDBs,
61
- };
62
- } else if (this.options.mode === "deploy") {
63
- const productionDB = knex(dbConfig.production_master);
64
- const testDB = knex(dbConfig.test);
65
-
66
- this.targets = {
67
- pending: productionDB,
68
- shadow: testDB,
69
- apply: [productionDB],
70
- };
71
- } else {
72
- throw new Error(`잘못된 모드 ${this.options.mode} 입력`);
73
- }
74
- }
75
-
76
27
  private async getMigrationCodes(): Promise<MigrationCode[]> {
77
28
  const srcMigrationsDir = path.join(Sonamu.apiRootPath, "src", "migrations"); // 이건 환경에 관계없이 항상 src에서 찾아야 해요.
78
29
 
@@ -90,6 +41,7 @@ export class Migrator {
90
41
  }))
91
42
  .sort((a, b) => (a.name < b.name ? 1 : -1)); // 이름 내림차순 정렬(최신순)
92
43
 
44
+ Naite.t("migrator:getMigrationCodes:results", codes);
93
45
  return codes;
94
46
  }
95
47
 
@@ -104,11 +56,14 @@ export class Migrator {
104
56
  */
105
57
  async getStatus(): Promise<MigrationStatus> {
106
58
  const codes = await this.getMigrationCodes();
59
+ Naite.t("migrator:getStatus:codes", codes);
107
60
 
108
61
  const connKeys = Object.keys(Sonamu.dbConfig).filter(
109
- (key) => key.endsWith("_slave") === false
62
+ (key) => key.endsWith("_slave") === false,
110
63
  ) as (keyof typeof Sonamu.dbConfig)[];
111
64
 
65
+ let migrationStatusError: string | undefined;
66
+
112
67
  const statuses = await Promise.all(
113
68
  connKeys.map(async (connKey) => {
114
69
  const knexOptions = Sonamu.dbConfig[connKey];
@@ -120,55 +75,58 @@ export class Migrator {
120
75
  } catch (err) {
121
76
  console.warn(
122
77
  chalk.yellow(
123
- `${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`
124
- )
78
+ `${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
79
+ ),
125
80
  );
126
- return "error" /*클라이언트에서 에러 체크에 사용하는 리터럴입니다.*/;
81
+ migrationStatusError = err instanceof Error ? err.message : String(err);
82
+ return "error";
127
83
  }
128
84
  })();
129
- const pending = await (async () => {
85
+ const pending: string[] = await (async () => {
130
86
  try {
131
87
  const [, fdList] = await tConn.migrate.list();
132
- return fdList.map((fd: { file: string }) =>
133
- fd.file.replace(".ts", "")
134
- );
88
+ return fdList.map((fd: { file: string }) => fd.file.replace(".ts", ""));
135
89
  } catch (err) {
90
+ migrationStatusError = err instanceof Error ? err.message : String(err);
136
91
  return [];
137
92
  }
138
93
  })();
139
94
  const currentVersion = await (async () => {
140
95
  try {
141
96
  return await tConn.migrate.currentVersion();
142
- } catch (err) {
97
+ } catch (_err) {
98
+ migrationStatusError = _err instanceof Error ? _err.message : String(_err);
143
99
  return "error";
144
100
  }
145
101
  })();
102
+ Naite.t("migrator:getStatus:status", status);
146
103
 
147
- const connection =
148
- knexOptions.connection as Knex.MySql2ConnectionConfig;
104
+ const connection = knexOptions.connection as Knex.PgConnectionConfig;
149
105
 
150
106
  await tConn.destroy();
151
107
 
152
108
  return {
153
109
  name: connKey.replace("_master", ""),
154
110
  connKey,
155
- connString: `mysql2://${connection.user ?? ""}@${connection.host}:${
111
+ connString: `pg://${connection.user ?? ""}@${connection.host}:${
156
112
  connection.port
157
113
  }/${connection.database}` as ConnString,
158
114
  currentVersion,
159
- status,
115
+ status: status as number | "error",
160
116
  pending,
161
117
  };
162
- })
118
+ }),
163
119
  );
164
120
 
121
+ Naite.t("migrator:getStatus:conns", statuses);
122
+
165
123
  const preparedCodes: GenMigrationCode[] = await (async () => {
166
124
  const status0conn = statuses.find((status) => status.status === 0);
167
125
  if (status0conn === undefined) {
168
126
  console.warn(
169
127
  chalk.yellow(
170
- `While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`
171
- )
128
+ `While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`,
129
+ ),
172
130
  );
173
131
  return [];
174
132
  }
@@ -181,20 +139,14 @@ export class Migrator {
181
139
  return genCodes;
182
140
  })();
183
141
 
142
+ Naite.t("migrator:getStatus:preparedCodes", preparedCodes);
143
+
184
144
  return {
185
145
  conns: statuses,
186
146
  codes,
187
147
  preparedCodes,
148
+ error: migrationStatusError,
188
149
  };
189
- /*
190
- DB 마이그레이션 상태 확인
191
- 1. 전체 DB설정에 대해서 현재 마이그레이션 상태 확인
192
- - connKey: string
193
- - status: number
194
- - currentVersion: string
195
- - list: { file: string; directory: string }[]
196
-
197
- */
198
150
  }
199
151
 
200
152
  /**
@@ -209,16 +161,13 @@ export class Migrator {
209
161
  */
210
162
  async runAction(
211
163
  action: "apply" | "rollback",
212
- targets: (keyof SonamuDBConfig)[]
213
- ): Promise<
214
- {
215
- connKey: string;
216
- batchNo: number;
217
- applied: string[];
218
- }[]
219
- > {
164
+ targets: (keyof SonamuDBConfig)[],
165
+ ): Promise<MigrationResult> {
166
+ Naite.t("migrator:runAction:action", action);
167
+ Naite.t("migrator:runAction:targets", targets);
168
+
220
169
  // get uniq knex configs
221
- const configs = _.uniqBy(
170
+ const configs = unique(
222
171
  targets
223
172
  .map((target) => ({
224
173
  connKey: target,
@@ -226,9 +175,9 @@ export class Migrator {
226
175
  }))
227
176
  .filter((c) => c.options !== undefined),
228
177
  ({ options }) =>
229
- `${(options.connection as Knex.MySql2ConnectionConfig).host}:${
230
- (options.connection as Knex.MySql2ConnectionConfig).port ?? 3306
231
- }/${(options.connection as Knex.MySql2ConnectionConfig).database}`
178
+ `${(options.connection as Knex.PgConnectionConfig).host}:${
179
+ (options.connection as Knex.PgConnectionConfig).port ?? 5432
180
+ }/${(options.connection as Knex.PgConnectionConfig).database}`,
232
181
  );
233
182
 
234
183
  // get connections
@@ -236,7 +185,7 @@ export class Migrator {
236
185
  configs.map(async (config) => ({
237
186
  connKey: config.connKey,
238
187
  knex: knex(config.options),
239
- }))
188
+ })),
240
189
  );
241
190
 
242
191
  // action
@@ -249,9 +198,9 @@ export class Migrator {
249
198
  return {
250
199
  connKey,
251
200
  batchNo,
252
- applied,
201
+ applied, // 이번 latest 호출로 인해 "up"이 적용된 마이그레이션 이름(e.g. "20251124233557_create__companies.ts")들의 배열입니다. 참고: https://github.com/knex/knex/blob/01b177c485d696f1b72858dee728ba143c4fad76/lib/migrations/migrate/Migrator.js#L560
253
202
  };
254
- })
203
+ }),
255
204
  );
256
205
  case "rollback":
257
206
  return Promise.all(
@@ -260,9 +209,9 @@ export class Migrator {
260
209
  return {
261
210
  connKey,
262
211
  batchNo,
263
- applied,
212
+ applied, // 이번 rollback 호출로 인해 "down"이 적용된(=롤백된) 마이그레이션 이름(e.g. "20251124233557_create__companies.ts")들의 배열입니다. 참고: https://github.com/knex/knex/blob/01b177c485d696f1b72858dee728ba143c4fad76/lib/migrations/migrate/Migrator.js#L611
264
213
  };
265
- })
214
+ }),
266
215
  );
267
216
  }
268
217
  })();
@@ -271,12 +220,32 @@ export class Migrator {
271
220
  await Promise.all(
272
221
  conns.map(({ knex }) => {
273
222
  return knex.destroy();
274
- })
223
+ }),
275
224
  );
276
225
 
226
+ Naite.t("migrator:runAction:result", result);
227
+
277
228
  return result;
278
229
  }
279
230
 
231
+ /**
232
+ * 삭제 가능한 마이그레이션 코드 파일을 검증합니다.
233
+ *
234
+ * @param conns 마이그레이션 상태 배열
235
+ * @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
236
+ * @returns 삭제 가능 여부 및 적용된 마이그레이션 코드 파일 이름
237
+ */
238
+ validateDeletable(conns: MigrationStatus["conns"], codeNames: string[]) {
239
+ const appliedCodes = codeNames.filter((codeName) =>
240
+ conns.some((conn) => conn.pending.includes(codeName) === false),
241
+ );
242
+
243
+ return {
244
+ canDelete: appliedCodes.length === 0,
245
+ appliedCodes,
246
+ };
247
+ }
248
+
280
249
  /**
281
250
  * 마이그레이션 코드 파일을 삭제합니다.
282
251
  *
@@ -287,33 +256,38 @@ export class Migrator {
287
256
  */
288
257
  async delCodes(codeNames: string[]): Promise<number> {
289
258
  const { conns } = await this.getStatus();
290
- if (
291
- conns.some((conn) => {
292
- return codeNames.some(
293
- (codeName) => conn.pending.includes(codeName) === false
294
- );
295
- })
296
- ) {
259
+ const { canDelete, appliedCodes } = this.validateDeletable(conns, codeNames);
260
+ if (!canDelete) {
297
261
  throw new Error(
298
- "You cannot delete a migration file if there is already applied."
262
+ `You cannot delete a migration file if there is already applied. Applied codes: ${appliedCodes.join(", ")}`,
299
263
  );
300
264
  }
301
265
 
302
- const delFiles = codeNames.map(
303
- (codeName) => `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`
266
+ return sum(
267
+ await Promise.all(
268
+ codeNames.map(async (codeName) => {
269
+ const filePath = `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`;
270
+ if (await exists(filePath)) {
271
+ await unlink(filePath);
272
+ return 1;
273
+ }
274
+ return 0;
275
+ }),
276
+ ),
304
277
  );
278
+ }
305
279
 
306
- const res = await Promise.all(
307
- delFiles.map(async (delFile) => {
308
- if (await exists(delFile)) {
309
- console.log(chalk.red(`DELETE: ${delFile}`));
310
- await unlink(delFile);
311
- return delFiles.includes(".ts") ? 1 : 0;
312
- }
313
- return 0;
314
- })
280
+ private genDateTag(index: number, baseDate: Date = new Date()): string {
281
+ const date = new Date(baseDate.getTime() + index * 1000);
282
+ const pad = (num: number, size: number = 2) => num.toString().padStart(size, "0");
283
+ return (
284
+ date.getFullYear().toString() +
285
+ pad(date.getMonth() + 1) +
286
+ pad(date.getDate()) +
287
+ pad(date.getHours()) +
288
+ pad(date.getMinutes()) +
289
+ pad(date.getSeconds())
315
290
  );
316
- return _.sum(res);
317
291
  }
318
292
 
319
293
  /**
@@ -325,6 +299,7 @@ export class Migrator {
325
299
  */
326
300
  async generatePreparedCodes(): Promise<number> {
327
301
  const { preparedCodes } = await this.getStatus();
302
+ Naite.t("migrator:generatePreparedCodes:preparedCodes", preparedCodes);
328
303
  if (preparedCodes.length === 0) {
329
304
  console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
330
305
  return 0;
@@ -335,322 +310,54 @@ export class Migrator {
335
310
 
336
311
  for (const [index, pcode] of preparedCodes.entries()) {
337
312
  if (pcode.formatted) {
338
- const dateTag = DateTime.local()
339
- .plus({ seconds: index })
340
- .toFormat("yyyyMMddHHmmss");
313
+ const dateTag = this.genDateTag(index);
341
314
  const filePath = `${migrationsDir}/${dateTag}_${pcode.title}.ts`;
342
- await writeFile(filePath, pcode.formatted!);
343
- console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
315
+ await writeFile(filePath, pcode.formatted);
316
+ !isTest() && console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
344
317
  }
345
318
  }
346
319
 
347
320
  return preparedCodes.length;
348
321
  }
349
322
 
350
- /**
351
- * pending 마이그레이션 목록을 삭제합니다.
352
- *
353
- * CLI에서 사용됩니다.
354
- */
355
- async clearPendingList(): Promise<void> {
356
- const [, pendingList] = (await this.targets.pending.migrate.list()) as [
357
- unknown,
358
- {
359
- file: string;
360
- directory: string;
361
- }[],
362
- ];
363
- const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
364
- const delList = pendingList.map((df) => {
365
- return path.join(migrationsDir, df.file);
366
- });
367
- for (let p of delList) {
368
- if (await exists(p)) {
369
- await unlink(p);
370
- }
371
- }
372
- }
373
-
374
- /**
375
- * 마이그레이션 코드 파일을 확인합니다.
376
- *
377
- * CLI에서 사용됩니다.
378
- */
379
- async check(): Promise<void> {
380
- const codes = await this.compareMigrations(this.targets.compare!);
381
- if (codes.length === 0) {
382
- console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
383
- return;
384
- }
385
-
386
- // 현재 생성된 코드 표기
387
- console.table(codes, ["type", "title"]);
388
- console.log(codes[0]);
389
- }
390
-
391
- /**
392
- * 마이그레이션을 수행합니다.
393
- *
394
- * runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
395
- * 이 함수는 생성자로 들어온 connection(knex)들에 대해 마이그레이션을 수행합니다.
396
- *
397
- * CLI에서 사용됩니다.
398
- */
399
- async run(): Promise<void> {
400
- // pending 마이그레이션 확인
401
- const [, pendingList] = await this.targets.pending.migrate.list();
402
- if (pendingList.length > 0) {
403
- console.log(
404
- chalk.red("pending 된 마이그레이션이 존재합니다."),
405
- pendingList.map((pending: any) => pending.file)
406
- );
407
-
408
- // pending이 있는 경우 Shadow DB 테스트 진행 여부 컨펌
409
- const answer = await prompts({
410
- type: "confirm",
411
- name: "value",
412
- message: "Shadow DB 테스트를 진행하시겠습니까?",
413
- initial: true,
414
- });
415
- if (answer.value === false) {
416
- return;
417
- }
418
-
419
- console.time(chalk.blue("Migrator - runShadowTest"));
420
- await this.runShadowTest();
421
- console.timeEnd(chalk.blue("Migrator - runShadowTest"));
422
- await Promise.all(
423
- this.targets.apply.map(async (applyDb) => {
424
- const label = chalk.green(
425
- `APPLIED ${
426
- applyDb.client.connectionSettings.host
427
- } ${applyDb.client.database()}`
428
- );
429
- console.time(label);
430
- const [,] = await applyDb.migrate.latest();
431
- console.timeEnd(label);
432
- })
433
- );
434
- }
435
-
436
- // Entity-DB간 비교하여 코드 생성 리턴
437
- const codes = await this.compareMigrations(this.targets.compare!);
438
- if (codes.length === 0) {
439
- console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
440
- return;
441
- }
442
-
443
- // 현재 생성된 코드 표기
444
- console.table(codes, ["type", "title"]);
445
-
446
- /* DEBUG: 디버깅용 코드
447
- codes.map((code) => console.log(code.formatted));
448
- process.exit();
449
- */
450
-
451
- // 실제 파일 생성 프롬프트
452
- const answer = await prompts({
453
- type: "confirm",
454
- name: "value",
455
- message: "마이그레이션 코드를 생성하시겠습니까?",
456
- initial: false,
457
- });
458
- if (answer.value === false) {
459
- return;
460
- }
461
-
462
- // 실제 코드 생성
463
- const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
464
-
465
- for (const [index, code] of codes.entries()) {
466
- if (code.formatted) {
467
- const dateTag = DateTime.local()
468
- .plus({ seconds: index })
469
- .toFormat("yyyyMMddHHmmss");
470
- const filePath = `${migrationsDir}/${dateTag}_${code.title}.ts`;
471
- await writeFile(filePath, code.formatted!);
472
- console.log(chalk.green(`MIGRTAION CREATED ${filePath}`));
473
- }
474
- }
475
- }
476
-
477
- /**
478
- * 타겟으로 지정된 DB를 롤백합니다.
479
- *
480
- * runAction이 인자로 들어온 타겟들에 대해 주어진 동작(apply/rollback)을 수행한다면,
481
- * 이 함수는 생성자로 들어온 connection(knex)들에 대해 롤백을 수행합니다.
482
- *
483
- * CLI에서 사용됩니다.
484
- */
485
- async rollback() {
486
- console.time(chalk.red("rollback:"));
487
- const rollbackAllResult = await Promise.all(
488
- this.targets.apply.map(async (db) => {
489
- await db.migrate.forceFreeMigrationsLock();
490
- return db.migrate.rollback(undefined, false);
491
- })
492
- );
493
- console.dir({ rollbackAllResult }, { depth: null });
494
- console.timeEnd(chalk.red("rollback:"));
495
- }
496
- /**
497
- * Shadow DB 테스트를 진행합니다.
498
- *
499
- * Sonamu UI에서 사용됩니다.
500
- *
501
- * @returns Shadow DB 테스트 결과
502
- */
503
- async runShadowTest(): Promise<
504
- {
505
- connKey: string;
506
- batchNo: number;
507
- applied: string[];
508
- }[]
509
- > {
510
- // ShadowDB 생성 후 테스트 진행
511
- const tdb = knex(Sonamu.dbConfig.test);
512
- const tdbConn = Sonamu.dbConfig.test
513
- .connection as Knex.MySql2ConnectionConfig;
514
- const shadowDatabase = tdbConn.database + "__migration_shadow";
515
- const tmpSqlPath = `/tmp/${shadowDatabase}.sql`;
516
-
517
- // 테스트DB 덤프 후 Database명 치환
518
- console.log(
519
- chalk.magenta(`${tdbConn.database}의 데이터 ${tmpSqlPath}로 덤프`)
520
- );
521
- execSync(
522
- `mysqldump -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${tdbConn.database} --single-transaction --no-create-db --triggers > ${tmpSqlPath};`
523
- );
524
- execSync(
525
- `sed -i'' -e 's/\`${tdbConn.database}\`/\`${shadowDatabase}\`/g' ${tmpSqlPath};`
526
- );
527
-
528
- // 기존 ShadowDB 리셋
529
- console.log(chalk.magenta(`${shadowDatabase} 리셋`));
530
- await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
531
- await tdb.raw(`CREATE DATABASE \`${shadowDatabase}\`;`);
532
-
533
- // ShadowDB 테이블 + 데이터 생성
534
- console.log(chalk.magenta(`${shadowDatabase} 데이터베이스 생성`));
535
- execSync(
536
- `mysql -h${tdbConn.host} -P${tdbConn.port ?? 3306} -u${tdbConn.user} -p'${tdbConn.password}' ${shadowDatabase} < ${tmpSqlPath};`
537
- );
538
-
539
- // shadow db 테스트 진행
540
- const sdb = knex({
541
- ...Sonamu.dbConfig.test,
542
- connection: {
543
- ...tdbConn,
544
- database: shadowDatabase,
545
- password: tdbConn.password,
546
- },
547
- });
548
-
549
- // shadow db 테스트 진행
550
- try {
551
- const [batchNo, applied] = await sdb.migrate.latest();
552
- console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
553
- batchNo,
554
- applied,
555
- });
556
-
557
- // 생성한 Shadow DB 삭제
558
- console.log(chalk.magenta(`${shadowDatabase} 삭제`));
559
- await tdb.raw(`DROP DATABASE IF EXISTS \`${shadowDatabase}\`;`);
560
-
561
- return [
562
- {
563
- connKey: "shadow",
564
- batchNo,
565
- applied,
566
- },
567
- ];
568
- } catch (e) {
569
- console.error(e);
570
- throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
571
- } finally {
572
- await tdb.destroy();
573
- }
574
- }
575
-
576
- /**
577
- * 모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제합니다.
578
- *
579
- * CLI에서 사용됩니다.
580
- *
581
- * @returns
582
- */
583
- async resetAll() {
584
- const answer = await prompts({
585
- type: "confirm",
586
- name: "value",
587
- message: "모든 DB를 롤백하고 전체 마이그레이션 파일을 삭제하시겠습니까?",
588
- initial: false,
589
- });
590
- if (answer.value === false) {
591
- return;
592
- }
593
-
594
- console.time(chalk.red("rollback-all:"));
595
- const rollbackAllResult = await Promise.all(
596
- this.targets.apply.map(async (db) => {
597
- await db.migrate.forceFreeMigrationsLock();
598
- return db.migrate.rollback(undefined, true);
599
- })
600
- );
601
- console.log({ rollbackAllResult });
602
- console.timeEnd(chalk.red("rollback-all:"));
603
-
604
- const migrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
605
- console.time(chalk.red("delete migration files"));
606
- execSync(`rm -f ${migrationsDir}/*`);
607
- execSync(`rm -f ${migrationsDir.replace("/src/", "/dist/")}/*`);
608
- console.timeEnd(chalk.red("delete migration files"));
609
- }
610
-
611
- private async compareMigrations(
612
- compareDB: Knex
613
- ): Promise<GenMigrationCode[]> {
323
+ async compareMigrations(compareDB: Knex): Promise<GenMigrationCode[]> {
614
324
  // Entity 순회하여 싱크
615
325
  const entityIds = EntityManager.getAllIds();
616
326
 
617
327
  // 조인테이블 포함하여 Entity에서 MigrationSet 추출
618
328
  const entitySetsWithJoinTable = entityIds
619
329
  .filter((entityId) => EntityManager.get(entityId).props.length > 0)
620
- .map((entityId) =>
621
- getMigrationSetFromEntity(EntityManager.get(entityId))
622
- );
330
+ .map((entityId) => getMigrationSetFromEntity(EntityManager.get(entityId)));
623
331
 
624
332
  // 조인테이블만 추출
625
- const joinTablesWithDup = entitySetsWithJoinTable
626
- .map((entitySet) => entitySet.joinTables)
627
- .flat();
333
+ const joinTablesWithDup = entitySetsWithJoinTable.flatMap((entitySet) => entitySet.joinTables);
628
334
  // 중복 제거 (중복인 경우 indexes를 병합)
629
- const joinTables = Object.values(
630
- _.groupBy(joinTablesWithDup, (jt) => jt.table)
631
- ).map((tables) => {
335
+ const joinTables = Object.values(group(joinTablesWithDup, (jt) => jt.table)).map((tables) => {
336
+ assert(tables !== undefined, "tables is undefined");
632
337
  if (tables.length === 1) {
633
338
  return tables[0];
634
339
  }
635
340
  return {
636
341
  ...tables[0],
637
- indexes: _.uniqBy(
342
+ indexes: unique(
638
343
  tables.flatMap((t) => t.indexes),
639
- (index) => [index.type, ...index.columns.sort()].join("-")
344
+ (index) => [index.type, ...index.columns.sort()].join("-"),
640
345
  ),
641
346
  };
642
347
  });
643
348
 
644
349
  // 조인테이블 포함하여 MigrationSet 배열
645
- const entitySets: MigrationSet[] = [
646
- ...entitySetsWithJoinTable,
647
- ...joinTables,
648
- ];
350
+ const entitySets: MigrationSet[] = [...entitySetsWithJoinTable, ...joinTables];
649
351
 
650
352
  const codes: GenMigrationCode[] = (
651
353
  await Promise.all(
652
354
  entitySets.map(async (entitySet) => {
653
- const dbSet = await getMigrationSetFromDB(compareDB, entitySet.table);
355
+ const dbSet = await PostgreSQLSchemaReader.getMigrationSetFromDB(
356
+ compareDB,
357
+ entitySet.table,
358
+ );
359
+ Naite.t(`migrator:compareMigrations:entitySet:${entitySet.table}`, entitySet);
360
+ Naite.t(`migrator:compareMigrations:dbSet:${entitySet.table}`, dbSet);
654
361
 
655
362
  if (dbSet === null) {
656
363
  // 기존 테이블 없음, 새로 테이블 생성
@@ -659,13 +366,13 @@ export class Migrator {
659
366
  // 기존 테이블 존재하는 케이스
660
367
  return await generateAlterCode(entitySet, dbSet);
661
368
  }
662
- })
369
+ }),
663
370
  )
664
371
  ).flat();
665
372
 
666
373
  // normal 타입이 앞으로, foreign이 뒤로
667
374
  codes.sort((codeA, codeB) => {
668
- if (codeA.type === "foreign" && codeB.type == "normal") {
375
+ if (codeA.type === "foreign" && codeB.type === "normal") {
669
376
  return 1;
670
377
  } else if (codeA.type === "normal" && codeB.type === "foreign") {
671
378
  return -1;
@@ -678,17 +385,67 @@ export class Migrator {
678
385
  }
679
386
 
680
387
  /**
681
- * 마이그레이션 대상 커넥션을 종료합니다.
388
+ * Shadow DB 테스트를 진행합니다.
682
389
  *
683
- * CLI에서 사용됩니다.
390
+ * Sonamu UI에서 사용됩니다.
684
391
  *
685
- * @returns {Promise<void>} 종료 결과
392
+ * @returns Shadow DB 테스트 결과
686
393
  */
687
- async destroy(): Promise<void> {
688
- await Promise.all(
689
- this.targets.apply.map((db) => {
690
- return db.destroy();
691
- })
692
- );
394
+ async runShadowTest(): Promise<MigrationResult> {
395
+ const tdbConn = Sonamu.dbConfig.test.connection as Knex.PgConnectionConfig;
396
+ const shadowDatabase = `${tdbConn.database}__migration_shadow`;
397
+
398
+ // 테스트 상황에서는 트랜잭션을 초기화하고, 새 데이터베이스 커넥션을 가져와야 함
399
+ if (isTest()) {
400
+ await DB.clearTestTransaction();
401
+ await DB.destroy();
402
+ }
403
+
404
+ // 기존 Shadow DB 삭제 후 Shadow DB 생성
405
+ const tdb = knex(Sonamu.dbConfig.test);
406
+ !isTest() && console.log(chalk.magenta(`${shadowDatabase} 삭제`));
407
+ await tdb.raw(`DROP DATABASE IF EXISTS ${shadowDatabase}`);
408
+ await tdb.raw(`CREATE DATABASE ${shadowDatabase} TEMPLATE ${tdbConn.database}`);
409
+
410
+ // Shadow DB에 연결
411
+ const sdb = knex({
412
+ ...Sonamu.dbConfig.test,
413
+ connection: {
414
+ ...tdbConn,
415
+ database: shadowDatabase,
416
+ password: tdbConn.password,
417
+ },
418
+ });
419
+
420
+ // shadow DB 테스트 진행
421
+ try {
422
+ const [batchNo, applied] = await sdb.migrate.latest();
423
+ !isTest() &&
424
+ console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
425
+ batchNo,
426
+ applied,
427
+ });
428
+
429
+ return [
430
+ {
431
+ connKey: "shadow",
432
+ batchNo,
433
+ applied,
434
+ },
435
+ ];
436
+ } catch (e) {
437
+ console.error(e);
438
+ throw new ServiceUnavailableException("Shadow DB 테스트 진행 중 에러");
439
+ } finally {
440
+ // Shadow DB 연결 종료
441
+ await sdb.destroy();
442
+
443
+ // Shadow DB 삭제
444
+ !isTest() && console.log(chalk.magenta(`${shadowDatabase} 삭제`));
445
+ await tdb.raw(`DROP DATABASE IF EXISTS ${shadowDatabase}`);
446
+
447
+ // Test DB 연결 종료
448
+ await tdb.destroy();
449
+ }
693
450
  }
694
451
  }