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 +11 -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 +104 -45
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
|
@@ -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';
|
|
@@ -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
|
-
|
|
472
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
|
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
|
-
|
|
556
|
-
|
|
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
|
|
610
|
-
|
|
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
|
|
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
|
|
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
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
-
|
|
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
|
}
|