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
@@ -0,0 +1,4 @@
1
+ /** Fastify가 비동기 실패를 오류 처리기로 전달할 수 있도록 Promise를 반환합니다. */
2
+ export function forwardAsyncErrors<Result>(handler: () => Promise<Result>): Promise<Result> {
3
+ return Promise.resolve().then(handler);
4
+ }
@@ -4,6 +4,7 @@ import os from "os";
4
4
  import path from "path";
5
5
 
6
6
  import { Sonamu } from "../api/sonamu";
7
+ import { isStringValue } from "../utils/runtime-value";
7
8
  import {
8
9
  type CddAcEntry,
9
10
  type CddAcFile,
@@ -17,6 +18,22 @@ import {
17
18
  type CddTreeNode,
18
19
  } from "./cdd-types";
19
20
 
21
+ interface CddTreeResult {
22
+ exists: boolean;
23
+ tree: CddTreeNode[];
24
+ }
25
+ interface EditorCliConfig {
26
+ cli: string;
27
+ waitFlag: string;
28
+ }
29
+ interface EditorCliResolution {
30
+ bin: string;
31
+ args: string[];
32
+ }
33
+ interface CddRulesResult {
34
+ rules: CddRuleSummary[];
35
+ }
36
+
20
37
  export type {
21
38
  CddAcEntry,
22
39
  CddAcFile,
@@ -93,7 +110,7 @@ function scanDirectory(dirPath: string, relativeTo: string): CddTreeNode[] {
93
110
  }
94
111
 
95
112
  /** contract/ 디렉터리의 트리 구조를 반환 */
96
- export function getCddTree(): { exists: boolean; tree: CddTreeNode[] } {
113
+ export function getCddTree(): CddTreeResult {
97
114
  const contractDir = getContractDir();
98
115
  if (!fs.existsSync(contractDir)) {
99
116
  return { exists: false, tree: [] };
@@ -142,17 +159,17 @@ export async function editContent(
142
159
  }
143
160
 
144
161
  /** 에디터별 앱 번들 내 CLI 경로 + --wait 플래그 매핑 */
145
- const EDITOR_CLI_MAP: Record<string, { cli: string; waitFlag: string }> = {
162
+ const EDITOR_CLI_MAP = {
146
163
  "Visual Studio Code": { cli: "Contents/Resources/app/bin/code", waitFlag: "--wait" },
147
164
  Zed: { cli: "Contents/MacOS/cli", waitFlag: "--wait" },
148
165
  Cursor: { cli: "Contents/Resources/app/bin/cursor", waitFlag: "--wait" },
149
- };
166
+ } satisfies Record<string, EditorCliConfig>;
150
167
 
151
168
  /** 앱 번들 CLI 경로를 resolve. wait=false이면 --wait 플래그를 생략 */
152
- function resolveEditorCli(options?: { wait?: boolean }): { bin: string; args: string[] } {
169
+ function resolveEditorCli(options?: { wait?: boolean }): EditorCliResolution {
153
170
  const wait = options?.wait ?? true;
154
171
  const appName = Sonamu.config.externalEditor ?? "Visual Studio Code";
155
- const mapping = EDITOR_CLI_MAP[appName];
172
+ const mapping = Object.entries(EDITOR_CLI_MAP).find(([name]) => name === appName)?.[1];
156
173
  if (!mapping) {
157
174
  throw new Error(
158
175
  `지원되지 않는 에디터입니다: ${appName} (지원: ${Object.keys(EDITOR_CLI_MAP).join(", ")})`,
@@ -218,7 +235,7 @@ export function openSourceFile(filePath: string): void {
218
235
  * ======================================================================== */
219
236
 
220
237
  /** contract/rules/ 디렉터리 내 .rules.json 파일 목록 반환 */
221
- export function listRules(): { rules: CddRuleSummary[] } {
238
+ export function listRules(): CddRulesResult {
222
239
  const contractDir = getContractDir();
223
240
  const rulesDir = path.join(contractDir, "rules");
224
241
  if (!fs.existsSync(rulesDir)) return { rules: [] };
@@ -235,11 +252,13 @@ export function listRules(): { rules: CddRuleSummary[] } {
235
252
 
236
253
  try {
237
254
  const raw = fs.readFileSync(absPath, "utf-8");
238
- const doc = JSON.parse(raw) as { description?: string; rules?: unknown[] };
255
+ const doc = /* SAFETY: UI 도구의 Zod 입력 스키마가 이 값의 타입을 보장한다. */ JSON.parse(
256
+ raw,
257
+ ) as { description?: string; rules?: unknown[] };
239
258
  rules.push({
240
259
  key,
241
260
  path: relPath,
242
- description: typeof doc.description === "string" ? doc.description : "",
261
+ description: isStringValue(doc.description) ? doc.description : "",
243
262
  ruleCount: Array.isArray(doc.rules) ? doc.rules.length : 0,
244
263
  });
245
264
  } catch (err) {
@@ -266,12 +285,14 @@ export function readRule(ruleKey: string): CddRuleDetail {
266
285
  }
267
286
 
268
287
  const raw = fs.readFileSync(absPath, "utf-8");
269
- const doc = JSON.parse(raw) as { description?: string; rules?: CddRuleEntry[] };
288
+ const doc = /* SAFETY: UI 도구의 Zod 입력 스키마가 이 값의 타입을 보장한다. */ JSON.parse(
289
+ raw,
290
+ ) as { description?: string; rules?: CddRuleEntry[] };
270
291
 
271
292
  return {
272
293
  key: ruleKey,
273
294
  path: `rules/${ruleKey}.rules.json`,
274
- description: typeof doc.description === "string" ? doc.description : "",
295
+ description: isStringValue(doc.description) ? doc.description : "",
275
296
  rules: Array.isArray(doc.rules) ? doc.rules : [],
276
297
  };
277
298
  }
@@ -287,7 +308,9 @@ export function addRule(req: CddAddRuleRequest): CddRuleDetail {
287
308
  }
288
309
 
289
310
  const raw = fs.readFileSync(absPath, "utf-8");
290
- const doc = JSON.parse(raw) as { description?: string; rules?: CddRuleEntry[] };
311
+ const doc = /* SAFETY: UI 도구의 Zod 입력 스키마가 이 값의 타입을 보장한다. */ JSON.parse(
312
+ raw,
313
+ ) as { description?: string; rules?: CddRuleEntry[] };
291
314
  const rules: CddRuleEntry[] = Array.isArray(doc.rules) ? doc.rules : [];
292
315
 
293
316
  const nextId = generateNextRuleId(rules);
@@ -308,7 +331,7 @@ export function addRule(req: CddAddRuleRequest): CddRuleDetail {
308
331
  return {
309
332
  key: req.ruleKey,
310
333
  path: `rules/${req.ruleKey}.rules.json`,
311
- description: typeof doc.description === "string" ? doc.description : "",
334
+ description: isStringValue(doc.description) ? doc.description : "",
312
335
  rules,
313
336
  };
314
337
  }
@@ -0,0 +1,381 @@
1
+ import { readFile } from "fs/promises";
2
+ import { PassThrough } from "stream";
3
+
4
+ import { type FastifyInstance, type FastifyReply } from "fastify";
5
+
6
+ import { Sonamu } from "../api/sonamu";
7
+ import { type SonamuDBConfig } from "../database/db";
8
+ import { SD } from "../dict/sd";
9
+ import { BadRequestException } from "../exceptions/so-exceptions";
10
+ import {
11
+ Migrator,
12
+ MigrationTargetExecutionError,
13
+ type MigrationResult,
14
+ } from "../migration/migrator";
15
+ import { SlackConfirm, type SlackConfirmPendingResult } from "../migration/slack-confirm";
16
+ import {
17
+ type MigrationAction,
18
+ type MigrationConnectionStatus,
19
+ type MigrationProgressEvent,
20
+ type MigrationStreamEvent,
21
+ type MigrationTarget,
22
+ } from "../migration/types";
23
+ import { forwardAsyncErrors } from "./async-route";
24
+
25
+ type ApprovalReady = { type: "ready" };
26
+ type ApprovalResult = ApprovalReady | SlackConfirmPendingResult;
27
+ type ApplyBody = { targets: MigrationTarget[]; requestor?: string };
28
+ type LegacyApplyBody = ApplyBody & { force?: boolean; forceReason?: string };
29
+
30
+ const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
31
+
32
+ function assertTargets(migrator: Migrator, targets: MigrationTarget[]): void {
33
+ const allowed = new Set(migrator.getConnections().map(({ connKey }) => connKey));
34
+ const invalid = targets.filter((target) => !allowed.has(target));
35
+ if (targets.length === 0 || invalid.length > 0) {
36
+ throw new BadRequestException(SD("error.badRequest"), {
37
+ message: `Invalid migration targets: ${invalid.length === 0 ? "(empty)" : invalid.join(", ")}`,
38
+ });
39
+ }
40
+ }
41
+
42
+ function targetRequiresApproval(slack: SlackConfirm, target: MigrationTarget): boolean {
43
+ const connection = /* SAFETY: UI 도구의 Zod 입력 스키마가 이 값의 타입을 보장한다. */ Sonamu
44
+ .dbConfig[target]?.connection as { host?: string } | undefined;
45
+ const remote = !LOCAL_HOSTS.has((connection?.host ?? "localhost").toLowerCase());
46
+ return remote && slack.isTargetRequiresApproval(target);
47
+ }
48
+
49
+ async function getAvailableTargetStatuses(
50
+ migrator: Migrator,
51
+ targets: MigrationTarget[],
52
+ ): Promise<MigrationConnectionStatus[]> {
53
+ const statuses = await Promise.all(targets.map((target) => migrator.getConnectionStatus(target)));
54
+ const failed = statuses.filter(({ status, error }) => status === "error" || error !== undefined);
55
+ if (failed.length > 0) {
56
+ throw new BadRequestException(SD("error.badRequest"), {
57
+ message: `Migration status could not be verified: ${failed.map(({ connKey }) => connKey).join(", ")}`,
58
+ });
59
+ }
60
+ return statuses;
61
+ }
62
+
63
+ function getPendingMigrations(statuses: MigrationConnectionStatus[]) {
64
+ return [...new Set(statuses.flatMap(({ pending }) => pending))];
65
+ }
66
+
67
+ async function requestApproval(
68
+ migrator: Migrator,
69
+ targets: MigrationTarget[],
70
+ requestor?: string,
71
+ ): Promise<ApprovalResult> {
72
+ assertTargets(migrator, targets);
73
+ const statuses = await getAvailableTargetStatuses(migrator, targets);
74
+ const slack = new SlackConfirm();
75
+ const requiresApproval = targets.some((target) => targetRequiresApproval(slack, target));
76
+ if (!requiresApproval || !slack.isConfigured()) {
77
+ return { type: "ready" };
78
+ }
79
+
80
+ const pending = getPendingMigrations(statuses);
81
+ if (pending.length === 0) {
82
+ return { type: "ready" };
83
+ }
84
+
85
+ const existing = await slack.getExistingRequest(pending);
86
+ if (existing !== null) {
87
+ const approval = await slack.checkApproval(existing.channel, existing.ts);
88
+ if (approval.approved) {
89
+ return { type: "ready" };
90
+ }
91
+ if (approval.rejected) {
92
+ throw new BadRequestException(SD("sonamu.error.migrationRejected"));
93
+ }
94
+ return { type: "pending", ...existing };
95
+ }
96
+
97
+ const created = await slack.postApprovalRequest(pending, targets, requestor);
98
+ await slack.saveRequest(pending, created.channel, created.ts);
99
+ return { type: "pending", ...created };
100
+ }
101
+
102
+ async function requireApprovedApply(
103
+ body: ApplyBody,
104
+ statuses: MigrationConnectionStatus[],
105
+ ): Promise<{ channel: string; ts: string } | null> {
106
+ const slack = new SlackConfirm();
107
+ const requiresApproval = body.targets.some((target) => targetRequiresApproval(slack, target));
108
+ if (!requiresApproval || !slack.isConfigured()) {
109
+ return null;
110
+ }
111
+
112
+ const pending = getPendingMigrations(statuses);
113
+ if (pending.length === 0) {
114
+ return null;
115
+ }
116
+
117
+ const existing = await slack.getExistingRequest(pending);
118
+ if (existing === null) {
119
+ throw new BadRequestException(SD("error.badRequest"), {
120
+ message: "Slack approval has not been requested",
121
+ });
122
+ }
123
+ const approval = await slack.checkApproval(existing.channel, existing.ts);
124
+ if (approval.rejected) {
125
+ throw new BadRequestException(SD("sonamu.error.migrationRejected"));
126
+ }
127
+ if (!approval.approved) {
128
+ throw new BadRequestException(SD("error.badRequest"), {
129
+ message: "Slack approval is still pending",
130
+ });
131
+ }
132
+ return existing;
133
+ }
134
+
135
+ async function runLegacyApply(
136
+ migrator: Migrator,
137
+ body: LegacyApplyBody,
138
+ ): Promise<MigrationResult | SlackConfirmPendingResult> {
139
+ assertTargets(migrator, body.targets);
140
+ const statuses = await getAvailableTargetStatuses(migrator, body.targets);
141
+ const slack = new SlackConfirm();
142
+ const requiresApproval = body.targets.some((target) => targetRequiresApproval(slack, target));
143
+ if (!requiresApproval || !slack.isConfigured()) {
144
+ return migrator.apply(body.targets);
145
+ }
146
+
147
+ const pending = getPendingMigrations(statuses);
148
+ if (pending.length === 0) {
149
+ return migrator.apply(body.targets);
150
+ }
151
+
152
+ const existing = await slack.getExistingRequest(pending);
153
+ if (existing === null) {
154
+ const created = await slack.postApprovalRequest(pending, body.targets, body.requestor);
155
+ await slack.saveRequest(pending, created.channel, created.ts);
156
+ return { type: "pending", ...created };
157
+ }
158
+
159
+ const approval = await slack.checkApproval(existing.channel, existing.ts);
160
+ if (approval.rejected) {
161
+ throw new BadRequestException(SD("sonamu.error.migrationRejected"));
162
+ }
163
+ if (!approval.approved && !body.force) {
164
+ return { type: "pending", ...existing };
165
+ }
166
+ if (!approval.approved) {
167
+ await slack.forceApproval(
168
+ existing.channel,
169
+ existing.ts,
170
+ body.forceReason ?? "사유 없음",
171
+ body.requestor,
172
+ );
173
+ }
174
+
175
+ const result = await migrator.apply(body.targets);
176
+ if (result.length > 0) {
177
+ await slack.logExecution(existing.channel, existing.ts, result, body.requestor);
178
+ }
179
+ return result;
180
+ }
181
+
182
+ function sendMigrationStream(
183
+ reply: FastifyReply,
184
+ action: MigrationAction,
185
+ targets: MigrationTarget[],
186
+ run: (onProgress: (event: MigrationProgressEvent) => void) => Promise<MigrationResult>,
187
+ afterComplete?: (result: MigrationResult) => Promise<void>,
188
+ ) {
189
+ const stream = new PassThrough();
190
+ let canWrite = true;
191
+ let current: MigrationProgressEvent | undefined;
192
+ const completedTargets: MigrationTarget[] = [];
193
+ const write = (event: MigrationStreamEvent) => {
194
+ if (canWrite && !stream.destroyed) {
195
+ stream.write(`${JSON.stringify(event)}\n`);
196
+ }
197
+ };
198
+
199
+ stream.on("close", () => {
200
+ canWrite = false;
201
+ });
202
+ reply
203
+ .header("content-type", "application/x-ndjson; charset=utf-8")
204
+ .header("cache-control", "no-cache, no-transform")
205
+ .header("content-encoding", "identity")
206
+ .header("x-no-compression", "1");
207
+
208
+ void (async () => {
209
+ try {
210
+ const result = await run((event) => {
211
+ if (event.type === "target-complete") {
212
+ if (event.connKey !== "shadow") completedTargets.push(event.connKey);
213
+ write(event);
214
+ // 다음 target의 lifecycle이 시작되기 전 오류를 직전 target에 잘못 귀속하지 않습니다.
215
+ current = undefined;
216
+ return;
217
+ }
218
+ current = event;
219
+ write(event);
220
+ });
221
+ await afterComplete?.(result);
222
+ write({ type: "complete", result });
223
+ } catch (caught) {
224
+ const message = caught instanceof Error ? caught.message : String(caught);
225
+ const failedTarget =
226
+ caught instanceof MigrationTargetExecutionError ? caught.connKey : current?.connKey;
227
+ write({
228
+ type: "error",
229
+ action,
230
+ message,
231
+ connKey: failedTarget,
232
+ file:
233
+ current?.type === "file-start" || current?.type === "file-executed"
234
+ ? current.file
235
+ : undefined,
236
+ completedTargets,
237
+ pendingTargets: targets.filter((target) => !completedTargets.includes(target)),
238
+ });
239
+ } finally {
240
+ if (!stream.destroyed) {
241
+ stream.end();
242
+ }
243
+ }
244
+ })();
245
+
246
+ return reply.send(stream);
247
+ }
248
+
249
+ export async function registerMigrationsApi(server: FastifyInstance) {
250
+ const migrator = new Migrator();
251
+
252
+ server.get("/api/migrations/connections", async () => ({
253
+ connections: migrator.getConnections(),
254
+ }));
255
+ server.get("/api/migrations/codes", async () => ({ codes: await migrator.getMigrationCodes() }));
256
+ server.get<{ Querystring: { codeName: string } }>("/api/migrations/code", (request) =>
257
+ forwardAsyncErrors(async () => {
258
+ const migrationCode = (await migrator.getMigrationCodes()).find(
259
+ ({ name }) => name === request.query.codeName,
260
+ );
261
+ if (migrationCode === undefined) {
262
+ throw new BadRequestException(SD("error.badRequest"));
263
+ }
264
+ return { code: await readFile(migrationCode.path, "utf8") };
265
+ }),
266
+ );
267
+ server.get<{ Querystring: { connKey?: MigrationTarget } }>("/api/migrations/status", (request) =>
268
+ forwardAsyncErrors(async () => {
269
+ if (request.query.connKey === undefined) {
270
+ return { status: await migrator.getStatus() };
271
+ }
272
+ return { status: await migrator.getConnectionStatus(request.query.connKey) };
273
+ }),
274
+ );
275
+ server.get<{ Querystring: { compareConnKey: MigrationTarget } }>(
276
+ "/api/migrations/prepared-codes",
277
+ (request) =>
278
+ forwardAsyncErrors(async () => ({
279
+ preparedCodes: await migrator.getPreparedCodes(request.query.compareConnKey),
280
+ })),
281
+ );
282
+
283
+ server.post<{ Body: ApplyBody }>("/api/migrations/request-approval", (request) =>
284
+ forwardAsyncErrors(async () => {
285
+ return requestApproval(migrator, request.body.targets, request.body.requestor);
286
+ }),
287
+ );
288
+ server.post<{ Body: { channel: string; ts: string } }>(
289
+ "/api/migrations/checkApproval",
290
+ (request) =>
291
+ forwardAsyncErrors(async () => {
292
+ const slack = new SlackConfirm();
293
+ return slack.isConfigured()
294
+ ? slack.checkApproval(request.body.channel, request.body.ts)
295
+ : { approved: true, rejected: false };
296
+ }),
297
+ );
298
+ server.post<{
299
+ Body: { channel: string; ts: string; reason: string; requestor?: string };
300
+ }>("/api/migrations/forceApproval", (request) =>
301
+ forwardAsyncErrors(async () => {
302
+ const slack = new SlackConfirm();
303
+ if (!slack.isConfigured()) {
304
+ throw new BadRequestException(SD("sonamu.error.slackConfirmNotConfigured"));
305
+ }
306
+ await slack.forceApproval(
307
+ request.body.channel,
308
+ request.body.ts,
309
+ request.body.reason,
310
+ request.body.requestor,
311
+ );
312
+ return { success: true };
313
+ }),
314
+ );
315
+
316
+ server.post("/api/migrations/shadow", async (_request, reply) => {
317
+ return sendMigrationStream(reply, "shadow", [], (onProgress) => {
318
+ return migrator.runShadowTest({ onProgress });
319
+ });
320
+ });
321
+ server.post<{ Body: ApplyBody }>("/api/migrations/apply", async (request, reply) => {
322
+ assertTargets(migrator, request.body.targets);
323
+ const statuses = await getAvailableTargetStatuses(migrator, request.body.targets);
324
+ const approval = await requireApprovedApply(request.body, statuses);
325
+ return sendMigrationStream(
326
+ reply,
327
+ "apply",
328
+ request.body.targets,
329
+ (onProgress) => migrator.apply(request.body.targets, { onProgress }),
330
+ approval === null
331
+ ? undefined
332
+ : async (result) => {
333
+ await new SlackConfirm().logExecution(
334
+ approval.channel,
335
+ approval.ts,
336
+ result,
337
+ request.body.requestor,
338
+ );
339
+ },
340
+ );
341
+ });
342
+ server.post<{ Body: { targets: MigrationTarget[] } }>(
343
+ "/api/migrations/rollback",
344
+ async (request, reply) => {
345
+ assertTargets(migrator, request.body.targets);
346
+ await getAvailableTargetStatuses(migrator, request.body.targets);
347
+ return sendMigrationStream(reply, "rollback", request.body.targets, (onProgress) => {
348
+ return migrator.rollback(request.body.targets, { onProgress });
349
+ });
350
+ },
351
+ );
352
+
353
+ server.post<{ Body: { compareConnKey?: MigrationTarget } }>(
354
+ "/api/migrations/generatePreparedCodes",
355
+ (request) =>
356
+ forwardAsyncErrors(async () => migrator.generatePreparedCodes(request.body.compareConnKey)),
357
+ );
358
+
359
+ // 이전 UI 번들과 외부 스크립트가 새 서버에서도 한 릴리스 동안 동작하도록 유지합니다.
360
+ server.post<{
361
+ Body: {
362
+ action: "apply" | "rollback" | "shadow";
363
+ targets: (keyof SonamuDBConfig)[];
364
+ force?: boolean;
365
+ forceReason?: string;
366
+ requestor?: string;
367
+ };
368
+ }>("/api/migrations/runAction", (request) =>
369
+ forwardAsyncErrors(async () => {
370
+ if (request.body.action === "shadow") {
371
+ return migrator.runShadowTest();
372
+ }
373
+ if (request.body.action === "apply") {
374
+ return runLegacyApply(migrator, request.body);
375
+ }
376
+ assertTargets(migrator, request.body.targets);
377
+ await getAvailableTargetStatuses(migrator, request.body.targets);
378
+ return migrator.rollback(request.body.targets);
379
+ }),
380
+ );
381
+ }
@@ -10,8 +10,9 @@ import { format, type FormatConfig } from "oxfmt";
10
10
  import { cached } from "./async-utils";
11
11
  import { isTest } from "./controller";
12
12
  import { execute } from "./process-utils";
13
+ import { isNumberValue } from "./runtime-value";
13
14
 
14
- const _require = createRequire(import.meta.url);
15
+ const requireFromHere = createRequire(import.meta.url);
15
16
 
16
17
  /**
17
18
  * 코드를 프로젝트의 oxfmt + oxlint 설정에 맞춰 포매팅한 문자열을 반환합니다.
@@ -75,9 +76,11 @@ async function loadOxfmtConfig(): Promise<FormatConfig> {
75
76
  while (true) {
76
77
  const candidate = join(dir, ".oxfmtrc.json");
77
78
  try {
79
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
78
80
  cachedOxfmtConfig = JSON.parse(await readFile(candidate, "utf-8")) as FormatConfig;
79
81
  return cachedOxfmtConfig;
80
82
  } catch (e) {
83
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
81
84
  if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
82
85
  !isTest() && console.error(`Failed to load ${candidate}:`, e);
83
86
  break;
@@ -121,7 +124,8 @@ async function runOxlint(code: string): Promise<string> {
121
124
  });
122
125
  } catch (e) {
123
126
  // lint 위반 시 exit code != 0이지만 --fix는 적용됨. exec 자체 실패만 throw.
124
- if (typeof (e as Error & { code?: number }).code !== "number") {
127
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
128
+ if (!isNumberValue((e as Error & { code?: number }).code)) {
125
129
  throw e;
126
130
  }
127
131
  }
@@ -138,7 +142,7 @@ async function runOxlint(code: string): Promise<string> {
138
142
 
139
143
  function resolveOxlintBin(): string {
140
144
  try {
141
- return _require.resolve("oxlint/bin/oxlint");
145
+ return requireFromHere.resolve("oxlint/bin/oxlint");
142
146
  } catch {
143
147
  return "oxlint";
144
148
  }
@@ -8,12 +8,12 @@ import { formatCode } from "./formatter";
8
8
  /**
9
9
  * fs/promises에는 exists가 없어요. 대신 access가 있습니다.
10
10
  * 근데 얘는 인터페이스가 쓰기 불편해요. 그래서 감싸주었습니다.
11
- * @param path
11
+ * @param targetPath
12
12
  * @returns
13
13
  */
14
- export async function exists(path: PathLike): Promise<boolean> {
14
+ export async function exists(targetPath: PathLike): Promise<boolean> {
15
15
  try {
16
- await access(path, constants.F_OK);
16
+ await access(targetPath, constants.F_OK);
17
17
  return true;
18
18
  } catch {
19
19
  return false;
@@ -21,9 +21,9 @@ export async function exists(path: PathLike): Promise<boolean> {
21
21
  }
22
22
 
23
23
  // 디렉토리가 아니라 파일만 존재하는지 확인합니다.
24
- export async function fileExists(path: PathLike): Promise<boolean> {
24
+ export async function fileExists(targetPath: PathLike): Promise<boolean> {
25
25
  try {
26
- const stats = await stat(path);
26
+ const stats = await stat(targetPath);
27
27
  return stats.isFile();
28
28
  } catch {
29
29
  return false;
@@ -1,9 +1,7 @@
1
- import { type SonamuQueryMode } from "../types/types";
1
+ import { type SonamuQueryMode, type SonamuSemanticParams } from "../types/types";
2
2
 
3
3
  // semanticQuery가 있으면 similarity를 추가하는 조건부 타입
4
- type WithSimilarity<LP, T> = LP extends { semanticQuery: Record<string, unknown> }
5
- ? T & { similarity: number }
6
- : T;
4
+ type WithSimilarity<LP, T> = "semanticQuery" extends keyof LP ? T & { similarity: number } : T;
7
5
 
8
6
  export type ListResult<
9
7
  LP extends { queryMode?: SonamuQueryMode },
@@ -20,6 +18,7 @@ export function asArray<T>(param: T | T[]): T[] {
20
18
  if (Array.isArray(param)) {
21
19
  return param;
22
20
  } else {
21
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
23
22
  return [param] as T[];
24
23
  }
25
24
  }
@@ -39,5 +38,5 @@ export interface BaseListParams {
39
38
  page?: number;
40
39
  keyword?: string;
41
40
  queryMode?: "list" | "count" | "both";
42
- semanticQuery?: Record<string, unknown>;
41
+ semanticQuery?: SonamuSemanticParams["semanticQuery"];
43
42
  }