@hubspot/cli 7.8.0-experimental.0 → 7.8.2-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 (143) hide show
  1. package/bin/cli.js +0 -2
  2. package/commands/__tests__/getStarted.test.js +2 -2
  3. package/commands/__tests__/mcp.test.js +1 -1
  4. package/commands/__tests__/project.test.js +0 -3
  5. package/commands/app/__tests__/migrate.test.js +0 -1
  6. package/commands/app/migrate.js +4 -5
  7. package/commands/app/secret/add.js +2 -1
  8. package/commands/app/secret/delete.js +2 -1
  9. package/commands/app/secret/list.js +2 -1
  10. package/commands/app/secret/update.js +2 -1
  11. package/commands/app/secret.js +2 -1
  12. package/commands/app.js +2 -2
  13. package/commands/config/set.js +0 -1
  14. package/commands/feedback.js +1 -1
  15. package/commands/getStarted.d.ts +1 -3
  16. package/commands/getStarted.js +66 -18
  17. package/commands/mcp/__tests__/setup.test.js +2 -2
  18. package/commands/mcp/setup.js +11 -2
  19. package/commands/mcp.js +3 -3
  20. package/commands/project/__tests__/create.test.js +6 -6
  21. package/commands/project/__tests__/deploy.test.js +0 -3
  22. package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
  23. package/commands/project/__tests__/logs.test.js +0 -3
  24. package/commands/project/__tests__/migrate.test.js +1 -2
  25. package/commands/project/__tests__/migrateApp.test.js +1 -2
  26. package/commands/project/__tests__/profile.test.js +1 -1
  27. package/commands/project/add.js +1 -5
  28. package/commands/project/create.js +3 -9
  29. package/commands/project/deploy.js +2 -2
  30. package/commands/project/dev/index.js +4 -3
  31. package/commands/project/dev/unifiedFlow.js +6 -4
  32. package/commands/project/download.js +1 -2
  33. package/commands/project/installDeps.js +1 -2
  34. package/commands/project/listBuilds.js +2 -2
  35. package/commands/project/logs.js +2 -2
  36. package/commands/project/migrate.js +28 -10
  37. package/commands/project/migrateApp.js +1 -2
  38. package/commands/project/open.js +1 -2
  39. package/commands/project/profile/add.js +3 -3
  40. package/commands/project/profile/delete.js +1 -2
  41. package/commands/project/profile.js +2 -3
  42. package/commands/project/upload.js +2 -2
  43. package/commands/project/validate.js +1 -1
  44. package/commands/project/watch.js +2 -2
  45. package/commands/project.js +1 -2
  46. package/commands/sandbox/delete.js +1 -1
  47. package/commands/testAccount/importData.d.ts +1 -1
  48. package/commands/testAccount/importData.js +1 -1
  49. package/commands/testAccount.js +1 -1
  50. package/lang/en.d.ts +15 -4
  51. package/lang/en.js +18 -6
  52. package/lib/__tests__/hasFeature.test.js +145 -7
  53. package/lib/app/__tests__/migrate.test.js +14 -51
  54. package/lib/app/migrate.d.ts +2 -8
  55. package/lib/app/migrate.js +5 -80
  56. package/lib/constants.d.ts +8 -0
  57. package/lib/constants.js +8 -0
  58. package/lib/dependencyManagement.d.ts +0 -5
  59. package/lib/dependencyManagement.js +0 -9
  60. package/lib/hasFeature.js +6 -0
  61. package/lib/links.d.ts +1 -0
  62. package/lib/links.js +10 -3
  63. package/lib/mcp/setup.js +1 -1
  64. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  65. package/lib/projects/__tests__/LocalDevProcess.test.js +227 -16
  66. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
  67. package/lib/projects/__tests__/deploy.test.js +71 -6
  68. package/lib/projects/__tests__/localDevProjectHelpers.test.js +4 -2
  69. package/lib/projects/create/__tests__/v3.test.js +79 -4
  70. package/lib/projects/create/v3.js +11 -8
  71. package/lib/projects/localDev/AppDevModeInterface.js +5 -5
  72. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  73. package/lib/projects/localDev/LocalDevLogger.js +22 -0
  74. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  75. package/lib/projects/localDev/LocalDevProcess.js +90 -19
  76. package/lib/projects/localDev/LocalDevState.d.ts +9 -8
  77. package/lib/projects/localDev/LocalDevState.js +18 -17
  78. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
  79. package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
  80. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  81. package/lib/projects/localDev/helpers/project.js +10 -7
  82. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
  83. package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
  84. package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
  85. package/lib/prompts/projectAddPrompt.js +2 -1
  86. package/lib/prompts/promptUtils.js +3 -0
  87. package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
  88. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  89. package/lib/theme/__tests__/migrate.test.js +233 -0
  90. package/lib/theme/migrate.d.ts +13 -0
  91. package/lib/theme/migrate.js +90 -0
  92. package/lib/ui/SpinniesManager.js +105 -8
  93. package/lib/usageTracking.js +2 -2
  94. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  95. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  96. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  97. package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
  98. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  99. package/mcp-server/tools/cms/HsListTool.js +1 -1
  100. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  101. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  102. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  103. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
  104. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  105. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  106. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  107. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  108. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  109. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  110. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  111. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  112. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  113. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  114. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  115. package/mcp-server/tools/project/GetConfigValuesTool.js +11 -5
  116. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  117. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  118. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  119. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  120. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  121. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  122. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  123. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  124. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  125. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  126. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  127. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  128. package/mcp-server/tools/project/constants.d.ts +1 -1
  129. package/mcp-server/tools/project/constants.js +9 -3
  130. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  131. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  132. package/mcp-server/utils/cliConfig.d.ts +1 -0
  133. package/mcp-server/utils/cliConfig.js +12 -0
  134. package/package.json +4 -9
  135. package/types/LocalDev.d.ts +19 -3
  136. package/ui/components/HorizontalSelectPrompt.js +1 -1
  137. package/ui/index.js +1 -1
  138. package/commands/getStartedV2.d.ts +0 -9
  139. package/commands/getStartedV2.js +0 -39
  140. package/ui/components/Ascii.d.ts +0 -10
  141. package/ui/components/Ascii.js +0 -11
  142. package/ui/views/GetStarted.d.ts +0 -7
  143. package/ui/views/GetStarted.js +0 -157
package/lang/en.js CHANGED
@@ -5,6 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
5
5
  import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
6
6
  import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
7
7
  import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
8
+ import { getProductUpdatesUrl } from '../lib/links.js';
8
9
  import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
9
10
  import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
10
11
  export const commands = {
@@ -18,6 +19,7 @@ export const commands = {
18
19
  },
19
20
  },
20
21
  getStarted: {
22
+ describe: 'A step-by-step command to get you started with a HubSpot project.',
21
23
  options: {
22
24
  dest: {
23
25
  describe: 'Directory where the project should be created',
@@ -862,8 +864,9 @@ export const commands = {
862
864
  tailLogs: (functionPath, accountId) => `Waiting for log entries for "${functionPath}" on account "${accountId}".\n`,
863
865
  },
864
866
  mcp: {
865
- describe: 'Commands for managing HubSpot MCP servers',
867
+ describe: 'Commands for managing HubSpot MCP servers.',
866
868
  setup: {
869
+ describe: 'Setup the HubSpot development MCP servers.',
867
870
  installingDocSearch: 'Adding the docs-search mcp server',
868
871
  claudeCode: 'Claude Code',
869
872
  cursor: 'Cursor',
@@ -875,6 +878,7 @@ export const commands = {
875
878
  },
876
879
  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')}.`,
877
880
  errors: {
881
+ needsMcpAccess: (accountId) => `You must opt in to the developer MCP beta to use this feature on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('239890', accountId))}`,
878
882
  needsNode20: `This feature requires node >=20`,
879
883
  errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
880
884
  },
@@ -1009,10 +1013,10 @@ export const commands = {
1009
1013
  dev: {
1010
1014
  describe: 'Start local dev for the current project.',
1011
1015
  logs: {
1012
- betaMessage: 'HubSpot projects local development',
1016
+ header: 'HubSpot projects local development',
1013
1017
  placeholderAccountSelection: 'Using default account as target account (for now)',
1014
1018
  accountTypeInformation: 'Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.',
1015
- learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://hubspot.mintlify.io/en-us/developer-tooling/local-development/hubspot-cli/project-commands#start-a-local-development-server')} | ${uiLink('HubSpot account types', 'https://developers.hubspot.com/docs/getting-started/account-types')}`,
1019
+ learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://developers.hubspot.com/docs/developer-tooling/local-development/hubspot-cli/project-commands#start-a-local-development-server')} | ${uiLink('HubSpot account types', 'https://developers.hubspot.com/docs/getting-started/account-types')}`,
1016
1020
  learnMoreMessageLegacy: uiLink('Learn more about the projects local dev server', 'https://developers.hubspot.com/docs/platform/project-cli-commands#start-a-local-development-server'),
1017
1021
  profileProjectAccountExplanation: (accountId, profileName) => `Using account ${uiAccountDescription(accountId)} from profile ${chalk.bold(profileName)} for project upload`,
1018
1022
  defaultProjectAccountExplanation: (accountId) => `Using default account ${uiAccountDescription(accountId)} for project upload`,
@@ -1069,7 +1073,7 @@ export const commands = {
1069
1073
  describe: 'Project name (cannot be changed)',
1070
1074
  },
1071
1075
  template: {
1072
- describe: 'The starting template',
1076
+ describe: 'The starting template. Only applies when platform version is less than 2025.2.',
1073
1077
  },
1074
1078
  templateSource: {
1075
1079
  describe: 'Path to custom GitHub repository from which to create project template',
@@ -1135,6 +1139,7 @@ export const commands = {
1135
1139
  describe: 'Migrate an existing project to the new version of the projects framework.',
1136
1140
  errors: {
1137
1141
  noProjectConfig: (command) => `No project detected. Please run this command again from a project directory. If you are trying to migrate an app, run ${command}`,
1142
+ noThemeMigrationAccess: (accountId) => `This project contains a CMS theme. You must opt in to theme migration beta to continue updating it on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('253920', accountId))}`,
1138
1143
  },
1139
1144
  examples: {
1140
1145
  default: 'Migrate an existing project to the new version of the projects framework.',
@@ -1192,7 +1197,7 @@ export const commands = {
1192
1197
  maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
1193
1198
  authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
1194
1199
  distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
1195
- portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature`,
1200
+ portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature.`,
1196
1201
  locationInProject: 'This command must be run from within a project directory.',
1197
1202
  failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
1198
1203
  projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
@@ -2588,8 +2593,13 @@ export const lib = {
2588
2593
  LocalDevProcess: {
2589
2594
  projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${uiCommandReference('hs project dev')}.`,
2590
2595
  uploadInitiated: 'Project upload initiated from Local Dev UI.',
2596
+ deployInitiated: 'Project deploy initiated from Local Dev UI.',
2591
2597
  uploadFailed: 'Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.',
2598
+ deployFailed: 'Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.',
2592
2599
  uploadSuccess: 'Project upload completed successfully. Resuming local dev...',
2600
+ uploadSuccessAutoDeployDisabled: 'Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.',
2601
+ deploySuccess: 'Project deploy completed successfully. Resuming local dev...',
2602
+ noBuildToDeploy: 'Error deploying project. No build was found to deploy.',
2593
2603
  },
2594
2604
  localDevHelpers: {
2595
2605
  project: {
@@ -2598,7 +2608,7 @@ export const lib = {
2598
2608
  checking: 'Checking if your deployed build is up to date...',
2599
2609
  upToDate: 'Deployed build is up to date.',
2600
2610
  notUpToDate: `Your project contains undeployed local changes.`,
2601
- notUpToDateExplanation: `Run ${uiCommandReference('hs project upload')} to upload these changes to HubSpot, then re-run ${uiCommandReference('hs project dev')} to continue local development.`,
2611
+ notUpToDateExplanation: (profile) => `Run ${uiCommandReference(`hs project upload ${profile ? `--profile ${profile}` : ''}`)} to upload these changes to HubSpot, then re-run ${uiCommandReference(`hs project dev ${profile ? `--profile ${profile}` : ''}`)} to continue local development.`,
2602
2612
  },
2603
2613
  createNewProjectForLocalDev: {
2604
2614
  projectMustExistExplanation: (projectName, accountId) => `The project ${projectName} does not exist in the target account ${uiAccountDescription(accountId)}. This command requires the project to exist in the target account.`,
@@ -3105,6 +3115,7 @@ export const lib = {
3105
3115
  },
3106
3116
  projectAddPrompt: {
3107
3117
  selectType: '[--type] Select an app feature to add: ',
3118
+ selectFeatures: '[--features] Select an app feature to add: ',
3108
3119
  enterName: '[--name] Give your component a name: ',
3109
3120
  errors: {
3110
3121
  nameRequired: 'A component name is required',
@@ -3382,6 +3393,7 @@ export const lib = {
3382
3393
  sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
3383
3394
  projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
3384
3395
  projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion 2025.2. This action cannot be undone. To ensure you have access to your original files, they will be copied to a new directory (archive) for safekeeping.\n\nThis command will guide you through the process, prompting you to enter the required fields and will download the new project source code into your project source directory.`, false),
3396
+ exitWithoutMigrating: 'Exiting without migrating',
3385
3397
  success: {
3386
3398
  downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
3387
3399
  themesMigrationSuccess: (platformVersion) => `Successfully migrated project to platformVersion ${chalk.bold(platformVersion)}. Upload your project using ${uiCommandReference('hs project upload')}`,
@@ -1,35 +1,173 @@
1
1
  import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
2
- import { hasFeature } from '../hasFeature.js';
2
+ import { http } from '@hubspot/local-dev-lib/http';
3
+ import { hasFeature, hasUnfiedAppsAccess } from '../hasFeature.js';
4
+ import { FEATURES } from '../constants.js';
3
5
  vi.mock('@hubspot/local-dev-lib/api/localDevAuth');
6
+ vi.mock('@hubspot/local-dev-lib/http');
4
7
  const mockedFetchEnabledFeatures = fetchEnabledFeatures;
8
+ const mockedHttp = http;
5
9
  describe('lib/hasFeature', () => {
6
10
  describe('hasFeature()', () => {
7
11
  const accountId = 123;
8
- beforeEach(() => {
12
+ afterEach(() => {
13
+ vi.clearAllMocks();
14
+ });
15
+ it('should return true if the feature is enabled', async () => {
9
16
  mockedFetchEnabledFeatures.mockResolvedValueOnce({
10
17
  data: {
11
18
  enabledFeatures: {
12
19
  'feature-1': true,
13
- 'feature-2': false,
14
- 'feature-3': true,
15
20
  },
16
21
  },
17
22
  });
18
- });
19
- it('should return true if the feature is enabled', async () => {
20
23
  // @ts-expect-error test data
21
24
  const result = await hasFeature(accountId, 'feature-1');
22
25
  expect(result).toBe(true);
23
26
  });
24
- it('should return false if the feature is not enabled', async () => {
27
+ it('should return false if the feature is disabled', async () => {
28
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
29
+ data: {
30
+ enabledFeatures: {
31
+ 'feature-2': false,
32
+ },
33
+ },
34
+ });
25
35
  // @ts-expect-error test data
26
36
  const result = await hasFeature(accountId, 'feature-2');
27
37
  expect(result).toBe(false);
28
38
  });
29
39
  it('should return false if the feature is not present', async () => {
40
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
41
+ data: {
42
+ enabledFeatures: {},
43
+ },
44
+ });
30
45
  // @ts-expect-error test data
31
46
  const result = await hasFeature(accountId, 'feature-4');
32
47
  expect(result).toBe(false);
33
48
  });
49
+ it('should return true for APPS_HOME feature when not present in enabled features (defaults on)', async () => {
50
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
51
+ data: {
52
+ enabledFeatures: {},
53
+ },
54
+ });
55
+ const result = await hasFeature(accountId, FEATURES.APPS_HOME);
56
+ expect(result).toBe(true);
57
+ });
58
+ it('should respect explicit setting for APPS_HOME feature even when it defaults on', async () => {
59
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
60
+ data: {
61
+ enabledFeatures: {
62
+ [FEATURES.APPS_HOME]: false,
63
+ },
64
+ },
65
+ });
66
+ const result = await hasFeature(accountId, FEATURES.APPS_HOME);
67
+ expect(result).toBe(false);
68
+ });
69
+ it('should handle truthy values correctly', async () => {
70
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
71
+ data: {
72
+ enabledFeatures: {
73
+ 'feature-truthy': 'yes',
74
+ },
75
+ },
76
+ });
77
+ // @ts-expect-error test data
78
+ const truthyResult = await hasFeature(accountId, 'feature-truthy');
79
+ expect(truthyResult).toBe(true);
80
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
81
+ data: {
82
+ enabledFeatures: {
83
+ 'feature-number': 1,
84
+ },
85
+ },
86
+ });
87
+ // @ts-expect-error test data
88
+ const numberResult = await hasFeature(accountId, 'feature-number');
89
+ expect(numberResult).toBe(true);
90
+ });
91
+ it('should handle falsy values correctly', async () => {
92
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
93
+ data: {
94
+ enabledFeatures: {
95
+ 'feature-null': null,
96
+ },
97
+ },
98
+ });
99
+ // @ts-expect-error test data
100
+ const nullResult = await hasFeature(accountId, 'feature-null');
101
+ expect(nullResult).toBe(false);
102
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
103
+ data: {
104
+ enabledFeatures: {
105
+ 'feature-zero': 0,
106
+ },
107
+ },
108
+ });
109
+ // @ts-expect-error test data
110
+ const zeroResult = await hasFeature(accountId, 'feature-zero');
111
+ expect(zeroResult).toBe(false);
112
+ mockedFetchEnabledFeatures.mockResolvedValueOnce({
113
+ data: {
114
+ enabledFeatures: {
115
+ 'feature-empty': '',
116
+ },
117
+ },
118
+ });
119
+ // @ts-expect-error test data
120
+ const emptyResult = await hasFeature(accountId, 'feature-empty');
121
+ expect(emptyResult).toBe(false);
122
+ });
123
+ it('should propagate errors from fetchEnabledFeatures', async () => {
124
+ const error = new Error('API error');
125
+ mockedFetchEnabledFeatures.mockRejectedValueOnce(error);
126
+ await expect(hasFeature(accountId, FEATURES.UNIFIED_APPS)).rejects.toThrow('API error');
127
+ });
128
+ });
129
+ describe('hasUnfiedAppsAccess()', () => {
130
+ const accountId = 123;
131
+ afterEach(() => {
132
+ vi.clearAllMocks();
133
+ });
134
+ it('should return true when API returns true', async () => {
135
+ // @ts-expect-error Don't want to mock the full response object
136
+ mockedHttp.get.mockResolvedValueOnce({ data: true });
137
+ const result = await hasUnfiedAppsAccess(accountId);
138
+ expect(result).toBe(true);
139
+ expect(mockedHttp.get).toHaveBeenCalledWith(accountId, {
140
+ url: 'developer-tooling/external/developer-portal/has-unified-dev-platform-access',
141
+ });
142
+ });
143
+ it('should return false when API returns false', async () => {
144
+ // @ts-expect-error Don't want to mock the full response object
145
+ mockedHttp.get.mockResolvedValueOnce({ data: false });
146
+ const result = await hasUnfiedAppsAccess(accountId);
147
+ expect(result).toBe(false);
148
+ });
149
+ it('should handle truthy values correctly', async () => {
150
+ // @ts-expect-error Don't want to mock the full response object
151
+ mockedHttp.get.mockResolvedValueOnce({ data: 'yes' });
152
+ const result = await hasUnfiedAppsAccess(accountId);
153
+ expect(result).toBe(true);
154
+ });
155
+ it('should handle falsy values correctly', async () => {
156
+ // @ts-expect-error Don't want to mock the full response object
157
+ mockedHttp.get.mockResolvedValueOnce({ data: null });
158
+ const result = await hasUnfiedAppsAccess(accountId);
159
+ expect(result).toBe(false);
160
+ });
161
+ it('should handle undefined response data', async () => {
162
+ // @ts-expect-error Don't want to mock the full response object
163
+ mockedHttp.get.mockResolvedValueOnce({ data: undefined });
164
+ const result = await hasUnfiedAppsAccess(accountId);
165
+ expect(result).toBe(false);
166
+ });
167
+ it('should propagate errors from http.get', async () => {
168
+ const error = new Error('Network error');
169
+ mockedHttp.get.mockRejectedValueOnce(error);
170
+ await expect(hasUnfiedAppsAccess(accountId)).rejects.toThrow('Network error');
171
+ });
34
172
  });
35
173
  });
@@ -1,7 +1,7 @@
1
1
  import { logger } from '@hubspot/local-dev-lib/logger';
2
2
  import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
3
3
  import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
4
- import { validateUid, getProjectThemeDetails, } from '@hubspot/project-parsing-lib';
4
+ import { validateUid } from '@hubspot/project-parsing-lib';
5
5
  import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
6
6
  import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
7
7
  import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
@@ -12,7 +12,7 @@ import { poll } from '../../polling.js';
12
12
  import { CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, listAppsForMigration, } from '../../../api/migrate.js';
13
13
  import { lib } from '../../../lang/en.js';
14
14
  import { hasUnfiedAppsAccess } from '../../hasFeature.js';
15
- import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, getHasMigratableThemes, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationAppsAndThemes, } from '../migrate.js';
15
+ import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationApps, } from '../migrate.js';
16
16
  vi.mock('@hubspot/local-dev-lib/logger');
17
17
  vi.mock('@hubspot/local-dev-lib/path');
18
18
  vi.mock('@hubspot/local-dev-lib/archive');
@@ -40,7 +40,6 @@ const mockedListPrompt = listPrompt;
40
40
  const mockedEnsureProjectExists = ensureProjectExists;
41
41
  const mockedPoll = poll;
42
42
  const mockedListAppsForMigration = listAppsForMigration;
43
- const mockedGetProjectThemeDetails = getProjectThemeDetails;
44
43
  const mockedInitializeAppMigration = initializeAppMigration;
45
44
  const mockedContinueAppMigration = continueAppMigration;
46
45
  const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
@@ -205,58 +204,31 @@ describe('lib/app/migrate', () => {
205
204
  expect(result.migratableApps[0].projectName).toBe(PROJECT_NAME);
206
205
  });
207
206
  });
208
- describe('getHasMigratableThemes', () => {
209
- it('should return false when no projectConfig is provided', async () => {
210
- const result = await getHasMigratableThemes();
211
- expect(result).toEqual({
212
- hasMigratableThemes: false,
213
- migratableThemesCount: 0,
214
- });
215
- });
216
- it('should return true when there are migratable themes', async () => {
217
- mockedGetProjectThemeDetails.mockResolvedValue({
218
- legacyThemeDetails: [
219
- {
220
- configFilepath: 'src/theme.json',
221
- themePath: 'src/theme',
222
- themeConfig: {
223
- secret_names: ['my-secret'],
224
- },
225
- },
226
- ],
227
- legacyReactThemeDetails: [],
228
- });
229
- const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
230
- const result = await getHasMigratableThemes(projectConfig);
231
- expect(result).toEqual({
232
- hasMigratableThemes: true,
233
- migratableThemesCount: 1,
234
- });
235
- });
236
- });
237
- describe('validateMigrationAppsAndThemes', () => {
207
+ describe('validateMigrationApps', () => {
238
208
  const mockMigratableApp1 = createMockMigratableApp(1, 'App 1', PROJECT_NAME);
239
209
  const mockMigratableApp2 = createMockMigratableApp(2, 'App 2', PROJECT_NAME);
240
210
  it('should throw an error when multiple apps are found for a project', async () => {
241
211
  const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
242
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, {
212
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
243
213
  migratableApps: [mockMigratableApp1, mockMigratableApp2],
244
214
  unmigratableApps: [],
245
- }, false, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
246
- });
247
- it('should throw an error when cms themes are found for a project', async () => {
248
- const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
249
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [mockMigratableApp1], unmigratableApps: [] }, true, projectConfig)).rejects.toThrow(lib.migrate.errors.project.themesAndAppsNotAllowed);
215
+ }, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
250
216
  });
251
217
  it('should throw an error when no apps are found for a project', async () => {
252
218
  const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
253
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, false, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
219
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
254
220
  });
255
221
  it('should throw an error when no migratable apps are found', async () => {
256
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: mockUnmigratableApps }, false)).rejects.toThrow(/No apps in account/);
222
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
223
+ migratableApps: [],
224
+ unmigratableApps: mockUnmigratableApps,
225
+ })).rejects.toThrow(/No apps in account/);
257
226
  });
258
227
  it('should throw an error when appId is provided but not found', async () => {
259
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, false)).rejects.toThrow(/No apps in account/);
228
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
229
+ migratableApps: [],
230
+ unmigratableApps: [],
231
+ })).rejects.toThrow(/No apps in account/);
260
232
  });
261
233
  });
262
234
  describe('promptForAppToMigrate', () => {
@@ -330,10 +302,6 @@ describe('lib/app/migrate', () => {
330
302
  unmigratableApps: [],
331
303
  },
332
304
  });
333
- mockedGetProjectThemeDetails.mockResolvedValue({
334
- legacyThemeDetails: [],
335
- legacyReactThemeDetails: [],
336
- });
337
305
  mockedListPrompt.mockResolvedValue({ appId: 1 });
338
306
  mockedConfirmPrompt.mockResolvedValue(true);
339
307
  mockedEnsureProjectExists.mockResolvedValue({ projectExists: false });
@@ -352,16 +320,11 @@ describe('lib/app/migrate', () => {
352
320
  unmigratableApps: [],
353
321
  },
354
322
  });
355
- mockedGetProjectThemeDetails.mockResolvedValueOnce({
356
- legacyThemeDetails: [],
357
- legacyReactThemeDetails: [],
358
- });
359
323
  const result = await handleMigrationSetup(ACCOUNT_ID, defaultOptions, projectConfig);
360
324
  expect(result).toEqual({
361
325
  appIdToMigrate: 1,
362
326
  projectName: PROJECT_NAME,
363
327
  projectDest: MOCK_PROJECT_DIR,
364
- isThemesMigration: false,
365
328
  });
366
329
  });
367
330
  it('should prompt for project name when not provided', async () => {
@@ -12,18 +12,14 @@ export type MigrateAppArgs = CommonArgs & AccountArgs & EnvironmentArgs & Config
12
12
  export declare function getUnmigratableReason(reasonCode: string, projectName: string | undefined, accountId: number): string;
13
13
  export declare function generateFilterAppsByProjectNameFunction(projectConfig?: LoadedProjectConfig): (app: MigrationApp) => boolean;
14
14
  export declare function buildErrorMessageFromMigrationStatus(error: MigrationFailed): string;
15
- export declare function getHasMigratableThemes(projectConfig?: LoadedProjectConfig): Promise<{
16
- hasMigratableThemes: boolean;
17
- migratableThemesCount: number;
18
- }>;
19
15
  export declare function fetchMigrationApps(derivedAccountId: number, platformVersion: string, projectConfig?: LoadedProjectConfig): Promise<{
20
16
  migratableApps: MigratableApp[];
21
17
  unmigratableApps: UnmigratableApp[];
22
18
  }>;
23
- export declare function validateMigrationAppsAndThemes(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
19
+ export declare function validateMigrationApps(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
24
20
  migratableApps: MigratableApp[];
25
21
  unmigratableApps: UnmigratableApp[];
26
- }, hasMigratableThemes: boolean, projectConfig?: LoadedProjectConfig): Promise<void>;
22
+ }, projectConfig?: LoadedProjectConfig): Promise<void>;
27
23
  export declare function promptForAppToMigrate(allApps: MigrationApp[], derivedAccountId: number): Promise<number>;
28
24
  export declare function selectAppToMigrate(allApps: MigrationApp[], derivedAccountId: number, appId?: number): Promise<{
29
25
  proceed: boolean;
@@ -33,9 +29,7 @@ export declare function handleMigrationSetup(derivedAccountId: number, options:
33
29
  appIdToMigrate?: number | undefined;
34
30
  projectName?: string;
35
31
  projectDest?: string;
36
- isThemesMigration?: boolean;
37
32
  }>;
38
- export declare function handleThemesMigration(projectConfig: LoadedProjectConfig, platformVersion: string): Promise<void>;
39
33
  export declare function beginAppMigration(derivedAccountId: number, appId: number, platformVersion: string): Promise<{
40
34
  migrationId: number;
41
35
  uidMap: Record<string, string>;
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
3
3
  import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
4
4
  import chalk from 'chalk';
5
- import { validateUid, migrateThemes, getProjectThemeDetails, } from '@hubspot/project-parsing-lib';
5
+ import { validateUid } from '@hubspot/project-parsing-lib';
6
6
  import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
7
7
  import { mapToUserFacingType } from '@hubspot/project-parsing-lib/src/lib/transform.js';
8
8
  import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
@@ -10,19 +10,15 @@ import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
10
10
  import { Separator } from '@inquirer/prompts';
11
11
  import { confirmPrompt, inputPrompt, listPrompt, } from '../prompts/promptUtils.js';
12
12
  import { uiAccountDescription, uiCommandReference, uiLine, uiLink, } from '../ui/index.js';
13
- import { writeProjectConfig } from '../projects/config.js';
14
13
  import { ensureProjectExists } from '../projects/ensureProjectExists.js';
15
14
  import SpinniesManager from '../ui/SpinniesManager.js';
16
15
  import { DEFAULT_POLLING_STATUS_LOOKUP, poll } from '../polling.js';
17
16
  import { checkMigrationStatusV2, CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, isMigrationStatus, listAppsForMigration, } from '../../api/migrate.js';
18
17
  import fs from 'fs';
19
18
  import { lib } from '../../lang/en.js';
20
- import { PROJECT_CONFIG_FILE } from '../constants.js';
21
19
  import { hasUnfiedAppsAccess } from '../hasFeature.js';
22
20
  import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
23
21
  import { uiLogger } from '../ui/logger.js';
24
- import { debugError } from '../errorHandlers/index.js';
25
- import { useV3Api } from '../projects/platformVersion.js';
26
22
  export function getUnmigratableReason(reasonCode, projectName, accountId) {
27
23
  switch (reasonCode) {
28
24
  case UNMIGRATABLE_REASONS.UP_TO_DATE:
@@ -59,17 +55,6 @@ export function buildErrorMessageFromMigrationStatus(error) {
59
55
  })
60
56
  .join('\n\t- ')}`;
61
57
  }
62
- export async function getHasMigratableThemes(projectConfig) {
63
- if (!projectConfig?.projectConfig?.name || !projectConfig?.projectDir) {
64
- return { hasMigratableThemes: false, migratableThemesCount: 0 };
65
- }
66
- const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
67
- const { legacyThemeDetails, legacyReactThemeDetails } = await getProjectThemeDetails(projectSrcDir);
68
- return {
69
- hasMigratableThemes: legacyThemeDetails.length > 0 || legacyReactThemeDetails.length > 0,
70
- migratableThemesCount: legacyThemeDetails.length + legacyReactThemeDetails.length,
71
- };
72
- }
73
58
  export async function fetchMigrationApps(derivedAccountId, platformVersion, projectConfig) {
74
59
  const { data: { migratableApps, unmigratableApps }, } = await listAppsForMigration(derivedAccountId, platformVersion);
75
60
  const filteredMigratableApps = migratableApps.filter(generateFilterAppsByProjectNameFunction(projectConfig));
@@ -79,23 +64,11 @@ export async function fetchMigrationApps(derivedAccountId, platformVersion, proj
79
64
  unmigratableApps: filteredUnmigratableApps,
80
65
  };
81
66
  }
82
- export async function validateMigrationAppsAndThemes(appId, derivedAccountId, { migratableApps, unmigratableApps, }, hasMigratableThemes, projectConfig) {
67
+ export async function validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps, }, projectConfig) {
83
68
  const allApps = [...migratableApps, ...unmigratableApps];
84
69
  if (allApps.length > 1 && projectConfig) {
85
70
  throw new Error(lib.migrate.errors.project.multipleApps);
86
71
  }
87
- if (hasMigratableThemes) {
88
- if (useV3Api(projectConfig?.projectConfig?.platformVersion)) {
89
- throw new Error(lib.migrate.errors.project.themesAlreadyMigrated);
90
- }
91
- if (allApps.length > 0 && projectConfig) {
92
- throw new Error(lib.migrate.errors.project.themesAndAppsNotAllowed);
93
- }
94
- if (!projectConfig) {
95
- throw new Error(lib.migrate.errors.project.noProjectForThemesMigration);
96
- }
97
- return;
98
- }
99
72
  if (!projectConfig?.projectConfig) {
100
73
  allApps.forEach(app => {
101
74
  if (app.projectName) {
@@ -178,35 +151,20 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
178
151
  text: lib.migrate.spinners.checkingForMigratableComponents,
179
152
  });
180
153
  const { name, dest, appId } = options;
181
- const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
182
154
  const { migratableApps, unmigratableApps } = await fetchMigrationApps(derivedAccountId, options.platformVersion, projectConfig);
183
155
  SpinniesManager.remove('checkingForMigratableComponents');
184
- await validateMigrationAppsAndThemes(appId, derivedAccountId, { migratableApps, unmigratableApps }, hasMigratableThemes, projectConfig);
156
+ await validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps }, projectConfig);
185
157
  const allApps = [...migratableApps, ...unmigratableApps];
186
- let proceed = false;
187
- let appIdToMigrate;
188
- if (hasMigratableThemes) {
189
- uiLogger.log(lib.migrate.prompt.themesMigration(migratableThemesCount));
190
- proceed = await confirmPrompt(lib.migrate.prompt.proceed, {
191
- defaultAnswer: false,
192
- });
193
- }
194
- else {
195
- const appSelectionPrompt = await selectAppToMigrate(allApps, derivedAccountId, appId);
196
- appIdToMigrate = appSelectionPrompt.appIdToMigrate;
197
- proceed = appSelectionPrompt.proceed;
198
- }
158
+ const { proceed, appIdToMigrate } = await selectAppToMigrate(allApps, derivedAccountId, appId);
199
159
  if (!proceed) {
200
160
  return {};
201
161
  }
202
162
  // If it's a project we don't want to prompt for dest and name, so just return early
203
- // Theme migrations can only be initiated from the project directory
204
163
  if (projectConfig &&
205
164
  projectConfig?.projectConfig &&
206
165
  projectConfig?.projectDir) {
207
166
  return {
208
167
  appIdToMigrate,
209
- isThemesMigration: hasMigratableThemes,
210
168
  projectName: projectConfig.projectConfig.name,
211
169
  projectDest: projectConfig.projectDir,
212
170
  };
@@ -231,35 +189,6 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
231
189
  }));
232
190
  return { appIdToMigrate, projectName, projectDest };
233
191
  }
234
- export async function handleThemesMigration(projectConfig, platformVersion) {
235
- if (!projectConfig?.projectDir || !projectConfig?.projectConfig?.srcDir) {
236
- throw new Error(lib.migrate.errors.project.invalidConfig);
237
- }
238
- const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
239
- let migrated = false;
240
- let failureReason;
241
- try {
242
- const migrationResult = await migrateThemes(projectConfig.projectDir, projectSrcDir);
243
- migrated = migrationResult.migrated;
244
- failureReason = migrationResult.failureReason;
245
- }
246
- catch (error) {
247
- debugError(error);
248
- throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
249
- }
250
- if (!migrated) {
251
- throw new Error(failureReason || lib.migrate.errors.project.failedToMigrateThemes);
252
- }
253
- const newProjectConfig = { ...projectConfig.projectConfig };
254
- newProjectConfig.platformVersion = platformVersion;
255
- const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
256
- const success = writeProjectConfig(projectConfigPath, newProjectConfig);
257
- if (!success) {
258
- throw new Error(lib.migrate.errors.project.failedToUpdateProjectConfig);
259
- }
260
- uiLogger.log('');
261
- uiLogger.log(lib.migrate.success.themesMigrationSuccess(platformVersion));
262
- }
263
192
  export async function beginAppMigration(derivedAccountId, appId, platformVersion) {
264
193
  SpinniesManager.add('beginningMigration', {
265
194
  text: lib.migrate.spinners.beginningMigration,
@@ -412,11 +341,7 @@ export async function migrateApp2025_2(derivedAccountId, options, projectConfig)
412
341
  throw new Error(lib.migrate.errors.project.doesNotExist(derivedAccountId));
413
342
  }
414
343
  }
415
- const { appIdToMigrate, projectName, projectDest, isThemesMigration } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
416
- if (isThemesMigration) {
417
- await handleThemesMigration(projectConfig, options.platformVersion);
418
- return;
419
- }
344
+ const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
420
345
  if (!appIdToMigrate || !projectName || !projectDest) {
421
346
  return;
422
347
  }
@@ -81,17 +81,25 @@ export declare const FEATURES: {
81
81
  readonly SANDBOXES_V2: "sandboxes:v2:enabled";
82
82
  readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
83
83
  readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
84
+ readonly APPS_HOME: "UIE:AppHome";
85
+ readonly MCP_ACCESS: "Developers:CLIMCPAccess";
86
+ readonly THEME_MIGRATION_2025_2: "Developers:ProjectThemeMigrations:2025.2";
87
+ readonly AGENT_TOOLS: "ThirdPartyAgentTools";
84
88
  };
85
89
  export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
86
90
  UPLOAD_SUCCESS: string;
87
91
  UPLOAD_FAILURE: string;
92
+ DEPLOY_SUCCESS: string;
93
+ DEPLOY_FAILURE: string;
88
94
  UPDATE_PROJECT_NODES: string;
89
95
  UPDATE_APP_DATA: string;
90
96
  UPDATE_PROJECT_DATA: string;
91
97
  UPDATE_UPLOAD_WARNINGS: string;
98
+ CLI_METADATA: string;
92
99
  };
93
100
  export declare const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES: {
94
101
  UPLOAD: string;
102
+ DEPLOY: string;
95
103
  VIEWED_WELCOME_SCREEN: string;
96
104
  };
97
105
  export declare const APP_INSTALLATION_STATES: {