npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usereelay/browser

v0.1.5

Published

Reelay SDK for browsers: error capture, breadcrumbs, fetch trace stitching, masked DOM session replay.

Readme

@usereelay/browser

Reelay's browser SDK — error monitoring, breadcrumbs, session replay, and frontend-to-backend trace stitching for web applications.

  • Zero runtime dependencies. The SDK ships everything it needs: transport, PII scrubbing, stack parsing, trace IDs, and DOM session replay.
  • Zero host disruption. Every listener and public API runs inside a defensive boundary. SDK failures go to the debug hook, never your app.
  • PII never leaves the page. Secrets are redacted client-side, before any payload is sent.

Install

npm install @usereelay/browser

Or use it directly without a bundler:

<script type="module">
  import * as Reelay from '/node_modules/@usereelay/browser/dist/index.js';
  Reelay.init({ endpoint: '...', token: '...' });
</script>

Quick start

Call init() once, as early as possible in your application — before your app boots, so uncaught errors during initialisation are captured too.

import * as Reelay from '@usereelay/browser';

Reelay.init({
  endpoint: 'https://api.usereelay.com',
  token: 'rlyt_live_…',
});

That's it. After init():

  • Uncaught errors and unhandled promise rejections are automatically captured.
  • Clicks, console errors, and fetch requests are recorded as breadcrumbs.
  • A masked DOM session replay is recorded and shipped on error.
  • Outgoing fetch requests to same-origin URLs get traceparent headers so backend errors link back to this session.

Verify your setup

Throw a test error to confirm everything is wired correctly:

<button onclick="throw new Error('test error')">Test Reelay</button>

The error should appear in your Reelay dashboard within seconds.


Automatic instrumentation

Once init() runs, the SDK instruments the browser automatically. You don't need to call anything else.

Global errors

window error and unhandledrejection events are captured via addEventListener (never by overwriting window.onerror). Other error monitoring tools on the page continue to work.

Symbolication requires source maps — see Source maps below.

Breadcrumbs

Three types of breadcrumbs are recorded automatically:

| Source | What's captured | Example | |---|---|---| | Clicks | Element tag + ID + up to 2 classes (no text content) | button.add-to-cart | | Console | console.error and console.warn messages (capped at 300 chars) | Error: something broke | | Fetch | HTTP method, URL, and response status | POST /api/checkout → 500 |

Breadcrumbs are stored in a ring buffer of the latest 50 entries. They are attached to every error event sent to Reelay.

Session replay

A masked DOM session replay is recorded automatically. It uses rrweb under the hood and operates in error-buffered mode: the last 30–60 seconds of session activity are kept in memory and shipped only when an error is captured. No replay is sent for pages that never error.

Privacy defaults:

  • All <input> values are masked (set to * before serialization).
  • All text content is masked when maskAllText (default true) is enabled.
  • CSS, fonts, and images are inlined so replays render independently of your live site.

Replay data is sent to {endpoint}/api/ingest/sessions alongside the error event. The dashboard reconstructs the clip using a shared session identifier, so each incident gets its own replay video.

Note: Session replay can be disabled with replay: false. Text masking can be disabled with maskAllText: false (not recommended for PII safety).

Trace stitching

When your frontend makes a fetch request to a server instrumented with @usereelay/node, the browser SDK automatically adds two headers:

  • traceparent — the W3C trace context header, so the backend error shares the same trace_id as the frontend session.
  • reelay-session-id — the browser session identifier, so the backend error links to the frontend replay.

By default, these headers are only sent to same-origin URLs. You can allow-list additional origins with tracePropagationTargets:

Reelay.init({
  endpoint: 'https://api.usereelay.com',
  token: 'rlyt_live_…',
  tracePropagationTargets: [
    'https://api.acme.com',
    /^https:\/\/.*\.internal\.acme\.com/,
  ],
});

Manual error capture

captureException(err)

Report a handled error from a try/catch block:

try {
  riskyOperation();
} catch (err) {
  Reelay.captureException(err);
}

Returns the event ID (a string like evt_...) or '' if the event was dropped.

captureMessage(message)

Report a plain string message as an error event (useful for debugging):

Reelay.captureMessage('Checkout flow completed');

A synthetic Error is created internally with name = 'Message'.


PII scrubbing

The SDK scrubs sensitive data before any payload leaves the page. Scrubbing runs on error messages, breadcrumb messages, HTTP context objects, and timeline data. It never touches stack trace frames (file paths, line numbers) or event metadata (timestamps, session IDs).

The scrubbing has two layers:

Layer 1: Built-in default patterns

The following patterns are always applied to every string value:

  • Bearer/token valuesbearer: xyz, token=abc, api_key: xxx, password: hunter2
  • JWTs — any string matching the eyJ... pattern
  • Credit card numbers — 13–19 digit sequences (with or without separators)
  • Email addresses[email protected]

These are always active. There is no way to disable them — if there's a false positive, use beforeSend to restore the value on the event object.

Layer 2: Sensitive keys

When scrubbing an object, any key matching the following list has its value replaced entirely with [redacted] — regardless of what the value looks like:

authorization, cookie, set-cookie, x-api-key, x-reelay-token, password, passwd, secret, token, access_token, refresh_token, credit_card, card_number, cvv, ssn

(Match is case-insensitive.)

Layer 3: User-configured redaction

You can redact additional object keys by name:

Reelay.init({
  endpoint: '...',
  token: '...',
  redactedFields: ['lastMeal', 'creditCard', 'internalNote'],
});

Every string value in the payload is also run through all regex patterns. You can add custom patterns with scrubPatterns:

// Stripe live keys can appear in log messages or HTTP headers:
Reelay.init({
  endpoint: '...',
  token: '...',
  scrubPatterns: [/sk_live_[A-Za-z0-9]+/g],
});

Matching substrings are replaced with [redacted].

Layer 4: beforeSend

For total control before the event is sent, use beforeSend:

Reelay.init({
  endpoint: '...',
  token: '...',
  beforeSend: (event) => {
    if (event.exception.value.includes('sensitive')) return null; // drop
    return event;
  },
});

Configuration reference

init(options)

| Option | Type | Default | Description | |---|---|---|---| | endpoint | string | — | Required. Ingestion URL, e.g. https://api.usereelay.com. | | token | string | — | Required. Node API key (rlyt_live_...). Never a ReelayID. | | sampleRate | number (0–1) | 1 | Fraction of errors to send. 1 = every error. Lower only to reduce volume on high-traffic apps. | | beforeSend | (event) => event \| null | — | Mutate or veto (return null) an event just before it is sent. Runs after PII scrubbing. | | scrubPatterns | RegExp[] | [] | Extra regex patterns applied to all string values. Matches are replaced with [redacted]. | | redactedFields | string[] | [] | Object key names whose values are replaced with [redacted] (case-sensitive, every nesting level). | | maxQueueSize | number | 30 | Maximum buffered events while offline or rate-limited. Oldest events are dropped when full. | | tracePropagationTargets | (string \| RegExp)[] | — | Origins (strings) or URL patterns (regexes) that receive W3C traceparent headers. Same-origin only when not set. | | replay | boolean | true | Enable DOM session replay. Disable if you don't need replay or want to avoid the rrweb dependency. | | maskAllText | boolean | true | Mask all text content in replay. Disabling exposes page text — only do this on pages without PII. | | debug | (msg, detail?) => void | — | Receive SDK-internal diagnostics. Never logs to console by default. |


API reference

Exported functions

import * as Reelay from '@usereelay/browser';

| Function | Signature | Description | |---|---|---| | init | (options: BrowserOptions) => BrowserClient | Initializes the singleton client and installs all instrumentation. Idempotent — subsequent calls return the existing client. | | getClient | () => BrowserClient \| undefined | Returns the active client, or undefined if init() hasn't been called. | | captureException | (err: unknown) => string | Reports an error through the active client. Returns the event ID ('' if dropped). | | captureMessage | (message: string) => string | Reports a string message as an error event. |

Exported types

import type { CoreOptions, ErrorEventPayload, Breadcrumb } from '@usereelay/browser';

| Type | Description | |---|---| | BrowserOptions | Extends CoreOptions with tracePropagationTargets, replay, and maskAllText. | | CoreOptions | Options shared by all Reelay SDKs (browser and Node). | | ErrorEventPayload | The event object delivered to the ingest API. Useful for type-safe beforeSend handlers. | | Breadcrumb | Shape of a breadcrumb object. |

BrowserClient methods

const client = Reelay.init({ ... });

| Method | Description | |---|---| | client.sessionId | The browser session identifier (sess_...), readable at any time. | | client.addBreadcrumb(crumb) | Manually add a breadcrumb. Scrubbed and bounded (latest 50 kept). | | client.capture(err, mechanism, trace?) | Capture an error with an explicit mechanism string and optional trace context. | | client.captureMessage(message, ctx?) | Capture a string as an error, optionally with context. | | client.flush() | Drain the pending event queue. Returns a promise. | | client.uninstall() | Remove all instrumentation and tear down the SDK. |


Source maps

Production frontend bundles are minified, so raw stack frames point at hashed files like assets/index-a1b2c3.js:1:54023. To see original file names, line numbers, and function names in the Reelay dashboard, you need to upload your source maps.

The SDK ships a CLI tool for this:

npx reelay-sourcemaps inject-and-upload ./dist \
  --url https://api.reelay.app \
  --token "$REELAY_TOKEN" \
  --delete

How it works:

  1. Inject debug IDs — stamps each JS chunk with a content-derived debug ID, stored in both the chunk (a tiny runtime snippet) and its source map.
  2. Upload — sends the map to Reelay's ingestion endpoint, keyed by debug ID.
  3. --delete — removes .map files after upload so they never reach your CDN.

At capture time, the SDK reads the debug ID registry (the runtime snippet injected in step 1) and stamps each stack frame with its bundle's debug ID. The backend resolves the exact map using this ID, regardless of CDN hashing or release naming.

Compatibility: The SDK also reads _sentryDebugIds registries. If you already use a Sentry bundler plugin, your existing debug IDs are picked up with no extra tooling.


Engineering guarantees

Zero host disruption

  • Every public API and event listener runs inside a try/catch boundary.
  • SDK failures are silently reported to the debug hook — never thrown into the host application.
  • fetch wrapping preserves the original function's exact arguments, return type, and this binding. If instrumentation fails, the call degrades to a transparent passthrough.
  • Click listeners are passive (they never block the UI thread) and capture-phase (they see every click before any handler's stopPropagation).

Performance

  • Session replay serialization runs in requestIdleCallback — it never competes with first paint or user interaction.
  • Breadcrumbs are an O(1) ring buffer with zero allocation on the hot path.
  • The transport queue is bounded and non-blocking: send() just pushes to an array and schedules an async drain. It never awaits network I/O.

Bounded memory

| Resource | Cap | |---|---| | Breadcrumbs | 50 entries (ring buffer) | | Transport queue | 30 entries (drop oldest) | | Replay delta buffer | 2 checkout segments (~60s), 8,000 events each | | Traced request map | 100 entries |

Data safety on navigation

When the page becomes hidden (visibilitychange), pending events are flushed using keepalive fetch, which survives page unload. Replay chunks are flushed similarly for payloads under the keepalive budget.


Development

npm install
npm run build      # tsup — ESM + CJS + type declarations
npm test           # vitest
npm run typecheck  # tsc --noEmit

See ../reelay-test-project/frontend for a runnable test page wired up with this SDK, including buttons that trigger every class of frontend error.