@hubspot/local-dev-lib 0.4.0-experimental.1 → 0.4.2-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.
package/errors/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isFileSystemError = exports.isSystemError = exports.isHubSpotHttpError = exports.isValidationError = exports.isAuthError = exports.isTimeoutError = exports.isGatingError = exports.isMissingScopeError = exports.isSpecifiedError = void 0;
3
+ exports.isFileSystemError = exports.isSystemError = exports.isGithubRateLimitError = exports.isHubSpotHttpError = exports.isValidationError = exports.isAuthError = exports.isTimeoutError = exports.isGatingError = exports.isMissingScopeError = exports.isSpecifiedError = void 0;
4
4
  const HubSpotHttpError_1 = require("../models/HubSpotHttpError");
5
5
  const FileSystemError_1 = require("../models/FileSystemError");
6
6
  function isSpecifiedError(err, { statusCode, category, subCategory, errorType, code, }) {
@@ -47,6 +47,15 @@ function isHubSpotHttpError(error) {
47
47
  return !!error && error instanceof HubSpotHttpError_1.HubSpotHttpError;
48
48
  }
49
49
  exports.isHubSpotHttpError = isHubSpotHttpError;
50
+ function isGithubRateLimitError(err) {
51
+ if (!isHubSpotHttpError(err)) {
52
+ return false;
53
+ }
54
+ return (!!err.headers &&
55
+ err.headers['x-ratelimit-remaining'] === '0' &&
56
+ 'x-github-request-id' in err.headers);
57
+ }
58
+ exports.isGithubRateLimitError = isGithubRateLimitError;
50
59
  function isSystemError(err) {
51
60
  return (err instanceof Error &&
52
61
  'errno' in err &&
package/lang/en.json CHANGED
@@ -31,6 +31,7 @@
31
31
  }
32
32
  },
33
33
  "github": {
34
+ "rateLimitError": "Github rate limit hit. Set the GITHUB_TOKEN env variable with your github PATH. This will increase the github api's rate limit.",
34
35
  "fetchFileFromRepository": {
35
36
  "fetching": "Fetching {{ path }}...",
36
37
  "errors": {
@@ -77,6 +78,15 @@
77
78
  "invalidPersonalAccessKey": "Error while retrieving new access token: {{ errorMessage }}"
78
79
  }
79
80
  },
81
+ "crm": {
82
+ "importData": {
83
+ "errors": {
84
+ "fileNotFound": "The file {{ fileName }} does not exist",
85
+ "notJson": "You must provide a JSON file for the import data request schema.",
86
+ "noFiles": "You must provide at least one data file for the import data request schema."
87
+ }
88
+ }
89
+ },
80
90
  "cms": {
81
91
  "modules": {
82
92
  "createModule": {
@@ -242,13 +252,13 @@
242
252
  "empty": "The config file was empty. Initializing an empty config."
243
253
  },
244
254
  "validate": {
245
- "noConfig": "Valiation failed: No config was found.",
246
- "noConfigAccounts": "Valiation failed: config.accounts[] is not defined.",
247
- "emptyAccountConfig": "Valiation failed: config.accounts[] has an empty entry.",
248
- "noAccountId": "Valiation failed: config.accounts[] has an entry missing accountId.",
249
- "duplicateAccountIds": "Valiation failed: config.accounts[] has multiple entries with {{ accountId }}.",
250
- "duplicateAccountNames": "Valiation failed: config.accounts[] has multiple entries with {{ accountName }}.",
251
- "nameContainsSpaces": "Valiation failed: config.name {{ accountName }} cannot contain spaces."
255
+ "noConfig": "Validation failed: No config was found.",
256
+ "noConfigAccounts": "Validation failed: config.accounts[] is not defined.",
257
+ "emptyAccountConfig": "Validation failed: config.accounts[] has an empty entry.",
258
+ "noAccountId": "Validation failed: config.accounts[] has an entry missing accountId.",
259
+ "duplicateAccountIds": "Validation failed: config.accounts[] has multiple entries with {{ accountId }}.",
260
+ "duplicateAccountNames": "Validation failed: config.accounts[] has multiple entries with {{ accountName }}.",
261
+ "nameContainsSpaces": "Validation failed: config.name {{ accountName }} cannot contain spaces."
252
262
  },
253
263
  "updateAccount": {
254
264
  "noConfigToUpdate": "No config to update.",
@@ -292,6 +302,11 @@
292
302
  "errors": {
293
303
  "invalidInput": "Unable to update allowUsageTracking. The value {{ isEnabled }} is invalid. The value must be a boolean."
294
304
  }
305
+ },
306
+ "updateAutoOpenBrowser": {
307
+ "errors": {
308
+ "invalidInput": "Unable to update autoOpenBrowser. The value {{ isEnabled }} is invalid. The value must be a boolean."
309
+ }
295
310
  }
296
311
  },
297
312
  "configFile": {
package/lib/archive.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  import { CopySourceToDestOptions } from '../types/Archive';
4
- export declare function extractZipArchive(zip: Buffer, name: string, dest: string, { sourceDir, includesRootDir, hideLogs }?: CopySourceToDestOptions): Promise<boolean>;
4
+ export declare function extractZipArchive(zip: Buffer, name: string, dest: string, { sourceDir, includesRootDir, hideLogs, handleCollision, }?: CopySourceToDestOptions): Promise<boolean>;
package/lib/archive.js CHANGED
@@ -1,16 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.extractZipArchive = void 0;
7
30
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = require("path");
31
+ const path_1 = __importStar(require("path"));
9
32
  const os_1 = require("os");
10
33
  const extract_zip_1 = __importDefault(require("extract-zip"));
11
34
  const logger_1 = require("./logger");
12
35
  const lang_1 = require("../utils/lang");
13
36
  const FileSystemError_1 = require("../models/FileSystemError");
37
+ const fs_1 = require("./fs");
14
38
  const i18nKey = 'lib.archive';
15
39
  async function extractZip(name, zip, hideLogs = false) {
16
40
  const result = { extractDir: '', tmpDir: '' };
@@ -55,7 +79,7 @@ async function extractZip(name, zip, hideLogs = false) {
55
79
  logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.extractZip.success`));
56
80
  return result;
57
81
  }
58
- async function copySourceToDest(src, dest, { sourceDir, includesRootDir = true, hideLogs = false, } = {}) {
82
+ async function copySourceToDest(src, dest, { sourceDir, includesRootDir = true, hideLogs = false, handleCollision, } = {}) {
59
83
  try {
60
84
  if (!hideLogs) {
61
85
  logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.init`));
@@ -73,11 +97,43 @@ async function copySourceToDest(src, dest, { sourceDir, includesRootDir = true,
73
97
  }
74
98
  srcDirPath.push(rootDir);
75
99
  }
100
+ const sourceDirs = [];
76
101
  if (sourceDir) {
77
- srcDirPath.push(sourceDir);
102
+ sourceDirs.push(...(Array.isArray(sourceDir) ? new Set(sourceDir) : [sourceDir]));
103
+ }
104
+ if (sourceDirs.length === 0) {
105
+ const projectSrcDir = (0, path_1.join)(...srcDirPath);
106
+ await fs_extra_1.default.copy(projectSrcDir, dest);
107
+ }
108
+ else {
109
+ for (let i = 0; i < sourceDirs.length; i++) {
110
+ const projectSrcDir = (0, path_1.join)(...srcDirPath, sourceDirs[i]);
111
+ let collisions = [];
112
+ let filesWithoutCollisions = [];
113
+ if (fs_extra_1.default.existsSync(dest) &&
114
+ handleCollision &&
115
+ typeof handleCollision === 'function') {
116
+ const existingFiles = (await (0, fs_1.walk)(dest, ['node_modules'])).map(file => path_1.default.normalize(path_1.default.relative(dest, file)));
117
+ const newFiles = (await (0, fs_1.walk)(projectSrcDir, ['node_modules'])).map(file => path_1.default.relative(projectSrcDir, file));
118
+ // Find files that exist in the same positions in both directories
119
+ collisions = existingFiles.filter(currentFile => newFiles.includes(currentFile));
120
+ filesWithoutCollisions = newFiles.filter(currentFile => !collisions.includes(currentFile));
121
+ }
122
+ if (collisions.length &&
123
+ handleCollision &&
124
+ typeof handleCollision === 'function') {
125
+ await handleCollision({
126
+ dest,
127
+ src: projectSrcDir,
128
+ collisions,
129
+ });
130
+ await Promise.all(filesWithoutCollisions.map(currentFile => fs_extra_1.default.copy(path_1.default.join(projectSrcDir, currentFile), path_1.default.join(dest, currentFile))));
131
+ }
132
+ else {
133
+ await fs_extra_1.default.copy(projectSrcDir, dest);
134
+ }
135
+ }
78
136
  }
79
- const projectSrcDir = (0, path_1.join)(...srcDirPath);
80
- await fs_extra_1.default.copy(projectSrcDir, dest);
81
137
  logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.copySourceToDest.success`));
82
138
  return true;
83
139
  }
@@ -99,7 +155,7 @@ async function cleanupTempDir(tmpDir) {
99
155
  logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.cleanupTempDir.error`, { tmpDir }));
100
156
  }
101
157
  }
102
- async function extractZipArchive(zip, name, dest, { sourceDir, includesRootDir, hideLogs } = {}) {
158
+ async function extractZipArchive(zip, name, dest, { sourceDir, includesRootDir, hideLogs, handleCollision, } = {}) {
103
159
  let success = false;
104
160
  if (zip) {
105
161
  const { extractDir, tmpDir } = await extractZip(name, zip, hideLogs);
@@ -108,6 +164,7 @@ async function extractZipArchive(zip, name, dest, { sourceDir, includesRootDir,
108
164
  sourceDir,
109
165
  includesRootDir,
110
166
  hideLogs,
167
+ handleCollision,
111
168
  });
112
169
  }
113
170
  await cleanupTempDir(tmpDir);
@@ -38,6 +38,16 @@ function getFileType(filePath) {
38
38
  return files_1.FILE_TYPES.other;
39
39
  }
40
40
  }
41
+ function isMetaJsonFile(filePath) {
42
+ return path_1.default.basename(filePath).toLowerCase() === 'meta.json';
43
+ }
44
+ function resolveUploadPath(file, fieldsJsPaths, tmpDirRegex, regex, dest) {
45
+ const fieldsJsFileInfo = fieldsJsPaths.find(f => f.outputPath === file);
46
+ const relativePath = file.replace(fieldsJsFileInfo ? tmpDirRegex : regex, '');
47
+ const destPath = (0, path_2.convertToUnixPath)(path_1.default.join(dest, relativePath));
48
+ const originalFilePath = fieldsJsFileInfo ? fieldsJsFileInfo.filePath : file;
49
+ return { fieldsJsFileInfo, relativePath, destPath, originalFilePath };
50
+ }
41
51
  async function getFilesByType(filePaths, projectDir, rootWriteDir, commandOptions) {
42
52
  const { convertFields, fieldOptions } = commandOptions;
43
53
  const projectDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(projectDir)}`);
@@ -106,6 +116,12 @@ const defaultUploadFinalErrorCallback = (accountId, file, destPath, error) => {
106
116
  payload: file,
107
117
  });
108
118
  };
119
+ async function uploadMetaJsonFiles(moduleFiles, uploadFile) {
120
+ const moduleMetaJsonFiles = moduleFiles.filter(isMetaJsonFile);
121
+ if (moduleMetaJsonFiles.length > 0) {
122
+ await queue.addAll(moduleMetaJsonFiles.map(uploadFile));
123
+ }
124
+ }
109
125
  async function uploadFolder(accountId, src, dest, fileMapperOptions, commandOptions = {}, filePaths = [], cmsPublishMode = null) {
110
126
  const { saveOutput, convertFields, onAttemptCallback, onSuccessCallback, onFirstErrorCallback, onRetryCallback, onFinalErrorCallback, } = commandOptions;
111
127
  const _onAttemptCallback = onAttemptCallback || defaultUploadAttemptCallback;
@@ -120,23 +136,15 @@ async function uploadFolder(accountId, src, dest, fileMapperOptions, commandOpti
120
136
  const apiOptions = (0, fileMapper_1.getFileMapperQueryValues)(cmsPublishMode, fileMapperOptions);
121
137
  const failures = [];
122
138
  let fieldsJsPaths = [];
123
- let tmpDirRegex;
139
+ const tmpDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(tmpDir || '')}`);
124
140
  const [filesByType, fieldsJsObjects] = await getFilesByType(filePaths, src, tmpDir, commandOptions);
125
- const fileList = Object.values(filesByType);
126
141
  if (fieldsJsObjects.length) {
127
142
  fieldsJsPaths = fieldsJsObjects.map(fieldsJs => {
128
143
  return { outputPath: fieldsJs.outputPath, filePath: fieldsJs.filePath };
129
144
  });
130
- tmpDirRegex = new RegExp(`^${(0, escapeRegExp_1.escapeRegExp)(tmpDir || '')}`);
131
145
  }
132
146
  function uploadFile(file) {
133
- const fieldsJsFileInfo = fieldsJsPaths.find(f => f.outputPath === file);
134
- const originalFilePath = fieldsJsFileInfo
135
- ? fieldsJsFileInfo.filePath
136
- : file;
137
- // files in fieldsJsPaths always belong to the tmp directory.
138
- const relativePath = file.replace(fieldsJsFileInfo ? tmpDirRegex : regex, '');
139
- const destPath = (0, path_2.convertToUnixPath)(path_1.default.join(dest, relativePath));
147
+ const { originalFilePath, destPath } = resolveUploadPath(file, fieldsJsPaths, tmpDirRegex, regex, dest);
140
148
  return async () => {
141
149
  _onAttemptCallback(originalFilePath, destPath);
142
150
  try {
@@ -155,9 +163,23 @@ async function uploadFolder(accountId, src, dest, fileMapperOptions, commandOpti
155
163
  }
156
164
  };
157
165
  }
158
- for (let i = 0; i < fileList.length; i++) {
159
- const filesToUpload = fileList[i];
160
- await queue.addAll(filesToUpload.map(uploadFile));
166
+ // Upload all meta.json files first
167
+ await uploadMetaJsonFiles(filesByType[files_1.FILE_TYPES.module] || [], uploadFile);
168
+ // Collect all remaining files for upload
169
+ const deferredFiles = [];
170
+ Object.entries(filesByType).forEach(([fileType, files]) => {
171
+ if (fileType === files_1.FILE_TYPES.module) {
172
+ // Add non-meta.json module files
173
+ deferredFiles.push(...files.filter(f => !isMetaJsonFile(f)));
174
+ }
175
+ else {
176
+ // Add all non-module files
177
+ deferredFiles.push(...files);
178
+ }
179
+ });
180
+ // Upload all remaining files concurrently
181
+ if (deferredFiles.length > 0) {
182
+ await queue.addAll(deferredFiles.map(uploadFile));
161
183
  }
162
184
  const results = await queue
163
185
  .addAll(failures.map(({ file, destPath }) => {
package/lib/crm.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { ImportRequest } from '../types/Crm';
2
+ export declare function getImportDataRequest(fileName: string): {
3
+ importRequest: ImportRequest;
4
+ dataFileNames: string[];
5
+ };
6
+ export declare function validateImportRequestFile(fileName: string): void;
package/lib/crm.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateImportRequestFile = exports.getImportDataRequest = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const path_2 = require("./path");
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const lang_1 = require("../utils/lang");
11
+ function getImportDataRequest(fileName) {
12
+ validateImportRequestFile(fileName);
13
+ const importRequest = fs_extra_1.default.readJsonSync(path_1.default.resolve((0, path_2.getCwd)(), fileName));
14
+ const dataFileNames = importRequest.files.map(file => file.fileName);
15
+ // allow relative paths in the provided import request
16
+ importRequest.files = importRequest.files.map(file => ({
17
+ ...file,
18
+ fileName: path_1.default.basename(file.fileName),
19
+ }));
20
+ if (dataFileNames.length === 0) {
21
+ throw new Error((0, lang_1.i18n)('lib.crm.importData.errors.noFiles'));
22
+ }
23
+ dataFileNames.forEach(fileName => {
24
+ if (!fileExists(fileName)) {
25
+ throw new Error((0, lang_1.i18n)('lib.crm.importData.errors.fileNotFound', { fileName }));
26
+ }
27
+ });
28
+ return { importRequest, dataFileNames };
29
+ }
30
+ exports.getImportDataRequest = getImportDataRequest;
31
+ function validateImportRequestFile(fileName) {
32
+ if (!fileExists(fileName)) {
33
+ throw new Error((0, lang_1.i18n)('lib.crm.importData.errors.fileNotFound', { fileName }));
34
+ }
35
+ if (path_1.default.extname(fileName) !== '.json') {
36
+ throw new Error((0, lang_1.i18n)('lib.crm.importData.errors.notJson'));
37
+ }
38
+ }
39
+ exports.validateImportRequestFile = validateImportRequestFile;
40
+ function fileExists(_path) {
41
+ try {
42
+ const absoluteSrcPath = path_1.default.resolve((0, path_2.getCwd)(), _path);
43
+ if (!absoluteSrcPath)
44
+ return false;
45
+ const stats = fs_extra_1.default.statSync(absoluteSrcPath);
46
+ const isFile = stats.isFile();
47
+ if (!isFile) {
48
+ return false;
49
+ }
50
+ }
51
+ catch (e) {
52
+ return false;
53
+ }
54
+ return true;
55
+ }
package/lib/github.js CHANGED
@@ -24,6 +24,7 @@ async function fetchFileFromRepository(repoPath, filePath, ref) {
24
24
  return data;
25
25
  }
26
26
  catch (err) {
27
+ checkGithubRateLimit(err);
27
28
  throw new Error((0, lang_1.i18n)(`${i18nKey}.fetchFileFromRepository.errors.fetchFail`), {
28
29
  cause: err,
29
30
  });
@@ -43,6 +44,7 @@ async function fetchReleaseData(repoPath, tag) {
43
44
  return data;
44
45
  }
45
46
  catch (err) {
47
+ checkGithubRateLimit(err);
46
48
  throw new Error((0, lang_1.i18n)(`${i18nKey}.fetchReleaseData.errors.fetchFail`, {
47
49
  tag: tag || 'latest',
48
50
  }), { cause: err });
@@ -72,13 +74,14 @@ async function downloadGithubRepoZip(repoPath, isRelease = false, options = {})
72
74
  return data;
73
75
  }
74
76
  catch (err) {
77
+ checkGithubRateLimit(err);
75
78
  throw new Error((0, lang_1.i18n)(`${i18nKey}.downloadGithubRepoZip.errors.fetchFail`), {
76
79
  cause: err,
77
80
  });
78
81
  }
79
82
  }
80
83
  async function cloneGithubRepo(repoPath, dest, options = {}) {
81
- const { tag, isRelease, branch, sourceDir, type, hideLogs } = options;
84
+ const { tag, isRelease, branch, sourceDir, type, hideLogs, handleCollision } = options;
82
85
  const zip = await downloadGithubRepoZip(repoPath, isRelease, {
83
86
  tag,
84
87
  branch,
@@ -87,6 +90,7 @@ async function cloneGithubRepo(repoPath, dest, options = {}) {
87
90
  const success = await (0, archive_1.extractZipArchive)(zip, repoName, dest, {
88
91
  sourceDir,
89
92
  hideLogs,
93
+ handleCollision,
90
94
  });
91
95
  if (success && !hideLogs) {
92
96
  logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.cloneGithubRepo.success`, {
@@ -146,6 +150,7 @@ async function downloadGithubRepoContents(repoPath, contentPath, dest, ref, filt
146
150
  await Promise.all(contentPromises);
147
151
  }
148
152
  catch (e) {
153
+ checkGithubRateLimit(e);
149
154
  if ((0, errors_1.isSystemError)(e) && e?.error?.message) {
150
155
  throw new Error((0, lang_1.i18n)(`${i18nKey}.downloadGithubRepoContents.errors.fetchFail`, {
151
156
  errorMessage: e.error.message,
@@ -165,6 +170,7 @@ async function listGithubRepoContents(repoPath, contentPath, fileFilter) {
165
170
  return filteredFiles;
166
171
  }
167
172
  catch (e) {
173
+ checkGithubRateLimit(e);
168
174
  if ((0, errors_1.isHubSpotHttpError)(e) && e.data.message) {
169
175
  throw new Error((0, lang_1.i18n)(`${i18nKey}.downloadGithubRepoContents.errors.fetchFail`, {
170
176
  errorMessage: e.data.message,
@@ -174,3 +180,10 @@ async function listGithubRepoContents(repoPath, contentPath, fileFilter) {
174
180
  }
175
181
  }
176
182
  exports.listGithubRepoContents = listGithubRepoContents;
183
+ function checkGithubRateLimit(err) {
184
+ if ((0, errors_1.isGithubRateLimitError)(err)) {
185
+ throw new Error((0, lang_1.i18n)(`${i18nKey}.rateLimitError`), {
186
+ cause: err,
187
+ });
188
+ }
189
+ }
package/lib/hubdb.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare function createHubDbTable(accountId: number, src: string): Promis
8
8
  tableId: string;
9
9
  rowCount: number;
10
10
  }>;
11
- export declare function updateHubDbTable(accountId: number, tableId: string, src: string): Promise<AxiosResponse<Table, any>>;
11
+ export declare function updateHubDbTable(accountId: number, tableId: string, src: string): Promise<AxiosResponse<Table, any, {}>>;
12
12
  export declare function downloadHubDbTable(accountId: number, tableId: string, dest: string): Promise<{
13
13
  filePath: string;
14
14
  }>;
@@ -0,0 +1 @@
1
+ export declare function isDeepEqual(object1: unknown, object2: unknown, ignoreKeys?: string[]): boolean;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDeepEqual = void 0;
4
+ function isDeepEqual(object1, object2, ignoreKeys) {
5
+ if (object1 === object2) {
6
+ return true;
7
+ }
8
+ if (object1 === null ||
9
+ object2 === null ||
10
+ typeof object1 !== 'object' ||
11
+ typeof object2 !== 'object') {
12
+ return object1 === object2;
13
+ }
14
+ if (typeof object1 !== typeof object2) {
15
+ return false;
16
+ }
17
+ const isArray1 = Array.isArray(object1);
18
+ const isArray2 = Array.isArray(object2);
19
+ if (isArray1 !== isArray2) {
20
+ return false;
21
+ }
22
+ const objKeys1 = Object.keys(object1).filter(key => !ignoreKeys?.includes(key));
23
+ const objKeys2 = Object.keys(object2).filter(key => !ignoreKeys?.includes(key));
24
+ if (objKeys1.length !== objKeys2.length)
25
+ return false;
26
+ for (const key of objKeys1) {
27
+ const value1 = object1[key];
28
+ const value2 = object2[key];
29
+ if (!isDeepEqual(value1, value2, ignoreKeys)) {
30
+ return false;
31
+ }
32
+ }
33
+ return true;
34
+ }
35
+ exports.isDeepEqual = isDeepEqual;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/local-dev-lib",
3
- "version": "0.4.0-experimental.1",
3
+ "version": "0.4.2-experimental.0",
4
4
  "description": "Provides library functionality for HubSpot local development tooling, including the HubSpot CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,6 +20,7 @@
20
20
  },
21
21
  "license": "Apache-2.0",
22
22
  "devDependencies": {
23
+ "@hubspot/npm-scripts": "0.0.4-beta.0",
23
24
  "@inquirer/prompts": "^7.0.1",
24
25
  "@types/content-disposition": "^0.5.5",
25
26
  "@types/cors": "^2.8.15",
@@ -61,7 +62,7 @@
61
62
  },
62
63
  "dependencies": {
63
64
  "address": "2.0.2",
64
- "axios": "1.8.4",
65
+ "axios": "1.12.0",
65
66
  "chalk": "2.4.2",
66
67
  "chokidar": "3.6.0",
67
68
  "content-disposition": "0.5.4",
@@ -70,6 +71,7 @@
70
71
  "express": "4.21.2",
71
72
  "extract-zip": "2.0.1",
72
73
  "findup-sync": "5.0.0",
74
+ "form-data": "^4.0.4",
73
75
  "fs-extra": "11.2.0",
74
76
  "ignore": "5.3.1",
75
77
  "js-yaml": "4.1.0",
@@ -1,9 +1,15 @@
1
+ export interface Collision {
2
+ dest: string;
3
+ src: string;
4
+ collisions: string[];
5
+ }
1
6
  export type ZipData = {
2
7
  extractDir: string;
3
8
  tmpDir: string;
4
9
  };
5
10
  export type CopySourceToDestOptions = {
6
- sourceDir?: string;
11
+ sourceDir?: string | string[];
7
12
  includesRootDir?: boolean;
8
13
  hideLogs?: boolean;
14
+ handleCollision?: (collision: Collision) => void | Promise<void>;
9
15
  };
package/types/Config.d.ts CHANGED
@@ -12,6 +12,8 @@ export interface CLIConfig_NEW {
12
12
  httpTimeout?: number;
13
13
  env?: Environment;
14
14
  httpUseLocalhost?: boolean;
15
+ autoOpenBrowser?: boolean;
16
+ flags?: Array<string>;
15
17
  }
16
18
  export interface CLIConfig_DEPRECATED {
17
19
  portals: Array<CLIAccount_DEPRECATED>;
@@ -23,6 +25,8 @@ export interface CLIConfig_DEPRECATED {
23
25
  httpTimeout?: number;
24
26
  env?: Environment;
25
27
  httpUseLocalhost?: boolean;
28
+ autoOpenBrowser?: boolean;
29
+ flags?: Array<string>;
26
30
  }
27
31
  export type CLIConfig = CLIConfig_NEW | CLIConfig_DEPRECATED;
28
32
  export type Environment = ValueOf<typeof ENVIRONMENTS> | '';
package/types/Crm.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export interface ImportRequest {
2
+ name: string;
3
+ importOperations: {
4
+ [objectTypeId: string]: 'CREATE' | 'UPDATE' | 'UPSERT';
5
+ };
6
+ dateFormat?: string;
7
+ marketableContactImport?: boolean;
8
+ createContactListFromImport?: boolean;
9
+ files: Array<{
10
+ fileName: string;
11
+ fileFormat: 'CSV' | 'XLSX' | 'XLS';
12
+ fileImportPage: {
13
+ hasHeader: boolean;
14
+ columnMappings: Array<{
15
+ columnObjectTypeId: string;
16
+ columnName: string;
17
+ propertyName: string;
18
+ columnType?: string;
19
+ }>;
20
+ };
21
+ }>;
22
+ }
23
+ export interface ImportResponse {
24
+ id: string;
25
+ state: 'STARTED' | 'PROCESSING' | 'DONE' | 'FAILED' | 'CANCELED' | 'DEFERRED';
26
+ }
package/types/Crm.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/types/Deploy.d.ts CHANGED
@@ -35,9 +35,24 @@ export type DeployStatusTaskLocator = {
35
35
  status: string;
36
36
  }>;
37
37
  };
38
- export type ProjectDeployResponse = {
38
+ export type ProjectDeployResponseQueued = {
39
39
  id: string;
40
+ buildResultType: 'DEPLOY_QUEUED';
40
41
  links: {
41
42
  status: string;
42
43
  };
43
44
  };
45
+ export type SubdeployValidationIssue = {
46
+ uid: string;
47
+ componentTypeName: string;
48
+ errorMessages: string[];
49
+ blockingMessages: {
50
+ message: string;
51
+ isWarning: boolean;
52
+ }[];
53
+ };
54
+ export type ProjectDeployResponseBlocked = {
55
+ buildResultType: 'DEPLOY_BLOCKED';
56
+ issues: SubdeployValidationIssue[];
57
+ };
58
+ export type ProjectDeployResponse = ProjectDeployResponseQueued | ProjectDeployResponseBlocked;
package/types/Github.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Collision } from './Archive';
1
2
  type GithubAuthor = {
2
3
  login: string;
3
4
  id: number;
@@ -71,7 +72,8 @@ export type CloneGithubRepoOptions = {
71
72
  type?: string;
72
73
  branch?: string;
73
74
  tag?: string;
74
- sourceDir?: string;
75
+ sourceDir?: string | string[];
75
76
  hideLogs?: boolean;
77
+ handleCollision?: (collision: Collision) => void;
76
78
  };
77
79
  export {};
@@ -111,6 +111,33 @@ export type Sandbox = {
111
111
  requestAccessFrom?: User | null;
112
112
  superAdminsInSandbox?: number;
113
113
  };
114
+ export declare const SandboxVersioning: {
115
+ readonly V1: "V1";
116
+ readonly V2: "V2";
117
+ };
118
+ export declare const SandboxStatus: {
119
+ readonly PENDING: "PENDING";
120
+ readonly READY: "READY";
121
+ readonly FAILED: "FAILED";
122
+ };
123
+ export type SandboxVersion = keyof typeof SandboxVersioning;
124
+ export type V2SandboxStatus = keyof typeof SandboxStatus;
125
+ export type V2Sandbox = {
126
+ sandboxHubId: number;
127
+ parentHubId: number;
128
+ name: string;
129
+ version: SandboxVersion;
130
+ type: string;
131
+ status: V2SandboxStatus;
132
+ createdAt: string;
133
+ createdByUser: User;
134
+ currentUserHasAccess?: boolean;
135
+ currentUserHasSuperAdminAccess?: boolean;
136
+ superAdminsInSandbox?: number;
137
+ requestAccessFrom?: User | null;
138
+ updatedAt?: string;
139
+ updatedByUser?: User | null;
140
+ };
114
141
  export type SandboxResponse = {
115
142
  sandbox: Sandbox;
116
143
  personalAccessKey: string;
@@ -152,4 +179,10 @@ export type SandboxType = {
152
179
  export type FetchTypesResponse = {
153
180
  results: Array<SandboxType>;
154
181
  };
182
+ export type SandboxPersonalAccessKey = {
183
+ personalAccessKey: {
184
+ encodedOAuthRefreshToken: string;
185
+ oauthAccessToken: string;
186
+ };
187
+ };
155
188
  export {};
package/types/Sandbox.js CHANGED
@@ -1,2 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SandboxStatus = exports.SandboxVersioning = void 0;
4
+ exports.SandboxVersioning = {
5
+ V1: 'V1',
6
+ V2: 'V2',
7
+ };
8
+ exports.SandboxStatus = {
9
+ PENDING: 'PENDING',
10
+ READY: 'READY',
11
+ FAILED: 'FAILED',
12
+ };