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

@boostpack/exception

v1.1.0

Published

Type-safe exception system with RFC 9457 Problem Details support

Readme

@boostpack/exception

Type-safe exception system with RFC 9457 Problem Details support.

Problem

In complex business logic, exceptions become painful:

  • No structurethrow new Error('something went wrong') loses context. What kind of error? What data was involved? Is it a client mistake or a server bug?
  • No type safetycatch (e) gives you unknown. Handling different error cases turns into fragile instanceof chains or string matching.
  • No standard for API responses — every service invents its own error format, making integration harder.

This package solves all three: exceptions carry structured data (kind, type, detail, data), support type-safe matching via mapByException, and follow RFC 9457 Problem Details out of the box.

Installation

npm install @boostpack/exception

Quick start

Define an exception

import { Exception, ExceptionKind } from '@boostpack/exception';

class UserNotFoundException extends Exception({
  name: 'UserNotFoundException',
  kind: ExceptionKind.NotFound,
  problemType: 'user_not_found',
  title: 'User Not Found',
}) {
  constructor(userId: string) {
    super({ detail: `User ${userId} not found` });
  }
}

Throw and catch

throw new UserNotFoundException('123');

The instance carries both business logic fields and RFC 9457 fields:

exception.kind;     // 'not_found' — category for routing/handling
exception.type;     // 'user_not_found' — RFC 9457 problem type identifier
exception.title;    // 'User Not Found'
exception.detail;   // 'User 123 not found'
exception.message;  // 'User 123 not found' (same as detail, for Error compatibility)

Features

Exception categories (kind)

Every exception belongs to a category via ExceptionKind. This determines how the exception is handled: HTTP status mapping, retry logic, logging level, etc.

Available kinds: client_data_validation, validation, unauthorized, forbidden, not_found, conflict, rate_limit, internal.

RFC 9457 Problem Details (type, title, detail, instance)

Exceptions follow RFC 9457 out of the box. The type field is a unique problem identifier per exception class (e.g. user_not_found). At the HTTP layer, it is typically prefixed as a URI: /api/problems/user_not_found.

Typed data payload (data)

Exceptions can carry a typed payload for the client. Pass dataType to the factory and the data field becomes required and fully typed at compile time.

Internal metadata (meta)

Arbitrary Record<string, unknown> for internal use — logging, debugging, tracing. Not typed, not meant for clients.

Type-safe matching (mapByException)

Named exceptions can be matched exhaustively without instanceof chains. The compiler ensures all cases are handled and narrows the type in each branch.

API

Exception(options)

Factory that creates an exception class.

Exception({
  name?: string;         // Literal name for type-safe matching
  kind?: ExceptionKind;  // Default: ExceptionKind.Internal
  problemType?: string;  // Default: 'internal_error'
  title?: string;        // Default RFC 9457 title
  dataType?: class;      // Class constructor for typed data
})

Typed data

class OrderData {
  constructor(public orderId: string, public total: number) {}
}

class OrderException extends Exception({
  name: 'OrderException',
  kind: ExceptionKind.Validation,
  problemType: 'order_validation',
  dataType: OrderData,
}) {
  constructor(orderId: string, total: number) {
    super({
      detail: `Invalid order ${orderId}`,
      data: new OrderData(orderId, total), // required when dataType is set
    });
  }
}

const e = new OrderException('abc', -5);
e.data.orderId; // string — fully typed

Named exceptions and mapByException

import { mapByException, getExceptionName } from '@boostpack/exception';

class NotFoundException extends Exception({ name: 'NotFoundException', kind: ExceptionKind.NotFound }) {
  constructor() { super({ detail: 'Not found' }); }
}

class ConflictException extends Exception({ name: 'ConflictException', kind: ExceptionKind.Conflict }) {
  constructor() { super({ detail: 'Conflict' }); }
}

function handle(e: NotFoundException | ConflictException) {
  return mapByException(e, {
    [getExceptionName(NotFoundException)]: (e) => ({ status: 404, detail: e.detail }),
    [getExceptionName(ConflictException)]: (e) => ({ status: 409, detail: e.detail }),
  });
}

Built-in exceptions

import { InternalException, NotImplementedException, ForbiddenException } from '@boostpack/exception';

throw new InternalException({ detail: 'Database connection lost', cause: originalError });
throw new NotImplementedException({ detail: 'Feature not available yet' });
throw new ForbiddenException({ detail: 'Insufficient permissions' });

JSON-RPC error code

Attach a JSON-RPC error code to an exception class via the jsonRpcCode option. Useful for WebSocket/JSON-RPC APIs where each exception type maps to a numeric code.

import { Exception, ExceptionKind, BaseJsonRpcCode } from '@boostpack/exception';

class InvalidOrderException extends Exception({
  kind: ExceptionKind.Validation,
  problemType: 'invalid_order',
  jsonRpcCode: BaseJsonRpcCode.InvalidParams,
}) {
  constructor() {
    super({ detail: 'Invalid order data' });
  }
}

InvalidOrderException.jsonRpcCode; // -32602 (InvalidParams)

BaseJsonRpcCode provides the five standard JSON-RPC 2.0 codes: ParseError (-32700), InvalidRequest (-32600), MethodNotFound (-32601), InvalidParams (-32602), InternalError (-32603). Pass any number for custom codes.

Utilities

ExceptionHttpStatusMapper

Maps ExceptionKind to HTTP status codes and back.

import { ExceptionHttpStatusMapper, ExceptionKind } from '@boostpack/exception';

ExceptionHttpStatusMapper.getHttpStatus(ExceptionKind.NotFound);  // 404
ExceptionHttpStatusMapper.getHttpStatus(ExceptionKind.Conflict);  // 409
ExceptionHttpStatusMapper.getKind(401); // ExceptionKind.Unauthorized
ExceptionHttpStatusMapper.getKind(418); // ExceptionKind.Internal (fallback)

generateProblemType(className)

Generates a problemType slug from a class name.

generateProblemType('UserNotFoundException');           // 'user_not_found'
generateProblemType('ExchangeAmountValidationException'); // 'exchange_amount_validation'

formatTitleFromClassName(className)

Generates a human-readable title from a class name.

formatTitleFromClassName('UserNotFoundException');           // 'User not found'
formatTitleFromClassName('ExchangeAmountValidationException'); // 'Exchange amount validation'

License

MIT