@hubspot/cli 7.7.30-experimental.0 → 7.7.31-experimental.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import yargs from 'yargs';
2
+ import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addGlobalOptions, addJSONOutputOptions, } from '../../../lib/commonOpts.js';
3
+ import installCommand from '../install.js';
4
+ vi.mock('../../../lib/commonOpts');
5
+ describe('commands/app/install', () => {
6
+ const yargsMock = yargs;
7
+ describe('command', () => {
8
+ it('should have the correct command structure', () => {
9
+ expect(installCommand.command).toEqual('install <test-account-id>');
10
+ });
11
+ });
12
+ describe('describe', () => {
13
+ it('should provide a description', () => {
14
+ expect(installCommand.describe).not.toBeDefined();
15
+ });
16
+ });
17
+ describe('builder', () => {
18
+ it('should support the correct options', () => {
19
+ installCommand.builder(yargsMock);
20
+ expect(addGlobalOptions).toHaveBeenCalledTimes(1);
21
+ expect(addGlobalOptions).toHaveBeenCalledWith(yargsMock);
22
+ expect(addAccountOptions).toHaveBeenCalledTimes(1);
23
+ expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
24
+ expect(addConfigOptions).toHaveBeenCalledTimes(1);
25
+ expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
26
+ expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
27
+ expect(addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
28
+ expect(addJSONOutputOptions).toHaveBeenCalledTimes(1);
29
+ expect(addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
30
+ expect(yargsMock.positional).toHaveBeenCalledTimes(1);
31
+ expect(yargsMock.positional).toHaveBeenCalledWith('test-account-id', expect.objectContaining({
32
+ type: 'number',
33
+ required: true,
34
+ describe: expect.any(String),
35
+ }));
36
+ expect(yargsMock.option).toHaveBeenCalledTimes(2);
37
+ expect(yargsMock.option).toHaveBeenCalledWith('app-uid', expect.objectContaining({
38
+ type: 'string',
39
+ describe: expect.any(String),
40
+ }));
41
+ expect(yargsMock.option).toHaveBeenCalledWith('project-name', expect.objectContaining({
42
+ type: 'string',
43
+ describe: expect.any(String),
44
+ }));
45
+ });
46
+ });
47
+ });
@@ -0,0 +1,8 @@
1
+ import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule, JSONOutputArgs } from '../../types/Yargs.js';
2
+ type InstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ appUid?: string;
4
+ projectName?: string;
5
+ testAccountId: number;
6
+ };
7
+ declare const installAppCommand: YargsCommandModule<unknown, InstallAppArgs>;
8
+ export default installAppCommand;
@@ -0,0 +1,122 @@
1
+ import { fetchDeveloperTestAccountOauthAppInstallStatus, installOauthAppIntoDeveloperTestAccount, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
2
+ import { trackCommandUsage } from '../../lib/usageTracking.js';
3
+ import { commands } from '../../lang/en.js';
4
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
5
+ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
6
+ import { APP_AUTH_TYPES } from '../../lib/constants.js';
7
+ import { uiLogger } from '../../lib/ui/logger.js';
8
+ import SpinniesManager from '../../lib/ui/SpinniesManager.js';
9
+ import { poll } from '../../lib/polling.js';
10
+ import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
11
+ import { handleTranslate } from '../../lib/projects/upload.js';
12
+ import { isAppIRNode } from '../../lib/projects/structure.js';
13
+ import { logError } from '../../lib/errorHandlers/index.js';
14
+ const command = 'install <test-account-id>';
15
+ const describe = undefined; // commands.app.subcommands.install.describe;
16
+ async function handler(args) {
17
+ const { derivedAccountId, appUid, projectName, testAccountId, formatOutputAsJson, } = args;
18
+ trackCommandUsage('app-install', {}, derivedAccountId);
19
+ const jsonOutput = {};
20
+ let targetProjectName = projectName;
21
+ let targetAppUid = appUid;
22
+ const { projectConfig, projectDir } = await getProjectConfig();
23
+ if (!targetProjectName) {
24
+ validateProjectConfig(projectConfig, projectDir);
25
+ targetProjectName = projectConfig?.name;
26
+ }
27
+ if (!targetProjectName) {
28
+ uiLogger.error(commands.app.subcommands.install.errors.mustSpecifyProjectName);
29
+ process.exit(EXIT_CODES.ERROR);
30
+ }
31
+ let isAppOauth = true;
32
+ if (!targetAppUid) {
33
+ const intermediateRepresentation = await handleTranslate(projectDir, projectConfig, derivedAccountId, true, undefined);
34
+ if (intermediateRepresentation) {
35
+ Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).forEach(node => {
36
+ if (isAppIRNode(node)) {
37
+ targetAppUid = node.uid;
38
+ isAppOauth = node.config.auth.type === APP_AUTH_TYPES.OAUTH;
39
+ }
40
+ });
41
+ }
42
+ }
43
+ if (!targetAppUid) {
44
+ uiLogger.error(commands.app.subcommands.install.errors.noAppUidFound);
45
+ process.exit(EXIT_CODES.ERROR);
46
+ }
47
+ if (!isAppOauth) {
48
+ uiLogger.error(commands.app.subcommands.install.errors.appMustBeOauth);
49
+ process.exit(EXIT_CODES.ERROR);
50
+ }
51
+ try {
52
+ const { data } = await installOauthAppIntoDeveloperTestAccount(derivedAccountId, testAccountId, targetProjectName, targetAppUid);
53
+ if (data?.authCodes.length > 0) {
54
+ jsonOutput.authCode = data.authCodes[0].authCode;
55
+ }
56
+ }
57
+ catch (err) {
58
+ logError(err);
59
+ process.exit(EXIT_CODES.ERROR);
60
+ }
61
+ SpinniesManager.init({
62
+ succeedColor: 'white',
63
+ });
64
+ SpinniesManager.add('installApp', {
65
+ text: commands.app.subcommands.install.polling.start,
66
+ });
67
+ let appInstallSucceeded = false;
68
+ try {
69
+ await poll(() => fetchDeveloperTestAccountOauthAppInstallStatus(derivedAccountId, targetProjectName, targetAppUid), {
70
+ successStates: ['SUCCESS'],
71
+ errorStates: [],
72
+ });
73
+ appInstallSucceeded = true;
74
+ }
75
+ catch (err) {
76
+ SpinniesManager.fail('installApp');
77
+ logError(err);
78
+ process.exit(EXIT_CODES.ERROR);
79
+ }
80
+ if (!appInstallSucceeded) {
81
+ SpinniesManager.fail('installApp');
82
+ process.exit(EXIT_CODES.ERROR);
83
+ }
84
+ SpinniesManager.succeed('installApp', {
85
+ text: commands.app.subcommands.install.polling.success,
86
+ });
87
+ if (formatOutputAsJson) {
88
+ uiLogger.json(jsonOutput);
89
+ }
90
+ process.exit(EXIT_CODES.SUCCESS);
91
+ }
92
+ function installAppBuilder(yargs) {
93
+ yargs.positional('test-account-id', {
94
+ describe: commands.app.subcommands.install.positionals.testAccountId,
95
+ required: true,
96
+ type: 'number',
97
+ });
98
+ yargs.option('app-uid', {
99
+ describe: commands.app.subcommands.install.options.appUid,
100
+ type: 'string',
101
+ });
102
+ yargs.option('project-name', {
103
+ describe: commands.app.subcommands.install.options.projectName,
104
+ type: 'string',
105
+ });
106
+ yargs.example('install 1234567890 --app-uid=my-app-uid --project-name=my-project', commands.app.subcommands.install.example);
107
+ return yargs;
108
+ }
109
+ const builder = makeYargsBuilder(installAppBuilder, command, commands.app.subcommands.install.describe, {
110
+ useGlobalOptions: true,
111
+ useAccountOptions: true,
112
+ useConfigOptions: true,
113
+ useEnvironmentOptions: true,
114
+ useJSONOutputOptions: true,
115
+ });
116
+ const installAppCommand = {
117
+ command,
118
+ describe,
119
+ handler,
120
+ builder,
121
+ };
122
+ export default installAppCommand;
package/commands/app.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import migrateCommand from './app/migrate.js';
2
2
  import appSecretCommand from './app/secret.js';
3
+ import installAppCommand from './app/install.js';
3
4
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
4
5
  const command = ['app', 'apps'];
5
6
  // Keep the command hidden for now
6
7
  const describe = undefined;
7
8
  function appBuilder(yargs) {
8
- yargs.command(migrateCommand).command(appSecretCommand).demandCommand(1, '');
9
+ yargs
10
+ .command(migrateCommand)
11
+ .command(appSecretCommand)
12
+ .command(installAppCommand)
13
+ .demandCommand(1, '');
9
14
  return yargs;
10
15
  }
11
16
  const builder = makeYargsBuilder(appBuilder, command, describe);
@@ -1,5 +1,5 @@
1
- import { CommonArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
- type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
1
+ import { CommonArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
+ type ProjectUploadArgs = CommonArgs & JSONOutputArgs & EnvironmentArgs & {
3
3
  forceCreate: boolean;
4
4
  message: string;
5
5
  m: string;
@@ -25,7 +25,7 @@ async function handler(args) {
25
25
  validateProjectConfig(projectConfig, projectDir);
26
26
  let targetAccountId;
27
27
  if (useV3Api(projectConfig.platformVersion)) {
28
- targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile);
28
+ targetAccountId = await loadAndValidateProfile(projectConfig, projectDir, profile, args.useEnv);
29
29
  }
30
30
  targetAccountId = targetAccountId || derivedAccountId;
31
31
  const accountConfig = getAccountConfig(targetAccountId);
@@ -75,6 +75,9 @@ async function handler(args) {
75
75
  resultJson.personalAccessKey = createResult.personalAccessKey;
76
76
  }
77
77
  catch (err) {
78
+ SpinniesManager.fail('createTestAccount', {
79
+ text: commands.testAccount.create.polling.createFailure,
80
+ });
78
81
  logError(err);
79
82
  SpinniesManager.fail('createTestAccount', {
80
83
  text: commands.testAccount.create.polling.createFailure,
package/lang/en.d.ts CHANGED
@@ -1550,6 +1550,28 @@ ${string}`;
1550
1550
  readonly app: {
1551
1551
  readonly describe: "Commands for managing apps.";
1552
1552
  readonly subcommands: {
1553
+ readonly install: {
1554
+ readonly describe: "Install an OAuth app into a test account.";
1555
+ readonly options: {
1556
+ readonly appUid: "The uid of the app to install";
1557
+ readonly projectName: "The name of the project that contains the app";
1558
+ };
1559
+ readonly positionals: {
1560
+ readonly testAccountId: "The id of the test account to install the app into";
1561
+ };
1562
+ readonly errors: {
1563
+ 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.`;
1564
+ 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.`;
1565
+ readonly appMustBeOauth: "This command only supports installing oauth apps. Please specify an app with oauth auth type.";
1566
+ };
1567
+ readonly polling: {
1568
+ readonly start: "Installing app...";
1569
+ readonly success: "App installed successfully";
1570
+ readonly failure: "App installation failed";
1571
+ readonly error: "Error installing app";
1572
+ };
1573
+ readonly example: "Install the app with uid my-app-uid from the project named \"my-project\" into the target account with id 1234567890";
1574
+ };
1553
1575
  readonly secret: {
1554
1576
  readonly describe: "Commands for managing secrets.";
1555
1577
  readonly subcommands: {
@@ -2569,6 +2591,8 @@ export declare const lib: {
2569
2591
  readonly activeInstallations: (appName: string, installCount: number) => string;
2570
2592
  readonly error: "An error occurred while checking installations for your app";
2571
2593
  };
2594
+ 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.`;
2595
+ 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.`;
2572
2596
  };
2573
2597
  readonly LocalDevWebsocketServer: {
2574
2598
  readonly errors: {
package/lang/en.js CHANGED
@@ -5,7 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
5
5
  import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
6
6
  import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
7
7
  import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
8
- import { PROJECT_CONFIG_FILE, PROJECT_WITH_APP } from '../lib/constants.js';
8
+ import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
9
9
  import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
10
10
  export const commands = {
11
11
  generalErrors: {
@@ -1547,6 +1547,28 @@ export const commands = {
1547
1547
  app: {
1548
1548
  describe: 'Commands for managing apps.',
1549
1549
  subcommands: {
1550
+ install: {
1551
+ describe: 'Install an OAuth app into a test account.',
1552
+ options: {
1553
+ appUid: 'The uid of the app to install',
1554
+ projectName: 'The name of the project that contains the app',
1555
+ },
1556
+ positionals: {
1557
+ testAccountId: 'The id of the test account to install the app into',
1558
+ },
1559
+ errors: {
1560
+ mustSpecifyProjectName: `You must specify a project name. Use the ${uiCommandReference('--project-name')} flag to specify the project name or run this command from within a project directory.`,
1561
+ noAppUidFound: `No app uid found. Please specify the app uid with the ${uiCommandReference('--app-uid')} flag or run this command from within a project that contains an app.`,
1562
+ appMustBeOauth: 'This command only supports installing oauth apps. Please specify an app with oauth auth type.',
1563
+ },
1564
+ polling: {
1565
+ start: 'Installing app...',
1566
+ success: 'App installed successfully',
1567
+ failure: 'App installation failed',
1568
+ error: 'Error installing app',
1569
+ },
1570
+ example: 'Install the app with uid my-app-uid from the project named "my-project" into the target account with id 1234567890',
1571
+ },
1550
1572
  secret: {
1551
1573
  describe: 'Commands for managing secrets.',
1552
1574
  subcommands: {
@@ -2566,6 +2588,8 @@ export const lib = {
2566
2588
  activeInstallations: (appName, installCount) => `[WARNING] Your app ${chalk.bold(appName)} is installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}`,
2567
2589
  error: 'An error occurred while checking installations for your app',
2568
2590
  },
2591
+ 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.`,
2592
+ 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.`,
2569
2593
  },
2570
2594
  LocalDevWebsocketServer: {
2571
2595
  errors: {
package/lib/mcp/setup.js CHANGED
@@ -182,7 +182,7 @@ export async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
182
182
  });
183
183
  await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
184
184
  }
185
- await execAsync(`claude mcp add-json "${mcpServerName}" ${JSON.stringify(mcpConfig)} --scope user`);
185
+ await execAsync(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
186
186
  SpinniesManager.succeed('claudeCode', {
187
187
  text: commands.mcp.setup.spinners.configuredClaudeCode,
188
188
  });
@@ -4,4 +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
+ export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined, useEnv?: boolean): Promise<number | undefined>;
@@ -38,7 +38,12 @@ export function loadProfile(projectConfig, projectDir, profileName) {
38
38
  uiLogger.error(lib.projectProfiles.loadProfile.errors.missingAccountId(profileFilename));
39
39
  return;
40
40
  }
41
- return profile;
41
+ return {
42
+ ...profile,
43
+ accountId: process.env.HUBSPOT_ACCOUNT_ID
44
+ ? Number(process.env.HUBSPOT_ACCOUNT_ID)
45
+ : profile.accountId,
46
+ };
42
47
  }
43
48
  catch (e) {
44
49
  uiLogger.error(lib.projectProfiles.loadProfile.errors.failedToLoadProfile(profileFilename));
@@ -54,7 +59,7 @@ export async function exitIfUsingProfiles(projectConfig, projectDir) {
54
59
  }
55
60
  }
56
61
  }
57
- export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile) {
62
+ export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile, useEnv = false) {
58
63
  if (argsProfile) {
59
64
  logProfileHeader(argsProfile);
60
65
  const profile = loadProfile(projectConfig, projectDir, argsProfile);
@@ -63,6 +68,9 @@ export async function loadAndValidateProfile(projectConfig, projectDir, argsProf
63
68
  process.exit(EXIT_CODES.ERROR);
64
69
  }
65
70
  logProfileFooter(profile, true);
71
+ if (useEnv) {
72
+ return Number(process.env.HUBSPOT_ACCOUNT_ID);
73
+ }
66
74
  return profile.accountId;
67
75
  }
68
76
  else {
@@ -100,6 +100,7 @@ describe('AppDevModeInterface', () => {
100
100
  setAppDataForUid: vi.fn(),
101
101
  addListener: vi.fn(),
102
102
  addUploadWarning: vi.fn(),
103
+ removeListener: vi.fn(),
103
104
  };
104
105
  mockLocalDevLogger = {};
105
106
  // Mock constructors
@@ -387,6 +388,15 @@ describe('AppDevModeInterface', () => {
387
388
  await appDevModeInterface.cleanup();
388
389
  expect(UIEDevModeInterface.cleanup).toHaveBeenCalled();
389
390
  });
391
+ it('should remove state listeners', async () => {
392
+ await appDevModeInterface.cleanup();
393
+ expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('devServerMessage',
394
+ // @ts-expect-error access private method for testing
395
+ appDevModeInterface.onDevServerMessage);
396
+ expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('projectNodes',
397
+ // @ts-expect-error
398
+ appDevModeInterface.onChangeProjectNodes);
399
+ });
390
400
  });
391
401
  describe('isAutomaticallyInstallable()', () => {
392
402
  it('should return true for static auth app on test account with correct parent', () => {
@@ -11,6 +11,7 @@ declare class AppDevModeInterface {
11
11
  _appNode?: AppIRNode | null;
12
12
  marketplaceAppInstalls?: number;
13
13
  constructor(options: AppDevModeInterfaceConstructorOptions);
14
+ private getAppNodeFromProjectNodes;
14
15
  private get appNode();
15
16
  private get appData();
16
17
  private set appData(value);
@@ -21,7 +22,10 @@ declare class AppDevModeInterface {
21
22
  private autoInstallStaticAuthApp;
22
23
  private installAppOrOpenInstallUrl;
23
24
  private checkTestAccountAppInstallation;
24
- private setUpLocalDevServerMessageListeners;
25
+ private onDevServerMessage;
26
+ private onChangeProjectNodes;
27
+ private setUpStateListeners;
28
+ private removeStateListeners;
25
29
  setup(args: any): Promise<void>;
26
30
  start(): Promise<void>;
27
31
  fileChange(filePath: string, event: string): Promise<void>;
@@ -30,12 +30,13 @@ class AppDevModeInterface {
30
30
  process.exit(EXIT_CODES.ERROR);
31
31
  }
32
32
  }
33
+ getAppNodeFromProjectNodes(projectNodes) {
34
+ return Object.values(projectNodes).find(isAppIRNode) || null;
35
+ }
33
36
  // Assumes only one app per project
34
37
  get appNode() {
35
38
  if (this._appNode === undefined) {
36
- this._appNode =
37
- Object.values(this.localDevState.projectNodes).find(isAppIRNode) ||
38
- null;
39
+ this._appNode = this.getAppNodeFromProjectNodes(this.localDevState.projectNodes);
39
40
  }
40
41
  return this._appNode;
41
42
  }
@@ -175,12 +176,33 @@ class AppDevModeInterface {
175
176
  }
176
177
  return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
177
178
  }
178
- setUpLocalDevServerMessageListeners() {
179
- this.localDevState.addListener('devServerMessage', message => {
180
- if (message === LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED) {
181
- this.checkTestAccountAppInstallation();
182
- }
183
- });
179
+ onDevServerMessage = (message) => {
180
+ if (message === LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED) {
181
+ this.checkTestAccountAppInstallation();
182
+ }
183
+ };
184
+ onChangeProjectNodes = (nodes) => {
185
+ const newAppNode = this.getAppNodeFromProjectNodes(nodes);
186
+ const oldDistribution = this.appNode?.config.distribution;
187
+ const newDistribution = newAppNode?.config.distribution;
188
+ const oldAuthType = this.appNode?.config.auth.type;
189
+ const newAuthType = newAppNode?.config.auth.type;
190
+ if (newDistribution === APP_DISTRIBUTION_TYPES.MARKETPLACE &&
191
+ oldDistribution !== APP_DISTRIBUTION_TYPES.MARKETPLACE) {
192
+ this.localDevState.addUploadWarning(lib.AppDevModeInterface.distributionChanged);
193
+ }
194
+ else if (newAuthType === APP_AUTH_TYPES.OAUTH &&
195
+ oldAuthType !== APP_AUTH_TYPES.OAUTH) {
196
+ this.localDevState.addUploadWarning(lib.AppDevModeInterface.authTypeChanged);
197
+ }
198
+ };
199
+ setUpStateListeners() {
200
+ this.localDevState.addListener('devServerMessage', this.onDevServerMessage);
201
+ this.localDevState.addListener('projectNodes', this.onChangeProjectNodes);
202
+ }
203
+ removeStateListeners() {
204
+ this.localDevState.removeListener('devServerMessage', this.onDevServerMessage);
205
+ this.localDevState.removeListener('projectNodes', this.onChangeProjectNodes);
184
206
  }
185
207
  // @ts-expect-error TODO: reconcile types between CLI and UIE Dev Server
186
208
  // In the future, update UIE Dev Server to use LocalDevState
@@ -215,7 +237,7 @@ class AppDevModeInterface {
215
237
  catch (e) {
216
238
  logError(e);
217
239
  }
218
- this.setUpLocalDevServerMessageListeners();
240
+ this.setUpStateListeners();
219
241
  return UIEDevModeInterface.setup(args);
220
242
  }
221
243
  async start() {
@@ -239,6 +261,7 @@ class AppDevModeInterface {
239
261
  if (!this.appNode) {
240
262
  return;
241
263
  }
264
+ this.removeStateListeners();
242
265
  return UIEDevModeInterface.cleanup();
243
266
  }
244
267
  }
@@ -1,5 +1,5 @@
1
1
  import { ComponentTypes, Component, GenericComponentConfig, PublicAppComponentConfig, PrivateAppComponentConfig, AppCardComponentConfig } from '../../types/Projects.js';
2
- import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
2
+ import { IntermediateRepresentationNode, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
3
3
  import { AppIRNode } from '../../types/ProjectComponents.js';
4
4
  export declare const CONFIG_FILES: {
5
5
  [k in ComponentTypes]: string;
@@ -15,4 +15,4 @@ export declare function getProjectComponentTypes(components: Array<Component>):
15
15
  export declare function getComponentUid(component?: Component | null): string | null;
16
16
  export declare function componentIsApp(component?: Component | null): component is Component<PublicAppComponentConfig | PrivateAppComponentConfig>;
17
17
  export declare function componentIsPublicApp(component?: Component | null): component is Component<PublicAppComponentConfig>;
18
- export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev): component is AppIRNode;
18
+ export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev | IntermediateRepresentationNode): component is AppIRNode;
@@ -1,4 +1,5 @@
1
1
  import { FileResult } from 'tmp';
2
+ import { IntermediateRepresentation } from '@hubspot/project-parsing-lib';
2
3
  import { ProjectConfig } from '../../types/Projects.js';
3
4
  type ProjectUploadCallbackFunction<T> = (accountId: number, projectConfig: ProjectConfig, tempFile: FileResult, buildId: number) => Promise<T>;
4
5
  type ProjectUploadResult<T> = {
@@ -20,5 +21,5 @@ type HandleProjectUploadArg<T> = {
20
21
  export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
21
22
  export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
22
23
  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>;
24
+ export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<IntermediateRepresentation | undefined>;
24
25
  export {};
@@ -133,4 +133,5 @@ export async function handleTranslate(projectDir, projectConfig, accountId, skip
133
133
  }
134
134
  throw e;
135
135
  }
136
+ return undefined;
136
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.7.30-experimental.0",
3
+ "version": "7.7.31-experimental.0",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
package/types/Yargs.d.ts CHANGED
@@ -15,7 +15,7 @@ export type AccountArgs = {
15
15
  account?: string;
16
16
  };
17
17
  export type EnvironmentArgs = {
18
- 'use-env'?: string;
18
+ 'use-env'?: boolean;
19
19
  };
20
20
  export type OverwriteArgs = Options & {
21
21
  o?: boolean;