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

@arvoretech/bug-jar

v0.7.2

Published

Capture everything for non-technical people to help debug. One script tag, zero config.

Readme

🪲 Bug Jar

Capture everything for non-technical people to help debug. One debug link, zero config.

Bug Jar is a lightweight JavaScript library that silently records all browser activity (network requests, console logs, errors, user actions, performance metrics, and more) and lets you capture a complete, encrypted debug session from a one-time debug link.

Why?

Because asking someone to "open DevTools and check the Network tab" doesn't scale. Bug Jar gives your support team and QA a link they can send to anyone to capture everything developers need to reproduce and fix bugs.

What it captures

| Category | Details | |----------|---------| | Network | All fetch/XHR/beacon requests with headers, payload, response, status, timing | | Console | log, warn, error, info, debug — with stack traces | | Errors | Uncaught exceptions, unhandled promise rejections, resource load failures | | User Actions | Clicks, inputs, navigation, scroll, resize, visibility changes | | Environment | Browser, OS, viewport, DPR, timezone, language, connection type | | Performance | Navigation timing, Web Vitals (LCP, FID, CLS), resource count | | Storage | localStorage keys, sessionStorage keys, cookie names | | Screenshot | Visual snapshot of the current page state | | HTML Snapshot | Full DOM at the moment of report | | Feature Flags | PostHog, LaunchDarkly, Statsig, Unleash (auto-detected) | | Memory | JS heap usage (Chrome) |

Quick Start

Bug Jar runs as a debug session: staff generates a one-time link, the user opens it in the real app, and the encrypted package is uploaded to S3. See Debug Session Mode below.

<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
<script>
  BugJar.init({
    debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
  })
</script>

Configuration

init({
  // Buffer sizes
  maxNetworkEntries: 100,
  maxConsoleEntries: 200,
  maxUserActions: 150,
  maxErrors: 50,

  // What to capture
  captureScreenshot: true,
  captureHtmlSnapshot: true,
  captureCookies: true,
  captureLocalStorage: true,
  captureSessionStorage: true,
  capturePerformance: true,
  captureWebVitals: true,
  captureMemory: true,
  captureConnectivity: true,

  // Privacy — fields containing these strings are redacted
  sensitiveFields: ['password', 'token', 'secret', 'authorization', 'cookie', 'session', 'credit_card', 'cvv', 'ssn', 'cpf'],

  // Callback when report is generated
  onCapture: (report) => console.log(report),

  // Debug session
  debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
  // debugToken is read automatically from the ?debug=<token> query param
})

Programmatic Usage

import { BugJar } from '@arvoretech/bug-jar'

const jar = new BugJar()
jar.start()

// Later, capture a report programmatically
const report = await jar.capture('User clicked X but nothing happened')

// Send it wherever you want
await fetch('/api/bugs', {
  method: 'POST',
  body: JSON.stringify(report),
})

Debug Session Mode (encrypted upload to S3)

For non-technical users, staff can generate a one-time debug link. The user opens the link in the real app, reproduces the bug, and Bug Jar records the whole flow, encrypts the package end-to-end in the browser, and uploads it straight to a private S3 bucket via a presigned URL. No AWS credentials ever reach the browser.

<script src="https://unpkg.com/@arvoretech/bug-jar"></script>
<script>
  BugJar.init({
    debugSessionEndpoint: 'https://livros.arvore.com.br/debug-sessions',
    // debugToken is read automatically from the ?debug=<token> query param
  })
</script>

Flow:

  1. Staff (admin) calls POST /debug-sessions with the target app URL and gets back a debugLink (https://app.../?debug=<token>), the server public key, and an expiry.
  2. The user opens the link. Bug Jar detects ?debug=<token>, persists the token in a .arvore.com.br cookie, fetches the session, starts screen recording, and shows an animated session border (no button, no banner).
  3. The user just uses the app. The session survives navigation across apps (app-v2 → legacy → reader) by re-hydrating the token from the cookie on each page. Each page streams encrypted chunks to S3 in real time:
    • data (network/console/errors/actions/storage) every 5s as JSONL
    • video chunks straight from MediaRecorder as they are produced Chunks are uploaded to debug/<token>/<pageId>/<stream>/<seq>.bin via per-chunk presigned PUT URLs (POST /debug-sessions/:token/chunk-url). Page metadata goes to debug/<token>/<pageId>/meta.json (POST /debug-sessions/:token/meta).
  4. Staff reconstructs the session offline:
    BUG_JAR_API=https://livros.arvore.com.br/debug-sessions \
    BUG_JAR_API_TOKEN=<admin-jwt> \
    node scripts/decrypt-session.cjs <token> ./out
    This reads the X25519 private key from Secrets Manager (bug-jar/keypair), downloads all chunks via GET /debug-sessions/:token/download, decrypts each per-page stream, and writes <pageId>/data.jsonl, <pageId>/video.webm, <pageId>/meta.json.

Speed test

At the start of each page, Bug Jar runs a fast.com-style network speed test against the Árvore backend and stores the result in that page's meta.json:

  • latency — minimum and jitter from repeated GET /speedtest/ping
  • download (Mbps) — 8 MB pulled across 4 parallel streams (GET /speedtest/download?bytes=N)
  • upload (Mbps) — 4 MB sent to POST /speedtest/upload (server discards it)

It measures the real end-to-end throughput between the user and our infrastructure (which is exactly what we want when debugging "the app is slow"). The test runs once per page and is best-effort — failures leave the fields null without blocking the session.

Encryption

Each stream (data and video, per page) is encrypted with libsodium crypto_secretstream_xchacha20poly1305: a random symmetric key drives the stream, and that key is sealed with crypto_box_seal (X25519) to the server public key. The first chunk of every stream is [magic][version][sealedKeyLen][sealedKey][header]; subsequent chunks are the encrypted stream messages, the last carrying TAG_FINAL. Chunks leave the browser already unreadable; only the holder of the X25519 private key can open them. libsodium-wrappers is loaded dynamically, so it only ships when debug mode is actually used.

Cross-app continuity works because all apps live under *.arvore.com.br and share the session cookie. Screen recording restarts per app (the browser does not allow getDisplayMedia to persist across full page loads / origins), so the video is reconstructed per page rather than as one continuous file.

Privacy & Security

  • Sensitive fields are automatically redacted (passwords, tokens, secrets, etc.)
  • Password inputs are never captured
  • You control what gets captured via configuration
  • No data leaves the browser unless a debug session is active
  • All processing happens client-side

Browser Support

Works in all modern browsers (Chrome, Firefox, Safari, Edge). Some features (memory info, connection type) are Chrome-only but degrade gracefully.

Size

~12kb gzipped, zero dependencies.

License

MIT