CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-puppeteer-extra

Modular plugin framework to teach puppeteer new tricks through plugins.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

browser-lifecycle.mddocs/

Browser Lifecycle

Enhanced browser launch and connection methods with integrated plugin lifecycle support, maintaining full compatibility with standard Puppeteer while adding plugin functionality.

Capabilities

Browser Launch

Launch a new browser instance with automatic plugin lifecycle integration.

/**
 * Launch a new browser instance with plugin lifecycle support
 * All registered plugins with beforeLaunch methods are called in sequence
 * @param options - Regular Puppeteer launch options (optional)
 * @returns Promise resolving to Browser instance
 */
launch(options?: {
  /** Whether to run browser in headless mode (default: true) */
  headless?: boolean;
  /** Path to a Chromium or Chrome executable */
  executablePath?: string;
  /** Additional arguments to pass to the browser instance */
  args?: Array<string>;
  /** Whether to ignore HTTPS errors during navigation */
  ignoreHTTPSErrors?: boolean;
  /** Slows down Puppeteer operations by specified milliseconds */
  slowMo?: number;
  /** Maximum time in milliseconds to wait for browser to start */
  timeout?: number;
  /** Whether to pipe browser process stdout and stderr */
  dumpio?: boolean;
  /** Path to a user data directory */
  userDataDir?: string;
  /** Close browser process on Ctrl-C (default: true) */
  handleSIGINT?: boolean;
  /** Close browser process on SIGTERM (default: true) */
  handleSIGTERM?: boolean;
  /** Close browser process on SIGHUP (default: true) */
  handleSIGHUP?: boolean;
  /** Additional environment variables to pass to browser */
  env?: Object;
  /** Connect to browser over a pipe instead of WebSocket */
  pipe?: boolean;
  /** Specify default viewport for each page */
  defaultViewport?: {
    width: number;
    height: number;
    deviceScaleFactor?: number;
    isMobile?: boolean;
    hasTouch?: boolean;
    isLandscape?: boolean;
  } | null;
}): Promise<Puppeteer.Browser>;

Usage Examples:

const puppeteer = require('puppeteer-extra');

// Basic launch with plugins
const browser = await puppeteer.launch();

// Launch with custom options
const browser = await puppeteer.launch({
  headless: false,
  args: ['--no-sandbox', '--disable-setuid-sandbox'],
  defaultViewport: { width: 1280, height: 720 }
});

// Plugins can modify these options before launch
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({ headless: true }); // Stealth plugin adds its arguments

Browser Connection

Connect to an existing Chromium instance with plugin lifecycle support.

/**
 * Connect to existing Chromium instance with plugin lifecycle support
 * All registered plugins with beforeConnect methods are called in sequence
 * @param options - Connection configuration options
 * @returns Promise resolving to Browser instance
 */
connect(options?: {
  /** WebSocket endpoint to connect to */
  browserWSEndpoint: string;
  /** Whether to ignore HTTPS errors during navigation (default: false) */
  ignoreHTTPSErrors?: boolean;
  /** Specify default viewport for each page */
  defaultViewport?: {
    width: number;
    height: number;
    deviceScaleFactor?: number;
    isMobile?: boolean;
    hasTouch?: boolean;
    isLandscape?: boolean;
  } | null;
  /** Slows down Puppeteer operations by specified milliseconds */
  slowMo?: number;
}): Promise<Puppeteer.Browser>;

Usage Examples:

// Connect to existing browser instance
const browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://localhost:9222/devtools/browser/...'
});

// Connect with custom options
const browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://localhost:9222/devtools/browser/...',
  ignoreHTTPSErrors: true,
  defaultViewport: null // Use browser's default viewport
});

Plugin Lifecycle Integration

The framework automatically manages plugin lifecycle during browser operations.

/**
 * Plugin lifecycle methods called during browser operations
 */
interface BrowserLifecycle {
  /** Called before launch with options that can be modified */
  beforeLaunch?(options: Object): Object | Promise<Object>;
  
  /** Called before connect with options that can be modified */
  beforeConnect?(options: Object): Object | Promise<Object>;
  
  /** Called after browser creation to bind event listeners */
  _bindBrowserEvents?(browser: Puppeteer.Browser, opts: {
    context: 'launch' | 'connect';
    options: Object;
    defaultArgs?: Array<string>;
  }): void | Promise<void>;
}

Plugin Integration Flow:

  1. Option Modification: Plugins with beforeLaunch/beforeConnect modify options
  2. Requirement Checking: Framework validates plugin requirements against options
  3. Browser Creation: Standard Puppeteer launch/connect with modified options
  4. Page Patching: Framework patches page creation methods for plugin compatibility
  5. Event Binding: Plugins with _bindBrowserEvents set up event listeners

Usage Examples:

// Example plugin that modifies launch options
class CustomArgsPlugin extends PuppeteerExtraPlugin {
  constructor(customArgs = []) {
    super();
    this.name = 'custom-args';
    this.customArgs = customArgs;
  }
  
  async beforeLaunch(options) {
    options.args = options.args || [];
    options.args.push(...this.customArgs);
    return options;
  }
}

// Plugin automatically integrates with launch
puppeteer.use(new CustomArgsPlugin(['--disable-dev-shm-usage']));
const browser = await puppeteer.launch(); // Custom args are automatically added

Page Creation Patching

The framework includes built-in workarounds for Puppeteer timing issues with page creation.

/**
 * Internal page creation method patching
 * Fixes timing issues where plugins can't modify page before first request
 * Automatically navigates to about:blank after page creation
 */
_patchPageCreationMethods(browser: Puppeteer.Browser): void;

Automatic Fixes:

  • Timing Issue: Ensures plugins can modify pages before the first navigation
  • Event Race Conditions: Prevents missed targetcreated events
  • Plugin Initialization: Allows plugins to set up page-level modifications

Usage Examples:

// This patching happens automatically - no user code needed
const browser = await puppeteer.launch();
const page = await browser.newPage(); // Automatically patched for plugin compatibility

// Pages created through browser.newPage() work correctly with plugins
// Pages created implicitly (e.g., window.open) may still have timing issues

Requirement Validation

The framework validates plugin requirements against launch/connect options.

/**
 * Check plugin requirements against current options
 * Warns user when plugins won't work as expected
 */
checkPluginRequirements(opts: {
  context: 'launch' | 'connect';
  options: Object;
  defaultArgs?: Array<string>;
}): void;

Validation Rules:

  • Headful Requirement: Warns if plugin needs headful mode but headless: true
  • Launch Requirement: Warns if plugin doesn't support connect() method
  • Custom Requirements: Plugins can define additional requirement checks

Usage Examples:

// Plugin with headful requirement
class HeadfulPlugin extends PuppeteerExtraPlugin {
  constructor() {
    super();
    this.name = 'headful-only';
    this.requirements = new Set(['headful']);
  }
}

puppeteer.use(new HeadfulPlugin());
const browser = await puppeteer.launch({ headless: true }); 
// Warning: Plugin 'headful-only' is not supported in headless mode.

Internal Framework Methods

These methods are used internally by the framework for plugin management and lifecycle coordination.

/**
 * Call plugins sequentially with the same values
 * Plugins that expose the supplied property will be called
 * @param prop - The plugin property/method to call
 * @param values - Any number of values to pass to plugins
 */
callPlugins(prop: string, ...values: any[]): Promise<void>;

/**
 * Call plugins sequentially and pass on a value (waterfall style)
 * Plugins can modify the value or return an updated one
 * @param prop - The plugin property/method to call  
 * @param value - Initial value to pass through plugins
 * @returns Updated value after passing through all plugins
 */
callPluginsWithValue(prop: string, value: any): Promise<any>;

/**
 * Get all plugins that feature a given property/method
 * @param prop - Property name to search for
 * @returns Array of plugins that have the specified property
 */
getPluginsByProp(prop: string): Array<PuppeteerExtraPlugin>;

/**
 * Get the names of all registered plugins
 * @returns Array of plugin names
 */
get pluginNames(): Array<string>;

Plugin Call Flow Examples:

// Internal usage during launch
await this.callPluginsWithValue('beforeLaunch', options);
await this.callPlugins('_bindBrowserEvents', browser, opts);

// How plugins modify options in sequence
let modifiedOptions = options;
for (const plugin of this.getPluginsByProp('beforeLaunch')) {
  const newValue = await plugin.beforeLaunch(modifiedOptions);
  if (newValue) {
    modifiedOptions = newValue;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-puppeteer-extra

docs

browser-lifecycle.md

index.md

plugin-management.md

tile.json