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
@@ -0,0 +1,282 @@
1
+ import assert from "assert";
2
+ import { readFile } from "fs/promises";
3
+ import inflection from "inflection";
4
+ import ts from "typescript";
5
+ import { type ExtendedApi, registeredApis } from "../api/decorators";
6
+ import { validateMethodName } from "../api/validator";
7
+ import type { ApiParam, ApiParamType } from "../types/types";
8
+ import type { AbsolutePath } from "../utils/path-utils";
9
+
10
+ /**
11
+ * TypeScript 파일을 파싱하여 API 메소드 정보를 추출합니다.
12
+ * @api 데코레이터가 붙은 메소드들의 타입 정보를 분석합니다.
13
+ * @param filePath - 파싱할 TypeScript 파일의 절대 경로
14
+ * @returns API 메소드 정보 배열 (타입 파라미터, 파라미터, 리턴 타입 등)
15
+ */
16
+ export async function readApisFromFile(filePath: AbsolutePath): Promise<ExtendedApi[]> {
17
+ if (!filePath.endsWith(".ts")) {
18
+ throw new Error(
19
+ `${filePath} does not seem to be a TypeScript file. Please check the file path. We only support parsing TypeScript files.`,
20
+ );
21
+ }
22
+
23
+ const sourceFile = ts.createSourceFile(
24
+ filePath,
25
+ (await readFile(filePath)).toString(),
26
+ ts.ScriptTarget.Latest,
27
+ );
28
+
29
+ const methods: Omit<ExtendedApi, "path" | "options">[] = [];
30
+ let modelName: string = "UnknownModel";
31
+ let methodName: string = "unknownMethod";
32
+ const visitor = (node: ts.Node) => {
33
+ if (ts.isClassDeclaration(node)) {
34
+ if (node.name && ts.isIdentifier(node.name)) {
35
+ modelName = node.name.escapedText.toString().replace(/Class$/, "");
36
+ }
37
+ }
38
+ if (ts.isMethodDeclaration(node)) {
39
+ if (ts.isIdentifier(node.name)) {
40
+ methodName = node.name.escapedText.toString();
41
+ validateMethodName(methodName);
42
+ }
43
+
44
+ const typeParameters: ApiParamType.TypeParam[] = (node.typeParameters ?? []).map(
45
+ (typeParam) => {
46
+ const tp = typeParam as ts.TypeParameterDeclaration;
47
+
48
+ return {
49
+ t: "type-param",
50
+ id: tp.name.escapedText.toString(),
51
+ constraint: tp.constraint ? resolveTypeNode(tp.constraint) : undefined,
52
+ };
53
+ },
54
+ );
55
+ const parameters: ApiParam[] = node.parameters.map((paramDec, index) => {
56
+ const defaultDef = printNode(paramDec.initializer, sourceFile);
57
+
58
+ // 기본값이 있는 경우 paramDec.type가 undefined로 나옴
59
+
60
+ return resolveParamDec(
61
+ {
62
+ name: paramDec.name,
63
+ type: paramDec.type as ts.TypeNode,
64
+ optional: paramDec.questionToken !== undefined || paramDec.initializer !== undefined,
65
+ defaultDef,
66
+ },
67
+ index,
68
+ );
69
+ });
70
+ if (node.type === undefined) {
71
+ throw new Error(`리턴 타입이 기재되지 않은 메소드 ${modelName}.${methodName}`);
72
+ }
73
+ const returnType = resolveTypeNode(node.type);
74
+
75
+ methods.push({
76
+ modelName,
77
+ methodName,
78
+ typeParameters,
79
+ parameters,
80
+ returnType,
81
+ });
82
+ }
83
+ ts.forEachChild(node, visitor);
84
+ };
85
+ visitor(sourceFile);
86
+
87
+ if (methods.length === 0) {
88
+ return [];
89
+ }
90
+
91
+ // 현재 파일의 등록된 API 필터
92
+ const currentModelApis = registeredApis.filter((api) => {
93
+ return methods.find(
94
+ (method) => method.modelName === api.modelName && method.methodName === api.methodName,
95
+ );
96
+ });
97
+ if (currentModelApis.length === 0) {
98
+ // const p = path.join(tmpdir(), "sonamu-syncer-error.json");
99
+ // writeFileSync(p, JSON.stringify(registeredApis, null, 2));
100
+ // execSync(`open ${p}`);
101
+ throw new Error(`현재 파일에 사전 등록된 API가 없습니다. ${filePath}`);
102
+ }
103
+
104
+ // 등록된 API에 현재 메소드 타입 정보 확장
105
+ const extendedApis = currentModelApis.map((api) => {
106
+ const foundMethod = methods.find(
107
+ (method) => method.modelName === api.modelName && method.methodName === api.methodName,
108
+ );
109
+ if (!foundMethod) {
110
+ throw new Error(`API ${api.modelName}.${api.methodName} not found in ${filePath}`);
111
+ }
112
+ return {
113
+ ...api,
114
+ typeParameters: foundMethod?.typeParameters,
115
+ parameters: foundMethod?.parameters,
116
+ returnType: foundMethod?.returnType,
117
+ };
118
+ });
119
+ return extendedApis;
120
+ }
121
+
122
+ function resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
123
+ switch (typeNode?.kind) {
124
+ case ts.SyntaxKind.AnyKeyword:
125
+ return "any";
126
+ case ts.SyntaxKind.UnknownKeyword:
127
+ return "unknown";
128
+ case ts.SyntaxKind.StringKeyword:
129
+ return "string";
130
+ case ts.SyntaxKind.NumberKeyword:
131
+ return "number";
132
+ case ts.SyntaxKind.BooleanKeyword:
133
+ return "boolean";
134
+ case ts.SyntaxKind.UndefinedKeyword:
135
+ return "undefined";
136
+ case ts.SyntaxKind.NullKeyword:
137
+ return "null";
138
+ case ts.SyntaxKind.VoidKeyword:
139
+ return "void";
140
+ case ts.SyntaxKind.LiteralType: {
141
+ const literal = (typeNode as ts.LiteralTypeNode).literal;
142
+ if (ts.isStringLiteral(literal)) {
143
+ return {
144
+ t: "string-literal",
145
+ value: literal.text,
146
+ };
147
+ } else if (ts.isNumericLiteral(literal)) {
148
+ return {
149
+ t: "numeric-literal",
150
+ value: Number(literal.text),
151
+ };
152
+ } else {
153
+ if (literal.kind === ts.SyntaxKind.NullKeyword) {
154
+ return "null";
155
+ } else if (literal.kind === ts.SyntaxKind.UndefinedKeyword) {
156
+ return "undefined";
157
+ } else if (literal.kind === ts.SyntaxKind.TrueKeyword) {
158
+ return "true";
159
+ } else if (literal.kind === ts.SyntaxKind.FalseKeyword) {
160
+ return "false";
161
+ }
162
+ throw new Error("알 수 없는 리터럴");
163
+ }
164
+ }
165
+ case ts.SyntaxKind.ArrayType: {
166
+ const arrNode = typeNode as ts.ArrayTypeNode;
167
+ return {
168
+ t: "array",
169
+ elementsType: resolveTypeNode(arrNode.elementType),
170
+ };
171
+ }
172
+ case ts.SyntaxKind.TypeLiteral: {
173
+ const literalNode = typeNode as ts.TypeLiteralNode;
174
+ return {
175
+ t: "object",
176
+ props: literalNode.members.map((member) => {
177
+ if (ts.isIndexSignatureDeclaration(member)) {
178
+ assert(member.parameters[0]);
179
+ const res = resolveParamDec({
180
+ name: member.parameters[0].name as ts.Identifier,
181
+ type: member.parameters[0].type as ts.TypeNode,
182
+ });
183
+
184
+ return resolveParamDec({
185
+ name: {
186
+ escapedText: `[${res.name}${res.optional ? "?" : ""}: ${res.type}]`,
187
+ } as ts.Identifier,
188
+ type: member.type as ts.TypeNode,
189
+ });
190
+ } else {
191
+ return resolveParamDec({
192
+ name: (member as ts.PropertySignature).name as ts.Identifier,
193
+ type: (member as ts.PropertySignature).type as ts.TypeNode,
194
+ optional: (member as ts.PropertySignature).questionToken !== undefined,
195
+ });
196
+ }
197
+ }),
198
+ };
199
+ }
200
+ case ts.SyntaxKind.TypeReference:
201
+ return {
202
+ t: "ref",
203
+ id: ((typeNode as ts.TypeReferenceNode).typeName as ts.Identifier).escapedText.toString(),
204
+ args: (typeNode as ts.TypeReferenceNode).typeArguments?.map((typeArg) =>
205
+ resolveTypeNode(typeArg),
206
+ ),
207
+ };
208
+ case ts.SyntaxKind.UnionType:
209
+ return {
210
+ t: "union",
211
+ types: (typeNode as ts.UnionTypeNode).types.map((type) => resolveTypeNode(type)),
212
+ };
213
+ case ts.SyntaxKind.IntersectionType:
214
+ return {
215
+ t: "intersection",
216
+ types: (typeNode as ts.IntersectionTypeNode).types.map((type) => resolveTypeNode(type)),
217
+ };
218
+ case ts.SyntaxKind.IndexedAccessType:
219
+ return {
220
+ t: "indexed-access",
221
+ object: resolveTypeNode((typeNode as ts.IndexedAccessTypeNode).objectType),
222
+ index: resolveTypeNode((typeNode as ts.IndexedAccessTypeNode).indexType),
223
+ };
224
+ case ts.SyntaxKind.TupleType:
225
+ if (ts.isTupleTypeNode(typeNode)) {
226
+ return {
227
+ t: "tuple-type",
228
+ elements: typeNode.elements.map((elem) => resolveTypeNode(elem)),
229
+ };
230
+ }
231
+ break;
232
+ case undefined:
233
+ throw new Error(`typeNode undefined`);
234
+ }
235
+
236
+ console.debug(typeNode);
237
+ throw new Error(`알 수 없는 SyntaxKind ${typeNode.kind}`);
238
+ }
239
+
240
+ function resolveParamDec(
241
+ paramDec: {
242
+ name: ts.BindingName;
243
+ type: ts.TypeNode;
244
+ optional?: boolean;
245
+ defaultDef?: string;
246
+ },
247
+ index: number = 0,
248
+ ): ApiParam {
249
+ const name = paramDec.name as ts.Identifier;
250
+ const type = resolveTypeNode(paramDec.type);
251
+
252
+ if (name === undefined) {
253
+ console.debug({ name, type, paramDec });
254
+ }
255
+
256
+ const result: ApiParam = {
257
+ name: name.escapedText ? name.escapedText.toString() : `nonameAt${index}`,
258
+ type,
259
+ optional: paramDec.optional === true,
260
+ defaultDef: paramDec?.defaultDef,
261
+ };
262
+
263
+ // 구조분해할당의 경우 타입이름 사용
264
+ if (
265
+ ts.isObjectBindingPattern(name) &&
266
+ ts.isTypeReferenceNode(paramDec.type) &&
267
+ ts.isIdentifier(paramDec.type.typeName)
268
+ ) {
269
+ result.name = inflection.camelize(paramDec.type.typeName.text, true);
270
+ }
271
+
272
+ return result;
273
+ }
274
+
275
+ function printNode(node: ts.Node | undefined, sourceFile: ts.SourceFile): string | undefined {
276
+ if (node === undefined) {
277
+ return undefined;
278
+ }
279
+
280
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
281
+ return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
282
+ }
@@ -0,0 +1,140 @@
1
+ import crypto, { type BinaryLike } from "crypto";
2
+ import equal from "fast-deep-equal";
3
+ import { createReadStream, type PathLike } from "fs";
4
+ import { readFile, writeFile } from "fs/promises";
5
+ import path from "path";
6
+ import { isEqual } from "radashi";
7
+ import { Sonamu } from "../api/sonamu";
8
+ import { globAsync } from "../utils/async-utils";
9
+ import { exists } from "../utils/fs-utils";
10
+ import type { AbsolutePath, ApiRelativePath } from "../utils/path-utils";
11
+ import { differenceWith } from "../utils/utils";
12
+ import { getChecksumPatternGroupInAbsolutePath } from "./file-patterns";
13
+
14
+ type PathAndChecksum = {
15
+ path: AbsolutePath;
16
+ checksum: string;
17
+ };
18
+
19
+ /**
20
+ * 체크섬 파일에 저장된 내용과 현재 실제 파일의 체크섬을 비교하여 변경된 파일을 찾습니다.
21
+ * @returns 변경된 파일 경로 배열. 프로젝트 루트부터 슬래시로 시작합니다. 예시: "/src/application/user/user.model.ts"
22
+ */
23
+ export async function findChangedFilesUsingChecksums(): Promise<AbsolutePath[]> {
24
+ const calculatedChecksums = await getCurrentChecksums();
25
+ const savedChecksums = await getPreviousChecksums();
26
+
27
+ const isSame = equal(calculatedChecksums, savedChecksums);
28
+ if (isSame) {
29
+ return [];
30
+ }
31
+
32
+ return differenceWith(calculatedChecksums, savedChecksums, isEqual).map((r) => r.path);
33
+ }
34
+
35
+ /**
36
+ * 체크섬을 갱신합니다.
37
+ * 현재 파일들의 체크섬을 계산해서 구한 다음, 체크섬 파일에 저장된 내용과 다르면 체크섬 파일을 갱신합니다.
38
+ */
39
+ export async function renewChecksums(): Promise<void> {
40
+ const calculatedChecksums = await getCurrentChecksums();
41
+ const savedChecksums = await getPreviousChecksums();
42
+
43
+ const isSame = equal(calculatedChecksums, savedChecksums);
44
+ if (isSame) {
45
+ return;
46
+ }
47
+
48
+ await saveChecksums(calculatedChecksums);
49
+ }
50
+
51
+ /**
52
+ * 두 파일의 내용이 같은지 체크섬으로 비교합니다.
53
+ * 만약 파일이 둘 중 하나라도 없다면 비교 불가로 false 반환합니다.
54
+ * @param one 파일 경로
55
+ * @param two 파일 경로
56
+ * @returns boolean
57
+ */
58
+ export async function areFilesSame(one: PathLike, two: PathLike): Promise<boolean> {
59
+ if (!(await exists(one)) || !(await exists(two))) {
60
+ return false;
61
+ }
62
+
63
+ const oneChecksum = await getChecksumOfFile(one);
64
+ const twoChecksum = await getChecksumOfFile(two);
65
+
66
+ return oneChecksum === twoChecksum;
67
+ }
68
+
69
+ async function getCurrentChecksums(): Promise<PathAndChecksum[]> {
70
+ const filePaths = (
71
+ await Promise.all(
72
+ Object.entries(getChecksumPatternGroupInAbsolutePath()).map(async ([_fileType, pattern]) => {
73
+ return globAsync(pattern) as Promise<AbsolutePath[]>;
74
+ }),
75
+ )
76
+ )
77
+ .flat()
78
+ .sort();
79
+
80
+ const fileChecksums = await Promise.all(
81
+ filePaths.map(async (filePath) => {
82
+ return {
83
+ path: filePath,
84
+ checksum: await getChecksumOfFile(filePath),
85
+ };
86
+ }),
87
+ );
88
+
89
+ return fileChecksums;
90
+ }
91
+
92
+ async function getPreviousChecksums(): Promise<PathAndChecksum[]> {
93
+ const checksumFilePath = getChecksumFilePath();
94
+ if (!(await exists(checksumFilePath))) {
95
+ return [];
96
+ }
97
+
98
+ const previousChecksums = JSON.parse(await readFile(checksumFilePath, "utf-8")).map(
99
+ (r: { path: ApiRelativePath; checksum: string }) => ({
100
+ path: path.join(Sonamu.apiRootPath, r.path), // 체크섬 파일에서 읽을 때: API 상대 경로 → 절대 경로
101
+ checksum: r.checksum,
102
+ }),
103
+ ) as PathAndChecksum[];
104
+ return previousChecksums;
105
+ }
106
+
107
+ function getChecksumFilePath(): AbsolutePath {
108
+ return path.join(Sonamu.apiRootPath, "sonamu.lock") as AbsolutePath;
109
+ }
110
+
111
+ async function saveChecksums(checksums: PathAndChecksum[]): Promise<void> {
112
+ const checksumFilePath = getChecksumFilePath();
113
+ await writeFile(
114
+ checksumFilePath,
115
+ JSON.stringify(
116
+ checksums.map((r) => ({
117
+ path: path.relative(Sonamu.apiRootPath, r.path), // 체크섬 파일에 저장할 때: 절대 경로 → API 상대 경로
118
+ checksum: r.checksum,
119
+ })),
120
+ null,
121
+ 2,
122
+ ),
123
+ "utf-8",
124
+ );
125
+ console.log("checksum saved", checksumFilePath);
126
+ }
127
+
128
+ async function getChecksumOfFile(filePath: PathLike): Promise<string> {
129
+ return new Promise<string>((resolve, reject) => {
130
+ const hash = crypto.createHash("sha1");
131
+ const input = createReadStream(filePath);
132
+ input.on("error", reject);
133
+ input.on("data", (chunk: BinaryLike) => {
134
+ hash.update(chunk);
135
+ });
136
+ input.on("close", () => {
137
+ resolve(hash.digest("hex"));
138
+ });
139
+ });
140
+ }
@@ -0,0 +1,198 @@
1
+ import chalk from "chalk";
2
+ import { mkdir, writeFile } from "fs/promises";
3
+ import path from "path";
4
+ import { unique } from "radashi";
5
+ import { Sonamu } from "../api/sonamu";
6
+ import { EntityManager } from "../entity/entity-manager";
7
+ import { AlreadyProcessedException } from "../exceptions/so-exceptions";
8
+ import { Naite } from "../naite/naite";
9
+ import type { RenderedTemplate } from "../template/template";
10
+ import { TemplateManager } from "../template/template-manager";
11
+ import type { GenerateOptions, PathAndCode, TemplateKey, TemplateOptions } from "../types/types";
12
+ import { everyAsync, filterAsync } from "../utils/async-utils";
13
+ import { isTest } from "../utils/controller";
14
+ import { formatCode } from "../utils/formatter";
15
+ import { exists } from "../utils/fs-utils";
16
+ import { wrapIf } from "../utils/lodash-able";
17
+ import type { AbsolutePath } from "../utils/path-utils";
18
+
19
+ /**
20
+ * 템플릿을 렌더링하고 파일로 생성합니다.
21
+ * overwrite 옵션이 false인 경우, 이미 존재하는 파일은 건너뜁니다.
22
+ * @param key - 템플릿 키 (예: "entity", "model", "service" 등)
23
+ * @param templateOptions - 템플릿 렌더링에 필요한 옵션
24
+ * @param _generateOptions - 생성 옵션 (overwrite 여부)
25
+ * @returns 생성된 파일 경로 배열
26
+ */
27
+ export async function generateTemplate<T extends TemplateKey>(
28
+ key: T,
29
+ templateOptions: TemplateOptions[T],
30
+ _generateOptions?: GenerateOptions,
31
+ ): Promise<AbsolutePath[]> {
32
+ const generateOptions = {
33
+ overwrite: false,
34
+ ..._generateOptions,
35
+ };
36
+ Naite.t("generateTemplate", { key, templateOptions, generateOptions });
37
+
38
+ // 키 children
39
+ const keys: TemplateKey[] = [key];
40
+
41
+ // 템플릿 렌더
42
+ const pathAndCodes = (
43
+ await Promise.all(
44
+ keys.map(async (key) => {
45
+ return await renderTemplate(key, templateOptions);
46
+ }),
47
+ )
48
+ ).flat();
49
+
50
+ const filteredPathAndCodes: PathAndCode[] = await (async () => {
51
+ if (generateOptions.overwrite === true) {
52
+ return pathAndCodes;
53
+ } else {
54
+ return await filterAsync(pathAndCodes, async (pathAndCode) => {
55
+ const { targets } = Sonamu.config.sync;
56
+ const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}`;
57
+ const dstFilePaths = targets.map((target) => filePath.replace("/:target/", `/${target}/`));
58
+ return await everyAsync(dstFilePaths, async (dstPath) => !(await exists(dstPath)));
59
+ });
60
+ }
61
+ })();
62
+
63
+ if (filteredPathAndCodes.length === 0) {
64
+ throw new AlreadyProcessedException("이미 경로에 모든 파일이 존재합니다.");
65
+ }
66
+
67
+ return (
68
+ await Promise.all(
69
+ filteredPathAndCodes.map((pathAndCode) => writeCodeToPathEachTarget(pathAndCode)),
70
+ )
71
+ ).flat();
72
+ }
73
+
74
+ /**
75
+ * 템플릿을 렌더링하여 PathAndCode 객체를 반환합니다.
76
+ * 파일로 쓰지 않고 메모리상에서만 렌더링합니다.
77
+ * @param key - 템플릿 키
78
+ * @param options - 템플릿 렌더링 옵션
79
+ * @returns 경로와 코드 쌍의 배열
80
+ */
81
+ export async function renderTemplate<T extends keyof TemplateOptions>(
82
+ key: T,
83
+ options: TemplateOptions[T],
84
+ ): Promise<PathAndCode[]> {
85
+ Naite.t("renderTemplate", { key, options });
86
+
87
+ const template = TemplateManager.get(key);
88
+
89
+ const rendered = await template.render(options);
90
+ const resolved = await resolveRenderedTemplate(key, rendered);
91
+
92
+ let preTemplateResolved: PathAndCode[] = [];
93
+ if (rendered.preTemplates) {
94
+ preTemplateResolved = (
95
+ await Promise.all(
96
+ rendered.preTemplates.map(({ key, options }) => {
97
+ return renderTemplate(key, options);
98
+ }),
99
+ )
100
+ ).flat();
101
+ }
102
+
103
+ return [resolved, ...preTemplateResolved];
104
+ }
105
+
106
+ async function resolveRenderedTemplate(
107
+ key: TemplateKey,
108
+ result: RenderedTemplate,
109
+ ): Promise<PathAndCode> {
110
+ Naite.t(`resolveRenderedTemplate${key}`, { key, result });
111
+
112
+ const { target, path: filePath, body, importKeys, customHeaders } = result;
113
+
114
+ // import 할 대상의 대상 path 추출
115
+ const importDefs = importKeys
116
+ .reduce(
117
+ (r, importKey) => {
118
+ const modulePath = EntityManager.getModulePath(importKey);
119
+ let importPath = modulePath;
120
+ if (modulePath.includes("/") || modulePath.includes(".")) {
121
+ importPath = wrapIf(path.relative(path.dirname(filePath), modulePath), (p) => [
122
+ p.startsWith(".") === false,
123
+ `./${p}`,
124
+ ]);
125
+ }
126
+
127
+ // 같은 파일에서 import 하는 경우 keys 로 나열 처리
128
+ const existsOne = r.find((importDef) => importDef.from === importPath);
129
+ if (existsOne) {
130
+ existsOne.keys = unique(existsOne.keys.concat(importKey));
131
+ } else {
132
+ r.push({
133
+ keys: [importKey],
134
+ from: importPath,
135
+ });
136
+ }
137
+ return r;
138
+ },
139
+ [] as {
140
+ keys: string[];
141
+ from: string;
142
+ }[],
143
+ )
144
+ // 셀프 참조 방지
145
+ .filter((importDef) => filePath.endsWith(`${importDef.from.replace("./", "")}.ts`) === false);
146
+
147
+ // 커스텀 헤더 포함하여 헤더 생성
148
+ const header = [
149
+ ...(customHeaders ?? []),
150
+ ...importDefs.map(
151
+ (importDef) => `import { ${importDef.keys.join(", ")} } from '${importDef.from}'`,
152
+ ),
153
+ ].join("\n");
154
+
155
+ const formatted = await (async () => {
156
+ if (key === "generated_http") {
157
+ return [header, body].join("\n\n");
158
+ } else {
159
+ Naite.t("resolveRenderedTemplate:beforeFormat", { key, header, body });
160
+ const formatted = formatCode(
161
+ [header, body].join("\n\n"),
162
+ key === "entity" ? "json" : "typescript",
163
+ `${Sonamu.appRootPath}/${filePath}`,
164
+ );
165
+ Naite.t(`resolveRenderedTemplate:formatted:${key}`, formatted);
166
+ return formatted;
167
+ }
168
+ })();
169
+
170
+ return {
171
+ path: `${target}/${filePath}`,
172
+ code: formatted,
173
+ };
174
+ }
175
+
176
+ async function writeCodeToPathEachTarget(pathAndCode: PathAndCode): Promise<AbsolutePath[]> {
177
+ const { targets } = Sonamu.config.sync;
178
+ const { appRootPath } = Sonamu;
179
+ const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}` as AbsolutePath;
180
+
181
+ const dstFilePaths = unique(
182
+ targets.map((target) => filePath.replace("/:target/", `/${target}/`)) as AbsolutePath[],
183
+ );
184
+ return await Promise.all(
185
+ dstFilePaths.map(async (dstFilePath) => {
186
+ const dir = path.dirname(dstFilePath);
187
+ if (!(await exists(dir))) {
188
+ await mkdir(dir, { recursive: true });
189
+ }
190
+ await writeFile(dstFilePath, pathAndCode.code);
191
+ !isTest() &&
192
+ console.log(
193
+ chalk.bold("Generated: ") + chalk.blue(`${dstFilePath.replace(`${appRootPath}/`, "")}`),
194
+ );
195
+ return dstFilePath;
196
+ }),
197
+ );
198
+ }
@@ -0,0 +1,65 @@
1
+ import chalk from "chalk";
2
+ import { rm } from "fs/promises";
3
+ import { Sonamu } from "../api/sonamu";
4
+ import { EntityManager } from "../entity/entity-manager";
5
+ import { BadRequestException } from "../exceptions/so-exceptions";
6
+ import type { TemplateOptions } from "../types/types";
7
+ import { isTest } from "../utils/controller";
8
+ import { exists } from "../utils/fs-utils";
9
+ import { generateTemplate } from "./code-generator";
10
+
11
+ /**
12
+ * 새로운 엔티티를 생성합니다.
13
+ * entityId는 반드시 CamelCase 형식이어야 합니다.
14
+ */
15
+ export async function createEntity(
16
+ form: Omit<TemplateOptions["entity"], "title"> & { title: string },
17
+ ) {
18
+ if (!/^[A-Z][a-zA-Z0-9]*$/.test(form.entityId)) {
19
+ throw new BadRequestException("entityId는 CamelCase 형식이어야 합니다.");
20
+ }
21
+
22
+ await generateTemplate("entity", form);
23
+
24
+ // reload entities
25
+ await EntityManager.reload();
26
+ }
27
+
28
+ /**
29
+ * 엔티티를 삭제합니다.
30
+ * parentId가 있는 서브 엔티티의 경우 entity.json만 삭제하고,
31
+ * 루트 엔티티의 경우 디렉토리 전체와 타겟 디렉토리를 삭제합니다.
32
+ */
33
+ export async function delEntity(entityId: string): Promise<{ delPaths: string[] }> {
34
+ const entity = EntityManager.get(entityId);
35
+
36
+ const delPaths = (() => {
37
+ if (entity.parentId) {
38
+ return [
39
+ `${Sonamu.apiRootPath}/src/application/${entity.names.parentFs}/${entity.names.fs}.entity.json`,
40
+ ];
41
+ } else {
42
+ return [
43
+ `${Sonamu.apiRootPath}/src/application/${entity.names.fs}`,
44
+ `${Sonamu.apiRootPath}/dist/application/${entity.names.fs}`,
45
+ ...Sonamu.config.sync.targets.flatMap((target) => [
46
+ `${Sonamu.appRootPath}/${target}/src/services/${entity.names.fs}`,
47
+ ]),
48
+ ];
49
+ }
50
+ })(); // iife
51
+
52
+ for await (const delPath of delPaths) {
53
+ if (await exists(delPath)) {
54
+ !isTest() && console.log(chalk.red(`DELETE ${delPath}`));
55
+ await rm(delPath, { recursive: true, force: true });
56
+ } else {
57
+ !isTest() && console.log(chalk.yellow(`NOT_EXISTS ${delPath}`));
58
+ }
59
+ }
60
+
61
+ // reload entities
62
+ await EntityManager.reload();
63
+
64
+ return { delPaths };
65
+ }