@hubspot/cli 7.6.0-beta.6 → 7.6.0-beta.7
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/README.md +0 -4
- package/commands/__tests__/testAccount.test.js +2 -0
- package/commands/project/__tests__/devUnifiedFlow.test.js +0 -21
- package/commands/project/dev/unifiedFlow.d.ts +1 -1
- package/commands/project/dev/unifiedFlow.js +2 -5
- package/commands/testAccount/__tests__/importData.test.js +93 -0
- package/commands/testAccount/importData.d.ts +9 -0
- package/commands/testAccount/importData.js +61 -0
- package/commands/testAccount.js +2 -0
- package/lang/en.d.ts +38 -1
- package/lang/en.js +39 -2
- package/lib/__tests__/importData.test.d.ts +1 -0
- package/lib/__tests__/importData.test.js +89 -0
- package/lib/accountTypes.js +2 -3
- package/lib/app/__tests__/migrate.test.js +5 -5
- package/lib/app/migrate.js +2 -3
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/hasFeature.d.ts +1 -0
- package/lib/hasFeature.js +7 -0
- package/lib/importData.d.ts +3 -0
- package/lib/importData.js +50 -0
- package/lib/projects/__tests__/AppDevModeInterface.test.js +2 -3
- package/lib/projects/__tests__/LocalDevProcess.test.js +5 -25
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +6 -6
- package/lib/projects/__tests__/localDevHelpers.test.js +2 -1
- package/lib/projects/localDev/AppDevModeInterface.js +1 -1
- package/lib/projects/localDev/LocalDevLogger.d.ts +0 -3
- package/lib/projects/localDev/LocalDevLogger.js +2 -15
- package/lib/projects/localDev/LocalDevProcess.d.ts +1 -1
- package/lib/projects/localDev/LocalDevProcess.js +3 -3
- package/lib/projects/localDev/LocalDevState.d.ts +6 -4
- package/lib/projects/localDev/LocalDevState.js +16 -10
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +1 -0
- package/lib/projects/localDev/LocalDevWebsocketServer.js +17 -2
- package/lib/projects/localDev/helpers.js +1 -1
- package/lib/prompts/confirmImportDataPrompt.d.ts +1 -0
- package/lib/prompts/confirmImportDataPrompt.js +12 -0
- package/lib/prompts/importDataFilePathPrompt.d.ts +1 -0
- package/lib/prompts/importDataFilePathPrompt.js +24 -0
- package/lib/prompts/importDataTestAccountSelectPrompt.d.ts +3 -0
- package/lib/prompts/importDataTestAccountSelectPrompt.js +29 -0
- package/lib/ui/__tests__/removeAnsiCodes.test.d.ts +1 -0
- package/lib/ui/__tests__/removeAnsiCodes.test.js +84 -0
- package/lib/ui/removeAnsiCodes.d.ts +1 -0
- package/lib/ui/removeAnsiCodes.js +4 -0
- package/package.json +2 -2
- package/types/LocalDev.d.ts +0 -1
- package/lib/utils/__tests__/isDeepEqual.test.js +0 -269
- package/lib/utils/isDeepEqual.d.ts +0 -1
- package/lib/utils/isDeepEqual.js +0 -31
- /package/{lib/utils/__tests__/isDeepEqual.test.d.ts → commands/testAccount/__tests__/importData.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
A CLI for HubSpot developers to enable local development and automation. [Learn more about building on HubSpot](https://developers.hubspot.com).
|
|
6
6
|
|
|
7
|
-
## Contributing
|
|
8
|
-
|
|
9
|
-
For more information on developing, see the [Contributing Guide](CONTRIBUTING.md).
|
|
10
|
-
|
|
11
7
|
## Getting started
|
|
12
8
|
|
|
13
9
|
For more information on using these tools, see [Local Development Tooling: Getting Started](https://developers.hubspot.com/docs/cms/guides/getting-started-with-local-development)
|
|
@@ -2,6 +2,7 @@ import yargs from 'yargs';
|
|
|
2
2
|
import testAccountCreateCommand from '../testAccount/create.js';
|
|
3
3
|
import testAccountCreateConfigCommand from '../testAccount/createConfig.js';
|
|
4
4
|
import testAccountDeleteCommand from '../testAccount/delete.js';
|
|
5
|
+
import testAccountImportDataCommand from '../testAccount/importData.js';
|
|
5
6
|
import testAccountCommands from '../testAccount.js';
|
|
6
7
|
vi.mock('../testAccount/create');
|
|
7
8
|
vi.mock('../testAccount/createConfig');
|
|
@@ -37,6 +38,7 @@ describe('commands/testAccount', () => {
|
|
|
37
38
|
testAccountCreateCommand,
|
|
38
39
|
testAccountCreateConfigCommand,
|
|
39
40
|
testAccountDeleteCommand,
|
|
41
|
+
testAccountImportDataCommand,
|
|
40
42
|
];
|
|
41
43
|
it('should demand the command takes one positional argument', () => {
|
|
42
44
|
testAccountCommands.builder(yargs);
|
|
@@ -326,26 +326,6 @@ describe('unifiedProjectDevFlow', () => {
|
|
|
326
326
|
projectName: mockProject.name,
|
|
327
327
|
}));
|
|
328
328
|
});
|
|
329
|
-
it('should detect GitHub linked projects', async () => {
|
|
330
|
-
const githubLinkedProject = {
|
|
331
|
-
...mockProject,
|
|
332
|
-
sourceIntegration: { source: 'GITHUB' },
|
|
333
|
-
};
|
|
334
|
-
ensureProjectExists.mockResolvedValue({
|
|
335
|
-
projectExists: true,
|
|
336
|
-
project: githubLinkedProject,
|
|
337
|
-
});
|
|
338
|
-
await unifiedProjectDevFlow({
|
|
339
|
-
args: mockArgs,
|
|
340
|
-
targetProjectAccountId: mockTargetProjectAccountId,
|
|
341
|
-
providedTargetTestingAccountId: mockProvidedTargetTestingAccountId,
|
|
342
|
-
projectConfig: mockProjectConfig,
|
|
343
|
-
projectDir: mockProjectDir,
|
|
344
|
-
});
|
|
345
|
-
expect(LocalDevProcess).toHaveBeenCalledWith(expect.objectContaining({
|
|
346
|
-
isGithubLinked: true,
|
|
347
|
-
}));
|
|
348
|
-
});
|
|
349
329
|
});
|
|
350
330
|
describe('local dev process setup', () => {
|
|
351
331
|
it('should initialize LocalDevProcess with correct parameters', async () => {
|
|
@@ -359,7 +339,6 @@ describe('unifiedProjectDevFlow', () => {
|
|
|
359
339
|
expect(LocalDevProcess).toHaveBeenCalledWith({
|
|
360
340
|
initialProjectNodes: mockProjectNodes,
|
|
361
341
|
debug: mockArgs.debug,
|
|
362
|
-
isGithubLinked: false,
|
|
363
342
|
profile: mockArgs.profile,
|
|
364
343
|
targetProjectAccountId: mockTargetProjectAccountId,
|
|
365
344
|
targetTestingAccountId: mockProvidedTargetTestingAccountId,
|
|
@@ -10,5 +10,5 @@ type UnifiedProjectDevFlowArgs = {
|
|
|
10
10
|
projectDir: string;
|
|
11
11
|
profileConfig?: HsProfileFile;
|
|
12
12
|
};
|
|
13
|
-
export declare function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir,
|
|
13
|
+
export declare function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir, }: UnifiedProjectDevFlowArgs): Promise<void>;
|
|
14
14
|
export {};
|
|
@@ -19,7 +19,7 @@ import { uiLine } from '../../../lib/ui/index.js';
|
|
|
19
19
|
import { uiLogger } from '../../../lib/ui/logger.js';
|
|
20
20
|
import { commands } from '../../../lang/en.js';
|
|
21
21
|
import LocalDevWebsocketServer from '../../../lib/projects/localDev/LocalDevWebsocketServer.js';
|
|
22
|
-
export async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir,
|
|
22
|
+
export async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir, }) {
|
|
23
23
|
const env = getValidEnv(getEnv(targetProjectAccountId));
|
|
24
24
|
let projectNodes;
|
|
25
25
|
// Get IR
|
|
@@ -53,7 +53,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
|
|
|
53
53
|
const accounts = getConfigAccounts();
|
|
54
54
|
const accountIsCombined = await isUnifiedAccount(targetProjectAccountConfig);
|
|
55
55
|
const targetProjectAccountIsTestAccountOrSandbox = isTestAccountOrSandbox(targetProjectAccountConfig);
|
|
56
|
-
if (!accountIsCombined
|
|
56
|
+
if (!accountIsCombined) {
|
|
57
57
|
uiLogger.error(commands.project.dev.errors.accountNotCombined);
|
|
58
58
|
process.exit(EXIT_CODES.ERROR);
|
|
59
59
|
}
|
|
@@ -105,11 +105,9 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
|
|
|
105
105
|
allowCreate: false,
|
|
106
106
|
noLogs: true,
|
|
107
107
|
});
|
|
108
|
-
let isGithubLinked = false;
|
|
109
108
|
let project = uploadedProject;
|
|
110
109
|
SpinniesManager.init();
|
|
111
110
|
if (projectExists && project) {
|
|
112
|
-
isGithubLinked = Boolean(project.sourceIntegration && project.sourceIntegration.source === 'GITHUB');
|
|
113
111
|
await compareLocalProjectToDeployed(projectConfig, targetProjectAccountId, project.deployedBuild?.buildId, projectNodes);
|
|
114
112
|
}
|
|
115
113
|
else {
|
|
@@ -120,7 +118,6 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
|
|
|
120
118
|
const localDevProcess = new LocalDevProcess({
|
|
121
119
|
initialProjectNodes: projectNodes,
|
|
122
120
|
debug: args.debug,
|
|
123
|
-
isGithubLinked,
|
|
124
121
|
profile: args.profile,
|
|
125
122
|
targetProjectAccountId,
|
|
126
123
|
targetTestingAccountId: targetTestingAccountId,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import yargs from 'yargs';
|
|
2
|
+
import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, } from '../../../lib/commonOpts.js';
|
|
3
|
+
import testAccountImportDataCommand from '../importData.js';
|
|
4
|
+
import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
5
|
+
import { handleImportData, handleTargetTestAccountSelectionFlow, } from '../../../lib/importData.js';
|
|
6
|
+
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
7
|
+
import { getImportDataRequest } from '@hubspot/local-dev-lib/crm';
|
|
8
|
+
import { logError } from '../../../lib/errorHandlers/index.js';
|
|
9
|
+
import { importDataFilePathPrompt } from '../../../lib/prompts/importDataFilePathPrompt.js';
|
|
10
|
+
import { confirmImportDataPrompt } from '../../../lib/prompts/confirmImportDataPrompt.js';
|
|
11
|
+
vi.mock('../../../lib/commonOpts');
|
|
12
|
+
vi.mock('../../../lib/importData');
|
|
13
|
+
vi.mock('../../../lib/usageTracking');
|
|
14
|
+
vi.mock('@hubspot/local-dev-lib/crm');
|
|
15
|
+
vi.mock('../../../lib/errorHandlers/index');
|
|
16
|
+
vi.mock('../../../lib/prompts/importDataFilePathPrompt');
|
|
17
|
+
vi.mock('../../../lib/prompts/confirmImportDataPrompt');
|
|
18
|
+
describe('commands/testAccount/importData', () => {
|
|
19
|
+
const yargsMock = yargs;
|
|
20
|
+
const mockExit = vi
|
|
21
|
+
.spyOn(process, 'exit')
|
|
22
|
+
.mockImplementation(() => undefined);
|
|
23
|
+
const mockHandleImportData = vi.mocked(handleImportData);
|
|
24
|
+
const mockHandleTargetTestAccountSelectionFlow = vi.mocked(handleTargetTestAccountSelectionFlow);
|
|
25
|
+
const mockTrackCommandUsage = vi.mocked(trackCommandUsage);
|
|
26
|
+
const mockGetImportDataRequest = vi.mocked(getImportDataRequest);
|
|
27
|
+
const mockLogError = vi.mocked(logError);
|
|
28
|
+
const mockImportDataFilePathPrompt = vi.mocked(importDataFilePathPrompt);
|
|
29
|
+
const mockConfirmImportDataPrompt = vi.mocked(confirmImportDataPrompt);
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockExit.mockReset();
|
|
32
|
+
mockHandleImportData.mockReset();
|
|
33
|
+
mockHandleTargetTestAccountSelectionFlow.mockReset();
|
|
34
|
+
mockTrackCommandUsage.mockReset();
|
|
35
|
+
mockGetImportDataRequest.mockReset();
|
|
36
|
+
mockLogError.mockReset();
|
|
37
|
+
mockImportDataFilePathPrompt.mockReset();
|
|
38
|
+
mockConfirmImportDataPrompt.mockReset();
|
|
39
|
+
});
|
|
40
|
+
describe('command', () => {
|
|
41
|
+
it('should have the correct command structure', () => {
|
|
42
|
+
expect(testAccountImportDataCommand.command).toEqual('import-data');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
// describe('describe', () => {
|
|
46
|
+
// it('should provide a description', () => {
|
|
47
|
+
// expect(testAccountImportDataCommand.describe).toBeDefined();
|
|
48
|
+
// });
|
|
49
|
+
// });
|
|
50
|
+
describe('builder', () => {
|
|
51
|
+
it('should support the correct options', () => {
|
|
52
|
+
testAccountImportDataCommand.builder(yargsMock);
|
|
53
|
+
expect(yargsMock.example).toHaveBeenCalledTimes(1);
|
|
54
|
+
expect(yargsMock.options).toHaveBeenCalledTimes(1);
|
|
55
|
+
expect(addAccountOptions).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
|
|
57
|
+
expect(addConfigOptions).toHaveBeenCalledTimes(1);
|
|
58
|
+
expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
|
|
59
|
+
expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
|
|
60
|
+
expect(addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('handler', () => {
|
|
64
|
+
it('should complete the flow given the correct args', async () => {
|
|
65
|
+
const targetAccountId = 123456789;
|
|
66
|
+
const mockArgs = {
|
|
67
|
+
d: false,
|
|
68
|
+
debug: false,
|
|
69
|
+
_: [],
|
|
70
|
+
$0: 'hs',
|
|
71
|
+
derivedAccountId: targetAccountId,
|
|
72
|
+
userProvidedAccount: 'test-account',
|
|
73
|
+
filePath: 'test-file.json',
|
|
74
|
+
skipConfirm: true,
|
|
75
|
+
};
|
|
76
|
+
mockHandleTargetTestAccountSelectionFlow.mockResolvedValue(targetAccountId);
|
|
77
|
+
mockGetImportDataRequest.mockReturnValue({
|
|
78
|
+
importRequest: {},
|
|
79
|
+
dataFileNames: ['test-file.json'],
|
|
80
|
+
});
|
|
81
|
+
mockHandleImportData.mockResolvedValue();
|
|
82
|
+
await testAccountImportDataCommand.handler(mockArgs);
|
|
83
|
+
expect(mockHandleTargetTestAccountSelectionFlow).toHaveBeenCalledWith(123456789, 'test-account');
|
|
84
|
+
expect(mockGetImportDataRequest).toHaveBeenCalledWith('test-file.json');
|
|
85
|
+
expect(mockHandleImportData).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(mockTrackCommandUsage).toHaveBeenCalledTimes(1);
|
|
87
|
+
expect(mockLogError).not.toHaveBeenCalled();
|
|
88
|
+
expect(mockImportDataFilePathPrompt).not.toHaveBeenCalled();
|
|
89
|
+
expect(mockConfirmImportDataPrompt).not.toHaveBeenCalled();
|
|
90
|
+
expect(mockExit).toHaveBeenCalledWith(EXIT_CODES.SUCCESS);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs.js';
|
|
2
|
+
export declare const command = "import-data";
|
|
3
|
+
export declare const describe: undefined;
|
|
4
|
+
type CrmImportDataArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
|
|
5
|
+
filePath: string | undefined;
|
|
6
|
+
skipConfirm: boolean | undefined;
|
|
7
|
+
};
|
|
8
|
+
declare const crmImportDataCommand: YargsCommandModule<unknown, CrmImportDataArgs>;
|
|
9
|
+
export default crmImportDataCommand;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getImportDataRequest } from '@hubspot/local-dev-lib/crm';
|
|
2
|
+
import { logError } from '../../lib/errorHandlers/index.js';
|
|
3
|
+
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
4
|
+
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
5
|
+
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
6
|
+
import { importDataFilePathPrompt } from '../../lib/prompts/importDataFilePathPrompt.js';
|
|
7
|
+
import { handleImportData, handleTargetTestAccountSelectionFlow, } from '../../lib/importData.js';
|
|
8
|
+
import { confirmImportDataPrompt } from '../../lib/prompts/confirmImportDataPrompt.js';
|
|
9
|
+
import { commands } from '../../lang/en.js';
|
|
10
|
+
export const command = 'import-data';
|
|
11
|
+
export const describe = undefined; // commands.testAccount.subcommands.importData.describe;
|
|
12
|
+
async function handler(args) {
|
|
13
|
+
const { derivedAccountId, userProvidedAccount, filePath: providedFilePath, skipConfirm, } = args;
|
|
14
|
+
trackCommandUsage('crm-import-data', {}, derivedAccountId);
|
|
15
|
+
let targetAccountId;
|
|
16
|
+
try {
|
|
17
|
+
targetAccountId = await handleTargetTestAccountSelectionFlow(derivedAccountId, userProvidedAccount);
|
|
18
|
+
const filePath = providedFilePath || (await importDataFilePathPrompt());
|
|
19
|
+
const { importRequest, dataFileNames } = getImportDataRequest(filePath);
|
|
20
|
+
const confirmImportData = skipConfirm ||
|
|
21
|
+
(await confirmImportDataPrompt(targetAccountId, dataFileNames));
|
|
22
|
+
if (!confirmImportData) {
|
|
23
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
24
|
+
}
|
|
25
|
+
await handleImportData(targetAccountId, dataFileNames, importRequest);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logError(error);
|
|
29
|
+
process.exit(EXIT_CODES.ERROR);
|
|
30
|
+
}
|
|
31
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
32
|
+
}
|
|
33
|
+
function crmImportDataBuilder(yargs) {
|
|
34
|
+
yargs.example([['$0 test-account import-data']]);
|
|
35
|
+
yargs.options({
|
|
36
|
+
'file-path': {
|
|
37
|
+
type: 'string',
|
|
38
|
+
describe: commands.testAccount.subcommands.importData.options.filePath.describe,
|
|
39
|
+
positional: false,
|
|
40
|
+
},
|
|
41
|
+
'skip-confirm': {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
describe: commands.testAccount.subcommands.importData.options.skipConfirm
|
|
44
|
+
.describe,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return yargs;
|
|
48
|
+
}
|
|
49
|
+
const builder = makeYargsBuilder(crmImportDataBuilder, command, describe, {
|
|
50
|
+
useGlobalOptions: true,
|
|
51
|
+
useConfigOptions: true,
|
|
52
|
+
useAccountOptions: true,
|
|
53
|
+
useEnvironmentOptions: true,
|
|
54
|
+
});
|
|
55
|
+
const crmImportDataCommand = {
|
|
56
|
+
command,
|
|
57
|
+
describe,
|
|
58
|
+
builder,
|
|
59
|
+
handler,
|
|
60
|
+
};
|
|
61
|
+
export default crmImportDataCommand;
|
package/commands/testAccount.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import createTestAccountCommand from './testAccount/create.js';
|
|
2
2
|
import createTestAccountConfigCommand from './testAccount/createConfig.js';
|
|
3
|
+
import importDataCommand from './testAccount/importData.js';
|
|
3
4
|
import deleteTestAccountCommand from './testAccount/delete.js';
|
|
4
5
|
import { makeYargsBuilder } from '../lib/yargsUtils.js';
|
|
5
6
|
import { commands } from '../lang/en.js';
|
|
@@ -10,6 +11,7 @@ function testAccountBuilder(yargs) {
|
|
|
10
11
|
.command(createTestAccountCommand)
|
|
11
12
|
.command(createTestAccountConfigCommand)
|
|
12
13
|
.command(deleteTestAccountCommand)
|
|
14
|
+
.command(importDataCommand)
|
|
13
15
|
.demandCommand(1, '');
|
|
14
16
|
return yargs;
|
|
15
17
|
}
|
package/lang/en.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CLIAccount } from '@hubspot/local-dev-lib/types/Accounts';
|
|
1
2
|
export declare const commands: {
|
|
2
3
|
readonly generalErrors: {
|
|
3
4
|
readonly srcIsProject: (src: string, command: string) => string;
|
|
@@ -976,7 +977,7 @@ Profiles enable you to reference variables in your component configuration files
|
|
|
976
977
|
readonly logs: {
|
|
977
978
|
readonly betaMessage: "HubSpot projects local development";
|
|
978
979
|
readonly placeholderAccountSelection: "Using default account as target account (for now)";
|
|
979
|
-
readonly learnMoreLocalDevServer:
|
|
980
|
+
readonly learnMoreLocalDevServer: string;
|
|
980
981
|
readonly 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.";
|
|
981
982
|
readonly learnMoreMessage: `
|
|
982
983
|
Visit our ${string} to learn more.`;
|
|
@@ -1803,6 +1804,19 @@ ${string}`;
|
|
|
1803
1804
|
};
|
|
1804
1805
|
readonly testAccount: {
|
|
1805
1806
|
readonly describe: "Commands for working with test accounts.";
|
|
1807
|
+
readonly subcommands: {
|
|
1808
|
+
readonly importData: {
|
|
1809
|
+
readonly describe: "Import data into the CRM";
|
|
1810
|
+
readonly options: {
|
|
1811
|
+
readonly skipConfirm: {
|
|
1812
|
+
readonly describe: "Skip the confirmation prompt";
|
|
1813
|
+
};
|
|
1814
|
+
readonly filePath: {
|
|
1815
|
+
readonly describe: "The path to the JSON file containing the import schema";
|
|
1816
|
+
};
|
|
1817
|
+
};
|
|
1818
|
+
};
|
|
1819
|
+
};
|
|
1806
1820
|
readonly create: {
|
|
1807
1821
|
readonly describe: "Create a test account from a config file";
|
|
1808
1822
|
readonly configPathPrompt: "[--config-path] Enter the path to the test account config: ";
|
|
@@ -2690,6 +2704,16 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
2690
2704
|
readonly boxen: {
|
|
2691
2705
|
readonly failedToLoad: "Failed to load boxen util.";
|
|
2692
2706
|
};
|
|
2707
|
+
readonly importData: {
|
|
2708
|
+
readonly errors: {
|
|
2709
|
+
readonly incorrectAccountType: (derivedAccountId: number) => string;
|
|
2710
|
+
readonly failedToImportData: "Failed to import data into portal.";
|
|
2711
|
+
readonly notDeveloperTestAccount: "The account is not a developer test account.";
|
|
2712
|
+
readonly noAccountConfig: (accountId: number) => string;
|
|
2713
|
+
};
|
|
2714
|
+
readonly inProgress: (portalId: number, fileNames: string[]) => string;
|
|
2715
|
+
readonly viewImportLink: (baseUrl: string, accountId: number, importId: string) => string;
|
|
2716
|
+
};
|
|
2693
2717
|
readonly ui: {
|
|
2694
2718
|
readonly betaTag: string;
|
|
2695
2719
|
readonly betaWarning: {
|
|
@@ -2849,6 +2873,19 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
2849
2873
|
};
|
|
2850
2874
|
};
|
|
2851
2875
|
readonly prompts: {
|
|
2876
|
+
readonly importDataFilePathPrompt: {
|
|
2877
|
+
readonly promptContext: `To view the JSON schema for data imports, visit ${string}`;
|
|
2878
|
+
readonly promptMessage: "[--file-path] Select the JSON file that will be used to import your data.";
|
|
2879
|
+
};
|
|
2880
|
+
readonly confirmImportDataPrompt: {
|
|
2881
|
+
readonly message: (dataFileNames: string[], cliAccount: CLIAccount | null) => string;
|
|
2882
|
+
};
|
|
2883
|
+
readonly importDataTestAccountSelectPrompt: {
|
|
2884
|
+
readonly errors: {
|
|
2885
|
+
readonly noAccountsFound: "No accounts found.";
|
|
2886
|
+
readonly noChildTestAccountsFound: (parentAccountId: number) => string;
|
|
2887
|
+
};
|
|
2888
|
+
};
|
|
2852
2889
|
readonly projectDevTargetAccountPrompt: {
|
|
2853
2890
|
readonly createNewSandboxOption: "<Test on a new development sandbox>";
|
|
2854
2891
|
readonly createNewDeveloperTestAccountOption: "<Test on a new developer test account>";
|
package/lang/en.js
CHANGED
|
@@ -5,6 +5,7 @@ import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUB
|
|
|
5
5
|
import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
|
|
6
6
|
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
|
|
7
7
|
import { PROJECT_CONFIG_FILE, PROJECT_WITH_APP } from '../lib/constants.js';
|
|
8
|
+
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
8
9
|
export const commands = {
|
|
9
10
|
generalErrors: {
|
|
10
11
|
srcIsProject: (src, command) => `"${src}" is in a project folder. Did you mean "hs project ${command}"?`,
|
|
@@ -976,7 +977,7 @@ export const commands = {
|
|
|
976
977
|
logs: {
|
|
977
978
|
betaMessage: 'HubSpot projects local development',
|
|
978
979
|
placeholderAccountSelection: 'Using default account as target account (for now)',
|
|
979
|
-
learnMoreLocalDevServer: 'Learn more about the projects local dev server',
|
|
980
|
+
learnMoreLocalDevServer: uiLink('Learn more about the projects local dev server', 'https://developers.hubspot.com/docs/platform/project-cli-commands#start-a-local-development-server'),
|
|
980
981
|
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.',
|
|
981
982
|
learnMoreMessage: `\nVisit our ${uiLink('docs on Developer Test and Sandbox accounts', 'https://developers.hubspot.com/docs/getting-started/account-types')} to learn more.`,
|
|
982
983
|
},
|
|
@@ -1798,6 +1799,19 @@ export const commands = {
|
|
|
1798
1799
|
},
|
|
1799
1800
|
testAccount: {
|
|
1800
1801
|
describe: 'Commands for working with test accounts.',
|
|
1802
|
+
subcommands: {
|
|
1803
|
+
importData: {
|
|
1804
|
+
describe: 'Import data into the CRM',
|
|
1805
|
+
options: {
|
|
1806
|
+
skipConfirm: {
|
|
1807
|
+
describe: 'Skip the confirmation prompt',
|
|
1808
|
+
},
|
|
1809
|
+
filePath: {
|
|
1810
|
+
describe: 'The path to the JSON file containing the import schema',
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1813
|
+
},
|
|
1814
|
+
},
|
|
1801
1815
|
create: {
|
|
1802
1816
|
describe: 'Create a test account from a config file',
|
|
1803
1817
|
configPathPrompt: '[--config-path] Enter the path to the test account config: ',
|
|
@@ -2498,7 +2512,7 @@ export const lib = {
|
|
|
2498
2512
|
},
|
|
2499
2513
|
},
|
|
2500
2514
|
AppDevModeInterface: {
|
|
2501
|
-
defaultMarketplaceAppWarning: (installCount) =>
|
|
2515
|
+
defaultMarketplaceAppWarning: (installCount) => `Your marketplace app is currently installed in ${chalk.bold(`${installCount} ${installCount === 1 ? 'account' : 'accounts'}`)}. Any uploaded changes will impact your app's users. We strongly recommend creating a copy of this app to test your changes before proceding.`,
|
|
2502
2516
|
autoInstallDeclined: 'You must install your app on your target test account to proceed with local development.',
|
|
2503
2517
|
autoInstallSuccess: (appName, targetTestAccountId) => `Successfully installed app ${appName} on account ${uiAccountDescription(targetTestAccountId)}\n`,
|
|
2504
2518
|
autoInstallError: (appName, targetTestAccountId) => `Error installing app ${appName} on account ${uiAccountDescription(targetTestAccountId)}. You may still be able to install your app in your browser.`,
|
|
@@ -2682,6 +2696,16 @@ export const lib = {
|
|
|
2682
2696
|
boxen: {
|
|
2683
2697
|
failedToLoad: 'Failed to load boxen util.',
|
|
2684
2698
|
},
|
|
2699
|
+
importData: {
|
|
2700
|
+
errors: {
|
|
2701
|
+
incorrectAccountType: (derivedAccountId) => `The account ${uiAccountDescription(derivedAccountId)} is not a standard account, developer test account, or app developer account.`,
|
|
2702
|
+
failedToImportData: 'Failed to import data into portal.',
|
|
2703
|
+
notDeveloperTestAccount: 'The account is not a developer test account.',
|
|
2704
|
+
noAccountConfig: (accountId) => `No account config found for ${uiAccountDescription(accountId)}`,
|
|
2705
|
+
},
|
|
2706
|
+
inProgress: (portalId, fileNames) => `Importing data into ${uiAccountDescription(portalId)} from [${fileNames.join(', ')}]`,
|
|
2707
|
+
viewImportLink: (baseUrl, accountId, importId) => `Data import currently processing. You can view the status of your import ${uiLink('here', `${baseUrl}/import/${accountId}/post/${importId}`)}`,
|
|
2708
|
+
},
|
|
2685
2709
|
ui: {
|
|
2686
2710
|
betaTag: chalk.bold('[BETA]'),
|
|
2687
2711
|
betaWarning: {
|
|
@@ -2841,6 +2865,19 @@ export const lib = {
|
|
|
2841
2865
|
},
|
|
2842
2866
|
},
|
|
2843
2867
|
prompts: {
|
|
2868
|
+
importDataFilePathPrompt: {
|
|
2869
|
+
promptContext: `To view the JSON schema for data imports, visit ${uiLink('the docs', 'https://developers.hubspot.com/docs/guides/api/crm/imports')}`,
|
|
2870
|
+
promptMessage: '[--file-path] Select the JSON file that will be used to import your data.',
|
|
2871
|
+
},
|
|
2872
|
+
confirmImportDataPrompt: {
|
|
2873
|
+
message: (dataFileNames, cliAccount) => `You are importing [${dataFileNames.join(', ')}] into ${uiAccountDescription(getAccountIdentifier(cliAccount))}. Continue?`,
|
|
2874
|
+
},
|
|
2875
|
+
importDataTestAccountSelectPrompt: {
|
|
2876
|
+
errors: {
|
|
2877
|
+
noAccountsFound: 'No accounts found.',
|
|
2878
|
+
noChildTestAccountsFound: (parentAccountId) => `No developer test accounts found under the parent account ${uiAccountDescription(parentAccountId)}`,
|
|
2879
|
+
},
|
|
2880
|
+
},
|
|
2844
2881
|
projectDevTargetAccountPrompt: {
|
|
2845
2882
|
createNewSandboxOption: '<Test on a new development sandbox>',
|
|
2846
2883
|
createNewDeveloperTestAccountOption: '<Test on a new developer test account>',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { uiLogger } from '../ui/logger.js';
|
|
2
|
+
import { createImport } from '@hubspot/local-dev-lib/api/crm';
|
|
3
|
+
import { getAccountConfig, getAccountId } from '@hubspot/local-dev-lib/config';
|
|
4
|
+
import { handleImportData, handleTargetTestAccountSelectionFlow, } from '../importData.js';
|
|
5
|
+
import { lib } from '../../lang/en.js';
|
|
6
|
+
import { isDeveloperTestAccount, isStandardAccount, isAppDeveloperAccount, } from '../accountTypes.js';
|
|
7
|
+
import { importDataTestAccountSelectPrompt } from '../prompts/importDataTestAccountSelectPrompt.js';
|
|
8
|
+
vi.mock('../ui/logger');
|
|
9
|
+
vi.mock('@hubspot/local-dev-lib/api/crm');
|
|
10
|
+
vi.mock('@hubspot/local-dev-lib/config');
|
|
11
|
+
vi.mock('../accountTypes');
|
|
12
|
+
vi.mock('../prompts/importDataTestAccountSelectPrompt');
|
|
13
|
+
describe('lib/importData', () => {
|
|
14
|
+
const mockUiLogger = vi.mocked(uiLogger);
|
|
15
|
+
const mockCreateImport = vi.mocked(createImport);
|
|
16
|
+
const mockGetAccountConfig = vi.mocked(getAccountConfig);
|
|
17
|
+
const mockGetAccountId = vi.mocked(getAccountId);
|
|
18
|
+
const mockIsDeveloperTestAccount = vi.mocked(isDeveloperTestAccount);
|
|
19
|
+
const mockIsStandardAccount = vi.mocked(isStandardAccount);
|
|
20
|
+
const mockIsAppDeveloperAccount = vi.mocked(isAppDeveloperAccount);
|
|
21
|
+
const mockImportDataTestAccountSelectPrompt = vi.mocked(importDataTestAccountSelectPrompt);
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockUiLogger.info.mockReset();
|
|
24
|
+
mockUiLogger.success.mockReset();
|
|
25
|
+
mockUiLogger.error.mockReset();
|
|
26
|
+
mockCreateImport.mockReset();
|
|
27
|
+
mockGetAccountConfig.mockReset();
|
|
28
|
+
mockGetAccountId.mockReset();
|
|
29
|
+
mockIsDeveloperTestAccount.mockReset();
|
|
30
|
+
mockIsStandardAccount.mockReset();
|
|
31
|
+
mockIsAppDeveloperAccount.mockReset();
|
|
32
|
+
mockImportDataTestAccountSelectPrompt.mockReset();
|
|
33
|
+
});
|
|
34
|
+
describe('handleImportData', () => {
|
|
35
|
+
const targetAccountId = 123456789;
|
|
36
|
+
const dataFileNames = ['test-file.json'];
|
|
37
|
+
const importRequest = {
|
|
38
|
+
name: 'test-import',
|
|
39
|
+
};
|
|
40
|
+
it('should log the correct success message', async () => {
|
|
41
|
+
// @ts-expect-error - mockCreateImport is not typed correctly
|
|
42
|
+
mockCreateImport.mockResolvedValue({
|
|
43
|
+
data: { id: '123' },
|
|
44
|
+
});
|
|
45
|
+
await handleImportData(targetAccountId, dataFileNames, importRequest);
|
|
46
|
+
expect(mockUiLogger.success).toHaveBeenCalledWith(lib.importData.viewImportLink('https://app.hubspot.com', targetAccountId, '123'));
|
|
47
|
+
});
|
|
48
|
+
it('should log the correct error message', async () => {
|
|
49
|
+
mockCreateImport.mockRejectedValue(new Error('test-error'));
|
|
50
|
+
// weird because we catch the error, log a specific message, and then throw it again
|
|
51
|
+
await expect(handleImportData(targetAccountId, dataFileNames, importRequest)).rejects.toThrow('test-error');
|
|
52
|
+
expect(mockUiLogger.error).toHaveBeenCalledWith(lib.importData.errors.failedToImportData);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('handleTargetTestAccountSelectionFlow', () => {
|
|
56
|
+
const userProvidedAccountId = '1234';
|
|
57
|
+
const derivedAccountId = 123456789;
|
|
58
|
+
it('should error if the userProvidedAccountId is not the right account type', async () => {
|
|
59
|
+
mockGetAccountConfig.mockReturnValue({});
|
|
60
|
+
mockGetAccountId.mockReturnValue(1234);
|
|
61
|
+
mockIsDeveloperTestAccount.mockReturnValue(false);
|
|
62
|
+
await expect(handleTargetTestAccountSelectionFlow(derivedAccountId, userProvidedAccountId)).rejects.toThrow(lib.importData.errors.notDeveloperTestAccount);
|
|
63
|
+
});
|
|
64
|
+
it('should error if the derivedAccountId belongs to the wrong account type', async () => {
|
|
65
|
+
mockGetAccountConfig.mockReturnValue({});
|
|
66
|
+
mockIsDeveloperTestAccount.mockReturnValue(false);
|
|
67
|
+
mockIsStandardAccount.mockReturnValue(false);
|
|
68
|
+
mockIsAppDeveloperAccount.mockReturnValue(false);
|
|
69
|
+
await expect(handleTargetTestAccountSelectionFlow(derivedAccountId, undefined)).rejects.toThrow(lib.importData.errors.incorrectAccountType(derivedAccountId));
|
|
70
|
+
});
|
|
71
|
+
it('should return the derivedAccountId if it is a developer test account', async () => {
|
|
72
|
+
mockGetAccountConfig.mockReturnValue({});
|
|
73
|
+
mockIsDeveloperTestAccount.mockReturnValue(true);
|
|
74
|
+
const result = await handleTargetTestAccountSelectionFlow(derivedAccountId, undefined);
|
|
75
|
+
expect(result).toBe(derivedAccountId);
|
|
76
|
+
});
|
|
77
|
+
it('should return the result of the importDataTestAccountSelectPrompt if the derivedAccountId is a standard or app developer account', async () => {
|
|
78
|
+
mockGetAccountConfig.mockReturnValue({});
|
|
79
|
+
mockIsDeveloperTestAccount.mockReturnValue(false);
|
|
80
|
+
mockIsStandardAccount.mockReturnValue(true);
|
|
81
|
+
mockIsAppDeveloperAccount.mockReturnValue(true);
|
|
82
|
+
mockImportDataTestAccountSelectPrompt.mockResolvedValue({
|
|
83
|
+
selectedAccountId: 890223,
|
|
84
|
+
});
|
|
85
|
+
const result = await handleTargetTestAccountSelectionFlow(derivedAccountId, undefined);
|
|
86
|
+
expect(result).toBe(890223);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
package/lib/accountTypes.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { HUBSPOT_ACCOUNT_TYPES } from '@hubspot/local-dev-lib/constants/config';
|
|
2
|
-
import {
|
|
3
|
-
import { FEATURES } from './constants.js';
|
|
2
|
+
import { hasUnfiedAppsAccess } from './hasFeature.js';
|
|
4
3
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
5
4
|
function isAccountType(accountConfig, accountType) {
|
|
6
5
|
return Boolean(accountConfig.accountType && accountType.includes(accountConfig.accountType));
|
|
@@ -40,5 +39,5 @@ export async function isUnifiedAccount(account) {
|
|
|
40
39
|
if (!accountId) {
|
|
41
40
|
return false;
|
|
42
41
|
}
|
|
43
|
-
return
|
|
42
|
+
return hasUnfiedAppsAccess(accountId);
|
|
44
43
|
}
|
|
@@ -11,7 +11,7 @@ import { ensureProjectExists } from '../../projects/ensureProjectExists.js';
|
|
|
11
11
|
import { poll } from '../../polling.js';
|
|
12
12
|
import { CLI_UNMIGRATABLE_REASONS, continueMigration, initializeMigration, listAppsForMigration, listThemesForMigration, } from '../../../api/migrate.js';
|
|
13
13
|
import { lib } from '../../../lang/en.js';
|
|
14
|
-
import {
|
|
14
|
+
import { hasUnfiedAppsAccess } from '../../hasFeature.js';
|
|
15
15
|
import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginMigration, pollMigrationStatus, finalizeMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationAppsAndThemes, } from '../migrate.js';
|
|
16
16
|
vi.mock('@hubspot/local-dev-lib/logger');
|
|
17
17
|
vi.mock('@hubspot/local-dev-lib/path');
|
|
@@ -43,7 +43,7 @@ const mockedListAppsForMigration = listAppsForMigration;
|
|
|
43
43
|
const mockedListThemesForMigration = listThemesForMigration;
|
|
44
44
|
const mockedInitializeMigration = initializeMigration;
|
|
45
45
|
const mockedContinueMigration = continueMigration;
|
|
46
|
-
const
|
|
46
|
+
const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
|
|
47
47
|
const mockedFs = fs;
|
|
48
48
|
const createMockMigratableApp = (id, name, projectName) => ({
|
|
49
49
|
appId: id,
|
|
@@ -82,7 +82,7 @@ describe('lib/app/migrate', () => {
|
|
|
82
82
|
mockedGetCwd.mockReturnValue(MOCK_CWD);
|
|
83
83
|
mockedSanitizeFileName.mockImplementation(name => name);
|
|
84
84
|
mockedValidateUid.mockReturnValue(undefined);
|
|
85
|
-
|
|
85
|
+
mockedHasUnfiedAppsAccess.mockResolvedValue(true);
|
|
86
86
|
mockedFs.renameSync.mockImplementation(() => { });
|
|
87
87
|
});
|
|
88
88
|
describe('getUnmigratableReason', () => {
|
|
@@ -472,10 +472,10 @@ describe('lib/app/migrate', () => {
|
|
|
472
472
|
unstable: false,
|
|
473
473
|
};
|
|
474
474
|
beforeEach(() => {
|
|
475
|
-
|
|
475
|
+
mockedHasUnfiedAppsAccess.mockResolvedValue(true);
|
|
476
476
|
});
|
|
477
477
|
it('should throw an error when account is not ungated for unified apps', async () => {
|
|
478
|
-
|
|
478
|
+
mockedHasUnfiedAppsAccess.mockResolvedValueOnce(false);
|
|
479
479
|
await expect(migrateApp2025_2(ACCOUNT_ID, options)).rejects.toThrowError(/isn't enrolled in the required product beta to access this command./);
|
|
480
480
|
});
|
|
481
481
|
it('should throw an error when projectConfig is invalid', async () => {
|
package/lib/app/migrate.js
CHANGED
|
@@ -16,8 +16,7 @@ import { DEFAULT_POLLING_STATUS_LOOKUP, poll } from '../polling.js';
|
|
|
16
16
|
import { checkMigrationStatusV2, CLI_UNMIGRATABLE_REASONS, continueMigration, initializeMigration, isMigrationStatus, listAppsForMigration, listThemesForMigration, } from '../../api/migrate.js';
|
|
17
17
|
import fs from 'fs';
|
|
18
18
|
import { lib } from '../../lang/en.js';
|
|
19
|
-
import {
|
|
20
|
-
import { FEATURES } from '../constants.js';
|
|
19
|
+
import { hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
21
20
|
import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
|
|
22
21
|
import { uiLogger } from '../ui/logger.js';
|
|
23
22
|
export function getUnmigratableReason(reasonCode, projectName, accountId) {
|
|
@@ -340,7 +339,7 @@ export async function downloadProjectFiles(derivedAccountId, projectName, buildI
|
|
|
340
339
|
}
|
|
341
340
|
export async function migrateApp2025_2(derivedAccountId, options, projectConfig) {
|
|
342
341
|
SpinniesManager.init();
|
|
343
|
-
const ungatedForUnifiedApps = await
|
|
342
|
+
const ungatedForUnifiedApps = await hasUnfiedAppsAccess(derivedAccountId);
|
|
344
343
|
if (!ungatedForUnifiedApps) {
|
|
345
344
|
throw new Error(lib.migrate.errors.notUngatedForUnifiedApps(uiAccountDescription(derivedAccountId)));
|
|
346
345
|
}
|
package/lib/constants.d.ts
CHANGED
|
@@ -87,6 +87,7 @@ export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
|
|
|
87
87
|
UPDATE_PROJECT_NODES: string;
|
|
88
88
|
UPDATE_APP_DATA: string;
|
|
89
89
|
UPDATE_PROJECT_DATA: string;
|
|
90
|
+
UPDATE_UPLOAD_WARNINGS: string;
|
|
90
91
|
};
|
|
91
92
|
export declare const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES: {
|
|
92
93
|
UPLOAD: string;
|
package/lib/constants.js
CHANGED
|
@@ -79,6 +79,7 @@ export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
|
|
|
79
79
|
UPDATE_PROJECT_NODES: 'server:updateProjectNodes',
|
|
80
80
|
UPDATE_APP_DATA: 'server:updateAppData',
|
|
81
81
|
UPDATE_PROJECT_DATA: 'server:updateProjectData',
|
|
82
|
+
UPDATE_UPLOAD_WARNINGS: 'server:updateUploadWarnings',
|
|
82
83
|
};
|
|
83
84
|
export const LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES = {
|
|
84
85
|
UPLOAD: 'client:upload',
|
package/lib/hasFeature.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { FEATURES } from './constants.js';
|
|
2
2
|
import { ValueOf } from '@hubspot/local-dev-lib/types/Utils';
|
|
3
3
|
export declare function hasFeature(accountId: number, feature: ValueOf<typeof FEATURES>): Promise<boolean>;
|
|
4
|
+
export declare function hasUnfiedAppsAccess(accountId: number): Promise<boolean>;
|