@hubspot/local-dev-lib 0.5.0-experimental.9 → 0.6.1-experimental.0

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.
@@ -1,3 +1,4 @@
1
1
  import { FireAlarm } from '../types/FireAlarm';
2
2
  import { HubSpotPromise } from '../types/Http';
3
+ export declare const FIREALARM_API_AUTH_PATH = "firealarm/v4/alarm";
3
4
  export declare function fetchFireAlarms(accountId: number): HubSpotPromise<Array<FireAlarm>>;
package/api/fireAlarm.js CHANGED
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchFireAlarms = void 0;
3
+ exports.fetchFireAlarms = exports.FIREALARM_API_AUTH_PATH = void 0;
4
4
  const http_1 = require("../http");
5
- const FIREALARM_API_AUTH_PATH = 'firealarm/v4/alarm';
5
+ exports.FIREALARM_API_AUTH_PATH = 'firealarm/v4/alarm';
6
6
  function fetchFireAlarms(accountId) {
7
7
  return http_1.http.get(accountId, {
8
- url: `${FIREALARM_API_AUTH_PATH}/hubspot-cli/${accountId}`,
8
+ url: `${exports.FIREALARM_API_AUTH_PATH}/hubspot-cli/${accountId}`,
9
9
  });
10
10
  }
11
11
  exports.fetchFireAlarms = fetchFireAlarms;
package/api/projects.d.ts CHANGED
@@ -4,7 +4,7 @@ import { HubSpotPromise, QueryParams } from '../types/Http';
4
4
  import { Project, FetchProjectResponse, UploadProjectResponse, ProjectSettings, FetchPlatformVersionResponse, WarnLogsResponse, UploadIRResponse } from '../types/Project';
5
5
  import { Build, FetchProjectBuildsResponse } from '../types/Build';
6
6
  import { ComponentStructureResponse, ProjectComponentsMetadata } from '../types/ComponentStructure';
7
- import { Deploy, ProjectDeployResponse } from '../types/Deploy';
7
+ import { Deploy, ProjectDeployResponse, ProjectDeployResponseV1 } from '../types/Deploy';
8
8
  import { MigrateAppResponse, CloneAppResponse, PollAppResponse } from '../types/Migration';
9
9
  export declare function fetchProjects(accountId: number): HubSpotPromise<FetchProjectResponse>;
10
10
  export declare function createProject(accountId: number, name: string): HubSpotPromise<Project>;
@@ -17,7 +17,12 @@ export declare function fetchPlatformVersions(accountId: number): HubSpotPromise
17
17
  export declare function fetchProjectBuilds(accountId: number, projectName: string, params?: QueryParams): HubSpotPromise<FetchProjectBuildsResponse>;
18
18
  export declare function getBuildStatus(accountId: number, projectName: string, buildId: number): HubSpotPromise<Build>;
19
19
  export declare function getBuildStructure(accountId: number, projectName: string, buildId: number): HubSpotPromise<ComponentStructureResponse>;
20
+ /**
21
+ * @deprecated Use the separate deployProjectV1 and deployProjectV2
22
+ */
20
23
  export declare function deployProject(accountId: number, projectName: string, buildId: number, useNewDeployApi?: boolean, force?: boolean): HubSpotPromise<ProjectDeployResponse>;
24
+ export declare function deployProjectV1(accountId: number, projectName: string, buildId: number, force?: boolean): HubSpotPromise<ProjectDeployResponseV1>;
25
+ export declare function deployProjectV2(accountId: number, projectName: string, buildId: number, force?: boolean): HubSpotPromise<ProjectDeployResponse>;
21
26
  export declare function getDeployStatus(accountId: number, projectName: string, deployId: number): HubSpotPromise<Deploy>;
22
27
  export declare function getDeployStructure(accountId: number, projectName: string, deployId: number): HubSpotPromise<ComponentStructureResponse>;
23
28
  export declare function fetchProjectSettings(accountId: number, projectName: string): HubSpotPromise<ProjectSettings>;
package/api/projects.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.downloadClonedProject = exports.checkCloneStatus = exports.cloneApp = exports.checkMigrationStatus = exports.migrateApp = exports.fetchDeployWarnLogs = exports.fetchBuildWarnLogs = exports.cancelStagedBuild = exports.deleteFileFromBuild = exports.uploadFileToBuild = exports.queueBuild = exports.provisionBuild = exports.fetchProjectSettings = exports.getDeployStructure = exports.getDeployStatus = exports.deployProject = exports.getBuildStructure = exports.getBuildStatus = exports.fetchProjectBuilds = exports.fetchPlatformVersions = exports.deleteProject = exports.downloadProject = exports.fetchProjectComponentsMetadata = exports.fetchProject = exports.uploadProject = exports.createProject = exports.fetchProjects = void 0;
6
+ exports.downloadClonedProject = exports.checkCloneStatus = exports.cloneApp = exports.checkMigrationStatus = exports.migrateApp = exports.fetchDeployWarnLogs = exports.fetchBuildWarnLogs = exports.cancelStagedBuild = exports.deleteFileFromBuild = exports.uploadFileToBuild = exports.queueBuild = exports.provisionBuild = exports.fetchProjectSettings = exports.getDeployStructure = exports.getDeployStatus = exports.deployProjectV2 = exports.deployProjectV1 = exports.deployProject = exports.getBuildStructure = exports.getBuildStatus = exports.fetchProjectBuilds = exports.fetchPlatformVersions = exports.deleteProject = exports.downloadProject = exports.fetchProjectComponentsMetadata = exports.fetchProject = exports.uploadProject = exports.createProject = exports.fetchProjects = void 0;
7
7
  const http_1 = require("../http");
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const PROJECTS_API_PATH = 'dfs/v1/projects';
@@ -116,6 +116,9 @@ function getBuildStructure(accountId, projectName, buildId) {
116
116
  });
117
117
  }
118
118
  exports.getBuildStructure = getBuildStructure;
119
+ /**
120
+ * @deprecated Use the separate deployProjectV1 and deployProjectV2
121
+ */
119
122
  function deployProject(accountId, projectName, buildId, useNewDeployApi = false, force = false) {
120
123
  if (useNewDeployApi) {
121
124
  return http_1.http.post(accountId, {
@@ -137,6 +140,28 @@ function deployProject(accountId, projectName, buildId, useNewDeployApi = false,
137
140
  });
138
141
  }
139
142
  exports.deployProject = deployProject;
143
+ function deployProjectV1(accountId, projectName, buildId, force = false) {
144
+ return http_1.http.post(accountId, {
145
+ url: `${PROJECTS_DEPLOY_API_PATH}/deploys/queue/async`,
146
+ data: {
147
+ projectName,
148
+ buildId,
149
+ skipRemovalWarning: force,
150
+ },
151
+ });
152
+ }
153
+ exports.deployProjectV1 = deployProjectV1;
154
+ function deployProjectV2(accountId, projectName, buildId, force = false) {
155
+ return http_1.http.post(accountId, {
156
+ url: `${PROJECTS_DEPLOY_API_PATH_V3}/deploys/queue/async`,
157
+ data: {
158
+ projectName,
159
+ targetBuildId: buildId,
160
+ ignoreWarnings: force,
161
+ },
162
+ });
163
+ }
164
+ exports.deployProjectV2 = deployProjectV2;
140
165
  function getDeployStatus(accountId, projectName, deployId) {
141
166
  return http_1.http.get(accountId, {
142
167
  url: `${PROJECTS_DEPLOY_API_PATH}/deploy-status/projects/${encodeURIComponent(projectName)}/deploys/${deployId}`,
package/config/index.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import { HubSpotConfigAccount } from '../types/Accounts';
2
- import { HubSpotConfig, ConfigFlag } from '../types/Config';
2
+ import { HubSpotConfig, ConfigFlag, HubSpotConfigValidationResult } from '../types/Config';
3
3
  import { CmsPublishMode } from '../types/Files';
4
4
  import { Environment } from '../types/Config';
5
+ export declare function getGlobalConfigFilePath(): string;
6
+ export declare function getLocalConfigFilePathIfExists(cwd?: string): string | null;
5
7
  export declare function localConfigFileExists(): boolean;
6
8
  export declare function globalConfigFileExists(): boolean;
7
9
  export declare function configFileExists(): boolean;
8
10
  export declare function getConfigFilePath(): string;
9
11
  export declare function getConfig(): HubSpotConfig;
10
- export declare function isConfigValid(): boolean;
12
+ export declare function validateConfig(): HubSpotConfigValidationResult;
11
13
  export declare function createEmptyConfigFile(useGlobalConfig?: boolean): void;
12
- export declare function deleteConfigFile(): void;
14
+ export declare function deleteConfigFileIfEmpty(): void;
13
15
  export declare function getConfigAccountById(accountId: number): HubSpotConfigAccount;
14
16
  export declare function getConfigAccountByName(accountName: string): HubSpotConfigAccount;
15
17
  export declare function getConfigAccountIfExists(identifier: number | string): HubSpotConfigAccount | undefined;
package/config/index.js CHANGED
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.removeLocalStateFlag = exports.addLocalStateFlag = exports.hasLocalStateFlag = exports.isConfigFlagEnabled = exports.updateDefaultCmsPublishMode = exports.updateAutoOpenBrowser = exports.updateAllowAutoUpdates = exports.updateAllowUsageTracking = exports.updateHttpTimeout = exports.removeAccountFromConfig = exports.renameConfigAccount = exports.setConfigAccountAsDefault = exports.updateConfigAccount = exports.addConfigAccount = exports.getConfigAccountEnvironment = exports.getAllConfigAccounts = exports.getConfigDefaultAccountIfExists = exports.getConfigDefaultAccount = exports.getConfigAccountIfExists = exports.getConfigAccountByName = exports.getConfigAccountById = exports.deleteConfigFile = exports.createEmptyConfigFile = exports.isConfigValid = exports.getConfig = exports.getConfigFilePath = exports.configFileExists = exports.globalConfigFileExists = exports.localConfigFileExists = void 0;
6
+ exports.removeLocalStateFlag = exports.addLocalStateFlag = exports.hasLocalStateFlag = exports.isConfigFlagEnabled = exports.updateDefaultCmsPublishMode = exports.updateAutoOpenBrowser = exports.updateAllowAutoUpdates = exports.updateAllowUsageTracking = exports.updateHttpTimeout = exports.removeAccountFromConfig = exports.renameConfigAccount = exports.setConfigAccountAsDefault = exports.updateConfigAccount = exports.addConfigAccount = exports.getConfigAccountEnvironment = exports.getAllConfigAccounts = exports.getConfigDefaultAccountIfExists = exports.getConfigDefaultAccount = exports.getConfigAccountIfExists = exports.getConfigAccountByName = exports.getConfigAccountById = exports.deleteConfigFileIfEmpty = exports.createEmptyConfigFile = exports.validateConfig = exports.getConfig = exports.getConfigFilePath = exports.configFileExists = exports.globalConfigFileExists = exports.localConfigFileExists = exports.getLocalConfigFilePathIfExists = exports.getGlobalConfigFilePath = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const findup_sync_1 = __importDefault(require("findup-sync"));
8
9
  const config_1 = require("../constants/config");
9
10
  const logger_1 = require("../lib/logger");
10
11
  const utils_1 = require("./utils");
@@ -14,12 +15,26 @@ const defaultAccountOverride_1 = require("./defaultAccountOverride");
14
15
  const environment_1 = require("../lib/environment");
15
16
  const HubSpotConfigError_1 = require("../models/HubSpotConfigError");
16
17
  const config_2 = require("../constants/config");
18
+ const isDeepEqual_1 = require("../lib/isDeepEqual");
19
+ const path_1 = require("../lib/path");
20
+ const EMPTY_CONFIG = { accounts: [] };
21
+ function getGlobalConfigFilePath() {
22
+ return config_1.GLOBAL_CONFIG_PATH;
23
+ }
24
+ exports.getGlobalConfigFilePath = getGlobalConfigFilePath;
25
+ function getLocalConfigFilePathIfExists(cwd) {
26
+ return (0, findup_sync_1.default)([
27
+ config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
28
+ config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME.replace('.yml', '.yaml'),
29
+ ], { cwd: cwd || (0, path_1.getCwd)() });
30
+ }
31
+ exports.getLocalConfigFilePathIfExists = getLocalConfigFilePathIfExists;
17
32
  function localConfigFileExists() {
18
- return Boolean((0, utils_1.getLocalConfigFilePath)());
33
+ return Boolean(getLocalConfigFilePathIfExists());
19
34
  }
20
35
  exports.localConfigFileExists = localConfigFileExists;
21
36
  function globalConfigFileExists() {
22
- return (0, utils_1.doesConfigFileExistAtPath)((0, utils_1.getGlobalConfigFilePath)());
37
+ return (0, utils_1.doesConfigFileExistAtPath)(getGlobalConfigFilePath());
23
38
  }
24
39
  exports.globalConfigFileExists = globalConfigFileExists;
25
40
  function configFileExists() {
@@ -32,11 +47,11 @@ function configFileExists() {
32
47
  }
33
48
  exports.configFileExists = configFileExists;
34
49
  function getConfigDefaultFilePath() {
35
- const globalConfigFilePath = (0, utils_1.getGlobalConfigFilePath)();
50
+ const globalConfigFilePath = getGlobalConfigFilePath();
36
51
  if ((0, utils_1.doesConfigFileExistAtPath)(globalConfigFilePath)) {
37
52
  return globalConfigFilePath;
38
53
  }
39
- const localConfigFilePath = (0, utils_1.getLocalConfigFilePath)();
54
+ const localConfigFilePath = getLocalConfigFilePathIfExists();
40
55
  if (!localConfigFilePath) {
41
56
  throw new HubSpotConfigError_1.HubSpotConfigError((0, lang_1.i18n)('config.getDefaultConfigFilePath.error'), config_2.HUBSPOT_CONFIG_ERROR_TYPES.CONFIG_NOT_FOUND, config_1.HUBSPOT_CONFIG_OPERATIONS.READ);
42
57
  }
@@ -66,57 +81,61 @@ function getConfig() {
66
81
  }
67
82
  }
68
83
  exports.getConfig = getConfig;
69
- function isConfigValid() {
84
+ function validateConfig() {
70
85
  const config = getConfig();
71
86
  if (config.accounts.length === 0) {
72
- logger_1.logger.debug((0, lang_1.i18n)('config.isConfigValid.missingAccounts'));
73
- return false;
87
+ return {
88
+ isValid: false,
89
+ errors: [(0, lang_1.i18n)('config.validateConfig.missingAccounts')],
90
+ };
74
91
  }
75
92
  const accountIdsMap = {};
76
93
  const accountNamesMap = {};
77
- return config.accounts.every(account => {
78
- if (!(0, utils_1.isConfigAccountValid)(account)) {
79
- return false;
94
+ const validationErrors = [];
95
+ config.accounts.forEach(account => {
96
+ const accountValidationResult = (0, utils_1.validateConfigAccount)(account);
97
+ if (!accountValidationResult.isValid) {
98
+ validationErrors.push(...accountValidationResult.errors);
80
99
  }
81
100
  if (accountIdsMap[account.accountId]) {
82
- logger_1.logger.debug((0, lang_1.i18n)('config.isConfigValid.duplicateAccountIds', {
101
+ validationErrors.push((0, lang_1.i18n)('config.validateConfig.duplicateAccountIds', {
83
102
  accountId: account.accountId,
84
103
  }));
85
- return false;
86
104
  }
87
105
  if (account.name) {
88
106
  if (accountNamesMap[account.name.toLowerCase()]) {
89
- logger_1.logger.debug((0, lang_1.i18n)('config.isConfigValid.duplicateAccountNames', {
107
+ validationErrors.push((0, lang_1.i18n)('config.validateConfig.duplicateAccountNames', {
90
108
  accountName: account.name,
91
109
  }));
92
- return false;
93
110
  }
94
111
  if (/\s+/.test(account.name)) {
95
- logger_1.logger.debug((0, lang_1.i18n)('config.isConfigValid.invalidAccountName', {
112
+ validationErrors.push((0, lang_1.i18n)('config.validateConfig.invalidAccountName', {
96
113
  accountName: account.name,
97
114
  }));
98
- return false;
99
115
  }
100
116
  accountNamesMap[account.name] = true;
101
117
  }
102
118
  accountIdsMap[account.accountId] = true;
103
- return true;
104
119
  });
120
+ return { isValid: validationErrors.length === 0, errors: validationErrors };
105
121
  }
106
- exports.isConfigValid = isConfigValid;
122
+ exports.validateConfig = validateConfig;
107
123
  function createEmptyConfigFile(useGlobalConfig = false) {
108
124
  const { configFilePathFromEnvironment } = (0, utils_1.getConfigPathEnvironmentVariables)();
109
125
  const defaultPath = useGlobalConfig
110
- ? (0, utils_1.getGlobalConfigFilePath)()
126
+ ? getGlobalConfigFilePath()
111
127
  : (0, utils_1.getLocalConfigDefaultFilePath)();
112
128
  const pathToWrite = configFilePathFromEnvironment || defaultPath;
113
- (0, utils_1.writeConfigFile)({ accounts: [] }, pathToWrite);
129
+ (0, utils_1.writeConfigFile)(EMPTY_CONFIG, pathToWrite);
114
130
  }
115
131
  exports.createEmptyConfigFile = createEmptyConfigFile;
116
- function deleteConfigFile() {
132
+ function deleteConfigFileIfEmpty() {
117
133
  const pathToDelete = getConfigFilePath();
118
134
  try {
119
- fs_extra_1.default.unlinkSync(pathToDelete);
135
+ const config = getConfig();
136
+ if ((0, isDeepEqual_1.isDeepEqual)(config, EMPTY_CONFIG)) {
137
+ fs_extra_1.default.unlinkSync(pathToDelete);
138
+ }
120
139
  }
121
140
  catch (error) {
122
141
  const { message, type } = (0, utils_1.handleConfigFileSystemError)(error, pathToDelete);
@@ -125,7 +144,7 @@ function deleteConfigFile() {
125
144
  });
126
145
  }
127
146
  }
128
- exports.deleteConfigFile = deleteConfigFile;
147
+ exports.deleteConfigFileIfEmpty = deleteConfigFileIfEmpty;
129
148
  function getConfigAccountById(accountId) {
130
149
  const { accounts } = getConfig();
131
150
  const account = (0, utils_1.getConfigAccountByIdentifier)(accounts, config_1.ACCOUNT_IDENTIFIERS.ACCOUNT_ID, accountId);
@@ -153,7 +172,7 @@ function getConfigDefaultAccount() {
153
172
  const { accounts, defaultAccount } = getConfig();
154
173
  let defaultAccountToUse = defaultAccount;
155
174
  const currentConfigPath = getConfigFilePath();
156
- const globalConfigPath = (0, utils_1.getGlobalConfigFilePath)();
175
+ const globalConfigPath = getGlobalConfigFilePath();
157
176
  if (currentConfigPath === globalConfigPath && globalConfigFileExists()) {
158
177
  const defaultAccountOverrideAccountId = (0, defaultAccountOverride_1.getDefaultAccountOverrideAccountId)();
159
178
  defaultAccountToUse = defaultAccountOverrideAccountId || defaultAccount;
@@ -175,7 +194,7 @@ function getConfigDefaultAccountIfExists() {
175
194
  let defaultAccountToUse = defaultAccount;
176
195
  // Only check for default account override if we're using the global config
177
196
  const currentConfigPath = getConfigFilePath();
178
- const globalConfigPath = (0, utils_1.getGlobalConfigFilePath)();
197
+ const globalConfigPath = getGlobalConfigFilePath();
179
198
  if (currentConfigPath === globalConfigPath && globalConfigFileExists()) {
180
199
  const defaultAccountOverrideAccountId = (0, defaultAccountOverride_1.getDefaultAccountOverrideAccountId)();
181
200
  defaultAccountToUse = defaultAccountOverrideAccountId || defaultAccount;
@@ -204,7 +223,7 @@ function getConfigAccountEnvironment(identifier) {
204
223
  }
205
224
  exports.getConfigAccountEnvironment = getConfigAccountEnvironment;
206
225
  function addConfigAccount(accountToAdd) {
207
- if (!(0, utils_1.isConfigAccountValid)(accountToAdd)) {
226
+ if (!(0, utils_1.validateConfigAccount)(accountToAdd)) {
208
227
  throw new HubSpotConfigError_1.HubSpotConfigError((0, lang_1.i18n)('config.addConfigAccount.invalidAccount'), config_2.HUBSPOT_CONFIG_ERROR_TYPES.INVALID_ACCOUNT, config_1.HUBSPOT_CONFIG_OPERATIONS.WRITE);
209
228
  }
210
229
  const config = getConfig();
@@ -219,7 +238,7 @@ function addConfigAccount(accountToAdd) {
219
238
  }
220
239
  exports.addConfigAccount = addConfigAccount;
221
240
  function updateConfigAccount(updatedAccount) {
222
- if (!(0, utils_1.isConfigAccountValid)(updatedAccount)) {
241
+ if (!(0, utils_1.validateConfigAccount)(updatedAccount)) {
223
242
  throw new HubSpotConfigError_1.HubSpotConfigError((0, lang_1.i18n)('config.updateConfigAccount.invalidAccount', {
224
243
  name: updatedAccount.name,
225
244
  }), config_2.HUBSPOT_CONFIG_ERROR_TYPES.INVALID_ACCOUNT, config_1.HUBSPOT_CONFIG_OPERATIONS.WRITE);
@@ -240,7 +259,7 @@ function setConfigAccountAsDefault(identifier) {
240
259
  const account = (0, utils_1.getConfigAccountByInferredIdentifier)(config.accounts, identifier);
241
260
  if (!account) {
242
261
  throw new HubSpotConfigError_1.HubSpotConfigError((0, lang_1.i18n)('config.setConfigAccountAsDefault.accountNotFound', {
243
- accountId: identifier,
262
+ identifier,
244
263
  }), config_2.HUBSPOT_CONFIG_ERROR_TYPES.ACCOUNT_NOT_FOUND, config_1.HUBSPOT_CONFIG_OPERATIONS.WRITE);
245
264
  }
246
265
  config.defaultAccount = account.accountId;
@@ -15,3 +15,4 @@ export declare function mergeConfigAccounts(toConfig: HubSpotConfig, fromConfig:
15
15
  configWithMergedAccounts: HubSpotConfig;
16
16
  skippedAccountIds: Array<string | number>;
17
17
  };
18
+ export declare function archiveConfigAtPath(configPath: string): void;
package/config/migrate.js CHANGED
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mergeConfigAccounts = exports.mergeConfigProperties = exports.migrateConfigAtPath = exports.getConfigAtPath = void 0;
6
+ exports.archiveConfigAtPath = exports.mergeConfigAccounts = exports.mergeConfigProperties = exports.migrateConfigAtPath = exports.getConfigAtPath = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const index_1 = require("./index");
9
9
  const config_1 = require("../constants/config");
10
10
  const utils_1 = require("./utils");
11
+ const path_1 = __importDefault(require("path"));
11
12
  function getConfigAtPath(path) {
12
13
  const configFileSource = (0, utils_1.readConfigFile)(path);
13
14
  return (0, utils_1.parseConfig)(configFileSource, path);
@@ -16,8 +17,7 @@ exports.getConfigAtPath = getConfigAtPath;
16
17
  function migrateConfigAtPath(path) {
17
18
  (0, index_1.createEmptyConfigFile)(true);
18
19
  const configToMigrate = getConfigAtPath(path);
19
- (0, utils_1.writeConfigFile)(configToMigrate, (0, utils_1.getGlobalConfigFilePath)());
20
- fs_1.default.unlinkSync(path);
20
+ (0, utils_1.writeConfigFile)(configToMigrate, (0, index_1.getGlobalConfigFilePath)());
21
21
  }
22
22
  exports.migrateConfigAtPath = migrateConfigAtPath;
23
23
  function mergeConfigProperties(toConfig, fromConfig, force) {
@@ -64,7 +64,9 @@ function mergeConfigProperties(toConfig, fromConfig, force) {
64
64
  config_1.DEFAULT_ACCOUNT,
65
65
  ];
66
66
  propertiesToCheck.forEach(prop => {
67
- if (toConfig[prop] !== undefined && toConfig[prop] !== fromConfig[prop]) {
67
+ if (toConfig[prop] !== undefined &&
68
+ fromConfig[prop] !== undefined &&
69
+ toConfig[prop] !== fromConfig[prop]) {
68
70
  conflicts.push({
69
71
  property: prop,
70
72
  oldValue: fromConfig[prop],
@@ -98,7 +100,13 @@ function buildConfigWithMergedAccounts(toConfig, fromConfig) {
98
100
  }
99
101
  function mergeConfigAccounts(toConfig, fromConfig) {
100
102
  const { configWithMergedAccounts, skippedAccountIds } = buildConfigWithMergedAccounts(toConfig, fromConfig);
101
- (0, utils_1.writeConfigFile)(configWithMergedAccounts, (0, utils_1.getGlobalConfigFilePath)());
103
+ (0, utils_1.writeConfigFile)(configWithMergedAccounts, (0, index_1.getGlobalConfigFilePath)());
102
104
  return { configWithMergedAccounts, skippedAccountIds };
103
105
  }
104
106
  exports.mergeConfigAccounts = mergeConfigAccounts;
107
+ function archiveConfigAtPath(configPath) {
108
+ const dir = path_1.default.dirname(configPath);
109
+ const archivedConfigPath = path_1.default.join(dir, config_1.ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME);
110
+ fs_1.default.renameSync(configPath, archivedConfigPath);
111
+ }
112
+ exports.archiveConfigAtPath = archiveConfigAtPath;
package/config/utils.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { ACCOUNT_IDENTIFIERS } from '../constants/config';
2
- import { HubSpotConfig, DeprecatedHubSpotConfigFields, HubSpotConfigErrorType } from '../types/Config';
3
- import { HubSpotConfigAccount, AccountType, TokenInfo } from '../types/Accounts';
2
+ import { HubSpotConfig, DeprecatedHubSpotConfigFields, HubSpotConfigErrorType, HubSpotConfigValidationResult } from '../types/Config';
3
+ import { HubSpotConfigAccount } from '../types/Accounts';
4
4
  import { ValueOf } from '../types/Utils';
5
- export declare function getGlobalConfigFilePath(): string;
6
- export declare function getLocalConfigFilePath(): string | null;
7
5
  export declare function getLocalConfigDefaultFilePath(): string;
8
6
  export declare function getConfigPathEnvironmentVariables(): {
9
7
  useEnvironmentConfig: boolean;
@@ -12,57 +10,10 @@ export declare function getConfigPathEnvironmentVariables(): {
12
10
  export declare function doesConfigFileExistAtPath(path: string): boolean;
13
11
  export declare function readConfigFile(configPath: string): string;
14
12
  export declare function removeUndefinedFieldsFromConfigAccount<T extends HubSpotConfigAccount | Partial<HubSpotConfigAccount> = HubSpotConfigAccount>(account: T): T;
15
- export declare function formatConfigForWrite(config: HubSpotConfig): {
16
- accounts: ({
17
- personalAccessKey: string;
18
- auth: {
19
- tokenInfo: TokenInfo;
20
- };
21
- accountType?: AccountType | undefined;
22
- defaultCmsPublishMode?: import("../types/Files").CmsPublishMode | undefined;
23
- parentAccountId?: number | undefined;
24
- name: string;
25
- accountId: number;
26
- env: import("../types/Config").Environment;
27
- authType: "apikey" | "oauth2" | "personalaccesskey";
28
- } | {
29
- auth: {
30
- clientId: string;
31
- clientSecret: string;
32
- scopes: string[];
33
- tokenInfo: TokenInfo;
34
- };
35
- accountType?: AccountType | undefined;
36
- defaultCmsPublishMode?: import("../types/Files").CmsPublishMode | undefined;
37
- parentAccountId?: number | undefined;
38
- name: string;
39
- accountId: number;
40
- env: import("../types/Config").Environment;
41
- authType: "apikey" | "oauth2" | "personalaccesskey";
42
- } | {
43
- apiKey: string;
44
- accountType?: AccountType | undefined;
45
- defaultCmsPublishMode?: import("../types/Files").CmsPublishMode | undefined;
46
- parentAccountId?: number | undefined;
47
- name: string;
48
- accountId: number;
49
- env: import("../types/Config").Environment;
50
- authType: "apikey" | "oauth2" | "personalaccesskey";
51
- })[];
52
- allowAutoUpdates?: boolean | undefined;
53
- defaultMode?: import("../types/Files").CmsPublishMode | undefined;
54
- env?: import("../types/Config").Environment | undefined;
55
- httpUseLocalhost?: boolean | undefined;
56
- autoOpenBrowser?: boolean | undefined;
57
- useCustomObjectHubfile?: boolean | undefined;
58
- flags?: string[] | undefined;
59
- defaultCmsPublishMode: import("../types/Files").CmsPublishMode | undefined;
60
- httpTimeout: number | undefined;
61
- allowUsageTracking: boolean | undefined;
62
- defaultAccount?: number | undefined;
63
- };
13
+ export declare function formatConfigForWrite(config: HubSpotConfig): HubSpotConfig;
64
14
  export declare function writeConfigFile(config: HubSpotConfig, configPath: string): void;
65
15
  export declare function normalizeParsedConfig(parsedConfig: HubSpotConfig & DeprecatedHubSpotConfigFields): HubSpotConfig;
16
+ export declare function convertToDeprecatedConfig(config: HubSpotConfig): Partial<HubSpotConfig> & Partial<DeprecatedHubSpotConfigFields>;
66
17
  export declare function parseConfig(configSource: string, configPath: string): HubSpotConfig;
67
18
  export declare function buildConfigFromEnvironment(): HubSpotConfig;
68
19
  export declare function getAccountIdentifierAndType(accountIdentifier: string | number): {
@@ -72,7 +23,7 @@ export declare function getAccountIdentifierAndType(accountIdentifier: string |
72
23
  export declare function getConfigAccountByIdentifier(accounts: Array<HubSpotConfigAccount>, identifierFieldName: ValueOf<typeof ACCOUNT_IDENTIFIERS>, identifier: string | number): HubSpotConfigAccount | undefined;
73
24
  export declare function getConfigAccountByInferredIdentifier(accounts: Array<HubSpotConfigAccount>, accountIdentifier: string | number): HubSpotConfigAccount | undefined;
74
25
  export declare function getConfigAccountIndexById(accounts: Array<HubSpotConfigAccount>, id: number): number;
75
- export declare function isConfigAccountValid(account: Partial<HubSpotConfigAccount>): boolean;
26
+ export declare function validateConfigAccount(account: Partial<HubSpotConfigAccount>): HubSpotConfigValidationResult;
76
27
  export declare function handleConfigFileSystemError(error: unknown, path: string): {
77
28
  message?: string;
78
29
  type: HubSpotConfigErrorType;
package/config/utils.js CHANGED
@@ -3,30 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleConfigFileSystemError = exports.isConfigAccountValid = exports.getConfigAccountIndexById = exports.getConfigAccountByInferredIdentifier = exports.getConfigAccountByIdentifier = exports.getAccountIdentifierAndType = exports.buildConfigFromEnvironment = exports.parseConfig = exports.normalizeParsedConfig = exports.writeConfigFile = exports.formatConfigForWrite = exports.removeUndefinedFieldsFromConfigAccount = exports.readConfigFile = exports.doesConfigFileExistAtPath = exports.getConfigPathEnvironmentVariables = exports.getLocalConfigDefaultFilePath = exports.getLocalConfigFilePath = exports.getGlobalConfigFilePath = void 0;
6
+ exports.handleConfigFileSystemError = exports.validateConfigAccount = exports.getConfigAccountIndexById = exports.getConfigAccountByInferredIdentifier = exports.getConfigAccountByIdentifier = exports.getAccountIdentifierAndType = exports.buildConfigFromEnvironment = exports.parseConfig = exports.convertToDeprecatedConfig = exports.normalizeParsedConfig = exports.writeConfigFile = exports.formatConfigForWrite = exports.removeUndefinedFieldsFromConfigAccount = exports.readConfigFile = exports.doesConfigFileExistAtPath = exports.getConfigPathEnvironmentVariables = exports.getLocalConfigDefaultFilePath = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const js_yaml_1 = __importDefault(require("js-yaml"));
9
- const findup_sync_1 = __importDefault(require("findup-sync"));
10
9
  const config_1 = require("../constants/config");
11
10
  const auth_1 = require("../constants/auth");
12
11
  const FileSystemError_1 = require("../models/FileSystemError");
13
- const logger_1 = require("../lib/logger");
14
12
  const environment_1 = require("../lib/environment");
15
13
  const path_1 = require("../lib/path");
16
14
  const files_1 = require("../constants/files");
17
15
  const lang_1 = require("../utils/lang");
18
16
  const HubSpotConfigError_1 = require("../models/HubSpotConfigError");
19
- function getGlobalConfigFilePath() {
20
- return config_1.GLOBAL_CONFIG_PATH;
21
- }
22
- exports.getGlobalConfigFilePath = getGlobalConfigFilePath;
23
- function getLocalConfigFilePath() {
24
- return (0, findup_sync_1.default)([
25
- config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
26
- config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME.replace('.yml', '.yaml'),
27
- ], { cwd: (0, path_1.getCwd)() });
28
- }
29
- exports.getLocalConfigFilePath = getLocalConfigFilePath;
30
17
  function getLocalConfigDefaultFilePath() {
31
18
  return `${(0, path_1.getCwd)()}/${config_1.DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME}`;
32
19
  }
@@ -104,20 +91,26 @@ function formatConfigForWrite(config) {
104
91
  ...rest,
105
92
  accounts: accounts.map(account => {
106
93
  const { name, accountId, env, authType, ...rest } = account;
107
- return {
94
+ const orderedAccount = {
108
95
  name,
109
96
  accountId,
110
97
  env,
111
98
  authType,
112
99
  ...rest,
100
+ // using ...rest messes with the typing
113
101
  };
102
+ return removeUndefinedFieldsFromConfigAccount(orderedAccount);
114
103
  }),
115
104
  };
116
- return removeUndefinedFieldsFromConfigAccount(orderedConfig);
105
+ return orderedConfig;
117
106
  }
118
107
  exports.formatConfigForWrite = formatConfigForWrite;
119
108
  function writeConfigFile(config, configPath) {
120
- const source = js_yaml_1.default.dump(formatConfigForWrite(config));
109
+ const formattedConfig = formatConfigForWrite(config);
110
+ const configToWrite = configPath == config_1.GLOBAL_CONFIG_PATH
111
+ ? formattedConfig
112
+ : convertToDeprecatedConfig(formattedConfig);
113
+ const source = js_yaml_1.default.dump(configToWrite);
121
114
  try {
122
115
  fs_extra_1.default.ensureFileSync(configPath);
123
116
  fs_extra_1.default.writeFileSync(configPath, source);
@@ -173,6 +166,30 @@ function normalizeParsedConfig(parsedConfig) {
173
166
  return parsedConfig;
174
167
  }
175
168
  exports.normalizeParsedConfig = normalizeParsedConfig;
169
+ function convertToDeprecatedConfig(config) {
170
+ const deprecatedConfig = structuredClone(config);
171
+ if (config.defaultAccount) {
172
+ const defaultAccount = getConfigAccountByIdentifier(config.accounts, config_1.ACCOUNT_IDENTIFIERS.ACCOUNT_ID, config.defaultAccount);
173
+ if (defaultAccount) {
174
+ deprecatedConfig.defaultPortal = defaultAccount.name;
175
+ delete deprecatedConfig.defaultAccount;
176
+ }
177
+ }
178
+ const portals = config.accounts.map(account => {
179
+ if (account.accountId) {
180
+ const deprecatedAccount = structuredClone(account);
181
+ deprecatedAccount.portalId = account.accountId;
182
+ // @ts-expect-error deleting accountId is intential since using deprecated config format
183
+ delete deprecatedAccount.accountId;
184
+ return deprecatedAccount;
185
+ }
186
+ return account;
187
+ });
188
+ deprecatedConfig.portals = portals;
189
+ delete deprecatedConfig.accounts;
190
+ return deprecatedConfig;
191
+ }
192
+ exports.convertToDeprecatedConfig = convertToDeprecatedConfig;
176
193
  function parseConfig(configSource, configPath) {
177
194
  let parsedYaml;
178
195
  try {
@@ -283,57 +300,61 @@ function getConfigAccountByIdentifier(accounts, identifierFieldName, identifier)
283
300
  exports.getConfigAccountByIdentifier = getConfigAccountByIdentifier;
284
301
  function getConfigAccountByInferredIdentifier(accounts, accountIdentifier) {
285
302
  const { identifier, identifierType } = getAccountIdentifierAndType(accountIdentifier);
286
- return accounts.find(account => account[identifierType] === identifier);
303
+ const account = getConfigAccountByIdentifier(accounts, identifierType, identifier);
304
+ if (account) {
305
+ return account;
306
+ }
307
+ // Fallback to handle accounts with numbers as names
308
+ return getConfigAccountByIdentifier(accounts, config_1.ACCOUNT_IDENTIFIERS.NAME, String(accountIdentifier));
287
309
  }
288
310
  exports.getConfigAccountByInferredIdentifier = getConfigAccountByInferredIdentifier;
289
311
  function getConfigAccountIndexById(accounts, id) {
290
312
  return accounts.findIndex(account => account.accountId === id);
291
313
  }
292
314
  exports.getConfigAccountIndexById = getConfigAccountIndexById;
293
- function isConfigAccountValid(account) {
315
+ function validateConfigAccount(account) {
316
+ const validationErrors = [];
294
317
  if (!account || typeof account !== 'object') {
295
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingAccount'));
296
- return false;
318
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingAccount'));
319
+ return { isValid: false, errors: validationErrors };
297
320
  }
298
321
  if (!account.accountId) {
299
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingAccountId'));
300
- return false;
322
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingAccountId'));
323
+ return { isValid: false, errors: validationErrors };
301
324
  }
302
325
  if (!account.authType) {
303
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingAuthType', {
326
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingAuthType', {
304
327
  accountId: account.accountId,
305
328
  }));
306
- return false;
329
+ return { isValid: false, errors: validationErrors };
307
330
  }
308
- let valid = false;
309
331
  if (account.authType === auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value) {
310
- valid =
311
- 'personalAccessKey' in account && Boolean(account.personalAccessKey);
312
- if (!valid) {
313
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingPersonalAccessKey', {
332
+ const isValidPersonalAccessKeyAccount = 'personalAccessKey' in account && Boolean(account.personalAccessKey);
333
+ if (!isValidPersonalAccessKeyAccount) {
334
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingPersonalAccessKey', {
314
335
  accountId: account.accountId,
315
336
  }));
316
337
  }
317
338
  }
318
339
  if (account.authType === auth_1.OAUTH_AUTH_METHOD.value) {
319
- valid = 'auth' in account && Boolean(account.auth);
320
- if (!valid) {
321
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingAuth', {
340
+ const isValidOAuthAccount = 'auth' in account && Boolean(account.auth);
341
+ if (!isValidOAuthAccount) {
342
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingAuth', {
322
343
  accountId: account.accountId,
323
344
  }));
324
345
  }
325
346
  }
326
347
  if (account.authType === auth_1.API_KEY_AUTH_METHOD.value) {
327
- valid = 'apiKey' in account && Boolean(account.apiKey);
328
- if (!valid) {
329
- logger_1.logger.debug((0, lang_1.i18n)('config.utils.isConfigAccountValid.missingApiKey', {
348
+ const isValidAPIKeyAccount = 'apiKey' in account && Boolean(account.apiKey);
349
+ if (!isValidAPIKeyAccount) {
350
+ validationErrors.push((0, lang_1.i18n)('config.utils.validateConfigAccount.missingApiKey', {
330
351
  accountId: account.accountId,
331
352
  }));
332
353
  }
333
354
  }
334
- return valid;
355
+ return { isValid: validationErrors.length === 0, errors: validationErrors };
335
356
  }
336
- exports.isConfigAccountValid = isConfigAccountValid;
357
+ exports.validateConfigAccount = validateConfigAccount;
337
358
  function handleConfigFileSystemError(error, path) {
338
359
  let message;
339
360
  let type = config_1.HUBSPOT_CONFIG_ERROR_TYPES.UNKNOWN;
package/errors/index.d.ts CHANGED
@@ -13,9 +13,9 @@ export declare function isMissingScopeError(err: unknown): err is HubSpotHttpErr
13
13
  export declare function isGatingError(err: unknown): err is HubSpotHttpError;
14
14
  export declare function isTimeoutError(err: unknown): err is HubSpotHttpError;
15
15
  export declare function isAuthError(err: unknown): err is HubSpotHttpError;
16
- export declare function isValidationError(err: unknown): boolean;
16
+ export declare function isValidationError(err: unknown): err is HubSpotHttpError;
17
17
  export declare function isHubSpotHttpError(error?: unknown): error is HubSpotHttpError;
18
- export declare function isGithubRateLimitError(err: unknown): boolean;
18
+ export declare function isGithubRateLimitError(err: unknown): err is HubSpotHttpError;
19
19
  export declare function isSystemError(err: unknown): err is BaseError;
20
20
  export declare function isFileSystemError(err: unknown): err is FileSystemError;
21
21
  export declare function isHubSpotConfigError(err: unknown): err is HubSpotConfigError;
package/errors/index.js CHANGED
@@ -45,7 +45,7 @@ function isValidationError(err) {
45
45
  }
46
46
  exports.isValidationError = isValidationError;
47
47
  function isHubSpotHttpError(error) {
48
- return !!error && error instanceof HubSpotHttpError_1.HubSpotHttpError;
48
+ return (!!error && error instanceof Error && error.name === HubSpotHttpError_1.HubSpotHttpErrorName);
49
49
  }
50
50
  exports.isHubSpotHttpError = isHubSpotHttpError;
51
51
  function isGithubRateLimitError(err) {
@@ -68,7 +68,7 @@ function isSystemError(err) {
68
68
  }
69
69
  exports.isSystemError = isSystemError;
70
70
  function isFileSystemError(err) {
71
- return err instanceof FileSystemError_1.FileSystemError;
71
+ return err instanceof Error && err.name === FileSystemError_1.FilerSystemErrorName;
72
72
  }
73
73
  exports.isFileSystemError = isFileSystemError;
74
74
  function isHubSpotConfigError(err) {
@@ -6,4 +6,6 @@ export declare const USER_AGENTS: {
6
6
  export declare function getDefaultUserAgentHeader(): {
7
7
  'User-Agent': string;
8
8
  };
9
+ export declare function hostnameMatchesNoProxyPattern(hostname: string, pattern: string): boolean;
10
+ export declare function shouldUseProxy(baseURL: string): boolean;
9
11
  export declare function getAxiosConfig(options: HttpOptions): AxiosRequestConfig;
@@ -3,12 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getAxiosConfig = exports.getDefaultUserAgentHeader = exports.USER_AGENTS = void 0;
6
+ exports.getAxiosConfig = exports.shouldUseProxy = exports.hostnameMatchesNoProxyPattern = exports.getDefaultUserAgentHeader = exports.USER_AGENTS = void 0;
7
7
  const package_json_1 = require("../package.json");
8
8
  const config_1 = require("../config");
9
9
  const urls_1 = require("../lib/urls");
10
- const https_1 = __importDefault(require("https"));
11
10
  const http_1 = __importDefault(require("http"));
11
+ const https_1 = __importDefault(require("https"));
12
+ const http_proxy_agent_1 = require("http-proxy-agent");
13
+ const https_proxy_agent_1 = require("https-proxy-agent");
12
14
  // Total number of sockets across all hosts
13
15
  const MAX_TOTAL_SOCKETS = 25;
14
16
  // Total number of sockets per each host
@@ -23,6 +25,39 @@ const httpsAgent = new https_1.default.Agent({
23
25
  maxTotalSockets: MAX_TOTAL_SOCKETS,
24
26
  maxSockets: MAX_SOCKETS_PER_HOST,
25
27
  });
28
+ function getHttpProxyEnvVariable() {
29
+ return process.env.HTTP_PROXY || process.env.http_proxy;
30
+ }
31
+ function getHttpsProxyEnvVariable() {
32
+ return process.env.HTTPS_PROXY || process.env.https_proxy;
33
+ }
34
+ function getAllProxyEnvVariable() {
35
+ return process.env.ALL_PROXY || process.env.all_proxy;
36
+ }
37
+ function getHttpProxyAgent() {
38
+ const proxyUrl = getHttpProxyEnvVariable() || getAllProxyEnvVariable();
39
+ if (!proxyUrl) {
40
+ return httpAgent;
41
+ }
42
+ return new http_proxy_agent_1.HttpProxyAgent(proxyUrl, {
43
+ keepAlive: true,
44
+ maxTotalSockets: MAX_TOTAL_SOCKETS,
45
+ maxSockets: MAX_SOCKETS_PER_HOST,
46
+ });
47
+ }
48
+ function getHttpsProxyAgent() {
49
+ const proxyUrl = getHttpProxyEnvVariable() ||
50
+ getHttpsProxyEnvVariable() ||
51
+ getAllProxyEnvVariable();
52
+ if (!proxyUrl) {
53
+ return httpsAgent;
54
+ }
55
+ return new https_proxy_agent_1.HttpsProxyAgent(proxyUrl, {
56
+ keepAlive: true,
57
+ maxTotalSockets: MAX_TOTAL_SOCKETS,
58
+ maxSockets: MAX_SOCKETS_PER_HOST,
59
+ });
60
+ }
26
61
  exports.USER_AGENTS = {
27
62
  'HubSpot Local Dev Lib': package_json_1.version,
28
63
  };
@@ -39,6 +74,37 @@ exports.getDefaultUserAgentHeader = getDefaultUserAgentHeader;
39
74
  const DEFAULT_TRANSITIONAL = {
40
75
  clarifyTimeoutError: true,
41
76
  };
77
+ function hostnameMatchesNoProxyPattern(hostname, pattern) {
78
+ const hostnameNormalized = hostname.toLowerCase();
79
+ const patternNormalized = pattern.trim().toLowerCase();
80
+ if (patternNormalized === '*') {
81
+ return true;
82
+ }
83
+ if (patternNormalized.startsWith('.')) {
84
+ return hostnameNormalized.endsWith(patternNormalized);
85
+ }
86
+ return (hostnameNormalized === patternNormalized || // exact match (e.g. "api.hubapi.com" matches "api.hubapi.com")
87
+ hostnameNormalized.endsWith(`.${patternNormalized}`) // domain suffix match (e.g. "api.hubapi.com" matches ".hubapi.com")
88
+ );
89
+ }
90
+ exports.hostnameMatchesNoProxyPattern = hostnameMatchesNoProxyPattern;
91
+ function shouldUseProxy(baseURL) {
92
+ if (!getHttpProxyEnvVariable() &&
93
+ !getHttpsProxyEnvVariable() &&
94
+ !getAllProxyEnvVariable()) {
95
+ return false;
96
+ }
97
+ const noProxy = process.env.NO_PROXY || process.env.no_proxy;
98
+ if (noProxy) {
99
+ const hostname = new URL(baseURL).hostname;
100
+ const noProxyList = noProxy.split(',').filter(Boolean);
101
+ if (noProxyList.some(pattern => hostnameMatchesNoProxyPattern(hostname, pattern))) {
102
+ return false;
103
+ }
104
+ }
105
+ return true;
106
+ }
107
+ exports.shouldUseProxy = shouldUseProxy;
42
108
  function getAxiosConfig(options) {
43
109
  const { env, localHostOverride, headers, ...rest } = options;
44
110
  let config;
@@ -56,16 +122,19 @@ function getAxiosConfig(options) {
56
122
  if (config && config.httpUseLocalhost) {
57
123
  httpUseLocalhost = config.httpUseLocalhost;
58
124
  }
125
+ const baseURL = (0, urls_1.getHubSpotApiOrigin)(env, localHostOverride ? false : httpUseLocalhost);
59
126
  return {
60
- baseURL: (0, urls_1.getHubSpotApiOrigin)(env, localHostOverride ? false : httpUseLocalhost),
127
+ baseURL,
61
128
  headers: {
62
129
  ...getDefaultUserAgentHeader(),
63
130
  ...(headers || {}),
64
131
  },
65
132
  timeout: httpTimeout,
66
133
  transitional: DEFAULT_TRANSITIONAL,
67
- httpAgent,
68
- httpsAgent,
134
+ // Disable axios's built-in proxy handling - we use custom agents instead
135
+ proxy: false,
136
+ httpAgent: shouldUseProxy(baseURL) ? getHttpProxyAgent() : httpAgent,
137
+ httpsAgent: shouldUseProxy(baseURL) ? getHttpsProxyAgent() : httpsAgent,
69
138
  ...rest,
70
139
  };
71
140
  }
package/http/index.js CHANGED
@@ -41,6 +41,7 @@ const lang_1 = require("../utils/lang");
41
41
  const HubSpotHttpError_1 = require("../models/HubSpotHttpError");
42
42
  const auth_1 = require("../constants/auth");
43
43
  const localDevAuth_1 = require("../api/localDevAuth");
44
+ const fireAlarm_1 = require("../api/fireAlarm");
44
45
  const util = __importStar(require("util"));
45
46
  const trackUsage_1 = require("../lib/trackUsage");
46
47
  const i18nKey = 'http.index';
@@ -48,14 +49,15 @@ const IGNORE_URLS_NETWORK_DEBUG = [
48
49
  localDevAuth_1.LOCALDEVAUTH_ACCESS_TOKEN_PATH,
49
50
  trackUsage_1.CMS_CLI_USAGE_PATH,
50
51
  trackUsage_1.VSCODE_USAGE_PATH,
52
+ fireAlarm_1.FIREALARM_API_AUTH_PATH,
51
53
  ];
52
54
  function logRequest(response) {
53
55
  try {
54
56
  if (!process.env.HUBSPOT_NETWORK_LOGGING) {
55
57
  return;
56
58
  }
57
- if (response?.config?.url &&
58
- IGNORE_URLS_NETWORK_DEBUG.includes(response.config.url)) {
59
+ if (!process.env.HUBSPOT_DEBUG_LOGGING_VERBOSE &&
60
+ IGNORE_URLS_NETWORK_DEBUG.some(url => response?.config?.url && response.config.url.includes(url))) {
59
61
  return;
60
62
  }
61
63
  logger_1.logger.debug(util.inspect({
@@ -82,6 +84,13 @@ axios_1.default.interceptors.response.use((response) => {
82
84
  catch (e) {
83
85
  // Ignore any errors that occur while logging the response
84
86
  }
87
+ // Don't re-wrap if already a HubSpotHttpError. This can happen when
88
+ // multiple copies of local-dev-lib are loaded and share the same axios
89
+ // instance, causing multiple interceptors to be registered.
90
+ if (error instanceof HubSpotHttpError_1.HubSpotHttpError ||
91
+ error?.name === HubSpotHttpError_1.HubSpotHttpErrorName) {
92
+ return Promise.reject(error);
93
+ }
85
94
  // Wrap all axios errors in our own Error class. Attach the error
86
95
  // as the cause for the new error, so we maintain the stack trace
87
96
  return Promise.reject(new HubSpotHttpError_1.HubSpotHttpError(error.message, { cause: error }));
package/lang/en.json CHANGED
@@ -251,11 +251,11 @@
251
251
  "error": "No config file found.",
252
252
  "errorWithPath": "No config file found at {{ path }}."
253
253
  },
254
- "isConfigValid": {
255
- "missingAccounts": "Invalid config: no accounts found",
256
- "duplicateAccountIds": "Invalid config: multiple accounts with accountId: {{ accountId }}",
257
- "duplicateAccountNames": "Invalid config: multiple accounts with name: {{ accountName }}",
258
- "invalidAccountName": "Invalid config: account name {{ accountName }} contains spaces"
254
+ "validateConfig": {
255
+ "missingAccounts": "No accounts found",
256
+ "duplicateAccountIds": "Multiple accounts with accountId: {{ accountId }}",
257
+ "duplicateAccountNames": "Multiple accounts with name: {{ accountName }}",
258
+ "invalidAccountName": "Account name {{ accountName }} contains spaces"
259
259
  },
260
260
  "getConfigAccountById": {
261
261
  "error": "No account with id {{ accountId }} exists in config"
@@ -276,7 +276,7 @@
276
276
  "accountNotFound": "Attempting to update account with id {{ id }}, but that account was not found in config"
277
277
  },
278
278
  "setConfigAccountAsDefault": {
279
- "accountNotFound": "Attempted to set account with id {{ accountId }} as default, but that account was not found in config"
279
+ "accountNotFound": "Attempted to set account with identifier {{ identifier }} as default, but that account was not found in config"
280
280
  },
281
281
  "renameConfigAccount": {
282
282
  "accountNotFound": "Attempted to rename account with name {{ currentName }}, but that account was not found in config",
@@ -299,13 +299,13 @@
299
299
  "configNotFoundError": "No config file found at {{ path }}.",
300
300
  "insufficientPermissionsError": "Insufficient permissions to access config file at {{ path }}"
301
301
  },
302
- "isConfigAccountValid": {
303
- "missingAccount": "Invalid config: at least one account in config is missing data",
304
- "missingAuthType": "Invalid config: account {{ accountId }} has no authType",
305
- "missingAccountId": "Invalid config: at least one account in config is missing accountId",
306
- "missingApiKey": "Invalid config: account {{ accountId }} has authType of apikey but is missing the apiKey field",
307
- "missingAuth": "Invalid config: account {{ accountId }} has authtype of oauth2 but is missing auth data",
308
- "missingPersonalAccessKey": "Invalid config: account {{ accountId }} has authType of personalAccessKey but is missing the personalAccessKey field"
302
+ "validateConfigAccount": {
303
+ "missingAccount": "At least one account in config is missing data",
304
+ "missingAuthType": "Account {{ accountId }} has no authType",
305
+ "missingAccountId": "At least one account in config is missing accountId",
306
+ "missingApiKey": "Account {{ accountId }} has authType of apikey but is missing the apiKey field",
307
+ "missingAuth": "Account {{ accountId }} has authtype of oauth2 but is missing auth data",
308
+ "missingPersonalAccessKey": "Account {{ accountId }} has authType of personalAccessKey but is missing the personalAccessKey field"
309
309
  },
310
310
  "getConfigPathEnvironmentVariables": {
311
311
  "invalidEnvironmentVariables": "USE_ENVIRONMENT_HUBSPOT_CONFIG and HUBSPOT_CONFIG_PATH cannot both be set simultaneously"
@@ -121,7 +121,7 @@ async function authorizedScopesForPortalAndUser(accountId) {
121
121
  exports.authorizedScopesForPortalAndUser = authorizedScopesForPortalAndUser;
122
122
  async function updateConfigWithAccessToken(token, personalAccessKey, env, name, makeDefault = false) {
123
123
  const { portalId, accessToken, expiresAt, accountType } = token;
124
- const account = name ? (0, config_1.getConfigAccountIfExists)(name) : undefined;
124
+ const account = (0, config_1.getConfigAccountIfExists)(portalId);
125
125
  const accountEnv = env || account?.env || environments_1.ENVIRONMENTS.PROD;
126
126
  let parentAccountId;
127
127
  try {
@@ -159,12 +159,12 @@ async function updateConfigWithAccessToken(token, personalAccessKey, env, name,
159
159
  accountId: portalId,
160
160
  accountType,
161
161
  personalAccessKey,
162
- name: name || account?.name || token.hubName,
162
+ name: name || account?.name,
163
163
  authType: auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
164
164
  auth: { tokenInfo: { accessToken, expiresAt } },
165
165
  parentAccountId,
166
166
  env: accountEnv,
167
- };
167
+ }; // Account may temporarily not have a name before prompted to add one in the CLI
168
168
  // Add new account if it doesn't exist, otherwise update existing account
169
169
  if (account) {
170
170
  (0, config_1.updateConfigAccount)(updatedAccount);
@@ -1,4 +1,4 @@
1
- import { RequestPortsData } from '../types/PortManager';
1
+ import { RequestPortsData, ServerPortMap } from '../types/PortManager';
2
2
  export declare const BASE_URL: string;
3
3
  export declare function isPortManagerPortAvailable(): Promise<boolean>;
4
4
  export declare function isPortManagerServerRunning(): Promise<boolean>;
@@ -7,6 +7,7 @@ export declare function stopPortManagerServer(): Promise<void>;
7
7
  export declare function requestPorts(portData: Array<RequestPortsData>): Promise<{
8
8
  [instanceId: string]: number;
9
9
  }>;
10
+ export declare function getActiveServers(): Promise<ServerPortMap>;
10
11
  export declare function getServerPortByInstanceId(serverInstanceId: string): Promise<number>;
11
12
  export declare function deleteServerInstance(serverInstanceId: string): Promise<void>;
12
13
  export declare function portManagerHasActiveServers(): Promise<boolean>;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.portManagerHasActiveServers = exports.deleteServerInstance = exports.getServerPortByInstanceId = exports.requestPorts = exports.stopPortManagerServer = exports.startPortManagerServer = exports.isPortManagerServerRunning = exports.isPortManagerPortAvailable = exports.BASE_URL = void 0;
6
+ exports.portManagerHasActiveServers = exports.deleteServerInstance = exports.getServerPortByInstanceId = exports.getActiveServers = exports.requestPorts = exports.stopPortManagerServer = exports.startPortManagerServer = exports.isPortManagerServerRunning = exports.isPortManagerPortAvailable = exports.BASE_URL = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const PortManagerServer_1 = require("../utils/PortManagerServer");
9
9
  const ports_1 = require("../constants/ports");
@@ -45,6 +45,11 @@ async function requestPorts(portData) {
45
45
  return data.ports;
46
46
  }
47
47
  exports.requestPorts = requestPorts;
48
+ async function getActiveServers() {
49
+ const { data } = await axios_1.default.get(`${exports.BASE_URL}/servers`);
50
+ return data.servers;
51
+ }
52
+ exports.getActiveServers = getActiveServers;
48
53
  async function getServerPortByInstanceId(serverInstanceId) {
49
54
  const { data } = await axios_1.default.get(`${exports.BASE_URL}/servers/${serverInstanceId}`);
50
55
  return data.port;
@@ -1,4 +1,5 @@
1
1
  import { FileSystemErrorContext } from '../types/Error';
2
+ export declare const FilerSystemErrorName = "FilerSystemError";
2
3
  export declare class FileSystemError extends Error {
3
4
  private context;
4
5
  constructor(options?: ErrorOptions, context?: FileSystemErrorContext);
@@ -1,14 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FileSystemError = void 0;
3
+ exports.FileSystemError = exports.FilerSystemErrorName = void 0;
4
4
  const lang_1 = require("../utils/lang");
5
5
  const errors_1 = require("../errors");
6
6
  const i18nKey = 'errors.fileSystemErrors';
7
+ exports.FilerSystemErrorName = 'FilerSystemError';
7
8
  class FileSystemError extends Error {
8
9
  context;
9
10
  constructor(options, context) {
10
11
  super('', options);
11
- this.name = 'FileSystemError';
12
+ this.name = exports.FilerSystemErrorName;
12
13
  this.context = context;
13
14
  if (context) {
14
15
  let fileAction = '';
@@ -1,4 +1,5 @@
1
1
  import { HubSpotHttpErrorContext } from '../types/Error';
2
+ export declare const HubSpotHttpErrorName = "HubSpotHttpError";
2
3
  export declare class HubSpotHttpError<T = any> extends Error {
3
4
  status?: number;
4
5
  code?: string;
@@ -17,7 +18,7 @@ export declare class HubSpotHttpError<T = any> extends Error {
17
18
  timeout?: number;
18
19
  correlationId?: string;
19
20
  constructor(message?: string, options?: ErrorOptions, context?: HubSpotHttpErrorContext);
20
- updateContext(context: Partial<HubSpotHttpErrorContext>): void;
21
+ updateContext(context: Partial<HubSpotHttpErrorContext>, additionalDebugContext?: string): void;
21
22
  toString(): string;
22
23
  formattedValidationErrors(): string;
23
24
  private extractDerivedContext;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HubSpotHttpError = void 0;
3
+ exports.HubSpotHttpError = exports.HubSpotHttpErrorName = void 0;
4
4
  const axios_1 = require("axios");
5
5
  const api_1 = require("../constants/api");
6
6
  const lang_1 = require("../utils/lang");
7
+ exports.HubSpotHttpErrorName = 'HubSpotHttpError';
7
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
9
  class HubSpotHttpError extends Error {
9
10
  status;
@@ -22,7 +23,7 @@ class HubSpotHttpError extends Error {
22
23
  correlationId;
23
24
  constructor(message, options, context) {
24
25
  super(message, options);
25
- this.name = 'HubSpotHttpError';
26
+ this.name = exports.HubSpotHttpErrorName;
26
27
  this.context = context;
27
28
  this.cause = options?.cause;
28
29
  if (options && (0, axios_1.isAxiosError)(options.cause)) {
@@ -60,11 +61,11 @@ class HubSpotHttpError extends Error {
60
61
  this.message = messages.join(' ');
61
62
  }
62
63
  }
63
- updateContext(context) {
64
+ updateContext(context, additionalDebugContext) {
64
65
  this.context = { ...this.context, ...context };
65
66
  // Update the error messages when the context is updated
66
67
  if ((0, axios_1.isAxiosError)(this.cause)) {
67
- this.message = this.joinErrorMessages(this.cause, this.context);
68
+ this.message = this.joinErrorMessages(this.cause, this.context, additionalDebugContext);
68
69
  }
69
70
  }
70
71
  toString() {
@@ -137,8 +138,7 @@ class HubSpotHttpError extends Error {
137
138
  }
138
139
  this.validationErrors = errorMessages;
139
140
  }
140
- joinErrorMessages(error, context = {}) {
141
- const i18nKey = 'errors.apiErrors';
141
+ joinErrorMessages(error, context = {}, additionalDebugContext) {
142
142
  const status = error.response?.status;
143
143
  const method = error.config?.method;
144
144
  let messageDetail;
@@ -149,48 +149,68 @@ class HubSpotHttpError extends Error {
149
149
  const requestName = context.request
150
150
  ? `${action} ${preposition} '${context.request}'`
151
151
  : action;
152
- messageDetail = (0, lang_1.i18n)(`${i18nKey}.messageDetail`, {
152
+ messageDetail = (0, lang_1.i18n)(`errors.apiErrors.messageDetail`, {
153
153
  accountId: context.accountId,
154
154
  requestName,
155
155
  });
156
156
  }
157
157
  else {
158
- messageDetail = (0, lang_1.i18n)(`${i18nKey}.genericMessageDetail`);
158
+ messageDetail = (0, lang_1.i18n)(`errors.apiErrors.genericMessageDetail`);
159
159
  }
160
160
  const errorMessage = [];
161
161
  if ((method === 'put' || method === 'post') && context.payload) {
162
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.unableToUpload`, { payload: context.payload }));
162
+ errorMessage.push((0, lang_1.i18n)(`errors.apiErrors.unableToUpload`, { payload: context.payload }));
163
163
  }
164
+ let statusBasedMessage;
164
165
  switch (status) {
165
166
  case 400:
166
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.400`, { messageDetail }));
167
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.400`, {
168
+ messageDetail,
169
+ });
167
170
  break;
168
171
  case 401:
169
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.401`, { messageDetail }));
172
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.401`, {
173
+ messageDetail,
174
+ });
170
175
  break;
171
176
  case 403:
172
177
  break;
173
178
  case 404:
174
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.404`, { messageDetail }));
179
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.404`, {
180
+ messageDetail,
181
+ });
175
182
  break;
176
183
  case 429:
177
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.429`, { messageDetail }));
184
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.429`, {
185
+ messageDetail,
186
+ });
178
187
  break;
179
188
  case 503:
180
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.503`, { messageDetail }));
189
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.503`, {
190
+ messageDetail,
191
+ });
181
192
  break;
182
193
  default:
183
194
  if (status && status >= 500 && status < 600) {
184
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.500Generic`, { messageDetail }));
195
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.500Generic`, {
196
+ messageDetail,
197
+ });
185
198
  }
186
199
  else if (status && status >= 400 && status < 500) {
187
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.400Generic`, { messageDetail }));
200
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.400Generic`, {
201
+ messageDetail,
202
+ });
188
203
  }
189
204
  else {
190
- errorMessage.push((0, lang_1.i18n)(`${i18nKey}.codes.generic`, { messageDetail }));
205
+ statusBasedMessage = (0, lang_1.i18n)(`errors.apiErrors.codes.generic`, {
206
+ messageDetail,
207
+ });
191
208
  }
192
209
  break;
193
210
  }
211
+ if (statusBasedMessage) {
212
+ errorMessage.push(`${statusBasedMessage}${additionalDebugContext ? ` ${additionalDebugContext}` : ''}`);
213
+ }
194
214
  if (error?.response?.data) {
195
215
  const { message, errors } = error.response.data;
196
216
  if (message) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/local-dev-lib",
3
- "version": "0.5.0-experimental.9",
3
+ "version": "0.6.1-experimental.0",
4
4
  "description": "Provides library functionality for HubSpot local development tooling, including the HubSpot CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,11 +11,11 @@
11
11
  "access": "public"
12
12
  },
13
13
  "scripts": {
14
- "build": "ts-node ./scripts/build.ts",
14
+ "build": "tsx ./scripts/build.ts",
15
15
  "lint": "eslint --max-warnings=0 . && prettier . --check",
16
16
  "local-dev": "yarn build && cd dist && yarn link && cd .. && tsc --watch --rootDir . --outdir dist",
17
17
  "prettier:write": "prettier . --write",
18
- "release": "ts-node ./scripts/release.ts release",
18
+ "release": "tsx ./scripts/release.ts release",
19
19
  "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ./node_modules/.bin/jest"
20
20
  },
21
21
  "license": "Apache-2.0",
@@ -39,7 +39,7 @@
39
39
  "jest": "^29.5.0",
40
40
  "open": "^8.4.2",
41
41
  "ts-jest": "^29.0.5",
42
- "ts-node": "^10.9.2",
42
+ "tsx": "^4.20.6",
43
43
  "typescript": "^4.9.5",
44
44
  "yargs": "^17.7.2"
45
45
  },
@@ -73,6 +73,8 @@
73
73
  "findup-sync": "5.0.0",
74
74
  "form-data": "^4.0.4",
75
75
  "fs-extra": "11.2.0",
76
+ "http-proxy-agent": "7.0.2",
77
+ "https-proxy-agent": "7.0.6",
76
78
  "ignore": "5.3.1",
77
79
  "js-yaml": "4.1.0",
78
80
  "moment": "2.30.1",
package/types/Config.d.ts CHANGED
@@ -34,3 +34,7 @@ export type HubSpotState = {
34
34
  };
35
35
  export type HubSpotConfigErrorType = ValueOf<typeof HUBSPOT_CONFIG_ERROR_TYPES>;
36
36
  export type HubSpotConfigOperation = ValueOf<typeof HUBSPOT_CONFIG_OPERATIONS>;
37
+ export type HubSpotConfigValidationResult = {
38
+ isValid: boolean;
39
+ errors: Array<string>;
40
+ };
package/types/Deploy.d.ts CHANGED
@@ -35,13 +35,6 @@ export type DeployStatusTaskLocator = {
35
35
  status: string;
36
36
  }>;
37
37
  };
38
- export type ProjectDeployResponseQueued = {
39
- id: string;
40
- buildResultType: 'DEPLOY_QUEUED';
41
- links: {
42
- status: string;
43
- };
44
- };
45
38
  export type SubdeployValidationIssue = {
46
39
  uid: string;
47
40
  componentTypeName: string;
@@ -51,8 +44,20 @@ export type SubdeployValidationIssue = {
51
44
  isWarning: boolean;
52
45
  }[];
53
46
  };
47
+ export type DeployResponseLinks = {
48
+ status: string;
49
+ };
50
+ export type ProjectDeployResponseQueued = {
51
+ id: string;
52
+ buildResultType: 'DEPLOY_QUEUED';
53
+ links: DeployResponseLinks;
54
+ };
54
55
  export type ProjectDeployResponseBlocked = {
55
56
  buildResultType: 'DEPLOY_BLOCKED';
56
57
  issues: SubdeployValidationIssue[];
57
58
  };
58
59
  export type ProjectDeployResponse = ProjectDeployResponseQueued | ProjectDeployResponseBlocked;
60
+ export type ProjectDeployResponseV1 = {
61
+ id: string;
62
+ links: DeployResponseLinks;
63
+ };
package/utils/git.js CHANGED
@@ -12,8 +12,9 @@ const GITIGNORE_FILE = '.gitignore';
12
12
  function makeComparisonDir(filepath) {
13
13
  if (typeof filepath !== 'string')
14
14
  return null;
15
+ let dir = path_1.default.dirname(path_1.default.resolve(filepath)).toLowerCase();
15
16
  // Append sep to make comparisons easier e.g. 'foos'.startsWith('foo')
16
- return path_1.default.dirname(path_1.default.resolve(filepath)).toLowerCase() + path_1.default.sep;
17
+ return dir + (!dir.endsWith(path_1.default.sep) ? path_1.default.sep : '');
17
18
  }
18
19
  function getGitComparisonDir() {
19
20
  return makeComparisonDir((0, findup_sync_1.default)('.git'));