@hubspot/cli 8.0.10-experimental.2 → 8.0.10-experimental.3
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/commands/account/auth.js +15 -5
- package/commands/account/use.js +14 -4
- package/commands/auth.js +10 -6
- package/commands/cms/theme/preview.js +9 -64
- package/commands/hubdb/clear.js +4 -0
- package/commands/hubdb/delete.js +4 -0
- package/commands/hubdb/fetch.js +4 -0
- package/commands/init.js +4 -0
- package/commands/project/dev/index.js +29 -19
- package/commands/project/download.js +5 -1
- package/commands/sandbox/__tests__/create.test.js +1 -48
- package/commands/sandbox/create.js +3 -30
- package/commands/testAccount/create.js +4 -0
- package/lang/en.d.ts +11 -0
- package/lang/en.js +11 -0
- package/lib/__tests__/buildAccount.test.js +1 -52
- package/lib/__tests__/sandboxes.test.js +1 -29
- package/lib/__tests__/serverlessLogs.test.js +10 -1
- package/lib/accountAuth.js +4 -0
- package/lib/buildAccount.d.ts +1 -6
- package/lib/buildAccount.js +9 -42
- package/lib/constants.d.ts +0 -2
- package/lib/constants.js +0 -2
- package/lib/errors/PromptExitError.d.ts +4 -0
- package/lib/errors/PromptExitError.js +8 -0
- package/lib/projects/__tests__/components.test.js +14 -0
- package/lib/projects/components.js +12 -2
- package/lib/projects/localDev/AppDevModeInterface.js +4 -0
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +4 -0
- package/lib/projects/localDev/helpers/account.js +5 -11
- package/lib/prompts/downloadProjectPrompt.js +11 -10
- package/lib/prompts/installAppPrompt.js +3 -2
- package/lib/prompts/personalAccessKeyPrompt.js +3 -2
- package/lib/prompts/projectDevTargetAccountPrompt.js +13 -16
- package/lib/prompts/selectHubDBTablePrompt.js +8 -4
- package/lib/prompts/selectPublicAppForMigrationPrompt.js +12 -6
- package/lib/sandboxes.d.ts +1 -9
- package/lib/sandboxes.js +0 -21
- package/lib/theme/cmsDevServerProcess.d.ts +12 -0
- package/lib/theme/cmsDevServerProcess.js +148 -0
- package/lib/theme/cmsDevServerRunner.d.ts +14 -0
- package/lib/theme/cmsDevServerRunner.js +90 -0
- package/lib/usageTracking.js +8 -5
- package/package.json +2 -3
- package/lib/__tests__/sandboxSync.test.d.ts +0 -1
- package/lib/__tests__/sandboxSync.test.js +0 -147
- package/lib/sandboxSync.d.ts +0 -4
- package/lib/sandboxSync.js +0 -102
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { uiLogger } from '../ui/logger.js';
|
|
2
2
|
import { getSandboxUsageLimits } from '@hubspot/local-dev-lib/api/sandboxHubs';
|
|
3
|
-
import { fetchTypes } from '@hubspot/local-dev-lib/api/sandboxSync';
|
|
4
3
|
import { getAllConfigAccounts, getConfigAccountIfExists, } from '@hubspot/local-dev-lib/config';
|
|
5
4
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
6
5
|
import { mockHubSpotHttpError } from '../testUtils.js';
|
|
7
|
-
import { getSandboxTypeAsString, getHasSandboxesByType,
|
|
6
|
+
import { getSandboxTypeAsString, getHasSandboxesByType, validateSandboxUsageLimits, handleSandboxCreateError, } from '../sandboxes.js';
|
|
8
7
|
import { isMissingScopeError, isSpecifiedError, } from '@hubspot/local-dev-lib/errors/index';
|
|
9
8
|
vi.mock('@hubspot/local-dev-lib/api/sandboxHubs');
|
|
10
9
|
vi.mock('@hubspot/local-dev-lib/api/sandboxSync');
|
|
@@ -12,7 +11,6 @@ vi.mock('@hubspot/local-dev-lib/config');
|
|
|
12
11
|
vi.mock('@hubspot/local-dev-lib/errors/index');
|
|
13
12
|
const mockedGetConfigAccountIfExists = getConfigAccountIfExists;
|
|
14
13
|
const mockedGetSandboxUsageLimits = getSandboxUsageLimits;
|
|
15
|
-
const mockedFetchTypes = fetchTypes;
|
|
16
14
|
const mockedGetAllConfigAccounts = getAllConfigAccounts;
|
|
17
15
|
const mockedUiLogger = uiLogger;
|
|
18
16
|
const mockedIsMissingScopeError = isMissingScopeError;
|
|
@@ -53,32 +51,6 @@ describe('lib/sandboxes', () => {
|
|
|
53
51
|
expect(getHasSandboxesByType(mockParentAccount, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX)).toBe(false);
|
|
54
52
|
});
|
|
55
53
|
});
|
|
56
|
-
describe('getAvailableSyncTypes()', () => {
|
|
57
|
-
const mockParentAccount = {
|
|
58
|
-
name: 'Parent Account',
|
|
59
|
-
accountId: 123,
|
|
60
|
-
env: 'qa',
|
|
61
|
-
};
|
|
62
|
-
const mockChildAccount = {
|
|
63
|
-
...mockParentAccount,
|
|
64
|
-
accountId: 456,
|
|
65
|
-
};
|
|
66
|
-
it('returns available sync types when fetch is successful', async () => {
|
|
67
|
-
const mockSyncTypes = [{ name: 'type1' }, { name: 'type2' }];
|
|
68
|
-
mockedGetConfigAccountIfExists
|
|
69
|
-
.mockReturnValue(mockParentAccount.accountId)
|
|
70
|
-
.mockReturnValue(mockChildAccount.accountId);
|
|
71
|
-
mockedFetchTypes.mockResolvedValue({
|
|
72
|
-
data: { results: mockSyncTypes },
|
|
73
|
-
});
|
|
74
|
-
const result = await getAvailableSyncTypes(mockParentAccount, mockChildAccount);
|
|
75
|
-
expect(result).toEqual([{ type: 'type1' }, { type: 'type2' }]);
|
|
76
|
-
});
|
|
77
|
-
it('throws error when sync types fetch fails', async () => {
|
|
78
|
-
mockedFetchTypes.mockResolvedValue({ data: { results: null } });
|
|
79
|
-
await expect(getAvailableSyncTypes(mockParentAccount, mockChildAccount)).rejects.toThrow(/Unable to fetch available sandbox sync types/);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
54
|
describe('validateSandboxUsageLimits()', () => {
|
|
83
55
|
const mockAccount = {
|
|
84
56
|
name: 'Test Account',
|
|
@@ -41,6 +41,7 @@ describe('lib/serverlessLogs', () => {
|
|
|
41
41
|
status: 200,
|
|
42
42
|
statusText: 'OK',
|
|
43
43
|
headers: {},
|
|
44
|
+
// eslint-disable-next-line
|
|
44
45
|
config: { headers: {} },
|
|
45
46
|
}));
|
|
46
47
|
const tailCall = vi.fn(() => Promise.resolve({
|
|
@@ -55,6 +56,7 @@ describe('lib/serverlessLogs', () => {
|
|
|
55
56
|
status: 200,
|
|
56
57
|
statusText: 'OK',
|
|
57
58
|
headers: {},
|
|
59
|
+
// eslint-disable-next-line
|
|
58
60
|
config: { headers: {} },
|
|
59
61
|
}));
|
|
60
62
|
// @ts-ignore - headers is not used in the actual function and does not need to be mocked
|
|
@@ -74,7 +76,12 @@ describe('lib/serverlessLogs', () => {
|
|
|
74
76
|
id: '1234',
|
|
75
77
|
executionTime: 510,
|
|
76
78
|
log: 'Log message',
|
|
77
|
-
error: {
|
|
79
|
+
error: {
|
|
80
|
+
message: '',
|
|
81
|
+
type: '',
|
|
82
|
+
stackTrace: [],
|
|
83
|
+
statusCode: null,
|
|
84
|
+
},
|
|
78
85
|
status: 'SUCCESS',
|
|
79
86
|
createdAt: 1620232011451,
|
|
80
87
|
memory: '70/128 MB',
|
|
@@ -83,6 +90,7 @@ describe('lib/serverlessLogs', () => {
|
|
|
83
90
|
status: 200,
|
|
84
91
|
statusText: 'OK',
|
|
85
92
|
headers: {},
|
|
93
|
+
// eslint-disable-next-line
|
|
86
94
|
config: { headers: {} },
|
|
87
95
|
}));
|
|
88
96
|
const latestLogResponse = {
|
|
@@ -119,6 +127,7 @@ describe('lib/serverlessLogs', () => {
|
|
|
119
127
|
status: 200,
|
|
120
128
|
statusText: 'OK',
|
|
121
129
|
headers: {},
|
|
130
|
+
// eslint-disable-next-line
|
|
122
131
|
config: { headers: {} },
|
|
123
132
|
}));
|
|
124
133
|
// @ts-ignore - headers is not used in the actual function and does not need to be mocked
|
package/lib/accountAuth.js
CHANGED
|
@@ -3,6 +3,7 @@ import { getAccessToken, updateConfigWithAccessToken, } from '@hubspot/local-dev
|
|
|
3
3
|
import { toKebabCase } from '@hubspot/local-dev-lib/text';
|
|
4
4
|
import { handleMerge, handleMigration } from './configMigrate.js';
|
|
5
5
|
import { debugError, logError } from './errorHandlers/index.js';
|
|
6
|
+
import { PromptExitError } from './errors/PromptExitError.js';
|
|
6
7
|
import { personalAccessKeyPrompt } from './prompts/personalAccessKeyPrompt.js';
|
|
7
8
|
import { cliAccountNamePrompt } from './prompts/accountNamePrompt.js';
|
|
8
9
|
import { setAsDefaultAccountPrompt } from './prompts/setAsDefaultAccountPrompt.js';
|
|
@@ -36,6 +37,9 @@ async function updateConfigWithNewAccount(env, configAlreadyExists, providedPers
|
|
|
36
37
|
return updatedConfig;
|
|
37
38
|
}
|
|
38
39
|
catch (e) {
|
|
40
|
+
if (e instanceof PromptExitError) {
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
39
43
|
debugError(e);
|
|
40
44
|
return null;
|
|
41
45
|
}
|
package/lib/buildAccount.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DeveloperTestAccountConfig } from '@hubspot/local-dev-lib/types/developerTestAccounts';
|
|
2
2
|
import { Environment } from '@hubspot/local-dev-lib/types/Accounts';
|
|
3
3
|
import { HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
|
|
4
|
-
import {
|
|
4
|
+
import { V2Sandbox } from '@hubspot/local-dev-lib/types/Sandbox';
|
|
5
5
|
import { SandboxAccountType } from '../types/Sandboxes.js';
|
|
6
6
|
export declare function saveAccountToConfig(accountId: number | undefined, accountName: string, env: Environment, personalAccessKey?: string, force?: boolean): Promise<string>;
|
|
7
7
|
export declare function createDeveloperTestAccountV2(parentAccountId: number, testAccountConfig: DeveloperTestAccountConfig): Promise<{
|
|
@@ -10,11 +10,6 @@ export declare function createDeveloperTestAccountV2(parentAccountId: number, te
|
|
|
10
10
|
personalAccessKey?: string;
|
|
11
11
|
}>;
|
|
12
12
|
export declare function buildDeveloperTestAccount(testAccountName: string, parentAccountConfig: HubSpotConfigAccount, env: Environment, portalLimit: number, useV2?: boolean): Promise<number>;
|
|
13
|
-
type SandboxAccount = SandboxResponse & {
|
|
14
|
-
name: string;
|
|
15
|
-
};
|
|
16
|
-
export declare function buildSandbox(sandboxName: string, parentAccountConfig: HubSpotConfigAccount, sandboxType: SandboxAccountType, env: Environment, force?: boolean): Promise<SandboxAccount>;
|
|
17
13
|
export declare function buildV2Sandbox(sandboxName: string, parentAccountConfig: HubSpotConfigAccount, sandboxType: SandboxAccountType, syncObjectRecords: boolean, env: Environment, force?: boolean): Promise<{
|
|
18
14
|
sandbox: V2Sandbox;
|
|
19
15
|
}>;
|
|
20
|
-
export {};
|
package/lib/buildAccount.js
CHANGED
|
@@ -3,13 +3,14 @@ import { getConfigAccountIfExists, updateConfigAccount, } from '@hubspot/local-d
|
|
|
3
3
|
import { uiLogger } from './ui/logger.js';
|
|
4
4
|
import { createDeveloperTestAccount, fetchDeveloperTestAccountGateSyncStatus, generateDeveloperTestAccountPersonalAccessKey, } from '@hubspot/local-dev-lib/api/developerTestAccounts';
|
|
5
5
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
6
|
-
import {
|
|
6
|
+
import { createV2Sandbox, getSandboxPersonalAccessKey, } from '@hubspot/local-dev-lib/api/sandboxHubs';
|
|
7
|
+
import { PromptExitError } from './errors/PromptExitError.js';
|
|
7
8
|
import { personalAccessKeyPrompt } from './prompts/personalAccessKeyPrompt.js';
|
|
8
9
|
import { createDeveloperTestAccountConfigPrompt } from './prompts/createDeveloperTestAccountConfigPrompt.js';
|
|
9
10
|
import { cliAccountNamePrompt } from './prompts/accountNamePrompt.js';
|
|
10
11
|
import SpinniesManager from './ui/SpinniesManager.js';
|
|
11
12
|
import { debugError, logError } from './errorHandlers/index.js';
|
|
12
|
-
import {
|
|
13
|
+
import { SANDBOX_TYPE_MAP_V2, handleSandboxCreateError } from './sandboxes.js';
|
|
13
14
|
import { handleDeveloperTestAccountCreateError } from './developerTestAccounts.js';
|
|
14
15
|
import { lib } from '../lang/en.js';
|
|
15
16
|
import { poll } from './polling.js';
|
|
@@ -127,51 +128,14 @@ export async function buildDeveloperTestAccount(testAccountName, parentAccountCo
|
|
|
127
128
|
await saveAccountToConfig(developerTestAccountId, testAccountName, env, developerTestAccountPersonalAccessKey);
|
|
128
129
|
}
|
|
129
130
|
catch (err) {
|
|
131
|
+
if (err instanceof PromptExitError) {
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
130
134
|
logError(err);
|
|
131
135
|
throw err;
|
|
132
136
|
}
|
|
133
137
|
return developerTestAccountId;
|
|
134
138
|
}
|
|
135
|
-
export async function buildSandbox(sandboxName, parentAccountConfig, sandboxType, env, force = false) {
|
|
136
|
-
const sandboxTypeKey = sandboxType === HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX
|
|
137
|
-
? 'standard'
|
|
138
|
-
: 'developer';
|
|
139
|
-
const parentAccountId = parentAccountConfig.accountId;
|
|
140
|
-
if (!parentAccountId) {
|
|
141
|
-
throw new Error(lib.sandbox.create[sandboxTypeKey].loading.fail(''));
|
|
142
|
-
}
|
|
143
|
-
SpinniesManager.init({
|
|
144
|
-
succeedColor: 'white',
|
|
145
|
-
});
|
|
146
|
-
uiLogger.log('');
|
|
147
|
-
SpinniesManager.add('buildSandbox', {
|
|
148
|
-
text: lib.sandbox.create[sandboxTypeKey].loading.add(sandboxName),
|
|
149
|
-
});
|
|
150
|
-
let sandbox;
|
|
151
|
-
try {
|
|
152
|
-
const sandboxApiType = SANDBOX_API_TYPE_MAP[sandboxType];
|
|
153
|
-
const { data } = await createSandbox(parentAccountId, sandboxName, sandboxApiType);
|
|
154
|
-
sandbox = { name: sandboxName, ...data };
|
|
155
|
-
SpinniesManager.succeed('buildSandbox', {
|
|
156
|
-
text: lib.sandbox.create[sandboxTypeKey].loading.succeed(sandboxName, sandbox.sandbox.sandboxHubId.toString()),
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
debugError(e);
|
|
161
|
-
SpinniesManager.fail('buildSandbox', {
|
|
162
|
-
text: lib.sandbox.create[sandboxTypeKey].loading.fail(sandboxName),
|
|
163
|
-
});
|
|
164
|
-
handleSandboxCreateError(e, env, sandboxName, parentAccountId);
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
await saveAccountToConfig(sandbox.sandbox.sandboxHubId, sandboxName, env, sandbox.personalAccessKey, force);
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
logError(err);
|
|
171
|
-
throw err;
|
|
172
|
-
}
|
|
173
|
-
return sandbox;
|
|
174
|
-
}
|
|
175
139
|
export async function buildV2Sandbox(sandboxName, parentAccountConfig, sandboxType, syncObjectRecords, env, force = false) {
|
|
176
140
|
const sandboxTypeKey = sandboxType === HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX
|
|
177
141
|
? 'standard'
|
|
@@ -210,6 +174,9 @@ export async function buildV2Sandbox(sandboxName, parentAccountConfig, sandboxTy
|
|
|
210
174
|
await saveAccountToConfig(sandbox.sandboxHubId, sandboxName, env, pak, force);
|
|
211
175
|
}
|
|
212
176
|
catch (err) {
|
|
177
|
+
if (err instanceof PromptExitError) {
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
213
180
|
logError(err);
|
|
214
181
|
throw err;
|
|
215
182
|
}
|
package/lib/constants.d.ts
CHANGED
|
@@ -77,8 +77,6 @@ export declare const APP_AUTH_TYPES: {
|
|
|
77
77
|
};
|
|
78
78
|
export declare const FEATURES: {
|
|
79
79
|
readonly UNIFIED_APPS: "Developers:UnifiedApps:PrivateBeta";
|
|
80
|
-
readonly SANDBOXES_V2: "sandboxes:v2:enabled";
|
|
81
|
-
readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
|
|
82
80
|
readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
|
|
83
81
|
readonly APPS_HOME: "UIE:AppHome";
|
|
84
82
|
readonly THEME_MIGRATION_2025_2: "Developers:ProjectThemeMigrations:2025.2";
|
package/lib/constants.js
CHANGED
|
@@ -69,8 +69,6 @@ export const APP_AUTH_TYPES = {
|
|
|
69
69
|
};
|
|
70
70
|
export const FEATURES = {
|
|
71
71
|
UNIFIED_APPS: 'Developers:UnifiedApps:PrivateBeta',
|
|
72
|
-
SANDBOXES_V2: 'sandboxes:v2:enabled',
|
|
73
|
-
SANDBOXES_V2_CLI: 'sandboxes:v2:cliEnabled',
|
|
74
72
|
APP_EVENTS: 'Developers:UnifiedApps:AppEventsAccess',
|
|
75
73
|
APPS_HOME: 'UIE:AppHome',
|
|
76
74
|
THEME_MIGRATION_2025_2: 'Developers:ProjectThemeMigrations:2025.2',
|
|
@@ -174,6 +174,20 @@ describe('lib/projects/components', () => {
|
|
|
174
174
|
expect(mockedFs.readFileSync).toHaveBeenCalledWith('/src/path/package.json', 'utf-8');
|
|
175
175
|
consoleSpy.mockRestore();
|
|
176
176
|
});
|
|
177
|
+
it('skips tooling config files on collision instead of renaming them', () => {
|
|
178
|
+
const collision = {
|
|
179
|
+
...mockCollision,
|
|
180
|
+
collisions: [
|
|
181
|
+
'src/app/cards/.prettierrc.json',
|
|
182
|
+
'src/app/cards/component.js',
|
|
183
|
+
'src/app/cards/eslint.config.js',
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
mockedFs.copyFileSync.mockImplementation(() => { });
|
|
187
|
+
handleComponentCollision(collision);
|
|
188
|
+
expect(mockedFs.copyFileSync).toHaveBeenCalledTimes(1);
|
|
189
|
+
expect(mockedFs.copyFileSync).toHaveBeenCalledWith('/src/path/src/app/cards/component.js', '/dest/path/src/app/cards/component-2.js');
|
|
190
|
+
});
|
|
177
191
|
it('falls back to timestamp when maxAttempts is exhausted', () => {
|
|
178
192
|
const collision = {
|
|
179
193
|
...mockCollision,
|
|
@@ -114,9 +114,19 @@ export function handleComponentCollision({ dest, src, collisions }) {
|
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
const filenameDifferentiator = generateSafeFilenameDifferentiator(sourceFiles, hsMetaFiles);
|
|
117
|
-
// Exclude markdown files fromthe rename process because they should not be duplicated
|
|
118
117
|
const sourceFilenameMapping = sourceFiles
|
|
119
|
-
.filter(filename =>
|
|
118
|
+
.filter(filename => {
|
|
119
|
+
// Exclude markdown files from the rename process because they should not be duplicated
|
|
120
|
+
if (filename.endsWith('.md')) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
// Also exclude the tooling config we add to new projects
|
|
124
|
+
const base = path.parse(filename).base;
|
|
125
|
+
if (base === '.prettierrc.json' || base === 'eslint.config.js') {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
})
|
|
120
130
|
.reduce((acc, filename) => {
|
|
121
131
|
return {
|
|
122
132
|
...acc,
|
|
@@ -6,6 +6,7 @@ import { EXIT_CODES } from '../../enums/exitCodes.js';
|
|
|
6
6
|
import { isAppIRNode } from '../../projects/structure.js';
|
|
7
7
|
import { uiLine } from '../../ui/index.js';
|
|
8
8
|
import { logError } from '../../errorHandlers/index.js';
|
|
9
|
+
import { PromptExitError } from '../../errors/PromptExitError.js';
|
|
9
10
|
import { installAppAutoPrompt, installAppBrowserPrompt, } from '../../prompts/installAppPrompt.js';
|
|
10
11
|
import { confirmPrompt } from '../../prompts/promptUtils.js';
|
|
11
12
|
import { lib } from '../../../lang/en.js';
|
|
@@ -313,6 +314,9 @@ class AppDevModeInterface {
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
catch (e) {
|
|
317
|
+
if (e instanceof PromptExitError) {
|
|
318
|
+
throw e;
|
|
319
|
+
}
|
|
316
320
|
if (SpinniesManager.pick('fetchAppData')) {
|
|
317
321
|
SpinniesManager.fail('fetchAppData', {
|
|
318
322
|
text: lib.AppDevModeInterface.fetchAppData.error,
|
|
@@ -15,6 +15,7 @@ import { componentIsApp, componentIsPublicApp, CONFIG_FILES, getAppCardConfigs,
|
|
|
15
15
|
import { ComponentTypes, } from '../../../types/Projects.js';
|
|
16
16
|
import { UI_COLORS, uiCommandReference, uiAccountDescription, uiLink, uiLine, } from '../../ui/index.js';
|
|
17
17
|
import { logError } from '../../errorHandlers/index.js';
|
|
18
|
+
import { PromptExitError } from '../../errors/PromptExitError.js';
|
|
18
19
|
import { installAppBrowserPrompt } from '../../prompts/installAppPrompt.js';
|
|
19
20
|
import { confirmPrompt } from '../../prompts/promptUtils.js';
|
|
20
21
|
import { handleKeypress } from '../../process.js';
|
|
@@ -89,6 +90,9 @@ class LocalDevManager_DEPRECATED {
|
|
|
89
90
|
await this.checkPublicAppInstallation();
|
|
90
91
|
}
|
|
91
92
|
catch (e) {
|
|
93
|
+
if (e instanceof PromptExitError) {
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
92
96
|
logError(e);
|
|
93
97
|
}
|
|
94
98
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HUBSPOT_ACCOUNT_TYPE_STRINGS } from '@hubspot/local-dev-lib/constants/config';
|
|
2
|
-
import {
|
|
2
|
+
import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
|
|
3
3
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
4
4
|
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
|
|
5
5
|
import { isMissingScopeError } from '@hubspot/local-dev-lib/errors/index';
|
|
@@ -18,12 +18,10 @@ import { selectDeveloperTestTargetAccountPrompt } from '../../../prompts/project
|
|
|
18
18
|
import { selectSandboxTargetAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
19
19
|
import { validateSandboxUsageLimits } from '../../../sandboxes.js';
|
|
20
20
|
import { logError } from '../../../errorHandlers/index.js';
|
|
21
|
-
import { syncSandbox } from '../../../sandboxSync.js';
|
|
22
|
-
import { getAvailableSyncTypes } from '../../../sandboxes.js';
|
|
23
21
|
import { hubspotAccountNamePrompt } from '../../../prompts/accountNamePrompt.js';
|
|
24
22
|
import { trackCommandMetadataUsage } from '../../../usageTracking.js';
|
|
25
23
|
import { validateDevTestAccountUsageLimits } from '../../../developerTestAccounts.js';
|
|
26
|
-
import {
|
|
24
|
+
import { buildDeveloperTestAccount, saveAccountToConfig, buildV2Sandbox, } from '../../../buildAccount.js';
|
|
27
25
|
import { debugError } from '../../../errorHandlers/index.js';
|
|
28
26
|
import { listPrompt } from '../../../prompts/promptUtils.js';
|
|
29
27
|
import { confirmUseExistingDeveloperTestAccountPrompt } from '../../../prompts/projectDevTargetAccountPrompt.js';
|
|
@@ -119,13 +117,9 @@ export async function createSandboxForLocalDev(accountId, accountConfig, env) {
|
|
|
119
117
|
accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
120
118
|
});
|
|
121
119
|
trackCommandMetadataUsage('sandbox-create', { step: 'project-dev' }, accountId);
|
|
122
|
-
const result = await
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const syncTasks = await getAvailableSyncTypes(accountConfig, sandboxAccountConfig);
|
|
126
|
-
// For v1 sandboxes, keep sync here. Once we migrate to v2, this will be handled by BE automatically
|
|
127
|
-
await syncSandbox(sandboxAccountConfig, accountConfig, env, syncTasks, true);
|
|
128
|
-
return targetAccountId;
|
|
120
|
+
const result = await buildV2Sandbox(name, accountConfig, HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX, false, // syncObjectRecords
|
|
121
|
+
env);
|
|
122
|
+
return result.sandbox.sandboxHubId;
|
|
129
123
|
}
|
|
130
124
|
catch (err) {
|
|
131
125
|
logError(err);
|
|
@@ -2,21 +2,22 @@ import { promptUser } from './promptUtils.js';
|
|
|
2
2
|
import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
|
|
3
3
|
import { fetchProjects } from '@hubspot/local-dev-lib/api/projects';
|
|
4
4
|
import { logError, ApiErrorContext } from '../errorHandlers/index.js';
|
|
5
|
-
import { uiLogger } from '../ui/logger.js';
|
|
6
|
-
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
7
5
|
import { lib } from '../../lang/en.js';
|
|
6
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
7
|
+
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
8
|
+
import { uiLogger } from '../ui/logger.js';
|
|
8
9
|
async function createProjectsList(accountId) {
|
|
9
|
-
|
|
10
|
-
if (accountId) {
|
|
11
|
-
const { data: projects } = await fetchProjects(accountId);
|
|
12
|
-
return projects.results;
|
|
13
|
-
}
|
|
10
|
+
if (!accountId) {
|
|
14
11
|
uiLogger.error(lib.prompts.downloadProjectPrompt.errors.accountIdRequired);
|
|
15
|
-
|
|
12
|
+
throw new PromptExitError(lib.prompts.downloadProjectPrompt.errors.accountIdRequired, EXIT_CODES.ERROR);
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const { data: projects } = await fetchProjects(accountId);
|
|
16
|
+
return projects.results;
|
|
16
17
|
}
|
|
17
18
|
catch (e) {
|
|
18
|
-
logError(e,
|
|
19
|
-
|
|
19
|
+
logError(e, new ApiErrorContext({ accountId }));
|
|
20
|
+
throw new PromptExitError('Failed to fetch projects', EXIT_CODES.ERROR);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
export async function downloadProjectPrompt(promptOptions) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import open from 'open';
|
|
2
2
|
import { promptUser } from './promptUtils.js';
|
|
3
|
-
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
4
3
|
import { lib } from '../../lang/en.js';
|
|
5
4
|
import { uiLogger } from '../ui/logger.js';
|
|
5
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
6
|
+
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
6
7
|
export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
|
|
7
8
|
uiLogger.log('');
|
|
8
9
|
if (isReinstall) {
|
|
@@ -20,7 +21,7 @@ export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
|
|
|
20
21
|
});
|
|
21
22
|
if (!isReinstall && !shouldOpenBrowser) {
|
|
22
23
|
uiLogger.log(lib.prompts.installAppPrompt.decline);
|
|
23
|
-
|
|
24
|
+
throw new PromptExitError(lib.prompts.installAppPrompt.decline, EXIT_CODES.SUCCESS);
|
|
24
25
|
}
|
|
25
26
|
else if (!shouldOpenBrowser) {
|
|
26
27
|
return;
|
|
@@ -6,8 +6,9 @@ import { uiLogger } from '../ui/logger.js';
|
|
|
6
6
|
import { promptUser } from './promptUtils.js';
|
|
7
7
|
import { getCliAccountNamePromptConfig, } from './accountNamePrompt.js';
|
|
8
8
|
import { uiInfoSection } from '../ui/index.js';
|
|
9
|
-
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
10
9
|
import { lib } from '../../lang/en.js';
|
|
10
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
11
|
+
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
11
12
|
/**
|
|
12
13
|
* Displays notification to user that we are about to open the browser,
|
|
13
14
|
* then opens their browser to the personal-access-key shortlink
|
|
@@ -27,7 +28,7 @@ export async function personalAccessKeyPrompt({ env, account, }) {
|
|
|
27
28
|
]);
|
|
28
29
|
if (!choice) {
|
|
29
30
|
deleteConfigFileIfEmpty();
|
|
30
|
-
|
|
31
|
+
throw new PromptExitError(lib.prompts.personalAccessKeyPrompt.errors.authCancelled, EXIT_CODES.SUCCESS);
|
|
31
32
|
}
|
|
32
33
|
if (choice ===
|
|
33
34
|
lib.prompts.personalAccessKeyPrompt.personalAccessKeyPromptChoices
|
|
@@ -6,6 +6,7 @@ import { lib } from '../../lang/en.js';
|
|
|
6
6
|
import { uiLogger } from '../ui/logger.js';
|
|
7
7
|
import { uiAccountDescription } from '../ui/index.js';
|
|
8
8
|
import { isSandbox } from '../accountTypes.js';
|
|
9
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
9
10
|
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
10
11
|
function mapNestedAccount(accountConfig) {
|
|
11
12
|
const parentAccountId = accountConfig.parentAccountId ?? null;
|
|
@@ -23,20 +24,18 @@ function getNonConfigDeveloperTestAccountName(account) {
|
|
|
23
24
|
}
|
|
24
25
|
export async function selectSandboxTargetAccountPrompt(accounts, defaultAccountConfig) {
|
|
25
26
|
const defaultAccountId = defaultAccountConfig.accountId;
|
|
27
|
+
if (!defaultAccountId) {
|
|
28
|
+
uiLogger.error(lib.prompts.projectDevTargetAccountPrompt.noAccountId);
|
|
29
|
+
throw new PromptExitError(lib.prompts.projectDevTargetAccountPrompt.noAccountId, EXIT_CODES.ERROR);
|
|
30
|
+
}
|
|
26
31
|
let choices = [];
|
|
27
32
|
let sandboxUsage = {
|
|
28
33
|
STANDARD: { used: 0, available: 0, limit: 0 },
|
|
29
34
|
DEVELOPER: { used: 0, available: 0, limit: 0 },
|
|
30
35
|
};
|
|
31
36
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
sandboxUsage = data.usage;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
uiLogger.error(lib.prompts.projectDevTargetAccountPrompt.noAccountId);
|
|
38
|
-
process.exit(EXIT_CODES.ERROR);
|
|
39
|
-
}
|
|
37
|
+
const { data } = await getSandboxUsageLimits(defaultAccountId);
|
|
38
|
+
sandboxUsage = data.usage;
|
|
40
39
|
}
|
|
41
40
|
catch (err) {
|
|
42
41
|
uiLogger.debug('Unable to fetch sandbox usage limits: ', err);
|
|
@@ -83,16 +82,14 @@ export async function selectSandboxTargetAccountPrompt(accounts, defaultAccountC
|
|
|
83
82
|
}
|
|
84
83
|
export async function selectDeveloperTestTargetAccountPrompt(accounts, defaultAccountConfig) {
|
|
85
84
|
const defaultAccountId = defaultAccountConfig.accountId;
|
|
85
|
+
if (!defaultAccountId) {
|
|
86
|
+
uiLogger.error(lib.prompts.projectDevTargetAccountPrompt.noAccountId);
|
|
87
|
+
throw new PromptExitError(lib.prompts.projectDevTargetAccountPrompt.noAccountId, EXIT_CODES.ERROR);
|
|
88
|
+
}
|
|
86
89
|
let devTestAccountsResponse;
|
|
87
90
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
devTestAccountsResponse = data;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
uiLogger.error(lib.prompts.projectDevTargetAccountPrompt.noAccountId);
|
|
94
|
-
process.exit(EXIT_CODES.ERROR);
|
|
95
|
-
}
|
|
91
|
+
const { data } = await fetchDeveloperTestAccounts(defaultAccountId);
|
|
92
|
+
devTestAccountsResponse = data;
|
|
96
93
|
}
|
|
97
94
|
catch (err) {
|
|
98
95
|
uiLogger.debug('Unable to fetch developer test account usage limits: ', err);
|
|
@@ -4,25 +4,29 @@ import { lib } from '../../lang/en.js';
|
|
|
4
4
|
import { debugError } from '../errorHandlers/index.js';
|
|
5
5
|
import { uiLogger } from '../ui/logger.js';
|
|
6
6
|
import { fetchTables } from '@hubspot/local-dev-lib/api/hubdb';
|
|
7
|
-
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
8
7
|
import { isValidPath, untildify } from '@hubspot/local-dev-lib/path';
|
|
8
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
9
|
+
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
9
10
|
async function fetchHubDBOptions(accountId) {
|
|
10
11
|
try {
|
|
11
12
|
const { data: { results: tables }, } = await fetchTables(accountId);
|
|
12
13
|
if (tables.length === 0) {
|
|
13
14
|
uiLogger.log(lib.prompts.selectHubDBTablePrompt.errors.noTables(accountId.toString()));
|
|
14
|
-
|
|
15
|
+
throw new PromptExitError(lib.prompts.selectHubDBTablePrompt.errors.noTables(accountId.toString()), EXIT_CODES.SUCCESS);
|
|
15
16
|
}
|
|
16
17
|
return tables;
|
|
17
18
|
}
|
|
18
19
|
catch (error) {
|
|
20
|
+
if (error instanceof PromptExitError) {
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
19
23
|
debugError(error, { accountId });
|
|
20
24
|
uiLogger.error(lib.prompts.selectHubDBTablePrompt.errors.errorFetchingTables(accountId.toString()));
|
|
21
|
-
|
|
25
|
+
throw new PromptExitError(lib.prompts.selectHubDBTablePrompt.errors.errorFetchingTables(accountId.toString()), EXIT_CODES.ERROR);
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
export async function selectHubDBTablePrompt({ accountId, options, skipDestPrompt = true, }) {
|
|
25
|
-
const hubdbTables =
|
|
29
|
+
const hubdbTables = await fetchHubDBOptions(accountId);
|
|
26
30
|
const id = options.tableId?.toString();
|
|
27
31
|
const isValidTable = options.tableId && hubdbTables.find(table => table.id === id);
|
|
28
32
|
return promptUser([
|
|
@@ -4,13 +4,14 @@ import { uiLine } from '../ui/index.js';
|
|
|
4
4
|
import { logError } from '../errorHandlers/index.js';
|
|
5
5
|
import { uiLogger } from '../ui/logger.js';
|
|
6
6
|
import { fetchPublicAppsForPortal } from '@hubspot/local-dev-lib/api/appsDev';
|
|
7
|
+
import { PromptExitError } from '../errors/PromptExitError.js';
|
|
7
8
|
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
8
9
|
async function fetchPublicAppOptions(accountId, accountName, isMigratingApp = false) {
|
|
10
|
+
if (!accountId) {
|
|
11
|
+
uiLogger.error(lib.prompts.selectPublicAppForMigrationPrompt.errors.noAccountId);
|
|
12
|
+
throw new PromptExitError(lib.prompts.selectPublicAppForMigrationPrompt.errors.noAccountId, EXIT_CODES.ERROR);
|
|
13
|
+
}
|
|
9
14
|
try {
|
|
10
|
-
if (!accountId) {
|
|
11
|
-
uiLogger.error(lib.prompts.selectPublicAppForMigrationPrompt.errors.noAccountId);
|
|
12
|
-
process.exit(EXIT_CODES.ERROR);
|
|
13
|
-
}
|
|
14
15
|
const { data: { results: publicApps }, } = await fetchPublicAppsForPortal(accountId);
|
|
15
16
|
const filteredPublicApps = publicApps.filter(app => !app.projectId && !app.sourceId);
|
|
16
17
|
if (!filteredPublicApps.length ||
|
|
@@ -24,14 +25,19 @@ async function fetchPublicAppOptions(accountId, accountName, isMigratingApp = fa
|
|
|
24
25
|
uiLogger.error(`${lib.prompts.selectPublicAppForMigrationPrompt.errors.noAppsClone}\n${lib.prompts.selectPublicAppForMigrationPrompt.errors.noAppsCloneMessage(accountName)}`);
|
|
25
26
|
}
|
|
26
27
|
uiLine();
|
|
27
|
-
|
|
28
|
+
throw new PromptExitError(isMigratingApp
|
|
29
|
+
? lib.prompts.selectPublicAppForMigrationPrompt.errors.noAppsMigration
|
|
30
|
+
: lib.prompts.selectPublicAppForMigrationPrompt.errors.noAppsClone, EXIT_CODES.SUCCESS);
|
|
28
31
|
}
|
|
29
32
|
return filteredPublicApps;
|
|
30
33
|
}
|
|
31
34
|
catch (error) {
|
|
35
|
+
if (error instanceof PromptExitError) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
32
38
|
logError(error, accountId ? { accountId } : undefined);
|
|
33
39
|
uiLogger.error(lib.prompts.selectPublicAppForMigrationPrompt.errors.errorFetchingApps);
|
|
34
|
-
|
|
40
|
+
throw new PromptExitError(lib.prompts.selectPublicAppForMigrationPrompt.errors.errorFetchingApps, EXIT_CODES.ERROR);
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
export async function selectPublicAppForMigrationPrompt({ accountId, accountName, isMigratingApp = false, }) {
|
package/lib/sandboxes.d.ts
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import { AccountType, HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
|
|
2
2
|
import { Environment } from '@hubspot/local-dev-lib/types/Accounts';
|
|
3
|
-
import {
|
|
4
|
-
export declare const SYNC_TYPES: {
|
|
5
|
-
readonly OBJECT_RECORDS: "object-records";
|
|
6
|
-
};
|
|
3
|
+
import { SandboxAccountType } from '../types/Sandboxes.js';
|
|
7
4
|
export declare const SANDBOX_TYPE_MAP: {
|
|
8
5
|
[key: string]: SandboxAccountType;
|
|
9
6
|
};
|
|
10
|
-
export declare const SANDBOX_API_TYPE_MAP: {
|
|
11
|
-
readonly STANDARD_SANDBOX: 1;
|
|
12
|
-
readonly DEVELOPMENT_SANDBOX: 2;
|
|
13
|
-
};
|
|
14
7
|
export declare const SANDBOX_TYPE_MAP_V2: {
|
|
15
8
|
readonly STANDARD_SANDBOX: "STANDARD";
|
|
16
9
|
readonly DEVELOPMENT_SANDBOX: "DEVELOPER";
|
|
17
10
|
};
|
|
18
11
|
export declare function getSandboxTypeAsString(accountType?: AccountType): string;
|
|
19
12
|
export declare function getHasSandboxesByType(parentAccountConfig: HubSpotConfigAccount, type: AccountType): boolean;
|
|
20
|
-
export declare function getAvailableSyncTypes(parentAccountConfig: HubSpotConfigAccount, config: HubSpotConfigAccount): Promise<Array<SandboxSyncTask>>;
|
|
21
13
|
export declare function validateSandboxUsageLimits(accountConfig: HubSpotConfigAccount, sandboxType: AccountType, env: Environment): Promise<void>;
|
|
22
14
|
export declare function handleSandboxCreateError(err: unknown, env: Environment, name: string, accountId: number): never;
|