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

@absolutejs/errors

v0.7.0

Published

Effect-native, Sentry-equivalent exception capture for the AbsoluteJS substrate. createErrorTracker decorates @absolutejs/audit + @absolutejs/telemetry + a durable issue store; capture() returns a CaptureOutcome with per-sink delivery state + typed (Data.

Readme

@absolutejs/errors

Effect-native, Sentry-equivalent exception capture for the AbsoluteJS substrate.

createErrorTracker is a thin decorator over @absolutejs/audit, @absolutejs/telemetry, and a durable issue store. capture(error, context?) is an Effect<CaptureOutcome, never>capturing an error can never itself fail — that does six things in one shot:

  1. Computes a stable fingerprint so the same error from different call sites collapses into one "issue."
  2. Records the exception on the active OTel span, when a tracer is supplied.
  3. Emits an audit event (kind: 'errors.captured').
  4. Upserts a grouped issue + appends the event to a durable store, and fires onIssue on a new/regressed issue.
  5. Pushes onto an in-process recent-errors LRU buffer.
  6. Bumps per-fingerprint metrics counters.

Nested Error.cause chains are appended to the stored stack and preserved as structured extra.errorCauses entries, including driver fields such as database error codes, severity, detail, and routine. The outer error continues to drive fingerprinting so causes add diagnosis without collapsing distinct operations.

Browser ingest recomputes its canonical fingerprint after the optional prepare() step. Source-mapped copies of the same failure therefore group together across releases even when their raw content-hashed chunk names differ. The ingest boundary also redacts credential-bearing fields, bearer/JWT values, breadcrumb text, and URL query/hash values after schema validation but before fingerprinting or buffering. This defense-in-depth pass is enabled by default; createIngestEndpoint({ redact: false }) is available only for hosts that replace it with an equivalent trusted-boundary policy, while a custom redact(event) function can extend the built-in policy.

Errors-as-values

Every fan-out (audit / tracer / store / onIssue) is a trust boundary. Each is wrapped so its failure becomes a typed, tagged value (Data.TaggedError) collected into the outcome — never swallowed into an anonymous counter or an onError(unknown). The outcome reports a per-sink delivery state ('ok' | 'failed' | 'skipped'), so a caller can react specifically — retry the store, page on audit loss, ignore a flaky onIssue — via an exhaustive switch (failure._tag).

import { Effect } from "effect";
import { status } from "elysia";
import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
import { tracerOrNoop } from "@absolutejs/telemetry";

const errors = createErrorTracker({
  audit: broker, // @absolutejs/audit
  tracer: tracerOrNoop(otelProvider, "app"),
  store: createMemoryIssueStore(), // or @absolutejs/errors-postgres
  project: "acme",
  release: process.env.RELEASE,
  environment: "production",
  onIssue: (r) => alert(r.issue), // only on new / regression
});

// Effect API (primary):
const outcome = await Effect.runPromise(
  errors.capture(e, {
    tenant,
    target: `order_${orderId}`,
    tags: { component: "billing" },
  }),
);

// Promise edge (for Promise-world consumers) — identical outcome:
const out = await errors.captureException(e);

if (out.failures.length > 0) {
  for (const f of out.failures) {
    switch (f._tag) {
      case "StoreFailure":
        retryLater(f.cause);
        break; // f.cause: IssueStoreError
      case "AuditSinkFailure":
        page("audit lost", f.cause);
        break;
      case "TracerFailure":
      case "OnIssueFailure":
      case "FingerprintFailure":
        /* tolerate */ break;
    }
  }
}
return status(
  "Internal Server Error",
  `Request failed. Reference: ${out.fingerprint}`,
);

API

createErrorTracker(options?: {
  audit?: { append: (event) => Promise<void> | void };
  tracer?: { startSpan?: (name) => Span };
  store?: IssueStore;          // durable "Issues" surface
  project?: string;            // default 'default'
  onIssue?: (r: IssueUpsertResult) => void | Promise<void>;  // new/regression only
  release?: string;
  environment?: string;
  fingerprint?: (error: Error, context: ErrorContext) => string | Promise<string>;
  maxRecent?: number;          // default 100
  maxFingerprints?: number;    // default 1000
  clock?: () => number;
}) => ErrorTracker
capture(error, context?)          => Effect<CaptureOutcome, never>   // primary
captureException(error, context?) => Promise<CaptureOutcome>         // Promise edge
recentErrors()                    => ReadonlyArray<CapturedError>
clearRecent()                     => void
metrics()                         => { captured, captureErrors, byFingerprint }
store?                            => IssueStore

type CaptureOutcome = {
  fingerprint: string;
  delivered: Record<'fingerprint'|'audit'|'tracer'|'store'|'onIssue', 'ok'|'failed'|'skipped'>;
  issue?: IssueUpsertResult;          // present iff the store delivered
  failures: CaptureFailure[];         // typed, tagged; empty ⇒ fully delivered
};

ErrorContext carries the standard Sentry-style triage envelope: tenant, target, traceId, spanId, replayId, tags, extra, level.

Elysia server integration

@absolutejs/errors/elysia provides one server plugin with separate settings for the two error paths:

import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
import { errorsPlugin, handledError } from "@absolutejs/errors/elysia";
import { Elysia, status } from "elysia";

const tracker = createErrorTracker({
  store: createMemoryIssueStore(), // use the Postgres adapter in production
});

const app = new Elysia()
  .use(
    errorsPlugin({
      tracker,
      server: {
        context: ({ request }) => ({
          tenant: request.headers.get("x-user-id") ?? undefined,
        }),
      },
      ingest: {
        path: "/ingest",
        // authorize, limits, buffer, symbolication, and drain settings
      },
    }),
  )
  .get("/report", async () => {
    try {
      return await buildReport();
    } catch (error) {
      return handledError(
        status(500, { message: "Unable to build the report." }),
        error,
      );
    }
  });

server captures thrown, handled, and unexplained returned 5xx responses. It is enabled by default and can be disabled with server: false. ingest mounts the browser-event endpoint and is opt-in; pass {} to use the tracker's store and defaults, or false/omit it to expose no route.

This subpath is server-only and contains the Effect-backed tracker/ingest runtime. Browser code should use @absolutejs/beacon (or @absolutejs/observability) and never import @absolutejs/errors/elysia. Forwarding-only servers can omit tracker and provide server.capture directly; this is still the same errorsPlugin.

Durable issues (the "Issues" surface)

The in-process buffer is for triage; for a persistent, queryable Issues product (first-seen / last-seen / occurrence count / state / assignee / regression detection) pass a store:

import { createErrorTracker, createMemoryIssueStore } from "@absolutejs/errors";
// or, for durability:
// import { createPostgresIssueStore } from '@absolutejs/errors-postgres';

const errors = createErrorTracker({
  project: "acme",
  release: process.env.RELEASE,
  store: createMemoryIssueStore(), // swap for the Postgres adapter
  onIssue: (r) => {
    // fires ONLY on new / regression
    if (r.isNew || r.isRegression) alert(r.issue); // → @absolutejs/dispatch
  },
});

Every capture upserts a grouped issue (keyed by (project, fingerprint)) and appends the event. onIssue fires only on the transitions worth paging someone for — a new issue or a regression (a resolved issue seen again, which flips back to unresolved). A resolved issue's severity escalates but never de-escalates; ignored issues stay muted.

Store writes are best-effort: a store outage surfaces as a typed StoreFailure in outcome.failures (wrapping the adapter's IssueStoreError), increments captureErrors, and never breaks capture.

Adapters are Effect-native — every method returns an Effect with a typed IssueStoreError channel (IssueStoreSchemaError / IssueStoreQueryError / IssueStoreSerializationError):

type IssueStore = {
  record: (event: StoredEvent) => Effect<IssueUpsertResult, IssueStoreError>;  // required
  listIssues?: (filter?) => Effect<IssueRecord[], IssueStoreError>;            // dashboard
  getIssue?:  (project, fingerprint) => Effect<Option<IssueRecord>, IssueStoreError>;
  setState?:  (project, fingerprint, 'unresolved'|'resolved'|'ignored') => Effect<void, IssueStoreError>;
  assign?:    (project, fingerprint, assignee | null) => Effect<void, IssueStoreError>;
  listEvents?:(project, fingerprint, limit?) => Effect<StoredEvent[], IssueStoreError>;
};

createMemoryIssueStore() is the zero-failure reference implementation (use for dev / tests / single-process); @absolutejs/errors-postgres is the durable adapter — both honor identical semantics.

StoredEvent carries traceId / spanId (→ @absolutejs/telemetry) and replayId (→ @absolutejs/replay) so the dashboard can cross-link an issue to its exact trace and DOM replay. issueTitle() / issueCulprit() are exported so store adapters derive the issue row without reaching into internals.

handoffErrorContext(summary) links an Issue to an @absolutejs/handoff summary through bounded tags and a privacy-safe projection. It excludes the latest evidence payload, messages, references, and external ids.

Fingerprinting

The default fingerprint is a 16-hex-char prefix of SHA-1 over

error.name | normalized(message) | first-user-stack-frame

normalized() strips digits and quoted string literals so user 'u_42' not found and user 'u_99' not found group together. Inject a custom fingerprint function for deterministic tests or domain-specific grouping rules.

Memory bounds

  • maxRecent (default 100) caps the in-process recent buffer.
  • maxFingerprints (default 1000) caps the byFingerprint counter map so an attacker who can synthesize unique errors can't blow process memory. Beyond the cap, older entries are evicted arbitrarily — the counters are approximate-by-design.

License

BSL-1.1 with a named carveout against hosted error-tracking SaaS (Sentry, Bugsnag, Rollbar, Honeybadger, Airbrake, Datadog Error Tracking). See LICENSE. Change date: 2030-05-31 → Apache 2.0.