jkunz 43d3f22534
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Skipped
Default (tags) / metadata (push) Skipped
v3.0.0
2026-08-05 17:17:45 +00:00
2023-09-11 10:18:45 +02:00
2021-11-07 20:42:49 +01:00
2026-08-05 17:17:45 +00:00
2026-08-05 17:17:45 +00:00
2020-06-01 20:19:25 +00:00
2026-08-05 17:17:45 +00:00
2026-08-05 17:17:45 +00:00
2024-04-01 21:33:53 +02:00

@push.rocks/smartbrowser

A simplified Puppeteer wrapper for easy browser automation, PDF generation, screenshots, and page evaluation.

The package also provides an isolated browser-side entry at @push.rocks/smartbrowser/web. Its LiveBrowserCanvasRenderer displays SmartPuppeteer live-session frames in a canvas and maps local input back to transport-neutral browser commands without bundling Puppeteer or Node.js code into the web application.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Install

Install the package with pnpm:

pnpm add @push.rocks/smartbrowser

The Node.js entry requires Node.js 22.12 or newer and a Chromium-compatible browser. It uses SmartPuppeteer 2.1, Puppeteer 25, and SmartPDF 5. SmartPuppeteer detects common local, CI, and container environments and configures Chromium accordingly.

Usage

@push.rocks/smartbrowser provides a high-level SmartBrowser class that wraps Puppeteer for common browser automation tasks: generating PDFs, capturing screenshots, and evaluating JavaScript on web pages.

Getting Started

Import and initialize a SmartBrowser instance:

import { SmartBrowser } from '@push.rocks/smartbrowser';

const smartBrowser = new SmartBrowser();
await smartBrowser.start();

Generating a PDF from a Webpage

Generate a full-page PDF from any URL. The result includes a Buffer with the PDF contents:

const pdfResult = await smartBrowser.pdfFromPage('https://example.com');
console.log(pdfResult.buffer); // PDF file buffer
console.log(pdfResult.name);   // Generated name identifier

The PDF generation is powered by @push.rocks/smartpdf, which is lazily initialized on first use. This means the SmartPdf server is only started when you actually call pdfFromPage(), keeping resource usage minimal.

Capturing a Screenshot of a Webpage

Capture a PNG screenshot of any webpage:

const screenshotResult = await smartBrowser.screenshotFromPage('https://example.com');
console.log(screenshotResult.buffer); // Screenshot buffer (PNG)
console.log(screenshotResult.name);   // Short unique identifier
console.log(screenshotResult.id);     // Identifier with extension

Evaluating JavaScript on a Webpage

Run arbitrary JavaScript inside a page context and retrieve the result:

const pageTitle = await smartBrowser.evaluateOnPage('https://example.com', async () => {
  return document.title;
});
console.log(pageTitle); // "Example Domain"

The evaluateOnPage method supports generic return types:

const metrics = await smartBrowser.evaluateOnPage<{ width: number; height: number }>(
  'https://example.com',
  async () => {
    return {
      width: window.innerWidth,
      height: window.innerHeight,
    };
  }
);
console.log(metrics.width, metrics.height);

Pages are automatically closed after evaluation, even if an error occurs.

Accessing the Underlying Puppeteer Browser

For advanced use cases, you can access the Puppeteer browser instance directly:

const page = await smartBrowser.headlessBrowser.newPage();
await page.goto('https://example.com');
// ... custom Puppeteer operations
await page.close();

You can also import the smartpuppeteer module directly for lower-level browser management:

import { smartpuppeteer } from '@push.rocks/smartbrowser';

const browser = await smartpuppeteer.getEnvAwareBrowserInstance();

Live Browser Canvas

Import the browser-only renderer from @push.rocks/smartbrowser/web. The Node.js root entry is intentionally separate and must not be imported into a frontend bundle.

The renderer receives a caller-provided ILiveBrowserCanvasClient. An application adapter implements that interface using its authenticated transport and keeps the latest ILiveBrowserState available through getState(). SmartBrowser does not prescribe TypedSocket, WebSocket framing, base64 conversion, authentication, authorization, or session ownership.

import {
  LiveBrowserCanvasRenderer,
  type ILiveBrowserCanvasClient,
} from '@push.rocks/smartbrowser/web';

export async function mountLiveBrowser(client: ILiveBrowserCanvasClient) {
  const viewport = document.querySelector<HTMLElement>('[data-live-browser-viewport]')!;
  const canvas = viewport.querySelector<HTMLCanvasElement>('canvas')!;

  const renderer = new LiveBrowserCanvasRenderer({
    canvas,
    client,
    // Observe a stable CSS-sized element, not the canvas backing bitmap.
    resizeTarget: viewport,
    onError: (error) => console.error(error.code, error.message),
  });

  await renderer.start();
  return async () => {
    // Stop the renderer before closing its transport so queued frames can be acknowledged.
    await renderer.stop();
  };
}

A minimal host keeps CSS sizing independent from the canvas's encoded backing dimensions:

<div data-live-browser-viewport style="width: 100%; height: 600px; overflow: hidden">
  <canvas style="display: block; width: 100%; height: 100%"></canvas>
</div>

ILiveBrowserCanvasClient exposes the renderer-facing subset of the canonical SmartPuppeteer live-session API:

  • Cached state and events: getState() and onEvent()
  • Frame flow control: acknowledgeFrame()
  • Viewport synchronization: setViewport()
  • Input: dispatchMouse(), dispatchWheel(), dispatchKey(), and insertText()

The renderer keeps at most one frame decoding and one newest frame queued. It makes one acknowledgement attempt after a frame is drawn, deliberately dropped, found stale, or fails decoding. A failed or timed-out acknowledgement is reported through onError and terminally stops the renderer because frame flow control is no longer reliable. Acknowledgement capacity exhaustion also terminally stops the renderer. Input identity always comes from the frame actually displayed, so tab, generation, and viewport changes block stale clicks and keystrokes. Encoded frame.width and frame.height set the canvas backing bitmap, while pointer coordinates map through the displayed canvas rectangle into the frame's logical CSS viewport.

Pointer and wheel input are captured from the canvas. Keyboard and best-effort compositionend input are captured from focusTarget, which defaults to the canvas and receives focus on pointer down. The renderer temporarily makes an unfocusable focus target focusable and restores its prior tabindex on stop. Applications with a dedicated text or IME control can call renderer.insertText(text) explicitly.

When resizeTarget is supplied, resize updates are deduplicated, serialized, and fenced by viewport revision. The target must have stable CSS dimensions that do not depend on canvas.width or canvas.height; this prevents intrinsic canvas updates from causing resize feedback.

ILiveBrowserCanvasRendererOptions supports:

  • canvas and client: required rendering and transport-adapter dependencies.
  • focusTarget: optional keyboard and composition event target; defaults to canvas.
  • resizeTarget: optional stable CSS-sized element observed for remote viewport updates.
  • getDeviceScaleFactor: optional scale provider; defaults to window.devicePixelRatio and is useful when the application controls remote scaling explicitly.
  • operationTimeoutMs: timeout for client acknowledgements, viewport updates, and input operations; defaults to 10 seconds. A timeout terminally stops the renderer because the underlying adapter operation cannot be cancelled through this interface.
  • frameDecodeTimeoutMs: image decode timeout; defaults to operationTimeoutMs. A timeout terminally stops the renderer and closes the decoded bitmap if it completes late.
  • onError: receives typed ILiveBrowserCanvasError values without interrupting renderer cleanup.
  • onFrameRendered: called after a current frame has been drawn.

The renderer exposes start(), stop(), insertText(), syncViewport(), and the isRunning getter. syncViewport() requests a fresh measurement of the configured resizeTarget; it is a no-op when no target is configured or the renderer is stopped. An explicitly stopped renderer can be started again. After an acknowledgement failure, input-release failure, capacity exhaustion, or operation/decode timeout, create a new renderer and client instead; the failed instance rejects future start() calls because its non-cancellable underlying work may still affect the old client session. The /web entry also exports ILiveBrowserCanvasClient, ILiveBrowserCanvasRendererOptions, ILiveBrowserCanvasError, TLiveBrowserCanvasErrorCode, and the canonical SmartPuppeteer live-browser contract types.

LiveBrowserCanvasRenderer is not a security boundary. The application adapter must authenticate viewers, authorize control, restrict navigation, enforce browser-session ownership, and apply network/egress policy before forwarding commands. The renderer displays webpage viewport pixels only. It does not provide native Chrome UI, audio, extensions, file transfer, clipboard, camera, microphone, or touch emulation.

Shutting Down

Always stop the browser instance when done to free resources:

await smartBrowser.stop();

This cleanly shuts down the SmartPdf server (if it was initialized) and closes the browser.

Full Example

import { SmartBrowser } from '@push.rocks/smartbrowser';

async function main() {
  const smartBrowser = new SmartBrowser();
  await smartBrowser.start();

  // Generate a PDF
  const pdfResult = await smartBrowser.pdfFromPage('https://example.com');
  console.log('PDF size:', pdfResult.buffer.length, 'bytes');

  // Take a screenshot
  const screenshot = await smartBrowser.screenshotFromPage('https://example.com');
  console.log('Screenshot size:', screenshot.buffer.length, 'bytes');

  // Evaluate JavaScript
  const title = await smartBrowser.evaluateOnPage('https://example.com', async () => {
    return document.title;
  });
  console.log('Page title:', title);

  await smartBrowser.stop();
}

main();

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license file.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at hello@task.vc.

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

S
Description
A simplified Puppeteer wrapper for easy automation and testing tasks.
Readme
978 KiB
Languages
TypeScript 100%