jumpy-lion 0.1.6-beta.7 → 1.0.0-beta.0
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/README.md +1 -607
- package/browser/fonts/macos-bundle/40-macos-aliases.conf +515 -0
- package/browser/fonts/macos-bundle/README.md +24 -0
- package/browser/fonts/macos-bundle/install.sh +83 -0
- package/dist/browser-controller.d.ts +47 -4
- package/dist/browser-controller.d.ts.map +1 -1
- package/dist/browser-controller.js +219 -278
- package/dist/browser-controller.js.map +1 -1
- package/dist/browser-plugin.d.ts +82 -110
- package/dist/browser-plugin.d.ts.map +1 -1
- package/dist/browser-plugin.js +376 -379
- package/dist/browser-plugin.js.map +1 -1
- package/dist/browser-process/align-fingerprint-config.d.ts +36 -0
- package/dist/browser-process/align-fingerprint-config.d.ts.map +1 -0
- package/dist/browser-process/align-fingerprint-config.js +74 -0
- package/dist/browser-process/align-fingerprint-config.js.map +1 -0
- package/dist/browser-process/anti-detect-browser.d.ts +2 -2
- package/dist/browser-process/anti-detect-browser.js +7 -7
- package/dist/browser-process/anti-detect-config.d.ts +131 -2
- package/dist/browser-process/anti-detect-config.d.ts.map +1 -1
- package/dist/browser-process/anti-detect-config.js +337 -127
- package/dist/browser-process/anti-detect-config.js.map +1 -1
- package/dist/browser-process/browser.d.ts +25 -92
- package/dist/browser-process/browser.d.ts.map +1 -1
- package/dist/browser-process/browser.js +429 -73
- package/dist/browser-process/browser.js.map +1 -1
- package/dist/browser-process/fingerprint-config.d.ts +146 -0
- package/dist/browser-process/fingerprint-config.d.ts.map +1 -0
- package/dist/browser-process/fingerprint-config.js +308 -0
- package/dist/browser-process/fingerprint-config.js.map +1 -0
- package/dist/browser-process/get-chrome-executable.d.ts +6 -0
- package/dist/browser-process/get-chrome-executable.d.ts.map +1 -1
- package/dist/browser-process/get-chrome-executable.js +79 -4
- package/dist/browser-process/get-chrome-executable.js.map +1 -1
- package/dist/browser-process/gpu-family-profiles.d.ts +58 -0
- package/dist/browser-process/gpu-family-profiles.d.ts.map +1 -0
- package/dist/browser-process/gpu-family-profiles.js +480 -0
- package/dist/browser-process/gpu-family-profiles.js.map +1 -0
- package/dist/browser-process/launch-options.d.ts +301 -0
- package/dist/browser-process/launch-options.d.ts.map +1 -0
- package/dist/browser-process/launch-options.js +2 -0
- package/dist/browser-process/launch-options.js.map +1 -0
- package/dist/browser-process/port-allocator.d.ts +64 -0
- package/dist/browser-process/port-allocator.d.ts.map +1 -0
- package/dist/browser-process/port-allocator.js +198 -0
- package/dist/browser-process/port-allocator.js.map +1 -0
- package/dist/browser-process/process.d.ts +85 -0
- package/dist/browser-process/process.d.ts.map +1 -1
- package/dist/browser-process/process.js +235 -19
- package/dist/browser-process/process.js.map +1 -1
- package/dist/browser-process/signal-cleanup.d.ts +40 -0
- package/dist/browser-process/signal-cleanup.d.ts.map +1 -0
- package/dist/browser-process/signal-cleanup.js +105 -0
- package/dist/browser-process/signal-cleanup.js.map +1 -0
- package/dist/browser-process/widevine-cdm.d.ts +144 -0
- package/dist/browser-process/widevine-cdm.d.ts.map +1 -0
- package/dist/browser-process/widevine-cdm.js +298 -0
- package/dist/browser-process/widevine-cdm.js.map +1 -0
- package/dist/browser-profiles/chrome/populate-profile.d.ts.map +1 -1
- package/dist/browser-profiles/chrome/populate-profile.js +21 -22
- package/dist/browser-profiles/chrome/populate-profile.js.map +1 -1
- package/dist/connection/cdp-reconnection.d.ts +50 -0
- package/dist/connection/cdp-reconnection.d.ts.map +1 -0
- package/dist/connection/cdp-reconnection.js +75 -0
- package/dist/connection/cdp-reconnection.js.map +1 -0
- package/dist/connection/page-session.d.ts +114 -0
- package/dist/connection/page-session.d.ts.map +1 -0
- package/dist/connection/page-session.js +237 -0
- package/dist/connection/page-session.js.map +1 -0
- package/dist/connection/session-client.d.ts +34 -0
- package/dist/connection/session-client.d.ts.map +1 -0
- package/dist/connection/session-client.js +129 -0
- package/dist/connection/session-client.js.map +1 -0
- package/dist/crawler.d.ts +9 -88
- package/dist/crawler.d.ts.map +1 -1
- package/dist/crawler.js +157 -8
- package/dist/crawler.js.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.d.ts +24 -114
- package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +305 -611
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +16 -14
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +6 -2
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +6 -2
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts +18 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.js +136 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/override.d.ts +151 -0
- package/dist/fingerprinting/fingerprint-overrides/override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/override.js +14 -0
- package/dist/fingerprinting/fingerprint-overrides/override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +46 -15
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/registry.d.ts +43 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.js +342 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts +16 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.js +175 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +75 -9
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js +34 -20
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +25 -6
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +1 -1
- package/dist/fingerprinting/hardware-realism.d.ts +83 -0
- package/dist/fingerprinting/hardware-realism.d.ts.map +1 -0
- package/dist/fingerprinting/hardware-realism.js +156 -0
- package/dist/fingerprinting/hardware-realism.js.map +1 -0
- package/dist/fingerprinting/injection-planner.d.ts +133 -0
- package/dist/fingerprinting/injection-planner.d.ts.map +1 -0
- package/dist/fingerprinting/injection-planner.js +497 -0
- package/dist/fingerprinting/injection-planner.js.map +1 -0
- package/dist/fingerprinting/locale-resolver.d.ts +8 -1
- package/dist/fingerprinting/locale-resolver.d.ts.map +1 -1
- package/dist/fingerprinting/locale-resolver.js +26 -3
- package/dist/fingerprinting/locale-resolver.js.map +1 -1
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts +4 -4
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts.map +1 -1
- package/dist/fingerprinting/non-apify-fingerprint-generator.js +24 -58
- package/dist/fingerprinting/non-apify-fingerprint-generator.js.map +1 -1
- package/dist/fingerprinting/non-apify-profiles.json +10515 -8494
- package/dist/fingerprinting/os-consistency.d.ts +28 -0
- package/dist/fingerprinting/os-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/os-consistency.js +45 -0
- package/dist/fingerprinting/os-consistency.js.map +1 -0
- package/dist/fingerprinting/profile-quality.d.ts +25 -0
- package/dist/fingerprinting/profile-quality.d.ts.map +1 -0
- package/dist/fingerprinting/profile-quality.js +310 -0
- package/dist/fingerprinting/profile-quality.js.map +1 -0
- package/dist/fingerprinting/profile-selector.d.ts +102 -0
- package/dist/fingerprinting/profile-selector.d.ts.map +1 -0
- package/dist/fingerprinting/profile-selector.js +168 -0
- package/dist/fingerprinting/profile-selector.js.map +1 -0
- package/dist/fingerprinting/ua-alignment.d.ts +83 -0
- package/dist/fingerprinting/ua-alignment.d.ts.map +1 -0
- package/dist/fingerprinting/ua-alignment.js +197 -0
- package/dist/fingerprinting/ua-alignment.js.map +1 -0
- package/dist/fingerprinting/ua-ch-brands.d.ts +88 -0
- package/dist/fingerprinting/ua-ch-brands.d.ts.map +1 -0
- package/dist/fingerprinting/ua-ch-brands.js +174 -0
- package/dist/fingerprinting/ua-ch-brands.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/input/dropdown-selector.d.ts +90 -0
- package/dist/input/dropdown-selector.d.ts.map +1 -0
- package/dist/input/dropdown-selector.js +394 -0
- package/dist/input/dropdown-selector.js.map +1 -0
- package/dist/input/element-target.d.ts +152 -0
- package/dist/input/element-target.d.ts.map +1 -0
- package/dist/input/element-target.js +461 -0
- package/dist/input/element-target.js.map +1 -0
- package/dist/input/input-emulator.d.ts +85 -0
- package/dist/input/input-emulator.d.ts.map +1 -0
- package/dist/input/input-emulator.js +325 -0
- package/dist/input/input-emulator.js.map +1 -0
- package/dist/input/input-transport.d.ts +60 -0
- package/dist/input/input-transport.d.ts.map +1 -0
- package/dist/input/input-transport.js +28 -0
- package/dist/input/input-transport.js.map +1 -0
- package/dist/input/recording-transport.d.ts +32 -0
- package/dist/input/recording-transport.d.ts.map +1 -0
- package/dist/input/recording-transport.js +43 -0
- package/dist/input/recording-transport.js.map +1 -0
- package/dist/keyboard-layout.d.ts +2 -2
- package/dist/keyboard-layout.js +2 -2
- package/dist/navigation/page-navigation.d.ts +67 -0
- package/dist/navigation/page-navigation.d.ts.map +1 -0
- package/dist/navigation/page-navigation.js +122 -0
- package/dist/navigation/page-navigation.js.map +1 -0
- package/dist/network/network-watch.d.ts +110 -0
- package/dist/network/network-watch.d.ts.map +1 -0
- package/dist/network/network-watch.js +223 -0
- package/dist/network/network-watch.js.map +1 -0
- package/dist/page.d.ts +118 -104
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +311 -1130
- package/dist/page.js.map +1 -1
- package/dist/session/inject-hook.d.ts +24 -0
- package/dist/session/inject-hook.d.ts.map +1 -0
- package/dist/session/inject-hook.js +80 -0
- package/dist/session/inject-hook.js.map +1 -0
- package/dist/session/save.d.ts +21 -0
- package/dist/session/save.d.ts.map +1 -0
- package/dist/session/save.js +163 -0
- package/dist/session/save.js.map +1 -0
- package/dist/session/tar-data-dir.d.ts +18 -0
- package/dist/session/tar-data-dir.d.ts.map +1 -0
- package/dist/session/tar-data-dir.js +105 -0
- package/dist/session/tar-data-dir.js.map +1 -0
- package/dist/session/types.d.ts +80 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +29 -0
- package/dist/session/types.js.map +1 -0
- package/dist/session-profile.d.ts +102 -0
- package/dist/session-profile.d.ts.map +1 -0
- package/dist/session-profile.js +163 -0
- package/dist/session-profile.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +17 -6
- package/scripts/postinstall.cjs +58 -0
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts +0 -87
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts.map +0 -1
- package/dist/fingerprinting/custom-fingerprint-injector.js +0 -342
- package/dist/fingerprinting/custom-fingerprint-injector.js.map +0 -1
- package/dist/launcher-wrap.d.ts +0 -10
- package/dist/launcher-wrap.d.ts.map +0 -1
- package/dist/launcher-wrap.js +0 -11
- package/dist/launcher-wrap.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-controller.d.ts","sourceRoot":"","sources":["../src/browser-controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"browser-controller.d.ts","sourceRoot":"","sources":["../src/browser-controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAc,MAAM,SAAS,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAK3F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,OAAO,MAAM,WAAW,CAAC;AAGhC,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC;AAEpC,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,iBAAiB,CAC/D,WAAW,EAAE,6CAA6C;AAC1D,oBAAoB,EAAE,gBAAgB;AACtC,YAAY,EAAE,4BAA4B;AAC1C,EAAE,EAAE,0DAA0D;AAC9D,aAAa,CAChB;IACG,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;OAKG;IACH,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,WAAW,CAAC,CAAoE;IACxF,OAAO,CAAC,qBAAqB,CAAC,CAAyB;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAQ;IAErD;;;;;;OAMG;IACH,OAAO,KAAK,MAAM,GAEjB;IAEK,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAsBxB,eAAe;IACf,OAAO,CAAC,eAAe;IAcvB;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAiBjC;;;OAGG;YACW,iBAAiB;IAwC/B;;;OAGG;cACa,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA8CvC;;;OAGG;cACa,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBtC;;;OAGG;cACa,QAAQ,CAAC,UAAU,SAAI,GAAG,OAAO,CAAC,aAAa,CAAC;IAqEhE;;;;;;OAMG;YACW,yBAAyB;IAOvC;;;;;OAKG;YACW,qBAAqB;IAiBnC;;;;OAIG;YACW,oBAAoB;IAUlC;;;OAGG;cACa,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlF;;;OAGG;cACa,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQ1D,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAI5G"}
|
|
@@ -1,59 +1,107 @@
|
|
|
1
1
|
import CDP from 'chrome-remote-interface';
|
|
2
2
|
import { BrowserController, log, utils } from 'crawlee';
|
|
3
|
-
import {
|
|
3
|
+
import { applyToPage } from './browser-process/fingerprint-config.js';
|
|
4
|
+
import { runWithRetries, fixedBackoff, disposeClient } from './connection/cdp-reconnection.js';
|
|
5
|
+
import { createSessionClient } from './connection/session-client.js';
|
|
4
6
|
import { PAGE_CLOSED } from './events.js';
|
|
5
|
-
import { CdpFingerprintInjector } from './fingerprinting/fingerprint-injector.js';
|
|
6
|
-
import { generateNonApifyFingerprint } from './fingerprinting/non-apify-fingerprint-generator.js';
|
|
7
7
|
import CdpPage from './page.js';
|
|
8
|
-
|
|
8
|
+
import { installSessionInjectionHook } from './session/inject-hook.js';
|
|
9
|
+
class CDPBrowserController extends BrowserController {
|
|
9
10
|
constructor() {
|
|
10
11
|
super(...arguments);
|
|
11
|
-
Object.defineProperty(this, "
|
|
12
|
+
Object.defineProperty(this, "hasDisconnectListener", {
|
|
12
13
|
enumerable: true,
|
|
13
14
|
configurable: true,
|
|
14
15
|
writable: true,
|
|
15
|
-
value:
|
|
16
|
-
});
|
|
17
|
-
Object.defineProperty(this, "
|
|
16
|
+
value: false
|
|
17
|
+
}); // Flag to track disconnect listener
|
|
18
|
+
Object.defineProperty(this, "isClosing", {
|
|
18
19
|
enumerable: true,
|
|
19
20
|
configurable: true,
|
|
20
21
|
writable: true,
|
|
21
|
-
value:
|
|
22
|
+
value: false
|
|
23
|
+
}); // Flag to track intentional browser closure
|
|
24
|
+
/**
|
|
25
|
+
* A SINGLE keep-alive ping loop for the whole browser socket. In flatten
|
|
26
|
+
* mode every page shares one websocket, so one ping loop (and one ws 'error'
|
|
27
|
+
* listener) covers them all - a per-page heartbeat would fan N intervals and
|
|
28
|
+
* N listeners onto the same EventEmitter.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(this, "heartbeat", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: void 0
|
|
22
35
|
});
|
|
23
|
-
Object.defineProperty(this, "
|
|
36
|
+
Object.defineProperty(this, "heartbeatWs", {
|
|
24
37
|
enumerable: true,
|
|
25
38
|
configurable: true,
|
|
26
39
|
writable: true,
|
|
27
|
-
value:
|
|
28
|
-
});
|
|
29
|
-
Object.defineProperty(this, "
|
|
40
|
+
value: void 0
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(this, "heartbeatErrorHandler", {
|
|
30
43
|
enumerable: true,
|
|
31
44
|
configurable: true,
|
|
32
45
|
writable: true,
|
|
33
|
-
value:
|
|
34
|
-
});
|
|
46
|
+
value: void 0
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The single browser-level CDP client. This is the ws-endpoint connection
|
|
51
|
+
* the plugin opened at launch (`_connectViaWsEndpoint`) and Crawlee handed us
|
|
52
|
+
* via `assignBrowser` - NOT a second connection. Every page is a flat CDP
|
|
53
|
+
* session multiplexed over this one socket (ADR 0003), so there is no
|
|
54
|
+
* per-page socket and no `CDP({ port })` here.
|
|
55
|
+
*/
|
|
56
|
+
get client() {
|
|
57
|
+
return this.browser;
|
|
35
58
|
}
|
|
36
59
|
async initialize() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
// No connection is opened here - `this.browser` is already the live
|
|
61
|
+
// browser-level socket. We only wire the disconnect listener + the single
|
|
62
|
+
// shared-socket heartbeat once. No domain is enabled (eager
|
|
63
|
+
// Network.enable/Page.enable are CDP detection vectors).
|
|
64
|
+
this._attachDisconnectListener();
|
|
65
|
+
this._attachHeartbeat();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* One keep-alive ping loop for the shared browser socket. Re-bound after a
|
|
69
|
+
* socket reconnect so it follows the live ws. Idempotent.
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
_attachHeartbeat() {
|
|
73
|
+
this._clearHeartbeat();
|
|
74
|
+
const ws = this.client?._ws;
|
|
75
|
+
if (!ws)
|
|
76
|
+
return;
|
|
77
|
+
const errorHandler = (error) => {
|
|
78
|
+
log.error('Browser WebSocket error occurred:', { error });
|
|
79
|
+
};
|
|
80
|
+
ws.on('error', errorHandler);
|
|
81
|
+
this.heartbeatWs = ws;
|
|
82
|
+
this.heartbeatErrorHandler = errorHandler;
|
|
83
|
+
this.heartbeat = setInterval(() => {
|
|
84
|
+
if (ws.readyState === ws.OPEN) {
|
|
85
|
+
ws.ping(() => { });
|
|
47
86
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (attempt === maxInitAttempts) {
|
|
52
|
-
throw error;
|
|
53
|
-
}
|
|
54
|
-
// Wait before retrying
|
|
55
|
-
await utils.sleep(2000 * attempt);
|
|
87
|
+
else if (!this.isClosing && ws.readyState !== ws.CONNECTING) {
|
|
88
|
+
log.debug('Browser socket health check failed, clearing heartbeat');
|
|
89
|
+
this._clearHeartbeat();
|
|
56
90
|
}
|
|
91
|
+
}, CDPBrowserController.HEARTBEAT_INTERVAL_MS);
|
|
92
|
+
}
|
|
93
|
+
/** @private */
|
|
94
|
+
_clearHeartbeat() {
|
|
95
|
+
if (this.heartbeat) {
|
|
96
|
+
clearInterval(this.heartbeat);
|
|
97
|
+
this.heartbeat = undefined;
|
|
98
|
+
}
|
|
99
|
+
// Detach the ws 'error' listener too, so a reconnect (which re-runs
|
|
100
|
+
// _attachHeartbeat) never stacks listeners toward MaxListenersExceeded.
|
|
101
|
+
if (this.heartbeatWs && this.heartbeatErrorHandler) {
|
|
102
|
+
this.heartbeatWs.removeListener('error', this.heartbeatErrorHandler);
|
|
103
|
+
this.heartbeatWs = undefined;
|
|
104
|
+
this.heartbeatErrorHandler = undefined;
|
|
57
105
|
}
|
|
58
106
|
}
|
|
59
107
|
/**
|
|
@@ -80,68 +128,85 @@ export default class CDPBrowserController extends BrowserController {
|
|
|
80
128
|
* @private
|
|
81
129
|
*/
|
|
82
130
|
async _attemptReconnect(maxAttempts = 5, delayMs = 2000) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
131
|
+
await runWithRetries({
|
|
132
|
+
label: 'browser socket reconnect',
|
|
133
|
+
policy: fixedBackoff(maxAttempts, delayMs / 2),
|
|
134
|
+
attempt: async (attempt) => {
|
|
135
|
+
log.info(`Reconnect attempt ${attempt} of ${maxAttempts}`);
|
|
136
|
+
// Reconnect the single browser socket over the SAME ws endpoint
|
|
137
|
+
// (flatten transport - no port, no root-target connect). Pages
|
|
138
|
+
// read the client through `() => this.browser`, so reassigning it
|
|
139
|
+
// here transparently points every page proxy at the fresh socket;
|
|
140
|
+
// each page re-attaches its own flat session lazily on its next
|
|
141
|
+
// op (PageSession re-attach). No domain is enabled on connect -
|
|
142
|
+
// that is a CDP detection vector.
|
|
143
|
+
const browserProcess = this.launchContext.process;
|
|
144
|
+
const wsUrl = await browserProcess.wsEndpoint();
|
|
145
|
+
const oldClient = this.browser;
|
|
146
|
+
this.browser = await CDP({ target: wsUrl, local: true });
|
|
147
|
+
// Detach the old client's event listeners BEFORE closing it. Closing
|
|
148
|
+
// emits 'disconnect'/'close' on the old client; leaving its listeners
|
|
149
|
+
// attached would (a) re-enter _attemptReconnect even though a fresh
|
|
150
|
+
// socket is already assigned, and (b) fire the plugin's teardown, which
|
|
151
|
+
// releases the debugging port for a still-live browser process.
|
|
152
|
+
oldClient.removeAllListeners?.();
|
|
153
|
+
// Close the replaced socket so its fd is not leaked per reconnect.
|
|
154
|
+
await disposeClient(oldClient);
|
|
97
155
|
log.info('Reconnected to browser successfully.');
|
|
98
156
|
this.hasDisconnectListener = false;
|
|
99
157
|
this._attachDisconnectListener();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
await utils.sleep(delayMs / 2);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
158
|
+
// Re-bind the heartbeat + error listener to the fresh socket.
|
|
159
|
+
this._attachHeartbeat();
|
|
160
|
+
return true;
|
|
161
|
+
},
|
|
162
|
+
onAttemptError: (error, attempt) => {
|
|
163
|
+
log.error(`Reconnect attempt ${attempt} failed.`, error);
|
|
164
|
+
},
|
|
165
|
+
});
|
|
112
166
|
}
|
|
113
167
|
/**
|
|
114
168
|
* Gracefully closes the browser and associated process.
|
|
115
169
|
* @private
|
|
116
170
|
*/
|
|
117
171
|
async _close() {
|
|
172
|
+
// Set first so the disconnect listener bails instead of fighting our
|
|
173
|
+
// own shutdown with reconnect attempts when Browser.close drops the WS.
|
|
174
|
+
this.isClosing = true;
|
|
175
|
+
this._clearHeartbeat();
|
|
176
|
+
const browserProcess = this.launchContext.process;
|
|
177
|
+
// 1) Graceful CDP quit. This lets Chromium tear down its OWN helper
|
|
178
|
+
// processes and write a clean-exit preference, so there is no macOS
|
|
179
|
+
// "quit unexpectedly" crash dialog. Fast-fail to the process kill -
|
|
180
|
+
// a hung / resource-exhausted browser will not answer this.
|
|
118
181
|
try {
|
|
119
|
-
this.isClosing = true;
|
|
120
182
|
if (this.client) {
|
|
121
183
|
await Promise.race([
|
|
122
|
-
this.client.close(),
|
|
123
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('
|
|
184
|
+
this.client.Browser.close(),
|
|
185
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Browser.close timeout')), 2000)),
|
|
124
186
|
]);
|
|
125
187
|
}
|
|
126
|
-
// Gracefully close the browser process with shorter timeout
|
|
127
|
-
await Promise.race([
|
|
128
|
-
this.launchContext.process.close(),
|
|
129
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Browser process close timeout')), 3000)),
|
|
130
|
-
]);
|
|
131
|
-
log.info('Browser process closed successfully');
|
|
132
188
|
}
|
|
133
189
|
catch (e) {
|
|
134
|
-
log.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
190
|
+
log.debug('Graceful Browser.close failed; falling back to process group kill', e);
|
|
191
|
+
}
|
|
192
|
+
// 2) Drop the websocket (best-effort - it may already be gone after
|
|
193
|
+
// Browser.close). Never let this block shutdown.
|
|
194
|
+
try {
|
|
195
|
+
if (this.client) {
|
|
196
|
+
await Promise.race([
|
|
197
|
+
this.client.close(),
|
|
198
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('CDP client close timeout')), 1000)),
|
|
199
|
+
]);
|
|
141
200
|
}
|
|
142
201
|
}
|
|
143
|
-
|
|
144
|
-
|
|
202
|
+
catch (e) {
|
|
203
|
+
log.debug('CDP client close failed/timed out', e);
|
|
204
|
+
}
|
|
205
|
+
// 3) Guarantee the process and its whole group are gone. close() does
|
|
206
|
+
// SIGTERM→SIGKILL on the process GROUP with real escalation, so even
|
|
207
|
+
// if the graceful path failed, no orphan helpers survive.
|
|
208
|
+
await browserProcess.close();
|
|
209
|
+
log.debug('Browser process closed successfully');
|
|
145
210
|
}
|
|
146
211
|
/**
|
|
147
212
|
* Force kills the browser process without any grace period.
|
|
@@ -150,6 +215,7 @@ export default class CDPBrowserController extends BrowserController {
|
|
|
150
215
|
async _kill() {
|
|
151
216
|
try {
|
|
152
217
|
this.isClosing = true;
|
|
218
|
+
this._clearHeartbeat();
|
|
153
219
|
if (this.client) {
|
|
154
220
|
await Promise.race([
|
|
155
221
|
this.client.close(),
|
|
@@ -170,27 +236,42 @@ export default class CDPBrowserController extends BrowserController {
|
|
|
170
236
|
let retries = 0;
|
|
171
237
|
while (retries < maxRetries) {
|
|
172
238
|
try {
|
|
173
|
-
if (!this.
|
|
239
|
+
if (!this.hasDisconnectListener) {
|
|
174
240
|
await this.initialize();
|
|
175
241
|
}
|
|
176
|
-
//
|
|
242
|
+
// Heal the shared browser socket if it has dropped, before we
|
|
243
|
+
// create a target on it.
|
|
177
244
|
// @ts-expect-error Accessing internal WebSocket
|
|
178
245
|
if (this.client._ws?.readyState !== 1) { // WebSocket.OPEN = 1
|
|
179
|
-
log.info('Browser
|
|
180
|
-
await this.
|
|
246
|
+
log.info('Browser socket lost, reconnecting...');
|
|
247
|
+
await this._attemptReconnect();
|
|
181
248
|
}
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
//
|
|
189
|
-
const
|
|
249
|
+
// Create the page target, then attach a FLAT session to it. The
|
|
250
|
+
// page is that session multiplexed over the single browser socket
|
|
251
|
+
// (ADR 0003) - no per-page websocket, no HTTP /json resolution.
|
|
252
|
+
const { targetId } = await this.client.Target.createTarget({ url: 'about:blank' });
|
|
253
|
+
// A mutable sessionId the page's proxy reads at call time; a
|
|
254
|
+
// re-attach after a transport drop rewrites it in place, so the
|
|
255
|
+
// proxy and PageSession stay stable across reconnects.
|
|
256
|
+
const sessionRef = { sessionId: '' };
|
|
257
|
+
const attach = async () => {
|
|
258
|
+
const { sessionId } = await this.client.Target.attachToTarget({ targetId, flatten: true });
|
|
259
|
+
sessionRef.sessionId = sessionId;
|
|
260
|
+
};
|
|
261
|
+
await attach();
|
|
262
|
+
await this.client.Target.activateTarget({ targetId });
|
|
263
|
+
const sessionClient = createSessionClient({
|
|
264
|
+
browser: () => this.client,
|
|
265
|
+
sessionId: () => sessionRef.sessionId,
|
|
266
|
+
});
|
|
267
|
+
// Create and return the new page over its session-scoped client.
|
|
268
|
+
const page = await CdpPage.create(sessionClient, { reattach: attach });
|
|
190
269
|
page.once(PAGE_CLOSED, () => {
|
|
191
270
|
this.activePages -= 1;
|
|
192
271
|
});
|
|
193
272
|
await this._maybeOverrideFingerprint(page);
|
|
273
|
+
await this._recordLaunchSnapshot(page);
|
|
274
|
+
await this._maybeRestoreSession(page);
|
|
194
275
|
return page;
|
|
195
276
|
}
|
|
196
277
|
catch (error) {
|
|
@@ -211,200 +292,53 @@ export default class CDPBrowserController extends BrowserController {
|
|
|
211
292
|
}
|
|
212
293
|
throw new Error('Failed to create a new page after maximum retry attempts.');
|
|
213
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Thin delegate to the FingerprintConfig module's post-launch path. It
|
|
297
|
+
* reuses the once-per-launch profile selection (stamped by the plugin's
|
|
298
|
+
* pre-launch native path) or selects here for the system-Chrome fallback,
|
|
299
|
+
* then drives the JS fingerprint injection on the new page. The pre-vs-post
|
|
300
|
+
* decision and the PROFILE_SELECTION_KEY handshake live inside that module.
|
|
301
|
+
*/
|
|
214
302
|
async _maybeOverrideFingerprint(page) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
303
|
+
await applyToPage(this.launchContext, page);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Records the launch-time inputs the saveSession() helper later needs to read
|
|
307
|
+
* off the page - fingerprintConfig, fingerprintOptions, resolvedLocale, fingerprint,
|
|
308
|
+
* userDataDir, proxyUrl, and the sessionBundle (if any).
|
|
309
|
+
* @private
|
|
310
|
+
*/
|
|
311
|
+
async _recordLaunchSnapshot(page) {
|
|
219
312
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// When useNonApifyFingerprints is set, replace the Crawlee-provided
|
|
228
|
-
// synthetic fingerprint with a real Chrome device profile. All fields
|
|
229
|
-
// (navigator, screen, GPU, UA-CH) come from the same physical device,
|
|
230
|
-
// guaranteeing cross-property consistency that synthetic generators miss.
|
|
231
|
-
const _fpOpts = this.launchContext.launchOptions?.fingerprintOptions;
|
|
232
|
-
if (_fpOpts?.useNonApifyFingerprints) {
|
|
233
|
-
// User-supplied fingerprintOptions.platform wins; falls back to
|
|
234
|
-
// whatever the existing fingerprint already says.
|
|
235
|
-
const userPlatform = _fpOpts?.platform;
|
|
236
|
-
const osHint = (() => {
|
|
237
|
-
const p = (fp?.navigator?.platform || '').toLowerCase();
|
|
238
|
-
if (p.includes('mac'))
|
|
239
|
-
return 'macos';
|
|
240
|
-
if (p.includes('linux'))
|
|
241
|
-
return 'linux';
|
|
242
|
-
return 'windows';
|
|
243
|
-
})();
|
|
244
|
-
const proxySeedKey = this.launchContext.proxyUrl
|
|
245
|
-
? (this.launchContext.proxyUrl.match(/session-([^,&]+)/i)?.[1] ?? null)
|
|
246
|
-
: null;
|
|
247
|
-
fingerprint = await generateNonApifyFingerprint({
|
|
248
|
-
platform: userPlatform,
|
|
249
|
-
os: osHint,
|
|
250
|
-
proxySeedKey,
|
|
251
|
-
});
|
|
252
|
-
fp = fingerprint.fingerprint;
|
|
253
|
-
this.launchContext.fingerprint = fingerprint;
|
|
254
|
-
log.info('Using non-Apify real Chrome device fingerprint', {
|
|
255
|
-
requestedPlatform: userPlatform ?? '(none — derived from existing fingerprint)',
|
|
256
|
-
os: osHint,
|
|
257
|
-
platform: fp?.navigator?.platform,
|
|
258
|
-
userAgent: `${(fp?.navigator?.userAgent || '').substring(0, 70)}...`,
|
|
259
|
-
webglRenderer: (fp?.videoCard?.renderer || '').substring(0, 70),
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (fingerprintGeneratorOptions?.operatingSystems?.length > 0) {
|
|
263
|
-
const requestedOS = fingerprintGeneratorOptions.operatingSystems[0]?.toLowerCase();
|
|
264
|
-
const currentPlatform = fp?.navigator?.platform?.toLowerCase() || '';
|
|
265
|
-
// Map platform names to OS names
|
|
266
|
-
const platformToOS = {
|
|
267
|
-
'win32': 'windows',
|
|
268
|
-
'macintel': 'macos',
|
|
269
|
-
'linux x86_64': 'linux',
|
|
270
|
-
'linux': 'linux',
|
|
271
|
-
};
|
|
272
|
-
const currentOS = platformToOS[currentPlatform] || currentPlatform;
|
|
273
|
-
// Check if the fingerprint matches the requested OS
|
|
274
|
-
const osMatches = requestedOS === currentOS ||
|
|
275
|
-
(requestedOS === 'windows' && currentPlatform.includes('win')) ||
|
|
276
|
-
(requestedOS === 'macos' && currentPlatform.includes('mac')) ||
|
|
277
|
-
(requestedOS === 'linux' && currentPlatform.includes('linux'));
|
|
278
|
-
if (!osMatches) {
|
|
279
|
-
log.info('Fingerprint OS mismatch detected, regenerating fingerprint', {
|
|
280
|
-
requestedOS,
|
|
281
|
-
currentPlatform,
|
|
282
|
-
currentOS,
|
|
283
|
-
fingerprintGeneratorOptions,
|
|
284
|
-
});
|
|
285
|
-
// Regenerate fingerprint with correct options using fingerprint-generator directly
|
|
286
|
-
try {
|
|
287
|
-
const generator = new FingerprintGenerator(fingerprintGeneratorOptions);
|
|
288
|
-
fingerprint = generator.getFingerprint();
|
|
289
|
-
fp = fingerprint.fingerprint;
|
|
290
|
-
// Update the launch context with the regenerated fingerprint
|
|
291
|
-
this.launchContext.fingerprint = fingerprint;
|
|
292
|
-
log.info('Fingerprint regenerated successfully', {
|
|
293
|
-
newPlatform: fp?.navigator?.platform,
|
|
294
|
-
newUserAgent: `${(fp?.navigator?.userAgent || '').substring(0, 50)}...`,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
catch (regenerateError) {
|
|
298
|
-
log.error('Failed to regenerate fingerprint, using original', { error: regenerateError });
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
log.debug('Fingerprint OS matches requested OS', { requestedOS, currentPlatform });
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
const webgl = fp?.webgl;
|
|
306
|
-
log.debug('Fingerprint data from generator:', {
|
|
307
|
-
hasFingerprint: !!fp,
|
|
308
|
-
hasHeaders: !!fingerprint.headers,
|
|
309
|
-
navigatorPlatform: fp?.navigator?.platform,
|
|
310
|
-
navigatorUserAgent: `${(fp?.navigator?.userAgent || '').substring(0, 50)}...`,
|
|
311
|
-
screenWidth: fp?.screen?.width,
|
|
312
|
-
screenHeight: fp?.screen?.height,
|
|
313
|
-
webglVendor: webgl?.vendorUnmasked?.substring(0, 30),
|
|
314
|
-
webglRenderer: webgl?.rendererUnmasked?.substring(0, 50),
|
|
315
|
-
});
|
|
316
|
-
// Detect platform from fingerprint or default to Windows for better evasion
|
|
317
|
-
const fingerprintPlatform = fp?.navigator?.platform;
|
|
318
|
-
const platform = fingerprintPlatform || 'Win32'; // Default to Windows instead of Linux
|
|
319
|
-
// Determine if we're running on Apify
|
|
320
|
-
const isApify = process.env.APIFY_IS_AT_HOME === 'true';
|
|
321
|
-
// Get fingerprint options from launch context
|
|
322
|
-
const fingerprintOptions = this.launchContext.launchOptions?.fingerprintOptions || {};
|
|
323
|
-
// Get recommended settings based on environment
|
|
324
|
-
const defaultOptions = isApify
|
|
325
|
-
? CdpFingerprintInjector.getApifyRecommendedSettings()
|
|
326
|
-
: {
|
|
327
|
-
debug: false, // Disable debug logging - anti-bot services detect debug prefixes
|
|
328
|
-
enableAdvancedStealth: true,
|
|
329
|
-
bypassRuntimeEnable: true,
|
|
330
|
-
spoofWebGL: true,
|
|
331
|
-
spoofAudioContext: true,
|
|
332
|
-
spoofClientRects: true,
|
|
333
|
-
spoofFonts: true,
|
|
334
|
-
spoofPerformance: true,
|
|
335
|
-
spoofLocale: true,
|
|
336
|
-
detectTimezone: !!this.launchContext.proxyUrl,
|
|
337
|
-
useFingerprintDefaults: true, // Use hardcoded defaults (set to false to use generated fingerprint data)
|
|
338
|
-
// 2024-2025 new defaults
|
|
339
|
-
bypassCDPDetection: true,
|
|
340
|
-
spoofStorage: true,
|
|
341
|
-
spoofTiming: true,
|
|
342
|
-
enableDataDomeBypass: false,
|
|
343
|
-
webRtcPolicy: 'spoof',
|
|
344
|
-
// Safe defaults for humanized interaction patterns
|
|
345
|
-
humanization: {
|
|
346
|
-
mouse: true,
|
|
347
|
-
keyboard: true,
|
|
348
|
-
scroll: true,
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
// Determine effective useFingerprintDefaults value.
|
|
352
|
-
// Real device profiles (useNonApifyFingerprints=true) contain consistent cross-property
|
|
353
|
-
// data measured from a real device — default to using it (useFingerprintDefaults=false).
|
|
354
|
-
// Synthetic Apify-generated fingerprints need the hardcoded defaults to avoid
|
|
355
|
-
// injecting inconsistent generated values, so they keep useFingerprintDefaults=true.
|
|
356
|
-
const usingRealDeviceProfile = !!_fpOpts?.useNonApifyFingerprints;
|
|
357
|
-
const effectiveUseFingerprintDefaults = fingerprintOptions.useFingerprintDefaults
|
|
358
|
-
?? (usingRealDeviceProfile ? false : (defaultOptions.useFingerprintDefaults ?? true));
|
|
359
|
-
// Platform selection logic:
|
|
360
|
-
// - When useFingerprintDefaults is false: prefer generated fingerprint platform
|
|
361
|
-
// - When useFingerprintDefaults is true (default): prefer hardcoded fingerprintOptions.platform
|
|
362
|
-
const effectivePlatform = effectiveUseFingerprintDefaults === false
|
|
363
|
-
? (platform || fingerprintOptions.platform || 'Win32') // Generated fingerprint first
|
|
364
|
-
: (fingerprintOptions.platform || platform || 'Win32'); // Hardcoded option first
|
|
365
|
-
// Check useNativeStealth from all possible locations (in priority order):
|
|
366
|
-
// 1. Top-level launchOptions (e.g. CDPBrowserPlugin constructor arg)
|
|
367
|
-
// 2. Inside fingerprintOptions (e.g. datadome test / crawler launchOptions)
|
|
368
|
-
// 3. At launchContext root (some CDPCrawler launchContext configurations)
|
|
369
|
-
const useNativeStealth = !!this.launchContext.launchOptions?.useNativeStealth ||
|
|
370
|
-
!!this.launchContext.launchOptions?.fingerprintOptions?.useNativeStealth ||
|
|
371
|
-
!!this.launchContext?.useNativeStealth;
|
|
372
|
-
// Resolved locale triple is populated in browser-plugin before launch. Pass the
|
|
373
|
-
// whole thing through so the injector sets navigator.language, Accept-Language,
|
|
374
|
-
// Intl defaults, and timezone from one coherent source of truth.
|
|
375
|
-
const resolvedLocale = this.launchContext.launchOptions?.resolvedLocale;
|
|
376
|
-
// Merge humanization options properly
|
|
377
|
-
const mergedOptions = {
|
|
378
|
-
...defaultOptions,
|
|
379
|
-
...fingerprintOptions,
|
|
380
|
-
useNativeStealth,
|
|
381
|
-
// Explicitly set the computed effective value — the spread above would resolve to
|
|
382
|
-
// defaultOptions.useFingerprintDefaults (always true) when fingerprintOptions
|
|
383
|
-
// does not include the key, so we must override it here.
|
|
384
|
-
useFingerprintDefaults: effectiveUseFingerprintDefaults,
|
|
313
|
+
const launchOptions = this.launchContext.launchOptions ?? {};
|
|
314
|
+
page.setLaunchSnapshot({
|
|
315
|
+
fingerprintConfig: launchOptions.fingerprintConfig,
|
|
316
|
+
fingerprintOptions: (launchOptions.fingerprintOptions ?? {}),
|
|
317
|
+
resolvedLocale: launchOptions.resolvedLocale,
|
|
318
|
+
fingerprint: this.launchContext.fingerprint,
|
|
319
|
+
userDataDir: launchOptions.userDataDir,
|
|
385
320
|
proxyUrl: this.launchContext.proxyUrl,
|
|
386
|
-
|
|
387
|
-
viewport: this.launchContext.launchOptions?.viewport,
|
|
388
|
-
resolvedLocale,
|
|
389
|
-
// Ensure humanization options are properly merged
|
|
390
|
-
humanization: {
|
|
391
|
-
...defaultOptions.humanization,
|
|
392
|
-
...fingerprintOptions.humanization,
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
await CdpFingerprintInjector.injectFingerprint(page, fingerprint, mergedOptions);
|
|
396
|
-
page.setHumanizationOptions(mergedOptions.humanization);
|
|
397
|
-
log.info('Fingerprint injected successfully', {
|
|
398
|
-
platform: mergedOptions.platform,
|
|
399
|
-
isApify,
|
|
400
|
-
hasProxy: !!this.launchContext.proxyUrl,
|
|
401
|
-
useFingerprintDefaults: mergedOptions.useFingerprintDefaults,
|
|
402
|
-
enableDataDomeBypass: mergedOptions.enableDataDomeBypass,
|
|
403
|
-
humanization: mergedOptions.humanization,
|
|
321
|
+
sessionBundle: launchOptions.sessionBundle,
|
|
404
322
|
});
|
|
405
323
|
}
|
|
406
|
-
catch (
|
|
407
|
-
log.
|
|
324
|
+
catch (err) {
|
|
325
|
+
log.warning('Failed to record launch snapshot on page', { err: err.message });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Replays cookies + per-origin localStorage/sessionStorage from the sessionBundle
|
|
330
|
+
* onto a fresh page before any navigation happens.
|
|
331
|
+
* @private
|
|
332
|
+
*/
|
|
333
|
+
async _maybeRestoreSession(page) {
|
|
334
|
+
const bundle = this.launchContext.launchOptions?.sessionBundle;
|
|
335
|
+
if (!bundle)
|
|
336
|
+
return;
|
|
337
|
+
try {
|
|
338
|
+
await installSessionInjectionHook(page, bundle);
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
log.error('Failed to inject session bundle on new page', { err: err.message });
|
|
408
342
|
}
|
|
409
343
|
}
|
|
410
344
|
/**
|
|
@@ -435,4 +369,11 @@ export default class CDPBrowserController extends BrowserController {
|
|
|
435
369
|
return {};
|
|
436
370
|
}
|
|
437
371
|
}
|
|
372
|
+
Object.defineProperty(CDPBrowserController, "HEARTBEAT_INTERVAL_MS", {
|
|
373
|
+
enumerable: true,
|
|
374
|
+
configurable: true,
|
|
375
|
+
writable: true,
|
|
376
|
+
value: 5000
|
|
377
|
+
});
|
|
378
|
+
export default CDPBrowserController;
|
|
438
379
|
//# sourceMappingURL=browser-controller.js.map
|