v0.1.x

Getting Started

StatusForge normalizes every error shape your app will ever encounter into a single predictable object. Install it, wrap your catch blocks, and stop writing client-specific error handling.

Installation

npm i @npmforge/statusforge

Basic usage

Import normalizeError and pass any caught value. You always get back the same five fields regardless of what threw.

app.ts
import { normalizeError } from "@npmforge/statusforge";

try {
  const res = await fetch("/api/data");
  if (!res.ok) throw res;
} catch (err) {
  const { code, message, status, retryable } = normalizeError(err);

  showToast(message);
  if (retryable) scheduleRetry();
}

Output schema

Every call to normalizeError returns a NormalizedError with these fields:

FieldTypeDescription
codestringMachine-readable error token for switch/match logic.
messagestringHuman-readable explanation safe to display in UI.
statusnumber | nullHTTP status code when available; null for transport or non-HTTP errors.
retryablebooleanWhether retrying the request is likely to succeed.
detailsRecord<string, unknown> | nullOriginal error value preserved for logging and observability.

Why StatusForge?

Every HTTP client throws differently. Axios wraps errors in AxiosError. Fetch never throws on non-2xx responses. ky throws HTTPError. Apollo returns an errors array. Node transport errors carry codes like ECONNRESET. Without a normalizer, every catch block needs its own branching logic — and it breaks the moment you switch libraries.

Consistent error handling

One function, one output shape. Your UI toasts, retry logic, and logging code stay the same regardless of which client made the request.

Built-in retry intelligence

The retryable flag is inferred from status codes, network error codes, and message patterns so you never have to maintain a list of retryable conditions.

Full observability

The details field preserves the original error for Sentry, Datadog, or any logging pipeline that needs the raw context.

Supported error sources

StatusForge ships six built-in adapters that handle the most common error shapes. See the Adapters reference for detection rules and examples.

AxiosFetch APIkyApollo / GraphQLNode.js http/httpsGeneric fallback

Recipes

Retry with exponential backoff

Use the retryable flag to decide whether to retry, without maintaining a list of retryable status codes per client.

retry.ts
import { normalizeError } from "@npmforge/statusforge";

async function fetchWithRetry(url: string, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      const res = await fetch(url);
      if (!res.ok) throw res;
      return await res.json();
    } catch (err) {
      const normalized = normalizeError(err);
      if (!normalized.retryable || i === attempts - 1) {
        throw normalized;
      }
      await new Promise((r) => setTimeout(r, 1000 * 2 ** i));
    }
  }
}

Structured logging

Pass the normalized error to your logger for consistent, structured error events.

logger.ts
import { normalizeError } from "@npmforge/statusforge";

function logError(err: unknown) {
  const { code, message, status, details } = normalizeError(err);

  logger.error({
    code,
    message,
    status,
    details,
    timestamp: Date.now()
  });
}