@hubspot/cli 7.7.32-experimental.0 → 7.7.34-experimental.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 (118) hide show
  1. package/commands/app/__tests__/install.test.js +47 -0
  2. package/commands/app/install.d.ts +8 -0
  3. package/commands/app/install.js +122 -0
  4. package/commands/app.js +6 -1
  5. package/commands/getStarted.js +7 -20
  6. package/commands/project/__tests__/add.test.js +3 -5
  7. package/commands/project/__tests__/deploy.test.js +3 -2
  8. package/commands/project/add.js +2 -4
  9. package/commands/project/deploy.js +9 -61
  10. package/commands/project/dev/index.js +1 -1
  11. package/commands/project/dev/unifiedFlow.js +4 -1
  12. package/commands/project/upload.d.ts +2 -2
  13. package/commands/project/upload.js +3 -3
  14. package/commands/project/validate.js +1 -1
  15. package/commands/project/watch.js +2 -2
  16. package/commands/testAccount/create.js +3 -0
  17. package/lang/en.d.ts +30 -4
  18. package/lang/en.js +31 -5
  19. package/lib/__tests__/hasFeature.test.js +145 -7
  20. package/lib/__tests__/importData.test.js +1 -1
  21. package/lib/app/migrate.js +9 -2
  22. package/lib/constants.d.ts +2 -0
  23. package/lib/constants.js +2 -0
  24. package/lib/errorHandlers/index.d.ts +4 -0
  25. package/lib/errorHandlers/index.js +1 -1
  26. package/lib/hasFeature.js +6 -0
  27. package/lib/importData.js +1 -1
  28. package/lib/mcp/setup.js +1 -1
  29. package/lib/projectProfiles.d.ts +1 -1
  30. package/lib/projectProfiles.js +10 -2
  31. package/lib/projects/__tests__/AppDevModeInterface.test.js +61 -44
  32. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  33. package/lib/projects/__tests__/deploy.test.d.ts +1 -0
  34. package/lib/projects/__tests__/deploy.test.js +164 -0
  35. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  36. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  37. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  38. package/lib/projects/add/__tests__/v3AddComponent.test.js +71 -1
  39. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  40. package/lib/projects/add/legacyAddComponent.js +5 -1
  41. package/lib/projects/add/v3AddComponent.d.ts +1 -0
  42. package/lib/projects/add/v3AddComponent.js +2 -2
  43. package/lib/projects/create/__tests__/v3.test.js +97 -9
  44. package/lib/projects/create/index.js +2 -2
  45. package/lib/projects/create/legacy.js +1 -1
  46. package/lib/projects/create/v3.d.ts +2 -2
  47. package/lib/projects/create/v3.js +35 -12
  48. package/lib/projects/deploy.d.ts +13 -0
  49. package/lib/projects/deploy.js +63 -0
  50. package/lib/projects/localDev/AppDevModeInterface.d.ts +0 -2
  51. package/lib/projects/localDev/AppDevModeInterface.js +65 -36
  52. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  53. package/lib/projects/localDev/LocalDevProcess.js +3 -1
  54. package/lib/projects/localDev/LocalDevState.d.ts +5 -2
  55. package/lib/projects/localDev/LocalDevState.js +9 -1
  56. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  57. package/lib/projects/localDev/helpers/project.js +6 -7
  58. package/lib/projects/platformVersion.d.ts +1 -0
  59. package/lib/projects/platformVersion.js +10 -0
  60. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  61. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
  62. package/lib/projects/structure.d.ts +2 -2
  63. package/lib/projects/upload.d.ts +2 -1
  64. package/lib/projects/upload.js +2 -1
  65. package/lib/projects/urls.d.ts +1 -0
  66. package/lib/projects/urls.js +3 -0
  67. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  68. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  69. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  70. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  71. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  72. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  73. package/lib/prompts/installAppPrompt.d.ts +6 -1
  74. package/lib/prompts/installAppPrompt.js +6 -1
  75. package/lib/prompts/projectAddPrompt.js +1 -1
  76. package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
  77. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  78. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  79. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  80. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  81. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  82. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  83. package/mcp-server/tools/cms/HsListTool.js +1 -1
  84. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  85. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  86. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  87. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  88. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  89. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  90. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  91. package/mcp-server/tools/index.js +2 -0
  92. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  93. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  94. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  95. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  96. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  97. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  98. package/mcp-server/tools/project/DocsSearchTool.js +2 -2
  99. package/mcp-server/tools/project/GetConfigValuesTool.js +4 -4
  100. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  101. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  102. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  103. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  104. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  105. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  106. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  107. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +2 -2
  108. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
  109. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  110. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  111. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  112. package/mcp-server/tools/project/constants.d.ts +1 -1
  113. package/mcp-server/tools/project/constants.js +14 -6
  114. package/package.json +3 -3
  115. package/types/LocalDev.d.ts +2 -1
  116. package/types/Projects.d.ts +1 -0
  117. package/types/Yargs.d.ts +1 -1
  118. /package/{lib/projects/__tests__/buildAndDeploy.test.d.ts → commands/app/__tests__/install.test.d.ts} +0 -0
@@ -0,0 +1,47 @@
1
+ import yargs from 'yargs';
2
+ import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addGlobalOptions, addJSONOutputOptions, } from '../../../lib/commonOpts.js';
3
+ import installCommand from '../install.js';
4
+ vi.mock('../../../lib/commonOpts');
5
+ describe('commands/app/install', () => {
6
+ const yargsMock = yargs;
7
+ describe('command', () => {
8
+ it('should have the correct command structure', () => {
9
+ expect(installCommand.command).toEqual('install <test-account-id>');
10
+ });
11
+ });
12
+ describe('describe', () => {
13
+ it('should provide a description', () => {
14
+ expect(installCommand.describe).not.toBeDefined();
15
+ });
16
+ });
17
+ describe('builder', () => {
18
+ it('should support the correct options', () => {
19
+ installCommand.builder(yargsMock);
20
+ expect(addGlobalOptions).toHaveBeenCalledTimes(1);
21
+ expect(addGlobalOptions).toHaveBeenCalledWith(yargsMock);
22
+ expect(addAccountOptions).toHaveBeenCalledTimes(1);
23
+ expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
24
+ expect(addConfigOptions).toHaveBeenCalledTimes(1);
25
+ expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
26
+ expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
27
+ expect(addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
28
+ expect(addJSONOutputOptions).toHaveBeenCalledTimes(1);
29
+ expect(addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
30
+ expect(yargsMock.positional).toHaveBeenCalledTimes(1);
31
+ expect(yargsMock.positional).toHaveBeenCalledWith('test-account-id', expect.objectContaining({
32
+ type: 'number',
33
+ required: true,
34
+ describe: expect.any(String),
35
+ }));
36
+ expect(yargsMock.option).toHaveBeenCalledTimes(2);
37
+ expect(yargsMock.option).toHaveBeenCalledWith('app-uid', expect.objectContaining({
38
+ type: 'string',
39
+ describe: expect.any(String),
40
+ }));
41
+ expect(yargsMock.option).toHaveBeenCalledWith('project-name', expect.objectContaining({
42
+ type: 'string',
43
+ describe: expect.any(String),
44
+ }));
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,8 @@
1
+ import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule, JSONOutputArgs } from '../../types/Yargs.js';
2
+ type InstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ appUid?: string;
4
+ projectName?: string;
5
+ testAccountId: number;
6
+ };
7
+ declare const installAppCommand: YargsCommandModule<unknown, InstallAppArgs>;
8
+ export default installAppCommand;
@@ -0,0 +1,122 @@
1
+ import { fetchDeveloperTestAccountOauthAppInstallStatus, installOauthAppIntoDeveloperTestAccount, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
2
+ import { trackCommandUsage } from '../../lib/usageTracking.js';
3
+ import { commands } from '../../lang/en.js';
4
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
5
+ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
6
+ import { APP_AUTH_TYPES } from '../../lib/constants.js';
7
+ import { uiLogger } from '../../lib/ui/logger.js';
8
+ import SpinniesManager from '../../lib/ui/SpinniesManager.js';
9
+ import { poll } from '../../lib/polling.js';
10
+ import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
11
+ import { handleTranslate } from '../../lib/projects/upload.js';
12
+ import { isAppIRNode } from '../../lib/projects/structure.js';
13
+ import { logError } from '../../lib/errorHandlers/index.js';
14
+ const command = 'install <test-account-id>';
15
+ const describe = undefined; // commands.app.subcommands.install.describe;
16
+ async function handler(args) {
17
+ const { derivedAccountId, appUid, projectName, testAccountId, formatOutputAsJson, } = args;
18
+ trackCommandUsage('app-install', {}, derivedAccountId);
19
+ const jsonOutput = {};
20
+ let targetProjectName = projectName;
21
+ let targetAppUid = appUid;
22
+ const { projectConfig, projectDir } = await getProjectConfig();
23
+ if (!targetProjectName) {
24
+ validateProjectConfig(projectConfig, projectDir);
25
+ targetProjectName = projectConfig?.name;
26
+ }
27
+ if (!targetProjectName) {
28
+ uiLogger.error(commands.app.subcommands.install.errors.mustSpecifyProjectName);
29
+ process.exit(EXIT_CODES.ERROR);
30
+ }
31
+ let isAppOauth = true;
32
+ if (!targetAppUid) {
33
+ const intermediateRepresentation = await handleTranslate(projectDir, projectConfig, derivedAccountId, true, undefined);
34
+ if (intermediateRepresentation) {
35
+ Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).forEach(node => {
36
+ if (isAppIRNode(node)) {
37
+ targetAppUid = node.uid;
38
+ isAppOauth = node.config.auth.type === APP_AUTH_TYPES.OAUTH;
39
+ }
40
+ });
41
+ }
42
+ }
43
+ if (!targetAppUid) {
44
+ uiLogger.error(commands.app.subcommands.install.errors.noAppUidFound);
45
+ process.exit(EXIT_CODES.ERROR);
46
+ }
47
+ if (!isAppOauth) {
48
+ uiLogger.error(commands.app.subcommands.install.errors.appMustBeOauth);
49
+ process.exit(EXIT_CODES.ERROR);
50
+ }
51
+ try {
52
+ const { data } = await installOauthAppIntoDeveloperTestAccount(derivedAccountId, testAccountId, targetProjectName, targetAppUid);
53
+ if (data?.authCodes.length > 0) {
54
+ jsonOutput.authCode = data.authCodes[0].authCode;
55
+ }
56
+ }
57
+ catch (err) {
58
+ logError(err);
59
+ process.exit(EXIT_CODES.ERROR);
60
+ }
61
+ SpinniesManager.init({
62
+ succeedColor: 'white',
63
+ });
64
+ SpinniesManager.add('installApp', {
65
+ text: commands.app.subcommands.install.polling.start,
66
+ });
67
+ let appInstallSucceeded = false;
68
+ try {
69
+ await poll(() => fetchDeveloperTestAccountOauthAppInstallStatus(derivedAccountId, targetProjectName, targetAppUid), {
70
+ successStates: ['SUCCESS'],
71
+ errorStates: [],
72
+ });
73
+ appInstallSucceeded = true;
74
+ }
75
+ catch (err) {
76
+ SpinniesManager.fail('installApp');
77
+ logError(err);
78
+ process.exit(EXIT_CODES.ERROR);
79
+ }
80
+ if (!appInstallSucceeded) {
81
+ SpinniesManager.fail('installApp');
82
+ process.exit(EXIT_CODES.ERROR);
83
+ }
84
+ SpinniesManager.succeed('installApp', {
85
+ text: commands.app.subcommands.install.polling.success,
86
+ });
87
+ if (formatOutputAsJson) {
88
+ uiLogger.json(jsonOutput);
89
+ }
90
+ process.exit(EXIT_CODES.SUCCESS);
91
+ }
92
+ function installAppBuilder(yargs) {
93
+ yargs.positional('test-account-id', {
94
+ describe: commands.app.subcommands.install.positionals.testAccountId,
95
+ required: true,
96
+ type: 'number',
97
+ });
98
+ yargs.option('app-uid', {
99
+ describe: commands.app.subcommands.install.options.appUid,
100
+ type: 'string',
101
+ });
102
+ yargs.option('project-name', {
103
+ describe: commands.app.subcommands.install.options.projectName,
104
+ type: 'string',
105
+ });
106
+ yargs.example('install 1234567890 --app-uid=my-app-uid --project-name=my-project', commands.app.subcommands.install.example);
107
+ return yargs;
108
+ }
109
+ const builder = makeYargsBuilder(installAppBuilder, command, commands.app.subcommands.install.describe, {
110
+ useGlobalOptions: true,
111
+ useAccountOptions: true,
112
+ useConfigOptions: true,
113
+ useEnvironmentOptions: true,
114
+ useJSONOutputOptions: true,
115
+ });
116
+ const installAppCommand = {
117
+ command,
118
+ describe,
119
+ handler,
120
+ builder,
121
+ };
122
+ export default installAppCommand;
package/commands/app.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import migrateCommand from './app/migrate.js';
2
2
  import appSecretCommand from './app/secret.js';
3
+ import installAppCommand from './app/install.js';
3
4
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
4
5
  const command = ['app', 'apps'];
5
6
  // Keep the command hidden for now
6
7
  const describe = undefined;
7
8
  function appBuilder(yargs) {
8
- yargs.command(migrateCommand).command(appSecretCommand).demandCommand(1, '');
9
+ yargs
10
+ .command(migrateCommand)
11
+ .command(appSecretCommand)
12
+ .command(installAppCommand)
13
+ .demandCommand(1, '');
9
14
  return yargs;
10
15
  }
11
16
  const builder = makeYargsBuilder(appBuilder, command, describe);
@@ -16,7 +16,8 @@ import { handleProjectUpload } from '../lib/projects/upload.js';
16
16
  import { PROJECT_CONFIG_FILE, GET_STARTED_OPTIONS, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, } from '../lib/constants.js';
17
17
  import { writeProjectConfig, getProjectConfig, validateProjectConfig, } from '../lib/projects/config.js';
18
18
  import { getProjectPackageJsonLocations, installPackages, } from '../lib/dependencyManagement.js';
19
- import { pollProjectBuildAndDeploy, useV3Api, } from '../lib/projects/buildAndDeploy.js';
19
+ import { pollProjectBuildAndDeploy } from '../lib/projects/pollProjectBuildAndDeploy.js';
20
+ import { useV3Api } from '../lib/projects/platformVersion.js';
20
21
  import { openLink } from '../lib/links.js';
21
22
  import { getStaticAuthAppInstallUrl } from '../lib/app/urls.js';
22
23
  import { getEnv } from '@hubspot/local-dev-lib/config';
@@ -28,10 +29,10 @@ async function handler(args) {
28
29
  const { derivedAccountId } = args;
29
30
  const env = getEnv(derivedAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
30
31
  await trackCommandUsage('get-started', {}, derivedAccountId);
31
- // TODO: Put this in constants.ts once we have a defined place for the template before INBOUND
32
- const templateSource = 'robrown-hubspot/hubspot-project-components-ua-app-objects-beta';
32
+ const accountName = uiAccountDescription(derivedAccountId);
33
33
  uiInfoSection(commands.getStarted.startTitle, () => {
34
34
  uiLogger.log(commands.getStarted.startDescription);
35
+ uiLogger.log(commands.getStarted.guideOverview(accountName));
35
36
  });
36
37
  const { default: selectedOption } = await promptUser([
37
38
  {
@@ -80,16 +81,7 @@ async function handler(args) {
80
81
  else {
81
82
  uiLogger.log(' ');
82
83
  uiLogger.log(commands.getStarted.logs.appSelected);
83
- // 1. Fetch project templates
84
- let latestRepoReleaseTag;
85
84
  const { dest, name } = await projectNameAndDestPrompt(args);
86
- // Specific template for get-started command
87
- const projectTemplate = {
88
- name: 'private-app-get-started-template',
89
- label: 'CRM getting started project with private apps',
90
- path: 'projects/private-app-get-started-template',
91
- };
92
- // 3. Create the project files
93
85
  const projectDest = path.resolve(getCwd(), dest);
94
86
  const { projectConfig: existingProjectConfig, projectDir: existingProjectDir, } = await getProjectConfig(projectDest);
95
87
  if (existingProjectConfig &&
@@ -104,13 +96,10 @@ async function handler(args) {
104
96
  uiLogger.error(commands.project.create.errors.cannotNestProjects(existingProjectDir));
105
97
  process.exit(EXIT_CODES.ERROR);
106
98
  }
107
- const repo = templateSource || HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH;
108
99
  // 4. Clone the project template from GitHub
109
- // This is temporary until we have the UA template in the main repo
110
100
  try {
111
- await cloneGithubRepo(repo, projectDest, {
112
- sourceDir: projectTemplate.path,
113
- tag: latestRepoReleaseTag,
101
+ await cloneGithubRepo(HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, projectDest, {
102
+ sourceDir: '2025.2/private-app-get-started-template',
114
103
  hideLogs: true,
115
104
  });
116
105
  await trackCommandMetadataUsage('get-started', {
@@ -163,7 +152,6 @@ async function handler(args) {
163
152
  uiLogger.log(' ');
164
153
  }
165
154
  // 6. Ask user if they want to upload the project
166
- const accountName = uiAccountDescription(derivedAccountId);
167
155
  const { shouldUpload } = await promptUser([
168
156
  {
169
157
  type: 'confirm',
@@ -192,12 +180,11 @@ async function handler(args) {
192
180
  process.exit(EXIT_CODES.ERROR);
193
181
  }
194
182
  validateProjectConfig(newProjectConfig, newProjectDir);
195
- const targetAccountId = derivedAccountId;
196
183
  uiLogger.log(' ');
197
184
  uiLogger.log(commands.getStarted.logs.uploadingProject);
198
185
  uiLogger.log(' ');
199
186
  const { result, uploadError } = await handleProjectUpload({
200
- accountId: targetAccountId,
187
+ accountId: derivedAccountId,
201
188
  projectConfig: newProjectConfig,
202
189
  projectDir: newProjectDir,
203
190
  callbackFunc: pollProjectBuildAndDeploy,
@@ -4,13 +4,13 @@ import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from
4
4
  import { v3AddComponent } from '../../../lib/projects/add/v3AddComponent.js';
5
5
  import { legacyAddComponent } from '../../../lib/projects/add/legacyAddComponent.js';
6
6
  import { getProjectConfig } from '../../../lib/projects/config.js';
7
- import { useV3Api } from '../../../lib/projects/buildAndDeploy.js';
7
+ import { useV3Api } from '../../../lib/projects/platformVersion.js';
8
8
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
9
9
  vi.mock('../../../lib/commonOpts');
10
10
  vi.mock('../../../lib/projects/add/v3AddComponent');
11
11
  vi.mock('../../../lib/projects/add/legacyAddComponent');
12
12
  vi.mock('../../../lib/projects/config');
13
- vi.mock('../../../lib/projects/buildAndDeploy');
13
+ vi.mock('../../../lib/projects/platformVersion');
14
14
  vi.mock('../../../lib/usageTracking');
15
15
  const mockedV3AddComponent = vi.mocked(v3AddComponent);
16
16
  const mockedLegacyAddComponent = vi.mocked(legacyAddComponent);
@@ -82,15 +82,13 @@ describe('commands/project/add', () => {
82
82
  it('should call v3AddComponent with accountId for v3 projects', async () => {
83
83
  mockedUseV3Api.mockReturnValue(true);
84
84
  await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
85
- expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', undefined, 123);
86
85
  expect(mockedV3AddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
87
86
  expect(mockedLegacyAddComponent).not.toHaveBeenCalled();
88
87
  });
89
88
  it('should call legacyAddComponent for non-v3 projects', async () => {
90
89
  mockedUseV3Api.mockReturnValue(false);
91
90
  await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
92
- expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', undefined, 123);
93
- expect(mockedLegacyAddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig);
91
+ expect(mockedLegacyAddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
94
92
  expect(mockedV3AddComponent).not.toHaveBeenCalled();
95
93
  });
96
94
  it('should exit with error when project config is not found', async () => {
@@ -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');
@@ -1,11 +1,10 @@
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
3
  import { uiBetaTag } from '../../lib/ui/index.js';
5
4
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
6
5
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
7
6
  import { commands } from '../../lang/en.js';
8
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
7
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
9
8
  import { legacyAddComponent } from '../../lib/projects/add/legacyAddComponent.js';
10
9
  import { v3AddComponent } from '../../lib/projects/add/v3AddComponent.js';
11
10
  import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../lib/constants.js';
@@ -15,7 +14,6 @@ const describe = uiBetaTag(commands.project.add.describe, false);
15
14
  async function handler(args) {
16
15
  try {
17
16
  const { derivedAccountId } = args;
18
- trackCommandUsage('project-add', undefined, derivedAccountId);
19
17
  const { projectConfig, projectDir } = await getProjectConfig();
20
18
  if (!projectDir || !projectConfig) {
21
19
  uiLogger.error(commands.project.add.error.locationInProject);
@@ -26,7 +24,7 @@ async function handler(args) {
26
24
  await v3AddComponent(args, projectDir, projectConfig, derivedAccountId);
27
25
  }
28
26
  else {
29
- await legacyAddComponent(args, projectDir, projectConfig);
27
+ await legacyAddComponent(args, projectDir, projectConfig, derivedAccountId);
30
28
  }
31
29
  }
32
30
  catch (e) {
@@ -1,11 +1,10 @@
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
10
  import { uiBetaTag, uiLine } from '../../lib/ui/index.js';
@@ -13,54 +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
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
- }
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
  }
@@ -5,7 +5,7 @@ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
5
5
  import { uiBetaTag, uiLine } from '../../../lib/ui/index.js';
6
6
  import { deprecatedProjectDevFlow } from './deprecatedFlow.js';
7
7
  import { unifiedProjectDevFlow } from './unifiedFlow.js';
8
- import { useV3Api } from '../../../lib/projects/buildAndDeploy.js';
8
+ import { useV3Api } from '../../../lib/projects/platformVersion.js';
9
9
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
10
  import { loadProfile, exitIfUsingProfiles, } from '../../../lib/projectProfiles.js';
11
11
  import { commands } from '../../../lang/en.js';
@@ -22,6 +22,7 @@ import LocalDevWebsocketServer from '../../../lib/projects/localDev/LocalDevWebs
22
22
  export async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir, }) {
23
23
  const env = getValidEnv(getEnv(targetProjectAccountId));
24
24
  let projectNodes;
25
+ let projectProfileData;
25
26
  // Get IR
26
27
  try {
27
28
  const intermediateRepresentation = await translateForLocalDev({
@@ -30,6 +31,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
30
31
  accountId: targetProjectAccountId,
31
32
  }, { profile: args.profile });
32
33
  projectNodes = intermediateRepresentation.intermediateNodesIndexedByUid;
34
+ projectProfileData = intermediateRepresentation.profileData;
33
35
  uiLogger.debug(util.inspect(projectNodes, false, null, true));
34
36
  }
35
37
  catch (e) {
@@ -107,7 +109,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
107
109
  let project = uploadedProject;
108
110
  SpinniesManager.init();
109
111
  if (projectExists && project) {
110
- await compareLocalProjectToDeployed(projectConfig, targetProjectAccountId, project.deployedBuild?.buildId, projectNodes);
112
+ await compareLocalProjectToDeployed(projectConfig, targetProjectAccountId, project.deployedBuild?.buildId, projectNodes, args.profile);
111
113
  }
112
114
  else {
113
115
  project = await createNewProjectForLocalDev(projectConfig, targetProjectAccountId, false, false);
@@ -116,6 +118,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
116
118
  // End setup, start local dev process
117
119
  const localDevProcess = new LocalDevProcess({
118
120
  initialProjectNodes: projectNodes,
121
+ initialProjectProfileData: projectProfileData,
119
122
  debug: args.debug,
120
123
  profile: args.profile,
121
124
  targetProjectAccountId,
@@ -1,5 +1,5 @@
1
- import { CommonArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
1
+ import { CommonArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
+ type ProjectUploadArgs = CommonArgs & JSONOutputArgs & EnvironmentArgs & {
3
3
  forceCreate: boolean;
4
4
  message: string;
5
5
  m: string;
@@ -2,14 +2,14 @@ import chalk from 'chalk';
2
2
  import { logger } from '@hubspot/local-dev-lib/logger';
3
3
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
4
4
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
5
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
5
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
6
6
  import { uiBetaTag, uiCommandReference } from '../../lib/ui/index.js';
7
7
  import { trackCommandUsage } from '../../lib/usageTracking.js';
8
8
  import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
9
9
  import { logFeedbackMessage } from '../../lib/projects/ui.js';
10
10
  import { handleProjectUpload } from '../../lib/projects/upload.js';
11
11
  import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
12
- import { displayWarnLogs, pollProjectBuildAndDeploy, } from '../../lib/projects/buildAndDeploy.js';
12
+ import { displayWarnLogs, pollProjectBuildAndDeploy, } from '../../lib/projects/pollProjectBuildAndDeploy.js';
13
13
  import { i18n } from '../../lib/lang.js';
14
14
  import { PROJECT_ERROR_TYPES } from '../../lib/constants.js';
15
15
  import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
@@ -25,7 +25,7 @@ async function handler(args) {
25
25
  validateProjectConfig(projectConfig, projectDir);
26
26
  let targetAccountId;
27
27
  if (useV3Api(projectConfig.platformVersion)) {
28
- targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
28
+ targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile, args.useEnv);
29
29
  }
30
30
  targetAccountId = targetAccountId || derivedAccountId;
31
31
  const accountConfig = getAccountConfig(targetAccountId);
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
3
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
3
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
4
4
  import { trackCommandUsage } from '../../lib/usageTracking.js';
5
5
  import { uiLogger } from '../../lib/ui/logger.js';
6
6
  import { getProjectConfig, validateProjectConfig as validateProjectConfig, } from '../../lib/projects/config.js';
@@ -1,6 +1,6 @@
1
1
  import { cancelStagedBuild, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
2
2
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
3
- import { useV3Api } from '../../lib/projects/buildAndDeploy.js';
3
+ import { useV3Api } from '../../lib/projects/platformVersion.js';
4
4
  import { uiCommandReference, uiLink, uiBetaTag } from '../../lib/ui/index.js';
5
5
  import { i18n } from '../../lib/lang.js';
6
6
  import { createWatcher } from '../../lib/projects/watch.js';
@@ -11,7 +11,7 @@ import { trackCommandUsage } from '../../lib/usageTracking.js';
11
11
  import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
12
12
  import { logFeedbackMessage } from '../../lib/projects/ui.js';
13
13
  import { handleProjectUpload } from '../../lib/projects/upload.js';
14
- import { pollBuildStatus, pollDeployStatus, } from '../../lib/projects/buildAndDeploy.js';
14
+ import { pollBuildStatus, pollDeployStatus, } from '../../lib/projects/pollProjectBuildAndDeploy.js';
15
15
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
16
16
  import { handleKeypress, handleExit } from '../../lib/process.js';
17
17
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
@@ -75,6 +75,9 @@ async function handler(args) {
75
75
  resultJson.personalAccessKey = createResult.personalAccessKey;
76
76
  }
77
77
  catch (err) {
78
+ SpinniesManager.fail('createTestAccount', {
79
+ text: commands.testAccount.create.polling.createFailure,
80
+ });
78
81
  logError(err);
79
82
  SpinniesManager.fail('createTestAccount', {
80
83
  text: commands.testAccount.create.polling.createFailure,