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

rrweb-cli

v1.0.2

Published

Agent-friendly CLI for parsing, listing, and diffing rrweb session recordings.

Downloads

15

Readme

rrweb-cli

Agent-friendly CLI for parsing, listing, and diffing rrweb session recordings (json files). Useful for tracing what a user did and how the page changed in response, and pairs naturally with har-cli for end-to-end webpage analysis — rrweb-cli covers DOM and user interactions, har-cli covers the network side.

Install

Requires Node.js ≥ 18.

# one-shot via npx (no install)
npx rrweb-cli -f recording.json

# or install globally and use the `rrweb-cli` binary
npm install -g rrweb-cli

Example

A typical session: hand a recording to your coding agent and ask what the user did. The agent drives rrweb-cli itself — list for an overview, then detail / diff to zoom in.

You: Analyze the user flow in demo.json.

Agent: The user searched hello~ on Google and browsed the results. Full path (~29s, 62 events):

  1. 0.0s — Loaded https://www.google.com/
  2. 1.8s — Clicked the search box (dropdown opened)
  3. 5.7–8.7s — Typed h → he → hel → hell → hello → hello~
  4. 16.6s — Submitted; results page loaded
  5. 22.3s — Re-clicked the search box, then dismissed the dropdown
  6. 24.5–26.1s — Scrolled through results (Wikipedia, Merriam-Webster…)

Getting a recording

The simplest way to capture a session: install the official rrweb Chrome extension, record a session, then extract its events from IndexedDB. The extension stores each session under the events database; on the session detail page you can download the json by pasting this into DevTools console:

(async () => {
  const id = decodeURIComponent(location.hash.split('/session/')[1] || '');
  if (!id) throw new Error('no session id in url');

  const db = await new Promise((res, rej) => {
    const r = indexedDB.open('events');
    r.onsuccess = () => res(r.result);
    r.onerror = () => rej(r.error);
  });
  const rec = await new Promise((res, rej) => {
    const r = db.transaction('events').objectStore('events').get(id);
    r.onsuccess = () => res(r.result);
    r.onerror = () => rej(r.error);
  });
  if (!rec) throw new Error(`no record for key ${id}`);

  const blob = new Blob([JSON.stringify(rec.events)], { type: 'application/json' });
  const a = Object.assign(document.createElement('a'), {
    href: URL.createObjectURL(blob),
    download: `${id}.json`,
  });
  a.click();
  URL.revokeObjectURL(a.href);
})();

You'll get <session-id>.json in your Downloads folder — feed it to rrweb-cli via -f.

Any rrweb json file works (an array of events or { events: [...] }); the extension is just one convenient source.

Usage

The CLI is designed to be driven by a coding agent. Point your agent at a recording file and it can list events, inspect any one in detail, or diff the DOM around a specific moment.

# default: list events that have a readPretty diff or are a key gesture
# (Click / DblClick / ContextMenu); other no-diff events (MouseDown,
# MouseUp, Focus, Blur, Scroll, MouseMove, …) are filtered out, and
# adjacent same-name runs are merged.
rrweb-cli -f recording.json

# include everything except MouseMove (Mouse/Focus/Blur/Scroll all show up)
rrweb-cli -f recording.json list --all

# also include MouseMove
rrweb-cli -f recording.json list --all --mousemove

# filter (an explicit --id disables merging — exact rows back)
rrweb-cli -f recording.json list -e Input,Click
rrweb-cli -f recording.json list --time 1.0-5.0
rrweb-cli -f recording.json list --id 9,12
rrweb-cli -f recording.json list --id 9-16

# pagination (default: page 1, 50 rows/page)
rrweb-cli -f recording.json list -p 2 --page-size 100

# inspect a specific event
rrweb-cli -f recording.json detail 9             # readPretty AFTER the event
rrweb-cli -f recording.json detail 9 --before    # readPretty BEFORE the event
rrweb-cli -f recording.json detail 9 --html      # innerHTML (style/svg collapsed)
rrweb-cli -f recording.json detail 9 --raw-html  # innerHTML, style/svg verbatim
rrweb-cli -f recording.json detail 9 --raw       # raw rrweb event json

# unified diffs (default: readPretty)
rrweb-cli -f recording.json diff 9               # diff for one event
rrweb-cli -f recording.json diff 9-16            # range diff (before 9 → after 16)
rrweb-cli -f recording.json diff 9 --html        # diff the innerHTML form instead

# json output (list only — detail/diff are always text)
rrweb-cli -f recording.json list --format json

# daemon controls
rrweb-cli daemon-clear                           # stop & cleanup

What it does

Given an rrweb recording (an array of events, or { events: [...] }):

  • Reconstructs the DOM tree using the official rrweb-snapshot package on top of a jsdom Document, then applies each incremental mutation in order.
  • After every event, formats the resulting DOM into a readPretty tree — a hierarchical, innerText-leaning rendering that drops decorative wrappers but preserves semantic attributes (role, aria-*, disabled, href, form placeholder / value, …) and atomic tags (img, svg, …).
  • Computes a unified-style diff between the readPretty tree before and after each event.
  • For events that don't move the DOM but still address a node (Click, Focus, Scroll, …), produces a one-line locator pointing at the target's readPretty line (with the nearest rendered ancestor when readPretty folded the target).
  • Folds noise: consecutive same-source events within a 1s gap merge into one row (Mutation(×7)); chains of locator events on the same target merge into MouseDown+Focus+MouseUp+Click.

Because reconstructing the DOM for a long recording is expensive, large files are served by a per-file daemon keyed by <absolute path, size, mtime> hash; daemons self-destruct after 10 minutes of inactivity.

List columns

| column | meaning | | ------ | ------- | | id | 1-based event id (matches the input array index + 1). Merged rows show as a range 9-16. | | event| rrweb event-type name. For IncrementalSnapshot it shows the source (Mutation, Input, Scroll, …); MouseInteraction shows the sub-type (Click, Focus, Blur, …). Merged rows show Mutation(×7) for same-source runs and MouseDown+Focus+MouseUp+Click for same-target locator chains. | | time | seconds since the first event (0.000s is the first event). Merged rows show a range 2.320-3.626s. | | diff | for the list preview, unified-diff context lines are stripped and only +/- lines are kept. When the change is ≤3 lines it's shown in full; otherwise it's truncated to ~3 lines with a (+N more line(s); use \diff ` for the full diff)hint. Locator rows (Click/Focus/…) instead show→ line N: and are never truncated.FullSnapshotrows showuse `detail ` for the full readPretty tree` since a unified diff against the prior state is rarely useful there. |

Daemon details

  • Threshold: 1 MB. Below it the CLI parses inline; at-or-above it auto-spawns a daemon for the file. If a daemon for the file is already running, every request is routed to it regardless of size. (No flag — the choice is automatic.)
  • Cache key: sha1(absPath + size + mtimeMs) — editing the file picks up a new daemon automatically.
  • Idle timeout: 10 minutes since the last request.
  • Runtime files live under $TMPDIR/rrweb-cli/<uid>/d-<hash>.{sock,pid,log}.

Project layout

src/
  cli.ts          # commander entry
  client.ts       # IPC client + daemon spawn/connect logic
  daemon.ts       # IPC server, idle timeout, request handler
  protocol.ts     # length-prefixed JSON over a unix socket
  version.ts      # file-version hashing + tmpdir paths
  index-build.ts  # walk events; build DOM, pretty trees, diffs, locators
  dom.ts          # rrweb-snapshot rebuild + mutation/input applier
  pretty.ts       # readPretty + innerHTML rendering (with line→element owners)
  diff.ts         # LCS-based line diff
  locator.ts      # `[Target]` locator diff for non-mutating events
  filter.ts       # list / detail / diff handlers + row merging
  event-name.ts   # rrweb event-type → label
  text.ts         # text-mode rendering of responses
  types.ts        # shared types