sonamu 0.10.12 → 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 (513) 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 +131 -27
  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 +12 -6
  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 +3 -2
  142. package/dist/migration/migrator.d.ts.map +1 -1
  143. package/dist/migration/migrator.js +27 -21
  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/naite/naite-reporter.js +2 -2
  149. package/dist/naite/naite.d.ts +74 -15
  150. package/dist/naite/naite.d.ts.map +1 -1
  151. package/dist/naite/naite.js +5 -19
  152. package/dist/runtime-type.d.ts +6 -0
  153. package/dist/runtime-type.d.ts.map +1 -0
  154. package/dist/runtime-type.js +26 -0
  155. package/dist/ssr/hmr-opt-out.d.ts +26 -0
  156. package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
  157. package/dist/ssr/hmr-opt-out.js +59 -0
  158. package/dist/ssr/renderer.d.ts.map +1 -1
  159. package/dist/ssr/renderer.js +4 -1
  160. package/dist/storage/base-file.js +14 -14
  161. package/dist/storage/buffered-file.d.ts +2 -2
  162. package/dist/storage/buffered-file.d.ts.map +1 -1
  163. package/dist/storage/buffered-file.js +12 -12
  164. package/dist/storage/s3-driver.d.ts.map +1 -1
  165. package/dist/storage/s3-driver.js +1 -1
  166. package/dist/storage/uploaded-file.d.ts +2 -2
  167. package/dist/storage/uploaded-file.d.ts.map +1 -1
  168. package/dist/storage/uploaded-file.js +8 -8
  169. package/dist/stream/sse.js +11 -11
  170. package/dist/stream/ws-core.d.ts +1 -1
  171. package/dist/stream/ws-core.d.ts.map +1 -1
  172. package/dist/stream/ws-delivery.d.ts +1 -1
  173. package/dist/stream/ws-delivery.d.ts.map +1 -1
  174. package/dist/stream/ws-delivery.js +6 -4
  175. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  176. package/dist/stream/ws-local-connection-store.js +4 -2
  177. package/dist/stream/ws-presence-store.js +1 -1
  178. package/dist/stream/ws-registry.d.ts +4 -4
  179. package/dist/stream/ws-registry.d.ts.map +1 -1
  180. package/dist/stream/ws-registry.js +4 -2
  181. package/dist/stream/ws-telemetry.d.ts +16 -12
  182. package/dist/stream/ws-telemetry.d.ts.map +1 -1
  183. package/dist/stream/ws-telemetry.js +22 -18
  184. package/dist/stream/ws.d.ts +18 -12
  185. package/dist/stream/ws.d.ts.map +1 -1
  186. package/dist/stream/ws.js +26 -22
  187. package/dist/syncer/api-parser.js +1 -1
  188. package/dist/syncer/checksum.d.ts.map +1 -1
  189. package/dist/syncer/checksum.js +7 -8
  190. package/dist/syncer/code-generator.d.ts.map +1 -1
  191. package/dist/syncer/code-generator.js +13 -14
  192. package/dist/syncer/entity-operations.d.ts.map +1 -1
  193. package/dist/syncer/entity-operations.js +5 -6
  194. package/dist/syncer/file-copy.d.ts +5 -0
  195. package/dist/syncer/file-copy.d.ts.map +1 -0
  196. package/dist/syncer/file-copy.js +36 -0
  197. package/dist/syncer/file-patterns.d.ts +1 -0
  198. package/dist/syncer/file-patterns.d.ts.map +1 -1
  199. package/dist/syncer/file-patterns.js +5 -2
  200. package/dist/syncer/file-tracking.d.ts.map +1 -1
  201. package/dist/syncer/file-tracking.js +5 -4
  202. package/dist/syncer/filesystem-dependencies.d.ts +15 -0
  203. package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
  204. package/dist/syncer/filesystem-dependencies.js +53 -0
  205. package/dist/syncer/module-loader.d.ts +1 -1
  206. package/dist/syncer/module-loader.d.ts.map +1 -1
  207. package/dist/syncer/module-loader.js +6 -4
  208. package/dist/syncer/syncer-actions.d.ts +4 -0
  209. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  210. package/dist/syncer/syncer-actions.js +34 -17
  211. package/dist/syncer/syncer.d.ts +17 -6
  212. package/dist/syncer/syncer.d.ts.map +1 -1
  213. package/dist/syncer/syncer.js +71 -29
  214. package/dist/syncer/watcher.d.ts.map +1 -1
  215. package/dist/syncer/watcher.js +3 -3
  216. package/dist/tasks/decorator.d.ts +6 -6
  217. package/dist/tasks/decorator.d.ts.map +1 -1
  218. package/dist/tasks/decorator.js +4 -4
  219. package/dist/tasks/step-wrapper.d.ts +6 -4
  220. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  221. package/dist/tasks/step-wrapper.js +5 -5
  222. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  223. package/dist/tasks/workflow-manager.js +13 -7
  224. package/dist/template/entity-converter.d.ts +1 -1
  225. package/dist/template/entity-converter.d.ts.map +1 -1
  226. package/dist/template/entity-converter.js +2 -2
  227. package/dist/template/helpers.d.ts +8 -6
  228. package/dist/template/helpers.d.ts.map +1 -1
  229. package/dist/template/helpers.js +1 -1
  230. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  231. package/dist/template/implementations/generated.template.js +4 -4
  232. package/dist/template/implementations/generated_http.template.d.ts +7 -4
  233. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  234. package/dist/template/implementations/generated_http.template.js +21 -21
  235. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  236. package/dist/template/implementations/generated_sso.template.js +6 -3
  237. package/dist/template/implementations/http_validators.template.d.ts +18 -0
  238. package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
  239. package/dist/template/implementations/http_validators.template.js +127 -0
  240. package/dist/template/implementations/model.template.d.ts.map +1 -1
  241. package/dist/template/implementations/model.template.js +1 -1
  242. package/dist/template/implementations/queries.template.js +1 -1
  243. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  244. package/dist/template/implementations/sd.template.js +79 -39
  245. package/dist/template/implementations/services.template.d.ts.map +1 -1
  246. package/dist/template/implementations/services.template.js +6 -4
  247. package/dist/template/implementations/view_form.template.d.ts +6 -1
  248. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  249. package/dist/template/implementations/view_form.template.js +23 -15
  250. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  251. package/dist/template/implementations/view_list.template.js +11 -6
  252. package/dist/template/template-manager.d.ts.map +1 -1
  253. package/dist/template/template-manager.js +9 -7
  254. package/dist/template/template-types.d.ts +12 -5
  255. package/dist/template/template-types.d.ts.map +1 -1
  256. package/dist/template/template-types.js +1 -1
  257. package/dist/template/template.d.ts +2 -2
  258. package/dist/template/template.d.ts.map +1 -1
  259. package/dist/template/template.js +6 -4
  260. package/dist/template/zod-converter.d.ts +0 -19
  261. package/dist/template/zod-converter.d.ts.map +1 -1
  262. package/dist/template/zod-converter.js +100 -84
  263. package/dist/testing/_relation-graph.d.ts.map +1 -1
  264. package/dist/testing/_relation-graph.js +2 -1
  265. package/dist/testing/bootstrap.d.ts.map +1 -1
  266. package/dist/testing/bootstrap.js +9 -6
  267. package/dist/testing/data-explorer.d.ts +32 -7
  268. package/dist/testing/data-explorer.d.ts.map +1 -1
  269. package/dist/testing/data-explorer.js +77 -31
  270. package/dist/testing/dev-test-routes.d.ts +8 -1
  271. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  272. package/dist/testing/dev-test-routes.js +125 -108
  273. package/dist/testing/dev-vitest-manager.d.ts +54 -8
  274. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  275. package/dist/testing/dev-vitest-manager.js +24 -9
  276. package/dist/testing/faker-mappings.d.ts +412 -1
  277. package/dist/testing/faker-mappings.d.ts.map +1 -1
  278. package/dist/testing/faker-mappings.js +1 -1
  279. package/dist/testing/filesystem.d.ts +2 -0
  280. package/dist/testing/filesystem.d.ts.map +1 -0
  281. package/dist/testing/filesystem.js +7 -0
  282. package/dist/testing/fixture-generator.d.ts +25 -8
  283. package/dist/testing/fixture-generator.d.ts.map +1 -1
  284. package/dist/testing/fixture-generator.js +132 -73
  285. package/dist/testing/fixture-loader.d.ts +3 -1
  286. package/dist/testing/fixture-loader.d.ts.map +1 -1
  287. package/dist/testing/fixture-loader.js +1 -20
  288. package/dist/testing/fixture-manager.d.ts +8 -7
  289. package/dist/testing/fixture-manager.d.ts.map +1 -1
  290. package/dist/testing/fixture-manager.js +91 -59
  291. package/dist/testing/global-setup.d.ts.map +1 -1
  292. package/dist/testing/global-setup.js +5 -2
  293. package/dist/testing/index.d.ts +2 -0
  294. package/dist/testing/index.d.ts.map +1 -1
  295. package/dist/testing/index.js +6 -3
  296. package/dist/types/types.d.ts +101 -99
  297. package/dist/types/types.d.ts.map +1 -1
  298. package/dist/types/types.js +234 -225
  299. package/dist/ui/ai-api.d.ts.map +1 -1
  300. package/dist/ui/ai-api.js +1 -1
  301. package/dist/ui/ai-client.d.ts.map +1 -1
  302. package/dist/ui/ai-client.js +28 -18
  303. package/dist/ui/api.d.ts.map +1 -1
  304. package/dist/ui/api.js +135 -128
  305. package/dist/ui/async-route.d.ts +3 -0
  306. package/dist/ui/async-route.d.ts.map +1 -0
  307. package/dist/ui/async-route.js +12 -0
  308. package/dist/ui/cdd-service.d.ts +9 -7
  309. package/dist/ui/cdd-service.d.ts.map +1 -1
  310. package/dist/ui/cdd-service.js +7 -5
  311. package/dist/ui/migrations-api.d.ts.map +1 -1
  312. package/dist/ui/migrations-api.js +17 -15
  313. package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
  314. package/dist/ui-web/index.html +1 -1
  315. package/dist/utils/formatter.d.ts.map +1 -1
  316. package/dist/utils/formatter.js +7 -5
  317. package/dist/utils/fs-utils.d.ts +3 -3
  318. package/dist/utils/fs-utils.d.ts.map +1 -1
  319. package/dist/utils/fs-utils.js +6 -6
  320. package/dist/utils/model.d.ts +3 -5
  321. package/dist/utils/model.d.ts.map +1 -1
  322. package/dist/utils/model.js +1 -1
  323. package/dist/utils/object-utils.d.ts +1 -1
  324. package/dist/utils/object-utils.d.ts.map +1 -1
  325. package/dist/utils/object-utils.js +17 -16
  326. package/dist/utils/process-utils.d.ts.map +1 -1
  327. package/dist/utils/process-utils.js +4 -2
  328. package/dist/utils/runtime-value.d.ts +13 -0
  329. package/dist/utils/runtime-value.d.ts.map +1 -0
  330. package/dist/utils/runtime-value.js +40 -0
  331. package/dist/utils/sql-parser.d.ts +8 -1
  332. package/dist/utils/sql-parser.d.ts.map +1 -1
  333. package/dist/utils/sql-parser.js +6 -3
  334. package/dist/utils/type-utils.d.ts.map +1 -1
  335. package/dist/utils/type-utils.js +10 -8
  336. package/dist/utils/utils.d.ts +2 -2
  337. package/dist/utils/utils.d.ts.map +1 -1
  338. package/dist/utils/utils.js +11 -8
  339. package/dist/utils/zod-error.d.ts.map +1 -1
  340. package/dist/utils/zod-error.js +7 -4
  341. package/dist/vector/chunking.d.ts.map +1 -1
  342. package/dist/vector/chunking.js +1 -1
  343. package/dist/vector/embedding.d.ts.map +1 -1
  344. package/dist/vector/embedding.js +1 -1
  345. package/dist/vector/types.d.ts +3 -2
  346. package/dist/vector/types.d.ts.map +1 -1
  347. package/package.json +5 -3
  348. package/src/ai/agents/agent.ts +5 -5
  349. package/src/ai/agents/types.ts +1 -1
  350. package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
  351. package/src/ai/providers/rtzr/error.ts +5 -3
  352. package/src/ai/providers/rtzr/model.ts +3 -2
  353. package/src/ai/providers/rtzr/options.ts +10 -5
  354. package/src/ai/providers/rtzr/provider.ts +10 -10
  355. package/src/ai/providers/rtzr/utils.ts +3 -1
  356. package/src/api/__tests__/config.test.ts +11 -21
  357. package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
  358. package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
  359. package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
  360. package/src/api/caster.ts +6 -8
  361. package/src/api/code-converters.ts +136 -36
  362. package/src/api/config.ts +62 -4
  363. package/src/api/context.ts +3 -3
  364. package/src/api/decorators.ts +28 -17
  365. package/src/api/http-validator.ts +149 -0
  366. package/src/api/sonamu.ts +459 -152
  367. package/src/api/websocket-helpers.ts +24 -17
  368. package/src/auth/audit-log/builders.ts +3 -2
  369. package/src/auth/audit-log/client-ip.ts +4 -2
  370. package/src/auth/audit-log/events.ts +15 -1
  371. package/src/auth/audit-log/plugin.ts +129 -200
  372. package/src/auth/audit-log-ingestor.ts +19 -15
  373. package/src/auth/auth-generator.ts +10 -5
  374. package/src/auth/better-auth-request.ts +4 -3
  375. package/src/auth/knex-adapter.ts +8 -2
  376. package/src/auth/plugins/entity-definitions/index.ts +6 -5
  377. package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
  378. package/src/bin/__tests__/test-command.test.ts +9 -15
  379. package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
  380. package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
  381. package/src/bin/build-config.ts +167 -0
  382. package/src/bin/cli.ts +153 -36
  383. package/src/bin/compiler-policy.ts +35 -0
  384. package/src/bin/fixture.ts +14 -3
  385. package/src/bin/test-command.ts +74 -29
  386. package/src/bin/ts-loader-registration.ts +22 -4
  387. package/src/cache/decorator.ts +10 -3
  388. package/src/compress/compress.ts +17 -4
  389. package/src/cone/cone-generator.ts +34 -6
  390. package/src/database/__tests__/db.test.ts +1 -0
  391. package/src/database/__tests__/puri.test.ts +16 -15
  392. package/src/database/__tests__/transaction-scope.test.ts +5 -0
  393. package/src/database/_batch_update.ts +1 -0
  394. package/src/database/base-model.ts +81 -50
  395. package/src/database/base-model.types.ts +6 -6
  396. package/src/database/db.ts +29 -19
  397. package/src/database/knex.ts +30 -17
  398. package/src/database/puri-subset.test-d.ts +17 -0
  399. package/src/database/puri.ts +181 -143
  400. package/src/database/puri.types.test-d.ts +31 -1
  401. package/src/database/puri.types.ts +23 -21
  402. package/src/database/upsert-builder.ts +75 -53
  403. package/src/dict/sd.ts +15 -5
  404. package/src/dict/sonamu-dictionary.ts +6 -6
  405. package/src/dict/utils.ts +9 -1
  406. package/src/entity/entity-manager.ts +26 -9
  407. package/src/entity/entity-template-cone.ts +5 -10
  408. package/src/entity/entity.ts +50 -34
  409. package/src/env.ts +7 -3
  410. package/src/exceptions/error-handler.ts +17 -2
  411. package/src/exceptions/so-exceptions.ts +26 -20
  412. package/src/filter/utils.ts +21 -11
  413. package/src/index.ts +3 -0
  414. package/src/logger/configure.ts +26 -18
  415. package/src/migration/__tests__/migrator.test.ts +50 -57
  416. package/src/migration/code-generation.ts +132 -104
  417. package/src/migration/index-where-predicate.ts +51 -24
  418. package/src/migration/migration-set.ts +11 -5
  419. package/src/migration/migrator.ts +68 -41
  420. package/src/migration/postgresql-schema-reader.ts +52 -47
  421. package/src/migration/slack-confirm.ts +17 -15
  422. package/src/naite/naite-reporter.ts +1 -1
  423. package/src/naite/naite.ts +97 -24
  424. package/src/runtime-type.ts +34 -0
  425. package/src/ssr/hmr-opt-out.ts +78 -0
  426. package/src/ssr/renderer.ts +6 -0
  427. package/src/storage/base-file.ts +13 -13
  428. package/src/storage/buffered-file.ts +11 -11
  429. package/src/storage/s3-driver.ts +4 -1
  430. package/src/storage/uploaded-file.ts +8 -8
  431. package/src/stream/__tests__/ws-contracts.test.ts +4 -2
  432. package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
  433. package/src/stream/__tests__/ws.test.ts +30 -6
  434. package/src/stream/sse.ts +12 -11
  435. package/src/stream/ws-core.ts +1 -1
  436. package/src/stream/ws-delivery.ts +16 -7
  437. package/src/stream/ws-local-connection-store.ts +2 -3
  438. package/src/stream/ws-presence-store.ts +6 -1
  439. package/src/stream/ws-registry.ts +16 -7
  440. package/src/stream/ws-telemetry.ts +66 -43
  441. package/src/stream/ws.ts +73 -39
  442. package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
  443. package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
  444. package/src/syncer/api-parser.ts +67 -27
  445. package/src/syncer/checksum.ts +17 -11
  446. package/src/syncer/code-generator.ts +25 -17
  447. package/src/syncer/entity-operations.ts +3 -5
  448. package/src/syncer/file-copy.ts +46 -0
  449. package/src/syncer/file-patterns.ts +11 -8
  450. package/src/syncer/file-tracking.ts +3 -4
  451. package/src/syncer/filesystem-dependencies.ts +73 -0
  452. package/src/syncer/module-loader.ts +8 -4
  453. package/src/syncer/syncer-actions.ts +56 -18
  454. package/src/syncer/syncer.ts +116 -43
  455. package/src/syncer/watcher.ts +8 -4
  456. package/src/tasks/decorator.ts +20 -12
  457. package/src/tasks/step-wrapper.ts +19 -9
  458. package/src/tasks/workflow-manager.ts +15 -9
  459. package/src/template/__tests__/http_validators.template.test.ts +160 -0
  460. package/src/template/__tests__/services.template.mutation.test.ts +24 -23
  461. package/src/template/__tests__/services.template.websocket.test.ts +21 -25
  462. package/src/template/entity-converter.ts +5 -2
  463. package/src/template/helpers.ts +8 -9
  464. package/src/template/implementations/generated.template.ts +5 -4
  465. package/src/template/implementations/generated_http.template.ts +61 -15
  466. package/src/template/implementations/generated_sso.template.ts +15 -4
  467. package/src/template/implementations/http_validators.template.ts +175 -0
  468. package/src/template/implementations/model.template.ts +3 -1
  469. package/src/template/implementations/sd.template.ts +81 -41
  470. package/src/template/implementations/services.template.ts +2 -2
  471. package/src/template/implementations/view_form.template.ts +56 -39
  472. package/src/template/implementations/view_list.template.ts +31 -23
  473. package/src/template/template-manager.ts +15 -13
  474. package/src/template/template-types.ts +13 -5
  475. package/src/template/template.ts +4 -3
  476. package/src/template/zod-converter.ts +206 -91
  477. package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
  478. package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
  479. package/src/testing/_relation-graph.ts +4 -1
  480. package/src/testing/bootstrap.ts +12 -5
  481. package/src/testing/data-explorer.ts +102 -53
  482. package/src/testing/dev-test-routes.ts +87 -50
  483. package/src/testing/dev-vitest-manager.ts +88 -32
  484. package/src/testing/faker-mappings.ts +2 -2
  485. package/src/testing/filesystem.ts +1 -0
  486. package/src/testing/fixture-generator.ts +191 -131
  487. package/src/testing/fixture-loader.ts +5 -1
  488. package/src/testing/fixture-manager.ts +129 -90
  489. package/src/testing/global-setup.ts +7 -3
  490. package/src/testing/index.ts +5 -0
  491. package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
  492. package/src/types/types.ts +184 -158
  493. package/src/ui/ai-api.ts +9 -7
  494. package/src/ui/ai-client.ts +61 -56
  495. package/src/ui/api.ts +727 -616
  496. package/src/ui/async-route.ts +4 -0
  497. package/src/ui/cdd-service.ts +35 -12
  498. package/src/ui/migrations-api.ts +63 -51
  499. package/src/utils/formatter.ts +7 -3
  500. package/src/utils/fs-utils.ts +5 -5
  501. package/src/utils/model.ts +4 -5
  502. package/src/utils/object-utils.ts +32 -22
  503. package/src/utils/process-utils.ts +4 -1
  504. package/src/utils/runtime-value.ts +43 -0
  505. package/src/utils/sql-parser.ts +13 -6
  506. package/src/utils/type-utils.ts +9 -7
  507. package/src/utils/utils.ts +19 -21
  508. package/src/utils/zod-error.ts +4 -2
  509. package/src/vector/chunking.ts +6 -1
  510. package/src/vector/embedding.ts +2 -1
  511. package/src/vector/types.ts +4 -2
  512. package/tsdown.api.config.ts +5 -2
  513. package/dist/ui-web/assets/index-C0m1pk6K.js +0 -177
package/src/stream/ws.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { type EventEmitter } from "node:events";
2
3
  import { hostname } from "node:os";
3
4
 
4
- import { type WebSocket } from "ws";
5
+ import { type RawData, type WebSocket } from "ws";
5
6
  import { z } from "zod";
6
7
 
8
+ import { runtimeTypeOf } from "../runtime-type";
9
+ import { isStringValue } from "../utils/runtime-value";
7
10
  import { type WebSocketAudience } from "./ws-audience";
8
11
  import { type WebSocketClusterBus } from "./ws-cluster-bus";
9
12
  import { type WebSocketPresenceStore } from "./ws-presence-store";
@@ -46,7 +49,7 @@ const OUTBOUND_RETRY_DELAY_MS = 5;
46
49
  // meta는 optional이므로 기존 `{event, data}` 메시지도 그대로 파싱됨 (backward-compatible)
47
50
  const WebSocketEnvelopeSchema = z.object({
48
51
  event: z.string(),
49
- data: z.unknown(),
52
+ data: z.json(),
50
53
  meta: z
51
54
  .object({
52
55
  traceparent: z.string().optional(),
@@ -61,17 +64,25 @@ type MessageHandler<T> = (
61
64
  ) => void | Promise<void>;
62
65
  type CloseHandler = () => void | Promise<void>;
63
66
 
64
- export type WebSocketEventMap = Record<string, unknown>;
67
+ export type WebSocketEventMap<EventPayload = never> = Record<string, EventPayload>;
68
+ export type WebSocketTransport = Pick<
69
+ WebSocket,
70
+ "bufferedAmount" | "close" | "ping" | "readyState" | "send" | "terminate"
71
+ > & {
72
+ off: (...args: Parameters<EventEmitter["off"]>) => void;
73
+ on: (...args: Parameters<EventEmitter["on"]>) => void;
74
+ };
65
75
 
66
- type InferWebSocketEventMap<TSchema extends z.ZodRawShape> = z.infer<z.ZodObject<TSchema>>;
76
+ type WebSocketEventFields = Record<string, z.ZodType>;
77
+ type InferWebSocketEventMap<TSchema extends WebSocketEventFields> = z.infer<z.ZodObject<TSchema>>;
67
78
 
68
- export type WebSocketOutEvents<TOut extends WebSocketEventMap = WebSocketEventMap> = TOut;
79
+ export type WebSocketOutEvents<TOut extends object = WebSocketEventMap> = TOut;
69
80
 
70
- export type WebSocketInEvents<TIn extends WebSocketEventMap = WebSocketEventMap> = TIn;
81
+ export type WebSocketInEvents<TIn extends object = WebSocketEventMap> = TIn;
71
82
 
72
83
  export interface WebSocketConnection<
73
- TOut extends WebSocketEventMap = WebSocketEventMap,
74
- TIn extends WebSocketEventMap = WebSocketEventMap,
84
+ TOut extends object = WebSocketEventMap,
85
+ TIn extends object = WebSocketEventMap,
75
86
  > extends ManagedWebSocketConnection {
76
87
  transport: "ws";
77
88
  onClose(callback: CloseHandler): void;
@@ -90,11 +101,14 @@ export interface WebSocketConnection<
90
101
  clearUserId(): void;
91
102
  }
92
103
 
93
- export type AnyWebSocketConnection = WebSocketConnection<WebSocketEventMap, WebSocketEventMap>;
104
+ export type AnyWebSocketConnection = WebSocketConnection<object, object>;
94
105
 
95
106
  type ParsedEnvelope = z.infer<typeof WebSocketEnvelopeSchema>;
96
107
 
97
- type WebSocketConnectionOptions<TOut extends z.ZodRawShape, TIn extends z.ZodRawShape> = {
108
+ type WebSocketConnectionOptions<
109
+ TOut extends WebSocketEventFields,
110
+ TIn extends WebSocketEventFields,
111
+ > = {
98
112
  namespace?: string;
99
113
  heartbeat?: number;
100
114
  active?: boolean;
@@ -137,8 +151,11 @@ export class WebSocketRuntime {
137
151
  this.registry = new WebSocketRegistry(registryOptions);
138
152
  }
139
153
 
140
- registerConnection<TOutSchema extends z.ZodRawShape, TInSchema extends z.ZodRawShape>(
141
- socket: WebSocket,
154
+ registerConnection<
155
+ TOutSchema extends WebSocketEventFields,
156
+ TInSchema extends WebSocketEventFields,
157
+ >(
158
+ socket: WebSocketTransport,
142
159
  options: Omit<
143
160
  WebSocketConnectionOptions<TOutSchema, TInSchema>,
144
161
  "registry" | "telemetryController"
@@ -155,19 +172,29 @@ export class WebSocketRuntime {
155
172
  this.registry.activate(connectionId);
156
173
  }
157
174
 
158
- broadcast(event: string, data: unknown, namespace?: string): void {
175
+ broadcast<Data>(event: string, data: Data, namespace?: string): void {
159
176
  this.registry.broadcast(event, data, namespace);
160
177
  }
161
178
 
162
- publishToRoom(roomId: WebSocketRoomId, event: string, data: unknown, namespace?: string): void {
179
+ publishToRoom<Data>(
180
+ roomId: WebSocketRoomId,
181
+ event: string,
182
+ data: Data,
183
+ namespace?: string,
184
+ ): void {
163
185
  this.registry.publishToRoom(roomId, event, data, namespace);
164
186
  }
165
187
 
166
- publishToUser(userId: WebSocketUserId, event: string, data: unknown, namespace?: string): void {
188
+ publishToUser<Data>(
189
+ userId: WebSocketUserId,
190
+ event: string,
191
+ data: Data,
192
+ namespace?: string,
193
+ ): void {
167
194
  this.registry.publishToUser(userId, event, data, namespace);
168
195
  }
169
196
 
170
- publishToAudience(audience: WebSocketAudience, event: string, data: unknown): void {
197
+ publishToAudience<Data>(audience: WebSocketAudience, event: string, data: Data): void {
171
198
  this.registry.publishToAudience(audience, event, data);
172
199
  }
173
200
 
@@ -185,7 +212,10 @@ export function createWebSocketRuntime(options: WebSocketRuntimeOptions = {}): W
185
212
  return new WebSocketRuntime(options);
186
213
  }
187
214
 
188
- class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extends z.ZodRawShape>
215
+ class WebSocketConnectionImpl<
216
+ TOutSchema extends WebSocketEventFields,
217
+ TInSchema extends WebSocketEventFields,
218
+ >
189
219
  implements
190
220
  WebSocketConnection<InferWebSocketEventMap<TOutSchema>, InferWebSocketEventMap<TInSchema>>,
191
221
  TelemetryInspectableConnection,
@@ -212,10 +242,10 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
212
242
  private readonly connectionStartedAt = performance.now();
213
243
 
214
244
  // connection 수명 동안 유지되는 식별자. setUserId/clearUserId로 업데이트되며 emit hot path에서 매번 registry lookup하지 않도록 캐시함
215
- private _userId?: string;
245
+ private currentUserId?: string;
216
246
 
217
247
  get userId(): string | undefined {
218
- return this._userId;
248
+ return this.currentUserId;
219
249
  }
220
250
 
221
251
  private closedInternal = false;
@@ -226,7 +256,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
226
256
  private outboundFlushScheduled = false;
227
257
 
228
258
  constructor(
229
- private readonly socket: WebSocket,
259
+ private readonly socket: WebSocketTransport,
230
260
  private readonly options: WebSocketConnectionOptions<TOutSchema, TInSchema>,
231
261
  ) {
232
262
  this.namespace = options.namespace ?? "default";
@@ -235,8 +265,8 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
235
265
  this.connectionSpanId = options.spanId;
236
266
  this.connectionParentSpanId = options.parentSpanId;
237
267
  this.connectionSampled = options.sampled;
238
- this.eventSchemasIn = options.inEvents.shape as unknown as Record<string, z.ZodTypeAny>;
239
- this.eventSchemasOut = options.outEvents.shape as unknown as Record<string, z.ZodTypeAny>;
268
+ this.eventSchemasIn = options.inEvents["shape"];
269
+ this.eventSchemasOut = options.outEvents["shape"];
240
270
 
241
271
  let resolveClosePromise!: () => void;
242
272
  this.closePromise = new Promise<void>((resolve) => {
@@ -266,7 +296,9 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
266
296
  ): void {
267
297
  const eventKey = String(event);
268
298
  const handlers = this.messageHandlers.get(eventKey) ?? [];
269
- handlers.push(handler as MessageHandler<unknown>);
299
+ handlers.push(
300
+ /* SAFETY: 등록된 WebSocket 이벤트 스키마가 이 값의 타입을 보장한다. */ handler as MessageHandler<unknown>,
301
+ );
270
302
  this.messageHandlers.set(eventKey, handlers);
271
303
  this.flushPendingMessages(eventKey);
272
304
  }
@@ -278,7 +310,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
278
310
  this.publishValidated(String(event), data);
279
311
  }
280
312
 
281
- publishUntyped(event: string, data: unknown): void {
313
+ publishUntyped<Data>(event: string, data: Data): void {
282
314
  this.publishValidated(event, data);
283
315
  }
284
316
 
@@ -295,12 +327,12 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
295
327
  }
296
328
 
297
329
  setUserId(userId: WebSocketUserId): void {
298
- this._userId = String(userId);
330
+ this.currentUserId = String(userId);
299
331
  this.options.registry.setUserId(this.id, userId);
300
332
  }
301
333
 
302
334
  clearUserId(): void {
303
- this._userId = undefined;
335
+ this.currentUserId = undefined;
304
336
  this.options.registry.clearUserId(this.id);
305
337
  }
306
338
 
@@ -327,7 +359,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
327
359
  return {
328
360
  connectionId: this.id,
329
361
  namespace: this.namespace,
330
- userId: this._userId,
362
+ userId: this.currentUserId,
331
363
  traceId: this.connectionTraceId,
332
364
  spanId: this.connectionSpanId,
333
365
  parentSpanId: this.connectionParentSpanId,
@@ -368,7 +400,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
368
400
  }
369
401
 
370
402
  // 인바운드 메시지를 순차 처리 큐에 올림. Sonamu envelope 검증 수행
371
- private readonly handleMessage = (raw: unknown) => {
403
+ private readonly handleMessage = (raw: RawData) => {
372
404
  this.enqueueMessageTask(async () => {
373
405
  const text = normalizeMessage(raw);
374
406
  const parsedEnvelope = safeParseEnvelope(text);
@@ -391,7 +423,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
391
423
  };
392
424
 
393
425
  private readonly handleClose = (code?: number, reason?: Buffer | string) => {
394
- const reasonText = typeof reason === "string" ? reason : reason?.toString();
426
+ const reasonText = isStringValue(reason) ? reason : reason?.toString();
395
427
  this.markClosed(code, reasonText);
396
428
  };
397
429
 
@@ -487,7 +519,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
487
519
  const messageTraceFields = {
488
520
  connectionId: this.id,
489
521
  namespace: this.namespace,
490
- userId: this._userId,
522
+ userId: this.currentUserId,
491
523
  traceId: traceCtx.traceId,
492
524
  spanId: traceCtx.spanId,
493
525
  parentSpanId: traceCtx.parentSpanId,
@@ -545,7 +577,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
545
577
  }
546
578
  }
547
579
 
548
- private publishValidated(event: string, data: unknown): void {
580
+ private publishValidated<Data>(event: string, data: Data): void {
549
581
  const schema = this.eventSchemasOut[event];
550
582
  if (!schema) {
551
583
  this.options.telemetryController.emit({
@@ -584,7 +616,6 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
584
616
  data: parsed.data,
585
617
  }),
586
618
  event,
587
- parsed.data,
588
619
  );
589
620
  }
590
621
 
@@ -705,8 +736,8 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
705
736
  }
706
737
 
707
738
  // queue가 한계에 도달하면 1013으로 닫아 느린 소비자가 메모리를 끝없이 잡아먹지 못하게 함
708
- // data 인자는 telemetry payload preview 용도로만 사용하며 pendingOutboundMessages에는 저장하지 않음 (큐 메모리 보호)
709
- private enqueueOutboundMessage(payload: string, event: string, data: unknown): void {
739
+ // telemetry preview는 직렬화된 payload에서 복원해 전송 데이터와 같은 JSON 계약을 유지함
740
+ private enqueueOutboundMessage(payload: string, event: string): void {
710
741
  if (this.pendingOutboundMessages.length >= MAX_PENDING_OUTBOUND_MESSAGES) {
711
742
  this.options.telemetryController.emit({
712
743
  name: "ws.backpressure.overflow",
@@ -723,7 +754,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
723
754
  level: "debug",
724
755
  ...this.telemetryFields(),
725
756
  detail: { event },
726
- payload: data,
757
+ payload: safeParseEnvelope(payload)?.data,
727
758
  });
728
759
  this.scheduleOutboundFlush();
729
760
  }
@@ -818,7 +849,7 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
818
849
  status: "error",
819
850
  ...fields,
820
851
  attributes: { event },
821
- errorType: error instanceof Error ? error.name : typeof error,
852
+ errorType: error instanceof Error ? error.name : runtimeTypeOf(error),
822
853
  });
823
854
  this.close(WS_CLOSE_CODE_INTERNAL_ERROR, "Outbound publish failed");
824
855
  return;
@@ -838,8 +869,8 @@ class WebSocketConnectionImpl<TOutSchema extends z.ZodRawShape, TInSchema extend
838
869
  }
839
870
  }
840
871
 
841
- function normalizeMessage(raw: unknown): string {
842
- if (typeof raw === "string") {
872
+ function normalizeMessage(raw: RawData): string {
873
+ if (isStringValue(raw)) {
843
874
  return raw;
844
875
  }
845
876
 
@@ -862,7 +893,10 @@ function normalizeMessage(raw: unknown): string {
862
893
 
863
894
  function safeParseEnvelope(raw: string): ParsedEnvelope | null {
864
895
  try {
865
- const parsed = JSON.parse(raw) as unknown;
896
+ const parsed =
897
+ /* SAFETY: 등록된 WebSocket 이벤트 스키마가 이 값의 타입을 보장한다. */ JSON.parse(
898
+ raw,
899
+ ) as unknown;
866
900
  const validated = WebSocketEnvelopeSchema.safeParse(parsed);
867
901
  return validated.success ? validated.data : null;
868
902
  } catch {
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
6
  import { z } from "zod";
7
7
 
8
8
  import { registeredApis } from "../../api/decorators";
9
+ import { type AbsolutePath } from "../../utils/path-utils";
9
10
  import { readApisFromFile } from "../api-parser";
10
11
 
11
12
  describe("readApisFromFile websocket type refs", () => {
@@ -47,7 +48,7 @@ describe("readApisFromFile websocket type refs", () => {
47
48
  });
48
49
 
49
50
  tempDir = await mkdtemp(path.join(tmpdir(), "sonamu-api-parser-"));
50
- const filePath = path.join(tempDir, "chat.frame.ts");
51
+ const filePath: AbsolutePath = `/${path.relative("/", path.join(tempDir, "chat.frame.ts"))}`;
51
52
  await writeFile(
52
53
  filePath,
53
54
  `
@@ -64,7 +65,7 @@ class ChatFrameClass {
64
65
  `.trim(),
65
66
  );
66
67
 
67
- const [api] = await readApisFromFile(filePath as `${string}.ts`);
68
+ const [api] = await readApisFromFile(filePath);
68
69
 
69
70
  expect(api?.websocketOptions?.outEventsTypeRef).toEqual({
70
71
  t: "ref",
@@ -0,0 +1,137 @@
1
+ import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { type SonamuConfig } from "../../api/config";
8
+ import { Sonamu } from "../../api/sonamu";
9
+ import { type AbsolutePath } from "../../utils/path-utils";
10
+ import { Syncer, type SyncerDependencies } from "../syncer";
11
+ import { actionGenerateHttpValidators } from "../syncer-actions";
12
+
13
+ function toAbsolutePath(filePath: string): AbsolutePath {
14
+ if (!path.isAbsolute(filePath)) {
15
+ throw new Error(`절대 경로가 필요합니다: ${filePath}`);
16
+ }
17
+ // SAFETY: path.isAbsolute 검사로 AbsolutePath 계약을 확인했다.
18
+ return filePath as AbsolutePath;
19
+ }
20
+
21
+ function createDependencies() {
22
+ return {
23
+ actionCopySharedToTargetsIfNotExists: vi
24
+ .fn<SyncerDependencies["actionCopySharedToTargetsIfNotExists"]>()
25
+ .mockResolvedValue(undefined),
26
+ actionGenerateHttpValidators: vi.fn<SyncerDependencies["actionGenerateHttpValidators"]>(),
27
+ actionGenerateSsrEntryServerIfNotExists: vi
28
+ .fn<SyncerDependencies["actionGenerateSsrEntryServerIfNotExists"]>()
29
+ .mockResolvedValue([]),
30
+ findChangedFilesUsingChecksums: vi.fn<SyncerDependencies["findChangedFilesUsingChecksums"]>(),
31
+ renewChecksums: vi.fn<SyncerDependencies["renewChecksums"]>().mockResolvedValue(undefined),
32
+ } satisfies SyncerDependencies;
33
+ }
34
+
35
+ const testConfig = {
36
+ api: { dir: "api", route: { prefix: "/api" } },
37
+ i18n: { defaultLocale: "ko", supportedLocales: ["ko"] },
38
+ sync: { targets: [] },
39
+ database: {},
40
+ server: {
41
+ apiConfig: {
42
+ contextProvider: (defaultContext) => defaultContext,
43
+ guardHandler: () => undefined,
44
+ },
45
+ },
46
+ } satisfies SonamuConfig;
47
+
48
+ describe("HTTP validator registry sync 정리", () => {
49
+ const tempRoots: string[] = [];
50
+
51
+ afterEach(async () => {
52
+ vi.restoreAllMocks();
53
+ await Promise.all(
54
+ tempRoots.splice(0).map((rootPath) => rm(rootPath, { recursive: true, force: true })),
55
+ );
56
+ });
57
+
58
+ it.each([
59
+ { label: "opt-out", value: false as const },
60
+ { label: "JIT", value: { api: "jit" as const } },
61
+ ])("$label 정책은 남아 있는 generated registry를 제거한다", async ({ value }) => {
62
+ const apiRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-registry-sync-test-"));
63
+ tempRoots.push(apiRootPath);
64
+ const registryPath = path.join(apiRootPath, "src/application/sonamu.validators.generated.ts");
65
+ await mkdir(path.dirname(registryPath), { recursive: true });
66
+ await writeFile(registryPath, "export const validators = new Map();\n");
67
+ Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
68
+ Sonamu.config = {
69
+ ...testConfig,
70
+ api: { ...testConfig.api, dir: "." },
71
+ validation: { zodCompiler: value },
72
+ };
73
+
74
+ await actionGenerateHttpValidators();
75
+
76
+ await expect(access(registryPath)).rejects.toMatchObject({ code: "ENOENT" });
77
+ });
78
+
79
+ it("registry-only drift를 경고로 넘기지 않고 즉시 재생성한다", async () => {
80
+ const appRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-registry-drift-test-"));
81
+ tempRoots.push(appRootPath);
82
+ const apiRootPath = path.join(appRootPath, "api");
83
+ const registryPath = toAbsolutePath(
84
+ path.join(apiRootPath, "src/application/sonamu.validators.generated.ts"),
85
+ );
86
+ await mkdir(path.dirname(registryPath), { recursive: true });
87
+ await writeFile(registryPath, "// drifted registry\n");
88
+ Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
89
+ Sonamu.config = {
90
+ ...testConfig,
91
+ validation: { zodCompiler: { api: "aot" } },
92
+ };
93
+ const dependencies = createDependencies();
94
+ dependencies.actionGenerateHttpValidators.mockResolvedValue(registryPath);
95
+ dependencies.findChangedFilesUsingChecksums.mockResolvedValue([registryPath]);
96
+ vi.spyOn(console, "warn").mockImplementation(() => {});
97
+
98
+ await new Syncer(dependencies).sync();
99
+
100
+ expect(dependencies.actionGenerateHttpValidators).toHaveBeenCalledTimes(1);
101
+ expect(dependencies.renewChecksums).toHaveBeenCalledTimes(1);
102
+ });
103
+
104
+ it("삭제된 registry가 checksum 변경 목록에서 빠져도 다른 tracked 변경 처리 중 복구한다", async () => {
105
+ const appRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-missing-registry-test-"));
106
+ tempRoots.push(appRootPath);
107
+ const apiRootPath = path.join(appRootPath, "api");
108
+ const registryPath = toAbsolutePath(
109
+ path.join(apiRootPath, "src/application/sonamu.validators.generated.ts"),
110
+ );
111
+ const unrelatedPath = toAbsolutePath(
112
+ path.join(apiRootPath, "src/application/queries.generated.ts"),
113
+ );
114
+ await mkdir(path.dirname(unrelatedPath), { recursive: true });
115
+ await writeFile(unrelatedPath, "// unrelated tracked change\n");
116
+ Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
117
+ Sonamu.config = {
118
+ ...testConfig,
119
+ validation: { zodCompiler: { api: "aot" } },
120
+ };
121
+ const order: string[] = [];
122
+ const dependencies = createDependencies();
123
+ dependencies.actionGenerateHttpValidators.mockImplementation(async () => {
124
+ order.push("registry");
125
+ return registryPath;
126
+ });
127
+ dependencies.findChangedFilesUsingChecksums.mockResolvedValue([unrelatedPath]);
128
+ dependencies.renewChecksums.mockImplementation(async () => {
129
+ order.push("checksum");
130
+ });
131
+ vi.spyOn(console, "warn").mockImplementation(() => {});
132
+
133
+ await new Syncer(dependencies).sync();
134
+
135
+ expect(order).toEqual(["registry", "checksum"]);
136
+ });
137
+ });
@@ -77,7 +77,7 @@ export async function readApisFromFile(filePath: AbsolutePath): Promise<Extended
77
77
  return resolveParamDec(
78
78
  {
79
79
  name: paramDec.name,
80
- type: paramDec.type as ts.TypeNode,
80
+ type: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ paramDec.type as ts.TypeNode,
81
81
  optional: paramDec.questionToken !== undefined || paramDec.initializer !== undefined,
82
82
  defaultDef,
83
83
  },
@@ -253,7 +253,10 @@ function resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
253
253
  case ts.SyntaxKind.VoidKeyword:
254
254
  return "void";
255
255
  case ts.SyntaxKind.LiteralType: {
256
- const literal = (typeNode as ts.LiteralTypeNode).literal;
256
+ const literal =
257
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
258
+ typeNode as ts.LiteralTypeNode
259
+ ).literal;
257
260
  if (ts.isStringLiteral(literal)) {
258
261
  return {
259
262
  t: "string-literal",
@@ -278,35 +281,46 @@ function resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
278
281
  }
279
282
  }
280
283
  case ts.SyntaxKind.ArrayType: {
281
- const arrNode = typeNode as ts.ArrayTypeNode;
284
+ const arrNode =
285
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ typeNode as ts.ArrayTypeNode;
282
286
  return {
283
287
  t: "array",
284
288
  elementsType: resolveTypeNode(arrNode.elementType),
285
289
  };
286
290
  }
287
291
  case ts.SyntaxKind.TypeLiteral: {
288
- const literalNode = typeNode as ts.TypeLiteralNode;
292
+ const literalNode =
293
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ typeNode as ts.TypeLiteralNode;
289
294
  return {
290
295
  t: "object",
291
296
  props: literalNode.members.map((member) => {
292
297
  if (ts.isIndexSignatureDeclaration(member)) {
293
298
  assert(member.parameters[0]);
294
299
  const res = resolveParamDec({
295
- name: member.parameters[0].name as ts.Identifier,
296
- type: member.parameters[0].type as ts.TypeNode,
300
+ name: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ member
301
+ .parameters[0].name as ts.Identifier,
302
+ type: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ member
303
+ .parameters[0].type as ts.TypeNode,
297
304
  });
298
305
 
299
306
  return resolveParamDec({
300
- name: {
307
+ name: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ {
301
308
  escapedText: `[${res.name}${res.optional ? "?" : ""}: ${res.type}]`,
302
309
  } as ts.Identifier,
303
310
  type: member.type,
304
311
  });
305
312
  } else {
306
313
  return resolveParamDec({
307
- name: (member as ts.PropertySignature).name as ts.Identifier,
308
- type: (member as ts.PropertySignature).type as ts.TypeNode,
309
- optional: (member as ts.PropertySignature).questionToken !== undefined,
314
+ name: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
315
+ member as ts.PropertySignature
316
+ ).name as ts.Identifier,
317
+ type: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
318
+ member as ts.PropertySignature
319
+ ).type as ts.TypeNode,
320
+ optional:
321
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
322
+ member as ts.PropertySignature
323
+ ).questionToken !== undefined,
310
324
  });
311
325
  }
312
326
  }),
@@ -315,26 +329,40 @@ function resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
315
329
  case ts.SyntaxKind.TypeReference:
316
330
  return {
317
331
  t: "ref",
318
- id: ((typeNode as ts.TypeReferenceNode).typeName as ts.Identifier).escapedText.toString(),
319
- args: (typeNode as ts.TypeReferenceNode).typeArguments?.map((typeArg) =>
320
- resolveTypeNode(typeArg),
321
- ),
332
+ id: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
333
+ (typeNode as ts.TypeReferenceNode).typeName as ts.Identifier
334
+ ).escapedText.toString(),
335
+ args: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
336
+ typeNode as ts.TypeReferenceNode
337
+ ).typeArguments?.map((typeArg) => resolveTypeNode(typeArg)),
322
338
  };
323
339
  case ts.SyntaxKind.UnionType:
324
340
  return {
325
341
  t: "union",
326
- types: (typeNode as ts.UnionTypeNode).types.map((type) => resolveTypeNode(type)),
342
+ types: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
343
+ typeNode as ts.UnionTypeNode
344
+ ).types.map((type) => resolveTypeNode(type)),
327
345
  };
328
346
  case ts.SyntaxKind.IntersectionType:
329
347
  return {
330
348
  t: "intersection",
331
- types: (typeNode as ts.IntersectionTypeNode).types.map((type) => resolveTypeNode(type)),
349
+ types: /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
350
+ typeNode as ts.IntersectionTypeNode
351
+ ).types.map((type) => resolveTypeNode(type)),
332
352
  };
333
353
  case ts.SyntaxKind.IndexedAccessType:
334
354
  return {
335
355
  t: "indexed-access",
336
- object: resolveTypeNode((typeNode as ts.IndexedAccessTypeNode).objectType),
337
- index: resolveTypeNode((typeNode as ts.IndexedAccessTypeNode).indexType),
356
+ object: resolveTypeNode(
357
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
358
+ typeNode as ts.IndexedAccessTypeNode
359
+ ).objectType,
360
+ ),
361
+ index: resolveTypeNode(
362
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
363
+ typeNode as ts.IndexedAccessTypeNode
364
+ ).indexType,
365
+ ),
338
366
  };
339
367
  case ts.SyntaxKind.TupleType:
340
368
  if (ts.isTupleTypeNode(typeNode)) {
@@ -347,18 +375,29 @@ function resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
347
375
  case ts.SyntaxKind.ParenthesizedType:
348
376
  // 괄호로 묶인 타입 (예: (A & B)[] 에서 (A & B))
349
377
  // 내부 타입을 재귀적으로 resolve
350
- return resolveTypeNode((typeNode as ts.ParenthesizedTypeNode).type);
378
+ return resolveTypeNode(
379
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
380
+ typeNode as ts.ParenthesizedTypeNode
381
+ ).type,
382
+ );
351
383
 
352
384
  case ts.SyntaxKind.FunctionType:
353
385
  return {
354
386
  t: "function",
355
- parameters: (typeNode as ts.FunctionTypeNode).parameters.map((param) => ({
356
- name: param.name.getText(),
357
- type: param.type ? resolveTypeNode(param.type) : "unknown",
358
- optional: param.questionToken !== undefined,
359
- defaultDef: undefined,
360
- })),
361
- returnType: resolveTypeNode((typeNode as ts.FunctionTypeNode).type),
387
+ parameters:
388
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
389
+ typeNode as ts.FunctionTypeNode
390
+ ).parameters.map((param) => ({
391
+ name: param.name.getText(),
392
+ type: param.type ? resolveTypeNode(param.type) : "unknown",
393
+ optional: param.questionToken !== undefined,
394
+ defaultDef: undefined,
395
+ })),
396
+ returnType: resolveTypeNode(
397
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
398
+ typeNode as ts.FunctionTypeNode
399
+ ).type,
400
+ ),
362
401
  };
363
402
  case undefined:
364
403
  throw new Error(`typeNode undefined`);
@@ -377,7 +416,8 @@ function resolveParamDec(
377
416
  },
378
417
  index: number = 0,
379
418
  ): ApiParam {
380
- const name = paramDec.name as ts.Identifier;
419
+ const name =
420
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ paramDec.name as ts.Identifier;
381
421
  const type = resolveTypeNode(paramDec.type);
382
422
 
383
423
  if (name === undefined) {