@hubspot/cli 8.0.10-experimental.0 → 8.0.10-experimental.2

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 (68) hide show
  1. package/commands/cms/__tests__/watch.test.js +0 -8
  2. package/commands/cms/function/logs.js +1 -0
  3. package/commands/cms/watch.d.ts +0 -1
  4. package/commands/cms/watch.js +2 -8
  5. package/commands/feedback.js +1 -1
  6. package/commands/mcp/__tests__/start.test.js +8 -1
  7. package/commands/mcp/setup.js +1 -9
  8. package/commands/mcp/start.js +0 -1
  9. package/commands/project/__tests__/create.test.js +1 -1
  10. package/commands/project/create.js +2 -2
  11. package/commands/project/watch.js +15 -2
  12. package/lang/en.d.ts +2 -6
  13. package/lang/en.js +2 -6
  14. package/lib/__tests__/serverlessLogs.test.js +71 -65
  15. package/lib/constants.d.ts +1 -1
  16. package/lib/constants.js +1 -1
  17. package/lib/generateSelectors.js +1 -2
  18. package/lib/mcp/__tests__/setup.test.js +357 -28
  19. package/lib/mcp/setup.d.ts +1 -0
  20. package/lib/mcp/setup.js +77 -30
  21. package/lib/projects/create/__tests__/legacy.test.js +6 -24
  22. package/lib/projects/create/index.js +1 -4
  23. package/lib/projects/create/legacy.js +3 -8
  24. package/lib/projects/create/v2.js +1 -9
  25. package/lib/projects/ensureProjectExists.js +1 -2
  26. package/lib/projects/pollProjectBuildAndDeploy.js +90 -85
  27. package/lib/projects/upload.d.ts +1 -0
  28. package/lib/projects/upload.js +37 -46
  29. package/lib/projects/watch.d.ts +2 -1
  30. package/lib/projects/watch.js +32 -24
  31. package/lib/serverlessLogs.js +50 -44
  32. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  33. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  34. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  35. package/mcp-server/tools/cms/HsFunctionLogsTool.js +1 -1
  36. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  37. package/mcp-server/tools/cms/HsListTool.js +1 -1
  38. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -2
  39. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -2
  40. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -2
  41. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +1 -2
  42. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -2
  43. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -2
  44. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  45. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  46. package/mcp-server/tools/project/CreateProjectTool.d.ts +1 -1
  47. package/mcp-server/tools/project/CreateProjectTool.js +1 -1
  48. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  49. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  50. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  51. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  52. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -2
  53. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -2
  54. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +1 -2
  55. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -2
  56. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +10 -2
  57. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +2 -2
  58. package/mcp-server/tools/project/constants.d.ts +1 -1
  59. package/mcp-server/utils/__tests__/command.test.js +233 -3
  60. package/mcp-server/utils/__tests__/feedbackTracking.test.js +9 -64
  61. package/mcp-server/utils/command.d.ts +5 -0
  62. package/mcp-server/utils/command.js +24 -0
  63. package/mcp-server/utils/feedbackTracking.js +2 -17
  64. package/package.json +5 -10
  65. package/mcp-server/utils/__tests__/project.test.d.ts +0 -1
  66. package/mcp-server/utils/__tests__/project.test.js +0 -140
  67. package/mcp-server/utils/project.d.ts +0 -5
  68. package/mcp-server/utils/project.js +0 -18
@@ -1,8 +1,6 @@
1
1
  import { fetchRepoFile } from '@hubspot/local-dev-lib/api/github';
2
2
  import { DEFAULT_PROJECT_TEMPLATE_BRANCH, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, PROJECT_COMPONENT_TYPES, } from '../../constants.js';
3
- import { EXIT_CODES } from '../../enums/exitCodes.js';
4
3
  import { debugError } from '../../errorHandlers/index.js';
5
- import { uiLogger } from '../../ui/logger.js';
6
4
  import { isV2Project } from '../platformVersion.js';
7
5
  import { lib } from '../../../lang/en.js';
8
6
  const PROJECT_TEMPLATE_PROPERTIES = ['name', 'label', 'path'];
@@ -36,18 +34,15 @@ export async function getProjectTemplateListFromRepo(templateSource, githubRef)
36
34
  }
37
35
  catch (e) {
38
36
  debugError(e);
39
- uiLogger.error(lib.projects.create.errors.missingConfigFileTemplateSource);
40
- return process.exit(EXIT_CODES.ERROR);
37
+ throw new Error(lib.projects.create.errors.missingConfigFileTemplateSource);
41
38
  }
42
39
  if (!config || !config[PROJECT_COMPONENT_TYPES.PROJECTS]) {
43
- uiLogger.error(lib.projects.create.errors.noProjectsInConfig);
44
- return process.exit(EXIT_CODES.ERROR);
40
+ throw new Error(lib.projects.create.errors.noProjectsInConfig);
45
41
  }
46
42
  const templates = config[PROJECT_COMPONENT_TYPES.PROJECTS];
47
43
  const templatesContainAllProperties = templates.every(config => PROJECT_TEMPLATE_PROPERTIES.every(p => Object.prototype.hasOwnProperty.call(config, p)));
48
44
  if (!templatesContainAllProperties) {
49
- uiLogger.error(lib.projects.create.errors.missingPropertiesInConfig);
50
- return process.exit(EXIT_CODES.ERROR);
45
+ throw new Error(lib.projects.create.errors.missingPropertiesInConfig);
51
46
  }
52
47
  return templates;
53
48
  }
@@ -5,8 +5,6 @@ import { APP_EVENTS_KEY as AppEventsKey, PAGES_KEY as PagesKey, } from '@hubspot
5
5
  import { isV2Project } from '../platformVersion.js';
6
6
  import path from 'path';
7
7
  import { getConfigForPlatformVersion } from './legacy.js';
8
- import { logError } from '../../errorHandlers/index.js';
9
- import { EXIT_CODES } from '../../enums/exitCodes.js';
10
8
  import { hasFeature } from '../../hasFeature.js';
11
9
  export async function createV2App(providedAuth, providedDistribution) {
12
10
  let authType;
@@ -111,13 +109,7 @@ export async function v2ComponentFlow(platformVersion, projectBase, providedAuth
111
109
  let repoConfig = undefined;
112
110
  let authType;
113
111
  let distribution;
114
- try {
115
- repoConfig = await getConfigForPlatformVersion(platformVersion);
116
- }
117
- catch (error) {
118
- logError(error);
119
- return process.exit(EXIT_CODES.SUCCESS);
120
- }
112
+ repoConfig = await getConfigForPlatformVersion(platformVersion);
121
113
  const projectContentsChoice = projectBase ||
122
114
  (await listPrompt(commands.project.create.prompts.parentComponents, {
123
115
  choices: [
@@ -2,7 +2,6 @@ import { createProject, fetchProject, } from '@hubspot/local-dev-lib/api/project
2
2
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
3
3
  import { DEFAULT_POLLING_DELAY } from '../constants.js';
4
4
  import { promptUser } from '../prompts/promptUtils.js';
5
- import { EXIT_CODES } from '../enums/exitCodes.js';
6
5
  import { uiAccountDescription } from '../ui/index.js';
7
6
  import SpinniesManager from '../ui/SpinniesManager.js';
8
7
  import { logError, ApiErrorContext } from '../errorHandlers/index.js';
@@ -85,6 +84,6 @@ export async function ensureProjectExists(accountId, projectName, { forceCreate
85
84
  }
86
85
  }
87
86
  logError(err, new ApiErrorContext({ accountId }));
88
- process.exit(EXIT_CODES.ERROR);
87
+ return { projectExists: false };
89
88
  }
90
89
  }
@@ -5,7 +5,6 @@ import SpinniesManager from '../ui/SpinniesManager.js';
5
5
  import { logError, ApiErrorContext } from '../errorHandlers/index.js';
6
6
  import { uiLine, uiLink, uiAccountDescription } from '../ui/index.js';
7
7
  import { getProjectBuildDetailUrl, getProjectDeployDetailUrl, getProjectActivityUrl, } from './urls.js';
8
- import { EXIT_CODES } from '../enums/exitCodes.js';
9
8
  import { lib } from '../../lang/en.js';
10
9
  import { uiLogger } from '../ui/logger.js';
11
10
  import { APP_FUNCTIONS_PACKAGE_KEY as AppFunctionsPackageKey } from '@hubspot/project-parsing-lib/constants';
@@ -32,8 +31,8 @@ function getSubtaskType(task) {
32
31
  return task.deployType;
33
32
  }
34
33
  function handleTaskStatusError(statusText) {
35
- uiLogger.error(lib.projectBuildAndDeploy.makePollTaskStatusFunc.errorFetchingTaskStatus(statusText.TYPE_KEY === PROJECT_BUILD_TEXT.TYPE_KEY ? 'build' : 'deploy'));
36
- process.exit(EXIT_CODES.ERROR);
34
+ const taskType = statusText.TYPE_KEY === PROJECT_BUILD_TEXT.TYPE_KEY ? 'build' : 'deploy';
35
+ throw new Error(lib.projectBuildAndDeploy.makePollTaskStatusFunc.errorFetchingTaskStatus(taskType));
37
36
  }
38
37
  function makePollTaskStatusFunc({ statusFn, structureFn, statusText, statusStrings, linkToHubSpot, }) {
39
38
  return async function (accountId, taskName, taskId, deployedBuildId, silenceLogs = false) {
@@ -111,97 +110,103 @@ function makePollTaskStatusFunc({ statusFn, structureFn, statusText, statusStrin
111
110
  task.subtasks.forEach((subtask, i) => addTaskSpinner(subtask, 4, i === task.subtasks.length - 1));
112
111
  });
113
112
  }
114
- return new Promise(resolve => {
113
+ return new Promise((resolve, reject) => {
115
114
  const pollInterval = setInterval(async () => {
116
- let taskStatus;
117
115
  try {
118
- const { data } = await statusFn(accountId, taskName, taskId);
119
- taskStatus = data;
120
- }
121
- catch (e) {
122
- uiLogger.debug(e);
123
- logError(e, new ApiErrorContext({
124
- accountId,
125
- projectName: taskName,
126
- }));
127
- handleTaskStatusError(statusText);
128
- }
129
- const subtasks = getSubtasks(taskStatus);
130
- if (!taskStatus || !taskStatus.status || !subtasks) {
131
- handleTaskStatusError(statusText);
132
- }
133
- const { status } = taskStatus;
134
- if (SpinniesManager.hasActiveSpinners()) {
135
- subtasks.forEach(subtask => {
136
- const { id, status } = subtask;
137
- const spinner = SpinniesManager.pick(id);
138
- if (!spinner || spinner.status !== SPINNER_STATUS.SPINNING) {
139
- return;
140
- }
141
- const topLevelTask = structuredTasks.find(t => t.id == id);
142
- if (status === statusText.STATES.SUCCESS ||
143
- status === statusText.STATES.FAILURE) {
144
- const taskStatusText = subtask.status === statusText.STATES.SUCCESS
145
- ? lib.projectBuildAndDeploy.makePollTaskStatusFunc
146
- .successStatusText
147
- : lib.projectBuildAndDeploy.makePollTaskStatusFunc
148
- .failedStatusText;
149
- const hasNewline = spinner?.text?.includes('\n') || Boolean(topLevelTask);
150
- const updatedText = `${spinner?.text?.replace('\n', '')} ${taskStatusText}${hasNewline ? '\n' : ''}`;
151
- if (status === statusText.STATES.SUCCESS) {
152
- SpinniesManager.succeed(id, { text: updatedText });
153
- }
154
- else {
155
- SpinniesManager.fail(id, { text: updatedText });
116
+ let taskStatus;
117
+ try {
118
+ const { data } = await statusFn(accountId, taskName, taskId);
119
+ taskStatus = data;
120
+ }
121
+ catch (e) {
122
+ uiLogger.debug(e);
123
+ logError(e, new ApiErrorContext({
124
+ accountId,
125
+ projectName: taskName,
126
+ }));
127
+ handleTaskStatusError(statusText);
128
+ }
129
+ const subtasks = getSubtasks(taskStatus);
130
+ if (!taskStatus || !taskStatus.status || !subtasks) {
131
+ handleTaskStatusError(statusText);
132
+ }
133
+ const { status } = taskStatus;
134
+ if (SpinniesManager.hasActiveSpinners()) {
135
+ subtasks.forEach(subtask => {
136
+ const { id, status } = subtask;
137
+ const spinner = SpinniesManager.pick(id);
138
+ if (!spinner || spinner.status !== SPINNER_STATUS.SPINNING) {
139
+ return;
156
140
  }
157
- if (topLevelTask) {
158
- topLevelTask.subtasks.forEach(currentSubtask => SpinniesManager.remove(currentSubtask.id));
141
+ const topLevelTask = structuredTasks.find(t => t.id == id);
142
+ if (status === statusText.STATES.SUCCESS ||
143
+ status === statusText.STATES.FAILURE) {
144
+ const taskStatusText = subtask.status === statusText.STATES.SUCCESS
145
+ ? lib.projectBuildAndDeploy.makePollTaskStatusFunc
146
+ .successStatusText
147
+ : lib.projectBuildAndDeploy.makePollTaskStatusFunc
148
+ .failedStatusText;
149
+ const hasNewline = spinner?.text?.includes('\n') || Boolean(topLevelTask);
150
+ const updatedText = `${spinner?.text?.replace('\n', '')} ${taskStatusText}${hasNewline ? '\n' : ''}`;
151
+ if (status === statusText.STATES.SUCCESS) {
152
+ SpinniesManager.succeed(id, { text: updatedText });
153
+ }
154
+ else {
155
+ SpinniesManager.fail(id, { text: updatedText });
156
+ }
157
+ if (topLevelTask) {
158
+ topLevelTask.subtasks.forEach(currentSubtask => SpinniesManager.remove(currentSubtask.id));
159
+ }
159
160
  }
160
- }
161
- });
162
- if (status === statusText.STATES.SUCCESS) {
163
- SpinniesManager.succeed(overallTaskSpinniesKey, {
164
- text: statusStrings.SUCCESS(taskName, displayId),
165
- });
166
- clearInterval(pollInterval);
167
- resolve(taskStatus);
168
- }
169
- else if (status === statusText.STATES.FAILURE) {
170
- SpinniesManager.fail(overallTaskSpinniesKey, {
171
- text: statusStrings.FAIL(taskName, displayId),
172
161
  });
173
- if (!silenceLogs) {
174
- const failedSubtasks = subtasks.filter(subtask => subtask.status === 'FAILURE');
175
- uiLine();
176
- uiLogger.log(`${statusStrings.SUBTASK_FAIL(failedSubtasks.length === 1
177
- ? getSubtaskName(failedSubtasks[0])
178
- : failedSubtasks.length + ' components', displayId)}\n`);
179
- uiLogger.log(lib.projectBuildAndDeploy.makePollTaskStatusFunc.errorSummary);
180
- uiLine();
181
- const displayErrors = failedSubtasks.filter(subtask => subtask?.standardError?.subCategory !==
182
- PROJECT_ERROR_TYPES.SUBBUILD_FAILED &&
183
- subtask?.standardError?.subCategory !==
184
- PROJECT_ERROR_TYPES.SUBDEPLOY_FAILED);
185
- displayErrors.forEach(subTask => {
186
- uiLogger.log(`\n--- ${chalk.bold(getSubtaskName(subTask))} failed with the following error ---`);
187
- uiLogger.error(subTask.errorMessage);
188
- // Log nested errors
189
- if (subTask.standardError && subTask.standardError.errors) {
190
- uiLogger.log('');
191
- subTask.standardError.errors.forEach(error => {
192
- uiLogger.log(error.message);
193
- });
194
- }
162
+ if (status === statusText.STATES.SUCCESS) {
163
+ SpinniesManager.succeed(overallTaskSpinniesKey, {
164
+ text: statusStrings.SUCCESS(taskName, displayId),
195
165
  });
166
+ clearInterval(pollInterval);
167
+ resolve(taskStatus);
168
+ }
169
+ else if (status === statusText.STATES.FAILURE) {
170
+ SpinniesManager.fail(overallTaskSpinniesKey, {
171
+ text: statusStrings.FAIL(taskName, displayId),
172
+ });
173
+ if (!silenceLogs) {
174
+ const failedSubtasks = subtasks.filter(subtask => subtask.status === 'FAILURE');
175
+ uiLine();
176
+ uiLogger.log(`${statusStrings.SUBTASK_FAIL(failedSubtasks.length === 1
177
+ ? getSubtaskName(failedSubtasks[0])
178
+ : failedSubtasks.length + ' components', displayId)}\n`);
179
+ uiLogger.log(lib.projectBuildAndDeploy.makePollTaskStatusFunc.errorSummary);
180
+ uiLine();
181
+ const displayErrors = failedSubtasks.filter(subtask => subtask?.standardError?.subCategory !==
182
+ PROJECT_ERROR_TYPES.SUBBUILD_FAILED &&
183
+ subtask?.standardError?.subCategory !==
184
+ PROJECT_ERROR_TYPES.SUBDEPLOY_FAILED);
185
+ displayErrors.forEach(subTask => {
186
+ uiLogger.log(`\n--- ${chalk.bold(getSubtaskName(subTask))} failed with the following error ---`);
187
+ uiLogger.error(subTask.errorMessage);
188
+ // Log nested errors
189
+ if (subTask.standardError && subTask.standardError.errors) {
190
+ uiLogger.log('');
191
+ subTask.standardError.errors.forEach(error => {
192
+ uiLogger.log(error.message);
193
+ });
194
+ }
195
+ });
196
+ }
197
+ clearInterval(pollInterval);
198
+ resolve(taskStatus);
199
+ }
200
+ else if (!subtasks.length) {
201
+ clearInterval(pollInterval);
202
+ resolve(taskStatus);
196
203
  }
197
- clearInterval(pollInterval);
198
- resolve(taskStatus);
199
- }
200
- else if (!subtasks.length) {
201
- clearInterval(pollInterval);
202
- resolve(taskStatus);
203
204
  }
204
205
  }
206
+ catch (e) {
207
+ clearInterval(pollInterval);
208
+ reject(e);
209
+ }
205
210
  }, DEFAULT_POLLING_DELAY);
206
211
  });
207
212
  };
@@ -4,6 +4,7 @@ type ProjectUploadCallbackFunction<T> = (accountId: number, projectConfig: Proje
4
4
  type ProjectUploadResult<T> = {
5
5
  result?: T;
6
6
  uploadError?: unknown;
7
+ projectNotFound?: boolean;
7
8
  };
8
9
  type HandleProjectUploadArg<T> = {
9
10
  accountId: number;
@@ -8,13 +8,11 @@ import { isTranslationError, translate, } from '@hubspot/project-parsing-lib/tra
8
8
  import { projectContainsHsMetaFiles } from '@hubspot/project-parsing-lib/projects';
9
9
  import SpinniesManager from '../ui/SpinniesManager.js';
10
10
  import { uiAccountDescription } from '../ui/index.js';
11
- import { logError } from '../errorHandlers/index.js';
12
11
  import util from 'node:util';
13
12
  import { lib } from '../../lang/en.js';
14
13
  import { ensureProjectExists } from './ensureProjectExists.js';
15
14
  import { uiLogger } from '../ui/logger.js';
16
15
  import { isV2Project } from './platformVersion.js';
17
- import { EXIT_CODES } from '../enums/exitCodes.js';
18
16
  import ProjectValidationError from '../errors/ProjectValidationError.js';
19
17
  import { walk } from '@hubspot/local-dev-lib/fs';
20
18
  import { LEGACY_CONFIG_FILES } from '../constants.js';
@@ -46,57 +44,50 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
46
44
  }
47
45
  export async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, }) {
48
46
  const srcDir = path.resolve(projectDir, projectConfig.srcDir);
49
- try {
50
- await validateSourceDirectory(srcDir, projectConfig, projectDir);
51
- }
52
- catch (e) {
53
- logError(e);
54
- process.exit(EXIT_CODES.ERROR);
55
- }
56
- try {
57
- await validateNoHSMetaMismatch(srcDir, projectConfig);
58
- }
59
- catch (e) {
60
- logError(e);
61
- process.exit(EXIT_CODES.ERROR);
62
- }
47
+ await validateSourceDirectory(srcDir, projectConfig, projectDir);
48
+ await validateNoHSMetaMismatch(srcDir, projectConfig);
63
49
  const tempFile = tmp.fileSync({ postfix: '.zip' });
64
50
  uiLogger.debug(lib.projectUpload.handleProjectUpload.compressing(tempFile.name));
65
51
  const output = fs.createWriteStream(tempFile.name);
66
52
  const archive = archiver('zip');
67
- const result = new Promise(resolve => output.on('close', async function () {
68
- uiLogger.debug(lib.projectUpload.handleProjectUpload.compressed(archive.pointer()));
69
- let intermediateRepresentation;
70
- if (sendIR) {
71
- try {
72
- intermediateRepresentation = await handleTranslate({
73
- projectDir,
74
- projectConfig,
75
- accountId,
76
- skipValidation,
77
- profile,
78
- });
53
+ const result = new Promise((resolve, reject) => output.on('close', async function () {
54
+ try {
55
+ uiLogger.debug(lib.projectUpload.handleProjectUpload.compressed(archive.pointer()));
56
+ let intermediateRepresentation;
57
+ if (sendIR) {
58
+ try {
59
+ intermediateRepresentation = await handleTranslate({
60
+ projectDir,
61
+ projectConfig,
62
+ accountId,
63
+ skipValidation,
64
+ profile,
65
+ });
66
+ }
67
+ catch (e) {
68
+ return resolve({ uploadError: e });
69
+ }
79
70
  }
80
- catch (e) {
81
- resolve({ uploadError: e });
71
+ const { projectExists } = await ensureProjectExists(accountId, projectConfig.name, {
72
+ forceCreate,
73
+ uploadCommand: isUploadCommand,
74
+ noLogs: true,
75
+ });
76
+ if (!projectExists) {
77
+ uiLogger.log(lib.projectUpload.handleProjectUpload.projectDoesNotExist(accountId));
78
+ return resolve({ projectNotFound: true });
79
+ }
80
+ const { buildId, error } = await uploadProjectFiles(accountId, projectConfig.name, tempFile.name, uploadMessage, projectConfig.platformVersion, intermediateRepresentation);
81
+ if (error) {
82
+ resolve({ uploadError: error });
83
+ }
84
+ else if (callbackFunc) {
85
+ const uploadResult = await callbackFunc(accountId, projectConfig, tempFile, buildId);
86
+ resolve({ result: uploadResult });
82
87
  }
83
88
  }
84
- const { projectExists } = await ensureProjectExists(accountId, projectConfig.name, {
85
- forceCreate,
86
- uploadCommand: isUploadCommand,
87
- noLogs: true,
88
- });
89
- if (!projectExists) {
90
- uiLogger.log(lib.projectUpload.handleProjectUpload.projectDoesNotExist(accountId));
91
- process.exit(EXIT_CODES.SUCCESS);
92
- }
93
- const { buildId, error } = await uploadProjectFiles(accountId, projectConfig.name, tempFile.name, uploadMessage, projectConfig.platformVersion, intermediateRepresentation);
94
- if (error) {
95
- resolve({ uploadError: error });
96
- }
97
- else if (callbackFunc) {
98
- const uploadResult = await callbackFunc(accountId, projectConfig, tempFile, buildId);
99
- resolve({ result: uploadResult });
89
+ catch (e) {
90
+ reject(e);
100
91
  }
101
92
  }));
102
93
  archive.pipe(output);
@@ -1,4 +1,5 @@
1
1
  import { ProjectConfig } from '../../types/Projects.js';
2
2
  type ProjectWatchHandlerFunction = (accountId: number, projectName: string, currentBuildId: number) => Promise<void> | void;
3
- export declare function createWatcher(accountId: number, projectConfig: ProjectConfig, projectDir: string, handleBuildStatusFn: ProjectWatchHandlerFunction, handleUserInputFn: ProjectWatchHandlerFunction): Promise<void>;
3
+ type WatchTerminationHandler = (error?: unknown) => void;
4
+ export declare function createWatcher(accountId: number, projectConfig: ProjectConfig, projectDir: string, handleBuildStatusFn: ProjectWatchHandlerFunction, handleUserInputFn: ProjectWatchHandlerFunction, handleWatchTerminationFn: WatchTerminationHandler): Promise<void>;
4
5
  export {};
@@ -18,6 +18,7 @@ const standbyQueue = [];
18
18
  let currentBuildId;
19
19
  let handleBuildStatus;
20
20
  let handleUserInput;
21
+ let handleWatchTermination = () => { };
21
22
  let timer;
22
23
  async function processStandByQueue(accountId, projectName, platformVersion) {
23
24
  queue.addAll(standbyQueue.map(({ filePath, remotePath, action }) => {
@@ -37,33 +38,39 @@ function debounceQueueBuild(accountId, projectName, platformVersion) {
37
38
  clearTimeout(timer);
38
39
  }
39
40
  timer = setTimeout(async () => {
40
- uiLogger.debug(commands.project.watch.debug.pause);
41
- queue.pause();
42
- await queue.onIdle();
43
41
  try {
44
- await queueBuild(accountId, projectName, platformVersion);
45
- uiLogger.debug(commands.project.watch.debug.buildStarted);
46
- }
47
- catch (err) {
48
- if (isSpecifiedError(err, {
49
- subCategory: PROJECT_ERROR_TYPES.MISSING_PROJECT_PROVISION,
50
- })) {
51
- uiLogger.log(commands.project.watch.logs.watchCancelledFromUi);
52
- process.exit(0);
42
+ uiLogger.debug(commands.project.watch.debug.pause);
43
+ queue.pause();
44
+ await queue.onIdle();
45
+ try {
46
+ await queueBuild(accountId, projectName, platformVersion);
47
+ uiLogger.debug(commands.project.watch.debug.buildStarted);
48
+ }
49
+ catch (err) {
50
+ if (isSpecifiedError(err, {
51
+ subCategory: PROJECT_ERROR_TYPES.MISSING_PROJECT_PROVISION,
52
+ })) {
53
+ uiLogger.log(commands.project.watch.logs.watchCancelledFromUi);
54
+ handleWatchTermination();
55
+ return;
56
+ }
57
+ else {
58
+ logError(err, new ApiErrorContext({ accountId }));
59
+ }
60
+ return;
53
61
  }
54
- else {
55
- logError(err, new ApiErrorContext({ accountId }));
62
+ await handleBuildStatus(accountId, projectName, currentBuildId);
63
+ await createNewStagingBuild(accountId, projectName, platformVersion);
64
+ if (standbyQueue.length > 0) {
65
+ await processStandByQueue(accountId, projectName, platformVersion);
56
66
  }
57
- return;
67
+ queue.start();
68
+ uiLogger.log(commands.project.watch.logs.resuming);
69
+ uiLogger.log(`\n> Press ${chalk.bold('q')} to quit watching\n`);
58
70
  }
59
- await handleBuildStatus(accountId, projectName, currentBuildId);
60
- await createNewStagingBuild(accountId, projectName, platformVersion);
61
- if (standbyQueue.length > 0) {
62
- await processStandByQueue(accountId, projectName, platformVersion);
71
+ catch (err) {
72
+ handleWatchTermination(err);
63
73
  }
64
- queue.start();
65
- uiLogger.log(commands.project.watch.logs.resuming);
66
- uiLogger.log(`\n> Press ${chalk.bold('q')} to quit watching\n`);
67
74
  }, 2000);
68
75
  }
69
76
  async function queueFileOrFolder(accountId, projectName, platformVersion, filePath, remotePath, action) {
@@ -111,7 +118,7 @@ async function createNewBuild(accountId, projectName, platformVersion) {
111
118
  await cancelStagedBuild(accountId, projectName);
112
119
  uiLogger.log(commands.project.watch.logs.previousStagingBuildCancelled);
113
120
  }
114
- process.exit(1);
121
+ throw err;
115
122
  }
116
123
  }
117
124
  async function handleWatchEvent(accountId, projectName, platformVersion, projectSourceDir, filePath, action = 'upload') {
@@ -132,10 +139,11 @@ async function handleWatchEvent(accountId, projectName, platformVersion, project
132
139
  await queueFileOrFolder(accountId, projectName, platformVersion, filePath, remotePath, action);
133
140
  }
134
141
  }
135
- export async function createWatcher(accountId, projectConfig, projectDir, handleBuildStatusFn, handleUserInputFn) {
142
+ export async function createWatcher(accountId, projectConfig, projectDir, handleBuildStatusFn, handleUserInputFn, handleWatchTerminationFn) {
136
143
  const projectSourceDir = path.join(projectDir, projectConfig.srcDir);
137
144
  handleBuildStatus = handleBuildStatusFn;
138
145
  handleUserInput = handleUserInputFn;
146
+ handleWatchTermination = handleWatchTerminationFn;
139
147
  await createNewStagingBuild(accountId, projectConfig.name, projectConfig.platformVersion);
140
148
  const watcher = chokidar.watch(projectSourceDir, {
141
149
  ignoreInitial: true,
@@ -9,7 +9,6 @@ import { outputLogs } from './ui/serverlessFunctionLogs.js';
9
9
  import { logError, ApiErrorContext } from './errorHandlers/index.js';
10
10
  import SpinniesManager from './ui/SpinniesManager.js';
11
11
  import { handleExit, handleKeypress } from './process.js';
12
- import { EXIT_CODES } from './enums/exitCodes.js';
13
12
  import { lib } from '../lang/en.js';
14
13
  const TAIL_DELAY = 5000;
15
14
  function base64EncodeString(valueToEncode) {
@@ -19,19 +18,6 @@ function base64EncodeString(valueToEncode) {
19
18
  const stringBuffer = Buffer.from(valueToEncode);
20
19
  return encodeURIComponent(stringBuffer.toString('base64'));
21
20
  }
22
- function handleUserInput() {
23
- const onTerminate = async () => {
24
- SpinniesManager.remove('tailLogs');
25
- SpinniesManager.remove('stopMessage');
26
- process.exit(EXIT_CODES.SUCCESS);
27
- };
28
- handleExit(onTerminate);
29
- handleKeypress(key => {
30
- if ((key.ctrl && key.name == 'c') || key.name === 'q') {
31
- onTerminate();
32
- }
33
- });
34
- }
35
21
  async function verifyAccessKeyAndUserAccess(accountId, scopeGroup) {
36
22
  const accountConfig = getConfigAccountById(accountId);
37
23
  if (!accountConfig) {
@@ -80,40 +66,60 @@ export async function tailLogs(accountId, name, fetchLatest, tailCall, compact =
80
66
  }
81
67
  }
82
68
  }
83
- async function tail(after) {
84
- let latestLog;
85
- let nextAfter;
86
- try {
87
- const { data } = await tailCall(after);
88
- latestLog = data;
89
- nextAfter = latestLog.paging.next.after;
69
+ return new Promise(resolve => {
70
+ function cleanup() {
71
+ SpinniesManager.remove('tailLogs');
72
+ SpinniesManager.remove('stopMessage');
90
73
  }
91
- catch (e) {
92
- if (isHubSpotHttpError(e) && e.status !== 404) {
93
- logError(e, new ApiErrorContext({
94
- accountId,
95
- }));
74
+ let resolved = false;
75
+ const onTerminate = async () => {
76
+ if (resolved)
77
+ return;
78
+ resolved = true;
79
+ cleanup();
80
+ resolve();
81
+ };
82
+ handleExit(onTerminate);
83
+ handleKeypress(key => {
84
+ if ((key.ctrl && key.name == 'c') || key.name === 'q') {
85
+ onTerminate();
96
86
  }
97
- process.exit(EXIT_CODES.SUCCESS);
98
- }
99
- if (latestLog && latestLog.results.length) {
100
- outputLogs(latestLog, {
101
- compact,
102
- });
87
+ });
88
+ async function tail(after) {
89
+ let latestLog;
90
+ let nextAfter;
91
+ try {
92
+ const { data } = await tailCall(after);
93
+ latestLog = data;
94
+ nextAfter = latestLog.paging.next.after;
95
+ }
96
+ catch (e) {
97
+ if (isHubSpotHttpError(e) && e.status !== 404) {
98
+ logError(e, new ApiErrorContext({
99
+ accountId,
100
+ }));
101
+ }
102
+ await onTerminate();
103
+ return;
104
+ }
105
+ if (latestLog && latestLog.results.length) {
106
+ outputLogs(latestLog, {
107
+ compact,
108
+ });
109
+ }
110
+ setTimeout(async () => {
111
+ await tail(nextAfter);
112
+ }, TAIL_DELAY);
103
113
  }
104
- setTimeout(async () => {
105
- await tail(nextAfter);
106
- }, TAIL_DELAY);
107
- }
108
- SpinniesManager.add('tailLogs', {
109
- text: `Following logs for ${name}`,
110
- });
111
- SpinniesManager.add('stopMessage', {
112
- text: `> Press ${chalk.bold('q')} to stop following`,
113
- status: 'non-spinnable',
114
+ SpinniesManager.add('tailLogs', {
115
+ text: `Following logs for ${name}`,
116
+ });
117
+ SpinniesManager.add('stopMessage', {
118
+ text: `> Press ${chalk.bold('q')} to stop following`,
119
+ status: 'non-spinnable',
120
+ });
121
+ tail(initialAfter);
114
122
  });
115
- handleUserInput();
116
- await tail(initialAfter);
117
123
  }
118
124
  export async function outputBuildLog(buildLogUrl) {
119
125
  if (!buildLogUrl) {
@@ -1,7 +1,7 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/project.js';
4
+ import { runCommandInDir } from '../../utils/command.js';
5
5
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  import { addFlag } from '../../utils/command.js';
@@ -1,7 +1,7 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/project.js';
4
+ import { runCommandInDir } from '../../utils/command.js';
5
5
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  import { addFlag } from '../../utils/command.js';
@@ -1,7 +1,7 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
- import { runCommandInDir } from '../../utils/project.js';
4
+ import { runCommandInDir } from '../../utils/command.js';
5
5
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  import { addFlag } from '../../utils/command.js';