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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/bin/cli.js +4 -0
  2. package/bin/hs +1 -1
  3. package/commands/getStarted.d.ts +9 -0
  4. package/commands/getStarted.js +227 -0
  5. package/commands/init.js +6 -1
  6. package/commands/mcp/setup.d.ts +2 -2
  7. package/commands/mcp/setup.js +2 -0
  8. package/commands/mcp/start.d.ts +2 -2
  9. package/commands/mcp/start.js +3 -1
  10. package/commands/project/deploy.d.ts +1 -0
  11. package/commands/project/deploy.js +29 -3
  12. package/commands/project/upload.d.ts +2 -2
  13. package/commands/project/upload.js +18 -23
  14. package/commands/project/validate.d.ts +6 -0
  15. package/commands/project/validate.js +82 -0
  16. package/commands/project.js +2 -0
  17. package/commands/testAccount/create.d.ts +6 -0
  18. package/commands/testAccount/create.js +110 -0
  19. package/commands/testAccount/createConfig.d.ts +10 -0
  20. package/commands/testAccount/createConfig.js +98 -0
  21. package/commands/testAccount/delete.d.ts +6 -0
  22. package/commands/testAccount/delete.js +48 -0
  23. package/commands/testAccount.d.ts +3 -0
  24. package/commands/testAccount.js +28 -0
  25. package/lang/en.d.ts +143 -29
  26. package/lang/en.js +143 -32
  27. package/lang/en.lyaml +9 -14
  28. package/lib/app/migrate.js +15 -3
  29. package/lib/commonOpts.d.ts +2 -0
  30. package/lib/commonOpts.js +21 -9
  31. package/lib/constants.d.ts +5 -0
  32. package/lib/constants.js +6 -1
  33. package/lib/doctor/Doctor.js +1 -1
  34. package/lib/errorHandlers/index.js +7 -0
  35. package/lib/mcp/setup.d.ts +9 -0
  36. package/lib/mcp/setup.js +23 -21
  37. package/lib/projectProfiles.d.ts +1 -0
  38. package/lib/projectProfiles.js +18 -0
  39. package/lib/projects/buildAndDeploy.js +1 -1
  40. package/lib/projects/localDev/AppDevModeInterface.d.ts +3 -0
  41. package/lib/projects/localDev/AppDevModeInterface.js +45 -16
  42. package/lib/projects/localDev/LocalDevManager.js +1 -1
  43. package/lib/projects/upload.d.ts +3 -0
  44. package/lib/projects/upload.js +56 -22
  45. package/lib/projects/urls.d.ts +2 -0
  46. package/lib/projects/urls.js +10 -0
  47. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +17 -0
  48. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +96 -0
  49. package/lib/prompts/installAppPrompt.d.ts +2 -1
  50. package/lib/prompts/installAppPrompt.js +12 -2
  51. package/lib/prompts/promptUtils.d.ts +1 -0
  52. package/lib/prompts/promptUtils.js +2 -0
  53. package/lib/ui/logger.d.ts +1 -0
  54. package/lib/ui/logger.js +1 -0
  55. package/lib/yargsUtils.d.ts +1 -0
  56. package/lib/yargsUtils.js +3 -0
  57. package/mcp-server/tools/index.js +2 -0
  58. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  59. package/mcp-server/tools/project/CreateProjectTool.js +5 -1
  60. package/mcp-server/tools/project/DeployProject.js +1 -1
  61. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  62. package/mcp-server/tools/project/ValidateProjectTool.d.ts +17 -0
  63. package/mcp-server/tools/project/ValidateProjectTool.js +35 -0
  64. package/package.json +10 -9
  65. package/types/LocalDev.d.ts +1 -0
  66. package/types/Yargs.d.ts +4 -0
package/lib/mcp/setup.js CHANGED
@@ -5,17 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.supportedTools = void 0;
7
7
  exports.addMintlifyMcpServer = addMintlifyMcpServer;
8
+ exports.setupMintlify = setupMintlify;
8
9
  exports.addMcpServerToConfig = addMcpServerToConfig;
10
+ exports.setupClaudeCode = setupClaudeCode;
11
+ exports.setupCursor = setupCursor;
12
+ exports.setupWindsurf = setupWindsurf;
9
13
  const logger_1 = require("../ui/logger");
10
14
  const en_1 = require("../../lang/en");
11
- const child_process_1 = require("child_process");
12
15
  const promptUtils_1 = require("../prompts/promptUtils");
13
16
  const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
14
17
  const errorHandlers_1 = require("../errorHandlers");
15
- const fs_1 = __importDefault(require("fs"));
16
18
  const command_1 = require("../../mcp-server/utils/command");
19
+ const child_process_1 = require("child_process");
17
20
  const path_1 = __importDefault(require("path"));
18
21
  const os_1 = __importDefault(require("os"));
22
+ const fs_extra_1 = __importDefault(require("fs-extra"));
23
+ const fs_1 = require("fs");
19
24
  const mcpServerName = 'hubspot-cli-mcp';
20
25
  const claudeCode = 'claude';
21
26
  const windsurf = 'windsurf';
@@ -26,12 +31,14 @@ exports.supportedTools = [
26
31
  { name: en_1.commands.mcp.setup.cursor, value: cursor },
27
32
  { name: en_1.commands.mcp.setup.windsurf, value: windsurf },
28
33
  ];
29
- const hsCommand = 'hs';
30
- const mcpCommandArgs = ['mcp', 'start'];
34
+ const defaultMcpCommand = {
35
+ command: 'hs',
36
+ args: ['mcp', 'start'],
37
+ };
31
38
  async function addMintlifyMcpServer(installTargets) {
32
39
  await runSetupFunction(() => setupMintlify(installTargets));
33
40
  }
34
- async function setupMintlify(derivedTargets) {
41
+ async function setupMintlify(derivedTargets = supportedMintlifyClients) {
35
42
  logger_1.uiLogger.info(en_1.commands.mcp.setup.installingDocSearch);
36
43
  logger_1.uiLogger.log('');
37
44
  return new Promise(resolve => {
@@ -101,15 +108,12 @@ function setupMcpConfigFile(config) {
101
108
  SpinniesManager_1.default.add('spinner', {
102
109
  text: config.configuringMessage,
103
110
  });
104
- if (!fs_1.default.existsSync(config.configPath)) {
105
- SpinniesManager_1.default.fail('spinner', {
106
- text: config.configMissingMessage,
107
- });
108
- return false;
111
+ if (!(0, fs_1.existsSync)(config.configPath)) {
112
+ fs_extra_1.default.writeFileSync(config.configPath, JSON.stringify({}, null, 2));
109
113
  }
110
114
  let mcpConfig = {};
111
115
  try {
112
- const configContent = fs_1.default.readFileSync(config.configPath, 'utf8');
116
+ const configContent = fs_extra_1.default.readFileSync(config.configPath, 'utf8');
113
117
  mcpConfig = JSON.parse(configContent);
114
118
  }
115
119
  catch (error) {
@@ -125,11 +129,10 @@ function setupMcpConfigFile(config) {
125
129
  }
126
130
  // Add or update HubSpot CLI MCP server
127
131
  mcpConfig.mcpServers[mcpServerName] = {
128
- command: hsCommand,
129
- args: mcpCommandArgs,
132
+ ...config.mcpCommand,
130
133
  };
131
134
  // Write the updated config
132
- fs_1.default.writeFileSync(config.configPath, JSON.stringify(mcpConfig, null, 2));
135
+ fs_extra_1.default.writeFileSync(config.configPath, JSON.stringify(mcpConfig, null, 2));
133
136
  SpinniesManager_1.default.succeed('spinner', {
134
137
  text: config.configuredMessage,
135
138
  });
@@ -143,7 +146,7 @@ function setupMcpConfigFile(config) {
143
146
  return false;
144
147
  }
145
148
  }
146
- async function setupClaudeCode() {
149
+ async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
147
150
  try {
148
151
  SpinniesManager_1.default.add('claudeCode', {
149
152
  text: en_1.commands.mcp.setup.spinners.configuringClaudeCode,
@@ -154,8 +157,7 @@ async function setupClaudeCode() {
154
157
  // Run claude mcp add command
155
158
  const mcpConfig = JSON.stringify({
156
159
  type: 'stdio',
157
- command: hsCommand,
158
- args: mcpCommandArgs,
160
+ ...mcpCommand,
159
161
  });
160
162
  const { stdout } = await (0, command_1.execAsync)('claude mcp list');
161
163
  if (stdout.includes(mcpServerName)) {
@@ -194,23 +196,23 @@ async function setupClaudeCode() {
194
196
  return false;
195
197
  }
196
198
  }
197
- function setupCursor() {
199
+ function setupCursor(mcpCommand = defaultMcpCommand) {
198
200
  const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
199
201
  return setupMcpConfigFile({
200
202
  configPath: cursorConfigPath,
201
203
  configuringMessage: en_1.commands.mcp.setup.spinners.configuringCursor,
202
204
  configuredMessage: en_1.commands.mcp.setup.spinners.configuredCursor,
203
205
  failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureCursor,
204
- configMissingMessage: en_1.commands.mcp.setup.spinners.noCursorMcpFile(cursorConfigPath),
206
+ mcpCommand,
205
207
  });
206
208
  }
207
- function setupWindsurf() {
209
+ function setupWindsurf(mcpCommand = defaultMcpCommand) {
208
210
  const windsurfConfigPath = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
209
211
  return setupMcpConfigFile({
210
212
  configPath: windsurfConfigPath,
211
213
  configuringMessage: en_1.commands.mcp.setup.spinners.configuringWindsurf,
212
214
  configuredMessage: en_1.commands.mcp.setup.spinners.configuredWindsurf,
213
215
  failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureWindsurf,
214
- configMissingMessage: en_1.commands.mcp.setup.spinners.noWindsurfFile(windsurfConfigPath),
216
+ mcpCommand,
215
217
  });
216
218
  }
@@ -4,3 +4,4 @@ export declare function logProfileHeader(profileName: string): void;
4
4
  export declare function logProfileFooter(profile: HsProfileFile, includeVariables?: boolean): void;
5
5
  export declare function loadProfile(projectConfig: ProjectConfig | null, projectDir: string | null, profileName: string): HsProfileFile | undefined;
6
6
  export declare function exitIfUsingProfiles(projectConfig: ProjectConfig | null, projectDir: string | null): Promise<void>;
7
+ export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined): Promise<number | undefined>;
@@ -7,6 +7,7 @@ exports.logProfileHeader = logProfileHeader;
7
7
  exports.logProfileFooter = logProfileFooter;
8
8
  exports.loadProfile = loadProfile;
9
9
  exports.exitIfUsingProfiles = exitIfUsingProfiles;
10
+ exports.loadAndValidateProfile = loadAndValidateProfile;
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
12
13
  const en_1 = require("../lang/en");
@@ -63,3 +64,20 @@ async function exitIfUsingProfiles(projectConfig, projectDir) {
63
64
  }
64
65
  }
65
66
  }
67
+ async function loadAndValidateProfile(projectConfig, projectDir, argsProfile) {
68
+ if (argsProfile) {
69
+ logProfileHeader(argsProfile);
70
+ const profile = loadProfile(projectConfig, projectDir, argsProfile);
71
+ if (!profile) {
72
+ (0, ui_1.uiLine)();
73
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
74
+ }
75
+ logProfileFooter(profile, true);
76
+ return profile.accountId;
77
+ }
78
+ else {
79
+ // A profile must be specified if this project has profiles configured
80
+ await exitIfUsingProfiles(projectConfig, projectDir);
81
+ }
82
+ return undefined;
83
+ }
@@ -228,7 +228,7 @@ function makePollTaskStatusFunc({ statusFn, structureFn, statusText, statusStrin
228
228
  }
229
229
  function pollBuildAutodeployStatus(accountId, taskName, buildId) {
230
230
  return new Promise((resolve, reject) => {
231
- let maxIntervals = (30 * 1000) / constants_1.DEFAULT_POLLING_DELAY; // Num of intervals in ~30s
231
+ let maxIntervals = (15 * 1000) / constants_1.DEFAULT_POLLING_DELAY; // Num of intervals in ~15s
232
232
  const pollInterval = setInterval(async () => {
233
233
  let build;
234
234
  try {
@@ -14,9 +14,12 @@ declare class AppDevModeInterface {
14
14
  private get appNode();
15
15
  private get appData();
16
16
  private set appData(value);
17
+ private isAutomaticallyInstallable;
17
18
  private getAppInstallUrl;
18
19
  private fetchAppData;
19
20
  private checkMarketplaceAppInstalls;
21
+ private autoInstallStaticAuthApp;
22
+ private installAppOrOpenInstallUrl;
20
23
  private checkTestAccountAppInstallation;
21
24
  private setUpLocalDevServerMessageListeners;
22
25
  setup(args: any): Promise<void>;
@@ -4,6 +4,7 @@ const localDevAuth_1 = require("@hubspot/local-dev-lib/api/localDevAuth");
4
4
  const appsDev_1 = require("@hubspot/local-dev-lib/api/appsDev");
5
5
  const ui_extensions_dev_server_1 = require("@hubspot/ui-extensions-dev-server");
6
6
  const portManager_1 = require("@hubspot/local-dev-lib/portManager");
7
+ const config_1 = require("@hubspot/local-dev-lib/config");
7
8
  const constants_1 = require("../../constants");
8
9
  const exitCodes_1 = require("../../enums/exitCodes");
9
10
  const structure_1 = require("../../projects/structure");
@@ -14,6 +15,7 @@ const promptUtils_1 = require("../../prompts/promptUtils");
14
15
  const en_1 = require("../../../lang/en");
15
16
  const logger_1 = require("../../ui/logger");
16
17
  const urls_1 = require("../../app/urls");
18
+ const accountTypes_1 = require("../../accountTypes");
17
19
  class AppDevModeInterface {
18
20
  localDevState;
19
21
  localDevLogger;
@@ -22,15 +24,6 @@ class AppDevModeInterface {
22
24
  constructor(options) {
23
25
  this.localDevState = options.localDevState;
24
26
  this.localDevLogger = options.localDevLogger;
25
- // Static auth apps are currently only installable in the portal that the project resides in
26
- // This limitation will eventually be removed, but in the meantime we need this check or the install
27
- // will always fail with a confusing message
28
- if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC &&
29
- this.localDevState.targetTestingAccountId !==
30
- this.localDevState.targetProjectAccountId) {
31
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
32
- process.exit(exitCodes_1.EXIT_CODES.ERROR);
33
- }
34
27
  if (!this.localDevState.targetProjectAccountId ||
35
28
  !this.localDevState.projectConfig ||
36
29
  !this.localDevState.projectDir) {
@@ -59,6 +52,19 @@ class AppDevModeInterface {
59
52
  }
60
53
  this.localDevState.setAppDataForUid(this.appNode.uid, appData);
61
54
  }
55
+ isAutomaticallyInstallable() {
56
+ const targetTestingAccount = (0, config_1.getAccountConfig)(this.localDevState.targetTestingAccountId);
57
+ if (!targetTestingAccount) {
58
+ return false;
59
+ }
60
+ const isTestAccount = (0, accountTypes_1.isDeveloperTestAccount)(targetTestingAccount) ||
61
+ (0, accountTypes_1.isSandbox)(targetTestingAccount);
62
+ const hasCorrectParent = targetTestingAccount.parentAccountId ===
63
+ this.localDevState.targetProjectAccountId;
64
+ return (isTestAccount &&
65
+ hasCorrectParent &&
66
+ this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC);
67
+ }
62
68
  async getAppInstallUrl() {
63
69
  if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.OAUTH) {
64
70
  return (0, urls_1.getOauthAppInstallUrl)({
@@ -72,7 +78,7 @@ class AppDevModeInterface {
72
78
  const { data: { results }, } = await (0, appsDev_1.fetchPublicAppsForPortal)(this.localDevState.targetProjectAccountId);
73
79
  const app = results.find(app => app.sourceId === this.appNode?.uid);
74
80
  if (!app) {
75
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
81
+ logger_1.uiLogger.error(en_1.lib.LocalDevManager.appNotFound(this.localDevState.targetProjectAccountId, this.appNode?.uid));
76
82
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
77
83
  }
78
84
  return (0, urls_1.getStaticAuthAppInstallUrl)({
@@ -93,6 +99,7 @@ class AppDevModeInterface {
93
99
  clientId: appData.clientId,
94
100
  name: appData.name,
95
101
  installationState: constants_1.APP_INSTALLATION_STATES.NOT_INSTALLED,
102
+ scopeGroupIds: appData.scopeGroupIds,
96
103
  };
97
104
  this.marketplaceAppInstalls = uniquePortalInstallCount;
98
105
  }
@@ -110,9 +117,31 @@ class AppDevModeInterface {
110
117
  }
111
118
  this.localDevLogger.addUploadWarning(en_1.lib.AppDevModeInterface.defaultMarketplaceAppWarning(this.marketplaceAppInstalls));
112
119
  }
120
+ async autoInstallStaticAuthApp() {
121
+ const shouldInstall = await (0, installAppPrompt_1.installAppAutoPrompt)();
122
+ if (!shouldInstall) {
123
+ logger_1.uiLogger.log(en_1.lib.AppDevModeInterface.autoInstallDeclined);
124
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
125
+ }
126
+ await (0, appsDev_1.installStaticAuthAppOnTestAccount)(this.appData.id, this.localDevState.targetTestingAccountId, this.appData.scopeGroupIds);
127
+ }
128
+ async installAppOrOpenInstallUrl(isReinstall) {
129
+ if (this.isAutomaticallyInstallable()) {
130
+ try {
131
+ await this.autoInstallStaticAuthApp();
132
+ logger_1.uiLogger.success(en_1.lib.AppDevModeInterface.autoInstallSuccess(this.appData.name, this.localDevState.targetTestingAccountId));
133
+ return;
134
+ }
135
+ catch (e) {
136
+ logger_1.uiLogger.error(en_1.lib.AppDevModeInterface.autoInstallError(this.appData.name, this.localDevState.targetTestingAccountId));
137
+ }
138
+ }
139
+ const installUrl = await this.getAppInstallUrl();
140
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
141
+ }
113
142
  async checkTestAccountAppInstallation() {
114
143
  if (!this.appNode || !this.appData) {
115
- return;
144
+ return {};
116
145
  }
117
146
  const { data: { isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups }, } = await (0, localDevAuth_1.fetchAppInstallationData)(this.localDevState.targetTestingAccountId, this.localDevState.projectId, this.appNode.uid, this.appNode.config.auth.requiredScopes, this.appNode.config.auth.optionalScopes);
118
147
  const isReinstall = previouslyAuthorizedScopeGroups.length > 0;
@@ -128,10 +157,7 @@ class AppDevModeInterface {
128
157
  installationState: constants_1.APP_INSTALLATION_STATES.INSTALLED_WITH_OUTDATED_SCOPES,
129
158
  };
130
159
  }
131
- if (!isInstalledWithScopeGroups) {
132
- const installUrl = await this.getAppInstallUrl();
133
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
134
- }
160
+ return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
135
161
  }
136
162
  setUpLocalDevServerMessageListeners() {
137
163
  this.localDevState.addListener('devServerMessage', message => {
@@ -151,7 +177,10 @@ class AppDevModeInterface {
151
177
  if (this.appNode.config.distribution === constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE) {
152
178
  await this.checkMarketplaceAppInstalls();
153
179
  }
154
- await this.checkTestAccountAppInstallation();
180
+ const { needsInstall, isReinstall } = await this.checkTestAccountAppInstallation();
181
+ if (needsInstall) {
182
+ await this.installAppOrOpenInstallUrl(isReinstall || false);
183
+ }
155
184
  }
156
185
  catch (e) {
157
186
  (0, index_1.logError)(e);
@@ -195,7 +195,7 @@ class LocalDevManager {
195
195
  scopes: this.activeApp.config.auth.requiredScopes,
196
196
  redirectUrls: this.activeApp.config.auth.redirectUrls,
197
197
  });
198
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
198
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
199
199
  }
200
200
  }
201
201
  updateKeypressListeners() {
@@ -18,4 +18,7 @@ type HandleProjectUploadArg<T> = {
18
18
  profile?: string;
19
19
  };
20
20
  export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
21
+ export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
22
+ export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
23
+ export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<unknown>;
21
24
  export {};
@@ -4,22 +4,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.handleProjectUpload = handleProjectUpload;
7
+ exports.validateSourceDirectory = validateSourceDirectory;
8
+ exports.validateNoHSMetaMismatch = validateNoHSMetaMismatch;
9
+ exports.handleTranslate = handleTranslate;
7
10
  const archiver_1 = __importDefault(require("archiver"));
8
11
  const tmp_1 = __importDefault(require("tmp"));
9
12
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
13
  const path_1 = __importDefault(require("path"));
11
14
  const projects_1 = require("@hubspot/local-dev-lib/api/projects");
12
15
  const ignoreRules_1 = require("@hubspot/local-dev-lib/ignoreRules");
16
+ const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
13
17
  const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
14
18
  const ui_1 = require("../ui");
15
- const exitCodes_1 = require("../enums/exitCodes");
16
- const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
17
19
  const errorHandlers_1 = require("../errorHandlers");
18
20
  const node_util_1 = __importDefault(require("node:util"));
19
21
  const en_1 = require("../../lang/en");
20
22
  const ensureProjectExists_1 = require("./ensureProjectExists");
21
23
  const logger_1 = require("../ui/logger");
22
24
  const buildAndDeploy_1 = require("./buildAndDeploy");
25
+ const exitCodes_1 = require("../enums/exitCodes");
23
26
  async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
24
27
  SpinniesManager_1.default.init({});
25
28
  const accountIdentifier = (0, ui_1.uiAccountDescription)(accountId);
@@ -49,14 +52,18 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
49
52
  }
50
53
  async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, }) {
51
54
  const srcDir = path_1.default.resolve(projectDir, projectConfig.srcDir);
52
- const filenames = fs_extra_1.default.readdirSync(srcDir);
53
- if (!filenames || filenames.length === 0) {
54
- logger_1.uiLogger.log(en_1.lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
55
- process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
55
+ try {
56
+ validateSourceDirectory(srcDir, projectConfig);
56
57
  }
57
- const hasHsMetaFiles = await (0, project_parsing_lib_1.projectContainsHsMetaFiles)(srcDir);
58
- if (!(0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion) && hasHsMetaFiles) {
59
- logger_1.uiLogger.error(en_1.lib.projectUpload.wrongPlatformVersionMetaFiles);
58
+ catch (e) {
59
+ (0, errorHandlers_1.logError)(e);
60
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
61
+ }
62
+ try {
63
+ await validateNoHSMetaMismatch(srcDir, projectConfig);
64
+ }
65
+ catch (e) {
66
+ (0, errorHandlers_1.logError)(e);
60
67
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
61
68
  }
62
69
  const tempFile = tmp_1.default.fileSync({ postfix: '.zip' });
@@ -68,21 +75,11 @@ async function handleProjectUpload({ accountId, projectConfig, projectDir, callb
68
75
  let intermediateRepresentation;
69
76
  if (sendIR) {
70
77
  try {
71
- intermediateRepresentation = await (0, project_parsing_lib_1.translate)({
72
- projectSourceDir: path_1.default.join(projectDir, projectConfig.srcDir),
73
- platformVersion: projectConfig.platformVersion,
74
- accountId,
75
- }, { skipValidation, profile });
76
- logger_1.uiLogger.debug(node_util_1.default.inspect(intermediateRepresentation, false, null, true));
78
+ intermediateRepresentation = await handleTranslate(projectDir, projectConfig, accountId, skipValidation, profile);
77
79
  }
78
80
  catch (e) {
79
- if ((0, project_parsing_lib_1.isTranslationError)(e)) {
80
- logger_1.uiLogger.error(e.toString());
81
- }
82
- else {
83
- (0, errorHandlers_1.logError)(e);
84
- }
85
- return process.exit(exitCodes_1.EXIT_CODES.ERROR);
81
+ (0, errorHandlers_1.logError)(e);
82
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
86
83
  }
87
84
  }
88
85
  await (0, ensureProjectExists_1.ensureProjectExists)(accountId, projectConfig.name, {
@@ -116,3 +113,40 @@ async function handleProjectUpload({ accountId, projectConfig, projectDir, callb
116
113
  archive.finalize();
117
114
  return result;
118
115
  }
116
+ function validateSourceDirectory(srcDir, projectConfig) {
117
+ const filenames = fs_extra_1.default.readdirSync(srcDir);
118
+ if (!filenames || filenames.length === 0) {
119
+ const validationError = new Error(en_1.lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
120
+ validationError.name = 'ProjectValidationError';
121
+ throw validationError;
122
+ }
123
+ }
124
+ async function validateNoHSMetaMismatch(srcDir, projectConfig) {
125
+ const hasHsMetaFiles = await (0, project_parsing_lib_1.projectContainsHsMetaFiles)(srcDir);
126
+ if (!(0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion) && hasHsMetaFiles) {
127
+ const validationError = new Error(en_1.lib.projectUpload.wrongPlatformVersionMetaFiles);
128
+ validationError.name = 'ProjectValidationError';
129
+ throw validationError;
130
+ }
131
+ }
132
+ async function handleTranslate(projectDir, projectConfig, accountId, skipValidation, profile) {
133
+ try {
134
+ const intermediateRepresentation = await (0, project_parsing_lib_1.translate)({
135
+ projectSourceDir: path_1.default.join(projectDir, projectConfig.srcDir),
136
+ platformVersion: projectConfig.platformVersion,
137
+ accountId,
138
+ }, { skipValidation, profile });
139
+ logger_1.uiLogger.debug(node_util_1.default.inspect(intermediateRepresentation, false, null, true));
140
+ return intermediateRepresentation;
141
+ }
142
+ catch (e) {
143
+ if ((0, project_parsing_lib_1.isTranslationError)(e)) {
144
+ const validationError = new Error(e.toString());
145
+ validationError.name = 'ProjectValidationError';
146
+ throw validationError;
147
+ }
148
+ else {
149
+ (0, errorHandlers_1.logError)(e);
150
+ }
151
+ }
152
+ }
@@ -1,3 +1,5 @@
1
+ export declare function getProjectComponentDistributionUrl(projectName: string, componentName: string, accountId: number): string;
2
+ export declare function getDeveloperOverviewUrl(accountId: number): string;
1
3
  export declare function getProjectDetailUrl(projectName: string, accountId: number): string | undefined;
2
4
  export declare function getProjectSettingsUrl(projectName: string, accountId: number): string | undefined;
3
5
  export declare function getProjectActivityUrl(projectName: string, accountId: number): string;
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProjectComponentDistributionUrl = getProjectComponentDistributionUrl;
4
+ exports.getDeveloperOverviewUrl = getDeveloperOverviewUrl;
3
5
  exports.getProjectDetailUrl = getProjectDetailUrl;
4
6
  exports.getProjectSettingsUrl = getProjectSettingsUrl;
5
7
  exports.getProjectActivityUrl = getProjectActivityUrl;
@@ -15,6 +17,14 @@ function getBaseUrl(accountId) {
15
17
  function getProjectHomeUrl(accountId) {
16
18
  return `${getBaseUrl(accountId)}/developer-projects/${accountId}`;
17
19
  }
20
+ function getProjectComponentDistributionUrl(projectName, componentName, accountId) {
21
+ const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)(accountId) === 'qa' ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD);
22
+ return `${baseUrl}/developer-projects/${accountId}/project/${projectName}/component/${componentName}/distribution`;
23
+ }
24
+ function getDeveloperOverviewUrl(accountId) {
25
+ const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)(accountId) === 'qa' ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD);
26
+ return `${baseUrl}/developer-overview/${accountId}`;
27
+ }
18
28
  function getProjectDetailUrl(projectName, accountId) {
19
29
  if (!projectName)
20
30
  return;
@@ -0,0 +1,17 @@
1
+ import { DeveloperTestAccountConfig } from '@hubspot/local-dev-lib/types/developerTestAccounts';
2
+ declare const hubs: {
3
+ readonly MARKETING: "marketingLevel";
4
+ readonly OPS: "opsLevel";
5
+ readonly SERVICE: "serviceLevel";
6
+ readonly SALES: "salesLevel";
7
+ readonly CONTENT: "contentLevel";
8
+ };
9
+ type HubName = keyof typeof hubs;
10
+ type HubTier = 'STARTER' | 'PROFESSIONAL' | 'ENTERPRISE';
11
+ export type HubConfig = `${HubName}:${HubTier}`;
12
+ export declare function createDeveloperTestAccountConfigPrompt(args?: {
13
+ name?: string;
14
+ description?: string;
15
+ tiers?: HubConfig[];
16
+ }): Promise<DeveloperTestAccountConfig>;
17
+ export {};
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDeveloperTestAccountConfigPrompt = createDeveloperTestAccountConfigPrompt;
4
+ const en_1 = require("../../lang/en");
5
+ const promptUtils_1 = require("./promptUtils");
6
+ const hubs = {
7
+ MARKETING: 'marketingLevel',
8
+ OPS: 'opsLevel',
9
+ SERVICE: 'serviceLevel',
10
+ SALES: 'salesLevel',
11
+ CONTENT: 'contentLevel',
12
+ };
13
+ const TEST_ACCOUNT_TIERS = [
14
+ { name: 'Marketing STARTER', value: 'MARKETING:STARTER' },
15
+ {
16
+ name: 'Marketing PROFESSIONAL',
17
+ value: 'MARKETING:PROFESSIONAL',
18
+ },
19
+ { name: 'Marketing ENTERPRISE', value: 'MARKETING:ENTERPRISE' },
20
+ promptUtils_1.Separator,
21
+ { name: 'Ops STARTER', value: 'OPS:STARTER' },
22
+ { name: 'Ops PROFESSIONAL', value: 'OPS:PROFESSIONAL' },
23
+ { name: 'Ops ENTERPRISE', value: 'OPS:ENTERPRISE' },
24
+ promptUtils_1.Separator,
25
+ { name: 'Service STARTER', value: 'SERVICE:STARTER' },
26
+ { name: 'Service PROFESSIONAL', value: 'SERVICE:PROFESSIONAL' },
27
+ { name: 'Service ENTERPRISE', value: 'SERVICE:ENTERPRISE' },
28
+ promptUtils_1.Separator,
29
+ { name: 'Sales STARTER', value: 'SALES:STARTER' },
30
+ { name: 'Sales PROFESSIONAL', value: 'SALES:PROFESSIONAL' },
31
+ { name: 'Sales ENTERPRISE', value: 'SALES:ENTERPRISE' },
32
+ promptUtils_1.Separator,
33
+ { name: 'Content STARTER', value: 'CONTENT:STARTER' },
34
+ { name: 'Content PROFESSIONAL', value: 'CONTENT:PROFESSIONAL' },
35
+ { name: 'Content ENTERPRISE', value: 'CONTENT:ENTERPRISE' },
36
+ promptUtils_1.Separator,
37
+ ];
38
+ async function createDeveloperTestAccountConfigPrompt(args = {}) {
39
+ const { name, description, tiers } = args;
40
+ let accountName = name;
41
+ let accountDescription = description;
42
+ let accountLevelsArray = tiers;
43
+ if (!accountName) {
44
+ const namePromptResult = await (0, promptUtils_1.promptUser)({
45
+ name: 'accountName',
46
+ message: en_1.lib.prompts.createDeveloperTestAccountConfigPrompt.namePrompt,
47
+ type: 'input',
48
+ });
49
+ accountName = namePromptResult.accountName;
50
+ }
51
+ if (!accountDescription) {
52
+ const descriptionPromptResult = await (0, promptUtils_1.promptUser)({
53
+ name: 'description',
54
+ message: en_1.lib.prompts.createDeveloperTestAccountConfigPrompt.descriptionPrompt,
55
+ type: 'input',
56
+ });
57
+ accountDescription = descriptionPromptResult.description;
58
+ }
59
+ if (!accountLevelsArray) {
60
+ const accountLevelsPromptResult = await (0, promptUtils_1.promptUser)({
61
+ name: 'testAccountLevels',
62
+ message: en_1.lib.prompts.createDeveloperTestAccountConfigPrompt.tiersPrompt,
63
+ type: 'checkbox',
64
+ choices: TEST_ACCOUNT_TIERS,
65
+ validate: choices => {
66
+ if (choices?.length > 1) {
67
+ const hubMap = {};
68
+ for (const choice of choices) {
69
+ const hub = choice.split(':')[0];
70
+ if (hubMap[hub]) {
71
+ return en_1.lib.prompts.createDeveloperTestAccountConfigPrompt.errors
72
+ .tiersError;
73
+ }
74
+ hubMap[hub] = true;
75
+ }
76
+ }
77
+ return true;
78
+ },
79
+ });
80
+ accountLevelsArray = accountLevelsPromptResult.testAccountLevels;
81
+ }
82
+ if (!accountLevelsArray) {
83
+ accountLevelsArray = [];
84
+ }
85
+ const accountLevels = accountLevelsArray.reduce((acc, level) => {
86
+ const [hubName, hubTier] = level.split(':');
87
+ const hubLevel = hubs[hubName];
88
+ acc[hubLevel] = hubTier;
89
+ return acc;
90
+ }, {});
91
+ return {
92
+ accountName: accountName,
93
+ description: accountDescription,
94
+ ...accountLevels,
95
+ };
96
+ }
@@ -1 +1,2 @@
1
- export declare function installAppPrompt(installUrl: string, isReinstall?: boolean): Promise<void>;
1
+ export declare function installAppBrowserPrompt(installUrl: string, isReinstall?: boolean): Promise<void>;
2
+ export declare function installAppAutoPrompt(): Promise<boolean>;
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.installAppPrompt = installAppPrompt;
6
+ exports.installAppBrowserPrompt = installAppBrowserPrompt;
7
+ exports.installAppAutoPrompt = installAppAutoPrompt;
7
8
  const open_1 = __importDefault(require("open"));
8
9
  const promptUtils_1 = require("./promptUtils");
9
10
  const exitCodes_1 = require("../enums/exitCodes");
10
11
  const en_1 = require("../../lang/en");
11
12
  const logger_1 = require("../ui/logger");
12
- async function installAppPrompt(installUrl, isReinstall = false) {
13
+ async function installAppBrowserPrompt(installUrl, isReinstall = false) {
13
14
  logger_1.uiLogger.log('');
14
15
  if (isReinstall) {
15
16
  logger_1.uiLogger.log(en_1.lib.prompts.installAppPrompt.reinstallExplanation);
@@ -33,3 +34,12 @@ async function installAppPrompt(installUrl, isReinstall = false) {
33
34
  }
34
35
  (0, open_1.default)(installUrl);
35
36
  }
37
+ async function installAppAutoPrompt() {
38
+ logger_1.uiLogger.log('');
39
+ const { shouldInstall } = await (0, promptUtils_1.promptUser)({
40
+ name: 'shouldInstall',
41
+ type: 'confirm',
42
+ message: en_1.lib.prompts.installAppPrompt.autoPrompt,
43
+ });
44
+ return shouldInstall;
45
+ }
@@ -1,4 +1,5 @@
1
1
  import { PromptConfig, GenericPromptResponse, PromptWhen, PromptChoices } from '../../types/Prompts';
2
+ export declare const Separator: any;
2
3
  export declare function promptUser<T extends GenericPromptResponse>(config: PromptConfig<T> | PromptConfig<T>[]): Promise<T>;
3
4
  export declare function confirmPrompt(message: string, options?: {
4
5
  defaultAnswer?: boolean;
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Separator = void 0;
3
4
  exports.promptUser = promptUser;
4
5
  exports.confirmPrompt = confirmPrompt;
5
6
  exports.listPrompt = listPrompt;
6
7
  exports.inputPrompt = inputPrompt;
7
8
  const inquirer = require('inquirer');
8
9
  const promptModule = inquirer.createPromptModule();
10
+ exports.Separator = new inquirer.Separator();
9
11
  function promptUser(config) {
10
12
  return promptModule(config);
11
13
  }
@@ -7,4 +7,5 @@ export declare const uiLogger: {
7
7
  debug: (...args: any[]) => void;
8
8
  group: (message: string) => void;
9
9
  groupEnd: () => void;
10
+ json: (json: object) => void;
10
11
  };