@hubspot/cli 7.8.0-experimental.0 → 7.8.0

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 (357) hide show
  1. package/bin/cli.js +31 -27
  2. package/commands/__tests__/auth.test.js +5 -0
  3. package/commands/__tests__/doctor.test.js +16 -16
  4. package/commands/__tests__/getStarted.test.js +2 -2
  5. package/commands/__tests__/mcp.test.js +1 -1
  6. package/commands/__tests__/project.test.js +2 -3
  7. package/commands/account/auth.js +1 -0
  8. package/commands/account/clean.js +18 -27
  9. package/commands/account/createOverride.js +13 -31
  10. package/commands/account/info.js +20 -31
  11. package/commands/account/list.js +16 -22
  12. package/commands/account/remove.js +12 -20
  13. package/commands/account/removeOverride.js +11 -21
  14. package/commands/account/rename.js +6 -9
  15. package/commands/account/use.js +12 -26
  16. package/commands/account.js +2 -2
  17. package/commands/app/__tests__/migrate.test.js +5 -6
  18. package/commands/app/migrate.js +13 -19
  19. package/commands/app/secret/add.js +2 -1
  20. package/commands/app/secret/delete.js +2 -1
  21. package/commands/app/secret/list.js +2 -1
  22. package/commands/app/secret/update.js +2 -1
  23. package/commands/app/secret.js +2 -1
  24. package/commands/app.js +2 -2
  25. package/commands/auth.d.ts +1 -0
  26. package/commands/auth.js +17 -7
  27. package/commands/cms/convertFields.js +7 -9
  28. package/commands/cms/getReactModule.js +9 -14
  29. package/commands/cms/lighthouseScore.js +33 -36
  30. package/commands/cms.js +2 -2
  31. package/commands/completion.js +3 -3
  32. package/commands/config/set.d.ts +1 -1
  33. package/commands/config/set.js +64 -37
  34. package/commands/config.js +2 -2
  35. package/commands/create.js +2 -2
  36. package/commands/customObject/create.js +10 -12
  37. package/commands/customObject/schema/create.js +9 -11
  38. package/commands/customObject/schema/delete.js +16 -16
  39. package/commands/customObject/schema/fetch-all.js +12 -11
  40. package/commands/customObject/schema/fetch.js +15 -15
  41. package/commands/customObject/schema/list.js +4 -4
  42. package/commands/customObject/schema/update.js +13 -13
  43. package/commands/customObject/schema.js +2 -2
  44. package/commands/customObject.js +6 -7
  45. package/commands/doctor.js +8 -11
  46. package/commands/feedback.js +8 -13
  47. package/commands/fetch.js +8 -8
  48. package/commands/filemanager/fetch.js +7 -7
  49. package/commands/filemanager/upload.js +15 -34
  50. package/commands/filemanager.js +2 -2
  51. package/commands/function/deploy.js +11 -29
  52. package/commands/function/list.js +8 -8
  53. package/commands/function/server.js +9 -11
  54. package/commands/function.d.ts +1 -1
  55. package/commands/function.js +2 -2
  56. package/commands/getStarted.d.ts +1 -3
  57. package/commands/getStarted.js +68 -20
  58. package/commands/hubdb/clear.js +7 -15
  59. package/commands/hubdb/create.js +9 -15
  60. package/commands/hubdb/delete.js +8 -15
  61. package/commands/hubdb/fetch.js +6 -9
  62. package/commands/hubdb.d.ts +1 -1
  63. package/commands/hubdb.js +2 -2
  64. package/commands/init.js +2 -3
  65. package/commands/lint.js +16 -16
  66. package/commands/list.js +8 -14
  67. package/commands/logs.js +14 -20
  68. package/commands/mcp/__tests__/setup.test.js +2 -2
  69. package/commands/mcp/setup.js +11 -2
  70. package/commands/mcp.js +3 -3
  71. package/commands/mv.js +6 -17
  72. package/commands/open.js +5 -5
  73. package/commands/project/__tests__/add.test.js +15 -13
  74. package/commands/project/__tests__/create.test.js +6 -6
  75. package/commands/project/__tests__/deploy.test.js +3 -7
  76. package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
  77. package/commands/project/__tests__/installDeps.test.js +8 -8
  78. package/commands/project/__tests__/list.test.js +31 -0
  79. package/commands/project/__tests__/logs.test.js +1 -4
  80. package/commands/project/__tests__/migrate.test.js +7 -7
  81. package/commands/project/__tests__/migrateApp.test.js +3 -7
  82. package/commands/project/__tests__/profile.test.js +1 -1
  83. package/commands/project/__tests__/validate.test.js +98 -0
  84. package/commands/project/add.d.ts +2 -2
  85. package/commands/project/add.js +7 -10
  86. package/commands/project/cloneApp.js +14 -19
  87. package/commands/project/create.js +4 -11
  88. package/commands/project/deploy.js +5 -5
  89. package/commands/project/dev/deprecatedFlow.js +9 -18
  90. package/commands/project/dev/index.js +21 -18
  91. package/commands/project/dev/unifiedFlow.js +15 -8
  92. package/commands/project/download.js +15 -16
  93. package/commands/project/installDeps.d.ts +2 -2
  94. package/commands/project/installDeps.js +9 -9
  95. package/commands/project/list.d.ts +4 -0
  96. package/commands/project/list.js +62 -0
  97. package/commands/project/listBuilds.js +12 -21
  98. package/commands/project/logs.js +21 -24
  99. package/commands/project/migrate.js +33 -12
  100. package/commands/project/migrateApp.js +10 -17
  101. package/commands/project/open.js +6 -14
  102. package/commands/project/profile/add.js +3 -3
  103. package/commands/project/profile/delete.js +1 -2
  104. package/commands/project/profile.js +2 -3
  105. package/commands/project/upload.js +16 -25
  106. package/commands/project/validate.js +7 -7
  107. package/commands/project/watch.js +13 -22
  108. package/commands/project.js +4 -3
  109. package/commands/sandbox/__tests__/create.test.js +5 -5
  110. package/commands/sandbox/create.js +22 -32
  111. package/commands/sandbox/delete.js +39 -64
  112. package/commands/sandbox.js +2 -2
  113. package/commands/secret/addSecret.js +7 -17
  114. package/commands/secret/deleteSecret.js +10 -20
  115. package/commands/secret/listSecret.js +8 -10
  116. package/commands/secret/updateSecret.js +9 -17
  117. package/commands/secret.js +2 -2
  118. package/commands/testAccount/__tests__/delete.test.js +2 -4
  119. package/commands/testAccount/create.js +2 -2
  120. package/commands/testAccount/delete.d.ts +4 -3
  121. package/commands/testAccount/delete.js +155 -14
  122. package/commands/testAccount/importData.d.ts +1 -1
  123. package/commands/testAccount/importData.js +1 -1
  124. package/commands/testAccount.js +1 -1
  125. package/commands/theme/preview.js +1 -4
  126. package/lang/en.d.ts +365 -111
  127. package/lang/en.js +409 -158
  128. package/lang/en.lyaml +4 -4
  129. package/lib/__tests__/buildAccount.test.js +4 -3
  130. package/lib/__tests__/commonOpts.test.js +1 -1
  131. package/lib/__tests__/dependencyManagement.test.js +1 -1
  132. package/lib/__tests__/developerTestAccounts.test.js +3 -3
  133. package/lib/__tests__/hasFeature.test.js +145 -7
  134. package/lib/__tests__/npm.test.js +1 -1
  135. package/lib/__tests__/oauth.test.js +4 -4
  136. package/lib/__tests__/process.test.js +10 -5
  137. package/lib/__tests__/sandboxSync.test.js +8 -8
  138. package/lib/__tests__/sandboxes.test.js +8 -8
  139. package/lib/__tests__/serverlessLogs.test.js +1 -1
  140. package/lib/__tests__/usageTracking.test.js +5 -5
  141. package/lib/__tests__/validation.test.js +2 -1
  142. package/lib/__tests__/yargsUtils.test.js +83 -9
  143. package/lib/app/__tests__/migrate.test.js +19 -56
  144. package/lib/app/__tests__/migrate_legacy.test.js +1 -1
  145. package/lib/app/migrate.d.ts +2 -8
  146. package/lib/app/migrate.js +6 -81
  147. package/lib/app/migrate_legacy.js +20 -24
  148. package/lib/buildAccount.d.ts +2 -2
  149. package/lib/buildAccount.js +32 -64
  150. package/lib/commonOpts.d.ts +1 -1
  151. package/lib/commonOpts.js +25 -22
  152. package/lib/configMigrate.js +88 -9
  153. package/lib/configOptions.js +7 -0
  154. package/lib/constants.d.ts +22 -1
  155. package/lib/constants.js +26 -1
  156. package/lib/dependencyManagement.d.ts +0 -5
  157. package/lib/dependencyManagement.js +9 -36
  158. package/lib/developerTestAccounts.js +9 -23
  159. package/lib/doctor/Diagnosis.js +11 -23
  160. package/lib/doctor/DiagnosticInfoBuilder.js +12 -11
  161. package/lib/doctor/Doctor.js +42 -90
  162. package/lib/doctor/__tests__/Doctor.test.js +4 -4
  163. package/lib/errorHandlers/index.js +12 -20
  164. package/lib/errorHandlers/suppressError.js +11 -18
  165. package/lib/hasFeature.js +6 -0
  166. package/lib/lang.js +6 -5
  167. package/lib/links.d.ts +1 -0
  168. package/lib/links.js +14 -7
  169. package/lib/mcp/setup.js +1 -1
  170. package/lib/middleware/__test__/commandTargetingUtils.test.js +99 -0
  171. package/lib/middleware/__test__/configMiddleware.test.js +11 -11
  172. package/lib/middleware/__test__/yargsChecksMiddleware.test.js +6 -8
  173. package/lib/middleware/commandTargetingUtils.d.ts +8 -0
  174. package/lib/middleware/commandTargetingUtils.js +74 -0
  175. package/lib/middleware/configMiddleware.d.ts +1 -1
  176. package/lib/middleware/configMiddleware.js +21 -81
  177. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  178. package/lib/middleware/gitMiddleware.js +5 -1
  179. package/lib/middleware/notificationsMiddleware.js +5 -11
  180. package/lib/middleware/yargsChecksMiddleware.js +6 -9
  181. package/lib/npm.js +2 -2
  182. package/lib/oauth.js +5 -5
  183. package/lib/process.js +5 -4
  184. package/lib/projects/__tests__/AppDevModeInterface.test.js +87 -90
  185. package/lib/projects/__tests__/LocalDevProcess.test.js +231 -19
  186. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +89 -63
  187. package/lib/projects/__tests__/deploy.test.js +73 -8
  188. package/lib/projects/__tests__/localDevProjectHelpers.test.js +6 -2
  189. package/lib/projects/__tests__/platformVersion.test.js +8 -8
  190. package/lib/projects/__tests__/projects.test.js +12 -12
  191. package/lib/projects/__tests__/structure.test.js +3 -3
  192. package/lib/projects/__tests__/upload.test.d.ts +1 -0
  193. package/lib/projects/__tests__/upload.test.js +82 -0
  194. package/lib/projects/add/__tests__/legacyAddComponent.test.js +6 -6
  195. package/lib/projects/add/__tests__/v2AddComponent.test.d.ts +1 -0
  196. package/lib/projects/add/__tests__/{v3AddComponent.test.js → v2AddComponent.test.js} +39 -39
  197. package/lib/projects/add/{v3AddComponent.d.ts → v2AddComponent.d.ts} +1 -1
  198. package/lib/projects/add/{v3AddComponent.js → v2AddComponent.js} +5 -5
  199. package/lib/projects/create/__tests__/legacy.test.js +5 -5
  200. package/lib/projects/create/__tests__/v2.test.d.ts +1 -0
  201. package/lib/projects/create/__tests__/{v3.test.js → v2.test.js} +82 -7
  202. package/lib/projects/create/index.js +4 -4
  203. package/lib/projects/create/legacy.js +2 -2
  204. package/lib/projects/create/{v3.d.ts → v2.d.ts} +3 -3
  205. package/lib/projects/create/{v3.js → v2.js} +16 -13
  206. package/lib/projects/deploy.d.ts +1 -1
  207. package/lib/projects/deploy.js +2 -2
  208. package/lib/projects/localDev/AppDevModeInterface.d.ts +10 -1
  209. package/lib/projects/localDev/AppDevModeInterface.js +118 -89
  210. package/lib/projects/localDev/DevServerManager.d.ts +11 -29
  211. package/lib/projects/localDev/DevServerManager.js +19 -61
  212. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +40 -0
  213. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +120 -0
  214. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  215. package/lib/projects/localDev/LocalDevLogger.js +27 -6
  216. package/lib/projects/localDev/{LocalDevManager.js → LocalDevManager_DEPRECATED.js} +10 -11
  217. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  218. package/lib/projects/localDev/LocalDevProcess.js +93 -21
  219. package/lib/projects/localDev/LocalDevState.d.ts +12 -8
  220. package/lib/projects/localDev/LocalDevState.js +27 -17
  221. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +6 -1
  222. package/lib/projects/localDev/LocalDevWebsocketServer.js +94 -33
  223. package/lib/projects/localDev/helpers/account.d.ts +1 -1
  224. package/lib/projects/localDev/helpers/account.js +2 -2
  225. package/lib/projects/localDev/helpers/project.d.ts +3 -2
  226. package/lib/projects/localDev/helpers/project.js +49 -10
  227. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +7 -0
  228. package/lib/projects/localDev/localDevWebsocketServerUtils.js +19 -0
  229. package/lib/projects/platformVersion.d.ts +1 -1
  230. package/lib/projects/platformVersion.js +1 -1
  231. package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
  232. package/lib/projects/structure.js +6 -6
  233. package/lib/projects/upload.d.ts +1 -1
  234. package/lib/projects/upload.js +17 -8
  235. package/lib/projects/urls.d.ts +0 -1
  236. package/lib/projects/urls.js +0 -3
  237. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +1 -0
  238. package/lib/prompts/__tests__/projectAddPrompt.test.js +10 -10
  239. package/lib/prompts/accountNamePrompt.js +14 -19
  240. package/lib/prompts/accountsPrompt.js +2 -2
  241. package/lib/prompts/cmsFieldPrompt.js +2 -2
  242. package/lib/prompts/createApiSamplePrompt.js +5 -5
  243. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +10 -1
  244. package/lib/prompts/createFunctionPrompt.js +14 -14
  245. package/lib/prompts/createModulePrompt.js +9 -9
  246. package/lib/prompts/createTemplatePrompt.js +2 -2
  247. package/lib/prompts/downloadProjectPrompt.js +5 -8
  248. package/lib/prompts/installAppPrompt.d.ts +1 -6
  249. package/lib/prompts/installAppPrompt.js +1 -6
  250. package/lib/prompts/personalAccessKeyPrompt.js +3 -3
  251. package/lib/prompts/previewPrompt.js +6 -6
  252. package/lib/prompts/projectAddPrompt.d.ts +2 -2
  253. package/lib/prompts/projectAddPrompt.js +9 -2
  254. package/lib/prompts/projectDevTargetAccountPrompt.js +20 -32
  255. package/lib/prompts/projectNamePrompt.js +4 -8
  256. package/lib/prompts/projectsLogsPrompt.js +2 -4
  257. package/lib/prompts/promptUtils.js +30 -9
  258. package/lib/prompts/sandboxesPrompt.js +7 -7
  259. package/lib/prompts/secretPrompt.js +3 -3
  260. package/lib/prompts/selectAppPrompt.js +3 -3
  261. package/lib/prompts/selectHubDBTablePrompt.js +9 -13
  262. package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
  263. package/lib/prompts/selectPublicAppForMigrationPrompt.js +15 -19
  264. package/lib/prompts/setAsDefaultAccountPrompt.js +4 -8
  265. package/lib/prompts/uploadPrompt.js +5 -5
  266. package/lib/sandboxSync.js +24 -41
  267. package/lib/sandboxes.js +19 -47
  268. package/lib/schema.js +3 -3
  269. package/lib/serverlessLogs.js +11 -13
  270. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  271. package/lib/theme/__tests__/migrate.test.js +233 -0
  272. package/lib/theme/migrate.d.ts +13 -0
  273. package/lib/theme/migrate.js +90 -0
  274. package/lib/ui/SpinniesManager.d.ts +2 -0
  275. package/lib/ui/SpinniesManager.js +112 -8
  276. package/lib/ui/boxen.js +1 -2
  277. package/lib/ui/git.js +13 -10
  278. package/lib/ui/index.d.ts +4 -0
  279. package/lib/ui/index.js +47 -38
  280. package/lib/ui/serverlessFunctionLogs.js +9 -7
  281. package/lib/ui/uiMessages.d.ts +72 -0
  282. package/lib/ui/uiMessages.js +75 -0
  283. package/lib/usageTracking.js +8 -8
  284. package/lib/validation.js +20 -23
  285. package/lib/yargsUtils.d.ts +1 -1
  286. package/lib/yargsUtils.js +12 -5
  287. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  288. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  289. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  290. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  291. package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
  292. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  293. package/mcp-server/tools/cms/HsListTool.js +1 -1
  294. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  295. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  296. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  297. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
  298. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  299. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  300. package/mcp-server/tools/index.js +4 -0
  301. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  302. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  303. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  304. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  305. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  306. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  307. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  308. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  309. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +23 -0
  310. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +68 -0
  311. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +11 -0
  312. package/mcp-server/tools/project/GetApplicationInfoTool.js +49 -0
  313. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  314. package/mcp-server/tools/project/GetConfigValuesTool.js +13 -7
  315. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +2 -2
  316. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  317. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  318. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  319. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  320. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +2 -2
  321. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  322. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  323. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  324. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.d.ts +1 -0
  325. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +169 -0
  326. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.d.ts +1 -0
  327. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +115 -0
  328. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  329. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  330. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  331. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  332. package/mcp-server/tools/project/constants.d.ts +1 -1
  333. package/mcp-server/tools/project/constants.js +9 -3
  334. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  335. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  336. package/mcp-server/utils/cliConfig.d.ts +1 -0
  337. package/mcp-server/utils/cliConfig.js +12 -0
  338. package/mcp-server/utils/toolUsageTracking.js +2 -2
  339. package/package.json +8 -12
  340. package/types/LocalDev.d.ts +19 -3
  341. package/ui/components/HorizontalSelectPrompt.js +1 -1
  342. package/ui/index.js +1 -1
  343. package/commands/getStartedV2.d.ts +0 -9
  344. package/commands/getStartedV2.js +0 -39
  345. package/lib/middleware/__test__/utils.test.js +0 -51
  346. package/lib/middleware/utils.d.ts +0 -8
  347. package/lib/middleware/utils.js +0 -14
  348. package/lib/projects/localDev/DevServerManagerV2.d.ts +0 -22
  349. package/lib/projects/localDev/DevServerManagerV2.js +0 -81
  350. package/ui/components/Ascii.d.ts +0 -10
  351. package/ui/components/Ascii.js +0 -11
  352. package/ui/views/GetStarted.d.ts +0 -7
  353. package/ui/views/GetStarted.js +0 -157
  354. /package/{lib/middleware/__test__/utils.test.d.ts → commands/project/__tests__/list.test.d.ts} +0 -0
  355. /package/{lib/projects/add/__tests__/v3AddComponent.test.d.ts → commands/project/__tests__/validate.test.d.ts} +0 -0
  356. /package/lib/{projects/create/__tests__/v3.test.d.ts → middleware/__test__/commandTargetingUtils.test.d.ts} +0 -0
  357. /package/lib/projects/localDev/{LocalDevManager.d.ts → LocalDevManager_DEPRECATED.d.ts} +0 -0
@@ -1,10 +1,13 @@
1
1
  import path from 'path';
2
2
  import { translateForLocalDev } from '@hubspot/project-parsing-lib';
3
3
  import { handleProjectUpload } from '../upload.js';
4
+ import { handleProjectDeploy } from '../deploy.js';
4
5
  import { getProjectConfig } from '../config.js';
6
+ import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
7
+ import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
5
8
  import LocalDevProcess from '../localDev/LocalDevProcess.js';
6
9
  import LocalDevLogger from '../localDev/LocalDevLogger.js';
7
- import DevServerManagerV2 from '../localDev/DevServerManagerV2.js';
10
+ import DevServerManager from '../localDev/DevServerManager.js';
8
11
  import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
9
12
  import { vi } from 'vitest';
10
13
  // Mock @hubspot/ui-extensions-dev-server
@@ -19,9 +22,12 @@ vi.mock('@hubspot/ui-extensions-dev-server', () => ({
19
22
  vi.mock('open');
20
23
  vi.mock('@hubspot/project-parsing-lib');
21
24
  vi.mock('../upload');
25
+ vi.mock('../deploy');
22
26
  vi.mock('../config');
27
+ vi.mock('@hubspot/local-dev-lib/api/projects');
28
+ vi.mock('@hubspot/local-dev-lib/errors/index');
23
29
  vi.mock('../localDev/LocalDevLogger');
24
- vi.mock('../localDev/DevServerManagerV2');
30
+ vi.mock('../localDev/DevServerManager');
25
31
  // Tests for LocalDevProcess and LocalDevState
26
32
  describe('LocalDevProcess', () => {
27
33
  let mockLocalDevLogger;
@@ -37,11 +43,36 @@ describe('LocalDevProcess', () => {
37
43
  projectConfig: mockProjectConfig,
38
44
  targetProjectAccountId: 123,
39
45
  targetTestingAccountId: 456,
40
- projectId: 789,
46
+ projectData: {
47
+ id: 789,
48
+ name: 'test-project',
49
+ portalId: 123,
50
+ createdAt: 0,
51
+ deletedAt: 0,
52
+ isLocked: false,
53
+ updatedAt: 0,
54
+ latestBuild: {
55
+ activitySource: { type: 'HUBSPOT_USER', userId: 456 },
56
+ buildId: 123,
57
+ createdAt: '2023-01-01T00:00:00Z',
58
+ deployableState: 'DEPLOYABLE',
59
+ deployStatusTaskLocator: { id: 'task-123', links: [] },
60
+ enqueuedAt: '2023-01-01T00:00:00Z',
61
+ finishedAt: '2023-01-01T00:05:00Z',
62
+ isAutoDeployEnabled: false,
63
+ portalId: 123,
64
+ projectName: 'test-project',
65
+ startedAt: '2023-01-01T00:01:00Z',
66
+ status: 'SUCCESS',
67
+ subbuildStatuses: [],
68
+ uploadMessage: 'Build completed',
69
+ autoDeployId: 0,
70
+ platformVersion: '2025.2',
71
+ },
72
+ },
41
73
  initialProjectNodes: {},
42
74
  initialProjectProfileData: {},
43
75
  env: ENVIRONMENTS.PROD,
44
- projectName: 'test-project',
45
76
  };
46
77
  beforeEach(() => {
47
78
  vi.clearAllMocks();
@@ -62,6 +93,9 @@ describe('LocalDevProcess', () => {
62
93
  uploadSuccess: vi.fn(),
63
94
  fileChangeError: vi.fn(),
64
95
  uploadWarning: vi.fn(),
96
+ deployInitiated: vi.fn(),
97
+ deployError: vi.fn(),
98
+ deploySuccess: vi.fn(),
65
99
  };
66
100
  mockDevServerManager = {
67
101
  setup: vi.fn().mockResolvedValue(undefined),
@@ -71,7 +105,9 @@ describe('LocalDevProcess', () => {
71
105
  };
72
106
  // Mock constructors
73
107
  LocalDevLogger.mockImplementation(() => mockLocalDevLogger);
74
- DevServerManagerV2.mockImplementation(() => mockDevServerManager);
108
+ DevServerManager.mockImplementation(() => mockDevServerManager);
109
+ // Mock external functions
110
+ isHubSpotHttpError.mockReturnValue(false);
75
111
  // Create process instance
76
112
  process = new LocalDevProcess(mockOptions);
77
113
  // Mock process.exit
@@ -141,9 +177,14 @@ describe('LocalDevProcess', () => {
141
177
  handleProjectUpload.mockResolvedValue({
142
178
  uploadError: new Error('Upload failed'),
143
179
  });
144
- const success = await process.uploadProject();
180
+ const result = await process.uploadProject();
145
181
  expect(mockLocalDevLogger.uploadError).toHaveBeenCalledWith(new Error('Upload failed'));
146
- expect(success).toBe(false);
182
+ expect(result).toEqual({
183
+ uploadSuccess: false,
184
+ buildSuccess: false,
185
+ deploySuccess: false,
186
+ deployId: undefined,
187
+ });
147
188
  });
148
189
  it('should handle successful upload', async () => {
149
190
  await process.handleConfigFileChange();
@@ -152,38 +193,120 @@ describe('LocalDevProcess', () => {
152
193
  });
153
194
  handleProjectUpload.mockResolvedValue({
154
195
  uploadError: null,
196
+ result: {
197
+ deployResult: {
198
+ id: 'deploy-123',
199
+ deployId: 123,
200
+ status: 'SUCCESS',
201
+ },
202
+ },
203
+ });
204
+ fetchProject.mockResolvedValue({
205
+ data: {
206
+ id: 789,
207
+ name: 'test-project',
208
+ portalId: 123,
209
+ createdAt: 0,
210
+ deletedAt: 0,
211
+ isLocked: false,
212
+ updatedAt: 0,
213
+ latestBuild: { id: 'build-1', status: 'SUCCESS' },
214
+ deployedBuild: { id: 'build-1', status: 'SUCCESS' },
215
+ },
155
216
  });
156
- const success = await process.uploadProject();
217
+ const result = await process.uploadProject();
218
+ expect(fetchProject).toHaveBeenCalledWith(mockOptions.targetProjectAccountId, mockOptions.projectConfig.name);
157
219
  expect(mockLocalDevLogger.uploadSuccess).toHaveBeenCalled();
158
220
  // @ts-expect-error accessing private property for testing
159
221
  expect(process.state.uploadWarnings.size).toBe(0);
160
- expect(success).toBe(true);
222
+ expect(result).toEqual({
223
+ uploadSuccess: true,
224
+ buildSuccess: true,
225
+ deploySuccess: true,
226
+ deployId: 123,
227
+ });
161
228
  });
162
- it('should reset projectNodesAtLastUpload', async () => {
163
- const mockNodes = { node1: { uid: 'node1' } };
164
- const initialProjectNodes = { existingNode: { uid: 'existingNode' } };
229
+ it('should reset projectNodesAtLastUpload if deploy is successful', async () => {
230
+ const mockInitialNodes = {
231
+ node1: {
232
+ uid: 'node1',
233
+ componentType: 'APP',
234
+ localDev: {
235
+ componentRoot: '/test/path',
236
+ componentConfigPath: '/test/path/config.json',
237
+ configUpdatedSinceLastUpload: false,
238
+ },
239
+ componentDeps: {},
240
+ metaFilePath: '/test/path',
241
+ config: { name: 'Node 1' },
242
+ files: [],
243
+ },
244
+ };
245
+ const mockNewNodes = {
246
+ node1: {
247
+ uid: 'node2',
248
+ componentType: 'APP',
249
+ localDev: {
250
+ componentRoot: '/test/path',
251
+ componentConfigPath: '/test/path/config.json',
252
+ configUpdatedSinceLastUpload: false,
253
+ },
254
+ componentDeps: {},
255
+ metaFilePath: '/test/path',
256
+ config: { name: 'Node 2' },
257
+ files: [],
258
+ },
259
+ };
165
260
  // @ts-expect-error accessing private property for testing
166
- process.state._projectNodesAtLastUpload = initialProjectNodes;
261
+ process.state.projectNodesAtLastDeploy = mockInitialNodes;
167
262
  getProjectConfig.mockResolvedValue({
168
263
  projectConfig: mockOptions.projectConfig,
169
264
  });
170
265
  handleProjectUpload.mockResolvedValue({
171
266
  uploadError: null,
267
+ result: {
268
+ deployResult: {
269
+ id: 'deploy-123',
270
+ deployId: 456,
271
+ status: 'SUCCESS',
272
+ },
273
+ },
172
274
  });
173
275
  translateForLocalDev.mockResolvedValue({
174
- intermediateNodesIndexedByUid: mockNodes,
276
+ intermediateNodesIndexedByUid: mockNewNodes,
175
277
  });
176
- const success = await process.uploadProject();
177
- // Verify translateForLocalDev was called without projectNodesAtLastUpload option
278
+ fetchProject.mockResolvedValue({
279
+ data: {
280
+ id: 789,
281
+ name: 'test-project',
282
+ portalId: 123,
283
+ createdAt: 0,
284
+ deletedAt: 0,
285
+ isLocked: false,
286
+ updatedAt: 0,
287
+ latestBuild: { id: 'build-1', status: 'SUCCESS' },
288
+ deployedBuild: { id: 'build-1', status: 'SUCCESS' },
289
+ },
290
+ });
291
+ const result = await process.uploadProject();
292
+ // Verify translateForLocalDev was called during updateProjectNodesAfterDeploy
178
293
  expect(translateForLocalDev).toHaveBeenCalledWith({
179
294
  projectSourceDir: path.join(mockOptions.projectDir, mockOptions.projectConfig.srcDir),
180
295
  platformVersion: mockOptions.projectConfig.platformVersion,
181
296
  accountId: mockOptions.targetProjectAccountId,
182
- }, { projectNodesAtLastUpload: undefined });
297
+ }, {
298
+ profile: undefined,
299
+ projectNodesAtLastUpload: undefined,
300
+ });
183
301
  // Verify projectNodesAtLastUpload was reset to the new nodes
184
302
  // @ts-expect-error accessing private property for testing
185
- expect(process.state.projectNodesAtLastUpload).toEqual(mockNodes);
186
- expect(success).toBe(true);
303
+ expect(process.state.projectNodesAtLastDeploy).toEqual(mockNewNodes);
304
+ expect(result).toEqual({
305
+ uploadSuccess: true,
306
+ buildSuccess: true,
307
+ deploySuccess: true,
308
+ deployId: 456,
309
+ });
187
310
  });
188
311
  });
189
312
  describe('handleFileChange()', () => {
@@ -252,4 +375,93 @@ describe('LocalDevProcess', () => {
252
375
  expect(listener).toHaveBeenCalledTimes(1);
253
376
  });
254
377
  });
378
+ describe('deployLatestBuild()', () => {
379
+ beforeEach(() => {
380
+ vi.clearAllMocks();
381
+ });
382
+ it('should successfully deploy latest build', async () => {
383
+ const mockDeploy = {
384
+ deployId: 456,
385
+ buildId: 123,
386
+ status: 'SUCCESS',
387
+ enqueuedAt: '2023-01-01T00:00:00Z',
388
+ startedAt: '2023-01-01T00:01:00Z',
389
+ finishedAt: '2023-01-01T00:05:00Z',
390
+ portalId: 123,
391
+ projectName: 'test-project',
392
+ userId: 789,
393
+ source: 'HUBSPOT_USER',
394
+ subdeployStatuses: [],
395
+ };
396
+ handleProjectDeploy.mockResolvedValue(mockDeploy);
397
+ const result = await process.deployLatestBuild();
398
+ expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
399
+ expect(handleProjectDeploy).toHaveBeenCalledWith(123, // targetProjectAccountId
400
+ 'test-project', // projectName
401
+ 123, // buildId
402
+ true, // useV2Api
403
+ false // force
404
+ );
405
+ expect(mockLocalDevLogger.deploySuccess).toHaveBeenCalled();
406
+ expect(result).toEqual({
407
+ success: true,
408
+ deployId: 456,
409
+ });
410
+ });
411
+ it('should deploy with force parameter', async () => {
412
+ const mockDeploy = {
413
+ deployId: 456,
414
+ buildId: 123,
415
+ status: 'SUCCESS',
416
+ enqueuedAt: '2023-01-01T00:00:00Z',
417
+ startedAt: '2023-01-01T00:01:00Z',
418
+ finishedAt: '2023-01-01T00:05:00Z',
419
+ portalId: 123,
420
+ projectName: 'test-project',
421
+ userId: 789,
422
+ source: 'HUBSPOT_USER',
423
+ subdeployStatuses: [],
424
+ };
425
+ handleProjectDeploy.mockResolvedValue(mockDeploy);
426
+ const result = await process.deployLatestBuild(true);
427
+ expect(handleProjectDeploy).toHaveBeenCalledWith(123, // targetProjectAccountId
428
+ 'test-project', // projectName
429
+ 123, // buildId
430
+ true, // useV2Api
431
+ true // force
432
+ );
433
+ expect(result).toEqual({
434
+ success: true,
435
+ deployId: 456,
436
+ });
437
+ });
438
+ it('should return error when no build exists', async () => {
439
+ // Create a process without latestBuild
440
+ const optionsWithoutBuild = {
441
+ ...mockOptions,
442
+ projectData: {
443
+ ...mockOptions.projectData,
444
+ latestBuild: undefined,
445
+ },
446
+ };
447
+ const processWithoutBuild = new LocalDevProcess(optionsWithoutBuild);
448
+ const result = await processWithoutBuild.deployLatestBuild();
449
+ expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
450
+ expect(mockLocalDevLogger.deployError).toHaveBeenCalledWith('Error deploying project. No build was found to deploy.');
451
+ expect(result).toEqual({
452
+ success: false,
453
+ });
454
+ expect(handleProjectDeploy).not.toHaveBeenCalled();
455
+ });
456
+ it('should handle deploy failure when no deploy object returned', async () => {
457
+ handleProjectDeploy.mockResolvedValue(undefined);
458
+ const result = await process.deployLatestBuild();
459
+ expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
460
+ expect(handleProjectDeploy).toHaveBeenCalled();
461
+ expect(result).toEqual({
462
+ success: false,
463
+ });
464
+ expect(mockLocalDevLogger.deploySuccess).not.toHaveBeenCalled();
465
+ });
466
+ });
255
467
  });
@@ -1,12 +1,12 @@
1
1
  import { WebSocketServer } from 'ws';
2
2
  import { isPortManagerServerRunning, requestPorts, } from '@hubspot/local-dev-lib/portManager';
3
- import { logger } from '@hubspot/local-dev-lib/logger';
3
+ import { uiLogger } from '../../ui/logger.js';
4
4
  import { LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES, LOCAL_DEV_UI_MESSAGE_SEND_TYPES, LOCAL_DEV_SERVER_MESSAGE_TYPES, } from '../../constants.js';
5
5
  import LocalDevWebsocketServer from '../localDev/LocalDevWebsocketServer.js';
6
6
  import { lib } from '../../../lang/en.js';
7
7
  vi.mock('ws');
8
8
  vi.mock('@hubspot/local-dev-lib/portManager');
9
- vi.mock('@hubspot/local-dev-lib/logger');
9
+ vi.mock('../../ui/logger.js');
10
10
  describe('LocalDevWebsocketServer', () => {
11
11
  let mockLocalDevProcess;
12
12
  let mockWebSocket;
@@ -30,8 +30,16 @@ describe('LocalDevWebsocketServer', () => {
30
30
  mockLocalDevProcess = {
31
31
  addStateListener: vi.fn(),
32
32
  removeStateListener: vi.fn(),
33
- uploadProject: vi.fn(),
33
+ uploadProject: vi.fn().mockResolvedValue({}),
34
34
  sendDevServerMessage: vi.fn(),
35
+ projectData: {
36
+ name: 'test-project',
37
+ id: 123,
38
+ latestBuild: { id: 'build-1', status: 'SUCCESS' },
39
+ deployedBuild: { id: 'build-1', status: 'SUCCESS' },
40
+ },
41
+ targetProjectAccountId: 456,
42
+ targetTestingAccountId: 789,
35
43
  };
36
44
  // Mock WebSocketServer constructor
37
45
  WebSocketServer.mockImplementation(() => mockWebSocketServer);
@@ -51,39 +59,68 @@ describe('LocalDevWebsocketServer', () => {
51
59
  await server.start();
52
60
  expect(WebSocketServer).toHaveBeenCalledWith({ port: 1234 });
53
61
  expect(mockWebSocketServer.on).toHaveBeenCalledWith('connection', expect.any(Function));
54
- expect(logger.log).toHaveBeenCalled();
62
+ expect(uiLogger.log).toHaveBeenCalled();
55
63
  });
56
- it('should accept connection from valid origin', async () => {
57
- isPortManagerServerRunning.mockResolvedValue(true);
58
- requestPorts.mockResolvedValue({
59
- 'local-dev-ui-websocket-server': 1234,
64
+ describe('valid origins', () => {
65
+ const validOrigins = [
66
+ 'https://app.hubspot.com',
67
+ 'https://app.hubspotqa.com',
68
+ 'https://local.hubspot.com',
69
+ 'https://local.hubspotqa.com',
70
+ 'https://app-na2.hubspot.com',
71
+ 'https://app-na2.hubspotqa.com',
72
+ 'https://app-na3.hubspot.com',
73
+ 'https://app-na3.hubspotqa.com',
74
+ 'https://app-ap1.hubspot.com',
75
+ 'https://app-ap1.hubspotqa.com',
76
+ 'https://app-eu1.hubspot.com',
77
+ 'https://app-eu1.hubspotqa.com',
78
+ ];
79
+ validOrigins.forEach(origin => {
80
+ it(`should accept connection from ${origin}`, async () => {
81
+ isPortManagerServerRunning.mockResolvedValue(true);
82
+ requestPorts.mockResolvedValue({
83
+ 'local-dev-ui-websocket-server': 1234,
84
+ });
85
+ await server.start();
86
+ // Get the connection callback
87
+ const connectionCallback = mockWebSocketServer.on.mock
88
+ .calls[0][1];
89
+ // Simulate connection from valid origin
90
+ connectionCallback(mockWebSocket, {
91
+ headers: { origin },
92
+ });
93
+ expect(mockWebSocket.on).toHaveBeenCalledWith('message', expect.any(Function));
94
+ expect(mockLocalDevProcess.addStateListener).toHaveBeenCalledWith('projectNodes', expect.any(Function));
95
+ expect(mockWebSocket.close).not.toHaveBeenCalled();
96
+ });
60
97
  });
61
- await server.start();
62
- // Get the connection callback
63
- const connectionCallback = mockWebSocketServer.on.mock.calls[0][1];
64
- // Simulate connection from valid origin
65
- connectionCallback(mockWebSocket, {
66
- headers: { origin: 'https://app.hubspot.com' },
67
- });
68
- expect(mockWebSocket.on).toHaveBeenCalledWith('message', expect.any(Function));
69
- expect(mockLocalDevProcess.addStateListener).toHaveBeenCalledWith('projectNodes', expect.any(Function));
70
- expect(mockWebSocket.close).not.toHaveBeenCalled();
71
98
  });
72
- it('should reject connection from invalid origin', async () => {
73
- isPortManagerServerRunning.mockResolvedValue(true);
74
- requestPorts.mockResolvedValue({
75
- 'local-dev-ui-websocket-server': 1234,
99
+ describe('invalid origins', () => {
100
+ const invalidOrigins = [
101
+ 'https://malicious-site.com',
102
+ 'https://app.malicious-site.com',
103
+ 'https://app.hubspot.com.evil.com',
104
+ ];
105
+ invalidOrigins.forEach(origin => {
106
+ it(`should reject connection from "${origin}"`, async () => {
107
+ isPortManagerServerRunning.mockResolvedValue(true);
108
+ requestPorts.mockResolvedValue({
109
+ 'local-dev-ui-websocket-server': 1234,
110
+ });
111
+ await server.start();
112
+ // Get the connection callback
113
+ const connectionCallback = mockWebSocketServer.on.mock
114
+ .calls[0][1];
115
+ // Simulate connection from invalid origin
116
+ connectionCallback(mockWebSocket, {
117
+ headers: { origin },
118
+ });
119
+ expect(mockWebSocket.close).toHaveBeenCalledWith(1008, lib.LocalDevWebsocketServer.errors.originNotAllowed(origin));
120
+ expect(mockWebSocket.on).not.toHaveBeenCalled();
121
+ expect(mockLocalDevProcess.addStateListener).not.toHaveBeenCalled();
122
+ });
76
123
  });
77
- await server.start();
78
- // Get the connection callback
79
- const connectionCallback = mockWebSocketServer.on.mock.calls[0][1];
80
- // Simulate connection from invalid origin
81
- connectionCallback(mockWebSocket, {
82
- headers: { origin: 'https://malicious-site.com' },
83
- });
84
- expect(mockWebSocket.close).toHaveBeenCalledWith(1008, lib.LocalDevWebsocketServer.errors.originNotAllowed('https://malicious-site.com'));
85
- expect(mockWebSocket.on).not.toHaveBeenCalled();
86
- expect(mockLocalDevProcess.addStateListener).not.toHaveBeenCalled();
87
124
  });
88
125
  it('should reject connection with no origin header', async () => {
89
126
  isPortManagerServerRunning.mockResolvedValue(true);
@@ -111,7 +148,7 @@ describe('LocalDevWebsocketServer', () => {
111
148
  const connectionCallback = mockWebSocketServer.on.mock.calls[0][1];
112
149
  // Simulate connection from valid origin
113
150
  connectionCallback(mockWebSocket, {
114
- headers: { origin: 'https://app.hubspot.com' },
151
+ headers: { origin: 'https://app-na3.hubspot.com' },
115
152
  });
116
153
  expect(mockLocalDevProcess.sendDevServerMessage).toHaveBeenCalledWith(LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED);
117
154
  });
@@ -140,7 +177,7 @@ describe('LocalDevWebsocketServer', () => {
140
177
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
141
178
  const message = {};
142
179
  messageCallback(JSON.stringify(message));
143
- expect(logger.error).toHaveBeenCalled();
180
+ expect(uiLogger.error).toHaveBeenCalled();
144
181
  });
145
182
  it('should log error for unknown message type', () => {
146
183
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
@@ -148,13 +185,13 @@ describe('LocalDevWebsocketServer', () => {
148
185
  type: 'UNKNOWN_TYPE',
149
186
  };
150
187
  messageCallback(JSON.stringify(message));
151
- expect(logger.error).toHaveBeenCalled();
188
+ expect(uiLogger.error).toHaveBeenCalled();
152
189
  });
153
190
  it('should log error for invalid JSON', () => {
154
191
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
155
192
  const invalidJson = 'invalid json';
156
193
  messageCallback(invalidJson);
157
- expect(logger.error).toHaveBeenCalled();
194
+ expect(uiLogger.error).toHaveBeenCalled();
158
195
  });
159
196
  });
160
197
  describe('shutdown()', () => {
@@ -205,7 +242,7 @@ describe('LocalDevWebsocketServer', () => {
205
242
  headers: { origin: 'https://app.hubspot.com' },
206
243
  });
207
244
  connectionCallback(mockWebSocket2, {
208
- headers: { origin: 'https://app.hubspotqa.com' },
245
+ headers: { origin: 'https://app-na2.hubspotqa.com' },
209
246
  });
210
247
  connectionCallback(mockWebSocket3, {
211
248
  headers: { origin: 'https://local.hubspot.com' },
@@ -215,7 +252,7 @@ describe('LocalDevWebsocketServer', () => {
215
252
  expect(mockWebSocket2.on).toHaveBeenCalledWith('message', expect.any(Function));
216
253
  expect(mockWebSocket3.on).toHaveBeenCalledWith('message', expect.any(Function));
217
254
  // Each connection should trigger state listener setup
218
- expect(mockLocalDevProcess.addStateListener).toHaveBeenCalledTimes(9); // 3 listeners per connection * 3 connections
255
+ expect(mockLocalDevProcess.addStateListener).toHaveBeenCalledTimes(12); // 4 listeners per connection * 3 connections
219
256
  // Each connection should trigger dev server message
220
257
  expect(mockLocalDevProcess.sendDevServerMessage).toHaveBeenCalledTimes(3);
221
258
  expect(mockLocalDevProcess.sendDevServerMessage).toHaveBeenCalledWith(LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED);
@@ -225,29 +262,12 @@ describe('LocalDevWebsocketServer', () => {
225
262
  expect(mockWebSocket3.close).not.toHaveBeenCalled();
226
263
  });
227
264
  it('should send project data to each connection independently', () => {
228
- // Setup mock project data properties as getters
229
- Object.defineProperty(mockLocalDevProcess, 'projectName', {
230
- get: () => 'test-project',
231
- configurable: true,
232
- });
233
- Object.defineProperty(mockLocalDevProcess, 'projectId', {
234
- get: () => 123,
235
- configurable: true,
236
- });
237
- Object.defineProperty(mockLocalDevProcess, 'targetProjectAccountId', {
238
- get: () => 456,
239
- configurable: true,
240
- });
241
- Object.defineProperty(mockLocalDevProcess, 'targetTestingAccountId', {
242
- get: () => 789,
243
- configurable: true,
244
- });
245
265
  // Establish multiple connections
246
266
  connectionCallback(mockWebSocket1, {
247
267
  headers: { origin: 'https://app.hubspot.com' },
248
268
  });
249
269
  connectionCallback(mockWebSocket2, {
250
- headers: { origin: 'https://app.hubspotqa.com' },
270
+ headers: { origin: 'https://app-eu1.hubspotqa.com' },
251
271
  });
252
272
  // Each websocket should receive project data
253
273
  expect(mockWebSocket1.send).toHaveBeenCalledWith(JSON.stringify({
@@ -255,6 +275,8 @@ describe('LocalDevWebsocketServer', () => {
255
275
  data: {
256
276
  projectName: 'test-project',
257
277
  projectId: 123,
278
+ latestBuild: { id: 'build-1', status: 'SUCCESS' },
279
+ deployedBuild: { id: 'build-1', status: 'SUCCESS' },
258
280
  targetProjectAccountId: 456,
259
281
  targetTestingAccountId: 789,
260
282
  },
@@ -264,6 +286,8 @@ describe('LocalDevWebsocketServer', () => {
264
286
  data: {
265
287
  projectName: 'test-project',
266
288
  projectId: 123,
289
+ latestBuild: { id: 'build-1', status: 'SUCCESS' },
290
+ deployedBuild: { id: 'build-1', status: 'SUCCESS' },
267
291
  targetProjectAccountId: 456,
268
292
  targetTestingAccountId: 789,
269
293
  },
@@ -275,7 +299,7 @@ describe('LocalDevWebsocketServer', () => {
275
299
  headers: { origin: 'https://app.hubspot.com' },
276
300
  });
277
301
  connectionCallback(mockWebSocket2, {
278
- headers: { origin: 'https://app.hubspotqa.com' },
302
+ headers: { origin: 'https://app-ap1.hubspotqa.com' },
279
303
  });
280
304
  // Get all the close callbacks for both connections (there should be 2 per connection)
281
305
  const closeCallbacks1 = mockWebSocket1.on.mock.calls
@@ -284,16 +308,16 @@ describe('LocalDevWebsocketServer', () => {
284
308
  const closeCallbacks2 = mockWebSocket2.on.mock.calls
285
309
  .filter(call => call[0] === 'close')
286
310
  .map(call => call[1]);
287
- expect(closeCallbacks1).toHaveLength(3); // projectNodes and appData listeners
288
- expect(closeCallbacks2).toHaveLength(3); // projectNodes and appData listeners
311
+ expect(closeCallbacks1).toHaveLength(4); // projectNodes, appData, devServersStarted, and uploadWarnings listeners
312
+ expect(closeCallbacks2).toHaveLength(4); // projectNodes, appData, devServersStarted, and uploadWarnings listeners
289
313
  // Simulate first connection closing (call all close callbacks)
290
314
  closeCallbacks1.forEach(callback => callback());
291
- // Should have removed listeners for first connection (2 listeners: projectNodes and appData)
292
- expect(mockLocalDevProcess.removeStateListener).toHaveBeenCalledTimes(3);
315
+ // Should have removed listeners for first connection (4 listeners: projectNodes, appData, devServersStarted, and uploadWarnings)
316
+ expect(mockLocalDevProcess.removeStateListener).toHaveBeenCalledTimes(4);
293
317
  // Simulate second connection closing
294
318
  closeCallbacks2.forEach(callback => callback());
295
319
  // Should have removed listeners for second connection as well
296
- expect(mockLocalDevProcess.removeStateListener).toHaveBeenCalledTimes(6);
320
+ expect(mockLocalDevProcess.removeStateListener).toHaveBeenCalledTimes(8);
297
321
  });
298
322
  it('should broadcast state changes to all connected clients', () => {
299
323
  // Establish connections
@@ -301,7 +325,7 @@ describe('LocalDevWebsocketServer', () => {
301
325
  headers: { origin: 'https://app.hubspot.com' },
302
326
  });
303
327
  connectionCallback(mockWebSocket2, {
304
- headers: { origin: 'https://app.hubspotqa.com' },
328
+ headers: { origin: 'https://local.hubspotqa.com' },
305
329
  });
306
330
  // Get the projectNodes listeners that were registered
307
331
  const projectNodesListeners = mockLocalDevProcess.addStateListener.mock.calls
@@ -317,6 +341,8 @@ describe('LocalDevWebsocketServer', () => {
317
341
  componentRoot: '/test/path',
318
342
  componentConfigPath: '/test/path/config.json',
319
343
  configUpdatedSinceLastUpload: false,
344
+ removed: false,
345
+ parsingErrors: [],
320
346
  },
321
347
  componentDeps: {},
322
348
  metaFilePath: '/test/path',