@hubspot/cli 7.8.0-experimental.0 → 7.8.2-experimental.0
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/bin/cli.js +0 -2
- package/commands/__tests__/getStarted.test.js +2 -2
- package/commands/__tests__/mcp.test.js +1 -1
- package/commands/__tests__/project.test.js +0 -3
- package/commands/app/__tests__/migrate.test.js +0 -1
- package/commands/app/migrate.js +4 -5
- package/commands/app/secret/add.js +2 -1
- package/commands/app/secret/delete.js +2 -1
- package/commands/app/secret/list.js +2 -1
- package/commands/app/secret/update.js +2 -1
- package/commands/app/secret.js +2 -1
- package/commands/app.js +2 -2
- package/commands/config/set.js +0 -1
- package/commands/feedback.js +1 -1
- package/commands/getStarted.d.ts +1 -3
- package/commands/getStarted.js +66 -18
- package/commands/mcp/__tests__/setup.test.js +2 -2
- package/commands/mcp/setup.js +11 -2
- package/commands/mcp.js +3 -3
- package/commands/project/__tests__/create.test.js +6 -6
- package/commands/project/__tests__/deploy.test.js +0 -3
- package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
- package/commands/project/__tests__/logs.test.js +0 -3
- package/commands/project/__tests__/migrate.test.js +1 -2
- package/commands/project/__tests__/migrateApp.test.js +1 -2
- package/commands/project/__tests__/profile.test.js +1 -1
- package/commands/project/add.js +1 -5
- package/commands/project/create.js +3 -9
- package/commands/project/deploy.js +2 -2
- package/commands/project/dev/index.js +4 -3
- package/commands/project/dev/unifiedFlow.js +6 -4
- package/commands/project/download.js +1 -2
- package/commands/project/installDeps.js +1 -2
- package/commands/project/listBuilds.js +2 -2
- package/commands/project/logs.js +2 -2
- package/commands/project/migrate.js +28 -10
- package/commands/project/migrateApp.js +1 -2
- package/commands/project/open.js +1 -2
- package/commands/project/profile/add.js +3 -3
- package/commands/project/profile/delete.js +1 -2
- package/commands/project/profile.js +2 -3
- package/commands/project/upload.js +2 -2
- package/commands/project/validate.js +1 -1
- package/commands/project/watch.js +2 -2
- package/commands/project.js +1 -2
- package/commands/sandbox/delete.js +1 -1
- package/commands/testAccount/importData.d.ts +1 -1
- package/commands/testAccount/importData.js +1 -1
- package/commands/testAccount.js +1 -1
- package/lang/en.d.ts +15 -4
- package/lang/en.js +18 -6
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/app/__tests__/migrate.test.js +14 -51
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +5 -80
- package/lib/constants.d.ts +8 -0
- package/lib/constants.js +8 -0
- package/lib/dependencyManagement.d.ts +0 -5
- package/lib/dependencyManagement.js +0 -9
- package/lib/hasFeature.js +6 -0
- package/lib/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.js +1 -1
- package/lib/middleware/fireAlarmMiddleware.js +15 -5
- package/lib/projects/__tests__/LocalDevProcess.test.js +227 -16
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
- package/lib/projects/__tests__/deploy.test.js +71 -6
- package/lib/projects/__tests__/localDevProjectHelpers.test.js +4 -2
- package/lib/projects/create/__tests__/v3.test.js +79 -4
- package/lib/projects/create/v3.js +11 -8
- package/lib/projects/localDev/AppDevModeInterface.js +5 -5
- package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
- package/lib/projects/localDev/LocalDevLogger.js +22 -0
- package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
- package/lib/projects/localDev/LocalDevProcess.js +90 -19
- package/lib/projects/localDev/LocalDevState.d.ts +9 -8
- package/lib/projects/localDev/LocalDevState.js +18 -17
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
- package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
- package/lib/projects/localDev/helpers/project.d.ts +2 -2
- package/lib/projects/localDev/helpers/project.js +10 -7
- package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
- package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
- package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
- package/lib/prompts/projectAddPrompt.js +2 -1
- package/lib/prompts/promptUtils.js +3 -0
- package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
- 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/SpinniesManager.js +105 -8
- package/lib/usageTracking.js +2 -2
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -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 +11 -5
- 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 +9 -3
- 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 -9
- package/types/LocalDev.d.ts +19 -3
- package/ui/components/HorizontalSelectPrompt.js +1 -1
- package/ui/index.js +1 -1
- package/commands/getStartedV2.d.ts +0 -9
- package/commands/getStartedV2.js +0 -39
- package/ui/components/Ascii.d.ts +0 -10
- package/ui/components/Ascii.js +0 -11
- package/ui/views/GetStarted.d.ts +0 -7
- package/ui/views/GetStarted.js +0 -157
package/lang/en.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
|
|
|
5
5
|
import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
|
|
6
6
|
import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
|
|
7
7
|
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
|
|
8
|
+
import { getProductUpdatesUrl } from '../lib/links.js';
|
|
8
9
|
import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
|
|
9
10
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
10
11
|
export const commands = {
|
|
@@ -18,6 +19,7 @@ export const commands = {
|
|
|
18
19
|
},
|
|
19
20
|
},
|
|
20
21
|
getStarted: {
|
|
22
|
+
describe: 'A step-by-step command to get you started with a HubSpot project.',
|
|
21
23
|
options: {
|
|
22
24
|
dest: {
|
|
23
25
|
describe: 'Directory where the project should be created',
|
|
@@ -862,8 +864,9 @@ export const commands = {
|
|
|
862
864
|
tailLogs: (functionPath, accountId) => `Waiting for log entries for "${functionPath}" on account "${accountId}".\n`,
|
|
863
865
|
},
|
|
864
866
|
mcp: {
|
|
865
|
-
describe: 'Commands for managing HubSpot MCP servers',
|
|
867
|
+
describe: 'Commands for managing HubSpot MCP servers.',
|
|
866
868
|
setup: {
|
|
869
|
+
describe: 'Setup the HubSpot development MCP servers.',
|
|
867
870
|
installingDocSearch: 'Adding the docs-search mcp server',
|
|
868
871
|
claudeCode: 'Claude Code',
|
|
869
872
|
cursor: 'Cursor',
|
|
@@ -875,6 +878,7 @@ export const commands = {
|
|
|
875
878
|
},
|
|
876
879
|
success: (derivedTargets) => `You can now use the HubSpot CLI MCP Server in ${derivedTargets.join(', ')}. ${chalk.bold('You may need to restart these tools to apply the changes')}.`,
|
|
877
880
|
errors: {
|
|
881
|
+
needsMcpAccess: (accountId) => `You must opt in to the developer MCP beta to use this feature on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('239890', accountId))}`,
|
|
878
882
|
needsNode20: `This feature requires node >=20`,
|
|
879
883
|
errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
|
|
880
884
|
},
|
|
@@ -1009,10 +1013,10 @@ export const commands = {
|
|
|
1009
1013
|
dev: {
|
|
1010
1014
|
describe: 'Start local dev for the current project.',
|
|
1011
1015
|
logs: {
|
|
1012
|
-
|
|
1016
|
+
header: 'HubSpot projects local development',
|
|
1013
1017
|
placeholderAccountSelection: 'Using default account as target account (for now)',
|
|
1014
1018
|
accountTypeInformation: 'Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.',
|
|
1015
|
-
learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://hubspot.
|
|
1019
|
+
learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://developers.hubspot.com/docs/developer-tooling/local-development/hubspot-cli/project-commands#start-a-local-development-server')} | ${uiLink('HubSpot account types', 'https://developers.hubspot.com/docs/getting-started/account-types')}`,
|
|
1016
1020
|
learnMoreMessageLegacy: uiLink('Learn more about the projects local dev server', 'https://developers.hubspot.com/docs/platform/project-cli-commands#start-a-local-development-server'),
|
|
1017
1021
|
profileProjectAccountExplanation: (accountId, profileName) => `Using account ${uiAccountDescription(accountId)} from profile ${chalk.bold(profileName)} for project upload`,
|
|
1018
1022
|
defaultProjectAccountExplanation: (accountId) => `Using default account ${uiAccountDescription(accountId)} for project upload`,
|
|
@@ -1069,7 +1073,7 @@ export const commands = {
|
|
|
1069
1073
|
describe: 'Project name (cannot be changed)',
|
|
1070
1074
|
},
|
|
1071
1075
|
template: {
|
|
1072
|
-
describe: 'The starting template',
|
|
1076
|
+
describe: 'The starting template. Only applies when platform version is less than 2025.2.',
|
|
1073
1077
|
},
|
|
1074
1078
|
templateSource: {
|
|
1075
1079
|
describe: 'Path to custom GitHub repository from which to create project template',
|
|
@@ -1135,6 +1139,7 @@ export const commands = {
|
|
|
1135
1139
|
describe: 'Migrate an existing project to the new version of the projects framework.',
|
|
1136
1140
|
errors: {
|
|
1137
1141
|
noProjectConfig: (command) => `No project detected. Please run this command again from a project directory. If you are trying to migrate an app, run ${command}`,
|
|
1142
|
+
noThemeMigrationAccess: (accountId) => `This project contains a CMS theme. You must opt in to theme migration beta to continue updating it on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('253920', accountId))}`,
|
|
1138
1143
|
},
|
|
1139
1144
|
examples: {
|
|
1140
1145
|
default: 'Migrate an existing project to the new version of the projects framework.',
|
|
@@ -1192,7 +1197,7 @@ export const commands = {
|
|
|
1192
1197
|
maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
|
|
1193
1198
|
authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
|
|
1194
1199
|
distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
|
|
1195
|
-
portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature
|
|
1200
|
+
portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature.`,
|
|
1196
1201
|
locationInProject: 'This command must be run from within a project directory.',
|
|
1197
1202
|
failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
|
|
1198
1203
|
projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
|
|
@@ -2588,8 +2593,13 @@ export const lib = {
|
|
|
2588
2593
|
LocalDevProcess: {
|
|
2589
2594
|
projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${uiCommandReference('hs project dev')}.`,
|
|
2590
2595
|
uploadInitiated: 'Project upload initiated from Local Dev UI.',
|
|
2596
|
+
deployInitiated: 'Project deploy initiated from Local Dev UI.',
|
|
2591
2597
|
uploadFailed: 'Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.',
|
|
2598
|
+
deployFailed: 'Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.',
|
|
2592
2599
|
uploadSuccess: 'Project upload completed successfully. Resuming local dev...',
|
|
2600
|
+
uploadSuccessAutoDeployDisabled: 'Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.',
|
|
2601
|
+
deploySuccess: 'Project deploy completed successfully. Resuming local dev...',
|
|
2602
|
+
noBuildToDeploy: 'Error deploying project. No build was found to deploy.',
|
|
2593
2603
|
},
|
|
2594
2604
|
localDevHelpers: {
|
|
2595
2605
|
project: {
|
|
@@ -2598,7 +2608,7 @@ export const lib = {
|
|
|
2598
2608
|
checking: 'Checking if your deployed build is up to date...',
|
|
2599
2609
|
upToDate: 'Deployed build is up to date.',
|
|
2600
2610
|
notUpToDate: `Your project contains undeployed local changes.`,
|
|
2601
|
-
notUpToDateExplanation: `Run ${uiCommandReference(
|
|
2611
|
+
notUpToDateExplanation: (profile) => `Run ${uiCommandReference(`hs project upload ${profile ? `--profile ${profile}` : ''}`)} to upload these changes to HubSpot, then re-run ${uiCommandReference(`hs project dev ${profile ? `--profile ${profile}` : ''}`)} to continue local development.`,
|
|
2602
2612
|
},
|
|
2603
2613
|
createNewProjectForLocalDev: {
|
|
2604
2614
|
projectMustExistExplanation: (projectName, accountId) => `The project ${projectName} does not exist in the target account ${uiAccountDescription(accountId)}. This command requires the project to exist in the target account.`,
|
|
@@ -3105,6 +3115,7 @@ export const lib = {
|
|
|
3105
3115
|
},
|
|
3106
3116
|
projectAddPrompt: {
|
|
3107
3117
|
selectType: '[--type] Select an app feature to add: ',
|
|
3118
|
+
selectFeatures: '[--features] Select an app feature to add: ',
|
|
3108
3119
|
enterName: '[--name] Give your component a name: ',
|
|
3109
3120
|
errors: {
|
|
3110
3121
|
nameRequired: 'A component name is required',
|
|
@@ -3382,6 +3393,7 @@ export const lib = {
|
|
|
3382
3393
|
sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
|
|
3383
3394
|
projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
|
|
3384
3395
|
projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion 2025.2. This action cannot be undone. To ensure you have access to your original files, they will be copied to a new directory (archive) for safekeeping.\n\nThis command will guide you through the process, prompting you to enter the required fields and will download the new project source code into your project source directory.`, false),
|
|
3396
|
+
exitWithoutMigrating: 'Exiting without migrating',
|
|
3385
3397
|
success: {
|
|
3386
3398
|
downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
|
|
3387
3399
|
themesMigrationSuccess: (platformVersion) => `Successfully migrated project to platformVersion ${chalk.bold(platformVersion)}. Upload your project using ${uiCommandReference('hs project upload')}`,
|
|
@@ -1,35 +1,173 @@
|
|
|
1
1
|
import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
2
|
-
import {
|
|
2
|
+
import { http } from '@hubspot/local-dev-lib/http';
|
|
3
|
+
import { hasFeature, hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
4
|
+
import { FEATURES } from '../constants.js';
|
|
3
5
|
vi.mock('@hubspot/local-dev-lib/api/localDevAuth');
|
|
6
|
+
vi.mock('@hubspot/local-dev-lib/http');
|
|
4
7
|
const mockedFetchEnabledFeatures = fetchEnabledFeatures;
|
|
8
|
+
const mockedHttp = http;
|
|
5
9
|
describe('lib/hasFeature', () => {
|
|
6
10
|
describe('hasFeature()', () => {
|
|
7
11
|
const accountId = 123;
|
|
8
|
-
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
it('should return true if the feature is enabled', async () => {
|
|
9
16
|
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
10
17
|
data: {
|
|
11
18
|
enabledFeatures: {
|
|
12
19
|
'feature-1': true,
|
|
13
|
-
'feature-2': false,
|
|
14
|
-
'feature-3': true,
|
|
15
20
|
},
|
|
16
21
|
},
|
|
17
22
|
});
|
|
18
|
-
});
|
|
19
|
-
it('should return true if the feature is enabled', async () => {
|
|
20
23
|
// @ts-expect-error test data
|
|
21
24
|
const result = await hasFeature(accountId, 'feature-1');
|
|
22
25
|
expect(result).toBe(true);
|
|
23
26
|
});
|
|
24
|
-
it('should return false if the feature is
|
|
27
|
+
it('should return false if the feature is disabled', async () => {
|
|
28
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
29
|
+
data: {
|
|
30
|
+
enabledFeatures: {
|
|
31
|
+
'feature-2': false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
25
35
|
// @ts-expect-error test data
|
|
26
36
|
const result = await hasFeature(accountId, 'feature-2');
|
|
27
37
|
expect(result).toBe(false);
|
|
28
38
|
});
|
|
29
39
|
it('should return false if the feature is not present', async () => {
|
|
40
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
41
|
+
data: {
|
|
42
|
+
enabledFeatures: {},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
30
45
|
// @ts-expect-error test data
|
|
31
46
|
const result = await hasFeature(accountId, 'feature-4');
|
|
32
47
|
expect(result).toBe(false);
|
|
33
48
|
});
|
|
49
|
+
it('should return true for APPS_HOME feature when not present in enabled features (defaults on)', async () => {
|
|
50
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
51
|
+
data: {
|
|
52
|
+
enabledFeatures: {},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const result = await hasFeature(accountId, FEATURES.APPS_HOME);
|
|
56
|
+
expect(result).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('should respect explicit setting for APPS_HOME feature even when it defaults on', async () => {
|
|
59
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
60
|
+
data: {
|
|
61
|
+
enabledFeatures: {
|
|
62
|
+
[FEATURES.APPS_HOME]: false,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const result = await hasFeature(accountId, FEATURES.APPS_HOME);
|
|
67
|
+
expect(result).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('should handle truthy values correctly', async () => {
|
|
70
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
71
|
+
data: {
|
|
72
|
+
enabledFeatures: {
|
|
73
|
+
'feature-truthy': 'yes',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// @ts-expect-error test data
|
|
78
|
+
const truthyResult = await hasFeature(accountId, 'feature-truthy');
|
|
79
|
+
expect(truthyResult).toBe(true);
|
|
80
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
81
|
+
data: {
|
|
82
|
+
enabledFeatures: {
|
|
83
|
+
'feature-number': 1,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// @ts-expect-error test data
|
|
88
|
+
const numberResult = await hasFeature(accountId, 'feature-number');
|
|
89
|
+
expect(numberResult).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it('should handle falsy values correctly', async () => {
|
|
92
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
93
|
+
data: {
|
|
94
|
+
enabledFeatures: {
|
|
95
|
+
'feature-null': null,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
// @ts-expect-error test data
|
|
100
|
+
const nullResult = await hasFeature(accountId, 'feature-null');
|
|
101
|
+
expect(nullResult).toBe(false);
|
|
102
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
103
|
+
data: {
|
|
104
|
+
enabledFeatures: {
|
|
105
|
+
'feature-zero': 0,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// @ts-expect-error test data
|
|
110
|
+
const zeroResult = await hasFeature(accountId, 'feature-zero');
|
|
111
|
+
expect(zeroResult).toBe(false);
|
|
112
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
113
|
+
data: {
|
|
114
|
+
enabledFeatures: {
|
|
115
|
+
'feature-empty': '',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
// @ts-expect-error test data
|
|
120
|
+
const emptyResult = await hasFeature(accountId, 'feature-empty');
|
|
121
|
+
expect(emptyResult).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
it('should propagate errors from fetchEnabledFeatures', async () => {
|
|
124
|
+
const error = new Error('API error');
|
|
125
|
+
mockedFetchEnabledFeatures.mockRejectedValueOnce(error);
|
|
126
|
+
await expect(hasFeature(accountId, FEATURES.UNIFIED_APPS)).rejects.toThrow('API error');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('hasUnfiedAppsAccess()', () => {
|
|
130
|
+
const accountId = 123;
|
|
131
|
+
afterEach(() => {
|
|
132
|
+
vi.clearAllMocks();
|
|
133
|
+
});
|
|
134
|
+
it('should return true when API returns true', async () => {
|
|
135
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
136
|
+
mockedHttp.get.mockResolvedValueOnce({ data: true });
|
|
137
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
138
|
+
expect(result).toBe(true);
|
|
139
|
+
expect(mockedHttp.get).toHaveBeenCalledWith(accountId, {
|
|
140
|
+
url: 'developer-tooling/external/developer-portal/has-unified-dev-platform-access',
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
it('should return false when API returns false', async () => {
|
|
144
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
145
|
+
mockedHttp.get.mockResolvedValueOnce({ data: false });
|
|
146
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
147
|
+
expect(result).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
it('should handle truthy values correctly', async () => {
|
|
150
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
151
|
+
mockedHttp.get.mockResolvedValueOnce({ data: 'yes' });
|
|
152
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
153
|
+
expect(result).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('should handle falsy values correctly', async () => {
|
|
156
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
157
|
+
mockedHttp.get.mockResolvedValueOnce({ data: null });
|
|
158
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
159
|
+
expect(result).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
it('should handle undefined response data', async () => {
|
|
162
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
163
|
+
mockedHttp.get.mockResolvedValueOnce({ data: undefined });
|
|
164
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
165
|
+
expect(result).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
it('should propagate errors from http.get', async () => {
|
|
168
|
+
const error = new Error('Network error');
|
|
169
|
+
mockedHttp.get.mockRejectedValueOnce(error);
|
|
170
|
+
await expect(hasUnfiedAppsAccess(accountId)).rejects.toThrow('Network error');
|
|
171
|
+
});
|
|
34
172
|
});
|
|
35
173
|
});
|
|
@@ -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/platformVersion.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,35 +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
|
-
let migrated = false;
|
|
240
|
-
let failureReason;
|
|
241
|
-
try {
|
|
242
|
-
const migrationResult = await migrateThemes(projectConfig.projectDir, projectSrcDir);
|
|
243
|
-
migrated = migrationResult.migrated;
|
|
244
|
-
failureReason = migrationResult.failureReason;
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
debugError(error);
|
|
248
|
-
throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
|
|
249
|
-
}
|
|
250
|
-
if (!migrated) {
|
|
251
|
-
throw new Error(failureReason || lib.migrate.errors.project.failedToMigrateThemes);
|
|
252
|
-
}
|
|
253
|
-
const newProjectConfig = { ...projectConfig.projectConfig };
|
|
254
|
-
newProjectConfig.platformVersion = platformVersion;
|
|
255
|
-
const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
|
|
256
|
-
const success = writeProjectConfig(projectConfigPath, newProjectConfig);
|
|
257
|
-
if (!success) {
|
|
258
|
-
throw new Error(lib.migrate.errors.project.failedToUpdateProjectConfig);
|
|
259
|
-
}
|
|
260
|
-
uiLogger.log('');
|
|
261
|
-
uiLogger.log(lib.migrate.success.themesMigrationSuccess(platformVersion));
|
|
262
|
-
}
|
|
263
192
|
export async function beginAppMigration(derivedAccountId, appId, platformVersion) {
|
|
264
193
|
SpinniesManager.add('beginningMigration', {
|
|
265
194
|
text: lib.migrate.spinners.beginningMigration,
|
|
@@ -412,11 +341,7 @@ export async function migrateApp2025_2(derivedAccountId, options, projectConfig)
|
|
|
412
341
|
throw new Error(lib.migrate.errors.project.doesNotExist(derivedAccountId));
|
|
413
342
|
}
|
|
414
343
|
}
|
|
415
|
-
const { appIdToMigrate, projectName, projectDest
|
|
416
|
-
if (isThemesMigration) {
|
|
417
|
-
await handleThemesMigration(projectConfig, options.platformVersion);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
344
|
+
const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
|
|
420
345
|
if (!appIdToMigrate || !projectName || !projectDest) {
|
|
421
346
|
return;
|
|
422
347
|
}
|
package/lib/constants.d.ts
CHANGED
|
@@ -81,17 +81,25 @@ export declare const FEATURES: {
|
|
|
81
81
|
readonly SANDBOXES_V2: "sandboxes:v2:enabled";
|
|
82
82
|
readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
|
|
83
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";
|
|
87
|
+
readonly AGENT_TOOLS: "ThirdPartyAgentTools";
|
|
84
88
|
};
|
|
85
89
|
export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
|
|
86
90
|
UPLOAD_SUCCESS: string;
|
|
87
91
|
UPLOAD_FAILURE: string;
|
|
92
|
+
DEPLOY_SUCCESS: string;
|
|
93
|
+
DEPLOY_FAILURE: string;
|
|
88
94
|
UPDATE_PROJECT_NODES: string;
|
|
89
95
|
UPDATE_APP_DATA: string;
|
|
90
96
|
UPDATE_PROJECT_DATA: string;
|
|
91
97
|
UPDATE_UPLOAD_WARNINGS: string;
|
|
98
|
+
CLI_METADATA: string;
|
|
92
99
|
};
|
|
93
100
|
export declare const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES: {
|
|
94
101
|
UPLOAD: string;
|
|
102
|
+
DEPLOY: string;
|
|
95
103
|
VIEWED_WELCOME_SCREEN: string;
|
|
96
104
|
};
|
|
97
105
|
export declare const APP_INSTALLATION_STATES: {
|