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,6 +1,5 @@
1
1
  import crypto from "crypto";
2
2
  import { createReadStream, type PathLike } from "fs";
3
- import { readFile, writeFile } from "fs/promises";
4
3
  import path from "path";
5
4
 
6
5
  import equal from "fast-deep-equal";
@@ -8,10 +7,10 @@ import { isEqual } from "radashi";
8
7
 
9
8
  import { Sonamu } from "../api/sonamu";
10
9
  import { globAsync } from "../utils/async-utils";
11
- import { exists } from "../utils/fs-utils";
12
10
  import { type AbsolutePath, type AppRelativePath } from "../utils/path-utils";
13
11
  import { differenceWith } from "../utils/utils";
14
12
  import { getChecksumPatternGroupInAbsolutePath, GLOB_EXCLUDE } from "./file-patterns";
13
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
15
14
 
16
15
  type PathAndChecksum = {
17
16
  path: AbsolutePath;
@@ -61,7 +60,10 @@ async function getCurrentChecksums(): Promise<PathAndChecksum[]> {
61
60
 
62
61
  // 동일 파일이 여러 패턴에 매치될 수 있으므로(예: sd.generated.ts는 generated와 sdGenerated에 모두 매치)
63
62
  // 중복 제거 후 안정 정렬.
64
- const filePaths = Array.from(new Set(allPaths)).toSorted() as AbsolutePath[];
63
+ const filePaths =
64
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ Array.from(
65
+ new Set(allPaths),
66
+ ).toSorted() as AbsolutePath[];
65
67
 
66
68
  return await Promise.all(
67
69
  filePaths.map(async (filePath) => {
@@ -75,17 +77,18 @@ async function getCurrentChecksums(): Promise<PathAndChecksum[]> {
75
77
 
76
78
  async function getPreviousChecksums(): Promise<PathAndChecksum[]> {
77
79
  const checksumFilePath = getChecksumFilePath();
78
- if (!(await exists(checksumFilePath))) {
80
+ if (!(await syncerFileExists(checksumFilePath))) {
79
81
  return [];
80
82
  }
81
83
 
82
84
  try {
83
- const previousChecksums = JSON.parse(await readFile(checksumFilePath, "utf-8")).map(
84
- (r: { path: AppRelativePath; checksum: string }) => ({
85
+ const previousChecksums =
86
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ JSON.parse(
87
+ await syncerFilesystem.readFile(checksumFilePath, "utf-8"),
88
+ ).map((r: { path: AppRelativePath; checksum: string }) => ({
85
89
  path: path.join(Sonamu.appRootPath, r.path), // 체크섬 파일에서 읽을 때: appRoot 상대 경로 → 절대 경로
86
90
  checksum: r.checksum,
87
- }),
88
- ) as PathAndChecksum[];
91
+ })) as PathAndChecksum[];
89
92
  return previousChecksums;
90
93
  } catch (e) {
91
94
  // 체크섬 파일이 손상된 경우 빈 배열 반환 (전체 재동기화 유도)
@@ -98,7 +101,10 @@ async function getPreviousChecksums(): Promise<PathAndChecksum[]> {
98
101
  }
99
102
 
100
103
  function getChecksumFilePath(): AbsolutePath {
101
- return path.join(Sonamu.apiRootPath, "sonamu.lock") as AbsolutePath;
104
+ return /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ path.join(
105
+ Sonamu.apiRootPath,
106
+ "sonamu.lock",
107
+ ) as AbsolutePath;
102
108
  }
103
109
 
104
110
  async function saveChecksums(checksums: PathAndChecksum[]): Promise<void> {
@@ -109,8 +115,8 @@ async function saveChecksums(checksums: PathAndChecksum[]): Promise<void> {
109
115
  path: path.relative(Sonamu.appRootPath, r.path), // 체크섬 파일에 저장할 때: 절대 경로 → appRoot 상대 경로
110
116
  checksum: r.checksum,
111
117
  }))
112
- .sort((a, b) => a.path.localeCompare(b.path));
113
- await writeFile(checksumFilePath, JSON.stringify(serialized, null, 2), "utf-8");
118
+ .toSorted((a, b) => a.path.localeCompare(b.path));
119
+ await syncerFilesystem.writeFile(checksumFilePath, JSON.stringify(serialized, null, 2), "utf-8");
114
120
  }
115
121
 
116
122
  async function getChecksumOfFile(filePath: PathLike): Promise<string> {
@@ -1,4 +1,3 @@
1
- import { mkdir, writeFile } from "fs/promises";
2
1
  import path from "path";
3
2
 
4
3
  import chalk from "chalk";
@@ -21,10 +20,10 @@ import {
21
20
  import { everyAsync, filterAsync } from "../utils/async-utils";
22
21
  import { isTest } from "../utils/controller";
23
22
  import { formatCode } from "../utils/formatter";
24
- import { exists } from "../utils/fs-utils";
25
23
  import { wrapIf } from "../utils/lodash-able";
26
24
  import { type AbsolutePath } from "../utils/path-utils";
27
25
  import { trackWritten } from "./file-tracking";
26
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
28
27
 
29
28
  /**
30
29
  * 템플릿을 렌더링하고 파일로 생성합니다.
@@ -51,8 +50,8 @@ export async function generateTemplate<T extends TemplateKey>(
51
50
  // 템플릿 렌더
52
51
  const pathAndCodes = (
53
52
  await Promise.all(
54
- keys.map(async (key) => {
55
- return await renderTemplate(key, templateOptions);
53
+ keys.map(async (templateKey) => {
54
+ return await renderTemplate(templateKey, templateOptions);
56
55
  }),
57
56
  )
58
57
  ).flat();
@@ -65,7 +64,10 @@ export async function generateTemplate<T extends TemplateKey>(
65
64
  const { targets } = Sonamu.config.sync;
66
65
  const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}`;
67
66
  const dstFilePaths = targets.map((target) => filePath.replace("/:target/", `/${target}/`));
68
- return await everyAsync(dstFilePaths, async (dstPath) => !(await exists(dstPath)));
67
+ return await everyAsync(
68
+ dstFilePaths,
69
+ async (dstPath) => !(await syncerFileExists(dstPath)),
70
+ );
69
71
  });
70
72
  }
71
73
  })();
@@ -103,8 +105,8 @@ export async function renderTemplate<T extends keyof TemplateOptions>(
103
105
  if (rendered.preTemplates) {
104
106
  preTemplateResolved = (
105
107
  await Promise.all(
106
- rendered.preTemplates.map(({ key, options }) => {
107
- return renderTemplate(key, options);
108
+ rendered.preTemplates.map(({ key: preTemplateKey, options: preTemplateOptions }) => {
109
+ return renderTemplate(preTemplateKey, preTemplateOptions);
108
110
  }),
109
111
  )
110
112
  ).flat();
@@ -122,7 +124,10 @@ async function resolveRenderedTemplate(
122
124
  const { target, path: filePath, body, importKeys, customHeaders } = result;
123
125
 
124
126
  // import 할 대상의 대상 path 추출
125
- const builtInSchemaNames = Object.values(BUILT_IN_TYPES).map((info) => info.schemaName as string);
127
+ const builtInSchemaNames = Object.values(BUILT_IN_TYPES).map(
128
+ (info) =>
129
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ info.schemaName as string,
130
+ );
126
131
  const importDefs = importKeys
127
132
  // 내장 타입 스키마는 이미 sonamu에서 import되므로 제외
128
133
  .filter((importKey) => !builtInSchemaNames.includes(importKey))
@@ -157,7 +162,7 @@ async function resolveRenderedTemplate(
157
162
  }
158
163
  return r;
159
164
  },
160
- [] as {
165
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ [] as {
161
166
  keys: string[];
162
167
  from: string;
163
168
  }[],
@@ -178,12 +183,12 @@ async function resolveRenderedTemplate(
178
183
  return [header, body].join("\n\n");
179
184
  } else {
180
185
  Naite.t("resolveRenderedTemplate:beforeFormat", { key, header, body });
181
- const formatted = await formatCode(
186
+ const formattedCode = await formatCode(
182
187
  [header, body].join("\n\n"),
183
188
  `${Sonamu.appRootPath}/${filePath}`,
184
189
  );
185
- Naite.t(`resolveRenderedTemplate:formatted:${key}`, formatted);
186
- return formatted;
190
+ Naite.t(`resolveRenderedTemplate:formatted:${key}`, formattedCode);
191
+ return formattedCode;
187
192
  }
188
193
  })();
189
194
 
@@ -196,18 +201,21 @@ async function resolveRenderedTemplate(
196
201
  async function writeCodeToPathEachTarget(pathAndCode: PathAndCode): Promise<AbsolutePath[]> {
197
202
  const { targets } = Sonamu.config.sync;
198
203
  const { appRootPath } = Sonamu;
199
- const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}` as AbsolutePath;
204
+ const filePath =
205
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ `${Sonamu.appRootPath}/${pathAndCode.path}` as AbsolutePath;
200
206
 
201
207
  const dstFilePaths = unique(
202
- targets.map((target) => filePath.replace("/:target/", `/${target}/`)) as AbsolutePath[],
208
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ targets.map(
209
+ (target) => filePath.replace("/:target/", `/${target}/`),
210
+ ) as AbsolutePath[],
203
211
  );
204
212
  return await Promise.all(
205
213
  dstFilePaths.map(async (dstFilePath) => {
206
214
  const dir = path.dirname(dstFilePath);
207
- if (!(await exists(dir))) {
208
- await mkdir(dir, { recursive: true });
215
+ if (!(await syncerFileExists(dir))) {
216
+ await syncerFilesystem.mkdir(dir, { recursive: true });
209
217
  }
210
- await writeFile(dstFilePath, pathAndCode.code);
218
+ await syncerFilesystem.writeFile(dstFilePath, pathAndCode.code);
211
219
  // 방금 우리가 쓴 path를 등록 → dev watcher의 후속 change 이벤트가
212
220
  // 외부 변경으로 오인되지 않도록 거름 가드 자료 제공.
213
221
  await trackWritten(dstFilePath);
@@ -1,5 +1,3 @@
1
- import { rm } from "fs/promises";
2
-
3
1
  import chalk from "chalk";
4
2
 
5
3
  import { Sonamu } from "../api/sonamu";
@@ -8,8 +6,8 @@ import { EntityManager } from "../entity/entity-manager";
8
6
  import { BadRequestException } from "../exceptions/so-exceptions";
9
7
  import { type TemplateOptions } from "../types/types";
10
8
  import { isTest } from "../utils/controller";
11
- import { exists } from "../utils/fs-utils";
12
9
  import { generateTemplate } from "./code-generator";
10
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
13
11
 
14
12
  /**
15
13
  * 새로운 엔티티를 생성합니다.
@@ -53,9 +51,9 @@ export async function delEntity(entityId: string): Promise<{ delPaths: string[]
53
51
  })(); // iife
54
52
 
55
53
  for (const delPath of delPaths) {
56
- if (await exists(delPath)) {
54
+ if (await syncerFileExists(delPath)) {
57
55
  !isTest() && console.log(chalk.red(`DELETE ${delPath}`));
58
- await rm(delPath, { recursive: true, force: true });
56
+ await syncerFilesystem.rm(delPath, { recursive: true, force: true });
59
57
  } else {
60
58
  !isTest() && console.log(chalk.yellow(`NOT_EXISTS ${delPath}`));
61
59
  }
@@ -0,0 +1,46 @@
1
+ import path, { dirname } from "node:path";
2
+
3
+ import { formatCode } from "../utils/formatter";
4
+ import { syncerFileExists, syncerFilesystem } from "./filesystem-dependencies";
5
+
6
+ /**
7
+ * Syncer 파일시스템 경계를 통해 파일을 복사하고 클라이언트용 import 경로를 변환합니다.
8
+ */
9
+ export async function copyFileWithReplaceCoreToShared(
10
+ fromPath: string,
11
+ toPath: string,
12
+ syncHeader?: string,
13
+ ): Promise<boolean> {
14
+ if (!(await syncerFileExists(fromPath))) {
15
+ return false;
16
+ }
17
+
18
+ const oldFileContent = await syncerFilesystem.readFile(fromPath, "utf-8");
19
+ let newFileContent = replaceCoreImport(oldFileContent, toPath);
20
+
21
+ if (syncHeader) {
22
+ const generatedBlockRegex = /\/\*\*\r?\n \* @generated\r?\n[\s\S]*?\*\/\r?\n(\r?\n)*/;
23
+ newFileContent = generatedBlockRegex.test(newFileContent)
24
+ ? newFileContent.replace(generatedBlockRegex, `${syncHeader}\n\n`)
25
+ : `${syncHeader}\n\n${newFileContent}`;
26
+ }
27
+
28
+ if (toPath.endsWith(".ts") || toPath.endsWith(".tsx")) {
29
+ newFileContent = await formatCode(newFileContent, toPath);
30
+ }
31
+
32
+ await syncerFilesystem.writeFile(toPath, newFileContent);
33
+ return true;
34
+ }
35
+
36
+ function replaceCoreImport(source: string, toPath: string): string {
37
+ const srcMatch = toPath.match(/^(.+\/src)\//);
38
+ if (!srcMatch) {
39
+ return source;
40
+ }
41
+
42
+ const servicesDir = path.join(srcMatch[1], "services");
43
+ const relativePath = path.relative(dirname(toPath), servicesDir);
44
+ const sharedPath = relativePath === "" ? "./sonamu.shared" : `${relativePath}/sonamu.shared`;
45
+ return source.replace(/from "sonamu(\/dict)?"/g, `from "${sharedPath}"`);
46
+ }
@@ -56,6 +56,7 @@ export function getChecksumPatternGroup() {
56
56
  // 여기에는 Sonamu의 모든 sync 산출물이 있는 것은 아닙니다.
57
57
  // sonamu.shared.ts와 entry-server.generated.tsx와 같은
58
58
  // sync 초반 1회성 부트스트랩 파일들은 관리 안 하기 때문에 여기에 리스팅도 안 합니다.
59
+ httpValidatorsGenerated: api("src/application/**/sonamu.validators.generated.ts"),
59
60
  generated: api("src/application/**/*.generated.{ts,tsx,sso.ts}"),
60
61
  generatedCopied: targets("src/services/**/sonamu.generated.{ts,tsx}"),
61
62
  httpGenerated: api("src/application/**/*.generated.http"),
@@ -67,6 +68,11 @@ export function getChecksumPatternGroup() {
67
68
  } satisfies Record<string, AppRelativePath>;
68
69
  }
69
70
 
71
+ // Node 내장 fs.glob의 brace expansion은 단일 멤버 {x}를 풀지 않으므로, 멤버가 1개일 때는 alternation 없이 직접 결합합니다.
72
+ function braceJoin(dirs: readonly string[]) {
73
+ return dirs.length === 1 ? dirs[0] : `{${dirs.join(",")}}`;
74
+ }
75
+
70
76
  /**
71
77
  * 위치 카테고리별 글롭 빌더를 만들어 반환합니다.
72
78
  * - api(rest): api 디렉토리에 한정
@@ -77,16 +83,13 @@ function globBuilders() {
77
83
  const apiDir = Sonamu.config.api.dir;
78
84
  const targetDirs = Sonamu.config.sync.targets;
79
85
 
80
- // Node 내장 fs.glob의 brace expansion은 단일 멤버 {x}를 풀지 않으므로, 멤버가 1개일 때는 alternation 없이 직접 결합합니다.
81
- const braceJoin = (dirs: readonly string[]) =>
82
- dirs.length === 1 ? dirs[0] : `{${dirs.join(",")}}`;
83
-
84
86
  return {
85
- api: (pathFromApi: string) => `${apiDir}/${pathFromApi}` as AppRelativePath,
87
+ api: (pathFromApi: string) =>
88
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ `${apiDir}/${pathFromApi}` as AppRelativePath,
86
89
  targets: (pathFromTarget: string) =>
87
- `${braceJoin(targetDirs)}/${pathFromTarget}` as AppRelativePath,
90
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ `${braceJoin(targetDirs)}/${pathFromTarget}` as AppRelativePath,
88
91
  anywhere: (pathFromAnywhere: string) =>
89
- `${braceJoin([apiDir, ...targetDirs])}/${pathFromAnywhere}` as AppRelativePath,
92
+ /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ `${braceJoin([apiDir, ...targetDirs])}/${pathFromAnywhere}` as AppRelativePath,
90
93
  };
91
94
  }
92
95
 
@@ -110,7 +113,7 @@ export const GLOB_EXCLUDE = ["**/node_modules/**", "**/dist/**", "**/build/**",
110
113
  */
111
114
  export function getChecksumPatternGroupInAbsolutePath(): GlobPattern<AbsolutePath> {
112
115
  const group = getChecksumPatternGroup();
113
- return Object.fromEntries(
116
+ return /* SAFETY: TypeScript AST 파싱과 동기화 입력 계약이 이 값의 타입을 보장한다. */ Object.fromEntries(
114
117
  Object.entries(group).map(([key, value]) => [
115
118
  key,
116
119
  path.join(Sonamu.appRootPath, value), // appRoot 상대 경로 → 절대 경로
@@ -1,6 +1,5 @@
1
- import { stat } from "fs/promises";
2
-
3
1
  import { type AbsolutePath } from "../utils/path-utils";
2
+ import { syncerFilesystem } from "./filesystem-dependencies";
4
3
 
5
4
  export const fileWrittenAt = new Map<AbsolutePath, number>();
6
5
 
@@ -9,7 +8,7 @@ export const fileWrittenAt = new Map<AbsolutePath, number>();
9
8
  * 디스크에서 mtime을 직접 읽어 정확한 값으로 등록합니다.
10
9
  */
11
10
  export async function trackWritten(filePath: AbsolutePath): Promise<void> {
12
- const fileStat = await stat(filePath);
11
+ const fileStat = await syncerFilesystem.stat(filePath);
13
12
  fileWrittenAt.set(filePath, fileStat.mtimeMs);
14
13
  }
15
14
 
@@ -22,6 +21,6 @@ export async function isLastChangedByMe(filePath: AbsolutePath): Promise<boolean
22
21
  if (registered === undefined) {
23
22
  return false;
24
23
  }
25
- const fileStat = await stat(filePath);
24
+ const fileStat = await syncerFilesystem.stat(filePath);
26
25
  return fileStat.mtimeMs <= registered;
27
26
  }
@@ -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
  }