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

error-replay

v2.0.0

Published

Lightweight error replay package that records user actions and generates detailed error reports

Downloads

72

Readme

Error Replay

npm version npm downloads License: MIT Bundle Size TypeScript

A lightweight JavaScript/TypeScript package that records user actions in a circular buffer and generates detailed error reports. Think of it as a flight recorder for your app - when something crashes, you get the last N actions that led to the error.

Works everywhere - browser and server, with native Slack integration to post error reports grouped as thread replies.

Features

  • Records clicks, inputs, navigation, network requests, and console errors
  • Captures React component names for precise debugging
  • Auto-sanitizes sensitive data (passwords, credit cards, SSNs)
  • Configurable circular buffer with granular tracking options
  • Slack Integration: Posts reports to Slack, automatically grouping same-user reports in thread replies to prevent channel clutter.
  • Dynamic User Identity: Supports deferred user identity loading (e.g. after login or API fetch).
  • Ships ESM + CJS, zero runtime dependencies.
  • Universal - runs in browser (CSR) and Node.js (SSR) with auto-detection.
  • Full TypeScript types and defineConfig helper included.

What gets captured in each environment?

| Capability | Browser (CSR) | Server (SSR / Node.js) | |---|---|---| | Click tracking | Yes | - | | Input tracking | Yes | - | | Navigation tracking | Yes | - | | Network tracking (fetch/XHR) | Yes | - | | Console errors & warnings | Yes | Yes | | Uncaught exceptions | Yes | Yes | | Unhandled promise rejections | Yes | Yes |


Installation

npm install error-replay

If you intend to use the Slack Integration (server-side only), install @slack/web-api as a peer dependency:

npm install @slack/web-api

Setup and Running

Follow these steps to set up and run Error Replay in your project:

Step 1: Create a configuration file

Create an error-replay.config.ts (or error-replay.config.js) file in the root of your project. Use the defineConfig helper to get full TypeScript autocomplete:

// error-replay.config.ts
import { defineConfig } from 'error-replay/config';

export default defineConfig({
    maxActions: 50,
    
    // Identity config: user and user.id are REQUIRED
    user: {
        id: 'anonymous', // Pass a placeholder initially, update at runtime
    },
    
    // Slack configuration (optional, server-side only)
    slack: {
        token: process.env.SLACK_BOT_TOKEN!, // Bot token (xoxb-...)
        channels: ['#error-reports', 'C0123456789'], // Channel names or IDs
        groupByField: 'id', // Thread reports by user ID
        enabled: true
    },

    // Optional error callback (works alongside Slack integration)
    onError: (report) => {
        console.log('Error captured:', report.reportId);
    }
});

Step 2: Initialize the package

Import the package and your configuration file at the entry point of your application (e.g., index.ts, main.tsx, or server.js):

import ErrorReplay from 'error-replay';
import config from './error-replay.config';

const replay = new ErrorReplay(config);
replay.start();

Calling replay.start() automatically detects your environment (browser or server) and installs the necessary global handlers.

Step 3: Stop or Clean up (Optional)

If your app is a Single Page Application (SPA) or you need to clean up event listeners during development or unmounting:

// Stops recording and removes all event listeners
replay.stop();

// Stops recording, removes event listeners, and empties the circular buffer
replay.cleanup();

Complete Frontend Setup Example (React)

For frontend applications, since you cannot execute Slack integration directly in the browser (to avoid exposing credentials), you configure the onError callback to proxy captured error reports to your backend API.

Here is a complete setup example:

1. Define Frontend Configuration (error-replay.config.ts)

import { defineConfig } from 'error-replay/config';

export default defineConfig({
    maxActions: 50,
    captureComponents: true,
    user: {
        id: 'anonymous', // placeholder, update dynamically later
    },
    onError: (report) => {
        // Send the report payload to your own backend server
        fetch('/api/errors', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(report),
        }).catch(err => console.error('Failed to send error report to backend:', err));
    }
});

2. Initialize in React Component (App.tsx)

import { useEffect } from 'react';
import ErrorReplay from 'error-replay';
import config from './error-replay.config';

// Initialize the single shared instance
const replay = new ErrorReplay(config);

export default function App() {
    useEffect(() => {
        // Start listening to clicks, inputs, console errors, etc.
        replay.start();

        // Load actual user info from an API and set it dynamically
        fetchUserFromSession().then(user => {
            replay.setUser({
                id: user.id,
                name: user.name,
                email: user.email
            });
        });

        // Clean up event listeners when the app unmounts
        return () => replay.cleanup();
    }, []);

    return (
        <div>
            <h1>My React Application</h1>
            <button onClick={() => { throw new Error('Uncaught frontend error!'); }}>
                Trigger Error
            </button>
        </div>
    );
}

3. Handle Reports on Backend (server.js)

On your server side, you can handle the incoming POST request and forward it to Slack:

import express from 'express';
import ErrorReplay from 'error-replay';

const app = express();
app.use(express.json());

// Initialize ErrorReplay on server side with Slack configured
const serverReplay = new ErrorReplay({
    user: { id: 'server' }, // required configuration
    slack: {
        token: process.env.SLACK_BOT_TOKEN,
        channels: ['#error-reports']
    }
});

// Endpoint that receives reports from the frontend
app.post('/api/errors', async (req, res) => {
    try {
        const report = req.body;
        
        // Forward the frontend report directly to Slack
        await serverReplay.sendToSlack(report);
        
        res.status(200).json({ success: true });
    } catch (err) {
        console.error('Slack forwarding failed:', err);
        res.status(500).json({ error: err.message });
    }
});

app.listen(3000);

Dynamic User Identity (e.g. loaded from API)

If user details are loaded asynchronously from an API after your application starts, initialize the config with a placeholder (like { id: 'anonymous' }), and then update the user identity dynamically when the data resolves:

// Update the user identity dynamically once fetched/loaded
async function onUserLoginOrLoad() {
    const apiUser = await fetchUserFromApi();
    
    replay.setUser({
        id: apiUser.id,
        name: apiUser.name,
        email: apiUser.email,
        metadata: { team: apiUser.team } // Nest arbitrary details in metadata
    });
}

Slack Integration Setup

The Slack Integration is server-side only (the SDK will throw an error if Slack is initialized in a browser environment to prevent exposing your Bot Token). For frontend apps, use the onError callback to proxy reports to your backend, and forward them to Slack from there.

Required Slack Bot Scopes

Ensure your Slack App has the following OAuth Scopes in the Slack API Dashboard:

| Scope | Type | Purpose | |---|---|---| | chat:write | Bot | Posting error reports and thread replies | | channels:read | Bot | Resolving channel names (e.g. #errors) to IDs for public channels | | groups:read | Bot | Resolving channel names for private channels | | files:write | Bot | Uploading large JSON reports as file attachments |

Note: Invite the Slack Bot to all target channels before running.


Configuration Reference

const config = {
    maxActions: 50,                    // Buffer size (default: 50)
    captureComponents: true,           // React component detection (default: true)
    trackClicks: true,                 // Click tracking (default: true)
    trackInputs: true,                 // Input tracking — boolean or object (default: true)
    trackNavigation: true,             // Route changes (default: true)
    trackNetwork: true,                // API calls (default: true)
    trackConsole: true,                // Console errors/warnings (default: true)
    sanitize: ['.private', 'secret'],  // Additional fields to sanitize (default: [])
    onError: (report) => {},           // Error callback (optional)
    
    // User Identity (REQUIRED)
    user: {
        id: string;                    // Unique identifier (REQUIRED)
        name?: string;                 // Display name (optional)
        email?: string;                // Email (optional)
        sessionId?: string;            // Session identifier (optional)
        metadata?: Record<string, unknown>; // Custom structured data (optional)
    },

    // Slack Integration (optional, server-side only)
    slack: {
        token: string;                 // Bot token (xoxb-...)
        channels?: string[];           // Channel names/IDs (default: ['#error-replay'])
        groupByField?: string;         // Field to group threads by. Supports 'id', 'name', 
                                       // 'email', 'sessionId', or 'metadata.<key>' (default: 'id')
        enabled?: boolean;             // Toggle Slack on/off (default: true)
    }
};

Granular Input Tracking

trackInputs: {
    text: true,      // Text inputs and textareas
    checkbox: true,  // Checkboxes and radio buttons
    select: true     // Select dropdowns
}

Privacy & Sanitization

The following fields are auto-sanitized (replaced with [SANITIZED]):

  • Password inputs (<input type="password">)
  • Phone inputs (<input type="tel">)
  • Fields with autocomplete: cc-number, cc-csc, new-password, etc.
  • Fields with name/id containing: password, secret, token, api-key, credit-card, cvv, ssn, pin
  • Values matching credit card or SSN patterns

API Reference

| Method | Returns | Description | |---|---|---| | start() | void | Begin recording user actions. | | stop() | void | Stop recording and remove all event listeners. | | setUser(user) | void | Dynamically update the user identity (requires user.id). | | capture(error) | ErrorReport | Manually capture an error and return a full report. | | sendToSlack(report)| Promise<void> | Manually send a report to Slack. Resolves on delivery confirmation. | | getActions() | UserAction[] | Get all currently buffered actions. | | clear() | void | Clear the action buffer. | | cleanup() | void | Stop recording and clear the buffer. | | isActive() | boolean | Check if recording is currently running. | | actionCount() | number | Get the number of buffered actions. |


License

MIT — see LICENSE for details.