@hubspot/cli 7.7.16-experimental.1 → 7.7.16-experimental.10

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 (53) hide show
  1. package/commands/account/auth.js +3 -3
  2. package/commands/app/install.d.ts +8 -0
  3. package/commands/app/install.js +127 -0
  4. package/commands/app.js +6 -1
  5. package/commands/auth.js +23 -25
  6. package/commands/getStarted.js +55 -8
  7. package/commands/init.js +29 -31
  8. package/commands/project/cloneApp.js +4 -4
  9. package/commands/project/create.js +9 -9
  10. package/commands/project/dev/deprecatedFlow.js +4 -4
  11. package/commands/project/dev/index.js +5 -5
  12. package/commands/project/dev/unifiedFlow.js +8 -0
  13. package/commands/sandbox/delete.js +5 -5
  14. package/commands/testAccount/create.js +53 -3
  15. package/lang/en.d.ts +67 -15
  16. package/lang/en.js +64 -12
  17. package/lib/accountTypes.d.ts +1 -0
  18. package/lib/accountTypes.js +20 -9
  19. package/lib/app/migrate_legacy.js +2 -3
  20. package/lib/app/urls.d.ts +1 -1
  21. package/lib/commonOpts.js +1 -1
  22. package/lib/middleware/__test__/configMiddleware.test.js +2 -2
  23. package/lib/middleware/configMiddleware.js +10 -2
  24. package/lib/parsing.d.ts +1 -0
  25. package/lib/parsing.js +11 -0
  26. package/lib/polling.d.ts +1 -1
  27. package/lib/polling.js +11 -1
  28. package/lib/projects/add/v3AddComponent.js +4 -0
  29. package/lib/projects/create/index.d.ts +3 -2
  30. package/lib/projects/create/index.js +11 -5
  31. package/lib/projects/create/v3.d.ts +3 -3
  32. package/lib/projects/create/v3.js +2 -2
  33. package/lib/projects/localDev/AppDevModeInterface.js +1 -1
  34. package/lib/projects/localDev/LocalDevProcess.d.ts +3 -2
  35. package/lib/projects/localDev/LocalDevProcess.js +16 -12
  36. package/lib/projects/localDev/LocalDevState.d.ts +10 -5
  37. package/lib/projects/localDev/LocalDevState.js +18 -20
  38. package/lib/projects/localDev/LocalDevWatcher.js +1 -1
  39. package/lib/projects/structure.d.ts +2 -2
  40. package/lib/projects/upload.d.ts +2 -1
  41. package/lib/projects/upload.js +1 -0
  42. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  43. package/lib/prompts/projectNameAndDestPrompt.d.ts +3 -0
  44. package/lib/prompts/projectNameAndDestPrompt.js +60 -0
  45. package/lib/prompts/selectProjectTemplatePrompt.d.ts +26 -0
  46. package/lib/prompts/{createProjectPrompt.js → selectProjectTemplatePrompt.js} +6 -55
  47. package/lib/validation.d.ts +1 -1
  48. package/lib/validation.js +4 -4
  49. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  50. package/package.json +3 -3
  51. package/types/LocalDev.d.ts +1 -0
  52. package/types/Yargs.d.ts +1 -1
  53. package/lib/prompts/createProjectPrompt.d.ts +0 -28
@@ -19,7 +19,7 @@ const process_1 = require("../../../lib/process");
19
19
  const accountTypes_1 = require("../../../lib/accountTypes");
20
20
  const ensureProjectExists_1 = require("../../../lib/projects/ensureProjectExists");
21
21
  async function deprecatedProjectDevFlow({ args, accountId, projectConfig, projectDir, }) {
22
- const { providedAccountId, derivedAccountId } = args;
22
+ const { userProvidedAccount, derivedAccountId } = args;
23
23
  const env = (0, environment_1.getValidEnv)((0, config_1.getEnv)(derivedAccountId));
24
24
  const components = await (0, structure_1.findProjectComponents)(projectDir);
25
25
  const runnableComponents = components.filter(component => component.runnable);
@@ -65,11 +65,11 @@ async function deprecatedProjectDevFlow({ args, accountId, projectConfig, projec
65
65
  }
66
66
  // targetProjectAccountId and targetTestingAccountId are set to null if --account flag is not provided.
67
67
  // By setting them to null, we can later check if they need to be assigned based on the default account configuration and the type of app.
68
- let targetProjectAccountId = providedAccountId ? derivedAccountId : null;
68
+ let targetProjectAccountId = userProvidedAccount ? derivedAccountId : null;
69
69
  // The account that we are locally testing against
70
- let targetTestingAccountId = providedAccountId ? derivedAccountId : null;
70
+ let targetTestingAccountId = userProvidedAccount ? derivedAccountId : null;
71
71
  // Check that the default account or flag option is valid for the type of app in this project
72
- if (providedAccountId) {
72
+ if (userProvidedAccount) {
73
73
  (0, helpers_1.checkIfAccountFlagIsSupported)(accountConfig, hasPublicApps);
74
74
  if (hasPublicApps) {
75
75
  targetProjectAccountId = accountConfig.parentAccountId || null;
@@ -15,19 +15,19 @@ const en_1 = require("../../../lang/en");
15
15
  const logger_1 = require("../../../lib/ui/logger");
16
16
  const command = 'dev';
17
17
  const describe = (0, ui_1.uiBetaTag)(en_1.commands.project.dev.describe, false);
18
- function validateAccountFlags(testingAccount, projectAccount, providedAccountId, useV3) {
18
+ function validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV3) {
19
19
  // Legacy projects do not support targetTestingAccount and targetProjectAccount
20
20
  if (testingAccount && projectAccount && !useV3) {
21
21
  logger_1.uiLogger.error(en_1.commands.project.dev.errors.unsupportedAccountFlagLegacy);
22
22
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
23
23
  }
24
- if (providedAccountId && useV3) {
24
+ if (userProvidedAccount && useV3) {
25
25
  logger_1.uiLogger.error(en_1.commands.project.dev.errors.unsupportedAccountFlagV3);
26
26
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
27
27
  }
28
28
  }
29
29
  async function handler(args) {
30
- const { derivedAccountId, providedAccountId, testingAccount, projectAccount, } = args;
30
+ const { derivedAccountId, userProvidedAccount, testingAccount, projectAccount, } = args;
31
31
  const { projectConfig, projectDir } = await (0, config_2.getProjectConfig)();
32
32
  (0, config_2.validateProjectConfig)(projectConfig, projectDir);
33
33
  const useV3 = (0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion);
@@ -35,9 +35,9 @@ async function handler(args) {
35
35
  logger_1.uiLogger.error(en_1.commands.project.dev.errors.noProjectConfig);
36
36
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
37
37
  }
38
- validateAccountFlags(testingAccount, projectAccount, providedAccountId, useV3);
38
+ validateAccountFlags(testingAccount, projectAccount, userProvidedAccount, useV3);
39
39
  let targetProjectAccountId = (projectAccount && (0, config_1.getAccountId)(projectAccount)) ||
40
- (providedAccountId && derivedAccountId);
40
+ (userProvidedAccount && derivedAccountId);
41
41
  let profile;
42
42
  if (!targetProjectAccountId && (0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion)) {
43
43
  if (args.profile) {
@@ -58,6 +58,7 @@ async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTar
58
58
  }
59
59
  const accounts = (0, config_2.getConfigAccounts)();
60
60
  const accountIsCombined = await (0, accountTypes_1.isUnifiedAccount)(targetProjectAccountConfig);
61
+ const targetProjectAccountIsTestAccountOrSandbox = (0, accountTypes_1.isTestAccountOrSandbox)(targetProjectAccountConfig);
61
62
  if (!accountIsCombined && !profileConfig) {
62
63
  logger_1.uiLogger.error(en_1.commands.project.dev.errors.accountNotCombined);
63
64
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
@@ -67,6 +68,12 @@ async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTar
67
68
  // Bypass the prompt for the testing account if the user has a profile configured
68
69
  targetTestingAccountId = profileConfig.accountId;
69
70
  }
71
+ else if (
72
+ // Bypass the prompt for the testing account if default account is already a test account
73
+ !targetTestingAccountId &&
74
+ targetProjectAccountIsTestAccountOrSandbox) {
75
+ targetTestingAccountId = targetProjectAccountId;
76
+ }
70
77
  else if (!targetTestingAccountId) {
71
78
  logger_1.uiLogger.log('');
72
79
  (0, ui_1.uiLine)();
@@ -121,6 +128,7 @@ async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTar
121
128
  debug: args.debug,
122
129
  deployedBuild,
123
130
  isGithubLinked,
131
+ profile: args.profile,
124
132
  targetProjectAccountId,
125
133
  targetTestingAccountId: targetTestingAccountId,
126
134
  projectConfig,
@@ -19,10 +19,10 @@ const yargsUtils_1 = require("../../lib/yargsUtils");
19
19
  const command = 'delete';
20
20
  const describe = (0, ui_1.uiBetaTag)((0, lang_1.i18n)(`commands.sandbox.subcommands.delete.describe`), false);
21
21
  async function handler(args) {
22
- const { providedAccountId, force } = args;
23
- (0, usageTracking_1.trackCommandUsage)('sandbox-delete', {}, providedAccountId);
22
+ const { userProvidedAccount, derivedAccountId, force } = args;
23
+ (0, usageTracking_1.trackCommandUsage)('sandbox-delete', {}, derivedAccountId);
24
24
  let accountPrompt;
25
- if (!providedAccountId) {
25
+ if (!userProvidedAccount) {
26
26
  if (!force) {
27
27
  accountPrompt = await (0, sandboxesPrompt_1.deleteSandboxPrompt)();
28
28
  }
@@ -40,7 +40,7 @@ async function handler(args) {
40
40
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
41
41
  }
42
42
  }
43
- const sandboxAccountId = (0, config_1.getAccountId)(providedAccountId || accountPrompt.account);
43
+ const sandboxAccountId = (0, config_1.getAccountId)(userProvidedAccount || accountPrompt.account);
44
44
  if (!sandboxAccountId) {
45
45
  logger_1.logger.error((0, lang_1.i18n)(`commands.sandbox.subcommands.delete.failure.noSandboxAccountId`));
46
46
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
@@ -114,7 +114,7 @@ async function handler(args) {
114
114
  : `commands.sandbox.subcommands.delete.success.delete`;
115
115
  logger_1.logger.log('');
116
116
  logger_1.logger.success((0, lang_1.i18n)(deleteKey, {
117
- account: providedAccountId || accountPrompt.account,
117
+ account: userProvidedAccount || accountPrompt.account,
118
118
  sandboxHubId: sandboxAccountId || '',
119
119
  }));
120
120
  logger_1.logger.log('');
@@ -15,6 +15,9 @@ const usageTracking_1 = require("../../lib/usageTracking");
15
15
  const validation_1 = require("../../lib/validation");
16
16
  const en_1 = require("../../lang/en");
17
17
  const createDeveloperTestAccountConfigPrompt_1 = require("../../lib/prompts/createDeveloperTestAccountConfigPrompt");
18
+ const errorHandlers_1 = require("../../lib/errorHandlers");
19
+ const polling_1 = require("../../lib/polling");
20
+ const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
18
21
  const command = 'create';
19
22
  const describe = en_1.commands.testAccount.create.describe;
20
23
  async function handler(args) {
@@ -62,19 +65,66 @@ async function handler(args) {
62
65
  testAccountConfig = await (0, createDeveloperTestAccountConfigPrompt_1.createDeveloperTestAccountConfigPrompt)();
63
66
  }
64
67
  const jsonOutput = {};
68
+ let testAccountId;
69
+ SpinniesManager_1.default.init({
70
+ succeedColor: 'white',
71
+ });
72
+ SpinniesManager_1.default.add('createTestAccount', {
73
+ text: en_1.commands.testAccount.create.polling.start(testAccountConfig.accountName),
74
+ });
65
75
  try {
66
76
  const { data } = await (0, developerTestAccounts_1.createDeveloperTestAccount)(derivedAccountId, testAccountConfig);
67
77
  if (formatOutputAsJson) {
68
78
  jsonOutput.accountName = data.accountName;
69
79
  jsonOutput.accountId = data.id;
70
- jsonOutput.personalAccessKey = data.personalAccessKey;
71
80
  }
72
- logger_1.uiLogger.success(en_1.commands.testAccount.create.success.configFileUpdated(data.accountName, data.id));
81
+ testAccountId = data.id;
82
+ }
83
+ catch (err) {
84
+ SpinniesManager_1.default.fail('createTestAccount', {
85
+ text: en_1.commands.testAccount.create.polling.createFailure,
86
+ });
87
+ (0, errorHandlers_1.logError)(err);
88
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
89
+ }
90
+ SpinniesManager_1.default.update('createTestAccount', {
91
+ text: en_1.commands.testAccount.create.polling.syncing,
92
+ });
93
+ try {
94
+ await (0, polling_1.poll)(() => (0, developerTestAccounts_1.fetchDeveloperTestAccountGateSyncStatus)(derivedAccountId, testAccountId), {
95
+ successStates: ['SUCCESS'],
96
+ errorStates: [],
97
+ });
73
98
  }
74
99
  catch (err) {
75
- logger_1.uiLogger.error(en_1.commands.testAccount.create.errors.failedToCreate);
100
+ (0, errorHandlers_1.debugError)(err);
101
+ SpinniesManager_1.default.fail('createTestAccount', {
102
+ text: en_1.commands.testAccount.create.polling.syncFailure,
103
+ });
76
104
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
77
105
  }
106
+ if (formatOutputAsJson) {
107
+ // HACK: The status endpoint sometimes returns an early success status.
108
+ // Sleep for an extra 10 seconds to make sure the sync is actually complete.
109
+ await new Promise(resolve => setTimeout(resolve, 10000));
110
+ try {
111
+ // Attempt to generate a new personal access key for the test account now that gate sync is complete.
112
+ const { data } = await (0, developerTestAccounts_1.generateDeveloperTestAccountPersonalAccessKey)(derivedAccountId, testAccountId);
113
+ if (data.personalAccessKey && data.personalAccessKey) {
114
+ jsonOutput.personalAccessKey = data.personalAccessKey;
115
+ }
116
+ }
117
+ catch (err) {
118
+ (0, errorHandlers_1.debugError)(err);
119
+ SpinniesManager_1.default.fail('createTestAccount', {
120
+ text: en_1.commands.testAccount.create.polling.pakFailure,
121
+ });
122
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
123
+ }
124
+ }
125
+ SpinniesManager_1.default.succeed('createTestAccount', {
126
+ text: en_1.commands.testAccount.create.polling.success(testAccountConfig.accountName, testAccountId),
127
+ });
78
128
  if (formatOutputAsJson) {
79
129
  logger_1.uiLogger.json(jsonOutput);
80
130
  }
package/lang/en.d.ts CHANGED
@@ -28,7 +28,7 @@ export declare const commands: {
28
28
  readonly openDesignManagerPrompt: "Open Design Manager in your browser?";
29
29
  readonly openedDesignManager: "Redirected to Design Manager!";
30
30
  readonly developerOverviewBrowserOpenPrep: "We'll take you to your HubSpot account and walk you through installing and previewing your new app.";
31
- readonly openDeveloperOverviewPrompt: "Open HubSpot to go to your account?";
31
+ readonly openInstallUrl: "Open HubSpot to install your app in your account?";
32
32
  readonly openedDeveloperOverview: "HubSpot opened!";
33
33
  readonly prompts: {
34
34
  readonly selectOption: "Are you looking to build apps or CMS?";
@@ -36,17 +36,20 @@ export declare const commands: {
36
36
  readonly app: "App";
37
37
  readonly cms: "CMS";
38
38
  };
39
+ readonly installDependencies: "Would you like to install dependencies now?";
39
40
  readonly uploadProject: "Would you like to upload your project to HubSpot now?";
40
- readonly appSelected: `We'll create a new project with a sample app for you.
41
- Projects are what you can use to create apps, themes, and more at HubSpot.
42
- Usually you'll use the ${string} command, but we'll go ahead and make one now.`;
43
41
  readonly projectCreated: {
44
42
  readonly title: string;
45
- readonly description: `Upload your project to HubSpot - typically, you'll use the command ${string} for this.
46
- We'll start the process now.`;
43
+ readonly description: `Let's prepare and upload your project to HubSpot.
44
+ You can use ${string} to ${string} and ${string} to ${string} your project.`;
47
45
  };
48
46
  };
49
47
  readonly logs: {
48
+ readonly appSelected: `We'll create a new project with a sample app for you.
49
+ Projects are what you can use to create apps, themes, and more at HubSpot.
50
+ Usually you'll use the ${string} command, but we'll go ahead and make one now.`;
51
+ readonly dependenciesInstalled: "Dependencies installed successfully.";
52
+ readonly dependenciesNotInstalled: `Dependencies not installed. Remember to do this later with the command ${string} from the project directory.`;
50
53
  readonly uploadingProject: "Uploading your project to HubSpot...";
51
54
  readonly uploadSuccess: "Project uploaded successfully!";
52
55
  readonly developerOverviewLink: "Open this link to navigate to your HubSpot developer portal";
@@ -54,6 +57,7 @@ We'll start the process now.`;
54
57
  readonly errors: {
55
58
  readonly uploadFailed: "Failed to upload project to HubSpot.";
56
59
  readonly configFileNotFound: "Could not find project configuration for upload.";
60
+ readonly installDepsFailed: "Failed to install dependencies.";
57
61
  };
58
62
  };
59
63
  readonly completion: {
@@ -189,8 +193,11 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
189
193
  };
190
194
  };
191
195
  readonly auth: {
192
- readonly describe: (configName: string) => string;
196
+ readonly describe: "Configure authentication for your HubSpot account.";
197
+ readonly verboseDescribe: (configName: string, authMethod: string) => string;
193
198
  readonly errors: {
199
+ readonly invalidAccountIdProvided: "--account must be a number.";
200
+ readonly globalConfigFileExists: (accountAuthCommand: string) => string;
194
201
  readonly noConfigFileFound: "No config file was found. To create a new config file, use the \"hs init\" command.";
195
202
  readonly unsupportedAuthType: (type: string, supportedProtocols: string) => string;
196
203
  };
@@ -735,7 +742,8 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
735
742
  };
736
743
  };
737
744
  readonly init: {
738
- readonly describe: (configName: string) => string;
745
+ readonly describe: "Create a CLI config file and configure authentication for your HubSpot account.";
746
+ readonly verboseDescribe: (configName: string, command: string, authMethod: string) => string;
739
747
  readonly options: {
740
748
  readonly authType: {
741
749
  readonly describe: "Authentication mechanism";
@@ -750,14 +758,16 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
750
758
  };
751
759
  readonly success: {
752
760
  readonly configFileCreated: (configPath: string) => string;
753
- readonly configFileUpdated: (account: string, authType: string) => string;
761
+ readonly configFileUpdated: (authType: string, account: string | number) => string;
754
762
  };
755
763
  readonly logs: {
756
764
  readonly updateConfig: "To update an existing config file, use the \"hs auth\" command.";
757
765
  };
758
766
  readonly errors: {
767
+ readonly invalidAccountIdProvided: "--account must be a number.";
759
768
  readonly configFileExists: (configPath: string) => string;
760
769
  readonly bothConfigFilesNotAllowed: (path: string) => string;
770
+ readonly globalConfigFileExists: `You are using our new global configuration for account management, which is not compatible with this command. Please use ${string} instead.`;
761
771
  };
762
772
  };
763
773
  readonly lint: {
@@ -1492,6 +1502,28 @@ ${string}`;
1492
1502
  readonly app: {
1493
1503
  readonly describe: "Commands for managing apps.";
1494
1504
  readonly subcommands: {
1505
+ readonly install: {
1506
+ readonly describe: "Install an OAuth app into a test account.";
1507
+ readonly options: {
1508
+ readonly appUid: "The uid of the app to install";
1509
+ readonly projectName: "The name of the project that contains the app";
1510
+ };
1511
+ readonly positionals: {
1512
+ readonly testAccountId: "The id of the test account to install the app into";
1513
+ };
1514
+ readonly errors: {
1515
+ readonly mustSpecifyProjectName: `You must specify a project name. Use the ${string} flag to specify the project name or run this command from within a project directory.`;
1516
+ readonly noAppUidFound: `No app uid found. Please specify the app uid with the ${string} flag or run this command from within a project that contains an app.`;
1517
+ readonly appMustBeOauth: "This command only supports installing oauth apps. Please specify an app with oauth auth type.";
1518
+ };
1519
+ readonly polling: {
1520
+ readonly start: "Installing app...";
1521
+ readonly success: "App installed successfully";
1522
+ readonly failure: "App installation failed";
1523
+ readonly error: "Error installing app";
1524
+ };
1525
+ readonly example: "Install the app with uid my-app-uid from the project named \"my-project\" into the target account with id 1234567890";
1526
+ };
1495
1527
  readonly secret: {
1496
1528
  readonly describe: "Commands for managing secrets.";
1497
1529
  readonly subcommands: {
@@ -1796,10 +1828,14 @@ ${string}`;
1796
1828
  readonly errors: {
1797
1829
  readonly configFileNotFound: (configPath: string) => string;
1798
1830
  readonly configFileParseFailed: (configPath: string) => string;
1799
- readonly failedToCreate: "Failed to create test account";
1800
1831
  };
1801
- readonly success: {
1802
- readonly configFileUpdated: (testAccountName: string, testAccountId: number) => string;
1832
+ readonly polling: {
1833
+ readonly start: (testAccountName: string) => string;
1834
+ readonly syncing: "Test account created! Syncing account data... (may take a few minutes - you can exit and the sync will continue)";
1835
+ readonly success: (testAccountName: string, testAccountId: number) => string;
1836
+ readonly createFailure: "Failed to create test account.";
1837
+ readonly syncFailure: "Failed to sync data into test account. The account may not be ready to use.";
1838
+ readonly pakFailure: "Failed to generate a personal access key for the test account.";
1803
1839
  };
1804
1840
  readonly options: {
1805
1841
  readonly configPath: "The path to the test account config";
@@ -2426,6 +2462,12 @@ ${string}`;
2426
2462
  };
2427
2463
  };
2428
2464
  export declare const lib: {
2465
+ readonly parsing: {
2466
+ readonly unableToParseStringToNumber: "Unable to parse string to number";
2467
+ };
2468
+ readonly configMiddleWare: {
2469
+ readonly invalidAccountIdEnvironmentVariable: "Unable to parse `HUBSPOT_ACCOUNT_ID` environment variable into a number";
2470
+ };
2429
2471
  readonly process: {
2430
2472
  readonly exitDebug: (signal: string) => string;
2431
2473
  };
@@ -2595,6 +2637,9 @@ Run ${string} to upgrade to version ${string}`;
2595
2637
  readonly invalidAuthDistCombo: (authType: string, distribution: string) => string;
2596
2638
  };
2597
2639
  };
2640
+ readonly add: {
2641
+ readonly nothingAdded: "No features added.";
2642
+ };
2598
2643
  readonly validateProjectConfig: {
2599
2644
  readonly configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${string} to create a new project.`;
2600
2645
  readonly configMissingFields: "The project configuration file is missing required fields.";
@@ -2905,16 +2950,20 @@ Run ${string} to upgrade to version ${string}`;
2905
2950
  readonly languageRequired: "Please select API sample app's language";
2906
2951
  };
2907
2952
  };
2908
- readonly createProjectPrompt: {
2953
+ readonly projectNameAndDestPrompt: {
2909
2954
  readonly enterName: "[--name] Give your project a name: ";
2910
2955
  readonly enterDest: "[--dest] Enter the folder to create the project in:";
2911
- readonly selectTemplate: "[--template] Choose a project template: ";
2912
- readonly features: "[--features] Which features would you like your app to include?";
2913
2956
  readonly errors: {
2914
2957
  readonly nameRequired: "A project name is required";
2915
2958
  readonly destRequired: "A project dest is required";
2916
2959
  readonly invalidDest: "There is an existing project at this destination. Please provide a new path for this project.";
2917
2960
  readonly invalidCharacters: "The selected destination contains invalid characters. Please provide a new path and try again.";
2961
+ };
2962
+ };
2963
+ readonly selectProjectTemplatePrompt: {
2964
+ readonly selectTemplate: "[--template] Choose a project template: ";
2965
+ readonly features: "[--features] Which features would you like your app to include?";
2966
+ readonly errors: {
2918
2967
  readonly invalidTemplate: (template: string) => string;
2919
2968
  readonly projectTemplateRequired: "Project template is required when projectTemplates is provided";
2920
2969
  };
@@ -3019,6 +3068,9 @@ Run ${string} to upgrade to version ${string}`;
3019
3068
  };
3020
3069
  };
3021
3070
  };
3071
+ readonly polling: {
3072
+ readonly timeoutError: (timeoutMs: number) => string;
3073
+ };
3022
3074
  readonly convertFields: {
3023
3075
  readonly positionals: {
3024
3076
  readonly src: {
package/lang/en.js CHANGED
@@ -42,7 +42,7 @@ exports.commands = {
42
42
  openDesignManagerPrompt: 'Open Design Manager in your browser?',
43
43
  openedDesignManager: 'Redirected to Design Manager!',
44
44
  developerOverviewBrowserOpenPrep: "We'll take you to your HubSpot account and walk you through installing and previewing your new app.",
45
- openDeveloperOverviewPrompt: 'Open HubSpot to go to your account?',
45
+ openInstallUrl: 'Open HubSpot to install your app in your account?',
46
46
  openedDeveloperOverview: 'HubSpot opened!',
47
47
  prompts: {
48
48
  selectOption: 'Are you looking to build apps or CMS?',
@@ -50,14 +50,17 @@ exports.commands = {
50
50
  app: 'App',
51
51
  cms: 'CMS',
52
52
  },
53
+ installDependencies: 'Would you like to install dependencies now?',
53
54
  uploadProject: 'Would you like to upload your project to HubSpot now?',
54
- appSelected: `We'll create a new project with a sample app for you.\nProjects are what you can use to create apps, themes, and more at HubSpot.\nUsually you'll use the ${(0, ui_1.uiCommandReference)('hs project create')} command, but we'll go ahead and make one now.`,
55
55
  projectCreated: {
56
56
  title: chalk_1.default.bold('Next steps:'),
57
- description: `Upload your project to HubSpot - typically, you'll use the command ${(0, ui_1.uiCommandReference)('hs project upload')} for this.\nWe'll start the process now.`,
57
+ description: `Let's prepare and upload your project to HubSpot.\nYou can use ${(0, ui_1.uiCommandReference)('hs project install-deps')} to ${chalk_1.default.bold('install dependencies')} and ${(0, ui_1.uiCommandReference)('hs project upload')} to ${chalk_1.default.bold('upload')} your project.`,
58
58
  },
59
59
  },
60
60
  logs: {
61
+ appSelected: `We'll create a new project with a sample app for you.\nProjects are what you can use to create apps, themes, and more at HubSpot.\nUsually you'll use the ${(0, ui_1.uiCommandReference)('hs project create')} command, but we'll go ahead and make one now.`,
62
+ dependenciesInstalled: 'Dependencies installed successfully.',
63
+ dependenciesNotInstalled: `Dependencies not installed. Remember to do this later with the command ${(0, ui_1.uiCommandReference)('hs project install-deps')} from the project directory.`,
61
64
  uploadingProject: 'Uploading your project to HubSpot...',
62
65
  uploadSuccess: 'Project uploaded successfully!',
63
66
  developerOverviewLink: 'Open this link to navigate to your HubSpot developer portal',
@@ -65,6 +68,7 @@ exports.commands = {
65
68
  errors: {
66
69
  uploadFailed: 'Failed to upload project to HubSpot.',
67
70
  configFileNotFound: 'Could not find project configuration for upload.',
71
+ installDepsFailed: 'Failed to install dependencies.',
68
72
  },
69
73
  },
70
74
  completion: {
@@ -196,8 +200,11 @@ exports.commands = {
196
200
  },
197
201
  },
198
202
  auth: {
199
- describe: (configName) => `Configure authentication for your HubSpot account. This will update the ${configName} file that stores your account information.`,
203
+ describe: 'Configure authentication for your HubSpot account.',
204
+ verboseDescribe: (configName, authMethod) => `Configure authentication for a HubSpot account. This will update the ${configName} file that stores your account information.\n\nThe recommended authentication method is ${chalk_1.default.bold(authMethod)}, which uses an access token tied to a specific user account.`,
200
205
  errors: {
206
+ invalidAccountIdProvided: `--account must be a number.`,
207
+ globalConfigFileExists: (accountAuthCommand) => `You are using our new global configuration for account management, which is not compatible with this command. Please use ${(0, ui_1.uiCommandReference)(accountAuthCommand)} instead.`,
201
208
  noConfigFileFound: 'No config file was found. To create a new config file, use the "hs init" command.',
202
209
  unsupportedAuthType: (type, supportedProtocols) => `Unsupported auth type: ${type}. The only supported authentication protocols are ${supportedProtocols}.`,
203
210
  },
@@ -744,7 +751,8 @@ exports.commands = {
744
751
  },
745
752
  },
746
753
  init: {
747
- describe: (configName) => `Configure authentication for your HubSpot account. This will create a ${configName} file to store your account information.`,
754
+ describe: 'Create a CLI config file and configure authentication for your HubSpot account.',
755
+ verboseDescribe: (configName, command, authMethod) => `Configure authentication for a HubSpot account. This will create a ${configName} file to store your account information. To configure authentication for additional accounts, run ${command}.\n\nThe recommended authentication method is ${chalk_1.default.bold(authMethod)}, which uses an access token tied to a specific user account.`,
748
756
  options: {
749
757
  authType: {
750
758
  describe: 'Authentication mechanism',
@@ -759,14 +767,16 @@ exports.commands = {
759
767
  },
760
768
  success: {
761
769
  configFileCreated: (configPath) => `Created config file "${configPath}"`,
762
- configFileUpdated: (account, authType) => `Connected account "${account}" using "${authType}" and set it as the default account`,
770
+ configFileUpdated: (authType, account) => `Connected account "${account}" using "${authType}" and set it as the default account`,
763
771
  },
764
772
  logs: {
765
773
  updateConfig: 'To update an existing config file, use the "hs auth" command.',
766
774
  },
767
775
  errors: {
776
+ invalidAccountIdProvided: `--account must be a number.`,
768
777
  configFileExists: (configPath) => `The config file ${configPath} already exists.`,
769
778
  bothConfigFilesNotAllowed: (path) => `Unable to create config file, because there is an existing one at "${path}". To create a new config file, delete the existing one and try again.`,
779
+ globalConfigFileExists: `You are using our new global configuration for account management, which is not compatible with this command. Please use ${(0, ui_1.uiCommandReference)('hs account auth')} instead.`,
770
780
  },
771
781
  },
772
782
  lint: {
@@ -1490,6 +1500,28 @@ exports.commands = {
1490
1500
  app: {
1491
1501
  describe: 'Commands for managing apps.',
1492
1502
  subcommands: {
1503
+ install: {
1504
+ describe: 'Install an OAuth app into a test account.',
1505
+ options: {
1506
+ appUid: 'The uid of the app to install',
1507
+ projectName: 'The name of the project that contains the app',
1508
+ },
1509
+ positionals: {
1510
+ testAccountId: 'The id of the test account to install the app into',
1511
+ },
1512
+ errors: {
1513
+ mustSpecifyProjectName: `You must specify a project name. Use the ${(0, ui_1.uiCommandReference)('--project-name')} flag to specify the project name or run this command from within a project directory.`,
1514
+ noAppUidFound: `No app uid found. Please specify the app uid with the ${(0, ui_1.uiCommandReference)('--app-uid')} flag or run this command from within a project that contains an app.`,
1515
+ appMustBeOauth: 'This command only supports installing oauth apps. Please specify an app with oauth auth type.',
1516
+ },
1517
+ polling: {
1518
+ start: 'Installing app...',
1519
+ success: 'App installed successfully',
1520
+ failure: 'App installation failed',
1521
+ error: 'Error installing app',
1522
+ },
1523
+ example: 'Install the app with uid my-app-uid from the project named "my-project" into the target account with id 1234567890',
1524
+ },
1493
1525
  secret: {
1494
1526
  describe: 'Commands for managing secrets.',
1495
1527
  subcommands: {
@@ -1794,10 +1826,14 @@ exports.commands = {
1794
1826
  errors: {
1795
1827
  configFileNotFound: (configPath) => `No test account config file exists at ${configPath}. Create a test account config file with the ${(0, ui_1.uiCommandReference)('hs test-account create-config')} command.`,
1796
1828
  configFileParseFailed: (configPath) => `Failed to parse test account config file at ${configPath}`,
1797
- failedToCreate: 'Failed to create test account',
1798
1829
  },
1799
- success: {
1800
- configFileUpdated: (testAccountName, testAccountId) => `Test account "${testAccountName}" successfully created with id ${testAccountId}`,
1830
+ polling: {
1831
+ start: (testAccountName) => `Creating test account "${chalk_1.default.bold(testAccountName)}"...`,
1832
+ syncing: 'Test account created! Syncing account data... (may take a few minutes - you can exit and the sync will continue)',
1833
+ success: (testAccountName, testAccountId) => `Test account "${chalk_1.default.bold(testAccountName)}" successfully created with id: ${chalk_1.default.bold(testAccountId)}`,
1834
+ createFailure: 'Failed to create test account.',
1835
+ syncFailure: 'Failed to sync data into test account. The account may not be ready to use.',
1836
+ pakFailure: 'Failed to generate a personal access key for the test account.',
1801
1837
  },
1802
1838
  options: {
1803
1839
  configPath: 'The path to the test account config',
@@ -2424,6 +2460,12 @@ exports.commands = {
2424
2460
  },
2425
2461
  };
2426
2462
  exports.lib = {
2463
+ parsing: {
2464
+ unableToParseStringToNumber: 'Unable to parse string to number',
2465
+ },
2466
+ configMiddleWare: {
2467
+ invalidAccountIdEnvironmentVariable: 'Unable to parse `HUBSPOT_ACCOUNT_ID` environment variable into a number',
2468
+ },
2427
2469
  process: {
2428
2470
  exitDebug: (signal) => `Attempting to gracefully exit. Triggered by ${signal}`,
2429
2471
  },
@@ -2592,6 +2634,9 @@ exports.lib = {
2592
2634
  invalidAuthDistCombo: (authType, distribution) => `Invalid distribution and auth combination. Apps with distribution '${distribution}' must have auth '${authType}'`,
2593
2635
  },
2594
2636
  },
2637
+ add: {
2638
+ nothingAdded: 'No features added.',
2639
+ },
2595
2640
  validateProjectConfig: {
2596
2641
  configNotFound: `Unable to locate a project configuration file. Try running again from a project directory, or run ${(0, ui_1.uiCommandReference)('hs project create')} to create a new project.`,
2597
2642
  configMissingFields: 'The project configuration file is missing required fields.',
@@ -2900,16 +2945,20 @@ exports.lib = {
2900
2945
  languageRequired: "Please select API sample app's language",
2901
2946
  },
2902
2947
  },
2903
- createProjectPrompt: {
2948
+ projectNameAndDestPrompt: {
2904
2949
  enterName: '[--name] Give your project a name: ',
2905
2950
  enterDest: '[--dest] Enter the folder to create the project in:',
2906
- selectTemplate: '[--template] Choose a project template: ',
2907
- features: '[--features] Which features would you like your app to include?',
2908
2951
  errors: {
2909
2952
  nameRequired: 'A project name is required',
2910
2953
  destRequired: 'A project dest is required',
2911
2954
  invalidDest: 'There is an existing project at this destination. Please provide a new path for this project.',
2912
2955
  invalidCharacters: 'The selected destination contains invalid characters. Please provide a new path and try again.',
2956
+ },
2957
+ },
2958
+ selectProjectTemplatePrompt: {
2959
+ selectTemplate: '[--template] Choose a project template: ',
2960
+ features: '[--features] Which features would you like your app to include?',
2961
+ errors: {
2913
2962
  invalidTemplate: (template) => `[--template] Could not find template "${template}". Please choose an available template:`,
2914
2963
  projectTemplateRequired: 'Project template is required when projectTemplates is provided',
2915
2964
  },
@@ -3014,6 +3063,9 @@ exports.lib = {
3014
3063
  },
3015
3064
  },
3016
3065
  },
3066
+ polling: {
3067
+ timeoutError: (timeoutMs) => `Polling timed out after ${timeoutMs}ms.`,
3068
+ },
3017
3069
  convertFields: {
3018
3070
  positionals: {
3019
3071
  src: {
@@ -5,4 +5,5 @@ export declare function isStandardSandbox(accountConfig: CLIAccount): boolean;
5
5
  export declare function isDevelopmentSandbox(accountConfig: CLIAccount): boolean;
6
6
  export declare function isDeveloperTestAccount(accountConfig: CLIAccount): boolean;
7
7
  export declare function isAppDeveloperAccount(accountConfig: CLIAccount): boolean;
8
+ export declare function isTestAccountOrSandbox(accountConfig: CLIAccount): boolean;
8
9
  export declare function isUnifiedAccount(account: CLIAccount): Promise<boolean>;
@@ -6,33 +6,44 @@ exports.isStandardSandbox = isStandardSandbox;
6
6
  exports.isDevelopmentSandbox = isDevelopmentSandbox;
7
7
  exports.isDeveloperTestAccount = isDeveloperTestAccount;
8
8
  exports.isAppDeveloperAccount = isAppDeveloperAccount;
9
+ exports.isTestAccountOrSandbox = isTestAccountOrSandbox;
9
10
  exports.isUnifiedAccount = isUnifiedAccount;
10
11
  const config_1 = require("@hubspot/local-dev-lib/constants/config");
11
12
  const hasFeature_1 = require("./hasFeature");
12
13
  const constants_1 = require("./constants");
13
14
  const getAccountIdentifier_1 = require("@hubspot/local-dev-lib/config/getAccountIdentifier");
14
15
  function isAccountType(accountConfig, accountType) {
15
- return (Boolean(accountConfig.accountType) &&
16
- accountConfig.accountType === accountType);
16
+ return Boolean(accountConfig.accountType && accountType.includes(accountConfig.accountType));
17
17
  }
18
18
  function isStandardAccount(accountConfig) {
19
- return isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD);
19
+ return isAccountType(accountConfig, [config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD]);
20
20
  }
21
21
  function isSandbox(accountConfig) {
22
- return (isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX) ||
23
- isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX));
22
+ return isAccountType(accountConfig, [
23
+ config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
24
+ config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
25
+ ]);
24
26
  }
25
27
  function isStandardSandbox(accountConfig) {
26
- return isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX);
28
+ return isAccountType(accountConfig, [config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX]);
27
29
  }
28
30
  function isDevelopmentSandbox(accountConfig) {
29
- return isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX);
31
+ return isAccountType(accountConfig, [
32
+ config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
33
+ ]);
30
34
  }
31
35
  function isDeveloperTestAccount(accountConfig) {
32
- return isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST);
36
+ return isAccountType(accountConfig, [config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]);
33
37
  }
34
38
  function isAppDeveloperAccount(accountConfig) {
35
- return isAccountType(accountConfig, config_1.HUBSPOT_ACCOUNT_TYPES.APP_DEVELOPER);
39
+ return isAccountType(accountConfig, [config_1.HUBSPOT_ACCOUNT_TYPES.APP_DEVELOPER]);
40
+ }
41
+ function isTestAccountOrSandbox(accountConfig) {
42
+ return isAccountType(accountConfig, [
43
+ config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
44
+ config_1.HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
45
+ config_1.HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
46
+ ]);
36
47
  }
37
48
  async function isUnifiedAccount(account) {
38
49
  const accountId = (0, getAccountIdentifier_1.getAccountIdentifier)(account);