@hubspot/cli 8.0.11-experimental.2 → 8.0.12-experimental.1
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 +4 -3
- package/commands/account/clean.js +2 -0
- package/commands/account/createOverride.js +3 -0
- package/commands/account/info.js +34 -16
- package/commands/account/link.d.ts +4 -0
- package/commands/account/link.js +89 -0
- package/commands/account/list.js +29 -71
- package/commands/account/remove.js +2 -0
- package/commands/account/removeOverride.js +3 -0
- package/commands/account/unlink.d.ts +4 -0
- package/commands/account/unlink.js +70 -0
- package/commands/account/use.js +71 -1
- package/commands/account.js +4 -0
- package/commands/project/appInstallStatus.d.ts +4 -0
- package/commands/project/appInstallStatus.js +132 -0
- package/commands/project/create.js +8 -0
- package/commands/project/dev/deprecatedFlow.js +20 -2
- package/commands/project/dev/index.js +6 -0
- package/commands/project/dev/unifiedFlow.js +20 -3
- package/commands/project/lint.js +20 -2
- package/commands/project/upload.d.ts +2 -0
- package/commands/project/upload.js +47 -3
- package/commands/project.js +2 -0
- package/lang/en.d.ts +122 -0
- package/lang/en.js +136 -8
- package/lib/app/migrate.js +2 -1
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +4 -0
- package/lib/doctor/Doctor.js +5 -5
- package/lib/link/accountTableUtils.d.ts +10 -0
- package/lib/link/accountTableUtils.js +39 -0
- package/lib/link/index.d.ts +18 -0
- package/lib/link/index.js +185 -0
- package/lib/link/linkUtils.d.ts +5 -0
- package/lib/link/linkUtils.js +49 -0
- package/lib/link/prompts.d.ts +7 -0
- package/lib/link/prompts.js +126 -0
- package/lib/link/renderLinkedAccountsTable.d.ts +2 -0
- package/lib/link/renderLinkedAccountsTable.js +14 -0
- package/lib/link/warnIfLinkedDirectory.d.ts +1 -0
- package/lib/link/warnIfLinkedDirectory.js +9 -0
- package/lib/projects/ProjectLogsManager.js +4 -1
- package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +2 -1
- package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +2 -0
- package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +3 -0
- package/lib/projects/preview.d.ts +7 -0
- package/lib/projects/preview.js +58 -0
- package/lib/projects/uieLinting.d.ts +17 -3
- package/lib/projects/uieLinting.js +93 -28
- package/lib/projects/upload.d.ts +1 -0
- package/lib/projects/upload.js +4 -3
- package/lib/prompts/projectsLogsPrompt.js +3 -0
- package/lib/prompts/promptUtils.js +1 -0
- package/lib/ui/accountTable.d.ts +8 -0
- package/lib/ui/accountTable.js +67 -0
- package/lib/yargs/parseYargsOrExit.d.ts +4 -0
- package/lib/yargs/parseYargsOrExit.js +25 -0
- package/mcp-server/server.js +39 -1
- package/mcp-server/tools/index.js +2 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
- package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/FindProjectsTool.d.ts +15 -0
- package/mcp-server/tools/project/FindProjectsTool.js +60 -0
- package/mcp-server/tools/project/GetBuildLogsTool.js +1 -1
- package/mcp-server/tools/project/GetBuildStatusTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +1 -1
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/package.json +7 -7
- package/types/Link.d.ts +32 -0
- package/types/Link.js +5 -0
- package/types/PackageJson.d.ts +1 -0
- package/types/Prompts.d.ts +1 -0
- package/types/Yargs.d.ts +1 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { getAllConfigAccounts, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
|
|
2
|
+
import { confirmPrompt } from '../prompts/promptUtils.js';
|
|
3
|
+
import { promptForAccountsToLink, promptForAccountsToUnlink, promptForAction, promptForDefaultAccount, } from './prompts.js';
|
|
4
|
+
import { ACTION_RESULT_STATUS, } from '../../types/Link.js';
|
|
5
|
+
import { getDefaultAccountOverrideFilePath, removeDefaultAccountOverrideFile, } from '@hubspot/local-dev-lib/config/defaultAccountOverride';
|
|
6
|
+
import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
|
|
7
|
+
import { authenticateNewAccount } from '../accountAuth.js';
|
|
8
|
+
import { commands } from '../../lang/en.js';
|
|
9
|
+
import { uiLogger } from '../ui/logger.js';
|
|
10
|
+
export class ActionHandlers {
|
|
11
|
+
static async link({ state, context, }) {
|
|
12
|
+
const { eligibleAccounts, inEligibleAccounts } = context.globalAccountsList.reduce((accumulator, account) => {
|
|
13
|
+
if (state.accounts.includes(account.accountId)) {
|
|
14
|
+
accumulator.inEligibleAccounts.push(account);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
accumulator.eligibleAccounts.push(account);
|
|
18
|
+
}
|
|
19
|
+
return accumulator;
|
|
20
|
+
}, { eligibleAccounts: [], inEligibleAccounts: [] });
|
|
21
|
+
const toAdd = await promptForAccountsToLink(context, eligibleAccounts, inEligibleAccounts, state.localDefaultAccount);
|
|
22
|
+
const accounts = [...state.accounts, ...toAdd];
|
|
23
|
+
uiLogger.info(commands.account.subcommands.link.events.accountsLinked(toAdd.length));
|
|
24
|
+
const overrideFilePath = getDefaultAccountOverrideFilePath();
|
|
25
|
+
if (overrideFilePath &&
|
|
26
|
+
context.accountOverrideId &&
|
|
27
|
+
accounts.includes(context.accountOverrideId)) {
|
|
28
|
+
uiLogger.log(commands.account.subcommands.link.events.overrideAccountDetected(context.accountOverrideId));
|
|
29
|
+
uiLogger.log('');
|
|
30
|
+
const useOverride = await confirmPrompt(commands.account.subcommands.link.prompts.keepAsDefault);
|
|
31
|
+
removeDefaultAccountOverrideFile();
|
|
32
|
+
uiLogger.success(commands.account.subcommands.link.events.overrideFileRemoved);
|
|
33
|
+
if (useOverride) {
|
|
34
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(context.accountOverrideId));
|
|
35
|
+
return {
|
|
36
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
37
|
+
settings: {
|
|
38
|
+
accounts,
|
|
39
|
+
localDefaultAccount: context.accountOverrideId,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const localDefaultAccount = await resolveDefaultAccount({
|
|
45
|
+
accounts,
|
|
46
|
+
currentDefault: state.localDefaultAccount,
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
50
|
+
settings: { accounts, localDefaultAccount },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
static async unlink({ state }) {
|
|
54
|
+
const toRemove = await promptForAccountsToUnlink(state.accounts, state.localDefaultAccount);
|
|
55
|
+
if (toRemove.length === 0) {
|
|
56
|
+
return { status: ACTION_RESULT_STATUS.NOOP };
|
|
57
|
+
}
|
|
58
|
+
const remainingAccounts = state.accounts.filter(account => !toRemove.includes(account));
|
|
59
|
+
const defaultWasRemoved = state.localDefaultAccount !== undefined &&
|
|
60
|
+
toRemove.includes(state.localDefaultAccount);
|
|
61
|
+
// All accounts removed
|
|
62
|
+
if (remainingAccounts.length === 0) {
|
|
63
|
+
uiLogger.success(commands.account.subcommands.link.events.noAccountsLinked);
|
|
64
|
+
return {
|
|
65
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
66
|
+
settings: {
|
|
67
|
+
accounts: remainingAccounts,
|
|
68
|
+
localDefaultAccount: undefined,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Default was NOT removed — accounts remain, default unchanged
|
|
73
|
+
if (!defaultWasRemoved) {
|
|
74
|
+
uiLogger.success(commands.account.subcommands.link.events.accountsUnlinked(toRemove.length));
|
|
75
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountRemains(state.localDefaultAccount));
|
|
76
|
+
return {
|
|
77
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
78
|
+
settings: {
|
|
79
|
+
accounts: remainingAccounts,
|
|
80
|
+
localDefaultAccount: state.localDefaultAccount,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Default WAS removed — need a new default
|
|
85
|
+
uiLogger.warn(commands.account.subcommands.link.events.defaultAccountRemoved(remainingAccounts.length !== 1));
|
|
86
|
+
const localDefaultAccount = await resolveDefaultAccount({
|
|
87
|
+
accounts: remainingAccounts,
|
|
88
|
+
currentDefault: undefined,
|
|
89
|
+
});
|
|
90
|
+
if (remainingAccounts.length === 1) {
|
|
91
|
+
uiLogger.success(commands.account.subcommands.link.events.updatedLinkedAccounts);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
95
|
+
settings: { accounts: remainingAccounts, localDefaultAccount },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
static async authenticate({ state, context, args, }) {
|
|
99
|
+
const updatedConfig = await authenticateNewAccount({
|
|
100
|
+
env: args.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD,
|
|
101
|
+
setAsDefaultAccount: false,
|
|
102
|
+
});
|
|
103
|
+
if (!updatedConfig) {
|
|
104
|
+
return {
|
|
105
|
+
status: ACTION_RESULT_STATUS.ERROR,
|
|
106
|
+
reason: commands.account.subcommands.link.errors.authFailed,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const updatedContext = {
|
|
110
|
+
globalAccountsList: getAllConfigAccounts(),
|
|
111
|
+
globalDefaultAccount: getConfigDefaultAccountIfExists(),
|
|
112
|
+
accountOverrideId: context.accountOverrideId,
|
|
113
|
+
preselectedAccountId: updatedConfig.accountId,
|
|
114
|
+
};
|
|
115
|
+
return ActionHandlers.link({ state, context: updatedContext, args });
|
|
116
|
+
}
|
|
117
|
+
static async cancel() {
|
|
118
|
+
return {
|
|
119
|
+
status: ACTION_RESULT_STATUS.NOOP,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function handleLinkFlow({ settings, accountOverrideId, args, }) {
|
|
124
|
+
const context = {
|
|
125
|
+
globalAccountsList: getAllConfigAccounts(),
|
|
126
|
+
globalDefaultAccount: getConfigDefaultAccountIfExists(),
|
|
127
|
+
accountOverrideId,
|
|
128
|
+
};
|
|
129
|
+
const accounts = settings.accounts ?? [];
|
|
130
|
+
// The default account must be one of the linked accounts. This can get
|
|
131
|
+
// out of sync if the settings file is manually edited.
|
|
132
|
+
const hasInvalidDefault = settings.localDefaultAccount !== undefined &&
|
|
133
|
+
!accounts.includes(settings.localDefaultAccount);
|
|
134
|
+
if (hasInvalidDefault) {
|
|
135
|
+
uiLogger.warn(commands.account.subcommands.link.events.invalidDefaultAccount(settings.localDefaultAccount));
|
|
136
|
+
}
|
|
137
|
+
const initialState = {
|
|
138
|
+
localDefaultAccount: hasInvalidDefault
|
|
139
|
+
? undefined
|
|
140
|
+
: settings.localDefaultAccount,
|
|
141
|
+
accounts,
|
|
142
|
+
};
|
|
143
|
+
return runAction(initialState, context, args);
|
|
144
|
+
}
|
|
145
|
+
async function runAction(state, context, args) {
|
|
146
|
+
const action = await promptForAction(state);
|
|
147
|
+
return ActionHandlers[action]({ state, context, args });
|
|
148
|
+
}
|
|
149
|
+
async function resolveDefaultAccount({ accounts, currentDefault, prompt = '', }) {
|
|
150
|
+
if (accounts.length === 1) {
|
|
151
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(accounts[0]));
|
|
152
|
+
return accounts[0];
|
|
153
|
+
}
|
|
154
|
+
return promptForDefaultAccount(accounts, currentDefault, prompt);
|
|
155
|
+
}
|
|
156
|
+
export async function handleLinkedUseAction({ state, targetAccountId, }) {
|
|
157
|
+
if (targetAccountId !== undefined) {
|
|
158
|
+
if (state.accounts.includes(targetAccountId)) {
|
|
159
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(targetAccountId));
|
|
160
|
+
return {
|
|
161
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
162
|
+
settings: {
|
|
163
|
+
accounts: state.accounts,
|
|
164
|
+
localDefaultAccount: targetAccountId,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const accounts = [...state.accounts, targetAccountId];
|
|
169
|
+
uiLogger.info(commands.account.subcommands.link.events.accountsLinked(1));
|
|
170
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(targetAccountId));
|
|
171
|
+
return {
|
|
172
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
173
|
+
settings: { accounts, localDefaultAccount: targetAccountId },
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const localDefaultAccount = await resolveDefaultAccount({
|
|
177
|
+
accounts: state.accounts,
|
|
178
|
+
currentDefault: state.localDefaultAccount,
|
|
179
|
+
});
|
|
180
|
+
uiLogger.success(commands.account.subcommands.link.events.defaultAccountSet(localDefaultAccount));
|
|
181
|
+
return {
|
|
182
|
+
status: ACTION_RESULT_STATUS.SUCCESS,
|
|
183
|
+
settings: { accounts: state.accounts, localDefaultAccount },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HsSettingsFile } from '@hubspot/local-dev-lib/types/HsSettings';
|
|
2
|
+
export declare function isDirectoryLinked(settings: HsSettingsFile | null): settings is HsSettingsFile;
|
|
3
|
+
export declare function hasDeprecatedConfigConflict(commandArgs: (string | number)[]): boolean;
|
|
4
|
+
export declare function addAccountToLinkedSettings(accountId: number): void;
|
|
5
|
+
export declare function writeLinkedSettings(settings: HsSettingsFile, settingsPath: string): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { localConfigFileExists } from '@hubspot/local-dev-lib/config';
|
|
2
|
+
import { getHsSettingsFileIfExists, writeHsSettingsFile, } from '@hubspot/local-dev-lib/config/hsSettings';
|
|
3
|
+
import { uiLogger } from '../ui/logger.js';
|
|
4
|
+
import { debugError } from '../errorHandlers/index.js';
|
|
5
|
+
import { commands } from '../../lang/en.js';
|
|
6
|
+
export function isDirectoryLinked(settings) {
|
|
7
|
+
return settings !== null && settings.accounts.length > 0;
|
|
8
|
+
}
|
|
9
|
+
export function hasDeprecatedConfigConflict(commandArgs) {
|
|
10
|
+
if (localConfigFileExists()) {
|
|
11
|
+
uiLogger.error(commands.account.subcommands.link.shared.deprecatedConfigNotSupported(`hs ${commandArgs.join(' ')}`));
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
export function addAccountToLinkedSettings(accountId) {
|
|
17
|
+
if (localConfigFileExists()) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const settings = getHsSettingsFileIfExists();
|
|
21
|
+
if (!settings || settings.accounts.length === 0) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (settings.accounts.includes(accountId)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const updated = {
|
|
28
|
+
...settings,
|
|
29
|
+
accounts: [...settings.accounts, accountId],
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
writeHsSettingsFile(updated);
|
|
33
|
+
uiLogger.info(commands.account.subcommands.link.shared.accountAutoLinked(accountId));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
uiLogger.warn(commands.account.subcommands.link.shared.accountAutoLinkFailed(accountId));
|
|
37
|
+
debugError(err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function writeLinkedSettings(settings, settingsPath) {
|
|
41
|
+
try {
|
|
42
|
+
writeHsSettingsFile(settings);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
uiLogger.error(commands.account.subcommands.link.shared.writeSettingsFailed(settingsPath, err));
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HubSpotConfigAccount } from '@hubspot/local-dev-lib/types/Accounts';
|
|
2
|
+
import { ActionName, LinkContext } from '../../types/Link.js';
|
|
3
|
+
import { HsSettingsFile } from '@hubspot/local-dev-lib/types/HsSettings';
|
|
4
|
+
export declare function promptForAction(state: HsSettingsFile): Promise<ActionName>;
|
|
5
|
+
export declare function promptForDefaultAccount(accounts: number[], currentDefaultAccount: number | undefined, prompt?: string): Promise<number>;
|
|
6
|
+
export declare function promptForAccountsToLink(context: LinkContext, eligibleAccounts: HubSpotConfigAccount[], inEligibleAccounts: HubSpotConfigAccount[], localDefaultAccount: number | undefined): Promise<number[]>;
|
|
7
|
+
export declare function promptForAccountsToUnlink(accounts: number[], localDefaultAccount: number | undefined): Promise<number[]>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { promptUser } from '../prompts/promptUtils.js';
|
|
2
|
+
import { uiAccountDescription } from '../ui/index.js';
|
|
3
|
+
import { uiLogger } from '../ui/logger.js';
|
|
4
|
+
import { commands } from '../../lang/en.js';
|
|
5
|
+
import { buildAccountRow, getNameColumnWidth, buildAccountHeader, sortDefaultFirst, } from './accountTableUtils.js';
|
|
6
|
+
import { Separator } from '@inquirer/prompts';
|
|
7
|
+
function buildColumnarChoices(accounts, localDefaultAccount) {
|
|
8
|
+
const rows = accounts.map(a => ({
|
|
9
|
+
...buildAccountRow(a.accountId, a.accountId === localDefaultAccount),
|
|
10
|
+
disabled: a.disabled,
|
|
11
|
+
checked: a.checked,
|
|
12
|
+
hint: a.hint,
|
|
13
|
+
}));
|
|
14
|
+
const nameWidth = getNameColumnWidth(rows);
|
|
15
|
+
const header = buildAccountHeader(nameWidth);
|
|
16
|
+
return [
|
|
17
|
+
new Separator(header),
|
|
18
|
+
...rows.map(row => {
|
|
19
|
+
const label = `${row.name.padEnd(nameWidth)} ${row.accountId}`;
|
|
20
|
+
return {
|
|
21
|
+
name: row.hint ? `${label} ${row.hint}` : label,
|
|
22
|
+
short: uiAccountDescription(Number(row.accountId), false),
|
|
23
|
+
value: Number(row.accountId),
|
|
24
|
+
disabled: row.disabled,
|
|
25
|
+
checked: row.checked,
|
|
26
|
+
};
|
|
27
|
+
}),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
function mapLinkAccountChoices(eligibleAccounts, inEligibleAccounts, accountOverrideId, localDefaultAccount, preselectedAccountId) {
|
|
31
|
+
const sortedIneligible = sortDefaultFirst(inEligibleAccounts, localDefaultAccount);
|
|
32
|
+
const accounts = [
|
|
33
|
+
...eligibleAccounts.map(a => ({
|
|
34
|
+
accountId: a.accountId,
|
|
35
|
+
disabled: false,
|
|
36
|
+
checked: a.accountId === accountOverrideId ||
|
|
37
|
+
a.accountId === preselectedAccountId,
|
|
38
|
+
hint: a.accountId === accountOverrideId
|
|
39
|
+
? commands.account.subcommands.link.prompts.fromHsAccount
|
|
40
|
+
: a.accountId === preselectedAccountId
|
|
41
|
+
? commands.account.subcommands.link.prompts.newlyAuthenticated
|
|
42
|
+
: undefined,
|
|
43
|
+
})),
|
|
44
|
+
...sortedIneligible.map(a => ({
|
|
45
|
+
accountId: a.accountId,
|
|
46
|
+
disabled: commands.account.subcommands.link.prompts.alreadyLinked,
|
|
47
|
+
checked: false,
|
|
48
|
+
})),
|
|
49
|
+
];
|
|
50
|
+
return buildColumnarChoices(accounts, localDefaultAccount);
|
|
51
|
+
}
|
|
52
|
+
export async function promptForAction(state) {
|
|
53
|
+
const isSettingsEmpty = state.accounts.length === 0 && state.localDefaultAccount === undefined;
|
|
54
|
+
const { accountEditOption } = await promptUser({
|
|
55
|
+
type: 'list',
|
|
56
|
+
name: 'accountEditOption',
|
|
57
|
+
message: isSettingsEmpty
|
|
58
|
+
? commands.account.subcommands.link.prompts.howToProceed
|
|
59
|
+
: commands.account.subcommands.link.prompts.whatToDo,
|
|
60
|
+
choices: [
|
|
61
|
+
{
|
|
62
|
+
name: commands.account.subcommands.link.prompts.linkExisting,
|
|
63
|
+
value: 'link',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: commands.account.subcommands.link.prompts.authenticateNew,
|
|
67
|
+
value: 'authenticate',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: commands.account.subcommands.link.prompts.cancel,
|
|
71
|
+
value: 'cancel',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
uiLogger.log('');
|
|
76
|
+
return accountEditOption;
|
|
77
|
+
}
|
|
78
|
+
export async function promptForDefaultAccount(accounts, currentDefaultAccount, prompt = '') {
|
|
79
|
+
const choiceAccounts = accounts.map(accountId => ({
|
|
80
|
+
accountId,
|
|
81
|
+
}));
|
|
82
|
+
const choices = buildColumnarChoices(choiceAccounts, currentDefaultAccount);
|
|
83
|
+
const { defaultAccount } = await promptUser({
|
|
84
|
+
type: 'list',
|
|
85
|
+
name: 'defaultAccount',
|
|
86
|
+
pageSize: 20,
|
|
87
|
+
message: prompt || commands.account.subcommands.link.prompts.selectDefault,
|
|
88
|
+
choices,
|
|
89
|
+
default: currentDefaultAccount ?? undefined,
|
|
90
|
+
});
|
|
91
|
+
uiLogger.log('');
|
|
92
|
+
return defaultAccount;
|
|
93
|
+
}
|
|
94
|
+
export async function promptForAccountsToLink(context, eligibleAccounts, inEligibleAccounts, localDefaultAccount) {
|
|
95
|
+
const { accountsToAdd } = await promptUser({
|
|
96
|
+
type: 'checkbox',
|
|
97
|
+
name: 'accountsToAdd',
|
|
98
|
+
pageSize: 20,
|
|
99
|
+
message: commands.account.subcommands.link.prompts.selectToLink,
|
|
100
|
+
choices: mapLinkAccountChoices(eligibleAccounts, inEligibleAccounts, context.accountOverrideId, localDefaultAccount, context.preselectedAccountId),
|
|
101
|
+
validate: (answer) => {
|
|
102
|
+
if (answer.length === 0) {
|
|
103
|
+
return commands.account.subcommands.link.prompts.mustSelectOne;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
uiLogger.log('');
|
|
109
|
+
return accountsToAdd;
|
|
110
|
+
}
|
|
111
|
+
export async function promptForAccountsToUnlink(accounts, localDefaultAccount) {
|
|
112
|
+
const sortedAccounts = sortDefaultFirst(accounts, localDefaultAccount);
|
|
113
|
+
const choiceAccounts = sortedAccounts.map(accountId => ({
|
|
114
|
+
accountId,
|
|
115
|
+
}));
|
|
116
|
+
const choices = buildColumnarChoices(choiceAccounts, localDefaultAccount);
|
|
117
|
+
const { accountsToRemove } = await promptUser({
|
|
118
|
+
type: 'checkbox',
|
|
119
|
+
name: 'accountsToRemove',
|
|
120
|
+
pageSize: 20,
|
|
121
|
+
message: commands.account.subcommands.link.prompts.selectToUnlink,
|
|
122
|
+
choices,
|
|
123
|
+
});
|
|
124
|
+
uiLogger.log('');
|
|
125
|
+
return accountsToRemove;
|
|
126
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { commands } from '../../lang/en.js';
|
|
2
|
+
import { renderTable } from '../../ui/render.js';
|
|
3
|
+
import { buildAccountRow, sortDefaultFirst } from './accountTableUtils.js';
|
|
4
|
+
export async function renderLinkedAccountsTable(settings) {
|
|
5
|
+
const labels = commands.account.subcommands.list.labels;
|
|
6
|
+
const tableHeader = [labels.name, labels.accountId];
|
|
7
|
+
const sortedAccounts = sortDefaultFirst(settings.accounts, settings.localDefaultAccount);
|
|
8
|
+
const tableData = sortedAccounts.map(accountId => {
|
|
9
|
+
const isDefault = accountId === settings.localDefaultAccount;
|
|
10
|
+
const row = buildAccountRow(accountId, isDefault);
|
|
11
|
+
return [row.name, row.accountId];
|
|
12
|
+
});
|
|
13
|
+
await renderTable(tableHeader, tableData, true);
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function warnIfLinkedDirectory(args: (string | number)[]): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getHsSettingsFilePath } from '@hubspot/local-dev-lib/config/hsSettings';
|
|
2
|
+
import { uiLogger } from '../ui/logger.js';
|
|
3
|
+
import { lib } from '../../lang/en.js';
|
|
4
|
+
export function warnIfLinkedDirectory(args) {
|
|
5
|
+
if (getHsSettingsFilePath() === null) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
uiLogger.warn(lib.linkedDirectory.warning(`hs ${args.join(' ')}`, getHsSettingsFilePath()));
|
|
9
|
+
}
|
|
@@ -134,9 +134,12 @@ class _ProjectLogsManager {
|
|
|
134
134
|
return this.functions.map(serverlessFunction => serverlessFunction.componentName);
|
|
135
135
|
}
|
|
136
136
|
setFunction(functionName) {
|
|
137
|
-
if (
|
|
137
|
+
if (this.functions.length === 0) {
|
|
138
138
|
throw new Error(commands.project.logs.errors.noFunctionsInProject);
|
|
139
139
|
}
|
|
140
|
+
if (!functionName) {
|
|
141
|
+
throw new Error(commands.project.logs.errors.functionNameRequired);
|
|
142
|
+
}
|
|
140
143
|
this.selectedFunction = this.functions.find(serverlessFunction => serverlessFunction.componentName === functionName);
|
|
141
144
|
if (!this.selectedFunction) {
|
|
142
145
|
throw new Error(commands.project.logs.errors.noFunctionWithName(functionName));
|
|
@@ -21,12 +21,13 @@ declare class DevServerManager_DEPRECATED {
|
|
|
21
21
|
[key: string]: Component;
|
|
22
22
|
}) => Promise<void>): Promise<void>;
|
|
23
23
|
arrangeComponentsByType(components: Component[]): ComponentsByType;
|
|
24
|
-
setup({ components, onUploadRequired, accountId, setActiveApp, exit, }: {
|
|
24
|
+
setup({ components, onUploadRequired, accountId, setActiveApp, exit, port, }: {
|
|
25
25
|
components: Component[];
|
|
26
26
|
onUploadRequired: () => void;
|
|
27
27
|
accountId: number;
|
|
28
28
|
setActiveApp: (appUid: string | undefined) => Promise<void>;
|
|
29
29
|
exit: ExitFunction;
|
|
30
|
+
port?: number;
|
|
30
31
|
}): Promise<void>;
|
|
31
32
|
start({ accountId, projectConfig, }: {
|
|
32
33
|
accountId: number;
|
|
@@ -57,7 +57,7 @@ class DevServerManager_DEPRECATED {
|
|
|
57
57
|
return acc;
|
|
58
58
|
}, {});
|
|
59
59
|
}
|
|
60
|
-
async setup({ components, onUploadRequired, accountId, setActiveApp, exit, }) {
|
|
60
|
+
async setup({ components, onUploadRequired, accountId, setActiveApp, exit, port, }) {
|
|
61
61
|
this.componentsByType = this.arrangeComponentsByType(components);
|
|
62
62
|
let env;
|
|
63
63
|
const accountConfig = getConfigAccountById(accountId);
|
|
@@ -65,7 +65,7 @@ class DevServerManager_DEPRECATED {
|
|
|
65
65
|
env = accountConfig.env;
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
68
|
-
await startPortManagerServer();
|
|
68
|
+
await startPortManagerServer(port);
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
71
71
|
logError(e);
|
|
@@ -16,6 +16,7 @@ type LocalDevManagerConstructorOptions = {
|
|
|
16
16
|
runnableComponents: Component[];
|
|
17
17
|
env: Environment;
|
|
18
18
|
exit: ExitFunction;
|
|
19
|
+
port?: number;
|
|
19
20
|
};
|
|
20
21
|
declare class LocalDevManager_DEPRECATED {
|
|
21
22
|
targetAccountId: number;
|
|
@@ -39,6 +40,7 @@ declare class LocalDevManager_DEPRECATED {
|
|
|
39
40
|
mostRecentUploadWarning: string | null;
|
|
40
41
|
private devSessionManager;
|
|
41
42
|
private exit;
|
|
43
|
+
private port?;
|
|
42
44
|
constructor(options: LocalDevManagerConstructorOptions);
|
|
43
45
|
setActiveApp(appUid?: string): Promise<void>;
|
|
44
46
|
setActivePublicAppData(): Promise<void>;
|
|
@@ -48,6 +48,7 @@ class LocalDevManager_DEPRECATED {
|
|
|
48
48
|
mostRecentUploadWarning;
|
|
49
49
|
devSessionManager;
|
|
50
50
|
exit;
|
|
51
|
+
port;
|
|
51
52
|
constructor(options) {
|
|
52
53
|
this.targetAccountId = options.targetAccountId;
|
|
53
54
|
// The account that the project exists in. This is not always the targetAccountId
|
|
@@ -67,6 +68,7 @@ class LocalDevManager_DEPRECATED {
|
|
|
67
68
|
this.publicAppActiveInstalls = null;
|
|
68
69
|
this.mostRecentUploadWarning = null;
|
|
69
70
|
this.exit = options.exit;
|
|
71
|
+
this.port = options.port;
|
|
70
72
|
this.projectSourceDir = path.join(this.projectDir, this.projectConfig.srcDir);
|
|
71
73
|
if (!this.targetAccountId || !this.projectConfig || !this.projectDir) {
|
|
72
74
|
uiLogger.error(lib.LocalDevManager.failedToInitialize);
|
|
@@ -337,6 +339,7 @@ class LocalDevManager_DEPRECATED {
|
|
|
337
339
|
accountId: this.targetAccountId,
|
|
338
340
|
setActiveApp: this.setActiveApp.bind(this),
|
|
339
341
|
exit: this.exit,
|
|
342
|
+
port: this.port,
|
|
340
343
|
});
|
|
341
344
|
return true;
|
|
342
345
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { triggerAutoRelease, getAutoReleaseStatus, } from '@hubspot/local-dev-lib/api/projects';
|
|
2
|
+
import { DEFAULT_POLLING_DELAY, PREVIEW_POLL_TIMEOUT } from '../constants.js';
|
|
3
|
+
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
4
|
+
import { logError, ApiErrorContext } from '../errorHandlers/index.js';
|
|
5
|
+
import { lib } from '../../lang/en.js';
|
|
6
|
+
export async function triggerAndPollPreview(accountId, projectId, buildId, targetPortalId) {
|
|
7
|
+
let triggerResponse;
|
|
8
|
+
SpinniesManager.add('preview', {
|
|
9
|
+
text: lib.projectPreview.triggeringPreview(buildId, targetPortalId),
|
|
10
|
+
succeedColor: 'white',
|
|
11
|
+
});
|
|
12
|
+
try {
|
|
13
|
+
const { data } = await triggerAutoRelease(accountId, projectId, buildId, targetPortalId);
|
|
14
|
+
triggerResponse = data;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
SpinniesManager.fail('preview', {
|
|
18
|
+
text: lib.projectPreview.triggerFailed,
|
|
19
|
+
});
|
|
20
|
+
logError(e, new ApiErrorContext({
|
|
21
|
+
accountId,
|
|
22
|
+
request: 'preview trigger',
|
|
23
|
+
}));
|
|
24
|
+
return { succeeded: false };
|
|
25
|
+
}
|
|
26
|
+
const { releaseTag, appId } = triggerResponse;
|
|
27
|
+
SpinniesManager.update('preview', {
|
|
28
|
+
text: lib.projectPreview.pollingStatus(releaseTag, targetPortalId),
|
|
29
|
+
});
|
|
30
|
+
try {
|
|
31
|
+
await pollPreviewStatus(accountId, projectId, targetPortalId, releaseTag, appId);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
SpinniesManager.fail('preview', {
|
|
35
|
+
text: lib.projectPreview.pollFailed,
|
|
36
|
+
});
|
|
37
|
+
logError(e, new ApiErrorContext({
|
|
38
|
+
accountId,
|
|
39
|
+
request: 'preview status',
|
|
40
|
+
}));
|
|
41
|
+
return { succeeded: false, releaseTag, appId };
|
|
42
|
+
}
|
|
43
|
+
SpinniesManager.succeed('preview', {
|
|
44
|
+
text: lib.projectPreview.succeeded(releaseTag, targetPortalId),
|
|
45
|
+
});
|
|
46
|
+
return { succeeded: true, releaseTag, appId };
|
|
47
|
+
}
|
|
48
|
+
async function pollPreviewStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId) {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
while (Date.now() - startTime < PREVIEW_POLL_TIMEOUT) {
|
|
51
|
+
await new Promise(resolve => setTimeout(resolve, DEFAULT_POLLING_DELAY));
|
|
52
|
+
const { data } = await getAutoReleaseStatus(accountId, projectId, targetPortalId, expectedReleaseTag, appId);
|
|
53
|
+
if (data.status === 'COMPLETE') {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
throw new Error(lib.projectPreview.timeout);
|
|
58
|
+
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
export declare const REQUIRED_PACKAGES_AND_MIN_VERSIONS: {
|
|
2
2
|
readonly eslint: "9.0.0";
|
|
3
|
-
readonly '@
|
|
4
|
-
readonly '@typescript-eslint/parser': "8.46.4";
|
|
3
|
+
readonly '@eslint/js': "9.0.0";
|
|
5
4
|
readonly 'typescript-eslint': "8.46.4";
|
|
5
|
+
readonly '@hubspot/eslint-config-ui-extensions': "1.0.0";
|
|
6
|
+
readonly 'eslint-config-prettier': "10.0.0";
|
|
7
|
+
readonly 'eslint-plugin-react': "7.0.0";
|
|
8
|
+
readonly 'eslint-plugin-react-hooks': "7.0.0";
|
|
9
|
+
readonly 'eslint-plugin-unused-imports': "4.0.0";
|
|
10
|
+
readonly prettier: "3.0.0";
|
|
6
11
|
readonly jiti: "2.6.1";
|
|
7
12
|
};
|
|
13
|
+
export declare const LINT_SCRIPTS: {
|
|
14
|
+
readonly lint: "eslint .";
|
|
15
|
+
readonly 'lint:fix': "eslint . --fix";
|
|
16
|
+
};
|
|
8
17
|
export declare function isEslintInstalled(directory: string): boolean;
|
|
9
18
|
export declare function areAllLintPackagesInstalled(directory: string): boolean;
|
|
10
19
|
export declare function getMissingLintPackages(directory: string): {
|
|
@@ -13,7 +22,7 @@ export declare function getMissingLintPackages(directory: string): {
|
|
|
13
22
|
export declare function hasEslintConfig(directory: string): boolean;
|
|
14
23
|
export declare function hasDeprecatedEslintConfig(directory: string): boolean;
|
|
15
24
|
export declare function getDeprecatedEslintConfigFiles(directory: string): string[];
|
|
16
|
-
export declare function createEslintConfig(directory: string): string
|
|
25
|
+
export declare function createEslintConfig(directory: string, platformVersion?: string | null): Promise<string>;
|
|
17
26
|
export declare function lintPackagesInDirectory(directory: string, projectDir?: string): Promise<{
|
|
18
27
|
success: boolean;
|
|
19
28
|
output: string;
|
|
@@ -31,3 +40,8 @@ export declare function displayLintResults(results: Array<{
|
|
|
31
40
|
success: boolean;
|
|
32
41
|
output: string;
|
|
33
42
|
}>): void;
|
|
43
|
+
export declare function getMissingLintScripts(directory: string): string[];
|
|
44
|
+
export declare function addLintScriptsToPackageJson(directory: string): {
|
|
45
|
+
added: string[];
|
|
46
|
+
relativePath: string;
|
|
47
|
+
};
|