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,23 +1,41 @@
1
- // 메타데이터 컬럼 제외
2
- type ExcludeMetadataColumns<T> = T extends {
3
- __fulltext__: readonly (infer _Col)[];
4
- }
5
- ? Omit<T, "__fulltext__">
6
- : T;
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: Puri.types.ts는 다양한 타입을 사용하고 있습니다. */
2
+
3
+ import type { QueryResult } from "pg";
4
+ import type { DatabaseSchemaExtend } from "../types/types";
5
+ import type { Puri } from "./puri";
6
+ import type { PuriWrapper } from "./puri-wrapper";
7
+
8
+ // 메타데이터 컬럼 유틸
9
+ type MetadataColumns = "__fulltext__" | "__virtual__";
10
+
11
+ // virtual 컬럼 타입 추출
12
+ type VirtualKeys<T> = T extends { __virtual__: readonly (infer V)[] } ? V & string : never;
13
+
14
+ // virtual 컬럼 제거
15
+ type StripVirtual<T> = Omit<T, VirtualKeys<T>>;
16
+
17
+ // 메타데이터 필드 제외한 실제 엔티티 컬럼
18
+ export type ColumnKeys<T> = Exclude<keyof StripVirtual<T>, MetadataColumns> & string;
19
+
20
+ // virtual 컬럼 제거 후 __fulltext__ 메타데이터 유지
21
+ export type PuriTable<T> = Omit<StripVirtual<T>, "__virtual__">;
22
+
23
+ // 메타데이터 컬럼 제외 타입 정의
24
+ export type OmitMetadataColumns<T> = Omit<T, MetadataColumns>;
7
25
 
8
26
  // TTables의 모든 테이블에서 사용 가능한 컬럼 경로
9
27
  export type AvailableColumns<TTables extends Record<string, any>> =
10
28
  | {
11
- [TAlias in keyof TTables]: `${TAlias & string}.${ExcludeMetadataColumns<keyof TTables[TAlias]> & string}`;
29
+ [TAlias in keyof TTables]: `${TAlias & string}.${ColumnKeys<TTables[TAlias]>}`;
12
30
  }[keyof TTables]
13
31
  | (IsSingleKey<TTables> extends true
14
- ? ExcludeMetadataColumns<keyof TTables[keyof TTables]> // 단일 테이블이면 컬럼명만도 허용
32
+ ? ColumnKeys<TTables[keyof TTables]> // 단일 테이블이면 컬럼명만도 허용
15
33
  : never);
34
+
16
35
  // Group By, Order By, Having 등에서 선택 가능한 컬럼
17
- export type ResultAvailableColumns<
18
- TTables extends Record<string, any>,
19
- TResult = any,
20
- > = AvailableColumns<TTables> | `${keyof TResult & string}`;
36
+ export type ResultAvailableColumns<TTables extends Record<string, any>, TResult = any> =
37
+ | AvailableColumns<TTables>
38
+ | `${keyof TResult & string}`;
21
39
 
22
40
  // Select 값 타입 확장
23
41
  export type SelectValue<TTables extends Record<string, any>> =
@@ -84,12 +102,11 @@ export type FulltextColumns<TTables extends Record<string, any>> = {
84
102
  export type ComparisonOperator = "=" | ">" | ">=" | "<" | "<=" | "<>" | "!=";
85
103
 
86
104
  // SQL Expression 타입 정의
87
- export type SqlExpression<T extends "string" | "number" | "boolean" | "date"> =
88
- {
89
- _type: "sql_expression"; // 또는 "computed_value"
90
- _return: T;
91
- _sql: string;
92
- };
105
+ export type SqlExpression<T extends "string" | "number" | "boolean" | "date"> = {
106
+ _type: "sql_expression"; // 또는 "computed_value"
107
+ _return: T;
108
+ _sql: string;
109
+ };
93
110
 
94
111
  // 결과 타입 가독성을 위한 타입 확장
95
112
  export type Expand<T> = T extends any[]
@@ -98,29 +115,58 @@ export type Expand<T> = T extends any[]
98
115
  ? { [K in keyof T]: T[K] }
99
116
  : T;
100
117
 
101
- type IsSingleKey<TTables extends Record<string, any>> =
102
- keyof TTables extends infer K
103
- ? K extends keyof TTables
104
- ? keyof TTables extends K // 역방향 체크로 단일 키 확인
105
- ? true
106
- : false
118
+ type IsSingleKey<TTables extends Record<string, any>> = keyof TTables extends infer K
119
+ ? K extends keyof TTables
120
+ ? keyof TTables extends K // 역방향 체크로 단일 키 확인
121
+ ? true
107
122
  : false
108
- : false;
123
+ : false
124
+ : false;
109
125
 
110
126
  export type SingleTableValue<TTables extends Record<string, any>> =
111
127
  IsSingleKey<TTables> extends true ? TTables[keyof TTables] : never;
112
128
 
113
129
  // Nullable을 Optional로 변환
114
130
  type NullableToOptional<T> = {
115
- [K in keyof T as T[K] extends null | undefined ? K : never]?: Exclude<
116
- T[K],
117
- null | undefined
118
- >;
131
+ [K in keyof T as T[K] extends null | undefined ? K : never]?: Exclude<T[K], null | undefined>;
119
132
  } & Partial<{
120
133
  [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
121
134
  }>;
122
135
 
123
136
  // Insert 타입: id, created_at 제외
124
137
  export type InsertData<T> = NullableToOptional<
125
- Omit<T, "id" | "created_at" | "__fulltext__">
138
+ Omit<PuriTable<T>, "id" | "created_at" | MetadataColumns>
126
139
  >;
140
+
141
+ // Insert Result 타입
142
+ export type InsertResult = Pick<QueryResult<any>, "command" | "rowCount" | "rows" | "oid">;
143
+
144
+ // SubsetQuery를 위한 타입 유틸리티
145
+ type ExtractTTables<T extends Puri<any, any, any>> = T extends Puri<any, infer TTables, any>
146
+ ? TTables
147
+ : never;
148
+ export type UnionExtractedTTables<
149
+ SubsetKey extends string,
150
+ SubsetQueries extends Record<
151
+ SubsetKey,
152
+ (qbWrapper: PuriWrapper<DatabaseSchemaExtend>) => Puri<any, any, any>
153
+ >,
154
+ > = {
155
+ [K in SubsetKey]: ExtractTTables<ReturnType<SubsetQueries[K]>>;
156
+ }[SubsetKey];
157
+
158
+ // ON CONFLICT 대상 타입
159
+ // - 단일 컬럼: "email"
160
+ // - 복수 컬럼: ["user_id", "product_id"]
161
+ export type OnConflictTarget = string | string[];
162
+
163
+ // ON CONFLICT 액션 타입
164
+ // - "nothing": DO NOTHING
165
+ // - { update: [...] }: DO UPDATE
166
+ export type OnConflictAction<TTables extends Record<string, unknown>> =
167
+ | "nothing"
168
+ | {
169
+ update:
170
+ | AvailableColumns<TTables>[] // 배열 형태 - ["name", "email"]
171
+ | WhereCondition<TTables>; // 객체 형태 - { name: "John", count: Puri.rawNumber(...) }
172
+ };
@@ -1,5 +1,5 @@
1
- import type { PuriTransactionWrapper } from "./puri-wrapper";
2
1
  import type { DBPreset } from "./db";
2
+ import type { PuriTransactionWrapper } from "./puri-wrapper";
3
3
 
4
4
  export class TransactionContext {
5
5
  private transactions: Map<DBPreset, PuriTransactionWrapper> = new Map();
@@ -1,13 +1,14 @@
1
1
  import { randomUUID } from "crypto";
2
- import _ from "lodash";
3
- import { Knex } from "knex";
2
+ import type { Knex } from "knex";
3
+ import { unique } from "radashi";
4
4
  import { EntityManager } from "../entity/entity-manager";
5
- import { nonNullable } from "../utils/utils";
6
- import { RowWithId, batchUpdate } from "./_batch_update";
5
+ import { Naite } from "../naite/naite";
6
+ import { assertDefined, chunk, nonNullable } from "../utils/utils";
7
+ import { batchUpdate, type RowWithId } from "./_batch_update";
7
8
 
8
9
  type TableData = {
9
10
  references: Set<string>;
10
- rows: any[];
11
+ rows: Record<string, unknown>[];
11
12
  uniqueIndexes: { name?: string; columns: string[] }[];
12
13
  uniquesMap: Map<string, string>;
13
14
  };
@@ -16,12 +17,12 @@ export type UBRef = {
16
17
  of: string;
17
18
  use?: string;
18
19
  };
19
- export function isRefField(field: any): field is UBRef {
20
+ export function isRefField(field: unknown): field is UBRef {
20
21
  return (
21
22
  field !== undefined &&
22
23
  field !== null &&
23
- field.of !== undefined &&
24
- field.uuid !== undefined
24
+ (field as UBRef)?.of !== undefined &&
25
+ (field as UBRef)?.uuid !== undefined
25
26
  );
26
27
  }
27
28
 
@@ -33,24 +34,26 @@ export class UpsertBuilder {
33
34
 
34
35
  getTable(tableName: string): TableData {
35
36
  const table = this.tables.get(tableName);
36
- if (table === undefined) {
37
- const tableSpec = (() => {
38
- try {
39
- return EntityManager.getTableSpec(tableName);
40
- } catch {
41
- return null;
42
- }
43
- })();
44
-
45
- this.tables.set(tableName, {
46
- references: new Set(),
47
- rows: [],
48
- uniqueIndexes: tableSpec?.uniqueIndexes ?? [],
49
- uniquesMap: new Map<string, string>(),
50
- });
37
+ if (table) {
38
+ return table;
51
39
  }
52
40
 
53
- return this.tables.get(tableName)!;
41
+ const tableSpec = (() => {
42
+ try {
43
+ return EntityManager.getTableSpec(tableName);
44
+ } catch {
45
+ return null;
46
+ }
47
+ })();
48
+
49
+ const tableData = {
50
+ references: new Set<string>(),
51
+ rows: [],
52
+ uniqueIndexes: tableSpec?.uniqueIndexes ?? [],
53
+ uniquesMap: new Map<string, string>(),
54
+ };
55
+ this.tables.set(tableName, tableData);
56
+ return tableData;
54
57
  }
55
58
 
56
59
  hasTable(tableName: string): boolean {
@@ -60,16 +63,8 @@ export class UpsertBuilder {
60
63
  register<T extends string>(
61
64
  tableName: string,
62
65
  row: {
63
- [key in T]?:
64
- | UBRef
65
- | string
66
- | number
67
- | boolean
68
- | bigint
69
- | null
70
- | object
71
- | unknown;
72
- }
66
+ [key in T]?: UBRef | string | number | boolean | bigint | null | object | unknown;
67
+ },
73
68
  ): UBRef {
74
69
  const table = this.getTable(tableName);
75
70
 
@@ -94,18 +89,21 @@ export class UpsertBuilder {
94
89
  .filter(nonNullable);
95
90
 
96
91
  // uuid 생성 로직
97
- const uuid: string = (() => {
92
+ const { uuid, isReused } = (() => {
98
93
  // 키를 순회하여 이미 존재하는 키가 있는지 확인
99
94
  if (uniqueKeys.length > 0) {
100
95
  for (const uniqueKey of uniqueKeys) {
101
96
  if (table.uniquesMap.has(uniqueKey)) {
102
- return table.uniquesMap.get(uniqueKey)!; // 이미 has 체크를 했으므로 undefined 불가능
97
+ return {
98
+ uuid: assertDefined(table.uniquesMap.get(uniqueKey), "Unique key not found"),
99
+ isReused: true,
100
+ };
103
101
  }
104
102
  }
105
103
  }
106
104
 
107
105
  // 찾을 수 없는 경우 생성
108
- return randomUUID();
106
+ return { uuid: randomUUID(), isReused: false };
109
107
  })();
110
108
 
111
109
  // 모든 유니크키에 대해 유니크맵에 uuid 저장
@@ -117,45 +115,45 @@ export class UpsertBuilder {
117
115
 
118
116
  // 이 테이블에 사용된 RefField를 순회하여, 현재 테이블 정보에 어떤 필드를 참조하는지 추가
119
117
  // 이 정보를 나중에 치환할 때 사용
120
- row = Object.keys(row).reduce((r, rowKey) => {
121
- const rowValue = row[rowKey as keyof typeof row];
122
-
123
- if (isRefField(rowValue)) {
124
- rowValue.use ??= "id";
125
- table.references.add(rowValue.of + "." + rowValue.use);
126
- r[rowKey] = rowValue;
127
- } else if (typeof rowValue === "object" && !(rowValue instanceof Date)) {
128
- // object인 경우 JSON으로 변환
129
- r[rowKey] = rowValue === null ? null : JSON.stringify(rowValue);
130
- } else {
131
- r[rowKey] = rowValue;
132
- }
133
- return r;
134
- }, {} as any);
118
+ row = Object.fromEntries(
119
+ Object.entries(row).map(([rowKey, rowValue]) => {
120
+ if (isRefField(rowValue)) {
121
+ rowValue.use ??= "id";
122
+ table.references.add(`${rowValue.of}.${rowValue.use}`);
123
+ return [rowKey, rowValue];
124
+ } else if (typeof rowValue === "object" && !(rowValue instanceof Date)) {
125
+ // object 경우 JSON으로 변환
126
+ return [rowKey, rowValue === null ? null : JSON.stringify(rowValue)];
127
+ } else {
128
+ return [rowKey, rowValue];
129
+ }
130
+ }),
131
+ ) as { [key in T]?: unknown };
135
132
 
136
133
  table.rows.push({
137
134
  uuid,
138
135
  ...row,
139
136
  });
140
137
 
141
- return {
138
+ const result: UBRef = {
142
139
  of: tableName,
143
140
  uuid: (row as { uuid?: string }).uuid ?? uuid,
144
141
  };
142
+
143
+ Naite.t("puri:ub-register", {
144
+ tableName,
145
+ uuid: result.uuid,
146
+ isUuidReused: isReused,
147
+ row,
148
+ });
149
+
150
+ return result;
145
151
  }
146
152
 
147
- async upsert(
148
- wdb: Knex,
149
- tableName: string,
150
- chunkSize?: number
151
- ): Promise<number[]> {
153
+ async upsert(wdb: Knex, tableName: string, chunkSize?: number): Promise<number[]> {
152
154
  return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
153
155
  }
154
- async insertOnly(
155
- wdb: Knex,
156
- tableName: string,
157
- chunkSize?: number
158
- ): Promise<number[]> {
156
+ async insertOnly(wdb: Knex, tableName: string, chunkSize?: number): Promise<number[]> {
159
157
  return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
160
158
  }
161
159
 
@@ -163,7 +161,7 @@ export class UpsertBuilder {
163
161
  wdb: Knex,
164
162
  tableName: string,
165
163
  mode: "upsert" | "insert",
166
- chunkSize?: number
164
+ chunkSize?: number,
167
165
  ): Promise<number[]> {
168
166
  if (this.hasTable(tableName) === false) {
169
167
  return [];
@@ -178,9 +176,7 @@ export class UpsertBuilder {
178
176
 
179
177
  if (
180
178
  table.rows.some((row) =>
181
- Object.entries(row).some(
182
- ([, value]) => isRefField(value) && value.of !== tableName
183
- )
179
+ Object.entries(row).some(([, value]) => isRefField(value) && value.of !== tableName),
184
180
  )
185
181
  ) {
186
182
  throw new Error(`${tableName} 해결되지 않은 참조가 있습니다.`);
@@ -190,7 +186,7 @@ export class UpsertBuilder {
190
186
  const { references, refTables } = Array.from(this.tables).reduce(
191
187
  (r, [, table]) => {
192
188
  const reference = Array.from(table.references.values()).find((ref) =>
193
- ref.includes(tableName + ".")
189
+ ref.includes(`${tableName}.`),
194
190
  );
195
191
  if (reference) {
196
192
  r.references.push(reference);
@@ -202,77 +198,132 @@ export class UpsertBuilder {
202
198
  {
203
199
  references: [] as string[],
204
200
  refTables: [] as TableData[],
205
- }
206
- );
207
- const extractFields = _.uniq(references).map(
208
- (reference) => reference.split(".")[1]
201
+ },
209
202
  );
203
+ const extractFields = unique(references)
204
+ .map((reference) => reference.split(".")[1])
205
+ .filter((field): field is string => field !== undefined);
210
206
 
211
- // 내부 참조 있는 경우 필터하여 분리
212
- const groups = _.groupBy(table.rows, (row) =>
213
- Object.entries(row).some(([, value]) => isRefField(value))
214
- ? "selfRef"
215
- : "normal"
216
- );
217
- const normalRows = groups.normal ?? [];
218
- const selfRefRows = groups.selfRef ?? [];
219
-
220
- const chunks = chunkSize ? _.chunk(normalRows, chunkSize) : [normalRows];
221
- const uuidMap = new Map<string, any>();
222
-
223
- for (const chunk of chunks) {
224
- const q = wdb.insert(chunk).into(tableName);
225
- if (mode === "insert") {
226
- await q;
227
- } else if (mode === "upsert") {
228
- await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));
229
- }
207
+ // 의존성 순서에 따라 레벨별 그룹화 (자기 참조가 없으면 Level 0 하나)
208
+ const { levels, hasCircular } = this.buildInsertLevels(table.rows, tableName);
209
+
210
+ if (hasCircular) {
211
+ throw new Error(`${tableName}에 순환 자기 참조가 있습니다.`);
212
+ }
213
+
214
+ // upsert 모드일 유니크 인덱스가 없으면 에러
215
+ if (mode === "upsert" && table.uniqueIndexes.length === 0) {
216
+ throw new Error(`${tableName}에 unique index가 정의되지 않아 upsert를 할 수 없습니다.`);
217
+ }
218
+
219
+ const uuidMap = new Map<string, unknown>();
220
+ const allIds: number[] = [];
230
221
 
231
- // upsert된 row들을 다시 조회하여 uuidMap에 저장
232
- const uuids = chunk.map((row) => row.uuid);
233
- const upsertedRows = await wdb(tableName)
234
- .select(_.uniq(["uuid", "id", ...extractFields]))
235
- .whereIn("uuid", uuids);
236
- upsertedRows.forEach((row: any) => {
237
- uuidMap.set(row.uuid, row);
222
+ // 레벨별로 순차 처리
223
+ for (const levelRows of levels) {
224
+ // 이전 레벨에서 얻은 ID로 자기 참조 해결
225
+ const resolvedRows = levelRows.map((row) => {
226
+ const resolved = { ...row };
227
+ for (const [key, value] of Object.entries(row)) {
228
+ if (isRefField(value) && value.of === tableName) {
229
+ const parent = uuidMap.get(value.uuid);
230
+
231
+ if (!parent) throw new Error(`존재하지 않는 uuid ${value.uuid} -- in ${tableName}`);
232
+
233
+ resolved[key] = (parent as Record<string, unknown>)[value.use ?? "id"];
234
+
235
+ Naite.t("puri:ub-ref-resolved", {
236
+ tableName,
237
+ field: key,
238
+ from: { of: value.of, uuid: value.uuid, use: value.use ?? "id" },
239
+ to: resolved[key],
240
+ });
241
+ }
242
+ }
243
+ return resolved;
238
244
  });
245
+
246
+ // 현재 레벨 upsert
247
+ const levelChunks = chunkSize ? chunk(resolvedRows, chunkSize) : [resolvedRows];
248
+ const selectFields = unique(["uuid", "id", ...extractFields]);
249
+
250
+ for (const dataChunk of levelChunks) {
251
+ if (dataChunk.length === 0) continue;
252
+
253
+ let resultRows: { uuid: string; id: number; [key: string]: unknown }[];
254
+
255
+ if (mode === "insert") {
256
+ // INSERT 모드
257
+ await wdb.insert(dataChunk).into(tableName);
258
+
259
+ const uuids = dataChunk.map((r) => r.uuid);
260
+ resultRows = await wdb(tableName)
261
+ .select(selectFields)
262
+ .whereIn("uuid", uuids as readonly string[]);
263
+ } else {
264
+ // UPSERT 모드 (uniqueIndexes 이미 체크됨)
265
+ const conflictColumns = table.uniqueIndexes[0].columns;
266
+ const updateColumns = Object.keys(dataChunk[0]).filter(
267
+ (col) => col !== "uuid" && !conflictColumns.includes(col),
268
+ );
269
+
270
+ // RETURNING으로 결과 받기
271
+ const query = wdb.insert(dataChunk).into(tableName).onConflict(conflictColumns);
272
+
273
+ // updateColumns가 비어있으면 ignore(), 아니면 merge()
274
+ if (updateColumns.length === 0) {
275
+ resultRows = await query.ignore().returning(selectFields);
276
+ } else {
277
+ resultRows = await query.merge(updateColumns).returning(selectFields);
278
+ }
279
+ }
280
+
281
+ // 양쪽 모드 공통 처리
282
+ for (const row of resultRows) {
283
+ uuidMap.set(row.uuid, row);
284
+ allIds.push(row.id);
285
+ }
286
+ }
239
287
  }
240
288
 
241
289
  // 해당 테이블 참조를 실제 밸류로 변경
242
- refTables.map((table) => {
290
+ for (const table of refTables) {
243
291
  table.rows = table.rows.map((row) => {
244
- Object.keys(row).map((key) => {
292
+ for (const key of Object.keys(row)) {
245
293
  const prop = row[key];
246
294
  if (isRefField(prop) && prop.of === tableName) {
247
295
  const parent = uuidMap.get(prop.uuid);
248
- if (parent === undefined) {
296
+ if (!parent) {
249
297
  console.error(prop);
250
- throw new Error(
251
- `존재하지 않는 uuid ${prop.uuid} -- in ${tableName}`
252
- );
298
+ throw new Error(`존재하지 않는 uuid ${prop.uuid} -- in ${tableName}`);
253
299
  }
254
- row[key] = parent[prop.use ?? "id"];
300
+ const resolvedValue = (parent as Record<string, unknown>)[prop.use ?? "id"];
301
+ row[key] = resolvedValue;
302
+
303
+ Naite.t("puri:ub-ref-resolved", {
304
+ tableName,
305
+ field: key,
306
+ from: { of: prop.of, uuid: prop.uuid, use: prop.use ?? "id" },
307
+ to: resolvedValue,
308
+ });
255
309
  }
256
- });
310
+ }
257
311
  return row;
258
312
  });
259
- });
260
-
261
- const allIds = Array.from(uuidMap.values()).map((row) => row.id);
262
-
263
- // 자기 참조가 있는 경우 재귀적으로 upsert
264
- if (selfRefRows.length > 0) {
265
- // 처리된 데이터를 제외하고 다시 upsert
266
- table.rows = selfRefRows;
267
- const selfRefIds = await this.upsert(wdb, tableName, chunkSize);
268
- allIds.push(...selfRefIds);
269
- } else {
270
- // 자기 참조가 없으면 해당 테이블의 데이터 초기화
271
- table.rows = [];
272
- table.references.clear();
273
- table.uniquesMap.clear();
274
313
  }
275
314
 
315
+ // 해당 테이블의 데이터 초기화
316
+ table.rows = [];
317
+ table.references.clear();
318
+ table.uniquesMap.clear();
319
+
320
+ Naite.t("puri:ub-upserted", {
321
+ tableName,
322
+ mode,
323
+ rowCount: allIds.length,
324
+ returnedIds: allIds,
325
+ });
326
+
276
327
  return allIds;
277
328
  }
278
329
 
@@ -282,34 +333,113 @@ export class UpsertBuilder {
282
333
  options?: {
283
334
  chunkSize?: number;
284
335
  where?: string | string[];
285
- }
336
+ },
286
337
  ): Promise<void> {
287
- options = _.defaults(options, {
288
- chunkSize: 500,
289
- where: "id",
290
- });
338
+ options = {
339
+ ...options,
340
+ chunkSize: options?.chunkSize ?? 500,
341
+ where: options?.where ?? "id",
342
+ };
291
343
 
292
344
  if (this.hasTable(tableName) === false) {
293
345
  return;
294
346
  }
295
- const table = this.tables.get(tableName)!;
296
- if (table.rows.length === 0) {
347
+ const table = this.tables.get(tableName);
348
+ if (!table) {
349
+ throw new Error(`등록되지 않은 테이블 ${tableName}에 updateBatch 요청`);
350
+ } else if (table.rows.length === 0) {
297
351
  return;
298
352
  }
299
353
 
300
- const whereColumns = Array.isArray(options.where)
301
- ? options.where
302
- : [options.where ?? "id"];
354
+ const whereColumns = Array.isArray(options.where) ? options.where : [options.where ?? "id"];
303
355
  const rows = table.rows.map((_row) => {
304
- const { uuid, ...row } = _row;
356
+ const { uuid: _, ...row } = _row; // uuid 제외
305
357
  return row as RowWithId<string>;
306
358
  });
307
359
 
308
360
  await batchUpdate(wdb, tableName, whereColumns, rows, options.chunkSize);
309
361
 
362
+ Naite.t("puri:ub-batch-updated", {
363
+ tableName,
364
+ rowCount: rows.length,
365
+ whereColumns,
366
+ });
367
+
310
368
  // updateBatch 완료 후 처리된 데이터 제거
311
369
  table.rows = [];
312
370
  table.references.clear();
313
371
  table.uniquesMap.clear();
314
372
  }
373
+
374
+ // ============================================================================
375
+ // Private Helpers
376
+ // ============================================================================
377
+
378
+ /**
379
+ * rows를 의존성 순서에 따라 레벨별로 그룹화
380
+ * - 자기 참조 없는 경우 : 모든 rows가 Level 0
381
+ * - 자기 참조 있는 경우 : 자기 참조 관계를 위상 정렬하여 레벨별로 그룹화
382
+ */
383
+ private buildInsertLevels(
384
+ rows: Record<string, unknown>[],
385
+ tableName: string,
386
+ ): { levels: Record<string, unknown>[][]; hasCircular: boolean } {
387
+ // 1. 자기 참조가 없으면 한 레벨로 처리
388
+ const hasSelfRef = rows
389
+ .flatMap((row) => Object.values(row))
390
+ .some((value) => isRefField(value) && value.of === tableName);
391
+ if (!hasSelfRef) return { levels: [rows], hasCircular: false };
392
+
393
+ // 2. uuid → row 매핑 (중복 uuid 방지)
394
+ const rowByUuid = new Map<string, Record<string, unknown>>();
395
+ for (const row of rows) {
396
+ const uuid = row.uuid as string | undefined;
397
+ if (!uuid) throw new Error(`buildInsertLevels: uuid가 없는 row -- in ${tableName}`);
398
+ rowByUuid.set(uuid, row);
399
+ }
400
+
401
+ let pending = Array.from(rowByUuid.values());
402
+ const levels: Record<string, unknown>[][] = [];
403
+ const inserted = new Set<string>();
404
+
405
+ // 3. 레벨별 분류
406
+ while (pending.length > 0) {
407
+ const currentLevel: Record<string, unknown>[] = [];
408
+ const nextPending: Record<string, unknown>[] = [];
409
+
410
+ for (const row of pending) {
411
+ // 이 row가 참조하는 자기 참조들
412
+ const selfRefs = Object.values(row).filter(
413
+ (value) => isRefField(value) && value.of === tableName,
414
+ ) as UBRef[];
415
+
416
+ // 참조하는 모든 uuid가 이미 inserted에 있어야 이번 레벨에 포함
417
+ const canInsert = selfRefs.every((ref) => {
418
+ if (!rowByUuid.has(ref.uuid)) {
419
+ throw new Error(`존재하지 않는 uuid ${ref.uuid} -- in ${tableName}`);
420
+ }
421
+ return inserted.has(ref.uuid);
422
+ });
423
+
424
+ if (canInsert) {
425
+ currentLevel.push(row);
426
+ } else {
427
+ nextPending.push(row);
428
+ }
429
+ }
430
+
431
+ // 순환 참조 감지
432
+ if (currentLevel.length === 0) return { levels: [], hasCircular: true };
433
+
434
+ // 레벨 확정 + inserted 갱신
435
+ levels.push(currentLevel);
436
+ for (const row of currentLevel) {
437
+ inserted.add(row.uuid as string);
438
+ }
439
+
440
+ pending = nextPending;
441
+ }
442
+
443
+ return { levels, hasCircular: false };
444
+ }
315
445
  }