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

@mikkurogue/nothrow

v0.0.1

Published

Lightweight Result-based error handling for TypeScript.

Readme

nothrow

Lightweight Result-based error handling for TypeScript, with strong tagged-error ergonomics for both sync and async flows.

Why nothrow

  • Model failures as data (Err) instead of exceptions.
  • Compose operations with map, andThen, catchTag, and catchTags.
  • Use one mental model for sync and async code.
  • Keep error handling explicit and type-driven.

Install

pnpm add @mikkurogue/nothrow

ESM-only

@mikkurogue/nothrow is published as ESM-only.

  • Use import/export syntax.
  • For Node.js projects, set "type": "module" in your package.json.
  • CommonJS (require) is not supported.

If you are in a CommonJS codebase, you can still consume @mikkurogue/nothrow via dynamic import:

const { Result } = await import('@mikkurogue/nothrow');

Quick Start

import { Result, err, ok } from '@mikkurogue/nothrow';

const parsePort = (input: string) =>
  Result.try(() => {
    const value = Number(input);
    if (!Number.isInteger(value) || value <= 0) {
      return err({ _tag: 'InvalidPort', input });
    }
    return ok(value);
  });

const out = parsePort('3000')
  .map((port) => port + 1)
  .catchTag('InvalidPort', () => ok(8080))
  .run();

console.log(out); // 3001

API Overview

Top-level helpers:

  • ok, err, isOk, isErr
  • map, mapErr, andThen, match, unwrapOr
  • fromThrowable
  • hasTag, hasTags
  • taggedError, TaggedError
  • try, tryAsync

Chain APIs:

  • SyncResultChain: map, mapErr, andThen, catchAll, catchTag, catchTags, tapTag, toResult, run, unwrapOr, match
  • AsyncResultChain: map, mapErr, andThen, catchAll, catchTag, catchTags, tapTag, toPromise, run, unwrapOr, match

Tagged Errors

Prefer TaggedError/taggedError for application and library boundaries.

  • They are real Error instances and work with logging/tracing tools.
  • They carry typed _tag discriminants for catchTag/catchTags flows.
  • They can extend your own domain error classes when needed.

Plain object errors are also supported and remain useful for lightweight internal pipelines.

import { Result } from '@mikkurogue/nothrow';

const NotFound = Result.taggedError('NotFound')<{ id: string }>();

const loadUser = (id: string) =>
  Result.try(() => {
    if (id === '0') {
      return Result.err(new NotFound({ id, message: 'User not found' }));
    }
    return Result.ok({ id, name: 'Ada' });
  });

const user = loadUser('0')
  .catchTag('NotFound', (e) => Result.ok({ id: e.id, name: 'Guest' }))
  .run();

Generator style (yield*)

Result.try and Result.tryAsync support generator-based composition. This gives you early-exit behavior with linear, imperative-looking code.

import { Result, err, ok } from '@mikkurogue/nothrow';

const readPort = (value: string) =>
  Result.try(() => {
    const parsed = Number(value);
    if (!Number.isInteger(parsed)) {
      return err({ _tag: 'InvalidPort', value });
    }
    return ok(parsed);
  });

const normalizePort = (raw: string) =>
  Result.try(function* () {
    const port = yield* readPort(raw);
    if (port < 1 || port > 65535) {
      return err({ _tag: 'PortOutOfRange', port });
    }
    return ok(port);
  });

Async generators can mix sync and async chains in the same flow:

import { Result, ok } from '@mikkurogue/nothrow';

const loadConfig = () => Result.tryAsync(async () => ok({ retry: 2 }));
const readEnv = () => Result.try(() => ok('prod'));

const buildRuntime = Result.tryAsync(function* () {
  const config = yield* loadConfig();
  const env = yield* readEnv();
  return ok({ env, retry: config.retry });
});

Generator gotchas:

  • Use yield* with Result.try(...) / Result.tryAsync(...) chains. Plain yield is not the intended API.
  • In Result.try (sync), yielding async values throws a TypeError by design.
  • In Result.tryAsync, both sync and async chains are supported.
  • Throwing inside the generator is captured and converted to Err.

Development

This repo uses Vite+ (vp) for local tooling.

vp install
vp test
vp check
vp run build

Safety Notes

  • SyncResultChain.run() and SyncResultChain.value are intended for chains where the error type is never.
  • That guarantee is type-level: if you force-cast types, runtime failures are still possible.
  • Prefer match, unwrapOr, or toResult/toPromise when you are not fully eliminating errors.

API Stability

  • Current API is pre-1.0 (0.x), so minor versions may include breaking changes.
  • Core constructors and chain combinators are intended to remain stable as the library matures.

Cookbook

Recipe-driven examples live in docs/cookbook.md.