@hubspot/cli 8.0.0-beta.0 → 8.0.0-beta.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.
@@ -101,12 +101,13 @@ describe('commands/cms/fetch', () => {
101
101
  await fetchCommand.handler(args);
102
102
  expect(trackCommandUsageSpy).toHaveBeenCalledWith('fetch', { mode: 'publish' }, 123456);
103
103
  });
104
- it('should fetch file successfully', async () => {
104
+ it('should fetch file successfully with increased timeout', async () => {
105
105
  await fetchCommand.handler(args);
106
106
  expect(downloadFileOrFolderSpy).toHaveBeenCalledWith(123456, '/remote/path/file.js', './local/path', 'publish', expect.objectContaining({
107
107
  assetVersion: undefined,
108
108
  staging: undefined,
109
109
  overwrite: undefined,
110
+ timeout: 60000,
110
111
  }));
111
112
  });
112
113
  it('should use default dest if not provided', async () => {
@@ -24,11 +24,12 @@ async function handler(options) {
24
24
  trackCommandUsage('fetch', { mode: cmsPublishMode }, derivedAccountId);
25
25
  const { assetVersion, staging, overwrite } = options;
26
26
  try {
27
- // Fetch and write file/folder.
27
+ // Fetch and write file/folder with increased timeout for large themes.
28
28
  await downloadFileOrFolder(derivedAccountId, src, resolveLocalPath(dest), cmsPublishMode, {
29
29
  assetVersion: assetVersion !== undefined ? `${assetVersion}` : assetVersion,
30
30
  staging,
31
31
  overwrite,
32
+ timeout: 60_000,
32
33
  });
33
34
  }
34
35
  catch (err) {
@@ -79,7 +79,7 @@ describe('commands/project/create', () => {
79
79
  projectCreateCommand.builder(yargsMock);
80
80
  const optionsCall = optionsSpy.mock.calls[0][0];
81
81
  expect(optionsCall.auth).toEqual(expect.objectContaining({
82
- describe: 'Authentication model for the application.',
82
+ describe: 'Authentication model for the app.',
83
83
  type: 'string',
84
84
  choices: ['oauth', 'static'],
85
85
  }));
@@ -2,6 +2,8 @@ import * as configLib from '@hubspot/local-dev-lib/config';
2
2
  import * as projectConfigLib from '../../../lib/projects/config.js';
3
3
  import * as platformVersionLib from '../../../lib/projects/platformVersion.js';
4
4
  import * as projectProfilesLib from '../../../lib/projects/projectProfiles.js';
5
+ import * as projectParsingProfiles from '@hubspot/project-parsing-lib/profiles';
6
+ import * as promptUtilsLib from '../../../lib/prompts/promptUtils.js';
5
7
  import * as usageTrackingLib from '../../../lib/usageTracking.js';
6
8
  import * as errorHandlers from '../../../lib/errorHandlers/index.js';
7
9
  import { uiLogger } from '../../../lib/ui/logger.js';
@@ -10,9 +12,11 @@ import * as deprecatedFlowLib from '../dev/deprecatedFlow.js';
10
12
  import * as unifiedFlowLib from '../dev/unifiedFlow.js';
11
13
  import projectDevCommand from '../dev/index.js';
12
14
  vi.mock('@hubspot/local-dev-lib/config');
15
+ vi.mock('@hubspot/project-parsing-lib/profiles');
13
16
  vi.mock('../../../lib/projects/config.js');
14
17
  vi.mock('../../../lib/projects/platformVersion.js');
15
18
  vi.mock('../../../lib/projects/projectProfiles.js');
19
+ vi.mock('../../../lib/prompts/promptUtils.js');
16
20
  vi.mock('../../../lib/errorHandlers/index.js');
17
21
  vi.mock('../../../lib/ui/index.js');
18
22
  vi.mock('../dev/deprecatedFlow.js');
@@ -23,6 +27,8 @@ const validateProjectConfigSpy = vi.spyOn(projectConfigLib, 'validateProjectConf
23
27
  const isV2ProjectSpy = vi.spyOn(platformVersionLib, 'isV2Project');
24
28
  const loadProfileSpy = vi.spyOn(projectProfilesLib, 'loadProfile');
25
29
  const enforceProfileUsageSpy = vi.spyOn(projectProfilesLib, 'enforceProfileUsage');
30
+ const getAllHsProfilesSpy = vi.spyOn(projectParsingProfiles, 'getAllHsProfiles');
31
+ const listPromptSpy = vi.spyOn(promptUtilsLib, 'listPrompt');
26
32
  const trackCommandUsageSpy = vi.spyOn(usageTrackingLib, 'trackCommandUsage');
27
33
  const logErrorSpy = vi.spyOn(errorHandlers, 'logError');
28
34
  const deprecatedProjectDevFlowSpy = vi.spyOn(deprecatedFlowLib, 'deprecatedProjectDevFlow');
@@ -46,6 +52,8 @@ describe('commands/project/dev', () => {
46
52
  deprecatedProjectDevFlowSpy.mockResolvedValue(undefined);
47
53
  unifiedProjectDevFlowSpy.mockResolvedValue(undefined);
48
54
  enforceProfileUsageSpy.mockResolvedValue(undefined);
55
+ getAllHsProfilesSpy.mockResolvedValue([]);
56
+ listPromptSpy.mockResolvedValue('dev');
49
57
  });
50
58
  describe('command', () => {
51
59
  it('should have the correct command structure', () => {
@@ -96,7 +104,11 @@ describe('commands/project/dev', () => {
96
104
  },
97
105
  projectDir: null,
98
106
  });
99
- await projectDevCommand.handler(args);
107
+ // Make process.exit actually throw to stop execution
108
+ processExitSpy.mockImplementation((code) => {
109
+ throw new Error(`process.exit called with ${code}`);
110
+ });
111
+ await expect(projectDevCommand.handler(args)).rejects.toThrow('process.exit called');
100
112
  expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining('project'));
101
113
  expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
102
114
  });
@@ -160,18 +172,37 @@ describe('commands/project/dev', () => {
160
172
  expect(logErrorSpy).toHaveBeenCalledWith(error);
161
173
  expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
162
174
  });
163
- it('should enforce profile usage for V2 projects without profile', async () => {
175
+ it('should prompt for profile selection when profiles exist and no profile is specified', async () => {
176
+ getAllHsProfilesSpy.mockResolvedValue(['dev', 'prod']);
177
+ listPromptSpy.mockResolvedValue('dev');
178
+ loadProfileSpy.mockReturnValue({
179
+ accountId: 789012,
180
+ variables: {},
181
+ });
164
182
  await projectDevCommand.handler(args);
165
- expect(enforceProfileUsageSpy).toHaveBeenCalledWith({
183
+ expect(getAllHsProfilesSpy).toHaveBeenCalledWith('/test/project/src');
184
+ expect(listPromptSpy).toHaveBeenCalledWith(expect.any(String), {
185
+ choices: ['dev', 'prod'],
186
+ });
187
+ expect(loadProfileSpy).toHaveBeenCalledWith({
166
188
  name: 'test-project',
167
189
  srcDir: 'src',
168
190
  platformVersion: 'v2',
169
- }, '/test/project');
191
+ }, '/test/project', 'dev');
192
+ expect(trackCommandUsageSpy).toHaveBeenCalledWith('project-dev', {}, 789012);
170
193
  });
171
- it('should exit if profile enforcement fails', async () => {
172
- const error = new Error('Profile required');
173
- enforceProfileUsageSpy.mockRejectedValue(error);
174
- await projectDevCommand.handler(args);
194
+ it('should exit if profile loading fails after selection', async () => {
195
+ getAllHsProfilesSpy.mockResolvedValue(['dev', 'prod']);
196
+ listPromptSpy.mockResolvedValue('dev');
197
+ const error = new Error('Failed to load profile');
198
+ loadProfileSpy.mockImplementation(() => {
199
+ throw error;
200
+ });
201
+ // Make process.exit actually throw to stop execution
202
+ processExitSpy.mockImplementation((code) => {
203
+ throw new Error(`process.exit called with ${code}`);
204
+ });
205
+ await expect(projectDevCommand.handler(args)).rejects.toThrow('process.exit called');
175
206
  expect(logErrorSpy).toHaveBeenCalledWith(error);
176
207
  expect(processExitSpy).toHaveBeenCalledWith(EXIT_CODES.ERROR);
177
208
  });
@@ -1,5 +1,6 @@
1
1
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
2
2
  import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
3
+ import { getAllHsProfiles, } from '@hubspot/project-parsing-lib/profiles';
3
4
  import { getProjectConfig, validateProjectConfig, } from '../../../lib/projects/config.js';
4
5
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
5
6
  import { uiLine } from '../../../lib/ui/index.js';
@@ -7,10 +8,12 @@ import { deprecatedProjectDevFlow } from './deprecatedFlow.js';
7
8
  import { unifiedProjectDevFlow } from './unifiedFlow.js';
8
9
  import { isV2Project } from '../../../lib/projects/platformVersion.js';
9
10
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
- import { loadProfile, enforceProfileUsage, } from '../../../lib/projects/projectProfiles.js';
11
+ import { loadProfile } from '../../../lib/projects/projectProfiles.js';
11
12
  import { commands } from '../../../lang/en.js';
12
13
  import { uiLogger } from '../../../lib/ui/logger.js';
13
14
  import { logError } from '../../../lib/errorHandlers/index.js';
15
+ import path from 'path';
16
+ import { listPrompt } from '../../../lib/prompts/promptUtils.js';
14
17
  const command = 'dev';
15
18
  const describe = commands.project.dev.describe;
16
19
  function validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV2) {
@@ -62,27 +65,27 @@ async function handler(args) {
62
65
  else if (userProvidedAccount && derivedAccountId) {
63
66
  targetProjectAccountId = derivedAccountId;
64
67
  }
68
+ // Determine profile name: from flag or prompt
65
69
  if (!targetProjectAccountId && isV2Project(projectConfig.platformVersion)) {
66
- if (args.profile) {
67
- try {
68
- profile = loadProfile(projectConfig, projectDir, args.profile);
69
- }
70
- catch (error) {
71
- logError(error);
72
- uiLine();
73
- process.exit(EXIT_CODES.ERROR);
70
+ let profileName = args.profile;
71
+ if (!profileName) {
72
+ const existingProfiles = await getAllHsProfiles(path.join(projectDir, projectConfig.srcDir));
73
+ if (existingProfiles.length !== 0) {
74
+ profileName = await listPrompt(commands.project.dev.prompts.selectProfile, {
75
+ choices: existingProfiles,
76
+ });
74
77
  }
75
- targetProjectAccountId = profile.accountId;
76
- uiLogger.log('');
77
- uiLogger.log(commands.project.dev.logs.profileProjectAccountExplanation(targetProjectAccountId, args.profile));
78
78
  }
79
- else {
80
- // A profile must be specified if this project has profiles configured
79
+ if (profileName) {
81
80
  try {
82
- await enforceProfileUsage(projectConfig, projectDir);
81
+ profile = loadProfile(projectConfig, projectDir, profileName);
82
+ targetProjectAccountId = profile.accountId;
83
+ uiLogger.log('');
84
+ uiLogger.log(commands.project.dev.logs.profileProjectAccountExplanation(targetProjectAccountId, profileName));
83
85
  }
84
86
  catch (error) {
85
87
  logError(error);
88
+ uiLine();
86
89
  process.exit(EXIT_CODES.ERROR);
87
90
  }
88
91
  }
@@ -114,7 +114,7 @@ async function handler(args) {
114
114
  process.exit(EXIT_CODES.ERROR);
115
115
  }
116
116
  SpinniesManager.succeed('createTestAccount', {
117
- text: commands.testAccount.create.polling.success(testAccountConfig.accountName, resultJson.accountId),
117
+ text: commands.testAccount.create.polling.success(testAccountConfig.accountName, resultJson.accountId, derivedAccountId),
118
118
  });
119
119
  if (formatOutputAsJson) {
120
120
  uiLogger.json(resultJson);
package/lang/en.d.ts CHANGED
@@ -1369,6 +1369,9 @@ export declare const commands: {
1369
1369
  examples: {
1370
1370
  default: string;
1371
1371
  };
1372
+ prompts: {
1373
+ selectProfile: string;
1374
+ };
1372
1375
  options: {
1373
1376
  profile: string;
1374
1377
  projectAccount: string;
@@ -2160,7 +2163,7 @@ export declare const commands: {
2160
2163
  polling: {
2161
2164
  start: (testAccountName: string) => string;
2162
2165
  syncing: string;
2163
- success: (testAccountName: string, testAccountId: number) => string;
2166
+ success: (testAccountName: string, testAccountId: number, parentAccountId: number) => string;
2164
2167
  createFailure: string;
2165
2168
  };
2166
2169
  options: {
package/lang/en.js CHANGED
@@ -1219,7 +1219,7 @@ export const commands = {
1219
1219
  windsurf: 'Windsurf',
1220
1220
  vsCode: 'VSCode',
1221
1221
  args: {
1222
- client: 'Target applications to configure',
1222
+ client: 'Target apps to configure',
1223
1223
  docsSearch: 'Should the docs search mcp server be installed',
1224
1224
  },
1225
1225
  success: (derivedTargets) => `You can now use the HubSpot CLI MCP Server in ${derivedTargets.join(', ')}. ${chalk.bold('You may need to restart these tools to apply the changes')}.`,
@@ -1261,7 +1261,7 @@ export const commands = {
1261
1261
  },
1262
1262
  prompts: {
1263
1263
  targets: '[--client] Which tools would you like to add the HubSpot CLI MCP server to?',
1264
- targetsRequired: 'Must choose at least one application to configure.',
1264
+ targetsRequired: 'Must choose at least one app to configure.',
1265
1265
  },
1266
1266
  },
1267
1267
  start: {
@@ -1385,6 +1385,9 @@ export const commands = {
1385
1385
  examples: {
1386
1386
  default: 'Start local dev for the current project',
1387
1387
  },
1388
+ prompts: {
1389
+ selectProfile: '[--profile] Select a profile to use for local development',
1390
+ },
1388
1391
  options: {
1389
1392
  profile: 'The profile to target during local dev',
1390
1393
  projectAccount: 'The id of the account to upload your project to. Must be used with --testing-account. Supported on platform versions 2025.2 and newer.',
@@ -1438,7 +1441,7 @@ export const commands = {
1438
1441
  describe: 'How the app will be distributed.',
1439
1442
  },
1440
1443
  auth: {
1441
- describe: 'Authentication model for the application.',
1444
+ describe: 'Authentication model for the app.',
1442
1445
  },
1443
1446
  features: {
1444
1447
  describe: `Features to include in the project. Only valid if project-base is ${PROJECT_WITH_APP}`,
@@ -1536,13 +1539,13 @@ export const commands = {
1536
1539
  describe: "The path to the component type's location within the hubspot-project-components Github repo: https://github.com/HubSpot/hubspot-project-components",
1537
1540
  },
1538
1541
  distribution: {
1539
- describe: 'The distribution method for the application.',
1542
+ describe: 'The distribution method for the app.',
1540
1543
  },
1541
1544
  auth: {
1542
- describe: 'The authentication type for the application.',
1545
+ describe: 'The authentication type for the app.',
1543
1546
  },
1544
1547
  features: {
1545
- describe: 'Which features to include with the application.',
1548
+ describe: 'Which features to include with the app.',
1546
1549
  },
1547
1550
  },
1548
1551
  creatingComponent: (projectName) => `Adding feature(s) to app [${chalk.bold(projectName)}]\n`,
@@ -2183,7 +2186,7 @@ export const commands = {
2183
2186
  polling: {
2184
2187
  start: (testAccountName) => `Creating test account "${chalk.bold(testAccountName)}"...`,
2185
2188
  syncing: 'Test account created! Syncing account data... (may take a few minutes - you can exit and the sync will continue)',
2186
- success: (testAccountName, testAccountId) => `Test account "${chalk.bold(testAccountName)}" successfully created with id: ${chalk.bold(testAccountId)}`,
2189
+ success: (testAccountName, testAccountId, parentAccountId) => `Test account "${chalk.bold(testAccountName)}" successfully created with id ${chalk.bold(testAccountId)} under parent account ${uiAccountDescription(parentAccountId)}`,
2187
2190
  createFailure: 'Failed to create test account.',
2188
2191
  },
2189
2192
  options: {
@@ -2206,7 +2209,7 @@ export const commands = {
2206
2209
  },
2207
2210
  createConfig: {
2208
2211
  describe: 'Create a test account config file.',
2209
- pathPrompt: '[--path] Enter the name of the Test Account config file: ',
2212
+ pathPrompt: '[--path] Enter the name of the test account config file: ',
2210
2213
  errors: {
2211
2214
  pathError: 'Path is required',
2212
2215
  pathFormatError: 'Path must end with .json',
@@ -2997,7 +3000,7 @@ export const lib = {
2997
3000
  privateApp: `This project contains a private app. Local development of private apps is not supported in developer accounts. Change your default account using ${uiCommandReference('hs account use')}, or link a new account with ${uiAuthCommandReference()}.`,
2998
3001
  },
2999
3002
  validateAccountOption: {
3000
- invalidPublicAppAccount: `This project contains a public app. The "--account" flag must point to a developer test account to develop this project locally. Alternatively, change your default account to an App Developer Account using ${uiCommandReference('hs account use')} and run ${uiCommandReference('hs project dev')} to set up a new Developer Test Account.`,
3003
+ invalidPublicAppAccount: `This project contains a public app. The "--account" flag must point to a developer test account to develop this project locally. Alternatively, change your default account to an app developer account using ${uiCommandReference('hs account use')} and run ${uiCommandReference('hs project dev')} to set up a new developer test account.`,
3001
3004
  invalidPrivateAppAccount: `This project contains a private app. The account specified with the "--account" flag points to a developer account, which do not support the local development of private apps. Update the "--account" flag to point to a standard, sandbox, or developer test account, or change your default account by running ${uiCommandReference('hs account use')}.`,
3002
3005
  nonSandboxWarning: `Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run ${uiCommandReference('hs account use')} before running the command again.`,
3003
3006
  publicAppNonDeveloperTestAccountWarning: `Local development of public apps is only supported in ${chalk.bold('developer test accounts')}.`,
@@ -3059,7 +3062,7 @@ export const lib = {
3059
3062
  prompt: {
3060
3063
  marketPlaceDistribution: 'On the HubSpot marketplace',
3061
3064
  privateDistribution: 'Privately',
3062
- distribution: '[--distribution] Choose how to distribute your application:',
3065
+ distribution: '[--distribution] Choose how to distribute your app:',
3063
3066
  auth: '[--auth] Choose your authentication type:',
3064
3067
  staticAuth: 'Static Auth',
3065
3068
  oauth: 'OAuth',
@@ -3386,10 +3389,10 @@ export const lib = {
3386
3389
  keepingCurrentDefault: (accountName) => `Account "${accountName}" will continue to be the default account`,
3387
3390
  },
3388
3391
  createDeveloperTestAccountConfigPrompt: {
3389
- namePrompt: (withFlag = true) => `${withFlag ? '[--name] ' : ''}Enter the name of the Test Account:`,
3390
- descriptionPrompt: (withFlag = true) => `${withFlag ? '[--description] ' : ''}Enter the description of the Test Account:`,
3392
+ namePrompt: (withFlag = true) => `${withFlag ? '[--name] ' : ''}Enter the name of the test account:`,
3393
+ descriptionPrompt: (withFlag = true) => `${withFlag ? '[--description] ' : ''}Enter the description of the test account:`,
3391
3394
  useDefaultAccountLevelsPrompt: {
3392
- message: 'Would you like to create a default Test Account, or customize your own?',
3395
+ message: 'Would you like to create a default test account, or customize your own?',
3393
3396
  default: 'Default (All Hubs, ENTERPRISE)',
3394
3397
  manual: 'Customize my own',
3395
3398
  },
@@ -3405,7 +3408,7 @@ export const lib = {
3405
3408
  errors: {
3406
3409
  allHubsRequired: 'Select a tier for each hub',
3407
3410
  tiersError: 'Cannot have more than one tier per hub',
3408
- nameRequired: 'The name may not be blank. Please add a name for the Test Account.',
3411
+ nameRequired: 'The name may not be blank. Please add a name for the test account.',
3409
3412
  },
3410
3413
  },
3411
3414
  accountNamePrompt: {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,278 @@
1
+ import { logError, debugError, ApiErrorContext, isErrorWithMessageOrReason, getErrorMessage, } from '../index.js';
2
+ import { uiLogger } from '../../ui/logger.js';
3
+ import { isHubSpotHttpError, isValidationError, } from '@hubspot/local-dev-lib/errors/index';
4
+ import { getConfig } from '@hubspot/local-dev-lib/config';
5
+ import { shouldSuppressError } from '../suppressError.js';
6
+ import { isProjectValidationError } from '../../errors/ProjectValidationError.js';
7
+ import { lib } from '../../../lang/en.js';
8
+ vi.mock('../../ui/logger.js');
9
+ vi.mock('@hubspot/local-dev-lib/errors/index');
10
+ vi.mock('@hubspot/local-dev-lib/config');
11
+ vi.mock('../suppressError.js');
12
+ vi.mock('../../errors/ProjectValidationError.js');
13
+ describe('lib/errorHandlers/index', () => {
14
+ const uiLoggerErrorMock = uiLogger.error;
15
+ const uiLoggerDebugMock = uiLogger.debug;
16
+ const isHubSpotHttpErrorMock = isHubSpotHttpError;
17
+ const isValidationErrorMock = isValidationError;
18
+ const getConfigMock = getConfig;
19
+ const shouldSuppressErrorMock = shouldSuppressError;
20
+ const isProjectValidationErrorMock = isProjectValidationError;
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ isHubSpotHttpErrorMock.mockReturnValue(false);
24
+ isValidationErrorMock.mockReturnValue(false);
25
+ shouldSuppressErrorMock.mockReturnValue(false);
26
+ isProjectValidationErrorMock.mockReturnValue(false);
27
+ getConfigMock.mockReturnValue({});
28
+ });
29
+ describe('logError', () => {
30
+ it('logs ProjectValidationError message and returns early', () => {
31
+ const error = {
32
+ message: 'Project validation failed',
33
+ name: 'ProjectValidationError',
34
+ };
35
+ isProjectValidationErrorMock.mockReturnValue(true);
36
+ logError(error);
37
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Project validation failed');
38
+ expect(uiLoggerErrorMock).toHaveBeenCalledTimes(1);
39
+ });
40
+ it('returns early when error should be suppressed', () => {
41
+ const error = new Error('Suppressed error');
42
+ shouldSuppressErrorMock.mockReturnValue(true);
43
+ logError(error);
44
+ expect(shouldSuppressErrorMock).toHaveBeenCalled();
45
+ expect(uiLoggerErrorMock).not.toHaveBeenCalled();
46
+ });
47
+ it('logs validation errors for HubSpotHttpError with validation errors', () => {
48
+ const mockError = {
49
+ formattedValidationErrors: vi
50
+ .fn()
51
+ .mockReturnValue('Formatted validation errors'),
52
+ updateContext: vi.fn(),
53
+ context: {},
54
+ };
55
+ isHubSpotHttpErrorMock.mockReturnValue(true);
56
+ isValidationErrorMock.mockReturnValue(true);
57
+ logError(mockError);
58
+ expect(mockError.formattedValidationErrors).toHaveBeenCalled();
59
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Formatted validation errors');
60
+ });
61
+ it('logs error message for errors with message property', () => {
62
+ const error = new Error('Something went wrong');
63
+ logError(error);
64
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Something went wrong');
65
+ });
66
+ it('logs error with both message and reason', () => {
67
+ const error = { message: 'Error message', reason: 'Error reason' };
68
+ logError(error);
69
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Error message Error reason');
70
+ });
71
+ it('logs unknown error message for errors without message or reason', () => {
72
+ const error = { foo: 'bar' };
73
+ logError(error);
74
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith(lib.errorHandlers.index.unknownErrorOccurred);
75
+ });
76
+ it('calls updateContext on HubSpotHttpError', () => {
77
+ const mockError = {
78
+ updateContext: vi.fn(),
79
+ context: {},
80
+ message: 'test',
81
+ };
82
+ isHubSpotHttpErrorMock.mockReturnValue(true);
83
+ logError(mockError);
84
+ expect(mockError.updateContext).toHaveBeenCalled();
85
+ });
86
+ describe('timeout error handling', () => {
87
+ it('shows config timeout message for direct ETIMEDOUT error matching default timeout', () => {
88
+ const mockError = {
89
+ code: 'ETIMEDOUT',
90
+ timeout: 15000,
91
+ updateContext: vi.fn(),
92
+ context: {},
93
+ message: 'Timeout',
94
+ };
95
+ isHubSpotHttpErrorMock.mockReturnValue(true);
96
+ getConfigMock.mockReturnValue({ httpTimeout: 15000 });
97
+ logError(mockError);
98
+ expect(uiLoggerErrorMock).toHaveBeenCalledTimes(2);
99
+ expect(uiLoggerErrorMock).toHaveBeenNthCalledWith(1, 'Timeout');
100
+ expect(uiLoggerErrorMock).toHaveBeenNthCalledWith(2, lib.errorHandlers.index.configTimeoutErrorOccurred(15000, 'hs config set'));
101
+ });
102
+ it('shows generic timeout message for direct ETIMEDOUT error with custom timeout', () => {
103
+ const mockError = {
104
+ code: 'ETIMEDOUT',
105
+ timeout: 30000,
106
+ updateContext: vi.fn(),
107
+ context: {},
108
+ message: 'Timeout',
109
+ };
110
+ isHubSpotHttpErrorMock.mockReturnValue(true);
111
+ getConfigMock.mockReturnValue({ httpTimeout: 15000 });
112
+ logError(mockError);
113
+ expect(uiLoggerErrorMock).toHaveBeenCalledTimes(2);
114
+ expect(uiLoggerErrorMock).toHaveBeenNthCalledWith(2, lib.errorHandlers.index.genericTimeoutErrorOccurred);
115
+ });
116
+ it('detects timeout error wrapped in error.cause', () => {
117
+ const causeError = {
118
+ code: 'ETIMEDOUT',
119
+ timeout: 15000,
120
+ name: 'HubSpotHttpError',
121
+ };
122
+ const wrapperError = new Error('Assets unavailable');
123
+ Object.defineProperty(wrapperError, 'cause', {
124
+ value: causeError,
125
+ writable: true,
126
+ });
127
+ isHubSpotHttpErrorMock.mockImplementation(err => {
128
+ return err === causeError;
129
+ });
130
+ getConfigMock.mockReturnValue({ httpTimeout: 15000 });
131
+ logError(wrapperError);
132
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Assets unavailable');
133
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith(lib.errorHandlers.index.configTimeoutErrorOccurred(15000, 'hs config set'));
134
+ });
135
+ it('shows generic timeout message for wrapped timeout with different timeout value', () => {
136
+ const causeError = {
137
+ code: 'ETIMEDOUT',
138
+ timeout: 60000,
139
+ name: 'HubSpotHttpError',
140
+ };
141
+ const wrapperError = new Error('Assets unavailable');
142
+ Object.defineProperty(wrapperError, 'cause', {
143
+ value: causeError,
144
+ writable: true,
145
+ });
146
+ isHubSpotHttpErrorMock.mockImplementation(err => {
147
+ return err === causeError;
148
+ });
149
+ getConfigMock.mockReturnValue({ httpTimeout: 15000 });
150
+ logError(wrapperError);
151
+ expect(uiLoggerErrorMock).toHaveBeenCalledTimes(2);
152
+ expect(uiLoggerErrorMock).toHaveBeenNthCalledWith(2, lib.errorHandlers.index.genericTimeoutErrorOccurred);
153
+ });
154
+ it('does not show timeout message for non-timeout errors', () => {
155
+ const error = new Error('Regular error');
156
+ logError(error);
157
+ expect(uiLoggerErrorMock).toHaveBeenCalledTimes(1);
158
+ expect(uiLoggerErrorMock).toHaveBeenCalledWith('Regular error');
159
+ });
160
+ });
161
+ });
162
+ describe('debugError', () => {
163
+ it('logs HubSpotHttpError using toString', () => {
164
+ const mockError = {
165
+ toString: vi.fn().mockReturnValue('HubSpotHttpError details'),
166
+ };
167
+ isHubSpotHttpErrorMock.mockReturnValue(true);
168
+ debugError(mockError);
169
+ expect(uiLoggerDebugMock).toHaveBeenCalledWith('HubSpotHttpError details');
170
+ });
171
+ it('logs regular error using lib.errorHandlers.index.errorOccurred', () => {
172
+ const error = new Error('Regular error');
173
+ debugError(error);
174
+ expect(uiLoggerDebugMock).toHaveBeenCalledWith(lib.errorHandlers.index.errorOccurred('Error: Regular error'));
175
+ });
176
+ it('logs error.cause when it is a HubSpotHttpError', () => {
177
+ const causeError = {
178
+ toString: vi.fn().mockReturnValue('Cause error details'),
179
+ };
180
+ const error = new Error('Wrapper error');
181
+ Object.defineProperty(error, 'cause', {
182
+ value: causeError,
183
+ writable: true,
184
+ });
185
+ isHubSpotHttpErrorMock.mockImplementation(err => {
186
+ return err === causeError;
187
+ });
188
+ debugError(error);
189
+ expect(causeError.toString).toHaveBeenCalled();
190
+ expect(uiLoggerDebugMock).toHaveBeenCalledWith('Cause error details');
191
+ });
192
+ it('logs error.cause using lib.errorHandlers.index.errorCause when not a HubSpotHttpError', () => {
193
+ const causeError = { customField: 'value' };
194
+ const error = new Error('Wrapper error');
195
+ Object.defineProperty(error, 'cause', {
196
+ value: causeError,
197
+ writable: true,
198
+ });
199
+ debugError(error);
200
+ expect(uiLoggerDebugMock).toHaveBeenCalledWith(expect.stringMatching(/^Cause:/));
201
+ });
202
+ it('logs context using lib.errorHandlers.index.errorContext when provided', () => {
203
+ const error = new Error('Error');
204
+ const context = new ApiErrorContext({
205
+ accountId: 123,
206
+ request: '/api/test',
207
+ });
208
+ debugError(error, context);
209
+ expect(uiLoggerDebugMock).toHaveBeenCalledWith(expect.stringMatching(/^Context:/));
210
+ });
211
+ });
212
+ describe('ApiErrorContext', () => {
213
+ it('creates context with all properties', () => {
214
+ const context = new ApiErrorContext({
215
+ accountId: 123,
216
+ request: '/api/test',
217
+ payload: '{"data": "value"}',
218
+ projectName: 'my-project',
219
+ });
220
+ expect(context.accountId).toBe(123);
221
+ expect(context.request).toBe('/api/test');
222
+ expect(context.payload).toBe('{"data": "value"}');
223
+ expect(context.projectName).toBe('my-project');
224
+ });
225
+ it('creates context with default values', () => {
226
+ const context = new ApiErrorContext();
227
+ expect(context.accountId).toBeUndefined();
228
+ expect(context.request).toBe('');
229
+ expect(context.payload).toBe('');
230
+ expect(context.projectName).toBe('');
231
+ });
232
+ it('creates context with partial properties', () => {
233
+ const context = new ApiErrorContext({
234
+ accountId: 456,
235
+ });
236
+ expect(context.accountId).toBe(456);
237
+ expect(context.request).toBe('');
238
+ expect(context.payload).toBe('');
239
+ expect(context.projectName).toBe('');
240
+ });
241
+ });
242
+ describe('isErrorWithMessageOrReason', () => {
243
+ it('returns true for object with message property', () => {
244
+ expect(isErrorWithMessageOrReason({ message: 'test' })).toBe(true);
245
+ });
246
+ it('returns true for object with reason property', () => {
247
+ expect(isErrorWithMessageOrReason({ reason: 'test' })).toBe(true);
248
+ });
249
+ it('returns true for object with both message and reason', () => {
250
+ expect(isErrorWithMessageOrReason({ message: 'msg', reason: 'rsn' })).toBe(true);
251
+ });
252
+ it('returns true for Error instances', () => {
253
+ expect(isErrorWithMessageOrReason(new Error('test'))).toBe(true);
254
+ });
255
+ it('returns false for null', () => {
256
+ expect(isErrorWithMessageOrReason(null)).toBe(false);
257
+ });
258
+ it('returns false for undefined', () => {
259
+ expect(isErrorWithMessageOrReason(undefined)).toBe(false);
260
+ });
261
+ it('returns false for primitive values', () => {
262
+ expect(isErrorWithMessageOrReason('string')).toBe(false);
263
+ expect(isErrorWithMessageOrReason(123)).toBe(false);
264
+ expect(isErrorWithMessageOrReason(true)).toBe(false);
265
+ });
266
+ it('returns false for object without message or reason', () => {
267
+ expect(isErrorWithMessageOrReason({ foo: 'bar' })).toBe(false);
268
+ });
269
+ it('returns false for empty object', () => {
270
+ expect(isErrorWithMessageOrReason({})).toBe(false);
271
+ });
272
+ });
273
+ describe('getErrorMessage', () => {
274
+ it('returns message from Error instance', () => {
275
+ expect(getErrorMessage(new Error('Error message'))).toBe('Error message');
276
+ });
277
+ });
278
+ });
@@ -36,12 +36,21 @@ export function logError(error, context) {
36
36
  // Unknown errors
37
37
  uiLogger.error(lib.errorHandlers.index.unknownErrorOccurred);
38
38
  }
39
+ let timeoutError = null;
39
40
  if (isHubSpotHttpError(error) && error.code === 'ETIMEDOUT') {
41
+ timeoutError = error;
42
+ }
43
+ else if (error instanceof Error &&
44
+ isHubSpotHttpError(error.cause) &&
45
+ error.cause.code === 'ETIMEDOUT') {
46
+ timeoutError = error.cause;
47
+ }
48
+ if (timeoutError) {
40
49
  const config = getConfig();
41
50
  const defaultTimeout = config?.httpTimeout;
42
51
  // Timeout was caused by the default timeout
43
- if (error.timeout && defaultTimeout === error.timeout) {
44
- uiLogger.error(lib.errorHandlers.index.configTimeoutErrorOccurred(error.timeout, 'hs config set'));
52
+ if (timeoutError.timeout && defaultTimeout === timeoutError.timeout) {
53
+ uiLogger.error(lib.errorHandlers.index.configTimeoutErrorOccurred(timeoutError.timeout, 'hs config set'));
45
54
  }
46
55
  // Timeout was caused by a custom timeout set by the CLI or LDL
47
56
  else {
@@ -285,7 +285,7 @@ describe('lib/projects/components', () => {
285
285
  type: 'app',
286
286
  uid: 'app_test_app',
287
287
  config: {
288
- name: 'test-app-Application',
288
+ name: 'test-app-App',
289
289
  other: 'property',
290
290
  },
291
291
  }, null, 2));
@@ -190,7 +190,7 @@ export async function updateHsMetaFilesWithAutoGeneratedFields(projectName, hsMe
190
190
  }
191
191
  component.uid = uid;
192
192
  if (component.type === AppKey && component.config) {
193
- component.config.name = `${projectName}-Application`;
193
+ component.config.name = `${projectName}-App`;
194
194
  }
195
195
  fs.writeFileSync(hsMetaFile, JSON.stringify(component, null, 2));
196
196
  }
@@ -19,7 +19,7 @@ const inputSchema = {
19
19
  z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
20
20
  z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
21
21
  ]))
22
- .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your application on the HubSpot marketplace. '),
22
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. '),
23
23
  auth: z
24
24
  .optional(z.union([
25
25
  z.literal(APP_AUTH_TYPES.STATIC),
@@ -47,7 +47,7 @@ export class AddFeatureToProjectTool extends Tool {
47
47
  command = addFlag(command, 'distribution', distribution);
48
48
  }
49
49
  else if (addApp) {
50
- content.push(formatTextContent(`Ask the user how they would you like to distribute the application. Options are ${APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${APP_DISTRIBUTION_TYPES.PRIVATE}`));
50
+ content.push(formatTextContent(`Ask the user how they would you like to distribute the app. Options are ${APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${APP_DISTRIBUTION_TYPES.PRIVATE}`));
51
51
  }
52
52
  if (auth) {
53
53
  command = addFlag(command, 'auth', auth);
@@ -25,7 +25,7 @@ const inputSchema = {
25
25
  z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
26
26
  z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
27
27
  ]))
28
- .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your application on the HubSpot marketplace. '),
28
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your app on the HubSpot marketplace. '),
29
29
  auth: z
30
30
  .optional(z.union([
31
31
  z.literal(APP_AUTH_TYPES.STATIC),
@@ -63,7 +63,7 @@ export class CreateProjectTool extends Tool {
63
63
  command = addFlag(command, 'distribution', distribution);
64
64
  }
65
65
  else if (projectBase === PROJECT_WITH_APP) {
66
- content.push(formatTextContent(`Ask the user how they would you like to distribute the application? Options are ${APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${APP_DISTRIBUTION_TYPES.PRIVATE}`));
66
+ content.push(formatTextContent(`Ask the user how they would you like to distribute the app? Options are ${APP_DISTRIBUTION_TYPES.MARKETPLACE} and ${APP_DISTRIBUTION_TYPES.PRIVATE}`));
67
67
  }
68
68
  if (auth) {
69
69
  command = addFlag(command, 'auth', auth);
@@ -13,7 +13,7 @@ const inputSchema = {
13
13
  appId: z
14
14
  .string()
15
15
  .regex(/^\d+$/, 'App ID must be a numeric string')
16
- .describe('The numeric app ID as a string (e.g., "3003909"). Use get-applications-info to find available app IDs.'),
16
+ .describe('The numeric app ID as a string (e.g., "3003909"). Use get-apps-info to find available app IDs.'),
17
17
  startDate: z
18
18
  .string()
19
19
  .regex(/^\d{4}-\d{2}-\d{2}$/, 'Start date must be in YYYY-MM-DD format')
@@ -65,7 +65,7 @@ export class GetApiUsagePatternsByAppIdTool extends Tool {
65
65
  register() {
66
66
  return this.mcpServer.registerTool(toolName, {
67
67
  title: 'Get API Usage Patterns by App ID',
68
- description: 'Retrieves detailed API usage pattern analytics for a specific HubSpot application. Requires an appId (string) to identify the target application. Optionally accepts startDate and endDate parameters in YYYY-MM-DD format to filter results within a specific time range. Returns patternSummaries object containing usage statistics including portalPercentage (percentage of portals using this pattern) and numOfPortals (total count of portals) for different usage patterns. This data helps analyze how the application is being used across different HubSpot portals and can inform optimization decisions.',
68
+ description: 'Retrieves detailed API usage pattern analytics for a specific HubSpot app. Requires an appId (string) to identify the target app. Optionally accepts startDate and endDate parameters in YYYY-MM-DD format to filter results within a specific time range. Returns patternSummaries object containing usage statistics including portalPercentage (percentage of portals using this pattern) and numOfPortals (total count of portals) for different usage patterns. This data helps analyze how the app is being used across different HubSpot portals and can inform optimization decisions.',
69
69
  inputSchema,
70
70
  annotations: {
71
71
  readOnlyHint: true,
@@ -11,7 +11,7 @@ import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
11
11
  const inputSchema = { absoluteCurrentWorkingDirectory };
12
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
13
13
  const inputSchemaZodObject = z.object({ ...inputSchema });
14
- const toolName = 'get-applications-info';
14
+ const toolName = 'get-apps-info';
15
15
  export class GetApplicationInfoTool extends Tool {
16
16
  constructor(mcpServer) {
17
17
  super(mcpServer);
@@ -44,8 +44,8 @@ export class GetApplicationInfoTool extends Tool {
44
44
  }
45
45
  register() {
46
46
  return this.mcpServer.registerTool(toolName, {
47
- title: 'Get Applications Information',
48
- description: 'Retrieves a list of all HubSpot applications available in the current account. Returns an array of applications, where each application contains an appId (numeric identifier) and appName (string). This information is useful for identifying available applications before using other tools that require specific application IDs, such as getting API usage patterns. No input parameters are required - this tool fetches all applications from the HubSpot Insights API.',
47
+ title: 'Get Apps Information',
48
+ description: 'Retrieves a list of all HubSpot apps available in the current account. Returns an array of apps, where each app contains an appId (numeric identifier) and appName (string). This information is useful for identifying available apps before using other tools that require specific app IDs, such as getting API usage patterns. No input parameters are required - this tool fetches all apps from the HubSpot Insights API.',
49
49
  inputSchema,
50
50
  annotations: {
51
51
  readOnlyHint: true,
@@ -62,12 +62,15 @@ export class UploadProjectTools extends Tool {
62
62
  };
63
63
  }
64
64
  const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, command);
65
- return formatTextContents(stdout, stderr);
65
+ const response = await formatTextContents(stdout, stderr);
66
+ // Add reminder about cards needing to be added to views
67
+ response.content.push(formatTextContent('\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.'));
68
+ return response;
66
69
  }
67
70
  register() {
68
71
  return this.mcpServer.registerTool(toolName, {
69
72
  title: 'Upload HubSpot Project',
70
- description: 'DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive. Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.',
73
+ description: 'DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive. Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory. IMPORTANT: Uploading a project does NOT automatically make cards live or visible to users. Cards must be manually added to a view in HubSpot after upload to become visible.',
71
74
  inputSchema,
72
75
  annotations: {
73
76
  readOnlyHint: false,
@@ -87,7 +87,7 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
87
87
  expect(result.content).toEqual([
88
88
  {
89
89
  type: 'text',
90
- text: expect.stringContaining('Ask the user how they would you like to distribute the application'),
90
+ text: expect.stringContaining('Ask the user how they would you like to distribute the app'),
91
91
  },
92
92
  {
93
93
  type: 'text',
@@ -85,7 +85,7 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
85
85
  expect(result.content).toEqual([
86
86
  {
87
87
  type: 'text',
88
- text: expect.stringContaining('Ask the user how they would you like to distribute the application?'),
88
+ text: expect.stringContaining('Ask the user how they would you like to distribute the app?'),
89
89
  },
90
90
  {
91
91
  type: 'text',
@@ -36,7 +36,7 @@ describe('mcp-server/tools/project/GetApiUsagePatternsByAppIdTool', () => {
36
36
  const result = tool.register();
37
37
  expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-api-usage-patterns-by-app-id', expect.objectContaining({
38
38
  title: 'Get API Usage Patterns by App ID',
39
- description: expect.stringContaining('Retrieves detailed API usage pattern analytics for a specific HubSpot application'),
39
+ description: expect.stringContaining('Retrieves detailed API usage pattern analytics for a specific HubSpot app'),
40
40
  inputSchema: expect.objectContaining({
41
41
  appId: expect.objectContaining({
42
42
  describe: expect.any(Function),
@@ -34,9 +34,9 @@ describe('mcp-server/tools/project/GetApplicationInfoTool', () => {
34
34
  describe('register', () => {
35
35
  it('should register tool with correct parameters', () => {
36
36
  const result = tool.register();
37
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-applications-info', expect.objectContaining({
38
- title: 'Get Applications Information',
39
- description: expect.stringContaining('Retrieves a list of all HubSpot applications available in the current account'),
37
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-apps-info', expect.objectContaining({
38
+ title: 'Get Apps Information',
39
+ description: expect.stringContaining('Retrieves a list of all HubSpot apps available in the current account'),
40
40
  inputSchema: expect.any(Object),
41
41
  }), expect.any(Function));
42
42
  expect(result).toBe(mockRegisteredTool);
@@ -69,6 +69,10 @@ describe('mcp-server/tools/project/UploadProjectTools', () => {
69
69
  content: [
70
70
  { type: 'text', text: 'Project uploaded successfully' },
71
71
  { type: 'text', text: '' },
72
+ {
73
+ type: 'text',
74
+ text: '\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.',
75
+ },
72
76
  ],
73
77
  });
74
78
  });
@@ -81,6 +85,10 @@ describe('mcp-server/tools/project/UploadProjectTools', () => {
81
85
  expect(result.content).toEqual([
82
86
  { type: 'text', text: 'Project uploaded with warnings' },
83
87
  { type: 'text', text: 'Warning: some files were ignored' },
88
+ {
89
+ type: 'text',
90
+ text: '\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.',
91
+ },
84
92
  ]);
85
93
  });
86
94
  it('should handle upload errors', async () => {
@@ -136,6 +144,10 @@ describe('mcp-server/tools/project/UploadProjectTools', () => {
136
144
  expect(result.content).toEqual([
137
145
  { type: 'text', text: '' },
138
146
  { type: 'text', text: '' },
147
+ {
148
+ type: 'text',
149
+ text: '\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.',
150
+ },
139
151
  ]);
140
152
  });
141
153
  it('should work with different project paths', async () => {
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "8.0.0-beta.0",
3
+ "version": "8.0.0-beta.1",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
7
7
  "type": "module",
8
8
  "dependencies": {
9
9
  "@hubspot/cms-dev-server": "1.2.1",
10
- "@hubspot/local-dev-lib": "5.1.0",
11
- "@hubspot/project-parsing-lib": "0.11.1",
10
+ "@hubspot/local-dev-lib": "5.1.1",
11
+ "@hubspot/project-parsing-lib": "0.11.2",
12
12
  "@hubspot/serverless-dev-runtime": "7.0.7",
13
13
  "@hubspot/ui-extensions-dev-server": "1.1.3",
14
14
  "@inquirer/prompts": "7.1.0",
package/types/Cms.d.ts CHANGED
@@ -3,7 +3,7 @@ export declare const TEMPLATE_TYPES: readonly ["page-template", "email-template"
3
3
  export type TemplateType = (typeof TEMPLATE_TYPES)[number];
4
4
  export declare const HTTP_METHODS: readonly ["DELETE", "GET", "PATCH", "POST", "PUT"];
5
5
  export type HttpMethod = (typeof HTTP_METHODS)[number];
6
- export declare const CONTENT_TYPES: readonly ["ANY", "LANDING_PAGE", "SITE_PAGE", "BLOG_POST", "BLOG_LISTING", "EMAIL", "KNOWLEDGE_BASE", "QUOTE_TEMPLATE", "CUSTOMER_PORTAL", "WEB_INTERACTIVE", "SUBSCRIPTION", "MEMBERSHIP"];
6
+ export declare const CONTENT_TYPES: readonly ["ANY", "LANDING_PAGE", "SITE_PAGE", "BLOG_POST", "BLOG_LISTING", "EMAIL", "KNOWLEDGE_BASE", "QUOTE_TEMPLATE", "QUOTE", "QUOTE_BLUEPRINT", "CUSTOMER_PORTAL", "WEB_INTERACTIVE", "SUBSCRIPTION", "MEMBERSHIP"];
7
7
  export type ContentType = (typeof CONTENT_TYPES)[number];
8
8
  export type CreateArgs = CommonArgs & ConfigArgs & {
9
9
  branch?: string;
package/types/Cms.js CHANGED
@@ -18,6 +18,8 @@ export const CONTENT_TYPES = [
18
18
  'EMAIL',
19
19
  'KNOWLEDGE_BASE',
20
20
  'QUOTE_TEMPLATE',
21
+ 'QUOTE',
22
+ 'QUOTE_BLUEPRINT',
21
23
  'CUSTOMER_PORTAL',
22
24
  'WEB_INTERACTIVE',
23
25
  'SUBSCRIPTION',