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

plugin-management.mddocs/

Plugin Management

Core functionality for registering and managing plugins that extend Puppeteer's capabilities through the plugin framework.

Capabilities

Plugin Registration

Register plugins with the framework to extend Puppeteer functionality.

/**
 * Register a plugin with the framework
 * @param plugin - Plugin instance extending PuppeteerExtraPlugin base class
 * @returns The PuppeteerExtra instance for method chaining
 */
use(plugin): this;

Usage Examples:

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

// Register stealth plugin with default settings
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());

// Register user agent plugin with custom options
const UserAgentPlugin = require('puppeteer-extra-plugin-anonymize-ua');
puppeteer.use(UserAgentPlugin({ makeWindows: true }));

// Chain multiple plugin registrations
puppeteer
  .use(StealthPlugin())
  .use(UserAgentPlugin())
  .use(require('puppeteer-extra-plugin-block-resources')());

Plugin Introspection

Access registered plugins and their data.

/**
 * Get all registered plugins
 * @returns Array of registered plugin instances
 */
get plugins(): Array<PuppeteerExtraPlugin>;

/**
 * Collect exposed data from all registered plugins
 * @param name - Optional filter by plugin name property
 * @returns Array of plugin data objects, flattened from all plugins
 */
getPluginData(name?: string): Array<Object>;

Usage Examples:

// Get all registered plugins
const allPlugins = puppeteer.plugins;
console.log(`Registered ${allPlugins.length} plugins`);

// Get plugin data (used by plugins that need data from other plugins)
const allData = puppeteer.getPluginData();
const stealthData = puppeteer.getPluginData('stealth');

Plugin Requirements and Dependencies

Plugins can declare requirements and dependencies that are automatically managed.

/**
 * Base plugin class with requirement and dependency management
 */
class PuppeteerExtraPlugin {
  /** Unique plugin name identifier */
  name: string;
  
  /** Set of plugin requirements (e.g., 'headful', 'launch', 'runLast') */
  requirements: Set<string>;
  
  /** Set of plugin dependencies (auto-resolved) */
  dependencies: Set<string>;
  
  /** Plugin-specific data for sharing with other plugins */
  data: any;
  
  /** Whether this is a valid PuppeteerExtraPlugin */
  _isPuppeteerExtraPlugin: boolean;
}

Plugin Requirements:

  • "headful" - Plugin requires headful mode (not compatible with headless)
  • "launch" - Plugin only works with launch(), not connect()
  • "runLast" - Plugin should be executed after other plugins
  • "dataFromPlugins" - Plugin needs access to data from other plugins

Usage Examples:

// Example plugin with requirements
class CustomPlugin extends PuppeteerExtraPlugin {
  constructor(opts = {}) {
    super(opts);
    this.name = 'custom-plugin';
    this.requirements = new Set(['headful', 'launch']);
    this.dependencies = new Set(['stealth']); // Auto-loads stealth plugin
  }
}

// Framework automatically handles requirements checking
puppeteer.use(new CustomPlugin());
const browser = await puppeteer.launch({ headless: true }); // Warns about headful requirement

Automatic Dependency Resolution

The framework automatically installs and registers missing plugin dependencies.

/**
 * Automatically resolve and install missing plugin dependencies
 * Dependencies are resolved recursively and installed on-demand
 */
resolvePluginDependencies(): void;

Usage Examples:

// Plugin declares dependency on 'stealth' plugin
class MyPlugin extends PuppeteerExtraPlugin {
  constructor() {
    super();
    this.name = 'my-plugin';
    this.dependencies = new Set(['stealth']); // Will auto-install puppeteer-extra-plugin-stealth
  }
}

// Framework automatically installs and registers the stealth plugin
puppeteer.use(new MyPlugin());
// No need to manually install or require the stealth plugin

Plugin Ordering

Plugins can specify execution order requirements for proper functionality.

/**
 * Order plugins based on requirements like 'runLast'
 * Ensures plugins with dependencies run in correct sequence
 */
orderPlugins(): void;

Usage Examples:

// Plugin that needs to run after others
class CleanupPlugin extends PuppeteerExtraPlugin {
  constructor() {
    super();
    this.name = 'cleanup';
    this.requirements = new Set(['runLast']); // Runs after all other plugins
  }
}

Plugin Lifecycle Hooks

Plugins can implement lifecycle methods that are called at specific points during browser creation and operation.

/**
 * Plugin lifecycle methods (implemented by plugins)
 */
interface PluginLifecycle {
  /** Called before browser launch with options that can be modified */
  beforeLaunch?(options: Object): Object | Promise<Object>;
  
  /** Called after browser launch with browser instance */
  afterLaunch?(browser: Puppeteer.Browser, opts: { options: Object }): void | Promise<void>;
  
  /** Called before browser connect with options that can be modified */
  beforeConnect?(options: Object): Object | Promise<Object>;
  
  /** Called after browser connect with browser instance */
  afterConnect?(browser: Puppeteer.Browser, opts: { options: Object }): void | Promise<void>;
  
  /** Called when browser is available (both launch and connect) */
  onBrowser?(browser: Puppeteer.Browser, opts: Object): void | Promise<void>;
  
  /** Called when a target is created (pages, service workers, etc.) */
  onTargetCreated?(target: Puppeteer.Target): void | Promise<void>;
  
  /** Called when a page target is created (convenience method) */
  onPageCreated?(page: Puppeteer.Page): void | Promise<void>;
  
  /** Called when a target URL changes */
  onTargetChanged?(target: Puppeteer.Target): void | Promise<void>;
  
  /** Called when a target is destroyed */
  onTargetDestroyed?(target: Puppeteer.Target): void | Promise<void>;
  
  /** Called when browser disconnects */
  onDisconnected?(): void | Promise<void>;
  
  /** Called when browser process closes (launch only) */
  onClose?(): void | Promise<void>;
  
  /** Called after plugin registration */
  onPluginRegistered?(): void | Promise<void>;
  
  /** Internal method for binding browser events */
  _bindBrowserEvents?(browser: Puppeteer.Browser, opts: Object): void | Promise<void>;
}

/**
 * Plugin base class properties and methods
 */
interface PuppeteerExtraPluginBase {
  /** Plugin name (required override) */
  get name(): string;
  
  /** Plugin default options */
  get defaults(): Object;
  
  /** Plugin requirements set */
  get requirements(): Set<string>;
  
  /** Plugin dependencies set */
  get dependencies(): Set<string>;
  
  /** Plugin data array for sharing */
  get data(): Array<{ name: string; value: any }>;
  
  /** Access to merged options */
  get opts(): Object;
  
  /** Debug logger function */
  get debug(): Function;
  
  /** Get data from other plugins (requires 'dataFromPlugins' requirement) */
  getDataFromPlugins?(name?: string): Array<{ name: string; value: any }>;
}

Usage Examples:

// Complete plugin example with multiple lifecycle methods
class ExamplePlugin extends PuppeteerExtraPlugin {
  constructor(opts = {}) {
    super(opts);
    this.name = 'example';
    this.requirements = new Set(['dataFromPlugins']);
  }
  
  get defaults() {
    return {
      enableLogging: true,
      customArgs: []
    };
  }
  
  // Modify launch options before browser starts
  async beforeLaunch(options) {
    options.args = options.args || [];
    options.args.push(...this.opts.customArgs);
    if (this.opts.enableLogging) {
      options.args.push('--enable-logging');
    }
    return options;
  }
  
  // Called after browser is launched
  async afterLaunch(browser, opts) {
    this.debug('Browser launched with options:', opts.options);
  }
  
  // Handle browser connection
  async beforeConnect(options) {
    this.debug('Connecting to browser:', options.browserWSEndpoint);
    return options;
  }
  
  // Called for both launch and connect
  async onBrowser(browser, opts) {
    this.debug('Browser available, context:', opts.context);
  }
  
  // Handle page creation
  async onPageCreated(page) {
    if (this.opts.enableLogging) {
      this.debug('New page created:', await page.url());
    }
    
    // Example: Set custom user agent
    const ua = await page.browser().userAgent();
    await page.setUserAgent(ua.replace('HeadlessChrome/', 'Chrome/'));
  }
  
  // Handle target events
  async onTargetCreated(target) {
    this.debug('Target created:', target.type(), target.url());
  }
  
  async onTargetDestroyed(target) {
    this.debug('Target destroyed:', target.type(), target.url());
  }
  
  // Handle disconnection
  async onDisconnected() {
    this.debug('Browser disconnected');
  }
  
  // Plugin registration callback
  async onPluginRegistered() {
    this.debug('Plugin registered successfully');
    
    // Access data from other plugins if needed
    if (this.getDataFromPlugins) {
      const pluginData = this.getDataFromPlugins('stealth');
      this.debug('Stealth plugin data:', pluginData);
    }
  }
}

// Usage
const plugin = new ExamplePlugin({
  enableLogging: true,
  customArgs: ['--disable-blink-features=AutomationControlled']
});

puppeteer.use(plugin);

Install with Tessl CLI

npx tessl i tessl/npm-puppeteer-extra

docs

browser-lifecycle.md

index.md

plugin-management.md

tile.json