webpeel 0.1.2 → 0.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 CHANGED
@@ -408,7 +408,7 @@ A: WebPeel runs locally for free (Firecrawl is cloud-only). We also have smart e
408
408
  A: Yes! Run `npm run serve` to start the API server. See [docs/self-hosting.md](docs/self-hosting.md) (coming soon).
409
409
 
410
410
  **Q: Does this violate websites' Terms of Service?**
411
- A: WebPeel respects `robots.txt` by default. Always check a site's ToS before scraping at scale.
411
+ A: WebPeel is a tool — how you use it is up to you. Always check a site's ToS before fetching at scale. We recommend respecting `robots.txt` in your own workflows.
412
412
 
413
413
  **Q: What about CAPTCHA and Cloudflare?**
414
414
  A: WebPeel handles most Cloudflare challenges automatically. For CAPTCHAs, you'll need a solving service (not included).
package/dist/cli.js CHANGED
@@ -14,12 +14,14 @@
14
14
  */
15
15
  import { Command } from 'commander';
16
16
  import ora from 'ora';
17
- import { peel, cleanup } from './index.js';
17
+ import { writeFileSync } from 'fs';
18
+ import { peel, peelBatch, cleanup } from './index.js';
18
19
  const program = new Command();
19
20
  program
20
21
  .name('webpeel')
21
22
  .description('Fast web fetcher for AI agents')
22
- .version('0.1.2');
23
+ .version('0.2.0')
24
+ .enablePositionalOptions();
23
25
  program
24
26
  .argument('[url]', 'URL to fetch')
25
27
  .option('-r, --render', 'Use headless browser (for JS-heavy sites)')
@@ -30,6 +32,12 @@ program
30
32
  .option('-t, --timeout <ms>', 'Request timeout (ms)', parseInt, 30000)
31
33
  .option('--ua <agent>', 'Custom user agent')
32
34
  .option('-s, --silent', 'Silent mode (no spinner)')
35
+ .option('--screenshot [path]', 'Take a screenshot (optionally save to file path)')
36
+ .option('--full-page', 'Full-page screenshot (use with --screenshot)')
37
+ .option('--selector <css>', 'CSS selector to extract (e.g., "article", ".content")')
38
+ .option('--exclude <selectors...>', 'CSS selectors to exclude (e.g., ".sidebar" ".ads")')
39
+ .option('-H, --header <header...>', 'Custom headers (e.g., "Authorization: Bearer token")')
40
+ .option('--cookie <cookie...>', 'Cookies to set (e.g., "session=abc123")')
33
41
  .action(async (url, options) => {
34
42
  if (!url) {
35
43
  console.error('Error: URL is required\n');
@@ -65,12 +73,34 @@ program
65
73
  console.error('Error: Wait time must be between 0 and 60000ms');
66
74
  process.exit(1);
67
75
  }
76
+ // Parse custom headers
77
+ let headers;
78
+ if (options.header && options.header.length > 0) {
79
+ headers = {};
80
+ for (const header of options.header) {
81
+ const colonIndex = header.indexOf(':');
82
+ if (colonIndex === -1) {
83
+ console.error(`Error: Invalid header format: ${header}`);
84
+ console.error('Expected format: "Key: Value"');
85
+ process.exit(1);
86
+ }
87
+ const key = header.slice(0, colonIndex).trim();
88
+ const value = header.slice(colonIndex + 1).trim();
89
+ headers[key] = value;
90
+ }
91
+ }
68
92
  // Build peel options
69
93
  const peelOptions = {
70
94
  render: options.render || false,
71
95
  wait: options.wait || 0,
72
96
  timeout: options.timeout,
73
97
  userAgent: options.ua,
98
+ screenshot: options.screenshot !== undefined,
99
+ screenshotFullPage: options.fullPage || false,
100
+ selector: options.selector,
101
+ exclude: options.exclude,
102
+ headers,
103
+ cookies: options.cookie,
74
104
  };
75
105
  // Determine format
76
106
  if (options.html) {
@@ -87,12 +117,42 @@ program
87
117
  if (spinner) {
88
118
  spinner.succeed(`Fetched in ${result.elapsed}ms using ${result.method} method`);
89
119
  }
90
- // Output results
120
+ // Handle screenshot saving
121
+ if (options.screenshot && result.screenshot) {
122
+ const screenshotPath = typeof options.screenshot === 'string'
123
+ ? options.screenshot
124
+ : 'screenshot.png';
125
+ const screenshotBuffer = Buffer.from(result.screenshot, 'base64');
126
+ writeFileSync(screenshotPath, screenshotBuffer);
127
+ if (!options.silent) {
128
+ console.error(`Screenshot saved to: ${screenshotPath}`);
129
+ }
130
+ // Remove screenshot from JSON output if saving to file
131
+ if (typeof options.screenshot === 'string') {
132
+ delete result.screenshot;
133
+ }
134
+ }
135
+ // Output results with proper stdout flushing
91
136
  if (options.json) {
92
- console.log(JSON.stringify(result, null, 2));
137
+ const jsonStr = JSON.stringify(result, null, 2);
138
+ await new Promise((resolve, reject) => {
139
+ process.stdout.write(jsonStr + '\n', (err) => {
140
+ if (err)
141
+ reject(err);
142
+ else
143
+ resolve();
144
+ });
145
+ });
93
146
  }
94
147
  else {
95
- console.log(result.content);
148
+ await new Promise((resolve, reject) => {
149
+ process.stdout.write(result.content + '\n', (err) => {
150
+ if (err)
151
+ reject(err);
152
+ else
153
+ resolve();
154
+ });
155
+ });
96
156
  }
97
157
  // Clean up and exit
98
158
  await cleanup();
@@ -112,15 +172,213 @@ program
112
172
  process.exit(1);
113
173
  }
114
174
  });
115
- // Future commands
175
+ // Search command
116
176
  program
117
- .command('search')
118
- .argument('<query>', 'Search query')
119
- .description('Search using DuckDuckGo (future)')
120
- .action(() => {
121
- console.log('Search command not yet implemented');
122
- console.log('Coming soon: DuckDuckGo search integration');
123
- process.exit(1);
177
+ .command('search <query>')
178
+ .description('Search using DuckDuckGo')
179
+ .option('-n, --count <n>', 'Number of results (1-10)', '5')
180
+ .option('--json', 'Output as JSON')
181
+ .option('-s, --silent', 'Silent mode')
182
+ .action(async (query, options) => {
183
+ const isJson = options.json;
184
+ const isSilent = options.silent;
185
+ const count = parseInt(options.count) || 5;
186
+ const spinner = isSilent ? null : ora('Searching...').start();
187
+ try {
188
+ // Import the search function dynamically
189
+ const { fetch: undiciFetch } = await import('undici');
190
+ const { load } = await import('cheerio');
191
+ const searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
192
+ const response = await undiciFetch(searchUrl, {
193
+ headers: {
194
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
195
+ },
196
+ });
197
+ if (!response.ok) {
198
+ throw new Error(`Search failed: HTTP ${response.status}`);
199
+ }
200
+ const html = await response.text();
201
+ const $ = load(html);
202
+ const results = [];
203
+ $('.result').each((_i, elem) => {
204
+ if (results.length >= count)
205
+ return;
206
+ const $result = $(elem);
207
+ const title = $result.find('.result__title').text().trim();
208
+ const rawUrl = $result.find('.result__a').attr('href') || '';
209
+ const snippet = $result.find('.result__snippet').text().trim();
210
+ if (!title || !rawUrl)
211
+ return;
212
+ // Extract actual URL from DuckDuckGo redirect
213
+ let url = rawUrl;
214
+ try {
215
+ const ddgUrl = new URL(rawUrl, 'https://duckduckgo.com');
216
+ const uddg = ddgUrl.searchParams.get('uddg');
217
+ if (uddg) {
218
+ url = decodeURIComponent(uddg);
219
+ }
220
+ }
221
+ catch {
222
+ // Use raw URL if parsing fails
223
+ }
224
+ // Validate final URL
225
+ try {
226
+ const parsed = new URL(url);
227
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
228
+ return;
229
+ }
230
+ url = parsed.href;
231
+ }
232
+ catch {
233
+ return;
234
+ }
235
+ results.push({
236
+ title: title.slice(0, 200),
237
+ url,
238
+ snippet: snippet.slice(0, 500)
239
+ });
240
+ });
241
+ if (spinner) {
242
+ spinner.succeed(`Found ${results.length} results`);
243
+ }
244
+ if (isJson) {
245
+ const jsonStr = JSON.stringify(results, null, 2);
246
+ await new Promise((resolve, reject) => {
247
+ process.stdout.write(jsonStr + '\n', (err) => {
248
+ if (err)
249
+ reject(err);
250
+ else
251
+ resolve();
252
+ });
253
+ });
254
+ }
255
+ else {
256
+ for (const result of results) {
257
+ console.log(`\n${result.title}`);
258
+ console.log(result.url);
259
+ console.log(result.snippet);
260
+ }
261
+ }
262
+ process.exit(0);
263
+ }
264
+ catch (error) {
265
+ if (spinner) {
266
+ spinner.fail('Search failed');
267
+ }
268
+ if (error instanceof Error) {
269
+ console.error(`\nError: ${error.message}`);
270
+ }
271
+ else {
272
+ console.error('\nError: Unknown error occurred');
273
+ }
274
+ process.exit(1);
275
+ }
276
+ });
277
+ // Batch command
278
+ program
279
+ .command('batch <file>')
280
+ .description('Fetch multiple URLs')
281
+ .option('-c, --concurrency <n>', 'Max concurrent fetches (default: 3)', '3')
282
+ .option('-o, --output <dir>', 'Output directory (one file per URL)')
283
+ .option('--json', 'Output as JSON array')
284
+ .option('-s, --silent', 'Silent mode')
285
+ .option('-r, --render', 'Use headless browser')
286
+ .option('--selector <css>', 'CSS selector to extract')
287
+ .action(async (file, options) => {
288
+ const isJson = options.json;
289
+ const isSilent = options.silent;
290
+ const shouldRender = options.render;
291
+ const selector = options.selector;
292
+ const spinner = isSilent ? null : ora('Loading URLs...').start();
293
+ try {
294
+ const { readFileSync } = await import('fs');
295
+ // Read URLs from file
296
+ let urls;
297
+ try {
298
+ const content = readFileSync(file, 'utf-8');
299
+ urls = content.split('\n')
300
+ .map(line => line.trim())
301
+ .filter(line => line && !line.startsWith('#'));
302
+ }
303
+ catch (error) {
304
+ throw new Error(`Failed to read file: ${file}`);
305
+ }
306
+ if (urls.length === 0) {
307
+ throw new Error('No URLs found in file');
308
+ }
309
+ if (spinner) {
310
+ spinner.text = `Fetching ${urls.length} URLs (concurrency: ${options.concurrency})...`;
311
+ }
312
+ // Batch fetch
313
+ const results = await peelBatch(urls, {
314
+ concurrency: parseInt(options.concurrency) || 3,
315
+ render: shouldRender,
316
+ selector: selector,
317
+ });
318
+ if (spinner) {
319
+ const successCount = results.filter(r => 'content' in r).length;
320
+ spinner.succeed(`Completed: ${successCount}/${urls.length} successful`);
321
+ }
322
+ // Output results
323
+ if (isJson) {
324
+ const jsonStr = JSON.stringify(results, null, 2);
325
+ await new Promise((resolve, reject) => {
326
+ process.stdout.write(jsonStr + '\n', (err) => {
327
+ if (err)
328
+ reject(err);
329
+ else
330
+ resolve();
331
+ });
332
+ });
333
+ }
334
+ else if (options.output) {
335
+ const { writeFileSync, mkdirSync } = await import('fs');
336
+ const { join } = await import('path');
337
+ // Create output directory
338
+ mkdirSync(options.output, { recursive: true });
339
+ results.forEach((result, i) => {
340
+ const urlObj = new URL(urls[i]);
341
+ const filename = `${i + 1}_${urlObj.hostname.replace(/[^a-z0-9]/gi, '_')}.md`;
342
+ const filepath = join(options.output, filename);
343
+ if ('content' in result) {
344
+ writeFileSync(filepath, result.content);
345
+ }
346
+ else {
347
+ writeFileSync(filepath, `Error: ${result.error}`);
348
+ }
349
+ });
350
+ if (!isSilent) {
351
+ console.log(`\nResults saved to: ${options.output}`);
352
+ }
353
+ }
354
+ else {
355
+ // Print results to stdout
356
+ results.forEach((result, i) => {
357
+ console.log(`\n=== ${urls[i]} ===\n`);
358
+ if ('content' in result) {
359
+ console.log(result.content.slice(0, 500) + '...');
360
+ }
361
+ else {
362
+ console.log(`Error: ${result.error}`);
363
+ }
364
+ });
365
+ }
366
+ await cleanup();
367
+ process.exit(0);
368
+ }
369
+ catch (error) {
370
+ if (spinner) {
371
+ spinner.fail('Batch fetch failed');
372
+ }
373
+ if (error instanceof Error) {
374
+ console.error(`\nError: ${error.message}`);
375
+ }
376
+ else {
377
+ console.error('\nError: Unknown error occurred');
378
+ }
379
+ await cleanup();
380
+ process.exit(1);
381
+ }
124
382
  });
125
383
  program
126
384
  .command('serve')
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -5,13 +5,15 @@ export interface FetchResult {
5
5
  html: string;
6
6
  url: string;
7
7
  statusCode?: number;
8
+ screenshot?: Buffer;
9
+ contentType?: string;
8
10
  }
9
11
  /**
10
12
  * Simple HTTP fetch using native fetch + Cheerio
11
13
  * Fast and lightweight, but can be blocked by Cloudflare/bot detection
12
14
  * SECURITY: Manual redirect handling with SSRF re-validation
13
15
  */
14
- export declare function simpleFetch(url: string, userAgent?: string, timeoutMs?: number): Promise<FetchResult>;
16
+ export declare function simpleFetch(url: string, userAgent?: string, timeoutMs?: number, customHeaders?: Record<string, string>): Promise<FetchResult>;
15
17
  /**
16
18
  * Fetch using headless Chromium via Playwright
17
19
  * Slower but can handle JavaScript-heavy sites and bypass some bot detection
@@ -20,6 +22,10 @@ export declare function browserFetch(url: string, options?: {
20
22
  userAgent?: string;
21
23
  waitMs?: number;
22
24
  timeoutMs?: number;
25
+ screenshot?: boolean;
26
+ screenshotFullPage?: boolean;
27
+ headers?: Record<string, string>;
28
+ cookies?: string[];
23
29
  }): Promise<FetchResult>;
24
30
  /**
25
31
  * Retry a fetch operation with exponential backoff
@@ -1 +1 @@
1
- {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/core/fetcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2PH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,GAAE,MAAc,GACxB,OAAO,CAAC,WAAW,CAAC,CAyItB;AAuBD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,WAAW,CAAC,CAoGtB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,GAAE,MAAU,EACvB,WAAW,GAAE,MAAa,GACzB,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7C"}
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/core/fetcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2PH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,GAAE,MAAc,EACzB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,OAAO,CAAC,WAAW,CAAC,CA2JtB;AAuBD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACf,GACL,OAAO,CAAC,WAAW,CAAC,CAiKtB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,GAAE,MAAU,EACvB,WAAW,GAAE,MAAa,GACzB,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7C"}
@@ -224,11 +224,31 @@ function validateUserAgent(userAgent) {
224
224
  * Fast and lightweight, but can be blocked by Cloudflare/bot detection
225
225
  * SECURITY: Manual redirect handling with SSRF re-validation
226
226
  */
227
- export async function simpleFetch(url, userAgent, timeoutMs = 30000) {
227
+ export async function simpleFetch(url, userAgent, timeoutMs = 30000, customHeaders) {
228
228
  // SECURITY: Validate URL to prevent SSRF
229
229
  validateUrl(url);
230
230
  // Validate user agent if provided
231
231
  const validatedUserAgent = userAgent ? validateUserAgent(userAgent) : getRandomUserAgent();
232
+ // SECURITY: Merge custom headers with defaults, block Host header override
233
+ const defaultHeaders = {
234
+ 'User-Agent': validatedUserAgent,
235
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
236
+ 'Accept-Language': 'en-US,en;q=0.9',
237
+ 'Accept-Encoding': 'gzip, deflate, br',
238
+ 'DNT': '1',
239
+ 'Connection': 'keep-alive',
240
+ 'Upgrade-Insecure-Requests': '1',
241
+ };
242
+ const mergedHeaders = { ...defaultHeaders };
243
+ if (customHeaders) {
244
+ for (const [key, value] of Object.entries(customHeaders)) {
245
+ // SECURITY: Block Host header override
246
+ if (key.toLowerCase() === 'host') {
247
+ throw new WebPeelError('Custom Host header is not allowed');
248
+ }
249
+ mergedHeaders[key] = value;
250
+ }
251
+ }
232
252
  const MAX_REDIRECTS = 10;
233
253
  let redirectCount = 0;
234
254
  let currentUrl = url;
@@ -245,15 +265,7 @@ export async function simpleFetch(url, userAgent, timeoutMs = 30000) {
245
265
  const timer = setTimeout(() => controller.abort(), timeoutMs);
246
266
  try {
247
267
  const response = await fetch(currentUrl, {
248
- headers: {
249
- 'User-Agent': validatedUserAgent,
250
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
251
- 'Accept-Language': 'en-US,en;q=0.9',
252
- 'Accept-Encoding': 'gzip, deflate, br',
253
- 'DNT': '1',
254
- 'Connection': 'keep-alive',
255
- 'Upgrade-Insecure-Requests': '1',
256
- },
268
+ headers: mergedHeaders,
257
269
  signal: controller.signal,
258
270
  redirect: 'manual', // SECURITY: Manual redirect handling
259
271
  });
@@ -277,8 +289,10 @@ export async function simpleFetch(url, userAgent, timeoutMs = 30000) {
277
289
  }
278
290
  // SECURITY: Validate Content-Type
279
291
  const contentType = response.headers.get('content-type') || '';
280
- if (!contentType.includes('text/html') && !contentType.includes('application/xhtml+xml')) {
281
- throw new WebPeelError('Unsupported content type. Only HTML is supported.');
292
+ if (!contentType.includes('text/html') &&
293
+ !contentType.includes('application/xhtml+xml') &&
294
+ !contentType.includes('application/pdf')) {
295
+ throw new WebPeelError(`Unsupported content type: ${contentType}. Supported: HTML, PDF`);
282
296
  }
283
297
  // SECURITY: Stream response with size limit (prevent memory exhaustion)
284
298
  const chunks = [];
@@ -323,6 +337,7 @@ export async function simpleFetch(url, userAgent, timeoutMs = 30000) {
323
337
  html,
324
338
  url: currentUrl,
325
339
  statusCode: response.status,
340
+ contentType,
326
341
  };
327
342
  }
328
343
  catch (error) {
@@ -364,13 +379,25 @@ async function getBrowser() {
364
379
  export async function browserFetch(url, options = {}) {
365
380
  // SECURITY: Validate URL to prevent SSRF
366
381
  validateUrl(url);
367
- const { userAgent, waitMs = 0, timeoutMs = 30000 } = options;
382
+ const { userAgent, waitMs = 0, timeoutMs = 30000, screenshot = false, screenshotFullPage = false, headers, cookies } = options;
368
383
  // Validate user agent if provided
369
384
  const validatedUserAgent = userAgent ? validateUserAgent(userAgent) : getRandomUserAgent();
370
385
  // Validate wait time
371
386
  if (waitMs < 0 || waitMs > 60000) {
372
387
  throw new WebPeelError('Wait time must be between 0 and 60000ms');
373
388
  }
389
+ // SECURITY: Validate custom headers if provided
390
+ if (headers) {
391
+ for (const [key, value] of Object.entries(headers)) {
392
+ // Block Host header override
393
+ if (key.toLowerCase() === 'host') {
394
+ throw new WebPeelError('Custom Host header is not allowed');
395
+ }
396
+ if (typeof value !== 'string' || value.length > 500) {
397
+ throw new WebPeelError('Invalid header value');
398
+ }
399
+ }
400
+ }
374
401
  // SECURITY: Limit concurrent browser pages with timeout
375
402
  const queueStartTime = Date.now();
376
403
  const QUEUE_TIMEOUT_MS = 30000; // 30 second max wait
@@ -387,16 +414,42 @@ export async function browserFetch(url, options = {}) {
387
414
  page = await browser.newPage({
388
415
  userAgent: validatedUserAgent,
389
416
  });
390
- // Block images, fonts, and other heavy resources for speed
391
- await page.route('**/*', (route) => {
392
- const resourceType = route.request().resourceType();
393
- if (['image', 'font', 'media', 'stylesheet'].includes(resourceType)) {
394
- route.abort();
395
- }
396
- else {
397
- route.continue();
398
- }
399
- });
417
+ // Set custom headers if provided
418
+ if (headers && Object.keys(headers).length > 0) {
419
+ await page.setExtraHTTPHeaders(headers);
420
+ }
421
+ // Set cookies if provided
422
+ if (cookies && cookies.length > 0) {
423
+ const parsedCookies = cookies.map(cookie => {
424
+ const [nameValue] = cookie.split(';').map(s => s.trim());
425
+ const [name, value] = nameValue.split('=');
426
+ if (!name || value === undefined) {
427
+ throw new WebPeelError(`Invalid cookie format: ${cookie}`);
428
+ }
429
+ return {
430
+ name: name.trim(),
431
+ value: value.trim(),
432
+ url,
433
+ };
434
+ });
435
+ await page.context().addCookies(parsedCookies);
436
+ }
437
+ // Block images, fonts, and other heavy resources for speed (unless screenshot is requested)
438
+ if (!screenshot) {
439
+ await page.route('**/*', (route) => {
440
+ const resourceType = route.request().resourceType();
441
+ if (['image', 'font', 'media', 'stylesheet'].includes(resourceType)) {
442
+ route.abort();
443
+ }
444
+ else {
445
+ route.continue();
446
+ }
447
+ });
448
+ }
449
+ else {
450
+ // For screenshots, allow all resources
451
+ await page.route('**/*', (route) => route.continue());
452
+ }
400
453
  // SECURITY: Wrap entire operation in timeout
401
454
  const fetchPromise = (async () => {
402
455
  await page.goto(url, {
@@ -422,9 +475,18 @@ export async function browserFetch(url, options = {}) {
422
475
  if (!html || html.length < 100) {
423
476
  throw new BlockedError('Empty or suspiciously small response from browser.');
424
477
  }
478
+ // Capture screenshot if requested
479
+ let screenshotBuffer;
480
+ if (screenshot) {
481
+ screenshotBuffer = await page.screenshot({
482
+ fullPage: screenshotFullPage,
483
+ type: 'png'
484
+ });
485
+ }
425
486
  return {
426
487
  html,
427
488
  url: finalUrl,
489
+ screenshot: screenshotBuffer,
428
490
  };
429
491
  }
430
492
  catch (error) {