@hubspot/cli 7.7.16-experimental.0 → 7.7.16-experimental.10

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 (106) hide show
  1. package/bin/cli.js +4 -0
  2. package/bin/hs +1 -1
  3. package/commands/account/auth.js +3 -3
  4. package/commands/app/install.d.ts +8 -0
  5. package/commands/app/install.js +127 -0
  6. package/commands/app.js +6 -1
  7. package/commands/auth.js +23 -25
  8. package/commands/getStarted.d.ts +9 -0
  9. package/commands/getStarted.js +274 -0
  10. package/commands/init.js +35 -32
  11. package/commands/mcp/setup.d.ts +2 -2
  12. package/commands/mcp/setup.js +2 -0
  13. package/commands/mcp/start.d.ts +2 -2
  14. package/commands/mcp/start.js +3 -1
  15. package/commands/project/cloneApp.js +4 -4
  16. package/commands/project/create.js +9 -9
  17. package/commands/project/deploy.d.ts +1 -0
  18. package/commands/project/deploy.js +29 -3
  19. package/commands/project/dev/deprecatedFlow.js +4 -4
  20. package/commands/project/dev/index.js +5 -5
  21. package/commands/project/dev/unifiedFlow.js +8 -0
  22. package/commands/project/upload.d.ts +2 -2
  23. package/commands/project/upload.js +18 -23
  24. package/commands/project/validate.d.ts +6 -0
  25. package/commands/project/validate.js +82 -0
  26. package/commands/project.js +2 -0
  27. package/commands/sandbox/delete.js +5 -5
  28. package/commands/testAccount/create.d.ts +6 -0
  29. package/commands/testAccount/create.js +160 -0
  30. package/commands/testAccount/createConfig.d.ts +10 -0
  31. package/commands/testAccount/createConfig.js +98 -0
  32. package/commands/testAccount/delete.d.ts +6 -0
  33. package/commands/testAccount/delete.js +48 -0
  34. package/commands/testAccount.d.ts +3 -0
  35. package/commands/testAccount.js +28 -0
  36. package/lang/en.d.ts +201 -35
  37. package/lang/en.js +201 -38
  38. package/lang/en.lyaml +9 -14
  39. package/lib/accountTypes.d.ts +1 -0
  40. package/lib/accountTypes.js +20 -9
  41. package/lib/app/migrate.js +15 -3
  42. package/lib/app/migrate_legacy.js +2 -3
  43. package/lib/app/urls.d.ts +1 -1
  44. package/lib/commonOpts.d.ts +2 -0
  45. package/lib/commonOpts.js +21 -9
  46. package/lib/constants.d.ts +5 -0
  47. package/lib/constants.js +6 -1
  48. package/lib/doctor/Doctor.js +1 -1
  49. package/lib/errorHandlers/index.js +7 -0
  50. package/lib/mcp/setup.d.ts +9 -0
  51. package/lib/mcp/setup.js +23 -21
  52. package/lib/middleware/__test__/configMiddleware.test.js +2 -2
  53. package/lib/middleware/configMiddleware.js +10 -2
  54. package/lib/parsing.d.ts +1 -0
  55. package/lib/parsing.js +11 -0
  56. package/lib/polling.d.ts +1 -1
  57. package/lib/polling.js +11 -1
  58. package/lib/projectProfiles.d.ts +1 -0
  59. package/lib/projectProfiles.js +18 -0
  60. package/lib/projects/add/v3AddComponent.js +4 -0
  61. package/lib/projects/buildAndDeploy.js +1 -1
  62. package/lib/projects/create/index.d.ts +3 -2
  63. package/lib/projects/create/index.js +11 -5
  64. package/lib/projects/create/v3.d.ts +3 -3
  65. package/lib/projects/create/v3.js +2 -2
  66. package/lib/projects/localDev/AppDevModeInterface.d.ts +3 -0
  67. package/lib/projects/localDev/AppDevModeInterface.js +46 -17
  68. package/lib/projects/localDev/LocalDevManager.js +1 -1
  69. package/lib/projects/localDev/LocalDevProcess.d.ts +3 -2
  70. package/lib/projects/localDev/LocalDevProcess.js +16 -12
  71. package/lib/projects/localDev/LocalDevState.d.ts +10 -5
  72. package/lib/projects/localDev/LocalDevState.js +18 -20
  73. package/lib/projects/localDev/LocalDevWatcher.js +1 -1
  74. package/lib/projects/structure.d.ts +2 -2
  75. package/lib/projects/upload.d.ts +4 -0
  76. package/lib/projects/upload.js +57 -22
  77. package/lib/projects/urls.d.ts +2 -0
  78. package/lib/projects/urls.js +10 -0
  79. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +17 -0
  80. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +96 -0
  81. package/lib/prompts/installAppPrompt.d.ts +2 -1
  82. package/lib/prompts/installAppPrompt.js +12 -2
  83. package/lib/prompts/personalAccessKeyPrompt.js +2 -2
  84. package/lib/prompts/projectNameAndDestPrompt.d.ts +3 -0
  85. package/lib/prompts/projectNameAndDestPrompt.js +60 -0
  86. package/lib/prompts/promptUtils.d.ts +1 -0
  87. package/lib/prompts/promptUtils.js +2 -0
  88. package/lib/prompts/selectProjectTemplatePrompt.d.ts +26 -0
  89. package/lib/prompts/{createProjectPrompt.js → selectProjectTemplatePrompt.js} +6 -55
  90. package/lib/ui/logger.d.ts +1 -0
  91. package/lib/ui/logger.js +1 -0
  92. package/lib/validation.d.ts +1 -1
  93. package/lib/validation.js +4 -4
  94. package/lib/yargsUtils.d.ts +1 -0
  95. package/lib/yargsUtils.js +3 -0
  96. package/mcp-server/tools/index.js +2 -0
  97. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  98. package/mcp-server/tools/project/CreateProjectTool.js +5 -1
  99. package/mcp-server/tools/project/DeployProject.js +1 -1
  100. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  101. package/mcp-server/tools/project/ValidateProjectTool.d.ts +17 -0
  102. package/mcp-server/tools/project/ValidateProjectTool.js +35 -0
  103. package/package.json +10 -9
  104. package/types/LocalDev.d.ts +2 -0
  105. package/types/Yargs.d.ts +5 -1
  106. package/lib/prompts/createProjectPrompt.d.ts +0 -28
@@ -4,6 +4,7 @@ const localDevAuth_1 = require("@hubspot/local-dev-lib/api/localDevAuth");
4
4
  const appsDev_1 = require("@hubspot/local-dev-lib/api/appsDev");
5
5
  const ui_extensions_dev_server_1 = require("@hubspot/ui-extensions-dev-server");
6
6
  const portManager_1 = require("@hubspot/local-dev-lib/portManager");
7
+ const config_1 = require("@hubspot/local-dev-lib/config");
7
8
  const constants_1 = require("../../constants");
8
9
  const exitCodes_1 = require("../../enums/exitCodes");
9
10
  const structure_1 = require("../../projects/structure");
@@ -14,6 +15,7 @@ const promptUtils_1 = require("../../prompts/promptUtils");
14
15
  const en_1 = require("../../../lang/en");
15
16
  const logger_1 = require("../../ui/logger");
16
17
  const urls_1 = require("../../app/urls");
18
+ const accountTypes_1 = require("../../accountTypes");
17
19
  class AppDevModeInterface {
18
20
  localDevState;
19
21
  localDevLogger;
@@ -22,15 +24,6 @@ class AppDevModeInterface {
22
24
  constructor(options) {
23
25
  this.localDevState = options.localDevState;
24
26
  this.localDevLogger = options.localDevLogger;
25
- // Static auth apps are currently only installable in the portal that the project resides in
26
- // This limitation will eventually be removed, but in the meantime we need this check or the install
27
- // will always fail with a confusing message
28
- if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC &&
29
- this.localDevState.targetTestingAccountId !==
30
- this.localDevState.targetProjectAccountId) {
31
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
32
- process.exit(exitCodes_1.EXIT_CODES.ERROR);
33
- }
34
27
  if (!this.localDevState.targetProjectAccountId ||
35
28
  !this.localDevState.projectConfig ||
36
29
  !this.localDevState.projectDir) {
@@ -59,6 +52,19 @@ class AppDevModeInterface {
59
52
  }
60
53
  this.localDevState.setAppDataForUid(this.appNode.uid, appData);
61
54
  }
55
+ isAutomaticallyInstallable() {
56
+ const targetTestingAccount = (0, config_1.getAccountConfig)(this.localDevState.targetTestingAccountId);
57
+ if (!targetTestingAccount) {
58
+ return false;
59
+ }
60
+ const isTestAccount = (0, accountTypes_1.isDeveloperTestAccount)(targetTestingAccount) ||
61
+ (0, accountTypes_1.isSandbox)(targetTestingAccount);
62
+ const hasCorrectParent = targetTestingAccount.parentAccountId ===
63
+ this.localDevState.targetProjectAccountId;
64
+ return (isTestAccount &&
65
+ hasCorrectParent &&
66
+ this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC);
67
+ }
62
68
  async getAppInstallUrl() {
63
69
  if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.OAUTH) {
64
70
  return (0, urls_1.getOauthAppInstallUrl)({
@@ -72,13 +78,13 @@ class AppDevModeInterface {
72
78
  const { data: { results }, } = await (0, appsDev_1.fetchPublicAppsForPortal)(this.localDevState.targetProjectAccountId);
73
79
  const app = results.find(app => app.sourceId === this.appNode?.uid);
74
80
  if (!app) {
75
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
81
+ logger_1.uiLogger.error(en_1.lib.LocalDevManager.appNotFound(this.localDevState.targetProjectAccountId, this.appNode?.uid));
76
82
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
77
83
  }
78
84
  return (0, urls_1.getStaticAuthAppInstallUrl)({
79
85
  targetAccountId: this.localDevState.targetTestingAccountId,
80
86
  env: this.localDevState.env,
81
- appId: `${app.id}`,
87
+ appId: app.id,
82
88
  });
83
89
  }
84
90
  async fetchAppData() {
@@ -93,6 +99,7 @@ class AppDevModeInterface {
93
99
  clientId: appData.clientId,
94
100
  name: appData.name,
95
101
  installationState: constants_1.APP_INSTALLATION_STATES.NOT_INSTALLED,
102
+ scopeGroupIds: appData.scopeGroupIds,
96
103
  };
97
104
  this.marketplaceAppInstalls = uniquePortalInstallCount;
98
105
  }
@@ -110,9 +117,31 @@ class AppDevModeInterface {
110
117
  }
111
118
  this.localDevLogger.addUploadWarning(en_1.lib.AppDevModeInterface.defaultMarketplaceAppWarning(this.marketplaceAppInstalls));
112
119
  }
120
+ async autoInstallStaticAuthApp() {
121
+ const shouldInstall = await (0, installAppPrompt_1.installAppAutoPrompt)();
122
+ if (!shouldInstall) {
123
+ logger_1.uiLogger.log(en_1.lib.AppDevModeInterface.autoInstallDeclined);
124
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
125
+ }
126
+ await (0, appsDev_1.installStaticAuthAppOnTestAccount)(this.appData.id, this.localDevState.targetTestingAccountId, this.appData.scopeGroupIds);
127
+ }
128
+ async installAppOrOpenInstallUrl(isReinstall) {
129
+ if (this.isAutomaticallyInstallable()) {
130
+ try {
131
+ await this.autoInstallStaticAuthApp();
132
+ logger_1.uiLogger.success(en_1.lib.AppDevModeInterface.autoInstallSuccess(this.appData.name, this.localDevState.targetTestingAccountId));
133
+ return;
134
+ }
135
+ catch (e) {
136
+ logger_1.uiLogger.error(en_1.lib.AppDevModeInterface.autoInstallError(this.appData.name, this.localDevState.targetTestingAccountId));
137
+ }
138
+ }
139
+ const installUrl = await this.getAppInstallUrl();
140
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
141
+ }
113
142
  async checkTestAccountAppInstallation() {
114
143
  if (!this.appNode || !this.appData) {
115
- return;
144
+ return {};
116
145
  }
117
146
  const { data: { isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups }, } = await (0, localDevAuth_1.fetchAppInstallationData)(this.localDevState.targetTestingAccountId, this.localDevState.projectId, this.appNode.uid, this.appNode.config.auth.requiredScopes, this.appNode.config.auth.optionalScopes);
118
147
  const isReinstall = previouslyAuthorizedScopeGroups.length > 0;
@@ -128,10 +157,7 @@ class AppDevModeInterface {
128
157
  installationState: constants_1.APP_INSTALLATION_STATES.INSTALLED_WITH_OUTDATED_SCOPES,
129
158
  };
130
159
  }
131
- if (!isInstalledWithScopeGroups) {
132
- const installUrl = await this.getAppInstallUrl();
133
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
134
- }
160
+ return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
135
161
  }
136
162
  setUpLocalDevServerMessageListeners() {
137
163
  this.localDevState.addListener('devServerMessage', message => {
@@ -151,7 +177,10 @@ class AppDevModeInterface {
151
177
  if (this.appNode.config.distribution === constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE) {
152
178
  await this.checkMarketplaceAppInstalls();
153
179
  }
154
- await this.checkTestAccountAppInstallation();
180
+ const { needsInstall, isReinstall } = await this.checkTestAccountAppInstallation();
181
+ if (needsInstall) {
182
+ await this.installAppOrOpenInstallUrl(isReinstall || false);
183
+ }
155
184
  }
156
185
  catch (e) {
157
186
  (0, index_1.logError)(e);
@@ -195,7 +195,7 @@ class LocalDevManager {
195
195
  scopes: this.activeApp.config.auth.requiredScopes,
196
196
  redirectUrls: this.activeApp.config.auth.redirectUrls,
197
197
  });
198
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
198
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
199
199
  }
200
200
  }
201
201
  updateKeypressListeners() {
@@ -16,15 +16,16 @@ declare class LocalDevProcess {
16
16
  [key: string]: IntermediateRepresentationNodeLocalDev;
17
17
  };
18
18
  get logger(): LocalDevLogger;
19
- get configFilesUpdatedSinceLastUpload(): Set<string>;
20
19
  private setupDevServers;
21
20
  private startDevServers;
22
21
  private cleanupDevServers;
23
22
  private compareLocalProjectToDeployed;
24
23
  private projectConfigValidForUpload;
24
+ private getIntermediateRepresentation;
25
25
  private updateProjectNodes;
26
+ private updateProjectNodesAfterUpload;
26
27
  handleFileChange(filePath: string, event: string): Promise<void>;
27
- handleConfigFileChange(filePath: string, event: string): Promise<void>;
28
+ handleConfigFileChange(): Promise<void>;
28
29
  start(): Promise<void>;
29
30
  stop(showProgress?: boolean): Promise<void>;
30
31
  uploadProject(): Promise<boolean>;
@@ -46,9 +46,6 @@ class LocalDevProcess {
46
46
  get logger() {
47
47
  return this._logger;
48
48
  }
49
- get configFilesUpdatedSinceLastUpload() {
50
- return this.state.configFilesUpdatedSinceLastUpload;
51
- }
52
49
  async setupDevServers() {
53
50
  try {
54
51
  await this.devServerManager.setup();
@@ -105,17 +102,28 @@ class LocalDevProcess {
105
102
  });
106
103
  return true;
107
104
  }
108
- async updateProjectNodes() {
109
- const intermediateRepresentation = await (0, project_parsing_lib_1.translateForLocalDev)({
105
+ getIntermediateRepresentation(projectNodesAtLastUpload) {
106
+ return (0, project_parsing_lib_1.translateForLocalDev)({
110
107
  projectSourceDir: path_1.default.join(this.state.projectDir, this.state.projectConfig.srcDir),
111
108
  platformVersion: this.state.projectConfig.platformVersion,
112
109
  accountId: this.state.targetProjectAccountId,
113
110
  }, {
114
- configFilesUpdatedSinceLastUpload: this.state.configFilesUpdatedSinceLastUpload,
111
+ projectNodesAtLastUpload,
112
+ profile: this.state.profile,
115
113
  });
114
+ }
115
+ async updateProjectNodes() {
116
+ const intermediateRepresentation = await this.getIntermediateRepresentation(this.state.projectNodesAtLastUpload);
116
117
  this.state.projectNodes =
117
118
  intermediateRepresentation.intermediateNodesIndexedByUid;
118
119
  }
120
+ async updateProjectNodesAfterUpload() {
121
+ const intermediateRepresentation = await this.getIntermediateRepresentation();
122
+ this.state.projectNodes =
123
+ intermediateRepresentation.intermediateNodesIndexedByUid;
124
+ this.state.projectNodesAtLastUpload =
125
+ intermediateRepresentation.intermediateNodesIndexedByUid;
126
+ }
119
127
  async handleFileChange(filePath, event) {
120
128
  await this.updateProjectNodes();
121
129
  try {
@@ -125,10 +133,7 @@ class LocalDevProcess {
125
133
  this.logger.fileChangeError(e);
126
134
  }
127
135
  }
128
- async handleConfigFileChange(filePath, event) {
129
- if (event === 'add' || event === 'change') {
130
- this.state.addUpdatedConfigFileUpdatedSinceLastUpload(filePath);
131
- }
136
+ async handleConfigFileChange() {
132
137
  await this.updateProjectNodes();
133
138
  this.logger.uploadWarning();
134
139
  }
@@ -185,8 +190,7 @@ class LocalDevProcess {
185
190
  this.logger.uploadError(uploadError);
186
191
  return false;
187
192
  }
188
- this.state.resetConfigFilesUpdatedSinceLastUpload();
189
- this.updateProjectNodes();
193
+ await this.updateProjectNodesAfterUpload();
190
194
  this.logger.uploadSuccess();
191
195
  this.logger.clearUploadWarnings();
192
196
  return true;
@@ -6,6 +6,7 @@ import { LocalDevStateConstructorOptions, LocalDevStateListener, AppLocalDevData
6
6
  declare class LocalDevState {
7
7
  private _targetProjectAccountId;
8
8
  private _targetTestingAccountId;
9
+ private _profile?;
9
10
  private _projectConfig;
10
11
  private _projectDir;
11
12
  private _projectId;
@@ -14,15 +15,16 @@ declare class LocalDevState {
14
15
  private _deployedBuild?;
15
16
  private _isGithubLinked;
16
17
  private _projectNodes;
18
+ private _projectNodesAtLastUpload;
17
19
  private _env;
18
20
  private _listeners;
19
- private _configFilesUpdatedSinceLastUpload;
20
21
  private _appData;
21
22
  private _devServerMessage;
22
- constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, env, }: LocalDevStateConstructorOptions);
23
+ constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, profile, env, }: LocalDevStateConstructorOptions);
23
24
  private runListeners;
24
25
  get targetProjectAccountId(): number;
25
26
  get targetTestingAccountId(): number;
27
+ get profile(): string | undefined;
26
28
  get projectConfig(): ProjectConfig;
27
29
  get projectDir(): string;
28
30
  get projectId(): number;
@@ -36,10 +38,13 @@ declare class LocalDevState {
36
38
  set projectNodes(nodes: {
37
39
  [key: string]: IntermediateRepresentationNodeLocalDev;
38
40
  });
41
+ get projectNodesAtLastUpload(): {
42
+ [key: string]: IntermediateRepresentationNodeLocalDev;
43
+ };
44
+ set projectNodesAtLastUpload(nodes: {
45
+ [key: string]: IntermediateRepresentationNodeLocalDev;
46
+ });
39
47
  get env(): Environment;
40
- get configFilesUpdatedSinceLastUpload(): Set<string>;
41
- addUpdatedConfigFileUpdatedSinceLastUpload(filePath: string): void;
42
- resetConfigFilesUpdatedSinceLastUpload(): void;
43
48
  get appData(): Record<string, AppLocalDevData>;
44
49
  getAppDataByUid(uid: string): AppLocalDevData | undefined;
45
50
  setAppDataForUid(uid: string, appData: AppLocalDevData): void;
@@ -4,6 +4,7 @@ const constants_1 = require("../../constants");
4
4
  class LocalDevState {
5
5
  _targetProjectAccountId;
6
6
  _targetTestingAccountId;
7
+ _profile;
7
8
  _projectConfig;
8
9
  _projectDir;
9
10
  _projectId;
@@ -12,14 +13,15 @@ class LocalDevState {
12
13
  _deployedBuild;
13
14
  _isGithubLinked;
14
15
  _projectNodes;
16
+ _projectNodesAtLastUpload;
15
17
  _env;
16
18
  _listeners;
17
- _configFilesUpdatedSinceLastUpload;
18
19
  _appData;
19
20
  _devServerMessage;
20
- constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, env, }) {
21
+ constructor({ targetProjectAccountId, targetTestingAccountId, projectConfig, projectDir, projectId, projectName, debug, deployedBuild, isGithubLinked, initialProjectNodes, profile, env, }) {
21
22
  this._targetProjectAccountId = targetProjectAccountId;
22
23
  this._targetTestingAccountId = targetTestingAccountId;
24
+ this._profile = profile;
23
25
  this._projectConfig = projectConfig;
24
26
  this._projectDir = projectDir;
25
27
  this._projectId = projectId;
@@ -28,8 +30,8 @@ class LocalDevState {
28
30
  this._deployedBuild = deployedBuild;
29
31
  this._isGithubLinked = isGithubLinked;
30
32
  this._projectNodes = initialProjectNodes;
33
+ this._projectNodesAtLastUpload = initialProjectNodes;
31
34
  this._env = env;
32
- this._configFilesUpdatedSinceLastUpload = new Set();
33
35
  this._appData = {};
34
36
  this._devServerMessage = constants_1.LOCAL_DEV_SERVER_MESSAGE_TYPES.INITIAL;
35
37
  this._listeners = {};
@@ -45,10 +47,11 @@ class LocalDevState {
45
47
  get targetTestingAccountId() {
46
48
  return this._targetTestingAccountId;
47
49
  }
50
+ get profile() {
51
+ return this._profile;
52
+ }
48
53
  get projectConfig() {
49
- return {
50
- ...this._projectConfig,
51
- };
54
+ return structuredClone(this._projectConfig);
52
55
  }
53
56
  get projectDir() {
54
57
  return this._projectDir;
@@ -63,34 +66,29 @@ class LocalDevState {
63
66
  return this._debug;
64
67
  }
65
68
  get deployedBuild() {
66
- return (this._deployedBuild && {
67
- ...this._deployedBuild,
68
- });
69
+ return this._deployedBuild && structuredClone(this._deployedBuild);
69
70
  }
70
71
  get isGithubLinked() {
71
72
  return this._isGithubLinked;
72
73
  }
73
74
  get projectNodes() {
74
- return { ...this._projectNodes };
75
+ return structuredClone(this._projectNodes);
75
76
  }
76
77
  set projectNodes(nodes) {
77
78
  this._projectNodes = nodes;
78
79
  this.runListeners('projectNodes');
79
80
  }
80
- get env() {
81
- return this._env;
82
- }
83
- get configFilesUpdatedSinceLastUpload() {
84
- return this._configFilesUpdatedSinceLastUpload;
81
+ get projectNodesAtLastUpload() {
82
+ return structuredClone(this._projectNodesAtLastUpload);
85
83
  }
86
- addUpdatedConfigFileUpdatedSinceLastUpload(filePath) {
87
- this._configFilesUpdatedSinceLastUpload.add(filePath);
84
+ set projectNodesAtLastUpload(nodes) {
85
+ this._projectNodesAtLastUpload = nodes;
88
86
  }
89
- resetConfigFilesUpdatedSinceLastUpload() {
90
- this._configFilesUpdatedSinceLastUpload.clear();
87
+ get env() {
88
+ return this._env;
91
89
  }
92
90
  get appData() {
93
- return { ...this._appData };
91
+ return structuredClone(this._appData);
94
92
  }
95
93
  getAppDataByUid(uid) {
96
94
  return { ...this._appData[uid] };
@@ -21,7 +21,7 @@ class LocalDevWatcher {
21
21
  }
22
22
  handleWatchEvent(filePath, event, configPaths) {
23
23
  if (configPaths.includes(filePath)) {
24
- return this.localDevProcess.handleConfigFileChange(filePath, event);
24
+ return this.localDevProcess.handleConfigFileChange();
25
25
  }
26
26
  return this.localDevProcess.handleFileChange(filePath, event);
27
27
  }
@@ -1,5 +1,5 @@
1
1
  import { ComponentTypes, Component, GenericComponentConfig, PublicAppComponentConfig, PrivateAppComponentConfig, AppCardComponentConfig } from '../../types/Projects';
2
- import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types';
2
+ import { IntermediateRepresentationNode, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types';
3
3
  import { AppIRNode } from '../../types/ProjectComponents';
4
4
  export declare const CONFIG_FILES: {
5
5
  [k in ComponentTypes]: string;
@@ -15,4 +15,4 @@ export declare function getProjectComponentTypes(components: Array<Component>):
15
15
  export declare function getComponentUid(component?: Component | null): string | null;
16
16
  export declare function componentIsApp(component?: Component | null): component is Component<PublicAppComponentConfig | PrivateAppComponentConfig>;
17
17
  export declare function componentIsPublicApp(component?: Component | null): component is Component<PublicAppComponentConfig>;
18
- export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev): component is AppIRNode;
18
+ export declare function isAppIRNode(component: IntermediateRepresentationNodeLocalDev | IntermediateRepresentationNode): component is AppIRNode;
@@ -1,4 +1,5 @@
1
1
  import { FileResult } from 'tmp';
2
+ import { IntermediateRepresentation } from '@hubspot/project-parsing-lib';
2
3
  import { ProjectConfig } from '../../types/Projects';
3
4
  type ProjectUploadCallbackFunction<T> = (accountId: number, projectConfig: ProjectConfig, tempFile: FileResult, buildId: number) => Promise<T>;
4
5
  type ProjectUploadResult<T> = {
@@ -18,4 +19,7 @@ type HandleProjectUploadArg<T> = {
18
19
  profile?: string;
19
20
  };
20
21
  export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
22
+ export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
23
+ export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
24
+ export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<IntermediateRepresentation | undefined>;
21
25
  export {};
@@ -4,22 +4,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.handleProjectUpload = handleProjectUpload;
7
+ exports.validateSourceDirectory = validateSourceDirectory;
8
+ exports.validateNoHSMetaMismatch = validateNoHSMetaMismatch;
9
+ exports.handleTranslate = handleTranslate;
7
10
  const archiver_1 = __importDefault(require("archiver"));
8
11
  const tmp_1 = __importDefault(require("tmp"));
9
12
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
13
  const path_1 = __importDefault(require("path"));
11
14
  const projects_1 = require("@hubspot/local-dev-lib/api/projects");
12
15
  const ignoreRules_1 = require("@hubspot/local-dev-lib/ignoreRules");
16
+ const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
13
17
  const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
14
18
  const ui_1 = require("../ui");
15
- const exitCodes_1 = require("../enums/exitCodes");
16
- const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
17
19
  const errorHandlers_1 = require("../errorHandlers");
18
20
  const node_util_1 = __importDefault(require("node:util"));
19
21
  const en_1 = require("../../lang/en");
20
22
  const ensureProjectExists_1 = require("./ensureProjectExists");
21
23
  const logger_1 = require("../ui/logger");
22
24
  const buildAndDeploy_1 = require("./buildAndDeploy");
25
+ const exitCodes_1 = require("../enums/exitCodes");
23
26
  async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
24
27
  SpinniesManager_1.default.init({});
25
28
  const accountIdentifier = (0, ui_1.uiAccountDescription)(accountId);
@@ -49,14 +52,18 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
49
52
  }
50
53
  async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, }) {
51
54
  const srcDir = path_1.default.resolve(projectDir, projectConfig.srcDir);
52
- const filenames = fs_extra_1.default.readdirSync(srcDir);
53
- if (!filenames || filenames.length === 0) {
54
- logger_1.uiLogger.log(en_1.lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
55
- process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
55
+ try {
56
+ validateSourceDirectory(srcDir, projectConfig);
56
57
  }
57
- const hasHsMetaFiles = await (0, project_parsing_lib_1.projectContainsHsMetaFiles)(srcDir);
58
- if (!(0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion) && hasHsMetaFiles) {
59
- logger_1.uiLogger.error(en_1.lib.projectUpload.wrongPlatformVersionMetaFiles);
58
+ catch (e) {
59
+ (0, errorHandlers_1.logError)(e);
60
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
61
+ }
62
+ try {
63
+ await validateNoHSMetaMismatch(srcDir, projectConfig);
64
+ }
65
+ catch (e) {
66
+ (0, errorHandlers_1.logError)(e);
60
67
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
61
68
  }
62
69
  const tempFile = tmp_1.default.fileSync({ postfix: '.zip' });
@@ -68,21 +75,11 @@ async function handleProjectUpload({ accountId, projectConfig, projectDir, callb
68
75
  let intermediateRepresentation;
69
76
  if (sendIR) {
70
77
  try {
71
- intermediateRepresentation = await (0, project_parsing_lib_1.translate)({
72
- projectSourceDir: path_1.default.join(projectDir, projectConfig.srcDir),
73
- platformVersion: projectConfig.platformVersion,
74
- accountId,
75
- }, { skipValidation, profile });
76
- logger_1.uiLogger.debug(node_util_1.default.inspect(intermediateRepresentation, false, null, true));
78
+ intermediateRepresentation = await handleTranslate(projectDir, projectConfig, accountId, skipValidation, profile);
77
79
  }
78
80
  catch (e) {
79
- if ((0, project_parsing_lib_1.isTranslationError)(e)) {
80
- logger_1.uiLogger.error(e.toString());
81
- }
82
- else {
83
- (0, errorHandlers_1.logError)(e);
84
- }
85
- return process.exit(exitCodes_1.EXIT_CODES.ERROR);
81
+ (0, errorHandlers_1.logError)(e);
82
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
86
83
  }
87
84
  }
88
85
  await (0, ensureProjectExists_1.ensureProjectExists)(accountId, projectConfig.name, {
@@ -116,3 +113,41 @@ async function handleProjectUpload({ accountId, projectConfig, projectDir, callb
116
113
  archive.finalize();
117
114
  return result;
118
115
  }
116
+ function validateSourceDirectory(srcDir, projectConfig) {
117
+ const filenames = fs_extra_1.default.readdirSync(srcDir);
118
+ if (!filenames || filenames.length === 0) {
119
+ const validationError = new Error(en_1.lib.projectUpload.handleProjectUpload.emptySource(projectConfig.srcDir));
120
+ validationError.name = 'ProjectValidationError';
121
+ throw validationError;
122
+ }
123
+ }
124
+ async function validateNoHSMetaMismatch(srcDir, projectConfig) {
125
+ const hasHsMetaFiles = await (0, project_parsing_lib_1.projectContainsHsMetaFiles)(srcDir);
126
+ if (!(0, buildAndDeploy_1.useV3Api)(projectConfig.platformVersion) && hasHsMetaFiles) {
127
+ const validationError = new Error(en_1.lib.projectUpload.wrongPlatformVersionMetaFiles);
128
+ validationError.name = 'ProjectValidationError';
129
+ throw validationError;
130
+ }
131
+ }
132
+ async function handleTranslate(projectDir, projectConfig, accountId, skipValidation, profile) {
133
+ try {
134
+ const intermediateRepresentation = await (0, project_parsing_lib_1.translate)({
135
+ projectSourceDir: path_1.default.join(projectDir, projectConfig.srcDir),
136
+ platformVersion: projectConfig.platformVersion,
137
+ accountId,
138
+ }, { skipValidation, profile });
139
+ logger_1.uiLogger.debug(node_util_1.default.inspect(intermediateRepresentation, false, null, true));
140
+ return intermediateRepresentation;
141
+ }
142
+ catch (e) {
143
+ if ((0, project_parsing_lib_1.isTranslationError)(e)) {
144
+ const validationError = new Error(e.toString());
145
+ validationError.name = 'ProjectValidationError';
146
+ throw validationError;
147
+ }
148
+ else {
149
+ (0, errorHandlers_1.logError)(e);
150
+ }
151
+ }
152
+ return undefined;
153
+ }
@@ -1,3 +1,5 @@
1
+ export declare function getProjectComponentDistributionUrl(projectName: string, componentName: string, accountId: number): string;
2
+ export declare function getDeveloperOverviewUrl(accountId: number): string;
1
3
  export declare function getProjectDetailUrl(projectName: string, accountId: number): string | undefined;
2
4
  export declare function getProjectSettingsUrl(projectName: string, accountId: number): string | undefined;
3
5
  export declare function getProjectActivityUrl(projectName: string, accountId: number): string;
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProjectComponentDistributionUrl = getProjectComponentDistributionUrl;
4
+ exports.getDeveloperOverviewUrl = getDeveloperOverviewUrl;
3
5
  exports.getProjectDetailUrl = getProjectDetailUrl;
4
6
  exports.getProjectSettingsUrl = getProjectSettingsUrl;
5
7
  exports.getProjectActivityUrl = getProjectActivityUrl;
@@ -15,6 +17,14 @@ function getBaseUrl(accountId) {
15
17
  function getProjectHomeUrl(accountId) {
16
18
  return `${getBaseUrl(accountId)}/developer-projects/${accountId}`;
17
19
  }
20
+ function getProjectComponentDistributionUrl(projectName, componentName, accountId) {
21
+ const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)(accountId) === 'qa' ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD);
22
+ return `${baseUrl}/developer-projects/${accountId}/project/${projectName}/component/${componentName}/distribution`;
23
+ }
24
+ function getDeveloperOverviewUrl(accountId) {
25
+ const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)(accountId) === 'qa' ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD);
26
+ return `${baseUrl}/developer-overview/${accountId}`;
27
+ }
18
28
  function getProjectDetailUrl(projectName, accountId) {
19
29
  if (!projectName)
20
30
  return;
@@ -0,0 +1,17 @@
1
+ import { DeveloperTestAccountConfig } from '@hubspot/local-dev-lib/types/developerTestAccounts';
2
+ declare const hubs: {
3
+ readonly MARKETING: "marketingLevel";
4
+ readonly OPS: "opsLevel";
5
+ readonly SERVICE: "serviceLevel";
6
+ readonly SALES: "salesLevel";
7
+ readonly CONTENT: "contentLevel";
8
+ };
9
+ type HubName = keyof typeof hubs;
10
+ type HubTier = 'STARTER' | 'PROFESSIONAL' | 'ENTERPRISE';
11
+ export type HubConfig = `${HubName}:${HubTier}`;
12
+ export declare function createDeveloperTestAccountConfigPrompt(args?: {
13
+ name?: string;
14
+ description?: string;
15
+ tiers?: HubConfig[];
16
+ }): Promise<DeveloperTestAccountConfig>;
17
+ export {};