@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.
@@ -1,14 +1,12 @@
1
1
  const { updateDefaultAccount } = require('@hubspot/cli-lib/lib/config');
2
2
  const { promptUser } = require('./promptUtils');
3
3
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
4
- const { getSandboxType } = require('../sandboxes');
4
+ const { getAccountName } = require('../sandboxes');
5
5
 
6
6
  const mapAccountChoices = portals =>
7
7
  portals.map(p => {
8
- const isSandbox = p.sandboxAccountType && p.sandboxAccountType !== null;
9
- const sandboxName = `[${getSandboxType(p.sandboxAccountType)} sandbox] `;
10
8
  return {
11
- name: `${p.name} ${isSandbox ? sandboxName : ''}(${p.portalId})`,
9
+ name: getAccountName(p),
12
10
  value: p.name || p.portalId,
13
11
  };
14
12
  });
@@ -0,0 +1,35 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
3
+
4
+ const i18nKey = 'cli.lib.prompts.buildIdPrompt';
5
+
6
+ const buildIdPrompt = (latestBuildId, deployedBuildId, projectName) => {
7
+ return promptUser({
8
+ name: 'buildId',
9
+ message: i18n(`${i18nKey}.enterBuildId`),
10
+ default: () => {
11
+ if (latestBuildId === deployedBuildId) {
12
+ return;
13
+ }
14
+ return latestBuildId;
15
+ },
16
+ validate: val => {
17
+ if (Number(val) > latestBuildId) {
18
+ return i18n(`${i18nKey}.errors.buildIdDoesNotExist`, {
19
+ buildId: val,
20
+ projectName,
21
+ });
22
+ }
23
+ if (Number(val) === deployedBuildId) {
24
+ return i18n(`${i18nKey}.errors.buildAlreadyDeployed`, {
25
+ buildId: val,
26
+ });
27
+ }
28
+ return true;
29
+ },
30
+ });
31
+ };
32
+
33
+ module.exports = {
34
+ buildIdPrompt,
35
+ };
@@ -1,12 +1,49 @@
1
1
  const path = require('path');
2
2
  const { getCwd } = require('@hubspot/cli-lib/path');
3
- const { PROJECT_TEMPLATES } = require('@hubspot/cli-lib/lib/constants');
3
+ const {
4
+ PROJECT_COMPONENT_TYPES,
5
+ PROJECT_PROPERTIES,
6
+ } = require('@hubspot/cli-lib/lib/constants');
4
7
  const { promptUser } = require('./promptUtils');
8
+ const { fetchJsonFromRepository } = require('@hubspot/cli-lib/github');
5
9
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
10
+ const { logger } = require('@hubspot/cli-lib/logger');
11
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
6
12
 
7
13
  const i18nKey = 'cli.lib.prompts.createProjectPrompt';
8
14
 
9
- const createProjectPrompt = (promptOptions = {}) => {
15
+ const hasAllProperties = projectList => {
16
+ return projectList.every(config =>
17
+ PROJECT_PROPERTIES.every(p =>
18
+ Object.prototype.hasOwnProperty.call(config, p)
19
+ )
20
+ );
21
+ };
22
+
23
+ const createTemplateOptions = async repoPath => {
24
+ const isRepoPath = !!repoPath;
25
+ const config = await fetchJsonFromRepository(
26
+ repoPath,
27
+ 'main/config.json',
28
+ isRepoPath
29
+ );
30
+
31
+ if (!config || !config[PROJECT_COMPONENT_TYPES.PROJECTS]) {
32
+ logger.error(i18n(`${i18nKey}.errors.noProjectsInConfig`));
33
+ process.exit(EXIT_CODES.ERROR);
34
+ }
35
+
36
+ if (!hasAllProperties(config[PROJECT_COMPONENT_TYPES.PROJECTS])) {
37
+ logger.error(i18n(`${i18nKey}.errors.missingPropertiesInConfig`));
38
+ process.exit(EXIT_CODES.ERROR);
39
+ }
40
+
41
+ return config[PROJECT_COMPONENT_TYPES.PROJECTS];
42
+ };
43
+
44
+ const createProjectPrompt = async (promptOptions = {}) => {
45
+ const projectTemplates = await createTemplateOptions(promptOptions.repoPath);
46
+
10
47
  return promptUser([
11
48
  {
12
49
  name: 'name',
@@ -37,7 +74,7 @@ const createProjectPrompt = (promptOptions = {}) => {
37
74
  name: 'template',
38
75
  message: () => {
39
76
  return promptOptions.template &&
40
- !PROJECT_TEMPLATES.find(t => t.name === promptOptions.template)
77
+ !projectTemplates.find(t => t.name === promptOptions.template)
41
78
  ? i18n(`${i18nKey}.errors.invalidTemplate`, {
42
79
  template: promptOptions.template,
43
80
  })
@@ -45,12 +82,12 @@ const createProjectPrompt = (promptOptions = {}) => {
45
82
  },
46
83
  when:
47
84
  !promptOptions.template ||
48
- !PROJECT_TEMPLATES.find(t => t.name === promptOptions.template),
85
+ !projectTemplates.find(t => t.name === promptOptions.template),
49
86
  type: 'list',
50
- choices: PROJECT_TEMPLATES.map(template => {
87
+ choices: projectTemplates.map(template => {
51
88
  return {
52
89
  name: template.label,
53
- value: template.name,
90
+ value: template,
54
91
  };
55
92
  }),
56
93
  },
@@ -0,0 +1,44 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { getAccountId } = require('@hubspot/cli-lib/lib/config');
3
+ const { fetchProjects } = require('@hubspot/cli-lib/api/dfs');
4
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
5
+
6
+ const i18nKey = 'cli.lib.prompts.downloadProjectPrompt';
7
+
8
+ const createProjectsList = async () => {
9
+ const projects = await fetchProjects(getAccountId());
10
+ return projects.results;
11
+ };
12
+
13
+ const downloadProjectPrompt = async (promptOptions = {}) => {
14
+ const projectsList = await createProjectsList();
15
+
16
+ return promptUser([
17
+ {
18
+ name: 'project',
19
+ message: () => {
20
+ return promptOptions.project &&
21
+ !projectsList.find(p => p.name === promptOptions.name)
22
+ ? i18n(`${i18nKey}.errors.projectNotFound`, {
23
+ projectName: promptOptions.project,
24
+ accountId: getAccountId(),
25
+ })
26
+ : i18n(`${i18nKey}.selectProject`);
27
+ },
28
+ when:
29
+ !promptOptions.project ||
30
+ !projectsList.find(p => p.name === promptOptions.project),
31
+ type: 'list',
32
+ choices: projectsList.map(project => {
33
+ return {
34
+ name: project.name,
35
+ value: project.name,
36
+ };
37
+ }),
38
+ },
39
+ ]);
40
+ };
41
+
42
+ module.exports = {
43
+ downloadProjectPrompt,
44
+ };
@@ -0,0 +1,57 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { fetchJsonFromRepository } = require('@hubspot/cli-lib/github');
3
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
4
+ const { PROJECT_COMPONENT_TYPES } = require('@hubspot/cli-lib/lib/constants');
5
+
6
+ const i18nKey = 'cli.lib.prompts.projectAddPrompt';
7
+
8
+ const createTypeOptions = async () => {
9
+ const config = await fetchJsonFromRepository(
10
+ 'HubSpot/hubspot-project-components',
11
+ 'main/config.json'
12
+ );
13
+
14
+ return config[PROJECT_COMPONENT_TYPES.COMPONENTS];
15
+ };
16
+
17
+ const projectAddPrompt = async (promptOptions = {}) => {
18
+ const components = await createTypeOptions();
19
+ return promptUser([
20
+ {
21
+ name: 'type',
22
+ message: () => {
23
+ return promptOptions.type &&
24
+ !components.find(t => t.path === promptOptions.type.path)
25
+ ? i18n(`${i18nKey}.errors.invalidType`, {
26
+ type: promptOptions.type,
27
+ })
28
+ : i18n(`${i18nKey}.selectType`);
29
+ },
30
+ when:
31
+ !promptOptions.type ||
32
+ !components.find(t => t.path === promptOptions.type.path),
33
+ type: 'list',
34
+ choices: components.map(type => {
35
+ return {
36
+ name: type.label,
37
+ value: type,
38
+ };
39
+ }),
40
+ },
41
+ {
42
+ name: 'name',
43
+ message: i18n(`${i18nKey}.enterName`),
44
+ when: !promptOptions.name,
45
+ validate: input => {
46
+ if (!input) {
47
+ return i18n(`${i18nKey}.errors.nameRequired`);
48
+ }
49
+ return true;
50
+ },
51
+ },
52
+ ]);
53
+ };
54
+
55
+ module.exports = {
56
+ projectAddPrompt,
57
+ };
@@ -0,0 +1,105 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
3
+ const { uiAccountDescription } = require('../ui');
4
+ const { isSandbox, getAccountName } = require('../sandboxes');
5
+ const { getAccountId } = require('@hubspot/cli-lib');
6
+ const { getSandboxUsageLimits } = require('@hubspot/cli-lib/sandboxes');
7
+ const { logger } = require('@hubspot/cli-lib/logger');
8
+
9
+ const i18nKey = 'cli.lib.prompts.projectDevTargetAccountPrompt';
10
+
11
+ const mapSandboxAccount = accountConfig => ({
12
+ name: getAccountName(accountConfig),
13
+ value: {
14
+ targetAccountId: getAccountId(accountConfig.name),
15
+ chooseNonSandbox: false,
16
+ createNewSandbox: false,
17
+ },
18
+ });
19
+
20
+ const selectTargetAccountPrompt = async (
21
+ accounts,
22
+ defaultAccountConfig,
23
+ nonSandbox = false
24
+ ) => {
25
+ let choices;
26
+
27
+ if (nonSandbox) {
28
+ choices = accounts
29
+ .filter(accountConfig => !isSandbox(accountConfig))
30
+ .map(accountConfig => {
31
+ const accountId = getAccountId(accountConfig.name);
32
+ return {
33
+ name: uiAccountDescription(accountId),
34
+ value: {
35
+ targetAccountId: accountId,
36
+ chooseNonSandbox: false,
37
+ createNewSandbox: false,
38
+ },
39
+ };
40
+ });
41
+ } else {
42
+ let sandboxUsage = {};
43
+ try {
44
+ const accountId = getAccountId(defaultAccountConfig.portalId);
45
+ sandboxUsage = await getSandboxUsageLimits(accountId);
46
+ } catch (err) {
47
+ logger.debug('Unable to fetch sandbox usage limits: ', err);
48
+ }
49
+ const sandboxAccounts = accounts.reverse().filter(isSandbox);
50
+ let disabledMessage = false;
51
+ if (isSandbox(defaultAccountConfig)) {
52
+ disabledMessage = i18n(`${i18nKey}.defaultAccountNotProd`);
53
+ }
54
+ if (
55
+ sandboxUsage['DEVELOPER'] &&
56
+ sandboxUsage['DEVELOPER'].available === 0
57
+ ) {
58
+ disabledMessage = i18n(`${i18nKey}.sandboxLimit`, {
59
+ limit: sandboxUsage['DEVELOPER'].limit,
60
+ });
61
+ }
62
+ // Order choices by Create new -> Developer Sandbox -> Standard Sandbox -> Non sandbox
63
+ choices = [
64
+ {
65
+ name: i18n(`${i18nKey}.createNewSandboxOption`),
66
+ value: {
67
+ targetAccountId: null,
68
+ chooseNonSandbox: false,
69
+ createNewSandbox: true,
70
+ },
71
+ disabled: disabledMessage,
72
+ },
73
+ ...sandboxAccounts
74
+ .filter(a => a.sandboxAccountType === 'DEVELOPER')
75
+ .map(mapSandboxAccount),
76
+ ...sandboxAccounts
77
+ .filter(a => a.sandboxAccountType === 'STANDARD')
78
+ .map(mapSandboxAccount),
79
+ {
80
+ name: i18n(`${i18nKey}.chooseNonSandboxOption`),
81
+ value: {
82
+ targetAccountId: null,
83
+ chooseNonSandbox: true,
84
+ createNewSandbox: false,
85
+ },
86
+ },
87
+ ];
88
+ }
89
+ const { targetAccountInfo } = await promptUser([
90
+ {
91
+ name: 'targetAccountInfo',
92
+ type: 'list',
93
+ message: nonSandbox
94
+ ? i18n(`${i18nKey}.chooseNonSandboxAccount`)
95
+ : i18n(`${i18nKey}.chooseSandboxAccount`),
96
+ choices,
97
+ },
98
+ ]);
99
+
100
+ return targetAccountInfo;
101
+ };
102
+
103
+ module.exports = {
104
+ selectTargetAccountPrompt,
105
+ };
@@ -5,6 +5,19 @@ const promptUser = async promptConfig => {
5
5
  return prompt(promptConfig);
6
6
  };
7
7
 
8
+ const confirmPrompt = async (message, defaultAnswer = true) => {
9
+ const { choice } = await promptUser([
10
+ {
11
+ name: 'choice',
12
+ type: 'confirm',
13
+ default: defaultAnswer,
14
+ message,
15
+ },
16
+ ]);
17
+ return choice;
18
+ };
19
+
8
20
  module.exports = {
9
21
  promptUser,
22
+ confirmPrompt,
10
23
  };
@@ -1,6 +1,11 @@
1
1
  const { promptUser } = require('./promptUtils');
2
2
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
3
- const { getSandboxType } = require('../sandboxes');
3
+ const {
4
+ getSandboxTypeAsString,
5
+ STANDARD_SANDBOX,
6
+ DEVELOPER_SANDBOX,
7
+ } = require('../sandboxes');
8
+ const { accountNameExistsInConfig } = require('@hubspot/cli-lib/lib/config');
4
9
 
5
10
  const i18nKey = 'cli.lib.prompts.sandboxesPrompt';
6
11
 
@@ -8,7 +13,9 @@ const mapSandboxAccountChoices = portals =>
8
13
  portals
9
14
  .filter(p => p.sandboxAccountType && p.sandboxAccountType !== null)
10
15
  .map(p => {
11
- const sandboxName = `[${getSandboxType(p.sandboxAccountType)} sandbox] `;
16
+ const sandboxName = `[${getSandboxTypeAsString(
17
+ p.sandboxAccountType
18
+ )} sandbox] `;
12
19
  return {
13
20
  name: `${p.name} ${sandboxName}(${p.portalId})`,
14
21
  value: p.name || p.portalId,
@@ -27,18 +34,50 @@ const mapNonSandboxAccountChoices = portals =>
27
34
  };
28
35
  });
29
36
 
30
- const createSandboxPrompt = () => {
37
+ const sandboxNamePrompt = (type = STANDARD_SANDBOX) => {
38
+ const isDeveloperSandbox = type === DEVELOPER_SANDBOX;
39
+ const namePromptMessage = isDeveloperSandbox
40
+ ? `${i18nKey}.name.developmentSandboxMessage`
41
+ : `${i18nKey}.name.message`;
31
42
  return promptUser([
32
43
  {
33
44
  name: 'name',
34
- message: i18n(`${i18nKey}.enterName`),
45
+ message: i18n(namePromptMessage),
35
46
  validate(val) {
36
47
  if (typeof val !== 'string') {
37
- return i18n(`${i18nKey}.errors.invalidName`);
48
+ return i18n(`${i18nKey}.name.errors.invalidName`);
49
+ } else if (!val.length) {
50
+ return i18n(`${i18nKey}.name.errors.nameRequired`);
38
51
  }
39
- return true;
52
+ return accountNameExistsInConfig(val)
53
+ ? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
54
+ : true;
40
55
  },
41
- default: 'New sandbox',
56
+ default: `New ${isDeveloperSandbox ? 'development ' : ''}sandbox`,
57
+ },
58
+ ]);
59
+ };
60
+
61
+ const sandboxTypeChoices = [
62
+ {
63
+ name: i18n(`${i18nKey}.type.developer`),
64
+ value: 'DEVELOPER',
65
+ },
66
+ {
67
+ name: i18n(`${i18nKey}.type.standard`),
68
+ value: 'STANDARD',
69
+ },
70
+ ];
71
+
72
+ const sandboxTypePrompt = () => {
73
+ return promptUser([
74
+ {
75
+ name: 'type',
76
+ message: i18n(`${i18nKey}.type.message`),
77
+ type: 'list',
78
+ look: false,
79
+ choices: sandboxTypeChoices,
80
+ default: 'DEVELOPER',
42
81
  },
43
82
  ]);
44
83
  };
@@ -55,8 +94,8 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
55
94
  name: 'account',
56
95
  message: i18n(
57
96
  promptParentAccount
58
- ? `${i18nKey}.selectParentAccountName`
59
- : `${i18nKey}.selectAccountName`
97
+ ? `${i18nKey}.name.selectParentAccountName`
98
+ : `${i18nKey}.name.selectAccountName`
60
99
  ),
61
100
  type: 'list',
62
101
  look: false,
@@ -68,7 +107,7 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
68
107
  };
69
108
 
70
109
  module.exports = {
71
- createSandboxPrompt,
110
+ sandboxNamePrompt,
111
+ sandboxTypePrompt,
72
112
  deleteSandboxPrompt,
73
- getSandboxType,
74
113
  };
@@ -0,0 +1,199 @@
1
+ const Spinnies = require('spinnies');
2
+ const {
3
+ getSandboxLimit,
4
+ getHasSandboxesByType,
5
+ saveSandboxToConfig,
6
+ sandboxApiTypeMap,
7
+ STANDARD_SANDBOX,
8
+ DEVELOPER_SANDBOX,
9
+ } = require('./sandboxes');
10
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
11
+ const { logger } = require('@hubspot/cli-lib/logger');
12
+ const {
13
+ debugErrorAndContext,
14
+ logErrorInstance,
15
+ } = require('@hubspot/cli-lib/errorHandlers/standardErrors');
16
+ const {
17
+ isMissingScopeError,
18
+ isSpecifiedError,
19
+ } = require('@hubspot/cli-lib/errorHandlers/apiErrors');
20
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
21
+ const { getEnv, getAccountId } = require('@hubspot/cli-lib');
22
+ const { createSandbox } = require('@hubspot/cli-lib/sandboxes');
23
+ const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
24
+
25
+ const i18nKey = 'cli.lib.sandbox.create';
26
+
27
+ /**
28
+ * @param {String} name - Name of sandbox
29
+ * @param {String} type - Sandbox type to be created (standard/developer)
30
+ * @param {Object} accountConfig - Account config of parent portal
31
+ * @param {String} env - Environment (QA/Prod)
32
+ * @returns {Object} Object containing sandboxConfigName string and sandbox instance from API
33
+ */
34
+ const buildSandbox = async ({
35
+ name,
36
+ type,
37
+ accountConfig,
38
+ env,
39
+ force = false,
40
+ }) => {
41
+ const spinnies = new Spinnies({
42
+ succeedColor: 'white',
43
+ });
44
+ const accountId = getAccountId(accountConfig.portalId);
45
+
46
+ let result;
47
+ const spinniesI18nKey = `${i18nKey}.loading.${type}`;
48
+
49
+ try {
50
+ logger.log('');
51
+ spinnies.add('sandboxCreate', {
52
+ text: i18n(`${spinniesI18nKey}.add`, {
53
+ sandboxName: name,
54
+ }),
55
+ });
56
+
57
+ const sandboxApiType = sandboxApiTypeMap[type]; // API expects sandbox type as 1 or 2
58
+ result = await createSandbox(accountId, name, sandboxApiType);
59
+
60
+ spinnies.succeed('sandboxCreate', {
61
+ text: i18n(`${spinniesI18nKey}.succeed`, {
62
+ name: result.sandbox.name,
63
+ sandboxHubId: result.sandbox.sandboxHubId,
64
+ }),
65
+ });
66
+ } catch (err) {
67
+ debugErrorAndContext(err);
68
+
69
+ spinnies.fail('sandboxCreate', {
70
+ text: i18n(`${spinniesI18nKey}.fail`, {
71
+ sandboxName: name,
72
+ }),
73
+ });
74
+
75
+ if (isMissingScopeError(err)) {
76
+ logger.error(
77
+ i18n(`${i18nKey}.failure.scopes.message`, {
78
+ accountName: accountConfig.name || accountId,
79
+ })
80
+ );
81
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
82
+ const url = `${websiteOrigin}/personal-access-key/${accountId}`;
83
+ logger.info(
84
+ i18n(`${i18nKey}.failure.scopes.instructions`, {
85
+ accountName: accountConfig.name || accountId,
86
+ url,
87
+ })
88
+ );
89
+ } else if (
90
+ isSpecifiedError(err, {
91
+ statusCode: 400,
92
+ category: 'VALIDATION_ERROR',
93
+ subCategory:
94
+ 'SandboxErrors.NUM_DEVELOPMENT_SANDBOXES_LIMIT_EXCEEDED_ERROR',
95
+ }) &&
96
+ err.error &&
97
+ err.error.message
98
+ ) {
99
+ logger.log('');
100
+ const devSandboxLimit = getSandboxLimit(err.error);
101
+ const plural = devSandboxLimit !== 1;
102
+ const hasDevelopmentSandboxes = getHasSandboxesByType(
103
+ accountConfig,
104
+ DEVELOPER_SANDBOX
105
+ );
106
+ if (hasDevelopmentSandboxes) {
107
+ logger.error(
108
+ i18n(
109
+ `${i18nKey}.failure.alreadyInConfig.developer.${
110
+ plural ? 'other' : 'one'
111
+ }`,
112
+ {
113
+ accountName: accountConfig.name || accountId,
114
+ limit: devSandboxLimit,
115
+ }
116
+ )
117
+ );
118
+ } else {
119
+ const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
120
+ logger.error(
121
+ i18n(
122
+ `${i18nKey}.failure.limit.developer.${plural ? 'other' : 'one'}`,
123
+ {
124
+ accountName: accountConfig.name || accountId,
125
+ limit: devSandboxLimit,
126
+ link: `${baseUrl}/sandboxes-developer/${accountId}/development`,
127
+ }
128
+ )
129
+ );
130
+ }
131
+ logger.log('');
132
+ } else if (
133
+ isSpecifiedError(err, {
134
+ statusCode: 400,
135
+ category: 'VALIDATION_ERROR',
136
+ subCategory:
137
+ 'SandboxErrors.NUM_STANDARD_SANDBOXES_LIMIT_EXCEEDED_ERROR',
138
+ }) &&
139
+ err.error &&
140
+ err.error.message
141
+ ) {
142
+ logger.log('');
143
+ const standardSandboxLimit = getSandboxLimit(err.error);
144
+ const plural = standardSandboxLimit !== 1;
145
+ const hasStandardSandboxes = getHasSandboxesByType(
146
+ accountConfig,
147
+ STANDARD_SANDBOX
148
+ );
149
+ if (hasStandardSandboxes) {
150
+ logger.error(
151
+ i18n(
152
+ `${i18nKey}.failure.alreadyInConfig.standard.${
153
+ plural ? 'other' : 'one'
154
+ }`,
155
+ {
156
+ accountName: accountConfig.name || accountId,
157
+ limit: standardSandboxLimit,
158
+ }
159
+ )
160
+ );
161
+ } else {
162
+ const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
163
+ logger.error(
164
+ i18n(
165
+ `${i18nKey}.failure.limit.standard.${plural ? 'other' : 'one'}`,
166
+ {
167
+ accountName: accountConfig.name || accountId,
168
+ limit: standardSandboxLimit,
169
+ link: `${baseUrl}/sandboxes-developer/${accountId}/standard`,
170
+ }
171
+ )
172
+ );
173
+ }
174
+ logger.log('');
175
+ } else {
176
+ logErrorInstance(err);
177
+ }
178
+ throw err;
179
+ }
180
+
181
+ let sandboxConfigName;
182
+
183
+ try {
184
+ // Response contains PAK, save to config here
185
+ sandboxConfigName = await saveSandboxToConfig(env, result, force);
186
+ } catch (err) {
187
+ logErrorInstance(err);
188
+ throw err;
189
+ }
190
+
191
+ return {
192
+ sandboxConfigName,
193
+ result,
194
+ };
195
+ };
196
+
197
+ module.exports = {
198
+ buildSandbox,
199
+ };