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 +17 -0
- package/examples/puppeter/cloud-browser.js +5 -3
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/extensions/extensions-extractor.js +7 -5
- package/src/extensions/extensions-manager.js +3 -1
- package/src/gologin.js +112 -45
- package/screenshot.png +0 -0
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 || '
|
|
6
|
-
const profileId = '
|
|
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
|
|
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
package/package.json
CHANGED
|
@@ -29,11 +29,13 @@ export const deleteExtensionArchive = (dest) => {
|
|
|
29
29
|
throw new Error('Missing parameter');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
471
|
-
|
|
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
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
|
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
|
-
|
|
555
|
-
|
|
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
|
|
602
|
-
|
|
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
|
|
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
|
|
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
|
|
1003
|
-
|
|
1004
|
-
|
|
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
|
|
1086
|
-
|
|
1087
|
-
|
|
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
|
-
|
|
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
|