spider-browser 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Spider Contributors
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.
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # spider-browser (TypeScript)
2
+
3
+ TypeScript client for [Spider's](https://spider.cloud) pre-warmed browser fleet. Full stealth browsers with smart retry, browser switching, and AI automation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install spider-browser
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { SpiderBrowser } from 'spider-browser';
15
+
16
+ const browser = new SpiderBrowser({
17
+ apiKey: process.env.SPIDER_API_KEY!,
18
+ browser: 'auto', // auto-selects best browser
19
+ });
20
+
21
+ await browser.init();
22
+
23
+ // Navigate
24
+ await browser.page.goto('https://example.com');
25
+
26
+ // Get page info
27
+ const title = await browser.page.title();
28
+ const url = await browser.page.url();
29
+ const html = await browser.page.content();
30
+ const screenshot = await browser.page.screenshot(); // base64 PNG
31
+
32
+ // Interact
33
+ await browser.page.click('a');
34
+ await browser.page.fill('input[name=q]', 'spider browser');
35
+ await browser.page.press('Enter');
36
+
37
+ // Evaluate JavaScript
38
+ const result = await browser.page.evaluate('document.title');
39
+
40
+ await browser.close();
41
+ ```
42
+
43
+ ## Browser Types
44
+
45
+ | Browser | Value | Description |
46
+ |---|---|---|
47
+ | Auto | `'auto'` | Server picks the best browser (default) |
48
+ | Chrome | `'chrome'` | Full stealth Chrome with CDP |
49
+ | Chrome-H | `'chrome-h'` | Chrome with enhanced headless rendering |
50
+ | Chrome-New | `'chrome-new'` | Chrome headless new mode |
51
+ | Firefox | `'firefox'` | Full stealth Firefox with BiDi |
52
+ | Servo | `'servo'` | Servo engine for edge cases |
53
+ | LightPanda | `'lightpanda'` | LightPanda for edge cases |
54
+
55
+ ```typescript
56
+ // Auto-select best browser (default)
57
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'auto' });
58
+
59
+ // Specific browsers
60
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'chrome' });
61
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'chrome-h' });
62
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'chrome-new' });
63
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'firefox' });
64
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'servo' });
65
+ new SpiderBrowser({ apiKey: 'sk-xxx', browser: 'lightpanda' });
66
+ ```
67
+
68
+ ## Configuration
69
+
70
+ ```typescript
71
+ const browser = new SpiderBrowser({
72
+ apiKey: 'sk-xxx', // Required: Spider API key
73
+ serverUrl: 'wss://browser.spider.cloud', // WebSocket server URL
74
+ browser: 'auto', // Browser type
75
+ url: 'https://example.com', // Target URL hint for smart routing
76
+ captcha: 'solve', // 'off' | 'detect' | 'solve'
77
+ smartRetry: true, // Auto-retry & browser switching
78
+ maxRetries: 12, // Max retry attempts
79
+ stealth: 0, // Stealth level (0=auto, 1-3=explicit)
80
+ maxStealthLevels: 3, // Max stealth level for auto-escalation
81
+ connectTimeoutMs: 30000, // WebSocket connect timeout
82
+ commandTimeoutMs: 30000, // CDP/BiDi command timeout
83
+ retryTimeoutMs: 15000, // Shorter timeout for retries
84
+ hedge: false, // Hedge session for parallel racing
85
+ record: false, // Enable screencast recording
86
+ logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'none'
87
+ llm: { ... }, // LLM config for AI methods
88
+ });
89
+ ```
90
+
91
+ ## Page API
92
+
93
+ ### Navigation
94
+
95
+ | Method | Description |
96
+ |---|---|
97
+ | `goto(url)` | Navigate to URL and wait for load |
98
+ | `gotoFast(url)` | Navigate with 5s max wait (for SPAs) |
99
+ | `goBack()` | Browser back |
100
+ | `goForward()` | Browser forward |
101
+ | `reload()` | Reload page |
102
+
103
+ ### Content
104
+
105
+ | Method | Description |
106
+ |---|---|
107
+ | `content(waitMs?, minLength?)` | Full HTML with readiness checks |
108
+ | `rawContent()` | Immediate HTML without waiting |
109
+ | `contentWithEarlyReturn(maxWaitMs?, minLength?, pollMs?)` | Poll for content with early return (SPAs) |
110
+ | `title()` | Page title |
111
+ | `url()` | Current URL |
112
+ | `screenshot()` | Base64 PNG |
113
+ | `evaluate(expression)` | Execute JS and return result |
114
+
115
+ ### Click Actions
116
+
117
+ | Method | Description |
118
+ |---|---|
119
+ | `click(selector)` | Click element |
120
+ | `clickAt(x, y)` | Click at coordinates |
121
+ | `clickAndHold(selector, holdMs?)` | Click and hold element (default 1000ms) |
122
+ | `clickAndHoldAt(x, y, holdMs?)` | Click and hold at coordinates (default 1000ms) |
123
+ | `dblclick(selector)` | Double-click element |
124
+ | `rightClick(selector)` | Right-click element |
125
+ | `clickAll(selector)` | Click all matching elements |
126
+
127
+ ```typescript
128
+ // Long press for mobile gestures or drag triggers
129
+ await browser.page.clickAndHold('#hold-button', 2000);
130
+
131
+ // Long press at coordinates
132
+ await browser.page.clickAndHoldAt(100, 200, 1500);
133
+ ```
134
+
135
+ ### Input
136
+
137
+ | Method | Description |
138
+ |---|---|
139
+ | `fill(selector, value)` | Focus, clear, and type into element |
140
+ | `type(value)` | Type into currently focused element |
141
+ | `press(key)` | Press a named key (Enter, Tab, etc.) |
142
+ | `clear(selector)` | Clear input field |
143
+ | `select(selector, value)` | Select dropdown option |
144
+
145
+ ### Focus & Hover
146
+
147
+ | Method | Description |
148
+ |---|---|
149
+ | `focus(selector)` | Focus element |
150
+ | `blur(selector)` | Unfocus element |
151
+ | `hover(selector)` | Hover over element |
152
+ | `drag(fromSelector, toSelector)` | Drag between elements |
153
+
154
+ ### Scroll
155
+
156
+ | Method | Description |
157
+ |---|---|
158
+ | `scrollY(pixels)` | Scroll vertically (positive = down) |
159
+ | `scrollX(pixels)` | Scroll horizontally (positive = right) |
160
+ | `scrollTo(selector)` | Scroll element into view |
161
+ | `scrollToPoint(x, y)` | Scroll to absolute page coordinates |
162
+
163
+ ### Wait
164
+
165
+ | Method | Description |
166
+ |---|---|
167
+ | `waitForSelector(selector, timeoutMs?)` | Wait for element (default 5s) |
168
+ | `waitForNavigation(timeoutMs?)` | Wait for page load (default 5s) |
169
+ | `waitForReady(timeoutMs?)` | Wait for readyState + DOM stability (default 10s) |
170
+ | `waitForContent(minLength?, timeoutMs?)` | Wait for content to exceed length (default 8s) |
171
+ | `waitForNetworkIdle(timeoutMs?)` | Wait for network idle + DOM stability (default 8s) |
172
+
173
+ ### DOM
174
+
175
+ | Method | Description |
176
+ |---|---|
177
+ | `querySelector(selector)` | Get element outer HTML |
178
+ | `querySelectorAll(selector)` | Get all matching elements |
179
+ | `textContent(selector)` | Get element text content |
180
+
181
+ ### Viewport
182
+
183
+ | Method | Description |
184
+ |---|---|
185
+ | `setViewport(w, h, dpr?, mobile?)` | Set viewport dimensions |
186
+
187
+ ## observe() — Element Discovery
188
+
189
+ Discover interactive elements on any page without an LLM:
190
+
191
+ ```typescript
192
+ await browser.page.goto('https://example.com');
193
+
194
+ const elements = await browser.observe();
195
+ for (const el of elements) {
196
+ console.log(`<${el.tag}> selector=${el.selector} text=${el.text}`);
197
+ }
198
+
199
+ // With LLM: rank elements by relevance
200
+ const relevant = await browser.observe('Find the login button');
201
+ ```
202
+
203
+ Each element includes: `selector`, `tag`, `type`, `text`, `ariaLabel`, `placeholder`, `href`, `value`, `rect`, and optionally `score` (with LLM).
204
+
205
+ ## AI Automation
206
+
207
+ ### Setup
208
+
209
+ ```typescript
210
+ const browser = new SpiderBrowser({
211
+ apiKey: process.env.SPIDER_API_KEY!,
212
+ llm: {
213
+ provider: 'openai', // or 'anthropic', 'openrouter'
214
+ model: 'gpt-4o',
215
+ apiKey: process.env.OPENAI_API_KEY!,
216
+ },
217
+ });
218
+ ```
219
+
220
+ ### act() — Single Action
221
+
222
+ ```typescript
223
+ await browser.page.goto('https://example.com');
224
+ await browser.act('Click the "More information" link');
225
+ ```
226
+
227
+ ### extract() — Structured Data
228
+
229
+ ```typescript
230
+ import { z } from 'zod';
231
+
232
+ const data = await browser.extract('Get the page heading and all links');
233
+
234
+ // With Zod schema validation
235
+ const PageInfo = z.object({
236
+ title: z.string(),
237
+ linkCount: z.number(),
238
+ });
239
+ const info = await browser.extract('Get page title and number of links', {
240
+ schema: PageInfo,
241
+ });
242
+ ```
243
+
244
+ ### agent() — Autonomous Multi-Step
245
+
246
+ ```typescript
247
+ const agent = browser.agent({
248
+ maxRounds: 20,
249
+ stepDelayMs: 1500,
250
+ });
251
+ const result = await agent.execute('Find and click every link on this page');
252
+ console.log(`Done: ${result.done}, Rounds: ${result.rounds}`);
253
+ ```
254
+
255
+ ## Smart Retry & Browser Switching
256
+
257
+ Enabled by default. On failure, the engine classifies errors and automatically switches browsers:
258
+
259
+ ```typescript
260
+ const browser = new SpiderBrowser({
261
+ apiKey: 'sk-xxx',
262
+ smartRetry: true, // default
263
+ maxRetries: 3,
264
+ });
265
+
266
+ // Wrap any operation with retry
267
+ const html = await browser.withRetry(async () => {
268
+ await browser.page.goto('https://target.com');
269
+ return browser.page.content();
270
+ });
271
+
272
+ browser.on('browser.switching', (e) => {
273
+ console.log(`Switching: ${e.from} -> ${e.to} (reason: ${e.reason})`);
274
+ });
275
+ ```
276
+
277
+ **Rotation order:** Chrome -> Chrome-H -> Firefox -> LightPanda
278
+
279
+ ## Session Recording
280
+
281
+ Enable screencast recording to capture browser sessions:
282
+
283
+ ```typescript
284
+ const browser = new SpiderBrowser({
285
+ apiKey: 'sk-xxx',
286
+ record: true,
287
+ });
288
+
289
+ browser.on('recording.started', (e) => console.log('Recording:', e.sessionId));
290
+ browser.on('screencast.frame', (e) => console.log('Frame:', e.frameIndex));
291
+ browser.on('recording.completed', (e) => {
292
+ console.log(`Done: ${e.frameCount} frames, ${e.durationMs}ms`);
293
+ });
294
+ ```
295
+
296
+ ## Events
297
+
298
+ ```typescript
299
+ // Connection
300
+ browser.on('ws.open', () => { });
301
+ browser.on('ws.close', (e) => { /* e.code, e.reason */ });
302
+ browser.on('ws.error', (e) => { /* e.error */ });
303
+
304
+ // Captcha
305
+ browser.on('captcha.detected', (e) => { /* e.types, e.url */ });
306
+ browser.on('captcha.solving', (e) => { /* e.types, e.round */ });
307
+ browser.on('captcha.solved', () => { });
308
+ browser.on('captcha.failed', (e) => { /* e.reason */ });
309
+
310
+ // Browser switching
311
+ browser.on('browser.switching', (e) => { /* e.from, e.to, e.reason */ });
312
+ browser.on('browser.switched', (e) => { /* e.browser */ });
313
+ browser.on('retry.attempt', (e) => { /* e.attempt, e.maxRetries, e.error */ });
314
+
315
+ // Stealth
316
+ browser.on('stealth.escalated', (e) => { /* e.from, e.to, e.reason */ });
317
+
318
+ // Metering
319
+ browser.on('metering', (e) => { /* e.credits, e.session_credits_used */ });
320
+
321
+ // Page
322
+ browser.on('page.navigated', (e) => { /* e.url */ });
323
+ browser.on('page.loaded', (e) => { /* e.url */ });
324
+
325
+ // Agent
326
+ browser.on('agent.step', (e) => { /* e.round, e.label */ });
327
+ browser.on('agent.done', (e) => { /* e.rounds, e.result */ });
328
+ browser.on('agent.error', (e) => { /* e.error, e.round */ });
329
+
330
+ // Recording
331
+ browser.on('recording.started', (e) => { /* e.sessionId */ });
332
+ browser.on('recording.completed', (e) => { /* e.sessionId, e.frameCount, e.durationMs */ });
333
+ browser.on('screencast.frame', (e) => { /* e.data, e.timestamp, e.frameIndex */ });
334
+ browser.on('screencast.rrwebEvents', (e) => { /* e.events */ });
335
+ ```
336
+
337
+ ## Supported LLM Providers
338
+
339
+ | Provider | Config | Notes |
340
+ |---|---|---|
341
+ | OpenAI | `provider: 'openai'` | GPT-4o, GPT-4-turbo |
342
+ | Anthropic | `provider: 'anthropic'` | Claude 4.5, Claude Opus |
343
+ | OpenRouter | `provider: 'openrouter'` | Any model via OpenRouter |
344
+ | Custom | `provider: 'openai', baseUrl: '...'` | Any OpenAI-compatible endpoint |
345
+
346
+ ## Requirements
347
+
348
+ - Node.js 18+
349
+ - `ws` >= 8.18.0
350
+ - `zod` >= 3.23.0 (for schema validation in extract)
351
+
352
+ ## License
353
+
354
+ MIT