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
@@ -36,7 +36,10 @@ import { importMembers } from "../utils/esm-utils";
36
36
  import { formatCode } from "../utils/formatter";
37
37
  import { exists } from "../utils/fs-utils";
38
38
  import { runtimePath } from "../utils/path-utils";
39
+ import { isStringValue } from "../utils/runtime-value";
39
40
  import { assertDefined, nonNullable } from "../utils/utils";
41
+
42
+ type NestedSelect = { [key: string]: string | NestedSelect };
40
43
  import { EntityManager } from "./entity-manager";
41
44
 
42
45
  export class Entity {
@@ -138,6 +141,7 @@ export class Entity {
138
141
  Object.entries(enums ?? {}).map(([key, enumDef]) => {
139
142
  // cone 추출
140
143
  if ("values" in enumDef && "cone" in enumDef && enumDef.cone) {
144
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
141
145
  this.enumCones[key] = enumDef.cone as Cone;
142
146
  }
143
147
  return [key, getEnumDefValues(enumDef)];
@@ -145,7 +149,8 @@ export class Entity {
145
149
  );
146
150
  this.enums = Object.fromEntries(
147
151
  Object.entries(this.enumLabels).map(([key, enumLabel]) => {
148
- return [key, z.enum(Object.keys(enumLabel) as unknown as readonly [string, ...string[]])];
152
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
153
+ return [key, z.enum(Object.keys(enumLabel))];
149
154
  }),
150
155
  );
151
156
 
@@ -211,11 +216,8 @@ export class Entity {
211
216
  * 이렇게 중첩 객체로 변환하여 select에 넘겨주면 ParseSelectObject 타입이 join된 객체의 타입을 잘 잡아줄 수 있습니다.
212
217
  * 즉, enhancer에서 row를 받았을 때 hydrate된 객체 자체의 nullity와 그 안쪽 필드의 nullity가 fk nullable 여부에 따라 잘 추론됩니다.
213
218
  */
214
- private buildNestedSelectObject(
215
- selectItems: string[],
216
- // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- 반환 오브젝트의 값은 string일 수도 있고 또다른 오브젝트일 수도 있는데, 이를 재귀 타입으로 나타낼 수 없어 any로 처리합니다.
217
- ): Record<string, any> {
218
- const result: ReturnType<typeof this.buildNestedSelectObject> = {};
219
+ private buildNestedSelectObject(selectItems: string[]): NestedSelect {
220
+ const result: NestedSelect = {};
219
221
 
220
222
  for (const selectItem of selectItems) {
221
223
  // "users.id" 또는 "users.id as user__id" 형태 파싱
@@ -238,7 +240,7 @@ export class Entity {
238
240
  for (let i = 0; i < parts.length - 1; i++) {
239
241
  const part = parts[i];
240
242
  if (part in current) {
241
- if (typeof current[part] === "string") {
243
+ if (isStringValue(current[part])) {
242
244
  // 입력이 ["user", "user__id"] 같은 경우!
243
245
  // 애초에 말도 안 되지만 안전하게 예외를 던집니다.
244
246
  throw new Error(
@@ -257,7 +259,7 @@ export class Entity {
257
259
  }
258
260
  }
259
261
 
260
- return result;
262
+ return Object.fromEntries(Object.entries(result));
261
263
  }
262
264
 
263
265
  /**
@@ -279,8 +281,7 @@ export class Entity {
279
281
  * @param withBraces true면 중괄호 포함, false면 내용만 반환
280
282
  */
281
283
  private stringifyNestedSelectObject(
282
- // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- 중첩 오브젝트의 값은 string일 수도 있고 또다른 오브젝트일 수도 있는데, 이를 재귀 타입으로 나타낼 수 없어 any로 처리합니다.
283
- obj: Record<string, any>,
284
+ obj: NestedSelect,
284
285
  indent: number = 0,
285
286
  withBraces: boolean = true,
286
287
  ): string {
@@ -291,7 +292,7 @@ export class Entity {
291
292
  if (entries.length === 0) return withBraces ? "{}" : "";
292
293
 
293
294
  const lines = entries.map(([key, value]) => {
294
- if (typeof value === "string") {
295
+ if (isStringValue(value)) {
295
296
  // 컬럼 경로 (이미 따옴표 포함)
296
297
  return `${innerSpaces}${key}: ${value},`;
297
298
  } else {
@@ -315,10 +316,10 @@ export class Entity {
315
316
  const lines: string[] = [`[`];
316
317
 
317
318
  // 재귀적으로 loader 생성하는 헬퍼 함수
318
- const generateLoaderCode = (loaders: SubsetQuery["loaders"]): string[] => {
319
+ const generateLoaderCode = (nestedLoaders: SubsetQuery["loaders"]): string[] => {
319
320
  const loaderLines: string[] = [];
320
321
 
321
- for (const loader of loaders) {
322
+ for (const loader of nestedLoaders) {
322
323
  const { toTable, toCol, through, fromTable } = loader.manyJoin;
323
324
 
324
325
  // fromTable의 Entity를 가져와서 PK 타입 확인
@@ -449,15 +450,15 @@ export class Entity {
449
450
 
450
451
  const result = Object.keys(subsetGroup).reduce(
451
452
  (r, groupKey) => {
452
- const fields = subsetGroup[groupKey];
453
- assert(fields !== undefined, "fields is undefined");
453
+ const groupedFields = subsetGroup[groupKey];
454
+ assert(groupedFields !== undefined, "fields is undefined");
454
455
 
455
456
  // 현재 테이블 필드셋은 select, virtual에 추가하고 리턴
456
457
  if (groupKey === "") {
457
- const realFields = fields.filter((field) => !isVirtualProp(this.propsDict[field]));
458
+ const realFields = groupedFields.filter((field) => !isVirtualProp(this.propsDict[field]));
458
459
  // virtualType: "code" (또는 undefined)인 virtual prop만 r.virtual에 추가
459
460
  // virtualType: "query"인 경우 사용자가 appendSelect로 직접 추가하므로 제외
460
- const virtualCodeFields = fields.filter((field) =>
461
+ const virtualCodeFields = groupedFields.filter((field) =>
461
462
  isVirtualCodeProp(this.propsDict[field]),
462
463
  );
463
464
 
@@ -483,7 +484,7 @@ export class Entity {
483
484
 
484
485
  if (isOneToOneRelationProp(relation) || isBelongsToOneRelationProp(relation)) {
485
486
  // -One Relation: JOIN 으로 처리
486
- const relFields = fields.map((field) => field.split(".").slice(1).join("."));
487
+ const relFields = groupedFields.map((field) => field.split(".").slice(1).join("."));
487
488
 
488
489
  // -One Relation에서 id 필드만 참조하는 경우 릴레이션 넘기지 않고 리턴
489
490
  if (relFields.length === 1 && relFields[0] === "id") {
@@ -585,7 +586,7 @@ export class Entity {
585
586
  r.joins = r.joins.concat(relSubsetQuery.joins);
586
587
  } else if (isHasManyRelationProp(relation) || isManyToManyRelationProp(relation)) {
587
588
  // -Many Relation: Loader 로 처리
588
- const relFields = fields.map((field) => field.split(".").slice(1).join("."));
589
+ const relFields = groupedFields.map((field) => field.split(".").slice(1).join("."));
589
590
  const relSubsetQuery = relEntity.resolveSubsetQuery("", relFields);
590
591
 
591
592
  let manyJoin: SubsetQuery["loaders"][number]["manyJoin"];
@@ -627,6 +628,7 @@ export class Entity {
627
628
 
628
629
  return r;
629
630
  },
631
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
630
632
  {
631
633
  select: [],
632
634
  virtual: [],
@@ -655,17 +657,18 @@ export class Entity {
655
657
 
656
658
  return result;
657
659
  },
660
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
658
661
  {} as {
659
662
  [k: string]: string[];
660
663
  },
661
664
  );
662
665
 
663
666
  return Object.keys(groups).flatMap<EntityPropNode, EntityPropNode[]>((key) => {
664
- const group = groups[key];
667
+ const groupedFieldExprs = groups[key];
665
668
 
666
669
  // 일반 prop 처리
667
670
  if (key === "") {
668
- return group.map((propName) => {
671
+ return groupedFieldExprs.map((propName) => {
669
672
  const prop = entity.props.find((p) => p.name === propName);
670
673
  if (prop === undefined) {
671
674
  throw new Error(
@@ -682,13 +685,16 @@ export class Entity {
682
685
  // relation prop 처리
683
686
  const prop = entity.propsDict[key];
684
687
  if (!isRelationProp(prop)) {
685
- throw new Error(`잘못된 FieldExpr ${key}.${group[0]}`);
688
+ throw new Error(`잘못된 FieldExpr ${key}.${groupedFieldExprs[0]}`);
686
689
  }
687
690
  const relEntity = EntityManager.get(prop.with);
688
691
 
689
692
  // relation -One 에 id 필드 하나인 경우
690
693
  if (isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop)) {
691
- if (group.length === 1 && (group[0] === "id" || group[0] === "id?")) {
694
+ if (
695
+ groupedFieldExprs.length === 1 &&
696
+ (groupedFieldExprs[0] === "id" || groupedFieldExprs[0] === "id?")
697
+ ) {
692
698
  // id 하나만 있는지 체크해서, 하나만 있으면 상위 prop으로 id를 리턴
693
699
  const idProp = relEntity.propsDict.id;
694
700
  return {
@@ -705,7 +711,7 @@ export class Entity {
705
711
  // -One 그외의 경우 object로 리턴
706
712
  // -Many의 경우 array로 리턴
707
713
  // Recursive 로 뎁스 처리
708
- const children = this.fieldExprsToPropNodes(group, relEntity);
714
+ const children = this.fieldExprsToPropNodes(groupedFieldExprs, relEntity);
709
715
  const nodeType =
710
716
  isBelongsToOneRelationProp(prop) || isOneToOneRelationProp(prop)
711
717
  ? ("object" as const)
@@ -806,6 +812,7 @@ export class Entity {
806
812
  if (isRelationProp(prop)) {
807
813
  // FK를 생성하는 relation만 포함
808
814
  if (this.hasForeignKey(prop)) {
815
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
809
816
  return {
810
817
  name: `${prop.name}_id`,
811
818
  type: "integer",
@@ -852,6 +859,7 @@ export class Entity {
852
859
 
853
860
  if (await exists(typesFilePath)) {
854
861
  const importedMembers = await importMembers<z.ZodTypeAny>(typesFilePath);
862
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
855
863
  this.types = Object.fromEntries(
856
864
  importedMembers.map(({ name, value }) => {
857
865
  EntityManager.setModulePath(name, typesModulePath);
@@ -975,6 +983,7 @@ export class Entity {
975
983
  for (const [propName, cone] of Object.entries(result.propCones)) {
976
984
  const prop = this.props.find((p) => p.name === propName);
977
985
  if (prop) {
986
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
978
987
  (prop as { cone?: Cone }).cone = cone;
979
988
  }
980
989
  }
@@ -996,6 +1005,10 @@ export class Entity {
996
1005
  preserveExisting?: boolean;
997
1006
  onlyEmpty?: boolean;
998
1007
  locale?: "ko" | "en" | "ja";
1008
+ /** 테스트나 대체 LLM 공급자를 위한 텍스트 생성 구현 */
1009
+ generateText?: import("../cone/cone-generator").ConeTextGenerator;
1010
+ /** false이면 생성 결과를 메모리에만 적용하고 entity 파일은 저장하지 않습니다. */
1011
+ persist?: boolean;
999
1012
  }): Promise<import("../cone/cone-generator").ConeGenerationResult> {
1000
1013
  const { generateCones } = await import("../cone/cone-generator");
1001
1014
  const context: import("../cone/cone-generator").ConeGenerationContext = {
@@ -1005,9 +1018,11 @@ export class Entity {
1005
1018
  onlyEmpty: options?.onlyEmpty ?? false,
1006
1019
  };
1007
1020
 
1008
- const result = await generateCones(context);
1021
+ const result = await generateCones(context, { generateText: options?.generateText });
1009
1022
  this.applyCones(result);
1010
- await this.save();
1023
+ if (options?.persist !== false) {
1024
+ await this.save();
1025
+ }
1011
1026
  return result;
1012
1027
  }
1013
1028
 
@@ -1016,28 +1031,28 @@ export class Entity {
1016
1031
  *
1017
1032
  * @returns 키가 "entity:id", "prop:name", "enum:enumId", "subset:key" 형식인 cone 맵
1018
1033
  */
1019
- private collectExistingCones(): Record<string, Cone> {
1020
- const cones: Record<string, Cone> = {};
1034
+ collectExistingCones(): Record<string, Cone> {
1035
+ const coneEntries: [string, Cone][] = [];
1021
1036
 
1022
1037
  if (this.cone) {
1023
- cones[`entity:${this.id}`] = this.cone;
1038
+ coneEntries.push([`entity:${this.id}`, this.cone]);
1024
1039
  }
1025
1040
 
1026
1041
  for (const prop of this.props) {
1027
1042
  if (prop.cone) {
1028
- cones[`prop:${prop.name}`] = prop.cone;
1043
+ coneEntries.push([`prop:${prop.name}`, prop.cone]);
1029
1044
  }
1030
1045
  }
1031
1046
 
1032
1047
  for (const [enumId, cone] of Object.entries(this.enumCones)) {
1033
- cones[`enum:${enumId}`] = cone;
1048
+ coneEntries.push([`enum:${enumId}`, cone]);
1034
1049
  }
1035
1050
 
1036
1051
  for (const [subsetKey, cone] of Object.entries(this.subsetCones)) {
1037
- cones[`subset:${subsetKey}`] = cone;
1052
+ coneEntries.push([`subset:${subsetKey}`, cone]);
1038
1053
  }
1039
1054
 
1040
- return cones;
1055
+ return Object.fromEntries(coneEntries);
1041
1056
  }
1042
1057
 
1043
1058
  /**
@@ -1045,7 +1060,7 @@ export class Entity {
1045
1060
  *
1046
1061
  * @param result - LLM으로 생성된 cone 결과
1047
1062
  */
1048
- private applyCones(result: import("../cone/cone-generator").ConeGenerationResult): void {
1063
+ applyCones(result: import("../cone/cone-generator").ConeGenerationResult): void {
1049
1064
  if (result.entityCone) {
1050
1065
  this.cone = result.entityCone;
1051
1066
  }
@@ -1053,6 +1068,7 @@ export class Entity {
1053
1068
  for (const [propName, cone] of Object.entries(result.propCones)) {
1054
1069
  const prop = this.props.find((p) => p.name === propName);
1055
1070
  if (prop) {
1071
+ // SAFETY: 스키마 검증과 메타데이터 계약이 이 타입을 보장합니다.
1056
1072
  (prop as { cone?: Cone }).cone = cone;
1057
1073
  }
1058
1074
  }
package/src/env.ts CHANGED
@@ -6,11 +6,15 @@ import dotenv from "dotenv";
6
6
  export const SONAMU_ENVIRONMENTS = ["test", "development", "staging", "production"] as const;
7
7
 
8
8
  export type SonamuEnvironment = (typeof SONAMU_ENVIRONMENTS)[number];
9
- export type EnvironmentSnapshot = Record<string, string>;
9
+ export interface EnvironmentSnapshot {
10
+ [key: string]: string;
11
+ }
10
12
  export type EnvironmentSnapshots = Record<SonamuEnvironment, EnvironmentSnapshot>;
11
13
 
12
14
  export function isSonamuEnvironment(value: string | undefined): value is SonamuEnvironment {
13
- return SONAMU_ENVIRONMENTS.includes(value as SonamuEnvironment);
15
+ return SONAMU_ENVIRONMENTS.includes(
16
+ /* SAFETY: 호출 경계의 선행 검증과 소유 타입 계약이 이 값의 타입을 보장한다. */ value as SonamuEnvironment,
17
+ );
14
18
  }
15
19
 
16
20
  export function getSonamuEnvironment(env: NodeJS.ProcessEnv = process.env): SonamuEnvironment {
@@ -87,7 +91,7 @@ export function readAllEnvironmentSnapshots(
87
91
  rootPath: string,
88
92
  baseEnv: NodeJS.ProcessEnv = {},
89
93
  ): EnvironmentSnapshots {
90
- return Object.fromEntries(
94
+ return /* SAFETY: 호출 경계의 선행 검증과 소유 타입 계약이 이 값의 타입을 보장한다. */ Object.fromEntries(
91
95
  SONAMU_ENVIRONMENTS.map((environment) => [
92
96
  environment,
93
97
  readEnvironmentSnapshot(rootPath, environment, baseEnv),
@@ -1,14 +1,29 @@
1
1
  import { type FastifyInstance } from "fastify";
2
2
  import type z from "zod";
3
3
 
4
+ import { isObjectValue, isStringValue } from "../utils/runtime-value";
4
5
  import { isSoException } from "./so-exceptions";
5
6
 
7
+ function isZodIssuePayload<Value>(payload: Value): payload is Value & z.core.$ZodIssue[] {
8
+ return (
9
+ Array.isArray(payload) &&
10
+ payload.every(
11
+ (issue) =>
12
+ isObjectValue(issue) &&
13
+ "message" in issue &&
14
+ isStringValue(issue.message) &&
15
+ "path" in issue &&
16
+ Array.isArray(issue.path),
17
+ )
18
+ );
19
+ }
20
+
6
21
  export function setupErrorHandler(server: FastifyInstance) {
7
22
  server.setErrorHandler((error, request, reply) => {
8
23
  error.statusCode ??= 400;
9
24
 
10
- if (isSoException(error) && error.payload && Array.isArray(error.payload)) {
11
- const issues = error.payload as z.core.$ZodIssue[];
25
+ if (isSoException(error) && isZodIssuePayload(error.payload)) {
26
+ const issues = error.payload;
12
27
  const [issue] = issues;
13
28
  const message = `${issue.message} (${issue.path.join("/")})`;
14
29
 
@@ -1,24 +1,30 @@
1
+ import type z from "zod";
2
+
1
3
  import { type LocalizedString } from "../dict/types";
4
+ import { type JsonValue } from "../types/types";
5
+
6
+ type ExceptionPayload = JsonValue | z.core.$ZodIssue[];
7
+ import { isObjectValue } from "../utils/runtime-value";
2
8
 
3
- export abstract class SoException extends Error {
9
+ export abstract class SoException<Payload = ExceptionPayload> extends Error {
4
10
  constructor(
5
11
  public readonly statusCode: number,
6
12
  public message: LocalizedString,
7
- public payload?: unknown,
13
+ public payload?: Payload,
8
14
  ) {
9
15
  super(String(message));
10
16
  }
11
17
  }
12
18
 
13
- export function isSoException(err: unknown): err is SoException {
14
- return (err as SoException).statusCode !== undefined;
19
+ export function isSoException<Value>(err: Value): err is Value & SoException {
20
+ return isObjectValue(err) && "statusCode" in err;
15
21
  }
16
22
 
17
23
  /*
18
24
  잘못된 매개변수 등 요청사항에 문제가 있는 경우
19
25
  */
20
- export class BadRequestException extends SoException {
21
- constructor(message: LocalizedString, payload?: unknown) {
26
+ export class BadRequestException<Payload = ExceptionPayload> extends SoException<Payload> {
27
+ constructor(message: LocalizedString, payload?: Payload) {
22
28
  super(400, message, payload);
23
29
  }
24
30
  }
@@ -26,8 +32,8 @@ export class BadRequestException extends SoException {
26
32
  /*
27
33
  로그인이 반드시 필요한 케이스에 로그아웃 상태인 경우 / 접근 권한이 없는 요청시
28
34
  */
29
- export class UnauthorizedException extends SoException {
30
- constructor(message: LocalizedString, payload?: unknown) {
35
+ export class UnauthorizedException<Payload = ExceptionPayload> extends SoException<Payload> {
36
+ constructor(message: LocalizedString, payload?: Payload) {
31
37
  super(401, message, payload);
32
38
  }
33
39
  }
@@ -35,8 +41,8 @@ export class UnauthorizedException extends SoException {
35
41
  /*
36
42
  존재하지 않는 레코드에 접근시
37
43
  */
38
- export class NotFoundException extends SoException {
39
- constructor(message: LocalizedString, payload?: unknown) {
44
+ export class NotFoundException<Payload = ExceptionPayload> extends SoException<Payload> {
45
+ constructor(message: LocalizedString, payload?: Payload) {
40
46
  super(404, message, payload);
41
47
  }
42
48
  }
@@ -44,8 +50,8 @@ export class NotFoundException extends SoException {
44
50
  /*
45
51
  현재 상태에서 처리가 불가능한 케이스
46
52
  */
47
- export class ServiceUnavailableException extends SoException {
48
- constructor(message: LocalizedString, payload?: unknown) {
53
+ export class ServiceUnavailableException<Payload = ExceptionPayload> extends SoException<Payload> {
54
+ constructor(message: LocalizedString, payload?: Payload) {
49
55
  super(503, message, payload);
50
56
  }
51
57
  }
@@ -53,8 +59,8 @@ export class ServiceUnavailableException extends SoException {
53
59
  /*
54
60
  내부 처리 로직 (외부 API 콜 포함) 오류 발생시
55
61
  */
56
- export class InternalServerErrorException extends SoException {
57
- constructor(message: LocalizedString, payload?: unknown) {
62
+ export class InternalServerErrorException<Payload = ExceptionPayload> extends SoException<Payload> {
63
+ constructor(message: LocalizedString, payload?: Payload) {
58
64
  super(500, message, payload);
59
65
  }
60
66
  }
@@ -62,8 +68,8 @@ export class InternalServerErrorException extends SoException {
62
68
  /*
63
69
  이미 처리함
64
70
  */
65
- export class AlreadyProcessedException extends SoException {
66
- constructor(message: LocalizedString, payload?: unknown) {
71
+ export class AlreadyProcessedException<Payload = ExceptionPayload> extends SoException<Payload> {
72
+ constructor(message: LocalizedString, payload?: Payload) {
67
73
  super(541, message, payload);
68
74
  }
69
75
  }
@@ -71,8 +77,8 @@ export class AlreadyProcessedException extends SoException {
71
77
  /*
72
78
  중복 허용하지 않는 케이스에 중복 요청
73
79
  */
74
- export class DuplicateRowException extends SoException {
75
- constructor(message: LocalizedString, payload?: unknown) {
80
+ export class DuplicateRowException<Payload = ExceptionPayload> extends SoException<Payload> {
81
+ constructor(message: LocalizedString, payload?: Payload) {
76
82
  super(542, message, payload);
77
83
  }
78
84
  }
@@ -80,8 +86,8 @@ export class DuplicateRowException extends SoException {
80
86
  /*
81
87
  뭔가를 하려고 했으나 대상이 없음
82
88
  */
83
- export class TargetNotFoundException extends SoException {
84
- constructor(message: LocalizedString, payload?: unknown) {
89
+ export class TargetNotFoundException<Payload = ExceptionPayload> extends SoException<Payload> {
90
+ constructor(message: LocalizedString, payload?: Payload) {
85
91
  super(520, message, payload);
86
92
  }
87
93
  }
@@ -1,8 +1,13 @@
1
1
  import { type Entity } from "../entity/entity";
2
2
  import { isEnumProp } from "../types/types";
3
+ import { isObjectValue, isStringValue } from "../utils/runtime-value";
3
4
  import { operatorsByPropType } from "./types";
4
5
  import { type FilterOperator, type FilterQuery } from "./types";
5
6
 
7
+ type DynamicFilterValue = string | number | boolean | Date | null | DynamicFilterValue[];
8
+ type DynamicFilterEntity = { [field: string]: DynamicFilterValue };
9
+ type ConvertedFilterValue<Value> = Value | number | boolean | null | ConvertedFilterValue<Value>[];
10
+
6
11
  // ============================================================
7
12
  // Query Normalization
8
13
  // URL 쿼리 스트링으로 전달된 문자열을 적절한 타입으로 변환
@@ -24,10 +29,10 @@ import { type FilterOperator, type FilterQuery } from "./types";
24
29
  * @param rawFilter Fastify가 파싱한 원본 sonamuFilter 객체
25
30
  * @returns 타입이 변환된 FilterQuery 객체
26
31
  */
27
- export function normalizeFilterQuery<TEntity = Record<string, unknown>>(
28
- rawFilter: unknown,
32
+ export function normalizeFilterQuery<TEntity = DynamicFilterEntity, Value = never>(
33
+ rawFilter: Value,
29
34
  ): FilterQuery<TEntity> {
30
- if (!rawFilter || typeof rawFilter !== "object") {
35
+ if (!rawFilter || !isObjectValue(rawFilter)) {
31
36
  return {};
32
37
  }
33
38
 
@@ -37,7 +42,8 @@ export function normalizeFilterQuery<TEntity = Record<string, unknown>>(
37
42
  if (condition === undefined || condition === null) continue;
38
43
 
39
44
  // 직접 값 (eq와 동일)
40
- if (typeof condition !== "object" || Array.isArray(condition)) {
45
+ if (!isObjectValue(condition) || Array.isArray(condition)) {
46
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
41
47
  normalized[field as keyof TEntity] = convertType(
42
48
  condition,
43
49
  ) as FilterQuery<TEntity>[keyof TEntity];
@@ -45,10 +51,11 @@ export function normalizeFilterQuery<TEntity = Record<string, unknown>>(
45
51
  }
46
52
 
47
53
  // 연산자 객체
48
- const operators: Record<string, unknown> = {};
54
+ const operators = {};
49
55
  for (const [operator, value] of Object.entries(condition)) {
50
- operators[operator] = convertType(value);
56
+ Object.assign(operators, { [operator]: convertType(value) });
51
57
  }
58
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
52
59
  normalized[field as keyof TEntity] = operators as FilterQuery<TEntity>[keyof TEntity];
53
60
  }
54
61
 
@@ -65,14 +72,14 @@ export function normalizeFilterQuery<TEntity = Record<string, unknown>>(
65
72
  * convertType("hello") → "hello"
66
73
  * convertType(["1", "2", "3"]) → [1, 2, 3]
67
74
  */
68
- function convertType(value: unknown): unknown {
75
+ function convertType<Value>(value: Value): ConvertedFilterValue<Value> {
69
76
  // 배열
70
77
  if (Array.isArray(value)) {
71
78
  return value.map(convertType);
72
79
  }
73
80
 
74
81
  // 이미 변환된 타입
75
- if (typeof value !== "string") {
82
+ if (!isStringValue(value)) {
76
83
  return value;
77
84
  }
78
85
 
@@ -109,7 +116,7 @@ function convertType(value: unknown): unknown {
109
116
  * @example
110
117
  * validateSonamuFilters({ status: "active", id: { gte: 1 } }, ProjectEntity);
111
118
  */
112
- export function validateSonamuFilters<TEntity = Record<string, unknown>>(
119
+ export function validateSonamuFilters<TEntity = DynamicFilterEntity>(
113
120
  filters: FilterQuery<TEntity> | undefined,
114
121
  entity: Entity,
115
122
  ): void {
@@ -131,12 +138,14 @@ export function validateSonamuFilters<TEntity = Record<string, unknown>>(
131
138
  }
132
139
 
133
140
  // 해당 prop 타입에 허용되는 연산자 목록
141
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
134
142
  const allowedOperators = (operatorsByPropType[
143
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
135
144
  prop.type as keyof typeof operatorsByPropType
136
145
  ] ?? ["eq"]) as readonly FilterOperator[];
137
146
 
138
147
  // 직접 값인 경우 (eq와 동일)
139
- if (typeof condition !== "object" || Array.isArray(condition)) {
148
+ if (!isObjectValue(condition) || Array.isArray(condition)) {
140
149
  // enum 타입이면 값 검증
141
150
  if (isEnumProp(prop)) {
142
151
  const enumValues = getEnumValues(entity, prop.id);
@@ -149,6 +158,7 @@ export function validateSonamuFilters<TEntity = Record<string, unknown>>(
149
158
 
150
159
  // 2. 연산자 객체인 경우
151
160
  for (const [operator, value] of Object.entries(condition)) {
161
+ // SAFETY: 선행 분기와 함수 계약이 이 타입을 보장합니다.
152
162
  const op = operator as FilterOperator;
153
163
 
154
164
  // 연산자가 해당 타입에서 지원되는지 검증
@@ -195,7 +205,7 @@ export function validateSonamuFilters<TEntity = Record<string, unknown>>(
195
205
  /**
196
206
  * Enum 값 검증 helper
197
207
  */
198
- function validateEnumValue(field: string, value: unknown, enumValues: string[]): void {
208
+ function validateEnumValue<Value>(field: string, value: Value, enumValues: string[]): void {
199
209
  if (value === null || value === undefined) {
200
210
  return;
201
211
  }
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@ export * from "./api/base-frame";
2
2
  export * from "./api/config";
3
3
  export type * from "./api/context";
4
4
  export * from "./api/decorators";
5
+ export { fastifyCaster } from "./api/caster";
6
+ export { getZodObjectFromApi } from "./api/code-converters";
7
+ export * from "./api/http-validator";
5
8
  export * from "./api/sonamu";
6
9
  export * from "./auth";
7
10
  export { cache } from "./cache/decorator";