@hubspot/cli 7.6.0-beta.11 → 7.6.0-beta.13
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/app/__tests__/migrate.test.js +1 -0
- package/commands/getStarted.js +7 -20
- package/commands/mcp/setup.d.ts +0 -1
- package/commands/mcp/setup.js +11 -11
- package/commands/project/__tests__/add.test.js +64 -0
- package/commands/project/__tests__/create.test.js +57 -0
- package/commands/project/__tests__/deploy.test.js +3 -2
- package/commands/project/add.d.ts +1 -1
- package/commands/project/add.js +3 -5
- package/commands/project/create.js +7 -2
- package/commands/project/deploy.js +9 -61
- package/commands/project/dev/index.js +1 -1
- package/commands/project/dev/unifiedFlow.js +4 -1
- package/commands/project/migrate.js +26 -7
- package/commands/project/upload.js +2 -2
- package/commands/project/validate.js +1 -1
- package/commands/project/watch.js +2 -2
- package/lang/en.d.ts +20 -5
- package/lang/en.js +38 -22
- package/lang/en.lyaml +12 -12
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/__tests__/importData.test.js +1 -1
- package/lib/app/__tests__/migrate.test.js +14 -51
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +5 -73
- package/lib/constants.d.ts +4 -0
- package/lib/constants.js +4 -0
- package/lib/errorHandlers/index.d.ts +4 -0
- package/lib/errorHandlers/index.js +1 -1
- package/lib/hasFeature.js +6 -0
- package/lib/importData.js +1 -1
- package/lib/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.d.ts +0 -2
- package/lib/mcp/setup.js +4 -29
- package/lib/projects/__tests__/AppDevModeInterface.test.js +71 -44
- package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
- package/lib/projects/__tests__/components.test.js +164 -7
- package/lib/projects/__tests__/deploy.test.js +164 -0
- package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
- package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
- package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
- package/lib/projects/add/__tests__/v3AddComponent.test.js +142 -8
- package/lib/projects/add/legacyAddComponent.d.ts +1 -1
- package/lib/projects/add/legacyAddComponent.js +5 -1
- package/lib/projects/add/v3AddComponent.d.ts +2 -1
- package/lib/projects/add/v3AddComponent.js +22 -5
- package/lib/projects/components.d.ts +1 -0
- package/lib/projects/components.js +27 -1
- package/lib/projects/create/__tests__/v3.test.js +97 -9
- package/lib/projects/create/index.js +2 -2
- package/lib/projects/create/legacy.js +1 -1
- package/lib/projects/create/v3.d.ts +2 -2
- package/lib/projects/create/v3.js +35 -12
- package/lib/projects/deploy.d.ts +13 -0
- package/lib/projects/deploy.js +63 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
- package/lib/projects/localDev/AppDevModeInterface.js +110 -47
- package/lib/projects/localDev/DevServerManagerV2.js +1 -0
- package/lib/projects/localDev/LocalDevProcess.js +3 -1
- package/lib/projects/localDev/LocalDevState.d.ts +5 -2
- package/lib/projects/localDev/LocalDevState.js +9 -1
- package/lib/projects/localDev/helpers/project.d.ts +2 -2
- package/lib/projects/localDev/helpers/project.js +6 -8
- package/lib/projects/platformVersion.d.ts +1 -0
- package/lib/projects/platformVersion.js +10 -0
- package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
- package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
- package/lib/projects/upload.js +1 -1
- package/lib/projects/urls.d.ts +1 -0
- package/lib/projects/urls.js +3 -0
- package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
- package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
- package/lib/prompts/importDataFilePathPrompt.js +4 -2
- package/lib/prompts/installAppPrompt.d.ts +6 -1
- package/lib/prompts/installAppPrompt.js +6 -1
- package/lib/prompts/projectAddPrompt.js +1 -1
- package/lib/prompts/promptUtils.d.ts +5 -0
- package/lib/prompts/promptUtils.js +9 -0
- package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
- package/lib/theme/__tests__/migrate.test.d.ts +1 -0
- package/lib/theme/__tests__/migrate.test.js +233 -0
- package/lib/theme/migrate.d.ts +13 -0
- package/lib/theme/migrate.js +90 -0
- package/lib/ui/index.js +3 -6
- package/lib/usageTracking.js +2 -2
- package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
- package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
- package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
- package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
- package/mcp-server/tools/index.js +13 -1
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
- package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/CreateProjectTool.js +5 -5
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/DocFetchTool.js +2 -2
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +7 -7
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +14 -8
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +2 -2
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
- package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
- package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/tools/project/constants.js +14 -6
- package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
- package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
- package/mcp-server/utils/cliConfig.d.ts +1 -0
- package/mcp-server/utils/cliConfig.js +12 -0
- package/package.json +4 -3
- package/types/LocalDev.d.ts +2 -1
- package/types/Projects.d.ts +1 -0
- package/ui/components/BoxWithTitle.d.ts +8 -0
- package/ui/components/BoxWithTitle.js +9 -0
- package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
- package/ui/components/HorizontalSelectPrompt.js +30 -0
- package/ui/components/StatusMessageBoxes.d.ts +12 -0
- package/ui/components/StatusMessageBoxes.js +31 -0
- package/ui/lib/ui-testing-utils.d.ts +9 -0
- package/ui/lib/ui-testing-utils.js +47 -0
- package/ui/lib/useTerminalSize.d.ts +13 -0
- package/ui/lib/useTerminalSize.js +31 -0
- package/ui/styles.d.ts +18 -0
- package/ui/styles.js +18 -0
- package/ui/views/UiSandbox.d.ts +5 -0
- package/ui/views/UiSandbox.js +25 -0
- /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
2
2
|
import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
|
-
import { validateUid
|
|
4
|
+
import { validateUid } from '@hubspot/project-parsing-lib';
|
|
5
5
|
import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
6
6
|
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
|
|
7
7
|
import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
@@ -12,7 +12,7 @@ import { poll } from '../../polling.js';
|
|
|
12
12
|
import { CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, listAppsForMigration, } from '../../../api/migrate.js';
|
|
13
13
|
import { lib } from '../../../lang/en.js';
|
|
14
14
|
import { hasUnfiedAppsAccess } from '../../hasFeature.js';
|
|
15
|
-
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup,
|
|
15
|
+
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationApps, } from '../migrate.js';
|
|
16
16
|
vi.mock('@hubspot/local-dev-lib/logger');
|
|
17
17
|
vi.mock('@hubspot/local-dev-lib/path');
|
|
18
18
|
vi.mock('@hubspot/local-dev-lib/archive');
|
|
@@ -40,7 +40,6 @@ const mockedListPrompt = listPrompt;
|
|
|
40
40
|
const mockedEnsureProjectExists = ensureProjectExists;
|
|
41
41
|
const mockedPoll = poll;
|
|
42
42
|
const mockedListAppsForMigration = listAppsForMigration;
|
|
43
|
-
const mockedGetProjectThemeDetails = getProjectThemeDetails;
|
|
44
43
|
const mockedInitializeAppMigration = initializeAppMigration;
|
|
45
44
|
const mockedContinueAppMigration = continueAppMigration;
|
|
46
45
|
const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
|
|
@@ -205,58 +204,31 @@ describe('lib/app/migrate', () => {
|
|
|
205
204
|
expect(result.migratableApps[0].projectName).toBe(PROJECT_NAME);
|
|
206
205
|
});
|
|
207
206
|
});
|
|
208
|
-
describe('
|
|
209
|
-
it('should return false when no projectConfig is provided', async () => {
|
|
210
|
-
const result = await getHasMigratableThemes();
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
hasMigratableThemes: false,
|
|
213
|
-
migratableThemesCount: 0,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
it('should return true when there are migratable themes', async () => {
|
|
217
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
218
|
-
legacyThemeDetails: [
|
|
219
|
-
{
|
|
220
|
-
configFilepath: 'src/theme.json',
|
|
221
|
-
themePath: 'src/theme',
|
|
222
|
-
themeConfig: {
|
|
223
|
-
secret_names: ['my-secret'],
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
legacyReactThemeDetails: [],
|
|
228
|
-
});
|
|
229
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
230
|
-
const result = await getHasMigratableThemes(projectConfig);
|
|
231
|
-
expect(result).toEqual({
|
|
232
|
-
hasMigratableThemes: true,
|
|
233
|
-
migratableThemesCount: 1,
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
describe('validateMigrationAppsAndThemes', () => {
|
|
207
|
+
describe('validateMigrationApps', () => {
|
|
238
208
|
const mockMigratableApp1 = createMockMigratableApp(1, 'App 1', PROJECT_NAME);
|
|
239
209
|
const mockMigratableApp2 = createMockMigratableApp(2, 'App 2', PROJECT_NAME);
|
|
240
210
|
it('should throw an error when multiple apps are found for a project', async () => {
|
|
241
211
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
242
|
-
await expect(
|
|
212
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
243
213
|
migratableApps: [mockMigratableApp1, mockMigratableApp2],
|
|
244
214
|
unmigratableApps: [],
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
it('should throw an error when cms themes are found for a project', async () => {
|
|
248
|
-
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
249
|
-
await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [mockMigratableApp1], unmigratableApps: [] }, true, projectConfig)).rejects.toThrow(lib.migrate.errors.project.themesAndAppsNotAllowed);
|
|
215
|
+
}, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
|
|
250
216
|
});
|
|
251
217
|
it('should throw an error when no apps are found for a project', async () => {
|
|
252
218
|
const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
|
|
253
|
-
await expect(
|
|
219
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
|
|
254
220
|
});
|
|
255
221
|
it('should throw an error when no migratable apps are found', async () => {
|
|
256
|
-
await expect(
|
|
222
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
223
|
+
migratableApps: [],
|
|
224
|
+
unmigratableApps: mockUnmigratableApps,
|
|
225
|
+
})).rejects.toThrow(/No apps in account/);
|
|
257
226
|
});
|
|
258
227
|
it('should throw an error when appId is provided but not found', async () => {
|
|
259
|
-
await expect(
|
|
228
|
+
await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
|
|
229
|
+
migratableApps: [],
|
|
230
|
+
unmigratableApps: [],
|
|
231
|
+
})).rejects.toThrow(/No apps in account/);
|
|
260
232
|
});
|
|
261
233
|
});
|
|
262
234
|
describe('promptForAppToMigrate', () => {
|
|
@@ -330,10 +302,6 @@ describe('lib/app/migrate', () => {
|
|
|
330
302
|
unmigratableApps: [],
|
|
331
303
|
},
|
|
332
304
|
});
|
|
333
|
-
mockedGetProjectThemeDetails.mockResolvedValue({
|
|
334
|
-
legacyThemeDetails: [],
|
|
335
|
-
legacyReactThemeDetails: [],
|
|
336
|
-
});
|
|
337
305
|
mockedListPrompt.mockResolvedValue({ appId: 1 });
|
|
338
306
|
mockedConfirmPrompt.mockResolvedValue(true);
|
|
339
307
|
mockedEnsureProjectExists.mockResolvedValue({ projectExists: false });
|
|
@@ -352,16 +320,11 @@ describe('lib/app/migrate', () => {
|
|
|
352
320
|
unmigratableApps: [],
|
|
353
321
|
},
|
|
354
322
|
});
|
|
355
|
-
mockedGetProjectThemeDetails.mockResolvedValueOnce({
|
|
356
|
-
legacyThemeDetails: [],
|
|
357
|
-
legacyReactThemeDetails: [],
|
|
358
|
-
});
|
|
359
323
|
const result = await handleMigrationSetup(ACCOUNT_ID, defaultOptions, projectConfig);
|
|
360
324
|
expect(result).toEqual({
|
|
361
325
|
appIdToMigrate: 1,
|
|
362
326
|
projectName: PROJECT_NAME,
|
|
363
327
|
projectDest: MOCK_PROJECT_DIR,
|
|
364
|
-
isThemesMigration: false,
|
|
365
328
|
});
|
|
366
329
|
});
|
|
367
330
|
it('should prompt for project name when not provided', async () => {
|
package/lib/app/migrate.d.ts
CHANGED
|
@@ -12,18 +12,14 @@ export type MigrateAppArgs = CommonArgs & AccountArgs & EnvironmentArgs & Config
|
|
|
12
12
|
export declare function getUnmigratableReason(reasonCode: string, projectName: string | undefined, accountId: number): string;
|
|
13
13
|
export declare function generateFilterAppsByProjectNameFunction(projectConfig?: LoadedProjectConfig): (app: MigrationApp) => boolean;
|
|
14
14
|
export declare function buildErrorMessageFromMigrationStatus(error: MigrationFailed): string;
|
|
15
|
-
export declare function getHasMigratableThemes(projectConfig?: LoadedProjectConfig): Promise<{
|
|
16
|
-
hasMigratableThemes: boolean;
|
|
17
|
-
migratableThemesCount: number;
|
|
18
|
-
}>;
|
|
19
15
|
export declare function fetchMigrationApps(derivedAccountId: number, platformVersion: string, projectConfig?: LoadedProjectConfig): Promise<{
|
|
20
16
|
migratableApps: MigratableApp[];
|
|
21
17
|
unmigratableApps: UnmigratableApp[];
|
|
22
18
|
}>;
|
|
23
|
-
export declare function
|
|
19
|
+
export declare function validateMigrationApps(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
|
|
24
20
|
migratableApps: MigratableApp[];
|
|
25
21
|
unmigratableApps: UnmigratableApp[];
|
|
26
|
-
},
|
|
22
|
+
}, projectConfig?: LoadedProjectConfig): Promise<void>;
|
|
27
23
|
export declare function promptForAppToMigrate(allApps: MigrationApp[], derivedAccountId: number): Promise<number>;
|
|
28
24
|
export declare function selectAppToMigrate(allApps: MigrationApp[], derivedAccountId: number, appId?: number): Promise<{
|
|
29
25
|
proceed: boolean;
|
|
@@ -33,9 +29,7 @@ export declare function handleMigrationSetup(derivedAccountId: number, options:
|
|
|
33
29
|
appIdToMigrate?: number | undefined;
|
|
34
30
|
projectName?: string;
|
|
35
31
|
projectDest?: string;
|
|
36
|
-
isThemesMigration?: boolean;
|
|
37
32
|
}>;
|
|
38
|
-
export declare function handleThemesMigration(projectConfig: LoadedProjectConfig, platformVersion: string): Promise<void>;
|
|
39
33
|
export declare function beginAppMigration(derivedAccountId: number, appId: number, platformVersion: string): Promise<{
|
|
40
34
|
migrationId: number;
|
|
41
35
|
uidMap: Record<string, string>;
|
package/lib/app/migrate.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
|
|
3
3
|
import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import { validateUid
|
|
5
|
+
import { validateUid } from '@hubspot/project-parsing-lib';
|
|
6
6
|
import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
7
7
|
import { mapToUserFacingType } from '@hubspot/project-parsing-lib/src/lib/transform.js';
|
|
8
8
|
import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
|
|
@@ -10,19 +10,15 @@ import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
|
|
|
10
10
|
import { Separator } from '@inquirer/prompts';
|
|
11
11
|
import { confirmPrompt, inputPrompt, listPrompt, } from '../prompts/promptUtils.js';
|
|
12
12
|
import { uiAccountDescription, uiCommandReference, uiLine, uiLink, } from '../ui/index.js';
|
|
13
|
-
import { writeProjectConfig } from '../projects/config.js';
|
|
14
13
|
import { ensureProjectExists } from '../projects/ensureProjectExists.js';
|
|
15
14
|
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
16
15
|
import { DEFAULT_POLLING_STATUS_LOOKUP, poll } from '../polling.js';
|
|
17
16
|
import { checkMigrationStatusV2, CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, isMigrationStatus, listAppsForMigration, } from '../../api/migrate.js';
|
|
18
17
|
import fs from 'fs';
|
|
19
18
|
import { lib } from '../../lang/en.js';
|
|
20
|
-
import { PROJECT_CONFIG_FILE } from '../constants.js';
|
|
21
19
|
import { hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
22
20
|
import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
|
|
23
21
|
import { uiLogger } from '../ui/logger.js';
|
|
24
|
-
import { debugError } from '../errorHandlers/index.js';
|
|
25
|
-
import { useV3Api } from '../projects/buildAndDeploy.js';
|
|
26
22
|
export function getUnmigratableReason(reasonCode, projectName, accountId) {
|
|
27
23
|
switch (reasonCode) {
|
|
28
24
|
case UNMIGRATABLE_REASONS.UP_TO_DATE:
|
|
@@ -59,17 +55,6 @@ export function buildErrorMessageFromMigrationStatus(error) {
|
|
|
59
55
|
})
|
|
60
56
|
.join('\n\t- ')}`;
|
|
61
57
|
}
|
|
62
|
-
export async function getHasMigratableThemes(projectConfig) {
|
|
63
|
-
if (!projectConfig?.projectConfig?.name || !projectConfig?.projectDir) {
|
|
64
|
-
return { hasMigratableThemes: false, migratableThemesCount: 0 };
|
|
65
|
-
}
|
|
66
|
-
const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
67
|
-
const { legacyThemeDetails, legacyReactThemeDetails } = await getProjectThemeDetails(projectSrcDir);
|
|
68
|
-
return {
|
|
69
|
-
hasMigratableThemes: legacyThemeDetails.length > 0 || legacyReactThemeDetails.length > 0,
|
|
70
|
-
migratableThemesCount: legacyThemeDetails.length + legacyReactThemeDetails.length,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
58
|
export async function fetchMigrationApps(derivedAccountId, platformVersion, projectConfig) {
|
|
74
59
|
const { data: { migratableApps, unmigratableApps }, } = await listAppsForMigration(derivedAccountId, platformVersion);
|
|
75
60
|
const filteredMigratableApps = migratableApps.filter(generateFilterAppsByProjectNameFunction(projectConfig));
|
|
@@ -79,23 +64,11 @@ export async function fetchMigrationApps(derivedAccountId, platformVersion, proj
|
|
|
79
64
|
unmigratableApps: filteredUnmigratableApps,
|
|
80
65
|
};
|
|
81
66
|
}
|
|
82
|
-
export async function
|
|
67
|
+
export async function validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps, }, projectConfig) {
|
|
83
68
|
const allApps = [...migratableApps, ...unmigratableApps];
|
|
84
69
|
if (allApps.length > 1 && projectConfig) {
|
|
85
70
|
throw new Error(lib.migrate.errors.project.multipleApps);
|
|
86
71
|
}
|
|
87
|
-
if (hasMigratableThemes) {
|
|
88
|
-
if (useV3Api(projectConfig?.projectConfig?.platformVersion)) {
|
|
89
|
-
throw new Error(lib.migrate.errors.project.themesAlreadyMigrated);
|
|
90
|
-
}
|
|
91
|
-
if (allApps.length > 0 && projectConfig) {
|
|
92
|
-
throw new Error(lib.migrate.errors.project.themesAndAppsNotAllowed);
|
|
93
|
-
}
|
|
94
|
-
if (!projectConfig) {
|
|
95
|
-
throw new Error(lib.migrate.errors.project.noProjectForThemesMigration);
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
72
|
if (!projectConfig?.projectConfig) {
|
|
100
73
|
allApps.forEach(app => {
|
|
101
74
|
if (app.projectName) {
|
|
@@ -178,35 +151,20 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
|
|
|
178
151
|
text: lib.migrate.spinners.checkingForMigratableComponents,
|
|
179
152
|
});
|
|
180
153
|
const { name, dest, appId } = options;
|
|
181
|
-
const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
|
|
182
154
|
const { migratableApps, unmigratableApps } = await fetchMigrationApps(derivedAccountId, options.platformVersion, projectConfig);
|
|
183
155
|
SpinniesManager.remove('checkingForMigratableComponents');
|
|
184
|
-
await
|
|
156
|
+
await validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps }, projectConfig);
|
|
185
157
|
const allApps = [...migratableApps, ...unmigratableApps];
|
|
186
|
-
|
|
187
|
-
let appIdToMigrate;
|
|
188
|
-
if (hasMigratableThemes) {
|
|
189
|
-
uiLogger.log(lib.migrate.prompt.themesMigration(migratableThemesCount));
|
|
190
|
-
proceed = await confirmPrompt(lib.migrate.prompt.proceed, {
|
|
191
|
-
defaultAnswer: false,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
const appSelectionPrompt = await selectAppToMigrate(allApps, derivedAccountId, appId);
|
|
196
|
-
appIdToMigrate = appSelectionPrompt.appIdToMigrate;
|
|
197
|
-
proceed = appSelectionPrompt.proceed;
|
|
198
|
-
}
|
|
158
|
+
const { proceed, appIdToMigrate } = await selectAppToMigrate(allApps, derivedAccountId, appId);
|
|
199
159
|
if (!proceed) {
|
|
200
160
|
return {};
|
|
201
161
|
}
|
|
202
162
|
// If it's a project we don't want to prompt for dest and name, so just return early
|
|
203
|
-
// Theme migrations can only be initiated from the project directory
|
|
204
163
|
if (projectConfig &&
|
|
205
164
|
projectConfig?.projectConfig &&
|
|
206
165
|
projectConfig?.projectDir) {
|
|
207
166
|
return {
|
|
208
167
|
appIdToMigrate,
|
|
209
|
-
isThemesMigration: hasMigratableThemes,
|
|
210
168
|
projectName: projectConfig.projectConfig.name,
|
|
211
169
|
projectDest: projectConfig.projectDir,
|
|
212
170
|
};
|
|
@@ -231,28 +189,6 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
|
|
|
231
189
|
}));
|
|
232
190
|
return { appIdToMigrate, projectName, projectDest };
|
|
233
191
|
}
|
|
234
|
-
export async function handleThemesMigration(projectConfig, platformVersion) {
|
|
235
|
-
if (!projectConfig?.projectDir || !projectConfig?.projectConfig?.srcDir) {
|
|
236
|
-
throw new Error(lib.migrate.errors.project.invalidConfig);
|
|
237
|
-
}
|
|
238
|
-
const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
239
|
-
try {
|
|
240
|
-
await migrateThemes(projectConfig.projectDir, projectSrcDir);
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
debugError(error);
|
|
244
|
-
throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
|
|
245
|
-
}
|
|
246
|
-
const newProjectConfig = { ...projectConfig.projectConfig };
|
|
247
|
-
newProjectConfig.platformVersion = platformVersion;
|
|
248
|
-
const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
|
|
249
|
-
const success = writeProjectConfig(projectConfigPath, newProjectConfig);
|
|
250
|
-
if (!success) {
|
|
251
|
-
throw new Error(lib.migrate.errors.project.failedToUpdateProjectConfig);
|
|
252
|
-
}
|
|
253
|
-
uiLogger.log('');
|
|
254
|
-
uiLogger.log(lib.migrate.success.themesMigrationSuccess(platformVersion));
|
|
255
|
-
}
|
|
256
192
|
export async function beginAppMigration(derivedAccountId, appId, platformVersion) {
|
|
257
193
|
SpinniesManager.add('beginningMigration', {
|
|
258
194
|
text: lib.migrate.spinners.beginningMigration,
|
|
@@ -405,11 +341,7 @@ export async function migrateApp2025_2(derivedAccountId, options, projectConfig)
|
|
|
405
341
|
throw new Error(lib.migrate.errors.project.doesNotExist(derivedAccountId));
|
|
406
342
|
}
|
|
407
343
|
}
|
|
408
|
-
const { appIdToMigrate, projectName, projectDest
|
|
409
|
-
if (isThemesMigration) {
|
|
410
|
-
await handleThemesMigration(projectConfig, options.platformVersion);
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
344
|
+
const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
|
|
413
345
|
if (!appIdToMigrate || !projectName || !projectDest) {
|
|
414
346
|
return;
|
|
415
347
|
}
|
package/lib/constants.d.ts
CHANGED
|
@@ -80,6 +80,10 @@ export declare const FEATURES: {
|
|
|
80
80
|
readonly UNIFIED_APPS: "Developers:UnifiedApps:PrivateBeta";
|
|
81
81
|
readonly SANDBOXES_V2: "sandboxes:v2:enabled";
|
|
82
82
|
readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
|
|
83
|
+
readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
|
|
84
|
+
readonly APPS_HOME: "UIE:AppHome";
|
|
85
|
+
readonly MCP_ACCESS: "Developers:CLIMCPAccess";
|
|
86
|
+
readonly THEME_MIGRATION_2025_2: "Developers:ProjectThemeMigrations:2025.2";
|
|
83
87
|
};
|
|
84
88
|
export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
|
|
85
89
|
UPLOAD_SUCCESS: string;
|
package/lib/constants.js
CHANGED
|
@@ -72,6 +72,10 @@ export const FEATURES = {
|
|
|
72
72
|
UNIFIED_APPS: 'Developers:UnifiedApps:PrivateBeta',
|
|
73
73
|
SANDBOXES_V2: 'sandboxes:v2:enabled',
|
|
74
74
|
SANDBOXES_V2_CLI: 'sandboxes:v2:cliEnabled',
|
|
75
|
+
APP_EVENTS: 'Developers:UnifiedApps:AppEventsAccess',
|
|
76
|
+
APPS_HOME: 'UIE:AppHome',
|
|
77
|
+
MCP_ACCESS: 'Developers:CLIMCPAccess',
|
|
78
|
+
THEME_MIGRATION_2025_2: 'Developers:ProjectThemeMigrations:2025.2',
|
|
75
79
|
};
|
|
76
80
|
export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
|
|
77
81
|
UPLOAD_SUCCESS: 'server:uploadSuccess',
|
|
@@ -85,7 +85,7 @@ export class ApiErrorContext {
|
|
|
85
85
|
this.projectName = props.projectName || '';
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
function isErrorWithMessageOrReason(error) {
|
|
88
|
+
export function isErrorWithMessageOrReason(error) {
|
|
89
89
|
return (typeof error === 'object' &&
|
|
90
90
|
error !== null &&
|
|
91
91
|
('message' in error || 'reason' in error));
|
package/lib/hasFeature.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { http } from '@hubspot/local-dev-lib/http';
|
|
2
2
|
import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
3
|
+
import { FEATURES } from './constants.js';
|
|
4
|
+
const FEATURES_THAT_DEFAULT_ON = [FEATURES.APPS_HOME];
|
|
3
5
|
export async function hasFeature(accountId, feature) {
|
|
4
6
|
const { data: { enabledFeatures }, } = await fetchEnabledFeatures(accountId);
|
|
7
|
+
if (enabledFeatures[feature] === undefined &&
|
|
8
|
+
FEATURES_THAT_DEFAULT_ON.includes(feature)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
5
11
|
return Boolean(enabledFeatures[feature]);
|
|
6
12
|
}
|
|
7
13
|
export async function hasUnfiedAppsAccess(accountId) {
|
package/lib/importData.js
CHANGED
|
@@ -10,7 +10,7 @@ export async function handleImportData(targetAccountId, dataFileNames, importReq
|
|
|
10
10
|
const baseUrl = getHubSpotWebsiteOrigin(getEnv());
|
|
11
11
|
const response = await createImport(targetAccountId, importRequest, dataFileNames);
|
|
12
12
|
const importId = response.data.id;
|
|
13
|
-
uiLogger.
|
|
13
|
+
uiLogger.info(lib.importData.viewImportLink(baseUrl, targetAccountId, importId));
|
|
14
14
|
}
|
|
15
15
|
catch (error) {
|
|
16
16
|
uiLogger.error(lib.importData.errors.failedToImportData);
|
package/lib/links.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ type SiteLink = {
|
|
|
7
7
|
export declare function getSiteLinksAsArray(accountId: number): SiteLink[];
|
|
8
8
|
export declare function logSiteLinks(accountId: number): void;
|
|
9
9
|
export declare function openLink(accountId: number, shortcut: string): void;
|
|
10
|
+
export declare function getProductUpdatesUrl(rolloutId: string, accountId?: number): string;
|
|
10
11
|
export {};
|
package/lib/links.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
|
4
4
|
import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
|
|
5
5
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
6
6
|
import { getTableContents, getTableHeader } from './ui/table.js';
|
|
7
|
-
const
|
|
7
|
+
const COMMON_SITE_LINKS = {
|
|
8
8
|
APPS_MARKETPLACE: {
|
|
9
9
|
shortcut: 'apps-marketplace',
|
|
10
10
|
alias: 'apm',
|
|
@@ -76,7 +76,7 @@ const SITE_LINKS = {
|
|
|
76
76
|
};
|
|
77
77
|
export function getSiteLinksAsArray(accountId) {
|
|
78
78
|
const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
79
|
-
return Object.values(
|
|
79
|
+
return Object.values(COMMON_SITE_LINKS)
|
|
80
80
|
.sort((a, b) => (a.shortcut < b.shortcut ? -1 : 1))
|
|
81
81
|
.map(l => ({ ...l, url: l.getUrl(accountId, baseUrl) }));
|
|
82
82
|
}
|
|
@@ -90,7 +90,7 @@ export function logSiteLinks(accountId) {
|
|
|
90
90
|
logger.log(getTableContents(linksAsArray));
|
|
91
91
|
}
|
|
92
92
|
export function openLink(accountId, shortcut) {
|
|
93
|
-
const match = Object.values(
|
|
93
|
+
const match = Object.values(COMMON_SITE_LINKS).find(l => l.shortcut === shortcut || (l.alias && l.alias === shortcut));
|
|
94
94
|
if (!match) {
|
|
95
95
|
logger.error(`We couldn't find a shortcut matching ${shortcut}. Type 'hs open list' to see a list of available shortcuts`);
|
|
96
96
|
return;
|
|
@@ -99,3 +99,10 @@ export function openLink(accountId, shortcut) {
|
|
|
99
99
|
open(match.getUrl(accountId, baseUrl), { url: true });
|
|
100
100
|
logger.success(`We opened ${match.getUrl(accountId, baseUrl)} in your browser`);
|
|
101
101
|
}
|
|
102
|
+
export function getProductUpdatesUrl(rolloutId, accountId) {
|
|
103
|
+
const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
104
|
+
if (accountId) {
|
|
105
|
+
return `${baseUrl}/product-updates/${accountId}/in-beta?rollout=${rolloutId}`;
|
|
106
|
+
}
|
|
107
|
+
return `${baseUrl}/l/product-updates/in-beta?rollout=${rolloutId}`;
|
|
108
|
+
}
|
package/lib/mcp/setup.d.ts
CHANGED
|
@@ -15,8 +15,6 @@ interface McpCommand {
|
|
|
15
15
|
command: string;
|
|
16
16
|
args: string[];
|
|
17
17
|
}
|
|
18
|
-
export declare function addMintlifyMcpServer(installTargets: string[]): Promise<void>;
|
|
19
|
-
export declare function setupMintlify(derivedTargets?: string[]): Promise<boolean>;
|
|
20
18
|
export declare function addMcpServerToConfig(targets: string[] | undefined): Promise<string[]>;
|
|
21
19
|
export declare function setupVsCode(mcpCommand?: McpCommand): Promise<boolean>;
|
|
22
20
|
export declare function setupClaudeCode(mcpCommand?: McpCommand): Promise<boolean>;
|
package/lib/mcp/setup.js
CHANGED
|
@@ -4,17 +4,15 @@ import { promptUser } from '../prompts/promptUtils.js';
|
|
|
4
4
|
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
5
5
|
import { logError } from '../errorHandlers/index.js';
|
|
6
6
|
import { execAsync } from '../../mcp-server/utils/command.js';
|
|
7
|
-
import { spawn } from 'node:child_process';
|
|
8
7
|
import path from 'path';
|
|
9
8
|
import os from 'os';
|
|
10
9
|
import fs from 'fs-extra';
|
|
11
10
|
import { existsSync } from 'fs';
|
|
12
|
-
const mcpServerName = '
|
|
11
|
+
const mcpServerName = 'HubSpot';
|
|
13
12
|
const claudeCode = 'claude';
|
|
14
13
|
const windsurf = 'windsurf';
|
|
15
14
|
const cursor = 'cursor';
|
|
16
15
|
const vscode = 'vscode';
|
|
17
|
-
const supportedMintlifyClients = [windsurf, cursor];
|
|
18
16
|
export const supportedTools = [
|
|
19
17
|
{ name: commands.mcp.setup.claudeCode, value: claudeCode },
|
|
20
18
|
{ name: commands.mcp.setup.cursor, value: cursor },
|
|
@@ -25,28 +23,6 @@ const defaultMcpCommand = {
|
|
|
25
23
|
command: 'hs',
|
|
26
24
|
args: ['mcp', 'start'],
|
|
27
25
|
};
|
|
28
|
-
export async function addMintlifyMcpServer(installTargets) {
|
|
29
|
-
await runSetupFunction(() => setupMintlify(installTargets));
|
|
30
|
-
}
|
|
31
|
-
export async function setupMintlify(derivedTargets = supportedMintlifyClients) {
|
|
32
|
-
uiLogger.info(commands.mcp.setup.installingDocSearch);
|
|
33
|
-
uiLogger.log('');
|
|
34
|
-
return new Promise(resolve => {
|
|
35
|
-
const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
|
|
36
|
-
const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
|
|
37
|
-
const childProcess = spawn(`npx`, docsSearchClients && docsSearchClients.length
|
|
38
|
-
? [...subcommands, '--client', ...docsSearchClients]
|
|
39
|
-
: subcommands, {
|
|
40
|
-
stdio: 'inherit',
|
|
41
|
-
});
|
|
42
|
-
childProcess.on('exit', code => {
|
|
43
|
-
if (code !== 0) {
|
|
44
|
-
resolve(false);
|
|
45
|
-
}
|
|
46
|
-
resolve(true);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
26
|
export async function addMcpServerToConfig(targets) {
|
|
51
27
|
try {
|
|
52
28
|
let derivedTargets = [];
|
|
@@ -164,7 +140,7 @@ export async function setupVsCode(mcpCommand = defaultMcpCommand) {
|
|
|
164
140
|
name: mcpServerName,
|
|
165
141
|
...buildCommandWithAgentString(mcpCommand, vscode),
|
|
166
142
|
});
|
|
167
|
-
await execAsync(`code --add-mcp
|
|
143
|
+
await execAsync(`code --add-mcp ${JSON.stringify(mcpConfig)}`);
|
|
168
144
|
SpinniesManager.succeed('vsCode', {
|
|
169
145
|
text: commands.mcp.setup.spinners.configuredVsCode,
|
|
170
146
|
});
|
|
@@ -206,15 +182,14 @@ export async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
|
|
|
206
182
|
});
|
|
207
183
|
await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
|
|
208
184
|
}
|
|
209
|
-
await execAsync(`claude mcp add-json "${mcpServerName}"
|
|
185
|
+
await execAsync(`claude mcp add-json "${mcpServerName}" ${JSON.stringify(mcpConfig)} --scope user`);
|
|
210
186
|
SpinniesManager.succeed('claudeCode', {
|
|
211
187
|
text: commands.mcp.setup.spinners.configuredClaudeCode,
|
|
212
188
|
});
|
|
213
189
|
return true;
|
|
214
190
|
}
|
|
215
191
|
catch (error) {
|
|
216
|
-
if (error instanceof Error &&
|
|
217
|
-
error.message.includes('claude: command not found')) {
|
|
192
|
+
if (error instanceof Error && error.message.includes('claude')) {
|
|
218
193
|
SpinniesManager.fail('claudeCode', {
|
|
219
194
|
text: commands.mcp.setup.spinners.claudeCodeNotFound,
|
|
220
195
|
});
|