@hubspot/cli 8.0.10-experimental.7 → 8.0.11-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.
Files changed (170) hide show
  1. package/bin/cli.js +2 -0
  2. package/commands/account/auth.js +12 -22
  3. package/commands/account/clean.js +5 -6
  4. package/commands/account/createOverride.js +7 -7
  5. package/commands/account/info.js +2 -1
  6. package/commands/account/list.js +3 -5
  7. package/commands/account/remove.js +2 -3
  8. package/commands/account/removeOverride.js +8 -10
  9. package/commands/account/rename.js +5 -6
  10. package/commands/account/use.js +8 -19
  11. package/commands/api.d.ts +10 -0
  12. package/commands/api.js +164 -0
  13. package/commands/app/migrate.js +8 -8
  14. package/commands/app/secret/add.js +6 -7
  15. package/commands/app/secret/delete.js +9 -10
  16. package/commands/app/secret/list.js +6 -7
  17. package/commands/app/secret/update.js +8 -9
  18. package/commands/auth.js +12 -12
  19. package/commands/cms/app/create.js +9 -5
  20. package/commands/cms/convertFields.js +8 -8
  21. package/commands/cms/delete.js +2 -3
  22. package/commands/cms/fetch.js +7 -7
  23. package/commands/cms/function/create.js +9 -5
  24. package/commands/cms/function/deploy.js +2 -3
  25. package/commands/cms/function/list.js +11 -7
  26. package/commands/cms/function/logs.js +17 -23
  27. package/commands/cms/function/server.js +2 -3
  28. package/commands/cms/getReactModule.js +7 -8
  29. package/commands/cms/lighthouseScore.js +25 -24
  30. package/commands/cms/lint.js +4 -5
  31. package/commands/cms/list.js +5 -6
  32. package/commands/cms/module/create.js +9 -5
  33. package/commands/cms/module/marketplace-validate.js +7 -8
  34. package/commands/cms/mv.js +2 -3
  35. package/commands/cms/template/create.js +10 -6
  36. package/commands/cms/theme/create.js +5 -5
  37. package/commands/cms/theme/generate-selectors.js +5 -4
  38. package/commands/cms/theme/marketplace-validate.js +8 -9
  39. package/commands/cms/theme/preview.js +16 -8
  40. package/commands/cms/upload.js +15 -12
  41. package/commands/cms/watch.js +5 -5
  42. package/commands/cms/webpack/create.js +5 -5
  43. package/commands/completion.js +3 -5
  44. package/commands/config/migrate.js +6 -7
  45. package/commands/config/set.js +5 -6
  46. package/commands/customObject/create.js +4 -5
  47. package/commands/customObject/createSchema.js +4 -5
  48. package/commands/customObject/deleteSchema.js +4 -5
  49. package/commands/customObject/fetchAllSchemas.js +2 -3
  50. package/commands/customObject/fetchSchema.js +2 -3
  51. package/commands/customObject/listSchemas.js +2 -3
  52. package/commands/customObject/updateSchema.js +4 -5
  53. package/commands/doctor.js +8 -8
  54. package/commands/feedback.js +6 -4
  55. package/commands/filemanager/fetch.js +5 -6
  56. package/commands/filemanager/upload.js +5 -5
  57. package/commands/getStarted.js +14 -16
  58. package/commands/hubdb/clear.js +5 -6
  59. package/commands/hubdb/create.js +4 -5
  60. package/commands/hubdb/delete.js +8 -9
  61. package/commands/hubdb/fetch.js +5 -6
  62. package/commands/hubdb/list.js +16 -14
  63. package/commands/init.js +14 -17
  64. package/commands/mcp/setup.js +5 -6
  65. package/commands/mcp/start.js +2 -3
  66. package/commands/open.js +4 -5
  67. package/commands/project/add.js +10 -5
  68. package/commands/project/create.js +10 -10
  69. package/commands/project/delete.d.ts +7 -0
  70. package/commands/project/delete.js +74 -0
  71. package/commands/project/deploy.js +36 -34
  72. package/commands/project/dev/deprecatedFlow.js +42 -15
  73. package/commands/project/dev/index.d.ts +3 -3
  74. package/commands/project/dev/index.js +24 -30
  75. package/commands/project/dev/unifiedFlow.js +37 -14
  76. package/commands/project/download.js +10 -11
  77. package/commands/project/info.d.ts +4 -0
  78. package/commands/project/info.js +67 -0
  79. package/commands/project/installDeps.js +9 -6
  80. package/commands/project/lint.js +11 -8
  81. package/commands/project/list.js +14 -14
  82. package/commands/project/listBuilds.js +8 -6
  83. package/commands/project/logs.js +5 -6
  84. package/commands/project/migrate.js +8 -8
  85. package/commands/project/open.js +5 -6
  86. package/commands/project/profile/add.js +12 -8
  87. package/commands/project/profile/delete.js +15 -11
  88. package/commands/project/updateDeps.js +9 -6
  89. package/commands/project/upload.js +31 -17
  90. package/commands/project/validate.js +11 -11
  91. package/commands/project/watch.js +20 -20
  92. package/commands/project.js +4 -0
  93. package/commands/sandbox/create.js +15 -15
  94. package/commands/sandbox/delete.js +13 -14
  95. package/commands/secret/addSecret.js +6 -7
  96. package/commands/secret/deleteSecret.js +5 -6
  97. package/commands/secret/listSecret.js +2 -3
  98. package/commands/secret/updateSecret.js +4 -5
  99. package/commands/testAccount/create.d.ts +1 -1
  100. package/commands/testAccount/create.js +20 -16
  101. package/commands/testAccount/createConfig.js +7 -8
  102. package/commands/testAccount/delete.js +27 -18
  103. package/commands/testAccount/importData.js +6 -7
  104. package/commands/upgrade.js +9 -10
  105. package/lang/en.d.ts +123 -5
  106. package/lang/en.js +121 -6
  107. package/lib/accountAuth.js +2 -2
  108. package/lib/buildAccount.js +3 -3
  109. package/lib/constants.d.ts +0 -1
  110. package/lib/constants.js +0 -1
  111. package/lib/doctor/Diagnosis.js +5 -5
  112. package/lib/errorHandlers/index.js +4 -3
  113. package/lib/errorHandlers/suppressError.js +4 -0
  114. package/lib/errors/PromptExitError.d.ts +4 -2
  115. package/lib/errors/PromptExitError.js +3 -0
  116. package/lib/hasFeature.js +1 -2
  117. package/lib/middleware/autoUpdateMiddleware.js +6 -3
  118. package/lib/process.d.ts +1 -1
  119. package/lib/process.js +10 -3
  120. package/lib/projects/create/v2.js +1 -2
  121. package/lib/projects/delete.d.ts +13 -0
  122. package/lib/projects/delete.js +193 -0
  123. package/lib/projects/localDev/AppDevModeInterface.js +11 -11
  124. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +3 -1
  125. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +2 -2
  126. package/lib/projects/localDev/DevSessionManager.d.ts +6 -3
  127. package/lib/projects/localDev/DevSessionManager.js +31 -19
  128. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +3 -0
  129. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +16 -12
  130. package/lib/projects/localDev/LocalDevProcess.js +6 -5
  131. package/lib/projects/localDev/LocalDevState.d.ts +3 -2
  132. package/lib/projects/localDev/LocalDevState.js +3 -1
  133. package/lib/projects/localDev/helpers/account.d.ts +4 -3
  134. package/lib/projects/localDev/helpers/account.js +16 -19
  135. package/lib/projects/localDev/helpers/process.d.ts +1 -1
  136. package/lib/projects/localDev/helpers/process.js +4 -10
  137. package/lib/projects/localDev/helpers/project.d.ts +4 -3
  138. package/lib/projects/localDev/helpers/project.js +31 -15
  139. package/lib/projects/projectInfo.d.ts +5 -0
  140. package/lib/projects/projectInfo.js +82 -0
  141. package/lib/projects/projectProfiles.d.ts +1 -2
  142. package/lib/projects/projectProfiles.js +5 -17
  143. package/lib/projects/upload.js +19 -0
  144. package/lib/projects/workspaces.d.ts +42 -0
  145. package/lib/projects/workspaces.js +350 -0
  146. package/lib/prompts/createApiSamplePrompt.js +4 -0
  147. package/lib/prompts/projectProfilePrompt.d.ts +2 -0
  148. package/lib/prompts/projectProfilePrompt.js +46 -0
  149. package/lib/prompts/promptUtils.js +3 -2
  150. package/lib/prompts/selectHubDBTablePrompt.js +2 -2
  151. package/lib/prompts/selectPublicAppForMigrationPrompt.js +2 -2
  152. package/lib/theme/cmsDevServerProcess.d.ts +2 -0
  153. package/lib/theme/cmsDevServerProcess.js +7 -6
  154. package/lib/ui/SpinniesManager.d.ts +1 -0
  155. package/lib/ui/SpinniesManager.js +20 -6
  156. package/lib/ui/spinniesUtils.d.ts +0 -1
  157. package/lib/ui/spinniesUtils.js +6 -16
  158. package/lib/usageTracking.d.ts +3 -4
  159. package/lib/yargs/makeYargsBuilder.d.ts +13 -0
  160. package/lib/yargs/makeYargsBuilder.js +33 -0
  161. package/lib/yargs/makeYargsHandlerWithUsageTracking.d.ts +3 -0
  162. package/lib/yargs/makeYargsHandlerWithUsageTracking.js +95 -0
  163. package/lib/yargs/strictEnforceBoolean.d.ts +1 -0
  164. package/lib/yargs/strictEnforceBoolean.js +13 -0
  165. package/lib/yargsUtils.d.ts +3 -16
  166. package/lib/yargsUtils.js +3 -48
  167. package/package.json +5 -4
  168. package/types/LocalDev.d.ts +5 -0
  169. package/types/Projects.d.ts +19 -0
  170. package/types/Yargs.d.ts +18 -1
@@ -6,3 +6,6 @@ export class PromptExitError extends Error {
6
6
  this.exitCode = exitCode;
7
7
  }
8
8
  }
9
+ export function isPromptExitError(e) {
10
+ return e instanceof PromptExitError;
11
+ }
package/lib/hasFeature.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { http } from '@hubspot/local-dev-lib/http';
2
2
  import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
3
- import { FEATURES } from './constants.js';
4
- const FEATURES_THAT_DEFAULT_ON = [FEATURES.APPS_HOME];
3
+ const FEATURES_THAT_DEFAULT_ON = [];
5
4
  export async function hasFeature(accountId, feature) {
6
5
  const { data: { enabledFeatures }, } = await fetchEnabledFeatures(accountId);
7
6
  if (enabledFeatures[feature] === undefined &&
@@ -42,6 +42,10 @@ export async function autoUpdateCLI(argv) {
42
42
  debugError(e);
43
43
  }
44
44
  const cliUpgradeInfo = getCliUpgradeInfo();
45
+ // Ignore all update notifications if the current version is a pre-release (contains a hyphen)
46
+ if (cliUpgradeInfo.current && cliUpgradeInfo.current.includes('-')) {
47
+ showManualInstallHelp = false;
48
+ }
45
49
  if (isAllowAutoUpdatesEnabled &&
46
50
  cliUpgradeInfo.current &&
47
51
  cliUpgradeInfo.latest &&
@@ -49,9 +53,8 @@ export async function autoUpdateCLI(argv) {
49
53
  !argv.useEnv &&
50
54
  !process.env.SKIP_HUBSPOT_CLI_AUTO_UPDATES &&
51
55
  !preventAutoUpdateForCommand(argv._)) {
52
- // Ignore all update notifications if the current version is a pre-release (contains a hyphen)
53
- if (cliUpgradeInfo.current.includes('-')) {
54
- showManualInstallHelp = false;
56
+ if (!showManualInstallHelp) {
57
+ // Pre-release version detected, skip auto-update
55
58
  }
56
59
  else if (!['major', 'latest'].includes(cliUpgradeInfo.type)) {
57
60
  // type "latest" => current installed version is latest
package/lib/process.d.ts CHANGED
@@ -8,6 +8,6 @@ interface KeyPress {
8
8
  export declare const TERMINATION_SIGNALS: string[];
9
9
  export declare function handleExit(callback: (onTerminate: {
10
10
  isSIGHUP: boolean;
11
- }) => void): void;
11
+ }) => void): () => void;
12
12
  export declare function handleKeypress(callback: (onKeyPress: KeyPress) => void): void;
13
13
  export {};
package/lib/process.js CHANGED
@@ -16,9 +16,9 @@ export const TERMINATION_SIGNALS = [
16
16
  ];
17
17
  export function handleExit(callback) {
18
18
  let exitInProgress = false;
19
+ const listeners = [];
19
20
  TERMINATION_SIGNALS.forEach(signal => {
20
- process.removeAllListeners(signal);
21
- process.on(signal, async (...args) => {
21
+ const handler = async (...args) => {
22
22
  // Prevent duplicate exit handling
23
23
  if (!exitInProgress) {
24
24
  exitInProgress = true;
@@ -38,8 +38,15 @@ export function handleExit(callback) {
38
38
  }
39
39
  await callback({ isSIGHUP });
40
40
  }
41
- });
41
+ };
42
+ listeners.push({ signal, handler });
43
+ process.on(signal, handler);
42
44
  });
45
+ return () => {
46
+ for (const { signal, handler } of listeners) {
47
+ process.removeListener(signal, handler);
48
+ }
49
+ };
43
50
  }
44
51
  export function handleKeypress(callback) {
45
52
  readline.createInterface(process.stdin, process.stdout);
@@ -1,7 +1,7 @@
1
1
  import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, EMPTY_PROJECT, PROJECT_WITH_APP, FEATURES, } from '../../constants.js';
2
2
  import { commands, lib } from '../../../lang/en.js';
3
3
  import { listPrompt } from '../../prompts/promptUtils.js';
4
- import { APP_EVENTS_KEY as AppEventsKey, PAGES_KEY as PagesKey, } from '@hubspot/project-parsing-lib/constants';
4
+ import { APP_EVENTS_KEY as AppEventsKey } from '@hubspot/project-parsing-lib/constants';
5
5
  import { isV2Project } from '../platformVersion.js';
6
6
  import path from 'path';
7
7
  import { getConfigForPlatformVersion } from './legacy.js';
@@ -47,7 +47,6 @@ export async function createV2App(providedAuth, providedDistribution) {
47
47
  }
48
48
  const componentTypeToGateMap = {
49
49
  [AppEventsKey]: FEATURES.APP_EVENTS,
50
- [PagesKey]: FEATURES.APPS_HOME,
51
50
  'workflow-action-tool': FEATURES.AGENT_TOOLS,
52
51
  };
53
52
  export async function calculateComponentTemplateChoices(components, authType, distribution, accountId, projectMetadata) {
@@ -0,0 +1,13 @@
1
+ export declare const DELETION_POLL_TIMEOUT_MS: number;
2
+ export declare const DELETION_DEPLOY_SUCCESS_STATES: string[];
3
+ export declare const DELETION_DEPLOY_ERROR_STATES: ("FAILURE" | "ERROR" | "REVERTED")[];
4
+ export declare function resolveProjectName(accountId: number, projectArg: string | undefined): Promise<string>;
5
+ export declare function fetchProjectInstallCount(accountId: number, projectId: number): Promise<number>;
6
+ export declare function checkDeployedComponents(accountId: number, projectName: string): Promise<{
7
+ platformVersion: string;
8
+ hasUnifiedComponents: boolean;
9
+ projectId: number;
10
+ }>;
11
+ export declare function deleteDeployedComponents(accountId: number, projectName: string): Promise<void>;
12
+ export declare function handleProjectDeletion(accountId: number, projectName: string): Promise<void>;
13
+ export declare function confirmDeletion(projectName: string, accountId: number, projectId: number): Promise<void>;
@@ -0,0 +1,193 @@
1
+ import { fetchProjects, fetchProject, stageProjectForDeletion, getDeployStatus, deleteProject, } from '@hubspot/local-dev-lib/api/projects';
2
+ import { fetchPublicAppsForPortal } from '@hubspot/local-dev-lib/api/appsDev';
3
+ import { debugError, getErrorMessage } from '../errorHandlers/index.js';
4
+ import { uiLogger } from '../ui/logger.js';
5
+ import SpinniesManager from '../ui/SpinniesManager.js';
6
+ import { commands } from '../../lang/en.js';
7
+ import { confirmPrompt, listPrompt } from '../prompts/promptUtils.js';
8
+ import { poll, DEFAULT_POLLING_STATES, DEFAULT_POLLING_STATUS_LOOKUP, } from '../polling.js';
9
+ import { PromptExitError } from '../errors/PromptExitError.js';
10
+ import { EXIT_CODES } from '../enums/exitCodes.js';
11
+ import { AUTO_GENERATED_COMPONENT_TYPES } from '@hubspot/project-parsing-lib/constants';
12
+ import { mapToUserFacingType } from '@hubspot/project-parsing-lib/transform';
13
+ import { isV2Project } from './platformVersion.js';
14
+ import { COMPONENT_TYPES, SUBCOMPONENT_TYPES, } from '@hubspot/local-dev-lib/enums/build';
15
+ export const DELETION_POLL_TIMEOUT_MS = 5 * 60 * 1000;
16
+ export const DELETION_DEPLOY_SUCCESS_STATES = [
17
+ DEFAULT_POLLING_STATES.SUCCESS,
18
+ 'FINISHED',
19
+ ];
20
+ export const DELETION_DEPLOY_ERROR_STATES = DEFAULT_POLLING_STATUS_LOOKUP.errorStates;
21
+ const LEGACY_COMPONENTS_TO_FILTER = [
22
+ SUBCOMPONENT_TYPES.SERVERLESS_PKG,
23
+ SUBCOMPONENT_TYPES.REACT_EXTENSION,
24
+ SUBCOMPONENT_TYPES.PACKAGE_LOCK_FILE,
25
+ COMPONENT_TYPES.PRIVATE_APP,
26
+ COMPONENT_TYPES.PUBLIC_APP,
27
+ ];
28
+ const legacyComponentFriendlyNames = {
29
+ [SUBCOMPONENT_TYPES.APP_ID]: 'Private App',
30
+ [SUBCOMPONENT_TYPES.PUBLIC_APP_ID]: 'Public App',
31
+ [SUBCOMPONENT_TYPES.CRM_CARD_V2]: 'Card',
32
+ [SUBCOMPONENT_TYPES.CARD_V2]: 'Card',
33
+ [SUBCOMPONENT_TYPES.SERVERLESS_FUNCTION]: 'Serverless Function',
34
+ [SUBCOMPONENT_TYPES.APP_FUNCTION]: 'App Function',
35
+ [SUBCOMPONENT_TYPES.AUTOMATION_ACTION]: 'Automation Action',
36
+ [SUBCOMPONENT_TYPES.WEBHOOKS]: 'Webhooks',
37
+ [COMPONENT_TYPES.THEME]: 'Theme',
38
+ [COMPONENT_TYPES.REACT_THEME]: 'React Theme',
39
+ };
40
+ function mapLegacyComponentToUserFriendlyName(type) {
41
+ return legacyComponentFriendlyNames[type] || 'unknown';
42
+ }
43
+ export async function resolveProjectName(accountId, projectArg) {
44
+ const { data } = await fetchProjects(accountId);
45
+ const projects = data.results;
46
+ if (projectArg) {
47
+ if (!projects.some(p => p.name === projectArg)) {
48
+ throw new Error(commands.project.delete.errors.projectNotFound(projectArg, accountId));
49
+ }
50
+ return projectArg;
51
+ }
52
+ if (projects.length === 0) {
53
+ throw new Error(commands.project.delete.errors.noProjectsFound(accountId));
54
+ }
55
+ const selected = await listPrompt(commands.project.delete.prompts.selectProject(accountId), {
56
+ choices: projects.map(p => ({ name: p.name, value: p.name })),
57
+ validate: value => !!value || commands.project.delete.prompts.validation.projectRequired,
58
+ });
59
+ if (!selected) {
60
+ throw new Error(commands.project.delete.prompts.validation.projectRequired);
61
+ }
62
+ return selected;
63
+ }
64
+ export async function fetchProjectInstallCount(accountId, projectId) {
65
+ try {
66
+ const { data } = await fetchPublicAppsForPortal(accountId);
67
+ return data.results
68
+ .filter(app => app.projectId === projectId)
69
+ .reduce((sum, app) => sum + app.publicApplicationInstallCounts.uniquePortalInstallCount, 0);
70
+ }
71
+ catch (e) {
72
+ debugError(e);
73
+ uiLogger.warn(commands.project.delete.logs.installCountUnknown);
74
+ return 0;
75
+ }
76
+ }
77
+ export async function checkDeployedComponents(accountId, projectName) {
78
+ const { data: projectData } = await fetchProject(accountId, projectName);
79
+ const platformVersion = projectData.deployedBuild?.platformVersion ||
80
+ projectData.latestBuild?.platformVersion;
81
+ if (!platformVersion) {
82
+ throw new Error(commands.project.delete.errors.noPlatformVersion);
83
+ }
84
+ if (!isV2Project(platformVersion)) {
85
+ const userVisibleComponents = [];
86
+ projectData.deployedBuild?.subbuildStatuses?.forEach(item => {
87
+ if (LEGACY_COMPONENTS_TO_FILTER.includes(item.buildType)) {
88
+ return;
89
+ }
90
+ userVisibleComponents.push(`${item.buildName} - (${mapLegacyComponentToUserFriendlyName(item.buildType)})`);
91
+ });
92
+ if (userVisibleComponents.length > 0) {
93
+ uiLogger.log(commands.project.delete.logs.componentsToDeleteLegacy(userVisibleComponents));
94
+ }
95
+ return {
96
+ platformVersion,
97
+ hasUnifiedComponents: false,
98
+ projectId: projectData.id,
99
+ };
100
+ }
101
+ try {
102
+ const { data } = await stageProjectForDeletion(accountId, projectName, true);
103
+ if (data.hasDeployedComponents) {
104
+ const userVisibleComponents = data.componentsToRemove.filter(item => !AUTO_GENERATED_COMPONENT_TYPES.includes(mapToUserFacingType(item.componentType)));
105
+ uiLogger.log(commands.project.delete.logs.componentsToDeleteUnified(userVisibleComponents));
106
+ }
107
+ return {
108
+ hasUnifiedComponents: data.hasDeployedComponents,
109
+ platformVersion,
110
+ projectId: projectData.id,
111
+ };
112
+ }
113
+ catch (e) {
114
+ debugError(e);
115
+ throw new Error(commands.project.delete.errors.cannotDelete(projectName, getErrorMessage(e)));
116
+ }
117
+ }
118
+ export async function deleteDeployedComponents(accountId, projectName) {
119
+ SpinniesManager.add('removeComponents', {
120
+ text: commands.project.delete.logs.deletingComponents(projectName),
121
+ });
122
+ let projectDeletionResponse;
123
+ try {
124
+ const { data } = await stageProjectForDeletion(accountId, projectName, false);
125
+ projectDeletionResponse = data;
126
+ }
127
+ catch (e) {
128
+ debugError(e);
129
+ SpinniesManager.fail('removeComponents', {
130
+ text: commands.project.delete.errors.componentDeletionFailed(projectName),
131
+ });
132
+ throw new Error(commands.project.delete.errors.componentDeletionFailed(projectName));
133
+ }
134
+ const { deployId, hasDeployedComponents } = projectDeletionResponse;
135
+ if (deployId === undefined) {
136
+ if (hasDeployedComponents) {
137
+ SpinniesManager.fail('removeComponents', {
138
+ text: commands.project.delete.logs.unableToDetermineIfComponentsWereDeleted(projectName),
139
+ });
140
+ throw new Error(commands.project.delete.logs.unableToDetermineIfComponentsWereDeleted(projectName));
141
+ }
142
+ else {
143
+ SpinniesManager.succeed('removeComponents', {
144
+ text: commands.project.delete.logs.componentsDeleted(projectName),
145
+ });
146
+ }
147
+ return;
148
+ }
149
+ try {
150
+ await poll(() => getDeployStatus(accountId, projectName, deployId), {
151
+ successStates: DELETION_DEPLOY_SUCCESS_STATES,
152
+ errorStates: DELETION_DEPLOY_ERROR_STATES,
153
+ }, DELETION_POLL_TIMEOUT_MS);
154
+ SpinniesManager.succeed('removeComponents', {
155
+ text: commands.project.delete.logs.componentsDeleted(projectName),
156
+ });
157
+ }
158
+ catch (e) {
159
+ debugError(e);
160
+ SpinniesManager.fail('removeComponents', {
161
+ text: commands.project.delete.errors.componentDeletionFailed(projectName),
162
+ });
163
+ throw new Error(commands.project.delete.errors.componentDeletionFailed(projectName));
164
+ }
165
+ }
166
+ export async function handleProjectDeletion(accountId, projectName) {
167
+ try {
168
+ SpinniesManager.add('deleteProject', {
169
+ text: commands.project.delete.logs.deleting(projectName),
170
+ });
171
+ await deleteProject(accountId, projectName);
172
+ SpinniesManager.succeed('deleteProject', {
173
+ text: commands.project.delete.logs.deleted(projectName, accountId),
174
+ });
175
+ }
176
+ catch (e) {
177
+ SpinniesManager.fail('deleteProject', {
178
+ text: commands.project.delete.errors.deleteFailed(projectName),
179
+ });
180
+ throw e;
181
+ }
182
+ }
183
+ export async function confirmDeletion(projectName, accountId, projectId) {
184
+ const installCount = await fetchProjectInstallCount(accountId, projectId);
185
+ if (installCount > 0) {
186
+ uiLogger.warn(commands.project.delete.logs.installWarning(installCount));
187
+ }
188
+ const confirmed = await confirmPrompt(commands.project.delete.prompts.confirmDelete(projectName, accountId), { defaultAnswer: false });
189
+ if (!confirmed) {
190
+ uiLogger.log(commands.project.delete.logs.cancelled);
191
+ throw new PromptExitError(commands.project.delete.logs.cancelled, EXIT_CODES.SUCCESS);
192
+ }
193
+ }
@@ -6,7 +6,7 @@ import { EXIT_CODES } from '../../enums/exitCodes.js';
6
6
  import { isAppIRNode } from '../../projects/structure.js';
7
7
  import { uiLine } from '../../ui/index.js';
8
8
  import { logError } from '../../errorHandlers/index.js';
9
- import { PromptExitError } from '../../errors/PromptExitError.js';
9
+ import { isPromptExitError } from '../../errors/PromptExitError.js';
10
10
  import { installAppAutoPrompt, installAppBrowserPrompt, } from '../../prompts/installAppPrompt.js';
11
11
  import { confirmPrompt } from '../../prompts/promptUtils.js';
12
12
  import { lib } from '../../../lang/en.js';
@@ -28,7 +28,7 @@ class AppDevModeInterface {
28
28
  !this.localDevState.projectConfig ||
29
29
  !this.localDevState.projectDir) {
30
30
  uiLogger.error(lib.LocalDevManager.failedToInitialize);
31
- process.exit(EXIT_CODES.ERROR);
31
+ throw new Error(lib.LocalDevManager.failedToInitialize);
32
32
  }
33
33
  }
34
34
  getAppNodeFromProjectNodes(projectNodes) {
@@ -46,12 +46,12 @@ class AppDevModeInterface {
46
46
  // App data will never be accessed before being set
47
47
  if (!this.appNode) {
48
48
  uiLogger.log(lib.AppDevModeInterface.appDataNotFound);
49
- process.exit(EXIT_CODES.ERROR);
49
+ throw new Error(lib.AppDevModeInterface.appDataNotFound);
50
50
  }
51
51
  const data = this.localDevState.getAppDataByUid(this.appNode.uid);
52
52
  if (!data) {
53
53
  uiLogger.log(lib.AppDevModeInterface.appDataNotFound);
54
- process.exit(EXIT_CODES.ERROR);
54
+ throw new Error(lib.AppDevModeInterface.appDataNotFound);
55
55
  }
56
56
  return data;
57
57
  }
@@ -108,7 +108,7 @@ class AppDevModeInterface {
108
108
  text: lib.AppDevModeInterface.fetchAppData.error,
109
109
  });
110
110
  logError(e);
111
- process.exit(EXIT_CODES.ERROR);
111
+ return this.localDevState.actions.exit(EXIT_CODES.ERROR);
112
112
  }
113
113
  if (!appData) {
114
114
  return;
@@ -136,7 +136,7 @@ class AppDevModeInterface {
136
136
  uiLine();
137
137
  const proceed = await confirmPrompt(lib.LocalDevManager.activeInstallWarning.confirmationPrompt, { defaultAnswer: false });
138
138
  if (!proceed) {
139
- process.exit(EXIT_CODES.SUCCESS);
139
+ return this.localDevState.actions.exit(EXIT_CODES.SUCCESS);
140
140
  }
141
141
  this.localDevState.addUploadWarning(lib.AppDevModeInterface.defaultMarketplaceAppWarning(this.marketplaceAppInstalls));
142
142
  }
@@ -156,7 +156,7 @@ class AppDevModeInterface {
156
156
  const shouldInstall = await installAppAutoPrompt();
157
157
  if (!shouldInstall) {
158
158
  uiLogger.log(lib.AppDevModeInterface.autoInstallDeclined);
159
- process.exit(EXIT_CODES.SUCCESS);
159
+ return this.localDevState.actions.exit(EXIT_CODES.SUCCESS);
160
160
  }
161
161
  uiLogger.log('');
162
162
  SpinniesManager.add('autoInstallStaticAuthApp', {
@@ -225,7 +225,7 @@ class AppDevModeInterface {
225
225
  if (!serverIsRunningAtRedirectUrl) {
226
226
  uiLogger.log('');
227
227
  uiLogger.error(lib.AppDevModeInterface.oauthAppRedirectUrlError(redirectUrl));
228
- process.exit(EXIT_CODES.ERROR);
228
+ return this.localDevState.actions.exit(EXIT_CODES.ERROR);
229
229
  }
230
230
  }
231
231
  resolveAppInstallPromise() {
@@ -246,7 +246,7 @@ class AppDevModeInterface {
246
246
  }
247
247
  handleAppInstallFailureDevServerMessage() {
248
248
  uiLogger.error(lib.AppDevModeInterface.installationFailed);
249
- process.exit(EXIT_CODES.ERROR);
249
+ this.localDevState.actions.exit(EXIT_CODES.ERROR);
250
250
  }
251
251
  onDevServerMessage = async (message) => {
252
252
  if (message === LOCAL_DEV_SERVER_MESSAGE_TYPES.WEBSOCKET_SERVER_CONNECTED) {
@@ -314,7 +314,7 @@ class AppDevModeInterface {
314
314
  }
315
315
  }
316
316
  catch (e) {
317
- if (e instanceof PromptExitError) {
317
+ if (isPromptExitError(e)) {
318
318
  throw e;
319
319
  }
320
320
  if (SpinniesManager.pick('fetchAppData')) {
@@ -324,7 +324,7 @@ class AppDevModeInterface {
324
324
  });
325
325
  }
326
326
  logError(e);
327
- process.exit(EXIT_CODES.ERROR);
327
+ return this.localDevState.actions.exit(EXIT_CODES.ERROR);
328
328
  }
329
329
  }
330
330
  async start() {
@@ -1,4 +1,5 @@
1
1
  import { ProjectConfig, ComponentTypes, Component } from '../../../types/Projects.js';
2
+ import { ExitFunction } from '../../../types/Yargs.js';
2
3
  type DevServerInterface = {
3
4
  setup?: Function;
4
5
  start?: (options: object) => Promise<void>;
@@ -20,11 +21,12 @@ declare class DevServerManager_DEPRECATED {
20
21
  [key: string]: Component;
21
22
  }) => Promise<void>): Promise<void>;
22
23
  arrangeComponentsByType(components: Component[]): ComponentsByType;
23
- setup({ components, onUploadRequired, accountId, setActiveApp, }: {
24
+ setup({ components, onUploadRequired, accountId, setActiveApp, exit, }: {
24
25
  components: Component[];
25
26
  onUploadRequired: () => void;
26
27
  accountId: number;
27
28
  setActiveApp: (appUid: string | undefined) => Promise<void>;
29
+ exit: ExitFunction;
28
30
  }): Promise<void>;
29
31
  start({ accountId, projectConfig, }: {
30
32
  accountId: number;
@@ -57,7 +57,7 @@ class DevServerManager_DEPRECATED {
57
57
  return acc;
58
58
  }, {});
59
59
  }
60
- async setup({ components, onUploadRequired, accountId, setActiveApp, }) {
60
+ async setup({ components, onUploadRequired, accountId, setActiveApp, exit, }) {
61
61
  this.componentsByType = this.arrangeComponentsByType(components);
62
62
  let env;
63
63
  const accountConfig = getConfigAccountById(accountId);
@@ -69,7 +69,7 @@ class DevServerManager_DEPRECATED {
69
69
  }
70
70
  catch (e) {
71
71
  logError(e);
72
- process.exit(EXIT_CODES.ERROR);
72
+ return exit(EXIT_CODES.ERROR);
73
73
  }
74
74
  await this.iterateDevServers(async (serverInterface, compatibleComponents) => {
75
75
  if (serverInterface.setup) {
@@ -1,14 +1,17 @@
1
1
  import LocalDevLogger from './LocalDevLogger.js';
2
+ import { ExitFunction } from '../../../types/Yargs.js';
2
3
  type DevSessionManagerConstructorOptions = {
3
4
  targetTestingAccountId: number;
4
5
  localDevLogger?: LocalDevLogger;
6
+ exit: ExitFunction;
5
7
  };
6
8
  declare class DevSessionManager {
7
9
  localDevLogger?: LocalDevLogger;
8
10
  targetTestingAccountId: number;
9
- protected _devSessionId: number | undefined;
10
- private _heartbeatInterval;
11
- private _heartbeatRetries;
11
+ protected devSessionId: number | undefined;
12
+ private heartbeatInterval;
13
+ private heartbeatRetries;
14
+ private exit;
12
15
  constructor(options: DevSessionManagerConstructorOptions);
13
16
  private validateSessionIdExists;
14
17
  registerSession(): Promise<boolean>;
@@ -7,18 +7,20 @@ import { getErrorMessage } from '../../errorHandlers/index.js';
7
7
  class DevSessionManager {
8
8
  localDevLogger;
9
9
  targetTestingAccountId;
10
- _devSessionId;
11
- _heartbeatInterval;
12
- _heartbeatRetries;
10
+ devSessionId;
11
+ heartbeatInterval;
12
+ heartbeatRetries;
13
+ exit;
13
14
  constructor(options) {
14
15
  this.targetTestingAccountId = options.targetTestingAccountId;
15
16
  this.localDevLogger = options.localDevLogger;
16
- this._devSessionId = undefined;
17
- this._heartbeatInterval = undefined;
18
- this._heartbeatRetries = 0;
17
+ this.exit = options.exit;
18
+ this.devSessionId = undefined;
19
+ this.heartbeatInterval = undefined;
20
+ this.heartbeatRetries = 0;
19
21
  }
20
22
  validateSessionIdExists() {
21
- if (!this._devSessionId) {
23
+ if (!this.devSessionId) {
22
24
  if (this.localDevLogger) {
23
25
  this.localDevLogger.devSessionMissingSessionIdError();
24
26
  }
@@ -26,7 +28,7 @@ class DevSessionManager {
26
28
  // Fallback for deprecated local dev manager
27
29
  uiLogger.error(lib.LocalDevManager.devSession.missingSessionIdError);
28
30
  }
29
- process.exit(EXIT_CODES.ERROR);
31
+ throw new Error(lib.LocalDevManager.devSession.missingSessionIdError);
30
32
  }
31
33
  }
32
34
  async registerSession() {
@@ -34,7 +36,7 @@ class DevSessionManager {
34
36
  const activeServers = await getActiveServers();
35
37
  const portData = Object.entries(activeServers).map(([serverId, port]) => ({ serverId, port }));
36
38
  const registerDevSessionResponse = await registerDevSession(this.targetTestingAccountId, portData);
37
- this._devSessionId = registerDevSessionResponse.data.sessionId;
39
+ this.devSessionId = registerDevSessionResponse.data.sessionId;
38
40
  }
39
41
  catch (e) {
40
42
  if (this.localDevLogger) {
@@ -46,19 +48,29 @@ class DevSessionManager {
46
48
  }
47
49
  return false;
48
50
  }
49
- this.validateSessionIdExists();
51
+ try {
52
+ this.validateSessionIdExists();
53
+ }
54
+ catch {
55
+ return this.exit(EXIT_CODES.ERROR);
56
+ }
50
57
  this.initializeHeartbeat();
51
58
  return true;
52
59
  }
53
60
  initializeHeartbeat() {
54
- this._heartbeatInterval = setInterval(async () => {
55
- this.validateSessionIdExists();
61
+ this.heartbeatInterval = setInterval(async () => {
62
+ try {
63
+ this.validateSessionIdExists();
64
+ }
65
+ catch {
66
+ return this.exit(EXIT_CODES.ERROR);
67
+ }
56
68
  try {
57
- await devSessionHeartbeat(this.targetTestingAccountId, this._devSessionId);
69
+ await devSessionHeartbeat(this.targetTestingAccountId, this.devSessionId);
58
70
  }
59
71
  catch (e) {
60
- if (this._heartbeatRetries < 3) {
61
- this._heartbeatRetries++;
72
+ if (this.heartbeatRetries < 3) {
73
+ this.heartbeatRetries++;
62
74
  return;
63
75
  }
64
76
  if (this.localDevLogger) {
@@ -68,15 +80,15 @@ class DevSessionManager {
68
80
  // Fallback for deprecated local dev manager
69
81
  uiLogger.error(lib.LocalDevManager.devSession.heartbeatError(getErrorMessage(e)));
70
82
  }
71
- process.exit(EXIT_CODES.ERROR);
83
+ return this.exit(EXIT_CODES.ERROR);
72
84
  }
73
85
  }, 30000);
74
86
  }
75
87
  async deleteDevSession() {
76
- if (this._devSessionId) {
77
- clearInterval(this._heartbeatInterval);
88
+ if (this.devSessionId) {
89
+ clearInterval(this.heartbeatInterval);
78
90
  try {
79
- await deleteDevSession(this.targetTestingAccountId, this._devSessionId);
91
+ await deleteDevSession(this.targetTestingAccountId, this.devSessionId);
80
92
  }
81
93
  catch (e) {
82
94
  if (this.localDevLogger) {
@@ -3,6 +3,7 @@ import { Build } from '@hubspot/local-dev-lib/types/Build';
3
3
  import { PublicApp } from '@hubspot/local-dev-lib/types/Apps';
4
4
  import { Environment } from '@hubspot/local-dev-lib/types/Accounts';
5
5
  import { Component, ProjectConfig } from '../../../types/Projects.js';
6
+ import { ExitFunction } from '../../../types/Yargs.js';
6
7
  type LocalDevManagerConstructorOptions = {
7
8
  targetAccountId: number;
8
9
  parentAccountId: number;
@@ -14,6 +15,7 @@ type LocalDevManagerConstructorOptions = {
14
15
  isGithubLinked: boolean;
15
16
  runnableComponents: Component[];
16
17
  env: Environment;
18
+ exit: ExitFunction;
17
19
  };
18
20
  declare class LocalDevManager_DEPRECATED {
19
21
  targetAccountId: number;
@@ -36,6 +38,7 @@ declare class LocalDevManager_DEPRECATED {
36
38
  projectSourceDir: string;
37
39
  mostRecentUploadWarning: string | null;
38
40
  private devSessionManager;
41
+ private exit;
39
42
  constructor(options: LocalDevManagerConstructorOptions);
40
43
  setActiveApp(appUid?: string): Promise<void>;
41
44
  setActivePublicAppData(): Promise<void>;