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

@lvigil/err

v1.3.0

Published

Enhanced Error with message history, context dict, and flag dict — flat, not chained, designed for logging.

Readme

err

A minimal, environment-agnostic enhancement to JavaScript's native Error.

err provides a simple way to attach runtime context, human-controlled message breadcrumbs, and error flags for program logic (like err.code), while preserving the behavior, simplicity, and semantics of a standard Error.

It does not modify prototypes. It does not wrap stack traces. It remains fully compatible with all runtimes (Node, browser, Deno, Bun, Workers).

Designed for logging and debugging — when you read the log, you see everything in one place, not scattered across nested layers.

Installation

pnpm add @lvigil/err

The Three Core Dimensions

This library handles three things:

| Dimension | What it is | How to use | Behavior | |-----------|-----------|------------|----------| | message_history | How the error bubbled up | Add with .m() | append | | context_dict | State at each layer for debugging | Pass as 2nd parameter | merge, old wins | | flag_dict | Flags for program logic | Pass as 3rd parameter | merge, new wins |

context_dict — for debugging. Any context you need, as key-value pairs.

flag_dict — for coding. Only use it when the caller needs if (err.code === ...) checks. Don't write it just for the sake of writing.

Flat, not chained

ES2022 cause creates a linked chain — each layer wraps the previous error. To see the full picture, you need to recursively walk through err.cause.cause.cause.... Context is scattered.

This library takes a flat approach — all three dimensions accumulate into single, accessible structures:

  • msgs[] — one array, read top to bottom
  • original{} — one object, all context merged
  • flag_dict — directly on err, latest values ready for checks

Trade-off: flat means possible key conflicts when merging. Each dimension handles this differently:

  • message_history — no conflict, just append
  • context_dictold wins, because the context closer to the error source is more valuable for debugging
  • flag_dictnew wins, because the caller may need to update flags for branching logic

When you console.log(err), everything is right there. No recursion needed.


Quick Start

Create an error

import { Err } from '@lvigil/err'

throw Err('Invalid config', { config })

Wrap and rethrow

One line — context, message, done:

import { OnErr } from '@lvigil/err'

catch (e) {
  throw OnErr(e, { userId, file }).m('Failed to load user data')
}

Usage

Creating an error

import { Err } from '@lvigil/err'

throw Err('invalid payload format', { payload })

Result:

{
  message: 'invalid payload format',
  msgs: ['invalid payload format'],
  original: { payload },
  stack: '...'
}

Enhancing an error during rethrows

import { Err, OnErr } from '@lvigil/err'

function loadPayload(file) {
  try {
    const payload = JSON.parse(fs.readFileSync(file, 'utf8'))
    if (Object(payload) !== payload) {
      throw Err('invalid payload', { payload })
    }
    return payload

  } catch (e) {
    throw OnErr(e, { file }).m('load payload failed')
  }
}

Result:

{
  message: 'invalid payload',
  msgs: ['invalid payload', 'load payload failed'],
  original: { payload, file },
  stack: '...'
}

OnErr preserves:

  • existing message
  • existing stack
  • existing msgs (appends new ones)
  • existing original (merges new context, old values win)

Using .m() for message breadcrumbs

.m() appends a message to err.msgs[] without changing err.message.

// Deep in the call stack
throw Err('ENOENT: file not found')

// Middle layer
catch (e) {
  throw OnErr(e, { configPath }).m('failed to read config')
}

// Top layer
catch (e) {
  throw OnErr(e).m('app initialization failed')
}

Final msgs:

['ENOENT: file not found', 'failed to read config', 'app initialization failed']

API

Err(message, [context_dict], [flag_dict])

Creates an enhanced Error.

| Parameter | Type | Description | |-----------|------|-------------| | message | string | Error message | | context_dict | object | Debugging context (key-value pairs) | | flag_dict | object \| string | Rarely needed. Only when caller checks err.code |

context_dict must be an object — it's a map of key-value pairs, not just values:

// Correct — key tells you what the value means
throw Err('load failed', { file, userId })
// → context: { file: '/data/x.json', userId: 123 }

// Wrong — just a value, no key
throw Err('load failed', file)
// → context: '/data/x.json'  ← what is this? no one knows when logging

Returns: Error with msgs, original, and .m() method.

Using flag_dict — when you need a flag but no context:

// No context needed, just a flag for the caller to check
throw Err('rate limit exceeded', null, { code: 'E_RATE_LIMIT' })

// Caller can then:
if (err.code === 'E_RATE_LIMIT') {
  await sleep(1000)
  retry()
}

Key-mirror shorthand — pass a string instead of { key: 'key' }:

// These are equivalent:
throw Err('rate limit exceeded', null, { E_RATE_LIMIT: 'E_RATE_LIMIT' })
throw Err('rate limit exceeded', null, 'E_RATE_LIMIT')

// Caller can then:
if (err.E_RATE_LIMIT) {
  await sleep(1000)
  retry()
}

OnErr(err, [context_dict], [flag_dict])

Wraps/enhances an existing error.

| Parameter | Type | Description | |-----------|------|-------------| | err | any | The error to wrap (will be converted to Err) | | context_dict | object | Additional context to merge (key-value pairs) | | flag_dict | object \| string | Rarely needed. Only when caller checks err.code |

Returns: The same error instance, enhanced.

  • If err is not an Error, it becomes one
  • msgs is initialized from err.message if not present
  • .m() method is added if not present

.m(message)

Appends a message to err.msgs[].

Returns: The error instance (for chaining).

throw OnErr(e, { file }).m('load failed')

.f(flag_dict)

Attaches flags to the error. Same interface as the flag_dict parameter in Err() and OnErr().

| Parameter | Type | Description | |-----------|------|-------------| | flag_dict | object \| string | Flags to attach (or key-mirror string) |

Returns: The error instance (for chaining).

throw OnErr(e).f('E_TIMEOUT')
throw OnErr(e).f({ code: 'E_TIMEOUT', retry: true })

Most of the time, passing flag_dict directly to Err() or OnErr() is sufficient. Use .f() when you need to add flags in a wrapper that doesn't accept flag_dict.

.c(context_dict)

Merges context into err.original. Same interface as the context_dict parameter in Err() and OnErr().

| Parameter | Type | Description | |-----------|------|-------------| | context_dict | object | Context to merge (old wins) |

Returns: The error instance (for chaining).

throw OnErr(e).c({ userId, file })

Most of the time, passing context_dict directly to Err() or OnErr() is sufficient. Use .c() when you need to add context in a wrapper that doesn't accept context_dict.


Protected Properties

These properties cannot be overwritten via flag_dict:

| Property | Reason | |----------|--------| | name, message, stack, cause | Standard Error properties | | msgs, original, m, f, c | Core functionality of this library | | response | Protected for compatibility with HTTP libraries (superagent, axios) |


Example: cause chain vs flat

Run the examples to see the difference:

npm run example-cause   # ES2022 cause chain
npm run example-onerr   # This library's flat approach

cause chain — 4 layers = 4 nested stacks, mostly redundant:

[16:57:39.242] ERROR (cause-chain): cause chain example
    err: {
      "message": "Request failed",
      "stack":
          Error: Request failed
              at handleRequest (example/cause-chain/api-handler.js:9:11)
              at run.js:6:3
      "cause": {
        "message": "Authentication failed",
        "stack":
            Error: Authentication failed
                at authenticate (example/cause-chain/auth-service.js:9:11)
                at handleRequest (example/cause-chain/api-handler.js:7:5)
                at run.js:6:3
        "cause": {
          "message": "Cannot find user 123",
          "stack":
              Error: Cannot find user 123
                  at findUser (example/cause-chain/user-repo.js:9:11)
                  at authenticate ...
                  at handleRequest ...
          "cause": {
            "message": "ECONNREFUSED 127.0.0.1:3306",
            "stack":
                Error: ECONNREFUSED 127.0.0.1:3306
                    at connect (example/cause-chain/db.js:4:9)
                    at findUser ...
                    at authenticate ...
                    at handleRequest ...
          }
        }
      }
    }

OnErr flat — 1 stack, all context merged:

[16:58:56.394] ERROR (onerr-flat): OnErr flat example
    err: {
      "message": "ECONNREFUSED",
      "stack":
          Error: ECONNREFUSED
              at connect (example/onerr-flat/db.js:6:9)
              at findUser (example/onerr-flat/user-repo.js:8:5)
              at authenticate (example/onerr-flat/auth-service.js:8:5)
              at handleRequest (example/onerr-flat/api-handler.js:8:5)
              at run.js:6:3
      "msgs": [
        "ECONNREFUSED",
        "Cannot find user",
        "Authentication failed",
        "Request failed"
      ],
      "original": {
        "endpoint": "/api/auth",
        "token": "abc",
        "userId": 123,
        "host": "127.0.0.1",
        "port": 3306
      }
    }

Same information, half the noise. Context tells you everything at a glance.


Philosophy

This library does not replace JavaScript's error system. It adds what real-world debugging needs:

  • Flat, not nested — everything in one place for easy logging
  • Message history — see how the error bubbled up
  • Context accumulation — see the state at each layer
  • Coding-friendlyif (err.code === 'E_TIMEOUT') just works
  • Minimal and predictable — no prototype hacks, no magic

The goal is clarity — not complexity.


License

MIT © Ben P.P. Tung