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

@replayio-app-building/session-recorder

v0.7.14

Published

Capture browser sessions and create Replay recordings via simulation

Readme

@replayio-app-building/session-recorder

Capture browser sessions and create Replay recordings via simulation. This package provides a client-side API for capturing DOM events in the browser and a server-side API for storing session data, triggering recording creation, and managing recordings.

Installation

npm install @replayio-app-building/session-recorder

Client API

Import from @replayio-app-building/session-recorder.

Early initialization

The session recorder intercepts localStorage, indexedDB, fetch, and WebSocket via proxies. If your app reads from localStorage at module scope (e.g. to restore an auth token), those reads happen during JS module evaluation — before any function call in your entry file can run. To ensure the recorder captures these reads, import the init entry point before any application modules:

// main.ts — init MUST be the first import
import "@replayio-app-building/session-recorder/init";
import { store } from "./store"; // module-scope localStorage reads are now captured
import App from "./App";

The init import installs all capture proxies as a side effect. You still call startSession() later to get a session handle.

startSession(): SessionHandle

Begins capturing DOM events (mouse, keyboard, scroll, resize, mutations) in the current browser window. Returns a handle for retrieving the captured data. Calls initializeCapture() internally if it hasn't been called yet (e.g. via the init import).

import "@replayio-app-building/session-recorder/init";
import { startSession } from "@replayio-app-building/session-recorder";

const session = startSession();
// ... user interacts with the page ...
const data = session.getSessionData();

Returns: SessionHandle with:

  • sessionId — A UUID identifying this capture session
  • getSessionData() — Returns the captured SimulationData (array of SimulationPacket events)

markEndpoint(): void

Marks the end of the session recording. When getSessionData() is called, only events captured before the endpoint are included. This is useful when the interesting part of a session ends before the page is closed — for example, after a form submission completes or a checkout flow finishes.

import { startSession, markEndpoint, uploadSession } from "@replayio-app-building/session-recorder";

const session = startSession();
// ... user completes the checkout flow ...
markEndpoint();
// ... page may continue with confirmation animations, etc. ...
const data = session.getSessionData(); // only includes events before markEndpoint()
await uploadSession(session.sessionId, data);

If markEndpoint() is never called, getSessionData() returns all captured events as before. Calling markEndpoint() again moves the endpoint to the current position, so the last call wins.

uploadSession(sessionId, sessionData, endpoint?): Promise<{ session_id: string }>

Compresses and uploads captured session data to the server. The data is gzip-compressed before sending.

import "@replayio-app-building/session-recorder/init";
import { startSession, uploadSession } from "@replayio-app-building/session-recorder";

const session = startSession();
// ... capture interactions ...
const data = session.getSessionData();
const result = await uploadSession(session.sessionId, data);
console.log(result.session_id); // UUID of the stored session

Parameters:

  • sessionId — The session UUID (from startSession().sessionId)
  • sessionData — The captured SimulationData array
  • endpoint — Upload URL (default: "/api/upload-session")

Types

  • SessionHandle{ sessionId: string; getSessionData: () => SimulationData }
  • SimulationData — Array of SimulationPacket events
  • SimulationPacket — Individual captured DOM event

Server API

Import from @replayio-app-building/session-recorder/server. All handler factories return (req: Request) => Promise<Response> functions suitable for use in Netlify Functions v2, Cloudflare Workers, or any platform with Web API Request/Response.

Each factory accepts a sql parameter — a Neon-style tagged-template SQL function:

type SqlFunction = (
  strings: TemplateStringsArray,
  ...values: unknown[]
) => Promise<unknown[]>;

createUploadSessionHandler(options)

Handles POST requests to store captured session data. Accepts JSON or gzip-compressed binary payloads. Compresses data with gzip, uploads it to UploadThing, and stores the resulting URL in the session_recorder_data table. Requires UPLOADTHING_TOKEN environment variable.

import { createUploadSessionHandler } from "@replayio-app-building/session-recorder/server";

const handler = createUploadSessionHandler({ sql });

Options:

  • sql — SQL function
  • authenticate?(req: Request) => Promise<boolean> — optional auth check

Request body (JSON): { session_id?: string, sessionData: unknown[] } Request body (binary): gzip-compressed JSON with the same shape

Response: { session_id: string } (201)

createSessionRecordingHandler(options)

Handles POST requests to create a Replay recording from a stored session. Updates the session_recorder_data row status and dispatches the work to a background recording process.

When sessionRecorderUrl is provided, delegates recording to an external session recorder service (e.g. this app's deployed instance) via its /api/create-recording and /api/get-session endpoints.

import { createSessionRecordingHandler } from "@replayio-app-building/session-recorder/server";

const handler = createSessionRecordingHandler({
  sql,
  baseUrl: "https://my-app.netlify.app",
  sessionRecorderUrl: "https://session-recorder-si6nol.netlify.app",
  pollTimeout: 60000,
});

Options:

  • sql — SQL function
  • baseUrl — Base URL of the calling app (used for webhook URLs)
  • authenticate? — Optional auth check
  • pollTimeout? — If set, the handler polls for completion up to this many milliseconds before returning. Without this, returns immediately with status 201.
  • sessionRecorderUrl? — Base URL of an external session recorder service. When set, recording is delegated to that service.

Request body: { session_id: string, check_description?: string }

  • session_id — The session UUID to create a recording from
  • check_description — Optional description of something that should be visible in both the original rrweb session data and the resulting simulation recording (e.g. a bug the user encountered). When provided, the recording pipeline verifies that the described behavior appears in the rrweb DOM snapshots and in the simulation recording. If the behavior is present in the rrweb data but missing from the simulation, a bug report is filed automatically.

Response: The session_recorder_data row as JSON (201 if returned immediately, 200 if poll completed)

External service protocol

When sessionRecorderUrl is provided, the handler:

  1. POSTs to {sessionRecorderUrl}/api/create-recording with:
    {
      "sessionId": "<uuid>",
      "sessionBlobUrl": "{baseUrl}/api/session-data/{sessionId}",
      "webhookUrl": "{baseUrl}/api/recording-complete?sessionId={sessionId}",
      "checkDescription": "<optional check_description from the request>"
    }
  2. If pollTimeout is set, polls {sessionRecorderUrl}/api/get-session?sessionId={sessionId} every 2 seconds until the session reaches a terminal status or the timeout expires.
  3. When the recording completes, the service calls the webhookUrl with the result.

createRecordingCompleteHandler(options)

Handles POST webhook callbacks when a recording finishes. Updates the session_recorder_data and recording_tasks tables.

import { createRecordingCompleteHandler } from "@replayio-app-building/session-recorder/server";

const handler = createRecordingCompleteHandler({ sql });

Options:

  • sql — SQL function
  • authenticate? — Optional auth check

Query parameter: sessionId (required) — the session ID

Request body:

  • { status: "recorded", recordingId: "<replay-recording-id>" } — on success
  • { status: "failed", error: "message" } — on failure

If the session has a caller_webhook_url, the handler forwards the completion payload to that URL via POST (best-effort).

createRecordingsHandler(options)

Handles GET requests to list or retrieve recordings.

import { createRecordingsHandler } from "@replayio-app-building/session-recorder/server";

const handler = createRecordingsHandler({ sql });

Options:

  • sql — SQL function
  • authenticate? — Optional auth check

Routes:

  • GET /api/recordings — Lists all recordings (newest first) with data size info
  • GET /api/recordings/{sessionId} — Returns a single recording by session ID

createSessionDataHandler(options)

Handles GET requests to retrieve stored session data for a given session ID. Decompresses gzip data URLs before returning.

import { createSessionDataHandler } from "@replayio-app-building/session-recorder/server";

const handler = createSessionDataHandler({ sql });

Options:

  • sql — SQL function
  • authenticate? — Optional auth check

Route: GET /api/session-data/{sessionId}

Response: { session_id, data_url } — where data_url is a base64-encoded JSON data URL

Database Schema

The server handlers expect two tables: session_recorder_data and recording_tasks. Both are operated on through the sql function you pass in, so both must exist in the consumer's database — not just in the recording service.

session_recorder_data

| Column | Type | Description | |--------|------|-------------| | session_id | TEXT (PK) | UUID identifying the captured session | | data_url | TEXT | UploadThing URL pointing to gzip-compressed session data | | status | TEXT | 'pending', 'queued', 'processing', 'complete', or 'failed' | | recording_id | TEXT | Replay recording ID (set when complete) | | error_message | TEXT | Error details (set when failed) | | check_description | TEXT | Optional description of expected behavior to verify in the recording | | caller_webhook_url | TEXT | Webhook URL to notify when recording completes (set by external callers) | | created_at | TIMESTAMPTZ | Row creation time | | updated_at | TIMESTAMPTZ | Last update time |

recording_tasks

createSessionRecordingHandler inserts a row into this table when a recording is requested — on both the local-pipeline path and the external-service path (sessionRecorderUrl) — and createRecordingCompleteHandler updates it from the webhook. It must exist in the consumer's database; without it, createSessionRecordingHandler fails with relation "recording_tasks" does not exist.

| Column | Type | Description | |--------|------|-------------| | session_id | TEXT (PK) | UUID identifying the captured session | | data_url | TEXT | UploadThing URL pointing to gzip-compressed session data | | webhook_url | TEXT | Recording-complete webhook URL for this task | | status | TEXT | 'pending', 'assigned', or 'failed' | | created_at | TIMESTAMPTZ | Row creation time | | updated_at | TIMESTAMPTZ | Last update time |

CREATE TABLE IF NOT EXISTS recording_tasks (
  session_id  TEXT PRIMARY KEY,
  data_url    TEXT,
  webhook_url TEXT,
  status      TEXT NOT NULL DEFAULT 'pending',
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);