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

errs

v1.0.0

Published

Simple error creation, boundaries, and formatting utilities

Readme

errs

Simple error creation, boundaries, and formatting utilities for modern Node.js applications.

Features

  • Simple Error Creation - Create errors with custom properties in one line
  • Error Boundaries - Collect errors without immediate throwing
  • Type Guards - Runtime type checking for errors with TypeScript support
  • Result Pattern - Rust-style tryCatch for exception-free error handling
  • Multi-Format Output - Format errors for terminal, JSON, or HTML
  • Native Cause Chains - Built-in support for ES2022 Error.cause
  • Error Type Registration - Register and reuse custom error types
  • Zero Dependencies - No runtime dependencies

Installation

npm install errs

Quick Start

import errs from 'errs'

// Create errors with custom properties
const err = errs.create({
  message: 'User not found',
  status: 404,
  code: 'USER_NOT_FOUND'
})

// Collect multiple errors without throwing
const boundary = errs.boundary()
boundary.add(new Error('Validation failed'))
boundary.add(new Error('Invalid email'))
if (boundary.hasErrors()) {
  boundary.throwIfErrors()
}

// Format errors for different outputs
console.log(errs.format(err, { format: 'terminal' }))

API Reference

Creating Errors

errs.create(opts)

Creates a new error with custom properties.

// From a string
const err = errs.create('Something went wrong')

// From an object
const err = errs.create({
  message: 'Database connection failed',
  status: 500,
  code: 'DB_ERROR'
})

// From an array (joined with spaces)
const err = errs.create(['Invalid', 'request', 'parameters'])

// From a function
const err = errs.create(() => ({
  message: 'Dynamic error',
  timestamp: Date.now()
}))

errs.create(type, opts)

Creates an error of a registered type.

class ValidationError extends Error {
  name = 'ValidationError'
}

errs.register('validation', ValidationError)

const err = errs.create('validation', {
  message: 'Invalid input',
  field: 'email'
})

Merging Errors

errs.merge(error, opts)

Merges an existing error with new properties and sets native cause chain.

try {
  await fetchData()
} catch (original) {
  throw errs.merge(original, {
    message: 'Failed to load user data',
    userId: 123
  })
  // merged.cause === original
}

Error Boundaries

errs.boundary(options)

Creates an ErrorBoundary for collecting errors without immediate throwing.

const boundary = errs.boundary({
  maxErrors: 100,      // Maximum errors to collect (default: 1000)
  captureStack: true   // Capture creation point (default: true)
})

// Collect errors
try {
  validateEmail(data.email)
} catch (e) {
  boundary.add(e)
}

try {
  validateAge(data.age)
} catch (e) {
  boundary.add(e)
}

// Check and throw if errors exist
if (boundary.hasErrors()) {
  console.log(`Collected ${boundary.count} errors`)
  boundary.throwIfErrors('Validation failed')
}

// Or get as AggregateError
const aggregate = boundary.toAggregateError()

// Clear and reuse
boundary.clear()

ErrorBoundary Methods

  • add(error) - Add an error to the boundary
  • hasErrors() - Check if any errors collected
  • count - Get number of errors
  • errors - Get copy of all errors
  • toAggregateError(message) - Convert to AggregateError
  • throwIfErrors(message) - Throw if errors exist
  • clear() - Remove all errors
  • trap(fn) - Execute sync function, trap errors to boundary
  • trapAsync(fn) - Execute async function, trap errors to boundary

boundary.trap(fn) / boundary.trapAsync(fn)

Execute functions and automatically collect errors without throwing.

const boundary = errs.boundary()

// Sync operations
const result = boundary.trap(() => JSON.parse(userInput))
if (result.ok) {
  console.log('Parsed:', result.value)
} else {
  console.log('Parse failed, error collected')
}

// Async operations
const fetched = await boundary.trapAsync(() => fetch(url))
if (fetched.ok) {
  const data = await fetched.value.json()
}

// Process multiple and handle all errors at once
items.forEach(item => boundary.trap(() => processItem(item)))
boundary.throwIfErrors('Batch processing failed')

Type Guards

errs.isErrorType(error, ErrorClass)

Type guard that checks if an error is an instance of a specific class.

try {
  await fetchData()
} catch (err) {
  if (errs.isErrorType(err, TypeError)) {
    // TypeScript knows err is TypeError here
    console.log('Type error occurred')
  }
}

errs.isRegisteredType(error, typeName)

Check if an error matches a registered type name.

errs.register('validation', ValidationError)

if (errs.isRegisteredType(err, 'validation')) {
  // Handle validation error
}

errs.assertErrorType(error, ErrorClass, message?)

Assert error is a specific type, throws TypeError if not.

function handleHttpError(err) {
  // Throws TypeError if err is not HttpError
  const httpErr = errs.assertErrorType(err, HttpError)
  return httpErr.status // TypeScript knows this is HttpError
}

Result Pattern

errs.tryCatch(fn)

Wraps a sync function to return a Result instead of throwing.

const result = errs.tryCatch(() => JSON.parse(input))
if (result.ok) {
  console.log(result.value)
} else {
  console.log('Parse error:', result.error.message)
}

errs.tryCatchAsync(fn)

Wraps an async function to return a Result instead of throwing.

const result = await errs.tryCatchAsync(() => fetch(url))
if (result.ok) {
  const data = await result.value.json()
} else {
  console.log('Fetch failed:', result.error.message)
}

Parallel Operations

errs.parallel(promises, options)

Run multiple promises and collect all errors.

const { results, boundary } = await errs.parallel([
  fetchUser(1),
  fetchUser(2),
  fetchUser(3)
])

// Get successful values
const users = results
  .filter(r => r.status === 'fulfilled')
  .map(r => r.value)

// Check for errors
if (boundary.hasErrors()) {
  console.error(`${boundary.count} requests failed`)
}

Error Formatting

errs.format(error, options)

Format errors for different output targets.

// Terminal output with colors
console.log(errs.format(error, { format: 'terminal', colors: true }))

// JSON for logging
logger.error(errs.format(error, { format: 'json', indent: 2 }))

// HTML for error pages
res.send(errs.format(error, { format: 'html' }))

Terminal Output:

╭─ TypeError: Cannot read property 'x' of undefined
│
│  at processUser (src/users.js:42:15)
│  at async main (src/index.js:10:3)
│
├─ Caused by: NetworkError: Connection refused
│
│  at fetch (src/api.js:23:9)
│
╰─ 2 errors in chain

Error Handling

errs.handle(error, callback, stream)

Unified error handling for callbacks and EventEmitters.

// With callback
errs.handle(err, (e) => console.error(e))

// With EventEmitter
errs.handle(err, emitter)

// With both
errs.handle(err, callback, emitter)

// Returns emitter if no callback
const emitter = errs.handle(err)
emitter.on('error', handleError)

Type Registration

errs.register(type, ErrorClass)

Register a custom error type.

class HttpError extends Error {
  name = 'HttpError'
  constructor(message, status) {
    super(message)
    this.status = status
  }
}

errs.register('http', HttpError)

// Use registered type
const err = errs.create('http', {
  message: 'Not found',
  status: 404
})

errs.unregister(type)

Unregister an error type.

errs.unregister('http')

JSON Conversion

errs.toJSON(error)

Convert error to JSON-serializable object.

const err = new Error('Test error')
err.code = 'TEST_CODE'

const json = errs.toJSON(err)
// { message: 'Test error', stack: '...', code: 'TEST_CODE' }

Named Exports

All functions are available as named exports:

import {
  create,
  boundary,
  format,
  parallel,
  isErrorType,
  isRegisteredType,
  assertErrorType,
  tryCatch,
  tryCatchAsync
} from 'errs'

const err = create('Error message')
const bound = boundary()
const result = tryCatch(() => JSON.parse(data))

Subpath Exports

Import specific modules directly:

import { ErrorBoundary } from 'errs/boundary'
import { format } from 'errs/format'

Use Cases

Form Validation

const boundary = errs.boundary()

if (!email.includes('@')) {
  boundary.add(new Error('Invalid email'))
}
if (password.length < 8) {
  boundary.add(new Error('Password too short'))
}

boundary.throwIfErrors('Validation failed')

Batch Processing

const boundary = errs.boundary()

for (const item of items) {
  try {
    await processItem(item)
  } catch (err) {
    boundary.add(err)
  }
}

if (boundary.hasErrors()) {
  console.error(`${boundary.count} items failed`)
  await logErrors(boundary.errors)
}

API Error Formatting

app.use((err, req, res, next) => {
  res.status(err.status || 500).send(
    errs.format(err, { format: 'json' })
  )
})

Requirements

  • Node.js >= 20.0.0

Migration from v0.x

See MIGRATION.md for detailed migration guide.

License

MIT

Contributors