@hubspot/cli 4.1.8-beta.0 → 4.1.8-beta.2

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.
package/lib/sandboxes.js CHANGED
@@ -1,13 +1,10 @@
1
- const cliProgress = require('cli-progress');
2
1
  const {
3
2
  getConfig,
4
3
  writeConfig,
5
4
  updateAccountConfig,
5
+ getAccountId,
6
6
  } = require('@hubspot/cli-lib');
7
- const {
8
- DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
9
- PERSONAL_ACCESS_KEY_AUTH_METHOD,
10
- } = require('@hubspot/cli-lib/lib/constants');
7
+ const chalk = require('chalk');
11
8
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
12
9
  const { logger } = require('@hubspot/cli-lib/logger');
13
10
  const {
@@ -15,36 +12,68 @@ const {
15
12
  } = require('@hubspot/cli-lib/personalAccessKey');
16
13
  const { EXIT_CODES } = require('./enums/exitCodes');
17
14
  const { enterAccountNamePrompt } = require('./prompts/enterAccountNamePrompt');
15
+ const {
16
+ fetchTaskStatus,
17
+ fetchTypes,
18
+ getSandboxUsageLimits,
19
+ } = require('@hubspot/cli-lib/sandboxes');
20
+ const { handleExit, handleKeypress } = require('@hubspot/cli-lib/lib/process');
21
+ const { accountNameExistsInConfig } = require('@hubspot/cli-lib/lib/config');
22
+ const CliProgressMultibarManager = require('./CliProgressMultibarManager');
23
+ const { promptUser } = require('./prompts/promptUtils');
24
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
18
25
  const {
19
26
  personalAccessKeyPrompt,
20
27
  } = require('./prompts/personalAccessKeyPrompt');
21
- const {
22
- setAsDefaultAccountPrompt,
23
- } = require('./prompts/setAsDefaultAccountPrompt');
24
- const { uiFeatureHighlight } = require('./ui');
25
- const { fetchTaskStatus, fetchTypes } = require('@hubspot/cli-lib/sandboxes');
26
- const { handleExit, handleKeypress } = require('@hubspot/cli-lib/lib/process');
27
28
 
28
- const getSandboxType = type =>
29
+ const STANDARD_SANDBOX = 'standard';
30
+ const DEVELOPER_SANDBOX = 'developer';
31
+
32
+ const syncTypes = {
33
+ OBJECT_RECORDS: 'object-records',
34
+ };
35
+
36
+ const sandboxTypeMap = {
37
+ DEV: DEVELOPER_SANDBOX,
38
+ dev: DEVELOPER_SANDBOX,
39
+ DEVELOPER: DEVELOPER_SANDBOX,
40
+ developer: DEVELOPER_SANDBOX,
41
+ DEVELOPMENT: DEVELOPER_SANDBOX,
42
+ development: DEVELOPER_SANDBOX,
43
+ STANDARD: STANDARD_SANDBOX,
44
+ standard: STANDARD_SANDBOX,
45
+ };
46
+
47
+ const sandboxApiTypeMap = {
48
+ standard: 1,
49
+ developer: 2,
50
+ };
51
+
52
+ const getSandboxTypeAsString = type =>
29
53
  type === 'DEVELOPER' ? 'development' : 'standard';
30
54
 
55
+ const isSandbox = config =>
56
+ config.sandboxAccountType && config.sandboxAccountType !== null;
57
+
31
58
  function getAccountName(config) {
32
- const isSandbox =
33
- config.sandboxAccountType && config.sandboxAccountType !== null;
34
- const sandboxName = `[${getSandboxType(config.sandboxAccountType)} sandbox] `;
35
- return `${config.name} ${isSandbox ? sandboxName : ''}(${config.portalId})`;
59
+ const sandboxName = `[${getSandboxTypeAsString(
60
+ config.sandboxAccountType
61
+ )} sandbox] `;
62
+ return chalk.bold(
63
+ `${config.name} ${isSandbox(config) ? sandboxName : ''}(${config.portalId})`
64
+ );
36
65
  }
37
66
 
38
- function getHasDevelopmentSandboxes(parentAccountConfig) {
67
+ function getHasSandboxesByType(parentAccountConfig, type) {
39
68
  const config = getConfig();
40
- const parentPortalId = parentAccountConfig.portalId;
69
+ const parentPortalId = getAccountId(parentAccountConfig.portalId);
41
70
  for (const portal of config.portals) {
42
71
  if (
43
72
  (portal.parentAccountId !== null ||
44
73
  portal.parentAccountId !== undefined) &&
45
74
  portal.parentAccountId === parentPortalId &&
46
75
  portal.sandboxAccountType &&
47
- portal.sandboxAccountType === 'DEVELOPER'
76
+ sandboxTypeMap[portal.sandboxAccountType] === type
48
77
  ) {
49
78
  return true;
50
79
  }
@@ -52,7 +81,7 @@ function getHasDevelopmentSandboxes(parentAccountConfig) {
52
81
  return false;
53
82
  }
54
83
 
55
- function getDevSandboxLimit(error) {
84
+ function getSandboxLimit(error) {
56
85
  // Error context should contain a limit property with a list of one number. That number is the current limit
57
86
  const limit = error.context && error.context.limit && error.context.limit[0];
58
87
  return limit ? parseInt(limit, 10) : 1; // Default to 1
@@ -60,31 +89,186 @@ function getDevSandboxLimit(error) {
60
89
 
61
90
  // Fetches available sync types for a given sandbox portal
62
91
  async function getAvailableSyncTypes(parentAccountConfig, config) {
63
- const parentPortalId = parentAccountConfig.portalId;
64
- const portalId = config.portalId;
92
+ const parentPortalId = getAccountId(parentAccountConfig.portalId);
93
+ const portalId = getAccountId(config.portalId);
65
94
  const syncTypes = await fetchTypes(parentPortalId, portalId);
95
+ if (!syncTypes) {
96
+ throw new Error(
97
+ 'Unable to fetch available sandbox sync types. Please try again.'
98
+ );
99
+ }
66
100
  return syncTypes.map(t => ({ type: t.name }));
67
101
  }
68
102
 
69
- const sandboxCreatePersonalAccessKeyFlow = async (env, account, name) => {
70
- const configData = await personalAccessKeyPrompt({ env, account });
71
- const updatedConfig = await updateConfigWithPersonalAccessKey(configData);
103
+ /**
104
+ * @param {Object} accountConfig - Account config of sandbox portal
105
+ * @param {Array} availableSyncTasks - Array of available sync tasks
106
+ * @param {Boolean} skipPrompt - Option to skip contact records prompt and return all available sync tasks
107
+ * @returns {Array} Adjusted available sync task items
108
+ */
109
+ const getSyncTypesWithContactRecordsPrompt = async (
110
+ accountConfig,
111
+ syncTasks,
112
+ skipPrompt = false
113
+ ) => {
114
+ // Fetches sync types based on default account. Parent account required for fetch
115
+
116
+ if (
117
+ syncTasks &&
118
+ syncTasks.some(t => t.type === syncTypes.OBJECT_RECORDS) &&
119
+ !skipPrompt
120
+ ) {
121
+ const { contactRecordsSyncPrompt } = await promptUser([
122
+ {
123
+ name: 'contactRecordsSyncPrompt',
124
+ type: 'confirm',
125
+ message: i18n(
126
+ `cli.lib.sandbox.sync.confirm.syncContactRecords.${
127
+ sandboxTypeMap[accountConfig.sandboxAccountType]
128
+ }`
129
+ ),
130
+ },
131
+ ]);
132
+ if (!contactRecordsSyncPrompt) {
133
+ return syncTasks.filter(t => t.type !== syncTypes.OBJECT_RECORDS);
134
+ }
135
+ }
136
+ return syncTasks;
137
+ };
138
+
139
+ /**
140
+ * @param {Object} accountConfig - Account config of sandbox portal
141
+ * @param {String} sandboxType - Sandbox type for limit validation
142
+ * @param {String} env - Environment
143
+ * @returns {null}
144
+ */
145
+ const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
146
+ const accountId = getAccountId(accountConfig.portalId);
147
+ const usage = await getSandboxUsageLimits(accountId);
148
+ if (!usage) {
149
+ throw new Error('Unable to fetch sandbox usage limits. Please try again.');
150
+ }
151
+ if (sandboxType === DEVELOPER_SANDBOX) {
152
+ if (usage['DEVELOPER'].available === 0) {
153
+ const devSandboxLimit = usage['DEVELOPER'].limit;
154
+ const plural = devSandboxLimit !== 1;
155
+ const hasDevelopmentSandboxes = getHasSandboxesByType(
156
+ accountConfig,
157
+ DEVELOPER_SANDBOX
158
+ );
159
+ if (hasDevelopmentSandboxes) {
160
+ throw new Error(
161
+ i18n(
162
+ `cli.lib.sandbox.create.failure.alreadyInConfig.developer.${
163
+ plural ? 'other' : 'one'
164
+ }`,
165
+ {
166
+ accountName: accountConfig.name || accountId,
167
+ limit: devSandboxLimit,
168
+ }
169
+ )
170
+ );
171
+ } else {
172
+ const baseUrl = getHubSpotWebsiteOrigin(env);
173
+ throw new Error(
174
+ i18n(
175
+ `cli.lib.sandbox.create.failure.limit.developer.${
176
+ plural ? 'other' : 'one'
177
+ }`,
178
+ {
179
+ accountName: accountConfig.name || accountId,
180
+ limit: devSandboxLimit,
181
+ link: `${baseUrl}/sandboxes-developer/${accountId}/development`,
182
+ }
183
+ )
184
+ );
185
+ }
186
+ }
187
+ }
188
+ if (sandboxType === STANDARD_SANDBOX) {
189
+ if (usage['STANDARD'].available === 0) {
190
+ const standardSandboxLimit = usage['STANDARD'].limit;
191
+ const plural = standardSandboxLimit !== 1;
192
+ const hasStandardSandboxes = getHasSandboxesByType(
193
+ accountConfig,
194
+ STANDARD_SANDBOX
195
+ );
196
+ if (hasStandardSandboxes) {
197
+ throw new Error(
198
+ i18n(
199
+ `cli.lib.sandbox.create.failure.alreadyInConfig.standard.${
200
+ plural ? 'other' : 'one'
201
+ }`,
202
+ {
203
+ accountName: accountConfig.name || accountId,
204
+ limit: standardSandboxLimit,
205
+ }
206
+ )
207
+ );
208
+ } else {
209
+ const baseUrl = getHubSpotWebsiteOrigin(env);
210
+ throw new Error(
211
+ i18n(
212
+ `cli.lib.sandbox.create.failure.limit.standard.${
213
+ plural ? 'other' : 'one'
214
+ }`,
215
+ {
216
+ accountName: accountConfig.name || accountId,
217
+ limit: standardSandboxLimit,
218
+ link: `${baseUrl}/sandboxes-developer/${accountId}/standard`,
219
+ }
220
+ )
221
+ );
222
+ }
223
+ }
224
+ }
225
+ };
72
226
 
227
+ /**
228
+ * @param {String} env - Environment (QA/Prod)
229
+ * @param {Object} result - Sandbox instance returned from API
230
+ * @param {Boolean} force - Force flag to skip prompt
231
+ * @returns {String} validName saved into config
232
+ */
233
+ const saveSandboxToConfig = async (env, result, force = false) => {
234
+ let configData = { env, personalAccessKey: result.personalAccessKey };
235
+ if (!result.personalAccessKey) {
236
+ configData = await personalAccessKeyPrompt({
237
+ env,
238
+ account: result.sandbox.sandboxHubId,
239
+ });
240
+ }
241
+ const updatedConfig = await updateConfigWithPersonalAccessKey(configData);
73
242
  if (!updatedConfig) {
74
- process.exit(EXIT_CODES.SUCCESS);
243
+ throw new Error('Failed to update config with personal access key.');
75
244
  }
76
245
 
77
246
  let validName = updatedConfig.name;
78
-
79
247
  if (!updatedConfig.name) {
80
- const nameForConfig = name
248
+ const nameForConfig = result.sandbox.name
81
249
  .toLowerCase()
82
250
  .split(' ')
83
251
  .join('-');
84
- const { name: promptName } = await enterAccountNamePrompt(nameForConfig);
85
- validName = promptName;
252
+ validName = nameForConfig;
253
+ const invalidAccountName = accountNameExistsInConfig(nameForConfig);
254
+ if (invalidAccountName) {
255
+ if (!force) {
256
+ logger.log(
257
+ i18n(
258
+ `cli.lib.prompts.enterAccountNamePrompt.errors.accountNameExists`,
259
+ { name: nameForConfig }
260
+ )
261
+ );
262
+ const { name: promptName } = await enterAccountNamePrompt(
263
+ nameForConfig
264
+ );
265
+ validName = promptName;
266
+ } else {
267
+ // Basic invalid name handling when force flag is passed
268
+ validName = nameForConfig + `_${Date.now()}`;
269
+ }
270
+ }
86
271
  }
87
-
88
272
  updateAccountConfig({
89
273
  ...updatedConfig,
90
274
  environment: updatedConfig.env,
@@ -93,35 +277,8 @@ const sandboxCreatePersonalAccessKeyFlow = async (env, account, name) => {
93
277
  });
94
278
  writeConfig();
95
279
 
96
- const setAsDefault = await setAsDefaultAccountPrompt(validName);
97
-
98
280
  logger.log('');
99
- if (setAsDefault) {
100
- logger.success(
101
- i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.setAsDefaultAccount`, {
102
- accountName: validName,
103
- })
104
- );
105
- } else {
106
- const config = getConfig();
107
- logger.info(
108
- i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
109
- accountName: config.defaultPortal,
110
- })
111
- );
112
- }
113
- logger.success(
114
- i18n('cli.commands.sandbox.subcommands.create.success.configFileUpdated', {
115
- configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
116
- authMethod: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
117
- account: validName,
118
- })
119
- );
120
- uiFeatureHighlight([
121
- 'accountsUseCommand',
122
- 'accountOption',
123
- 'accountsListCommand',
124
- ]);
281
+ return validName;
125
282
  };
126
283
 
127
284
  const ACTIVE_TASK_POLL_INTERVAL = 1000;
@@ -133,46 +290,56 @@ const isTaskComplete = task => {
133
290
  return task.status === 'COMPLETE';
134
291
  };
135
292
 
136
- // Returns a promise to poll a sync task with taskId. Interval runs until sync task status is equal to 'COMPLETE'
137
- function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
138
- const i18nKey = 'cli.commands.sandbox.subcommands.sync.types';
139
- const multibar = new cliProgress.MultiBar(
140
- {
141
- hideCursor: true,
142
- format: '[{bar}] {percentage}% | {taskType}',
143
- gracefulExit: true,
144
- },
145
- cliProgress.Presets.rect
146
- );
293
+ const incrementBy = (value, multiplier = 3) => {
294
+ return Math.min(value + Math.floor(Math.random() * multiplier), 99);
295
+ };
296
+
297
+ /**
298
+ * @param {Number} accountId - Parent portal ID (needs sandbox scopes)
299
+ * @param {String} taksId - Task ID to poll
300
+ * @param {String} syncStatusUrl - Link to UI to check polling status
301
+ * @param {Boolean} allowEarlyTermination - Option to allow a keypress to terminate early
302
+ * @returns {Promise} Interval runs until sync task status is equal to 'COMPLETE'
303
+ */
304
+ function pollSyncTaskStatus(
305
+ accountId,
306
+ taskId,
307
+ syncStatusUrl,
308
+ allowEarlyTermination = true
309
+ ) {
310
+ const i18nKey = 'cli.lib.sandbox.sync.types';
311
+ const progressBar = CliProgressMultibarManager.init();
147
312
  const mergeTasks = {
148
313
  'lead-flows': 'forms', // lead-flows are a subset of forms. We combine these in the UI as a single item, so we want to merge here for consistency.
149
314
  };
150
- const barInstances = {};
315
+ let progressCounter = {};
151
316
  let pollInterval;
152
317
  // Handle manual exit for return key and ctrl+c
153
318
  const onTerminate = () => {
154
319
  clearInterval(pollInterval);
155
- multibar.stop();
320
+ progressBar.stop();
156
321
  logger.log('');
157
322
  logger.log('Exiting, sync will continue in the background.');
158
323
  logger.log('');
159
324
  logger.log(
160
- i18n('cli.commands.sandbox.subcommands.sync.info.syncStatus', {
325
+ i18n('cli.lib.sandbox.sync.info.syncStatus', {
161
326
  url: syncStatusUrl,
162
327
  })
163
328
  );
164
329
  process.exit(EXIT_CODES.SUCCESS);
165
330
  };
166
- handleExit(onTerminate);
167
- handleKeypress(key => {
168
- if (
169
- (key && key.ctrl && key.name == 'c') ||
170
- key.name === 'enter' ||
171
- key.name === 'return'
172
- ) {
173
- onTerminate();
174
- }
175
- });
331
+ if (allowEarlyTermination) {
332
+ handleExit(onTerminate);
333
+ handleKeypress(key => {
334
+ if (
335
+ (key && key.ctrl && key.name == 'c') ||
336
+ key.name === 'enter' ||
337
+ key.name === 'return'
338
+ ) {
339
+ onTerminate();
340
+ }
341
+ });
342
+ }
176
343
  return new Promise((resolve, reject) => {
177
344
  pollInterval = setInterval(async () => {
178
345
  const taskResult = await fetchTaskStatus(accountId, taskId).catch(reject);
@@ -181,13 +348,17 @@ function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
181
348
  for (const task of taskResult.tasks) {
182
349
  // For each sync task, show a progress bar and increment bar each time we run this interval until status is 'COMPLETE'
183
350
  const taskType = task.type;
184
- if (!barInstances[taskType] && !mergeTasks[taskType]) {
185
- // skip creation of lead-flows bar because we're combining lead-flows into the forms bar
186
- barInstances[taskType] = multibar.create(100, 0, {
187
- taskType: i18n(`${i18nKey}.${taskType}.label`),
351
+ if (!progressBar.get(taskType) && !mergeTasks[taskType]) {
352
+ // skip creation of lead-flows bar because we're combining lead-flows into the forms bar, otherwise create a bar instance for the type
353
+ progressCounter[taskType] = 0;
354
+ progressBar.create(taskType, 100, 0, {
355
+ label: i18n(`${i18nKey}.${taskType}.label`),
188
356
  });
189
357
  } else if (mergeTasks[taskType]) {
190
- // If its a lead-flow, merge status into the forms progress bar
358
+ // It's a lead-flow here, merge status into the forms progress bar
359
+ if (!progressCounter[mergeTasks[taskType]]) {
360
+ progressCounter[mergeTasks[taskType]] = 0;
361
+ }
191
362
  const formsTask = taskResult.tasks.filter(
192
363
  t => t.type === mergeTasks[taskType]
193
364
  )[0];
@@ -197,46 +368,66 @@ function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
197
368
  formsTaskStatus !== 'COMPLETE' ||
198
369
  leadFlowsTaskStatus !== 'COMPLETE'
199
370
  ) {
200
- barInstances[mergeTasks[taskType]].increment(
201
- Math.floor(Math.random() * 3),
371
+ // Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
372
+ progressCounter[mergeTasks[taskType]] = incrementBy(
373
+ progressCounter[mergeTasks[taskType]]
374
+ );
375
+ progressBar.update(
376
+ mergeTasks[taskType],
377
+ progressCounter[mergeTasks[taskType]],
202
378
  {
203
- taskType: i18n(`${i18nKey}.${mergeTasks[taskType]}.label`),
379
+ label: i18n(`${i18nKey}.${mergeTasks[taskType]}.label`),
204
380
  }
205
381
  );
206
382
  }
207
383
  }
208
- if (barInstances[taskType] && task.status === 'COMPLETE') {
209
- barInstances[taskType].update(100, {
210
- taskType: i18n(`${i18nKey}.${taskType}.label`),
384
+ if (progressBar.get(taskType) && task.status === 'COMPLETE') {
385
+ progressBar.update(taskType, 100, {
386
+ label: i18n(`${i18nKey}.${taskType}.label`),
211
387
  });
212
- } else if (barInstances[taskType] && task.status === 'PROCESSING') {
213
- // Do not increment for tasks still in PENDING state
214
- barInstances[taskType].increment(Math.floor(Math.random() * 3), {
215
- // Randomly increment bar by 0 - 2 while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
216
- taskType: i18n(`${i18nKey}.${taskType}.label`),
388
+ } else if (
389
+ // Do not start incrementing for tasks still in PENDING state
390
+ progressBar.get(taskType) &&
391
+ task.status === 'PROCESSING'
392
+ ) {
393
+ // Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
394
+ progressCounter[taskType] = incrementBy(
395
+ progressCounter[taskType],
396
+ taskType === syncTypes.OBJECT_RECORDS ? 2 : 3 // slower progress for object-records, sync can take up to a few minutes
397
+ );
398
+ progressBar.update(taskType, progressCounter[taskType], {
399
+ label: i18n(`${i18nKey}.${taskType}.label`),
217
400
  });
218
401
  }
219
402
  }
220
403
  } else {
221
404
  clearInterval(pollInterval);
222
405
  reject();
223
- multibar.stop();
406
+ progressBar.stop();
224
407
  }
225
408
  if (isTaskComplete(taskResult)) {
226
409
  clearInterval(pollInterval);
227
410
  resolve(taskResult);
228
- multibar.stop();
411
+ progressBar.stop();
229
412
  }
230
413
  }, ACTIVE_TASK_POLL_INTERVAL);
231
414
  });
232
415
  }
233
416
 
234
417
  module.exports = {
235
- getSandboxType,
418
+ STANDARD_SANDBOX,
419
+ DEVELOPER_SANDBOX,
420
+ sandboxTypeMap,
421
+ sandboxApiTypeMap,
422
+ syncTypes,
423
+ isSandbox,
424
+ getSandboxTypeAsString,
236
425
  getAccountName,
237
- getHasDevelopmentSandboxes,
238
- getDevSandboxLimit,
426
+ saveSandboxToConfig,
427
+ getHasSandboxesByType,
428
+ getSandboxLimit,
429
+ validateSandboxUsageLimits,
239
430
  getAvailableSyncTypes,
240
- sandboxCreatePersonalAccessKeyFlow,
431
+ getSyncTypesWithContactRecordsPrompt,
241
432
  pollSyncTaskStatus,
242
433
  };
package/lib/ui.js CHANGED
@@ -1,3 +1,4 @@
1
+ const process = require('process');
1
2
  const chalk = require('chalk');
2
3
  const supportsHyperlinks = require('../lib/supportHyperlinks');
3
4
  const supportsColor = require('../lib/supportsColor');
@@ -35,7 +36,7 @@ const getTerminalUISupport = () => {
35
36
  * @param {object} options
36
37
  * @returns {string}
37
38
  */
38
- const uiLink = (linkText, url) => {
39
+ const uiLink = (linkText, url, { inSpinnies = false } = {}) => {
39
40
  const terminalUISupport = getTerminalUISupport();
40
41
  const encodedUrl = encodeURI(url);
41
42
  if (terminalUISupport.hyperlinks) {
@@ -46,12 +47,20 @@ const uiLink = (linkText, url) => {
46
47
  linkText,
47
48
  '\u001B]8;;\u0007',
48
49
  ].join('');
49
- return terminalUISupport.color ? chalk.cyan(result) : result;
50
- } else {
51
- return terminalUISupport.color
52
- ? `${linkText}: ${chalk.reset.cyan(encodedUrl)}`
53
- : `${linkText}: ${encodedUrl}`;
50
+
51
+ // Required b/c spinnies will break long lines
52
+ // See https://github.com/jbcarpanelli/spinnies/blob/d672dedcab8c8ce0f6de0bb26ca5582bf602afd7/utils.js#L68-L74
53
+ const columns = process.stderr.columns || 95;
54
+ const validLength = !inSpinnies || result.length < columns;
55
+
56
+ if (validLength) {
57
+ return terminalUISupport.color ? chalk.cyan(result) : result;
58
+ }
54
59
  }
60
+
61
+ return terminalUISupport.color
62
+ ? `${linkText}: ${chalk.reset.cyan(encodedUrl)}`
63
+ : `${linkText}: ${encodedUrl}`;
55
64
  };
56
65
 
57
66
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "4.1.8-beta.0",
3
+ "version": "4.1.8-beta.2",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,10 +8,11 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/cli-lib": "4.1.8-beta.0",
12
- "@hubspot/serverless-dev-runtime": "4.1.8-beta.0",
11
+ "@hubspot/cli-lib": "4.1.8-beta.2",
12
+ "@hubspot/serverless-dev-runtime": "4.1.8-beta.2",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
+ "chokidar": "^3.0.1",
15
16
  "cli-progress": "^3.11.2",
16
17
  "express": "^4.17.1",
17
18
  "findup-sync": "^4.0.0",
@@ -20,6 +21,7 @@
20
21
  "moment": "^2.29.1",
21
22
  "open": "^7.0.3",
22
23
  "ora": "^4.0.3",
24
+ "p-queue": "^6.0.2",
23
25
  "spinnies": "^0.5.1",
24
26
  "tmp": "^0.2.1",
25
27
  "update-notifier": "^5.1.0",
@@ -38,5 +40,5 @@
38
40
  "publishConfig": {
39
41
  "access": "public"
40
42
  },
41
- "gitHead": "af16182b2c7abd401771d5e0eed3de21e7b0477c"
43
+ "gitHead": "99dd6099b4d643bd335b1a1c3a28250ff7b0c39d"
42
44
  }