sonamu 0.10.11 → 0.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (519) hide show
  1. package/dist/ai/agents/agent.d.ts +1 -1
  2. package/dist/ai/agents/agent.d.ts.map +1 -1
  3. package/dist/ai/agents/agent.js +5 -6
  4. package/dist/ai/agents/types.d.ts +1 -1
  5. package/dist/ai/agents/types.d.ts.map +1 -1
  6. package/dist/ai/providers/rtzr/api.js +18 -18
  7. package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
  8. package/dist/ai/providers/rtzr/error.js +17 -15
  9. package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
  10. package/dist/ai/providers/rtzr/model.js +3 -2
  11. package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
  12. package/dist/ai/providers/rtzr/options.js +16 -16
  13. package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
  14. package/dist/ai/providers/rtzr/provider.js +9 -9
  15. package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
  16. package/dist/ai/providers/rtzr/utils.js +1 -1
  17. package/dist/api/caster.d.ts.map +1 -1
  18. package/dist/api/caster.js +17 -17
  19. package/dist/api/code-converters.d.ts +4 -2
  20. package/dist/api/code-converters.d.ts.map +1 -1
  21. package/dist/api/code-converters.js +100 -19
  22. package/dist/api/config.d.ts +16 -1
  23. package/dist/api/config.d.ts.map +1 -1
  24. package/dist/api/config.js +28 -21
  25. package/dist/api/context.d.ts +2 -2
  26. package/dist/api/context.d.ts.map +1 -1
  27. package/dist/api/context.js +1 -1
  28. package/dist/api/decorators.d.ts.map +1 -1
  29. package/dist/api/decorators.js +18 -11
  30. package/dist/api/http-validator.d.ts +36 -0
  31. package/dist/api/http-validator.d.ts.map +1 -0
  32. package/dist/api/http-validator.js +72 -0
  33. package/dist/api/index.js +6 -4
  34. package/dist/api/sonamu.d.ts +59 -20
  35. package/dist/api/sonamu.d.ts.map +1 -1
  36. package/dist/api/sonamu.js +250 -106
  37. package/dist/api/websocket-helpers.d.ts +7 -5
  38. package/dist/api/websocket-helpers.d.ts.map +1 -1
  39. package/dist/api/websocket-helpers.js +6 -6
  40. package/dist/auth/audit-log/builders.d.ts.map +1 -1
  41. package/dist/auth/audit-log/builders.js +1 -1
  42. package/dist/auth/audit-log/client-ip.d.ts +1 -1
  43. package/dist/auth/audit-log/client-ip.d.ts.map +1 -1
  44. package/dist/auth/audit-log/client-ip.js +4 -2
  45. package/dist/auth/audit-log/events.d.ts +5 -1
  46. package/dist/auth/audit-log/events.d.ts.map +1 -1
  47. package/dist/auth/audit-log/events.js +1 -1
  48. package/dist/auth/audit-log/plugin.d.ts +0 -8
  49. package/dist/auth/audit-log/plugin.d.ts.map +1 -1
  50. package/dist/auth/audit-log/plugin.js +48 -52
  51. package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
  52. package/dist/auth/audit-log-ingestor.js +16 -12
  53. package/dist/auth/auth-generator.d.ts.map +1 -1
  54. package/dist/auth/auth-generator.js +1 -1
  55. package/dist/auth/better-auth-request.d.ts.map +1 -1
  56. package/dist/auth/better-auth-request.js +6 -5
  57. package/dist/auth/knex-adapter.js +1 -1
  58. package/dist/auth/plugins/entity-definitions/index.d.ts +13 -1
  59. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  60. package/dist/auth/plugins/entity-definitions/index.js +1 -1
  61. package/dist/auth/plugins/wrappers/admin.d.ts +1 -1
  62. package/dist/bin/build-config.d.ts +25 -0
  63. package/dist/bin/build-config.d.ts.map +1 -1
  64. package/dist/bin/build-config.js +105 -2
  65. package/dist/bin/cli.js +133 -29
  66. package/dist/bin/compiler-policy.d.ts +3 -0
  67. package/dist/bin/compiler-policy.d.ts.map +1 -0
  68. package/dist/bin/compiler-policy.js +24 -0
  69. package/dist/bin/fixture.d.ts.map +1 -1
  70. package/dist/bin/fixture.js +5 -3
  71. package/dist/bin/test-command.d.ts +22 -1
  72. package/dist/bin/test-command.d.ts.map +1 -1
  73. package/dist/bin/test-command.js +18 -10
  74. package/dist/bin/ts-loader-registration.d.ts +7 -1
  75. package/dist/bin/ts-loader-registration.d.ts.map +1 -1
  76. package/dist/bin/ts-loader-registration.js +13 -4
  77. package/dist/cache/decorator.d.ts.map +1 -1
  78. package/dist/cache/decorator.js +11 -4
  79. package/dist/compress/compress.d.ts.map +1 -1
  80. package/dist/compress/compress.js +1 -1
  81. package/dist/cone/cone-generator.d.ts +14 -1
  82. package/dist/cone/cone-generator.d.ts.map +1 -1
  83. package/dist/cone/cone-generator.js +13 -9
  84. package/dist/database/_batch_update.js +1 -1
  85. package/dist/database/base-model.d.ts +22 -10
  86. package/dist/database/base-model.d.ts.map +1 -1
  87. package/dist/database/base-model.js +26 -16
  88. package/dist/database/base-model.types.d.ts +15 -3
  89. package/dist/database/base-model.types.d.ts.map +1 -1
  90. package/dist/database/db.d.ts +12 -2
  91. package/dist/database/db.d.ts.map +1 -1
  92. package/dist/database/db.js +10 -7
  93. package/dist/database/knex.d.ts.map +1 -1
  94. package/dist/database/knex.js +17 -14
  95. package/dist/database/puri.d.ts +11 -9
  96. package/dist/database/puri.d.ts.map +1 -1
  97. package/dist/database/puri.js +93 -79
  98. package/dist/database/puri.types.d.ts +21 -21
  99. package/dist/database/puri.types.d.ts.map +1 -1
  100. package/dist/database/puri.types.js +1 -1
  101. package/dist/database/upsert-builder.d.ts +12 -10
  102. package/dist/database/upsert-builder.d.ts.map +1 -1
  103. package/dist/database/upsert-builder.js +20 -12
  104. package/dist/dict/sd.d.ts.map +1 -1
  105. package/dist/dict/sd.js +1 -1
  106. package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
  107. package/dist/dict/sonamu-dictionary.js +1 -1
  108. package/dist/dict/utils.d.ts.map +1 -1
  109. package/dist/dict/utils.js +7 -2
  110. package/dist/entity/entity-manager.d.ts +13 -4
  111. package/dist/entity/entity-manager.d.ts.map +1 -1
  112. package/dist/entity/entity-manager.js +19 -11
  113. package/dist/entity/entity-template-cone.d.ts +73 -5
  114. package/dist/entity/entity-template-cone.d.ts.map +1 -1
  115. package/dist/entity/entity-template-cone.js +4 -2
  116. package/dist/entity/entity.d.ts +6 -2
  117. package/dist/entity/entity.d.ts.map +1 -1
  118. package/dist/entity/entity.js +30 -28
  119. package/dist/env.d.ts +3 -1
  120. package/dist/env.d.ts.map +1 -1
  121. package/dist/env.js +1 -1
  122. package/dist/exceptions/error-handler.d.ts.map +1 -1
  123. package/dist/exceptions/error-handler.js +7 -2
  124. package/dist/exceptions/so-exceptions.d.ts +24 -20
  125. package/dist/exceptions/so-exceptions.d.ts.map +1 -1
  126. package/dist/exceptions/so-exceptions.js +4 -2
  127. package/dist/filter/utils.d.ts +7 -2
  128. package/dist/filter/utils.d.ts.map +1 -1
  129. package/dist/filter/utils.js +8 -6
  130. package/dist/index.d.ts +3 -0
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +13 -7
  133. package/dist/logger/configure.d.ts.map +1 -1
  134. package/dist/logger/configure.js +10 -10
  135. package/dist/migration/code-generation.d.ts.map +1 -1
  136. package/dist/migration/code-generation.js +50 -46
  137. package/dist/migration/index-where-predicate.d.ts.map +1 -1
  138. package/dist/migration/index-where-predicate.js +22 -8
  139. package/dist/migration/migration-set.d.ts.map +1 -1
  140. package/dist/migration/migration-set.js +1 -1
  141. package/dist/migration/migrator.d.ts +61 -36
  142. package/dist/migration/migrator.d.ts.map +1 -1
  143. package/dist/migration/migrator.js +276 -167
  144. package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
  145. package/dist/migration/postgresql-schema-reader.js +51 -36
  146. package/dist/migration/slack-confirm.d.ts.map +1 -1
  147. package/dist/migration/slack-confirm.js +1 -1
  148. package/dist/migration/types.d.ts +54 -0
  149. package/dist/migration/types.d.ts.map +1 -1
  150. package/dist/naite/naite-reporter.js +2 -2
  151. package/dist/naite/naite.d.ts +74 -15
  152. package/dist/naite/naite.d.ts.map +1 -1
  153. package/dist/naite/naite.js +5 -19
  154. package/dist/runtime-type.d.ts +6 -0
  155. package/dist/runtime-type.d.ts.map +1 -0
  156. package/dist/runtime-type.js +26 -0
  157. package/dist/ssr/hmr-opt-out.d.ts +26 -0
  158. package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
  159. package/dist/ssr/hmr-opt-out.js +59 -0
  160. package/dist/ssr/renderer.d.ts.map +1 -1
  161. package/dist/ssr/renderer.js +4 -1
  162. package/dist/storage/base-file.js +14 -14
  163. package/dist/storage/buffered-file.d.ts +2 -2
  164. package/dist/storage/buffered-file.d.ts.map +1 -1
  165. package/dist/storage/buffered-file.js +12 -12
  166. package/dist/storage/s3-driver.d.ts.map +1 -1
  167. package/dist/storage/s3-driver.js +1 -1
  168. package/dist/storage/uploaded-file.d.ts +2 -2
  169. package/dist/storage/uploaded-file.d.ts.map +1 -1
  170. package/dist/storage/uploaded-file.js +8 -8
  171. package/dist/stream/sse.js +11 -11
  172. package/dist/stream/ws-core.d.ts +1 -1
  173. package/dist/stream/ws-core.d.ts.map +1 -1
  174. package/dist/stream/ws-delivery.d.ts +1 -1
  175. package/dist/stream/ws-delivery.d.ts.map +1 -1
  176. package/dist/stream/ws-delivery.js +6 -4
  177. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  178. package/dist/stream/ws-local-connection-store.js +4 -2
  179. package/dist/stream/ws-presence-store.js +1 -1
  180. package/dist/stream/ws-registry.d.ts +4 -4
  181. package/dist/stream/ws-registry.d.ts.map +1 -1
  182. package/dist/stream/ws-registry.js +4 -2
  183. package/dist/stream/ws-telemetry.d.ts +16 -12
  184. package/dist/stream/ws-telemetry.d.ts.map +1 -1
  185. package/dist/stream/ws-telemetry.js +22 -18
  186. package/dist/stream/ws.d.ts +18 -12
  187. package/dist/stream/ws.d.ts.map +1 -1
  188. package/dist/stream/ws.js +26 -22
  189. package/dist/syncer/api-parser.js +1 -1
  190. package/dist/syncer/checksum.d.ts.map +1 -1
  191. package/dist/syncer/checksum.js +7 -8
  192. package/dist/syncer/code-generator.d.ts.map +1 -1
  193. package/dist/syncer/code-generator.js +13 -14
  194. package/dist/syncer/entity-operations.d.ts.map +1 -1
  195. package/dist/syncer/entity-operations.js +5 -6
  196. package/dist/syncer/file-copy.d.ts +5 -0
  197. package/dist/syncer/file-copy.d.ts.map +1 -0
  198. package/dist/syncer/file-copy.js +36 -0
  199. package/dist/syncer/file-patterns.d.ts +1 -0
  200. package/dist/syncer/file-patterns.d.ts.map +1 -1
  201. package/dist/syncer/file-patterns.js +5 -2
  202. package/dist/syncer/file-tracking.d.ts.map +1 -1
  203. package/dist/syncer/file-tracking.js +5 -4
  204. package/dist/syncer/filesystem-dependencies.d.ts +15 -0
  205. package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
  206. package/dist/syncer/filesystem-dependencies.js +53 -0
  207. package/dist/syncer/module-loader.d.ts +1 -1
  208. package/dist/syncer/module-loader.d.ts.map +1 -1
  209. package/dist/syncer/module-loader.js +6 -4
  210. package/dist/syncer/syncer-actions.d.ts +4 -0
  211. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  212. package/dist/syncer/syncer-actions.js +34 -17
  213. package/dist/syncer/syncer.d.ts +17 -6
  214. package/dist/syncer/syncer.d.ts.map +1 -1
  215. package/dist/syncer/syncer.js +71 -29
  216. package/dist/syncer/watcher.d.ts.map +1 -1
  217. package/dist/syncer/watcher.js +3 -3
  218. package/dist/tasks/decorator.d.ts +6 -6
  219. package/dist/tasks/decorator.d.ts.map +1 -1
  220. package/dist/tasks/decorator.js +4 -4
  221. package/dist/tasks/step-wrapper.d.ts +6 -4
  222. package/dist/tasks/step-wrapper.d.ts.map +1 -1
  223. package/dist/tasks/step-wrapper.js +5 -5
  224. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  225. package/dist/tasks/workflow-manager.js +13 -7
  226. package/dist/template/entity-converter.d.ts +1 -1
  227. package/dist/template/entity-converter.d.ts.map +1 -1
  228. package/dist/template/entity-converter.js +2 -2
  229. package/dist/template/helpers.d.ts +8 -6
  230. package/dist/template/helpers.d.ts.map +1 -1
  231. package/dist/template/helpers.js +1 -1
  232. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  233. package/dist/template/implementations/generated.template.js +4 -4
  234. package/dist/template/implementations/generated_http.template.d.ts +7 -4
  235. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  236. package/dist/template/implementations/generated_http.template.js +21 -21
  237. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  238. package/dist/template/implementations/generated_sso.template.js +6 -3
  239. package/dist/template/implementations/http_validators.template.d.ts +18 -0
  240. package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
  241. package/dist/template/implementations/http_validators.template.js +127 -0
  242. package/dist/template/implementations/model.template.d.ts.map +1 -1
  243. package/dist/template/implementations/model.template.js +1 -1
  244. package/dist/template/implementations/queries.template.js +1 -1
  245. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  246. package/dist/template/implementations/sd.template.js +79 -39
  247. package/dist/template/implementations/services.template.d.ts.map +1 -1
  248. package/dist/template/implementations/services.template.js +6 -4
  249. package/dist/template/implementations/view_form.template.d.ts +6 -1
  250. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  251. package/dist/template/implementations/view_form.template.js +23 -15
  252. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  253. package/dist/template/implementations/view_list.template.js +11 -6
  254. package/dist/template/template-manager.d.ts.map +1 -1
  255. package/dist/template/template-manager.js +9 -7
  256. package/dist/template/template-types.d.ts +12 -5
  257. package/dist/template/template-types.d.ts.map +1 -1
  258. package/dist/template/template-types.js +1 -1
  259. package/dist/template/template.d.ts +2 -2
  260. package/dist/template/template.d.ts.map +1 -1
  261. package/dist/template/template.js +6 -4
  262. package/dist/template/zod-converter.d.ts +0 -19
  263. package/dist/template/zod-converter.d.ts.map +1 -1
  264. package/dist/template/zod-converter.js +100 -84
  265. package/dist/testing/_relation-graph.d.ts.map +1 -1
  266. package/dist/testing/_relation-graph.js +2 -1
  267. package/dist/testing/bootstrap.d.ts.map +1 -1
  268. package/dist/testing/bootstrap.js +9 -6
  269. package/dist/testing/data-explorer.d.ts +32 -7
  270. package/dist/testing/data-explorer.d.ts.map +1 -1
  271. package/dist/testing/data-explorer.js +77 -31
  272. package/dist/testing/dev-test-routes.d.ts +8 -1
  273. package/dist/testing/dev-test-routes.d.ts.map +1 -1
  274. package/dist/testing/dev-test-routes.js +125 -108
  275. package/dist/testing/dev-vitest-manager.d.ts +54 -8
  276. package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
  277. package/dist/testing/dev-vitest-manager.js +24 -9
  278. package/dist/testing/faker-mappings.d.ts +412 -1
  279. package/dist/testing/faker-mappings.d.ts.map +1 -1
  280. package/dist/testing/faker-mappings.js +1 -1
  281. package/dist/testing/filesystem.d.ts +2 -0
  282. package/dist/testing/filesystem.d.ts.map +1 -0
  283. package/dist/testing/filesystem.js +7 -0
  284. package/dist/testing/fixture-generator.d.ts +25 -8
  285. package/dist/testing/fixture-generator.d.ts.map +1 -1
  286. package/dist/testing/fixture-generator.js +132 -73
  287. package/dist/testing/fixture-loader.d.ts +3 -1
  288. package/dist/testing/fixture-loader.d.ts.map +1 -1
  289. package/dist/testing/fixture-loader.js +1 -20
  290. package/dist/testing/fixture-manager.d.ts +8 -7
  291. package/dist/testing/fixture-manager.d.ts.map +1 -1
  292. package/dist/testing/fixture-manager.js +91 -59
  293. package/dist/testing/global-setup.d.ts.map +1 -1
  294. package/dist/testing/global-setup.js +5 -2
  295. package/dist/testing/index.d.ts +2 -0
  296. package/dist/testing/index.d.ts.map +1 -1
  297. package/dist/testing/index.js +6 -3
  298. package/dist/types/types.d.ts +101 -99
  299. package/dist/types/types.d.ts.map +1 -1
  300. package/dist/types/types.js +234 -225
  301. package/dist/ui/ai-api.d.ts.map +1 -1
  302. package/dist/ui/ai-api.js +1 -1
  303. package/dist/ui/ai-client.d.ts.map +1 -1
  304. package/dist/ui/ai-client.js +28 -18
  305. package/dist/ui/api.d.ts.map +1 -1
  306. package/dist/ui/api.js +146 -210
  307. package/dist/ui/async-route.d.ts +3 -0
  308. package/dist/ui/async-route.d.ts.map +1 -0
  309. package/dist/ui/async-route.js +12 -0
  310. package/dist/ui/cdd-service.d.ts +9 -7
  311. package/dist/ui/cdd-service.d.ts.map +1 -1
  312. package/dist/ui/cdd-service.js +7 -5
  313. package/dist/ui/migrations-api.d.ts +3 -0
  314. package/dist/ui/migrations-api.d.ts.map +1 -0
  315. package/dist/ui/migrations-api.js +215 -0
  316. package/dist/ui-web/assets/index-D3QNH-Dv.css +1 -0
  317. package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
  318. package/dist/ui-web/index.html +2 -2
  319. package/dist/utils/formatter.d.ts.map +1 -1
  320. package/dist/utils/formatter.js +7 -5
  321. package/dist/utils/fs-utils.d.ts +3 -3
  322. package/dist/utils/fs-utils.d.ts.map +1 -1
  323. package/dist/utils/fs-utils.js +6 -6
  324. package/dist/utils/model.d.ts +3 -5
  325. package/dist/utils/model.d.ts.map +1 -1
  326. package/dist/utils/model.js +1 -1
  327. package/dist/utils/object-utils.d.ts +1 -1
  328. package/dist/utils/object-utils.d.ts.map +1 -1
  329. package/dist/utils/object-utils.js +17 -16
  330. package/dist/utils/process-utils.d.ts.map +1 -1
  331. package/dist/utils/process-utils.js +4 -2
  332. package/dist/utils/runtime-value.d.ts +13 -0
  333. package/dist/utils/runtime-value.d.ts.map +1 -0
  334. package/dist/utils/runtime-value.js +40 -0
  335. package/dist/utils/sql-parser.d.ts +8 -1
  336. package/dist/utils/sql-parser.d.ts.map +1 -1
  337. package/dist/utils/sql-parser.js +6 -3
  338. package/dist/utils/type-utils.d.ts.map +1 -1
  339. package/dist/utils/type-utils.js +10 -8
  340. package/dist/utils/utils.d.ts +2 -2
  341. package/dist/utils/utils.d.ts.map +1 -1
  342. package/dist/utils/utils.js +11 -8
  343. package/dist/utils/zod-error.d.ts.map +1 -1
  344. package/dist/utils/zod-error.js +7 -4
  345. package/dist/vector/chunking.d.ts.map +1 -1
  346. package/dist/vector/chunking.js +1 -1
  347. package/dist/vector/embedding.d.ts.map +1 -1
  348. package/dist/vector/embedding.js +1 -1
  349. package/dist/vector/types.d.ts +3 -2
  350. package/dist/vector/types.d.ts.map +1 -1
  351. package/package.json +11 -7
  352. package/src/ai/agents/agent.ts +5 -5
  353. package/src/ai/agents/types.ts +1 -1
  354. package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
  355. package/src/ai/providers/rtzr/error.ts +5 -3
  356. package/src/ai/providers/rtzr/model.ts +3 -2
  357. package/src/ai/providers/rtzr/options.ts +10 -5
  358. package/src/ai/providers/rtzr/provider.ts +10 -10
  359. package/src/ai/providers/rtzr/utils.ts +3 -1
  360. package/src/api/__tests__/config.test.ts +11 -21
  361. package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
  362. package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
  363. package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
  364. package/src/api/caster.ts +6 -8
  365. package/src/api/code-converters.ts +136 -36
  366. package/src/api/config.ts +62 -4
  367. package/src/api/context.ts +3 -3
  368. package/src/api/decorators.ts +28 -17
  369. package/src/api/http-validator.ts +149 -0
  370. package/src/api/sonamu.ts +459 -152
  371. package/src/api/websocket-helpers.ts +24 -17
  372. package/src/auth/audit-log/builders.ts +3 -2
  373. package/src/auth/audit-log/client-ip.ts +4 -2
  374. package/src/auth/audit-log/events.ts +15 -1
  375. package/src/auth/audit-log/plugin.ts +129 -200
  376. package/src/auth/audit-log-ingestor.ts +19 -15
  377. package/src/auth/auth-generator.ts +10 -5
  378. package/src/auth/better-auth-request.ts +4 -3
  379. package/src/auth/knex-adapter.ts +8 -2
  380. package/src/auth/plugins/entity-definitions/index.ts +6 -5
  381. package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
  382. package/src/bin/__tests__/test-command.test.ts +9 -15
  383. package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
  384. package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
  385. package/src/bin/build-config.ts +167 -0
  386. package/src/bin/cli.ts +155 -38
  387. package/src/bin/compiler-policy.ts +35 -0
  388. package/src/bin/fixture.ts +14 -3
  389. package/src/bin/test-command.ts +74 -29
  390. package/src/bin/ts-loader-registration.ts +22 -4
  391. package/src/cache/decorator.ts +10 -3
  392. package/src/compress/compress.ts +17 -4
  393. package/src/cone/cone-generator.ts +34 -6
  394. package/src/database/__tests__/db.test.ts +1 -0
  395. package/src/database/__tests__/puri.test.ts +16 -15
  396. package/src/database/__tests__/transaction-scope.test.ts +5 -0
  397. package/src/database/_batch_update.ts +1 -0
  398. package/src/database/base-model.ts +81 -50
  399. package/src/database/base-model.types.ts +6 -6
  400. package/src/database/db.ts +29 -19
  401. package/src/database/knex.ts +30 -17
  402. package/src/database/puri-subset.test-d.ts +17 -0
  403. package/src/database/puri.ts +181 -143
  404. package/src/database/puri.types.test-d.ts +31 -1
  405. package/src/database/puri.types.ts +23 -21
  406. package/src/database/upsert-builder.ts +75 -53
  407. package/src/dict/sd.ts +15 -5
  408. package/src/dict/sonamu-dictionary.ts +6 -6
  409. package/src/dict/utils.ts +9 -1
  410. package/src/entity/entity-manager.ts +26 -9
  411. package/src/entity/entity-template-cone.ts +5 -10
  412. package/src/entity/entity.ts +50 -34
  413. package/src/env.ts +7 -3
  414. package/src/exceptions/error-handler.ts +17 -2
  415. package/src/exceptions/so-exceptions.ts +26 -20
  416. package/src/filter/utils.ts +21 -11
  417. package/src/index.ts +3 -0
  418. package/src/logger/configure.ts +26 -18
  419. package/src/migration/__tests__/migrator.test.ts +50 -57
  420. package/src/migration/code-generation.ts +132 -104
  421. package/src/migration/index-where-predicate.ts +51 -24
  422. package/src/migration/migration-set.ts +11 -5
  423. package/src/migration/migrator.ts +392 -244
  424. package/src/migration/postgresql-schema-reader.ts +52 -47
  425. package/src/migration/slack-confirm.ts +17 -15
  426. package/src/migration/types.ts +57 -0
  427. package/src/naite/naite-reporter.ts +1 -1
  428. package/src/naite/naite.ts +97 -24
  429. package/src/runtime-type.ts +34 -0
  430. package/src/ssr/hmr-opt-out.ts +78 -0
  431. package/src/ssr/renderer.ts +6 -0
  432. package/src/storage/base-file.ts +13 -13
  433. package/src/storage/buffered-file.ts +11 -11
  434. package/src/storage/s3-driver.ts +4 -1
  435. package/src/storage/uploaded-file.ts +8 -8
  436. package/src/stream/__tests__/ws-contracts.test.ts +4 -2
  437. package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
  438. package/src/stream/__tests__/ws.test.ts +30 -6
  439. package/src/stream/sse.ts +12 -11
  440. package/src/stream/ws-core.ts +1 -1
  441. package/src/stream/ws-delivery.ts +16 -7
  442. package/src/stream/ws-local-connection-store.ts +2 -3
  443. package/src/stream/ws-presence-store.ts +6 -1
  444. package/src/stream/ws-registry.ts +16 -7
  445. package/src/stream/ws-telemetry.ts +66 -43
  446. package/src/stream/ws.ts +73 -39
  447. package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
  448. package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
  449. package/src/syncer/api-parser.ts +67 -27
  450. package/src/syncer/checksum.ts +17 -11
  451. package/src/syncer/code-generator.ts +25 -17
  452. package/src/syncer/entity-operations.ts +3 -5
  453. package/src/syncer/file-copy.ts +46 -0
  454. package/src/syncer/file-patterns.ts +11 -8
  455. package/src/syncer/file-tracking.ts +3 -4
  456. package/src/syncer/filesystem-dependencies.ts +73 -0
  457. package/src/syncer/module-loader.ts +8 -4
  458. package/src/syncer/syncer-actions.ts +56 -18
  459. package/src/syncer/syncer.ts +116 -43
  460. package/src/syncer/watcher.ts +8 -4
  461. package/src/tasks/decorator.ts +20 -12
  462. package/src/tasks/step-wrapper.ts +19 -9
  463. package/src/tasks/workflow-manager.ts +15 -9
  464. package/src/template/__tests__/http_validators.template.test.ts +160 -0
  465. package/src/template/__tests__/services.template.mutation.test.ts +24 -23
  466. package/src/template/__tests__/services.template.websocket.test.ts +21 -25
  467. package/src/template/entity-converter.ts +5 -2
  468. package/src/template/helpers.ts +8 -9
  469. package/src/template/implementations/generated.template.ts +5 -4
  470. package/src/template/implementations/generated_http.template.ts +61 -15
  471. package/src/template/implementations/generated_sso.template.ts +15 -4
  472. package/src/template/implementations/http_validators.template.ts +175 -0
  473. package/src/template/implementations/model.template.ts +3 -1
  474. package/src/template/implementations/sd.template.ts +81 -41
  475. package/src/template/implementations/services.template.ts +2 -2
  476. package/src/template/implementations/view_form.template.ts +56 -39
  477. package/src/template/implementations/view_list.template.ts +31 -23
  478. package/src/template/template-manager.ts +15 -13
  479. package/src/template/template-types.ts +13 -5
  480. package/src/template/template.ts +4 -3
  481. package/src/template/zod-converter.ts +206 -91
  482. package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
  483. package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
  484. package/src/testing/_relation-graph.ts +4 -1
  485. package/src/testing/bootstrap.ts +12 -5
  486. package/src/testing/data-explorer.ts +102 -53
  487. package/src/testing/dev-test-routes.ts +87 -50
  488. package/src/testing/dev-vitest-manager.ts +88 -32
  489. package/src/testing/faker-mappings.ts +2 -2
  490. package/src/testing/filesystem.ts +1 -0
  491. package/src/testing/fixture-generator.ts +191 -131
  492. package/src/testing/fixture-loader.ts +5 -1
  493. package/src/testing/fixture-manager.ts +129 -90
  494. package/src/testing/global-setup.ts +7 -3
  495. package/src/testing/index.ts +5 -0
  496. package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
  497. package/src/types/types.ts +184 -158
  498. package/src/ui/ai-api.ts +9 -7
  499. package/src/ui/ai-client.ts +61 -56
  500. package/src/ui/api.ts +726 -770
  501. package/src/ui/async-route.ts +4 -0
  502. package/src/ui/cdd-service.ts +35 -12
  503. package/src/ui/migrations-api.ts +381 -0
  504. package/src/utils/formatter.ts +7 -3
  505. package/src/utils/fs-utils.ts +5 -5
  506. package/src/utils/model.ts +4 -5
  507. package/src/utils/object-utils.ts +32 -22
  508. package/src/utils/process-utils.ts +4 -1
  509. package/src/utils/runtime-value.ts +43 -0
  510. package/src/utils/sql-parser.ts +13 -6
  511. package/src/utils/type-utils.ts +9 -7
  512. package/src/utils/utils.ts +19 -21
  513. package/src/utils/zod-error.ts +4 -2
  514. package/src/vector/chunking.ts +6 -1
  515. package/src/vector/embedding.ts +2 -1
  516. package/src/vector/types.ts +4 -2
  517. package/tsdown.api.config.ts +5 -2
  518. package/dist/ui-web/assets/index-CJf8uJYf.js +0 -176
  519. package/dist/ui-web/assets/index-GMMIVGja.css +0 -1
@@ -0,0 +1,73 @@
1
+ import {
2
+ constants,
3
+ type MakeDirectoryOptions,
4
+ type PathLike,
5
+ type RmOptions,
6
+ type Stats,
7
+ } from "node:fs";
8
+ import { access, mkdir, readFile, rm, stat, unlink, writeFile } from "node:fs/promises";
9
+
10
+ export type SyncerFilesystemDependencies = {
11
+ access(path: PathLike, mode?: number): Promise<void>;
12
+ mkdir(path: PathLike, options?: MakeDirectoryOptions): Promise<string | undefined>;
13
+ readFile(path: PathLike, encoding: BufferEncoding): Promise<string>;
14
+ rm(path: PathLike, options?: RmOptions): Promise<void>;
15
+ stat(path: PathLike): Promise<Stats>;
16
+ unlink(path: PathLike): Promise<void>;
17
+ writeFile(
18
+ path: PathLike,
19
+ data: string | NodeJS.ArrayBufferView,
20
+ encoding?: BufferEncoding,
21
+ ): Promise<void>;
22
+ };
23
+
24
+ const defaults: SyncerFilesystemDependencies = {
25
+ access: (path, mode) => access(path, mode),
26
+ mkdir: (path, options) => mkdir(path, options),
27
+ readFile: (path, encoding) => readFile(path, encoding),
28
+ rm: (path, options) => rm(path, options),
29
+ stat: (path) => stat(path),
30
+ unlink: (path) => unlink(path),
31
+ writeFile: (path, data, encoding) => writeFile(path, data, encoding),
32
+ };
33
+
34
+ export const syncerFilesystem: SyncerFilesystemDependencies = { ...defaults };
35
+
36
+ export async function syncerFileExists(path: PathLike): Promise<boolean> {
37
+ try {
38
+ await syncerFilesystem.access(path, constants.F_OK);
39
+ return true;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ /** 테스트와 임베딩 환경에서 Syncer의 파일시스템 경계를 교체합니다. */
46
+ export function configureSyncerFilesystem(
47
+ dependencies: Partial<SyncerFilesystemDependencies>,
48
+ ): () => void {
49
+ const restorers: Array<() => void> = [];
50
+ const override = <Key extends keyof SyncerFilesystemDependencies>(key: Key): void => {
51
+ if (!(key in dependencies)) return;
52
+
53
+ const previousDependency = syncerFilesystem[key];
54
+ Object.assign(syncerFilesystem, { [key]: dependencies[key] });
55
+ restorers.push(() => Object.assign(syncerFilesystem, { [key]: previousDependency }));
56
+ };
57
+
58
+ override("access");
59
+ override("mkdir");
60
+ override("readFile");
61
+ override("rm");
62
+ override("stat");
63
+ override("unlink");
64
+ override("writeFile");
65
+
66
+ let configured = true;
67
+ return () => {
68
+ if (!configured) return;
69
+ configured = false;
70
+
71
+ for (const restore of restorers) restore();
72
+ };
73
+ }
@@ -11,6 +11,7 @@ import { globAsync } from "../utils/async-utils";
11
11
  import { importMembers } from "../utils/esm-utils";
12
12
  import { runtimePath } from "../utils/path-utils";
13
13
  import { type AbsolutePath } from "../utils/path-utils";
14
+ import { isFunctionValue, isObjectValue } from "../utils/runtime-value";
14
15
  import { readApisFromFile } from "./api-parser";
15
16
 
16
17
  export type LoadedApis = ExtendedApi[];
@@ -35,7 +36,10 @@ export async function loadApis(): Promise<LoadedApis> {
35
36
  Sonamu.apiRootPath,
36
37
  "src/application/**/*.{model,frame}.ts", // !! runtimePath 안 씀 주의 !!
37
38
  );
38
- const modelPaths = (await globAsync(modelPathsPattern)) as AbsolutePath[];
39
+ const modelPaths =
40
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (await globAsync(
41
+ modelPathsPattern,
42
+ )) as AbsolutePath[];
39
43
 
40
44
  const apis: LoadedApis = [];
41
45
  for (const filePath of modelPaths) {
@@ -125,16 +129,16 @@ export async function loadWorkflows() {
125
129
  importedMembers
126
130
  .filter(({ value }) => {
127
131
  return (
128
- typeof value === "object" &&
132
+ isObjectValue(value) &&
129
133
  value !== null &&
130
134
  "type" in value &&
131
135
  value.type === "workflow" &&
132
136
  "fn" in value &&
133
- typeof value.fn === "function"
137
+ isFunctionValue(value.fn)
134
138
  );
135
139
  })
136
140
  .map(({ value }) => {
137
- return value as WorkflowMetadata;
141
+ return /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ value as WorkflowMetadata;
138
142
  }),
139
143
  );
140
144
  }
@@ -1,5 +1,4 @@
1
1
  import assert from "assert";
2
- import { mkdir, readFile, writeFile } from "fs/promises";
3
2
  import path, { dirname } from "path";
4
3
 
5
4
  import chalk from "chalk";
@@ -10,10 +9,12 @@ import { AlreadyProcessedException } from "../exceptions/so-exceptions";
10
9
  import { Naite } from "../naite/naite";
11
10
  import { isTest } from "../utils/controller";
12
11
  import { formatCode } from "../utils/formatter";
13
- import { copyFileWithReplaceCoreToShared, exists } from "../utils/fs-utils";
14
12
  import { type AbsolutePath } from "../utils/path-utils";
13
+ import { isObjectValue } from "../utils/runtime-value";
15
14
  import { generateTemplate } from "./code-generator";
15
+ import { copyFileWithReplaceCoreToShared } from "./file-copy";
16
16
  import { trackWritten } from "./file-tracking";
17
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
17
18
 
18
19
  // web/.sonamu.env 에 현재 설정값 저장
19
20
  export async function actionSyncConfig() {
@@ -22,8 +23,11 @@ export async function actionSyncConfig() {
22
23
 
23
24
  Naite.t("actionSyncConfig", { content });
24
25
  await Promise.all([
25
- ...Sonamu.config.sync.targets.map(async (target) => {
26
- await writeFile(path.join(Sonamu.appRootPath, target, ".sonamu.env"), content);
26
+ ...Sonamu.config.sync.targets.map((target) => {
27
+ return syncerFilesystem.writeFile(
28
+ path.join(Sonamu.appRootPath, target, ".sonamu.env"),
29
+ content,
30
+ );
27
31
  }),
28
32
  generateTemplate("generated_sso", {}, { overwrite: true }),
29
33
  ]);
@@ -88,6 +92,30 @@ export async function actionGenerateHttps(): Promise<AbsolutePath> {
88
92
  return res;
89
93
  }
90
94
 
95
+ /**
96
+ * 최종 REST caster를 담는 API 전용 validator registry를 재생성합니다.
97
+ */
98
+ export async function actionGenerateHttpValidators(): Promise<AbsolutePath> {
99
+ const registryPath =
100
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ path.join(
101
+ Sonamu.apiRootPath,
102
+ "src/application/sonamu.validators.generated.ts",
103
+ ) as AbsolutePath;
104
+ const policy = Sonamu.config.validation?.zodCompiler;
105
+ const isAot = isObjectValue(policy) && policy !== null && policy.api === "aot";
106
+ if (!isAot) {
107
+ // opt-out/JIT 전환 뒤 AOT compile import가 애플리케이션에 남지 않도록 산출물도 함께 정리합니다.
108
+ if (await syncerFileExists(registryPath)) {
109
+ await syncerFilesystem.unlink(registryPath);
110
+ }
111
+ return registryPath;
112
+ }
113
+
114
+ const [generatedPath] = await generateTemplate("http_validators", {}, { overwrite: true });
115
+ assert(generatedPath);
116
+ return generatedPath;
117
+ }
118
+
91
119
  /**
92
120
  * queries.generated.ts 재생성합니다.
93
121
  * @returns 생성된 파일 경로 배열.
@@ -137,8 +165,8 @@ export async function actionSyncFilesToTargets(tsPaths: AbsolutePath[]): Promise
137
165
  .replace(`/${apiDir}/`, `/${target}/`)
138
166
  .replace("/application/", "/services/");
139
167
  const dir = dirname(dst);
140
- if (!(await exists(dir))) {
141
- await mkdir(dir, { recursive: true });
168
+ if (!(await syncerFileExists(dir))) {
169
+ await syncerFilesystem.mkdir(dir, { recursive: true });
142
170
  }
143
171
  const syncHeader = [
144
172
  "/**",
@@ -147,7 +175,9 @@ export async function actionSyncFilesToTargets(tsPaths: AbsolutePath[]): Promise
147
175
  " */",
148
176
  ].join("\n");
149
177
  await copyFileWithReplaceCoreToShared(realSrc, dst, syncHeader);
150
- await trackWritten(dst as AbsolutePath);
178
+ await trackWritten(
179
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ dst as AbsolutePath,
180
+ );
151
181
  !isTest() &&
152
182
  console.log(
153
183
  chalk.bold("Copied: ") + chalk.blue(dst.replace(`${Sonamu.appRootPath}/`, "")),
@@ -174,7 +204,9 @@ export async function actionCopySharedToTargetsIfNotExists(): Promise<void> {
174
204
  import.meta.dirname.replace("/dist/", "/src/"),
175
205
  "../dict/utils.ts",
176
206
  );
177
- const dictUtilsCode = (await exists(dictUtilsPath)) ? await readFile(dictUtilsPath, "utf-8") : "";
207
+ const dictUtilsCode = (await syncerFileExists(dictUtilsPath))
208
+ ? await syncerFilesystem.readFile(dictUtilsPath, "utf-8")
209
+ : "";
178
210
 
179
211
  // 특정 변수 치환을 위해서 사용합니다.
180
212
  const convertMap = {
@@ -192,16 +224,16 @@ export async function actionCopySharedToTargetsIfNotExists(): Promise<void> {
192
224
  import.meta.dirname.replace("/dist/", "/src/"),
193
225
  `../shared/${target}.shared.ts.txt`,
194
226
  );
195
- if (!(await exists(srcPath))) {
227
+ if (!(await syncerFileExists(srcPath))) {
196
228
  continue;
197
229
  }
198
- if (!(await exists(path.join(Sonamu.appRootPath, target)))) {
230
+ if (!(await syncerFileExists(path.join(Sonamu.appRootPath, target)))) {
199
231
  throw new Error(
200
232
  `Tried to copy sonamu.shared.ts to target '${target}' but the target directory does not exist. Please check your project directory structure.`,
201
233
  );
202
234
  }
203
235
 
204
- const fullText = await readFile(srcPath, "utf-8");
236
+ const fullText = await syncerFilesystem.readFile(srcPath, "utf-8");
205
237
  const convertedText = Object.entries(convertMap).reduce(
206
238
  (acc, [key, value]) => acc.replace(`$[[${key}]]`, value),
207
239
  fullText,
@@ -211,8 +243,8 @@ export async function actionCopySharedToTargetsIfNotExists(): Promise<void> {
211
243
  const destPath = path.join(Sonamu.appRootPath, target, "src/services/sonamu.shared.ts");
212
244
 
213
245
  // 정말 혹시나지만 target 디렉토리는 있어도 src/services 디렉토리는 없을 수 있으므로 미리 생성해줍니다.
214
- if (!(await exists(path.dirname(destPath)))) {
215
- await mkdir(path.dirname(destPath), { recursive: true });
246
+ if (!(await syncerFileExists(path.dirname(destPath)))) {
247
+ await syncerFilesystem.mkdir(path.dirname(destPath), { recursive: true });
216
248
  console.warn(`Created directory '${path.dirname(destPath)}' because it did not exist.`);
217
249
  }
218
250
 
@@ -221,11 +253,11 @@ export async function actionCopySharedToTargetsIfNotExists(): Promise<void> {
221
253
  // 최초 1회만 생성하고 이후에는 덮어쓰지 않습니다.
222
254
  // 템플릿 내용($[[dictUtils]] 등)이 변경되었을 때 반영이 필요하면,
223
255
  // 해당 파일을 삭제한 뒤 `pnpm sonamu sync`로 재생성하면 됩니다.
224
- if (await exists(destPath)) {
256
+ if (await syncerFileExists(destPath)) {
225
257
  continue;
226
258
  }
227
259
 
228
- await writeFile(destPath, await formatCode(convertedText, destPath));
260
+ await syncerFilesystem.writeFile(destPath, await formatCode(convertedText, destPath));
229
261
  !isTest() &&
230
262
  console.log(chalk.bold("Copied: ") + chalk.blue(path.relative(Sonamu.appRootPath, destPath)));
231
263
  }
@@ -244,7 +276,11 @@ export async function actionSyncSonamuDictionary(): Promise<void> {
244
276
  const { targets } = Sonamu.config.sync;
245
277
  const i18nConfig = Sonamu.config.i18n;
246
278
 
247
- const targetList = ["api", ...targets] as ("api" | "web" | "app")[];
279
+ const targetList =
280
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ [
281
+ "api",
282
+ ...targets,
283
+ ] as ("api" | "web" | "app")[];
248
284
 
249
285
  const apiI18nDir = path.join(Sonamu.appRootPath, Sonamu.config.api.dir, "src/i18n");
250
286
 
@@ -273,7 +309,7 @@ async function syncLocaleFiles(
273
309
  const targetI18nDir = path.join(Sonamu.appRootPath, target, "src/i18n");
274
310
 
275
311
  // 디렉토리가 없으면 생성
276
- await mkdir(targetI18nDir, { recursive: true });
312
+ await syncerFilesystem.mkdir(targetI18nDir, { recursive: true });
277
313
 
278
314
  for (const locale of locales) {
279
315
  const sourceFile = path.join(apiI18nDir, `${locale}.ts`);
@@ -286,7 +322,9 @@ async function syncLocaleFiles(
286
322
  " */",
287
323
  ].join("\n");
288
324
  await copyFileWithReplaceCoreToShared(sourceFile, targetFile, syncHeader);
289
- await trackWritten(targetFile as AbsolutePath);
325
+ await trackWritten(
326
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ targetFile as AbsolutePath,
327
+ );
290
328
  !isTest() &&
291
329
  console.log(chalk.bold("Copied: ") + chalk.cyan(`${target}/src/i18n/${locale}.ts`));
292
330
  }
@@ -1,12 +1,11 @@
1
1
  import assert from "assert";
2
2
  import { EventEmitter } from "events";
3
- import { unlink } from "fs/promises";
4
3
  import path from "path";
5
4
 
6
5
  import { hot } from "@sonamu-kit/hmr-hook";
7
6
  import chalk from "chalk";
8
7
  import { minimatch } from "minimatch";
9
- import { group, unique } from "radashi";
8
+ import { unique } from "radashi";
10
9
  import { type z } from "zod";
11
10
 
12
11
  import { registeredApis } from "../api/decorators";
@@ -22,20 +21,36 @@ import { type TemplateOptions } from "../types/types";
22
21
  import { mapAsync, reduceAsync } from "../utils/async-utils";
23
22
  import { centerText } from "../utils/console-util";
24
23
  import { isTest } from "../utils/controller";
25
- import { exists } from "../utils/fs-utils";
26
24
  import { type AbsolutePath } from "../utils/path-utils";
27
25
  import { runWithGracefulShutdown } from "../utils/process-utils";
26
+ import { isObjectValue } from "../utils/runtime-value";
28
27
  import { findChangedFilesUsingChecksums, renewChecksums } from "./checksum";
29
28
  import { generateTemplate, renderTemplate } from "./code-generator";
30
29
  import { createEntity, delEntity } from "./entity-operations";
31
30
  import { getChecksumPatternGroup, getChecksumPatternGroupInAbsolutePath } from "./file-patterns";
32
31
  import { type FileType } from "./file-patterns";
32
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
33
33
  import { loadApis, loadModels, loadTypes, loadWorkflows } from "./module-loader";
34
34
  import { type LoadedApis, type LoadedModels, type LoadedTypes } from "./module-loader";
35
35
  import * as SyncerActions from "./syncer-actions";
36
36
 
37
- type DiffGroups = {
38
- [key in FileType]: AbsolutePath[];
37
+ type DiffGroupKey = FileType | "unknown";
38
+ type DiffGroups = Partial<Record<DiffGroupKey, AbsolutePath[]>>;
39
+
40
+ export interface SyncerDependencies {
41
+ actionCopySharedToTargetsIfNotExists: typeof SyncerActions.actionCopySharedToTargetsIfNotExists;
42
+ actionGenerateHttpValidators: typeof SyncerActions.actionGenerateHttpValidators;
43
+ actionGenerateSsrEntryServerIfNotExists: typeof SyncerActions.actionGenerateSsrEntryServerIfNotExists;
44
+ findChangedFilesUsingChecksums: typeof findChangedFilesUsingChecksums;
45
+ renewChecksums: typeof renewChecksums;
46
+ }
47
+
48
+ const defaultSyncerDependencies: SyncerDependencies = {
49
+ actionCopySharedToTargetsIfNotExists: SyncerActions.actionCopySharedToTargetsIfNotExists,
50
+ actionGenerateHttpValidators: SyncerActions.actionGenerateHttpValidators,
51
+ actionGenerateSsrEntryServerIfNotExists: SyncerActions.actionGenerateSsrEntryServerIfNotExists,
52
+ findChangedFilesUsingChecksums,
53
+ renewChecksums,
39
54
  };
40
55
 
41
56
  export class Syncer {
@@ -45,6 +60,8 @@ export class Syncer {
45
60
  workflows: Map<string, WorkflowMetadata[]> = new Map();
46
61
  eventEmitter: EventEmitter = new EventEmitter();
47
62
 
63
+ constructor(private readonly dependencies: SyncerDependencies = defaultSyncerDependencies) {}
64
+
48
65
  /**
49
66
  * 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
50
67
  * dev 서버가 처음 떴을 때, sonamu sync 할 때 실행됩니다. 이후에는 hmrAndSync 경로를 타요.
@@ -54,12 +71,15 @@ export class Syncer {
54
71
  // 초기 부트스트랩! 얘네들은 idempotent하고 가볍기 때문에 무지성 실행해도 됩니다.
55
72
  // 얘네들은 sonamu.lock에 들어가지도 않고 따라서 HMR 경로를 타지도 않는 친구들입니다.
56
73
  // 그래서 아무 때나 그냥 돌려주면 되는데, hmrAndSync에서 매번 하는 것은 낭비이니 여기서 한 번만 합니다.
57
- await SyncerActions.actionCopySharedToTargetsIfNotExists();
58
- await SyncerActions.actionGenerateSsrEntryServerIfNotExists();
74
+ await this.dependencies.actionCopySharedToTargetsIfNotExists();
75
+ await this.dependencies.actionGenerateSsrEntryServerIfNotExists();
59
76
 
60
77
  // 바뀐 것이 없으면 그냥 넘어가요.
61
- const changedFiles = await findChangedFilesUsingChecksums();
78
+ const changedFiles = await this.dependencies.findChangedFilesUsingChecksums();
62
79
  if (changedFiles.length === 0) {
80
+ if (await this.reconcileHttpValidatorRegistry()) {
81
+ await this.dependencies.renewChecksums();
82
+ }
63
83
  console.log(chalk.black.bgGreen(centerText("All files are synced!")));
64
84
  return;
65
85
  }
@@ -71,14 +91,31 @@ export class Syncer {
71
91
  async () => {
72
92
  // 얘가 싱크 작업 수행하는 본체입니다.
73
93
  await this.doSyncActions(changedFiles);
94
+ await this.reconcileHttpValidatorRegistry();
74
95
 
75
96
  // 싱크 액션이 끝나면 항상 체크섬을 다시 갱신합니다.
76
- await renewChecksums();
97
+ await this.dependencies.renewChecksums();
77
98
  },
78
99
  { whenThisHappens: "SIGUSR2", waitForUpTo: 20000 },
79
100
  );
80
101
  }
81
102
 
103
+ private async reconcileHttpValidatorRegistry(): Promise<boolean> {
104
+ const registryPath = path.join(
105
+ Sonamu.apiRootPath,
106
+ "src/application/sonamu.validators.generated.ts",
107
+ );
108
+ const policy = Sonamu.config.validation?.zodCompiler;
109
+ const isAot = isObjectValue(policy) && policy !== null && policy.api === "aot";
110
+ if (isAot === (await syncerFileExists(registryPath))) {
111
+ return false;
112
+ }
113
+
114
+ // 변경 목록이 registry 삭제를 놓쳐도 checksum 갱신 전에 정책과 산출물을 다시 맞춥니다.
115
+ await this.dependencies.actionGenerateHttpValidators();
116
+ return true;
117
+ }
118
+
82
119
  /**
83
120
  * 강제 풀-싱크: lock을 무시하고 처음부터 다시 싱크합니다.
84
121
  *
@@ -88,8 +125,8 @@ export class Syncer {
88
125
  */
89
126
  async forceSync(): Promise<void> {
90
127
  const lockPath = path.join(Sonamu.apiRootPath, "sonamu.lock");
91
- if (await exists(lockPath)) {
92
- await unlink(lockPath);
128
+ if (await syncerFileExists(lockPath)) {
129
+ await syncerFilesystem.unlink(lockPath);
93
130
  }
94
131
  await this.sync();
95
132
  }
@@ -137,6 +174,7 @@ export class Syncer {
137
174
  await this.autoloadApis();
138
175
  await this.autoloadWorkflows();
139
176
  await this.autoloadSsrRoutes();
177
+ await Sonamu.refreshHttpValidators();
140
178
 
141
179
  this.eventEmitter.emit("onHMRCompleted");
142
180
  }
@@ -182,7 +220,11 @@ export class Syncer {
182
220
  // 그러니 안심하고 invalidate 해도 됩니다.
183
221
  // 테스트 환경에서는 hot.invalidateFile시 초기 에러가 발생하기 때문에 invalidate 하지 않습니다.
184
222
  if (!isTest()) {
185
- const invalidatedPaths = (await hot.invalidateFile(diffFilePath, event)) as AbsolutePath[];
223
+ const invalidatedPaths =
224
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (await hot.invalidateFile(
225
+ diffFilePath,
226
+ event,
227
+ )) as AbsolutePath[];
186
228
 
187
229
  if (invalidatedPaths.length > 0) {
188
230
  console.log(chalk.bold(`🔄 Invalidated:`));
@@ -271,7 +313,7 @@ export class Syncer {
271
313
  clearSSRRoutes();
272
314
 
273
315
  // ssr 폴더 없으면 스킵
274
- if (!(await exists(ssrConfigPath))) {
316
+ if (!(await syncerFileExists(ssrConfigPath))) {
275
317
  return;
276
318
  }
277
319
 
@@ -302,7 +344,10 @@ export class Syncer {
302
344
  */
303
345
  async doSyncActions(diffFilePaths: AbsolutePath[]): Promise<{ diffTypes: FileType[] }> {
304
346
  const diffGroups = this.calculateDiffGroups(diffFilePaths);
305
- const diffTypes = Object.keys(diffGroups) as FileType[];
347
+ const diffTypes =
348
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ Object.keys(
349
+ diffGroups,
350
+ ) as FileType[];
306
351
 
307
352
  // 여기는 별로 중요한 파트는 아닙니다.
308
353
  // 아래의 if 전개를 깔끔하게 하려고 만든 DSL 같은 거라서, 무시하셔도 됩니다.
@@ -324,11 +369,27 @@ export class Syncer {
324
369
  }
325
370
 
326
371
  if (changeMatches("config")) {
327
- await this.handleConfigChanges(diffGroups);
372
+ await this.handleConfigChanges();
328
373
  }
329
374
 
330
375
  if (changeMatches("i18n", "entity" /*레이블*/, "config" /*defaultLocale등*/)) {
331
- await this.handleSonamuDictionaryRelatedChanges(diffGroups);
376
+ await this.handleSonamuDictionaryRelatedChanges();
377
+ }
378
+
379
+ const metadataChanged = diffTypes.some((type) =>
380
+ ["entity", "types", "model", "frame", "config"].includes(type),
381
+ );
382
+ if (changeMatches("httpValidatorsGenerated") && !metadataChanged) {
383
+ // registry 자체 drift는 최신 in-memory metadata로 즉시 덮어써 lock 갱신 전에 정합성을 복구합니다.
384
+ await this.dependencies.actionGenerateHttpValidators();
385
+ }
386
+
387
+ if (metadataChanged) {
388
+ // 모든 생성/복사 액션 뒤 최신 metadata를 다시 읽어 registry를 한 번만 확정합니다.
389
+ await this.autoloadTypes();
390
+ await this.autoloadModels();
391
+ await this.autoloadApis();
392
+ await this.dependencies.actionGenerateHttpValidators();
332
393
  }
333
394
 
334
395
  if (nothingMatches()) {
@@ -343,22 +404,26 @@ export class Syncer {
343
404
  };
344
405
  }
345
406
 
346
- calculateDiffGroups(diffFiles: AbsolutePath[]): DiffGroups {
407
+ calculateDiffGroups(diffFiles: AbsolutePath[]) {
347
408
  const patternGroup = getChecksumPatternGroup();
348
- const fileTypes = Object.keys(patternGroup) as FileType[];
349
-
350
- return group(diffFiles, (filePath) => {
351
- // 절대 경로 → appRoot 기준 상대 경로 (예: "api/src/...", "web/src/...")
409
+ const fileTypes =
410
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ Object.keys(
411
+ patternGroup,
412
+ ) as FileType[];
413
+
414
+ const diffGroups: DiffGroups = {};
415
+ for (const filePath of diffFiles) {
416
+ // 절대 경로를 appRoot 기준 상대 경로로 바꿔 체크섬 패턴과 비교한다.
352
417
  const relativePath = path.relative(Sonamu.appRootPath, filePath);
353
- if (relativePath.startsWith("..")) return "unknown";
354
-
355
- for (const fileType of fileTypes) {
356
- if (minimatch(relativePath, patternGroup[fileType])) {
357
- return fileType;
358
- }
359
- }
360
- return "unknown";
361
- }) as unknown as DiffGroups;
418
+ const fileType = fileTypes.find((candidate) =>
419
+ minimatch(relativePath, patternGroup[candidate]),
420
+ );
421
+ const groupKey: DiffGroupKey = relativePath.startsWith("..")
422
+ ? "unknown"
423
+ : (fileType ?? "unknown");
424
+ (diffGroups[groupKey] ??= []).push(filePath);
425
+ }
426
+ return diffGroups;
362
427
  }
363
428
 
364
429
  private changeMatcher(diffTypes: FileType[], diffGroups: DiffGroups) {
@@ -404,12 +469,13 @@ export class Syncer {
404
469
  const entity = EntityManager.get(entityId);
405
470
 
406
471
  // 프로젝트에 생성되어야 하는 .ts 파일의 경로입니다.
407
- const typeFilePath = path.join(
408
- Sonamu.apiRootPath,
409
- `src/application/${entity.names.fs}/${entity.names.fs}.types.ts`,
410
- ) as AbsolutePath;
472
+ const typeFilePath =
473
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ path.join(
474
+ Sonamu.apiRootPath,
475
+ `src/application/${entity.names.fs}/${entity.names.fs}.types.ts`,
476
+ ) as AbsolutePath;
411
477
 
412
- if (entity.parentId === undefined && !(await exists(typeFilePath))) {
478
+ if (entity.parentId === undefined && !(await syncerFileExists(typeFilePath))) {
413
479
  // *.types.ts가 만들어집니다.
414
480
  const types = await SyncerActions.actionGenerateInitialTypes(entityId);
415
481
 
@@ -464,11 +530,15 @@ export class Syncer {
464
530
  await SyncerActions.actionSyncFilesToTargets(tsPaths);
465
531
  }
466
532
 
467
- async handleConfigChanges(_: DiffGroups): Promise<void> {
533
+ async handleConfigChanges(): Promise<void> {
534
+ if (process.env.HOT === "yes") {
535
+ const { loadConfig } = await import("../api/config");
536
+ Sonamu.config = await loadConfig(Sonamu.apiRootPath);
537
+ }
468
538
  await SyncerActions.actionSyncConfig();
469
539
  }
470
540
 
471
- async handleSonamuDictionaryRelatedChanges(_: DiffGroups): Promise<void> {
541
+ async handleSonamuDictionaryRelatedChanges(): Promise<void> {
472
542
  await SyncerActions.actionSyncSonamuDictionary();
473
543
  }
474
544
 
@@ -508,7 +578,7 @@ export class Syncer {
508
578
  return {
509
579
  subPath,
510
580
  fullPath,
511
- isExists: await exists(fullPath),
581
+ isExists: await syncerFileExists(fullPath),
512
582
  };
513
583
  }
514
584
 
@@ -535,7 +605,7 @@ export class Syncer {
535
605
  if (key.startsWith("view_enums")) {
536
606
  await mapAsync(enumsKeys, async (componentId) => {
537
607
  const { target, path: p } = tpl.getTargetAndPath(names, componentId);
538
- result[`${key}__${componentId}`] = await exists(
608
+ result[`${key}__${componentId}`] = await syncerFileExists(
539
609
  path.join(Sonamu.appRootPath, target, p),
540
610
  );
541
611
  });
@@ -546,17 +616,20 @@ export class Syncer {
546
616
  const { targets } = Sonamu.config.sync;
547
617
  if (target.includes(":target")) {
548
618
  await mapAsync(targets, async (t) => {
549
- result[`${key}__${t}`] = await exists(
619
+ result[`${key}__${t}`] = await syncerFileExists(
550
620
  path.join(Sonamu.appRootPath, target.replace(":target", t), p),
551
621
  );
552
622
  });
553
623
  } else {
554
- result[key] = await exists(path.join(Sonamu.appRootPath, target, p));
624
+ result[key] = await syncerFileExists(path.join(Sonamu.appRootPath, target, p));
555
625
  }
556
626
 
557
627
  return result;
558
628
  },
559
- {} as Record<`${TemplateKey}${string}`, boolean>,
629
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ {} as Record<
630
+ `${TemplateKey}${string}`,
631
+ boolean
632
+ >,
560
633
  );
561
634
  }
562
635
 
@@ -599,6 +672,6 @@ export class Syncer {
599
672
  * 하위호환용 프록시 메소드입니다.
600
673
  */
601
674
  async renewChecksums(): Promise<void> {
602
- return await renewChecksums();
675
+ return await this.dependencies.renewChecksums();
603
676
  }
604
677
  }
@@ -3,7 +3,7 @@ import { type Stats } from "node:fs";
3
3
  import path from "path";
4
4
 
5
5
  import chalk from "chalk";
6
- import chokidar, { type FSWatcher } from "chokidar";
6
+ import { type FSWatcher, watch } from "chokidar";
7
7
  import { minimatch } from "minimatch";
8
8
 
9
9
  import { Sonamu } from "../api";
@@ -22,7 +22,7 @@ export async function setupWatcher(
22
22
  // api 본인뿐 아니라 sync target들의 src도 봅니다.
23
23
  // target 산출물(sonamu.generated, services.generated, i18n copy 등)이 외부에서
24
24
  // 변경되는 경우를 drift로 잡아 워닝을 띄우기 위함입니다.
25
- const watcher = chokidar.watch(apiAndTargetsSrcPaths(), {
25
+ const watcher = watch(apiAndTargetsSrcPaths(), {
26
26
  ignored: ignoreIfExtensionIsNotOneOf(".ts", ".json", ".http"),
27
27
  persistent: true,
28
28
  ignoreInitial: true,
@@ -49,7 +49,8 @@ export async function setupWatcher(
49
49
  });
50
50
 
51
51
  watcher.on("all", (event: string, filePath: string) => {
52
- const absolutePath = filePath as AbsolutePath;
52
+ const absolutePath =
53
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ filePath as AbsolutePath;
53
54
  assert(
54
55
  absolutePath.startsWith(Sonamu.appRootPath),
55
56
  "File path is not within the app root path",
@@ -92,7 +93,10 @@ function apiAndTargetsSrcPaths() {
92
93
  * 따라서 "이 확장자만 허용"을 표현하려면 *다른 확장자에 대해 true*를 반환해야 합니다.
93
94
  */
94
95
  function ignoreIfExtensionIsNotOneOf(...allowedExtensions: `.${string}`[]) {
95
- const isAllowed = (ext: string) => (allowedExtensions as string[]).includes(ext);
96
+ const isAllowed = (ext: string) =>
97
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ (
98
+ allowedExtensions as string[]
99
+ ).includes(ext);
96
100
 
97
101
  return (p: string, stats?: Stats) => !!stats?.isFile() && !isAllowed(path.extname(p));
98
102
  }