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.
Files changed (232) hide show
  1. package/README.md +1 -607
  2. package/browser/fonts/macos-bundle/40-macos-aliases.conf +515 -0
  3. package/browser/fonts/macos-bundle/README.md +24 -0
  4. package/browser/fonts/macos-bundle/install.sh +83 -0
  5. package/dist/browser-controller.d.ts +47 -4
  6. package/dist/browser-controller.d.ts.map +1 -1
  7. package/dist/browser-controller.js +219 -278
  8. package/dist/browser-controller.js.map +1 -1
  9. package/dist/browser-plugin.d.ts +82 -110
  10. package/dist/browser-plugin.d.ts.map +1 -1
  11. package/dist/browser-plugin.js +376 -379
  12. package/dist/browser-plugin.js.map +1 -1
  13. package/dist/browser-process/align-fingerprint-config.d.ts +36 -0
  14. package/dist/browser-process/align-fingerprint-config.d.ts.map +1 -0
  15. package/dist/browser-process/align-fingerprint-config.js +74 -0
  16. package/dist/browser-process/align-fingerprint-config.js.map +1 -0
  17. package/dist/browser-process/anti-detect-browser.d.ts +2 -2
  18. package/dist/browser-process/anti-detect-browser.js +7 -7
  19. package/dist/browser-process/anti-detect-config.d.ts +131 -2
  20. package/dist/browser-process/anti-detect-config.d.ts.map +1 -1
  21. package/dist/browser-process/anti-detect-config.js +337 -127
  22. package/dist/browser-process/anti-detect-config.js.map +1 -1
  23. package/dist/browser-process/browser.d.ts +25 -92
  24. package/dist/browser-process/browser.d.ts.map +1 -1
  25. package/dist/browser-process/browser.js +429 -73
  26. package/dist/browser-process/browser.js.map +1 -1
  27. package/dist/browser-process/fingerprint-config.d.ts +146 -0
  28. package/dist/browser-process/fingerprint-config.d.ts.map +1 -0
  29. package/dist/browser-process/fingerprint-config.js +308 -0
  30. package/dist/browser-process/fingerprint-config.js.map +1 -0
  31. package/dist/browser-process/get-chrome-executable.d.ts +6 -0
  32. package/dist/browser-process/get-chrome-executable.d.ts.map +1 -1
  33. package/dist/browser-process/get-chrome-executable.js +79 -4
  34. package/dist/browser-process/get-chrome-executable.js.map +1 -1
  35. package/dist/browser-process/gpu-family-profiles.d.ts +58 -0
  36. package/dist/browser-process/gpu-family-profiles.d.ts.map +1 -0
  37. package/dist/browser-process/gpu-family-profiles.js +480 -0
  38. package/dist/browser-process/gpu-family-profiles.js.map +1 -0
  39. package/dist/browser-process/launch-options.d.ts +301 -0
  40. package/dist/browser-process/launch-options.d.ts.map +1 -0
  41. package/dist/browser-process/launch-options.js +2 -0
  42. package/dist/browser-process/launch-options.js.map +1 -0
  43. package/dist/browser-process/port-allocator.d.ts +64 -0
  44. package/dist/browser-process/port-allocator.d.ts.map +1 -0
  45. package/dist/browser-process/port-allocator.js +198 -0
  46. package/dist/browser-process/port-allocator.js.map +1 -0
  47. package/dist/browser-process/process.d.ts +85 -0
  48. package/dist/browser-process/process.d.ts.map +1 -1
  49. package/dist/browser-process/process.js +235 -19
  50. package/dist/browser-process/process.js.map +1 -1
  51. package/dist/browser-process/signal-cleanup.d.ts +40 -0
  52. package/dist/browser-process/signal-cleanup.d.ts.map +1 -0
  53. package/dist/browser-process/signal-cleanup.js +105 -0
  54. package/dist/browser-process/signal-cleanup.js.map +1 -0
  55. package/dist/browser-process/widevine-cdm.d.ts +144 -0
  56. package/dist/browser-process/widevine-cdm.d.ts.map +1 -0
  57. package/dist/browser-process/widevine-cdm.js +298 -0
  58. package/dist/browser-process/widevine-cdm.js.map +1 -0
  59. package/dist/browser-profiles/chrome/populate-profile.d.ts.map +1 -1
  60. package/dist/browser-profiles/chrome/populate-profile.js +21 -22
  61. package/dist/browser-profiles/chrome/populate-profile.js.map +1 -1
  62. package/dist/connection/cdp-reconnection.d.ts +50 -0
  63. package/dist/connection/cdp-reconnection.d.ts.map +1 -0
  64. package/dist/connection/cdp-reconnection.js +75 -0
  65. package/dist/connection/cdp-reconnection.js.map +1 -0
  66. package/dist/connection/page-session.d.ts +114 -0
  67. package/dist/connection/page-session.d.ts.map +1 -0
  68. package/dist/connection/page-session.js +237 -0
  69. package/dist/connection/page-session.js.map +1 -0
  70. package/dist/connection/session-client.d.ts +34 -0
  71. package/dist/connection/session-client.d.ts.map +1 -0
  72. package/dist/connection/session-client.js +129 -0
  73. package/dist/connection/session-client.js.map +1 -0
  74. package/dist/crawler.d.ts +9 -88
  75. package/dist/crawler.d.ts.map +1 -1
  76. package/dist/crawler.js +157 -8
  77. package/dist/crawler.js.map +1 -1
  78. package/dist/fingerprinting/fingerprint-injector.d.ts +24 -114
  79. package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
  80. package/dist/fingerprinting/fingerprint-injector.js +305 -611
  81. package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
  82. package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts +1 -1
  83. package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -1
  84. package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +16 -14
  85. package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -1
  86. package/dist/fingerprinting/fingerprint-overrides/index.d.ts +6 -2
  87. package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
  88. package/dist/fingerprinting/fingerprint-overrides/index.js +6 -2
  89. package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
  90. package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +1 -1
  91. package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +4 -4
  92. package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -1
  93. package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +4 -4
  94. package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts +18 -0
  95. package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts.map +1 -0
  96. package/dist/fingerprinting/fingerprint-overrides/navigator-override.js +136 -0
  97. package/dist/fingerprinting/fingerprint-overrides/navigator-override.js.map +1 -0
  98. package/dist/fingerprinting/fingerprint-overrides/override.d.ts +151 -0
  99. package/dist/fingerprinting/fingerprint-overrides/override.d.ts.map +1 -0
  100. package/dist/fingerprinting/fingerprint-overrides/override.js +14 -0
  101. package/dist/fingerprinting/fingerprint-overrides/override.js.map +1 -0
  102. package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
  103. package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +46 -15
  104. package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
  105. package/dist/fingerprinting/fingerprint-overrides/registry.d.ts +43 -0
  106. package/dist/fingerprinting/fingerprint-overrides/registry.d.ts.map +1 -0
  107. package/dist/fingerprinting/fingerprint-overrides/registry.js +342 -0
  108. package/dist/fingerprinting/fingerprint-overrides/registry.js.map +1 -0
  109. package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts +16 -0
  110. package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts.map +1 -0
  111. package/dist/fingerprinting/fingerprint-overrides/screen-override.js +175 -0
  112. package/dist/fingerprinting/fingerprint-overrides/screen-override.js.map +1 -0
  113. package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +1 -1
  114. package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +4 -4
  115. package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -1
  116. package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
  117. package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +75 -9
  118. package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
  119. package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts +1 -1
  120. package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts.map +1 -1
  121. package/dist/fingerprinting/fingerprint-overrides/ua-ch.js +34 -20
  122. package/dist/fingerprinting/fingerprint-overrides/ua-ch.js.map +1 -1
  123. package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
  124. package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +25 -6
  125. package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
  126. package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +1 -1
  127. package/dist/fingerprinting/hardware-realism.d.ts +83 -0
  128. package/dist/fingerprinting/hardware-realism.d.ts.map +1 -0
  129. package/dist/fingerprinting/hardware-realism.js +156 -0
  130. package/dist/fingerprinting/hardware-realism.js.map +1 -0
  131. package/dist/fingerprinting/injection-planner.d.ts +133 -0
  132. package/dist/fingerprinting/injection-planner.d.ts.map +1 -0
  133. package/dist/fingerprinting/injection-planner.js +497 -0
  134. package/dist/fingerprinting/injection-planner.js.map +1 -0
  135. package/dist/fingerprinting/locale-resolver.d.ts +8 -1
  136. package/dist/fingerprinting/locale-resolver.d.ts.map +1 -1
  137. package/dist/fingerprinting/locale-resolver.js +26 -3
  138. package/dist/fingerprinting/locale-resolver.js.map +1 -1
  139. package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts +4 -4
  140. package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts.map +1 -1
  141. package/dist/fingerprinting/non-apify-fingerprint-generator.js +24 -58
  142. package/dist/fingerprinting/non-apify-fingerprint-generator.js.map +1 -1
  143. package/dist/fingerprinting/non-apify-profiles.json +10515 -8494
  144. package/dist/fingerprinting/os-consistency.d.ts +28 -0
  145. package/dist/fingerprinting/os-consistency.d.ts.map +1 -0
  146. package/dist/fingerprinting/os-consistency.js +45 -0
  147. package/dist/fingerprinting/os-consistency.js.map +1 -0
  148. package/dist/fingerprinting/profile-quality.d.ts +25 -0
  149. package/dist/fingerprinting/profile-quality.d.ts.map +1 -0
  150. package/dist/fingerprinting/profile-quality.js +310 -0
  151. package/dist/fingerprinting/profile-quality.js.map +1 -0
  152. package/dist/fingerprinting/profile-selector.d.ts +102 -0
  153. package/dist/fingerprinting/profile-selector.d.ts.map +1 -0
  154. package/dist/fingerprinting/profile-selector.js +168 -0
  155. package/dist/fingerprinting/profile-selector.js.map +1 -0
  156. package/dist/fingerprinting/ua-alignment.d.ts +83 -0
  157. package/dist/fingerprinting/ua-alignment.d.ts.map +1 -0
  158. package/dist/fingerprinting/ua-alignment.js +197 -0
  159. package/dist/fingerprinting/ua-alignment.js.map +1 -0
  160. package/dist/fingerprinting/ua-ch-brands.d.ts +88 -0
  161. package/dist/fingerprinting/ua-ch-brands.d.ts.map +1 -0
  162. package/dist/fingerprinting/ua-ch-brands.js +174 -0
  163. package/dist/fingerprinting/ua-ch-brands.js.map +1 -0
  164. package/dist/index.d.ts +5 -0
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +6 -0
  167. package/dist/index.js.map +1 -1
  168. package/dist/input/dropdown-selector.d.ts +90 -0
  169. package/dist/input/dropdown-selector.d.ts.map +1 -0
  170. package/dist/input/dropdown-selector.js +394 -0
  171. package/dist/input/dropdown-selector.js.map +1 -0
  172. package/dist/input/element-target.d.ts +152 -0
  173. package/dist/input/element-target.d.ts.map +1 -0
  174. package/dist/input/element-target.js +461 -0
  175. package/dist/input/element-target.js.map +1 -0
  176. package/dist/input/input-emulator.d.ts +85 -0
  177. package/dist/input/input-emulator.d.ts.map +1 -0
  178. package/dist/input/input-emulator.js +325 -0
  179. package/dist/input/input-emulator.js.map +1 -0
  180. package/dist/input/input-transport.d.ts +60 -0
  181. package/dist/input/input-transport.d.ts.map +1 -0
  182. package/dist/input/input-transport.js +28 -0
  183. package/dist/input/input-transport.js.map +1 -0
  184. package/dist/input/recording-transport.d.ts +32 -0
  185. package/dist/input/recording-transport.d.ts.map +1 -0
  186. package/dist/input/recording-transport.js +43 -0
  187. package/dist/input/recording-transport.js.map +1 -0
  188. package/dist/keyboard-layout.d.ts +2 -2
  189. package/dist/keyboard-layout.js +2 -2
  190. package/dist/navigation/page-navigation.d.ts +67 -0
  191. package/dist/navigation/page-navigation.d.ts.map +1 -0
  192. package/dist/navigation/page-navigation.js +122 -0
  193. package/dist/navigation/page-navigation.js.map +1 -0
  194. package/dist/network/network-watch.d.ts +110 -0
  195. package/dist/network/network-watch.d.ts.map +1 -0
  196. package/dist/network/network-watch.js +223 -0
  197. package/dist/network/network-watch.js.map +1 -0
  198. package/dist/page.d.ts +118 -104
  199. package/dist/page.d.ts.map +1 -1
  200. package/dist/page.js +311 -1130
  201. package/dist/page.js.map +1 -1
  202. package/dist/session/inject-hook.d.ts +24 -0
  203. package/dist/session/inject-hook.d.ts.map +1 -0
  204. package/dist/session/inject-hook.js +80 -0
  205. package/dist/session/inject-hook.js.map +1 -0
  206. package/dist/session/save.d.ts +21 -0
  207. package/dist/session/save.d.ts.map +1 -0
  208. package/dist/session/save.js +163 -0
  209. package/dist/session/save.js.map +1 -0
  210. package/dist/session/tar-data-dir.d.ts +18 -0
  211. package/dist/session/tar-data-dir.d.ts.map +1 -0
  212. package/dist/session/tar-data-dir.js +105 -0
  213. package/dist/session/tar-data-dir.js.map +1 -0
  214. package/dist/session/types.d.ts +80 -0
  215. package/dist/session/types.d.ts.map +1 -0
  216. package/dist/session/types.js +29 -0
  217. package/dist/session/types.js.map +1 -0
  218. package/dist/session-profile.d.ts +102 -0
  219. package/dist/session-profile.d.ts.map +1 -0
  220. package/dist/session-profile.js +163 -0
  221. package/dist/session-profile.js.map +1 -0
  222. package/dist/tsconfig.build.tsbuildinfo +1 -1
  223. package/package.json +17 -6
  224. package/scripts/postinstall.cjs +58 -0
  225. package/dist/fingerprinting/custom-fingerprint-injector.d.ts +0 -87
  226. package/dist/fingerprinting/custom-fingerprint-injector.d.ts.map +0 -1
  227. package/dist/fingerprinting/custom-fingerprint-injector.js +0 -342
  228. package/dist/fingerprinting/custom-fingerprint-injector.js.map +0 -1
  229. package/dist/launcher-wrap.d.ts +0 -10
  230. package/dist/launcher-wrap.d.ts.map +0 -1
  231. package/dist/launcher-wrap.js +0 -11
  232. 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;AAGxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhE,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,OAAO,MAAM,WAAW,CAAC;AAEhC,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,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,SAAS,CAAS;IAEpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BjC;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAiBjC;;;OAGG;YACW,iBAAiB;IAqC/B;;;OAGG;cACa,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCvC;;;OAGG;cACa,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBtC;;;OAGG;cACa,QAAQ,CAAC,UAAU,SAAI,GAAG,OAAO,CAAC,aAAa,CAAC;YAqDlD,yBAAyB;IAgOvC;;;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
+ {"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 { FingerprintGenerator } from 'fingerprint-generator';
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
- export default class CDPBrowserController extends BrowserController {
8
+ import { installSessionInjectionHook } from './session/inject-hook.js';
9
+ class CDPBrowserController extends BrowserController {
9
10
  constructor() {
10
11
  super(...arguments);
11
- Object.defineProperty(this, "client", {
12
+ Object.defineProperty(this, "hasDisconnectListener", {
12
13
  enumerable: true,
13
14
  configurable: true,
14
15
  writable: true,
15
- value: void 0
16
- });
17
- Object.defineProperty(this, "reconnectAttempts", {
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: 0
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, "hasDisconnectListener", {
36
+ Object.defineProperty(this, "heartbeatWs", {
24
37
  enumerable: true,
25
38
  configurable: true,
26
39
  writable: true,
27
- value: false
28
- }); // Flag to track disconnect listener
29
- Object.defineProperty(this, "isClosing", {
40
+ value: void 0
41
+ });
42
+ Object.defineProperty(this, "heartbeatErrorHandler", {
30
43
  enumerable: true,
31
44
  configurable: true,
32
45
  writable: true,
33
- value: false
34
- }); // Flag to track intentional browser closure
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
- const maxInitAttempts = 3;
38
- let attempt = 0;
39
- while (attempt < maxInitAttempts) {
40
- try {
41
- this.client = await CDP({
42
- port: this.launchContext.debuggingPort,
43
- local: true,
44
- });
45
- this._attachDisconnectListener();
46
- return;
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
- catch (error) {
49
- attempt++;
50
- log.error(`Failed to initialize CDP client (attempt ${attempt}/${maxInitAttempts})`, error);
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
- this.reconnectAttempts = 0;
84
- while (this.reconnectAttempts < maxAttempts) {
85
- try {
86
- log.info(`Reconnect attempt ${this.reconnectAttempts + 1} of ${maxAttempts}`);
87
- // Connect to browser target directly
88
- this.client = await CDP({
89
- port: this.launchContext.debuggingPort,
90
- target: 'browser', // This connects to the root target without creating new ones
91
- local: true,
92
- });
93
- // NOTE: Network.enable() and Page.enable() REMOVED - they are CDP detection vectors!
94
- // These domains will be enabled lazily only when actually needed
95
- // Enabling them on connect triggers detectable side effects
96
- this.reconnectAttempts = 0;
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
- return;
101
- }
102
- catch (error) {
103
- this.reconnectAttempts++;
104
- log.error(`Reconnect attempt ${this.reconnectAttempts} failed.`, error);
105
- if (this.reconnectAttempts === maxAttempts) {
106
- log.error('Max reconnect attempts reached.');
107
- return;
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('CDP client close timeout')), 3000)),
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.warning('Could not close browser gracefully, force killing', e);
135
- // Force kill the browser process immediately
136
- try {
137
- await this.launchContext.process.kill();
138
- }
139
- catch (killError) {
140
- log.error('Could not kill browser process', killError);
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
- // Wait for 5 seconds to ensure the browser is fully closed
144
- await utils.sleep(5000);
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.client) {
239
+ if (!this.hasDisconnectListener) {
174
240
  await this.initialize();
175
241
  }
176
- // Check if client is still connected
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 connection lost, reinitializing...');
180
- await this.initialize();
246
+ log.info('Browser socket lost, reconnecting...');
247
+ await this._attemptReconnect();
181
248
  }
182
- const { Target } = this.client;
183
- // Always create a new target
184
- const { targetId } = await Target.createTarget({ url: 'about:blank' });
185
- const session = await CDP({ target: targetId, port: this.launchContext.debuggingPort });
186
- // Activate the target
187
- await Target.activateTarget({ targetId });
188
- // Create and return the new page using the static create method
189
- const page = await CdpPage.create(session);
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
- if (!this.launchContext.fingerprint) {
216
- log.debug('No fingerprint provided in launchContext, skipping fingerprint injection');
217
- return;
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
- // Get fingerprint generator options from launch context (passed from browserPoolOptions)
221
- const fingerprintGeneratorOptions = this.launchContext.launchOptions?.fingerprintGeneratorOptions;
222
- // Check if we need to regenerate the fingerprint based on OS mismatch
223
- const { fingerprint: launchFingerprint } = this.launchContext;
224
- let fingerprint = launchFingerprint;
225
- let fp = fingerprint.fingerprint;
226
- // ── Non-Apify real-device fingerprint override ───────────────────────
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
- platform: effectivePlatform,
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 (error) {
407
- log.error('Error attaching fingerprint', { error });
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