gologin 2.1.31 → 2.1.33

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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  Combined changelog for GoLogin node.js SDK
4
4
 
5
+ ## [2.1.33] 2025-09-11
6
+
7
+
8
+ ### Fixes
9
+
10
+ * Orbita download doesnt interupts while opening two profiles in parallel
11
+ * Error gathering changes
12
+
13
+ ## [2.1.32] 2025-09-11
14
+
15
+
16
+ ### Fixes
17
+
18
+ * Custom extension downloading now works correctly
19
+
5
20
  ## [2.1.31] 2025-09-01
6
21
 
7
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "2.1.31",
3
+ "version": "2.1.33",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "types": "./index.d.ts",
6
6
  "main": "./src/gologin.js",
@@ -7,9 +7,9 @@ import { homedir } from 'os';
7
7
  import { join } from 'path';
8
8
  import ProgressBar from 'progress';
9
9
  import util from 'util';
10
- import { createInterface } from 'readline';
11
10
 
12
11
  import { API_URL, getOS } from '../utils/common.js';
12
+ import BrowserDownloadLockManager from './browser-download-manager.js';
13
13
 
14
14
  const exec = util.promisify(execNonPromise);
15
15
  const { access, mkdir, readdir, rmdir, unlink, copyFile, readlink, symlink, lstat, rename, writeFile, readFile } = _promises;
@@ -19,37 +19,31 @@ const ARCH = process.arch;
19
19
 
20
20
  const VERSION_FILE = 'latest-version.txt';
21
21
 
22
- const WIN_FOLDERSIZE_FILE = 'foldersize.txt';
23
- const WIN_FOLDERSIZE_FILE_LINK = `https://orbita-browser-windows.gologin.com/${WIN_FOLDERSIZE_FILE}`;
24
-
25
22
  const BROWSER_ARCHIVE_NAME = `orbita-browser-latest.${PLATFORM === 'win32' ? 'zip' : 'tar.gz'}`;
26
23
 
27
24
  const MAC_HASH_FILE = 'hashfile.mtree';
28
25
  const DEB_HASH_FILE = 'hashfile.txt';
29
- const WIN_HASH_FILE = DEB_HASH_FILE;
30
26
  const MAC_HASHFILE_LINK = `https://orbita-browser-mac.gologin.com/${MAC_HASH_FILE}`;
31
27
  const DEB_HASHFILE_LINK = `https://orbita-browser-linux.gologin.com/${DEB_HASH_FILE}`;
32
- const WIN_HASHFILE_LINK = `https://orbita-browser-windows.gologin.com/${WIN_HASH_FILE}`;
33
28
  const MAC_ARM_HASHFILE_LINK = `https://orbita-browser-mac-arm.gologin.com/${MAC_HASH_FILE}`;
34
29
 
35
30
  const FAIL_SUM_MATCH_MESSAGE = 'hash_sum_not_matched';
36
31
  const EXTRACTED_FOLDER = 'extracted-browser';
37
32
 
38
33
  export class BrowserChecker {
39
- #homedir = homedir();
40
- #browserPath = join(this.#homedir, '.gologin', 'browser');
41
- #executableFilePath;
42
- #skipOrbitaHashChecking = false;
43
-
44
34
  constructor() {
45
-
35
+ this.homedir = homedir();
36
+ this.browserPath = join(this.homedir, '.gologin', 'browser');
37
+ this.executableFilePath = null;
38
+ this.skipOrbitaHashChecking = false;
39
+ this.downloadManager = BrowserDownloadLockManager.getInstance();
40
+ this.downloadManager.cleanupStaleLocks().catch(console.warn);
46
41
  }
47
42
 
48
- async checkBrowser({ autoUpdateBrowser, checkBrowserUpdate, majorVersion }) {
49
- const isBrowserFolderExists = await access(join(this.#browserPath, `orbita-browser-${majorVersion}`)).then(() => true).catch(() => false);
50
-
43
+ async checkBrowser({ autoUpdateBrowser, majorVersion }) {
44
+ const isBrowserFolderExists = await access(join(this.browserPath, `orbita-browser-${majorVersion}`)).then(() => true).catch(() => false);
51
45
  if (!isBrowserFolderExists || autoUpdateBrowser) {
52
- await this.downloadBrowser(majorVersion);
46
+ await this.downloadManager.ensureBrowserDownload(majorVersion, () => this.downloadBrowser(majorVersion));
53
47
 
54
48
  return this.getBrowserExecutablePath(majorVersion);
55
49
  }
@@ -71,8 +65,6 @@ export class BrowserChecker {
71
65
  // return this.getBrowserExecutablePath(majorVersion);
72
66
  // }
73
67
 
74
-
75
-
76
68
  // return new Promise(resolve => {
77
69
  // const rl = createInterface(process.stdin, process.stdout);
78
70
  // const timeout = setTimeout(() => {
@@ -94,9 +86,9 @@ export class BrowserChecker {
94
86
  }
95
87
 
96
88
  async downloadBrowser(majorVersion) {
97
- await mkdir(this.#browserPath, { recursive: true });
89
+ await mkdir(this.browserPath, { recursive: true });
98
90
 
99
- const browserPath = join(this.#browserPath, BROWSER_ARCHIVE_NAME);
91
+ const browserPath = join(this.browserPath, BROWSER_ARCHIVE_NAME);
100
92
 
101
93
  const browserDownloadUrl = this.getBrowserDownloadUrl(majorVersion);
102
94
 
@@ -110,13 +102,13 @@ export class BrowserChecker {
110
102
  const os = getOS();
111
103
  switch (os) {
112
104
  case 'mac':
113
- return join(this.#browserPath, `orbita-browser-${majorVersion}`, 'Orbita-Browser.app', 'Contents', 'MacOS', 'Orbita');
105
+ return join(this.browserPath, `orbita-browser-${majorVersion}`, 'Orbita-Browser.app', 'Contents', 'MacOS', 'Orbita');
114
106
  case 'win':
115
- return join(this.#browserPath, `orbita-browser-${majorVersion}`, 'chrome.exe');
107
+ return join(this.browserPath, `orbita-browser-${majorVersion}`, 'chrome.exe');
116
108
  case 'macM1':
117
- return join(this.#browserPath, `orbita-browser-${majorVersion}`, 'Orbita-Browser.app', 'Contents', 'MacOS', 'Orbita');
109
+ return join(this.browserPath, `orbita-browser-${majorVersion}`, 'Orbita-Browser.app', 'Contents', 'MacOS', 'Orbita');
118
110
  default:
119
- return join(this.#browserPath, `orbita-browser-${majorVersion}`, 'chrome');
111
+ return join(this.browserPath, `orbita-browser-${majorVersion}`, 'chrome');
120
112
  }
121
113
  }
122
114
 
@@ -135,8 +127,8 @@ export class BrowserChecker {
135
127
  }
136
128
 
137
129
  addLatestVersion(latestVersion) {
138
- return mkdir(join(this.#browserPath, 'orbita-browser', 'version'), { recursive: true })
139
- .then(() => writeFile(join(this.#browserPath, 'orbita-browser', 'version', 'latest-version.txt'), latestVersion));
130
+ return mkdir(join(this.browserPath, 'orbita-browser', 'version'), { recursive: true })
131
+ .then(() => writeFile(join(this.browserPath, 'orbita-browser', 'version', 'latest-version.txt'), latestVersion));
140
132
  }
141
133
 
142
134
  async downloadBrowserArchive(link, pathStr) {
@@ -155,6 +147,7 @@ export class BrowserChecker {
155
147
  const formattedLen = len / 1024 / 1024;
156
148
  if (isNaN(formattedLen)) {
157
149
  reject(new Error('Error downloading browser'));
150
+
158
151
  return;
159
152
  }
160
153
 
@@ -204,9 +197,9 @@ export class BrowserChecker {
204
197
 
205
198
  async extractBrowser() {
206
199
  console.log('Extracting Orbita');
207
- await mkdir(join(this.#browserPath, EXTRACTED_FOLDER), { recursive: true });
200
+ await mkdir(join(this.browserPath, EXTRACTED_FOLDER), { recursive: true });
208
201
  if (PLATFORM === 'win32') {
209
- return decompress(join(this.#browserPath, BROWSER_ARCHIVE_NAME), join(this.#browserPath, EXTRACTED_FOLDER),
202
+ return decompress(join(this.browserPath, BROWSER_ARCHIVE_NAME), join(this.browserPath, EXTRACTED_FOLDER),
210
203
  {
211
204
  plugins: [decompressUnzip()],
212
205
  filter: file => !file.path.endsWith('/'),
@@ -215,20 +208,20 @@ export class BrowserChecker {
215
208
  }
216
209
 
217
210
  return exec(
218
- `tar xzf ${join(this.#browserPath, BROWSER_ARCHIVE_NAME)} --directory ${join(this.#browserPath, EXTRACTED_FOLDER)}`,
211
+ `tar xzf ${join(this.browserPath, BROWSER_ARCHIVE_NAME)} --directory ${join(this.browserPath, EXTRACTED_FOLDER)}`,
219
212
  );
220
213
  }
221
214
 
222
215
  async downloadHashFile(latestVersion) {
223
216
  let hashLink = DEB_HASHFILE_LINK;
224
- let resultPath = join(this.#browserPath, DEB_HASH_FILE);
217
+ let resultPath = join(this.browserPath, DEB_HASH_FILE);
225
218
  if (PLATFORM === 'darwin') {
226
219
  hashLink = MAC_HASHFILE_LINK;
227
220
  if (ARCH === 'arm64') {
228
221
  hashLink = MAC_ARM_HASHFILE_LINK;
229
222
  }
230
223
 
231
- resultPath = join(this.#browserPath, MAC_HASH_FILE);
224
+ resultPath = join(this.browserPath, MAC_HASH_FILE);
232
225
  }
233
226
 
234
227
  if (latestVersion) {
@@ -256,13 +249,13 @@ export class BrowserChecker {
256
249
  }).on('error', (err) => writableStream.destroy(err)));
257
250
 
258
251
  const hashFile = PLATFORM === 'darwin' ? MAC_HASH_FILE : DEB_HASH_FILE;
259
- const hashFilePath = join(this.#browserPath, hashFile);
252
+ const hashFilePath = join(this.browserPath, hashFile);
260
253
 
261
254
  return access(hashFilePath);
262
255
  }
263
256
 
264
257
  async checkBrowserSum(latestVersion) {
265
- if (this.#skipOrbitaHashChecking) {
258
+ if (this.skipOrbitaHashChecking) {
266
259
  return Promise.resolve();
267
260
  }
268
261
 
@@ -274,7 +267,7 @@ export class BrowserChecker {
274
267
  await this.downloadHashFile(latestVersion);
275
268
  if (PLATFORM === 'darwin') {
276
269
  const calculatedHash = await exec(
277
- `mtree -p ${join(this.#browserPath, EXTRACTED_FOLDER, 'Orbita-Browser.app')} < ${join(this.#browserPath, MAC_HASH_FILE)} || echo ${FAIL_SUM_MATCH_MESSAGE}`,
270
+ `mtree -p ${join(this.browserPath, EXTRACTED_FOLDER, 'Orbita-Browser.app')} < ${join(this.browserPath, MAC_HASH_FILE)} || echo ${FAIL_SUM_MATCH_MESSAGE}`,
278
271
  );
279
272
 
280
273
  const checkedRes = (calculatedHash || '').toString().trim();
@@ -285,16 +278,16 @@ export class BrowserChecker {
285
278
  return;
286
279
  }
287
280
 
288
- const hashFileContent = await exec(`cat ${join(this.#browserPath, DEB_HASH_FILE)}`);
281
+ const hashFileContent = await exec(`cat ${join(this.browserPath, DEB_HASH_FILE)}`);
289
282
  let serverRes = (hashFileContent.stdout || '').toString().trim();
290
283
  serverRes = serverRes.split(' ')[0];
291
284
 
292
285
  const calculateLocalBrowserHash = await exec(
293
- `cd ${join(this.#browserPath, EXTRACTED_FOLDER)} && find orbita-browser -type f -print0 | sort -z | \
294
- xargs -0 sha256sum > ${this.#browserPath}/calculatedFolderSha.txt`,
286
+ `cd ${join(this.browserPath, EXTRACTED_FOLDER)} && find orbita-browser -type f -print0 | sort -z | \
287
+ xargs -0 sha256sum > ${this.browserPath}/calculatedFolderSha.txt`,
295
288
  );
296
289
 
297
- const localHashContent = await exec(`cd ${this.#browserPath} && sha256sum calculatedFolderSha.txt`);
290
+ const localHashContent = await exec(`cd ${this.browserPath} && sha256sum calculatedFolderSha.txt`);
298
291
  let userRes = (localHashContent.stdout || '').toString().trim();
299
292
  userRes = userRes.split(' ')[0];
300
293
  if (userRes !== serverRes) {
@@ -304,28 +297,28 @@ export class BrowserChecker {
304
297
 
305
298
  async replaceBrowser(majorVersion) {
306
299
  console.log('Copy Orbita to target path');
307
- const targetBrowserPath = join(this.#browserPath, `orbita-browser-${majorVersion}`);
300
+ const targetBrowserPath = join(this.browserPath, `orbita-browser-${majorVersion}`);
308
301
  await this.deleteDir(targetBrowserPath);
309
302
 
310
303
  if (PLATFORM === 'darwin') {
311
- return rename(join(this.#browserPath, EXTRACTED_FOLDER), targetBrowserPath);
304
+ return rename(join(this.browserPath, EXTRACTED_FOLDER), targetBrowserPath);
312
305
  }
313
306
 
314
307
  await this.copyDir(
315
- join(this.#browserPath, EXTRACTED_FOLDER, 'orbita-browser'),
308
+ join(this.browserPath, EXTRACTED_FOLDER, 'orbita-browser'),
316
309
  targetBrowserPath,
317
310
  );
318
311
  }
319
312
 
320
313
  async deleteOldArchives() {
321
- await this.deleteDir(join(this.#browserPath, EXTRACTED_FOLDER));
314
+ await this.deleteDir(join(this.browserPath, EXTRACTED_FOLDER));
322
315
 
323
- return readdir(this.#browserPath)
316
+ return readdir(this.browserPath)
324
317
  .then((files) => {
325
318
  const promises = [];
326
319
  files.forEach((filename) => {
327
320
  if (filename.match(/(txt|dylib|mtree)/)) {
328
- promises.push(unlink(join(this.#browserPath, filename)));
321
+ promises.push(unlink(join(this.browserPath, filename)));
329
322
  }
330
323
  });
331
324
 
@@ -355,12 +348,12 @@ export class BrowserChecker {
355
348
  }
356
349
 
357
350
  async getCurrentVersion(majorVersion) {
358
- let versionFilePath = join(this.#browserPath, `orbita-browser-${majorVersion}`, 'version');
351
+ let versionFilePath = join(this.browserPath, `orbita-browser-${majorVersion}`, 'version');
359
352
  if (PLATFORM === 'darwin') {
360
- versionFilePath = join(this.#browserPath, `orbita-browser-${majorVersion}`, 'version', VERSION_FILE);
353
+ versionFilePath = join(this.browserPath, `orbita-browser-${majorVersion}`, 'version', VERSION_FILE);
361
354
  }
362
355
 
363
- return (await readFile(versionFilePath, 'utf8').catch(() => '0.0.0')).replace(/[\r\n\t\f\v\u0000-\u001F\u007F]/g, '');
356
+ return (await readFile(versionFilePath, 'utf8').catch(() => '0.0.0')).replace(/[\r\n\t\f\v\x00-\x1F\x7F]/g, '');
364
357
  }
365
358
 
366
359
  getLatestBrowserVersion() {
@@ -386,7 +379,7 @@ export class BrowserChecker {
386
379
  }
387
380
 
388
381
  get getOrbitaPath() {
389
- return this.#executableFilePath;
382
+ return this.executableFilePath;
390
383
  }
391
384
 
392
385
  async deleteDir(path = '') {
@@ -0,0 +1,222 @@
1
+ import { spawn } from 'child_process';
2
+ import { promises as _promises } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+
6
+ const { access, writeFile, unlink, mkdir } = _promises;
7
+
8
+ const HOMEDIR = homedir();
9
+ const BROWSER_PATH = join(HOMEDIR, '.gologin', 'browser');
10
+ const LOCK_FILE_PREFIX = 'download-lock-';
11
+
12
+ export class BrowserDownloadLockManager {
13
+ constructor() {
14
+ if (BrowserDownloadLockManager.instance) {
15
+ return BrowserDownloadLockManager.instance;
16
+ }
17
+
18
+ BrowserDownloadLockManager.instance = this;
19
+ BrowserDownloadLockManager.downloadPromises = new Map();
20
+ }
21
+
22
+ static getInstance() {
23
+ if (!BrowserDownloadLockManager.instance) {
24
+ BrowserDownloadLockManager.instance = new BrowserDownloadLockManager();
25
+ }
26
+
27
+ return BrowserDownloadLockManager.instance;
28
+ }
29
+
30
+ async ensureBrowserDownload(majorVersion, downloadFunction) {
31
+ const lockKey = `${majorVersion}`;
32
+
33
+ if (BrowserDownloadLockManager.downloadPromises.has(lockKey)) {
34
+ return BrowserDownloadLockManager.downloadPromises.get(lockKey);
35
+ }
36
+
37
+ const lockFilePath = join(BROWSER_PATH, `${LOCK_FILE_PREFIX}${majorVersion}.json`);
38
+
39
+ try {
40
+ await mkdir(BROWSER_PATH, { recursive: true });
41
+
42
+ const isBrowserExists = await this.checkBrowserExists(majorVersion);
43
+ if (isBrowserExists) {
44
+ return majorVersion;
45
+ }
46
+
47
+ const lockPromise = this.acquireLockAndDownload(lockFilePath, majorVersion, downloadFunction);
48
+ BrowserDownloadLockManager.downloadPromises.set(lockKey, lockPromise);
49
+
50
+ try {
51
+ const result = await lockPromise;
52
+
53
+ return result;
54
+ } finally {
55
+ BrowserDownloadLockManager.downloadPromises.delete(lockKey);
56
+ }
57
+ } catch (error) {
58
+ BrowserDownloadLockManager.downloadPromises.delete(lockKey);
59
+ throw error;
60
+ }
61
+ }
62
+
63
+ async acquireLockAndDownload(lockFilePath, majorVersion, downloadFunction) {
64
+ const maxWaitTime = 300000;
65
+ const checkInterval = 1000;
66
+ const startTime = Date.now();
67
+
68
+ while (Date.now() - startTime < maxWaitTime) {
69
+ try {
70
+ await this.createLockFile(lockFilePath, majorVersion);
71
+
72
+ const isBrowserExists = await this.checkBrowserExists(majorVersion);
73
+ if (isBrowserExists) {
74
+ await this.releaseLock(lockFilePath);
75
+
76
+ return majorVersion;
77
+ }
78
+
79
+ await downloadFunction();
80
+ await this.releaseLock(lockFilePath);
81
+
82
+ return majorVersion;
83
+ } catch (error) {
84
+ if (error.code === 'EEXIST') {
85
+ const lockValid = await this.checkLockValidity(lockFilePath);
86
+ if (!lockValid) {
87
+ console.log(`Lock file ${lockFilePath} is stale, removing it`);
88
+ await this.releaseLock(lockFilePath);
89
+ continue;
90
+ }
91
+
92
+ await this.waitForLockRelease(lockFilePath, checkInterval);
93
+ continue;
94
+ }
95
+
96
+ await this.releaseLock(lockFilePath);
97
+ throw error;
98
+ }
99
+ }
100
+
101
+ throw new Error(`Timeout waiting for browser download lock for version ${majorVersion}`);
102
+ }
103
+
104
+ async createLockFile(lockFilePath, majorVersion) {
105
+ const lockData = {
106
+ majorVersion,
107
+ timestamp: Date.now(),
108
+ pid: process.pid,
109
+ };
110
+
111
+ await writeFile(lockFilePath, JSON.stringify(lockData), { flag: 'wx' });
112
+ }
113
+
114
+ async releaseLock(lockFilePath) {
115
+ try {
116
+ await unlink(lockFilePath);
117
+ } catch (error) {
118
+ if (error.code !== 'ENOENT') {
119
+ console.warn('Failed to release download lock:', error.message);
120
+ }
121
+ }
122
+ }
123
+
124
+ async waitForLockRelease(lockFilePath, checkInterval) {
125
+ return new Promise((resolve) => {
126
+ const checkLock = async () => {
127
+ try {
128
+ await access(lockFilePath);
129
+ setTimeout(checkLock, checkInterval);
130
+ } catch (error) {
131
+ if (error.code === 'ENOENT') {
132
+ resolve();
133
+ } else {
134
+ setTimeout(checkLock, checkInterval);
135
+ }
136
+ }
137
+ };
138
+
139
+ checkLock();
140
+ });
141
+ }
142
+
143
+ async checkBrowserExists(majorVersion) {
144
+ try {
145
+ await access(join(BROWSER_PATH, `orbita-browser-${majorVersion}`));
146
+
147
+ return true;
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
152
+
153
+ async isProcessRunning(pid) {
154
+ return new Promise((resolve) => {
155
+ const { platform } = process;
156
+ let command;
157
+ let args;
158
+
159
+ if (platform === 'win32') {
160
+ command = 'tasklist';
161
+ args = ['/FI', `PID eq ${pid}`];
162
+ } else {
163
+ command = 'ps';
164
+ args = ['-p', pid.toString()];
165
+ }
166
+
167
+ const child = spawn(command, args, { stdio: 'pipe' });
168
+
169
+ child.on('error', () => resolve(false));
170
+ child.on('close', (code) => {
171
+ resolve(code === 0);
172
+ });
173
+ });
174
+ }
175
+
176
+ async checkLockValidity(lockFilePath) {
177
+ try {
178
+ const lockContent = await _promises.readFile(lockFilePath, 'utf8');
179
+ const lockData = JSON.parse(lockContent);
180
+
181
+ const lockAge = Date.now() - lockData.timestamp;
182
+ const maxLockAge = 300000;
183
+
184
+ if (lockAge > maxLockAge) {
185
+ return false;
186
+ }
187
+
188
+ const isProcessAlive = await this.isProcessRunning(lockData.pid);
189
+
190
+ return isProcessAlive;
191
+ } catch (error) {
192
+ return false;
193
+ }
194
+ }
195
+
196
+ async cleanupStaleLocks() {
197
+ try {
198
+ const files = await _promises.readdir(BROWSER_PATH);
199
+ const lockFiles = files.filter(file => file.startsWith(LOCK_FILE_PREFIX));
200
+
201
+ for (const lockFile of lockFiles) {
202
+ try {
203
+ const lockFilePath = join(BROWSER_PATH, lockFile);
204
+ const isValid = await this.checkLockValidity(lockFilePath);
205
+
206
+ if (!isValid) {
207
+ await unlink(lockFilePath);
208
+ console.log(`Cleaned up stale lock file: ${lockFile}`);
209
+ }
210
+ } catch (error) {
211
+ console.warn(`Failed to process lock file ${lockFile}:`, error.message);
212
+ }
213
+ }
214
+ } catch (error) {
215
+ if (error.code !== 'ENOENT') {
216
+ console.warn('Failed to cleanup stale locks:', error.message);
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ export default BrowserDownloadLockManager;
@@ -80,7 +80,7 @@ export class UserExtensionsManager {
80
80
  }
81
81
 
82
82
  const extensionsToDownloadPaths = await makeRequest(`${this.#API_BASE_URL}/extensions/user_chrome_extensions_paths`, {
83
- fullResponse: false,
83
+ fullResponse: true,
84
84
  json: {
85
85
  existedUserChromeExtensions: this.#existedUserExtensions,
86
86
  profileId,
package/src/gologin.js CHANGED
@@ -5,10 +5,9 @@ import decompress from 'decompress';
5
5
  import decompressUnzip from 'decompress-unzip';
6
6
  import { existsSync, mkdirSync, promises as _promises } from 'fs';
7
7
  import { tmpdir } from 'os';
8
- import { dirname, join, resolve as _resolve, sep } from 'path';
8
+ import { join, resolve as _resolve, sep } from 'path';
9
9
  import rimraf from 'rimraf';
10
10
  import { SocksProxyAgent } from 'socks-proxy-agent';
11
- import { fileURLToPath } from 'url';
12
11
 
13
12
  import { fontsCollection } from '../fonts.js';
14
13
  import { getCurrentProfileBookmarks } from './bookmarks/utils.js';
@@ -33,8 +32,10 @@ import { STORAGE_GATEWAY_BASE_URL } from './utils/constants.js';
33
32
  import { get, isPortReachable } from './utils/utils.js';
34
33
  export { exitAll, GologinApi } from './gologin-api.js';
35
34
  import { checkSocksProxy, makeRequest } from './utils/http.js';
35
+ import { captureGroupedSentryError } from './utils/sentry.js';
36
36
  import { zeroProfileBookmarks } from './utils/zero-profile-bookmarks.js';
37
37
  import { zeroProfilePreferences } from './utils/zero-profile-preferences.js';
38
+
38
39
  const { access, unlink, writeFile, readFile, mkdir, copyFile } = _promises;
39
40
 
40
41
  const SEPARATOR = sep;
@@ -89,7 +90,7 @@ export class GoLogin {
89
90
  dsn: 'https://a13d5939a60ae4f6583e228597f1f2a0@sentry-new.amzn.pro/24',
90
91
  tracesSampleRate: 1.0,
91
92
  defaultIntegrations: false,
92
- release: process.env.npm_package_version || '2.1.24',
93
+ release: process.env.npm_package_version || '2.1.33',
93
94
  });
94
95
  }
95
96
 
@@ -213,16 +214,6 @@ export class GoLogin {
213
214
  console.log('Profile has been uploaded to S3 successfully');
214
215
  }
215
216
 
216
- async emptyProfileFolder() {
217
- debug('get emptyProfileFolder');
218
- const currentDir = dirname(fileURLToPath(import.meta.url));
219
- const zeroProfilePath = join(currentDir, '..', 'zero_profile.zip');
220
- const profile = await readFile(_resolve(zeroProfilePath));
221
- debug('emptyProfileFolder LENGTH ::', profile.length);
222
-
223
- return profile;
224
- }
225
-
226
217
  getGologinPreferences(profileData) {
227
218
  const os = profileData.os || '';
228
219
  const osSpec = profileData.osSpec || '';
@@ -417,9 +408,7 @@ export class GoLogin {
417
408
  debug('extraction done');
418
409
  } catch (e) {
419
410
  console.trace(e);
420
- profile_folder = await this.emptyProfileFolder();
421
- await writeFile(this.profile_zip_path, profile_folder);
422
- await this.extractProfile(profilePath, this.profile_zip_path);
411
+ await this.createZeroProfile(this.createCookiesTableQuery);
423
412
  }
424
413
 
425
414
  const singletonLockPath = join(profilePath, 'SingletonLock');
@@ -1094,6 +1083,7 @@ export class GoLogin {
1094
1083
  debug('browser killed');
1095
1084
  } catch (error) {
1096
1085
  console.error(error);
1086
+ captureGroupedSentryError(error, { method: 'killBrowser', profileId: this.profile_id });
1097
1087
  }
1098
1088
  }
1099
1089
 
@@ -1371,18 +1361,20 @@ export class GoLogin {
1371
1361
  }
1372
1362
  } catch (error) {
1373
1363
  if (!isSecondTry && (error.message.includes('table cookies has no column') || error.message.includes('NOT NULL constraint failed'))) {
1374
- await _promises.rm(cookiesPaths.primary, { recursive: true, force: true });
1364
+ await db.close();
1365
+ await _promises.rm(cookiesPaths.primary, { recursive: true, force: true }).catch(() => null);
1375
1366
  await createDBFile({
1376
1367
  cookiesFilePath: cookiesPaths.primary,
1377
1368
  cookiesFileSecondPath: cookiesPaths.secondary,
1378
1369
  createCookiesTableQuery: this.createCookiesTableQuery,
1379
- });
1380
- await this.writeCookiesToFile(cookies, true);
1370
+ }).catch(console.error);
1371
+ await this.writeCookiesToFile(cookies, true).catch(console.error);
1381
1372
 
1382
1373
  return;
1383
1374
  }
1384
1375
 
1385
1376
  console.error(error.message);
1377
+ captureGroupedSentryError(error, { method: 'writeCookiesToFile', profileId: this.profile_id });
1386
1378
  } finally {
1387
1379
  db && await db.close();
1388
1380
  await ensureDirectoryExists(cookiesPaths.primary);
@@ -1407,7 +1399,7 @@ export class GoLogin {
1407
1399
 
1408
1400
  return { status: 'success', wsUrl: startResponse.wsUrl, resolution: startResponse.resolution };
1409
1401
  } catch (error) {
1410
- Sentry.captureException(error);
1402
+ captureGroupedSentryError(error, { method: 'start', profileId: this.profile_id });
1411
1403
  throw error;
1412
1404
  }
1413
1405
  }
package/src/utils/http.js CHANGED
@@ -46,7 +46,9 @@ export const makeRequest = async (url, options, internalOptions) => {
46
46
  return await attemptRequest(url, options);
47
47
  } catch (error) {
48
48
  if (internalOptions?.fallbackUrl && !error.statusCode) {
49
- return attemptRequest(internalOptions.fallbackUrl, options);
49
+ const fallbackData = await attemptRequest(internalOptions.fallbackUrl, options);
50
+
51
+ return fallbackData;
50
52
  }
51
53
 
52
54
  throw error;
@@ -0,0 +1,73 @@
1
+ import * as Sentry from '@sentry/node';
2
+
3
+ export const captureGroupedSentryError = (error, context = {}) => {
4
+ if (process.env.DISABLE_TELEMETRY === 'true') {
5
+ return;
6
+ }
7
+
8
+ const errorMessage = error?.message || 'Unknown error';
9
+ let fingerprint = ['default'];
10
+ let tags = { errorType: 'unknown' };
11
+
12
+ switch (true) {
13
+ case errorMessage.includes('Profile deleted or not found'):
14
+ fingerprint = ['profile-not-found'];
15
+ tags = { errorType: 'profile', category: 'configuration' };
16
+ break;
17
+
18
+ case errorMessage.includes('Request timeout after 13000ms'):
19
+ case errorMessage.includes('Proxy Error'):
20
+ fingerprint = ['proxy-error'];
21
+ tags = { errorType: 'proxy', category: 'configuration' };
22
+ break;
23
+
24
+ case errorMessage.includes('ENOSPC'):
25
+ case errorMessage.includes('database or disk is full'):
26
+ fingerprint = ['out-of-space'];
27
+ tags = { errorType: 'out-of-space', category: 'filesystem' };
28
+ break;
29
+
30
+ case errorMessage.includes('ECONNREFUSED 127.0.0.1:'):
31
+ fingerprint = ['browser-not-found'];
32
+ tags = { errorType: 'browser', category: 'configuration' };
33
+ break;
34
+
35
+ case errorMessage.includes('end of central directory record signature not found'):
36
+ case errorMessage.includes('invalid code lengths set'):
37
+ case errorMessage.includes('Command failed: tar xzf'):
38
+ fingerprint = ['archive-error'];
39
+ tags = { errorType: 'archive', category: 'binaries' };
40
+ break;
41
+
42
+ case errorMessage.includes('spawn UNKNOWN'):
43
+ fingerprint = ['spawn-error'];
44
+ tags = { errorType: 'spawn', category: 'runtime' };
45
+ break;
46
+
47
+ case errorMessage.includes('unable to verify the first certificate'):
48
+ case errorMessage.includes('write EPROTO'):
49
+ fingerprint = ['ssl-error'];
50
+ tags = { errorType: 'ssl', category: 'network' };
51
+ break;
52
+
53
+ case errorMessage.includes('You have reached your free API requests limit'):
54
+ fingerprint = ['api-limit-reached'];
55
+ tags = { errorType: 'api', category: 'rate-limit' };
56
+ break;
57
+
58
+ default:
59
+ fingerprint = ['uncategorized', errorMessage.substring(0, 50)];
60
+ tags = { errorType: 'uncategorized', category: 'unknown' };
61
+ break;
62
+ }
63
+
64
+ Sentry.withScope((scope) => {
65
+ scope.setFingerprint(fingerprint);
66
+ scope.setTags(tags);
67
+ scope.setContext('errorDetails', {
68
+ originalMessage: errorMessage,
69
+ ...context,
70
+ });
71
+ Sentry.captureException(error);
72
+ });
73
+ };