brave-real-browser 1.5.95 → 1.5.98

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.
@@ -0,0 +1,131 @@
1
+ # Brave-Real-Browser - Critical Fixes Needed
2
+
3
+ ## ⚠️ Note
4
+ यह project compiled JavaScript library है। Source code इस repository में नहीं है।
5
+
6
+ ## Critical Issues to Fix in Source Repository
7
+
8
+ ### 1. **pageController Error Handling** (Critical)
9
+ **File**: `src/module/pageController.js` (in source repo)
10
+ **Issue**: Promise rejections और errors को properly handle नहीं किया गया
11
+ **Fix Required**:
12
+ ```javascript
13
+ // Add comprehensive try-catch blocks
14
+ async function handleNavigation(page, url) {
15
+ try {
16
+ await page.goto(url, { timeout: 30000 });
17
+ } catch (error) {
18
+ if (error.name === 'TimeoutError') {
19
+ throw new NavigationTimeoutError(`Navigation to ${url} timed out`);
20
+ }
21
+ throw new NavigationError(`Failed to navigate: ${error.message}`);
22
+ }
23
+ }
24
+
25
+ // Add error recovery mechanisms
26
+ async function withRetry(fn, maxRetries = 3) {
27
+ for (let i = 0; i < maxRetries; i++) {
28
+ try {
29
+ return await fn();
30
+ } catch (error) {
31
+ if (i === maxRetries - 1) throw error;
32
+ await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### 2. **turnstileSolver Infinite Loop Fix** (Critical)
39
+ **File**: `src/module/turnstileSolver.js` (in source repo)
40
+ **Issue**: Infinite loop possibility without timeout or max attempts
41
+ **Fix Required**:
42
+ ```javascript
43
+ async function solveTurnstile(page, maxAttempts = 10, timeout = 60000) {
44
+ const startTime = Date.now();
45
+ let attempts = 0;
46
+
47
+ while (attempts < maxAttempts) {
48
+ // Check timeout
49
+ if (Date.now() - startTime > timeout) {
50
+ throw new TurnstileTimeoutError('Turnstile solving timed out');
51
+ }
52
+
53
+ attempts++;
54
+
55
+ try {
56
+ const solved = await attemptSolve(page);
57
+ if (solved) return true;
58
+ } catch (error) {
59
+ console.error(`Turnstile attempt ${attempts} failed:`, error);
60
+ }
61
+
62
+ // Exponential backoff
63
+ await new Promise(resolve =>
64
+ setTimeout(resolve, Math.min(1000 * Math.pow(2, attempts), 10000))
65
+ );
66
+ }
67
+
68
+ throw new TurnstileMaxAttemptsError(`Failed after ${maxAttempts} attempts`);
69
+ }
70
+ ```
71
+
72
+ ### 3. **Proxy Validation** (Medium)
73
+ **Fix Required**:
74
+ ```javascript
75
+ function validateProxy(proxy) {
76
+ if (!proxy) return null;
77
+
78
+ const proxyRegex = /^(https?|socks[45]):\/\/([^:]+):(\d+)$/;
79
+ if (!proxyRegex.test(proxy)) {
80
+ throw new Error('Invalid proxy format. Expected: protocol://host:port');
81
+ }
82
+
83
+ return proxy;
84
+ }
85
+ ```
86
+
87
+ ### 4. **Better Error Messages**
88
+ ```javascript
89
+ class BrowserError extends Error {
90
+ constructor(message, code, details) {
91
+ super(message);
92
+ this.name = 'BrowserError';
93
+ this.code = code;
94
+ this.details = details;
95
+ }
96
+ }
97
+
98
+ class NavigationError extends BrowserError {
99
+ constructor(message, details) {
100
+ super(message, 'NAVIGATION_ERROR', details);
101
+ this.name = 'NavigationError';
102
+ }
103
+ }
104
+ ```
105
+
106
+ ## Version Synchronization Required
107
+ - Current version: 1.5.95
108
+ - Should match: brave-real-launcher, brave-real-puppeteer-core
109
+ - Add version validation on startup
110
+
111
+ ## Dependencies to Update
112
+ ```json
113
+ {
114
+ "dependencies": {
115
+ "brave-real-launcher": "^1.5.95",
116
+ "brave-real-puppeteer-core": "^1.5.95"
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## Testing Requirements
122
+ 1. Add unit tests for error handling
123
+ 2. Add integration tests with other components
124
+ 3. Add timeout/infinite loop regression tests
125
+ 4. Add proxy validation tests
126
+
127
+ ## Documentation Needed
128
+ 1. API documentation
129
+ 2. Error handling guide
130
+ 3. Configuration examples
131
+ 4. Troubleshooting guide
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Error Handling System for Brave Real Browser
3
+ *
4
+ * Provides standardized error classes and utilities for browser operations
5
+ */
6
+
7
+ // ============================================================================
8
+ // ERROR CATEGORIES
9
+ // ============================================================================
10
+
11
+ const ErrorCategory = {
12
+ // Browser connection errors
13
+ CONNECTION_FAILED: 'CONNECTION_FAILED',
14
+ BROWSER_LAUNCH_FAILED: 'BROWSER_LAUNCH_FAILED',
15
+ BROWSER_CLOSED: 'BROWSER_CLOSED',
16
+
17
+ // Page errors
18
+ PAGE_CREATION_FAILED: 'PAGE_CREATION_FAILED',
19
+ PAGE_CLOSED: 'PAGE_CLOSED',
20
+
21
+ // Plugin errors
22
+ PLUGIN_LOAD_FAILED: 'PLUGIN_LOAD_FAILED',
23
+
24
+ // Proxy errors
25
+ PROXY_CONNECTION_FAILED: 'PROXY_CONNECTION_FAILED',
26
+ INVALID_PROXY_CONFIG: 'INVALID_PROXY_CONFIG',
27
+
28
+ // System errors
29
+ XVFB_INIT_FAILED: 'XVFB_INIT_FAILED',
30
+ PORT_UNAVAILABLE: 'PORT_UNAVAILABLE',
31
+
32
+ // Protocol errors
33
+ PROTOCOL_ERROR: 'PROTOCOL_ERROR',
34
+ TIMEOUT_ERROR: 'TIMEOUT_ERROR',
35
+
36
+ // Unknown
37
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR',
38
+ };
39
+
40
+ // ============================================================================
41
+ // ERROR SEVERITY LEVELS
42
+ // ============================================================================
43
+
44
+ const ErrorSeverity = {
45
+ LOW: 'LOW',
46
+ MEDIUM: 'MEDIUM',
47
+ HIGH: 'HIGH',
48
+ CRITICAL: 'CRITICAL',
49
+ };
50
+
51
+ // ============================================================================
52
+ // CUSTOM ERROR CLASS
53
+ // ============================================================================
54
+
55
+ /**
56
+ * Base error class for Brave Real Browser errors
57
+ */
58
+ class BraveRealBrowserError extends Error {
59
+ constructor(
60
+ message,
61
+ category = ErrorCategory.UNKNOWN_ERROR,
62
+ severity = ErrorSeverity.MEDIUM,
63
+ isRecoverable = false,
64
+ context = {}
65
+ ) {
66
+ super(message);
67
+ this.name = 'BraveRealBrowserError';
68
+ this.category = category;
69
+ this.severity = severity;
70
+ this.isRecoverable = isRecoverable;
71
+ this.timestamp = new Date();
72
+ this.context = context;
73
+
74
+ // Maintains proper stack trace
75
+ if (Error.captureStackTrace) {
76
+ Error.captureStackTrace(this, this.constructor);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Convert error to user-friendly message
82
+ */
83
+ toUserMessage() {
84
+ let message = `❌ ${this.message}\n\n`;
85
+ message += `📋 Category: ${this.category}\n`;
86
+ message += `⚠️ Severity: ${this.severity}\n`;
87
+
88
+ if (Object.keys(this.context).length > 0) {
89
+ message += `\n🔍 Context: ${JSON.stringify(this.context, null, 2)}\n`;
90
+ }
91
+
92
+ return message;
93
+ }
94
+ }
95
+
96
+ // ============================================================================
97
+ // ERROR FACTORY FUNCTIONS
98
+ // ============================================================================
99
+
100
+ /**
101
+ * Create browser launch error
102
+ */
103
+ function createBrowserLaunchError(originalError, context = {}) {
104
+ return new BraveRealBrowserError(
105
+ `Failed to launch browser: ${originalError.message}`,
106
+ ErrorCategory.BROWSER_LAUNCH_FAILED,
107
+ ErrorSeverity.CRITICAL,
108
+ false,
109
+ { originalError: originalError.message, ...context }
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Create connection error
115
+ */
116
+ function createConnectionError(url, originalError) {
117
+ return new BraveRealBrowserError(
118
+ `Failed to connect to browser at ${url}: ${originalError.message}`,
119
+ ErrorCategory.CONNECTION_FAILED,
120
+ ErrorSeverity.CRITICAL,
121
+ true,
122
+ { url, originalError: originalError.message }
123
+ );
124
+ }
125
+
126
+ /**
127
+ * Create page creation error
128
+ */
129
+ function createPageCreationError(originalError) {
130
+ return new BraveRealBrowserError(
131
+ `Failed to create or access page: ${originalError.message}`,
132
+ ErrorCategory.PAGE_CREATION_FAILED,
133
+ ErrorSeverity.HIGH,
134
+ true,
135
+ { originalError: originalError.message }
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Create plugin load error
141
+ */
142
+ function createPluginLoadError(pluginName, originalError) {
143
+ return new BraveRealBrowserError(
144
+ `Failed to load plugin '${pluginName}': ${originalError.message}`,
145
+ ErrorCategory.PLUGIN_LOAD_FAILED,
146
+ ErrorSeverity.MEDIUM,
147
+ true,
148
+ { pluginName, originalError: originalError.message }
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Create proxy error
154
+ */
155
+ function createProxyError(proxyConfig, originalError) {
156
+ return new BraveRealBrowserError(
157
+ `Proxy connection failed: ${originalError.message}`,
158
+ ErrorCategory.PROXY_CONNECTION_FAILED,
159
+ ErrorSeverity.HIGH,
160
+ true,
161
+ { proxyConfig, originalError: originalError.message }
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Create XVFB initialization error
167
+ */
168
+ function createXvfbError(originalError) {
169
+ return new BraveRealBrowserError(
170
+ `Failed to initialize Xvfb: ${originalError.message}`,
171
+ ErrorCategory.XVFB_INIT_FAILED,
172
+ ErrorSeverity.MEDIUM,
173
+ false,
174
+ {
175
+ originalError: originalError.message,
176
+ suggestion: 'Install xvfb with: sudo apt-get install xvfb'
177
+ }
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Create port unavailable error
183
+ */
184
+ function createPortUnavailableError(port) {
185
+ return new BraveRealBrowserError(
186
+ `Port ${port} is not available`,
187
+ ErrorCategory.PORT_UNAVAILABLE,
188
+ ErrorSeverity.HIGH,
189
+ true,
190
+ { port }
191
+ );
192
+ }
193
+
194
+ // ============================================================================
195
+ // ERROR CATEGORIZATION
196
+ // ============================================================================
197
+
198
+ /**
199
+ * Categorize a generic error into BraveRealBrowserError
200
+ */
201
+ function categorizeError(error) {
202
+ // Already a BraveRealBrowserError
203
+ if (error instanceof BraveRealBrowserError) {
204
+ return error;
205
+ }
206
+
207
+ // Convert to Error if not already
208
+ const err = error instanceof Error ? error : new Error(String(error));
209
+ const message = err.message.toLowerCase();
210
+
211
+ // Browser launch errors
212
+ if (message.includes('failed to launch') || message.includes('chrome') && message.includes('launch')) {
213
+ return createBrowserLaunchError(err);
214
+ }
215
+
216
+ // Connection errors
217
+ if (message.includes('econnrefused') || message.includes('connection refused')) {
218
+ return new BraveRealBrowserError(
219
+ err.message,
220
+ ErrorCategory.CONNECTION_FAILED,
221
+ ErrorSeverity.CRITICAL,
222
+ true,
223
+ { originalError: err.message }
224
+ );
225
+ }
226
+
227
+ // Protocol errors
228
+ if (message.includes('protocol error') || message.includes('target closed')) {
229
+ return new BraveRealBrowserError(
230
+ err.message,
231
+ ErrorCategory.PROTOCOL_ERROR,
232
+ ErrorSeverity.HIGH,
233
+ true,
234
+ { originalError: err.message }
235
+ );
236
+ }
237
+
238
+ // Timeout errors
239
+ if (message.includes('timeout')) {
240
+ return new BraveRealBrowserError(
241
+ err.message,
242
+ ErrorCategory.TIMEOUT_ERROR,
243
+ ErrorSeverity.MEDIUM,
244
+ true,
245
+ { originalError: err.message }
246
+ );
247
+ }
248
+
249
+ // Port errors
250
+ if (message.includes('eaddrinuse') || message.includes('address in use')) {
251
+ return new BraveRealBrowserError(
252
+ err.message,
253
+ ErrorCategory.PORT_UNAVAILABLE,
254
+ ErrorSeverity.HIGH,
255
+ true,
256
+ { originalError: err.message }
257
+ );
258
+ }
259
+
260
+ // Default unknown error
261
+ return new BraveRealBrowserError(
262
+ err.message,
263
+ ErrorCategory.UNKNOWN_ERROR,
264
+ ErrorSeverity.MEDIUM,
265
+ false,
266
+ { originalError: err.message, stack: err.stack }
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Check if error is recoverable
272
+ */
273
+ function isRecoverableError(error) {
274
+ if (error instanceof BraveRealBrowserError) {
275
+ return error.isRecoverable;
276
+ }
277
+
278
+ const categorized = categorizeError(error);
279
+ return categorized.isRecoverable;
280
+ }
281
+
282
+ // ============================================================================
283
+ // EXPORTS
284
+ // ============================================================================
285
+
286
+ module.exports = {
287
+ ErrorCategory,
288
+ ErrorSeverity,
289
+ BraveRealBrowserError,
290
+ createBrowserLaunchError,
291
+ createConnectionError,
292
+ createPageCreationError,
293
+ createPluginLoadError,
294
+ createProxyError,
295
+ createXvfbError,
296
+ createPortUnavailableError,
297
+ categorizeError,
298
+ isRecoverableError,
299
+ };
@@ -2,6 +2,11 @@ const { createCursor } = require('ghost-cursor');
2
2
  const { checkTurnstile } = require('./turnstile.js');
3
3
  const kill = require('tree-kill');
4
4
 
5
+ // Configuration constants
6
+ const TURNSTILE_CHECK_INTERVAL = 1000; // 1 second
7
+ const TURNSTILE_MAX_ATTEMPTS = 300; // 5 minutes max (300 attempts * 1 second)
8
+ const TURNSTILE_TIMEOUT = 300000; // 5 minutes in milliseconds
9
+
5
10
  function getRandomInt(min, max) {
6
11
  min = Math.ceil(min);
7
12
  max = Math.floor(max);
@@ -10,60 +15,162 @@ function getRandomInt(min, max) {
10
15
 
11
16
  async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) {
12
17
 
13
- let solveStatus = turnstile
18
+ let solveStatus = turnstile;
19
+ let turnstileSolverRunning = false;
20
+
21
+ // Safe cleanup function
22
+ const cleanup = async () => {
23
+ solveStatus = false;
24
+ if (killProcess === true) {
25
+ if (xvfbsession) {
26
+ try {
27
+ xvfbsession.stopSync();
28
+ } catch (err) {
29
+ console.error('Error stopping Xvfb session:', err.message);
30
+ }
31
+ }
32
+ if (chrome) {
33
+ try {
34
+ chrome.kill();
35
+ } catch (err) {
36
+ console.error('Error killing Chrome process:', err.message);
37
+ }
38
+ }
39
+ if (pid) {
40
+ try {
41
+ kill(pid, 'SIGKILL', (err) => {
42
+ if (err) console.error('Error killing process:', err.message);
43
+ });
44
+ } catch (err) {
45
+ console.error('Error in kill command:', err.message);
46
+ }
47
+ }
48
+ }
49
+ };
14
50
 
15
51
  page.on('close', () => {
16
- solveStatus = false
52
+ solveStatus = false;
17
53
  });
18
54
 
19
-
20
55
  browser.on('disconnected', async () => {
21
- solveStatus = false
22
- if (killProcess === true) {
23
- if (xvfbsession) try { xvfbsession.stopSync() } catch (err) { }
24
- if (chrome) try { chrome.kill() } catch (err) { console.log(err); }
25
- if (pid) try { kill(pid, 'SIGKILL', () => { }) } catch (err) { }
26
- }
56
+ await cleanup();
27
57
  });
28
58
 
59
+ // Enhanced turnstile solver with timeout and max attempts protection
29
60
  async function turnstileSolver() {
30
- while (solveStatus) {
31
- await checkTurnstile({ page }).catch(() => { });
32
- await new Promise(r => setTimeout(r, 1000));
61
+ if (turnstileSolverRunning) {
62
+ console.warn('Turnstile solver already running, skipping duplicate start');
63
+ return;
33
64
  }
34
- return
35
- }
65
+
66
+ turnstileSolverRunning = true;
67
+ const startTime = Date.now();
68
+ let attempts = 0;
69
+
70
+ try {
71
+ while (solveStatus && attempts < TURNSTILE_MAX_ATTEMPTS) {
72
+ // Check timeout
73
+ if (Date.now() - startTime > TURNSTILE_TIMEOUT) {
74
+ console.warn('Turnstile solver timeout reached after', attempts, 'attempts');
75
+ break;
76
+ }
77
+
78
+ // Check if page is still valid
79
+ if (page.isClosed()) {
80
+ console.log('Page closed, stopping turnstile solver');
81
+ break;
82
+ }
83
+
84
+ attempts++;
85
+
86
+ try {
87
+ await checkTurnstile({ page });
88
+ } catch (err) {
89
+ // Silent fail for individual checks, log only if debug enabled
90
+ if (process.env.DEBUG === 'true') {
91
+ console.debug('Turnstile check failed:', err.message);
92
+ }
93
+ }
94
+
95
+ // Wait before next check
96
+ await new Promise(r => setTimeout(r, TURNSTILE_CHECK_INTERVAL));
97
+ }
36
98
 
37
- turnstileSolver()
99
+ if (attempts >= TURNSTILE_MAX_ATTEMPTS) {
100
+ console.warn('Turnstile solver stopped: max attempts reached');
101
+ }
102
+ } catch (err) {
103
+ console.error('Error in turnstile solver:', err.message);
104
+ } finally {
105
+ turnstileSolverRunning = false;
106
+ }
107
+ }
38
108
 
39
- if (proxy.username && proxy.password) await page.authenticate({ username: proxy.username, password: proxy.password });
109
+ // Start turnstile solver only if enabled
110
+ if (turnstile) {
111
+ turnstileSolver().catch(err => {
112
+ console.error('Failed to start turnstile solver:', err.message);
113
+ });
114
+ }
40
115
 
41
- if (plugins.length > 0) {
42
- for (const plugin of plugins) {
43
- plugin.onPageCreated(page)
116
+ // Proxy authentication with error handling
117
+ if (proxy && proxy.username && proxy.password) {
118
+ try {
119
+ await page.authenticate({
120
+ username: proxy.username,
121
+ password: proxy.password
122
+ });
123
+ } catch (err) {
124
+ console.error('Proxy authentication failed:', err.message);
125
+ throw new Error(`Failed to authenticate proxy: ${err.message}`);
44
126
  }
45
127
  }
46
128
 
47
- await page.evaluateOnNewDocument(() => {
48
- Object.defineProperty(MouseEvent.prototype, 'screenX', {
49
- get: function () {
50
- return this.clientX + window.screenX;
129
+ // Plugin initialization with error handling
130
+ if (plugins && plugins.length > 0) {
131
+ for (const plugin of plugins) {
132
+ try {
133
+ if (plugin && typeof plugin.onPageCreated === 'function') {
134
+ await plugin.onPageCreated(page);
135
+ }
136
+ } catch (err) {
137
+ console.error('Plugin initialization failed:', err.message);
138
+ // Continue with other plugins even if one fails
51
139
  }
52
- });
140
+ }
141
+ }
53
142
 
54
- Object.defineProperty(MouseEvent.prototype, 'screenY', {
55
- get: function () {
56
- return this.clientY + window.screenY;
57
- }
143
+ // Mouse event handling with error handling
144
+ try {
145
+ await page.evaluateOnNewDocument(() => {
146
+ Object.defineProperty(MouseEvent.prototype, 'screenX', {
147
+ get: function () {
148
+ return this.clientX + window.screenX;
149
+ }
150
+ });
151
+
152
+ Object.defineProperty(MouseEvent.prototype, 'screenY', {
153
+ get: function () {
154
+ return this.clientY + window.screenY;
155
+ }
156
+ });
58
157
  });
158
+ } catch (err) {
159
+ console.error('Failed to setup mouse event handlers:', err.message);
160
+ // Non-critical, continue anyway
161
+ }
59
162
 
60
- });
61
-
62
- const cursor = createCursor(page);
63
- page.realCursor = cursor
64
- page.realClick = cursor.click
163
+ // Cursor setup with error handling
164
+ try {
165
+ const cursor = createCursor(page);
166
+ page.realCursor = cursor;
167
+ page.realClick = cursor.click;
168
+ } catch (err) {
169
+ console.error('Failed to create ghost cursor:', err.message);
170
+ // Non-critical, continue anyway
171
+ }
65
172
 
66
- return page
173
+ return page;
67
174
  }
68
175
 
69
176
  module.exports = { pageController }
@@ -2,65 +2,175 @@ import { createCursor } from 'ghost-cursor';
2
2
  import { checkTurnstile } from './turnstile.mjs';
3
3
  import kill from 'tree-kill';
4
4
 
5
+ // Configuration constants
6
+ const TURNSTILE_CHECK_INTERVAL = 1000; // 1 second
7
+ const TURNSTILE_MAX_ATTEMPTS = 300; // 5 minutes max (300 attempts * 1 second)
8
+ const TURNSTILE_TIMEOUT = 300000; // 5 minutes in milliseconds
9
+
5
10
  function getRandomInt(min, max) {
6
11
  min = Math.ceil(min);
7
12
  max = Math.floor(max);
8
13
  return Math.floor(Math.random() * (max - min + 1)) + min;
9
14
  }
10
15
 
11
- export async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) {
16
+ async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) {
17
+
18
+ let solveStatus = turnstile;
19
+ let turnstileSolverRunning = false;
12
20
 
13
- let solveStatus = turnstile
21
+ // Safe cleanup function
22
+ const cleanup = async () => {
23
+ solveStatus = false;
24
+ if (killProcess === true) {
25
+ if (xvfbsession) {
26
+ try {
27
+ xvfbsession.stopSync();
28
+ } catch (err) {
29
+ console.error('Error stopping Xvfb session:', err.message);
30
+ }
31
+ }
32
+ if (chrome) {
33
+ try {
34
+ chrome.kill();
35
+ } catch (err) {
36
+ console.error('Error killing Chrome process:', err.message);
37
+ }
38
+ }
39
+ if (pid) {
40
+ try {
41
+ kill(pid, 'SIGKILL', (err) => {
42
+ if (err) console.error('Error killing process:', err.message);
43
+ });
44
+ } catch (err) {
45
+ console.error('Error in kill command:', err.message);
46
+ }
47
+ }
48
+ }
49
+ };
14
50
 
15
51
  page.on('close', () => {
16
- solveStatus = false
52
+ solveStatus = false;
17
53
  });
18
54
 
19
-
20
55
  browser.on('disconnected', async () => {
21
- solveStatus = false
22
- if (killProcess === true) {
23
- if (xvfbsession) try { xvfbsession.stopSync() } catch (err) { }
24
- if (chrome) try { chrome.kill() } catch (err) { console.log(err); }
25
- if (pid) try { kill(pid, 'SIGKILL', () => { }) } catch (err) { }
26
- }
56
+ await cleanup();
27
57
  });
28
58
 
59
+ // Enhanced turnstile solver with timeout and max attempts protection
29
60
  async function turnstileSolver() {
30
- while (solveStatus) {
31
- await checkTurnstile({ page }).catch(() => { });
32
- await new Promise(r => setTimeout(r, 1000));
61
+ if (turnstileSolverRunning) {
62
+ console.warn('Turnstile solver already running, skipping duplicate start');
63
+ return;
33
64
  }
34
- return
35
- }
65
+
66
+ turnstileSolverRunning = true;
67
+ const startTime = Date.now();
68
+ let attempts = 0;
36
69
 
37
- turnstileSolver()
70
+ try {
71
+ while (solveStatus && attempts < TURNSTILE_MAX_ATTEMPTS) {
72
+ // Check timeout
73
+ if (Date.now() - startTime > TURNSTILE_TIMEOUT) {
74
+ console.warn('Turnstile solver timeout reached after', attempts, 'attempts');
75
+ break;
76
+ }
38
77
 
39
- if (proxy.username && proxy.password) await page.authenticate({ username: proxy.username, password: proxy.password });
78
+ // Check if page is still valid
79
+ if (page.isClosed()) {
80
+ console.log('Page closed, stopping turnstile solver');
81
+ break;
82
+ }
40
83
 
41
- if (plugins.length > 0) {
42
- for (const plugin of plugins) {
43
- plugin.onPageCreated(page)
84
+ attempts++;
85
+
86
+ try {
87
+ await checkTurnstile({ page });
88
+ } catch (err) {
89
+ // Silent fail for individual checks, log only if debug enabled
90
+ if (process.env.DEBUG === 'true') {
91
+ console.debug('Turnstile check failed:', err.message);
92
+ }
93
+ }
94
+
95
+ // Wait before next check
96
+ await new Promise(r => setTimeout(r, TURNSTILE_CHECK_INTERVAL));
97
+ }
98
+
99
+ if (attempts >= TURNSTILE_MAX_ATTEMPTS) {
100
+ console.warn('Turnstile solver stopped: max attempts reached');
101
+ }
102
+ } catch (err) {
103
+ console.error('Error in turnstile solver:', err.message);
104
+ } finally {
105
+ turnstileSolverRunning = false;
44
106
  }
45
107
  }
46
108
 
47
- await page.evaluateOnNewDocument(() => {
48
- Object.defineProperty(MouseEvent.prototype, 'screenX', {
49
- get: function () {
50
- return this.clientX + window.screenX;
51
- }
109
+ // Start turnstile solver only if enabled
110
+ if (turnstile) {
111
+ turnstileSolver().catch(err => {
112
+ console.error('Failed to start turnstile solver:', err.message);
52
113
  });
114
+ }
115
+
116
+ // Proxy authentication with error handling
117
+ if (proxy && proxy.username && proxy.password) {
118
+ try {
119
+ await page.authenticate({
120
+ username: proxy.username,
121
+ password: proxy.password
122
+ });
123
+ } catch (err) {
124
+ console.error('Proxy authentication failed:', err.message);
125
+ throw new Error(`Failed to authenticate proxy: ${err.message}`);
126
+ }
127
+ }
53
128
 
54
- Object.defineProperty(MouseEvent.prototype, 'screenY', {
55
- get: function () {
56
- return this.clientY + window.screenY;
129
+ // Plugin initialization with error handling
130
+ if (plugins && plugins.length > 0) {
131
+ for (const plugin of plugins) {
132
+ try {
133
+ if (plugin && typeof plugin.onPageCreated === 'function') {
134
+ await plugin.onPageCreated(page);
135
+ }
136
+ } catch (err) {
137
+ console.error('Plugin initialization failed:', err.message);
138
+ // Continue with other plugins even if one fails
57
139
  }
140
+ }
141
+ }
142
+
143
+ // Mouse event handling with error handling
144
+ try {
145
+ await page.evaluateOnNewDocument(() => {
146
+ Object.defineProperty(MouseEvent.prototype, 'screenX', {
147
+ get: function () {
148
+ return this.clientX + window.screenX;
149
+ }
150
+ });
151
+
152
+ Object.defineProperty(MouseEvent.prototype, 'screenY', {
153
+ get: function () {
154
+ return this.clientY + window.screenY;
155
+ }
156
+ });
58
157
  });
158
+ } catch (err) {
159
+ console.error('Failed to setup mouse event handlers:', err.message);
160
+ // Non-critical, continue anyway
161
+ }
59
162
 
60
- });
163
+ // Cursor setup with error handling
164
+ try {
165
+ const cursor = createCursor(page);
166
+ page.realCursor = cursor;
167
+ page.realClick = cursor.click;
168
+ } catch (err) {
169
+ console.error('Failed to create ghost cursor:', err.message);
170
+ // Non-critical, continue anyway
171
+ }
172
+
173
+ return page;
174
+ }
61
175
 
62
- const cursor = createCursor(page);
63
- page.realCursor = cursor
64
- page.realClick = cursor.click
65
- return page
66
- }
176
+ export { pageController };
@@ -1,7 +1,6 @@
1
- export const checkTurnstile = ({ page }) => {
1
+ const checkTurnstile = ({ page }) => {
2
2
  return new Promise(async (resolve, reject) => {
3
3
  var waitInterval = setTimeout(() => { clearInterval(waitInterval); resolve(false) }, 5000);
4
-
5
4
  try {
6
5
  const elements = await page.$$('[name="cf-turnstile-response"]');
7
6
  if (elements.length <= 0) {
@@ -59,4 +58,6 @@ export const checkTurnstile = ({ page }) => {
59
58
  resolve(false)
60
59
  }
61
60
  })
62
- }
61
+ }
62
+
63
+ export { checkTurnstile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser",
3
- "version": "1.5.95",
3
+ "version": "1.5.98",
4
4
  "description": "This package is designed to bypass puppeteer's bot-detecting captchas such as Cloudflare. It acts like a real browser and can be managed with puppeteer.",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.mjs",
@@ -40,7 +40,7 @@
40
40
  "dependencies": {
41
41
  "brave-real-launcher": "latest",
42
42
  "brave-real-puppeteer-core": "latest",
43
- "ghost-cursor": "^1.4.1 ",
43
+ "ghost-cursor": "^1.4.1",
44
44
  "puppeteer-extra": "^3.3.6",
45
45
  "tree-kill": "^1.2.2",
46
46
  "xvfb": "^0.4.0"