@hubspot/cli 7.8.12-experimental.0 → 7.8.12-experimental.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 (244) hide show
  1. package/bin/cli.js +31 -25
  2. package/commands/__tests__/auth.test.js +5 -0
  3. package/commands/__tests__/doctor.test.js +16 -16
  4. package/commands/account/clean.js +18 -27
  5. package/commands/account/createOverride.js +13 -31
  6. package/commands/account/info.js +20 -31
  7. package/commands/account/list.js +16 -22
  8. package/commands/account/remove.js +12 -20
  9. package/commands/account/removeOverride.js +11 -21
  10. package/commands/account/rename.js +6 -9
  11. package/commands/account/use.js +12 -26
  12. package/commands/account.js +2 -2
  13. package/commands/app/__tests__/migrate.test.js +5 -5
  14. package/commands/app/migrate.js +13 -18
  15. package/commands/app.js +1 -6
  16. package/commands/auth.d.ts +1 -0
  17. package/commands/auth.js +16 -7
  18. package/commands/cms/convertFields.js +7 -9
  19. package/commands/cms/getReactModule.js +9 -14
  20. package/commands/cms/lighthouseScore.js +33 -36
  21. package/commands/cms.js +2 -2
  22. package/commands/completion.js +3 -3
  23. package/commands/config/set.d.ts +1 -1
  24. package/commands/config/set.js +64 -36
  25. package/commands/config.js +2 -2
  26. package/commands/create.js +2 -2
  27. package/commands/customObject/create.js +10 -12
  28. package/commands/customObject/schema/create.js +9 -11
  29. package/commands/customObject/schema/delete.js +16 -16
  30. package/commands/customObject/schema/fetch-all.js +12 -11
  31. package/commands/customObject/schema/fetch.js +15 -15
  32. package/commands/customObject/schema/list.js +4 -4
  33. package/commands/customObject/schema/update.js +13 -13
  34. package/commands/customObject/schema.js +2 -2
  35. package/commands/customObject.js +6 -7
  36. package/commands/doctor.js +8 -11
  37. package/commands/feedback.js +6 -11
  38. package/commands/fetch.js +8 -8
  39. package/commands/filemanager/fetch.js +7 -7
  40. package/commands/filemanager/upload.js +15 -34
  41. package/commands/filemanager.js +2 -2
  42. package/commands/function/deploy.js +11 -29
  43. package/commands/function/list.js +8 -8
  44. package/commands/function/server.js +9 -11
  45. package/commands/function.d.ts +1 -1
  46. package/commands/function.js +2 -2
  47. package/commands/getStarted.js +2 -2
  48. package/commands/hubdb/clear.js +7 -15
  49. package/commands/hubdb/create.js +9 -15
  50. package/commands/hubdb/delete.js +8 -15
  51. package/commands/hubdb/fetch.js +6 -9
  52. package/commands/hubdb.d.ts +1 -1
  53. package/commands/hubdb.js +2 -2
  54. package/commands/init.js +2 -3
  55. package/commands/lint.js +16 -16
  56. package/commands/list.js +8 -14
  57. package/commands/logs.js +14 -20
  58. package/commands/mv.js +6 -17
  59. package/commands/open.js +5 -5
  60. package/commands/project/__tests__/add.test.js +4 -2
  61. package/commands/project/__tests__/deploy.test.js +3 -4
  62. package/commands/project/__tests__/installDeps.test.js +8 -8
  63. package/commands/project/__tests__/logs.test.js +1 -1
  64. package/commands/project/__tests__/migrate.test.js +5 -5
  65. package/commands/project/__tests__/migrateApp.test.js +2 -5
  66. package/commands/project/__tests__/validate.test.js +98 -0
  67. package/commands/project/add.js +3 -3
  68. package/commands/project/cloneApp.js +14 -19
  69. package/commands/project/create.js +0 -1
  70. package/commands/project/deploy.js +3 -3
  71. package/commands/project/dev/deprecatedFlow.js +7 -16
  72. package/commands/project/dev/index.js +14 -12
  73. package/commands/project/dev/unifiedFlow.js +3 -1
  74. package/commands/project/download.js +10 -13
  75. package/commands/project/installDeps.js +8 -8
  76. package/commands/project/listBuilds.js +11 -20
  77. package/commands/project/logs.js +21 -24
  78. package/commands/project/migrateApp.js +9 -15
  79. package/commands/project/open.js +6 -13
  80. package/commands/project/upload.d.ts +2 -2
  81. package/commands/project/upload.js +17 -26
  82. package/commands/project/validate.js +6 -6
  83. package/commands/project/watch.js +13 -22
  84. package/commands/project.js +2 -2
  85. package/commands/sandbox/__tests__/create.test.js +5 -5
  86. package/commands/sandbox/create.js +22 -32
  87. package/commands/sandbox/delete.js +38 -63
  88. package/commands/sandbox.js +2 -2
  89. package/commands/secret/addSecret.js +7 -17
  90. package/commands/secret/deleteSecret.js +10 -20
  91. package/commands/secret/listSecret.js +8 -10
  92. package/commands/secret/updateSecret.js +9 -17
  93. package/commands/secret.js +2 -2
  94. package/commands/testAccount/__tests__/delete.test.js +2 -4
  95. package/commands/testAccount/create.js +0 -3
  96. package/commands/testAccount/delete.d.ts +4 -3
  97. package/commands/testAccount/delete.js +155 -14
  98. package/commands/theme/preview.js +1 -4
  99. package/lang/en.d.ts +310 -124
  100. package/lang/en.js +351 -169
  101. package/lang/en.lyaml +2 -2
  102. package/lib/__tests__/buildAccount.test.js +2 -1
  103. package/lib/__tests__/commonOpts.test.js +1 -1
  104. package/lib/__tests__/dependencyManagement.test.js +1 -1
  105. package/lib/__tests__/developerTestAccounts.test.js +3 -3
  106. package/lib/__tests__/npm.test.js +1 -1
  107. package/lib/__tests__/oauth.test.js +4 -4
  108. package/lib/__tests__/process.test.js +10 -5
  109. package/lib/__tests__/sandboxSync.test.js +8 -8
  110. package/lib/__tests__/sandboxes.test.js +8 -8
  111. package/lib/__tests__/serverlessLogs.test.js +1 -1
  112. package/lib/__tests__/usageTracking.test.js +5 -5
  113. package/lib/__tests__/validation.test.js +2 -1
  114. package/lib/__tests__/yargsUtils.test.js +83 -9
  115. package/lib/app/__tests__/migrate.test.js +5 -5
  116. package/lib/app/__tests__/migrate_legacy.test.js +1 -1
  117. package/lib/app/migrate.js +1 -1
  118. package/lib/app/migrate_legacy.js +20 -24
  119. package/lib/buildAccount.js +25 -57
  120. package/lib/commonOpts.d.ts +1 -1
  121. package/lib/commonOpts.js +25 -22
  122. package/lib/configOptions.js +7 -0
  123. package/lib/constants.d.ts +6 -1
  124. package/lib/constants.js +10 -1
  125. package/lib/dependencyManagement.js +9 -27
  126. package/lib/developerTestAccounts.js +9 -23
  127. package/lib/doctor/Diagnosis.js +11 -23
  128. package/lib/doctor/DiagnosticInfoBuilder.js +12 -11
  129. package/lib/doctor/Doctor.js +42 -90
  130. package/lib/doctor/__tests__/Doctor.test.js +4 -4
  131. package/lib/errorHandlers/index.js +12 -20
  132. package/lib/errorHandlers/suppressError.js +11 -18
  133. package/lib/lang.js +6 -5
  134. package/lib/links.js +4 -4
  135. package/lib/middleware/__test__/commandTargetingUtils.test.js +99 -0
  136. package/lib/middleware/__test__/configMiddleware.test.js +11 -11
  137. package/lib/middleware/__test__/yargsChecksMiddleware.test.js +6 -8
  138. package/lib/middleware/commandTargetingUtils.d.ts +8 -0
  139. package/lib/middleware/commandTargetingUtils.js +78 -0
  140. package/lib/middleware/configMiddleware.d.ts +1 -1
  141. package/lib/middleware/configMiddleware.js +21 -81
  142. package/lib/middleware/gitMiddleware.js +5 -1
  143. package/lib/middleware/notificationsMiddleware.js +5 -11
  144. package/lib/middleware/yargsChecksMiddleware.js +6 -9
  145. package/lib/npm.js +2 -2
  146. package/lib/oauth.js +5 -5
  147. package/lib/process.js +5 -4
  148. package/lib/projectProfiles.d.ts +1 -1
  149. package/lib/projectProfiles.js +2 -10
  150. package/lib/projects/__tests__/AppDevModeInterface.test.js +14 -34
  151. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +70 -39
  152. package/lib/projects/__tests__/localDevProjectHelpers.test.js +2 -0
  153. package/lib/projects/__tests__/platformVersion.test.js +8 -8
  154. package/lib/projects/__tests__/projects.test.js +12 -12
  155. package/lib/projects/__tests__/structure.test.js +3 -3
  156. package/lib/projects/__tests__/upload.test.d.ts +1 -0
  157. package/lib/projects/__tests__/upload.test.js +82 -0
  158. package/lib/projects/add/__tests__/legacyAddComponent.test.js +6 -6
  159. package/lib/projects/add/__tests__/v3AddComponent.test.js +4 -4
  160. package/lib/projects/create/__tests__/legacy.test.js +5 -5
  161. package/lib/projects/create/__tests__/v3.test.js +1 -1
  162. package/lib/projects/create/index.js +2 -2
  163. package/lib/projects/create/legacy.js +2 -2
  164. package/lib/projects/create/v3.js +2 -2
  165. package/lib/projects/localDev/AppDevModeInterface.d.ts +2 -0
  166. package/lib/projects/localDev/AppDevModeInterface.js +22 -20
  167. package/lib/projects/localDev/LocalDevLogger.js +10 -11
  168. package/lib/projects/localDev/LocalDevManager.js +4 -5
  169. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +0 -1
  170. package/lib/projects/localDev/LocalDevWebsocketServer.js +7 -10
  171. package/lib/projects/localDev/helpers/project.d.ts +1 -0
  172. package/lib/projects/localDev/helpers/project.js +37 -0
  173. package/lib/projects/platformVersion.d.ts +1 -1
  174. package/lib/projects/platformVersion.js +1 -1
  175. package/lib/projects/structure.d.ts +2 -2
  176. package/lib/projects/structure.js +6 -6
  177. package/lib/projects/upload.d.ts +2 -3
  178. package/lib/projects/upload.js +17 -9
  179. package/lib/prompts/__tests__/downloadProjectPrompt.test.js +1 -0
  180. package/lib/prompts/accountNamePrompt.js +14 -19
  181. package/lib/prompts/accountsPrompt.js +2 -2
  182. package/lib/prompts/cmsFieldPrompt.js +2 -2
  183. package/lib/prompts/createApiSamplePrompt.js +5 -5
  184. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +10 -1
  185. package/lib/prompts/createFunctionPrompt.js +14 -14
  186. package/lib/prompts/createModulePrompt.js +9 -9
  187. package/lib/prompts/createTemplatePrompt.js +2 -2
  188. package/lib/prompts/downloadProjectPrompt.js +5 -8
  189. package/lib/prompts/personalAccessKeyPrompt.js +3 -3
  190. package/lib/prompts/previewPrompt.js +6 -6
  191. package/lib/prompts/projectAddPrompt.js +6 -0
  192. package/lib/prompts/projectDevTargetAccountPrompt.js +20 -32
  193. package/lib/prompts/projectNamePrompt.js +4 -8
  194. package/lib/prompts/projectsLogsPrompt.js +2 -4
  195. package/lib/prompts/promptUtils.js +27 -9
  196. package/lib/prompts/sandboxesPrompt.js +7 -7
  197. package/lib/prompts/secretPrompt.js +3 -3
  198. package/lib/prompts/selectAppPrompt.js +3 -3
  199. package/lib/prompts/selectHubDBTablePrompt.js +9 -13
  200. package/lib/prompts/selectPublicAppForMigrationPrompt.js +15 -19
  201. package/lib/prompts/setAsDefaultAccountPrompt.js +4 -8
  202. package/lib/prompts/uploadPrompt.js +5 -5
  203. package/lib/sandboxSync.js +24 -41
  204. package/lib/sandboxes.js +19 -47
  205. package/lib/schema.js +3 -3
  206. package/lib/serverlessLogs.js +11 -13
  207. package/lib/theme/__tests__/migrate.test.js +3 -3
  208. package/lib/theme/migrate.js +2 -2
  209. package/lib/ui/SpinniesManager.d.ts +2 -0
  210. package/lib/ui/SpinniesManager.js +7 -0
  211. package/lib/ui/boxen.js +1 -2
  212. package/lib/ui/git.js +13 -10
  213. package/lib/ui/index.d.ts +4 -0
  214. package/lib/ui/index.js +47 -38
  215. package/lib/ui/serverlessFunctionLogs.js +9 -7
  216. package/lib/ui/uiMessages.d.ts +68 -0
  217. package/lib/ui/uiMessages.js +71 -0
  218. package/lib/usageTracking.js +7 -7
  219. package/lib/validation.js +20 -23
  220. package/lib/yargsUtils.d.ts +1 -1
  221. package/lib/yargsUtils.js +12 -5
  222. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  223. package/mcp-server/tools/index.js +4 -0
  224. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +23 -0
  225. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +68 -0
  226. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +11 -0
  227. package/mcp-server/tools/project/GetApplicationInfoTool.js +49 -0
  228. package/mcp-server/tools/project/GetConfigValuesTool.js +2 -2
  229. package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +2 -2
  230. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.d.ts +1 -0
  231. package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +169 -0
  232. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.d.ts +1 -0
  233. package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +115 -0
  234. package/mcp-server/utils/toolUsageTracking.js +2 -2
  235. package/package.json +6 -6
  236. package/types/Yargs.d.ts +1 -1
  237. package/commands/app/__tests__/install.test.js +0 -47
  238. package/commands/app/install.d.ts +0 -8
  239. package/commands/app/install.js +0 -122
  240. package/lib/middleware/__test__/utils.test.js +0 -51
  241. package/lib/middleware/utils.d.ts +0 -8
  242. package/lib/middleware/utils.js +0 -14
  243. /package/commands/{app/__tests__/install.test.d.ts → project/__tests__/validate.test.d.ts} +0 -0
  244. /package/lib/middleware/__test__/{utils.test.d.ts → commandTargetingUtils.test.d.ts} +0 -0
@@ -11,7 +11,7 @@ vi.mock('@hubspot/ui-extensions-dev-server', () => {
11
11
  };
12
12
  });
13
13
  import { fetchAppInstallationData } from '@hubspot/local-dev-lib/api/localDevAuth';
14
- import { fetchPublicAppsForPortal, fetchPublicAppProductionInstallCounts, installStaticAuthAppOnTestAccount, } from '@hubspot/local-dev-lib/api/appsDev';
14
+ import { fetchAppMetadataByUid, fetchPublicAppProductionInstallCounts, installStaticAuthAppOnTestAccount, } from '@hubspot/local-dev-lib/api/appsDev';
15
15
  import { DevModeUnifiedInterface as UIEDevModeInterface } from '@hubspot/ui-extensions-dev-server';
16
16
  import { requestPorts } from '@hubspot/local-dev-lib/portManager';
17
17
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
@@ -67,6 +67,8 @@ describe('AppDevModeInterface', () => {
67
67
  componentRoot: '/test/path',
68
68
  componentConfigPath: '/test/path/config.json',
69
69
  configUpdatedSinceLastUpload: false,
70
+ removed: false,
71
+ parsingErrors: [],
70
72
  },
71
73
  componentDeps: {},
72
74
  metaFilePath: '/test/path',
@@ -107,8 +109,8 @@ describe('AppDevModeInterface', () => {
107
109
  LocalDevState.mockImplementation(() => mockLocalDevState);
108
110
  LocalDevLogger.mockImplementation(() => mockLocalDevLogger);
109
111
  // Mock external dependencies
110
- fetchPublicAppsForPortal.mockResolvedValue({
111
- data: { results: [mockPublicApp] },
112
+ fetchAppMetadataByUid.mockResolvedValue({
113
+ data: mockPublicApp,
112
114
  });
113
115
  fetchPublicAppProductionInstallCounts.mockResolvedValue({
114
116
  data: { uniquePortalInstallCount: 5 },
@@ -189,12 +191,12 @@ describe('AppDevModeInterface', () => {
189
191
  it('should return early if no app node exists', async () => {
190
192
  mockLocalDevState.projectNodes = {};
191
193
  await appDevModeInterface.setup({});
192
- expect(fetchPublicAppsForPortal).not.toHaveBeenCalled();
194
+ expect(fetchAppMetadataByUid).not.toHaveBeenCalled();
193
195
  expect(UIEDevModeInterface.setup).not.toHaveBeenCalled();
194
196
  });
195
197
  it('should setup successfully with private app', async () => {
196
198
  await appDevModeInterface.setup({});
197
- expect(fetchPublicAppsForPortal).toHaveBeenCalledWith(12345);
199
+ expect(fetchAppMetadataByUid).toHaveBeenCalledWith('test-app-uid', 12345);
198
200
  expect(fetchPublicAppProductionInstallCounts).toHaveBeenCalledWith(123, 12345);
199
201
  expect(fetchAppInstallationData).toHaveBeenCalledWith(67890, 999, 'test-app-uid', ['test-scope'], []);
200
202
  expect(UIEDevModeInterface.setup).toHaveBeenCalled();
@@ -302,32 +304,10 @@ describe('AppDevModeInterface', () => {
302
304
  });
303
305
  it('should handle errors during setup', async () => {
304
306
  const error = new Error('Setup failed');
305
- fetchPublicAppsForPortal.mockRejectedValue(error);
307
+ fetchAppMetadataByUid.mockRejectedValue(error);
306
308
  await appDevModeInterface.setup({});
307
309
  expect(logError).toHaveBeenCalledWith(error);
308
310
  });
309
- it('should exit if app not found in portal', async () => {
310
- // Set up conditions for non-automatic installation to force getAppInstallUrl call
311
- getAccountConfig.mockReturnValue(null);
312
- // First call for fetchAppData succeeds
313
- fetchPublicAppsForPortal
314
- .mockResolvedValueOnce({
315
- data: { results: [mockPublicApp] },
316
- })
317
- // Second call for getAppInstallUrl fails (app not found)
318
- .mockResolvedValueOnce({
319
- data: { results: [] },
320
- });
321
- fetchAppInstallationData.mockResolvedValue({
322
- data: {
323
- isInstalledWithScopeGroups: false,
324
- previouslyAuthorizedScopeGroups: [],
325
- },
326
- });
327
- // The setup method catches the error, so we check that process.exit was called
328
- await appDevModeInterface.setup({});
329
- expect(process.exit).toHaveBeenCalledWith(1);
330
- });
331
311
  // @TODO: Restore test account auto install functionality
332
312
  // it('should exit if user declines auto-install', async () => {
333
313
  // // Set up conditions for automatic installation
@@ -424,8 +404,8 @@ describe('AppDevModeInterface', () => {
424
404
  // Reset mocks to ensure clean state
425
405
  vi.clearAllMocks();
426
406
  // Set up basic mocks
427
- fetchPublicAppsForPortal.mockResolvedValue({
428
- data: { results: [mockPublicApp] },
407
+ fetchAppMetadataByUid.mockResolvedValue({
408
+ data: mockPublicApp,
429
409
  });
430
410
  fetchPublicAppProductionInstallCounts.mockResolvedValue({
431
411
  data: { uniquePortalInstallCount: 5 },
@@ -457,8 +437,8 @@ describe('AppDevModeInterface', () => {
457
437
  // Reset mocks to ensure clean state
458
438
  vi.clearAllMocks();
459
439
  // Set up basic mocks
460
- fetchPublicAppsForPortal.mockResolvedValue({
461
- data: { results: [mockPublicApp] },
440
+ fetchAppMetadataByUid.mockResolvedValue({
441
+ data: mockPublicApp,
462
442
  });
463
443
  fetchPublicAppProductionInstallCounts.mockResolvedValue({
464
444
  data: { uniquePortalInstallCount: 5 },
@@ -501,8 +481,8 @@ describe('AppDevModeInterface', () => {
501
481
  // Reset mocks to ensure clean state
502
482
  vi.clearAllMocks();
503
483
  // Set up basic mocks
504
- fetchPublicAppsForPortal.mockResolvedValue({
505
- data: { results: [mockPublicApp] },
484
+ fetchAppMetadataByUid.mockResolvedValue({
485
+ data: mockPublicApp,
506
486
  });
507
487
  fetchPublicAppProductionInstallCounts.mockResolvedValue({
508
488
  data: { uniquePortalInstallCount: 5 },
@@ -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;
@@ -59,39 +59,68 @@ describe('LocalDevWebsocketServer', () => {
59
59
  await server.start();
60
60
  expect(WebSocketServer).toHaveBeenCalledWith({ port: 1234 });
61
61
  expect(mockWebSocketServer.on).toHaveBeenCalledWith('connection', expect.any(Function));
62
- expect(logger.log).toHaveBeenCalled();
62
+ expect(uiLogger.log).toHaveBeenCalled();
63
63
  });
64
- it('should accept connection from valid origin', async () => {
65
- isPortManagerServerRunning.mockResolvedValue(true);
66
- requestPorts.mockResolvedValue({
67
- '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
+ });
68
97
  });
69
- await server.start();
70
- // Get the connection callback
71
- const connectionCallback = mockWebSocketServer.on.mock.calls[0][1];
72
- // Simulate connection from valid origin
73
- connectionCallback(mockWebSocket, {
74
- headers: { origin: 'https://app.hubspot.com' },
75
- });
76
- expect(mockWebSocket.on).toHaveBeenCalledWith('message', expect.any(Function));
77
- expect(mockLocalDevProcess.addStateListener).toHaveBeenCalledWith('projectNodes', expect.any(Function));
78
- expect(mockWebSocket.close).not.toHaveBeenCalled();
79
98
  });
80
- it('should reject connection from invalid 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.calls[0][1];
88
- // Simulate connection from invalid origin
89
- connectionCallback(mockWebSocket, {
90
- headers: { origin: 'https://malicious-site.com' },
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
+ });
91
123
  });
92
- expect(mockWebSocket.close).toHaveBeenCalledWith(1008, lib.LocalDevWebsocketServer.errors.originNotAllowed('https://malicious-site.com'));
93
- expect(mockWebSocket.on).not.toHaveBeenCalled();
94
- expect(mockLocalDevProcess.addStateListener).not.toHaveBeenCalled();
95
124
  });
96
125
  it('should reject connection with no origin header', async () => {
97
126
  isPortManagerServerRunning.mockResolvedValue(true);
@@ -119,7 +148,7 @@ describe('LocalDevWebsocketServer', () => {
119
148
  const connectionCallback = mockWebSocketServer.on.mock.calls[0][1];
120
149
  // Simulate connection from valid origin
121
150
  connectionCallback(mockWebSocket, {
122
- headers: { origin: 'https://app.hubspot.com' },
151
+ headers: { origin: 'https://app-na3.hubspot.com' },
123
152
  });
124
153
  expect(mockLocalDevProcess.sendDevServerMessage).toHaveBeenCalledWith(LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED);
125
154
  });
@@ -148,7 +177,7 @@ describe('LocalDevWebsocketServer', () => {
148
177
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
149
178
  const message = {};
150
179
  messageCallback(JSON.stringify(message));
151
- expect(logger.error).toHaveBeenCalled();
180
+ expect(uiLogger.error).toHaveBeenCalled();
152
181
  });
153
182
  it('should log error for unknown message type', () => {
154
183
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
@@ -156,13 +185,13 @@ describe('LocalDevWebsocketServer', () => {
156
185
  type: 'UNKNOWN_TYPE',
157
186
  };
158
187
  messageCallback(JSON.stringify(message));
159
- expect(logger.error).toHaveBeenCalled();
188
+ expect(uiLogger.error).toHaveBeenCalled();
160
189
  });
161
190
  it('should log error for invalid JSON', () => {
162
191
  const messageCallback = mockWebSocket.on.mock.calls[0][1];
163
192
  const invalidJson = 'invalid json';
164
193
  messageCallback(invalidJson);
165
- expect(logger.error).toHaveBeenCalled();
194
+ expect(uiLogger.error).toHaveBeenCalled();
166
195
  });
167
196
  });
168
197
  describe('shutdown()', () => {
@@ -213,7 +242,7 @@ describe('LocalDevWebsocketServer', () => {
213
242
  headers: { origin: 'https://app.hubspot.com' },
214
243
  });
215
244
  connectionCallback(mockWebSocket2, {
216
- headers: { origin: 'https://app.hubspotqa.com' },
245
+ headers: { origin: 'https://app-na2.hubspotqa.com' },
217
246
  });
218
247
  connectionCallback(mockWebSocket3, {
219
248
  headers: { origin: 'https://local.hubspot.com' },
@@ -238,7 +267,7 @@ describe('LocalDevWebsocketServer', () => {
238
267
  headers: { origin: 'https://app.hubspot.com' },
239
268
  });
240
269
  connectionCallback(mockWebSocket2, {
241
- headers: { origin: 'https://app.hubspotqa.com' },
270
+ headers: { origin: 'https://app-eu1.hubspotqa.com' },
242
271
  });
243
272
  // Each websocket should receive project data
244
273
  expect(mockWebSocket1.send).toHaveBeenCalledWith(JSON.stringify({
@@ -270,7 +299,7 @@ describe('LocalDevWebsocketServer', () => {
270
299
  headers: { origin: 'https://app.hubspot.com' },
271
300
  });
272
301
  connectionCallback(mockWebSocket2, {
273
- headers: { origin: 'https://app.hubspotqa.com' },
302
+ headers: { origin: 'https://app-ap1.hubspotqa.com' },
274
303
  });
275
304
  // Get all the close callbacks for both connections (there should be 2 per connection)
276
305
  const closeCallbacks1 = mockWebSocket1.on.mock.calls
@@ -296,7 +325,7 @@ describe('LocalDevWebsocketServer', () => {
296
325
  headers: { origin: 'https://app.hubspot.com' },
297
326
  });
298
327
  connectionCallback(mockWebSocket2, {
299
- headers: { origin: 'https://app.hubspotqa.com' },
328
+ headers: { origin: 'https://local.hubspotqa.com' },
300
329
  });
301
330
  // Get the projectNodes listeners that were registered
302
331
  const projectNodesListeners = mockLocalDevProcess.addStateListener.mock.calls
@@ -312,6 +341,8 @@ describe('LocalDevWebsocketServer', () => {
312
341
  componentRoot: '/test/path',
313
342
  componentConfigPath: '/test/path/config.json',
314
343
  configUpdatedSinceLastUpload: false,
344
+ removed: false,
345
+ parsingErrors: [],
315
346
  },
316
347
  componentDeps: {},
317
348
  metaFilePath: '/test/path',
@@ -30,6 +30,8 @@ describe('isDeployedProjectUpToDateWithLocal', () => {
30
30
  componentRoot: '/local/path',
31
31
  componentConfigPath: '/local/path/config.json',
32
32
  configUpdatedSinceLastUpload: false,
33
+ removed: false,
34
+ parsingErrors: [],
33
35
  },
34
36
  componentDeps: {},
35
37
  metaFilePath: '/local/path',
@@ -1,23 +1,23 @@
1
- import { useV3Api } from '../platformVersion.js';
1
+ import { isV2Project } from '../platformVersion.js';
2
2
  describe('platformVersion', () => {
3
- describe('useV3Api', () => {
3
+ describe('isV2Project', () => {
4
4
  it('returns true if platform version is UNSTABLE', () => {
5
- expect(useV3Api('UNSTABLE')).toBe(true);
5
+ expect(isV2Project('UNSTABLE')).toBe(true);
6
6
  });
7
7
  it('returns true if platform version is equal to the minimum', () => {
8
- expect(useV3Api('2025.2')).toBe(true);
8
+ expect(isV2Project('2025.2')).toBe(true);
9
9
  });
10
10
  it('returns true if platform version is greater than the minimum', () => {
11
- expect(useV3Api('2026.2')).toBe(true);
11
+ expect(isV2Project('2026.2')).toBe(true);
12
12
  });
13
13
  it('returns false if platform version is less than the minimum', () => {
14
- expect(useV3Api('2025.0')).toBe(false);
14
+ expect(isV2Project('2025.0')).toBe(false);
15
15
  });
16
16
  it('returns false if platform version is invalid', () => {
17
- expect(useV3Api(null)).toBe(false);
17
+ expect(isV2Project(null)).toBe(false);
18
18
  });
19
19
  it('returns false for an invalid platform version', () => {
20
- expect(useV3Api('notplaformversion')).toBe(false);
20
+ expect(isV2Project('notplaformversion')).toBe(false);
21
21
  });
22
22
  });
23
23
  });
@@ -3,8 +3,8 @@ import os from 'os';
3
3
  import path from 'path';
4
4
  import { EXIT_CODES } from '../../enums/exitCodes.js';
5
5
  import { validateProjectConfig } from '../../projects/config.js';
6
- import { logger } from '@hubspot/local-dev-lib/logger';
7
- vi.mock('@hubspot/local-dev-lib/logger');
6
+ import { uiLogger } from '../../ui/logger.js';
7
+ vi.mock('../../ui/logger.js');
8
8
  describe('lib/projects', () => {
9
9
  describe('validateProjectConfig()', () => {
10
10
  let projectDir;
@@ -26,58 +26,58 @@ describe('lib/projects', () => {
26
26
  // @ts-ignore Testing invalid input
27
27
  validateProjectConfig(null, projectDir);
28
28
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
29
- expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/.*Unable to locate a project configuration file. Try running again from a project directory, or run*/));
29
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*Unable to locate a project configuration file. Try running again from a project directory, or run*/));
30
30
  });
31
31
  it('rejects configuration with missing name', () => {
32
32
  // @ts-ignore Testing invalid input
33
33
  validateProjectConfig({ srcDir: '.' }, projectDir);
34
34
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
35
- expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields*/));
35
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields*/));
36
36
  });
37
37
  it('rejects configuration with missing srcDir', () => {
38
38
  // @ts-ignore Testing invalid input
39
39
  validateProjectConfig({ name: 'hello' }, projectDir);
40
40
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
41
- expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields.*/));
41
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields.*/));
42
42
  });
43
43
  describe('rejects configuration with srcDir outside project directory', () => {
44
44
  it('for parent directory', () => {
45
45
  validateProjectConfig({ name: 'hello', srcDir: '..', platformVersion: '' }, projectDir);
46
46
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
47
- expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: ".."'));
47
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: ".."'));
48
48
  });
49
49
  it('for root directory', () => {
50
50
  validateProjectConfig({ name: 'hello', srcDir: '/', platformVersion: '' }, projectDir);
51
51
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
52
- expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: "/"'));
52
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: "/"'));
53
53
  });
54
54
  it('for complicated directory', () => {
55
55
  const srcDir = './src/././../src/../../src';
56
56
  validateProjectConfig({ name: 'hello', srcDir, platformVersion: '' }, projectDir);
57
57
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
58
- expect(logger.error).toHaveBeenCalledWith(expect.stringContaining(`srcDir: "${srcDir}"`));
58
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining(`srcDir: "${srcDir}"`));
59
59
  });
60
60
  });
61
61
  it('rejects configuration with srcDir that does not exist', () => {
62
62
  validateProjectConfig({ name: 'hello', srcDir: 'foo', platformVersion: '' }, projectDir);
63
63
  expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
64
- expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/.*could not be found in.*/));
64
+ expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*could not be found in.*/));
65
65
  });
66
66
  describe('accepts configuration with valid srcDir', () => {
67
67
  it('for current directory', () => {
68
68
  validateProjectConfig({ name: 'hello', srcDir: '.', platformVersion: '' }, projectDir);
69
69
  expect(exitMock).not.toHaveBeenCalled();
70
- expect(logger.error).not.toHaveBeenCalled();
70
+ expect(uiLogger.error).not.toHaveBeenCalled();
71
71
  });
72
72
  it('for relative directory', () => {
73
73
  validateProjectConfig({ name: 'hello', srcDir: './src', platformVersion: '' }, projectDir);
74
74
  expect(exitMock).not.toHaveBeenCalled();
75
- expect(logger.error).not.toHaveBeenCalled();
75
+ expect(uiLogger.error).not.toHaveBeenCalled();
76
76
  });
77
77
  it('for implied relative directory', () => {
78
78
  validateProjectConfig({ name: 'hello', srcDir: 'src', platformVersion: '' }, projectDir);
79
79
  expect(exitMock).not.toHaveBeenCalled();
80
- expect(logger.error).not.toHaveBeenCalled();
80
+ expect(uiLogger.error).not.toHaveBeenCalled();
81
81
  });
82
82
  });
83
83
  });
@@ -1,11 +1,11 @@
1
1
  import fs from 'fs';
2
2
  import * as HSfs from '@hubspot/local-dev-lib/fs';
3
- import { logger } from '@hubspot/local-dev-lib/logger';
3
+ import { uiLogger } from '../../ui/logger.js';
4
4
  import { getComponentTypeFromConfigFile, loadConfigFile, getAppCardConfigs, getIsLegacyApp, componentIsApp, findProjectComponents, getProjectComponentTypes, getComponentUid, componentIsPublicApp, } from '../structure.js';
5
5
  import { ComponentTypes } from '../../../types/Projects.js';
6
6
  vi.mock('fs');
7
7
  vi.mock('@hubspot/local-dev-lib/fs');
8
- vi.mock('@hubspot/local-dev-lib/logger');
8
+ vi.mock('../../ui/logger.js');
9
9
  const mockedReadFileSync = fs.readFileSync;
10
10
  const mockedWalk = HSfs.walk;
11
11
  const getMockPrivateAppConfig = (cards = []) => ({
@@ -46,7 +46,7 @@ describe('lib/projects/structure', () => {
46
46
  throw new Error('File not found');
47
47
  });
48
48
  expect(loadConfigFile('nonexistent/path/app.json')).toBeNull();
49
- expect(logger.debug).toHaveBeenCalled();
49
+ expect(uiLogger.debug).toHaveBeenCalled();
50
50
  });
51
51
  });
52
52
  describe('getAppCardConfigs()', () => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import fs from 'fs-extra';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { vi } from 'vitest';
5
+ import { validateSourceDirectory } from '../upload.js';
6
+ import { uiLogger } from '../../ui/logger.js';
7
+ import { lib } from '../../../lang/en.js';
8
+ import { isV2Project } from '../platformVersion.js';
9
+ import ProjectValidationError from '../../errors/ProjectValidationError.js';
10
+ import { walk } from '@hubspot/local-dev-lib/fs';
11
+ // Mock dependencies
12
+ vi.mock('../../ui/logger.js');
13
+ vi.mock('../platformVersion.js');
14
+ vi.mock('@hubspot/local-dev-lib/fs');
15
+ describe('lib/projects/upload', () => {
16
+ describe('validateSourceDirectory', () => {
17
+ let tempDir;
18
+ let srcDir;
19
+ let projectConfig;
20
+ beforeEach(() => {
21
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'upload-test-'));
22
+ srcDir = path.join(tempDir, 'src');
23
+ fs.mkdirSync(srcDir, { recursive: true });
24
+ projectConfig = {
25
+ name: 'test-project',
26
+ srcDir: 'src',
27
+ platformVersion: '2025.2',
28
+ };
29
+ vi.clearAllMocks();
30
+ });
31
+ afterEach(() => {
32
+ fs.removeSync(tempDir);
33
+ });
34
+ it('should throw ProjectValidationError when source directory is empty', async () => {
35
+ vi.mocked(walk).mockResolvedValue([]);
36
+ await expect(validateSourceDirectory(srcDir, projectConfig, tempDir)).rejects.toThrow(ProjectValidationError);
37
+ expect(walk).toHaveBeenCalledWith(srcDir, ['node_modules']);
38
+ });
39
+ it('should warn about legacy files in V3 projects', async () => {
40
+ vi.mocked(isV2Project).mockReturnValue(true);
41
+ const legacyFilePath = path.join(srcDir, 'app', 'serverless.json');
42
+ vi.mocked(walk).mockResolvedValue([legacyFilePath]);
43
+ await validateSourceDirectory(srcDir, projectConfig, tempDir);
44
+ expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app/serverless.json', '2025.2'));
45
+ });
46
+ it('should warn about multiple legacy files', async () => {
47
+ vi.mocked(isV2Project).mockReturnValue(true);
48
+ const filePaths = [
49
+ path.join(srcDir, 'app1', 'serverless.json'),
50
+ path.join(srcDir, 'app2', 'app.json'),
51
+ path.join(srcDir, 'app3', 'public-app.json'),
52
+ ];
53
+ vi.mocked(walk).mockResolvedValue(filePaths);
54
+ await validateSourceDirectory(srcDir, projectConfig, tempDir);
55
+ expect(uiLogger.warn).toHaveBeenCalledTimes(3);
56
+ expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app1/serverless.json', '2025.2'));
57
+ expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app2/app.json', '2025.2'));
58
+ expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app3/public-app.json', '2025.2'));
59
+ });
60
+ it('should not warn about non-legacy files', async () => {
61
+ vi.mocked(isV2Project).mockReturnValue(true);
62
+ const filePaths = [
63
+ path.join(srcDir, 'component.js'),
64
+ path.join(srcDir, 'config.json'),
65
+ ];
66
+ vi.mocked(walk).mockResolvedValue(filePaths);
67
+ await validateSourceDirectory(srcDir, projectConfig, tempDir);
68
+ expect(uiLogger.warn).not.toHaveBeenCalled();
69
+ });
70
+ it('should not warn about legacy files in non-V3 projects', async () => {
71
+ vi.mocked(isV2Project).mockReturnValue(false);
72
+ projectConfig.platformVersion = '2025.1';
73
+ const filePaths = [
74
+ path.join(srcDir, 'app', 'serverless.json'),
75
+ path.join(srcDir, 'app', 'app.json'),
76
+ ];
77
+ vi.mocked(walk).mockResolvedValue(filePaths);
78
+ await validateSourceDirectory(srcDir, projectConfig, tempDir);
79
+ expect(uiLogger.warn).not.toHaveBeenCalled();
80
+ });
81
+ });
82
+ });
@@ -2,7 +2,7 @@ import { legacyAddComponent } from '../legacyAddComponent.js';
2
2
  import { findProjectComponents } from '../../structure.js';
3
3
  import { getProjectComponentListFromRepo } from '../../create/legacy.js';
4
4
  import { projectAddPrompt } from '../../../prompts/projectAddPrompt.js';
5
- import { logger } from '@hubspot/local-dev-lib/logger';
5
+ import { uiLogger } from '../../../ui/logger.js';
6
6
  import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
7
7
  import { trackCommandUsage } from '../../../usageTracking.js';
8
8
  import { ComponentTypes, } from '../../../../types/Projects.js';
@@ -10,13 +10,13 @@ import { commands } from '../../../../lang/en.js';
10
10
  vi.mock('../../structure');
11
11
  vi.mock('../../create/legacy');
12
12
  vi.mock('../../../prompts/projectAddPrompt');
13
- vi.mock('@hubspot/local-dev-lib/logger');
13
+ vi.mock('../../../ui/logger.js');
14
14
  vi.mock('@hubspot/local-dev-lib/github');
15
15
  vi.mock('../../../usageTracking.js');
16
16
  const mockedFindProjectComponents = vi.mocked(findProjectComponents);
17
17
  const mockedGetProjectComponentListFromRepo = vi.mocked(getProjectComponentListFromRepo);
18
18
  const mockedProjectAddPrompt = vi.mocked(projectAddPrompt);
19
- const mockedLogger = vi.mocked(logger);
19
+ const mockedUiLogger = vi.mocked(uiLogger);
20
20
  const mockedCloneGithubRepo = vi.mocked(cloneGithubRepo);
21
21
  const mockedTrackCommandUsage = vi.mocked(trackCommandUsage);
22
22
  describe('lib/projects/add/legacyAddComponent', () => {
@@ -75,8 +75,8 @@ describe('lib/projects/add/legacyAddComponent', () => {
75
75
  expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
76
76
  type: 'module',
77
77
  }, accountId);
78
- expect(mockedLogger.log).toHaveBeenCalledWith(commands.project.add.creatingComponent('test-project'));
79
- expect(mockedLogger.success).toHaveBeenCalledWith(commands.project.add.success('new-component'));
78
+ expect(mockedUiLogger.log).toHaveBeenCalledWith(commands.project.add.creatingComponent('test-project'));
79
+ expect(mockedUiLogger.success).toHaveBeenCalledWith(commands.project.add.success('new-component'));
80
80
  });
81
81
  it('throws an error when project contains a public app', async () => {
82
82
  const mockComponents = [
@@ -206,7 +206,7 @@ describe('lib/projects/add/legacyAddComponent', () => {
206
206
  mockedCloneGithubRepo.mockRejectedValue(new Error('Clone failed'));
207
207
  await expect(legacyAddComponent(mockArgs, projectDir, mockProjectConfig, accountId)).rejects.toThrow(commands.project.add.error.failedToDownloadComponent);
208
208
  expect(mockedCloneGithubRepo).toHaveBeenCalled();
209
- expect(mockedLogger.success).not.toHaveBeenCalled();
209
+ expect(mockedUiLogger.success).not.toHaveBeenCalled();
210
210
  });
211
211
  it('calls trackCommandUsage with correct component type', async () => {
212
212
  const mockComponents = [
@@ -5,7 +5,7 @@ import { createV3App } from '../../create/v3.js';
5
5
  import { confirmPrompt } from '../../../prompts/promptUtils.js';
6
6
  import { projectAddPromptV3 } from '../../../prompts/projectAddPrompt.js';
7
7
  import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
8
- import { logger } from '@hubspot/local-dev-lib/logger';
8
+ import { uiLogger } from '../../../ui/logger.js';
9
9
  import { getProjectMetadata } from '@hubspot/project-parsing-lib/src/lib/project.js';
10
10
  import { trackCommandUsage } from '../../../usageTracking.js';
11
11
  import { commands } from '../../../../lang/en.js';
@@ -15,7 +15,7 @@ vi.mock('../../create/legacy');
15
15
  vi.mock('../../create/v3');
16
16
  vi.mock('../../../prompts/projectAddPrompt');
17
17
  vi.mock('@hubspot/local-dev-lib/github');
18
- vi.mock('@hubspot/local-dev-lib/logger');
18
+ vi.mock('../../../ui/logger.js');
19
19
  vi.mock('@hubspot/project-parsing-lib/src/lib/project');
20
20
  vi.mock('../../../usageTracking');
21
21
  const mockedFs = vi.mocked(fs);
@@ -24,7 +24,7 @@ const mockedConfirmPrompt = vi.mocked(confirmPrompt);
24
24
  const mockedCreateV3App = vi.mocked(createV3App);
25
25
  const mockedProjectAddPromptV3 = vi.mocked(projectAddPromptV3);
26
26
  const mockedCloneGithubRepo = vi.mocked(cloneGithubRepo);
27
- const mockedLogger = vi.mocked(logger);
27
+ const mockedUiLogger = vi.mocked(uiLogger);
28
28
  const mockedGetProjectMetadata = vi.mocked(getProjectMetadata);
29
29
  const mockedTrackCommandUsage = vi.mocked(trackCommandUsage);
30
30
  describe('lib/projects/add/v3AddComponent', () => {
@@ -100,7 +100,7 @@ describe('lib/projects/add/v3AddComponent', () => {
100
100
  hideLogs: true,
101
101
  branch: 'main',
102
102
  }));
103
- expect(mockedLogger.success).toHaveBeenCalled();
103
+ expect(mockedUiLogger.success).toHaveBeenCalled();
104
104
  });
105
105
  it('creates an app when no app exists and user confirms', async () => {
106
106
  const mockProjectMetadataNoApps = {