sonamu 0.10.12 → 0.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. package/dist/ai/agents/agent.d.ts +1 -1
  2. package/dist/ai/agents/agent.d.ts.map +1 -1
  3. package/dist/ai/agents/agent.js +5 -6
  4. package/dist/ai/agents/types.d.ts +1 -1
  5. package/dist/ai/agents/types.d.ts.map +1 -1
  6. package/dist/ai/providers/rtzr/api.js +18 -18
  7. package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
  8. package/dist/ai/providers/rtzr/error.js +17 -15
  9. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  10. package/dist/ai/providers/rtzr/model.js +3 -2
  11. package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
  12. package/dist/ai/providers/rtzr/options.js +16 -16
  13. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
  14. package/dist/ai/providers/rtzr/provider.js +9 -9
  15. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  16. package/dist/ai/providers/rtzr/utils.js +1 -1
  17. package/dist/api/caster.d.ts.map +1 -1
  18. package/dist/api/caster.js +17 -17
  19. package/dist/api/code-converters.d.ts +4 -2
  20. package/dist/api/code-converters.d.ts.map +1 -1
  21. package/dist/api/code-converters.js +100 -19
  22. package/dist/api/config.d.ts +16 -1
  23. package/dist/api/config.d.ts.map +1 -1
  24. package/dist/api/config.js +28 -21
  25. package/dist/api/context.d.ts +2 -2
  26. package/dist/api/context.d.ts.map +1 -1
  27. package/dist/api/context.js +1 -1
  28. package/dist/api/decorators.d.ts.map +1 -1
  29. package/dist/api/decorators.js +18 -11
  30. package/dist/api/http-validator.d.ts +36 -0
  31. package/dist/api/http-validator.d.ts.map +1 -0
  32. package/dist/api/http-validator.js +72 -0
  33. package/dist/api/index.js +6 -4
  34. package/dist/api/sonamu.d.ts +59 -20
  35. package/dist/api/sonamu.d.ts.map +1 -1
  36. package/dist/api/sonamu.js +250 -106
  37. package/dist/api/websocket-helpers.d.ts +7 -5
  38. package/dist/api/websocket-helpers.d.ts.map +1 -1
  39. package/dist/api/websocket-helpers.js +6 -6
  40. package/dist/auth/audit-log/builders.d.ts.map +1 -1
  41. package/dist/auth/audit-log/builders.js +1 -1
  42. package/dist/auth/audit-log/client-ip.d.ts +1 -1
  43. package/dist/auth/audit-log/client-ip.d.ts.map +1 -1
  44. package/dist/auth/audit-log/client-ip.js +4 -2
  45. package/dist/auth/audit-log/events.d.ts +5 -1
  46. package/dist/auth/audit-log/events.d.ts.map +1 -1
  47. package/dist/auth/audit-log/events.js +1 -1
  48. package/dist/auth/audit-log/plugin.d.ts +0 -8
  49. package/dist/auth/audit-log/plugin.d.ts.map +1 -1
  50. package/dist/auth/audit-log/plugin.js +48 -52
  51. package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
  52. package/dist/auth/audit-log-ingestor.js +16 -12
  53. package/dist/auth/auth-generator.d.ts.map +1 -1
  54. package/dist/auth/auth-generator.js +1 -1
  55. package/dist/auth/better-auth-request.d.ts.map +1 -1
  56. package/dist/auth/better-auth-request.js +6 -5
  57. package/dist/auth/knex-adapter.js +1 -1
  58. package/dist/auth/plugins/entity-definitions/index.d.ts +13 -1
  59. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  60. package/dist/auth/plugins/entity-definitions/index.js +1 -1
  61. package/dist/auth/plugins/wrappers/admin.d.ts +1 -1
  62. package/dist/bin/build-config.d.ts +25 -0
  63. package/dist/bin/build-config.d.ts.map +1 -1
  64. package/dist/bin/build-config.js +105 -2
  65. package/dist/bin/cli.js +131 -27
  66. package/dist/bin/compiler-policy.d.ts +3 -0
  67. package/dist/bin/compiler-policy.d.ts.map +1 -0
  68. package/dist/bin/compiler-policy.js +24 -0
  69. package/dist/bin/fixture.d.ts.map +1 -1
  70. package/dist/bin/fixture.js +5 -3
  71. package/dist/bin/test-command.d.ts +22 -1
  72. package/dist/bin/test-command.d.ts.map +1 -1
  73. package/dist/bin/test-command.js +18 -10
  74. package/dist/bin/ts-loader-registration.d.ts +7 -1
  75. package/dist/bin/ts-loader-registration.d.ts.map +1 -1
  76. package/dist/bin/ts-loader-registration.js +13 -4
  77. package/dist/cache/decorator.d.ts.map +1 -1
  78. package/dist/cache/decorator.js +11 -4
  79. package/dist/compress/compress.d.ts.map +1 -1
  80. package/dist/compress/compress.js +1 -1
  81. package/dist/cone/cone-generator.d.ts +14 -1
  82. package/dist/cone/cone-generator.d.ts.map +1 -1
  83. package/dist/cone/cone-generator.js +13 -9
  84. package/dist/database/_batch_update.js +1 -1
  85. package/dist/database/base-model.d.ts +22 -10
  86. package/dist/database/base-model.d.ts.map +1 -1
  87. package/dist/database/base-model.js +26 -16
  88. package/dist/database/base-model.types.d.ts +15 -3
  89. package/dist/database/base-model.types.d.ts.map +1 -1
  90. package/dist/database/db.d.ts +12 -2
  91. package/dist/database/db.d.ts.map +1 -1
  92. package/dist/database/db.js +10 -7
  93. package/dist/database/knex.d.ts.map +1 -1
  94. package/dist/database/knex.js +17 -14
  95. package/dist/database/puri.d.ts +11 -9
  96. package/dist/database/puri.d.ts.map +1 -1
  97. package/dist/database/puri.js +93 -79
  98. package/dist/database/puri.types.d.ts +21 -21
  99. package/dist/database/puri.types.d.ts.map +1 -1
  100. package/dist/database/puri.types.js +1 -1
  101. package/dist/database/upsert-builder.d.ts +12 -10
  102. package/dist/database/upsert-builder.d.ts.map +1 -1
  103. package/dist/database/upsert-builder.js +20 -12
  104. package/dist/dict/sd.d.ts.map +1 -1
  105. package/dist/dict/sd.js +1 -1
  106. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  107. package/dist/dict/sonamu-dictionary.js +1 -1
  108. package/dist/dict/utils.d.ts.map +1 -1
  109. package/dist/dict/utils.js +7 -2
  110. package/dist/entity/entity-manager.d.ts +13 -4
  111. package/dist/entity/entity-manager.d.ts.map +1 -1
  112. package/dist/entity/entity-manager.js +19 -11
  113. package/dist/entity/entity-template-cone.d.ts +73 -5
  114. package/dist/entity/entity-template-cone.d.ts.map +1 -1
  115. package/dist/entity/entity-template-cone.js +4 -2
  116. package/dist/entity/entity.d.ts +6 -2
  117. package/dist/entity/entity.d.ts.map +1 -1
  118. package/dist/entity/entity.js +30 -28
  119. package/dist/env.d.ts +3 -1
  120. package/dist/env.d.ts.map +1 -1
  121. package/dist/env.js +1 -1
  122. package/dist/exceptions/error-handler.d.ts.map +1 -1
  123. package/dist/exceptions/error-handler.js +7 -2
  124. package/dist/exceptions/so-exceptions.d.ts +24 -20
  125. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  126. package/dist/exceptions/so-exceptions.js +4 -2
  127. package/dist/filter/utils.d.ts +7 -2
  128. package/dist/filter/utils.d.ts.map +1 -1
  129. package/dist/filter/utils.js +8 -6
  130. package/dist/index.d.ts +3 -0
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +12 -6
  133. package/dist/logger/configure.d.ts.map +1 -1
  134. package/dist/logger/configure.js +10 -10
  135. package/dist/migration/code-generation.d.ts.map +1 -1
  136. package/dist/migration/code-generation.js +50 -46
  137. package/dist/migration/index-where-predicate.d.ts.map +1 -1
  138. package/dist/migration/index-where-predicate.js +22 -8
  139. package/dist/migration/migration-set.d.ts.map +1 -1
  140. package/dist/migration/migration-set.js +1 -1
  141. package/dist/migration/migrator.d.ts +3 -2
  142. package/dist/migration/migrator.d.ts.map +1 -1
  143. package/dist/migration/migrator.js +27 -21
  144. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  145. package/dist/migration/postgresql-schema-reader.js +51 -36
  146. package/dist/migration/slack-confirm.d.ts.map +1 -1
  147. package/dist/migration/slack-confirm.js +1 -1
  148. package/dist/naite/naite-reporter.js +2 -2
  149. package/dist/naite/naite.d.ts +74 -15
  150. package/dist/naite/naite.d.ts.map +1 -1
  151. package/dist/naite/naite.js +5 -19
  152. package/dist/runtime-type.d.ts +6 -0
  153. package/dist/runtime-type.d.ts.map +1 -0
  154. package/dist/runtime-type.js +26 -0
  155. package/dist/ssr/hmr-opt-out.d.ts +26 -0
  156. package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
  157. package/dist/ssr/hmr-opt-out.js +59 -0
  158. package/dist/ssr/renderer.d.ts.map +1 -1
  159. package/dist/ssr/renderer.js +4 -1
  160. package/dist/storage/base-file.js +14 -14
  161. package/dist/storage/buffered-file.d.ts +2 -2
  162. package/dist/storage/buffered-file.d.ts.map +1 -1
  163. package/dist/storage/buffered-file.js +12 -12
  164. package/dist/storage/s3-driver.d.ts.map +1 -1
  165. package/dist/storage/s3-driver.js +1 -1
  166. package/dist/storage/uploaded-file.d.ts +2 -2
  167. package/dist/storage/uploaded-file.d.ts.map +1 -1
  168. package/dist/storage/uploaded-file.js +8 -8
  169. package/dist/stream/sse.js +11 -11
  170. package/dist/stream/ws-core.d.ts +1 -1
  171. package/dist/stream/ws-core.d.ts.map +1 -1
  172. package/dist/stream/ws-delivery.d.ts +1 -1
  173. package/dist/stream/ws-delivery.d.ts.map +1 -1
  174. package/dist/stream/ws-delivery.js +6 -4
  175. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  176. package/dist/stream/ws-local-connection-store.js +4 -2
  177. package/dist/stream/ws-presence-store.js +1 -1
  178. package/dist/stream/ws-registry.d.ts +4 -4
  179. package/dist/stream/ws-registry.d.ts.map +1 -1
  180. package/dist/stream/ws-registry.js +4 -2
  181. package/dist/stream/ws-telemetry.d.ts +16 -12
  182. package/dist/stream/ws-telemetry.d.ts.map +1 -1
  183. package/dist/stream/ws-telemetry.js +22 -18
  184. package/dist/stream/ws.d.ts +18 -12
  185. package/dist/stream/ws.d.ts.map +1 -1
  186. package/dist/stream/ws.js +26 -22
  187. package/dist/syncer/api-parser.js +1 -1
  188. package/dist/syncer/checksum.d.ts.map +1 -1
  189. package/dist/syncer/checksum.js +7 -8
  190. package/dist/syncer/code-generator.d.ts.map +1 -1
  191. package/dist/syncer/code-generator.js +13 -14
  192. package/dist/syncer/entity-operations.d.ts.map +1 -1
  193. package/dist/syncer/entity-operations.js +5 -6
  194. package/dist/syncer/file-copy.d.ts +5 -0
  195. package/dist/syncer/file-copy.d.ts.map +1 -0
  196. package/dist/syncer/file-copy.js +36 -0
  197. package/dist/syncer/file-patterns.d.ts +1 -0
  198. package/dist/syncer/file-patterns.d.ts.map +1 -1
  199. package/dist/syncer/file-patterns.js +5 -2
  200. package/dist/syncer/file-tracking.d.ts.map +1 -1
  201. package/dist/syncer/file-tracking.js +5 -4
  202. package/dist/syncer/filesystem-dependencies.d.ts +15 -0
  203. package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
  204. package/dist/syncer/filesystem-dependencies.js +53 -0
  205. package/dist/syncer/module-loader.d.ts +1 -1
  206. package/dist/syncer/module-loader.d.ts.map +1 -1
  207. package/dist/syncer/module-loader.js +6 -4
  208. package/dist/syncer/syncer-actions.d.ts +4 -0
  209. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  210. package/dist/syncer/syncer-actions.js +34 -17
  211. package/dist/syncer/syncer.d.ts +17 -6
  212. package/dist/syncer/syncer.d.ts.map +1 -1
  213. package/dist/syncer/syncer.js +71 -29
  214. package/dist/syncer/watcher.d.ts.map +1 -1
  215. package/dist/syncer/watcher.js +3 -3
  216. package/dist/tasks/decorator.d.ts +6 -6
  217. package/dist/tasks/decorator.d.ts.map +1 -1
  218. package/dist/tasks/decorator.js +4 -4
  219. package/dist/tasks/step-wrapper.d.ts +6 -4
  220. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  221. package/dist/tasks/step-wrapper.js +5 -5
  222. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  223. package/dist/tasks/workflow-manager.js +13 -7
  224. package/dist/template/entity-converter.d.ts +1 -1
  225. package/dist/template/entity-converter.d.ts.map +1 -1
  226. package/dist/template/entity-converter.js +2 -2
  227. package/dist/template/helpers.d.ts +8 -6
  228. package/dist/template/helpers.d.ts.map +1 -1
  229. package/dist/template/helpers.js +1 -1
  230. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  231. package/dist/template/implementations/generated.template.js +4 -4
  232. package/dist/template/implementations/generated_http.template.d.ts +7 -4
  233. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  234. package/dist/template/implementations/generated_http.template.js +21 -21
  235. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  236. package/dist/template/implementations/generated_sso.template.js +6 -3
  237. package/dist/template/implementations/http_validators.template.d.ts +18 -0
  238. package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
  239. package/dist/template/implementations/http_validators.template.js +127 -0
  240. package/dist/template/implementations/model.template.d.ts.map +1 -1
  241. package/dist/template/implementations/model.template.js +1 -1
  242. package/dist/template/implementations/queries.template.js +1 -1
  243. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  244. package/dist/template/implementations/sd.template.js +79 -39
  245. package/dist/template/implementations/services.template.d.ts.map +1 -1
  246. package/dist/template/implementations/services.template.js +6 -4
  247. package/dist/template/implementations/view_form.template.d.ts +6 -1
  248. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  249. package/dist/template/implementations/view_form.template.js +23 -15
  250. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  251. package/dist/template/implementations/view_list.template.js +11 -6
  252. package/dist/template/template-manager.d.ts.map +1 -1
  253. package/dist/template/template-manager.js +9 -7
  254. package/dist/template/template-types.d.ts +12 -5
  255. package/dist/template/template-types.d.ts.map +1 -1
  256. package/dist/template/template-types.js +1 -1
  257. package/dist/template/template.d.ts +2 -2
  258. package/dist/template/template.d.ts.map +1 -1
  259. package/dist/template/template.js +6 -4
  260. package/dist/template/zod-converter.d.ts +0 -19
  261. package/dist/template/zod-converter.d.ts.map +1 -1
  262. package/dist/template/zod-converter.js +100 -84
  263. package/dist/testing/_relation-graph.d.ts.map +1 -1
  264. package/dist/testing/_relation-graph.js +2 -1
  265. package/dist/testing/bootstrap.d.ts.map +1 -1
  266. package/dist/testing/bootstrap.js +9 -6
  267. package/dist/testing/data-explorer.d.ts +32 -7
  268. package/dist/testing/data-explorer.d.ts.map +1 -1
  269. package/dist/testing/data-explorer.js +77 -31
  270. package/dist/testing/dev-test-routes.d.ts +8 -1
  271. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  272. package/dist/testing/dev-test-routes.js +125 -108
  273. package/dist/testing/dev-vitest-manager.d.ts +54 -8
  274. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  275. package/dist/testing/dev-vitest-manager.js +24 -9
  276. package/dist/testing/faker-mappings.d.ts +412 -1
  277. package/dist/testing/faker-mappings.d.ts.map +1 -1
  278. package/dist/testing/faker-mappings.js +1 -1
  279. package/dist/testing/filesystem.d.ts +2 -0
  280. package/dist/testing/filesystem.d.ts.map +1 -0
  281. package/dist/testing/filesystem.js +7 -0
  282. package/dist/testing/fixture-generator.d.ts +25 -8
  283. package/dist/testing/fixture-generator.d.ts.map +1 -1
  284. package/dist/testing/fixture-generator.js +132 -73
  285. package/dist/testing/fixture-loader.d.ts +3 -1
  286. package/dist/testing/fixture-loader.d.ts.map +1 -1
  287. package/dist/testing/fixture-loader.js +1 -20
  288. package/dist/testing/fixture-manager.d.ts +8 -7
  289. package/dist/testing/fixture-manager.d.ts.map +1 -1
  290. package/dist/testing/fixture-manager.js +91 -59
  291. package/dist/testing/global-setup.d.ts.map +1 -1
  292. package/dist/testing/global-setup.js +5 -2
  293. package/dist/testing/index.d.ts +2 -0
  294. package/dist/testing/index.d.ts.map +1 -1
  295. package/dist/testing/index.js +6 -3
  296. package/dist/types/types.d.ts +101 -99
  297. package/dist/types/types.d.ts.map +1 -1
  298. package/dist/types/types.js +234 -225
  299. package/dist/ui/ai-api.d.ts.map +1 -1
  300. package/dist/ui/ai-api.js +1 -1
  301. package/dist/ui/ai-client.d.ts.map +1 -1
  302. package/dist/ui/ai-client.js +28 -18
  303. package/dist/ui/api.d.ts.map +1 -1
  304. package/dist/ui/api.js +135 -128
  305. package/dist/ui/async-route.d.ts +3 -0
  306. package/dist/ui/async-route.d.ts.map +1 -0
  307. package/dist/ui/async-route.js +12 -0
  308. package/dist/ui/cdd-service.d.ts +9 -7
  309. package/dist/ui/cdd-service.d.ts.map +1 -1
  310. package/dist/ui/cdd-service.js +7 -5
  311. package/dist/ui/migrations-api.d.ts.map +1 -1
  312. package/dist/ui/migrations-api.js +17 -15
  313. package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
  314. package/dist/ui-web/index.html +1 -1
  315. package/dist/utils/formatter.d.ts.map +1 -1
  316. package/dist/utils/formatter.js +7 -5
  317. package/dist/utils/fs-utils.d.ts +3 -3
  318. package/dist/utils/fs-utils.d.ts.map +1 -1
  319. package/dist/utils/fs-utils.js +6 -6
  320. package/dist/utils/model.d.ts +3 -5
  321. package/dist/utils/model.d.ts.map +1 -1
  322. package/dist/utils/model.js +1 -1
  323. package/dist/utils/object-utils.d.ts +1 -1
  324. package/dist/utils/object-utils.d.ts.map +1 -1
  325. package/dist/utils/object-utils.js +17 -16
  326. package/dist/utils/process-utils.d.ts.map +1 -1
  327. package/dist/utils/process-utils.js +4 -2
  328. package/dist/utils/runtime-value.d.ts +13 -0
  329. package/dist/utils/runtime-value.d.ts.map +1 -0
  330. package/dist/utils/runtime-value.js +40 -0
  331. package/dist/utils/sql-parser.d.ts +8 -1
  332. package/dist/utils/sql-parser.d.ts.map +1 -1
  333. package/dist/utils/sql-parser.js +6 -3
  334. package/dist/utils/type-utils.d.ts.map +1 -1
  335. package/dist/utils/type-utils.js +10 -8
  336. package/dist/utils/utils.d.ts +2 -2
  337. package/dist/utils/utils.d.ts.map +1 -1
  338. package/dist/utils/utils.js +11 -8
  339. package/dist/utils/zod-error.d.ts.map +1 -1
  340. package/dist/utils/zod-error.js +7 -4
  341. package/dist/vector/chunking.d.ts.map +1 -1
  342. package/dist/vector/chunking.js +1 -1
  343. package/dist/vector/embedding.d.ts.map +1 -1
  344. package/dist/vector/embedding.js +1 -1
  345. package/dist/vector/types.d.ts +3 -2
  346. package/dist/vector/types.d.ts.map +1 -1
  347. package/package.json +5 -3
  348. package/src/ai/agents/agent.ts +5 -5
  349. package/src/ai/agents/types.ts +1 -1
  350. package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
  351. package/src/ai/providers/rtzr/error.ts +5 -3
  352. package/src/ai/providers/rtzr/model.ts +3 -2
  353. package/src/ai/providers/rtzr/options.ts +10 -5
  354. package/src/ai/providers/rtzr/provider.ts +10 -10
  355. package/src/ai/providers/rtzr/utils.ts +3 -1
  356. package/src/api/__tests__/config.test.ts +11 -21
  357. package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
  358. package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
  359. package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
  360. package/src/api/caster.ts +6 -8
  361. package/src/api/code-converters.ts +136 -36
  362. package/src/api/config.ts +62 -4
  363. package/src/api/context.ts +3 -3
  364. package/src/api/decorators.ts +28 -17
  365. package/src/api/http-validator.ts +149 -0
  366. package/src/api/sonamu.ts +459 -152
  367. package/src/api/websocket-helpers.ts +24 -17
  368. package/src/auth/audit-log/builders.ts +3 -2
  369. package/src/auth/audit-log/client-ip.ts +4 -2
  370. package/src/auth/audit-log/events.ts +15 -1
  371. package/src/auth/audit-log/plugin.ts +129 -200
  372. package/src/auth/audit-log-ingestor.ts +19 -15
  373. package/src/auth/auth-generator.ts +10 -5
  374. package/src/auth/better-auth-request.ts +4 -3
  375. package/src/auth/knex-adapter.ts +8 -2
  376. package/src/auth/plugins/entity-definitions/index.ts +6 -5
  377. package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
  378. package/src/bin/__tests__/test-command.test.ts +9 -15
  379. package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
  380. package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
  381. package/src/bin/build-config.ts +167 -0
  382. package/src/bin/cli.ts +153 -36
  383. package/src/bin/compiler-policy.ts +35 -0
  384. package/src/bin/fixture.ts +14 -3
  385. package/src/bin/test-command.ts +74 -29
  386. package/src/bin/ts-loader-registration.ts +22 -4
  387. package/src/cache/decorator.ts +10 -3
  388. package/src/compress/compress.ts +17 -4
  389. package/src/cone/cone-generator.ts +34 -6
  390. package/src/database/__tests__/db.test.ts +1 -0
  391. package/src/database/__tests__/puri.test.ts +16 -15
  392. package/src/database/__tests__/transaction-scope.test.ts +5 -0
  393. package/src/database/_batch_update.ts +1 -0
  394. package/src/database/base-model.ts +81 -50
  395. package/src/database/base-model.types.ts +6 -6
  396. package/src/database/db.ts +29 -19
  397. package/src/database/knex.ts +30 -17
  398. package/src/database/puri-subset.test-d.ts +17 -0
  399. package/src/database/puri.ts +181 -143
  400. package/src/database/puri.types.test-d.ts +31 -1
  401. package/src/database/puri.types.ts +23 -21
  402. package/src/database/upsert-builder.ts +75 -53
  403. package/src/dict/sd.ts +15 -5
  404. package/src/dict/sonamu-dictionary.ts +6 -6
  405. package/src/dict/utils.ts +9 -1
  406. package/src/entity/entity-manager.ts +26 -9
  407. package/src/entity/entity-template-cone.ts +5 -10
  408. package/src/entity/entity.ts +50 -34
  409. package/src/env.ts +7 -3
  410. package/src/exceptions/error-handler.ts +17 -2
  411. package/src/exceptions/so-exceptions.ts +26 -20
  412. package/src/filter/utils.ts +21 -11
  413. package/src/index.ts +3 -0
  414. package/src/logger/configure.ts +26 -18
  415. package/src/migration/__tests__/migrator.test.ts +50 -57
  416. package/src/migration/code-generation.ts +132 -104
  417. package/src/migration/index-where-predicate.ts +51 -24
  418. package/src/migration/migration-set.ts +11 -5
  419. package/src/migration/migrator.ts +68 -41
  420. package/src/migration/postgresql-schema-reader.ts +52 -47
  421. package/src/migration/slack-confirm.ts +17 -15
  422. package/src/naite/naite-reporter.ts +1 -1
  423. package/src/naite/naite.ts +97 -24
  424. package/src/runtime-type.ts +34 -0
  425. package/src/ssr/hmr-opt-out.ts +78 -0
  426. package/src/ssr/renderer.ts +6 -0
  427. package/src/storage/base-file.ts +13 -13
  428. package/src/storage/buffered-file.ts +11 -11
  429. package/src/storage/s3-driver.ts +4 -1
  430. package/src/storage/uploaded-file.ts +8 -8
  431. package/src/stream/__tests__/ws-contracts.test.ts +4 -2
  432. package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
  433. package/src/stream/__tests__/ws.test.ts +30 -6
  434. package/src/stream/sse.ts +12 -11
  435. package/src/stream/ws-core.ts +1 -1
  436. package/src/stream/ws-delivery.ts +16 -7
  437. package/src/stream/ws-local-connection-store.ts +2 -3
  438. package/src/stream/ws-presence-store.ts +6 -1
  439. package/src/stream/ws-registry.ts +16 -7
  440. package/src/stream/ws-telemetry.ts +66 -43
  441. package/src/stream/ws.ts +73 -39
  442. package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
  443. package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
  444. package/src/syncer/api-parser.ts +67 -27
  445. package/src/syncer/checksum.ts +17 -11
  446. package/src/syncer/code-generator.ts +25 -17
  447. package/src/syncer/entity-operations.ts +3 -5
  448. package/src/syncer/file-copy.ts +46 -0
  449. package/src/syncer/file-patterns.ts +11 -8
  450. package/src/syncer/file-tracking.ts +3 -4
  451. package/src/syncer/filesystem-dependencies.ts +73 -0
  452. package/src/syncer/module-loader.ts +8 -4
  453. package/src/syncer/syncer-actions.ts +56 -18
  454. package/src/syncer/syncer.ts +116 -43
  455. package/src/syncer/watcher.ts +8 -4
  456. package/src/tasks/decorator.ts +20 -12
  457. package/src/tasks/step-wrapper.ts +19 -9
  458. package/src/tasks/workflow-manager.ts +15 -9
  459. package/src/template/__tests__/http_validators.template.test.ts +160 -0
  460. package/src/template/__tests__/services.template.mutation.test.ts +24 -23
  461. package/src/template/__tests__/services.template.websocket.test.ts +21 -25
  462. package/src/template/entity-converter.ts +5 -2
  463. package/src/template/helpers.ts +8 -9
  464. package/src/template/implementations/generated.template.ts +5 -4
  465. package/src/template/implementations/generated_http.template.ts +61 -15
  466. package/src/template/implementations/generated_sso.template.ts +15 -4
  467. package/src/template/implementations/http_validators.template.ts +175 -0
  468. package/src/template/implementations/model.template.ts +3 -1
  469. package/src/template/implementations/sd.template.ts +81 -41
  470. package/src/template/implementations/services.template.ts +2 -2
  471. package/src/template/implementations/view_form.template.ts +56 -39
  472. package/src/template/implementations/view_list.template.ts +31 -23
  473. package/src/template/template-manager.ts +15 -13
  474. package/src/template/template-types.ts +13 -5
  475. package/src/template/template.ts +4 -3
  476. package/src/template/zod-converter.ts +206 -91
  477. package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
  478. package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
  479. package/src/testing/_relation-graph.ts +4 -1
  480. package/src/testing/bootstrap.ts +12 -5
  481. package/src/testing/data-explorer.ts +102 -53
  482. package/src/testing/dev-test-routes.ts +87 -50
  483. package/src/testing/dev-vitest-manager.ts +88 -32
  484. package/src/testing/faker-mappings.ts +2 -2
  485. package/src/testing/filesystem.ts +1 -0
  486. package/src/testing/fixture-generator.ts +191 -131
  487. package/src/testing/fixture-loader.ts +5 -1
  488. package/src/testing/fixture-manager.ts +129 -90
  489. package/src/testing/global-setup.ts +7 -3
  490. package/src/testing/index.ts +5 -0
  491. package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
  492. package/src/types/types.ts +184 -158
  493. package/src/ui/ai-api.ts +9 -7
  494. package/src/ui/ai-client.ts +61 -56
  495. package/src/ui/api.ts +727 -616
  496. package/src/ui/async-route.ts +4 -0
  497. package/src/ui/cdd-service.ts +35 -12
  498. package/src/ui/migrations-api.ts +63 -51
  499. package/src/utils/formatter.ts +7 -3
  500. package/src/utils/fs-utils.ts +5 -5
  501. package/src/utils/model.ts +4 -5
  502. package/src/utils/object-utils.ts +32 -22
  503. package/src/utils/process-utils.ts +4 -1
  504. package/src/utils/runtime-value.ts +43 -0
  505. package/src/utils/sql-parser.ts +13 -6
  506. package/src/utils/type-utils.ts +9 -7
  507. package/src/utils/utils.ts +19 -21
  508. package/src/utils/zod-error.ts +4 -2
  509. package/src/vector/chunking.ts +6 -1
  510. package/src/vector/embedding.ts +2 -1
  511. package/src/vector/types.ts +4 -2
  512. package/tsdown.api.config.ts +5 -2
  513. package/dist/ui-web/assets/index-C0m1pk6K.js +0 -177
@@ -1,5 +1,7 @@
1
+ import { type LanguageModel } from "ai";
1
2
  import chalk from "chalk";
2
3
  import { type Knex } from "knex";
4
+ import { z } from "zod";
3
5
 
4
6
  import { type SonamuDBPreset } from "../database/db";
5
7
  import { type Entity } from "../entity/entity";
@@ -7,27 +9,75 @@ import { type EntityManager } from "../entity/entity-manager";
7
9
  import { type EntityProp, type FixtureImportResult, type FixtureRecord } from "../types/types";
8
10
  import { isBelongsToOneRelationProp, isOneToOneRelationProp, isRelationProp } from "../types/types";
9
11
  import { isTest } from "../utils/controller";
10
- import { DataExplorer } from "./data-explorer";
12
+ import { isFunctionValue } from "../utils/runtime-value";
13
+ import { DataExplorer, dataExplorerRecordSchema, dataExplorerValueSchema } from "./data-explorer";
14
+ import { dataSourceConfigSchema } from "./data-explorer";
15
+ import { type DataExplorerRecord, type DataExplorerValue } from "./data-explorer";
11
16
  import { type ExploreWithRelationsOptions, type ExploreWithRelationsResult } from "./data-explorer";
12
17
  import { fakerMappings } from "./faker-mappings";
13
18
  import { type FakerMappings } from "./faker-mappings";
14
19
  import { FixtureManager } from "./fixture-manager";
20
+ import { type FixtureSourceRecord } from "./fixture-manager";
15
21
 
16
22
  export type Locale = "ko" | "en" | "ja";
23
+ export type FixtureOverrides = object;
24
+
25
+ type FakerFunction = (...args: DataExplorerValue[]) => DataExplorerValue;
26
+ type FakerNamespaceMember = FakerFunction | FakerNamespace;
27
+ type FakerNamespace = { [name: string]: FakerNamespaceMember };
28
+
29
+ const fakerFunctionSchema = z.custom<FakerFunction>(
30
+ isFunctionValue,
31
+ "Faker 경로의 마지막 값은 함수여야 합니다.",
32
+ );
33
+ const fakerNamespaceObjectSchema = z.object({});
34
+ const dynamicNamespaceSchema = z.custom<FakerNamespace>(
35
+ (candidate) => fakerNamespaceObjectSchema.safeParse(candidate).success,
36
+ "Faker 경로는 객체 namespace여야 합니다.",
37
+ );
38
+ const generatorArgumentsSchema = z.array(dataExplorerValueSchema);
39
+ const fixtureRecordIdSchema = z.union([z.number(), z.string()]);
40
+ const fixtureWhereValueSchema = z.union([z.string(), z.number(), z.boolean(), z.date(), z.null()]);
41
+
42
+ export type FixtureGenerationSpec = {
43
+ entity: string;
44
+ count: number;
45
+ overrides?: FixtureOverrides;
46
+ };
47
+
48
+ type GeneratedFixture = {
49
+ entity: string;
50
+ data: DataExplorerRecord;
51
+ explicitId?: boolean;
52
+ };
53
+
54
+ function parseFixtureSourceRecord(record: DataExplorerRecord): FixtureSourceRecord {
55
+ return {
56
+ ...record,
57
+ id: fixtureRecordIdSchema.parse(record.id),
58
+ };
59
+ }
17
60
 
18
61
  export type FixtureGeneratorOptions = {
19
62
  locale?: Locale;
20
63
  useLLM?: boolean;
21
64
  enableLLMCache?: boolean;
22
65
  llmModel?: string;
66
+ /** 테스트나 대체 LLM 공급자를 위한 텍스트 생성 구현 */
67
+ generateText?: FixtureTextGenerator;
23
68
  };
24
69
 
70
+ export type FixtureTextGenerator = (request: {
71
+ model: LanguageModel;
72
+ prompt: string;
73
+ }) => Promise<{ text: string; usage?: { totalTokens?: number } }>;
74
+
25
75
  export type GeneratorContext = {
26
76
  /** 생성 중인 fixture들 (메모리 상) */
27
- fixtures: Map<string, Record<string, unknown>>;
77
+ fixtures: Map<string, DataExplorerRecord>;
28
78
 
29
79
  /** 참조 데이터 캐시 (DataExplorer 결과) */
30
- referenceCache: Map<string, Record<string, unknown>[]>;
80
+ referenceCache: Map<string, DataExplorerRecord[]>;
31
81
 
32
82
  /** 이미 import된 레코드를 추적하여 중복 import를 방지합니다 */
33
83
  importedRecords: Set<string>; // "User#123"
@@ -37,7 +87,7 @@ export class FixtureGenerator {
37
87
  private dataExplorer: DataExplorer;
38
88
  private locale: Locale;
39
89
  private mappings: FakerMappings;
40
- private llmCache: Map<string, unknown> = new Map();
90
+ private llmCache: Map<string, DataExplorerValue> = new Map();
41
91
  private entityCache: Map<string, Entity> = new Map();
42
92
  private options: FixtureGeneratorOptions;
43
93
 
@@ -58,6 +108,7 @@ export class FixtureGenerator {
58
108
  useLLM: options?.useLLM || false,
59
109
  enableLLMCache: options?.enableLLMCache !== false,
60
110
  llmModel: options?.llmModel || "claude-sonnet-4-6",
111
+ generateText: options?.generateText,
61
112
  };
62
113
  }
63
114
 
@@ -65,11 +116,12 @@ export class FixtureGenerator {
65
116
  * Fixture 생성 (단일)
66
117
  * @returns 생성된 fixture 데이터 (메모리 상)
67
118
  */
68
- async generate(
119
+ async generate<Overrides = undefined>(
69
120
  entityName: string,
70
- overrides: Record<string, unknown> = {},
121
+ overrides?: Overrides,
71
122
  context: GeneratorContext = this.createContext(),
72
- ): Promise<Record<string, unknown>> {
123
+ ): Promise<DataExplorerRecord> {
124
+ const parsedOverrides = dataExplorerRecordSchema.parse(overrides ?? {});
73
125
  // Entity 캐싱: 테스트에서 entity cone 수정이 반영되도록 보장
74
126
  let entity = this.entityCache.get(entityName);
75
127
  if (!entity) {
@@ -83,7 +135,7 @@ export class FixtureGenerator {
83
135
  const rowKey = this.options.useLLM ? `${entityName}#row#${Date.now()}` : undefined;
84
136
 
85
137
  // 각 prop별 값 생성
86
- const fixture: Record<string, unknown> = {};
138
+ const fixture: DataExplorerRecord = {};
87
139
 
88
140
  for (const prop of entity.props) {
89
141
  // Virtual prop은 스킵
@@ -99,8 +151,7 @@ export class FixtureGenerator {
99
151
  prop.type === "bigInteger" ||
100
152
  (prop.type === "string" &&
101
153
  "dbDefault" in prop &&
102
- typeof prop.dbDefault === "string" &&
103
- prop.dbDefault.includes("nextval"));
154
+ z.string().safeParse(prop.dbDefault).data?.includes("nextval") === true);
104
155
  if (hasDbSequence) {
105
156
  // generateBatch에서 처리하므로 여기선 스킵
106
157
  continue;
@@ -120,8 +171,8 @@ export class FixtureGenerator {
120
171
  }
121
172
 
122
173
  // override가 있으면 사용
123
- if (prop.name in overrides) {
124
- fixture[prop.name] = overrides[prop.name];
174
+ if (prop.name in parsedOverrides) {
175
+ fixture[prop.name] = parsedOverrides[prop.name];
125
176
  continue;
126
177
  }
127
178
 
@@ -133,10 +184,10 @@ export class FixtureGenerator {
133
184
  // BelongsToOne / OneToOne(hasJoinColumn)은 FK 컬럼명({prop.name}_id)으로도 override를 받는다
134
185
  const fkColName = `${prop.name}_id`;
135
186
  if (
136
- fkColName in overrides &&
187
+ fkColName in parsedOverrides &&
137
188
  (isBelongsToOneRelationProp(prop) || (isOneToOneRelationProp(prop) && prop.hasJoinColumn))
138
189
  ) {
139
- fixture[fkColName] = overrides[fkColName];
190
+ fixture[fkColName] = parsedOverrides[fkColName];
140
191
  continue;
141
192
  }
142
193
 
@@ -158,13 +209,10 @@ export class FixtureGenerator {
158
209
  try {
159
210
  const llmValue = await this.generateWithLLM(cone.note, prop, entity, rowKey);
160
211
  // string 타입이고 length 제약이 있으면 초과 시 truncation
161
- if (
162
- typeof llmValue === "string" &&
163
- "length" in prop &&
164
- typeof prop.length === "number" &&
165
- llmValue.length > prop.length
166
- ) {
167
- fixture[prop.name] = llmValue.slice(0, prop.length);
212
+ const llmText = z.string().safeParse(llmValue);
213
+ const maxLength = "length" in prop ? z.number().safeParse(prop.length) : undefined;
214
+ if (llmText.success && maxLength?.success && llmText.data.length > maxLength.data) {
215
+ fixture[prop.name] = llmText.data.slice(0, maxLength.data);
168
216
  } else {
169
217
  fixture[prop.name] = llmValue;
170
218
  }
@@ -186,7 +234,7 @@ export class FixtureGenerator {
186
234
 
187
235
  // 4. fixtureDefault 사용
188
236
  if (cone?.fixtureDefault !== undefined) {
189
- fixture[prop.name] = cone.fixtureDefault;
237
+ fixture[prop.name] = dataExplorerValueSchema.parse(cone.fixtureDefault);
190
238
  continue;
191
239
  }
192
240
 
@@ -195,19 +243,22 @@ export class FixtureGenerator {
195
243
  }
196
244
 
197
245
  // 6. email 필드가 있고 name 필드가 있으면, email의 로컬 파트를 name 기반으로 보정
198
- if ("email" in fixture && typeof fixture.email === "string" && !("email" in overrides)) {
246
+ const email = z.string().safeParse(fixture.email);
247
+ if (email.success && !("email" in parsedOverrides)) {
199
248
  const nameValue = fixture.name || fixture.username || fixture.full_name || fixture.name_en;
200
- if (nameValue && typeof nameValue === "string") {
201
- const domain = fixture.email.split("@")[1] || "example.com";
202
- const romanized = await this.romanizeName(nameValue);
249
+ const parsedName = z.string().safeParse(nameValue);
250
+ if (parsedName.success) {
251
+ const domain = email.data.split("@")[1] || "example.com";
252
+ const romanized = await this.romanizeName(parsedName.data);
203
253
  fixture.email = `${romanized}@${domain}`;
204
254
  }
205
255
  }
206
256
 
207
257
  // 7. password 필드 암호화
208
- if ("password" in fixture && fixture.password && typeof fixture.password === "string") {
258
+ const password = z.string().safeParse(fixture.password);
259
+ if (password.success && password.data.length > 0) {
209
260
  const bcrypt = await import("bcrypt");
210
- fixture.password = await bcrypt.hash(fixture.password, 10);
261
+ fixture.password = await bcrypt.hash(password.data, 10);
211
262
  }
212
263
 
213
264
  context.fixtures.set(tempId, fixture);
@@ -221,7 +272,7 @@ export class FixtureGenerator {
221
272
  entity: Entity,
222
273
  prop: EntityProp,
223
274
  context: GeneratorContext,
224
- ): Promise<number | null> {
275
+ ): Promise<number | string | null> {
225
276
  if (!isRelationProp(prop)) {
226
277
  throw new Error(`FixtureGenerator: ${entity.id}.${prop.name} is not a relation prop`);
227
278
  }
@@ -243,15 +294,13 @@ export class FixtureGenerator {
243
294
  const cacheKey = `${prop.with}:${JSON.stringify(dataSource)}`;
244
295
 
245
296
  if (!context.referenceCache.has(cacheKey)) {
297
+ const dataSourceConfig = dataSourceConfigSchema.parse(dataSource.config ?? {});
246
298
  const exploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
247
299
  strategy: dataSource.strategy,
248
- limit:
249
- ((dataSource.config as Record<string, unknown> | undefined)?.limit as
250
- | number
251
- | undefined) || 10,
300
+ limit: dataSourceConfig.limit ?? 10,
252
301
  includeRelations: true,
253
302
  maxDepth: 3,
254
- ...(dataSource.config as Record<string, unknown> | undefined),
303
+ ...dataSourceConfig,
255
304
  });
256
305
  context.referenceCache.set(cacheKey, exploreResult.main.records);
257
306
 
@@ -263,7 +312,7 @@ export class FixtureGenerator {
263
312
  if (candidates && candidates.length > 0) {
264
313
  // 랜덤하게 하나 선택
265
314
  const selected = candidates[Math.floor(Math.random() * candidates.length)];
266
- return selected.id as number;
315
+ return z.union([z.number(), z.string()]).parse(selected.id);
267
316
  }
268
317
  }
269
318
 
@@ -290,7 +339,7 @@ export class FixtureGenerator {
290
339
  if (autoCandidates && autoCandidates.length > 0) {
291
340
  // 랜덤하게 하나 선택
292
341
  const selected = autoCandidates[Math.floor(Math.random() * autoCandidates.length)];
293
- return selected.id as number;
342
+ return z.union([z.number(), z.string()]).parse(selected.id);
294
343
  }
295
344
 
296
345
  // 참조 데이터가 없으면 null 반환 (nullable인 경우)
@@ -320,7 +369,7 @@ export class FixtureGenerator {
320
369
  // 1. Related entities import (Company, Department 등)
321
370
  for (const [entityId, records] of exploreResult.related.entries()) {
322
371
  const entity = this.entityManager.get(entityId);
323
- const recordsToImport: Record<string, unknown>[] = [];
372
+ const recordsToImport: DataExplorerRecord[] = [];
324
373
 
325
374
  !isTest() &&
326
375
  console.log(
@@ -346,8 +395,8 @@ export class FixtureGenerator {
346
395
  );
347
396
  const fixtureRecords = await FixtureManager.createFixtureRecord(
348
397
  entity,
349
- record as { id: number | string; [key: string]: string | number | boolean | null },
350
- { _db: this.sourceDb, singleRecord: true },
398
+ parseFixtureSourceRecord(record),
399
+ { db: this.sourceDb, singleRecord: true },
351
400
  );
352
401
  allFixtureRecords.push(...fixtureRecords);
353
402
  }
@@ -356,7 +405,7 @@ export class FixtureGenerator {
356
405
 
357
406
  // 2. Main entity import (Employee 등)
358
407
  const mainEntity = this.entityManager.get(exploreResult.main.entityId);
359
- const mainRecordsToImport: Record<string, unknown>[] = [];
408
+ const mainRecordsToImport: DataExplorerRecord[] = [];
360
409
 
361
410
  !isTest() &&
362
411
  console.log(
@@ -384,8 +433,8 @@ export class FixtureGenerator {
384
433
  );
385
434
  const fixtureRecords = await FixtureManager.createFixtureRecord(
386
435
  mainEntity,
387
- record as { id: number | string; [key: string]: string | number | boolean | null },
388
- { _db: this.sourceDb, singleRecord: true },
436
+ parseFixtureSourceRecord(record),
437
+ { db: this.sourceDb, singleRecord: true },
389
438
  );
390
439
  allFixtureRecords.push(...fixtureRecords);
391
440
  }
@@ -416,7 +465,7 @@ export class FixtureGenerator {
416
465
  generator: string,
417
466
  prop: EntityProp,
418
467
  entity: Entity,
419
- ): Promise<unknown> {
468
+ ): Promise<DataExplorerValue> {
420
469
  // Faker.js 표현식만 지원
421
470
  if (generator.startsWith("faker.")) {
422
471
  // username이나 name 필드는 한국어 faker 사용
@@ -438,26 +487,25 @@ export class FixtureGenerator {
438
487
  const parts = path.split(".");
439
488
 
440
489
  // faker 객체에서 함수 찾기
441
- let fn: unknown = faker;
490
+ let member = dynamicNamespaceSchema.parse(faker);
442
491
  for (const part of parts) {
443
- if (typeof fn === "object" && fn !== null && part in fn) {
444
- fn = (fn as Record<string, unknown>)[part];
445
- } else {
492
+ if (!(part in member)) {
446
493
  throw new Error(`FixtureGenerator: Invalid faker path for ${prop.name}: faker.${path}`);
447
494
  }
495
+ const nextMember = member[part];
496
+ if (part !== parts.at(-1)) {
497
+ member = dynamicNamespaceSchema.parse(nextMember);
498
+ }
448
499
  }
449
500
 
450
- // 함수가 아니면 에러
451
- if (typeof fn !== "function") {
452
- throw new Error(`FixtureGenerator: faker.${path} is not a function (for ${prop.name})`);
453
- }
501
+ const fn = fakerFunctionSchema.parse(member[parts.at(-1) ?? ""]);
454
502
 
455
- let args: unknown[] = [];
503
+ let args: DataExplorerValue[] = [];
456
504
  if (argsStr?.trim()) {
457
505
  args = this.parseGeneratorArgs(argsStr, prop.name);
458
506
  }
459
507
 
460
- return fn(...args);
508
+ return dataExplorerValueSchema.parse(fn(...args));
461
509
  } catch (error) {
462
510
  !isTest() &&
463
511
  console.log(
@@ -490,7 +538,10 @@ export class FixtureGenerator {
490
538
  * 4. Enum 타입
491
539
  * 5. 타입별 기본값
492
540
  */
493
- private async generateDefaultValue(prop: EntityProp, entity?: Entity): Promise<unknown> {
541
+ private async generateDefaultValue(
542
+ prop: EntityProp,
543
+ entity?: Entity,
544
+ ): Promise<DataExplorerValue> {
494
545
  const fakerModule = await import("@faker-js/faker");
495
546
  const faker = fakerModule.faker;
496
547
  const fakerKO = fakerModule.fakerKO;
@@ -680,7 +731,7 @@ export class FixtureGenerator {
680
731
  _entity: Entity | undefined,
681
732
  faker: typeof import("@faker-js/faker").faker,
682
733
  _localeFaker: typeof import("@faker-js/faker").faker,
683
- ): unknown[] {
734
+ ): DataExplorerValue[] {
684
735
  const count = faker.number.int({ min: 1, max: 3 });
685
736
 
686
737
  /** SonamuFile[]은 Sonamu 내장 타입으로 구조가 정해져 있습니다 */
@@ -726,7 +777,10 @@ export class FixtureGenerator {
726
777
  *
727
778
  * fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다.
728
779
  */
729
- private async executeFakerExpression(expression: string, prop: EntityProp): Promise<unknown> {
780
+ private async executeFakerExpression(
781
+ expression: string,
782
+ prop: EntityProp,
783
+ ): Promise<DataExplorerValue> {
730
784
  const fakerModule = await import("@faker-js/faker");
731
785
  const faker = fakerModule.faker;
732
786
  const fakerKO = fakerModule.fakerKO;
@@ -735,7 +789,7 @@ export class FixtureGenerator {
735
789
  /** Faker 표현식이 아닌 리터럴 값은 JSON으로 파싱합니다 */
736
790
  if (!expression.startsWith("faker")) {
737
791
  try {
738
- return JSON.parse(expression);
792
+ return dataExplorerValueSchema.parse(JSON.parse(expression));
739
793
  } catch {
740
794
  return expression;
741
795
  }
@@ -760,25 +814,25 @@ export class FixtureGenerator {
760
814
  const parts = path.split(".");
761
815
 
762
816
  /** 점 표기법(dot notation)으로 Faker 함수를 찾아갑니다 */
763
- let fn: unknown = selectedFaker;
817
+ let member = dynamicNamespaceSchema.parse(selectedFaker);
764
818
  for (const part of parts) {
765
- if (typeof fn === "object" && fn !== null && part in fn) {
766
- fn = (fn as Record<string, unknown>)[part];
767
- } else {
819
+ if (!(part in member)) {
768
820
  throw new Error(`Invalid faker path for ${prop.name}: ${fakerName}.${path}`);
769
821
  }
822
+ const nextMember = member[part];
823
+ if (part !== parts.at(-1)) {
824
+ member = dynamicNamespaceSchema.parse(nextMember);
825
+ }
770
826
  }
771
827
 
772
- if (typeof fn !== "function") {
773
- throw new Error(`${fakerName}.${path} is not a function (for ${prop.name})`);
774
- }
828
+ const fn = fakerFunctionSchema.parse(member[parts.at(-1) ?? ""]);
775
829
 
776
- let args: unknown[] = [];
830
+ let args: DataExplorerValue[] = [];
777
831
  if (argsStr?.trim()) {
778
832
  args = this.parseGeneratorArgs(argsStr, prop.name);
779
833
  }
780
834
 
781
- return fn(...args);
835
+ return dataExplorerValueSchema.parse(fn(...args));
782
836
  }
783
837
 
784
838
  /**
@@ -796,7 +850,7 @@ export class FixtureGenerator {
796
850
  prop: EntityProp,
797
851
  entity: Entity,
798
852
  rowKey?: string,
799
- ): Promise<unknown> {
853
+ ): Promise<DataExplorerValue> {
800
854
  // rowKey가 있으면 row 단위 생성 전략 사용
801
855
  if (rowKey) {
802
856
  const rowCacheKey = `${rowKey}:${prop.name}`;
@@ -825,18 +879,19 @@ export class FixtureGenerator {
825
879
 
826
880
  const apiKey = this.getApiKey();
827
881
  const { createAnthropic } = await import("@ai-sdk/anthropic");
828
- const { generateText } = await import("ai");
882
+ const generateText = await this.getTextGenerator();
829
883
 
830
884
  const rowResponse = await generateText({
831
885
  model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
832
886
  prompt: this.buildRowLLMPrompt(llmProps, entity),
833
887
  });
834
- if (!rowResponse || typeof rowResponse.text !== "string") {
888
+ const rowResponseText = z.string().safeParse(rowResponse?.text);
889
+ if (!rowResponseText.success) {
835
890
  throw new Error("Invalid LLM response");
836
891
  }
837
892
 
838
893
  // 응답을 파싱하여 각 필드에 대한 결과를 캐시에 저장
839
- const rowResult = this.parseRowLLMResponse(rowResponse.text, llmProps);
894
+ const rowResult = this.parseRowLLMResponse(rowResponseText.data, llmProps);
840
895
  for (const [fieldName, value] of Object.entries(rowResult)) {
841
896
  this.llmCache.set(`${rowKey}:${fieldName}`, value);
842
897
  }
@@ -861,7 +916,7 @@ export class FixtureGenerator {
861
916
  fixtureHint: string,
862
917
  prop: EntityProp,
863
918
  entity: Entity,
864
- ): Promise<unknown> {
919
+ ): Promise<DataExplorerValue> {
865
920
  const cacheKey = `${entity.id}:${prop.name}:${fixtureHint}`;
866
921
  if (this.options.enableLLMCache && this.llmCache.has(cacheKey)) {
867
922
  return this.llmCache.get(cacheKey);
@@ -869,17 +924,18 @@ export class FixtureGenerator {
869
924
 
870
925
  const apiKey = this.getApiKey();
871
926
  const { createAnthropic } = await import("@ai-sdk/anthropic");
872
- const { generateText } = await import("ai");
927
+ const generateText = await this.getTextGenerator();
873
928
 
874
929
  const singleResponse = await generateText({
875
930
  model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
876
931
  prompt: this.buildLLMPrompt(fixtureHint, prop, entity),
877
932
  });
878
- if (!singleResponse || typeof singleResponse.text !== "string") {
933
+ const singleResponseText = z.string().safeParse(singleResponse?.text);
934
+ if (!singleResponseText.success) {
879
935
  throw new Error("Invalid LLM response");
880
936
  }
881
937
 
882
- const value = this.parseLLMResponse(singleResponse.text, prop.type);
938
+ const value = this.parseLLMResponse(singleResponseText.data, prop.type);
883
939
  if (this.options.enableLLMCache) {
884
940
  this.llmCache.set(cacheKey, value);
885
941
  }
@@ -887,6 +943,14 @@ export class FixtureGenerator {
887
943
  return value;
888
944
  }
889
945
 
946
+ private async getTextGenerator(): Promise<FixtureTextGenerator> {
947
+ if (this.options.generateText) {
948
+ return this.options.generateText;
949
+ }
950
+ const ai = await import("ai");
951
+ return async (request) => ai.generateText(request);
952
+ }
953
+
890
954
  /**
891
955
  * row 전체를 한 번에 생성하는 LLM 프롬프트를 만듭니다.
892
956
  */
@@ -938,7 +1002,7 @@ export class FixtureGenerator {
938
1002
  ? `\n\nOther fields in this entity (for context, do NOT generate these):\n${otherProps}`
939
1003
  : "";
940
1004
 
941
- const outputShape = props.map((p) => ` "${p.name}": <${p.type}>`).join(",\n");
1005
+ const resultTemplate = props.map((p) => ` "${p.name}": <${p.type}>`).join(",\n");
942
1006
 
943
1007
  const entityContext = entity.cone?.note ? `\nEntity description: ${entity.cone.note}` : "";
944
1008
 
@@ -958,35 +1022,35 @@ Rules:
958
1022
 
959
1023
  Return exactly this JSON shape:
960
1024
  {
961
- ${outputShape}
1025
+ ${resultTemplate}
962
1026
  }`;
963
1027
  }
964
1028
 
965
1029
  /**
966
1030
  * row LLM 응답을 파싱하여 필드별 값으로 변환합니다.
967
1031
  */
968
- private parseRowLLMResponse(text: string, props: EntityProp[]): Record<string, unknown> {
1032
+ private parseRowLLMResponse(text: string, props: EntityProp[]): DataExplorerRecord {
969
1033
  const jsonText = text
970
1034
  .trim()
971
1035
  .replace(/^```json\s*/i, "")
972
1036
  .replace(/```\s*$/, "")
973
1037
  .trim();
974
1038
 
975
- let parsed: Record<string, unknown>;
1039
+ let parsed: DataExplorerRecord;
976
1040
  try {
977
- const raw = JSON.parse(jsonText);
978
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1041
+ const parsedRecord = dataExplorerRecordSchema.safeParse(JSON.parse(jsonText));
1042
+ if (!parsedRecord.success) {
979
1043
  !isTest() &&
980
1044
  console.warn("[FixtureGenerator] Row LLM response is not a plain object:", text);
981
1045
  return {};
982
1046
  }
983
- parsed = raw as Record<string, unknown>;
1047
+ parsed = parsedRecord.data;
984
1048
  } catch {
985
1049
  !isTest() && console.warn("[FixtureGenerator] Failed to parse row LLM response:", text);
986
1050
  return {};
987
1051
  }
988
1052
 
989
- const result: Record<string, unknown> = {};
1053
+ const result: DataExplorerRecord = {};
990
1054
  for (const prop of props) {
991
1055
  if (prop.name in parsed) {
992
1056
  result[prop.name] = this.parseLLMResponse(String(parsed[prop.name] ?? ""), prop.type);
@@ -1038,13 +1102,13 @@ Rules:
1038
1102
  return prompt;
1039
1103
  }
1040
1104
 
1041
- private parseLLMResponse(text: string, propType: string): unknown {
1105
+ private parseLLMResponse(text: string, propType: string): DataExplorerValue {
1042
1106
  const cleaned = text.trim();
1043
1107
 
1044
1108
  // 배열 타입 처리
1045
1109
  if (propType.endsWith("[]")) {
1046
1110
  try {
1047
- const parsed = JSON.parse(cleaned);
1111
+ const parsed = dataExplorerValueSchema.parse(JSON.parse(cleaned));
1048
1112
  const baseType = propType.slice(0, -2); // "integer[]" -> "integer"
1049
1113
 
1050
1114
  if (Array.isArray(parsed)) {
@@ -1054,7 +1118,7 @@ Rules:
1054
1118
  return this.getDefaultValueForType(baseType);
1055
1119
  }
1056
1120
  // 객체는 JSON.stringify 후 파싱 (json 타입인 경우)
1057
- if (typeof item === "object") {
1121
+ if (dataExplorerRecordSchema.safeParse(item).success || Array.isArray(item)) {
1058
1122
  return baseType === "json"
1059
1123
  ? item
1060
1124
  : this.parseScalarValue(JSON.stringify(item), baseType);
@@ -1077,7 +1141,7 @@ Rules:
1077
1141
  return this.parseScalarValue(cleaned, propType);
1078
1142
  }
1079
1143
 
1080
- private getDefaultValueForType(propType: string): unknown {
1144
+ private getDefaultValueForType(propType: string): DataExplorerValue {
1081
1145
  switch (propType) {
1082
1146
  case "integer":
1083
1147
  return 0;
@@ -1100,7 +1164,7 @@ Rules:
1100
1164
  }
1101
1165
  }
1102
1166
 
1103
- private parseScalarValue(text: string, propType: string): unknown {
1167
+ private parseScalarValue(text: string, propType: string): DataExplorerValue {
1104
1168
  const cleaned = text.trim();
1105
1169
 
1106
1170
  switch (propType) {
@@ -1129,7 +1193,7 @@ Rules:
1129
1193
  }
1130
1194
  case "json":
1131
1195
  try {
1132
- return JSON.parse(cleaned);
1196
+ return dataExplorerValueSchema.parse(JSON.parse(cleaned));
1133
1197
  } catch {
1134
1198
  return cleaned;
1135
1199
  }
@@ -1149,22 +1213,27 @@ Rules:
1149
1213
  * 2. JS 객체 리터럴 → JSON 변환 후 재시도 (single quote, unquoted key 처리)
1150
1214
  * 3. 단순 단일 인자 폴백 (숫자, 문자열)
1151
1215
  */
1152
- private parseGeneratorArgs(argsStr: string, propName: string): unknown[] {
1216
+ private parseGeneratorArgs(argsStr: string, propName: string): DataExplorerValue[] {
1153
1217
  // 1. JSON 직접 파싱
1218
+ let parsedArguments: DataExplorerValue[] | null;
1154
1219
  try {
1155
- const parsed = JSON.parse(`[${argsStr}]`) as unknown;
1156
- return Array.isArray(parsed) ? parsed : [parsed];
1220
+ parsedArguments = generatorArgumentsSchema.parse(JSON.parse(`[${argsStr}]`));
1157
1221
  } catch {
1158
- // 계속
1222
+ parsedArguments = null;
1223
+ }
1224
+ if (parsedArguments) {
1225
+ return parsedArguments;
1159
1226
  }
1160
1227
 
1161
1228
  // 2. JS 객체 리터럴 → JSON 변환 후 재시도
1162
1229
  try {
1163
1230
  const jsonStr = this.convertJsLiteralToJson(argsStr);
1164
- const parsed = JSON.parse(`[${jsonStr}]`) as unknown;
1165
- return Array.isArray(parsed) ? parsed : [parsed];
1231
+ parsedArguments = generatorArgumentsSchema.parse(JSON.parse(`[${jsonStr}]`));
1166
1232
  } catch {
1167
- // 계속
1233
+ parsedArguments = null;
1234
+ }
1235
+ if (parsedArguments) {
1236
+ return parsedArguments;
1168
1237
  }
1169
1238
 
1170
1239
  // 3. 단순 단일 인자 폴백
@@ -1229,6 +1298,14 @@ Rules:
1229
1298
  return apiKey;
1230
1299
  }
1231
1300
 
1301
+ private hasSequenceDefault(prop: EntityProp): boolean {
1302
+ if (!("dbDefault" in prop)) {
1303
+ return false;
1304
+ }
1305
+ const dbDefault = z.string().safeParse(prop.dbDefault);
1306
+ return dbDefault.success && dbDefault.data.includes("nextval");
1307
+ }
1308
+
1232
1309
  private getExpectedFormat(propType: string): string {
1233
1310
  // 배열 타입 처리
1234
1311
  if (propType.endsWith("[]")) {
@@ -1460,15 +1537,9 @@ Rules:
1460
1537
  *
1461
1538
  * @returns 저장된 fixture 데이터 (실제 DB ID 포함)
1462
1539
  */
1463
- async generateBatch(
1464
- specs: Array<{ entity: string; count: number; overrides?: Record<string, unknown> }>,
1465
- ): Promise<FixtureImportResult[]> {
1540
+ async generateBatch(specs: FixtureGenerationSpec[]): Promise<FixtureImportResult[]> {
1466
1541
  const context = this.createContext();
1467
- const generatedFixtures: Array<{
1468
- entity: string;
1469
- data: Record<string, unknown>;
1470
- explicitId?: boolean;
1471
- }> = [];
1542
+ const generatedFixtures: GeneratedFixture[] = [];
1472
1543
 
1473
1544
  // 1. 각 spec별로 fixture 생성
1474
1545
  for (const spec of specs) {
@@ -1484,10 +1555,7 @@ Rules:
1484
1555
  // 부모 테이블에서 서브타입 테이블에 없는 id를 조회
1485
1556
  let query = this.sourceDb(parentEntity.table).select(`${parentEntity.table}.id`);
1486
1557
  for (const [col, val] of Object.entries(parentOverrides)) {
1487
- query = query.where(
1488
- `${parentEntity.table}.${col}`,
1489
- val as string | number | boolean | null,
1490
- );
1558
+ query = query.where(`${parentEntity.table}.${col}`, fixtureWhereValueSchema.parse(val));
1491
1559
  }
1492
1560
  query = query
1493
1561
  .leftJoin(specEntity.table, `${specEntity.table}.id`, `${parentEntity.table}.id`)
@@ -1536,10 +1604,7 @@ Rules:
1536
1604
  !explicitId &&
1537
1605
  (idProp?.type === "integer" ||
1538
1606
  idProp?.type === "bigInteger" ||
1539
- (idProp?.type === "string" &&
1540
- "dbDefault" in (idProp ?? {}) &&
1541
- typeof (idProp as { dbDefault?: unknown })?.dbDefault === "string" &&
1542
- ((idProp as { dbDefault?: string })?.dbDefault ?? "").includes("nextval")));
1607
+ (idProp?.type === "string" && this.hasSequenceDefault(idProp)));
1543
1608
 
1544
1609
  const dataForRecord = usesSequence
1545
1610
  ? { ...data, id: Math.floor(Math.random() * 1000000) }
@@ -1547,7 +1612,7 @@ Rules:
1547
1612
 
1548
1613
  const records = await FixtureManager.createFixtureRecord(
1549
1614
  entity,
1550
- dataForRecord as { id: number | string; [key: string]: string | number | boolean | null },
1615
+ parseFixtureSourceRecord(dataForRecord),
1551
1616
  { singleRecord: true },
1552
1617
  );
1553
1618
  fixtureRecords.push(...records);
@@ -1572,7 +1637,7 @@ Rules:
1572
1637
  * generateBatch()를 다시 호출하지 않고 직접 삽입합니다.
1573
1638
  */
1574
1639
  private async generateCompanions(
1575
- specs: Array<{ entity: string; count: number; overrides?: Record<string, unknown> }>,
1640
+ specs: FixtureGenerationSpec[],
1576
1641
  parentResults: FixtureImportResult[],
1577
1642
  ): Promise<FixtureImportResult[]> {
1578
1643
  const allResults: FixtureImportResult[] = [];
@@ -1612,10 +1677,7 @@ Rules:
1612
1677
  const usesSequence =
1613
1678
  companionIdProp?.type === "integer" ||
1614
1679
  companionIdProp?.type === "bigInteger" ||
1615
- (companionIdProp?.type === "string" &&
1616
- "dbDefault" in (companionIdProp ?? {}) &&
1617
- typeof (companionIdProp as { dbDefault?: unknown })?.dbDefault === "string" &&
1618
- ((companionIdProp as { dbDefault?: string })?.dbDefault ?? "").includes("nextval"));
1680
+ (companionIdProp?.type === "string" && this.hasSequenceDefault(companionIdProp));
1619
1681
  const companionCount = companion.count ?? 1;
1620
1682
 
1621
1683
  // 각 parent result에 대해 companion fixture 생성
@@ -1624,7 +1686,7 @@ Rules:
1624
1686
 
1625
1687
  for (const parentResult of entityResults) {
1626
1688
  const resolvedOverrides = this.resolveTemplateOverrides(
1627
- companion.overrides ?? {},
1689
+ dataExplorerRecordSchema.parse(companion.overrides ?? {}),
1628
1690
  parentResult.data,
1629
1691
  );
1630
1692
  resolvedOverrides[fkColName] = parentResult.data.id;
@@ -1638,10 +1700,7 @@ Rules:
1638
1700
 
1639
1701
  const records = await FixtureManager.createFixtureRecord(
1640
1702
  companionEntity,
1641
- dataForRecord as {
1642
- id: number | string;
1643
- [key: string]: string | number | boolean | null;
1644
- },
1703
+ parseFixtureSourceRecord(dataForRecord),
1645
1704
  { singleRecord: true },
1646
1705
  );
1647
1706
  companionFixtureRecords.push(...records);
@@ -1672,13 +1731,14 @@ Rules:
1672
1731
  * 예: { "account_id": "{{email}}" } → { "account_id": "user@example.com" }
1673
1732
  */
1674
1733
  private resolveTemplateOverrides(
1675
- overrides: Record<string, unknown>,
1734
+ overrides: DataExplorerRecord,
1676
1735
  parentData: { [key: string]: string | number | boolean | Date | null },
1677
- ): Record<string, unknown> {
1678
- const resolved: Record<string, unknown> = {};
1736
+ ): DataExplorerRecord {
1737
+ const resolved: DataExplorerRecord = {};
1679
1738
  for (const [key, value] of Object.entries(overrides)) {
1680
- if (typeof value === "string" && value.startsWith("{{") && value.endsWith("}}")) {
1681
- const fieldName = value.slice(2, -2).trim();
1739
+ const template = z.string().safeParse(value);
1740
+ if (template.success && template.data.startsWith("{{") && template.data.endsWith("}}")) {
1741
+ const fieldName = template.data.slice(2, -2).trim();
1682
1742
  if (!(fieldName in parentData)) {
1683
1743
  throw new Error(
1684
1744
  `템플릿 필드 "${fieldName}"이(가) 부모 fixture 데이터에 존재하지 않습니다 (override key: "${key}")`,
@@ -1741,8 +1801,8 @@ Rules:
1741
1801
  for (const record of exploreResult.main.records) {
1742
1802
  const records = await FixtureManager.createFixtureRecord(
1743
1803
  mainEntity,
1744
- record as { id: number | string; [key: string]: string | number | boolean | null },
1745
- { _db: this.sourceDb, singleRecord: true },
1804
+ parseFixtureSourceRecord(record),
1805
+ { db: this.sourceDb, singleRecord: true },
1746
1806
  );
1747
1807
  fixtureRecords.push(...records);
1748
1808
  }
@@ -1753,8 +1813,8 @@ Rules:
1753
1813
  for (const record of relatedRecords) {
1754
1814
  const records = await FixtureManager.createFixtureRecord(
1755
1815
  relatedEntity,
1756
- record as { id: number | string; [key: string]: string | number | boolean | null },
1757
- { _db: this.sourceDb, singleRecord: true },
1816
+ parseFixtureSourceRecord(record),
1817
+ { db: this.sourceDb, singleRecord: true },
1758
1818
  );
1759
1819
  fixtureRecords.push(...records);
1760
1820
  }