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
@@ -12,11 +12,96 @@ export declare class BrowserProcess implements Process {
12
12
  private userDataDir;
13
13
  private shouldCleanupUserDataDir;
14
14
  private isKilling;
15
+ /**
16
+ * Temp fingerprint-config file(s) written for this launch (the
17
+ * --blink-platform-overrides-file JSON). Tracked here so both the normal
18
+ * exit path and the SIGINT/SIGTERM signal handler can remove them.
19
+ */
20
+ private fingerprintConfigPaths;
21
+ /**
22
+ * Browser-level CDP WebSocket endpoint, parsed from the "DevTools listening
23
+ * on ws://…" line Chromium prints to stderr once its remote-debugging server
24
+ * is accepting connections. The launcher (browser.ts) drives this: it calls
25
+ * {@link resolveWsEndpoint} on the match and {@link rejectWsEndpoint} if the
26
+ * process dies first. Consumers await {@link wsEndpoint} instead of polling
27
+ * the TCP port - Puppeteer-style readiness. Settled exactly once.
28
+ */
29
+ private wsEndpointResolve;
30
+ private wsEndpointReject;
31
+ private wsEndpointSettled;
32
+ private readonly wsEndpointPromise;
15
33
  constructor(browserProcess: ChildProcess, userDataDir: string, shouldCleanupUserDataDir?: boolean);
34
+ /**
35
+ * The browser-level CDP WebSocket endpoint, resolved once the launcher parses
36
+ * the "DevTools listening on ws://…" line from Chromium's stderr, or rejected
37
+ * if the process exits/errors first. Awaited by the plugin's connect path in
38
+ * place of the former TCP port poll + fixed pre-connect delay.
39
+ */
40
+ wsEndpoint(): Promise<string>;
41
+ /**
42
+ * Called by the launcher when it matches the DevTools ws line on stderr.
43
+ * Idempotent - the first settlement (resolve or reject) wins.
44
+ */
45
+ resolveWsEndpoint(wsUrl: string): void;
46
+ /**
47
+ * Called by the launcher's process exit/error handlers when the process dies
48
+ * before the ws line appears. The error should carry the buffered stderr
49
+ * tail so a genuinely dead binary fails fast with an actionable message.
50
+ * Idempotent - a no-op once the endpoint has already settled.
51
+ */
52
+ rejectWsEndpoint(error: Error): void;
53
+ /**
54
+ * Record a temp fingerprint-config file that must be removed when this
55
+ * browser exits (or on signal). Called by the launcher after it writes the
56
+ * --blink-platform-overrides-file JSON.
57
+ */
58
+ trackFingerprintConfigPath(configPath: string): void;
59
+ /** Absolute path of the profile directory this browser launched with. */
60
+ getUserDataDir(): string;
61
+ /** Temp fingerprint-config files tracked for this launch (read-only copy). */
62
+ getFingerprintConfigPaths(): string[];
63
+ /**
64
+ * Signal the browser's whole process group, not just the parent PID.
65
+ *
66
+ * Chromium spawns helper processes (renderer / GPU / utility) that - unless
67
+ * the group is signalled - survive a SIGTERM/SIGKILL aimed only at the
68
+ * parent and reparent to launchd/init, leaking memory and (on macOS)
69
+ * exhausting IOSurface kernel clients until the GPU process aborts.
70
+ *
71
+ * Because the browser is spawned with `detached: true`, the parent is its
72
+ * own process-group leader (pgid === pid) and the helpers inherit that
73
+ * group, so `process.kill(-pid, signal)` reaps the entire tree in one shot.
74
+ * Falls back to signalling the parent alone if the group send throws
75
+ * (ESRCH = already gone, EPERM, or a platform where `detached` didn't take).
76
+ *
77
+ * NOTE the `pid > 1` guard: a failed spawn leaves `pid === -1`, and
78
+ * `process.kill(-(-1))` === `process.kill(1)` would signal init - never do
79
+ * that.
80
+ */
81
+ private signalGroup;
16
82
  close(): Promise<void>;
17
83
  kill(): Promise<void>;
18
84
  signal(signal?: NodeJS.Signals): void;
85
+ /**
86
+ * Synchronous teardown for the SIGINT/SIGTERM handler. A signal handler
87
+ * cannot await Promises - once it returns, Node re-raises the (now default)
88
+ * signal and exits, so any pending async fs work is dropped. This does the
89
+ * minimum synchronously: SIGKILL the browser (no graceful wait - we are
90
+ * being interrupted) and remove the temp profile dir + fingerprint config
91
+ * with the *Sync fs calls. Idempotent and never throws.
92
+ */
93
+ killAndCleanupSync(): void;
19
94
  private cleanup;
95
+ /** Remove every tracked temp fingerprint-config file (async path). */
96
+ private cleanupFingerprintConfigs;
97
+ /**
98
+ * Synchronous variant of cleanup() for the signal handler. Removes the temp
99
+ * fingerprint configs and the temp profile dir (honoring
100
+ * shouldCleanupUserDataDir) using blocking fs calls. Never throws.
101
+ */
102
+ private cleanupSync;
103
+ /** Blocking recursive delete used by the signal handler. Never throws. */
104
+ private forceDeleteDirectorySync;
20
105
  /**
21
106
  * Aggressively force delete the browser profile directory
22
107
  */
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/browser-process/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAMlD,MAAM,WAAW,OAAO;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CACzC;AAED,qBAAa,cAAe,YAAW,OAAO;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,CAAC,cAAc,CAAe;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,wBAAwB,CAAU;IAC1C,OAAO,CAAC,SAAS,CAAS;gBAEd,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,wBAAwB,UAAO;IAmBxF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,MAAM,CAAC,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,IAAI;YAOlC,OAAO;IAcrB;;OAEG;YACW,oBAAoB;IAmClC;;OAEG;YACW,uBAAuB;CAsBxC"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/browser-process/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AASlD,MAAM,WAAW,OAAO;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CACzC;AAED,qBAAa,cAAe,YAAW,OAAO;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,CAAC,cAAc,CAAe;IACrC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,wBAAwB,CAAU;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB,CAAgB;IAE9C;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAG/B;gBAES,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,wBAAwB,UAAO;IAgC9F;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC;;;OAGG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMtC;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAMpC;;;;OAIG;IACH,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAMpD,yEAAyE;IACzE,cAAc,IAAI,MAAM;IAIxB,8EAA8E;IAC9E,yBAAyB,IAAI,MAAM,EAAE;IAIrC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,WAAW;IAgBb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B,MAAM,CAAC,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,IAAI;IAKhD;;;;;;;OAOG;IACH,kBAAkB,IAAI,IAAI;YAOZ,OAAO;IAgBrB,sEAAsE;YACxD,yBAAyB;IAUvC;;;;OAIG;IACH,OAAO,CAAC,WAAW;IA2BnB,0EAA0E;IAC1E,OAAO,CAAC,wBAAwB;IAShC;;OAEG;YACW,oBAAoB;IAmClC;;OAEG;YACW,uBAAuB;CAsBxC"}
@@ -1,6 +1,8 @@
1
+ import fsSync from 'fs';
1
2
  import fs from 'fs/promises';
2
3
  import path from 'path';
3
4
  import { log } from '@crawlee/core';
5
+ import { registerLiveBrowserProcess, unregisterLiveBrowserProcess } from './signal-cleanup.js';
4
6
  export class BrowserProcess {
5
7
  constructor(browserProcess, userDataDir, shouldCleanupUserDataDir = true) {
6
8
  Object.defineProperty(this, "pid", {
@@ -39,62 +41,223 @@ export class BrowserProcess {
39
41
  writable: true,
40
42
  value: false
41
43
  });
44
+ /**
45
+ * Temp fingerprint-config file(s) written for this launch (the
46
+ * --blink-platform-overrides-file JSON). Tracked here so both the normal
47
+ * exit path and the SIGINT/SIGTERM signal handler can remove them.
48
+ */
49
+ Object.defineProperty(this, "fingerprintConfigPaths", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: []
54
+ });
55
+ /**
56
+ * Browser-level CDP WebSocket endpoint, parsed from the "DevTools listening
57
+ * on ws://…" line Chromium prints to stderr once its remote-debugging server
58
+ * is accepting connections. The launcher (browser.ts) drives this: it calls
59
+ * {@link resolveWsEndpoint} on the match and {@link rejectWsEndpoint} if the
60
+ * process dies first. Consumers await {@link wsEndpoint} instead of polling
61
+ * the TCP port - Puppeteer-style readiness. Settled exactly once.
62
+ */
63
+ Object.defineProperty(this, "wsEndpointResolve", {
64
+ enumerable: true,
65
+ configurable: true,
66
+ writable: true,
67
+ value: void 0
68
+ });
69
+ Object.defineProperty(this, "wsEndpointReject", {
70
+ enumerable: true,
71
+ configurable: true,
72
+ writable: true,
73
+ value: void 0
74
+ });
75
+ Object.defineProperty(this, "wsEndpointSettled", {
76
+ enumerable: true,
77
+ configurable: true,
78
+ writable: true,
79
+ value: false
80
+ });
81
+ Object.defineProperty(this, "wsEndpointPromise", {
82
+ enumerable: true,
83
+ configurable: true,
84
+ writable: true,
85
+ value: new Promise((resolve, reject) => {
86
+ this.wsEndpointResolve = resolve;
87
+ this.wsEndpointReject = reject;
88
+ })
89
+ });
42
90
  this.browserProcess = browserProcess;
43
91
  this.pid = browserProcess.pid || -1;
44
92
  this.userDataDir = userDataDir;
45
93
  this.shouldCleanupUserDataDir = shouldCleanupUserDataDir;
94
+ // Swallow the rejection on the stored promise so a launch that fails
95
+ // before anyone awaits wsEndpoint() (or one that resolves, making a
96
+ // later rejection a no-op) never surfaces as an unhandledRejection. The
97
+ // real awaiter still observes the settlement through its own await.
98
+ this.wsEndpointPromise.catch(() => { });
99
+ // Register in the process-wide signal-cleanup registry so a top-level
100
+ // SIGINT/SIGTERM can kill this browser and remove its temp files even
101
+ // when Crawlee's async teardown never gets a chance to run.
102
+ registerLiveBrowserProcess(this);
46
103
  this.onExit = new Promise((resolve) => {
47
104
  browserProcess.once('exit', () => {
105
+ unregisterLiveBrowserProcess(this);
48
106
  void this.cleanup().catch(log.debug);
49
107
  resolve();
50
108
  });
51
109
  browserProcess.once('error', (err) => {
110
+ unregisterLiveBrowserProcess(this);
52
111
  void this.cleanup().catch(log.debug);
53
112
  log.error(`Browser crashed: ${err.message}`);
54
113
  resolve();
55
114
  });
56
115
  });
57
116
  }
117
+ /**
118
+ * The browser-level CDP WebSocket endpoint, resolved once the launcher parses
119
+ * the "DevTools listening on ws://…" line from Chromium's stderr, or rejected
120
+ * if the process exits/errors first. Awaited by the plugin's connect path in
121
+ * place of the former TCP port poll + fixed pre-connect delay.
122
+ */
123
+ async wsEndpoint() {
124
+ return this.wsEndpointPromise;
125
+ }
126
+ /**
127
+ * Called by the launcher when it matches the DevTools ws line on stderr.
128
+ * Idempotent - the first settlement (resolve or reject) wins.
129
+ */
130
+ resolveWsEndpoint(wsUrl) {
131
+ if (this.wsEndpointSettled)
132
+ return;
133
+ this.wsEndpointSettled = true;
134
+ this.wsEndpointResolve(wsUrl);
135
+ }
136
+ /**
137
+ * Called by the launcher's process exit/error handlers when the process dies
138
+ * before the ws line appears. The error should carry the buffered stderr
139
+ * tail so a genuinely dead binary fails fast with an actionable message.
140
+ * Idempotent - a no-op once the endpoint has already settled.
141
+ */
142
+ rejectWsEndpoint(error) {
143
+ if (this.wsEndpointSettled)
144
+ return;
145
+ this.wsEndpointSettled = true;
146
+ this.wsEndpointReject(error);
147
+ }
148
+ /**
149
+ * Record a temp fingerprint-config file that must be removed when this
150
+ * browser exits (or on signal). Called by the launcher after it writes the
151
+ * --blink-platform-overrides-file JSON.
152
+ */
153
+ trackFingerprintConfigPath(configPath) {
154
+ if (configPath && !this.fingerprintConfigPaths.includes(configPath)) {
155
+ this.fingerprintConfigPaths.push(configPath);
156
+ }
157
+ }
158
+ /** Absolute path of the profile directory this browser launched with. */
159
+ getUserDataDir() {
160
+ return this.userDataDir;
161
+ }
162
+ /** Temp fingerprint-config files tracked for this launch (read-only copy). */
163
+ getFingerprintConfigPaths() {
164
+ return [...this.fingerprintConfigPaths];
165
+ }
166
+ /**
167
+ * Signal the browser's whole process group, not just the parent PID.
168
+ *
169
+ * Chromium spawns helper processes (renderer / GPU / utility) that - unless
170
+ * the group is signalled - survive a SIGTERM/SIGKILL aimed only at the
171
+ * parent and reparent to launchd/init, leaking memory and (on macOS)
172
+ * exhausting IOSurface kernel clients until the GPU process aborts.
173
+ *
174
+ * Because the browser is spawned with `detached: true`, the parent is its
175
+ * own process-group leader (pgid === pid) and the helpers inherit that
176
+ * group, so `process.kill(-pid, signal)` reaps the entire tree in one shot.
177
+ * Falls back to signalling the parent alone if the group send throws
178
+ * (ESRCH = already gone, EPERM, or a platform where `detached` didn't take).
179
+ *
180
+ * NOTE the `pid > 1` guard: a failed spawn leaves `pid === -1`, and
181
+ * `process.kill(-(-1))` === `process.kill(1)` would signal init - never do
182
+ * that.
183
+ */
184
+ signalGroup(signal) {
185
+ if (this.pid && this.pid > 1) {
186
+ try {
187
+ process.kill(-this.pid, signal);
188
+ return;
189
+ }
190
+ catch {
191
+ // Group gone / not a leader - fall through to parent-only.
192
+ }
193
+ }
194
+ try {
195
+ this.browserProcess.kill(signal);
196
+ }
197
+ catch {
198
+ // ESRCH - process already dead. Fine.
199
+ }
200
+ }
58
201
  async close() {
59
- if (!this.browserProcess.killed && !this.isKilling) {
202
+ if (!this.isKilling) {
60
203
  this.isKilling = true;
61
- this.browserProcess.kill();
204
+ // Orderly shutdown: SIGTERM the whole group (parent + helpers).
205
+ this.signalGroup('SIGTERM');
62
206
  }
63
- // Add timeout to prevent hanging indefinitely
64
- await Promise.race([
65
- this.onExit,
66
- new Promise((resolve) => setTimeout(() => {
67
- log.warning('Browser process close timeout, force killing');
68
- this.signal('SIGKILL');
69
- resolve();
70
- }, 5000)),
207
+ // Wait for graceful exit; if it never comes, SIGKILL the group. Unlike
208
+ // the old code, this escalation actually fires - it is no longer gated
209
+ // on `browserProcess.killed` (which SIGTERM had already set to true,
210
+ // making the force-kill a silent no-op and leaking the process tree).
211
+ const exited = await Promise.race([
212
+ this.onExit.then(() => true),
213
+ new Promise((resolve) => setTimeout(() => resolve(false), 2000)),
71
214
  ]);
215
+ if (!exited) {
216
+ log.warning('Browser process close timeout, force killing process group');
217
+ this.signalGroup('SIGKILL');
218
+ await Promise.race([
219
+ this.onExit,
220
+ new Promise((resolve) => setTimeout(resolve, 1000)),
221
+ ]);
222
+ }
72
223
  // Ensure cleanup happens after process closes
73
224
  await this.cleanup();
74
225
  }
75
226
  async kill() {
76
- if (!this.browserProcess.killed && !this.isKilling) {
77
- this.isKilling = true;
78
- this.signal('SIGKILL');
79
- }
227
+ this.isKilling = true;
228
+ // Force path: SIGKILL the whole group immediately.
229
+ this.signalGroup('SIGKILL');
80
230
  // Add timeout for kill operation
81
231
  await Promise.race([
82
232
  this.onExit,
83
233
  new Promise((resolve) => setTimeout(() => {
84
234
  log.warning('Browser process kill timeout');
85
235
  resolve();
86
- }, 3000)),
236
+ }, 1000)),
87
237
  ]);
88
238
  // Ensure cleanup happens after process is killed
89
239
  await this.cleanup();
90
240
  }
91
241
  signal(signal = 'SIGTERM') {
92
- if (!this.browserProcess.killed) {
93
- this.browserProcess.kill(signal);
94
- void this.onExit;
95
- }
242
+ this.signalGroup(signal);
243
+ void this.onExit;
244
+ }
245
+ /**
246
+ * Synchronous teardown for the SIGINT/SIGTERM handler. A signal handler
247
+ * cannot await Promises - once it returns, Node re-raises the (now default)
248
+ * signal and exits, so any pending async fs work is dropped. This does the
249
+ * minimum synchronously: SIGKILL the browser (no graceful wait - we are
250
+ * being interrupted) and remove the temp profile dir + fingerprint config
251
+ * with the *Sync fs calls. Idempotent and never throws.
252
+ */
253
+ killAndCleanupSync() {
254
+ // SIGKILL the whole group (parent + Chromium helpers), not just the
255
+ // parent - otherwise Ctrl+C leaves orphaned renderer/GPU processes.
256
+ this.signalGroup('SIGKILL');
257
+ this.cleanupSync();
96
258
  }
97
259
  async cleanup() {
260
+ await this.cleanupFingerprintConfigs();
98
261
  if (!this.shouldCleanupUserDataDir) {
99
262
  log.debug(`Preserving browser profile directory: ${this.userDataDir}`);
100
263
  return;
@@ -107,6 +270,59 @@ export class BrowserProcess {
107
270
  log.warning(`Could not delete browser profile dir: ${e.message}`);
108
271
  }
109
272
  }
273
+ /** Remove every tracked temp fingerprint-config file (async path). */
274
+ async cleanupFingerprintConfigs() {
275
+ for (const configPath of this.fingerprintConfigPaths) {
276
+ try {
277
+ await fs.unlink(configPath);
278
+ }
279
+ catch {
280
+ // Non-fatal - temp file will be cleaned by the OS eventually.
281
+ }
282
+ }
283
+ }
284
+ /**
285
+ * Synchronous variant of cleanup() for the signal handler. Removes the temp
286
+ * fingerprint configs and the temp profile dir (honoring
287
+ * shouldCleanupUserDataDir) using blocking fs calls. Never throws.
288
+ */
289
+ cleanupSync() {
290
+ for (const configPath of this.fingerprintConfigPaths) {
291
+ try {
292
+ fsSync.unlinkSync(configPath);
293
+ }
294
+ catch {
295
+ // ENOENT / already removed - ignore.
296
+ }
297
+ }
298
+ if (!this.shouldCleanupUserDataDir)
299
+ return;
300
+ // Remove the profile dir and any earlier .cleanup-* rename fallbacks the
301
+ // async path may have left behind in the same parent.
302
+ this.forceDeleteDirectorySync(this.userDataDir);
303
+ try {
304
+ const parentDir = path.dirname(this.userDataDir);
305
+ const base = path.basename(this.userDataDir);
306
+ for (const entry of fsSync.readdirSync(parentDir)) {
307
+ if (entry.startsWith('.cleanup-') && entry.endsWith(base)) {
308
+ this.forceDeleteDirectorySync(path.join(parentDir, entry));
309
+ }
310
+ }
311
+ }
312
+ catch {
313
+ // Parent unreadable - nothing more we can do synchronously.
314
+ }
315
+ }
316
+ /** Blocking recursive delete used by the signal handler. Never throws. */
317
+ forceDeleteDirectorySync(dirPath) {
318
+ try {
319
+ fsSync.rmSync(dirPath, { recursive: true, force: true });
320
+ }
321
+ catch {
322
+ // Best-effort - a stuck handle on Windows may block this, but we
323
+ // are exiting anyway so the OS reclaims the temp dir.
324
+ }
325
+ }
110
326
  /**
111
327
  * Aggressively force delete the browser profile directory
112
328
  */
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/browser-process/process.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AASpC,MAAM,OAAO,cAAc;IAQvB,YAAY,cAA4B,EAAE,WAAmB,EAAE,wBAAwB,GAAG,IAAI;QAP9F;;;;;WAAY;QACZ;;;;;WAAsB;QACd;;;;;WAA6B;QAC7B;;;;;WAAoB;QACpB;;;;;WAAkC;QAClC;;;;mBAAY,KAAK;WAAC;QAGtB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,wBAAwB,GAAG,wBAAwB,CAAC;QAEzD,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACxC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;gBAC7B,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACrC,OAAO,EAAE,CAAC;YACd,CAAC,CAAC,CAAC;YACH,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACjC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACrC,GAAG,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7C,OAAO,EAAE,CAAC;YACd,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACjD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QAED,8CAA8C;QAC9C,MAAM,OAAO,CAAC,IAAI,CAAC;YACf,IAAI,CAAC,MAAM;YACX,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAC1B,UAAU,CAAC,GAAG,EAAE;gBACZ,GAAG,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC;gBAC5D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACvB,OAAO,EAAE,CAAC;YACd,CAAC,EAAE,IAAI,CAAC,CACX;SACJ,CAAC,CAAC;QAEH,8CAA8C;QAC9C,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,IAAI;QACN,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACjD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,CAAC,IAAI,CAAC;YACf,IAAI,CAAC,MAAM;YACX,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAC1B,UAAU,CAAC,GAAG,EAAE;gBACZ,GAAG,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;gBAC5C,OAAO,EAAE,CAAC;YACd,CAAC,EAAE,IAAI,CAAC,CACX;SACJ,CAAC,CAAC;QAEH,iDAAiD;QACjD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,SAAyB,SAAS;QACrC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,KAAK,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,OAAO;QACjB,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACjC,GAAG,CAAC,KAAK,CAAC,yCAAyC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACvE,OAAO;QACX,CAAC;QAED,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAClD,GAAG,CAAC,KAAK,CAAC,sDAAsD,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YACd,GAAG,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB,CAAC,OAAe;QAC9C,IAAI,CAAC;YACD,4BAA4B;YAC5B,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACL,+CAA+C;YAC/C,OAAO;QACX,CAAC;QAED,mCAAmC;QACnC,IAAI,CAAC;YACD,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,OAAO;QACX,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,GAAG,CAAC,KAAK,CAAC,4BAA4B,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;QACtF,CAAC;QAED,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,qEAAqE;YACrE,IAAI,CAAC;gBACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACxC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC3F,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;gBACrC,GAAG,CAAC,KAAK,CAAC,oDAAoD,UAAU,EAAE,CAAC,CAAC;YAChF,CAAC;YAAC,OAAO,WAAgB,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9E,CAAC;QACL,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,uBAAuB,CAAC,OAAe;QACjD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAEhD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,MAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;gBAC7C,IAAI,CAAC;oBACD,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7B,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBAClB,GAAG,CAAC,KAAK,CAAC,8BAA8B,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC1E,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC;oBACD,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBAClB,GAAG,CAAC,KAAK,CAAC,yBAAyB,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBACrE,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;CACJ"}
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/browser-process/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * signal-cleanup.ts - process-wide SIGINT/SIGTERM teardown for live browsers.
3
+ *
4
+ * Problem (issue #73): when a crawl is interrupted with Ctrl+C (SIGINT) or
5
+ * SIGTERM, Node runs no graceful teardown of its own. Crawlee's async
6
+ * BrowserController._close()/_kill() and BrowserProcess.cleanup() only fire on
7
+ * normal shutdown; on a signal the process exits before that async work
8
+ * completes, leaving an orphaned Chromium plus leftover temp `userDataDir`
9
+ * directories (and their `.cleanup-*` rename fallbacks) and temp
10
+ * fingerprint-config files in the working directory.
11
+ *
12
+ * Fix: keep a registry of live BrowserProcess instances and install ONE
13
+ * idempotent handler per signal. On the signal we synchronously SIGKILL each
14
+ * registered browser and remove its temp files (a signal handler cannot await
15
+ * Promises - see BrowserProcess.killAndCleanupSync), then re-raise the signal
16
+ * after detaching only our own listeners. Whoever else is listening decides what
17
+ * happens next: with no listener left Node applies the default disposition and
18
+ * the process exits 128+signo; with Crawlee's own SIGINT handler still attached
19
+ * that handler runs and owns the exit. Either way our browsers are already gone.
20
+ *
21
+ * Design constraints honored:
22
+ * - Idempotent: a second signal while we're already tearing down is ignored.
23
+ * - Registered at most once per process, regardless of how many browsers /
24
+ * crawlers spin up.
25
+ * - Does not swallow or block Crawlee's own shutdown: we only ADD listeners
26
+ * (never removeAllListeners), and re-raise rather than calling process.exit,
27
+ * so a remaining Crawlee/host handler still gets to run its own teardown.
28
+ */
29
+ /** Minimal shape the registry needs - avoids an import cycle with process.ts. */
30
+ export interface SignalCleanupTarget {
31
+ pid: number;
32
+ killAndCleanupSync(): void;
33
+ }
34
+ /** Add a live browser to the signal-cleanup registry. */
35
+ export declare function registerLiveBrowserProcess(target: SignalCleanupTarget): void;
36
+ /** Remove a browser from the registry once it has exited (normal path). */
37
+ export declare function unregisterLiveBrowserProcess(target: SignalCleanupTarget): void;
38
+ /** Test seam: current number of live registered browsers. */
39
+ export declare function liveBrowserProcessCount(): number;
40
+ //# sourceMappingURL=signal-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-cleanup.d.ts","sourceRoot":"","sources":["../../src/browser-process/signal-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB,IAAI,IAAI,CAAC;CAC9B;AAsED,yDAAyD;AACzD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAG5E;AAED,2EAA2E;AAC3E,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAE9E;AAED,6DAA6D;AAC7D,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * signal-cleanup.ts - process-wide SIGINT/SIGTERM teardown for live browsers.
3
+ *
4
+ * Problem (issue #73): when a crawl is interrupted with Ctrl+C (SIGINT) or
5
+ * SIGTERM, Node runs no graceful teardown of its own. Crawlee's async
6
+ * BrowserController._close()/_kill() and BrowserProcess.cleanup() only fire on
7
+ * normal shutdown; on a signal the process exits before that async work
8
+ * completes, leaving an orphaned Chromium plus leftover temp `userDataDir`
9
+ * directories (and their `.cleanup-*` rename fallbacks) and temp
10
+ * fingerprint-config files in the working directory.
11
+ *
12
+ * Fix: keep a registry of live BrowserProcess instances and install ONE
13
+ * idempotent handler per signal. On the signal we synchronously SIGKILL each
14
+ * registered browser and remove its temp files (a signal handler cannot await
15
+ * Promises - see BrowserProcess.killAndCleanupSync), then re-raise the signal
16
+ * after detaching only our own listeners. Whoever else is listening decides what
17
+ * happens next: with no listener left Node applies the default disposition and
18
+ * the process exits 128+signo; with Crawlee's own SIGINT handler still attached
19
+ * that handler runs and owns the exit. Either way our browsers are already gone.
20
+ *
21
+ * Design constraints honored:
22
+ * - Idempotent: a second signal while we're already tearing down is ignored.
23
+ * - Registered at most once per process, regardless of how many browsers /
24
+ * crawlers spin up.
25
+ * - Does not swallow or block Crawlee's own shutdown: we only ADD listeners
26
+ * (never removeAllListeners), and re-raise rather than calling process.exit,
27
+ * so a remaining Crawlee/host handler still gets to run its own teardown.
28
+ */
29
+ import { log } from '@crawlee/core';
30
+ const liveBrowserProcesses = new Set();
31
+ let handlersInstalled = false;
32
+ let tearingDown = false;
33
+ const HANDLED_SIGNALS = ['SIGINT', 'SIGTERM'];
34
+ /**
35
+ * Run the synchronous teardown for every live browser, then re-raise the
36
+ * signal with its default disposition so the process exits normally. Guarded so
37
+ * a repeated signal during teardown does not re-enter.
38
+ */
39
+ function handleSignal(signal) {
40
+ if (tearingDown)
41
+ return;
42
+ tearingDown = true;
43
+ const targets = [...liveBrowserProcesses];
44
+ log.debug(`[signal-cleanup] ${signal} received - tearing down ${targets.length} browser(s)`);
45
+ for (const target of targets) {
46
+ try {
47
+ target.killAndCleanupSync();
48
+ }
49
+ catch {
50
+ // killAndCleanupSync is already best-effort; never let one browser
51
+ // block cleanup of the others.
52
+ }
53
+ finally {
54
+ liveBrowserProcesses.delete(target);
55
+ }
56
+ }
57
+ // Re-raise the signal, having first removed only OUR listeners so this does
58
+ // not re-enter handleSignal.
59
+ //
60
+ // What this actually does depends on who else is listening. Node applies a
61
+ // signal's default disposition only when NO listener remains, so:
62
+ // - we are the last listener -> default action runs, process exits 128+signo
63
+ // - Crawlee (or the host app) still has a SIGINT/SIGTERM listener -> that
64
+ // handler runs instead, and IT owns the exit
65
+ // The second case is the common one and is intentional: our browsers are
66
+ // already dead by this point, so handing control back to Crawlee's own
67
+ // graceful shutdown is the correct outcome. What we must not do is exit the
68
+ // process ourselves and cut that shutdown short.
69
+ for (const sig of HANDLED_SIGNALS) {
70
+ process.removeListener(sig, signalListeners[sig]);
71
+ }
72
+ process.kill(process.pid, signal);
73
+ }
74
+ const signalListeners = {};
75
+ /**
76
+ * Install the SIGINT/SIGTERM handlers exactly once for this process. Called
77
+ * lazily the first time a BrowserProcess registers, so processes that never
78
+ * launch a browser pay nothing.
79
+ */
80
+ function ensureHandlersInstalled() {
81
+ if (handlersInstalled)
82
+ return;
83
+ handlersInstalled = true;
84
+ for (const sig of HANDLED_SIGNALS) {
85
+ const listener = () => handleSignal(sig);
86
+ signalListeners[sig] = listener;
87
+ // Additive: do not clobber Crawlee's or the host app's own handlers.
88
+ process.on(sig, listener);
89
+ }
90
+ log.debug('[signal-cleanup] Installed SIGINT/SIGTERM handlers');
91
+ }
92
+ /** Add a live browser to the signal-cleanup registry. */
93
+ export function registerLiveBrowserProcess(target) {
94
+ ensureHandlersInstalled();
95
+ liveBrowserProcesses.add(target);
96
+ }
97
+ /** Remove a browser from the registry once it has exited (normal path). */
98
+ export function unregisterLiveBrowserProcess(target) {
99
+ liveBrowserProcesses.delete(target);
100
+ }
101
+ /** Test seam: current number of live registered browsers. */
102
+ export function liveBrowserProcessCount() {
103
+ return liveBrowserProcesses.size;
104
+ }
105
+ //# sourceMappingURL=signal-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-cleanup.js","sourceRoot":"","sources":["../../src/browser-process/signal-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAQpC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAuB,CAAC;AAE5D,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAC9B,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,MAAM,eAAe,GAAqB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEhE;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAAsB;IACxC,IAAI,WAAW;QAAE,OAAO;IACxB,WAAW,GAAG,IAAI,CAAC;IAEnB,MAAM,OAAO,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAC1C,GAAG,CAAC,KAAK,CAAC,oBAAoB,MAAM,4BAA4B,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC;IAE7F,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACD,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACL,mEAAmE;YACnE,+BAA+B;QACnC,CAAC;gBAAS,CAAC;YACP,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,6BAA6B;IAC7B,EAAE;IACF,2EAA2E;IAC3E,kEAAkE;IAClE,gFAAgF;IAChF,4EAA4E;IAC5E,iDAAiD;IACjD,yEAAyE;IACzE,uEAAuE;IACvE,4EAA4E;IAC5E,iDAAiD;IACjD,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAChC,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,eAAe,GAAqD,EAAE,CAAC;AAE7E;;;;GAIG;AACH,SAAS,uBAAuB;IAC5B,IAAI,iBAAiB;QAAE,OAAO;IAC9B,iBAAiB,GAAG,IAAI,CAAC;IAEzB,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACzC,eAAe,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;QAChC,qEAAqE;QACrE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC9B,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACpE,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,0BAA0B,CAAC,MAA2B;IAClE,uBAAuB,EAAE,CAAC;IAC1B,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,4BAA4B,CAAC,MAA2B;IACpE,oBAAoB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,uBAAuB;IACnC,OAAO,oBAAoB,CAAC,IAAI,CAAC;AACrC,CAAC"}