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

@nreactive/core

v0.2.0

Published

Automatic server-side error capture and reporting for nreactive

Readme

@nreactive/core

Automatic server-side error capture for Node.js — the companion to the nreactive AI error-fix pipeline that catches runtime errors in production and opens pull requests that fix them.

  • Zero runtime dependencies (native fetch, node:diagnostics_channel).
  • Auto-captures uncaughtException, unhandledRejection, process warnings, and outbound HTTP failures by default.
  • Per-request context propagated via AsyncLocalStorage.
  • Built for Node ≥ 20.

Install

npm install @nreactive/core
# or: pnpm add @nreactive/core
# or: yarn add @nreactive/core
# or: bun add @nreactive/core

Using a framework? Install a sibling package instead — they depend on @nreactive/core and wire request context into your router:

AI-assisted setup

Paste the prompt below into Claude Code, Cursor, Copilot, or any agent with file-write access. It has enough information to finish the integration end-to-end — no manual wiring required.

You are adding the @nreactive/core SDK to this Node.js project. nreactive is a production error-monitoring service that opens AI-generated pull requests to fix the errors it catches.

  1. Detect the package manager from the lockfile (package-lock.json → npm, pnpm-lock.yaml → pnpm, yarn.lock → yarn, bun.lockb → bun).
  2. Install @nreactive/core with that package manager.
  3. Create a file src/observability/nreactive.ts (or .js if the project is not TypeScript) that imports init from @nreactive/core and calls it with { appId: process.env.NREACTIVE_APP_ID, environment: process.env.NODE_ENV, release: process.env.APP_VERSION }. Export nothing — the module is used for its import side effect.
  4. In the server entry file (commonly src/index.ts, src/server.ts, or the main field of package.json), add import "./observability/nreactive"; as the very first import, before any framework imports.
  5. Add NREACTIVE_APP_ID= to .env.example (create it if missing) and document the variable in the README's environment-variables section. The user will obtain their App ID from https://nreactive.com/dashboard/apps.
  6. Do not invent additional configuration — the defaults capture uncaughtException, unhandledRejection, process warnings, and outbound HTTP failures automatically. If the project looks like it uses Express or Fastify, stop and advise the user to install @nreactive/express or @nreactive/fastify instead.

Quick start

import { init, captureException } from "@nreactive/core";

init({
  appId: process.env.NREACTIVE_APP_ID!,
  environment: process.env.NODE_ENV,       // "production" | "development" | …
  release: process.env.APP_VERSION,        // e.g. "1.2.3"
});

// From this point, the following are captured automatically:
//   - uncaughtException  → severity "critical" + process.exit(1) after a 2s flush
//   - unhandledRejection → severity "critical"
//   - process warnings   → severity "warn" (Deprecation/Experimental filtered)
//   - outbound HTTP      → 4xx "error", 5xx "critical", >3s "warn", net fail "critical"

// Manual capture is always available:
captureException(new Error("payment failed"), "critical", {
  tags: { feature: "checkout" },
  user: { id: "u_42" },
});

Get your App ID from the nreactive dashboard.

Configuration reference

init({
  // ── Required ───────────────────────────────────────────────────────────
  appId: "…",                              // issued by nreactive

  // ── Identity ───────────────────────────────────────────────────────────
  endpoint: "https://nreactive.com",       // default: nreactive.com
  environment: "production",               // default: process.env.NODE_ENV ?? "production"
  release: "1.2.3",                        // app version — surfaced in metadata.browser
  serverName: os.hostname(),               // default: os.hostname()
  sendInDevelopment: false,                // default false → no-op when env !== "production"

  // ── Auto-capture toggles ───────────────────────────────────────────────
  captureUncaughtException: true,          // default true; accepts object form below
  captureUnhandledRejection: true,         // default true
  captureProcessWarning: true,             // default true (filters Deprecation/Experimental)
  captureHttpClient: true,                 // default true — fetch + http/https outbound
  captureHttpServer: false,                // default false — prefer framework adapters
  captureEventLoopLag: false,              // default false — mean>=200ms "warn", p99>=500ms "critical"
  captureConsole: false,                   // default false — wraps console.error/.warn

  // Object form of captureUncaughtException — all fields optional
  captureUncaughtException: {
    exit: true,                            // process.exit after flush (default true)
    exitCode: 1,
    flushTimeout: 2000,                    // ms; cap on how long flush blocks exit
    mimicNative: true,                     // write raw stack to stderr like Node does
  },

  // ── Filtering & shaping ────────────────────────────────────────────────
  beforeSend: (evt) => evt | null,         // sync or async; return null to drop
  ignoreErrors: [/AbortError/, "ECONNRESET", (e) => e instanceof MyOwnError],
  denyUrls: [/^node:/, "/node_modules/"],

  // ── Scrubbing ──────────────────────────────────────────────────────────
  scrubHeaders: ["authorization", "cookie", "x-api-key"],
  scrubQueryParams: ["token", "password", "secret", "api_key"],

  // ── Tuning ─────────────────────────────────────────────────────────────
  batchInterval: 5000,                     // ms — server may override via /validate
  maxQueueSize: 100,                       // events buffered; drop-oldest when full
  maxErrorsPerSession: 50,                 // per-process cap; server may override

  // ── Transport ──────────────────────────────────────────────────────────
  transportContextMode: "both",            // "metadata" (≤300 char string) | "both" (adds structured JSON)
  debug: false,                            // when true, log SDK internals to stderr
});

Public API

import {
  init,                 // set up the SDK (call once at process start)
  captureException,     // captureException(err, severity?, { tags?, extra?, user?, url?, fingerprint? })
  captureMessage,       // captureMessage(msg, severity?, opts?)
  addContext,           // addContext({ user, tags, extra, requestId, … }) — merges into active store
  withContext,          // withContext(ctx, fn) — isolated context for the duration of fn()
  addBreadcrumb,        // addBreadcrumb({ category, message, level, data })
  getContext,           // read the current context
  flush,                // await flush(timeoutMs?) — best-effort send
  close,                // await close(timeoutMs?) — flush + remove handlers
  ready,                // await ready() — resolve when init+validation complete
  getClient,            // low-level access to the Client singleton
} from "@nreactive/core";

withContext vs addContext

withContext(ctx, fn) creates a new AsyncLocalStorage frame that is scoped to the lifetime of fn(). Use this to wrap an inbound request, a background job, or a queue message:

withContext({ requestId: req.id, user: { id: req.user?.id } }, () => {
  // any captureException inside this closure (including async work) is tagged
});

addContext({...}) merges into the currently active context frame. Use this to add fields as the request progresses (e.g. after looking up the user from the session).

Capture sources

| Source | Default | Severity mapping | |---|---|---| | process.on('uncaughtException') | ON | critical, then process.exit(1) after 2s flush | | process.on('unhandledRejection') | ON | critical | | process.on('warning') | ON | warn (DeprecationWarning + ExperimentalWarning filtered) | | Outbound HTTP (fetch + http/https) | ON | 4xx error, 5xx critical, net fail critical, >3s warn | | SIGTERM / SIGINT / beforeExit | ON | flush + re-raise signal so user handlers fire | | Inbound HTTP (http.Server) | OFF | Use the framework adapters (@nreactive/express, @nreactive/fastify) for richer context. | | console.error / console.warn | OFF | error / warn | | Event-loop lag | OFF | warn (mean ≥ 200ms), critical (p99 ≥ 500ms) |

Severity auto-classification for manual captureException():

  • critical: TypeError, ReferenceError, RangeError, SyntaxError, and Node errors with code in ENOENT, ECONNREFUSED, ETIMEDOUT, EPIPE, ERR_UNHANDLED_ERROR.
  • error: default.
  • warn: pass "warn" explicitly (used by console + event-loop integrations).

Shutdown guarantees

  • All internal timers (setInterval flush, setTimeout abort guards) are .unref()d — the SDK never holds the event loop open on its own.
  • uncaughtException: the handler runs captureExceptionflush(captureUncaughtException.flushTimeout)process.exit(captureUncaughtException.exitCode). If mimicNative: true (default) the raw stack is written to stderr first so logs look like a normal Node crash.
  • SIGTERM/SIGINT: the handler flushes, unregisters itself, then re-raises the signal via process.kill(pid, sig) so any user signal handlers you've installed still fire. The SDK does not call process.exit on signals — that's your application's job.
  • close(timeout): marks the client disabled (rejects new events), tears down all integrations, flushes once, stops the timer. Idempotent.

Context transport

Every event includes:

  • metadata.browser — ≤300 char string packed with node/vX.Y host=… pid=… env=… rel=… METHOD /path req=<requestId> user=<userId>. Never fingerprinted by the backend — safe to vary per-request.
  • metadata.oslinux 6.5.0-x86_64 or equivalent.
  • metadata.timestamp — ISO timestamp.
  • context (≤4000 chars, optional) — structured JSON with request, user, tags, extra, breadcrumbs. Sent when transportContextMode: "both" (default). Forward-compatible: old backends ignore it.

The backend-side fingerprint is computed only over message + stack + source.file + source.line, so varying request context does not create duplicate issues.

Self-loop protection

Every outbound request from the SDK includes an x-nreactive-self: 1 header. The HTTP-client integration skips any request carrying that header (and any request whose origin matches the configured endpoint) so reporting errors never triggers more errors.

Privacy & compliance

  • Headers matching scrubHeaders (default: authorization, cookie, set-cookie, x-api-key, x-auth-token, proxy-authorization) are replaced with [REDACTED] before transport.
  • Query parameters matching scrubQueryParams (default: token, password, secret, api_key, apikey, access_token, refresh_token) are redacted in metadata.browser and in source.url.
  • Body scrubbing is applied to breadcrumb data and CaptureOptions.extra payloads (bounded depth of 8).
  • When environment !== "production" the SDK sends Origin: http://localhost so the backend's localhost short-circuit still activates for misconfigured local/dev workloads — no events are persisted.
  • sendInDevelopment: false (the default) disables the network path entirely when environment !== "production". Flip to true only if you intentionally want staging/dev errors on your dashboard.

Links

License

PROPRIETARY. See LICENSE.