gologin 3.0.2 → 3.0.3

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,17 @@
2
2
 
3
3
  Combined changelog for GoLogin node.js SDK
4
4
 
5
+ ## [3.0.3] 2026-08-03
6
+
7
+ ### Fixes
8
+
9
+ * Fixed Chrome extensions download race when the same extension IDs are listed in both `chromeExtensions` and `userChromeExtensions`
10
+ * Made extension archive deletion ignore missing files (`ENOENT`) during parallel extract cleanup
11
+
12
+ ### Features
13
+
14
+ * Added profile start step timings via `debug` (`DEBUG=gologin`)
15
+
5
16
  ## [3.0.2] 2026-07-29
6
17
 
7
18
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A high-level API to control Orbita browser over GoLogin API",
5
5
  "types": "./index.d.ts",
6
6
  "main": "./src/index.js",
@@ -29,11 +29,13 @@ export const deleteExtensionArchive = (dest) => {
29
29
  throw new Error('Missing parameter');
30
30
  }
31
31
 
32
- return access(dest)
33
- .then(
34
- () => unlink(dest),
35
- () => Promise.resolve(),
36
- );
32
+ return unlink(dest).catch((error) => {
33
+ if (error?.code === 'ENOENT') {
34
+ return;
35
+ }
36
+
37
+ throw error;
38
+ });
37
39
  };
38
40
 
39
41
  const withRetry = optionsOrUndefined => {
@@ -107,11 +107,13 @@ export class ExtensionsManager extends UserExtensionsManager {
107
107
 
108
108
  return originalId;
109
109
  });
110
+ const queuedOriginalIds = new Set();
110
111
 
111
112
  return profileExtensions.reduce((res, val) => {
112
113
  const [originalId] = val.split('@');
113
- const extensionExists = existedOriginalIds.includes(originalId);
114
+ const extensionExists = existedOriginalIds.includes(originalId) || queuedOriginalIds.has(originalId);
114
115
  if (!extensionExists) {
116
+ queuedOriginalIds.add(originalId);
115
117
  res.push(val);
116
118
  }
117
119
 
package/src/gologin.js CHANGED
@@ -40,6 +40,15 @@ const PROXY_NONE = 'none';
40
40
  const debug = debugDefault('gologin');
41
41
  const delay = (time) => new Promise((resolve) => setTimeout(resolve, time));
42
42
 
43
+ const withTiming = async ({ name, promise }) => {
44
+ const start = Date.now();
45
+ try {
46
+ return await promise;
47
+ } finally {
48
+ debug(`[timing] ${name}: ${Date.now() - start}ms`);
49
+ }
50
+ };
51
+
43
52
  export class GoLogin {
44
53
  constructor(options = {}) {
45
54
  this.browserLang = 'en-US';
@@ -463,13 +472,17 @@ export class GoLogin {
463
472
  }
464
473
 
465
474
  async createStartup(local = false) {
475
+ const startupStart = Date.now();
466
476
  const profilePath = join(this.tmpdir, `gologin_profile_${this.profile_id}`);
467
477
  const startupDepsPromise = preloadStartupDeps();
468
478
 
469
479
  const [profile] = await Promise.all([
470
- this.getProfile(),
471
- startupDepsPromise.then(() => removePath(profilePath)
472
- .then(() => debug('-', profilePath, 'dropped'))),
480
+ withTiming({ name: 'getProfile', promise: this.getProfile() }),
481
+ withTiming({
482
+ name: 'preloadDepsAndClearProfile',
483
+ promise: startupDepsPromise.then(() => removePath(profilePath)
484
+ .then(() => debug('-', profilePath, 'dropped'))),
485
+ }),
473
486
  ]);
474
487
 
475
488
  if (!profile) {
@@ -525,37 +538,54 @@ export class GoLogin {
525
538
  this.proxy = proxy;
526
539
 
527
540
  await Promise.all([
528
- this.resolveProfileBrowserVersion(profile),
529
- this.initProfileStorage({ profile, local }),
530
- this.getTimeZone(proxy).catch((e) => {
531
- console.error('Proxy Error. Check it and try again.');
532
- throw new Error(`Proxy Error. ${e.message}`);
541
+ withTiming({ name: 'resolveProfileBrowserVersion', promise: this.resolveProfileBrowserVersion(profile) }),
542
+ withTiming({ name: 'initProfileStorage', promise: this.initProfileStorage({ profile, local }) }),
543
+ withTiming({
544
+ name: 'getTimeZone',
545
+ promise: this.getTimeZone(proxy).catch((e) => {
546
+ console.error('Proxy Error. Check it and try again.');
547
+ throw new Error(`Proxy Error. ${e.message}`);
548
+ }),
533
549
  }),
534
- this.initCookiesFile(profile),
550
+ withTiming({ name: 'initCookiesFile', promise: this.initCookiesFile(profile) }),
535
551
  ]);
536
552
 
537
- await _promises.rm(join(profilePath, 'Default', 'Sync Data'), { recursive: true }).catch(() => null);
538
- const pref_file_name = join(profilePath, 'Default', 'Preferences');
539
- debug('reading', pref_file_name);
540
-
541
- const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
542
- if (!prefFileExists) {
543
- debug('Preferences file not exists waiting', pref_file_name, '. Using empty profile');
544
- await mkdir(join(profilePath, 'Default'), { recursive: true });
545
- await writeFile(pref_file_name, '{}');
546
- }
553
+ await withTiming({
554
+ name: 'preparePreferencesFile',
555
+ promise: (async () => {
556
+ await _promises.rm(join(profilePath, 'Default', 'Sync Data'), { recursive: true }).catch(() => null);
557
+ const pref_file_name = join(profilePath, 'Default', 'Preferences');
558
+ debug('reading', pref_file_name);
559
+
560
+ const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
561
+ if (!prefFileExists) {
562
+ debug('Preferences file not exists waiting', pref_file_name, '. Using empty profile');
563
+ await mkdir(join(profilePath, 'Default'), { recursive: true });
564
+ await writeFile(pref_file_name, '{}');
565
+ }
566
+ })(),
567
+ });
547
568
 
548
- const preferences_raw = await readFile(pref_file_name);
569
+ const preferences_raw = await withTiming({
570
+ name: 'readPreferences',
571
+ promise: readFile(join(profilePath, 'Default', 'Preferences')),
572
+ });
549
573
  const preferences = JSON.parse(preferences_raw.toString());
550
574
  const chromeExtensions = get(profile, 'chromeExtensions') || [];
551
575
  const userChromeExtensions = get(profile, 'userChromeExtensions') || [];
552
- const allExtensions = [...chromeExtensions, ...userChromeExtensions];
576
+ const allExtensions = [...new Set([...chromeExtensions, ...userChromeExtensions])];
553
577
 
554
578
  const [, orbitaParamsToken] = await Promise.all([
555
- this.setupProfileExtensions({
556
- preferences, allExtensions, userChromeExtensions, profilePath,
579
+ withTiming({
580
+ name: 'setupProfileExtensions',
581
+ promise: this.setupProfileExtensions({
582
+ preferences,
583
+ allExtensions,
584
+ userChromeExtensions: [...new Set(userChromeExtensions)],
585
+ profilePath,
586
+ }),
557
587
  }),
558
- this.fetchOrbitaParamsToken(profile),
588
+ withTiming({ name: 'fetchOrbitaParamsToken', promise: this.fetchOrbitaParamsToken(profile) }),
559
589
  ]);
560
590
 
561
591
  if (this.fontsMasking) {
@@ -606,14 +636,21 @@ export class GoLogin {
606
636
  },
607
637
  };
608
638
 
609
- await writeFile(join(profilePath, 'orbita.config'), JSON.stringify(orbitaConfig, null, '\t'), { encoding: 'utf-8' }).catch(console.log);
610
- await writeFile(join(profilePath, 'Default', 'Preferences'), JSON.stringify(prefsToWrite));
639
+ await withTiming({
640
+ name: 'writeProfileFiles',
641
+ promise: (async () => {
642
+ await writeFile(join(profilePath, 'orbita.config'), JSON.stringify(orbitaConfig, null, '\t'), { encoding: 'utf-8' }).catch(console.log);
643
+ await writeFile(join(profilePath, 'Default', 'Preferences'), JSON.stringify(prefsToWrite));
644
+
645
+ const bookmarksParsedData = await getCurrentProfileBookmarks(this.bookmarksFilePath);
646
+ const bookmarksFromDb = profile.bookmarks?.bookmark_bar;
647
+ bookmarksParsedData.roots = bookmarksFromDb ? profile.bookmarks : bookmarksParsedData.roots;
648
+ await writeFile(this.bookmarksFilePath, JSON.stringify(bookmarksParsedData));
649
+ })(),
650
+ });
611
651
 
612
- const bookmarksParsedData = await getCurrentProfileBookmarks(this.bookmarksFilePath);
613
- const bookmarksFromDb = profile.bookmarks?.bookmark_bar;
614
- bookmarksParsedData.roots = bookmarksFromDb ? profile.bookmarks : bookmarksParsedData.roots;
615
- await writeFile(this.bookmarksFilePath, JSON.stringify(bookmarksParsedData));
616
652
  debug('Profile ready. Path: ', profilePath, 'PROXY', JSON.stringify(get(preferences, 'gologin.proxy')));
653
+ debug(`[timing] createStartup total: ${Date.now() - startupStart}ms`);
617
654
 
618
655
  return profilePath;
619
656
  }
@@ -723,8 +760,9 @@ export class GoLogin {
723
760
 
724
761
  const profileExtensionPathRes = extensionsResult.find(el => 'profileExtensionsCheckRes' in el) || {};
725
762
  const profileUserExtensionPathRes = extensionsResult.find(el => 'profileUserExtensionsCheckRes' in el);
726
- profileExtensionsCheckRes =
727
- (profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []);
763
+ profileExtensionsCheckRes = [...new Set(
764
+ (profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []),
765
+ )];
728
766
  }
729
767
 
730
768
  let extSettings;
@@ -978,9 +1016,13 @@ export class GoLogin {
978
1016
  }
979
1017
 
980
1018
  async spawnBrowser() {
1019
+ const spawnStart = Date.now();
981
1020
  let { remote_debugging_port, customArgs } = this;
982
1021
  if (!remote_debugging_port) {
983
- remote_debugging_port = await this.getRandomPort();
1022
+ remote_debugging_port = await withTiming({
1023
+ name: 'getRandomPort',
1024
+ promise: this.getRandomPort(),
1025
+ });
984
1026
  }
985
1027
 
986
1028
  const profile_path = this.profilePath();
@@ -996,7 +1038,10 @@ export class GoLogin {
996
1038
 
997
1039
  let ORBITA_BROWSER = this.executablePath;
998
1040
  if (!ORBITA_BROWSER) {
999
- const browserChecker = await this.getBrowserChecker();
1041
+ const browserChecker = await withTiming({
1042
+ name: 'getBrowserChecker',
1043
+ promise: this.getBrowserChecker(),
1044
+ });
1000
1045
  ORBITA_BROWSER = browserChecker.getOrbitaPath;
1001
1046
  }
1002
1047
 
@@ -1007,9 +1052,12 @@ export class GoLogin {
1007
1052
  env[key] = process.env[key];
1008
1053
  });
1009
1054
 
1010
- const tz = await this.getTimeZone(this.proxy).catch((e) => {
1011
- console.error('Proxy Error. Check it and try again.');
1012
- throw e;
1055
+ const tz = await withTiming({
1056
+ name: 'getTimeZone',
1057
+ promise: this.getTimeZone(this.proxy).catch((e) => {
1058
+ console.error('Proxy Error. Check it and try again.');
1059
+ throw e;
1060
+ }),
1013
1061
  });
1014
1062
 
1015
1063
  env.TZ = tz;
@@ -1090,17 +1138,23 @@ export class GoLogin {
1090
1138
 
1091
1139
  if (this.waitWebsocket) {
1092
1140
  debug('GETTING WS URL FROM BROWSER');
1093
- const data = await makeRequest(
1094
- `http://127.0.0.1:${remote_debugging_port}/json/version`,
1095
- { json: true, maxAttempts: 60, retryDelay: 100, method: 'GET' },
1096
- );
1141
+ const data = await withTiming({
1142
+ name: 'waitWebsocket',
1143
+ promise: makeRequest(
1144
+ `http://127.0.0.1:${remote_debugging_port}/json/version`,
1145
+ { json: true, maxAttempts: 120, retryDelay: 100, method: 'GET' },
1146
+ ),
1147
+ });
1097
1148
 
1098
1149
  debug('WS IS', get(data, 'webSocketDebuggerUrl', ''));
1099
1150
  this.is_active = true;
1151
+ debug(`[timing] spawnBrowser total: ${Date.now() - spawnStart}ms`);
1100
1152
 
1101
1153
  return { wsUrl: get(data, 'webSocketDebuggerUrl', ''), resolution: this.resolution };
1102
1154
  }
1103
1155
 
1156
+ debug(`[timing] spawnBrowser total: ${Date.now() - spawnStart}ms`);
1157
+
1104
1158
  return '';
1105
1159
  }
1106
1160
 
@@ -1508,10 +1562,12 @@ export class GoLogin {
1508
1562
 
1509
1563
  async start() {
1510
1564
  // ensureSentryInitialized({ release: process.env.npm_package_version || '2.1.34' });
1565
+ const startTotal = Date.now();
1511
1566
 
1512
- await this.createStartup();
1513
- const startResponse = await this.spawnBrowser();
1567
+ await withTiming({ name: 'createStartup', promise: this.createStartup() });
1568
+ const startResponse = await withTiming({ name: 'spawnBrowser', promise: this.spawnBrowser() });
1514
1569
  this.setActive(true);
1570
+ debug(`[timing] start total: ${Date.now() - startTotal}ms`);
1515
1571
 
1516
1572
  return {
1517
1573
  status: 'success',
@@ -1521,11 +1577,14 @@ export class GoLogin {
1521
1577
  }
1522
1578
 
1523
1579
  async startLocal() {
1524
- await this.createStartup(true);
1580
+ const startTotal = Date.now();
1581
+
1582
+ await withTiming({ name: 'createStartup', promise: this.createStartup(true) });
1525
1583
 
1526
1584
  // await this.createBrowserExtension();
1527
- const startResponse = await this.spawnBrowser();
1585
+ const startResponse = await withTiming({ name: 'spawnBrowser', promise: this.spawnBrowser() });
1528
1586
  this.setActive(true);
1587
+ debug(`[timing] startLocal total: ${Date.now() - startTotal}ms`);
1529
1588
 
1530
1589
  return { status: 'success', wsUrl: startResponse.wsUrl };
1531
1590
  }