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
@@ -6,6 +6,20 @@ import { type ExtendedApi } from "../../api/decorators";
6
6
  import { Sonamu } from "../../api/sonamu";
7
7
  import { Template } from "../template";
8
8
 
9
+ type RequestDefaultValue =
10
+ | null
11
+ | undefined
12
+ | bigint
13
+ | boolean
14
+ | number
15
+ | string
16
+ | RequestDefaultValue[]
17
+ | RequestDefaultObject;
18
+
19
+ interface RequestDefaultObject {
20
+ [key: string]: RequestDefaultValue;
21
+ }
22
+
9
23
  export class Template__generated_http extends Template {
10
24
  constructor() {
11
25
  super("generated_http");
@@ -67,16 +81,23 @@ export class Template__generated_http extends Template {
67
81
  };
68
82
  }
69
83
 
70
- zodTypeToReqDefault(zodType: z.ZodType, name: string): unknown {
84
+ zodTypeToReqDefault(zodType: z.ZodType, name: string): RequestDefaultValue {
71
85
  if (zodType instanceof z.ZodObject) {
72
86
  return Object.fromEntries(
73
- Object.keys(zodType.shape).map((key) => [
87
+ Object.keys(zodType["shape"]).map((key) => [
74
88
  key,
75
- this.zodTypeToReqDefault(zodType.shape[key], key),
89
+ this.zodTypeToReqDefault(zodType["shape"][key], key),
76
90
  ]),
77
91
  );
78
92
  } else if (zodType instanceof z.ZodArray) {
79
- return [this.zodTypeToReqDefault((zodType as z.ZodArray<z.ZodType>).element, name)];
93
+ return [
94
+ this.zodTypeToReqDefault(
95
+ /* SAFETY: ZodArray 분기이므로 element가 배열 원소 스키마다. */ (
96
+ zodType as z.ZodArray<z.ZodType>
97
+ ).element,
98
+ name,
99
+ ),
100
+ ];
80
101
  } else if (zodType instanceof z.core.$ZodString) {
81
102
  // NOTE: z.ZodString으로 비교하면 z.url(), z.email() 등의 타입에서 문제가 생기므로 z.core.$ZodString으로 비교함
82
103
  if (name.endsWith("_at") || name.endsWith("_date") || name === "range") {
@@ -96,11 +117,21 @@ export class Template__generated_http extends Template {
96
117
  } else if (zodType instanceof z.ZodEnum) {
97
118
  return zodType.options[0];
98
119
  } else if (zodType instanceof z.ZodOptional) {
99
- return this.zodTypeToReqDefault((zodType as z.ZodOptional<z.ZodType>).def.innerType, name);
120
+ return this.zodTypeToReqDefault(
121
+ /* SAFETY: ZodOptional 분기이므로 innerType이 선택 속성의 스키마다. */ (
122
+ zodType as z.ZodOptional<z.ZodType>
123
+ ).def.innerType,
124
+ name,
125
+ );
100
126
  } else if (zodType instanceof z.ZodNullable) {
101
127
  return null;
102
128
  } else if (zodType instanceof z.ZodUnion) {
103
- return this.zodTypeToReqDefault((zodType as z.ZodUnion<z.ZodType[]>).def.options[0], name);
129
+ return this.zodTypeToReqDefault(
130
+ /* SAFETY: ZodUnion 분기이므로 options의 첫 항목이 유효한 대안 스키마다. */ (
131
+ zodType as z.ZodUnion<z.ZodType[]>
132
+ ).def.options[0],
133
+ name,
134
+ );
104
135
  } else if (zodType instanceof z.ZodUnknown) {
105
136
  return "unknown";
106
137
  } else if (zodType instanceof z.ZodTuple) {
@@ -112,20 +143,36 @@ export class Template__generated_http extends Template {
112
143
  return zodType.value;
113
144
  } else if (zodType instanceof z.ZodRecord || zodType instanceof z.ZodMap) {
114
145
  // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- ZodRecord 타입 사용
115
- const kvDef = (zodType as z.ZodRecord<any, z.ZodType> | z.ZodMap<z.ZodType, z.ZodType>).def;
146
+ const kvDef = /* SAFETY: ZodRecord/ZodMap 분기이므로 def가 키-값 스키마 정의다. */ (
147
+ zodType as z.ZodRecord<any, z.ZodType> | z.ZodMap<z.ZodType, z.ZodType>
148
+ ).def;
116
149
  // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- ZodIntersection 타입 사용
117
- const key = this.zodTypeToReqDefault(kvDef.keyType, name) as any;
150
+ const key = String(this.zodTypeToReqDefault(kvDef.keyType, name));
118
151
  const value = this.zodTypeToReqDefault(kvDef.valueType, name);
119
152
  return { [key]: value };
120
153
  } else if (zodType instanceof z.ZodSet) {
121
- return [this.zodTypeToReqDefault((zodType as z.ZodSet<z.ZodType>).def.valueType, name)];
154
+ return [
155
+ this.zodTypeToReqDefault(
156
+ /* SAFETY: ZodSet 분기이므로 valueType이 집합 원소 스키마다. */ (
157
+ zodType as z.ZodSet<z.ZodType>
158
+ ).def.valueType,
159
+ name,
160
+ ),
161
+ ];
122
162
  } else if (zodType instanceof z.ZodIntersection) {
123
163
  return this.zodTypeToReqDefault(
124
- (zodType as z.ZodIntersection<z.ZodType, z.ZodType>).def.right,
164
+ /* SAFETY: ZodIntersection 분기이므로 right가 교차 타입의 우측 스키마다. */ (
165
+ zodType as z.ZodIntersection<z.ZodType, z.ZodType>
166
+ ).def.right,
125
167
  name,
126
168
  );
127
169
  } else if (zodType instanceof z.ZodDefault) {
128
- return this.zodTypeToReqDefault((zodType as z.ZodDefault<z.ZodType>).def.innerType, name);
170
+ return this.zodTypeToReqDefault(
171
+ /* SAFETY: ZodDefault 분기이므로 innerType이 기본값 적용 전 스키마다. */ (
172
+ zodType as z.ZodDefault<z.ZodType>
173
+ ).def.innerType,
174
+ name,
175
+ );
129
176
  } else {
130
177
  // console.log(zodType);
131
178
  return `unknown-${zodType.type}`;
@@ -135,12 +182,11 @@ export class Template__generated_http extends Template {
135
182
  resolveApiParams(
136
183
  api: ExtendedApi,
137
184
  references: { [typeName: string]: z.ZodType },
138
- ): { [key: string]: unknown } {
185
+ ): RequestDefaultObject {
139
186
  const reqType = getZodObjectFromApi(api, references);
140
187
  try {
141
- const def = this.zodTypeToReqDefault(reqType, "unknownName") as {
142
- [key: string]: unknown;
143
- };
188
+ // SAFETY: getZodObjectFromApi는 객체 스키마를 반환하므로 기본값도 객체이다.
189
+ const def = this.zodTypeToReqDefault(reqType, "unknownName") as RequestDefaultObject;
144
190
  return def;
145
191
  } catch (error) {
146
192
  console.error(error);
@@ -36,6 +36,13 @@ function mapBetterAuthFieldType(type: string): string {
36
36
  }
37
37
  }
38
38
 
39
+ function addLoaderIdTypeSafetyComments(query: string): string {
40
+ return query.replaceAll(
41
+ /fromIds as (?:number|string)\[\]/g,
42
+ "/* SAFETY: 로더가 전달하는 ID는 소스 엔티티의 기본 키 타입과 일치한다. */ $&",
43
+ );
44
+ }
45
+
39
46
  export class Template__generated_sso extends Template {
40
47
  constructor() {
41
48
  super("generated_sso");
@@ -88,7 +95,8 @@ export class Template__generated_sso extends Template {
88
95
  lines: [
89
96
  `export const ${entityCamelName}LoaderQueries = {`,
90
97
  ...subsetKeys.map((subsetKey) => {
91
- return `${subsetKey}: ${entity.getPuriLoaderQuery(subsetKey)},`;
98
+ const query = entity.getPuriLoaderQuery(subsetKey);
99
+ return `${subsetKey}: ${addLoaderIdTypeSafetyComments(query)},`;
92
100
  }),
93
101
  `} as const satisfies PuriLoaderQueries<${subsetKeyTypeName}>;`,
94
102
  "",
@@ -128,7 +136,7 @@ export class Template__generated_sso extends Template {
128
136
  importKeys: unique([...result.importKeys, ...ts.importKeys]),
129
137
  };
130
138
  },
131
- {
139
+ /* SAFETY: reduce 누산기는 label을 제외한 SourceCode의 두 배열로 초기화된다. */ {
132
140
  lines: [],
133
141
  importKeys: [],
134
142
  } as Omit<SourceCode, "label">,
@@ -295,7 +303,9 @@ export class Template__generated_sso extends Template {
295
303
  }
296
304
 
297
305
  // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- additionalFields 타입이 동적임
298
- const additionalFields = (authConfig as any)?.user?.additionalFields;
306
+ const additionalFields =
307
+ /* SAFETY: better-auth 설정의 user.additionalFields는 동적 필드 맵이다. */ (authConfig as any)
308
+ ?.user?.additionalFields;
299
309
  if (!additionalFields || Object.keys(additionalFields).length === 0) {
300
310
  // additionalFields가 없으면 기본 User 타입만 사용
301
311
  return {
@@ -310,7 +320,8 @@ export class Template__generated_sso extends Template {
310
320
  // additionalFields를 TypeScript 타입으로 변환
311
321
  const fieldLines = Object.entries(additionalFields).map(([key, value]) => {
312
322
  // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- better-auth additionalFields 구조
313
- const fieldConfig = value as any;
323
+ const fieldConfig =
324
+ /* SAFETY: additionalFields의 각 값은 better-auth 필드 설정 객체이다. */ value as any;
314
325
  const isRequired = fieldConfig.required !== false;
315
326
 
316
327
  let fieldType: string;
@@ -0,0 +1,175 @@
1
+ import { type ExtendedApi } from "../../api/decorators";
2
+ import {
3
+ getHttpValidatorFingerprint,
4
+ getHttpValidatorRouteIdentifier,
5
+ getHttpValidatorRouteKey,
6
+ } from "../../api/http-validator";
7
+ import { Sonamu } from "../../api/sonamu";
8
+ import { type ApiParamType } from "../../types/types";
9
+ import { isObjectValue, isStringValue } from "../../utils/runtime-value";
10
+ import { Template } from "../template";
11
+
12
+ function collectReferencedTypes(type: ApiParamType, result: Set<string>): void {
13
+ if (isStringValue(type)) {
14
+ return;
15
+ }
16
+ switch (type.t) {
17
+ case "ref":
18
+ result.add(type.id);
19
+ for (const argument of type.args ?? []) {
20
+ collectReferencedTypes(argument, result);
21
+ }
22
+ return;
23
+ case "array":
24
+ collectReferencedTypes(type.elementsType, result);
25
+ return;
26
+ case "object":
27
+ for (const prop of type.props) {
28
+ collectReferencedTypes(prop.type, result);
29
+ }
30
+ return;
31
+ case "union":
32
+ case "intersection":
33
+ for (const member of type.types) {
34
+ collectReferencedTypes(member, result);
35
+ }
36
+ return;
37
+ case "tuple-type":
38
+ for (const element of type.elements) {
39
+ collectReferencedTypes(element, result);
40
+ }
41
+ return;
42
+ case "indexed-access":
43
+ collectReferencedTypes(type.object, result);
44
+ collectReferencedTypes(type.index, result);
45
+ return;
46
+ case "type-param":
47
+ if (type.constraint !== undefined) {
48
+ collectReferencedTypes(type.constraint, result);
49
+ }
50
+ return;
51
+ case "function":
52
+ for (const parameter of type.parameters) {
53
+ collectReferencedTypes(parameter.type, result);
54
+ }
55
+ collectReferencedTypes(type.returnType, result);
56
+ return;
57
+ case "string-literal":
58
+ case "numeric-literal":
59
+ return;
60
+ }
61
+ }
62
+
63
+ export class Template__http_validators extends Template {
64
+ constructor() {
65
+ super("http_validators");
66
+ }
67
+
68
+ getTargetAndPath() {
69
+ return {
70
+ target: `${Sonamu.config.api.dir}/src/application`,
71
+ path: "sonamu.validators.generated.ts",
72
+ };
73
+ }
74
+
75
+ render() {
76
+ const apis = [...Sonamu.syncer.apis]
77
+ .filter((api) => api.websocketOptions === undefined)
78
+ .toSorted((left, right) =>
79
+ getHttpValidatorRouteKey(left).localeCompare(getHttpValidatorRouteKey(right)),
80
+ );
81
+ const routeKeys = apis.map(getHttpValidatorRouteKey);
82
+ const duplicateKey = routeKeys.find((key, index) => routeKeys.indexOf(key) !== index);
83
+ if (duplicateKey !== undefined) {
84
+ throw new Error(`중복된 HTTP validator route key: ${duplicateKey}`);
85
+ }
86
+
87
+ const fingerprint = getHttpValidatorFingerprint(apis);
88
+ const policy = Sonamu.config.validation?.zodCompiler;
89
+ const isAot = isObjectValue(policy) && policy !== null && policy.api === "aot";
90
+ const hasAotValidators = isAot && apis.length > 0;
91
+ const referencedTypes = new Set<string>();
92
+ for (const api of apis) {
93
+ for (const typeParameter of api.typeParameters) {
94
+ collectReferencedTypes(typeParameter, referencedTypes);
95
+ }
96
+ for (const parameter of api.parameters) {
97
+ collectReferencedTypes(parameter.type, referencedTypes);
98
+ }
99
+ }
100
+ const importKeys = [...referencedTypes]
101
+ .filter((typeName) => Sonamu.syncer.types[typeName] !== undefined)
102
+ .toSorted();
103
+ const body = isAot
104
+ ? this.renderAotRegistry(apis, fingerprint, importKeys)
105
+ : this.renderPlainStub(apis, fingerprint);
106
+
107
+ return {
108
+ ...this.getTargetAndPath(),
109
+ body,
110
+ importKeys,
111
+ customHeaders: [
112
+ "/**",
113
+ " * @generated",
114
+ " * 직접 수정하지 마세요.",
115
+ " */",
116
+ "",
117
+ "/* oxlint-disable */",
118
+ "",
119
+ ...(hasAotValidators
120
+ ? [
121
+ `import { compile } from "zod-compiler";`,
122
+ `import { fastifyCaster, getZodObjectFromApi } from "sonamu";`,
123
+ ]
124
+ : []),
125
+ ],
126
+ };
127
+ }
128
+
129
+ private renderAotRegistry(
130
+ apis: ExtendedApi[],
131
+ fingerprint: string,
132
+ importKeys: string[],
133
+ ): string {
134
+ const declarations = apis.map((api) => {
135
+ const identifier = `validator_${getHttpValidatorRouteIdentifier(api)}`;
136
+ const schemaApi = {
137
+ modelName: api.modelName,
138
+ methodName: api.methodName,
139
+ path: api.path,
140
+ options: { httpMethod: api.options.httpMethod ?? "GET" },
141
+ typeParameters: api.typeParameters,
142
+ parameters: api.parameters,
143
+ returnType: "unknown",
144
+ } satisfies ExtendedApi;
145
+ return `export const ${identifier} = compile(fastifyCaster(getZodObjectFromApi(${JSON.stringify(schemaApi)}, { ...types })));`;
146
+ });
147
+ const routeIds = Object.fromEntries(
148
+ apis.map((api) => [getHttpValidatorRouteKey(api), getHttpValidatorRouteIdentifier(api)]),
149
+ );
150
+
151
+ return [
152
+ `export const fingerprint = ${JSON.stringify(fingerprint)};`,
153
+ `export const routeIds = ${JSON.stringify(routeIds)};`,
154
+ `const types = { ${importKeys.join(", ")} };`,
155
+ ...declarations,
156
+ `export const validators = new Map([`,
157
+ ...apis.map((api) => {
158
+ const routeKey = getHttpValidatorRouteKey(api);
159
+ return ` [${JSON.stringify(routeKey)}, validator_${getHttpValidatorRouteIdentifier(api)}],`;
160
+ }),
161
+ `]);`,
162
+ ].join("\n");
163
+ }
164
+
165
+ private renderPlainStub(apis: ExtendedApi[], fingerprint: string): string {
166
+ const routeIds = Object.fromEntries(
167
+ apis.map((api) => [getHttpValidatorRouteKey(api), getHttpValidatorRouteIdentifier(api)]),
168
+ );
169
+ return [
170
+ `export const fingerprint = ${JSON.stringify(fingerprint)};`,
171
+ `export const routeIds = ${JSON.stringify(routeIds)};`,
172
+ `export const validators = new Map();`,
173
+ ].join("\n");
174
+ }
175
+ }
@@ -33,7 +33,9 @@ export class Template__model extends Template {
33
33
  const listParamsNode = zodTypeToRenderingNode(listParamsZodType);
34
34
 
35
35
  const subsetKeyZodType = await getZodTypeById(`${entityId}SubsetKey`);
36
- const subsetKeys = (subsetKeyZodType as z.ZodEnum).enum;
36
+ const subsetKeys =
37
+ /* SAFETY: 생성된 *SubsetKey 타입은 enum 스키마로 등록된다. */ (subsetKeyZodType as z.ZodEnum)
38
+ .enum;
37
39
 
38
40
  const names = EntityManager.getNamesFromId(entityId);
39
41
  const entity = EntityManager.get(entityId);
@@ -52,8 +52,9 @@ const SUPPORTED_LOCALES = ${JSON.stringify(supportedLocales)} as const;
52
52
  function getCurrentLocale(): (typeof SUPPORTED_LOCALES)[number] {
53
53
  try {
54
54
  const ctx = Sonamu.getContext();
55
- return ctx.locale as (typeof SUPPORTED_LOCALES)[number] ?? DEFAULT_LOCALE;
56
- } catch (_) {
55
+ const locale = ctx.locale;
56
+ return SUPPORTED_LOCALES.find((supportedLocale) => supportedLocale === locale) ?? DEFAULT_LOCALE;
57
+ } catch {
57
58
  return DEFAULT_LOCALE;
58
59
  }
59
60
  }
@@ -61,14 +62,14 @@ function getCurrentLocale(): (typeof SUPPORTED_LOCALES)[number] {
61
62
  : `
62
63
  const DEFAULT_LOCALE = "${defaultLocale}" as const;
63
64
  export const SUPPORTED_LOCALES = ${JSON.stringify(supportedLocales)} as const;
64
- let _currentLocale: (typeof SUPPORTED_LOCALES)[number] = DEFAULT_LOCALE;
65
+ let currentLocale: (typeof SUPPORTED_LOCALES)[number] = DEFAULT_LOCALE;
65
66
 
66
67
  export function setLocale(locale: (typeof SUPPORTED_LOCALES)[number]) {
67
- _currentLocale = locale;
68
+ currentLocale = locale;
68
69
  }
69
70
 
70
71
  export function getCurrentLocale(): (typeof SUPPORTED_LOCALES)[number] {
71
- return _currentLocale;
72
+ return currentLocale;
72
73
  }
73
74
  `.trim();
74
75
 
@@ -126,7 +127,10 @@ export function defineLocale(dict: Partial<MergedDictionary>) {
126
127
  }
127
128
 
128
129
  // 각 locale별로 rc-keys + entity labels + 프로젝트 dict 합침
129
- const dictionaries: Record<string, Partial<MergedDictionary>> = {
130
+ type DictionaryRegistry = {
131
+ [L in (typeof SUPPORTED_LOCALES)[number]]: Partial<MergedDictionary>;
132
+ };
133
+ const dictionaries: DictionaryRegistry = {
130
134
  ${defaultLocale}: { ...${getRCKeysVarName(defaultLocale)}, ...entityLabels, ...${defaultLocale} },
131
135
  ${supportedLocales
132
136
  .filter((locale) => locale !== defaultLocale)
@@ -138,29 +142,33 @@ type SDReturnType<K extends DictKey> = MergedDictionary[K] extends (...args: inf
138
142
  ? (...args: P) => LocalizedString
139
143
  : LocalizedString;
140
144
 
141
- function getDictValue<K extends DictKey>(key: K, locale: string): SDReturnType<K> {
145
+ function getDictValue<K extends DictKey>(key: K, locale: (typeof SUPPORTED_LOCALES)[number]): SDReturnType<K> {
142
146
  // 1. 지정된 locale에서 조회
143
147
  const dict = dictionaries[locale];
144
148
  if (dict?.[key] !== undefined) {
145
- return dict[key] as unknown as SDReturnType<K>;
149
+ // SAFETY: 요청 locale의 같은 키는 MergedDictionary가 정한 반환 종류를 유지합니다.
150
+ return dict[key] as SDReturnType<K>;
146
151
  }
147
152
 
148
153
  // 2. default locale에서 조회
149
154
  if (locale !== DEFAULT_LOCALE && dictionaries[DEFAULT_LOCALE]?.[key] !== undefined) {
150
- return dictionaries[DEFAULT_LOCALE][key] as unknown as SDReturnType<K>;
155
+ // SAFETY: 기본 locale의 같은 키는 MergedDictionary가 정한 반환 종류를 유지합니다.
156
+ return dictionaries[DEFAULT_LOCALE][key] as SDReturnType<K>;
151
157
  }
152
158
 
153
159
  // 3. supported locales 순회
154
160
  for (const supportedLocale of SUPPORTED_LOCALES) {
155
161
  if (supportedLocale !== locale && supportedLocale !== DEFAULT_LOCALE) {
156
162
  if (dictionaries[supportedLocale]?.[key] !== undefined) {
157
- return dictionaries[supportedLocale][key] as unknown as SDReturnType<K>;
163
+ // SAFETY: 대체 locale의 같은 키는 MergedDictionary가 정한 반환 종류를 유지합니다.
164
+ return dictionaries[supportedLocale][key] as SDReturnType<K>;
158
165
  }
159
166
  }
160
167
  }
161
168
 
162
- // 4. 모두 실패 시 key 반환
163
- return key as unknown as SDReturnType<K>;
169
+ // 모든 locale에 값이 없으면 동적으로 조합된 키를 그대로 노출합니다.
170
+ // SAFETY: 누락된 사전 키의 문자열 표현은 LocalizedString fallback 규약을 따릅니다.
171
+ return String(key) as SDReturnType<K>;
164
172
  }
165
173
 
166
174
  /**
@@ -226,58 +234,85 @@ type LocalizedColumnCandidate<T, K extends string> =
226
234
  type LocalizedColumnReturn<T, K extends string> =
227
235
  | LocalizedColumnValueFrom<LocalizedColumnCandidate<T, K>>
228
236
  | undefined;
229
- type LocaleValueMap = Partial<Record<(typeof SUPPORTED_LOCALES)[number], unknown>>;
237
+ type LocaleValueMap = {
238
+ [L in (typeof SUPPORTED_LOCALES)[number]]?: LocalizedColumnValue;
239
+ };
240
+ function isString<V>(value: V): value is V & string {
241
+ return Object.prototype.toString.call(value) === "[object String]";
242
+ }
230
243
 
231
- function isLocalizedColumnValue(value: unknown): value is LocalizedColumnValue {
232
- return typeof value === "string" || (Array.isArray(value) && value.every((item) => typeof item === "string"));
244
+ function isLocalizedColumnValue<V>(value: V): value is V & LocalizedColumnValue {
245
+ return isString(value) || (Array.isArray(value) && value.every(isString));
233
246
  }
234
247
 
235
- function isLocalizedColumnScalarValue(value: unknown): value is LocalizedColumnScalarValue {
236
- return ["string", "number", "boolean", "bigint"].includes(typeof value);
248
+ function isLocalizedColumnScalarValue<V>(value: V): value is V & LocalizedColumnScalarValue {
249
+ return ["[object String]", "[object Number]", "[object Boolean]", "[object BigInt]"].includes(
250
+ Object.prototype.toString.call(value),
251
+ );
237
252
  }
238
253
 
239
- function isEmptyLocalizedColumnValue(value: unknown): value is null | undefined | "" {
254
+ function isEmptyLocalizedColumnValue<V>(value: V): value is V & (null | undefined | "") {
240
255
  return value === null || value === undefined || value === "";
241
256
  }
242
257
 
243
- function getNestedLocaleValue<T extends Record<string, unknown>, K extends LocalizedBaseColumn<T>>(
258
+ type PropertyValue<T, K extends PropertyKey> = K extends keyof T ? T[K] : undefined;
259
+
260
+ function getProperty<T extends object, K extends PropertyKey>(row: T, key: K): PropertyValue<T, K> {
261
+ if (!(key in row)) {
262
+ // SAFETY: 존재하지 않는 후보 키는 조건부 속성 타입에서도 undefined입니다.
263
+ return undefined as PropertyValue<T, K>;
264
+ }
265
+
266
+ // SAFETY: own/inherited 키 존재를 확인했으므로 해당 키의 조건부 속성 타입으로 좁힐 수 있습니다.
267
+ return (row as T & Record<K, PropertyValue<T, K>>)[key];
268
+ }
269
+
270
+ function getNestedLocaleValue<T extends object, K extends LocalizedBaseColumn<T>>(
244
271
  row: T,
245
272
  column: K,
246
273
  locale: (typeof SUPPORTED_LOCALES)[number],
247
- ): unknown {
248
- const columnValue = row[column];
249
- if (columnValue === null || typeof columnValue !== "object" || Array.isArray(columnValue)) {
274
+ ): LocalizedColumnValue | undefined {
275
+ const columnValue = getProperty(row, column);
276
+ if (columnValue === null || Object.prototype.toString.call(columnValue) !== "[object Object]") {
250
277
  return undefined;
251
278
  }
252
279
 
280
+ // SAFETY: 일반 객체 검사를 통과한 값만 locale별 값 맵으로 조회합니다.
253
281
  return (columnValue as LocaleValueMap)[locale];
254
282
  }
255
283
 
256
- export function localizedColumn<T extends Record<string, unknown>, K extends LocalizedBaseColumn<T>>(
284
+ function localizedKey<K extends string>(columnName: K, localeName: (typeof SUPPORTED_LOCALES)[number]) {
285
+ return \`\${columnName}_\${localeName}\`;
286
+ }
287
+
288
+ export function localizedColumn<T extends object, K extends LocalizedBaseColumn<T>>(
257
289
  row: T,
258
290
  column: K,
259
291
  ): LocalizedColumnReturn<T, K> {
260
- const currentLocale = getCurrentLocale();
261
- const locale = SUPPORTED_LOCALES.includes(currentLocale) ? currentLocale : DEFAULT_LOCALE;
262
- const otherLocales = SUPPORTED_LOCALES.filter((l: string) => l !== locale && l !== DEFAULT_LOCALE);
263
- const localizedKey = (column: K, locale: (typeof SUPPORTED_LOCALES)[number]) => \`\${column}_\${locale}\`;
292
+ const requestedLocale = getCurrentLocale();
293
+ const locale = SUPPORTED_LOCALES.includes(requestedLocale) ? requestedLocale : DEFAULT_LOCALE;
294
+ const otherLocales = SUPPORTED_LOCALES.filter(
295
+ (candidateLocale) => candidateLocale !== locale && candidateLocale !== DEFAULT_LOCALE,
296
+ );
264
297
  const values = [
265
- { value: row[localizedKey(column, locale)], source: "direct" },
298
+ { value: getProperty(row, localizedKey(column, locale)), source: "direct" },
266
299
  { value: getNestedLocaleValue(row, column, locale), source: "nested" },
267
- { value: row[column], source: "direct" },
268
- { value: row[localizedKey(column, DEFAULT_LOCALE)], source: "direct" },
300
+ { value: getProperty(row, column), source: "direct" },
301
+ { value: getProperty(row, localizedKey(column, DEFAULT_LOCALE)), source: "direct" },
269
302
  { value: getNestedLocaleValue(row, column, DEFAULT_LOCALE), source: "nested" },
270
303
  ...otherLocales.flatMap((l) => [
271
- { value: row[localizedKey(column, l)], source: "direct" },
304
+ { value: getProperty(row, localizedKey(column, l)), source: "direct" },
272
305
  { value: getNestedLocaleValue(row, column, l), source: "nested" },
273
306
  ]),
274
307
  ];
275
308
 
276
309
  for (const { value, source } of values) {
277
310
  if (!isEmptyLocalizedColumnValue(value) && isLocalizedColumnValue(value)) {
311
+ // SAFETY: 값 가드가 선택한 컬럼의 문자열 또는 문자열 배열임을 확인했습니다.
278
312
  return value as LocalizedColumnReturn<T, K>;
279
313
  }
280
314
  if (source === "direct" && !isEmptyLocalizedColumnValue(value) && isLocalizedColumnScalarValue(value)) {
315
+ // SAFETY: 직접 컬럼의 스칼라 값은 문자열로 변환해 지역화 반환 타입을 충족합니다.
281
316
  return String(value) as LocalizedColumnReturn<T, K>;
282
317
  }
283
318
  }
@@ -292,14 +327,19 @@ export function localizedColumn<T extends Record<string, unknown>, K extends Loc
292
327
  * @example
293
328
  * SD.enumLabels("TagOrderBy")[key] // → 현재 locale의 라벨
294
329
  */
295
- SD.enumLabels = (enumName: string): Record<string, LocalizedString> => {
296
- return new Proxy({} as Record<string, LocalizedString>, {
330
+ interface EnumLabels {
331
+ [key: string]: LocalizedString;
332
+ }
333
+
334
+ SD.enumLabels = (enumName: string): EnumLabels => {
335
+ return new Proxy<EnumLabels>({}, {
297
336
  get(_, key: string | symbol) {
298
- if (typeof key === "symbol") {
337
+ if (Object.prototype.toString.call(key) === "[object Symbol]") {
299
338
  return undefined;
300
339
  }
301
340
 
302
- const dictKey = \`enum.\${enumName}.\${key}\` as DictKey;
341
+ // SAFETY: 생성된 enum 이름과 값의 조합은 enum 사전 키 형식을 따릅니다.
342
+ const dictKey = \`enum.\${enumName}.\${String(key)}\` as DictKey;
303
343
  return getDictValue(dictKey, getCurrentLocale());
304
344
  }
305
345
  });
@@ -318,16 +358,16 @@ SD.enumLabels = (enumName: string): Record<string, LocalizedString> => {
318
358
  * react-components의 rc-keys를 추출합
319
359
  * sonamu/src/dict/rc-keys.ts 파일의 소스 코드를 직접 읽어서 반환
320
360
  */
321
- private extractRCKeysSourceCode(): { ko: string; en: string } {
361
+ private extractRCKeysSourceCode() {
322
362
  try {
323
363
  // rc-keys.ts 파일 경로
324
- const __filename = fileURLToPath(import.meta.url);
325
- const __dirname = path.dirname(__filename);
364
+ const currentFilename = fileURLToPath(import.meta.url);
365
+ const currentDirectory = path.dirname(currentFilename);
326
366
 
327
367
  // dist에서 실행될 때는 src로 경로 변경
328
- const basePath = __dirname.includes("/dist/")
329
- ? __dirname.replace("/dist/", "/src/")
330
- : __dirname;
368
+ const basePath = currentDirectory.includes("/dist/")
369
+ ? currentDirectory.replace("/dist/", "/src/")
370
+ : currentDirectory;
331
371
 
332
372
  const rcKeysPath = path.join(basePath, "../../dict/rc-keys.ts");
333
373
 
@@ -11,6 +11,7 @@ import { Sonamu } from "../../api/sonamu";
11
11
  import { EntityManager } from "../../entity/entity-manager";
12
12
  import { type TemplateOptions } from "../../types/types";
13
13
  import { ApiParamType } from "../../types/types";
14
+ import { isStringValue } from "../../utils/runtime-value";
14
15
  import { assertDefined } from "../../utils/utils";
15
16
  import { Template } from "../template";
16
17
  import { BUILT_IN_TYPES, zodTypeToTsTypeDef } from "../zod-converter";
@@ -197,8 +198,7 @@ export function ${methodNameWebSocketCamelized}(
197
198
  .map((param) => {
198
199
  // primitive 타입인지 체크
199
200
  const isPrimitive =
200
- typeof param.type === "string" &&
201
- ["string", "number", "boolean"].includes(param.type);
201
+ isStringValue(param.type) && ["string", "number", "boolean"].includes(param.type);
202
202
 
203
203
  if (isPrimitive) {
204
204
  // primitive: 직접 formData.append