@wrnexus/tracking
Error/event capture, middleware, filtering, and sinks.
bun add @wrnexus/tracking@0.2.15Error 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:
| Option | Type | Description | |
|---|---|---|---|
sinks | ErrorSink[] | Initial sinks to fan events out to. Defaults to []. | |
now | () => number | Clock 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:
| Member | Signature | Description |
|---|---|---|
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) => void | Registers an additional sink at runtime. |
middleware | () => Middleware | Returns 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) — theContextandMiddlewaretypes - Sink dispatch is fire-and-forget-safe: all sinks run via
Promise.all, and a
used by tracker.middleware() come from there.
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/trackingExample 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;
}