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

@replayfyapp/browser

v0.1.16

Published

Replayfy browser SDK — session replay, product analytics, and error capture for the web.

Readme

Replayfy for Web

Session replay, product analytics, and error monitoring for the browser.

Replayfy records what your users actually experience — pixel-accurate session replays, product events and funnels, and every JavaScript error — and sends it to your Replayfy dashboard so you can see, measure, and fix what matters.

Features

  • Session replay — high-fidelity playback of real user sessions, including DOM changes, scrolling, input, and navigation.
  • Product analytics — identify users and track custom events to power funnels, segments, and retention.
  • Error monitoring — automatic capture of unhandled errors and promise rejections, plus a captureException API for handled errors, with parsed stack traces grouped into issues.
  • Console & network capture — console logs and fetch/XHR requests are recorded alongside the replay for fast debugging.
  • Web performance — Core Web Vitals (LCP, CLS, INP), long tasks, and resource timing.
  • Rage- & dead-click detection — surfaces frustrated interactions automatically.
  • Privacy-first — inputs are masked by default, with selectors and URL rules to redact anything sensitive before it leaves the browser.
  • Tiny & dependency-safe — ships as ESM/CJS for bundlers and as a single self-contained <script> bundle for no-build setups.

Install

npm (bundlers / frameworks)

npm install @replayfyapp/browser

Script tag (CDN, no build step)

Drop the self-contained bundle on any page — it exposes a global Replayfy. The recommended snippet loads it asynchronously (so it never blocks page render) and stands up a tiny stub that queues init/track/… calls until the bundle is ready, then replays them:

<!-- Async, non-blocking loader — queues calls until the SDK is ready.
     `crossorigin` lets the browser report full error stack traces instead of a
     bare "Script error." (the CDN sends the matching Access-Control-Allow-Origin). -->
<script>
  !(function () {
    var r = (window.Replayfy = window.Replayfy || []);
    ["init", "identify", "track", "captureException", "flush", "stop"].forEach(function (m) {
      r[m] = function () { r.push([m].concat([].slice.call(arguments))); };
    });
    var s = document.createElement("script");
    s.async = true;
    s.crossOrigin = "anonymous";
    s.src = "https://cdn.replayfy.app/v1/replay.global.js";
    var f = document.getElementsByTagName("script")[0];
    f.parentNode.insertBefore(s, f);
  })();
</script>

Each queued call is an array [method, ...args]; the bundle drains that queue on load (replaying init() first). Prefer this over a plain <script src> tag, which blocks rendering until the bundle downloads.

Quick start

With a bundler

import { initReplay } from "@replayfyapp/browser";

const replay = initReplay({
  apiKey: "pk_live_123",
  apiHost: "https://us.replayfy.app",
});

replay.identify("user_123");
replay.track("checkout_started");

With the script tag

<!-- Async, non-blocking loader (see Install above) -->
<script>
  !(function () {
    var r = (window.Replayfy = window.Replayfy || []);
    ["init", "identify", "track", "captureException", "flush", "stop"].forEach(function (m) {
      r[m] = function () { r.push([m].concat([].slice.call(arguments))); };
    });
    var s = document.createElement("script");
    s.async = true;
    s.crossOrigin = "anonymous";
    s.src = "https://cdn.replayfy.app/v1/replay.global.js";
    var f = document.getElementsByTagName("script")[0];
    f.parentNode.insertBefore(s, f);
  })();
  Replayfy.init({
    apiKey: "pk_live_123",
    apiHost: "https://us.replayfy.app",
  });
  Replayfy.identify("user_123");
  Replayfy.track("checkout_started");
</script>

init() is idempotent — a second call returns the live session rather than starting a second recording. After init(), the Replayfy global forwards the same API (identify / track / captureException / flush / stop), so you never have to hold onto the returned controller.

Full example

Every capture and privacy option can be set at init — only apiKey and apiHost are required:

const replay = initReplay({
  apiKey: "pk_live_123",
  apiHost: "https://us.replayfy.app",

  // capture (all default to true except captureHeaders)
  captureConsole: true, // console.* output on the timeline
  captureNetwork: true, // fetch / XHR requests
  captureErrors: true, // unhandled errors + promise rejections
  captureHeaders: false, // request/response headers (off by default)

  // privacy
  maskAllInputs: true, // mask every <input> value (default)
  maskTextSelector: ".rpf-mask", // mask the text of these elements
  blockSelector: ".rpf-block", // fully block these elements
  redactUrls: [/\/reset-password\/[^/]+/], // strip tokens from captured URLs
});

Configuration

Pass these options to initReplay(config) (npm) or Replayfy.init(config) (CDN). Only apiKey and apiHost are required.

| Option | Type | Default | Description | | --- | --- | --- | --- | | apiKey | string | — | Required. Your project's publishable API key. | | apiHost | string | — | Required. Replayfy ingest host, e.g. https://us.replayfy.app. | | projectId | string | — | Optional project identifier, useful when one key spans environments. | | distinctId | string | — | Identify the user at startup (equivalent to calling identify immediately). | | revId | string | — | Build / release id of the deployed app, so you can scope replays and funnels to one release. | | sessionId | string | auto | Override the generated session id (advanced; normally left unset). | | flushIntervalMs | number | 3000 | How often, in milliseconds, buffered events are sent. | | maxBufferSize | number | — | Maximum number of events to hold in memory between flushes. | | captureConsole | boolean | true | Record console.* output on the session timeline. | | captureNetwork | boolean | true | Record fetch/XHR requests (method, URL, status, timing). | | captureErrors | boolean | true | Capture unhandled errors and promise rejections. | | captureHeaders | boolean | false | Include request/response headers on captured network events. | | redactHeaderNames | string[] | — | Extra header names (beyond the built-in deny-list) whose values are masked before sending. | | maxBodyBytes | number | 8192 | Maximum captured request/response body size, in bytes. | | captureResourceTimings | boolean | true | Capture per-resource timing (images, scripts, CSS, fonts). | | resourceMinDurationMs | number | 0 | Skip resource-timing entries faster than this many milliseconds to trim volume. | | maskAllInputs | boolean | true | Mask the value of every <input> in the replay. | | maskTextSelector | string | — | CSS selector for text nodes to mask in the replay. | | blockSelector | string | — | CSS selector for elements to fully block (rendered as a placeholder). | | redactUrls | Array<string \| RegExp> | — | Patterns whose matches are stripped from captured URLs. | | sdk | object | — | Override SDK descriptor fields (name/version) reported with each batch. | | beforeSend | (event) => event \| null | — | Inspect or mutate each batch before it's sent; return null to drop it. | | fetchImpl | typeof fetch | window.fetch | Custom fetch implementation (advanced/testing). |

API

initReplay() returns a controller with the methods below. When using the CDN global, the same methods are available as Replayfy.identify(...), Replayfy.track(...), etc., after Replayfy.init(...).

identify(distinctId, props?)

Associate the current session with a user. Accepts a distinct id plus optional traits, or a single payload object.

replay.identify("user_123", { email: "[email protected]", plan: "pro" });

// or a single object
replay.identify({ distinctId: "user_123", name: "Ada Lovelace" });

track(name, properties?)

Record a custom product event. Event names should be stable identifiers (snake_case or camelCase, no spaces) so they're searchable in funnels.

replay.track("checkout_started");
replay.track("plan_upgraded", { from: "free", to: "pro" });

captureException(error, opts?)

Report a developer-caught exception on the session timeline. It becomes a first-class issue in the dashboard, with its name, message, and parsed stack. opts.handled defaults to true; pass false to record it as a fatal error.

try {
  risky();
} catch (e) {
  replay.captureException(e);
}

flush()

Send any buffered events immediately. Returns a Promise.

await replay.flush();

stop()

Stop all capture, end the session, and flush the final batch. Returns a Promise.

await replay.stop();

sessionId

The current session's id (string), handy for correlating with your own logs.

console.log(replay.sessionId);

Privacy & masking

Replayfy is designed to keep sensitive data in the browser. By default, every input value is masked. Tune redaction with these options:

  • maskAllInputs (default true) — masks the value of every input field in the replay.
  • maskTextSelector — a CSS selector for text nodes to mask (e.g. .pii, .account-number).
  • blockSelector — a CSS selector for elements to block entirely; they render as a placeholder in playback.
  • redactUrls — string/RegExp patterns stripped from captured URLs, so tokens and ids never leave the page.
  • captureHeaders (default false) — headers are only captured when explicitly enabled.
  • redactHeaderNames — additional header names whose values are masked, on top of the always-on built-in deny-list.
  • maxBodyBytes — caps how much of a request/response body is ever captured.

Mask a specific input or element

Every <input> is masked by default. To hide anything else — a text node, a whole widget, a specific field — add a class (or use any CSS selector) and point the mask/block selectors at it. The real values never leave the browser:

<!-- inputs are masked automatically -->
<input type="text" name="ssn" />

<!-- mask the TEXT of an element: shown as ●●●● in playback -->
<span class="rpf-mask">4111 1111 1111 1111</span>

<!-- BLOCK an element entirely: rendered as a placeholder box -->
<div class="rpf-block"><!-- third-party chat widget --></div>
initReplay({
  apiKey: "pk_live_123",
  apiHost: "https://us.replayfy.app",
  maskAllInputs: true, // default — every <input> value is masked
  maskTextSelector: ".rpf-mask", // mask the text content of matching elements
  blockSelector: ".rpf-block", // replace matching elements with a placeholder
});

For full control, beforeSend receives each batch before it leaves the browser, so you can drop or scrub anything programmatically.

Dashboard configuration

Privacy, capture, and sampling settings you configure in your Replayfy dashboard are fetched at startup and take precedence over the values you pass to init(). This lets you tighten masking, turn console or network capture on or off, adjust sampling, or pause recording for a workspace — without shipping a new build of your app. Treat the options above as your defaults; when a setting is configured in the dashboard, the dashboard wins.

Links

  • Docs: https://docs.replayfy.app/platforms/web
  • Dashboard: https://app.replayfy.app