W WRNexusJS
Runtime

@wrnexus/tracking

Error/event capture, middleware, filtering, and sinks.

bun add @wrnexus/tracking@0.2.15
Error tracking for WRNexusJS apps: capture exceptions manually or via middleware and fan them out to pluggable sinks.

Part of the WRNexusJS framework — an SSR-first, Bun-native full-stack web framework.

Overview

@wrnexus/tracking is a small, server-side error-capture layer. You create a tracker with one or more sinks, then feed it errors — either manually with tracker.capture(err, context) or automatically by mounting tracker.middleware() in your request pipeline. A consoleSink is included; forwarding to Sentry, Datadog, or any other backend is just a matter of writing a tiny sink. Reach for it when you want a single, sink-agnostic place to route application errors. Sinks run best-effort — a throwing sink never breaks the request.

Installation

bun add @wrnexus/tracking
Private package — the machine must be authenticated to the wrnexus npm org
(a read token in ~/.npmrc). Requires Bun (Node is not supported).

API

createTracker(options?): Tracker

Creates a tracker. TrackerOptions:

OptionTypeDescription
sinksErrorSink[]Initial sinks to fan events out to. Defaults to [].
now() => numberClock used for event.timestamp (epoch ms). Defaults to Date.now.
beforeSend`(event: ErrorEvent) => ErrorEvent \null`Scrub/enrich an event before it reaches any sink. Return null to drop it.

The returned Tracker:

MemberSignatureDescription
capture(error: unknown, context?: Record<string, unknown>) => Promise<void>Normalizes any thrown value into an Error, builds an ErrorEvent, runs beforeSend, then dispatches to all sinks. Non-Error values are wrapped in an Error named NonError.
addSink(sink: ErrorSink) => voidRegisters an additional sink at runtime.
middleware() => MiddlewareReturns a WRNexusJS Middleware that captures any error thrown downstream, then re-throws it so the framework's error handler still produces the response.

The middleware attaches this context to captured events:

{ method: ctx.req.method, path: ctx.url.pathname, requestId: ctx.locals.requestId }

consoleSink: ErrorSink

A built-in sink that logs a compact one-line message via console.error, e.g. [error] TypeError: cannot read x {"userId":42}.

Types

interface ErrorEvent {
  error: Error;
  context: Record<string, unknown>; // request info, user id, tags…
  timestamp: number; // epoch ms
}

interface ErrorSink {
  name?: string;
  capture(event: ErrorEvent): void | Promise<void>;
}

Usage

Manual capture:

import { createTracker, consoleSink } from "@wrnexus/tracking";

const tracker = createTracker({ sinks: [consoleSink] });

try {
  await doWork();
} catch (err) {
  await tracker.capture(err, { userId: 42, op: "doWork" });
  throw err;
}

As request middleware:

import { createTracker, consoleSink } from "@wrnexus/tracking";

const tracker = createTracker({ sinks: [consoleSink] });

app.use(tracker.middleware()); // captures + re-throws downstream errors

A custom sink with beforeSend scrubbing:

import { createTracker, type ErrorSink } from "@wrnexus/tracking";

const sentrySink: ErrorSink = {
  name: "sentry",
  async capture(event) {
    await Sentry.captureException(event.error, { extra: event.context });
  },
};

const tracker = createTracker({
  sinks: [sentrySink],
  beforeSend(event) {
    delete event.context.password; // scrub secrets
    return event; // return null to drop the event entirely
  },
});

tracker.addSink(anotherSink); // add more sinks later

Requirements / Notes

  • Runs on Bun only (Node is not supported).
  • Peer package: [@wrnexus/core](../core) — the Context and Middleware types
  • used by tracker.middleware() come from there.

  • Sink dispatch is fire-and-forget-safe: all sinks run via Promise.all, and a
  • sink that throws is swallowed so it can never break the app.

Complete TypeScript API

This declaration is generated from the exact published package and lists its exported functions, classes, interfaces, and types.

import { Middleware } from '@wrnexus/core';

/**
 * @wrnexus/tracking — error tracking with pluggable sinks. Capture exceptions
 * manually or via middleware, and fan them out to any sink (console by default;
 * write a small sink to forward to Sentry/Datadog/etc.).
 *
 *   const tracker = createTracker({ sinks: [consoleSink] });
 *   app-middleware: tracker.middleware()   // captures + re-throws request errors
 *   tracker.capture(err, { userId });      // manual
 */

interface ErrorEvent {
    error: Error;
    /** Arbitrary structured context (request info, user id, tags…). */
    context: Record<string, unknown>;
    /** Epoch ms. */
    timestamp: number;
}
interface ErrorSink {
    name?: string;
    capture(event: ErrorEvent): void | Promise<void>;
}
interface Tracker {
    capture(error: unknown, context?: Record<string, unknown>): Promise<void>;
    addSink(sink: ErrorSink): void;
    /** Middleware that captures errors thrown downstream, then re-throws them. */
    middleware(): Middleware;
}
interface TrackerOptions {
    sinks?: ErrorSink[];
    now?: () => number;
    /** Scrub/enrich an event before it hits sinks (return null to drop it). */
    beforeSend?: (event: ErrorEvent) => ErrorEvent | null;
}
/** A sink that logs a compact one-line error to the console. */
declare const consoleSink: ErrorSink;
declare function createTracker(options?: TrackerOptions): Tracker;

export { type ErrorEvent, type ErrorSink, type Tracker, type TrackerOptions, consoleSink, createTracker };

Examples

Copy-ready examples taken from this package's published documentation.

Example 1

bun add @wrnexus/tracking

Example 2

{ method: ctx.req.method, path: ctx.url.pathname, requestId: ctx.locals.requestId }

Example 3

interface ErrorEvent {
  error: Error;
  context: Record<string, unknown>; // request info, user id, tags…
  timestamp: number; // epoch ms
}

interface ErrorSink {
  name?: string;
  capture(event: ErrorEvent): void | Promise<void>;
}

Example 4

import { createTracker, consoleSink } from "@wrnexus/tracking";

const tracker = createTracker({ sinks: [consoleSink] });

try {
  await doWork();
} catch (err) {
  await tracker.capture(err, { userId: 42, op: "doWork" });
  throw err;
}