@monostate/browsernative-client 1.2.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -386
- package/index.d.ts +175 -7
- package/index.js +232 -1
- package/package.json +2 -2
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,461 +1,173 @@
|
|
|
1
1
|
# @monostate/browsernative-client
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> JavaScript/TypeScript client for the Browser Native web scraping API
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@monostate/browsernative-client)
|
|
6
|
+
[](https://github.com/monostate/browsernative-client/blob/main/LICENSE)
|
|
6
7
|
[](https://www.typescriptlang.org/)
|
|
7
|
-
[](../../LICENSE)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Zero-dependency client for scraping, screenshots, and AI analysis. Works in Node.js, browsers, and edge environments.
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
### Installation
|
|
11
|
+
## Install
|
|
14
12
|
|
|
15
13
|
```bash
|
|
16
14
|
npm install @monostate/browsernative-client
|
|
17
|
-
# or
|
|
18
|
-
yarn add @monostate/browsernative-client
|
|
19
|
-
# or
|
|
20
|
-
pnpm add @monostate/browsernative-client
|
|
21
15
|
```
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
1. Sign up at [bnca.monostate.ai](https://bnca.monostate.ai)
|
|
26
|
-
2. Get your API key from the dashboard
|
|
27
|
-
3. Start scraping!
|
|
17
|
+
Get an API key at [bnca.monostate.ai](https://bnca.monostate.ai).
|
|
28
18
|
|
|
29
|
-
|
|
19
|
+
## Usage
|
|
30
20
|
|
|
31
21
|
```javascript
|
|
32
22
|
import { BrowserNativeClient } from '@monostate/browsernative-client';
|
|
33
23
|
|
|
34
24
|
const client = new BrowserNativeClient('your-api-key');
|
|
35
25
|
|
|
36
|
-
// Scrape
|
|
26
|
+
// Scrape
|
|
37
27
|
const result = await client.scrape('https://example.com');
|
|
38
|
-
console.log(result.data.title);
|
|
39
|
-
console.log(result.data.content);
|
|
40
|
-
|
|
41
|
-
// Quick screenshot capture (optimized for speed)
|
|
42
|
-
const screenshot = await client.quickshot('https://example.com');
|
|
43
|
-
console.log(screenshot.screenshot); // Base64 image
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Quick Functions
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
import { quickScrape, quickScreenshot, quickShot, quickAnalyze } from '@monostate/browsernative-client';
|
|
50
|
-
|
|
51
|
-
// One-line scraping
|
|
52
|
-
const content = await quickScrape('https://example.com', 'your-api-key');
|
|
53
28
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
// Quick screenshot only (fastest option)
|
|
58
|
-
const quickScreenshot = await quickShot('https://example.com', 'your-api-key');
|
|
59
|
-
|
|
60
|
-
// AI-powered analysis
|
|
61
|
-
const analysis = await quickAnalyze(
|
|
62
|
-
'https://news.ycombinator.com',
|
|
63
|
-
'What are the top 3 trending topics?',
|
|
64
|
-
'your-api-key'
|
|
65
|
-
);
|
|
66
|
-
```
|
|
29
|
+
// Force a specific scraping method
|
|
30
|
+
const result = await client.scrape('https://example.com', { method: 'lightpanda' });
|
|
67
31
|
|
|
68
|
-
|
|
32
|
+
// Screenshot
|
|
33
|
+
const screenshot = await client.screenshot('https://example.com');
|
|
69
34
|
|
|
70
|
-
|
|
35
|
+
// Quick screenshot (faster, no content extraction)
|
|
36
|
+
const quick = await client.quickshot('https://example.com');
|
|
71
37
|
|
|
72
|
-
|
|
73
|
-
const
|
|
38
|
+
// AI Q&A
|
|
39
|
+
const analysis = await client.analyze('https://example.com', 'What is this site about?');
|
|
74
40
|
```
|
|
75
41
|
|
|
76
|
-
|
|
77
|
-
- `baseUrl` (string): API base URL (default: `https://bnca-api.fly.dev`)
|
|
78
|
-
- `timeout` (number): Request timeout in ms (default: `30000`)
|
|
79
|
-
- `retries` (number): Number of retry attempts (default: `2`)
|
|
80
|
-
- `verbose` (boolean): Enable logging (default: `false`)
|
|
81
|
-
|
|
82
|
-
### Methods
|
|
83
|
-
|
|
84
|
-
#### `client.scrape(url, options)`
|
|
85
|
-
|
|
86
|
-
Extract structured content from any webpage.
|
|
42
|
+
### Convenience functions
|
|
87
43
|
|
|
88
44
|
```javascript
|
|
89
|
-
|
|
90
|
-
includeScreenshot: true,
|
|
91
|
-
waitForSelector: '.main-content',
|
|
92
|
-
extractMetadata: true,
|
|
93
|
-
userAgent: 'Custom Bot 1.0'
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
console.log(result.data.title); // Page title
|
|
97
|
-
console.log(result.data.content); // Main content
|
|
98
|
-
console.log(result.data.metadata); // Meta tags, etc.
|
|
99
|
-
console.log(result.screenshot); // Base64 image (if requested)
|
|
100
|
-
console.log(result.method); // Scraping method used
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**Options:**
|
|
104
|
-
- `includeScreenshot` (boolean): Include page screenshot
|
|
105
|
-
- `waitForSelector` (string): CSS selector to wait for
|
|
106
|
-
- `userAgent` (string): Custom user agent
|
|
107
|
-
- `viewport` (object): `{ width: number, height: number }`
|
|
108
|
-
- `extractMetadata` (boolean): Extract meta tags and structured data
|
|
109
|
-
|
|
110
|
-
#### `client.screenshot(url, options)`
|
|
111
|
-
|
|
112
|
-
Take high-quality screenshots of webpages with content extraction.
|
|
45
|
+
import { quickScrape, quickScreenshot, quickShot, quickAnalyze, bulkScrape } from '@monostate/browsernative-client';
|
|
113
46
|
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
format: 'png',
|
|
118
|
-
viewport: { width: 1920, height: 1080 }
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// Use the base64 image
|
|
122
|
-
const img = `data:image/png;base64,${result.screenshot}`;
|
|
47
|
+
const content = await quickScrape('https://example.com', 'your-api-key');
|
|
48
|
+
const shot = await quickShot('https://example.com', 'your-api-key');
|
|
49
|
+
const answer = await quickAnalyze('https://example.com', 'What is this?', 'your-api-key');
|
|
123
50
|
```
|
|
124
51
|
|
|
125
|
-
|
|
126
|
-
- `fullPage` (boolean): Capture full page scroll
|
|
127
|
-
- `format` (string): `'png'`, `'jpeg'`, or `'webp'`
|
|
128
|
-
- `quality` (number): JPEG quality (1-100)
|
|
129
|
-
- All scrape options are also available
|
|
130
|
-
|
|
131
|
-
#### `client.quickshot(url, options)`
|
|
132
|
-
|
|
133
|
-
Optimized screenshot capture for maximum speed (no content extraction).
|
|
52
|
+
### Bulk scraping
|
|
134
53
|
|
|
135
54
|
```javascript
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
// Returns screenshot immediately
|
|
139
|
-
if (result.success && result.screenshot) {
|
|
140
|
-
const img = result.screenshot; // Already includes data:image/png;base64,
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**Benefits:**
|
|
145
|
-
- 2-3x faster than regular screenshot
|
|
146
|
-
- Optimized for visual capture only
|
|
147
|
-
- Perfect for thumbnails and previews
|
|
148
|
-
|
|
149
|
-
#### `client.analyze(url, question, options)`
|
|
150
|
-
|
|
151
|
-
AI-powered content analysis and question answering.
|
|
152
|
-
|
|
153
|
-
```javascript
|
|
154
|
-
const result = await client.analyze(
|
|
155
|
-
'https://techcrunch.com',
|
|
156
|
-
'What are the latest AI developments mentioned?',
|
|
157
|
-
{
|
|
158
|
-
language: 'en',
|
|
159
|
-
style: 'detailed'
|
|
160
|
-
}
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
console.log(result.analysis.answer); // AI response
|
|
164
|
-
console.log(result.analysis.confidence); // Confidence score
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**Options:**
|
|
168
|
-
- `language` (string): Response language (`'en'`, `'pt'`, `'es'`, `'fr'`, `'de'`, `'auto'`)
|
|
169
|
-
- `style` (string): Response style (`'concise'`, `'detailed'`, `'technical'`)
|
|
170
|
-
- All scrape options are also available
|
|
171
|
-
|
|
172
|
-
#### `client.getUsage(days)`
|
|
173
|
-
|
|
174
|
-
Get your account usage statistics.
|
|
55
|
+
const urls = ['https://site1.com', 'https://site2.com', 'https://site3.com'];
|
|
175
56
|
|
|
176
|
-
|
|
177
|
-
|
|
57
|
+
const { results, stats } = await client.bulkScrape(urls, {
|
|
58
|
+
concurrency: 5,
|
|
59
|
+
continueOnError: true,
|
|
60
|
+
progressCallback: (p) => console.log(`${p.percentage.toFixed(1)}%`),
|
|
61
|
+
});
|
|
178
62
|
|
|
179
|
-
console.log(
|
|
180
|
-
console.log(usage.data.currentUsage);
|
|
181
|
-
console.log(usage.data.usageLimit);
|
|
63
|
+
console.log(`${stats.successful}/${stats.total} succeeded in ${stats.totalTime}ms`);
|
|
182
64
|
```
|
|
183
65
|
|
|
184
|
-
|
|
66
|
+
## API Reference
|
|
185
67
|
|
|
186
|
-
|
|
68
|
+
### Client options
|
|
187
69
|
|
|
188
70
|
```javascript
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
71
|
+
const client = new BrowserNativeClient(apiKey, {
|
|
72
|
+
baseUrl: 'https://bnca-api.fly.dev', // API endpoint
|
|
73
|
+
timeout: 30000, // Request timeout (ms)
|
|
74
|
+
retries: 2, // Retry attempts
|
|
75
|
+
verbose: false, // Debug logging
|
|
76
|
+
});
|
|
193
77
|
```
|
|
194
78
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
### React Hook
|
|
198
|
-
|
|
199
|
-
```javascript
|
|
200
|
-
// hooks/useBrowserNative.js
|
|
201
|
-
import { useState } from 'react';
|
|
202
|
-
import { BrowserNativeClient } from '@monostate/browsernative-client';
|
|
79
|
+
### Methods
|
|
203
80
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} finally {
|
|
225
|
-
setLoading(false);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
return { scrape, loading, data, error };
|
|
81
|
+
| Method | Description |
|
|
82
|
+
|--------|-------------|
|
|
83
|
+
| `scrape(url, opts?)` | Extract structured content |
|
|
84
|
+
| `screenshot(url, opts?)` | Screenshot with content extraction |
|
|
85
|
+
| `quickshot(url, opts?)` | Fast screenshot only |
|
|
86
|
+
| `analyze(url, question, opts?)` | AI-powered Q&A |
|
|
87
|
+
| `bulkScrape(urls, opts?)` | Scrape multiple URLs concurrently |
|
|
88
|
+
| `getUsage(days?)` | Account usage statistics |
|
|
89
|
+
| `healthCheck()` | API health status |
|
|
90
|
+
|
|
91
|
+
### Scrape options
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
{
|
|
95
|
+
method: 'auto', // 'auto' | 'direct' | 'lightpanda' | 'puppeteer'
|
|
96
|
+
includeScreenshot: false,
|
|
97
|
+
waitForSelector: '.content',
|
|
98
|
+
userAgent: 'Custom Bot',
|
|
99
|
+
viewport: { width: 1920, height: 1080 },
|
|
100
|
+
extractMetadata: true,
|
|
230
101
|
}
|
|
231
102
|
```
|
|
232
103
|
|
|
233
|
-
###
|
|
104
|
+
### Response shape
|
|
234
105
|
|
|
235
106
|
```javascript
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const loading = ref(false);
|
|
242
|
-
const data = ref(null);
|
|
243
|
-
const error = ref(null);
|
|
244
|
-
|
|
245
|
-
const client = new BrowserNativeClient(apiKey);
|
|
246
|
-
|
|
247
|
-
const scrape = async (url, options = {}) => {
|
|
248
|
-
loading.value = true;
|
|
249
|
-
error.value = null;
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
const result = await client.scrape(url, options);
|
|
253
|
-
if (result.success) {
|
|
254
|
-
data.value = result.data;
|
|
255
|
-
} else {
|
|
256
|
-
error.value = result.error;
|
|
257
|
-
}
|
|
258
|
-
} catch (err) {
|
|
259
|
-
error.value = err.message;
|
|
260
|
-
} finally {
|
|
261
|
-
loading.value = false;
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
return { scrape, loading, data, error };
|
|
107
|
+
{
|
|
108
|
+
success: true,
|
|
109
|
+
data: { title, content, metadata, ... },
|
|
110
|
+
responseTime: 1234,
|
|
111
|
+
attempt: 1,
|
|
266
112
|
}
|
|
267
113
|
```
|
|
268
114
|
|
|
269
|
-
###
|
|
115
|
+
### Browser sessions
|
|
270
116
|
|
|
271
|
-
|
|
272
|
-
// pages/api/scrape.js
|
|
273
|
-
import { BrowserNativeClient } from '@monostate/browsernative-client';
|
|
274
|
-
|
|
275
|
-
const client = new BrowserNativeClient(process.env.BROWSER_NATIVE_API_KEY);
|
|
276
|
-
|
|
277
|
-
export default async function handler(req, res) {
|
|
278
|
-
if (req.method !== 'POST') {
|
|
279
|
-
return res.status(405).json({ error: 'Method not allowed' });
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
try {
|
|
283
|
-
const { url, question } = req.body;
|
|
284
|
-
|
|
285
|
-
let result;
|
|
286
|
-
if (question) {
|
|
287
|
-
result = await client.analyze(url, question);
|
|
288
|
-
} else {
|
|
289
|
-
result = await client.scrape(url);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
res.status(200).json(result);
|
|
293
|
-
} catch (error) {
|
|
294
|
-
res.status(500).json({ error: error.message });
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### Express.js
|
|
117
|
+
Persistent browser sessions with real-time control over WebSocket:
|
|
300
118
|
|
|
301
119
|
```javascript
|
|
302
|
-
|
|
303
|
-
import { BrowserNativeClient } from '@monostate/browsernative-client';
|
|
304
|
-
|
|
305
|
-
const app = express();
|
|
306
|
-
const client = new BrowserNativeClient(process.env.BROWSER_NATIVE_API_KEY);
|
|
307
|
-
|
|
308
|
-
app.post('/scrape', async (req, res) => {
|
|
309
|
-
try {
|
|
310
|
-
const { url } = req.body;
|
|
311
|
-
const result = await client.scrape(url);
|
|
312
|
-
res.json(result);
|
|
313
|
-
} catch (error) {
|
|
314
|
-
res.status(500).json({ error: error.message });
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
## 🔒 Environment Variables
|
|
320
|
-
|
|
321
|
-
Create a `.env` file:
|
|
322
|
-
|
|
323
|
-
```bash
|
|
324
|
-
BROWSER_NATIVE_API_KEY=your_api_key_here
|
|
325
|
-
```
|
|
120
|
+
const session = await client.createSession({ mode: 'auto' });
|
|
326
121
|
|
|
327
|
-
|
|
122
|
+
await session.goto('https://example.com');
|
|
123
|
+
await session.click('#login');
|
|
124
|
+
await session.type('#email', 'user@example.com');
|
|
125
|
+
const state = await session.getPageState({ includeScreenshot: true });
|
|
126
|
+
await session.screenshot();
|
|
328
127
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
```
|
|
128
|
+
// Listen for backend fallback events
|
|
129
|
+
session.on('fallback', (e) => console.log(`${e.from} -> ${e.to}: ${e.reason}`));
|
|
332
130
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
You can use this client directly in browsers, but be careful with API keys:
|
|
336
|
-
|
|
337
|
-
```html
|
|
338
|
-
<!DOCTYPE html>
|
|
339
|
-
<html>
|
|
340
|
-
<head>
|
|
341
|
-
<title>Browser Native Demo</title>
|
|
342
|
-
</head>
|
|
343
|
-
<body>
|
|
344
|
-
<script type="module">
|
|
345
|
-
import { BrowserNativeClient } from 'https://cdn.skypack.dev/@monostate/browsernative-client';
|
|
346
|
-
|
|
347
|
-
// ⚠️ Never expose your API key in client-side code!
|
|
348
|
-
// Use a proxy server or environment variables
|
|
349
|
-
const client = new BrowserNativeClient(await getApiKeyFromServer());
|
|
350
|
-
|
|
351
|
-
const result = await client.scrape('https://example.com');
|
|
352
|
-
console.log(result);
|
|
353
|
-
</script>
|
|
354
|
-
</body>
|
|
355
|
-
</html>
|
|
131
|
+
session.close();
|
|
356
132
|
```
|
|
357
133
|
|
|
358
|
-
|
|
134
|
+
Modes: `auto` (LightPanda + Chrome fallback), `headless`, `visual`, `computer-use`.
|
|
359
135
|
|
|
360
|
-
|
|
136
|
+
#### Computer use mode
|
|
361
137
|
|
|
362
|
-
|
|
363
|
-
2. **Enable Retries**: The client automatically retries failed requests
|
|
364
|
-
3. **Use Appropriate Timeouts**: Adjust timeout based on your use case
|
|
365
|
-
4. **Batch Requests**: Process multiple URLs concurrently
|
|
138
|
+
For AI agents that control the browser by pixel coordinates:
|
|
366
139
|
|
|
367
140
|
```javascript
|
|
368
|
-
const
|
|
369
|
-
timeout: 45000, // Longer timeout for complex sites
|
|
370
|
-
retries: 3, // More retries for reliability
|
|
371
|
-
verbose: true // Enable logging for debugging
|
|
372
|
-
});
|
|
141
|
+
const session = await client.createSession({ mode: 'computer-use', screenWidth: 1280, screenHeight: 800 });
|
|
373
142
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
);
|
|
379
|
-
|
|
143
|
+
await session.goto('https://example.com');
|
|
144
|
+
await session.clickAt(640, 400);
|
|
145
|
+
await session.typeText('hello world');
|
|
146
|
+
await session.mouseMove(100, 200);
|
|
147
|
+
await session.drag(10, 20, 300, 400);
|
|
148
|
+
await session.scrollAt(640, 400, 'down', 5);
|
|
149
|
+
const pos = await session.getCursorPosition();
|
|
150
|
+
const size = await session.getScreenSize();
|
|
151
|
+
const screenshot = await session.screenshot();
|
|
380
152
|
|
|
381
|
-
|
|
153
|
+
// VNC streaming URL
|
|
154
|
+
console.log(session.vncUrl);
|
|
382
155
|
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
const result = await client.scrape(url);
|
|
386
|
-
|
|
387
|
-
if (result.success) {
|
|
388
|
-
console.log('Success:', result.data);
|
|
389
|
-
} else {
|
|
390
|
-
console.error('Scraping failed:', result.error);
|
|
391
|
-
}
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error('Request failed:', error.message);
|
|
394
|
-
}
|
|
156
|
+
session.close();
|
|
395
157
|
```
|
|
396
158
|
|
|
397
|
-
|
|
398
|
-
- **Invalid API Key**: Check your API key and account status
|
|
399
|
-
- **Rate Limited**: Upgrade your plan or reduce request frequency
|
|
400
|
-
- **Timeout**: Increase timeout or try a simpler extraction method
|
|
401
|
-
- **Invalid URL**: Ensure the URL is accessible and properly formatted
|
|
159
|
+
## TypeScript
|
|
402
160
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
Full TypeScript support with comprehensive type definitions:
|
|
161
|
+
Full type definitions included.
|
|
406
162
|
|
|
407
163
|
```typescript
|
|
408
|
-
import { BrowserNativeClient, ScrapeResult,
|
|
409
|
-
|
|
410
|
-
const client: BrowserNativeClient = new BrowserNativeClient('your-api-key');
|
|
411
|
-
|
|
412
|
-
const result: ScrapeResult = await client.scrape('https://example.com');
|
|
413
|
-
const analysis: AnalyzeResult = await client.analyze(url, question);
|
|
164
|
+
import { BrowserNativeClient, ScrapeResult, BulkScrapeResult } from '@monostate/browsernative-client';
|
|
414
165
|
```
|
|
415
166
|
|
|
416
|
-
##
|
|
417
|
-
|
|
418
|
-
| Plan | Requests/Month | Rate Limit |
|
|
419
|
-
|------|----------------|------------|
|
|
420
|
-
| **Free** | 1,000 | 10/minute |
|
|
421
|
-
| **Starter** | 10,000 | 60/minute |
|
|
422
|
-
| **Pro** | 100,000 | 300/minute |
|
|
423
|
-
| **Enterprise** | Unlimited | Custom |
|
|
424
|
-
|
|
425
|
-
## 📋 Changelog
|
|
426
|
-
|
|
427
|
-
### v1.2.0 (Latest)
|
|
428
|
-
- 🔧 **Timeout Improvements**: Enhanced timeout handling and request reliability
|
|
429
|
-
- 📝 **Documentation Updates**: Comprehensive API documentation and examples
|
|
430
|
-
- 🏷️ **Package Naming**: Proper package name consistency across all imports
|
|
431
|
-
- ⚡ **Performance Optimizations**: Better error handling and response processing
|
|
432
|
-
- 🌐 **Framework Integration**: Improved React, Vue, and Next.js examples
|
|
433
|
-
|
|
434
|
-
### v1.1.2
|
|
435
|
-
- Bug fixes and stability improvements
|
|
436
|
-
- Enhanced error handling
|
|
437
|
-
|
|
438
|
-
### v1.1.1
|
|
439
|
-
- Initial TypeScript support
|
|
440
|
-
- Basic API client functionality
|
|
441
|
-
|
|
442
|
-
## 🤝 Support
|
|
443
|
-
|
|
444
|
-
- 📧 **Email**: [support@bnca.monostate.ai](mailto:support@bnca.monostate.ai)
|
|
445
|
-
- 📖 **Documentation**: [bnca.monostate.ai/docs](https://bnca.monostate.ai/docs)
|
|
446
|
-
- 🐛 **Issues**: [GitHub Issues](https://github.com/browsernative/client-sdk/issues)
|
|
447
|
-
- 💬 **Discord**: [Join our community](https://discord.gg/browsernative)
|
|
448
|
-
|
|
449
|
-
## 📄 License
|
|
450
|
-
|
|
451
|
-
MIT License - see [LICENSE](../../LICENSE) file for details.
|
|
452
|
-
|
|
453
|
-
---
|
|
454
|
-
|
|
455
|
-
<div align="center">
|
|
167
|
+
## Changelog
|
|
456
168
|
|
|
457
|
-
|
|
169
|
+
See [CHANGELOG.md](./CHANGELOG.md).
|
|
458
170
|
|
|
459
|
-
|
|
171
|
+
## License
|
|
460
172
|
|
|
461
|
-
|
|
173
|
+
MIT
|
package/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface ClientOptions {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface ScrapeOptions {
|
|
17
|
+
/** Force a specific scraping method */
|
|
18
|
+
method?: 'auto' | 'direct' | 'lightpanda' | 'puppeteer';
|
|
17
19
|
/** Include screenshot in the response */
|
|
18
20
|
includeScreenshot?: boolean;
|
|
19
21
|
/** Wait for specific selector before extracting content */
|
|
@@ -29,6 +31,26 @@ export interface ScrapeOptions {
|
|
|
29
31
|
extractMetadata?: boolean;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export interface BulkScrapeOptions extends ScrapeOptions {
|
|
35
|
+
/** Number of concurrent requests (default: 5) */
|
|
36
|
+
concurrency?: number;
|
|
37
|
+
/** Continue on error (default: true) */
|
|
38
|
+
continueOnError?: boolean;
|
|
39
|
+
/** Progress callback */
|
|
40
|
+
progressCallback?: (progress: { processed: number; total: number; percentage: number }) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface BulkScrapeResult {
|
|
44
|
+
results: Array<ScrapeResult & { url: string }>;
|
|
45
|
+
stats: {
|
|
46
|
+
total: number;
|
|
47
|
+
successful: number;
|
|
48
|
+
failed: number;
|
|
49
|
+
totalTime: number;
|
|
50
|
+
averageTime: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
export interface ScreenshotOptions extends ScrapeOptions {
|
|
33
55
|
/** Force full page screenshot */
|
|
34
56
|
fullPage?: boolean;
|
|
@@ -74,14 +96,33 @@ export interface ScrapeResult {
|
|
|
74
96
|
attempt?: number;
|
|
75
97
|
}
|
|
76
98
|
|
|
77
|
-
export interface AnalyzeResult
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
export interface AnalyzeResult {
|
|
100
|
+
/** Whether the request was successful */
|
|
101
|
+
success: boolean;
|
|
102
|
+
/** API response data */
|
|
103
|
+
data?: {
|
|
104
|
+
/** Whether the inner request was successful */
|
|
105
|
+
success?: boolean;
|
|
106
|
+
/** The answer from AI analysis */
|
|
107
|
+
answer?: string;
|
|
108
|
+
/** Metadata about the request */
|
|
109
|
+
metadata?: {
|
|
110
|
+
url: string;
|
|
111
|
+
question: string;
|
|
112
|
+
method: string;
|
|
113
|
+
timestamp: string;
|
|
114
|
+
performance: {
|
|
115
|
+
totalTime: number;
|
|
116
|
+
};
|
|
117
|
+
screenshot?: string | null;
|
|
118
|
+
};
|
|
84
119
|
};
|
|
120
|
+
/** Error message (if failed) */
|
|
121
|
+
error?: string;
|
|
122
|
+
/** Response time in milliseconds */
|
|
123
|
+
responseTime: number;
|
|
124
|
+
/** Attempt number that succeeded */
|
|
125
|
+
attempt?: number;
|
|
85
126
|
}
|
|
86
127
|
|
|
87
128
|
export interface UsageResult {
|
|
@@ -127,6 +168,114 @@ export interface HealthResult {
|
|
|
127
168
|
responseTime: number;
|
|
128
169
|
}
|
|
129
170
|
|
|
171
|
+
export interface SessionOptions {
|
|
172
|
+
/** Browser mode (default: 'auto') */
|
|
173
|
+
mode?: 'auto' | 'headless' | 'visual' | 'computer-use';
|
|
174
|
+
/** Screen width for computer-use mode */
|
|
175
|
+
screenWidth?: number;
|
|
176
|
+
/** Screen height for computer-use mode */
|
|
177
|
+
screenHeight?: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface PageState {
|
|
181
|
+
url: string;
|
|
182
|
+
title: string;
|
|
183
|
+
content?: string;
|
|
184
|
+
screenshot?: string;
|
|
185
|
+
interactiveElements?: Array<{
|
|
186
|
+
tag: string;
|
|
187
|
+
text?: string;
|
|
188
|
+
selector: string;
|
|
189
|
+
type?: string;
|
|
190
|
+
href?: string;
|
|
191
|
+
}>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface FallbackEvent {
|
|
195
|
+
type: 'fallback';
|
|
196
|
+
from: string;
|
|
197
|
+
to: string;
|
|
198
|
+
reason: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface ClosedEvent {
|
|
202
|
+
type: 'closed';
|
|
203
|
+
reason: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface Cookie {
|
|
207
|
+
name: string;
|
|
208
|
+
value: string;
|
|
209
|
+
domain?: string;
|
|
210
|
+
path?: string;
|
|
211
|
+
expires?: number;
|
|
212
|
+
httpOnly?: boolean;
|
|
213
|
+
secure?: boolean;
|
|
214
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Persistent browser session over WebSocket
|
|
219
|
+
*/
|
|
220
|
+
export declare class BrowserSession {
|
|
221
|
+
/** Session ID assigned by the server */
|
|
222
|
+
sessionId: string | null;
|
|
223
|
+
/** Current browser backend ('lightpanda', 'chrome', or 'computer-use') */
|
|
224
|
+
backend: string | null;
|
|
225
|
+
/** VNC URL for computer-use mode (null otherwise) */
|
|
226
|
+
vncUrl: string | null;
|
|
227
|
+
|
|
228
|
+
/** Navigate to a URL */
|
|
229
|
+
goto(url: string): Promise<any>;
|
|
230
|
+
/** Click an element */
|
|
231
|
+
click(selector: string, options?: { timeout?: number }): Promise<any>;
|
|
232
|
+
/** Type text into an element */
|
|
233
|
+
type(selector: string, text: string, options?: { clear?: boolean; delay?: number }): Promise<any>;
|
|
234
|
+
/** Scroll the page */
|
|
235
|
+
scroll(direction?: 'up' | 'down', amount?: number): Promise<any>;
|
|
236
|
+
/** Hover over an element */
|
|
237
|
+
hover(selector: string): Promise<any>;
|
|
238
|
+
/** Select option(s) in a dropdown */
|
|
239
|
+
select(selector: string, ...values: string[]): Promise<any>;
|
|
240
|
+
/** Press a keyboard key */
|
|
241
|
+
pressKey(key: string): Promise<any>;
|
|
242
|
+
/** Navigate back */
|
|
243
|
+
goBack(): Promise<any>;
|
|
244
|
+
/** Navigate forward */
|
|
245
|
+
goForward(): Promise<any>;
|
|
246
|
+
/** Take a screenshot */
|
|
247
|
+
screenshot(options?: { fullPage?: boolean; type?: 'png' | 'jpeg' | 'webp'; quality?: number }): Promise<any>;
|
|
248
|
+
/** Get current page state with interactive elements */
|
|
249
|
+
getPageState(options?: { includeScreenshot?: boolean }): Promise<PageState>;
|
|
250
|
+
/** Extract page content as structured data */
|
|
251
|
+
extractContent(): Promise<any>;
|
|
252
|
+
/** Wait for a selector to appear */
|
|
253
|
+
waitFor(selector: string, timeout?: number): Promise<any>;
|
|
254
|
+
/** Evaluate JavaScript in the page context */
|
|
255
|
+
evaluate(fn: string): Promise<any>;
|
|
256
|
+
/** Get all cookies */
|
|
257
|
+
getCookies(): Promise<Cookie[]>;
|
|
258
|
+
/** Set cookies */
|
|
259
|
+
setCookies(cookies: Cookie[]): Promise<any>;
|
|
260
|
+
|
|
261
|
+
/** Coordinate-based actions (computer-use mode) */
|
|
262
|
+
mouseMove(x: number, y: number): Promise<any>;
|
|
263
|
+
clickAt(x: number, y: number, button?: 'left' | 'right' | 'middle'): Promise<any>;
|
|
264
|
+
doubleClickAt(x: number, y: number, button?: 'left' | 'right' | 'middle'): Promise<any>;
|
|
265
|
+
drag(startX: number, startY: number, endX: number, endY: number): Promise<any>;
|
|
266
|
+
scrollAt(x: number, y: number, direction: 'up' | 'down', amount?: number): Promise<any>;
|
|
267
|
+
typeText(text: string): Promise<any>;
|
|
268
|
+
getCursorPosition(): Promise<{ x: number; y: number }>;
|
|
269
|
+
getScreenSize(): Promise<{ width: number; height: number }>;
|
|
270
|
+
|
|
271
|
+
/** Listen for session events */
|
|
272
|
+
on(event: 'fallback', handler: (event: FallbackEvent) => void): this;
|
|
273
|
+
on(event: 'closed', handler: (event: ClosedEvent) => void): this;
|
|
274
|
+
|
|
275
|
+
/** Close the session */
|
|
276
|
+
close(): void;
|
|
277
|
+
}
|
|
278
|
+
|
|
130
279
|
/**
|
|
131
280
|
* Browser Native API Client
|
|
132
281
|
*/
|
|
@@ -153,11 +302,21 @@ export declare class BrowserNativeClient {
|
|
|
153
302
|
*/
|
|
154
303
|
analyze(url: string, question: string, options?: AnalyzeOptions): Promise<AnalyzeResult>;
|
|
155
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Scrape multiple URLs with concurrency control
|
|
307
|
+
*/
|
|
308
|
+
bulkScrape(urls: string[], options?: BulkScrapeOptions): Promise<BulkScrapeResult>;
|
|
309
|
+
|
|
156
310
|
/**
|
|
157
311
|
* Get account usage statistics
|
|
158
312
|
*/
|
|
159
313
|
getUsage(days?: number): Promise<UsageResult>;
|
|
160
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Create a persistent browser session via WebSocket
|
|
317
|
+
*/
|
|
318
|
+
createSession(options?: SessionOptions): Promise<BrowserSession>;
|
|
319
|
+
|
|
161
320
|
/**
|
|
162
321
|
* Check API health and your account status
|
|
163
322
|
*/
|
|
@@ -201,4 +360,13 @@ export declare function quickShot(
|
|
|
201
360
|
options?: ScreenshotOptions
|
|
202
361
|
): Promise<ScrapeResult>;
|
|
203
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Convenience function for bulk scraping multiple URLs
|
|
365
|
+
*/
|
|
366
|
+
export declare function bulkScrape(
|
|
367
|
+
urls: string[],
|
|
368
|
+
apiKey: string,
|
|
369
|
+
options?: BulkScrapeOptions
|
|
370
|
+
): Promise<BulkScrapeResult>;
|
|
371
|
+
|
|
204
372
|
export default BrowserNativeClient;
|
package/index.js
CHANGED
|
@@ -83,6 +83,57 @@ export class BrowserNativeClient {
|
|
|
83
83
|
return this._makeRequest('/aireply', payload);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Scrape multiple URLs with concurrency control
|
|
88
|
+
* @param {string[]} urls - URLs to scrape
|
|
89
|
+
* @param {object} options - Bulk scraping options
|
|
90
|
+
* @returns {Promise<object>} Aggregated results
|
|
91
|
+
*/
|
|
92
|
+
async bulkScrape(urls, options = {}) {
|
|
93
|
+
const concurrency = options.concurrency || 5;
|
|
94
|
+
const continueOnError = options.continueOnError !== false;
|
|
95
|
+
const results = [];
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
let processed = 0;
|
|
98
|
+
|
|
99
|
+
const queue = [...urls];
|
|
100
|
+
const workers = Array.from({ length: Math.min(concurrency, urls.length) }, async () => {
|
|
101
|
+
while (queue.length > 0) {
|
|
102
|
+
const url = queue.shift();
|
|
103
|
+
if (!url) break;
|
|
104
|
+
try {
|
|
105
|
+
const result = await this.scrape(url, options);
|
|
106
|
+
results.push({ url, ...result });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
results.push({ url, success: false, error: error.message });
|
|
109
|
+
if (!continueOnError) throw error;
|
|
110
|
+
}
|
|
111
|
+
processed++;
|
|
112
|
+
if (options.progressCallback) {
|
|
113
|
+
options.progressCallback({
|
|
114
|
+
processed,
|
|
115
|
+
total: urls.length,
|
|
116
|
+
percentage: (processed / urls.length) * 100,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await Promise.all(workers);
|
|
123
|
+
|
|
124
|
+
const successful = results.filter(r => r.success).length;
|
|
125
|
+
return {
|
|
126
|
+
results,
|
|
127
|
+
stats: {
|
|
128
|
+
total: urls.length,
|
|
129
|
+
successful,
|
|
130
|
+
failed: urls.length - successful,
|
|
131
|
+
totalTime: Date.now() - startTime,
|
|
132
|
+
averageTime: Math.round((Date.now() - startTime) / urls.length),
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
86
137
|
/**
|
|
87
138
|
* Get account usage statistics
|
|
88
139
|
* @param {number} days - Number of days to fetch (max 30)
|
|
@@ -92,6 +143,30 @@ export class BrowserNativeClient {
|
|
|
92
143
|
return this._makeRequest('/stats', {}, 'GET');
|
|
93
144
|
}
|
|
94
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Create a persistent browser session via WebSocket
|
|
148
|
+
* @param {object} options - Session options
|
|
149
|
+
* @param {'auto'|'headless'|'visual'|'computer-use'} options.mode - Browser mode (default: 'auto')
|
|
150
|
+
* @param {number} [options.screenWidth] - Screen width for computer-use mode
|
|
151
|
+
* @param {number} [options.screenHeight] - Screen height for computer-use mode
|
|
152
|
+
* @returns {Promise<BrowserSession>} Connected browser session
|
|
153
|
+
*/
|
|
154
|
+
async createSession(options = {}) {
|
|
155
|
+
const wsUrl = this.baseUrl.replace(/^http/, 'ws');
|
|
156
|
+
const mode = options.mode || 'auto';
|
|
157
|
+
const params = new URLSearchParams({
|
|
158
|
+
apiKey: this.apiKey,
|
|
159
|
+
mode,
|
|
160
|
+
});
|
|
161
|
+
if (options.screenWidth) params.set('screenWidth', String(options.screenWidth));
|
|
162
|
+
if (options.screenHeight) params.set('screenHeight', String(options.screenHeight));
|
|
163
|
+
const url = `${wsUrl}/session?${params}`;
|
|
164
|
+
|
|
165
|
+
const session = new BrowserSession(url, { verbose: this.verbose });
|
|
166
|
+
await session.connect();
|
|
167
|
+
return session;
|
|
168
|
+
}
|
|
169
|
+
|
|
95
170
|
/**
|
|
96
171
|
* Check API health and your account status
|
|
97
172
|
* @returns {Promise<object>} Health check result
|
|
@@ -121,7 +196,7 @@ export class BrowserNativeClient {
|
|
|
121
196
|
headers: {
|
|
122
197
|
'x-api-key': this.apiKey,
|
|
123
198
|
'Content-Type': 'application/json',
|
|
124
|
-
'User-Agent': 'Browser Native Client SDK/
|
|
199
|
+
'User-Agent': 'Browser Native Client SDK/2.0.0'
|
|
125
200
|
}
|
|
126
201
|
};
|
|
127
202
|
|
|
@@ -231,5 +306,161 @@ export async function quickShot(url, apiKey, options = {}) {
|
|
|
231
306
|
return client.quickshot(url, options);
|
|
232
307
|
}
|
|
233
308
|
|
|
309
|
+
/**
|
|
310
|
+
* Convenience function for bulk scraping multiple URLs
|
|
311
|
+
* @param {string[]} urls - URLs to scrape
|
|
312
|
+
* @param {string} apiKey - Your API key
|
|
313
|
+
* @param {object} options - Bulk options (concurrency, continueOnError, progressCallback)
|
|
314
|
+
* @returns {Promise<object>} Aggregated results
|
|
315
|
+
*/
|
|
316
|
+
export async function bulkScrape(urls, apiKey, options = {}) {
|
|
317
|
+
const client = new BrowserNativeClient(apiKey, options);
|
|
318
|
+
return client.bulkScrape(urls, options);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ── Browser Session (WebSocket) ──────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
class BrowserSession {
|
|
324
|
+
constructor(url, options = {}) {
|
|
325
|
+
this._url = url;
|
|
326
|
+
this._ws = null;
|
|
327
|
+
this._nextId = 1;
|
|
328
|
+
this._pending = new Map(); // id → { resolve, reject }
|
|
329
|
+
this._verbose = options.verbose || false;
|
|
330
|
+
this._eventHandlers = {};
|
|
331
|
+
this.sessionId = null;
|
|
332
|
+
this.backend = null;
|
|
333
|
+
this.vncUrl = null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async connect() {
|
|
337
|
+
return new Promise((resolve, reject) => {
|
|
338
|
+
const WebSocketImpl = typeof WebSocket !== 'undefined'
|
|
339
|
+
? WebSocket
|
|
340
|
+
: null;
|
|
341
|
+
|
|
342
|
+
if (!WebSocketImpl) {
|
|
343
|
+
// Node.js — try dynamic import
|
|
344
|
+
import('ws').then(ws => {
|
|
345
|
+
this._ws = new ws.default(this._url);
|
|
346
|
+
this._wireEvents(resolve, reject);
|
|
347
|
+
}).catch(() => {
|
|
348
|
+
reject(new Error('WebSocket not available. Install "ws" package for Node.js: npm install ws'));
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this._ws = new WebSocketImpl(this._url);
|
|
354
|
+
this._wireEvents(resolve, reject);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
_wireEvents(onConnected, onError) {
|
|
359
|
+
this._ws.onmessage = (event) => {
|
|
360
|
+
const data = typeof event.data === 'string' ? event.data : event.data.toString();
|
|
361
|
+
let msg;
|
|
362
|
+
try { msg = JSON.parse(data); } catch { return; }
|
|
363
|
+
|
|
364
|
+
if (msg.type === 'connected') {
|
|
365
|
+
this.sessionId = msg.sessionId;
|
|
366
|
+
this.backend = msg.backend;
|
|
367
|
+
this.vncUrl = msg.vncUrl || null;
|
|
368
|
+
if (this._verbose) console.log(`Browser Native: Session ${msg.sessionId} connected (${msg.backend})`);
|
|
369
|
+
onConnected(this);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (msg.type === 'fallback') {
|
|
374
|
+
this.backend = msg.to;
|
|
375
|
+
if (this._verbose) console.log(`Browser Native: Fallback ${msg.from} → ${msg.to} (${msg.reason})`);
|
|
376
|
+
if (this._eventHandlers.fallback) this._eventHandlers.fallback(msg);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (msg.type === 'closed') {
|
|
381
|
+
if (this._verbose) console.log(`Browser Native: Session closed (${msg.reason})`);
|
|
382
|
+
if (this._eventHandlers.closed) this._eventHandlers.closed(msg);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Match response to pending request by id
|
|
387
|
+
if (msg.id != null && this._pending.has(msg.id)) {
|
|
388
|
+
const { resolve, reject } = this._pending.get(msg.id);
|
|
389
|
+
this._pending.delete(msg.id);
|
|
390
|
+
if (msg.type === 'error') {
|
|
391
|
+
reject(new Error(msg.message));
|
|
392
|
+
} else {
|
|
393
|
+
if (msg.backend) this.backend = msg.backend;
|
|
394
|
+
resolve(msg.data);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
this._ws.onerror = (err) => {
|
|
400
|
+
onError(new Error(err.message || 'WebSocket connection failed'));
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
this._ws.onclose = (event) => {
|
|
404
|
+
// Reject all pending requests
|
|
405
|
+
for (const [, { reject }] of this._pending) {
|
|
406
|
+
reject(new Error(`Session closed: ${event.reason || 'unknown'}`));
|
|
407
|
+
}
|
|
408
|
+
this._pending.clear();
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
_send(action, params = {}) {
|
|
413
|
+
return new Promise((resolve, reject) => {
|
|
414
|
+
if (!this._ws || this._ws.readyState !== 1) {
|
|
415
|
+
return reject(new Error('Session not connected'));
|
|
416
|
+
}
|
|
417
|
+
const id = this._nextId++;
|
|
418
|
+
this._pending.set(id, { resolve, reject });
|
|
419
|
+
this._ws.send(JSON.stringify({ id, action, ...params }));
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
on(event, handler) {
|
|
424
|
+
this._eventHandlers[event] = handler;
|
|
425
|
+
return this;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async goto(url) { return this._send('goto', { url }); }
|
|
429
|
+
async click(selector, options = {}) { return this._send('click', { selector, ...options }); }
|
|
430
|
+
async type(selector, text, options = {}) { return this._send('type', { selector, text, ...options }); }
|
|
431
|
+
async scroll(direction = 'down', amount = 500) { return this._send('scroll', { direction, amount }); }
|
|
432
|
+
async hover(selector) { return this._send('hover', { selector }); }
|
|
433
|
+
async select(selector, ...values) { return this._send('select', { selector, values }); }
|
|
434
|
+
async pressKey(key) { return this._send('pressKey', { key }); }
|
|
435
|
+
async goBack() { return this._send('goBack'); }
|
|
436
|
+
async goForward() { return this._send('goForward'); }
|
|
437
|
+
async screenshot(options = {}) { return this._send('screenshot', options); }
|
|
438
|
+
async getPageState(options = {}) { return this._send('getPageState', options); }
|
|
439
|
+
async extractContent() { return this._send('extractContent'); }
|
|
440
|
+
async waitFor(selector, timeout) { return this._send('waitFor', { selector, timeout }); }
|
|
441
|
+
async evaluate(fn) { return this._send('evaluate', { fn }); }
|
|
442
|
+
async getCookies() { return this._send('getCookies'); }
|
|
443
|
+
async setCookies(cookies) { return this._send('setCookies', { cookies }); }
|
|
444
|
+
|
|
445
|
+
// Coordinate-based actions (computer-use mode)
|
|
446
|
+
async mouseMove(x, y) { return this._send('mouseMove', { x, y }); }
|
|
447
|
+
async clickAt(x, y, button = 'left') { return this._send('clickAt', { x, y, button }); }
|
|
448
|
+
async doubleClickAt(x, y, button = 'left') { return this._send('doubleClickAt', { x, y, button }); }
|
|
449
|
+
async drag(startX, startY, endX, endY) { return this._send('drag', { startX, startY, endX, endY }); }
|
|
450
|
+
async scrollAt(x, y, direction, amount) { return this._send('scrollAt', { x, y, direction, amount }); }
|
|
451
|
+
async typeText(text) { return this._send('typeText', { text }); }
|
|
452
|
+
async getCursorPosition() { return this._send('getCursorPosition'); }
|
|
453
|
+
async getScreenSize() { return this._send('getScreenSize'); }
|
|
454
|
+
|
|
455
|
+
close() {
|
|
456
|
+
if (this._ws) {
|
|
457
|
+
this._ws.close();
|
|
458
|
+
this._ws = null;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export { BrowserSession };
|
|
464
|
+
|
|
234
465
|
// Default export for CommonJS compatibility
|
|
235
466
|
export default BrowserNativeClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monostate/browsernative-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Browser Native client SDK for web scraping and content extraction API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {},
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
37
|
},
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 BNCA Team
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|