@simplysm/sd-cli 13.0.100 → 14.0.1

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 (409) hide show
  1. package/dist/commands/build.js +29 -19
  2. package/dist/commands/build.js.map +1 -6
  3. package/dist/commands/check.d.ts +1 -0
  4. package/dist/commands/check.d.ts.map +1 -1
  5. package/dist/commands/check.js +130 -115
  6. package/dist/commands/check.js.map +1 -6
  7. package/dist/commands/dev.d.ts +6 -7
  8. package/dist/commands/dev.d.ts.map +1 -1
  9. package/dist/commands/dev.js +24 -14
  10. package/dist/commands/dev.js.map +1 -6
  11. package/dist/commands/lint.d.ts +1 -1
  12. package/dist/commands/lint.js +158 -116
  13. package/dist/commands/lint.js.map +1 -6
  14. package/dist/commands/publish.d.ts.map +1 -1
  15. package/dist/commands/publish.js +637 -510
  16. package/dist/commands/publish.js.map +1 -6
  17. package/dist/commands/replace-deps.js +12 -12
  18. package/dist/commands/replace-deps.js.map +1 -6
  19. package/dist/commands/typecheck.d.ts +5 -30
  20. package/dist/commands/typecheck.d.ts.map +1 -1
  21. package/dist/commands/typecheck.js +144 -207
  22. package/dist/commands/typecheck.js.map +1 -6
  23. package/dist/commands/watch.d.ts +6 -4
  24. package/dist/commands/watch.d.ts.map +1 -1
  25. package/dist/commands/watch.js +25 -16
  26. package/dist/commands/watch.js.map +1 -6
  27. package/dist/engines/NgtscEngine.d.ts +47 -0
  28. package/dist/engines/NgtscEngine.d.ts.map +1 -0
  29. package/dist/engines/NgtscEngine.js +151 -0
  30. package/dist/engines/NgtscEngine.js.map +1 -0
  31. package/dist/engines/ServerEsbuildEngine.d.ts +47 -0
  32. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -0
  33. package/dist/engines/ServerEsbuildEngine.js +159 -0
  34. package/dist/engines/ServerEsbuildEngine.js.map +1 -0
  35. package/dist/engines/TscEngine.d.ts +47 -0
  36. package/dist/engines/TscEngine.d.ts.map +1 -0
  37. package/dist/engines/TscEngine.js +153 -0
  38. package/dist/engines/TscEngine.js.map +1 -0
  39. package/dist/engines/ViteEngine.d.ts +49 -0
  40. package/dist/engines/ViteEngine.d.ts.map +1 -0
  41. package/dist/engines/ViteEngine.js +161 -0
  42. package/dist/engines/ViteEngine.js.map +1 -0
  43. package/dist/engines/index.d.ts +26 -0
  44. package/dist/engines/index.d.ts.map +1 -0
  45. package/dist/engines/index.js +30 -0
  46. package/dist/engines/index.js.map +1 -0
  47. package/dist/engines/types.d.ts +77 -0
  48. package/dist/engines/types.d.ts.map +1 -0
  49. package/dist/engines/types.js +2 -0
  50. package/dist/engines/types.js.map +1 -0
  51. package/dist/index.d.ts +0 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -6
  55. package/dist/infra/ResultCollector.d.ts +1 -1
  56. package/dist/infra/ResultCollector.d.ts.map +1 -1
  57. package/dist/infra/ResultCollector.js +30 -27
  58. package/dist/infra/ResultCollector.js.map +1 -6
  59. package/dist/infra/SignalHandler.js +45 -42
  60. package/dist/infra/SignalHandler.js.map +1 -6
  61. package/dist/infra/WorkerManager.js +56 -53
  62. package/dist/infra/WorkerManager.js.map +1 -6
  63. package/dist/orchestrators/BuildOrchestrator.d.ts +33 -1
  64. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  65. package/dist/orchestrators/BuildOrchestrator.js +314 -309
  66. package/dist/orchestrators/BuildOrchestrator.js.map +1 -6
  67. package/dist/orchestrators/DevWatchOrchestrator.d.ts +60 -0
  68. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -0
  69. package/dist/orchestrators/DevWatchOrchestrator.js +465 -0
  70. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -0
  71. package/dist/sd-cli-entry.d.ts.map +1 -1
  72. package/dist/sd-cli-entry.js +190 -266
  73. package/dist/sd-cli-entry.js.map +1 -6
  74. package/dist/sd-cli.js +77 -49
  75. package/dist/sd-cli.js.map +1 -6
  76. package/dist/sd-config.types.d.ts +2 -0
  77. package/dist/sd-config.types.d.ts.map +1 -1
  78. package/dist/sd-config.types.js +2 -1
  79. package/dist/sd-config.types.js.map +1 -6
  80. package/dist/utils/angular-build.d.ts +77 -0
  81. package/dist/utils/angular-build.d.ts.map +1 -0
  82. package/dist/utils/angular-build.js +84 -0
  83. package/dist/utils/angular-build.js.map +1 -0
  84. package/dist/utils/build-env.js +9 -9
  85. package/dist/utils/build-env.js.map +1 -6
  86. package/dist/utils/concurrency.d.ts +15 -0
  87. package/dist/utils/concurrency.d.ts.map +1 -0
  88. package/dist/utils/concurrency.js +38 -0
  89. package/dist/utils/concurrency.js.map +1 -0
  90. package/dist/utils/copy-public.js +104 -87
  91. package/dist/utils/copy-public.js.map +1 -6
  92. package/dist/utils/copy-src.js +49 -35
  93. package/dist/utils/copy-src.js.map +1 -6
  94. package/dist/utils/esbuild-config.d.ts +0 -29
  95. package/dist/utils/esbuild-config.d.ts.map +1 -1
  96. package/dist/utils/esbuild-config.js +151 -218
  97. package/dist/utils/esbuild-config.js.map +1 -6
  98. package/dist/utils/ngtsc-build-core.d.ts +49 -0
  99. package/dist/utils/ngtsc-build-core.d.ts.map +1 -0
  100. package/dist/utils/ngtsc-build-core.js +250 -0
  101. package/dist/utils/ngtsc-build-core.js.map +1 -0
  102. package/dist/utils/output-path-rewriter.d.ts +23 -0
  103. package/dist/utils/output-path-rewriter.d.ts.map +1 -0
  104. package/dist/utils/output-path-rewriter.js +74 -0
  105. package/dist/utils/output-path-rewriter.js.map +1 -0
  106. package/dist/utils/output-utils.js +55 -40
  107. package/dist/utils/output-utils.js.map +1 -6
  108. package/dist/utils/package-utils.d.ts +8 -0
  109. package/dist/utils/package-utils.d.ts.map +1 -1
  110. package/dist/utils/package-utils.js +103 -73
  111. package/dist/utils/package-utils.js.map +1 -6
  112. package/dist/utils/rebuild-manager.js +41 -44
  113. package/dist/utils/rebuild-manager.js.map +1 -6
  114. package/dist/utils/replace-deps.js +283 -184
  115. package/dist/utils/replace-deps.js.map +1 -6
  116. package/dist/utils/scss-compiler.d.ts +10 -0
  117. package/dist/utils/scss-compiler.d.ts.map +1 -0
  118. package/dist/utils/scss-compiler.js +36 -0
  119. package/dist/utils/scss-compiler.js.map +1 -0
  120. package/dist/utils/sd-config.js +29 -19
  121. package/dist/utils/sd-config.js.map +1 -6
  122. package/dist/utils/tsc-build.d.ts +36 -0
  123. package/dist/utils/tsc-build.d.ts.map +1 -0
  124. package/dist/utils/tsc-build.js +130 -0
  125. package/dist/utils/tsc-build.js.map +1 -0
  126. package/dist/utils/tsconfig.d.ts +7 -26
  127. package/dist/utils/tsconfig.d.ts.map +1 -1
  128. package/dist/utils/tsconfig.js +39 -64
  129. package/dist/utils/tsconfig.js.map +1 -6
  130. package/dist/utils/typecheck-non-package.d.ts +18 -0
  131. package/dist/utils/typecheck-non-package.d.ts.map +1 -0
  132. package/dist/utils/typecheck-non-package.js +64 -0
  133. package/dist/utils/typecheck-non-package.js.map +1 -0
  134. package/dist/utils/typecheck-serialization.js +58 -40
  135. package/dist/utils/typecheck-serialization.js.map +1 -6
  136. package/dist/utils/worker-events.js +48 -40
  137. package/dist/utils/worker-events.js.map +1 -6
  138. package/dist/utils/worker-utils.js +48 -28
  139. package/dist/utils/worker-utils.js.map +1 -6
  140. package/dist/vitest-plugin.d.ts +9 -0
  141. package/dist/vitest-plugin.d.ts.map +1 -0
  142. package/dist/vitest-plugin.js +85 -0
  143. package/dist/vitest-plugin.js.map +1 -0
  144. package/dist/workers/library-build.worker.d.ts +54 -0
  145. package/dist/workers/library-build.worker.d.ts.map +1 -0
  146. package/dist/workers/library-build.worker.js +97 -0
  147. package/dist/workers/library-build.worker.js.map +1 -0
  148. package/dist/workers/lint.worker.js +9 -6
  149. package/dist/workers/lint.worker.js.map +1 -6
  150. package/dist/workers/ngtsc-build.worker.d.ts +23 -0
  151. package/dist/workers/ngtsc-build.worker.d.ts.map +1 -0
  152. package/dist/workers/ngtsc-build.worker.js +98 -0
  153. package/dist/workers/ngtsc-build.worker.js.map +1 -0
  154. package/dist/workers/{server.worker.d.ts → server-build.worker.d.ts} +39 -29
  155. package/dist/workers/server-build.worker.d.ts.map +1 -0
  156. package/dist/workers/server-build.worker.js +399 -0
  157. package/dist/workers/server-build.worker.js.map +1 -0
  158. package/dist/workers/server-runtime.worker.d.ts +3 -2
  159. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  160. package/dist/workers/server-runtime.worker.js +100 -95
  161. package/dist/workers/server-runtime.worker.js.map +1 -6
  162. package/dist/workers/vite-build.worker.d.ts +56 -0
  163. package/dist/workers/vite-build.worker.d.ts.map +1 -0
  164. package/dist/workers/vite-build.worker.js +167 -0
  165. package/dist/workers/vite-build.worker.js.map +1 -0
  166. package/package.json +10 -16
  167. package/src/commands/check.ts +21 -3
  168. package/src/commands/dev.ts +10 -8
  169. package/src/commands/lint.ts +1 -1
  170. package/src/commands/publish.ts +4 -0
  171. package/src/commands/typecheck.ts +89 -256
  172. package/src/commands/watch.ts +9 -8
  173. package/src/engines/NgtscEngine.ts +190 -0
  174. package/src/engines/ServerEsbuildEngine.ts +195 -0
  175. package/src/engines/TscEngine.ts +189 -0
  176. package/src/engines/ViteEngine.ts +203 -0
  177. package/src/engines/index.ts +49 -0
  178. package/src/engines/types.ts +79 -0
  179. package/src/index.ts +0 -3
  180. package/src/infra/ResultCollector.ts +1 -1
  181. package/src/orchestrators/BuildOrchestrator.ts +87 -157
  182. package/src/orchestrators/DevWatchOrchestrator.ts +573 -0
  183. package/src/sd-cli-entry.ts +13 -116
  184. package/src/sd-config.types.ts +2 -0
  185. package/src/utils/angular-build.ts +157 -0
  186. package/src/utils/concurrency.ts +43 -0
  187. package/src/utils/esbuild-config.ts +1 -122
  188. package/src/utils/ngtsc-build-core.ts +379 -0
  189. package/src/utils/output-path-rewriter.ts +82 -0
  190. package/src/utils/package-utils.ts +20 -0
  191. package/src/utils/scss-compiler.ts +58 -0
  192. package/src/utils/tsc-build.ts +175 -0
  193. package/src/utils/tsconfig.ts +27 -95
  194. package/src/utils/typecheck-non-package.ts +87 -0
  195. package/src/vitest-plugin.ts +118 -0
  196. package/src/workers/library-build.worker.ts +153 -0
  197. package/src/workers/ngtsc-build.worker.ts +146 -0
  198. package/src/workers/server-build.worker.ts +565 -0
  199. package/src/workers/server-runtime.worker.ts +17 -26
  200. package/src/workers/vite-build.worker.ts +252 -0
  201. package/tests/commands/check.spec.ts +276 -0
  202. package/tests/commands/dev.spec.ts +53 -0
  203. package/tests/commands/lint.spec.ts +243 -0
  204. package/tests/commands/publish.spec.ts +1159 -0
  205. package/tests/commands/typecheck.spec.ts +294 -0
  206. package/tests/commands/watch.spec.ts +53 -0
  207. package/tests/engines/engine-selection.spec.ts +247 -0
  208. package/tests/engines/ngtsc-engine.spec.ts +274 -0
  209. package/tests/engines/server-esbuild-engine.spec.ts +256 -0
  210. package/tests/engines/tsc-engine.spec.ts +213 -0
  211. package/tests/engines/vite-engine.spec.ts +358 -0
  212. package/tests/infra/result-collector.spec.ts +46 -0
  213. package/tests/infra/signal-handler.spec.ts +32 -0
  214. package/tests/infra/worker-manager.spec.ts +63 -0
  215. package/tests/orchestrators/build-orchestrator.spec.ts +772 -0
  216. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +1173 -0
  217. package/tests/sd-cli-entry.spec.ts +49 -0
  218. package/tests/utils/angular-build.spec.ts +251 -0
  219. package/tests/utils/build-env.spec.ts +33 -0
  220. package/tests/utils/concurrency.spec.ts +65 -0
  221. package/tests/utils/copy-src.spec.ts +144 -0
  222. package/tests/utils/esbuild-config.spec.ts +186 -0
  223. package/tests/utils/external-modules.spec.ts +161 -0
  224. package/tests/utils/ngtsc-scss-refactor.spec.ts +66 -0
  225. package/tests/utils/output-path-rewriter.spec.ts +165 -0
  226. package/tests/utils/output-utils.spec.ts +104 -0
  227. package/tests/utils/package-utils.spec.ts +52 -0
  228. package/tests/utils/rebuild-manager.spec.ts +30 -27
  229. package/tests/utils/replace-deps.spec.ts +69 -0
  230. package/tests/utils/scss-compiler.spec.ts +131 -0
  231. package/tests/utils/sd-config.spec.ts +77 -0
  232. package/tests/utils/tsc-build.spec.ts +358 -0
  233. package/tests/utils/tsconfig-angular.spec.ts +71 -0
  234. package/tests/utils/typecheck-non-package.spec.ts +120 -0
  235. package/tests/utils/worker-events.spec.ts +155 -0
  236. package/tests/utils/worker-utils.spec.ts +43 -0
  237. package/tests/vitest-plugin-cwd.spec.ts +68 -0
  238. package/tests/vitest-plugin.spec.ts +103 -0
  239. package/tests/workers/library-build-worker.spec.ts +258 -0
  240. package/tests/workers/ngtsc-build-worker.spec.ts +187 -0
  241. package/tests/workers/server-build-worker.spec.ts +566 -0
  242. package/tests/workers/server-runtime-worker.spec.ts +251 -0
  243. package/README.md +0 -295
  244. package/dist/builders/BaseBuilder.d.ts +0 -88
  245. package/dist/builders/BaseBuilder.d.ts.map +0 -1
  246. package/dist/builders/BaseBuilder.js +0 -142
  247. package/dist/builders/BaseBuilder.js.map +0 -6
  248. package/dist/builders/DtsBuilder.d.ts +0 -22
  249. package/dist/builders/DtsBuilder.d.ts.map +0 -1
  250. package/dist/builders/DtsBuilder.js +0 -72
  251. package/dist/builders/DtsBuilder.js.map +0 -6
  252. package/dist/builders/LibraryBuilder.d.ts +0 -22
  253. package/dist/builders/LibraryBuilder.d.ts.map +0 -1
  254. package/dist/builders/LibraryBuilder.js +0 -85
  255. package/dist/builders/LibraryBuilder.js.map +0 -6
  256. package/dist/builders/types.d.ts +0 -55
  257. package/dist/builders/types.d.ts.map +0 -1
  258. package/dist/builders/types.js +0 -1
  259. package/dist/builders/types.js.map +0 -6
  260. package/dist/capacitor/capacitor.d.ts +0 -151
  261. package/dist/capacitor/capacitor.d.ts.map +0 -1
  262. package/dist/capacitor/capacitor.js +0 -694
  263. package/dist/capacitor/capacitor.js.map +0 -6
  264. package/dist/commands/device.d.ts +0 -22
  265. package/dist/commands/device.d.ts.map +0 -1
  266. package/dist/commands/device.js +0 -98
  267. package/dist/commands/device.js.map +0 -6
  268. package/dist/commands/init.d.ts +0 -14
  269. package/dist/commands/init.d.ts.map +0 -1
  270. package/dist/commands/init.js +0 -72
  271. package/dist/commands/init.js.map +0 -6
  272. package/dist/electron/electron.d.ts +0 -84
  273. package/dist/electron/electron.d.ts.map +0 -1
  274. package/dist/electron/electron.js +0 -263
  275. package/dist/electron/electron.js.map +0 -6
  276. package/dist/orchestrators/DevOrchestrator.d.ts +0 -83
  277. package/dist/orchestrators/DevOrchestrator.d.ts.map +0 -1
  278. package/dist/orchestrators/DevOrchestrator.js +0 -540
  279. package/dist/orchestrators/DevOrchestrator.js.map +0 -6
  280. package/dist/orchestrators/WatchOrchestrator.d.ts +0 -57
  281. package/dist/orchestrators/WatchOrchestrator.d.ts.map +0 -1
  282. package/dist/orchestrators/WatchOrchestrator.js +0 -199
  283. package/dist/orchestrators/WatchOrchestrator.js.map +0 -6
  284. package/dist/utils/tailwind-config-deps.d.ts +0 -8
  285. package/dist/utils/tailwind-config-deps.d.ts.map +0 -1
  286. package/dist/utils/tailwind-config-deps.js +0 -82
  287. package/dist/utils/tailwind-config-deps.js.map +0 -6
  288. package/dist/utils/template.d.ts +0 -14
  289. package/dist/utils/template.d.ts.map +0 -1
  290. package/dist/utils/template.js +0 -33
  291. package/dist/utils/template.js.map +0 -6
  292. package/dist/utils/vite-config.d.ts +0 -35
  293. package/dist/utils/vite-config.d.ts.map +0 -1
  294. package/dist/utils/vite-config.js +0 -259
  295. package/dist/utils/vite-config.js.map +0 -6
  296. package/dist/workers/client.worker.d.ts +0 -83
  297. package/dist/workers/client.worker.d.ts.map +0 -1
  298. package/dist/workers/client.worker.js +0 -111
  299. package/dist/workers/client.worker.js.map +0 -6
  300. package/dist/workers/dts.worker.d.ts +0 -75
  301. package/dist/workers/dts.worker.d.ts.map +0 -1
  302. package/dist/workers/dts.worker.js +0 -270
  303. package/dist/workers/dts.worker.js.map +0 -6
  304. package/dist/workers/library.worker.d.ts +0 -75
  305. package/dist/workers/library.worker.d.ts.map +0 -1
  306. package/dist/workers/library.worker.js +0 -166
  307. package/dist/workers/library.worker.js.map +0 -6
  308. package/dist/workers/server.worker.d.ts.map +0 -1
  309. package/dist/workers/server.worker.js +0 -482
  310. package/dist/workers/server.worker.js.map +0 -6
  311. package/src/builders/BaseBuilder.ts +0 -218
  312. package/src/builders/DtsBuilder.ts +0 -92
  313. package/src/builders/LibraryBuilder.ts +0 -110
  314. package/src/builders/types.ts +0 -60
  315. package/src/capacitor/capacitor.ts +0 -931
  316. package/src/commands/device.ts +0 -140
  317. package/src/commands/init.ts +0 -113
  318. package/src/electron/electron.ts +0 -362
  319. package/src/orchestrators/DevOrchestrator.ts +0 -744
  320. package/src/orchestrators/WatchOrchestrator.ts +0 -277
  321. package/src/utils/tailwind-config-deps.ts +0 -98
  322. package/src/utils/template.ts +0 -56
  323. package/src/utils/vite-config.ts +0 -390
  324. package/src/workers/client.worker.ts +0 -250
  325. package/src/workers/dts.worker.ts +0 -453
  326. package/src/workers/library.worker.ts +0 -316
  327. package/src/workers/server.worker.ts +0 -734
  328. package/templates/init/.gitignore.hbs +0 -34
  329. package/templates/init/.npmrc.hbs +0 -1
  330. package/templates/init/.prettierignore +0 -1
  331. package/templates/init/.prettierrc.yaml +0 -12
  332. package/templates/init/eslint.config.ts +0 -15
  333. package/templates/init/mise.toml +0 -3
  334. package/templates/init/package.json.hbs +0 -32
  335. package/templates/init/packages/client-admin/index.html.hbs +0 -144
  336. package/templates/init/packages/client-admin/package.json.hbs +0 -27
  337. package/templates/init/packages/client-admin/public/assets/logo-landscape.png +0 -0
  338. package/templates/init/packages/client-admin/public/assets/logo.png +0 -0
  339. package/templates/init/packages/client-admin/public/favicon.ico +0 -0
  340. package/templates/init/packages/client-admin/src/App.tsx +0 -42
  341. package/templates/init/packages/client-admin/src/dev/DevDialog.tsx +0 -34
  342. package/templates/init/packages/client-admin/src/events/AuthChangeEvent.ts +0 -3
  343. package/templates/init/packages/client-admin/src/main.css +0 -4
  344. package/templates/init/packages/client-admin/src/main.tsx.hbs +0 -146
  345. package/templates/init/packages/client-admin/src/providers/AppServiceProvider.tsx.hbs +0 -103
  346. package/templates/init/packages/client-admin/src/providers/AppStructureProvider.tsx +0 -84
  347. package/templates/init/packages/client-admin/src/providers/AuthProvider.tsx.hbs +0 -96
  348. package/templates/init/packages/client-admin/src/providers/configureSharedData.ts.hbs +0 -67
  349. package/templates/init/packages/client-admin/src/views/auth/LoginView.tsx +0 -132
  350. package/templates/init/packages/client-admin/src/views/home/HomeView.tsx +0 -108
  351. package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +0 -243
  352. package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeSheet.tsx.hbs +0 -271
  353. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +0 -146
  354. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +0 -121
  355. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +0 -52
  356. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleSheet.tsx.hbs +0 -125
  357. package/templates/init/packages/client-admin/src/views/home/main/MainView.tsx.hbs +0 -13
  358. package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +0 -241
  359. package/templates/init/packages/client-admin/src/views/home/system/system-log/SystemLogSheet.tsx.hbs +0 -169
  360. package/templates/init/packages/client-admin/src/views/not-found/NotFoundView.tsx +0 -15
  361. package/templates/init/packages/client-admin/tailwind.config.ts +0 -10
  362. package/templates/init/packages/db-main/package.json.hbs +0 -13
  363. package/templates/init/packages/db-main/src/MainDbContext.ts +0 -22
  364. package/templates/init/packages/db-main/src/dataLogExt.ts +0 -127
  365. package/templates/init/packages/db-main/src/index.ts +0 -14
  366. package/templates/init/packages/db-main/src/tables/base/Employee.ts +0 -24
  367. package/templates/init/packages/db-main/src/tables/base/EmployeeConfig.ts +0 -13
  368. package/templates/init/packages/db-main/src/tables/base/Role.ts +0 -9
  369. package/templates/init/packages/db-main/src/tables/base/RolePermission.ts +0 -13
  370. package/templates/init/packages/db-main/src/tables/system/_DataLog.ts +0 -19
  371. package/templates/init/packages/db-main/src/tables/system/_Log.ts +0 -16
  372. package/templates/init/packages/server/package.json.hbs +0 -20
  373. package/templates/init/packages/server/public-dev/dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  374. package/templates/init/packages/server/src/index.ts +0 -4
  375. package/templates/init/packages/server/src/main.ts.hbs +0 -34
  376. package/templates/init/packages/server/src/services/AuthService.ts.hbs +0 -171
  377. package/templates/init/packages/server/src/services/DevService.ts.hbs +0 -94
  378. package/templates/init/packages/server/src/services/EmployeeService.ts.hbs +0 -122
  379. package/templates/init/packages/server/src/services/RoleService.ts.hbs +0 -59
  380. package/templates/init/pnpm-workspace.yaml +0 -15
  381. package/templates/init/sd.config.ts.hbs +0 -48
  382. package/templates/init/tsconfig.json.hbs +0 -39
  383. package/templates/init/vitest.config.ts +0 -36
  384. package/tests/capacitor-exclude.spec.ts +0 -78
  385. package/tests/capacitor.spec.ts +0 -49
  386. package/tests/copy-src.spec.ts +0 -50
  387. package/tests/electron-exclude.spec.ts +0 -61
  388. package/tests/get-compiler-options-for-package.spec.ts +0 -80
  389. package/tests/get-package-source-files.spec.ts +0 -139
  390. package/tests/get-types-from-package-json.spec.ts +0 -92
  391. package/tests/infra/ResultCollector.spec.ts +0 -30
  392. package/tests/infra/SignalHandler.spec.ts +0 -38
  393. package/tests/infra/WorkerManager.spec.ts +0 -63
  394. package/tests/load-ignore-patterns.spec.ts +0 -163
  395. package/tests/load-sd-config.spec.ts +0 -100
  396. package/tests/package-utils.spec.ts +0 -188
  397. package/tests/parse-root-tsconfig.spec.ts +0 -89
  398. package/tests/publish-config-narrowing.spec.ts +0 -20
  399. package/tests/replace-deps.spec.ts +0 -308
  400. package/tests/run-lint.spec.ts +0 -366
  401. package/tests/run-typecheck.spec.ts +0 -544
  402. package/tests/run-watch.spec.ts +0 -76
  403. package/tests/sd-cli.spec.ts +0 -265
  404. package/tests/sd-public-dev-plugin-mime.spec.ts +0 -19
  405. package/tests/tailwind-config-deps.spec.ts +0 -30
  406. package/tests/template.spec.ts +0 -70
  407. package/tests/vite-config-exclude.spec.ts +0 -35
  408. package/tests/vite-config-outdir.spec.ts +0 -38
  409. package/tests/write-changed-output-files.spec.ts +0 -97
@@ -0,0 +1,1173 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ // --- Mock factories (vi.mock is hoisted) ---
4
+
5
+ vi.mock("consola", () => ({
6
+ consola: {
7
+ withTag: vi.fn(() => ({
8
+ debug: vi.fn(),
9
+ info: vi.fn(),
10
+ warn: vi.fn(),
11
+ error: vi.fn(),
12
+ start: vi.fn(),
13
+ success: vi.fn(),
14
+ })),
15
+ },
16
+ }));
17
+
18
+ vi.mock("../../src/utils/sd-config", () => ({
19
+ loadSdConfig: vi.fn(),
20
+ }));
21
+
22
+ vi.mock("../../src/utils/build-env", () => ({
23
+ getVersion: vi.fn(),
24
+ }));
25
+
26
+ vi.mock("../../src/utils/package-utils", () => ({
27
+ filterPackagesByTargets: vi.fn(),
28
+ validateTargets: vi.fn(),
29
+ }));
30
+
31
+ vi.mock("../../src/utils/replace-deps", () => ({
32
+ watchReplaceDeps: vi.fn(),
33
+ }));
34
+
35
+ vi.mock("../../src/utils/output-utils", () => ({
36
+ printErrors: vi.fn(),
37
+ printServers: vi.fn(),
38
+ }));
39
+
40
+ vi.mock("../../src/utils/rebuild-manager", () => ({
41
+ RebuildManager: vi.fn().mockImplementation(function (this: any) {
42
+ this.on = vi.fn();
43
+ this.registerBuild = vi.fn().mockReturnValue(vi.fn());
44
+ }),
45
+ }));
46
+
47
+ vi.mock("../../src/infra/ResultCollector", () => ({
48
+ ResultCollector: vi.fn().mockImplementation(function (this: any) {
49
+ const store = new Map<string, any>();
50
+ this.add = vi.fn((result: any) => {
51
+ store.set(`${result.name}:${result.type}`, result);
52
+ });
53
+ this.get = vi.fn((key: string) => store.get(key));
54
+ this.toMap = vi.fn(() => new Map(store));
55
+ }),
56
+ }));
57
+
58
+ vi.mock("../../src/infra/SignalHandler", () => ({
59
+ SignalHandler: vi.fn().mockImplementation(function (this: any) {
60
+ this.waitForTermination = vi.fn().mockResolvedValue(undefined);
61
+ this.isTerminated = vi.fn().mockReturnValue(false);
62
+ this.requestTermination = vi.fn();
63
+ }),
64
+ }));
65
+
66
+ vi.mock("../../src/utils/copy-src", () => ({
67
+ watchCopySrcFiles: vi.fn().mockResolvedValue({
68
+ close: vi.fn().mockResolvedValue(undefined),
69
+ }),
70
+ }));
71
+
72
+ vi.mock("@simplysm/core-common", () => ({
73
+ err: {
74
+ message: vi.fn((e: any) => (e instanceof Error ? e.message : String(e))),
75
+ },
76
+ }));
77
+
78
+ // Engine mock — tracks created engines and the package they were created for
79
+ const mockBuildEngines: Array<{
80
+ run: ReturnType<typeof vi.fn>;
81
+ startWatch: ReturnType<typeof vi.fn>;
82
+ stop: ReturnType<typeof vi.fn>;
83
+ _pkgName: string;
84
+ }> = [];
85
+
86
+ vi.mock("../../src/engines/index", () => ({
87
+ createBuildEngine: vi.fn((pkg: any, _options: any) => {
88
+ const engine = {
89
+ run: vi.fn().mockResolvedValue({
90
+ success: true,
91
+ js: { success: true, errors: [], warnings: [] },
92
+ dts: { success: true, errors: [], warnings: [], diagnostics: [] },
93
+ }),
94
+ startWatch: vi.fn().mockResolvedValue(undefined),
95
+ stop: vi.fn().mockResolvedValue(undefined),
96
+ _pkgName: pkg.name,
97
+ };
98
+ mockBuildEngines.push(engine);
99
+ return engine;
100
+ }),
101
+ }));
102
+
103
+ vi.mock("@simplysm/core-node", () => ({
104
+ FsWatcher: {
105
+ watch: vi.fn().mockResolvedValue({
106
+ onChange: vi.fn(),
107
+ close: vi.fn().mockResolvedValue(undefined),
108
+ }),
109
+ },
110
+ Worker: {
111
+ create: vi.fn(),
112
+ },
113
+ }));
114
+
115
+ vi.mock("child_process", () => ({
116
+ spawn: vi.fn(() => ({
117
+ on: vi.fn(),
118
+ kill: vi.fn(),
119
+ exitCode: 0,
120
+ })),
121
+ }));
122
+
123
+ // --- Dynamic imports after mocking ---
124
+
125
+ const { DevWatchOrchestrator } = await import("../../src/orchestrators/DevWatchOrchestrator");
126
+ const { loadSdConfig } = await import("../../src/utils/sd-config");
127
+ const { filterPackagesByTargets, validateTargets } = await import("../../src/utils/package-utils");
128
+ const { watchReplaceDeps } = await import("../../src/utils/replace-deps");
129
+ const { printErrors, printServers: _printServers } = await import("../../src/utils/output-utils");
130
+ const { createBuildEngine } = await import("../../src/engines/index");
131
+ const { ResultCollector } = await import("../../src/infra/ResultCollector");
132
+ const { RebuildManager } = await import("../../src/utils/rebuild-manager");
133
+ const { watchCopySrcFiles } = await import("../../src/utils/copy-src");
134
+ const { FsWatcher, Worker } = await import("@simplysm/core-node");
135
+ const { spawn } = await import("child_process");
136
+ const { getVersion } = await import("../../src/utils/build-env");
137
+
138
+ import type { SdConfig } from "../../src/sd-config.types";
139
+
140
+ // --- Runtime worker mock helper ---
141
+
142
+ interface MockRuntimeProxy {
143
+ on: ReturnType<typeof vi.fn>;
144
+ start: ReturnType<typeof vi.fn>;
145
+ terminate: ReturnType<typeof vi.fn>;
146
+ emit: (event: string, data?: unknown) => void;
147
+ }
148
+
149
+ function createMockRuntimeProxy(): MockRuntimeProxy {
150
+ const handlers = new Map<string, (data: unknown) => void>();
151
+ return {
152
+ on: vi.fn((event: string, handler: (data: unknown) => void) => {
153
+ handlers.set(event, handler);
154
+ }),
155
+ start: vi.fn().mockResolvedValue(undefined),
156
+ terminate: vi.fn().mockResolvedValue(undefined),
157
+ emit(event: string, data?: unknown) {
158
+ handlers.get(event)?.(data);
159
+ },
160
+ };
161
+ }
162
+
163
+ let mockRuntimeProxies: MockRuntimeProxy[];
164
+
165
+ // --- Helpers ---
166
+
167
+ function createConfig(overrides: Partial<SdConfig> = {}): SdConfig {
168
+ return { packages: {}, ...overrides };
169
+ }
170
+
171
+ function setupDefaults(config: SdConfig): void {
172
+ vi.mocked(loadSdConfig).mockResolvedValue(config);
173
+ vi.mocked(filterPackagesByTargets).mockImplementation((pkgs) => {
174
+ const result: Record<string, any> = {};
175
+ for (const [k, v] of Object.entries(pkgs)) {
176
+ if (v != null) result[k] = v;
177
+ }
178
+ return result;
179
+ });
180
+ vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: vi.fn() });
181
+ vi.mocked(getVersion).mockResolvedValue("1.0.0");
182
+ }
183
+
184
+ // --- Tests ---
185
+
186
+ describe("DevWatchOrchestrator", () => {
187
+ beforeEach(() => {
188
+ vi.clearAllMocks();
189
+ mockBuildEngines.length = 0;
190
+ mockRuntimeProxies = [];
191
+ vi.spyOn(process, "cwd").mockReturnValue("/test-root");
192
+ vi.spyOn(process.stdout, "write").mockImplementation(() => true);
193
+ vi.mocked(Worker.create).mockImplementation(() => {
194
+ const proxy = createMockRuntimeProxy();
195
+ mockRuntimeProxies.push(proxy);
196
+ return proxy as any;
197
+ });
198
+ });
199
+
200
+ describe("Slice 1: watch mode", () => {
201
+ // --- Unit Tests ---
202
+
203
+ it("creates BuildEngine for library packages with correct output flags", async () => {
204
+ setupDefaults(createConfig({
205
+ packages: { "core-common": { target: "node" }, "core-browser": { target: "browser" } },
206
+ }));
207
+
208
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
209
+ await orchestrator.initialize();
210
+
211
+ expect(createBuildEngine).toHaveBeenCalledTimes(2);
212
+ });
213
+
214
+ it("does not create BuildEngine for server packages in watch mode", async () => {
215
+ setupDefaults(createConfig({
216
+ packages: { "service-server": { target: "server" } },
217
+ }));
218
+
219
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
220
+ await orchestrator.initialize();
221
+
222
+ expect(createBuildEngine).not.toHaveBeenCalled();
223
+ });
224
+
225
+ it("outputs warning when no watchable packages exist", async () => {
226
+ setupDefaults(createConfig({
227
+ packages: { "sd-scripts": { target: "scripts" } },
228
+ }));
229
+
230
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
231
+ await orchestrator.initialize();
232
+
233
+ expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("No packages"));
234
+ expect(createBuildEngine).not.toHaveBeenCalled();
235
+ });
236
+
237
+ // --- Acceptance: Scenario 1 — watch 모드는 Library + Scripts만 대상 (server 제외) ---
238
+ it("processes Library and Scripts packages in watch mode, excludes server", async () => {
239
+ setupDefaults(createConfig({
240
+ packages: {
241
+ "core-common": { target: "node" },
242
+ "service-server": { target: "server" },
243
+ "sd-scripts": { target: "scripts", watch: { target: ["dist/**/*.js"], cmd: "node" } },
244
+ },
245
+ }));
246
+
247
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
248
+ await orchestrator.initialize();
249
+ await orchestrator.start();
250
+
251
+ // Library only = 1 engine (server excluded)
252
+ expect(createBuildEngine).toHaveBeenCalledTimes(1);
253
+ expect(createBuildEngine).toHaveBeenCalledWith(
254
+ expect.objectContaining({ name: "core-common" }),
255
+ expect.any(Object),
256
+ );
257
+
258
+ // Library engine: startWatch with js+dts
259
+ const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
260
+ expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
261
+
262
+ // Scripts+watch: spawn called for hook
263
+ expect(spawn).toHaveBeenCalled();
264
+ });
265
+
266
+ // --- Acceptance: Scenario 2 — 통합 Orchestrator가 공통 인프라를 사용한다 ---
267
+ it("creates ResultCollector, RebuildManager, SignalHandler on initialize", async () => {
268
+ setupDefaults(createConfig({
269
+ packages: { "core-common": { target: "node" } },
270
+ }));
271
+
272
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
273
+ await orchestrator.initialize();
274
+
275
+ expect(ResultCollector).toHaveBeenCalledTimes(1);
276
+ expect(RebuildManager).toHaveBeenCalledTimes(1);
277
+
278
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
279
+ expect(rebuildInstance.on).toHaveBeenCalledWith("batchComplete", expect.any(Function));
280
+ });
281
+
282
+ // --- Acceptance: Scenario 3 — Library 패키지 watch ---
283
+ it("starts library engine with js+dts output", async () => {
284
+ setupDefaults(createConfig({
285
+ packages: {
286
+ "core-common": { target: "node" },
287
+ "core-browser": { target: "browser" },
288
+ "storage": { target: "neutral" },
289
+ },
290
+ }));
291
+
292
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
293
+ await orchestrator.initialize();
294
+ await orchestrator.start();
295
+
296
+ for (const engine of mockBuildEngines) {
297
+ expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
298
+ }
299
+ });
300
+
301
+ // --- Acceptance: Scenario 4 — Server 패키지는 watch에서 제외 ---
302
+ it("excludes server packages from watch mode entirely", async () => {
303
+ setupDefaults(createConfig({
304
+ packages: {
305
+ "core-common": { target: "node" },
306
+ "service-server": { target: "server" },
307
+ },
308
+ }));
309
+
310
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
311
+ await orchestrator.initialize();
312
+ await orchestrator.start();
313
+
314
+ // Only library engine created, no server engine
315
+ expect(createBuildEngine).toHaveBeenCalledTimes(1);
316
+ expect(createBuildEngine).toHaveBeenCalledWith(
317
+ expect.objectContaining({ name: "core-common" }),
318
+ expect.any(Object),
319
+ );
320
+ expect(Worker.create).not.toHaveBeenCalled();
321
+ });
322
+
323
+ // --- Acceptance: Scenario 5 — Scripts+watch 패키지 hook 실행 ---
324
+ it("runs initial hook and starts file watcher for scripts+watch packages", async () => {
325
+ setupDefaults(createConfig({
326
+ packages: {
327
+ "sd-scripts": {
328
+ target: "scripts",
329
+ watch: { target: ["dist/**/*.js"], cmd: "node", args: ["run-task.js"] },
330
+ },
331
+ },
332
+ }));
333
+
334
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
335
+ await orchestrator.initialize();
336
+ await orchestrator.start();
337
+
338
+ expect(spawn).toHaveBeenCalledWith("node", ["run-task.js"], expect.objectContaining({ shell: true }));
339
+ expect(FsWatcher.watch).toHaveBeenCalled();
340
+ });
341
+
342
+ // Unit: file change re-runs hook
343
+ it("re-runs watch hook when watched files change", async () => {
344
+ let onChangeCallback: (...args: any[]) => void = () => {};
345
+ vi.mocked(FsWatcher.watch).mockResolvedValue({
346
+ onChange: vi.fn((_opts: any, cb: any) => { onChangeCallback = cb; }),
347
+ close: vi.fn().mockResolvedValue(undefined),
348
+ } as any);
349
+
350
+ setupDefaults(createConfig({
351
+ packages: {
352
+ "sd-scripts": {
353
+ target: "scripts",
354
+ watch: { target: ["dist/**/*.js"], cmd: "node", args: ["run-task.js"] },
355
+ },
356
+ },
357
+ }));
358
+
359
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
360
+ await orchestrator.initialize();
361
+ await orchestrator.start();
362
+
363
+ expect(spawn).toHaveBeenCalledTimes(1);
364
+ onChangeCallback();
365
+ expect(spawn).toHaveBeenCalledTimes(2);
366
+ });
367
+
368
+ // --- Acceptance: Scenario 6 — watch에서 copySrc 파일 감시 ---
369
+ it("starts copySrc watcher when library package has copySrc config", async () => {
370
+ setupDefaults(createConfig({
371
+ packages: {
372
+ "core-common": { target: "node", copySrc: ["**/*.json"] },
373
+ },
374
+ }));
375
+
376
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
377
+ await orchestrator.initialize();
378
+ await orchestrator.start();
379
+
380
+ expect(watchCopySrcFiles).toHaveBeenCalledWith(
381
+ expect.stringContaining("core-common"),
382
+ ["**/*.json"],
383
+ );
384
+ });
385
+
386
+ // Unit: no copySrc watcher when config absent
387
+ it("does not start copySrc watcher when config is absent", async () => {
388
+ setupDefaults(createConfig({
389
+ packages: { "core-common": { target: "node" } },
390
+ }));
391
+
392
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
393
+ await orchestrator.initialize();
394
+ await orchestrator.start();
395
+
396
+ expect(watchCopySrcFiles).not.toHaveBeenCalled();
397
+ });
398
+
399
+ // --- Acceptance: Library 패키지에 watch hook 설정 시 빌드 엔진 + hook 동시 실행 ---
400
+ it("runs both build engine and watch hook for library package with watch config", async () => {
401
+ setupDefaults(createConfig({
402
+ packages: {
403
+ "core-common": {
404
+ target: "node",
405
+ watch: { target: ["scripts/**/*.mjs"], cmd: "node", args: ["sync.mjs"] },
406
+ },
407
+ },
408
+ }));
409
+
410
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
411
+ await orchestrator.initialize();
412
+ await orchestrator.start();
413
+
414
+ // Build engine created and started
415
+ expect(createBuildEngine).toHaveBeenCalledTimes(1);
416
+ const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
417
+ expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
418
+
419
+ // Watch hook also executed
420
+ expect(spawn).toHaveBeenCalledWith("node", ["sync.mjs"], expect.objectContaining({ shell: true }));
421
+ });
422
+
423
+ // --- Unit: Library 패키지에 watch hook 없으면 hook 미실행 ---
424
+ it("does not run watch hook for library package without watch config", async () => {
425
+ setupDefaults(createConfig({
426
+ packages: { "core-common": { target: "node" } },
427
+ }));
428
+
429
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
430
+ await orchestrator.initialize();
431
+ await orchestrator.start();
432
+
433
+ // Build engine created
434
+ expect(createBuildEngine).toHaveBeenCalledTimes(1);
435
+ // No hook
436
+ expect(spawn).not.toHaveBeenCalled();
437
+ });
438
+
439
+ // --- Acceptance: Scenario 7 — watch에서 replaceDeps 감시 ---
440
+ it("starts replaceDeps watcher when config exists", async () => {
441
+ const replaceDeps = { "@simplysm/*": "packages/*/src" };
442
+ setupDefaults(createConfig({
443
+ packages: { "core-common": { target: "node" } },
444
+ replaceDeps,
445
+ }));
446
+
447
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
448
+ await orchestrator.initialize();
449
+
450
+ expect(watchReplaceDeps).toHaveBeenCalledWith("/test-root", replaceDeps);
451
+ });
452
+
453
+ // Unit: no replaceDeps watcher when config absent
454
+ it("does not start replaceDeps watcher when config is absent", async () => {
455
+ setupDefaults(createConfig({
456
+ packages: { "core-common": { target: "node" } },
457
+ }));
458
+
459
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
460
+ await orchestrator.initialize();
461
+
462
+ expect(watchReplaceDeps).not.toHaveBeenCalled();
463
+ });
464
+
465
+ // --- Shutdown tests ---
466
+
467
+ it("stops all engines, closes watchers, and disposes replaceDeps on shutdown", async () => {
468
+ const mockDispose = vi.fn();
469
+ vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
470
+
471
+ const mockCopySrcWatcher = { close: vi.fn().mockResolvedValue(undefined) };
472
+ vi.mocked(watchCopySrcFiles).mockResolvedValue(mockCopySrcWatcher as any);
473
+
474
+ setupDefaults(createConfig({
475
+ packages: {
476
+ "core-common": { target: "node", copySrc: ["**/*.json"] },
477
+ },
478
+ replaceDeps: { "@simplysm/*": "packages/*/src" },
479
+ }));
480
+ vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
481
+
482
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
483
+ await orchestrator.initialize();
484
+ await orchestrator.start();
485
+ await orchestrator.shutdown();
486
+
487
+ for (const engine of mockBuildEngines) {
488
+ expect(engine.stop).toHaveBeenCalledOnce();
489
+ }
490
+ expect(mockCopySrcWatcher.close).toHaveBeenCalledOnce();
491
+ expect(mockDispose).toHaveBeenCalledOnce();
492
+ });
493
+
494
+ // --- batchComplete handler ---
495
+
496
+ it("triggers printErrors on batchComplete", async () => {
497
+ setupDefaults(createConfig({
498
+ packages: { "core-common": { target: "node" } },
499
+ }));
500
+
501
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
502
+ await orchestrator.initialize();
503
+
504
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
505
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
506
+ const batchHandler = onCall?.[1] as (() => void) | undefined;
507
+
508
+ vi.mocked(printErrors).mockClear();
509
+ batchHandler?.();
510
+
511
+ expect(printErrors).toHaveBeenCalledOnce();
512
+ });
513
+
514
+ // --- awaitTermination ---
515
+
516
+ it("delegates awaitTermination to SignalHandler", async () => {
517
+ setupDefaults(createConfig({
518
+ packages: { "core-common": { target: "node" } },
519
+ }));
520
+
521
+ const { SignalHandler } = await import("../../src/infra/SignalHandler");
522
+
523
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
524
+ await orchestrator.initialize();
525
+ await orchestrator.awaitTermination();
526
+
527
+ const signalInstance = vi.mocked(SignalHandler).mock.instances[0];
528
+ expect(signalInstance.waitForTermination).toHaveBeenCalled();
529
+ });
530
+
531
+ // --- targets filter ---
532
+
533
+ it("passes targets to filterPackagesByTargets", async () => {
534
+ setupDefaults(createConfig({
535
+ packages: { "core-common": { target: "node" }, "storage": { target: "node" } },
536
+ }));
537
+
538
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["core-common"], options: [] });
539
+ await orchestrator.initialize();
540
+
541
+ expect(filterPackagesByTargets).toHaveBeenCalledWith(
542
+ expect.objectContaining({ "core-common": { target: "node" } }),
543
+ ["core-common"],
544
+ );
545
+ });
546
+ });
547
+
548
+ describe("Slice 2: dev mode", () => {
549
+ // Helper: override engine mock to add build result to resultCollector
550
+ function setupEngineWithResult(status: "success" | "error" = "success"): void {
551
+ vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
552
+ const engine = {
553
+ run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
554
+ startWatch: vi.fn().mockImplementation(() => {
555
+ options.resultCollector?.add({
556
+ name: pkg.name, target: "server", type: "build", status,
557
+ });
558
+ }),
559
+ stop: vi.fn().mockResolvedValue(undefined),
560
+ _pkgName: pkg.name,
561
+ };
562
+ mockBuildEngines.push(engine);
563
+ return engine as any;
564
+ });
565
+ }
566
+
567
+ // --- Acceptance: dev 명령어가 통합 Orchestrator를 사용한다 ---
568
+ it("creates engine for server packages and starts runtime in dev mode", async () => {
569
+ setupDefaults(createConfig({
570
+ packages: { "service-server": { target: "server" } },
571
+ }));
572
+ setupEngineWithResult("success");
573
+
574
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
575
+ await orchestrator.initialize();
576
+ await orchestrator.start();
577
+
578
+ expect(createBuildEngine).toHaveBeenCalledOnce();
579
+ expect(createBuildEngine).toHaveBeenCalledWith(
580
+ expect.objectContaining({ name: "service-server" }),
581
+ expect.any(Object),
582
+ );
583
+ expect(mockBuildEngines[0].startWatch).toHaveBeenCalledWith({ js: true, dts: false });
584
+ expect(Worker.create).toHaveBeenCalledTimes(1);
585
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
586
+ expect.objectContaining({ env: expect.objectContaining({ VER: "1.0.0", DEV: "true" }) }),
587
+ );
588
+ });
589
+
590
+ // --- Acceptance: Server 패키지 dev 모드 실행 ---
591
+ it("passes merged env (VER+DEV+config.env) to engine and runtime", async () => {
592
+ setupDefaults(createConfig({
593
+ packages: {
594
+ "service-server": { target: "server", env: { DB_HOST: "localhost" } },
595
+ },
596
+ }));
597
+ setupEngineWithResult("success");
598
+
599
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
600
+ await orchestrator.initialize();
601
+ await orchestrator.start();
602
+
603
+ expect(createBuildEngine).toHaveBeenCalledWith(
604
+ expect.objectContaining({
605
+ config: expect.objectContaining({
606
+ env: expect.objectContaining({ VER: "1.0.0", DEV: "true", DB_HOST: "localhost" }),
607
+ }),
608
+ }),
609
+ expect.any(Object),
610
+ );
611
+ });
612
+
613
+ // --- Acceptance: Server 리빌드 실패 시 runtime 미재시작 ---
614
+ it("does not start runtime when build fails", async () => {
615
+ setupDefaults(createConfig({
616
+ packages: { "service-server": { target: "server" } },
617
+ }));
618
+ setupEngineWithResult("error");
619
+
620
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
621
+ await orchestrator.initialize();
622
+ await orchestrator.start();
623
+
624
+ expect(Worker.create).not.toHaveBeenCalled();
625
+ expect(printErrors).toHaveBeenCalled();
626
+ });
627
+
628
+ // --- Acceptance: Library 패키지가 dev에서 제외된다 ---
629
+ it("excludes library packages from dev mode", async () => {
630
+ setupDefaults(createConfig({
631
+ packages: {
632
+ "core-common": { target: "node" },
633
+ "service-server": { target: "server" },
634
+ },
635
+ }));
636
+ setupEngineWithResult("success");
637
+
638
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
639
+ await orchestrator.initialize();
640
+ await orchestrator.start();
641
+
642
+ // Only server engine created
643
+ expect(createBuildEngine).toHaveBeenCalledOnce();
644
+ expect(createBuildEngine).toHaveBeenCalledWith(
645
+ expect.objectContaining({ name: "service-server" }),
646
+ expect.any(Object),
647
+ );
648
+ });
649
+
650
+ // --- Acceptance: client target 패키지를 분류한다 ---
651
+ it("creates engine for client packages in dev mode", async () => {
652
+ setupDefaults(createConfig({
653
+ packages: {
654
+ "service-server": { target: "server" },
655
+ "my-client": { target: "client", server: "service-server" },
656
+ },
657
+ }));
658
+ setupEngineWithResult("success");
659
+
660
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
661
+ await orchestrator.initialize();
662
+
663
+ // Server + Client engines created
664
+ expect(createBuildEngine).toHaveBeenCalledTimes(2);
665
+ expect(createBuildEngine).toHaveBeenCalledWith(
666
+ expect.objectContaining({ name: "my-client" }),
667
+ expect.any(Object),
668
+ );
669
+ });
670
+
671
+ // --- Acceptance: server가 string이고 해당 서버가 dev 대상 → 서버 연결 클라이언트 ---
672
+ it("maps client to server when config.server is a string and server is a dev target", async () => {
673
+ setupDefaults(createConfig({
674
+ packages: {
675
+ "service-server": { target: "server" },
676
+ "my-client": { target: "client", server: "service-server" },
677
+ },
678
+ }));
679
+ setupEngineWithResult("success");
680
+
681
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
682
+ await orchestrator.initialize();
683
+
684
+ // Client engine created (not skipped)
685
+ const clientEngineCall = vi.mocked(createBuildEngine).mock.calls.find(
686
+ (c: any[]) => c[0].name === "my-client",
687
+ );
688
+ expect(clientEngineCall).toBeDefined();
689
+ });
690
+
691
+ // --- Acceptance: server가 string이지만 서버가 dev 대상 아닌 경우 → 경고 후 독립 전환 ---
692
+ it("warns and treats as standalone when config.server names a non-dev-target server", async () => {
693
+ setupDefaults(createConfig({
694
+ packages: {
695
+ "my-client": { target: "client", server: "non-existent-server" },
696
+ },
697
+ }));
698
+ setupEngineWithResult("success");
699
+
700
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
701
+ await orchestrator.initialize();
702
+
703
+ // Client engine still created
704
+ expect(createBuildEngine).toHaveBeenCalledWith(
705
+ expect.objectContaining({ name: "my-client" }),
706
+ expect.any(Object),
707
+ );
708
+ // Warning about non-target server
709
+ expect(process.stdout.write).toHaveBeenCalledWith(
710
+ expect.stringContaining("non-existent-server"),
711
+ );
712
+ });
713
+
714
+ // --- Acceptance: client 패키지가 0개 → 기존 동작 유지 ---
715
+ it("operates normally with server only when no client packages exist", async () => {
716
+ setupDefaults(createConfig({
717
+ packages: {
718
+ "service-server": { target: "server" },
719
+ },
720
+ }));
721
+ setupEngineWithResult("success");
722
+
723
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
724
+ await orchestrator.initialize();
725
+ await orchestrator.start();
726
+
727
+ expect(createBuildEngine).toHaveBeenCalledOnce();
728
+ expect(createBuildEngine).toHaveBeenCalledWith(
729
+ expect.objectContaining({ name: "service-server" }),
730
+ expect.any(Object),
731
+ );
732
+ });
733
+
734
+ // --- Unit: server가 number인 경우 독립 클라이언트 ---
735
+ it("creates engine for standalone client with config.server as number", async () => {
736
+ setupDefaults(createConfig({
737
+ packages: {
738
+ "standalone": { target: "client", server: 4200 },
739
+ },
740
+ }));
741
+ setupEngineWithResult("success");
742
+
743
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
744
+ await orchestrator.initialize();
745
+
746
+ expect(createBuildEngine).toHaveBeenCalledWith(
747
+ expect.objectContaining({ name: "standalone" }),
748
+ expect.any(Object),
749
+ );
750
+ });
751
+
752
+ // --- Unit: client only (no server) still initializes ---
753
+ it("initializes when only client packages exist (no server)", async () => {
754
+ setupDefaults(createConfig({
755
+ packages: {
756
+ "my-client": { target: "client", server: 4200 },
757
+ },
758
+ }));
759
+ setupEngineWithResult("success");
760
+
761
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
762
+ await orchestrator.initialize();
763
+
764
+ expect(createBuildEngine).toHaveBeenCalledOnce();
765
+ });
766
+
767
+ // --- Acceptance: Server 리빌드 시 runtime 재시작 ---
768
+ it("restarts runtime on batchComplete with success", async () => {
769
+ setupDefaults(createConfig({
770
+ packages: { "service-server": { target: "server" } },
771
+ }));
772
+ setupEngineWithResult("success");
773
+
774
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
775
+ await orchestrator.initialize();
776
+ await orchestrator.start();
777
+
778
+ // Get batchComplete handler
779
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
780
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
781
+ const batchHandler = onCall?.[1] as (() => void) | undefined;
782
+
783
+ batchHandler?.();
784
+ await new Promise((r) => setTimeout(r, 0));
785
+
786
+ // Old runtime terminated, new one created
787
+ expect(mockRuntimeProxies).toHaveLength(2);
788
+ expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
789
+ });
790
+
791
+ // --- Acceptance: SIGINT/SIGTERM 시 graceful shutdown (dev mode) ---
792
+ it("stops engines and terminates runtime workers on shutdown", async () => {
793
+ setupDefaults(createConfig({
794
+ packages: { "service-server": { target: "server" } },
795
+ }));
796
+ setupEngineWithResult("success");
797
+
798
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
799
+ await orchestrator.initialize();
800
+ await orchestrator.start();
801
+ await orchestrator.shutdown();
802
+
803
+ expect(mockBuildEngines[0].stop).toHaveBeenCalledOnce();
804
+ expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
805
+ expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("Shutting down"));
806
+ });
807
+
808
+ // --- Acceptance: dev에서 replaceDeps 감시 ---
809
+ it("starts replaceDeps watcher in dev mode", async () => {
810
+ const replaceDeps = { "@simplysm/*": "packages/*/src" };
811
+ setupDefaults(createConfig({
812
+ packages: { "service-server": { target: "server" } },
813
+ replaceDeps,
814
+ }));
815
+ setupEngineWithResult("success");
816
+
817
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
818
+ await orchestrator.initialize();
819
+
820
+ expect(watchReplaceDeps).toHaveBeenCalledWith("/test-root", replaceDeps);
821
+ });
822
+
823
+ // Unit: outputs warning when no server packages
824
+ it("outputs warning when no server packages in dev mode", async () => {
825
+ setupDefaults(createConfig({
826
+ packages: { "core-common": { target: "node" } },
827
+ }));
828
+
829
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
830
+ await orchestrator.initialize();
831
+
832
+ expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("No"));
833
+ });
834
+
835
+ // Unit: multiple server packages
836
+ it("creates engines and runtimes for multiple server packages", async () => {
837
+ setupDefaults(createConfig({
838
+ packages: {
839
+ "service-server": { target: "server" },
840
+ "api-server": { target: "server" },
841
+ },
842
+ }));
843
+ setupEngineWithResult("success");
844
+
845
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
846
+ await orchestrator.initialize();
847
+ await orchestrator.start();
848
+
849
+ expect(createBuildEngine).toHaveBeenCalledTimes(2);
850
+ expect(Worker.create).toHaveBeenCalledTimes(2);
851
+ });
852
+
853
+ // Unit: skips start when no packages
854
+ it("skips start when no server packages found", async () => {
855
+ setupDefaults(createConfig({
856
+ packages: { "core-common": { target: "node" } },
857
+ }));
858
+
859
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
860
+ await orchestrator.initialize();
861
+ await orchestrator.start();
862
+
863
+ expect(createBuildEngine).not.toHaveBeenCalled();
864
+ });
865
+
866
+ // Unit: disposes replaceDeps on dev shutdown
867
+ it("disposes replaceDeps watcher on dev shutdown", async () => {
868
+ const mockDispose = vi.fn();
869
+ vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose } as any);
870
+
871
+ setupDefaults(createConfig({
872
+ packages: { "service-server": { target: "server" } },
873
+ replaceDeps: { "@simplysm/*": "packages/*/src" },
874
+ }));
875
+ vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose } as any);
876
+ setupEngineWithResult("success");
877
+
878
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
879
+ await orchestrator.initialize();
880
+ await orchestrator.start();
881
+ await orchestrator.shutdown();
882
+
883
+ expect(mockDispose).toHaveBeenCalledOnce();
884
+ });
885
+ });
886
+
887
+ describe("Slice 4: dev mode client integration", () => {
888
+ // Helper: engine mock with ViteEngine-like port behavior for clients
889
+ function setupEngineWithClientPort(
890
+ serverStatus: "success" | "error" = "success",
891
+ clientPort: number | null = 54321,
892
+ ): void {
893
+ vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
894
+ const isClient = pkg.config.target === "client";
895
+ const engine: any = {
896
+ run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
897
+ startWatch: vi.fn().mockImplementation(() => {
898
+ if (isClient) {
899
+ // Client: simulate Vite serverReady
900
+ if (clientPort != null) {
901
+ engine.port = clientPort;
902
+ }
903
+ } else {
904
+ // Server: report build result
905
+ options.resultCollector?.add({
906
+ name: pkg.name, target: "server", type: "build", status: serverStatus,
907
+ });
908
+ }
909
+ }),
910
+ stop: vi.fn().mockResolvedValue(undefined),
911
+ _pkgName: pkg.name,
912
+ port: undefined,
913
+ };
914
+ mockBuildEngines.push(engine);
915
+ return engine;
916
+ });
917
+ }
918
+
919
+ // --- Acceptance: 서버 연결 클라이언트의 Vite dev server 시작 ---
920
+ it("starts client engines with startWatch({ js: true, dts: false })", async () => {
921
+ setupDefaults(createConfig({
922
+ packages: {
923
+ "service-server": { target: "server" },
924
+ "my-client": { target: "client", server: "service-server" },
925
+ },
926
+ }));
927
+ setupEngineWithClientPort("success");
928
+
929
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
930
+ await orchestrator.initialize();
931
+ await orchestrator.start();
932
+
933
+ const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
934
+ expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false });
935
+ });
936
+
937
+ // --- Acceptance: 서버 연결 클라이언트 ready 대기 후 서버 시작 → clientPorts 전달 ---
938
+ it("passes clientPorts to server runtime for connected clients", async () => {
939
+ setupDefaults(createConfig({
940
+ packages: {
941
+ "service-server": { target: "server" },
942
+ "my-client": { target: "client", server: "service-server" },
943
+ },
944
+ }));
945
+ setupEngineWithClientPort("success", 54321);
946
+
947
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
948
+ await orchestrator.initialize();
949
+ await orchestrator.start();
950
+
951
+ expect(Worker.create).toHaveBeenCalled();
952
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
953
+ expect.objectContaining({
954
+ clientPorts: { "my-client": 54321 },
955
+ }),
956
+ );
957
+ });
958
+
959
+ // --- Acceptance: 독립 클라이언트는 서버 시작 대기에 영향 없음 ---
960
+ it("does not include standalone client ports in server runtime clientPorts", async () => {
961
+ setupDefaults(createConfig({
962
+ packages: {
963
+ "service-server": { target: "server" },
964
+ "standalone": { target: "client", server: 4200 },
965
+ },
966
+ }));
967
+ setupEngineWithClientPort("success", 4200);
968
+
969
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
970
+ await orchestrator.initialize();
971
+ await orchestrator.start();
972
+
973
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
974
+ expect.objectContaining({
975
+ clientPorts: {},
976
+ }),
977
+ );
978
+ });
979
+
980
+ // --- Acceptance: 독립 클라이언트 URL 출력 (ResultCollector 등록) ---
981
+ it("registers standalone client as running server in ResultCollector", async () => {
982
+ setupDefaults(createConfig({
983
+ packages: {
984
+ "standalone": { target: "client", server: 4200 },
985
+ },
986
+ }));
987
+ setupEngineWithClientPort("success", 4200);
988
+
989
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
990
+ await orchestrator.initialize();
991
+ await orchestrator.start();
992
+
993
+ const resultCollector = vi.mocked(ResultCollector).mock.instances[0];
994
+ expect(resultCollector.add).toHaveBeenCalledWith(
995
+ expect.objectContaining({
996
+ name: "standalone",
997
+ target: "client",
998
+ type: "server",
999
+ status: "running",
1000
+ port: 4200,
1001
+ }),
1002
+ );
1003
+ });
1004
+
1005
+ // --- Acceptance: 클라이언트 Vite 서버 실패 시 프록시 없이 서버 시작 ---
1006
+ it("starts server without proxy when client port is unavailable", async () => {
1007
+ setupDefaults(createConfig({
1008
+ packages: {
1009
+ "service-server": { target: "server" },
1010
+ "my-client": { target: "client", server: "service-server" },
1011
+ },
1012
+ }));
1013
+ setupEngineWithClientPort("success", null);
1014
+
1015
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1016
+ await orchestrator.initialize();
1017
+ await orchestrator.start();
1018
+
1019
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
1020
+ expect.objectContaining({
1021
+ clientPorts: {},
1022
+ }),
1023
+ );
1024
+ });
1025
+
1026
+ // --- Acceptance: printServers에 serverClientsMap 전달 ---
1027
+ it("passes serverClientsMap to printServers", async () => {
1028
+ setupDefaults(createConfig({
1029
+ packages: {
1030
+ "service-server": { target: "server" },
1031
+ "my-client": { target: "client", server: "service-server" },
1032
+ },
1033
+ }));
1034
+ setupEngineWithClientPort("success", 54321);
1035
+
1036
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1037
+ await orchestrator.initialize();
1038
+ await orchestrator.start();
1039
+
1040
+ expect(_printServers).toHaveBeenCalledWith(
1041
+ expect.any(Map),
1042
+ expect.any(Map),
1043
+ );
1044
+ const serverClientsMap = vi.mocked(_printServers).mock.calls[0][1] as Map<string, string[]>;
1045
+ expect(serverClientsMap.get("service-server")).toEqual(["my-client"]);
1046
+ });
1047
+
1048
+ // --- Acceptance: 서버 rebuild 시 프록시 재등록 ---
1049
+ it("passes clientPorts on server rebuild restart", async () => {
1050
+ setupDefaults(createConfig({
1051
+ packages: {
1052
+ "service-server": { target: "server" },
1053
+ "my-client": { target: "client", server: "service-server" },
1054
+ },
1055
+ }));
1056
+ setupEngineWithClientPort("success", 54321);
1057
+
1058
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1059
+ await orchestrator.initialize();
1060
+ await orchestrator.start();
1061
+
1062
+ // Trigger batchComplete
1063
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
1064
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
1065
+ const batchHandler = onCall?.[1] as (() => void) | undefined;
1066
+ batchHandler?.();
1067
+ await new Promise((r) => setTimeout(r, 0));
1068
+
1069
+ // Second runtime start should also have clientPorts
1070
+ expect(mockRuntimeProxies).toHaveLength(2);
1071
+ expect(mockRuntimeProxies[1].start).toHaveBeenCalledWith(
1072
+ expect.objectContaining({
1073
+ clientPorts: { "my-client": 54321 },
1074
+ }),
1075
+ );
1076
+ });
1077
+
1078
+ // --- Unit: shutdown stops client engines ---
1079
+ it("stops client engines on shutdown", async () => {
1080
+ setupDefaults(createConfig({
1081
+ packages: {
1082
+ "service-server": { target: "server" },
1083
+ "my-client": { target: "client", server: "service-server" },
1084
+ },
1085
+ }));
1086
+ setupEngineWithClientPort("success", 54321);
1087
+
1088
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1089
+ await orchestrator.initialize();
1090
+ await orchestrator.start();
1091
+ await orchestrator.shutdown();
1092
+
1093
+ const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
1094
+ expect(clientEngine.stop).toHaveBeenCalledOnce();
1095
+ });
1096
+
1097
+ // --- Unit: Client dev server에 env를 주입하지 않음 ---
1098
+ it("does not pass env to client engine config", async () => {
1099
+ setupDefaults(createConfig({
1100
+ packages: {
1101
+ "my-client": { target: "client", server: 4200, env: { API_URL: "http://example.com" } },
1102
+ },
1103
+ }));
1104
+ setupEngineWithClientPort("success", 4200);
1105
+
1106
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1107
+ await orchestrator.initialize();
1108
+
1109
+ // Client engine should not have VER/DEV in config
1110
+ const clientCall = vi.mocked(createBuildEngine).mock.calls.find(
1111
+ (c: any[]) => c[0].name === "my-client",
1112
+ );
1113
+ const clientConfig = clientCall?.[0] as any;
1114
+ expect(clientConfig.config.env).toEqual({ API_URL: "http://example.com" });
1115
+ expect(clientConfig.config.env?.VER).toBeUndefined();
1116
+ expect(clientConfig.config.env?.DEV).toBeUndefined();
1117
+ });
1118
+ });
1119
+
1120
+ describe("target validation", () => {
1121
+ it("calls validateTargets during watch initialize", async () => {
1122
+ setupDefaults(createConfig({
1123
+ packages: { "core-common": { target: "node" } },
1124
+ }));
1125
+
1126
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["core-common"], options: [] });
1127
+ await orchestrator.initialize();
1128
+
1129
+ expect(validateTargets).toHaveBeenCalledWith(
1130
+ ["core-common"],
1131
+ expect.objectContaining({ "core-common": { target: "node" } }),
1132
+ );
1133
+ });
1134
+
1135
+ it("calls validateTargets during dev initialize", async () => {
1136
+ setupDefaults(createConfig({
1137
+ packages: { "service-server": { target: "server" } },
1138
+ }));
1139
+
1140
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: ["service-server"], options: [] });
1141
+ await orchestrator.initialize();
1142
+
1143
+ expect(validateTargets).toHaveBeenCalledWith(
1144
+ ["service-server"],
1145
+ expect.objectContaining({ "service-server": { target: "server" } }),
1146
+ );
1147
+ });
1148
+
1149
+ it("throws when validateTargets throws for unknown target in watch", async () => {
1150
+ setupDefaults(createConfig({
1151
+ packages: { "core-common": { target: "node" } },
1152
+ }));
1153
+ vi.mocked(validateTargets).mockImplementation(() => {
1154
+ throw new Error("Unknown target: nonexistent");
1155
+ });
1156
+
1157
+ const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["nonexistent"], options: [] });
1158
+ await expect(orchestrator.initialize()).rejects.toThrow("Unknown target: nonexistent");
1159
+ });
1160
+
1161
+ it("throws when validateTargets throws for unknown target in dev", async () => {
1162
+ setupDefaults(createConfig({
1163
+ packages: { "service-server": { target: "server" } },
1164
+ }));
1165
+ vi.mocked(validateTargets).mockImplementation(() => {
1166
+ throw new Error("Unknown target: nonexistent");
1167
+ });
1168
+
1169
+ const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: ["nonexistent"], options: [] });
1170
+ await expect(orchestrator.initialize()).rejects.toThrow("Unknown target: nonexistent");
1171
+ });
1172
+ });
1173
+ });