@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,4 +1,4 @@
|
|
|
1
|
-
import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
1
|
+
import { IntermediateRepresentationNodeLocalDev, HSProfileVariables } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
2
2
|
import { Environment } from '@hubspot/local-dev-lib/types/Config';
|
|
3
3
|
import { ProjectConfig } from '../../../types/Projects.js';
|
|
4
4
|
import { LocalDevStateConstructorOptions, LocalDevStateListener, AppLocalDevData, LocalDevServerMessage } from '../../../types/LocalDev.js';
|
|
@@ -12,13 +12,14 @@ declare class LocalDevState {
|
|
|
12
12
|
private _projectName;
|
|
13
13
|
private _debug;
|
|
14
14
|
private _projectNodes;
|
|
15
|
+
private _projectProfileData;
|
|
15
16
|
private _projectNodesAtLastUpload;
|
|
16
17
|
private _env;
|
|
17
18
|
private _listeners;
|
|
18
19
|
private _appData;
|
|
19
20
|
private _devServerMessage;
|
|
20
21
|
private _uploadWarnings;
|
|
21
|
-
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, initialProjectNodes, profile, env, }: LocalDevStateConstructorOptions);
|
|
22
|
+
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, initialProjectNodes, initialProjectProfileData, profile, env, }: LocalDevStateConstructorOptions);
|
|
22
23
|
private runListeners;
|
|
23
24
|
get targetProjectAccountId(): number;
|
|
24
25
|
get targetTestingAccountId(): number;
|
|
@@ -34,6 +35,8 @@ declare class LocalDevState {
|
|
|
34
35
|
set projectNodes(nodes: {
|
|
35
36
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
36
37
|
});
|
|
38
|
+
get projectProfileData(): HSProfileVariables;
|
|
39
|
+
set projectProfileData(profileData: HSProfileVariables);
|
|
37
40
|
get projectNodesAtLastUpload(): {
|
|
38
41
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
39
42
|
};
|
|
@@ -9,13 +9,14 @@ class LocalDevState {
|
|
|
9
9
|
_projectName;
|
|
10
10
|
_debug;
|
|
11
11
|
_projectNodes;
|
|
12
|
+
_projectProfileData;
|
|
12
13
|
_projectNodesAtLastUpload;
|
|
13
14
|
_env;
|
|
14
15
|
_listeners;
|
|
15
16
|
_appData;
|
|
16
17
|
_devServerMessage;
|
|
17
18
|
_uploadWarnings;
|
|
18
|
-
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, initialProjectNodes, profile, env, }) {
|
|
19
|
+
constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, initialProjectNodes, initialProjectProfileData, profile, env, }) {
|
|
19
20
|
this._targetProjectAccountId = targetProjectAccountId;
|
|
20
21
|
this._targetTestingAccountId = targetTestingAccountId;
|
|
21
22
|
this._profile = profile;
|
|
@@ -26,6 +27,7 @@ class LocalDevState {
|
|
|
26
27
|
this._debug = debug || false;
|
|
27
28
|
this._projectNodes = initialProjectNodes;
|
|
28
29
|
this._projectNodesAtLastUpload = initialProjectNodes;
|
|
30
|
+
this._projectProfileData = initialProjectProfileData;
|
|
29
31
|
this._env = env;
|
|
30
32
|
this._appData = {};
|
|
31
33
|
this._devServerMessage = LOCAL_DEV_SERVER_MESSAGE_TYPES.INITIAL;
|
|
@@ -68,6 +70,12 @@ class LocalDevState {
|
|
|
68
70
|
this._projectNodes = nodes;
|
|
69
71
|
this.runListeners('projectNodes');
|
|
70
72
|
}
|
|
73
|
+
get projectProfileData() {
|
|
74
|
+
return structuredClone(this._projectProfileData);
|
|
75
|
+
}
|
|
76
|
+
set projectProfileData(profileData) {
|
|
77
|
+
this._projectProfileData = profileData;
|
|
78
|
+
}
|
|
71
79
|
get projectNodesAtLastUpload() {
|
|
72
80
|
return structuredClone(this._projectNodesAtLastUpload);
|
|
73
81
|
}
|
|
@@ -6,7 +6,7 @@ export declare function createNewProjectForLocalDev(projectConfig: ProjectConfig
|
|
|
6
6
|
export declare function createInitialBuildForNewProject(projectConfig: ProjectConfig, projectDir: string, targetAccountId: number, sendIR?: boolean, profile?: string): Promise<Build>;
|
|
7
7
|
export declare function compareLocalProjectToDeployed(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number | undefined, localProjectNodes: {
|
|
8
8
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
9
|
-
}): Promise<void>;
|
|
9
|
+
}, profile?: string): Promise<void>;
|
|
10
10
|
export declare function isDeployedProjectUpToDateWithLocal(projectConfig: ProjectConfig, accountId: number, deployedBuildId: number, localProjectNodes: {
|
|
11
11
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
12
|
-
}): Promise<boolean>;
|
|
12
|
+
}, profile?: string): Promise<boolean>;
|
|
@@ -17,7 +17,7 @@ import { uiAccountDescription } from '../../../ui/index.js';
|
|
|
17
17
|
import SpinniesManager from '../../../ui/SpinniesManager.js';
|
|
18
18
|
import { EXIT_CODES } from '../../../enums/exitCodes.js';
|
|
19
19
|
import { handleProjectUpload } from '../../upload.js';
|
|
20
|
-
import { pollProjectBuildAndDeploy } from '../../
|
|
20
|
+
import { pollProjectBuildAndDeploy } from '../../pollProjectBuildAndDeploy.js';
|
|
21
21
|
import { logError } from '../../../errorHandlers/index.js';
|
|
22
22
|
import { ApiErrorContext } from '../../../errorHandlers/index.js';
|
|
23
23
|
// Prompt the user to create a new project if one doesn't exist on their target account
|
|
@@ -123,7 +123,7 @@ export async function createInitialBuildForNewProject(projectConfig, projectDir,
|
|
|
123
123
|
}
|
|
124
124
|
return initialUploadResult.buildResult;
|
|
125
125
|
}
|
|
126
|
-
export async function compareLocalProjectToDeployed(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
126
|
+
export async function compareLocalProjectToDeployed(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
|
|
127
127
|
uiLogger.log('');
|
|
128
128
|
if (!deployedBuildId) {
|
|
129
129
|
uiLogger.error(lib.localDevHelpers.project.compareLocalProjectToDeployed.noDeployedBuild(projectConfig.name, uiAccountDescription(accountId)));
|
|
@@ -132,12 +132,11 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
|
|
|
132
132
|
SpinniesManager.add('compareLocalProjectToDeployed', {
|
|
133
133
|
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.checking,
|
|
134
134
|
});
|
|
135
|
-
const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes);
|
|
135
|
+
const isUpToDate = await isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile);
|
|
136
136
|
if (isUpToDate) {
|
|
137
137
|
SpinniesManager.succeed('compareLocalProjectToDeployed', {
|
|
138
138
|
text: lib.localDevHelpers.project.compareLocalProjectToDeployed.upToDate,
|
|
139
139
|
});
|
|
140
|
-
uiLogger.log('');
|
|
141
140
|
}
|
|
142
141
|
else {
|
|
143
142
|
SpinniesManager.fail('compareLocalProjectToDeployed', {
|
|
@@ -145,12 +144,11 @@ export async function compareLocalProjectToDeployed(projectConfig, accountId, de
|
|
|
145
144
|
.notUpToDate,
|
|
146
145
|
});
|
|
147
146
|
uiLogger.log('');
|
|
148
|
-
uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed
|
|
149
|
-
.notUpToDateExplanation);
|
|
147
|
+
uiLogger.log(lib.localDevHelpers.project.compareLocalProjectToDeployed.notUpToDateExplanation(profile));
|
|
150
148
|
process.exit(EXIT_CODES.SUCCESS);
|
|
151
149
|
}
|
|
152
150
|
}
|
|
153
|
-
export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes) {
|
|
151
|
+
export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountId, deployedBuildId, localProjectNodes, profile) {
|
|
154
152
|
let tempDir = null;
|
|
155
153
|
try {
|
|
156
154
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hubspot-project-compare-'));
|
|
@@ -162,7 +160,7 @@ export async function isDeployedProjectUpToDateWithLocal(projectConfig, accountI
|
|
|
162
160
|
projectSourceDir: deployedProjectSourceDir,
|
|
163
161
|
platformVersion: projectConfig.platformVersion,
|
|
164
162
|
accountId: accountId,
|
|
165
|
-
}, {});
|
|
163
|
+
}, { profile });
|
|
166
164
|
return isDeepEqual(localProjectNodes, deployedProjectNodes, ['localDev']);
|
|
167
165
|
}
|
|
168
166
|
finally {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useV3Api(platformVersion?: string | null): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function useV3Api(platformVersion) {
|
|
2
|
+
if (!platformVersion || typeof platformVersion !== 'string') {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (platformVersion.toLowerCase() === 'unstable') {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
const [year, minor] = platformVersion.split('.');
|
|
9
|
+
return Number(year) >= 2025 && Number(minor) >= 2;
|
|
10
|
+
}
|
|
@@ -2,7 +2,6 @@ import { FileResult } from 'tmp';
|
|
|
2
2
|
import { Build } from '@hubspot/local-dev-lib/types/Build';
|
|
3
3
|
import { Deploy } from '@hubspot/local-dev-lib/types/Deploy';
|
|
4
4
|
import { ProjectConfig, ProjectTask, ProjectPollResult } from '../../types/Projects.js';
|
|
5
|
-
export declare function useV3Api(platformVersion?: string | null): boolean;
|
|
6
5
|
type PollTaskStatusFunction<T extends ProjectTask> = (accountId: number, taskName: string, taskId: number, deployedBuildId: number | null, silenceLogs?: boolean) => Promise<T>;
|
|
7
6
|
export declare const pollBuildStatus: PollTaskStatusFunction<Build>;
|
|
8
7
|
export declare const pollDeployStatus: PollTaskStatusFunction<Deploy>;
|
|
@@ -13,16 +13,6 @@ import { mapToInternalType } from '@hubspot/project-parsing-lib';
|
|
|
13
13
|
const SPINNER_STATUS = {
|
|
14
14
|
SPINNING: 'spinning',
|
|
15
15
|
};
|
|
16
|
-
export function useV3Api(platformVersion) {
|
|
17
|
-
if (!platformVersion || typeof platformVersion !== 'string') {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
if (platformVersion.toLowerCase() === 'unstable') {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
const [year, minor] = platformVersion.split('.');
|
|
24
|
-
return Number(year) >= 2025 && Number(minor) >= 2;
|
|
25
|
-
}
|
|
26
16
|
function getSubtasks(task) {
|
|
27
17
|
if ('subbuildStatuses' in task) {
|
|
28
18
|
return task.subbuildStatuses;
|
package/lib/projects/upload.js
CHANGED
|
@@ -12,7 +12,7 @@ import util from 'node:util';
|
|
|
12
12
|
import { lib } from '../../lang/en.js';
|
|
13
13
|
import { ensureProjectExists } from './ensureProjectExists.js';
|
|
14
14
|
import { uiLogger } from '../ui/logger.js';
|
|
15
|
-
import { useV3Api } from './
|
|
15
|
+
import { useV3Api } from './platformVersion.js';
|
|
16
16
|
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
17
17
|
import ProjectValidationError from '../errors/ProjectValidationError.js';
|
|
18
18
|
async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
|
package/lib/projects/urls.d.ts
CHANGED
|
@@ -7,3 +7,4 @@ export declare function getProjectBuildDetailUrl(projectName: string, buildId: n
|
|
|
7
7
|
export declare function getProjectDeployDetailUrl(projectName: string, deployId: number, accountId: number): string;
|
|
8
8
|
export declare function getLocalDevUiUrl(accountId: number, showWelcomeScreen?: boolean): string;
|
|
9
9
|
export declare function getAccountHomeUrl(accountId: number): string;
|
|
10
|
+
export declare function getAppAllowlistUrl(accountId: number, projectName: string, appUid: string): string;
|
package/lib/projects/urls.js
CHANGED
|
@@ -41,3 +41,6 @@ export function getAccountHomeUrl(accountId) {
|
|
|
41
41
|
const baseUrl = getHubSpotWebsiteOrigin(getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
|
|
42
42
|
return `${baseUrl}/home?portalId=${accountId}`;
|
|
43
43
|
}
|
|
44
|
+
export function getAppAllowlistUrl(accountId, projectName, appUid) {
|
|
45
|
+
return `${getProjectHomeUrl(accountId)}/project/${projectName}/component/${appUid}/distribution?panel=static-token-allowlist`;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Separator } from '@inquirer/prompts';
|
|
2
|
+
import { projectAddPromptV3 } from '../projectAddPrompt.js';
|
|
3
|
+
import { promptUser } from '../promptUtils.js';
|
|
4
|
+
vi.mock('../promptUtils');
|
|
5
|
+
const mockedPromptUser = vi.mocked(promptUser);
|
|
6
|
+
describe('lib/prompts/projectAddPrompt', () => {
|
|
7
|
+
const mockComponentTemplate = {
|
|
8
|
+
label: 'Test Module',
|
|
9
|
+
path: 'test-module',
|
|
10
|
+
type: 'module',
|
|
11
|
+
supportedAuthTypes: ['oauth'],
|
|
12
|
+
supportedDistributions: ['private'],
|
|
13
|
+
};
|
|
14
|
+
const mockComponentTemplateWithCliSelector = {
|
|
15
|
+
label: 'Workflow Action Tool',
|
|
16
|
+
path: 'workflow-action-tool',
|
|
17
|
+
type: 'workflow-action',
|
|
18
|
+
cliSelector: 'workflow-action-tool',
|
|
19
|
+
supportedAuthTypes: ['oauth'],
|
|
20
|
+
supportedDistributions: ['private'],
|
|
21
|
+
};
|
|
22
|
+
describe('projectAddPromptV3()', () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Mock returns empty result, logic will use selectedComponents when selectedFeatures provided
|
|
25
|
+
mockedPromptUser.mockResolvedValue({});
|
|
26
|
+
});
|
|
27
|
+
it('should select component based on cliSelector when provided', async () => {
|
|
28
|
+
const templateChoice = {
|
|
29
|
+
name: 'Workflow Action Tool',
|
|
30
|
+
value: mockComponentTemplateWithCliSelector,
|
|
31
|
+
};
|
|
32
|
+
const components = [templateChoice];
|
|
33
|
+
const selectedFeatures = ['workflow-action-tool'];
|
|
34
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
35
|
+
expect(result.componentTemplate).toEqual([
|
|
36
|
+
mockComponentTemplateWithCliSelector,
|
|
37
|
+
]);
|
|
38
|
+
expect(mockedPromptUser).toHaveBeenCalledWith([
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
name: 'componentTemplate',
|
|
41
|
+
when: false, // selectedFeatures provided, so skip prompt
|
|
42
|
+
}),
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
it('should select component based on type when cliSelector not provided', async () => {
|
|
46
|
+
const templateChoice = {
|
|
47
|
+
name: 'Test Module',
|
|
48
|
+
value: mockComponentTemplate,
|
|
49
|
+
};
|
|
50
|
+
const components = [templateChoice];
|
|
51
|
+
const selectedFeatures = ['module'];
|
|
52
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
53
|
+
expect(result.componentTemplate).toEqual([mockComponentTemplate]);
|
|
54
|
+
expect(mockedPromptUser).toHaveBeenCalledWith([
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
name: 'componentTemplate',
|
|
57
|
+
when: false, // selectedFeatures provided and selectedComponents found
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
it('should prefer cliSelector over type when both are available', async () => {
|
|
62
|
+
const templateChoice = {
|
|
63
|
+
name: 'Workflow Action Tool',
|
|
64
|
+
value: mockComponentTemplateWithCliSelector,
|
|
65
|
+
};
|
|
66
|
+
const components = [templateChoice];
|
|
67
|
+
const selectedFeatures = ['workflow-action-tool']; // matches cliSelector
|
|
68
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
69
|
+
expect(result.componentTemplate).toEqual([
|
|
70
|
+
mockComponentTemplateWithCliSelector,
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
it('should not select component when neither cliSelector nor type matches', async () => {
|
|
74
|
+
const templateChoice = {
|
|
75
|
+
name: 'Test Module',
|
|
76
|
+
value: mockComponentTemplate,
|
|
77
|
+
};
|
|
78
|
+
const components = [templateChoice];
|
|
79
|
+
const selectedFeatures = ['non-matching-feature'];
|
|
80
|
+
mockedPromptUser.mockResolvedValue({ componentTemplate: [] });
|
|
81
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
82
|
+
expect(result.componentTemplate).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
it('should throw error when selected feature component is disabled', async () => {
|
|
85
|
+
const disabledTemplateChoice = {
|
|
86
|
+
name: 'Disabled Component',
|
|
87
|
+
value: mockComponentTemplateWithCliSelector,
|
|
88
|
+
disabled: 'Component is disabled for testing',
|
|
89
|
+
};
|
|
90
|
+
const components = [disabledTemplateChoice];
|
|
91
|
+
const selectedFeatures = ['workflow-action-tool'];
|
|
92
|
+
await expect(projectAddPromptV3(components, selectedFeatures)).rejects.toThrow(/Cannot.*feature.*workflow-action/);
|
|
93
|
+
});
|
|
94
|
+
it('should handle multiple components with mixed cliSelector availability', async () => {
|
|
95
|
+
const choice1 = {
|
|
96
|
+
name: 'Test Module',
|
|
97
|
+
value: mockComponentTemplate,
|
|
98
|
+
};
|
|
99
|
+
const choice2 = {
|
|
100
|
+
name: 'Workflow Action Tool',
|
|
101
|
+
value: mockComponentTemplateWithCliSelector,
|
|
102
|
+
};
|
|
103
|
+
const components = [choice1, choice2];
|
|
104
|
+
const selectedFeatures = ['module', 'workflow-action-tool'];
|
|
105
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
106
|
+
expect(result.componentTemplate).toEqual([
|
|
107
|
+
mockComponentTemplate,
|
|
108
|
+
mockComponentTemplateWithCliSelector,
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
it('should skip Separator instances when processing components', async () => {
|
|
112
|
+
const separator = new Separator();
|
|
113
|
+
const templateChoice = {
|
|
114
|
+
name: 'Test Module',
|
|
115
|
+
value: mockComponentTemplate,
|
|
116
|
+
};
|
|
117
|
+
const components = [separator, templateChoice];
|
|
118
|
+
const selectedFeatures = ['module'];
|
|
119
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
120
|
+
expect(result.componentTemplate).toEqual([mockComponentTemplate]);
|
|
121
|
+
});
|
|
122
|
+
it('should prompt user when no selectedFeatures provided', async () => {
|
|
123
|
+
const templateChoice = {
|
|
124
|
+
name: 'Test Module',
|
|
125
|
+
value: mockComponentTemplate,
|
|
126
|
+
};
|
|
127
|
+
const components = [templateChoice];
|
|
128
|
+
const selectedFeatures = undefined;
|
|
129
|
+
mockedPromptUser.mockResolvedValue({
|
|
130
|
+
componentTemplate: [mockComponentTemplate],
|
|
131
|
+
});
|
|
132
|
+
const result = await projectAddPromptV3(components, selectedFeatures);
|
|
133
|
+
expect(mockedPromptUser).toHaveBeenCalledWith([
|
|
134
|
+
expect.objectContaining({
|
|
135
|
+
name: 'componentTemplate',
|
|
136
|
+
type: 'checkbox',
|
|
137
|
+
choices: components,
|
|
138
|
+
}),
|
|
139
|
+
]);
|
|
140
|
+
expect(result.componentTemplate).toEqual([mockComponentTemplate]);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Separator } from '@inquirer/prompts';
|
|
2
|
+
import { selectProjectTemplatePrompt } from '../selectProjectTemplatePrompt.js';
|
|
3
|
+
import { promptUser } from '../promptUtils.js';
|
|
4
|
+
vi.mock('../promptUtils');
|
|
5
|
+
const mockedPromptUser = vi.mocked(promptUser);
|
|
6
|
+
describe('lib/prompts/selectProjectTemplatePrompt', () => {
|
|
7
|
+
const mockComponentTemplate = {
|
|
8
|
+
label: 'Test Module',
|
|
9
|
+
path: 'test-module',
|
|
10
|
+
type: 'module',
|
|
11
|
+
supportedAuthTypes: ['oauth'],
|
|
12
|
+
supportedDistributions: ['private'],
|
|
13
|
+
};
|
|
14
|
+
const mockComponentTemplateWithCliSelector = {
|
|
15
|
+
label: 'Workflow Action Tool',
|
|
16
|
+
path: 'workflow-action-tool',
|
|
17
|
+
type: 'workflow-action',
|
|
18
|
+
cliSelector: 'workflow-action-tool',
|
|
19
|
+
supportedAuthTypes: ['oauth'],
|
|
20
|
+
supportedDistributions: ['private'],
|
|
21
|
+
};
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockedPromptUser.mockResolvedValue({});
|
|
24
|
+
});
|
|
25
|
+
describe('selectProjectTemplatePrompt with component templates', () => {
|
|
26
|
+
it('should select component based on cliSelector when provided', async () => {
|
|
27
|
+
const templateChoice = {
|
|
28
|
+
name: 'Workflow Action Tool',
|
|
29
|
+
value: mockComponentTemplateWithCliSelector,
|
|
30
|
+
};
|
|
31
|
+
const componentTemplates = [templateChoice];
|
|
32
|
+
const promptOptions = {
|
|
33
|
+
features: ['workflow-action-tool'],
|
|
34
|
+
};
|
|
35
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
36
|
+
expect(result.componentTemplates).toEqual([
|
|
37
|
+
mockComponentTemplateWithCliSelector,
|
|
38
|
+
]);
|
|
39
|
+
expect(mockedPromptUser).toHaveBeenCalledWith([
|
|
40
|
+
expect.objectContaining({ name: 'projectTemplate' }),
|
|
41
|
+
expect.objectContaining({ name: 'componentTemplates' }),
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
it('should select component based on type when cliSelector not provided', async () => {
|
|
45
|
+
const templateChoice = {
|
|
46
|
+
name: 'Test Module',
|
|
47
|
+
value: mockComponentTemplate,
|
|
48
|
+
};
|
|
49
|
+
const componentTemplates = [templateChoice];
|
|
50
|
+
const promptOptions = {
|
|
51
|
+
features: ['module'],
|
|
52
|
+
};
|
|
53
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
54
|
+
expect(result.componentTemplates).toEqual([mockComponentTemplate]);
|
|
55
|
+
});
|
|
56
|
+
it('should prefer cliSelector over type when both are available', async () => {
|
|
57
|
+
const templateChoice = {
|
|
58
|
+
name: 'Workflow Action Tool',
|
|
59
|
+
value: mockComponentTemplateWithCliSelector,
|
|
60
|
+
};
|
|
61
|
+
const componentTemplates = [templateChoice];
|
|
62
|
+
const promptOptions = {
|
|
63
|
+
features: ['workflow-action-tool'], // matches cliSelector, not type
|
|
64
|
+
};
|
|
65
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
66
|
+
expect(result.componentTemplates).toEqual([
|
|
67
|
+
mockComponentTemplateWithCliSelector,
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
it('should not select component when neither cliSelector nor type matches', async () => {
|
|
71
|
+
const templateChoice = {
|
|
72
|
+
name: 'Test Module',
|
|
73
|
+
value: mockComponentTemplate,
|
|
74
|
+
};
|
|
75
|
+
const componentTemplates = [templateChoice];
|
|
76
|
+
const promptOptions = {
|
|
77
|
+
features: ['non-matching-feature'],
|
|
78
|
+
};
|
|
79
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
80
|
+
expect(result.componentTemplates).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
it('should throw error when selected feature component is disabled', async () => {
|
|
83
|
+
const disabledTemplateChoice = {
|
|
84
|
+
name: 'Disabled Component',
|
|
85
|
+
value: mockComponentTemplateWithCliSelector,
|
|
86
|
+
disabled: 'Component is disabled for testing',
|
|
87
|
+
};
|
|
88
|
+
const componentTemplates = [disabledTemplateChoice];
|
|
89
|
+
const promptOptions = {
|
|
90
|
+
features: ['workflow-action-tool'],
|
|
91
|
+
};
|
|
92
|
+
await expect(selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates)).rejects.toThrow(/Cannot create project with template.*workflow-action/);
|
|
93
|
+
});
|
|
94
|
+
it('should handle multiple components with mixed cliSelector availability', async () => {
|
|
95
|
+
const choice1 = {
|
|
96
|
+
name: 'Test Module',
|
|
97
|
+
value: mockComponentTemplate,
|
|
98
|
+
};
|
|
99
|
+
const choice2 = {
|
|
100
|
+
name: 'Workflow Action Tool',
|
|
101
|
+
value: mockComponentTemplateWithCliSelector,
|
|
102
|
+
};
|
|
103
|
+
const componentTemplates = [choice1, choice2];
|
|
104
|
+
const promptOptions = {
|
|
105
|
+
features: ['module', 'workflow-action-tool'],
|
|
106
|
+
};
|
|
107
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
108
|
+
expect(result.componentTemplates).toEqual([
|
|
109
|
+
mockComponentTemplate,
|
|
110
|
+
mockComponentTemplateWithCliSelector,
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
it('should skip Separator instances when processing components', async () => {
|
|
114
|
+
const separator = new Separator();
|
|
115
|
+
const templateChoice = {
|
|
116
|
+
name: 'Test Module',
|
|
117
|
+
value: mockComponentTemplate,
|
|
118
|
+
};
|
|
119
|
+
const componentTemplates = [separator, templateChoice];
|
|
120
|
+
const promptOptions = {
|
|
121
|
+
features: ['module'],
|
|
122
|
+
};
|
|
123
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
124
|
+
expect(result.componentTemplates).toEqual([mockComponentTemplate]);
|
|
125
|
+
});
|
|
126
|
+
it('should prompt user when no features provided', async () => {
|
|
127
|
+
const templateChoice = {
|
|
128
|
+
name: 'Test Module',
|
|
129
|
+
value: mockComponentTemplate,
|
|
130
|
+
};
|
|
131
|
+
const componentTemplates = [templateChoice];
|
|
132
|
+
const promptOptions = {};
|
|
133
|
+
mockedPromptUser.mockResolvedValue({
|
|
134
|
+
componentTemplates: [mockComponentTemplate],
|
|
135
|
+
});
|
|
136
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
137
|
+
expect(mockedPromptUser).toHaveBeenCalledWith([
|
|
138
|
+
expect.objectContaining({ name: 'projectTemplate' }),
|
|
139
|
+
expect.objectContaining({
|
|
140
|
+
name: 'componentTemplates',
|
|
141
|
+
type: 'checkbox',
|
|
142
|
+
choices: componentTemplates,
|
|
143
|
+
}),
|
|
144
|
+
]);
|
|
145
|
+
expect(result.componentTemplates).toEqual([mockComponentTemplate]);
|
|
146
|
+
});
|
|
147
|
+
it('should handle empty componentTemplates selection', async () => {
|
|
148
|
+
const templateChoice = {
|
|
149
|
+
name: 'Test Module',
|
|
150
|
+
value: mockComponentTemplate,
|
|
151
|
+
};
|
|
152
|
+
const componentTemplates = [templateChoice];
|
|
153
|
+
const promptOptions = {
|
|
154
|
+
features: ['non-matching-feature'],
|
|
155
|
+
};
|
|
156
|
+
const result = await selectProjectTemplatePrompt(promptOptions, undefined, componentTemplates);
|
|
157
|
+
expect(result.componentTemplates).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -101,6 +101,7 @@ export async function createDeveloperTestAccountConfigPrompt(args = {}, supportF
|
|
|
101
101
|
type: 'checkbox',
|
|
102
102
|
pageSize: 13,
|
|
103
103
|
choices: TEST_ACCOUNT_TIERS,
|
|
104
|
+
loop: false,
|
|
104
105
|
validate: choices => {
|
|
105
106
|
if (choices?.length < Object.keys(hubs).length) {
|
|
106
107
|
return lib.prompts.createDeveloperTestAccountConfigPrompt.errors
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { validateImportRequestFile } from '@hubspot/local-dev-lib/crm';
|
|
2
2
|
import { promptUser } from './promptUtils.js';
|
|
3
3
|
import { lib } from '../../lang/en.js';
|
|
4
|
-
import { logError } from '../errorHandlers/index.js';
|
|
5
4
|
import { uiLogger } from '../ui/logger.js';
|
|
5
|
+
import { isErrorWithMessageOrReason } from '../errorHandlers/index.js';
|
|
6
6
|
export async function importDataFilePathPrompt() {
|
|
7
7
|
uiLogger.log(lib.prompts.importDataFilePathPrompt.promptContext);
|
|
8
8
|
const { filePath } = await promptUser({
|
|
@@ -15,7 +15,9 @@ export async function importDataFilePathPrompt() {
|
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
17
|
catch (error) {
|
|
18
|
-
|
|
18
|
+
if (isErrorWithMessageOrReason(error) && error.message) {
|
|
19
|
+
return error.message;
|
|
20
|
+
}
|
|
19
21
|
return false;
|
|
20
22
|
}
|
|
21
23
|
},
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
export declare function installAppBrowserPrompt(installUrl: string, isReinstall?: boolean
|
|
1
|
+
export declare function installAppBrowserPrompt(installUrl: string, isReinstall?: boolean, staticAuthInstallOptions?: {
|
|
2
|
+
testingAccountId: number;
|
|
3
|
+
projectAccountId: number;
|
|
4
|
+
projectName: string;
|
|
5
|
+
appUid: string;
|
|
6
|
+
}): Promise<void>;
|
|
2
7
|
export declare function installAppAutoPrompt(): Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ import { promptUser } from './promptUtils.js';
|
|
|
3
3
|
import { EXIT_CODES } from '../enums/exitCodes.js';
|
|
4
4
|
import { lib } from '../../lang/en.js';
|
|
5
5
|
import { uiLogger } from '../ui/logger.js';
|
|
6
|
-
export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
|
|
6
|
+
export async function installAppBrowserPrompt(installUrl, isReinstall = false, staticAuthInstallOptions) {
|
|
7
7
|
uiLogger.log('');
|
|
8
8
|
if (isReinstall) {
|
|
9
9
|
uiLogger.log(lib.prompts.installAppPrompt.reinstallExplanation);
|
|
@@ -11,6 +11,11 @@ export async function installAppBrowserPrompt(installUrl, isReinstall = false) {
|
|
|
11
11
|
else {
|
|
12
12
|
uiLogger.log(lib.prompts.installAppPrompt.explanation);
|
|
13
13
|
}
|
|
14
|
+
if (staticAuthInstallOptions) {
|
|
15
|
+
const { testingAccountId, projectAccountId, projectName, appUid } = staticAuthInstallOptions;
|
|
16
|
+
uiLogger.log(lib.prompts.installAppPrompt.staticAuthExplanation(projectAccountId, testingAccountId, projectName, appUid));
|
|
17
|
+
uiLogger.log('');
|
|
18
|
+
}
|
|
14
19
|
const { shouldOpenBrowser } = await promptUser({
|
|
15
20
|
name: 'shouldOpenBrowser',
|
|
16
21
|
type: 'confirm',
|
|
@@ -51,7 +51,7 @@ export async function projectAddPromptV3(components, selectedFeatures) {
|
|
|
51
51
|
if (template instanceof Separator || !template.value) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
if (selectedFeatures?.includes(template.value.type)) {
|
|
54
|
+
if (selectedFeatures?.includes(template.value.cliSelector || template.value.type)) {
|
|
55
55
|
if (template.disabled) {
|
|
56
56
|
throw new Error(lib.prompts.projectAddPrompt.errors.cannotAddFeature(template.value.type, template.disabled));
|
|
57
57
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Separator as _Separator } from '@inquirer/prompts';
|
|
2
2
|
import { PromptConfig, GenericPromptResponse, PromptWhen, PromptChoices } from '../../types/Prompts.js';
|
|
3
3
|
export declare const Separator: _Separator;
|
|
4
|
+
export declare const PROMPT_THEME: {
|
|
5
|
+
prefix: {
|
|
6
|
+
idle: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
4
9
|
export declare function promptUser<T extends GenericPromptResponse>(config: PromptConfig<T> | PromptConfig<T>[]): Promise<T>;
|
|
5
10
|
export declare function confirmPrompt(message: string, options?: {
|
|
6
11
|
defaultAnswer?: boolean;
|