sonamu 0.10.11 → 0.10.13

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 (519) hide show
  1. package/dist/ai/agents/agent.d.ts +1 -1
  2. package/dist/ai/agents/agent.d.ts.map +1 -1
  3. package/dist/ai/agents/agent.js +5 -6
  4. package/dist/ai/agents/types.d.ts +1 -1
  5. package/dist/ai/agents/types.d.ts.map +1 -1
  6. package/dist/ai/providers/rtzr/api.js +18 -18
  7. package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
  8. package/dist/ai/providers/rtzr/error.js +17 -15
  9. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  10. package/dist/ai/providers/rtzr/model.js +3 -2
  11. package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
  12. package/dist/ai/providers/rtzr/options.js +16 -16
  13. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
  14. package/dist/ai/providers/rtzr/provider.js +9 -9
  15. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  16. package/dist/ai/providers/rtzr/utils.js +1 -1
  17. package/dist/api/caster.d.ts.map +1 -1
  18. package/dist/api/caster.js +17 -17
  19. package/dist/api/code-converters.d.ts +4 -2
  20. package/dist/api/code-converters.d.ts.map +1 -1
  21. package/dist/api/code-converters.js +100 -19
  22. package/dist/api/config.d.ts +16 -1
  23. package/dist/api/config.d.ts.map +1 -1
  24. package/dist/api/config.js +28 -21
  25. package/dist/api/context.d.ts +2 -2
  26. package/dist/api/context.d.ts.map +1 -1
  27. package/dist/api/context.js +1 -1
  28. package/dist/api/decorators.d.ts.map +1 -1
  29. package/dist/api/decorators.js +18 -11
  30. package/dist/api/http-validator.d.ts +36 -0
  31. package/dist/api/http-validator.d.ts.map +1 -0
  32. package/dist/api/http-validator.js +72 -0
  33. package/dist/api/index.js +6 -4
  34. package/dist/api/sonamu.d.ts +59 -20
  35. package/dist/api/sonamu.d.ts.map +1 -1
  36. package/dist/api/sonamu.js +250 -106
  37. package/dist/api/websocket-helpers.d.ts +7 -5
  38. package/dist/api/websocket-helpers.d.ts.map +1 -1
  39. package/dist/api/websocket-helpers.js +6 -6
  40. package/dist/auth/audit-log/builders.d.ts.map +1 -1
  41. package/dist/auth/audit-log/builders.js +1 -1
  42. package/dist/auth/audit-log/client-ip.d.ts +1 -1
  43. package/dist/auth/audit-log/client-ip.d.ts.map +1 -1
  44. package/dist/auth/audit-log/client-ip.js +4 -2
  45. package/dist/auth/audit-log/events.d.ts +5 -1
  46. package/dist/auth/audit-log/events.d.ts.map +1 -1
  47. package/dist/auth/audit-log/events.js +1 -1
  48. package/dist/auth/audit-log/plugin.d.ts +0 -8
  49. package/dist/auth/audit-log/plugin.d.ts.map +1 -1
  50. package/dist/auth/audit-log/plugin.js +48 -52
  51. package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
  52. package/dist/auth/audit-log-ingestor.js +16 -12
  53. package/dist/auth/auth-generator.d.ts.map +1 -1
  54. package/dist/auth/auth-generator.js +1 -1
  55. package/dist/auth/better-auth-request.d.ts.map +1 -1
  56. package/dist/auth/better-auth-request.js +6 -5
  57. package/dist/auth/knex-adapter.js +1 -1
  58. package/dist/auth/plugins/entity-definitions/index.d.ts +13 -1
  59. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  60. package/dist/auth/plugins/entity-definitions/index.js +1 -1
  61. package/dist/auth/plugins/wrappers/admin.d.ts +1 -1
  62. package/dist/bin/build-config.d.ts +25 -0
  63. package/dist/bin/build-config.d.ts.map +1 -1
  64. package/dist/bin/build-config.js +105 -2
  65. package/dist/bin/cli.js +133 -29
  66. package/dist/bin/compiler-policy.d.ts +3 -0
  67. package/dist/bin/compiler-policy.d.ts.map +1 -0
  68. package/dist/bin/compiler-policy.js +24 -0
  69. package/dist/bin/fixture.d.ts.map +1 -1
  70. package/dist/bin/fixture.js +5 -3
  71. package/dist/bin/test-command.d.ts +22 -1
  72. package/dist/bin/test-command.d.ts.map +1 -1
  73. package/dist/bin/test-command.js +18 -10
  74. package/dist/bin/ts-loader-registration.d.ts +7 -1
  75. package/dist/bin/ts-loader-registration.d.ts.map +1 -1
  76. package/dist/bin/ts-loader-registration.js +13 -4
  77. package/dist/cache/decorator.d.ts.map +1 -1
  78. package/dist/cache/decorator.js +11 -4
  79. package/dist/compress/compress.d.ts.map +1 -1
  80. package/dist/compress/compress.js +1 -1
  81. package/dist/cone/cone-generator.d.ts +14 -1
  82. package/dist/cone/cone-generator.d.ts.map +1 -1
  83. package/dist/cone/cone-generator.js +13 -9
  84. package/dist/database/_batch_update.js +1 -1
  85. package/dist/database/base-model.d.ts +22 -10
  86. package/dist/database/base-model.d.ts.map +1 -1
  87. package/dist/database/base-model.js +26 -16
  88. package/dist/database/base-model.types.d.ts +15 -3
  89. package/dist/database/base-model.types.d.ts.map +1 -1
  90. package/dist/database/db.d.ts +12 -2
  91. package/dist/database/db.d.ts.map +1 -1
  92. package/dist/database/db.js +10 -7
  93. package/dist/database/knex.d.ts.map +1 -1
  94. package/dist/database/knex.js +17 -14
  95. package/dist/database/puri.d.ts +11 -9
  96. package/dist/database/puri.d.ts.map +1 -1
  97. package/dist/database/puri.js +93 -79
  98. package/dist/database/puri.types.d.ts +21 -21
  99. package/dist/database/puri.types.d.ts.map +1 -1
  100. package/dist/database/puri.types.js +1 -1
  101. package/dist/database/upsert-builder.d.ts +12 -10
  102. package/dist/database/upsert-builder.d.ts.map +1 -1
  103. package/dist/database/upsert-builder.js +20 -12
  104. package/dist/dict/sd.d.ts.map +1 -1
  105. package/dist/dict/sd.js +1 -1
  106. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  107. package/dist/dict/sonamu-dictionary.js +1 -1
  108. package/dist/dict/utils.d.ts.map +1 -1
  109. package/dist/dict/utils.js +7 -2
  110. package/dist/entity/entity-manager.d.ts +13 -4
  111. package/dist/entity/entity-manager.d.ts.map +1 -1
  112. package/dist/entity/entity-manager.js +19 -11
  113. package/dist/entity/entity-template-cone.d.ts +73 -5
  114. package/dist/entity/entity-template-cone.d.ts.map +1 -1
  115. package/dist/entity/entity-template-cone.js +4 -2
  116. package/dist/entity/entity.d.ts +6 -2
  117. package/dist/entity/entity.d.ts.map +1 -1
  118. package/dist/entity/entity.js +30 -28
  119. package/dist/env.d.ts +3 -1
  120. package/dist/env.d.ts.map +1 -1
  121. package/dist/env.js +1 -1
  122. package/dist/exceptions/error-handler.d.ts.map +1 -1
  123. package/dist/exceptions/error-handler.js +7 -2
  124. package/dist/exceptions/so-exceptions.d.ts +24 -20
  125. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  126. package/dist/exceptions/so-exceptions.js +4 -2
  127. package/dist/filter/utils.d.ts +7 -2
  128. package/dist/filter/utils.d.ts.map +1 -1
  129. package/dist/filter/utils.js +8 -6
  130. package/dist/index.d.ts +3 -0
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +13 -7
  133. package/dist/logger/configure.d.ts.map +1 -1
  134. package/dist/logger/configure.js +10 -10
  135. package/dist/migration/code-generation.d.ts.map +1 -1
  136. package/dist/migration/code-generation.js +50 -46
  137. package/dist/migration/index-where-predicate.d.ts.map +1 -1
  138. package/dist/migration/index-where-predicate.js +22 -8
  139. package/dist/migration/migration-set.d.ts.map +1 -1
  140. package/dist/migration/migration-set.js +1 -1
  141. package/dist/migration/migrator.d.ts +61 -36
  142. package/dist/migration/migrator.d.ts.map +1 -1
  143. package/dist/migration/migrator.js +276 -167
  144. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  145. package/dist/migration/postgresql-schema-reader.js +51 -36
  146. package/dist/migration/slack-confirm.d.ts.map +1 -1
  147. package/dist/migration/slack-confirm.js +1 -1
  148. package/dist/migration/types.d.ts +54 -0
  149. package/dist/migration/types.d.ts.map +1 -1
  150. package/dist/naite/naite-reporter.js +2 -2
  151. package/dist/naite/naite.d.ts +74 -15
  152. package/dist/naite/naite.d.ts.map +1 -1
  153. package/dist/naite/naite.js +5 -19
  154. package/dist/runtime-type.d.ts +6 -0
  155. package/dist/runtime-type.d.ts.map +1 -0
  156. package/dist/runtime-type.js +26 -0
  157. package/dist/ssr/hmr-opt-out.d.ts +26 -0
  158. package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
  159. package/dist/ssr/hmr-opt-out.js +59 -0
  160. package/dist/ssr/renderer.d.ts.map +1 -1
  161. package/dist/ssr/renderer.js +4 -1
  162. package/dist/storage/base-file.js +14 -14
  163. package/dist/storage/buffered-file.d.ts +2 -2
  164. package/dist/storage/buffered-file.d.ts.map +1 -1
  165. package/dist/storage/buffered-file.js +12 -12
  166. package/dist/storage/s3-driver.d.ts.map +1 -1
  167. package/dist/storage/s3-driver.js +1 -1
  168. package/dist/storage/uploaded-file.d.ts +2 -2
  169. package/dist/storage/uploaded-file.d.ts.map +1 -1
  170. package/dist/storage/uploaded-file.js +8 -8
  171. package/dist/stream/sse.js +11 -11
  172. package/dist/stream/ws-core.d.ts +1 -1
  173. package/dist/stream/ws-core.d.ts.map +1 -1
  174. package/dist/stream/ws-delivery.d.ts +1 -1
  175. package/dist/stream/ws-delivery.d.ts.map +1 -1
  176. package/dist/stream/ws-delivery.js +6 -4
  177. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  178. package/dist/stream/ws-local-connection-store.js +4 -2
  179. package/dist/stream/ws-presence-store.js +1 -1
  180. package/dist/stream/ws-registry.d.ts +4 -4
  181. package/dist/stream/ws-registry.d.ts.map +1 -1
  182. package/dist/stream/ws-registry.js +4 -2
  183. package/dist/stream/ws-telemetry.d.ts +16 -12
  184. package/dist/stream/ws-telemetry.d.ts.map +1 -1
  185. package/dist/stream/ws-telemetry.js +22 -18
  186. package/dist/stream/ws.d.ts +18 -12
  187. package/dist/stream/ws.d.ts.map +1 -1
  188. package/dist/stream/ws.js +26 -22
  189. package/dist/syncer/api-parser.js +1 -1
  190. package/dist/syncer/checksum.d.ts.map +1 -1
  191. package/dist/syncer/checksum.js +7 -8
  192. package/dist/syncer/code-generator.d.ts.map +1 -1
  193. package/dist/syncer/code-generator.js +13 -14
  194. package/dist/syncer/entity-operations.d.ts.map +1 -1
  195. package/dist/syncer/entity-operations.js +5 -6
  196. package/dist/syncer/file-copy.d.ts +5 -0
  197. package/dist/syncer/file-copy.d.ts.map +1 -0
  198. package/dist/syncer/file-copy.js +36 -0
  199. package/dist/syncer/file-patterns.d.ts +1 -0
  200. package/dist/syncer/file-patterns.d.ts.map +1 -1
  201. package/dist/syncer/file-patterns.js +5 -2
  202. package/dist/syncer/file-tracking.d.ts.map +1 -1
  203. package/dist/syncer/file-tracking.js +5 -4
  204. package/dist/syncer/filesystem-dependencies.d.ts +15 -0
  205. package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
  206. package/dist/syncer/filesystem-dependencies.js +53 -0
  207. package/dist/syncer/module-loader.d.ts +1 -1
  208. package/dist/syncer/module-loader.d.ts.map +1 -1
  209. package/dist/syncer/module-loader.js +6 -4
  210. package/dist/syncer/syncer-actions.d.ts +4 -0
  211. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  212. package/dist/syncer/syncer-actions.js +34 -17
  213. package/dist/syncer/syncer.d.ts +17 -6
  214. package/dist/syncer/syncer.d.ts.map +1 -1
  215. package/dist/syncer/syncer.js +71 -29
  216. package/dist/syncer/watcher.d.ts.map +1 -1
  217. package/dist/syncer/watcher.js +3 -3
  218. package/dist/tasks/decorator.d.ts +6 -6
  219. package/dist/tasks/decorator.d.ts.map +1 -1
  220. package/dist/tasks/decorator.js +4 -4
  221. package/dist/tasks/step-wrapper.d.ts +6 -4
  222. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  223. package/dist/tasks/step-wrapper.js +5 -5
  224. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  225. package/dist/tasks/workflow-manager.js +13 -7
  226. package/dist/template/entity-converter.d.ts +1 -1
  227. package/dist/template/entity-converter.d.ts.map +1 -1
  228. package/dist/template/entity-converter.js +2 -2
  229. package/dist/template/helpers.d.ts +8 -6
  230. package/dist/template/helpers.d.ts.map +1 -1
  231. package/dist/template/helpers.js +1 -1
  232. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  233. package/dist/template/implementations/generated.template.js +4 -4
  234. package/dist/template/implementations/generated_http.template.d.ts +7 -4
  235. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  236. package/dist/template/implementations/generated_http.template.js +21 -21
  237. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  238. package/dist/template/implementations/generated_sso.template.js +6 -3
  239. package/dist/template/implementations/http_validators.template.d.ts +18 -0
  240. package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
  241. package/dist/template/implementations/http_validators.template.js +127 -0
  242. package/dist/template/implementations/model.template.d.ts.map +1 -1
  243. package/dist/template/implementations/model.template.js +1 -1
  244. package/dist/template/implementations/queries.template.js +1 -1
  245. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  246. package/dist/template/implementations/sd.template.js +79 -39
  247. package/dist/template/implementations/services.template.d.ts.map +1 -1
  248. package/dist/template/implementations/services.template.js +6 -4
  249. package/dist/template/implementations/view_form.template.d.ts +6 -1
  250. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  251. package/dist/template/implementations/view_form.template.js +23 -15
  252. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  253. package/dist/template/implementations/view_list.template.js +11 -6
  254. package/dist/template/template-manager.d.ts.map +1 -1
  255. package/dist/template/template-manager.js +9 -7
  256. package/dist/template/template-types.d.ts +12 -5
  257. package/dist/template/template-types.d.ts.map +1 -1
  258. package/dist/template/template-types.js +1 -1
  259. package/dist/template/template.d.ts +2 -2
  260. package/dist/template/template.d.ts.map +1 -1
  261. package/dist/template/template.js +6 -4
  262. package/dist/template/zod-converter.d.ts +0 -19
  263. package/dist/template/zod-converter.d.ts.map +1 -1
  264. package/dist/template/zod-converter.js +100 -84
  265. package/dist/testing/_relation-graph.d.ts.map +1 -1
  266. package/dist/testing/_relation-graph.js +2 -1
  267. package/dist/testing/bootstrap.d.ts.map +1 -1
  268. package/dist/testing/bootstrap.js +9 -6
  269. package/dist/testing/data-explorer.d.ts +32 -7
  270. package/dist/testing/data-explorer.d.ts.map +1 -1
  271. package/dist/testing/data-explorer.js +77 -31
  272. package/dist/testing/dev-test-routes.d.ts +8 -1
  273. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  274. package/dist/testing/dev-test-routes.js +125 -108
  275. package/dist/testing/dev-vitest-manager.d.ts +54 -8
  276. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  277. package/dist/testing/dev-vitest-manager.js +24 -9
  278. package/dist/testing/faker-mappings.d.ts +412 -1
  279. package/dist/testing/faker-mappings.d.ts.map +1 -1
  280. package/dist/testing/faker-mappings.js +1 -1
  281. package/dist/testing/filesystem.d.ts +2 -0
  282. package/dist/testing/filesystem.d.ts.map +1 -0
  283. package/dist/testing/filesystem.js +7 -0
  284. package/dist/testing/fixture-generator.d.ts +25 -8
  285. package/dist/testing/fixture-generator.d.ts.map +1 -1
  286. package/dist/testing/fixture-generator.js +132 -73
  287. package/dist/testing/fixture-loader.d.ts +3 -1
  288. package/dist/testing/fixture-loader.d.ts.map +1 -1
  289. package/dist/testing/fixture-loader.js +1 -20
  290. package/dist/testing/fixture-manager.d.ts +8 -7
  291. package/dist/testing/fixture-manager.d.ts.map +1 -1
  292. package/dist/testing/fixture-manager.js +91 -59
  293. package/dist/testing/global-setup.d.ts.map +1 -1
  294. package/dist/testing/global-setup.js +5 -2
  295. package/dist/testing/index.d.ts +2 -0
  296. package/dist/testing/index.d.ts.map +1 -1
  297. package/dist/testing/index.js +6 -3
  298. package/dist/types/types.d.ts +101 -99
  299. package/dist/types/types.d.ts.map +1 -1
  300. package/dist/types/types.js +234 -225
  301. package/dist/ui/ai-api.d.ts.map +1 -1
  302. package/dist/ui/ai-api.js +1 -1
  303. package/dist/ui/ai-client.d.ts.map +1 -1
  304. package/dist/ui/ai-client.js +28 -18
  305. package/dist/ui/api.d.ts.map +1 -1
  306. package/dist/ui/api.js +146 -210
  307. package/dist/ui/async-route.d.ts +3 -0
  308. package/dist/ui/async-route.d.ts.map +1 -0
  309. package/dist/ui/async-route.js +12 -0
  310. package/dist/ui/cdd-service.d.ts +9 -7
  311. package/dist/ui/cdd-service.d.ts.map +1 -1
  312. package/dist/ui/cdd-service.js +7 -5
  313. package/dist/ui/migrations-api.d.ts +3 -0
  314. package/dist/ui/migrations-api.d.ts.map +1 -0
  315. package/dist/ui/migrations-api.js +215 -0
  316. package/dist/ui-web/assets/index-D3QNH-Dv.css +1 -0
  317. package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
  318. package/dist/ui-web/index.html +2 -2
  319. package/dist/utils/formatter.d.ts.map +1 -1
  320. package/dist/utils/formatter.js +7 -5
  321. package/dist/utils/fs-utils.d.ts +3 -3
  322. package/dist/utils/fs-utils.d.ts.map +1 -1
  323. package/dist/utils/fs-utils.js +6 -6
  324. package/dist/utils/model.d.ts +3 -5
  325. package/dist/utils/model.d.ts.map +1 -1
  326. package/dist/utils/model.js +1 -1
  327. package/dist/utils/object-utils.d.ts +1 -1
  328. package/dist/utils/object-utils.d.ts.map +1 -1
  329. package/dist/utils/object-utils.js +17 -16
  330. package/dist/utils/process-utils.d.ts.map +1 -1
  331. package/dist/utils/process-utils.js +4 -2
  332. package/dist/utils/runtime-value.d.ts +13 -0
  333. package/dist/utils/runtime-value.d.ts.map +1 -0
  334. package/dist/utils/runtime-value.js +40 -0
  335. package/dist/utils/sql-parser.d.ts +8 -1
  336. package/dist/utils/sql-parser.d.ts.map +1 -1
  337. package/dist/utils/sql-parser.js +6 -3
  338. package/dist/utils/type-utils.d.ts.map +1 -1
  339. package/dist/utils/type-utils.js +10 -8
  340. package/dist/utils/utils.d.ts +2 -2
  341. package/dist/utils/utils.d.ts.map +1 -1
  342. package/dist/utils/utils.js +11 -8
  343. package/dist/utils/zod-error.d.ts.map +1 -1
  344. package/dist/utils/zod-error.js +7 -4
  345. package/dist/vector/chunking.d.ts.map +1 -1
  346. package/dist/vector/chunking.js +1 -1
  347. package/dist/vector/embedding.d.ts.map +1 -1
  348. package/dist/vector/embedding.js +1 -1
  349. package/dist/vector/types.d.ts +3 -2
  350. package/dist/vector/types.d.ts.map +1 -1
  351. package/package.json +11 -7
  352. package/src/ai/agents/agent.ts +5 -5
  353. package/src/ai/agents/types.ts +1 -1
  354. package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
  355. package/src/ai/providers/rtzr/error.ts +5 -3
  356. package/src/ai/providers/rtzr/model.ts +3 -2
  357. package/src/ai/providers/rtzr/options.ts +10 -5
  358. package/src/ai/providers/rtzr/provider.ts +10 -10
  359. package/src/ai/providers/rtzr/utils.ts +3 -1
  360. package/src/api/__tests__/config.test.ts +11 -21
  361. package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
  362. package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
  363. package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
  364. package/src/api/caster.ts +6 -8
  365. package/src/api/code-converters.ts +136 -36
  366. package/src/api/config.ts +62 -4
  367. package/src/api/context.ts +3 -3
  368. package/src/api/decorators.ts +28 -17
  369. package/src/api/http-validator.ts +149 -0
  370. package/src/api/sonamu.ts +459 -152
  371. package/src/api/websocket-helpers.ts +24 -17
  372. package/src/auth/audit-log/builders.ts +3 -2
  373. package/src/auth/audit-log/client-ip.ts +4 -2
  374. package/src/auth/audit-log/events.ts +15 -1
  375. package/src/auth/audit-log/plugin.ts +129 -200
  376. package/src/auth/audit-log-ingestor.ts +19 -15
  377. package/src/auth/auth-generator.ts +10 -5
  378. package/src/auth/better-auth-request.ts +4 -3
  379. package/src/auth/knex-adapter.ts +8 -2
  380. package/src/auth/plugins/entity-definitions/index.ts +6 -5
  381. package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
  382. package/src/bin/__tests__/test-command.test.ts +9 -15
  383. package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
  384. package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
  385. package/src/bin/build-config.ts +167 -0
  386. package/src/bin/cli.ts +155 -38
  387. package/src/bin/compiler-policy.ts +35 -0
  388. package/src/bin/fixture.ts +14 -3
  389. package/src/bin/test-command.ts +74 -29
  390. package/src/bin/ts-loader-registration.ts +22 -4
  391. package/src/cache/decorator.ts +10 -3
  392. package/src/compress/compress.ts +17 -4
  393. package/src/cone/cone-generator.ts +34 -6
  394. package/src/database/__tests__/db.test.ts +1 -0
  395. package/src/database/__tests__/puri.test.ts +16 -15
  396. package/src/database/__tests__/transaction-scope.test.ts +5 -0
  397. package/src/database/_batch_update.ts +1 -0
  398. package/src/database/base-model.ts +81 -50
  399. package/src/database/base-model.types.ts +6 -6
  400. package/src/database/db.ts +29 -19
  401. package/src/database/knex.ts +30 -17
  402. package/src/database/puri-subset.test-d.ts +17 -0
  403. package/src/database/puri.ts +181 -143
  404. package/src/database/puri.types.test-d.ts +31 -1
  405. package/src/database/puri.types.ts +23 -21
  406. package/src/database/upsert-builder.ts +75 -53
  407. package/src/dict/sd.ts +15 -5
  408. package/src/dict/sonamu-dictionary.ts +6 -6
  409. package/src/dict/utils.ts +9 -1
  410. package/src/entity/entity-manager.ts +26 -9
  411. package/src/entity/entity-template-cone.ts +5 -10
  412. package/src/entity/entity.ts +50 -34
  413. package/src/env.ts +7 -3
  414. package/src/exceptions/error-handler.ts +17 -2
  415. package/src/exceptions/so-exceptions.ts +26 -20
  416. package/src/filter/utils.ts +21 -11
  417. package/src/index.ts +3 -0
  418. package/src/logger/configure.ts +26 -18
  419. package/src/migration/__tests__/migrator.test.ts +50 -57
  420. package/src/migration/code-generation.ts +132 -104
  421. package/src/migration/index-where-predicate.ts +51 -24
  422. package/src/migration/migration-set.ts +11 -5
  423. package/src/migration/migrator.ts +392 -244
  424. package/src/migration/postgresql-schema-reader.ts +52 -47
  425. package/src/migration/slack-confirm.ts +17 -15
  426. package/src/migration/types.ts +57 -0
  427. package/src/naite/naite-reporter.ts +1 -1
  428. package/src/naite/naite.ts +97 -24
  429. package/src/runtime-type.ts +34 -0
  430. package/src/ssr/hmr-opt-out.ts +78 -0
  431. package/src/ssr/renderer.ts +6 -0
  432. package/src/storage/base-file.ts +13 -13
  433. package/src/storage/buffered-file.ts +11 -11
  434. package/src/storage/s3-driver.ts +4 -1
  435. package/src/storage/uploaded-file.ts +8 -8
  436. package/src/stream/__tests__/ws-contracts.test.ts +4 -2
  437. package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
  438. package/src/stream/__tests__/ws.test.ts +30 -6
  439. package/src/stream/sse.ts +12 -11
  440. package/src/stream/ws-core.ts +1 -1
  441. package/src/stream/ws-delivery.ts +16 -7
  442. package/src/stream/ws-local-connection-store.ts +2 -3
  443. package/src/stream/ws-presence-store.ts +6 -1
  444. package/src/stream/ws-registry.ts +16 -7
  445. package/src/stream/ws-telemetry.ts +66 -43
  446. package/src/stream/ws.ts +73 -39
  447. package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
  448. package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
  449. package/src/syncer/api-parser.ts +67 -27
  450. package/src/syncer/checksum.ts +17 -11
  451. package/src/syncer/code-generator.ts +25 -17
  452. package/src/syncer/entity-operations.ts +3 -5
  453. package/src/syncer/file-copy.ts +46 -0
  454. package/src/syncer/file-patterns.ts +11 -8
  455. package/src/syncer/file-tracking.ts +3 -4
  456. package/src/syncer/filesystem-dependencies.ts +73 -0
  457. package/src/syncer/module-loader.ts +8 -4
  458. package/src/syncer/syncer-actions.ts +56 -18
  459. package/src/syncer/syncer.ts +116 -43
  460. package/src/syncer/watcher.ts +8 -4
  461. package/src/tasks/decorator.ts +20 -12
  462. package/src/tasks/step-wrapper.ts +19 -9
  463. package/src/tasks/workflow-manager.ts +15 -9
  464. package/src/template/__tests__/http_validators.template.test.ts +160 -0
  465. package/src/template/__tests__/services.template.mutation.test.ts +24 -23
  466. package/src/template/__tests__/services.template.websocket.test.ts +21 -25
  467. package/src/template/entity-converter.ts +5 -2
  468. package/src/template/helpers.ts +8 -9
  469. package/src/template/implementations/generated.template.ts +5 -4
  470. package/src/template/implementations/generated_http.template.ts +61 -15
  471. package/src/template/implementations/generated_sso.template.ts +15 -4
  472. package/src/template/implementations/http_validators.template.ts +175 -0
  473. package/src/template/implementations/model.template.ts +3 -1
  474. package/src/template/implementations/sd.template.ts +81 -41
  475. package/src/template/implementations/services.template.ts +2 -2
  476. package/src/template/implementations/view_form.template.ts +56 -39
  477. package/src/template/implementations/view_list.template.ts +31 -23
  478. package/src/template/template-manager.ts +15 -13
  479. package/src/template/template-types.ts +13 -5
  480. package/src/template/template.ts +4 -3
  481. package/src/template/zod-converter.ts +206 -91
  482. package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
  483. package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
  484. package/src/testing/_relation-graph.ts +4 -1
  485. package/src/testing/bootstrap.ts +12 -5
  486. package/src/testing/data-explorer.ts +102 -53
  487. package/src/testing/dev-test-routes.ts +87 -50
  488. package/src/testing/dev-vitest-manager.ts +88 -32
  489. package/src/testing/faker-mappings.ts +2 -2
  490. package/src/testing/filesystem.ts +1 -0
  491. package/src/testing/fixture-generator.ts +191 -131
  492. package/src/testing/fixture-loader.ts +5 -1
  493. package/src/testing/fixture-manager.ts +129 -90
  494. package/src/testing/global-setup.ts +7 -3
  495. package/src/testing/index.ts +5 -0
  496. package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
  497. package/src/types/types.ts +184 -158
  498. package/src/ui/ai-api.ts +9 -7
  499. package/src/ui/ai-client.ts +61 -56
  500. package/src/ui/api.ts +726 -770
  501. package/src/ui/async-route.ts +4 -0
  502. package/src/ui/cdd-service.ts +35 -12
  503. package/src/ui/migrations-api.ts +381 -0
  504. package/src/utils/formatter.ts +7 -3
  505. package/src/utils/fs-utils.ts +5 -5
  506. package/src/utils/model.ts +4 -5
  507. package/src/utils/object-utils.ts +32 -22
  508. package/src/utils/process-utils.ts +4 -1
  509. package/src/utils/runtime-value.ts +43 -0
  510. package/src/utils/sql-parser.ts +13 -6
  511. package/src/utils/type-utils.ts +9 -7
  512. package/src/utils/utils.ts +19 -21
  513. package/src/utils/zod-error.ts +4 -2
  514. package/src/vector/chunking.ts +6 -1
  515. package/src/vector/embedding.ts +2 -1
  516. package/src/vector/types.ts +4 -2
  517. package/tsdown.api.config.ts +5 -2
  518. package/dist/ui-web/assets/index-CJf8uJYf.js +0 -176
  519. package/dist/ui-web/assets/index-GMMIVGja.css +0 -1
@@ -1,10 +1,10 @@
1
1
  import assert from "assert";
2
- import { mkdir, readdir, unlink, writeFile } from "fs/promises";
2
+ import { mkdir, readdir, writeFile } from "fs/promises";
3
3
  import path from "path";
4
4
 
5
5
  import chalk from "chalk";
6
6
  import { type Knex } from "knex";
7
- import { group, sum, unique } from "radashi";
7
+ import { group, unique } from "radashi";
8
8
 
9
9
  import { Sonamu } from "../api/sonamu";
10
10
  import { DB } from "../database/db";
@@ -18,10 +18,22 @@ import { Naite } from "../naite/naite";
18
18
  import { type GenMigrationCode, type MigrationSet } from "../types/types";
19
19
  import { isLocal, isTest } from "../utils/controller";
20
20
  import { exists } from "../utils/fs-utils";
21
+ import { isObjectValue, isStringValue } from "../utils/runtime-value";
21
22
  import { generateAlterCode, generateCreateCode } from "./code-generation";
22
23
  import { getMigrationSetFromEntity } from "./migration-set";
23
24
  import { PostgreSQLSchemaReader } from "./postgresql-schema-reader";
24
- import { type ConnString, type MigrationCode, type MigrationStatus } from "./types";
25
+ import { SlackConfirm } from "./slack-confirm";
26
+ import {
27
+ type ConnString,
28
+ type MigrationAction,
29
+ type MigrationCode,
30
+ type MigrationConnectionMeta,
31
+ type MigrationConnectionStatus,
32
+ type MigrationProgressEvent,
33
+ type MigrationRunOptions,
34
+ type MigrationStatus,
35
+ type MigrationTarget,
36
+ } from "./types";
25
37
 
26
38
  export type MigrationResult = {
27
39
  connKey: string;
@@ -29,10 +41,25 @@ export type MigrationResult = {
29
41
  applied: string[];
30
42
  }[];
31
43
 
44
+ function padDatePart(number: number, size: number = 2): string {
45
+ return number.toString().padStart(size, "0");
46
+ }
47
+
48
+ export class MigrationTargetExecutionError extends Error {
49
+ constructor(
50
+ public readonly connKey: MigrationTarget | "shadow",
51
+ cause: unknown,
52
+ ) {
53
+ super(cause instanceof Error ? cause.message : String(cause));
54
+ this.name = "MigrationTargetExecutionError";
55
+ }
56
+ }
57
+
32
58
  // 마이그레이션 상태 조회 시 DB 연결 확인 타임아웃(ms).
33
59
  // createKnexInstance의 커넥션 획득 타임아웃(30초)에 status/list/currentVersion이
34
60
  // 각각 걸리면 CLI/UI가 최대 수십 초 hang한다. 정상 응답은 수백 ms이므로 5초면 충분하다.
35
61
  const MIGRATION_CONN_TIMEOUT_MS = 5000;
62
+ const LOCAL_DB_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
36
63
 
37
64
  /**
38
65
  * 주어진 Promise가 ms 안에 완료되지 않으면 onTimeout 에러로 reject한다.
@@ -54,24 +81,35 @@ function withTimeout<T>(promise: Promise<T>, ms: number, onTimeout: () => Error)
54
81
  });
55
82
  }
56
83
 
84
+ /**
85
+ * Note 2026-08-26, 병준
86
+ * 이 클래스에서 deprecated 붙어있는 친구들은 현 CLI를 위한 하위 호환이니,
87
+ * CLI 리팩토링 시점에서 제거해주셔도 무방합니다.
88
+ */
57
89
  export class Migrator {
58
- private isMissingMigrationTableError(error: unknown): boolean {
59
- if (typeof error !== "object" || error === null) {
90
+ private isMissingMigrationTableError<Value>(error: Value): boolean {
91
+ if (!isObjectValue(error) || error === null) {
60
92
  return false;
61
93
  }
62
94
 
63
- const maybePostgresError = error as { code?: unknown; message?: unknown };
95
+ const maybePostgresError =
96
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ error as {
97
+ code?: unknown;
98
+ message?: unknown;
99
+ };
64
100
  return (
65
101
  maybePostgresError.code === "42P01" &&
66
- typeof maybePostgresError.message === "string" &&
102
+ isStringValue(maybePostgresError.message) &&
67
103
  maybePostgresError.message.includes("knex_migrations")
68
104
  );
69
105
  }
70
106
 
71
- private getMigrationTargetKeys(): (keyof SonamuDBConfig)[] {
72
- const connKeys = Object.keys(Sonamu.dbConfig).filter(
73
- (key) => !key.endsWith("_readonly"),
74
- ) as (keyof SonamuDBConfig)[];
107
+ /** 현재 실행 환경에서 허용된 마이그레이션 대상 키를 반환합니다. */
108
+ getMigrationTargetKeys(): MigrationTarget[] {
109
+ const connKeys =
110
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Object.keys(
111
+ Sonamu.dbConfig,
112
+ ).filter((key) => !key.endsWith("_readonly")) as (keyof SonamuDBConfig)[];
75
113
 
76
114
  if (isLocal()) {
77
115
  return connKeys;
@@ -84,24 +122,143 @@ export class Migrator {
84
122
  private async runMigrationsSequentially(
85
123
  conns: { connKey: keyof SonamuDBConfig; knex: Knex }[],
86
124
  action: "apply" | "rollback",
125
+ options?: MigrationRunOptions,
87
126
  ): Promise<MigrationResult> {
88
127
  const results: MigrationResult = [];
89
128
 
90
129
  for (const { connKey, knex } of conns) {
91
- const [batchNo, applied] =
92
- action === "apply" ? await knex.migrate.latest() : await knex.migrate.rollback();
130
+ const progress = this.createProgressHooks(action, connKey, options);
131
+ let migrationResult: [number, string[]];
132
+ try {
133
+ migrationResult =
134
+ action === "apply"
135
+ ? await knex.migrate.latest(progress)
136
+ : await knex.migrate.rollback(progress);
137
+ } catch (caught) {
138
+ // lifecycle hook 이전의 연결·lock·목록 조회 실패도 실제 대상 DB에 귀속합니다.
139
+ throw new MigrationTargetExecutionError(connKey, caught);
140
+ }
141
+ const [batchNo, applied] = migrationResult;
93
142
 
94
143
  results.push({
95
144
  connKey,
96
145
  batchNo,
97
146
  applied,
98
147
  });
148
+ this.emitProgress(options, {
149
+ type: "target-complete",
150
+ action,
151
+ connKey,
152
+ batchNo,
153
+ files: applied,
154
+ });
99
155
  }
100
156
 
101
157
  return results;
102
158
  }
103
159
 
104
- private async getMigrationCodes(): Promise<MigrationCode[]> {
160
+ private migrationName<Value>(migration: Value): string {
161
+ if (isStringValue(migration)) {
162
+ return path.basename(migration);
163
+ }
164
+ if (isObjectValue(migration) && migration !== null) {
165
+ const candidate =
166
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ migration as {
167
+ file?: unknown;
168
+ name?: unknown;
169
+ };
170
+ if (isStringValue(candidate.file)) {
171
+ return path.basename(candidate.file);
172
+ }
173
+ if (isStringValue(candidate.name)) {
174
+ return path.basename(candidate.name);
175
+ }
176
+ }
177
+ return String(migration);
178
+ }
179
+
180
+ private emitProgress(options: MigrationRunOptions | undefined, event: MigrationProgressEvent) {
181
+ try {
182
+ options?.onProgress?.(event);
183
+ } catch (error) {
184
+ // 진행률 소비자의 실패가 이미 시작한 DB 작업과 정리를 방해하면 안 됩니다.
185
+ console.warn("Migration progress observer failed:", error);
186
+ }
187
+ }
188
+
189
+ private createProgressHooks(
190
+ action: MigrationAction,
191
+ connKey: MigrationTarget | "shadow",
192
+ options?: MigrationRunOptions,
193
+ ): Knex.MigratorConfigWithLifecycleHooks {
194
+ let files: string[] = [];
195
+ const emitFile = <Value>(type: "file-start" | "file-executed", migration: Value) => {
196
+ const file = this.migrationName(migration);
197
+ const index = Math.max(files.indexOf(file), 0);
198
+ this.emitProgress(options, {
199
+ type,
200
+ action,
201
+ connKey,
202
+ file,
203
+ index,
204
+ total: files.length,
205
+ });
206
+ };
207
+
208
+ return {
209
+ beforeAll: async (_knex, migrations) => {
210
+ files = migrations.map((migration) => this.migrationName(migration));
211
+ this.emitProgress(options, { type: "target-start", action, connKey, files });
212
+ },
213
+ beforeEach: async (_knex, migrations) => {
214
+ migrations.forEach((migration) => emitFile("file-start", migration));
215
+ },
216
+ afterEach: async (_knex, migrations) => {
217
+ migrations.forEach((migration) => emitFile("file-executed", migration));
218
+ },
219
+ };
220
+ }
221
+
222
+ private assertMigrationTarget(connKey: MigrationTarget): void {
223
+ if (!this.getMigrationTargetKeys().includes(connKey)) {
224
+ throw new Error(
225
+ `Migration target is not allowed in NODE_ENV=${getSonamuEnvironment()}: ${String(connKey)}`,
226
+ );
227
+ }
228
+ }
229
+
230
+ /**
231
+ * 마이그레이션 대상 커넥션의 정적 메타데이터를 반환합니다.
232
+ * DB에 연결하지 않으므로 CLI와 Web이 상태 조회 전에 즉시 사용할 수 있습니다.
233
+ *
234
+ * @category 분리형 마이그레이션 API
235
+ */
236
+ getConnections(): MigrationConnectionMeta[] {
237
+ const slackConfirm = new SlackConfirm();
238
+ return this.getMigrationTargetKeys().map((connKey) => {
239
+ const connection =
240
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig[
241
+ connKey
242
+ ].connection as Knex.PgConnectionConfig;
243
+ const host = connection.host ?? "localhost";
244
+ return {
245
+ connKey,
246
+ name: String(connKey),
247
+ host,
248
+ port: connection.port ?? 5432,
249
+ database: connection.database ?? "",
250
+ remote: !LOCAL_DB_HOSTS.has(host.toLowerCase()),
251
+ requiresApproval: slackConfirm.isTargetRequiresApproval(connKey),
252
+ };
253
+ });
254
+ }
255
+
256
+ /**
257
+ * 소스에 존재하는 마이그레이션 파일 목록을 반환합니다.
258
+ *
259
+ * @category 분리형 마이그레이션 API
260
+ */
261
+ async getMigrationCodes(): Promise<MigrationCode[]> {
105
262
  const srcMigrationsDir = path.join(Sonamu.apiRootPath, "src", "migrations"); // 이건 환경에 관계없이 항상 src에서 찾아야 해요.
106
263
 
107
264
  if (!(await exists(srcMigrationsDir))) {
@@ -123,183 +280,214 @@ export class Migrator {
123
280
  }
124
281
 
125
282
  /**
126
- * 타겟별 마이그레이션 상태와 코드 생성/준비 상태를 구해옵니다.
127
- * 실제로 DB에 접근도 하고 마이그레이션 코드 파일도 확인하고,
128
- * 필요하다면 적용할 수 있는 코드를 생성까지 해옵니다.
283
+ * 커넥션 하나의 현재 버전과 pending 목록을 독립적으로 조회합니다.
284
+ * 여러 대상은 호출자가 병렬로 조립할 수 있습니다.
129
285
  *
130
- * CLI와 Sonamu UI에서 사용됩니다.
131
- *
132
- * @returns
286
+ * @category 분리형 마이그레이션 API
133
287
  */
134
- async getStatus(): Promise<MigrationStatus> {
288
+ async getConnectionStatus(connKey: MigrationTarget): Promise<MigrationConnectionStatus> {
289
+ this.assertMigrationTarget(connKey);
290
+ const startedAt = performance.now();
135
291
  const codes = await this.getMigrationCodes();
136
- Naite.t("migrator:getStatus:codes", codes);
292
+ const knexOptions = Sonamu.dbConfig[connKey];
293
+ const connection =
294
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ knexOptions.connection as Knex.PgConnectionConfig;
295
+ // SAFETY: Knex가 제공한 PostgreSQL 연결 구성을 복사해 timeout 필드만 덮어쓴다.
296
+ const tConn = createKnexInstance({
297
+ ...knexOptions,
298
+ connection: {
299
+ ...connection,
300
+ connectionTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
301
+ },
302
+ pool: {
303
+ ...knexOptions.pool,
304
+ min: 0,
305
+ acquireTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
306
+ createTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
307
+ propagateCreateError: true,
308
+ },
309
+ });
137
310
 
138
- const connKeys = this.getMigrationTargetKeys();
139
-
140
- let migrationStatusError: string | undefined;
141
-
142
- const statuses = await Promise.all(
143
- connKeys.map(async (connKey) => {
144
- const knexOptions = Sonamu.dbConfig[connKey];
145
- const connection = knexOptions.connection as Knex.PgConnectionConfig;
146
- // 상태 조회용 커넥션은 fail-fast로 만든다: 불통 DB에서 커넥션 획득을 무한정
147
- // 재시도하며 CLI 프로세스가 종료되지 않는 것을 막는다.
148
- // - connectionTimeoutMillis: 소켓 연결 자체를 5초 안에 포기 → 소켓이 정리되어 프로세스가 종료됨
149
- // - propagateCreateError: 첫 연결 실패를 즉시 전파(풀 재시도 없이 바로 실패)
150
- const tConn = createKnexInstance({
151
- ...knexOptions,
152
- connection: {
153
- ...(connection as Record<string, unknown>),
154
- connectionTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
155
- },
156
- pool: {
157
- ...knexOptions.pool,
158
- min: 0,
159
- acquireTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
160
- createTimeoutMillis: MIGRATION_CONN_TIMEOUT_MS,
161
- propagateCreateError: true,
162
- },
163
- });
164
- const connString =
165
- `pg://${connection.user ?? ""}@${connection.host}:${connection.port}/${connection.database}` as ConnString;
166
-
167
- try {
168
- // DB 연결 확인: 불통 DB에서 status/list/currentVersion이 커넥션 획득
169
- // 타임아웃까지 hang하는 것을 막기 위해, 먼저 5초 안에 응답하는지 확인한다.
170
- try {
171
- await withTimeout(
172
- tConn.raw("select 1"),
173
- MIGRATION_CONN_TIMEOUT_MS,
174
- () =>
175
- new Error(
176
- `DB 연결 시간 초과 (${MIGRATION_CONN_TIMEOUT_MS}ms) — ${connection.host}:${connection.port}/${connection.database}`,
177
- ),
178
- );
179
- } catch (err) {
180
- console.warn(
181
- chalk.yellow(
182
- `${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스에 연결할 수 없습니다.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
183
- ),
184
- );
185
- migrationStatusError = err instanceof Error ? err.message : String(err);
186
- return {
187
- name: connKey,
188
- connKey,
189
- connString,
190
- currentVersion: "error" as const,
191
- status: "error" as const,
192
- pending: [] as string[],
193
- };
194
- }
311
+ try {
312
+ await withTimeout(tConn.raw("select 1"), MIGRATION_CONN_TIMEOUT_MS, () => {
313
+ return new Error(
314
+ `DB 연결 시간 초과 (${MIGRATION_CONN_TIMEOUT_MS}ms) — ${connection.host}:${connection.port}/${connection.database}`,
315
+ );
316
+ });
195
317
 
196
- const status: number | "error" = await (async () => {
197
- try {
198
- return await tConn.migrate.status();
199
- } catch (err) {
200
- if (this.isMissingMigrationTableError(err)) {
201
- return codes.length;
202
- }
203
-
204
- console.warn(
205
- chalk.yellow(
206
- `${connKey}의 마이그레이션 상태를 가져오는 데에 실패하였습니다. 데이터베이스가 올바르게 구성되지 않은 것 같습니다. 확인하시고 다시 시도해주세요.\n시도한 연결 설정:\n${JSON.stringify(knexOptions.connection, null, 2)}\n발생한 에러:\n${err}\n`,
207
- ),
208
- );
209
- migrationStatusError = err instanceof Error ? err.message : String(err);
210
- return "error";
211
- }
212
- })();
213
- const pending: string[] = await (async () => {
214
- try {
215
- const [, fdList] = await tConn.migrate.list();
216
- return fdList.map((fd: { file: string }) => fd.file.replace(".ts", ""));
217
- } catch (err) {
218
- if (this.isMissingMigrationTableError(err)) {
219
- return codes.map((code) => code.name);
220
- }
221
-
222
- migrationStatusError = err instanceof Error ? err.message : String(err);
223
- return [];
224
- }
225
- })();
226
- const currentVersion: string | "error" = await (async () => {
227
- try {
228
- return await tConn.migrate.currentVersion();
229
- } catch (_err) {
230
- if (this.isMissingMigrationTableError(_err)) {
231
- return "none";
232
- }
233
-
234
- migrationStatusError = _err instanceof Error ? _err.message : String(_err);
235
- return "error";
236
- }
237
- })();
238
- Naite.t("migrator:getStatus:status", status);
239
-
240
- return {
241
- name: connKey,
242
- connKey,
243
- connString,
244
- currentVersion,
245
- status: status,
246
- pending,
247
- };
248
- } finally {
249
- // 불통 DB의 커넥션 정리가 커넥션 획득 타임아웃까지 hang하지 않도록 감싼다.
250
- await withTimeout(
251
- tConn.destroy(),
252
- MIGRATION_CONN_TIMEOUT_MS,
253
- () => new Error("connection destroy timeout"),
254
- ).catch(() => {});
318
+ let error: string | undefined;
319
+ const status: number | "error" = await tConn.migrate.status().catch((caught) => {
320
+ if (this.isMissingMigrationTableError(caught)) {
321
+ return codes.length;
255
322
  }
256
- }),
257
- );
323
+ error = caught instanceof Error ? caught.message : String(caught);
324
+ return "error" as const;
325
+ });
326
+ const pending: string[] = await tConn.migrate.list().then(
327
+ ([, files]: [unknown[], { file: string }[]]) =>
328
+ files.map(({ file }) => file.replace(/\.ts$/, "")),
329
+ (cause: unknown) => {
330
+ if (this.isMissingMigrationTableError(cause)) {
331
+ return codes.map(({ name }) => name);
332
+ }
333
+ error ??= cause instanceof Error ? cause.message : String(cause);
334
+ return [];
335
+ },
336
+ );
337
+ const currentVersion: string | "none" | "error" = await tConn.migrate
338
+ .currentVersion()
339
+ .catch((cause: unknown) => {
340
+ if (this.isMissingMigrationTableError(cause)) {
341
+ return "none" as const;
342
+ }
343
+ error ??= cause instanceof Error ? cause.message : String(cause);
344
+ return "error" as const;
345
+ });
346
+ Naite.t("migrator:getStatus:status", status);
347
+ const connectionStatus: MigrationConnectionStatus = {
348
+ connKey,
349
+ currentVersion,
350
+ status,
351
+ pending,
352
+ latencyMs: performance.now() - startedAt,
353
+ };
354
+ if (error !== undefined) connectionStatus.error = error;
355
+ return connectionStatus;
356
+ } catch (caught) {
357
+ const error = caught instanceof Error ? caught.message : String(caught);
358
+ console.warn(
359
+ chalk.yellow(
360
+ `${String(connKey)}의 마이그레이션 상태를 가져오는 데에 실패하였습니다.\n${error}`,
361
+ ),
362
+ );
363
+ return {
364
+ connKey,
365
+ currentVersion: "error",
366
+ status: "error",
367
+ pending: [],
368
+ latencyMs: performance.now() - startedAt,
369
+ error,
370
+ };
371
+ } finally {
372
+ await withTimeout(tConn.destroy(), MIGRATION_CONN_TIMEOUT_MS, () => {
373
+ return new Error("connection destroy timeout");
374
+ }).catch(() => {});
375
+ }
376
+ }
258
377
 
259
- Naite.t("migrator:getStatus:conns", statuses);
378
+ /**
379
+ * 최신 상태인 기준 DB와 엔티티 정의를 비교해 생성 예정 코드를 반환합니다.
380
+ *
381
+ * @category 분리형 마이그레이션 API
382
+ */
383
+ async getPreparedCodes(compareConnKey: MigrationTarget): Promise<GenMigrationCode[]> {
384
+ const status = await this.getConnectionStatus(compareConnKey);
385
+ if (status.status !== 0 || status.error !== undefined) {
386
+ throw new Error(
387
+ `Migration comparison requires an up-to-date database: ${String(compareConnKey)}`,
388
+ );
389
+ }
260
390
 
261
- const preparedCodes: GenMigrationCode[] = await (async () => {
262
- const status0conn = statuses.find((status) => status.status === 0);
263
- if (status0conn === undefined) {
264
- console.warn(
265
- chalk.yellow(
266
- `While trying to prepare migration codes, we found that there is no database to compare migrations. We need at least one database where every migration is applied(status === 0). You might want to apply your existing migrations to one of the databases.`,
267
- ),
268
- );
269
- return [];
270
- }
391
+ return this.compareWithConnection(compareConnKey);
392
+ }
271
393
 
272
- const compareDBconn = createKnexInstance(Sonamu.dbConfig[status0conn.connKey]);
273
- try {
274
- return await this.compareMigrations(compareDBconn);
275
- } finally {
276
- await compareDBconn.destroy();
277
- }
278
- })();
394
+ private async compareWithConnection(compareConnKey: MigrationTarget) {
395
+ const compareDB = createKnexInstance(Sonamu.dbConfig[compareConnKey]);
396
+ try {
397
+ return await this.compareMigrations(compareDB);
398
+ } finally {
399
+ await compareDB.destroy();
400
+ }
401
+ }
402
+
403
+ /**
404
+ * @deprecated 신규 UI에서는 커넥션별 조회 API를 사용합니다.
405
+ * 기존 CLI와 스크립트 호환을 위해 전체 상태를 조립합니다.
406
+ * @category 호환용 마이그레이션 API
407
+ */
408
+ async getStatus(): Promise<MigrationStatus> {
409
+ const [codes, connections] = await Promise.all([
410
+ this.getMigrationCodes(),
411
+ Promise.resolve(this.getConnections()),
412
+ ]);
413
+ Naite.t("migrator:getStatus:codes", codes);
279
414
 
415
+ const connectionStatuses = await Promise.all(
416
+ connections.map(({ connKey }) => this.getConnectionStatus(connKey)),
417
+ );
418
+ const conns = connections.map((connection, index) => {
419
+ const status = connectionStatuses[index];
420
+ assert(status !== undefined);
421
+ const configured =
422
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig[
423
+ connection.connKey
424
+ ].connection as Knex.PgConnectionConfig;
425
+ return {
426
+ name: connection.name,
427
+ connKey: connection.connKey,
428
+ connString:
429
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ `pg://${configured.user ?? ""}@${configured.host}:${configured.port}/${configured.database}` as ConnString,
430
+ currentVersion: status.currentVersion,
431
+ status: status.status,
432
+ pending: status.pending,
433
+ };
434
+ });
435
+ Naite.t("migrator:getStatus:conns", conns);
436
+
437
+ const compareConn = connectionStatuses.find(
438
+ ({ status, error }) => status === 0 && error === undefined,
439
+ );
440
+ const preparedCodes =
441
+ compareConn === undefined ? [] : await this.compareWithConnection(compareConn.connKey);
280
442
  Naite.t("migrator:getStatus:preparedCodes", preparedCodes);
281
443
 
282
444
  return {
283
- conns: statuses,
445
+ conns,
284
446
  codes,
285
447
  preparedCodes,
286
- error: migrationStatusError,
448
+ error: connectionStatuses.find(({ error }) => error !== undefined)?.error,
287
449
  };
288
450
  }
289
451
 
290
452
  /**
291
- * 마이그레이션을 적용하거나 롤백합니다.
292
- * Sonamu UI에서 마이그레이션 작업을 수행할 때 사용됩니다.
453
+ * 대상 DB에 pending 마이그레이션을 적용합니다.
454
+ * CLI와 Web은 같은 메서드를 사용하며, Web은 `onProgress`를 stream으로 변환합니다.
293
455
  *
294
- * CLI와 Sonamu UI에서 사용됩니다.
456
+ * @category 분리형 마이그레이션 API
457
+ */
458
+ async apply(targets: MigrationTarget[], options?: MigrationRunOptions): Promise<MigrationResult> {
459
+ return this.runActionWithProgress("apply", targets, options);
460
+ }
461
+
462
+ /**
463
+ * 대상 DB에서 가장 최근 migration batch를 롤백합니다.
464
+ * CLI와 Web은 같은 메서드를 사용하며, Web은 `onProgress`를 stream으로 변환합니다.
295
465
  *
296
- * @param action 작업 유형 (apply/rollback)
297
- * @param targets 작업 대상 DB 설정 키 (keyof SonamuDBConfig)
298
- * @returns 작업 결과
466
+ * @category 분리형 마이그레이션 API
467
+ */
468
+ async rollback(
469
+ targets: MigrationTarget[],
470
+ options?: MigrationRunOptions,
471
+ ): Promise<MigrationResult> {
472
+ return this.runActionWithProgress("rollback", targets, options);
473
+ }
474
+
475
+ /**
476
+ * @deprecated action 문자열 대신 {@link apply} 또는 {@link rollback}을 직접 사용합니다.
477
+ * 기존 외부 호출의 하위 호환만을 위해 유지합니다.
478
+ * @category 호환용 마이그레이션 API
299
479
  */
300
480
  async runAction(
301
481
  action: "apply" | "rollback",
302
482
  targets: (keyof SonamuDBConfig)[],
483
+ ): Promise<MigrationResult> {
484
+ return action === "apply" ? this.apply(targets) : this.rollback(targets);
485
+ }
486
+
487
+ private async runActionWithProgress(
488
+ action: "apply" | "rollback",
489
+ targets: MigrationTarget[],
490
+ options?: MigrationRunOptions,
303
491
  ): Promise<MigrationResult> {
304
492
  Naite.t("migrator:runAction:action", action);
305
493
  Naite.t("migrator:runAction:targets", targets);
@@ -320,10 +508,12 @@ export class Migrator {
320
508
  options: Sonamu.dbConfig[target],
321
509
  }))
322
510
  .filter((c) => c.options !== undefined),
323
- ({ options }) =>
324
- `${(options.connection as Knex.PgConnectionConfig).host}:${
325
- (options.connection as Knex.PgConnectionConfig).port ?? 5432
326
- }/${(options.connection as Knex.PgConnectionConfig).database}`,
511
+ ({ options: dbOptions }) =>
512
+ `${/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (dbOptions.connection as Knex.PgConnectionConfig).host}:${
513
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (
514
+ dbOptions.connection as Knex.PgConnectionConfig
515
+ ).port ?? 5432
516
+ }/${/* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ (dbOptions.connection as Knex.PgConnectionConfig).database}`,
327
517
  );
328
518
 
329
519
  // get connections
@@ -335,15 +525,7 @@ export class Migrator {
335
525
  );
336
526
 
337
527
  try {
338
- // action
339
- const result = await (async () => {
340
- switch (action) {
341
- case "apply":
342
- return this.runMigrationsSequentially(conns, "apply");
343
- case "rollback":
344
- return this.runMigrationsSequentially(conns, "rollback");
345
- }
346
- })();
528
+ const result = await this.runMigrationsSequentially(conns, action, options);
347
529
 
348
530
  Naite.t("migrator:runAction:result", result);
349
531
 
@@ -357,65 +539,15 @@ export class Migrator {
357
539
  }
358
540
  }
359
541
 
360
- /**
361
- * 삭제 가능한 마이그레이션 코드 파일을 검증합니다.
362
- *
363
- * @param conns 마이그레이션 상태 배열
364
- * @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
365
- * @returns 삭제 가능 여부 및 적용된 마이그레이션 코드 파일 이름
366
- */
367
- validateDeletable(conns: MigrationStatus["conns"], codeNames: string[]) {
368
- const appliedCodes = codeNames.filter((codeName) =>
369
- conns.some((conn) => !conn.pending.includes(codeName)),
370
- );
371
-
372
- return {
373
- canDelete: appliedCodes.length === 0,
374
- appliedCodes,
375
- };
376
- }
377
-
378
- /**
379
- * 마이그레이션 코드 파일을 삭제합니다.
380
- *
381
- * Sonamu UI에서 사용됩니다.
382
- *
383
- * @param codeNames 삭제할 마이그레이션 코드 파일 이름 배열
384
- * @returns 삭제된 마이그레이션 코드 파일 개수
385
- */
386
- async delCodes(codeNames: string[]): Promise<number> {
387
- const { conns } = await this.getStatus();
388
- const { canDelete, appliedCodes } = this.validateDeletable(conns, codeNames);
389
- if (!canDelete) {
390
- throw new Error(
391
- `You cannot delete a migration file if there is already applied. Applied codes: ${appliedCodes.join(", ")}`,
392
- );
393
- }
394
-
395
- return sum(
396
- await Promise.all(
397
- codeNames.map(async (codeName) => {
398
- const filePath = `${Sonamu.apiRootPath}/src/migrations/${codeName}.ts`;
399
- if (await exists(filePath)) {
400
- await unlink(filePath);
401
- return 1;
402
- }
403
- return 0;
404
- }),
405
- ),
406
- );
407
- }
408
-
409
542
  private genDateTag(index: number, baseDate: Date = new Date()): string {
410
543
  const date = new Date(baseDate.getTime() + index * 1000);
411
- const pad = (num: number, size: number = 2) => num.toString().padStart(size, "0");
412
544
  return (
413
545
  date.getFullYear().toString() +
414
- pad(date.getMonth() + 1) +
415
- pad(date.getDate()) +
416
- pad(date.getHours()) +
417
- pad(date.getMinutes()) +
418
- pad(date.getSeconds())
546
+ padDatePart(date.getMonth() + 1) +
547
+ padDatePart(date.getDate()) +
548
+ padDatePart(date.getHours()) +
549
+ padDatePart(date.getMinutes()) +
550
+ padDatePart(date.getSeconds())
419
551
  );
420
552
  }
421
553
 
@@ -426,8 +558,11 @@ export class Migrator {
426
558
  *
427
559
  * @returns 생성된 마이그레이션 코드 파일 개수
428
560
  */
429
- async generatePreparedCodes(): Promise<number> {
430
- const { preparedCodes } = await this.getStatus();
561
+ async generatePreparedCodes(compareConnKey?: MigrationTarget): Promise<number> {
562
+ const preparedCodes =
563
+ compareConnKey === undefined
564
+ ? (await this.getStatus()).preparedCodes
565
+ : await this.getPreparedCodes(compareConnKey);
431
566
  Naite.t("migrator:generatePreparedCodes:preparedCodes", preparedCodes);
432
567
  if (preparedCodes.length === 0) {
433
568
  console.log(chalk.green("\n현재 모두 싱크된 상태입니다."));
@@ -519,14 +654,15 @@ export class Migrator {
519
654
  }
520
655
 
521
656
  /**
522
- * Shadow DB 테스트를 진행합니다.
657
+ * test DB의 snapshot으로 임시 Shadow DB를 만들고 전체 마이그레이션을 검증합니다.
658
+ * CLI와 Web은 `onProgress` 유무만 달리해 같은 실행 의미를 공유할 수 있습니다.
523
659
  *
524
- * Sonamu UI에서 사용됩니다.
525
- *
526
- * @returns Shadow DB 테스트 결과
660
+ * @category 분리형 마이그레이션 API
527
661
  */
528
- async runShadowTest(): Promise<MigrationResult> {
529
- const baseTestConn = Sonamu.dbConfig.test.connection as Knex.PgConnectionConfig;
662
+ async runShadowTest(options?: MigrationRunOptions): Promise<MigrationResult> {
663
+ const baseTestConn =
664
+ /* SAFETY: Knex와 PostgreSQL 스키마 조회 계약이 이 값의 타입을 보장한다. */ Sonamu.dbConfig
665
+ .test.connection as Knex.PgConnectionConfig;
530
666
  const workerId = process.env.SONAMU_WORKER_DB === "true" ? process.env.VITEST_POOL_ID : null;
531
667
  const templateDatabase =
532
668
  workerId !== null ? `${baseTestConn.database}_${workerId ?? "1"}` : baseTestConn.database;
@@ -570,13 +706,22 @@ export class Migrator {
570
706
 
571
707
  // shadow DB 테스트 진행
572
708
  try {
573
- const [batchNo, applied] = await sdb.migrate.latest();
709
+ const progress = this.createProgressHooks("shadow", "shadow", options);
710
+ const [batchNo, applied] = await sdb.migrate.latest(progress);
574
711
  !isTest() &&
575
712
  console.log(chalk.green("Shadow DB 테스트에 성공했습니다!"), {
576
713
  batchNo,
577
714
  applied,
578
715
  });
579
716
 
717
+ this.emitProgress(options, {
718
+ type: "target-complete",
719
+ action: "shadow",
720
+ connKey: "shadow",
721
+ batchNo,
722
+ files: applied,
723
+ });
724
+
580
725
  return [
581
726
  {
582
727
  connKey: "shadow",
@@ -586,7 +731,10 @@ export class Migrator {
586
731
  ];
587
732
  } catch (e) {
588
733
  console.error(e);
589
- throw new ServiceUnavailableException(SD("sonamu.error.shadowDbTestFailed"));
734
+ throw new MigrationTargetExecutionError(
735
+ "shadow",
736
+ new ServiceUnavailableException(SD("sonamu.error.shadowDbTestFailed")),
737
+ );
590
738
  } finally {
591
739
  await sdb.destroy();
592
740
  }