@hubspot/cli 7.6.0-beta.11 → 7.6.0-beta.13

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 (158) hide show
  1. package/commands/app/__tests__/migrate.test.js +1 -0
  2. package/commands/getStarted.js +7 -20
  3. package/commands/mcp/setup.d.ts +0 -1
  4. package/commands/mcp/setup.js +11 -11
  5. package/commands/project/__tests__/add.test.js +64 -0
  6. package/commands/project/__tests__/create.test.js +57 -0
  7. package/commands/project/__tests__/deploy.test.js +3 -2
  8. package/commands/project/add.d.ts +1 -1
  9. package/commands/project/add.js +3 -5
  10. package/commands/project/create.js +7 -2
  11. package/commands/project/deploy.js +9 -61
  12. package/commands/project/dev/index.js +1 -1
  13. package/commands/project/dev/unifiedFlow.js +4 -1
  14. package/commands/project/migrate.js +26 -7
  15. package/commands/project/upload.js +2 -2
  16. package/commands/project/validate.js +1 -1
  17. package/commands/project/watch.js +2 -2
  18. package/lang/en.d.ts +20 -5
  19. package/lang/en.js +38 -22
  20. package/lang/en.lyaml +12 -12
  21. package/lib/__tests__/hasFeature.test.js +145 -7
  22. package/lib/__tests__/importData.test.js +1 -1
  23. package/lib/app/__tests__/migrate.test.js +14 -51
  24. package/lib/app/migrate.d.ts +2 -8
  25. package/lib/app/migrate.js +5 -73
  26. package/lib/constants.d.ts +4 -0
  27. package/lib/constants.js +4 -0
  28. package/lib/errorHandlers/index.d.ts +4 -0
  29. package/lib/errorHandlers/index.js +1 -1
  30. package/lib/hasFeature.js +6 -0
  31. package/lib/importData.js +1 -1
  32. package/lib/links.d.ts +1 -0
  33. package/lib/links.js +10 -3
  34. package/lib/mcp/setup.d.ts +0 -2
  35. package/lib/mcp/setup.js +4 -29
  36. package/lib/projects/__tests__/AppDevModeInterface.test.js +71 -44
  37. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  38. package/lib/projects/__tests__/components.test.js +164 -7
  39. package/lib/projects/__tests__/deploy.test.js +164 -0
  40. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  41. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  42. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  43. package/lib/projects/add/__tests__/v3AddComponent.test.js +142 -8
  44. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  45. package/lib/projects/add/legacyAddComponent.js +5 -1
  46. package/lib/projects/add/v3AddComponent.d.ts +2 -1
  47. package/lib/projects/add/v3AddComponent.js +22 -5
  48. package/lib/projects/components.d.ts +1 -0
  49. package/lib/projects/components.js +27 -1
  50. package/lib/projects/create/__tests__/v3.test.js +97 -9
  51. package/lib/projects/create/index.js +2 -2
  52. package/lib/projects/create/legacy.js +1 -1
  53. package/lib/projects/create/v3.d.ts +2 -2
  54. package/lib/projects/create/v3.js +35 -12
  55. package/lib/projects/deploy.d.ts +13 -0
  56. package/lib/projects/deploy.js +63 -0
  57. package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
  58. package/lib/projects/localDev/AppDevModeInterface.js +110 -47
  59. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  60. package/lib/projects/localDev/LocalDevProcess.js +3 -1
  61. package/lib/projects/localDev/LocalDevState.d.ts +5 -2
  62. package/lib/projects/localDev/LocalDevState.js +9 -1
  63. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  64. package/lib/projects/localDev/helpers/project.js +6 -8
  65. package/lib/projects/platformVersion.d.ts +1 -0
  66. package/lib/projects/platformVersion.js +10 -0
  67. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  68. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
  69. package/lib/projects/upload.js +1 -1
  70. package/lib/projects/urls.d.ts +1 -0
  71. package/lib/projects/urls.js +3 -0
  72. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  73. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  74. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  75. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  76. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  77. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  78. package/lib/prompts/installAppPrompt.d.ts +6 -1
  79. package/lib/prompts/installAppPrompt.js +6 -1
  80. package/lib/prompts/projectAddPrompt.js +1 -1
  81. package/lib/prompts/promptUtils.d.ts +5 -0
  82. package/lib/prompts/promptUtils.js +9 -0
  83. package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
  84. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  85. package/lib/theme/__tests__/migrate.test.js +233 -0
  86. package/lib/theme/migrate.d.ts +13 -0
  87. package/lib/theme/migrate.js +90 -0
  88. package/lib/ui/index.js +3 -6
  89. package/lib/usageTracking.js +2 -2
  90. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
  91. package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
  92. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
  93. package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
  94. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
  95. package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
  96. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  97. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  98. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
  99. package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
  100. package/mcp-server/tools/cms/HsListTool.js +1 -1
  101. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
  102. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
  103. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
  104. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
  105. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
  106. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
  107. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  108. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  109. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
  110. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
  111. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  112. package/mcp-server/tools/index.js +13 -1
  113. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  114. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  115. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  116. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  117. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  118. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  119. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  120. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  121. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  122. package/mcp-server/tools/project/GetConfigValuesTool.js +14 -8
  123. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  124. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  125. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  126. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  127. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  128. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  129. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  130. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  131. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  132. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  133. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  134. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  135. package/mcp-server/tools/project/constants.d.ts +1 -1
  136. package/mcp-server/tools/project/constants.js +14 -6
  137. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  138. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  139. package/mcp-server/utils/cliConfig.d.ts +1 -0
  140. package/mcp-server/utils/cliConfig.js +12 -0
  141. package/package.json +4 -3
  142. package/types/LocalDev.d.ts +2 -1
  143. package/types/Projects.d.ts +1 -0
  144. package/ui/components/BoxWithTitle.d.ts +8 -0
  145. package/ui/components/BoxWithTitle.js +9 -0
  146. package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
  147. package/ui/components/HorizontalSelectPrompt.js +30 -0
  148. package/ui/components/StatusMessageBoxes.d.ts +12 -0
  149. package/ui/components/StatusMessageBoxes.js +31 -0
  150. package/ui/lib/ui-testing-utils.d.ts +9 -0
  151. package/ui/lib/ui-testing-utils.js +47 -0
  152. package/ui/lib/useTerminalSize.d.ts +13 -0
  153. package/ui/lib/useTerminalSize.js +31 -0
  154. package/ui/styles.d.ts +18 -0
  155. package/ui/styles.js +18 -0
  156. package/ui/views/UiSandbox.d.ts +5 -0
  157. package/ui/views/UiSandbox.js +25 -0
  158. /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
package/lang/en.d.ts CHANGED
@@ -23,7 +23,8 @@ export declare const commands: {
23
23
  };
24
24
  readonly startTitle: "Welcome to HubSpot Development!";
25
25
  readonly verboseDescribe: "A step-by-step command to get you started with a HubSpot project.";
26
- readonly startDescription: "You can use the HubSpot CLI to build apps, CMS themes, and more.";
26
+ readonly startDescription: "You can use the HubSpot CLI to build apps, CMS themes, and more.\n";
27
+ readonly guideOverview: (accountName: string) => string;
27
28
  readonly designManager: "To onboard with CMS, please visit the HubSpot Design Manager in your account and follow the checklist items.";
28
29
  readonly openDesignManager: "Click here to go to the HubSpot Design Manager";
29
30
  readonly openDesignManagerPrompt: "Open Design Manager in your browser?";
@@ -871,6 +872,7 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
871
872
  };
872
873
  readonly success: (derivedTargets: string[]) => string;
873
874
  readonly errors: {
875
+ readonly needsMcpAccess: (accountId?: number) => string;
874
876
  readonly needsNode20: "This feature requires node >=20";
875
877
  readonly errorParsingJsonFIle: (filename: string, errorMessage: string) => string;
876
878
  };
@@ -1135,6 +1137,7 @@ ${string}`;
1135
1137
  readonly describe: "Migrate an existing project to the new version of the projects framework.";
1136
1138
  readonly errors: {
1137
1139
  readonly noProjectConfig: (command: string) => string;
1140
+ readonly noThemeMigrationAccess: (accountId?: number) => string;
1138
1141
  };
1139
1142
  readonly examples: {
1140
1143
  readonly default: "Migrate an existing project to the new version of the projects framework.";
@@ -1188,9 +1191,11 @@ ${string}`;
1188
1191
  readonly success: (componentName: string, multiple?: boolean) => string;
1189
1192
  readonly error: {
1190
1193
  readonly failedToDownloadComponent: "Failed to download project. Please try again later.";
1194
+ readonly invalidComponentType: (componentType: string) => string;
1191
1195
  readonly maxExceeded: (maxCount: number) => string;
1192
1196
  readonly authTypeNotAllowed: (authType: string) => string;
1193
1197
  readonly distributionNotAllowed: (dist: string) => string;
1198
+ readonly portalDoesNotHaveAccessToThisFeature: (accountId: number) => string;
1194
1199
  readonly locationInProject: "This command must be run from within a project directory.";
1195
1200
  readonly failedToFetchComponentList: "Failed to fetch the list of available features. Please try again later.";
1196
1201
  readonly projectContainsPublicApp: "This project contains a public app. This command is currently only compatible with projects that contain private apps.";
@@ -2567,7 +2572,10 @@ export declare const lib: {
2567
2572
  readonly success: (appName: string, accountId: number) => string;
2568
2573
  readonly notInstalled: (appName: string, accountId: number) => string;
2569
2574
  readonly activeInstallations: (appName: string, installCount: number) => string;
2575
+ readonly error: "An error occurred while checking installations for your app";
2570
2576
  };
2577
+ readonly distributionChanged: `Your app's distribution type has been changed from private to marketplace. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's distribution type. This will uninstall your app from all accounts.`;
2578
+ readonly authTypeChanged: `Your app's auth type has been changed from static to oauth. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${string} change your app's auth type. This will uninstall your app from all accounts.`;
2571
2579
  };
2572
2580
  readonly LocalDevWebsocketServer: {
2573
2581
  readonly errors: {
@@ -2595,7 +2603,7 @@ export declare const lib: {
2595
2603
  readonly checking: "Checking if your deployed build is up to date...";
2596
2604
  readonly upToDate: "Deployed build is up to date.";
2597
2605
  readonly notUpToDate: "Your project contains undeployed local changes.";
2598
- readonly notUpToDateExplanation: `Run ${string} to upload these changes to HubSpot, then re-run ${string} to continue local development.`;
2606
+ readonly notUpToDateExplanation: (profile?: string) => string;
2599
2607
  };
2600
2608
  readonly createNewProjectForLocalDev: {
2601
2609
  readonly projectMustExistExplanation: (projectName: string, accountId: number) => string;
@@ -2695,6 +2703,11 @@ Run ${string} to upgrade to version ${string}`;
2695
2703
  readonly add: {
2696
2704
  readonly nothingAdded: "No features added.";
2697
2705
  };
2706
+ readonly updateHsMetaFilesWithAutoGeneratedFields: {
2707
+ readonly header: "Created the following components and features:";
2708
+ readonly applicationLog: (componentType: string, uid: string, name: string) => string;
2709
+ readonly componentLog: (componentType: string, uid: string) => string;
2710
+ };
2698
2711
  readonly validateProjectConfig: {
2699
2712
  readonly configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${string} to create a new project.`;
2700
2713
  readonly configMissingFields: "The project configuration file is missing required fields.";
@@ -3152,8 +3165,9 @@ Run ${string} to upgrade to version ${string}`;
3152
3165
  };
3153
3166
  };
3154
3167
  readonly installAppPrompt: {
3155
- readonly explanation: "Local development requires this app to be installed in the target test account";
3168
+ readonly explanation: "Local development requires this app to be installed in the target test account.";
3156
3169
  readonly reinstallExplanation: "This app's required scopes have been updated since it was last installed on the target test account. To avoid issues with local development, we recommend reinstalling the app with the updated scopes.";
3170
+ readonly staticAuthExplanation: (projectAccountId: number, testingAccountId: number, projectName: string, appUid: string) => string;
3157
3171
  readonly prompt: "Open HubSpot to install this app?";
3158
3172
  readonly autoPrompt: "Install this app in your target test account?";
3159
3173
  readonly reinstallPrompt: "Open HubSpot to reinstall this app?";
@@ -3374,8 +3388,9 @@ Run ${string} to upgrade to version ${string}`;
3374
3388
  readonly componentsToBeMigrated: (components: string) => string;
3375
3389
  readonly componentsThatWillNotBeMigrated: (components: string) => string;
3376
3390
  readonly sourceContentsMoved: (newLocation: string) => string;
3377
- readonly projectMigrationWarningTitle: "⚠️ Important: Migrating to platformVersion 2025.2 is irreversible ⚠️";
3391
+ readonly projectMigrationWarningTitle: "Important: Migrating to platformVersion 2025.2 is irreversible";
3378
3392
  readonly projectMigrationWarning: string;
3393
+ readonly exitWithoutMigrating: "Exiting without migrating";
3379
3394
  readonly success: {
3380
3395
  readonly downloadedProject: (projectName: string, projectDest: string) => string;
3381
3396
  readonly themesMigrationSuccess: (platformVersion: string) => string;
@@ -3389,7 +3404,7 @@ Run ${string} to upgrade to version ${string}`;
3389
3404
  readonly themesAndAppsNotAllowed: "Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.";
3390
3405
  readonly multipleApps: "Multiple apps found in project, this is not allowed in 2025.2";
3391
3406
  readonly alreadyExists: (projectName: string) => string;
3392
- readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that you have propoerly formatted themes before trying again.";
3407
+ readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.";
3393
3408
  readonly failedToUpdateProjectConfig: "Failed to update project config file. Please update the platformVersion in the project config file manually.";
3394
3409
  };
3395
3410
  readonly unmigratableReasons: {
package/lang/en.js CHANGED
@@ -4,8 +4,9 @@ import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';
4
4
  import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
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
- import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
8
- import { PROJECT_CONFIG_FILE, PROJECT_WITH_APP } from '../lib/constants.js';
7
+ import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
8
+ import { getProductUpdatesUrl } from '../lib/links.js';
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 = {
11
12
  generalErrors: {
@@ -31,7 +32,8 @@ export const commands = {
31
32
  },
32
33
  startTitle: 'Welcome to HubSpot Development!',
33
34
  verboseDescribe: 'A step-by-step command to get you started with a HubSpot project.',
34
- startDescription: 'You can use the HubSpot CLI to build apps, CMS themes, and more.',
35
+ startDescription: 'You can use the HubSpot CLI to build apps, CMS themes, and more.\n',
36
+ guideOverview: (accountName) => `This guide will walk you through deploying your first project to ${chalk.bold(accountName)}.\nTo target a different account, exit this guide ${chalk.bold('(ctrl + c)')} and run ${uiCommandReference('hs account use')} before trying again.`,
35
37
  designManager: 'To onboard with CMS, please visit the HubSpot Design Manager in your account and follow the checklist items.',
36
38
  openDesignManager: 'Click here to go to the HubSpot Design Manager',
37
39
  openDesignManagerPrompt: 'Open Design Manager in your browser?',
@@ -874,6 +876,7 @@ export const commands = {
874
876
  },
875
877
  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')}.`,
876
878
  errors: {
879
+ 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))}`,
877
880
  needsNode20: `This feature requires node >=20`,
878
881
  errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
879
882
  },
@@ -1048,7 +1051,7 @@ export const commands = {
1048
1051
  cannotNestProjects: (projectDir) => `A project already exists at ${projectDir}. Projects cannot be nested within other projects. Please choose a different destination and try again.`,
1049
1052
  },
1050
1053
  logs: {
1051
- success: (projectName, projectDest) => `\nProject ${chalk.bold(projectName)} was successfully created in ${projectDest}`,
1054
+ success: (projectName, projectDest) => `Project ${chalk.bold(projectName)} was successfully created in ${projectDest}`,
1052
1055
  welcomeMessage: `\n${chalk.bold('Welcome to HubSpot Developer Projects!')}`,
1053
1056
  },
1054
1057
  prompts: {
@@ -1134,6 +1137,7 @@ export const commands = {
1134
1137
  describe: 'Migrate an existing project to the new version of the projects framework.',
1135
1138
  errors: {
1136
1139
  noProjectConfig: (command) => `No project detected. Please run this command again from a project directory. If you are trying to migrate an app, run ${command}`,
1140
+ 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))}`,
1137
1141
  },
1138
1142
  examples: {
1139
1143
  default: 'Migrate an existing project to the new version of the projects framework.',
@@ -1187,9 +1191,11 @@ export const commands = {
1187
1191
  success: (componentName, multiple = false) => `${componentName || 'An app'} ${multiple ? 'were' : 'was'} successfully added to your ${componentName ? 'app' : 'project'}.`,
1188
1192
  error: {
1189
1193
  failedToDownloadComponent: 'Failed to download project. Please try again later.',
1194
+ invalidComponentType: (componentType) => `'${componentType}' is not a valid project component type.`,
1190
1195
  maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
1191
1196
  authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
1192
1197
  distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
1198
+ portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature`,
1193
1199
  locationInProject: 'This command must be run from within a project directory.',
1194
1200
  failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
1195
1201
  projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
@@ -2560,11 +2566,14 @@ export const lib = {
2560
2566
  autoInstallSuccess: (appName, targetTestAccountId) => `Successfully installed app ${appName} on account ${uiAccountDescription(targetTestAccountId)}\n`,
2561
2567
  autoInstallError: (appName, targetTestAccountId) => `Error installing app ${appName} on account ${uiAccountDescription(targetTestAccountId)}. You may still be able to install your app in your browser.`,
2562
2568
  fetchAppData: {
2563
- checking: (appName) => `Checking installations for your app ${appName}...`,
2564
- success: (appName, accountId) => `Your app ${appName} is installed on account ${uiAccountDescription(accountId, false)}`,
2565
- notInstalled: (appName, accountId) => `Your app ${appName} is not currently installed on account ${uiAccountDescription(accountId, false)}`,
2566
- activeInstallations: (appName, installCount) => chalk.bold(`Your app ${appName} is installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}`),
2569
+ checking: (appName) => `Checking installations for your app ${chalk.bold(appName)}...`,
2570
+ success: (appName, accountId) => `Your app ${chalk.bold(appName)} is installed on account ${uiAccountDescription(accountId, false)}`,
2571
+ notInstalled: (appName, accountId) => `Your app ${chalk.bold(appName)} is not currently installed on account ${uiAccountDescription(accountId, false)}`,
2572
+ activeInstallations: (appName, installCount) => `[WARNING] Your app ${chalk.bold(appName)} is installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}`,
2573
+ error: 'An error occurred while checking installations for your app',
2567
2574
  },
2575
+ distributionChanged: `Your app's distribution type has been changed from ${APP_DISTRIBUTION_TYPES.PRIVATE} to ${APP_DISTRIBUTION_TYPES.MARKETPLACE}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's distribution type. This will uninstall your app from all accounts.`,
2576
+ authTypeChanged: `Your app's auth type has been changed from ${APP_AUTH_TYPES.STATIC} to ${APP_AUTH_TYPES.OAUTH}. Once uploaded, this change cannot be reversed. Before uploading your project, confirm that you want to ${chalk.bold('permanantly')} change your app's auth type. This will uninstall your app from all accounts.`,
2568
2577
  },
2569
2578
  LocalDevWebsocketServer: {
2570
2579
  errors: {
@@ -2592,7 +2601,7 @@ export const lib = {
2592
2601
  checking: 'Checking if your deployed build is up to date...',
2593
2602
  upToDate: 'Deployed build is up to date.',
2594
2603
  notUpToDate: `Your project contains undeployed local changes.`,
2595
- notUpToDateExplanation: `Run ${uiCommandReference('hs project upload')} to upload these changes to HubSpot, then re-run ${uiCommandReference('hs project dev')} to continue local development.`,
2604
+ 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.`,
2596
2605
  },
2597
2606
  createNewProjectForLocalDev: {
2598
2607
  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.`,
@@ -2691,6 +2700,11 @@ export const lib = {
2691
2700
  add: {
2692
2701
  nothingAdded: 'No features added.',
2693
2702
  },
2703
+ updateHsMetaFilesWithAutoGeneratedFields: {
2704
+ header: 'Created the following components and features:',
2705
+ applicationLog: (componentType, uid, name) => ` - Created ${chalk.bold(componentType)} with uid ${chalk.bold(uid)} and name ${chalk.bold(name)}`,
2706
+ componentLog: (componentType, uid) => ` - Created ${chalk.bold(componentType)} feature with uid ${chalk.bold(uid)}`,
2707
+ },
2694
2708
  validateProjectConfig: {
2695
2709
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${uiCommandReference('hs project create')} to create a new project.`,
2696
2710
  configMissingFields: 'The project configuration file is missing required fields.',
@@ -2782,27 +2796,27 @@ export const lib = {
2782
2796
  },
2783
2797
  accountsListCommand: {
2784
2798
  command: 'hs accounts list',
2785
- message: (command) => `Run ${command} to see a list of configured HubSpot accounts`,
2799
+ message: (command) => `${command} - See a list of configured HubSpot accounts`,
2786
2800
  },
2787
2801
  accountsUseCommand: {
2788
2802
  command: 'hs accounts use',
2789
- message: (command) => `Run ${command} to set the Hubspot account that the CLI will target by default`,
2803
+ message: (command) => `${command} - Set the Hubspot account that the CLI will target by default`,
2790
2804
  },
2791
2805
  authCommand: {
2792
2806
  command: 'hs auth',
2793
- message: (command) => `Run ${command} to connect the CLI to additional HubSpot accounts`,
2807
+ message: (command) => `${command} - Connect the CLI to additional HubSpot accounts`,
2794
2808
  },
2795
2809
  feedbackCommand: {
2796
2810
  command: 'hs feedback',
2797
- message: (command) => `Run ${command} to report a bug or leave feedback`,
2811
+ message: (command) => `${command} - Report a bug or leave feedback`,
2798
2812
  },
2799
2813
  helpCommand: {
2800
2814
  command: 'hs help',
2801
- message: (command) => `Run ${command} to see a list of available commands`,
2815
+ message: (command) => `${command} - See a list of available commands`,
2802
2816
  },
2803
2817
  projectCreateCommand: {
2804
2818
  command: 'hs project create',
2805
- message: (command) => `Run ${command} to create a new project`,
2819
+ message: (command) => `${command} - Create a new project`,
2806
2820
  },
2807
2821
  projectDeployCommand: {
2808
2822
  command: 'hs project deploy',
@@ -2810,19 +2824,19 @@ export const lib = {
2810
2824
  },
2811
2825
  projectHelpCommand: {
2812
2826
  command: 'hs project --help',
2813
- message: (command) => `Run ${command} to learn more about available project commands`,
2827
+ message: (command) => `${command} - Learn more about available project commands`,
2814
2828
  },
2815
2829
  projectUploadCommand: {
2816
2830
  command: 'hs project upload',
2817
- message: (command) => `Run ${command} to upload your project to HubSpot and trigger builds`,
2831
+ message: (command) => `${command} - Upload your project to HubSpot and trigger builds`,
2818
2832
  },
2819
2833
  projectDevCommand: {
2820
2834
  command: 'hs project dev',
2821
- message: (command) => `Run ${command} from within your project directory to set up your test environment and start local development`,
2835
+ message: (command) => `${command} - Set up a test environment and start local development`,
2822
2836
  },
2823
2837
  projectInstallDepsCommand: {
2824
2838
  command: 'hs project install-deps',
2825
- message: (command) => `Run ${command} to install dependencies for your project components`,
2839
+ message: (command) => `${command} - Install all project dependencies`,
2826
2840
  },
2827
2841
  sampleProjects: {
2828
2842
  linkText: "HubSpot's sample projects",
@@ -3146,8 +3160,9 @@ export const lib = {
3146
3160
  },
3147
3161
  },
3148
3162
  installAppPrompt: {
3149
- explanation: 'Local development requires this app to be installed in the target test account',
3163
+ explanation: 'Local development requires this app to be installed in the target test account.',
3150
3164
  reinstallExplanation: "This app's required scopes have been updated since it was last installed on the target test account. To avoid issues with local development, we recommend reinstalling the app with the updated scopes.",
3165
+ staticAuthExplanation: (projectAccountId, testingAccountId, projectName, appUid) => `To install this static auth app, your testing account ${uiAccountDescription(testingAccountId)} must be on ${uiLink("this app's allowlist", getAppAllowlistUrl(projectAccountId, projectName, appUid))}.`,
3151
3166
  prompt: 'Open HubSpot to install this app?',
3152
3167
  autoPrompt: 'Install this app in your target test account?',
3153
3168
  reinstallPrompt: 'Open HubSpot to reinstall this app?',
@@ -3368,8 +3383,9 @@ export const lib = {
3368
3383
  componentsToBeMigrated: (components) => `The following features will be migrated: ${components}`,
3369
3384
  componentsThatWillNotBeMigrated: (components) => `[NOTE] These features are not yet supported for migration but will be available later: ${components}`,
3370
3385
  sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
3371
- projectMigrationWarningTitle: '⚠️ Important: Migrating to platformVersion 2025.2 is irreversible ⚠️',
3386
+ projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
3372
3387
  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),
3388
+ exitWithoutMigrating: 'Exiting without migrating',
3373
3389
  success: {
3374
3390
  downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
3375
3391
  themesMigrationSuccess: (platformVersion) => `Successfully migrated project to platformVersion ${chalk.bold(platformVersion)}. Upload your project using ${uiCommandReference('hs project upload')}`,
@@ -3383,7 +3399,7 @@ export const lib = {
3383
3399
  themesAndAppsNotAllowed: 'Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.',
3384
3400
  multipleApps: 'Multiple apps found in project, this is not allowed in 2025.2',
3385
3401
  alreadyExists: (projectName) => `A project with name ${projectName} already exists. Please choose another name.`,
3386
- failedToMigrateThemes: 'Failed to migrate project themes. Please verify that you have propoerly formatted themes before trying again.',
3402
+ failedToMigrateThemes: 'Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.',
3387
3403
  failedToUpdateProjectConfig: 'Failed to update project config file. Please update the platformVersion in the project config file manually.',
3388
3404
  },
3389
3405
  unmigratableReasons: {
package/lang/en.lyaml CHANGED
@@ -1081,43 +1081,43 @@ en:
1081
1081
  message: "Use the {{ command }} option with any command to override the default account"
1082
1082
  accountsListCommand:
1083
1083
  command: "hs account list"
1084
- message: "Run {{ command }} to see a list of configured HubSpot accounts"
1084
+ message: "{{ command }} - See a list of configured HubSpot accounts"
1085
1085
  accountsUseCommand:
1086
1086
  command: "hs account use"
1087
- message: "Run {{ command }} to set the Hubspot account that the CLI will target by default"
1087
+ message: "{{ command }} - Set the Hubspot account that the CLI will target by default"
1088
1088
  accountAuthCommand:
1089
1089
  command: "hs account auth"
1090
- message: "Run {{ command }} to connect the CLI to additional HubSpot accounts"
1090
+ message: "{{ command }} - Connect the CLI to additional HubSpot accounts"
1091
1091
  authCommand:
1092
1092
  command: "hs auth"
1093
- message: "Run {{ command }} to connect the CLI to additional HubSpot accounts"
1093
+ message: "{{ command }} - Connect the CLI to additional HubSpot accounts"
1094
1094
  feedbackCommand:
1095
1095
  command: "hs feedback"
1096
- message: "Run {{ command }} to report a bug or leave feedback"
1096
+ message: "{{ command }} - Report bugs or leave feedback"
1097
1097
  getStartedCommand:
1098
1098
  command: "hs get-started"
1099
- message: "Run {{ command }} to get started with HubSpot development"
1099
+ message: "{{ command }} - Get started with HubSpot development"
1100
1100
  helpCommand:
1101
1101
  command: "hs help"
1102
- message: "Run {{ command }} to see a list of available commands"
1102
+ message: "{{ command }} - See a list of available commands"
1103
1103
  projectCreateCommand:
1104
1104
  command: "hs project create"
1105
- message: "Run {{ command }} to create a new project"
1105
+ message: "{{ command }} - create a new project"
1106
1106
  projectDeployCommand:
1107
1107
  command: "hs project deploy"
1108
1108
  message: "Ready to take your project live? Run {{ command }}"
1109
1109
  projectHelpCommand:
1110
1110
  command: "hs project --help"
1111
- message: "Run {{ command }} to learn more about available project commands"
1111
+ message: "{{ command }} - Learn more about available project commands"
1112
1112
  projectUploadCommand:
1113
1113
  command: "hs project upload"
1114
- message: "Run {{ command }} to upload your project to your HubSpot account"
1114
+ message: "{{ command }} - Upload the project to your HubSpot account"
1115
1115
  projectDevCommand:
1116
1116
  command: "hs project dev"
1117
- message: "Run {{ command }} from within your project directory to set up your test environment and start local development"
1117
+ message: "{{ command }} - Set up a test environment and start local development"
1118
1118
  projectInstallDepsCommand:
1119
1119
  command: "hs project install-deps"
1120
- message: "Run {{ command }} to install dependencies for your project components"
1120
+ message: "{{ command }} - Install all project dependencies"
1121
1121
  projectCommandTip:
1122
1122
  message: "Tip: All project commands must be run from within a project directory"
1123
1123
  sampleProjects:
@@ -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
  });
@@ -43,7 +43,7 @@ describe('lib/importData', () => {
43
43
  data: { id: '123' },
44
44
  });
45
45
  await handleImportData(targetAccountId, dataFileNames, importRequest);
46
- expect(mockUiLogger.success).toHaveBeenCalledWith(lib.importData.viewImportLink('https://app.hubspot.com', targetAccountId, '123'));
46
+ expect(mockUiLogger.info).toHaveBeenCalledWith(lib.importData.viewImportLink('https://app.hubspot.com', targetAccountId, '123'));
47
47
  });
48
48
  it('should log the correct error message', async () => {
49
49
  mockCreateImport.mockRejectedValue(new Error('test-error'));