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

causewalk

v0.1.1

Published

A minimal set of Go-inspired utilities for handling error wrapping in TypeScript

Downloads

37

Readme

causewalk

A dead-simple library for traversing native JavaScript errors.

JavaScript already has great support for error wrapping/chaining via Error.cause:

try {
  throw new Error()
} catch (cause) {
  throw new Error("Oh no!", { cause })
}

causewalk doesn't try to reinvent the wheel or provide a custom error class, error matchers or result type. There are already great libraries like neverthrow out there if you're ready to commit.

Instead, causewalk provides three tiny utility functions: as/is/all.

  • Works in any and all JavaScript runtimes.
  • Small and unopinionated.
  • Works with AggregateError and SuppressedError.

A real-world example

Imagine a payment SDK throws a CardDeclinedError. The checkout service adds useful context before letting the error bubble up:

async function checkout(order: Order) {
  try {
    await payments.charge(order)
  } catch (cause) {
    throw new Error(`Checkout failed for order ${order.id}`, { cause })
  }
}

At the edge of the application, you need to turn that failure into the right response. causewalk finds the original error without discarding the context added along the way:

import { as } from "causewalk"
import { CardDeclinedError } from "./payments"

try {
  await checkout(order)
  return response.status(204).send()
} catch (error) {
  const declined = as(error, CardDeclinedError)

  if (declined) {
    return response.status(402).send({ message: declined.message })
  }

  throw error
}

The same pattern works when deciding whether to retry a job, what to log, or how to report a failure.

[!NOTE] When would I use this?

If you're already using a rich/custom error library, causewalk probably isn't for you. It's designed as a simple set of utilities over native error wrapping.

The second you need to care about what "type" an error is, causewalk lets you inspect the full chain without sacrificing the rich additional context that native error wrapping enables.

Installation

npm install causewalk

API

as

Walks the error chain looking for the closest match. Pass it an error constructor:

import { as } from "causewalk"

class RetryableError extends Error {
  isRetryable() {
    return true
  }
}

const error = new Error("Could not fetch results", {
  cause: new RetryableError("Oh no"),
})

// `RetryableError` | null
const result = as(error, RetryableError)

Or a type predicate, if you're looking for something a little more specific:

function hasCode(error: unknown): error is { code: string } {
  return (
    typeof error === "object" && error !== null && "code" in error && typeof error.code === "string"
  )
}

// `{ code: string } | null`
const coded = as(error, hasCode)

Returns null when there isn't a match.

is

Checks whether a particular error exists anywhere in the error chain:

import { is } from "causewalk"

const timeout = new Error("Timed out")
const error = new Error("Could not fetch results", { cause: timeout })

is(error, timeout) // true
is(error, new Error("Timed out")) // false

Errors are compared by identity, not their type or contents. If you want to check for a type of error, use as instead.

all

Works like as, but returns every match in the error chain:

import { all } from "causewalk"

const error = new AggregateError([
  new TypeError("First"),
  new Error("Something else"),
  new TypeError("Second"),
])

// `TypeError[]`
const typeErrors = all(error, TypeError)

all also accepts a type predicate. It returns an empty array when there aren't any matches.

The nitty-gritty

Most of the time, an error and its cause form a straight line and there's only one possible match. When the chain branches, causewalk searches depth-first and returns the first match it finds.

The error you pass in is checked first:

const inner = new TypeError("Inner")
const outer = new TypeError("Outer", { cause: inner })

as(outer, TypeError) // outer
all(outer, TypeError) // [outer, inner]

An AggregateError's errors are searched in their original order, before its cause:

const first = new TypeError("First")
const second = new TypeError("Second")
const cause = new TypeError("Cause")

const error = new AggregateError(
  [new Error("First wrapper", { cause: first }), second],
  "Everything failed",
  { cause },
)

as(error, TypeError) // first
all(error, TypeError) // [first, second, cause]

For a SuppressedError, the newer error is searched before the older suppressed error:

const applicationError = new TypeError("Application failed")
const cleanupError = new TypeError("Cleanup failed")
const error = new SuppressedError(cleanupError, applicationError, "Cleanup also failed")

as(error, TypeError) // cleanupError
all(error, TypeError) // [cleanupError, applicationError]

Error chains can contain the same error more than once, or even contain cycles. Each object is only visited once, so these won't duplicate results or loop forever.

Can we have...

If it's already ergonomic in vanilla JS, causewalk has no desire to reimplement it. Dedicated error libraries with custom types provide very fluent helpers like myErr.wrap('Additional Context'). Causewalk doesn't: it works with native errors, and doesn't try to extend the built-ins.

Here are a few examples which are tempting but would absolutely nothing:

  • unwrap: It's just error.cause.
  • join: It's just AggregateError.
  • wrap: It's just new Error('Outer', { cause: new Error('Inner' )})