@hubspot/cli 8.0.12-experimental.0 → 8.0.12-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 (53) hide show
  1. package/bin/cli.js +4 -3
  2. package/commands/account/clean.js +2 -0
  3. package/commands/account/createOverride.js +3 -0
  4. package/commands/account/info.js +34 -16
  5. package/commands/account/link.js +3 -2
  6. package/commands/account/list.js +29 -71
  7. package/commands/account/remove.js +2 -0
  8. package/commands/account/removeOverride.js +3 -0
  9. package/commands/account/unlink.js +3 -2
  10. package/commands/account/use.js +71 -1
  11. package/commands/project/dev/deprecatedFlow.js +20 -2
  12. package/commands/project/dev/index.js +6 -0
  13. package/commands/project/dev/unifiedFlow.js +20 -3
  14. package/commands/project/lint.js +18 -1
  15. package/commands/project/upload.js +27 -15
  16. package/lang/en.d.ts +28 -0
  17. package/lang/en.js +33 -4
  18. package/lib/constants.d.ts +2 -0
  19. package/lib/constants.js +4 -0
  20. package/lib/doctor/Doctor.js +5 -5
  21. package/lib/link/index.d.ts +4 -0
  22. package/lib/link/index.js +40 -9
  23. package/lib/link/linkUtils.d.ts +1 -0
  24. package/lib/link/linkUtils.js +26 -1
  25. package/lib/link/warnIfLinkedDirectory.d.ts +1 -0
  26. package/lib/link/warnIfLinkedDirectory.js +9 -0
  27. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +2 -1
  28. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  29. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +2 -0
  30. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -0
  31. package/lib/projects/preview.js +11 -24
  32. package/lib/projects/uieLinting.d.ts +9 -0
  33. package/lib/projects/uieLinting.js +45 -1
  34. package/lib/ui/accountTable.d.ts +8 -0
  35. package/lib/ui/accountTable.js +67 -0
  36. package/lib/yargs/parseYargsOrExit.d.ts +4 -0
  37. package/lib/yargs/parseYargsOrExit.js +25 -0
  38. package/mcp-server/server.js +8 -4
  39. package/mcp-server/tools/index.js +2 -0
  40. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  41. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  42. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  43. package/mcp-server/tools/project/FindProjectsTool.d.ts +15 -0
  44. package/mcp-server/tools/project/FindProjectsTool.js +60 -0
  45. package/mcp-server/tools/project/GetBuildLogsTool.js +1 -1
  46. package/mcp-server/tools/project/GetBuildStatusTool.js +1 -1
  47. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  48. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  49. package/package.json +1 -1
  50. package/types/Link.d.ts +8 -3
  51. package/types/Link.js +5 -1
  52. package/types/PackageJson.d.ts +1 -0
  53. package/types/Yargs.d.ts +1 -0
@@ -18,6 +18,20 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
18
  import { projectProfilePrompt } from '../../lib/prompts/projectProfilePrompt.js';
19
19
  const command = 'upload';
20
20
  const describe = commands.project.upload.describe;
21
+ async function handlePreview(accountId, projectId, buildId, targetPortalId) {
22
+ if (!projectId) {
23
+ uiLogger.warn(lib.projectPreview.missingProjectId);
24
+ return;
25
+ }
26
+ const previewResult = await triggerAndPollPreview(accountId, projectId, buildId, targetPortalId);
27
+ if (!previewResult.succeeded) {
28
+ uiLogger.warn(lib.projectPreview.warning);
29
+ }
30
+ return {
31
+ releaseTag: previewResult.releaseTag,
32
+ succeeded: previewResult.succeeded,
33
+ };
34
+ }
21
35
  async function handler(args) {
22
36
  const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, } = args;
23
37
  const jsonOutput = {};
@@ -92,20 +106,9 @@ async function handler(args) {
92
106
  await displayWarnLogs(targetAccountId, projectConfig.name, result.buildId);
93
107
  }
94
108
  if (result && result.succeeded && preview && targetPortalId) {
95
- if (!projectId) {
96
- uiLogger.warn(lib.projectPreview.missingProjectId);
97
- }
98
- else {
99
- const previewResult = await triggerAndPollPreview(targetAccountId, projectId, result.buildId, targetPortalId);
100
- if (!previewResult.succeeded) {
101
- uiLogger.warn(lib.projectPreview.warning);
102
- }
103
- if (formatOutputAsJson) {
104
- jsonOutput.preview = {
105
- releaseTag: previewResult.releaseTag,
106
- succeeded: previewResult.succeeded,
107
- };
108
- }
109
+ const previewJson = await handlePreview(targetAccountId, projectId, result.buildId, targetPortalId);
110
+ if (previewJson && formatOutputAsJson) {
111
+ jsonOutput.preview = previewJson;
109
112
  }
110
113
  }
111
114
  if (result && result.succeeded && formatOutputAsJson) {
@@ -158,10 +161,19 @@ function projectUploadBuilder(yargs) {
158
161
  target: {
159
162
  describe: commands.project.upload.options.target.describe,
160
163
  type: 'number',
164
+ requiresArg: true,
161
165
  },
162
166
  });
167
+ yargs.check(argv => {
168
+ if (argv.preview && argv.target == null) {
169
+ throw new Error(commands.project.upload.errors.previewRequiresTarget);
170
+ }
171
+ if (argv.target != null && !argv.preview) {
172
+ throw new Error(commands.project.upload.errors.targetRequiresPreview);
173
+ }
174
+ return true;
175
+ });
163
176
  yargs.conflicts('profile', 'account');
164
- yargs.implies('preview', 'target');
165
177
  yargs.example([
166
178
  ['$0 project upload', commands.project.upload.examples.default],
167
179
  [
package/lang/en.d.ts CHANGED
@@ -129,8 +129,12 @@ export declare const commands: {
129
129
  };
130
130
  list: {
131
131
  accounts: string;
132
+ allAccounts: string;
133
+ linkedAccounts: string;
132
134
  defaultAccountTitle: string;
135
+ linkedDefaultTitle: string;
133
136
  currentResolvedDefaultAccount: (accountId: number) => string;
137
+ directory: (dir: string) => string;
134
138
  describe: string;
135
139
  configPath: (configPath: string) => string;
136
140
  overrideFilePathTitle: string;
@@ -177,6 +181,15 @@ export declare const commands: {
177
181
  success: {
178
182
  defaultAccountUpdated: (accountName: string) => string;
179
183
  };
184
+ linked: {
185
+ editingLinkedDefault: (dir: string) => string;
186
+ alreadyDefault: (accountId: number) => string;
187
+ setLinkedDefault: (account: string) => string;
188
+ accountNotLinked: (account: string) => string;
189
+ promptToLink: (account: string) => string;
190
+ settingGlobalDefault: string;
191
+ nonInteractiveNotLinked: (account: string) => string;
192
+ };
180
193
  };
181
194
  link: {
182
195
  describe: string;
@@ -188,6 +201,9 @@ export declare const commands: {
188
201
  deprecatedConfigNotSupported: (command: string) => string;
189
202
  writeSettingsFailed: (path: string, err: unknown) => string;
190
203
  savedToSettings: (path: string) => string;
204
+ usingLinkedAccounts: (settingsPath: string) => string;
205
+ accountAutoLinked: (accountId: number) => string;
206
+ accountAutoLinkFailed: (accountId: number) => string;
191
207
  };
192
208
  linkingDirectory: (dir: string) => string;
193
209
  managingLinkedAccounts: (dir: string) => string;
@@ -297,6 +313,9 @@ export declare const commands: {
297
313
  };
298
314
  name: (name: string) => string;
299
315
  scopeGroups: string;
316
+ linkedDefaultTitle: string;
317
+ settingsPath: (path: string) => string;
318
+ linkedDefault: (account: string) => string;
300
319
  };
301
320
  clean: {
302
321
  describe: string;
@@ -1500,6 +1519,7 @@ export declare const commands: {
1500
1519
  projectAccount: string;
1501
1520
  testingAccount: string;
1502
1521
  account: string;
1522
+ port: string;
1503
1523
  };
1504
1524
  };
1505
1525
  create: {
@@ -1796,6 +1816,8 @@ export declare const commands: {
1796
1816
  errors: {
1797
1817
  noProjectConfig: string;
1798
1818
  projectLockedError: string;
1819
+ previewRequiresTarget: string;
1820
+ targetRequiresPreview: string;
1799
1821
  };
1800
1822
  options: {
1801
1823
  forceCreate: {
@@ -1927,6 +1949,7 @@ export declare const commands: {
1927
1949
  loading: {
1928
1950
  checking: string;
1929
1951
  creatingConfig: string;
1952
+ addingLintScripts: string;
1930
1953
  linting: string;
1931
1954
  };
1932
1955
  noProjectConfig: string;
@@ -1943,6 +1966,8 @@ export declare const commands: {
1943
1966
  failedToFetchRemoteEslintConfig: (platformVersion: string) => string;
1944
1967
  failedToCreateEslintConfig: (configPath: string) => string;
1945
1968
  eslintConfigRequired: string;
1969
+ lintScriptsAdded: (scriptNames: string[], packageJsonPath: string) => string;
1970
+ failedToAddLintScripts: (packageJsonPath: string) => string;
1946
1971
  };
1947
1972
  updateDeps: {
1948
1973
  help: {
@@ -3061,6 +3086,9 @@ export declare const commands: {
3061
3086
  };
3062
3087
  };
3063
3088
  export declare const lib: {
3089
+ linkedDirectory: {
3090
+ warning: (action: string, settingsPath: string) => string;
3091
+ };
3064
3092
  parsing: {
3065
3093
  unableToParseStringToNumber: string;
3066
3094
  };
package/lang/en.js CHANGED
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import { mapToUserFriendlyName } from '@hubspot/project-parsing-lib/transform';
3
3
  import { PLATFORM_VERSIONS } from '@hubspot/project-parsing-lib/constants';
4
4
  import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
5
+ import { LOCAL_DEV_DEFAULT_PORT } from '../lib/constants.js';
5
6
  import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, } from '@hubspot/local-dev-lib/constants/config';
6
7
  import { indent, UI_COLORS, uiAccountDescription, uiAuthCommandReference, uiBetaTag, uiCommandReference, uiLink, } from '../lib/ui/index.js';
7
8
  import { getLocalDevUiUrl, getProjectDetailUrl, getProjectSettingsUrl, } from '../lib/projects/urls.js';
@@ -137,8 +138,12 @@ export const commands = {
137
138
  },
138
139
  list: {
139
140
  accounts: `${chalk.bold('Accounts')}:`,
141
+ allAccounts: `${chalk.bold('All Accounts')}:`,
142
+ linkedAccounts: `${chalk.bold('Linked Accounts')}:`,
140
143
  defaultAccountTitle: `${chalk.bold('Default Account')}`,
144
+ linkedDefaultTitle: `${chalk.bold('Linked Default Account')}`,
141
145
  currentResolvedDefaultAccount: (accountId) => `Account: ${uiAccountDescription(accountId)}`,
146
+ directory: (dir) => `Directory: ${dir}`,
142
147
  describe: 'List names of accounts defined in config.',
143
148
  configPath: (configPath) => `Source: ${configPath}`,
144
149
  overrideFilePathTitle: `${chalk.bold('Default Account Override')}`,
@@ -185,6 +190,15 @@ export const commands = {
185
190
  success: {
186
191
  defaultAccountUpdated: (accountName) => `Default account updated to "${accountName}"`,
187
192
  },
193
+ linked: {
194
+ editingLinkedDefault: (dir) => `Editing the default linked account for this directory (not the global default):\n${indent(1)}${dir}`,
195
+ alreadyDefault: (accountId) => `${uiAccountDescription(accountId)} is the only linked account and is already the default.`,
196
+ setLinkedDefault: (account) => `Linked default set to ${chalk.cyan(account)}`,
197
+ accountNotLinked: (account) => `${account} is not linked to this directory.`,
198
+ promptToLink: (account) => `Would you like to link ${account} to this directory?`,
199
+ settingGlobalDefault: 'Setting global default instead.',
200
+ nonInteractiveNotLinked: (account) => `${account} is not linked to this directory. Setting global default instead.`,
201
+ },
188
202
  },
189
203
  link: {
190
204
  describe: 'Link authenticated HubSpot accounts to the current directory',
@@ -196,6 +210,9 @@ export const commands = {
196
210
  deprecatedConfigNotSupported: (command) => `${uiCommandReference(command)} does not support deprecated config. Run ${uiCommandReference('hs config migrate')} to migrate to global config. Then re-run ${uiCommandReference(command)}`,
197
211
  writeSettingsFailed: (path, err) => `Failed to write to ${path}: ${err}`,
198
212
  savedToSettings: (path) => `Saved to ${path}`,
213
+ usingLinkedAccounts: (settingsPath) => `This directory has linked accounts via ${settingsPath}. Only linked accounts will be available for this command. To manage linked accounts, run ${uiCommandReference('hs account link')}.`,
214
+ accountAutoLinked: (accountId) => `Automatically linked ${uiAccountDescription(accountId)} to this directory.`,
215
+ accountAutoLinkFailed: (accountId) => `Could not automatically link ${uiAccountDescription(accountId)} to this directory. Run ${uiCommandReference('hs account link')} to link it manually.`,
199
216
  },
200
217
  linkingDirectory: (dir) => `Linking HubSpot account(s) for directory:\n${indent(1)}${dir}`,
201
218
  managingLinkedAccounts: (dir) => `Managing linked accounts for:\n${indent(1)}${dir}`,
@@ -305,6 +322,9 @@ export const commands = {
305
322
  },
306
323
  name: (name) => `${chalk.bold('Account name')}: ${name}`,
307
324
  scopeGroups: `${chalk.bold('Scopes available')}:`,
325
+ linkedDefaultTitle: `${chalk.bold('Linked Default Account')}`,
326
+ settingsPath: (path) => `Source: ${path}`,
327
+ linkedDefault: (account) => `Account: ${account}`,
308
328
  },
309
329
  clean: {
310
330
  describe: 'Check for inactive accounts and removes them from the CLI config.',
@@ -1516,6 +1536,7 @@ export const commands = {
1516
1536
  projectAccount: 'The id of the account to upload your project to. Must be used with --testing-account. Supported on platform versions 2025.2 and newer.',
1517
1537
  testingAccount: 'The id of the account to install apps and test on. Must be used with --project-account. Supported on platform versions 2025.2 and newer.',
1518
1538
  account: 'The id of the account to upload your project to. Unsupported on platform versions 2025.2 and newer.',
1539
+ port: `The port for the local dev server. Defaults to ${LOCAL_DEV_DEFAULT_PORT}.`,
1519
1540
  },
1520
1541
  },
1521
1542
  create: {
@@ -1812,6 +1833,8 @@ export const commands = {
1812
1833
  errors: {
1813
1834
  noProjectConfig: 'No project detected. Run this command from a project directory.',
1814
1835
  projectLockedError: `Your project is locked. This may mean that another user is running the ${uiCommandReference('hs project dev')} command for this project. If this is you, unlock the project in Projects UI.`,
1836
+ previewRequiresTarget: `${uiCommandReference('--preview')} requires ${uiCommandReference('--target=<portalId>')} to specify the portal to preview on.`,
1837
+ targetRequiresPreview: `${uiCommandReference('--target')} can only be used with ${uiCommandReference('--preview')}.`,
1815
1838
  },
1816
1839
  options: {
1817
1840
  forceCreate: {
@@ -1943,6 +1966,7 @@ export const commands = {
1943
1966
  loading: {
1944
1967
  checking: 'Checking lint packages and configuration…',
1945
1968
  creatingConfig: 'Creating ESLint configuration files…',
1969
+ addingLintScripts: 'Adding lint scripts to package.json files…',
1946
1970
  linting: 'Linting…',
1947
1971
  },
1948
1972
  noProjectConfig: 'No project detected. Run this command from a project directory.',
@@ -1971,6 +1995,8 @@ export const commands = {
1971
1995
  failedToFetchRemoteEslintConfig: (platformVersion) => `Could not download the ESLint config from HubSpot project components for platform version ${platformVersion}. Check your network connection and try again, or add eslint.config.js manually.`,
1972
1996
  failedToCreateEslintConfig: (configPath) => `Failed to create ESLint configuration at ${configPath}`,
1973
1997
  eslintConfigRequired: 'ESLint configuration is required to run the lint command. Run the command again to create the configuration.',
1998
+ lintScriptsAdded: (scriptNames, packageJsonPath) => `Added ${scriptNames.map(s => `"${s}"`).join(' and ')} to ${packageJsonPath}`,
1999
+ failedToAddLintScripts: (packageJsonPath) => `Failed to add lint scripts to ${packageJsonPath}`,
1974
2000
  },
1975
2001
  updateDeps: {
1976
2002
  help: {
@@ -2433,7 +2459,7 @@ export const commands = {
2433
2459
  opsLevel: 'Operations Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2434
2460
  serviceLevel: 'Service Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2435
2461
  salesLevel: 'Sales Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2436
- contentLevel: 'CMS Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2462
+ contentLevel: 'Content Hub tier. Options: FREE, STARTER, PROFESSIONAL, ENTERPRISE',
2437
2463
  commerceLevel: 'Commerce Hub tier. Options: FREE, PROFESSIONAL, ENTERPRISE',
2438
2464
  },
2439
2465
  example: (configPath) => `Create a test account from the config file at ${configPath}`,
@@ -3086,6 +3112,9 @@ export const commands = {
3086
3112
  },
3087
3113
  };
3088
3114
  export const lib = {
3115
+ linkedDirectory: {
3116
+ warning: (action, settingsPath) => `This directory has linked accounts via ${settingsPath}. ${uiCommandReference(action)} modifies your global config, not the linked directory settings. Use ${uiCommandReference('hs account link')} to manage linked accounts.\n`,
3117
+ },
3089
3118
  parsing: {
3090
3119
  unableToParseStringToNumber: 'Unable to parse string to number',
3091
3120
  },
@@ -4062,9 +4091,9 @@ export const lib = {
4062
4091
  validJson: 'JSON files valid',
4063
4092
  },
4064
4093
  port: {
4065
- inUse: (port) => `Port ${port} is in use`,
4066
- inUseSecondary: `Make sure it is available before running ${uiCommandReference('hs project dev')}`,
4067
- available: (port) => `Port ${port} available for local development`,
4094
+ inUse: (port) => `Default port ${port} is in use`,
4095
+ inUseSecondary: `Make sure it is available before running ${uiCommandReference('hs project dev')}, or use ${uiCommandReference('--port')} to specify a different port`,
4096
+ available: (port) => `Default port ${port} available for local development`,
4068
4097
  },
4069
4098
  projectValidation: {
4070
4099
  valid: 'Project configuration and structure is valid',
@@ -4,6 +4,7 @@ export declare const FEEDBACK_INTERVAL: 10;
4
4
  export declare const HUBSPOT_FOLDER: "@hubspot";
5
5
  export declare const MARKETPLACE_FOLDER: "@marketplace";
6
6
  export declare const DEFAULT_POLLING_DELAY = 2000;
7
+ export declare const PREVIEW_POLL_TIMEOUT: number;
7
8
  export declare const PROJECT_CONFIG_FILE: "hsproject.json";
8
9
  export declare const PROJECT_BUILD_STATES: {
9
10
  readonly BUILDING: "BUILDING";
@@ -142,3 +143,4 @@ export declare const ACCOUNT_LEVELS: {
142
143
  };
143
144
  export declare const ACCOUNT_LEVEL_CHOICES: readonly ["FREE", "STARTER", "PROFESSIONAL", "ENTERPRISE"];
144
145
  export declare const FEEDBACK_URL = "https://developers.hubspot.com/feedback";
146
+ export declare const LOCAL_DEV_DEFAULT_PORT = 4828;
package/lib/constants.js CHANGED
@@ -4,6 +4,7 @@ export const FEEDBACK_INTERVAL = 10;
4
4
  export const HUBSPOT_FOLDER = '@hubspot';
5
5
  export const MARKETPLACE_FOLDER = '@marketplace';
6
6
  export const DEFAULT_POLLING_DELAY = 2000;
7
+ export const PREVIEW_POLL_TIMEOUT = 5 * 60 * 1000;
7
8
  export const PROJECT_CONFIG_FILE = 'hsproject.json';
8
9
  export const PROJECT_BUILD_STATES = {
9
10
  BUILDING: 'BUILDING',
@@ -143,3 +144,6 @@ export const ACCOUNT_LEVEL_CHOICES = [
143
144
  ACCOUNT_LEVELS.ENTERPRISE,
144
145
  ];
145
146
  export const FEEDBACK_URL = 'https://developers.hubspot.com/feedback';
147
+ // TODO: remove this constant and use PORT_MANAGER_SERVER_PORT from
148
+ // @hubspot/local-dev-lib once LDL changes its default to 4828.
149
+ export const LOCAL_DEV_DEFAULT_PORT = 4828;
@@ -10,8 +10,8 @@ import fs from 'fs';
10
10
  import path from 'path';
11
11
  import { Diagnosis } from './Diagnosis.js';
12
12
  import { DiagnosticInfoBuilder, } from './DiagnosticInfoBuilder.js';
13
- import { isPortManagerPortAvailable } from '@hubspot/local-dev-lib/portManager';
14
- import { PORT_MANAGER_SERVER_PORT } from '@hubspot/local-dev-lib/constants/ports';
13
+ import { isPortAvailable } from '@hubspot/local-dev-lib/portManager';
14
+ import { LOCAL_DEV_DEFAULT_PORT } from '../constants.js';
15
15
  import { accessTokenForPersonalAccessKey,
16
16
  // scopesOnAccessToken,
17
17
  } from '@hubspot/local-dev-lib/personalAccessKey';
@@ -322,16 +322,16 @@ export class Doctor {
322
322
  }
323
323
  }
324
324
  async checkIfPortsAreAvailable() {
325
- if (await isPortManagerPortAvailable()) {
325
+ if (await isPortAvailable(LOCAL_DEV_DEFAULT_PORT)) {
326
326
  this.diagnosis?.addProjectSection({
327
327
  type: 'success',
328
- message: lib.doctor.port.available(PORT_MANAGER_SERVER_PORT),
328
+ message: lib.doctor.port.available(LOCAL_DEV_DEFAULT_PORT),
329
329
  });
330
330
  return;
331
331
  }
332
332
  this.diagnosis?.addProjectSection({
333
333
  type: 'warning',
334
- message: lib.doctor.port.inUse(PORT_MANAGER_SERVER_PORT),
334
+ message: lib.doctor.port.inUse(LOCAL_DEV_DEFAULT_PORT),
335
335
  secondaryMessaging: lib.doctor.port.inUseSecondary,
336
336
  });
337
337
  }
@@ -12,3 +12,7 @@ export declare function handleLinkFlow({ settings, accountOverrideId, args, }: {
12
12
  accountOverrideId: number | null;
13
13
  args: ArgumentsCamelCase<LinkArgs>;
14
14
  }): Promise<ActionResult>;
15
+ export declare function handleLinkedUseAction({ state, targetAccountId, }: {
16
+ state: HsSettingsFile;
17
+ targetAccountId?: number;
18
+ }): Promise<ActionResult>;
package/lib/link/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { getAllConfigAccounts, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
2
2
  import { confirmPrompt } from '../prompts/promptUtils.js';
3
3
  import { promptForAccountsToLink, promptForAccountsToUnlink, promptForAction, promptForDefaultAccount, } from './prompts.js';
4
+ import { ACTION_RESULT_STATUS, } from '../../types/Link.js';
4
5
  import { getDefaultAccountOverrideFilePath, removeDefaultAccountOverrideFile, } from '@hubspot/local-dev-lib/config/defaultAccountOverride';
5
6
  import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
6
7
  import { authenticateNewAccount } from '../accountAuth.js';
@@ -19,7 +20,7 @@ export class ActionHandlers {
19
20
  }, { eligibleAccounts: [], inEligibleAccounts: [] });
20
21
  const toAdd = await promptForAccountsToLink(context, eligibleAccounts, inEligibleAccounts, state.localDefaultAccount);
21
22
  const accounts = [...state.accounts, ...toAdd];
22
- uiLogger.success(commands.account.subcommands.link.events.accountsLinked(toAdd.length));
23
+ uiLogger.info(commands.account.subcommands.link.events.accountsLinked(toAdd.length));
23
24
  const overrideFilePath = getDefaultAccountOverrideFilePath();
24
25
  if (overrideFilePath &&
25
26
  context.accountOverrideId &&
@@ -32,7 +33,7 @@ export class ActionHandlers {
32
33
  if (useOverride) {
33
34
  uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(context.accountOverrideId));
34
35
  return {
35
- status: 'success',
36
+ status: ACTION_RESULT_STATUS.SUCCESS,
36
37
  settings: {
37
38
  accounts,
38
39
  localDefaultAccount: context.accountOverrideId,
@@ -45,14 +46,14 @@ export class ActionHandlers {
45
46
  currentDefault: state.localDefaultAccount,
46
47
  });
47
48
  return {
48
- status: 'success',
49
+ status: ACTION_RESULT_STATUS.SUCCESS,
49
50
  settings: { accounts, localDefaultAccount },
50
51
  };
51
52
  }
52
53
  static async unlink({ state }) {
53
54
  const toRemove = await promptForAccountsToUnlink(state.accounts, state.localDefaultAccount);
54
55
  if (toRemove.length === 0) {
55
- return { status: 'noop' };
56
+ return { status: ACTION_RESULT_STATUS.NOOP };
56
57
  }
57
58
  const remainingAccounts = state.accounts.filter(account => !toRemove.includes(account));
58
59
  const defaultWasRemoved = state.localDefaultAccount !== undefined &&
@@ -61,7 +62,7 @@ export class ActionHandlers {
61
62
  if (remainingAccounts.length === 0) {
62
63
  uiLogger.success(commands.account.subcommands.link.events.noAccountsLinked);
63
64
  return {
64
- status: 'success',
65
+ status: ACTION_RESULT_STATUS.SUCCESS,
65
66
  settings: {
66
67
  accounts: remainingAccounts,
67
68
  localDefaultAccount: undefined,
@@ -73,7 +74,7 @@ export class ActionHandlers {
73
74
  uiLogger.success(commands.account.subcommands.link.events.accountsUnlinked(toRemove.length));
74
75
  uiLogger.success(commands.account.subcommands.link.events.defaultAccountRemains(state.localDefaultAccount));
75
76
  return {
76
- status: 'success',
77
+ status: ACTION_RESULT_STATUS.SUCCESS,
77
78
  settings: {
78
79
  accounts: remainingAccounts,
79
80
  localDefaultAccount: state.localDefaultAccount,
@@ -90,7 +91,7 @@ export class ActionHandlers {
90
91
  uiLogger.success(commands.account.subcommands.link.events.updatedLinkedAccounts);
91
92
  }
92
93
  return {
93
- status: 'success',
94
+ status: ACTION_RESULT_STATUS.SUCCESS,
94
95
  settings: { accounts: remainingAccounts, localDefaultAccount },
95
96
  };
96
97
  }
@@ -101,7 +102,7 @@ export class ActionHandlers {
101
102
  });
102
103
  if (!updatedConfig) {
103
104
  return {
104
- status: 'error',
105
+ status: ACTION_RESULT_STATUS.ERROR,
105
106
  reason: commands.account.subcommands.link.errors.authFailed,
106
107
  };
107
108
  }
@@ -115,7 +116,7 @@ export class ActionHandlers {
115
116
  }
116
117
  static async cancel() {
117
118
  return {
118
- status: 'noop',
119
+ status: ACTION_RESULT_STATUS.NOOP,
119
120
  };
120
121
  }
121
122
  }
@@ -152,3 +153,33 @@ async function resolveDefaultAccount({ accounts, currentDefault, prompt = '', })
152
153
  }
153
154
  return promptForDefaultAccount(accounts, currentDefault, prompt);
154
155
  }
156
+ export async function handleLinkedUseAction({ state, targetAccountId, }) {
157
+ if (targetAccountId !== undefined) {
158
+ if (state.accounts.includes(targetAccountId)) {
159
+ uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(targetAccountId));
160
+ return {
161
+ status: ACTION_RESULT_STATUS.SUCCESS,
162
+ settings: {
163
+ accounts: state.accounts,
164
+ localDefaultAccount: targetAccountId,
165
+ },
166
+ };
167
+ }
168
+ const accounts = [...state.accounts, targetAccountId];
169
+ uiLogger.info(commands.account.subcommands.link.events.accountsLinked(1));
170
+ uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(targetAccountId));
171
+ return {
172
+ status: ACTION_RESULT_STATUS.SUCCESS,
173
+ settings: { accounts, localDefaultAccount: targetAccountId },
174
+ };
175
+ }
176
+ const localDefaultAccount = await resolveDefaultAccount({
177
+ accounts: state.accounts,
178
+ currentDefault: state.localDefaultAccount,
179
+ });
180
+ uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(localDefaultAccount));
181
+ return {
182
+ status: ACTION_RESULT_STATUS.SUCCESS,
183
+ settings: { accounts: state.accounts, localDefaultAccount },
184
+ };
185
+ }
@@ -1,4 +1,5 @@
1
1
  import { HsSettingsFile } from '@hubspot/local-dev-lib/types/HsSettings';
2
2
  export declare function isDirectoryLinked(settings: HsSettingsFile | null): settings is HsSettingsFile;
3
3
  export declare function hasDeprecatedConfigConflict(commandArgs: (string | number)[]): boolean;
4
+ export declare function addAccountToLinkedSettings(accountId: number): void;
4
5
  export declare function writeLinkedSettings(settings: HsSettingsFile, settingsPath: string): boolean;
@@ -1,6 +1,7 @@
1
1
  import { localConfigFileExists } from '@hubspot/local-dev-lib/config';
2
- import { writeHsSettingsFile } from '@hubspot/local-dev-lib/config/hsSettings';
2
+ import { getHsSettingsFileIfExists, writeHsSettingsFile, } from '@hubspot/local-dev-lib/config/hsSettings';
3
3
  import { uiLogger } from '../ui/logger.js';
4
+ import { debugError } from '../errorHandlers/index.js';
4
5
  import { commands } from '../../lang/en.js';
5
6
  export function isDirectoryLinked(settings) {
6
7
  return settings !== null && settings.accounts.length > 0;
@@ -12,6 +13,30 @@ export function hasDeprecatedConfigConflict(commandArgs) {
12
13
  }
13
14
  return false;
14
15
  }
16
+ export function addAccountToLinkedSettings(accountId) {
17
+ if (localConfigFileExists()) {
18
+ return;
19
+ }
20
+ const settings = getHsSettingsFileIfExists();
21
+ if (!settings || settings.accounts.length === 0) {
22
+ return;
23
+ }
24
+ if (settings.accounts.includes(accountId)) {
25
+ return;
26
+ }
27
+ const updated = {
28
+ ...settings,
29
+ accounts: [...settings.accounts, accountId],
30
+ };
31
+ try {
32
+ writeHsSettingsFile(updated);
33
+ uiLogger.info(commands.account.subcommands.link.shared.accountAutoLinked(accountId));
34
+ }
35
+ catch (err) {
36
+ uiLogger.warn(commands.account.subcommands.link.shared.accountAutoLinkFailed(accountId));
37
+ debugError(err);
38
+ }
39
+ }
15
40
  export function writeLinkedSettings(settings, settingsPath) {
16
41
  try {
17
42
  writeHsSettingsFile(settings);
@@ -0,0 +1 @@
1
+ export declare function warnIfLinkedDirectory(args: (string | number)[]): void;
@@ -0,0 +1,9 @@
1
+ import { getHsSettingsFilePath } from '@hubspot/local-dev-lib/config/hsSettings';
2
+ import { uiLogger } from '../ui/logger.js';
3
+ import { lib } from '../../lang/en.js';
4
+ export function warnIfLinkedDirectory(args) {
5
+ if (getHsSettingsFilePath() === null) {
6
+ return;
7
+ }
8
+ uiLogger.warn(lib.linkedDirectory.warning(`hs ${args.join(' ')}`, getHsSettingsFilePath()));
9
+ }
@@ -21,12 +21,13 @@ declare class DevServerManager_DEPRECATED {
21
21
  [key: string]: Component;
22
22
  }) => Promise<void>): Promise<void>;
23
23
  arrangeComponentsByType(components: Component[]): ComponentsByType;
24
- setup({ components, onUploadRequired, accountId, setActiveApp, exit, }: {
24
+ setup({ components, onUploadRequired, accountId, setActiveApp, exit, port, }: {
25
25
  components: Component[];
26
26
  onUploadRequired: () => void;
27
27
  accountId: number;
28
28
  setActiveApp: (appUid: string | undefined) => Promise<void>;
29
29
  exit: ExitFunction;
30
+ port?: number;
30
31
  }): Promise<void>;
31
32
  start({ accountId, projectConfig, }: {
32
33
  accountId: number;
@@ -57,7 +57,7 @@ class DevServerManager_DEPRECATED {
57
57
  return acc;
58
58
  }, {});
59
59
  }
60
- async setup({ components, onUploadRequired, accountId, setActiveApp, exit, }) {
60
+ async setup({ components, onUploadRequired, accountId, setActiveApp, exit, port, }) {
61
61
  this.componentsByType = this.arrangeComponentsByType(components);
62
62
  let env;
63
63
  const accountConfig = getConfigAccountById(accountId);
@@ -65,7 +65,7 @@ class DevServerManager_DEPRECATED {
65
65
  env = accountConfig.env;
66
66
  }
67
67
  try {
68
- await startPortManagerServer();
68
+ await startPortManagerServer(port);
69
69
  }
70
70
  catch (e) {
71
71
  logError(e);
@@ -16,6 +16,7 @@ type LocalDevManagerConstructorOptions = {
16
16
  runnableComponents: Component[];
17
17
  env: Environment;
18
18
  exit: ExitFunction;
19
+ port?: number;
19
20
  };
20
21
  declare class LocalDevManager_DEPRECATED {
21
22
  targetAccountId: number;
@@ -39,6 +40,7 @@ declare class LocalDevManager_DEPRECATED {
39
40
  mostRecentUploadWarning: string | null;
40
41
  private devSessionManager;
41
42
  private exit;
43
+ private port?;
42
44
  constructor(options: LocalDevManagerConstructorOptions);
43
45
  setActiveApp(appUid?: string): Promise<void>;
44
46
  setActivePublicAppData(): Promise<void>;
@@ -48,6 +48,7 @@ class LocalDevManager_DEPRECATED {
48
48
  mostRecentUploadWarning;
49
49
  devSessionManager;
50
50
  exit;
51
+ port;
51
52
  constructor(options) {
52
53
  this.targetAccountId = options.targetAccountId;
53
54
  // The account that the project exists in. This is not always the targetAccountId
@@ -67,6 +68,7 @@ class LocalDevManager_DEPRECATED {
67
68
  this.publicAppActiveInstalls = null;
68
69
  this.mostRecentUploadWarning = null;
69
70
  this.exit = options.exit;
71
+ this.port = options.port;
70
72
  this.projectSourceDir = path.join(this.projectDir, this.projectConfig.srcDir);
71
73
  if (!this.targetAccountId || !this.projectConfig || !this.projectDir) {
72
74
  uiLogger.error(lib.LocalDevManager.failedToInitialize);
@@ -337,6 +339,7 @@ class LocalDevManager_DEPRECATED {
337
339
  accountId: this.targetAccountId,
338
340
  setActiveApp: this.setActiveApp.bind(this),
339
341
  exit: this.exit,
342
+ port: this.port,
340
343
  });
341
344
  return true;
342
345
  }
@@ -1,9 +1,8 @@
1
1
  import { triggerAutoRelease, getAutoReleaseStatus, } from '@hubspot/local-dev-lib/api/projects';
2
- import { DEFAULT_POLLING_DELAY } from '../constants.js';
2
+ import { DEFAULT_POLLING_DELAY, PREVIEW_POLL_TIMEOUT } from '../constants.js';
3
3
  import SpinniesManager from '../ui/SpinniesManager.js';
4
4
  import { logError, ApiErrorContext } from '../errorHandlers/index.js';
5
5
  import { lib } from '../../lang/en.js';
6
- const PREVIEW_POLL_TIMEOUT = 5 * 60 * 1000;
7
6
  export async function triggerAndPollPreview(accountId, projectId, buildId, targetPortalId) {
8
7
  let triggerResponse;
9
8
  SpinniesManager.add('preview', {
@@ -46,26 +45,14 @@ export async function triggerAndPollPreview(accountId, projectId, buildId, targe
46
45
  });
47
46
  return { succeeded: true, releaseTag, appId };
48
47
  }
49
- function pollPreviewStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId) {
50
- return new Promise((resolve, reject) => {
51
- const startTime = Date.now();
52
- const pollInterval = setInterval(async () => {
53
- try {
54
- const { data } = await getAutoReleaseStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId);
55
- if (data.status === 'COMPLETE') {
56
- clearInterval(pollInterval);
57
- resolve();
58
- return;
59
- }
60
- if (Date.now() - startTime >= PREVIEW_POLL_TIMEOUT) {
61
- clearInterval(pollInterval);
62
- reject(new Error(lib.projectPreview.timeout));
63
- }
64
- }
65
- catch (e) {
66
- clearInterval(pollInterval);
67
- reject(e);
68
- }
69
- }, DEFAULT_POLLING_DELAY);
70
- });
48
+ async function pollPreviewStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId) {
49
+ const startTime = Date.now();
50
+ while (Date.now() - startTime < PREVIEW_POLL_TIMEOUT) {
51
+ await new Promise(resolve => setTimeout(resolve, DEFAULT_POLLING_DELAY));
52
+ const { data } = await getAutoReleaseStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId);
53
+ if (data.status === 'COMPLETE') {
54
+ return;
55
+ }
56
+ }
57
+ throw new Error(lib.projectPreview.timeout);
71
58
  }