
Modern, factory-based management for Puppeteer with advanced navigation features. Ideal for web scraping, testing, and automation.
- Browser Factory - Multiple browser instances with intelligent reuse
- Enhanced Navigation - CloudFlare bypass, retry mechanisms, and more
- TypeScript Support - Full type definitions for better DX
- Singleton Compatibility - Maintains backward compatibility
- Stealth Mode - Integrated puppeteer-extra-plugin-stealth
pnpm install puppeteer-extends
import { PuppeteerExtends, Logger } from 'puppeteer-extends';
import path from 'path';
const main = async () => {
const browser = await PuppeteerExtends.getBrowser({
isHeadless: true,
isDebug: true
});
const page = await browser.newPage();
await PuppeteerExtends.goto(page, 'https://example.com', {
maxRetries: 2,
timeout: 30000
});
await page.screenshot({
path: path.join(__dirname, 'screenshot.png')
});
await PuppeteerExtends.closePage(page);
await PuppeteerExtends.closeBrowser();
};
main().catch(console.error);
import { PuppeteerExtends } from 'puppeteer-extends';
const main = async () => {
const mainBrowser = await PuppeteerExtends.getBrowser({
instanceId: 'main',
isHeadless: true
});
const proxyBrowser = await PuppeteerExtends.getBrowser({
instanceId: 'proxy',
isHeadless: true,
customArguments: [
'--proxy-server=http://my-proxy.com:8080',
'--no-sandbox'
]
});
const mainPage = await mainBrowser.newPage();
const proxyPage = await proxyBrowser.newPage();
await PuppeteerExtends.closeBrowser('proxy');
await PuppeteerExtends.closeAllBrowsers();
};
import { PuppeteerExtends } from 'puppeteer-extends';
const scrape = async () => {
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
const success = await PuppeteerExtends.goto(page, 'https://example.com', {
waitUntil: ['networkidle0'],
timeout: 60000,
maxRetries: 3,
retryDelay: 5000,
isDebug: true
});
if (success) {
const elementVisible = await PuppeteerExtends.waitForSelector(
page,
'.content-loaded',
10000
);
if (elementVisible) {
const data = await page.evaluate(() => {
return {
title: document.title,
content: document.querySelector('.content')?.textContent
};
});
console.log('Scraped data:', data);
}
}
await PuppeteerExtends.closePage(page);
await PuppeteerExtends.closeBrowser();
};
import { Logger } from 'puppeteer-extends';
import path from 'path';
Logger.configure(path.join(__dirname, 'custom-logs'));
Logger.info('Starting browser');
Logger.debug('Debug information');
Logger.warn('Warning message');
Logger.error('Error occurred', new Error('Something went wrong'));
puppeteer-extends v1.7.0 introduces a powerful plugin system that allows extending functionality through lifecycle hooks:
import { PuppeteerExtends, StealthPlugin, ProxyPlugin } from 'puppeteer-extends';
await PuppeteerExtends.registerPlugin(new StealthPlugin({
hideWebDriver: true,
hideChromeRuntime: true
}));
await PuppeteerExtends.registerPlugin(new ProxyPlugin({
proxies: [
{ host: '123.45.67.89', port: 8080, username: 'user', password: 'pass' },
{ host: '98.76.54.32', port: 8080 },
],
rotationStrategy: 'sequential',
rotateOnNavigation: true,
rotateOnError: true
}));
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
| Plugin |
Description |
StealthPlugin |
Enhances anti-bot detection protection |
ProxyPlugin |
Provides proxy rotation with multiple strategies |
You can create custom plugins by implementing the PuppeteerPlugin interface:
import { PuppeteerPlugin, PluginContext } from 'puppeteer-extends';
class MyCustomPlugin implements PuppeteerPlugin {
name = 'my-custom-plugin';
async initialize(options?: any): Promise<void> {
console.log('Plugin initialized with options:', options);
}
async onBeforeBrowserLaunch(options: any, context: PluginContext): Promise<any> {
return {
...options,
args: [...options.args, '--disable-features=site-per-process']
};
}
async onPageCreated(page: Page, context: PluginContext): Promise<void> {
await page.evaluateOnNewDocument(() => {
});
}
async onBeforeNavigation(page: Page, url: string, options: any, context: PluginContext): Promise<void> {
console.log(`Navigating to: ${url}`);
}
async onError(error: Error, context: PluginContext): Promise<boolean> {
console.error('Plugin caught error:', error);
return false;
}
async cleanup(): Promise<void> {
console.log('Plugin cleanup');
}
}
await PuppeteerExtends.registerPlugin(new MyCustomPlugin(), {
customOption: 'value'
});
| Hook |
Description |
initialize |
Called when plugin is registered |
cleanup |
Called when plugin is unregistered |
onBeforeBrowserLaunch |
Before browser instance is created |
onAfterBrowserLaunch |
After browser instance is created |
onBeforeBrowserClose |
Before browser instance is closed |
onPageCreated |
When a new page is created |
onBeforePageClose |
Before a page is closed |
onBeforeNavigation |
Before navigation to a URL |
onAfterNavigation |
After navigation completes (success or failure) |
onError |
When an error occurs in any operation |
puppeteer-extends v1.7.0 introduces powerful session management capabilities, allowing you to persist cookies, localStorage, and other browser state between runs:
import { PuppeteerExtends, SessionManager } from 'puppeteer-extends';
const sessionManager = new SessionManager({
name: 'my-session',
sessionDir: './sessions',
persistCookies: true,
persistLocalStorage: true,
domains: ['example.com']
});
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
await sessionManager.applySession(page);
await PuppeteerExtends.goto(page, 'https://example.com/login');
await page.type('#username', 'myuser');
await page.type('#password', 'mypassword');
await page.click('#login-button');
await PuppeteerExtends.waitForNavigation(page);
await sessionManager.extractSession(page);
For more integrated session management, use the built-in SessionPlugin:
import { PuppeteerExtends } from 'puppeteer-extends';
import { SessionPlugin } from 'puppeteer-extends/plugins';
await PuppeteerExtends.registerPlugin(new SessionPlugin({
name: 'my-session',
persistCookies: true,
persistLocalStorage: true,
extractAfterNavigation: true,
applyBeforeNavigation: true
}));
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
await PuppeteerExtends.goto(page, 'https://example.com');
| Option |
Type |
Default |
Description |
sessionDir |
string |
"./tmp/puppeteer-extends/sessions" |
Directory to store session files |
name |
string |
"default" |
Session name (used for file name) |
persistCookies |
boolean |
true |
Whether to save/load cookies |
persistLocalStorage |
boolean |
true |
Whether to save/load localStorage |
persistSessionStorage |
boolean |
false |
Whether to save/load sessionStorage |
persistUserAgent |
boolean |
true |
Whether to save/load userAgent |
domains |
string[] |
undefined |
Domains to include when saving cookies |
extractAfterNavigation* |
boolean |
true |
Save session after each navigation |
applyBeforeNavigation* |
boolean |
true |
Apply session before each navigation |
*Only available in SessionPlugin
puppeteer-extends v1.7.0 includes a powerful event system that allows you to react to various lifecycle events:
import { PuppeteerExtends, Events, PuppeteerEvents } from 'puppeteer-extends';
Events.on(PuppeteerEvents.BROWSER_CREATED, (params) => {
console.log(`Browser created with ID: ${params.instanceId}`);
});
Events.on(PuppeteerEvents.NAVIGATION_STARTED, (params) => {
console.log(`Navigation started to: ${params.url}`);
});
Events.on(PuppeteerEvents.NAVIGATION_SUCCEEDED, (params) => {
console.log(`Successfully navigated to: ${params.url}`);
});
Events.on(PuppeteerEvents.ERROR, (params) => {
console.error(`Error in ${params.source}: ${params.error.message}`);
});
Events.once(PuppeteerEvents.PAGE_CREATED, (params) => {
console.log('First page created!');
});
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
await PuppeteerExtends.goto(page, 'https://example.com');
| Event Type |
Description |
BROWSER_CREATED |
Browser instance created |
BROWSER_CLOSED |
Browser instance closed |
PAGE_CREATED |
New page created |
PAGE_CLOSED |
Page closed |
NAVIGATION_STARTED |
Navigation to URL started |
NAVIGATION_SUCCEEDED |
Navigation completed successfully |
NAVIGATION_FAILED |
Navigation failed after retries |
ERROR |
Generic error occurred |
NAVIGATION_ERROR |
Error during navigation |
BROWSER_ERROR |
Error in browser operations |
PAGE_ERROR |
Error in page operations |
SESSION_APPLIED |
Session data applied to page |
SESSION_EXTRACTED |
Session data extracted from page |
SESSION_CLEARED |
Session data cleared |
PLUGIN_REGISTERED |
Plugin registered |
PLUGIN_UNREGISTERED |
Plugin unregistered |
You can use asynchronous event handlers and wait for them:
Events.on(PuppeteerEvents.NAVIGATION_SUCCEEDED, async (params) => {
await saveToDatabase(params.url);
});
await Events.emitAsync(PuppeteerEvents.CUSTOM_EVENT, { data: 'value' });
puppeteer-extends v1.7.0 introduces sophisticated captcha detection and solving capabilities:
import { PuppeteerExtends, CaptchaService } from 'puppeteer-extends';
import { CaptchaPlugin } from 'puppeteer-extends/plugins';
await PuppeteerExtends.registerPlugin(new CaptchaPlugin({
service: CaptchaService.TWOCAPTCHA,
apiKey: 'your-api-key',
autoDetect: true,
autoSolve: true
}));
const browser = await PuppeteerExtends.getBrowser();
const page = await browser.newPage();
await PuppeteerExtends.goto(page, 'https://example.com/with-captcha');
const plugin = PuppeteerExtends.getPlugin('captcha-plugin');
const captchaHelper = plugin.getCaptchaHelper();
const captchas = await captchaHelper.detectCaptchas(page);
if (captchas.recaptchaV2.length > 0) {
const solution = await captchaHelper.solveRecaptchaV2(page, captchas.recaptchaV2[0]);
console.log(`Solved captcha: ${solution.token}`);
}
| Type |
Description |
RECAPTCHA_V2 |
Standard and invisible reCAPTCHA v2 |
RECAPTCHA_V3 |
Score-based reCAPTCHA v3 |
HCAPTCHA |
hCaptcha challenges |
IMAGE_CAPTCHA |
Traditional text/image captchas |
FUNCAPTCHA |
Arkose Labs FunCaptcha |
TURNSTILE |
Cloudflare Turnstile |
| Option |
Type |
Default |
Description |
service |
CaptchaService |
- |
Captcha solving service to use |
apiKey |
string |
- |
API key for the service |
autoDetect |
boolean |
true |
Auto-detect captchas on page load |
autoSolve |
boolean |
true |
Auto-solve detected captchas |
timeout |
number |
120 |
Timeout for solving (seconds) |
ignoreUrls |
string[] |
[] |
URLs to ignore when detecting captchas |
| Method |
Description |
getBrowser(options?) |
Get or create browser instance |
closeBrowser(instanceId?) |
Close specific browser |
closeAllBrowsers() |
Close all browser instances |
goto(page, url, options?) |
Navigate to URL with enhanced features |
waitForNavigation(page, options?) |
Wait for navigation to complete |
waitForSelector(page, selector, timeout?) |
Wait for element to be visible |
closePage(page) |
Close page safely |
| Option |
Type |
Default |
Description |
isHeadless |
boolean |
true |
Launch browser in headless mode |
isDebug |
boolean |
false |
Enable debug logging |
customArguments |
string[] |
DEFAULT_BROWSER_ARGS |
Custom browser launch arguments |
userDataDir |
string |
./tmp/puppeteer-extends |
User data directory path |
instanceId |
string |
"default" |
Browser instance identifier |
| Option |
Type |
Default |
Description |
waitUntil |
string[] |
["load", "networkidle0"] |
Navigation completion events |
timeout |
number |
30000 |
Navigation timeout (ms) |
maxRetries |
number |
1 |
Maximum retry attempts |
retryDelay |
number |
5000 |
Delay between retries (ms) |
isDebug |
boolean |
false |
Enable debug logging |
headers |
object |
{} |
Custom request headers |
pnpm run test
pnpm run test:coverage
pnpm run test:watch
Apache-2.0