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