brave-real-browser 1.5.95 → 1.5.97

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
@@ -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.97",
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"