@hubspot/cli 7.6.0-beta.9 → 7.6.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 (217) hide show
  1. package/api/__tests__/migrate.test.js +5 -5
  2. package/api/migrate.d.ts +4 -5
  3. package/api/migrate.js +2 -10
  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 +0 -3
  7. package/commands/app/__tests__/migrate.test.js +1 -1
  8. package/commands/app/migrate.js +4 -5
  9. package/commands/app/secret/add.js +2 -1
  10. package/commands/app/secret/delete.js +2 -1
  11. package/commands/app/secret/list.js +2 -1
  12. package/commands/app/secret/update.js +2 -1
  13. package/commands/app/secret.js +2 -1
  14. package/commands/app.js +2 -2
  15. package/commands/config/set.js +0 -1
  16. package/commands/getStarted.d.ts +0 -2
  17. package/commands/getStarted.js +72 -24
  18. package/commands/mcp/__tests__/setup.test.js +2 -2
  19. package/commands/mcp/setup.d.ts +0 -1
  20. package/commands/mcp/setup.js +14 -13
  21. package/commands/mcp.js +3 -3
  22. package/commands/project/__tests__/add.test.js +64 -0
  23. package/commands/project/__tests__/create.test.js +57 -0
  24. package/commands/project/__tests__/deploy.test.js +3 -5
  25. package/commands/project/__tests__/devUnifiedFlow.test.js +20 -11
  26. package/commands/project/__tests__/logs.test.js +0 -3
  27. package/commands/project/__tests__/migrate.test.js +1 -2
  28. package/commands/project/__tests__/migrateApp.test.js +1 -2
  29. package/commands/project/__tests__/profile.test.js +1 -1
  30. package/commands/project/add.d.ts +1 -1
  31. package/commands/project/add.js +4 -10
  32. package/commands/project/create.js +10 -11
  33. package/commands/project/deploy.js +11 -63
  34. package/commands/project/dev/deprecatedFlow.js +2 -1
  35. package/commands/project/dev/index.js +36 -15
  36. package/commands/project/dev/unifiedFlow.js +14 -10
  37. package/commands/project/download.js +1 -2
  38. package/commands/project/installDeps.js +1 -2
  39. package/commands/project/listBuilds.js +2 -2
  40. package/commands/project/logs.js +2 -2
  41. package/commands/project/migrate.js +41 -13
  42. package/commands/project/migrateApp.js +1 -2
  43. package/commands/project/open.js +1 -2
  44. package/commands/project/profile/add.js +3 -3
  45. package/commands/project/profile/delete.js +1 -2
  46. package/commands/project/profile.js +2 -3
  47. package/commands/project/upload.js +4 -4
  48. package/commands/project/validate.js +2 -2
  49. package/commands/project/watch.js +4 -4
  50. package/commands/project.js +1 -2
  51. package/commands/sandbox/delete.js +1 -1
  52. package/commands/testAccount/importData.d.ts +1 -1
  53. package/commands/testAccount/importData.js +1 -1
  54. package/commands/testAccount.js +1 -1
  55. package/lang/en.d.ts +104 -56
  56. package/lang/en.js +118 -68
  57. package/lang/en.lyaml +12 -12
  58. package/lib/__tests__/hasFeature.test.js +145 -7
  59. package/lib/__tests__/importData.test.js +1 -1
  60. package/lib/app/__tests__/migrate.test.js +26 -31
  61. package/lib/app/migrate.d.ts +3 -10
  62. package/lib/app/migrate.js +14 -25
  63. package/lib/constants.d.ts +9 -0
  64. package/lib/constants.js +9 -0
  65. package/lib/errorHandlers/index.d.ts +4 -0
  66. package/lib/errorHandlers/index.js +1 -1
  67. package/lib/hasFeature.js +6 -0
  68. package/lib/importData.js +1 -1
  69. package/lib/links.d.ts +1 -0
  70. package/lib/links.js +10 -3
  71. package/lib/mcp/setup.d.ts +0 -2
  72. package/lib/mcp/setup.js +4 -29
  73. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  74. package/lib/projects/__tests__/AppDevModeInterface.test.js +72 -44
  75. package/lib/projects/__tests__/LocalDevProcess.test.js +228 -16
  76. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
  77. package/lib/projects/__tests__/components.test.js +164 -7
  78. package/lib/projects/__tests__/deploy.test.js +229 -0
  79. package/lib/projects/__tests__/{localDevHelpers.test.js → localDevProjectHelpers.test.js} +5 -3
  80. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  81. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  82. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  83. package/lib/projects/add/__tests__/v3AddComponent.test.js +142 -8
  84. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  85. package/lib/projects/add/legacyAddComponent.js +5 -1
  86. package/lib/projects/add/v3AddComponent.d.ts +2 -1
  87. package/lib/projects/add/v3AddComponent.js +22 -5
  88. package/lib/projects/components.d.ts +1 -0
  89. package/lib/projects/components.js +27 -1
  90. package/lib/projects/create/__tests__/v3.test.js +174 -11
  91. package/lib/projects/create/index.js +2 -2
  92. package/lib/projects/create/legacy.js +1 -1
  93. package/lib/projects/create/v3.d.ts +2 -2
  94. package/lib/projects/create/v3.js +38 -13
  95. package/lib/projects/deploy.d.ts +13 -0
  96. package/lib/projects/deploy.js +63 -0
  97. package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
  98. package/lib/projects/localDev/AppDevModeInterface.js +132 -48
  99. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  100. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  101. package/lib/projects/localDev/LocalDevLogger.js +22 -0
  102. package/lib/projects/localDev/LocalDevManager.js +1 -1
  103. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  104. package/lib/projects/localDev/LocalDevProcess.js +93 -20
  105. package/lib/projects/localDev/LocalDevState.d.ts +13 -9
  106. package/lib/projects/localDev/LocalDevState.js +26 -17
  107. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
  108. package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
  109. package/lib/projects/localDev/{helpers.d.ts → helpers/account.d.ts} +1 -14
  110. package/lib/projects/localDev/helpers/account.js +233 -0
  111. package/lib/projects/localDev/helpers/project.d.ts +12 -0
  112. package/lib/projects/localDev/helpers/project.js +176 -0
  113. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
  114. package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
  115. package/lib/projects/platformVersion.d.ts +1 -0
  116. package/lib/projects/platformVersion.js +10 -0
  117. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  118. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +4 -14
  119. package/lib/projects/upload.js +1 -1
  120. package/lib/projects/urls.d.ts +2 -0
  121. package/lib/projects/urls.js +7 -0
  122. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  123. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  124. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  125. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  126. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  127. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  128. package/lib/prompts/installAppPrompt.d.ts +6 -1
  129. package/lib/prompts/installAppPrompt.js +6 -1
  130. package/lib/prompts/projectAddPrompt.js +3 -2
  131. package/lib/prompts/projectDevTargetAccountPrompt.js +1 -0
  132. package/lib/prompts/promptUtils.d.ts +7 -1
  133. package/lib/prompts/promptUtils.js +17 -1
  134. package/lib/prompts/selectProjectTemplatePrompt.js +3 -1
  135. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  136. package/lib/theme/__tests__/migrate.test.js +233 -0
  137. package/lib/theme/migrate.d.ts +13 -0
  138. package/lib/theme/migrate.js +90 -0
  139. package/lib/ui/index.js +3 -6
  140. package/lib/usageTracking.js +2 -2
  141. package/mcp-server/server.js +2 -1
  142. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
  143. package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
  144. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
  145. package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
  146. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
  147. package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
  148. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  149. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  150. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
  151. package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
  152. package/mcp-server/tools/cms/HsListTool.d.ts +23 -0
  153. package/mcp-server/tools/cms/HsListTool.js +58 -0
  154. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
  155. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
  156. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
  157. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
  158. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
  159. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
  160. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  161. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  162. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
  163. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
  164. package/mcp-server/tools/cms/__tests__/HsListTool.test.d.ts +1 -0
  165. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +120 -0
  166. package/mcp-server/tools/index.d.ts +1 -0
  167. package/mcp-server/tools/index.js +16 -0
  168. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  169. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  170. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  171. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  172. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  173. package/mcp-server/tools/project/DocFetchTool.js +3 -3
  174. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  175. package/mcp-server/tools/project/DocsSearchTool.js +8 -8
  176. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  177. package/mcp-server/tools/project/GetConfigValuesTool.js +14 -8
  178. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  179. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  180. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  181. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  182. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  183. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  184. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +3 -3
  185. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +15 -13
  186. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  187. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  188. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  189. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  190. package/mcp-server/tools/project/constants.d.ts +1 -1
  191. package/mcp-server/tools/project/constants.js +14 -6
  192. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  193. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  194. package/mcp-server/utils/cliConfig.d.ts +1 -0
  195. package/mcp-server/utils/cliConfig.js +12 -0
  196. package/package.json +5 -4
  197. package/types/LocalDev.d.ts +21 -4
  198. package/types/Projects.d.ts +1 -0
  199. package/types/Prompts.d.ts +1 -0
  200. package/ui/components/BoxWithTitle.d.ts +8 -0
  201. package/ui/components/BoxWithTitle.js +9 -0
  202. package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
  203. package/ui/components/HorizontalSelectPrompt.js +30 -0
  204. package/ui/components/StatusMessageBoxes.d.ts +12 -0
  205. package/ui/components/StatusMessageBoxes.js +31 -0
  206. package/ui/index.js +1 -1
  207. package/ui/lib/ui-testing-utils.d.ts +9 -0
  208. package/ui/lib/ui-testing-utils.js +47 -0
  209. package/ui/lib/useTerminalSize.d.ts +13 -0
  210. package/ui/lib/useTerminalSize.js +31 -0
  211. package/ui/styles.d.ts +18 -0
  212. package/ui/styles.js +18 -0
  213. package/ui/views/UiSandbox.d.ts +5 -0
  214. package/ui/views/UiSandbox.js +25 -0
  215. package/lib/projects/localDev/helpers.js +0 -388
  216. /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
  217. /package/lib/projects/__tests__/{localDevHelpers.test.d.ts → localDevProjectHelpers.test.d.ts} +0 -0
@@ -1,7 +1,22 @@
1
1
  import yargs from 'yargs';
2
2
  import projectAddCommand from '../add.js';
3
3
  import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../../lib/constants.js';
4
+ import { v3AddComponent } from '../../../lib/projects/add/v3AddComponent.js';
5
+ import { legacyAddComponent } from '../../../lib/projects/add/legacyAddComponent.js';
6
+ import { getProjectConfig } from '../../../lib/projects/config.js';
7
+ import { useV3Api } from '../../../lib/projects/platformVersion.js';
8
+ import { trackCommandUsage } from '../../../lib/usageTracking.js';
4
9
  vi.mock('../../../lib/commonOpts');
10
+ vi.mock('../../../lib/projects/add/v3AddComponent');
11
+ vi.mock('../../../lib/projects/add/legacyAddComponent');
12
+ vi.mock('../../../lib/projects/config');
13
+ vi.mock('../../../lib/projects/platformVersion');
14
+ vi.mock('../../../lib/usageTracking');
15
+ const mockedV3AddComponent = vi.mocked(v3AddComponent);
16
+ const mockedLegacyAddComponent = vi.mocked(legacyAddComponent);
17
+ const mockedGetProjectConfig = vi.mocked(getProjectConfig);
18
+ const mockedUseV3Api = vi.mocked(useV3Api);
19
+ const mockedTrackCommandUsage = vi.mocked(trackCommandUsage);
5
20
  describe('commands/project/add', () => {
6
21
  const yargsMock = yargs;
7
22
  describe('command', () => {
@@ -40,4 +55,53 @@ describe('commands/project/add', () => {
40
55
  });
41
56
  });
42
57
  });
58
+ describe('handler', () => {
59
+ const mockProjectConfig = {
60
+ name: 'test-project',
61
+ srcDir: 'src',
62
+ platformVersion: 'v3',
63
+ };
64
+ const mockProjectDir = '/path/to/project';
65
+ const mockArgs = {
66
+ derivedAccountId: 123,
67
+ name: 'test-component',
68
+ type: 'module',
69
+ };
70
+ beforeEach(() => {
71
+ mockedGetProjectConfig.mockResolvedValue({
72
+ projectConfig: mockProjectConfig,
73
+ projectDir: mockProjectDir,
74
+ });
75
+ mockedTrackCommandUsage.mockResolvedValue();
76
+ mockedV3AddComponent.mockResolvedValue();
77
+ mockedLegacyAddComponent.mockResolvedValue();
78
+ vi.spyOn(process, 'exit').mockImplementation(() => {
79
+ throw new Error('process.exit called');
80
+ });
81
+ });
82
+ it('should call v3AddComponent with accountId for v3 projects', async () => {
83
+ mockedUseV3Api.mockReturnValue(true);
84
+ await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
85
+ expect(mockedV3AddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
86
+ expect(mockedLegacyAddComponent).not.toHaveBeenCalled();
87
+ });
88
+ it('should call legacyAddComponent for non-v3 projects', async () => {
89
+ mockedUseV3Api.mockReturnValue(false);
90
+ await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
91
+ expect(mockedLegacyAddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
92
+ expect(mockedV3AddComponent).not.toHaveBeenCalled();
93
+ });
94
+ it('should exit with error when project config is not found', async () => {
95
+ mockedGetProjectConfig.mockResolvedValue({
96
+ projectConfig: null,
97
+ projectDir: null,
98
+ });
99
+ const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
100
+ throw new Error('process.exit called');
101
+ });
102
+ await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
103
+ expect(mockExit).toHaveBeenCalledWith(1);
104
+ mockExit.mockRestore();
105
+ });
106
+ });
43
107
  });
@@ -27,14 +27,71 @@ describe('commands/project/create', () => {
27
27
  it('should define project creation options', () => {
28
28
  const optionsSpy = vi.spyOn(yargsMock, 'options');
29
29
  const exampleSpy = vi.spyOn(yargsMock, 'example');
30
+ const conflictsSpy = vi.spyOn(yargsMock, 'conflicts');
30
31
  projectCreateCommand.builder(yargsMock);
31
32
  expect(optionsSpy).toHaveBeenCalledWith(expect.objectContaining({
32
33
  name: expect.any(Object),
33
34
  dest: expect.any(Object),
34
35
  template: expect.any(Object),
35
36
  'template-source': expect.any(Object),
37
+ 'platform-version': expect.any(Object),
38
+ 'project-base': expect.any(Object),
39
+ distribution: expect.any(Object),
40
+ auth: expect.any(Object),
41
+ features: expect.any(Object),
36
42
  }));
43
+ expect(conflictsSpy).toHaveBeenCalledWith('template', 'features');
37
44
  expect(exampleSpy).toHaveBeenCalled();
38
45
  });
46
+ it('should define platform version option with correct choices', () => {
47
+ const optionsSpy = vi.spyOn(yargsMock, 'options');
48
+ projectCreateCommand.builder(yargsMock);
49
+ const optionsCall = optionsSpy.mock.calls[0][0];
50
+ expect(optionsCall['platform-version']).toEqual(expect.objectContaining({
51
+ describe: 'The target platform version for the new project.',
52
+ type: 'string',
53
+ choices: ['2023.2', '2025.1', '2025.2'],
54
+ default: '2025.2',
55
+ }));
56
+ });
57
+ it('should define project base option with correct choices', () => {
58
+ const optionsSpy = vi.spyOn(yargsMock, 'options');
59
+ projectCreateCommand.builder(yargsMock);
60
+ const optionsCall = optionsSpy.mock.calls[0][0];
61
+ expect(optionsCall['project-base']).toEqual(expect.objectContaining({
62
+ describe: 'The top level component to include in the project.',
63
+ type: 'string',
64
+ choices: ['empty', 'app'],
65
+ }));
66
+ });
67
+ it('should define distribution option with correct choices', () => {
68
+ const optionsSpy = vi.spyOn(yargsMock, 'options');
69
+ projectCreateCommand.builder(yargsMock);
70
+ const optionsCall = optionsSpy.mock.calls[0][0];
71
+ expect(optionsCall.distribution).toEqual(expect.objectContaining({
72
+ describe: 'How the app will be distributed.',
73
+ type: 'string',
74
+ choices: ['private', 'marketplace'],
75
+ }));
76
+ });
77
+ it('should define auth option with correct choices', () => {
78
+ const optionsSpy = vi.spyOn(yargsMock, 'options');
79
+ projectCreateCommand.builder(yargsMock);
80
+ const optionsCall = optionsSpy.mock.calls[0][0];
81
+ expect(optionsCall.auth).toEqual(expect.objectContaining({
82
+ describe: 'Authentication model for the application.',
83
+ type: 'string',
84
+ choices: ['oauth', 'static'],
85
+ }));
86
+ });
87
+ it('should define features option as array', () => {
88
+ const optionsSpy = vi.spyOn(yargsMock, 'options');
89
+ projectCreateCommand.builder(yargsMock);
90
+ const optionsCall = optionsSpy.mock.calls[0][0];
91
+ expect(optionsCall.features).toEqual(expect.objectContaining({
92
+ describe: 'Features to include in the project. Only valid if project-base is app',
93
+ type: 'array',
94
+ }));
95
+ });
39
96
  });
40
97
  });
@@ -8,7 +8,7 @@ import * as ui from '../../../lib/ui/index.js';
8
8
  import { addAccountOptions, addConfigOptions, addJSONOutputOptions, addUseEnvironmentOptions, } from '../../../lib/commonOpts.js';
9
9
  import * as projectUtils from '../../../lib/projects/config.js';
10
10
  import * as projectUrlUtils from '../../../lib/projects/urls.js';
11
- import { pollDeployStatus } from '../../../lib/projects/buildAndDeploy.js';
11
+ import { pollDeployStatus } from '../../../lib/projects/pollProjectBuildAndDeploy.js';
12
12
  import * as projectNamePrompt from '../../../lib/prompts/projectNamePrompt.js';
13
13
  import * as promptUtils from '../../../lib/prompts/promptUtils.js';
14
14
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
@@ -23,7 +23,8 @@ vi.mock('../../../lib/commonOpts');
23
23
  vi.mock('../../../lib/validation');
24
24
  vi.mock('../../../lib/projects/config');
25
25
  vi.mock('../../../lib/projects/urls');
26
- vi.mock('../../../lib/projects/buildAndDeploy');
26
+ vi.mock('../../../lib/projects/pollProjectBuildAndDeploy');
27
+ vi.mock('../../../lib/projects/platformVersion');
27
28
  vi.mock('../../../lib/prompts/projectNamePrompt');
28
29
  vi.mock('../../../lib/prompts/promptUtils');
29
30
  vi.mock('../../../lib/usageTracking');
@@ -61,9 +62,6 @@ describe('commands/project/deploy', () => {
61
62
  });
62
63
  });
63
64
  describe('describe', () => {
64
- it('should contain the beta tag', () => {
65
- expect(projectDeployCommand.describe).toContain('[BETA]');
66
- });
67
65
  it('should provide a description', () => {
68
66
  expect(projectDeployCommand.describe).toBeDefined();
69
67
  });
@@ -5,7 +5,8 @@ import { getConfigAccounts, getAccountConfig, } from '@hubspot/local-dev-lib/con
5
5
  import { getValidEnv } from '@hubspot/local-dev-lib/environment';
6
6
  import { logError } from '../../../lib/errorHandlers/index.js';
7
7
  import { ensureProjectExists } from '../../../lib/projects/ensureProjectExists.js';
8
- import { createInitialBuildForNewProject, createNewProjectForLocalDev, useExistingDevTestAccount, createDeveloperTestAccountForLocalDev, selectAccountTypePrompt, } from '../../../lib/projects/localDev/helpers.js';
8
+ import { createInitialBuildForNewProject, createNewProjectForLocalDev, } from '../../../lib/projects/localDev/helpers/project.js';
9
+ import { useExistingDevTestAccount, createDeveloperTestAccountForLocalDev, selectAccountTypePrompt, } from '../../../lib/projects/localDev/helpers/account.js';
9
10
  import { selectDeveloperTestTargetAccountPrompt, selectSandboxTargetAccountPrompt, } from '../../../lib/prompts/projectDevTargetAccountPrompt.js';
10
11
  import SpinniesManager from '../../../lib/ui/SpinniesManager.js';
11
12
  import LocalDevProcess from '../../../lib/projects/localDev/LocalDevProcess.js';
@@ -34,7 +35,8 @@ vi.mock('@hubspot/local-dev-lib/config');
34
35
  vi.mock('@hubspot/local-dev-lib/environment');
35
36
  vi.mock('../../../lib/errorHandlers');
36
37
  vi.mock('../../../lib/projects/ensureProjectExists');
37
- vi.mock('../../../lib/projects/localDev/helpers');
38
+ vi.mock('../../../lib/projects/localDev/helpers/project');
39
+ vi.mock('../../../lib/projects/localDev/helpers/account');
38
40
  vi.mock('../../../lib/prompts/projectDevTargetAccountPrompt');
39
41
  vi.mock('../../../lib/ui/SpinniesManager');
40
42
  vi.mock('../../../lib/projects/localDev/LocalDevProcess');
@@ -322,8 +324,7 @@ describe('unifiedProjectDevFlow', () => {
322
324
  });
323
325
  expect(createNewProjectForLocalDev).not.toHaveBeenCalled();
324
326
  expect(LocalDevProcess).toHaveBeenCalledWith(expect.objectContaining({
325
- projectId: mockProject.id,
326
- projectName: mockProject.name,
327
+ projectData: mockProject,
327
328
  }));
328
329
  });
329
330
  });
@@ -344,8 +345,7 @@ describe('unifiedProjectDevFlow', () => {
344
345
  targetTestingAccountId: mockProvidedTargetTestingAccountId,
345
346
  projectConfig: mockProjectConfig,
346
347
  projectDir: mockProjectDir,
347
- projectName: mockProject.name,
348
- projectId: mockProject.id,
348
+ projectData: mockProject,
349
349
  env: ENVIRONMENTS.PROD,
350
350
  });
351
351
  });
@@ -378,17 +378,26 @@ describe('unifiedProjectDevFlow', () => {
378
378
  beforeEach(() => {
379
379
  isTestAccountOrSandbox.mockReturnValue(false);
380
380
  });
381
- it('should display account type information when prompting', async () => {
382
- selectAccountTypePrompt.mockResolvedValue(HUBSPOT_ACCOUNT_TYPES.STANDARD);
381
+ it('should log info message when default account is a sandbox or test account', async () => {
382
+ isTestAccountOrSandbox.mockReturnValue(true);
383
+ await unifiedProjectDevFlow({
384
+ args: mockArgs,
385
+ targetProjectAccountId: mockTargetProjectAccountId,
386
+ projectConfig: mockProjectConfig,
387
+ projectDir: mockProjectDir,
388
+ });
389
+ expect(uiLogger.log).toHaveBeenCalledWith(commands.project.dev.logs.defaultSandboxOrDevTestTestingAccountExplanation(mockTargetProjectAccountId));
390
+ });
391
+ it('should log info message when testingAccount flag is provided', async () => {
392
+ const providedTestingAccountId = 999;
383
393
  await unifiedProjectDevFlow({
384
394
  args: mockArgs,
385
395
  targetProjectAccountId: mockTargetProjectAccountId,
396
+ providedTargetTestingAccountId: providedTestingAccountId,
386
397
  projectConfig: mockProjectConfig,
387
398
  projectDir: mockProjectDir,
388
399
  });
389
- expect(uiLine).toHaveBeenCalled();
390
- expect(uiLogger.log).toHaveBeenCalledWith(commands.project.dev.logs.accountTypeInformation);
391
- expect(uiLogger.log).toHaveBeenCalledWith(commands.project.dev.logs.learnMoreMessage);
400
+ expect(uiLogger.log).toHaveBeenCalledWith(commands.project.dev.logs.testingAccountFlagExplanation(providedTestingAccountId));
392
401
  });
393
402
  });
394
403
  });
@@ -45,9 +45,6 @@ describe('commands/project/logs', () => {
45
45
  });
46
46
  });
47
47
  describe('describe', () => {
48
- it('should contain the beta tag', () => {
49
- expect(projectLogsCommand.describe).toContain('[BETA]');
50
- });
51
48
  it('should provide a description', () => {
52
49
  expect(projectLogsCommand.describe).toBeDefined();
53
50
  });
@@ -44,7 +44,7 @@ describe('commands/project/migrate', () => {
44
44
  });
45
45
  describe('describe', () => {
46
46
  it('should provide a description', () => {
47
- expect(migrateCommand.describe).toBe(undefined);
47
+ expect(migrateCommand.describe).toBeDefined();
48
48
  });
49
49
  });
50
50
  describe('builder', () => {
@@ -54,7 +54,6 @@ describe('commands/project/migrate', () => {
54
54
  type: 'string',
55
55
  choices: [v2025_2],
56
56
  default: v2025_2,
57
- hidden: true,
58
57
  });
59
58
  expect(optionsSpy).toHaveBeenCalledWith('unstable', {
60
59
  type: 'boolean',
@@ -57,8 +57,7 @@ describe('commands/project/migrateApp', () => {
57
57
  'platform-version': expect.objectContaining({
58
58
  type: 'string',
59
59
  choices: [v2023_2, v2025_2],
60
- hidden: true,
61
- default: v2023_2,
60
+ default: v2025_2,
62
61
  }),
63
62
  });
64
63
  expect(exampleSpy).toHaveBeenCalled();
@@ -19,7 +19,7 @@ describe('commands/project', () => {
19
19
  });
20
20
  describe('describe', () => {
21
21
  it('should not provide a description', () => {
22
- expect(profileCommand.describe).not.toBeDefined();
22
+ expect(profileCommand.describe).toBeDefined();
23
23
  });
24
24
  });
25
25
  describe('builder', () => {
@@ -1,5 +1,5 @@
1
1
  import { YargsCommandModule, CommonArgs } from '../../types/Yargs.js';
2
- type ProjectAddArgs = CommonArgs & {
2
+ export type ProjectAddArgs = CommonArgs & {
3
3
  type?: string;
4
4
  name?: string;
5
5
  features?: string[];
@@ -1,21 +1,18 @@
1
1
  import { logError } from '../../lib/errorHandlers/index.js';
2
- import { trackCommandUsage } from '../../lib/usageTracking.js';
3
2
  import { getProjectConfig } from '../../lib/projects/config.js';
4
- import { uiBetaTag } from '../../lib/ui/index.js';
5
3
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
6
4
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
7
5
  import { commands } from '../../lang/en.js';
8
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
6
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
9
7
  import { legacyAddComponent } from '../../lib/projects/add/legacyAddComponent.js';
10
8
  import { v3AddComponent } from '../../lib/projects/add/v3AddComponent.js';
11
9
  import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../lib/constants.js';
12
10
  import { uiLogger } from '../../lib/ui/logger.js';
13
11
  const command = 'add';
14
- const describe = uiBetaTag(commands.project.add.describe, false);
12
+ const describe = commands.project.add.describe;
15
13
  async function handler(args) {
16
14
  try {
17
15
  const { derivedAccountId } = args;
18
- trackCommandUsage('project-add', undefined, derivedAccountId);
19
16
  const { projectConfig, projectDir } = await getProjectConfig();
20
17
  if (!projectDir || !projectConfig) {
21
18
  uiLogger.error(commands.project.add.error.locationInProject);
@@ -23,10 +20,10 @@ async function handler(args) {
23
20
  }
24
21
  const isV3ProjectCreate = useV3Api(projectConfig.platformVersion);
25
22
  if (isV3ProjectCreate) {
26
- await v3AddComponent(args, projectDir, projectConfig);
23
+ await v3AddComponent(args, projectDir, projectConfig, derivedAccountId);
27
24
  }
28
25
  else {
29
- await legacyAddComponent(args, projectDir, projectConfig);
26
+ await legacyAddComponent(args, projectDir, projectConfig, derivedAccountId);
30
27
  }
31
28
  }
32
29
  catch (e) {
@@ -47,19 +44,16 @@ function projectAddBuilder(yargs) {
47
44
  },
48
45
  distribution: {
49
46
  describe: commands.project.add.options.distribution.describe,
50
- hidden: true,
51
47
  type: 'string',
52
48
  choices: [privateDistribution, marketplaceDistribution],
53
49
  },
54
50
  auth: {
55
51
  describe: commands.project.add.options.auth.describe,
56
- hidden: true,
57
52
  type: 'string',
58
53
  choices: [oAuth, staticAuth],
59
54
  },
60
55
  features: {
61
56
  describe: commands.project.add.options.features.describe,
62
- hidden: true,
63
57
  type: 'array',
64
58
  },
65
59
  });
@@ -7,7 +7,7 @@ import { writeProjectConfig, getProjectConfig, } from '../../lib/projects/config
7
7
  import { EMPTY_PROJECT_TEMPLATE_NAME } from '../../lib/projects/create/legacy.js';
8
8
  import { generateComponentPaths } from '../../lib/projects/create/v3.js';
9
9
  import { PROJECT_WITH_APP, EMPTY_PROJECT } from '../../lib/constants.js';
10
- import { uiBetaTag, uiFeatureHighlight } from '../../lib/ui/index.js';
10
+ import { uiFeatureHighlight } from '../../lib/ui/index.js';
11
11
  import { debugError, logError } from '../../lib/errorHandlers/index.js';
12
12
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
13
13
  import { PROJECT_CONFIG_FILE, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, marketplaceDistribution, privateDistribution, oAuth, staticAuth, DEFAULT_PROJECT_TEMPLATE_BRANCH, } from '../../lib/constants.js';
@@ -16,8 +16,10 @@ import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';
16
16
  import { commands } from '../../lang/en.js';
17
17
  import { uiLogger } from '../../lib/ui/logger.js';
18
18
  import { handleProjectCreationFlow, } from '../../lib/projects/create/index.js';
19
+ import { getProjectMetadata, } from '@hubspot/project-parsing-lib/src/lib/project.js';
20
+ import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/components.js';
19
21
  const command = ['create', 'init'];
20
- const describe = uiBetaTag(commands.project.create.describe, false);
22
+ const describe = commands.project.create.describe;
21
23
  const { v2023_2, v2025_1, v2025_2 } = PLATFORM_VERSIONS;
22
24
  async function handler(args) {
23
25
  const { derivedAccountId, platformVersion, templateSource } = args;
@@ -39,7 +41,7 @@ async function handler(args) {
39
41
  type: selectProjectTemplatePromptResponse.projectTemplate?.name ||
40
42
  (selectProjectTemplatePromptResponse.componentTemplates || [])
41
43
  // @ts-expect-error
42
- .map((item) => item.label)
44
+ .map((item) => item.type)
43
45
  .join(','),
44
46
  }, derivedAccountId);
45
47
  const projectDest = path.resolve(getCwd(), projectNameAndDestPromptResponse.dest);
@@ -73,10 +75,13 @@ async function handler(args) {
73
75
  }
74
76
  const projectConfigPath = path.join(projectDest, PROJECT_CONFIG_FILE);
75
77
  const parsedConfigFile = JSON.parse(fs.readFileSync(projectConfigPath).toString());
78
+ const projectName = projectNameAndDestPromptResponse.name;
76
79
  writeProjectConfig(projectConfigPath, {
77
80
  ...parsedConfigFile,
78
- name: projectNameAndDestPromptResponse.name,
81
+ name: projectName,
79
82
  });
83
+ const projectMetadata = await getProjectMetadata(path.join(projectDest, parsedConfigFile.srcDir));
84
+ updateHsMetaFilesWithAutoGeneratedFields(projectName, projectMetadata.hsMetaFiles);
80
85
  // If the template is 'no-template', we need to manually create a src directory
81
86
  if (selectProjectTemplatePromptResponse.projectTemplate?.name ===
82
87
  EMPTY_PROJECT_TEMPLATE_NAME ||
@@ -107,7 +112,6 @@ function projectCreateBuilder(yargs) {
107
112
  type: 'string',
108
113
  },
109
114
  template: {
110
- // TODO: When we release 2025.2 scaffolding, we need to point out this is only valid for 2025.1 prior
111
115
  describe: commands.project.create.options.template.describe,
112
116
  type: 'string',
113
117
  },
@@ -117,32 +121,27 @@ function projectCreateBuilder(yargs) {
117
121
  },
118
122
  'platform-version': {
119
123
  describe: commands.project.create.options.platformVersion.describe,
120
- hidden: true,
121
124
  type: 'string',
122
125
  choices: [v2023_2, v2025_1, v2025_2],
123
- default: v2023_2,
126
+ default: v2025_2,
124
127
  },
125
128
  'project-base': {
126
129
  describe: commands.project.create.options.projectBase.describe,
127
- hidden: true,
128
130
  type: 'string',
129
131
  choices: [EMPTY_PROJECT, PROJECT_WITH_APP],
130
132
  },
131
133
  distribution: {
132
134
  describe: commands.project.create.options.distribution.describe,
133
- hidden: true,
134
135
  type: 'string',
135
136
  choices: [privateDistribution, marketplaceDistribution],
136
137
  },
137
138
  auth: {
138
139
  describe: commands.project.create.options.auth.describe,
139
- hidden: true,
140
140
  type: 'string',
141
141
  choices: [oAuth, staticAuth],
142
142
  },
143
143
  features: {
144
144
  describe: commands.project.create.options.features.describe,
145
- hidden: true,
146
145
  type: 'array',
147
146
  },
148
147
  });
@@ -1,66 +1,22 @@
1
- import { deployProject, fetchProject, } from '@hubspot/local-dev-lib/api/projects';
1
+ import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
2
2
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
3
3
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
4
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
4
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
5
5
  import { trackCommandUsage } from '../../lib/usageTracking.js';
6
6
  import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
7
7
  import { getProjectConfig } from '../../lib/projects/config.js';
8
- import { pollDeployStatus } from '../../lib/projects/buildAndDeploy.js';
9
8
  import { projectNamePrompt } from '../../lib/prompts/projectNamePrompt.js';
10
9
  import { promptUser } from '../../lib/prompts/promptUtils.js';
11
- import { uiBetaTag, uiLine } from '../../lib/ui/index.js';
10
+ import { uiLine } from '../../lib/ui/index.js';
12
11
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
13
12
  import { uiLogger } from '../../lib/ui/logger.js';
14
13
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
15
14
  import { loadProfile, logProfileFooter, logProfileHeader, exitIfUsingProfiles, } from '../../lib/projectProfiles.js';
16
- import { PROJECT_ERROR_TYPES, PROJECT_DEPLOY_TEXT, } from '../../lib/constants.js';
15
+ import { PROJECT_DEPLOY_TEXT } from '../../lib/constants.js';
17
16
  import { commands } from '../../lang/en.js';
17
+ import { handleProjectDeploy, validateBuildIdForDeploy, logDeployErrors, } from '../../lib/projects/deploy.js';
18
18
  const command = 'deploy';
19
- const describe = uiBetaTag(commands.project.deploy.describe, false);
20
- function validateBuildId(buildId, deployedBuildId, latestBuildId, projectName, accountId) {
21
- if (Number(buildId) > latestBuildId) {
22
- return commands.project.deploy.errors.buildIdDoesNotExist(accountId, buildId, projectName);
23
- }
24
- if (Number(buildId) === deployedBuildId) {
25
- return commands.project.deploy.errors.buildAlreadyDeployed(accountId, buildId, projectName);
26
- }
27
- return true;
28
- }
29
- function logDeployErrors(errorData) {
30
- uiLogger.error(errorData.message);
31
- errorData.errors.forEach(err => {
32
- // This is how the pre-deploy check manifests itself in < 2025.2 projects
33
- if (err.subCategory === PROJECT_ERROR_TYPES.DEPLOY_CONTAINS_REMOVALS) {
34
- uiLogger.log(commands.project.deploy.errors.deployContainsRemovals(err.context.COMPONENT_NAME));
35
- }
36
- else {
37
- uiLogger.log(err.message);
38
- }
39
- });
40
- }
41
- function handleBlockedDeploy(deployResp) {
42
- const deployCanBeForced = deployResp.issues.every(issue => issue.blockingMessages.every(message => message.isWarning));
43
- uiLogger.log('');
44
- if (deployCanBeForced) {
45
- uiLogger.warn(commands.project.deploy.errors.deployWarningsHeader);
46
- uiLogger.log('');
47
- }
48
- else {
49
- uiLogger.error(commands.project.deploy.errors.deployBlockedHeader);
50
- uiLogger.log('');
51
- }
52
- deployResp.issues.forEach(issue => {
53
- if (issue.blockingMessages.length > 0) {
54
- issue.blockingMessages.forEach(message => {
55
- uiLogger.log(commands.project.deploy.errors.deployIssueComponentWarning(issue.uid, issue.componentTypeName, message.message));
56
- });
57
- }
58
- else {
59
- uiLogger.log(commands.project.deploy.errors.deployIssueComponentGeneric(issue.uid, issue.componentTypeName));
60
- }
61
- uiLogger.log('');
62
- });
63
- }
19
+ const describe = commands.project.deploy.describe;
64
20
  async function handler(args) {
65
21
  const { derivedAccountId, project: projectOption, buildId: buildIdOption, force: forceOption, deployLatestBuild: deployLatestBuildOption, json: formatOutputAsJson, } = args;
66
22
  const accountConfig = getAccountConfig(derivedAccountId);
@@ -105,7 +61,7 @@ async function handler(args) {
105
61
  return process.exit(EXIT_CODES.ERROR);
106
62
  }
107
63
  if (buildIdToDeploy) {
108
- const validationResult = validateBuildId(buildIdToDeploy, deployedBuildId, latestBuild.buildId, projectName, targetAccountId);
64
+ const validationResult = validateBuildIdForDeploy(buildIdToDeploy, deployedBuildId, latestBuild.buildId, projectName, targetAccountId);
109
65
  if (validationResult !== true) {
110
66
  uiLogger.error(validationResult.toString());
111
67
  return process.exit(EXIT_CODES.ERROR);
@@ -122,7 +78,7 @@ async function handler(args) {
122
78
  default: latestBuild.buildId === deployedBuildId
123
79
  ? undefined
124
80
  : latestBuild.buildId,
125
- validate: buildId => validateBuildId(buildId, deployedBuildId, latestBuild.buildId, projectName, targetAccountId),
81
+ validate: buildId => validateBuildIdForDeploy(buildId, deployedBuildId, latestBuild.buildId, projectName, targetAccountId),
126
82
  });
127
83
  buildIdToDeploy = deployBuildIdPromptResponse.buildId;
128
84
  }
@@ -131,21 +87,13 @@ async function handler(args) {
131
87
  uiLogger.error(commands.project.deploy.errors.noBuildId);
132
88
  return process.exit(EXIT_CODES.ERROR);
133
89
  }
134
- const { data: deployResp } = await deployProject(targetAccountId, projectName, buildIdToDeploy, useV3Api(projectConfig?.platformVersion), forceOption);
135
- if (!deployResp || deployResp.buildResultType !== 'DEPLOY_QUEUED') {
136
- if (deployResp?.buildResultType === 'DEPLOY_BLOCKED') {
137
- handleBlockedDeploy(deployResp);
138
- process.exit(EXIT_CODES.ERROR);
139
- }
140
- else {
141
- uiLogger.error(commands.project.deploy.errors.deploy);
142
- }
90
+ const deployResult = await handleProjectDeploy(targetAccountId, projectName, buildIdToDeploy, useV3Api(projectConfig?.platformVersion), forceOption);
91
+ if (!deployResult) {
143
92
  return process.exit(EXIT_CODES.ERROR);
144
93
  }
145
94
  else if (formatOutputAsJson) {
146
- jsonOutput.deployId = Number(deployResp.id);
95
+ jsonOutput.deployId = deployResult.deployId;
147
96
  }
148
- const deployResult = await pollDeployStatus(targetAccountId, projectName, Number(deployResp.id), buildIdToDeploy);
149
97
  if (deployResult.status === PROJECT_DEPLOY_TEXT.STATES.SUCCESS) {
150
98
  deploySuccess = true;
151
99
  }
@@ -8,7 +8,8 @@ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
8
8
  import { uiCommandReference } from '../../../lib/ui/index.js';
9
9
  import SpinniesManager from '../../../lib/ui/SpinniesManager.js';
10
10
  import LocalDevManager from '../../../lib/projects/localDev/LocalDevManager.js';
11
- import { confirmDefaultAccountIsTarget, suggestRecommendedNestedAccount, checkIfAccountFlagIsSupported, checkIfDefaultAccountIsSupported, createSandboxForLocalDev, createDeveloperTestAccountForLocalDev, createNewProjectForLocalDev, createInitialBuildForNewProject, useExistingDevTestAccount, checkIfParentAccountIsAuthed, hasSandboxes, } from '../../../lib/projects/localDev/helpers.js';
11
+ import { confirmDefaultAccountIsTarget, suggestRecommendedNestedAccount, checkIfAccountFlagIsSupported, checkIfDefaultAccountIsSupported, createSandboxForLocalDev, createDeveloperTestAccountForLocalDev, useExistingDevTestAccount, checkIfParentAccountIsAuthed, hasSandboxes, } from '../../../lib/projects/localDev/helpers/account.js';
12
+ import { createInitialBuildForNewProject, createNewProjectForLocalDev, } from '../../../lib/projects/localDev/helpers/project.js';
12
13
  import { handleExit } from '../../../lib/process.js';
13
14
  import { isSandbox, isDeveloperTestAccount, } from '../../../lib/accountTypes.js';
14
15
  import { ensureProjectExists } from '../../../lib/projects/ensureProjectExists.js';