gologin 3.0.1 → 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,23 @@
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
+
16
+ ## [3.0.2] 2026-07-29
17
+
18
+ ### Features
19
+
20
+ * Added `customChromeFrame` option to control writing `browser.custom_chrome_frame: false` into profile Preferences
21
+
5
22
  ## [3.0.1] 2026-07-28
6
23
 
7
24
  ### Fixes
@@ -2,8 +2,8 @@
2
2
  // It will handle the browser start and close management - you just need to control the browser with puppeteer
3
3
  import puppeteer from 'puppeteer-core';
4
4
 
5
- const token = process.env.GL_API_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqd3RpZCI6IjZhNjM4NDk5YWFlYTkyY2Q4YzE3NjcwNyIsInR5cGUiOiJ1c2VyIiwic3ViIjoiNjkyNjI0YTMzMTQ1YzRjMGU5MDBmNmNkIn0.vA0bWpR47ozYcNFl4DMLxqjg1veYS2gNAYpVJKMtYQ4';
6
- const profileId = '6a294f7a398a99123f1d2a4a';
5
+ const token = process.env.GL_API_TOKEN || 'your dev token here';
6
+ const profileId = 'profile ID';
7
7
 
8
8
  const CLOUD_BROWSER_URL = `https://cloudbrowser.gologin.com/connect?token=${token}&profile=${profileId}`;
9
9
  const STOP_PROFILE_URL = `https://api.gologin.com/browser/${profileId}/web`;
@@ -31,7 +31,9 @@ async function main() {
31
31
  const page = await browser.newPage();
32
32
 
33
33
  await page.goto('https://iphey.com/', { waitUntil: 'networkidle2' });
34
- await page.screenshot({ path: 'screenshot.png' });
34
+ const status = await page.$eval('.trustworthy:not(.hide)',
35
+ (elt) => elt?.innerText?.trim(),
36
+ );
35
37
 
36
38
  return status;
37
39
  }
package/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export type GoLoginOptions = {
20
20
  proxyCheckAttempts?: number;
21
21
  autoUpdateBrowser?: boolean;
22
22
  checkBrowserUpdate?: boolean;
23
+ customChromeFrame?: boolean;
23
24
  };
24
25
 
25
26
  export type GoLoginStartResult = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gologin",
3
- "version": "3.0.1",
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';
@@ -78,6 +87,7 @@ export class GoLogin {
78
87
  this.newProxyOrbitaMajorVersion = 135;
79
88
  this.proxyCheckTimeout = options.proxyCheckTimeout || 13 * 1000;
80
89
  this.proxyCheckAttempts = options.proxyCheckAttempts || 3;
90
+ this.customChromeFrame = options.customChromeFrame;
81
91
 
82
92
  if (options.tmpdir) {
83
93
  this.tmpdir = options.tmpdir;
@@ -462,13 +472,17 @@ export class GoLogin {
462
472
  }
463
473
 
464
474
  async createStartup(local = false) {
475
+ const startupStart = Date.now();
465
476
  const profilePath = join(this.tmpdir, `gologin_profile_${this.profile_id}`);
466
477
  const startupDepsPromise = preloadStartupDeps();
467
478
 
468
479
  const [profile] = await Promise.all([
469
- this.getProfile(),
470
- startupDepsPromise.then(() => removePath(profilePath)
471
- .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
+ }),
472
486
  ]);
473
487
 
474
488
  if (!profile) {
@@ -524,37 +538,54 @@ export class GoLogin {
524
538
  this.proxy = proxy;
525
539
 
526
540
  await Promise.all([
527
- this.resolveProfileBrowserVersion(profile),
528
- this.initProfileStorage({ profile, local }),
529
- this.getTimeZone(proxy).catch((e) => {
530
- console.error('Proxy Error. Check it and try again.');
531
- 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
+ }),
532
549
  }),
533
- this.initCookiesFile(profile),
550
+ withTiming({ name: 'initCookiesFile', promise: this.initCookiesFile(profile) }),
534
551
  ]);
535
552
 
536
- await _promises.rm(join(profilePath, 'Default', 'Sync Data'), { recursive: true }).catch(() => null);
537
- const pref_file_name = join(profilePath, 'Default', 'Preferences');
538
- debug('reading', pref_file_name);
539
-
540
- const prefFileExists = await access(pref_file_name).then(() => true).catch(() => false);
541
- if (!prefFileExists) {
542
- debug('Preferences file not exists waiting', pref_file_name, '. Using empty profile');
543
- await mkdir(join(profilePath, 'Default'), { recursive: true });
544
- await writeFile(pref_file_name, '{}');
545
- }
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
+ });
546
568
 
547
- 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
+ });
548
573
  const preferences = JSON.parse(preferences_raw.toString());
549
574
  const chromeExtensions = get(profile, 'chromeExtensions') || [];
550
575
  const userChromeExtensions = get(profile, 'userChromeExtensions') || [];
551
- const allExtensions = [...chromeExtensions, ...userChromeExtensions];
576
+ const allExtensions = [...new Set([...chromeExtensions, ...userChromeExtensions])];
552
577
 
553
578
  const [, orbitaParamsToken] = await Promise.all([
554
- this.setupProfileExtensions({
555
- 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
+ }),
556
587
  }),
557
- this.fetchOrbitaParamsToken(profile),
588
+ withTiming({ name: 'fetchOrbitaParamsToken', promise: this.fetchOrbitaParamsToken(profile) }),
558
589
  ]);
559
590
 
560
591
  if (this.fontsMasking) {
@@ -581,6 +612,13 @@ export class GoLogin {
581
612
 
582
613
  this.browserLang = isMAC ? 'en-US' : checkAutoLangResult;
583
614
  const prefsToWrite = Object.assign(preferences, { gologin });
615
+ if (this.customChromeFrame === false) {
616
+ prefsToWrite.browser = {
617
+ ...(prefsToWrite.browser || {}),
618
+ custom_chrome_frame: false,
619
+ };
620
+ }
621
+
584
622
  if (this.browserMajorVersion >= this.newProxyOrbitaMajorVersion && this.proxy?.mode !== 'none') {
585
623
  prefsToWrite.proxy = {
586
624
  mode: 'fixed_servers',
@@ -598,14 +636,21 @@ export class GoLogin {
598
636
  },
599
637
  };
600
638
 
601
- await writeFile(join(profilePath, 'orbita.config'), JSON.stringify(orbitaConfig, null, '\t'), { encoding: 'utf-8' }).catch(console.log);
602
- 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
+ });
603
651
 
604
- const bookmarksParsedData = await getCurrentProfileBookmarks(this.bookmarksFilePath);
605
- const bookmarksFromDb = profile.bookmarks?.bookmark_bar;
606
- bookmarksParsedData.roots = bookmarksFromDb ? profile.bookmarks : bookmarksParsedData.roots;
607
- await writeFile(this.bookmarksFilePath, JSON.stringify(bookmarksParsedData));
608
652
  debug('Profile ready. Path: ', profilePath, 'PROXY', JSON.stringify(get(preferences, 'gologin.proxy')));
653
+ debug(`[timing] createStartup total: ${Date.now() - startupStart}ms`);
609
654
 
610
655
  return profilePath;
611
656
  }
@@ -715,8 +760,9 @@ export class GoLogin {
715
760
 
716
761
  const profileExtensionPathRes = extensionsResult.find(el => 'profileExtensionsCheckRes' in el) || {};
717
762
  const profileUserExtensionPathRes = extensionsResult.find(el => 'profileUserExtensionsCheckRes' in el);
718
- profileExtensionsCheckRes =
719
- (profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []);
763
+ profileExtensionsCheckRes = [...new Set(
764
+ (profileExtensionPathRes?.profileExtensionsCheckRes || []).concat(profileUserExtensionPathRes?.profileUserExtensionsCheckRes || []),
765
+ )];
720
766
  }
721
767
 
722
768
  let extSettings;
@@ -970,9 +1016,13 @@ export class GoLogin {
970
1016
  }
971
1017
 
972
1018
  async spawnBrowser() {
1019
+ const spawnStart = Date.now();
973
1020
  let { remote_debugging_port, customArgs } = this;
974
1021
  if (!remote_debugging_port) {
975
- remote_debugging_port = await this.getRandomPort();
1022
+ remote_debugging_port = await withTiming({
1023
+ name: 'getRandomPort',
1024
+ promise: this.getRandomPort(),
1025
+ });
976
1026
  }
977
1027
 
978
1028
  const profile_path = this.profilePath();
@@ -988,7 +1038,10 @@ export class GoLogin {
988
1038
 
989
1039
  let ORBITA_BROWSER = this.executablePath;
990
1040
  if (!ORBITA_BROWSER) {
991
- const browserChecker = await this.getBrowserChecker();
1041
+ const browserChecker = await withTiming({
1042
+ name: 'getBrowserChecker',
1043
+ promise: this.getBrowserChecker(),
1044
+ });
992
1045
  ORBITA_BROWSER = browserChecker.getOrbitaPath;
993
1046
  }
994
1047
 
@@ -999,9 +1052,12 @@ export class GoLogin {
999
1052
  env[key] = process.env[key];
1000
1053
  });
1001
1054
 
1002
- const tz = await this.getTimeZone(this.proxy).catch((e) => {
1003
- console.error('Proxy Error. Check it and try again.');
1004
- 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
+ }),
1005
1061
  });
1006
1062
 
1007
1063
  env.TZ = tz;
@@ -1082,17 +1138,23 @@ export class GoLogin {
1082
1138
 
1083
1139
  if (this.waitWebsocket) {
1084
1140
  debug('GETTING WS URL FROM BROWSER');
1085
- const data = await makeRequest(
1086
- `http://127.0.0.1:${remote_debugging_port}/json/version`,
1087
- { json: true, maxAttempts: 60, retryDelay: 100, method: 'GET' },
1088
- );
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
+ });
1089
1148
 
1090
1149
  debug('WS IS', get(data, 'webSocketDebuggerUrl', ''));
1091
1150
  this.is_active = true;
1151
+ debug(`[timing] spawnBrowser total: ${Date.now() - spawnStart}ms`);
1092
1152
 
1093
1153
  return { wsUrl: get(data, 'webSocketDebuggerUrl', ''), resolution: this.resolution };
1094
1154
  }
1095
1155
 
1156
+ debug(`[timing] spawnBrowser total: ${Date.now() - spawnStart}ms`);
1157
+
1096
1158
  return '';
1097
1159
  }
1098
1160
 
@@ -1500,10 +1562,12 @@ export class GoLogin {
1500
1562
 
1501
1563
  async start() {
1502
1564
  // ensureSentryInitialized({ release: process.env.npm_package_version || '2.1.34' });
1565
+ const startTotal = Date.now();
1503
1566
 
1504
- await this.createStartup();
1505
- const startResponse = await this.spawnBrowser();
1567
+ await withTiming({ name: 'createStartup', promise: this.createStartup() });
1568
+ const startResponse = await withTiming({ name: 'spawnBrowser', promise: this.spawnBrowser() });
1506
1569
  this.setActive(true);
1570
+ debug(`[timing] start total: ${Date.now() - startTotal}ms`);
1507
1571
 
1508
1572
  return {
1509
1573
  status: 'success',
@@ -1513,11 +1577,14 @@ export class GoLogin {
1513
1577
  }
1514
1578
 
1515
1579
  async startLocal() {
1516
- await this.createStartup(true);
1580
+ const startTotal = Date.now();
1581
+
1582
+ await withTiming({ name: 'createStartup', promise: this.createStartup(true) });
1517
1583
 
1518
1584
  // await this.createBrowserExtension();
1519
- const startResponse = await this.spawnBrowser();
1585
+ const startResponse = await withTiming({ name: 'spawnBrowser', promise: this.spawnBrowser() });
1520
1586
  this.setActive(true);
1587
+ debug(`[timing] startLocal total: ${Date.now() - startTotal}ms`);
1521
1588
 
1522
1589
  return { status: 'success', wsUrl: startResponse.wsUrl };
1523
1590
  }
package/screenshot.png DELETED
Binary file