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

@zerospin/error

v2.1.5

Published

ZeroSpin Error provides a type-safe, structured error system for Effect-based applications. It extends Effect's `Data.TaggedError` to provide consistent error handling with serialization support, error codes, and optional metadata.

Readme

@zerospin/error

ZeroSpin Error provides a type-safe, structured error system for Effect-based applications. It extends Effect's Data.TaggedError to provide consistent error handling with serialization support, error codes, and optional metadata.

Installation

npm install @zerospin/error effect

Basic Usage

Creating Errors

ZerospinError can be created in two ways:

Simple form (code only):

import { ZerospinError } from '@zerospin/error';

const error = new ZerospinError('my-error-code');
// Creates an error with code: 'my-error-code', message: 'my-error-code'

Full form (with metadata):

const error = new ZerospinError({
  code: 'failed-to-fetch',
  message: 'Failed to fetch data from server',
  cause: originalError,
  extra: { url: 'https://api.example.com' },
  status: 500,
});

Using ZerospinError in Effect Contexts

1. Throwing Errors in Effect.gen

In Effect.gen functions, use yield* to throw errors:

import { Effect } from 'effect';
import { ZerospinError } from '@zerospin/error';

const fetchData = Effect.gen(function* () {
  const res = yield* Effect.tryPromise(() => fetch('/api/data'));

  if (res.status === 404) {
    return yield* new ZerospinError({
      code: 'resource-not-found',
      message: 'Resource not found',
    });
  }

  return yield* res.json();
});

Example from ZeroSpin:

// From packages/client/src/ZerospinClientAdapter.ts
export const ZerospinClientAdapter = Layer.effect(
  ZerospinStorageAdapter,
  Effect.fn('getClientAdapter')(function* () {
    const isOPFSSupported = yield* OPFSAdapter.check().pipe(
      Effect.either,
      Effect.map(either => Either.isRight(either)),
    );
    if (isOPFSSupported) {
      return OPFSAdapter;
    }
    const isLocalStorageSupported = yield* LocalStorageAdapter.check().pipe(
      Effect.either,
      Effect.map(either => Either.isRight(either)),
    );
    if (isLocalStorageSupported) {
      return LocalStorageAdapter;
    }
    return yield* new ZerospinError({
      code: 'no-adapter-available',
      message: 'No adapter available',
    });
  })(),
);

2. Mapping Errors with Effect.mapError

Transform errors from other Effect operations:

import { Effect } from 'effect';
import { ZerospinError } from '@zerospin/error';

const parseJson = Effect.tryPromise(() => res.json()).pipe(
  Effect.mapError(error => {
    return new ZerospinError({
      code: 'failed-to-parse-json',
      message: 'Failed to parse JSON',
      cause: error,
    });
  }),
);

Example from ZeroSpin:

// From packages/client/src/makeZerospinFetchFrontendApi.ts
const res =
  yield *
  Effect.tryPromise(async () => {
    return fetch(url, {
      body: JSON.stringify(body),
      headers: { 'Content-Type': 'application/json' },
      method: 'POST',
    }).catch((error: unknown) => {
      throw new ZerospinError({
        code: 'failed-to-fetch',
        message: 'Failed to fetch',
        cause: error,
      });
    });
  }).pipe(
    Effect.mapError(error => {
      return new ZerospinError({
        code: 'failed-to-fetch',
        message: 'Failed to fetch',
        cause: error,
      });
    }),
  );

3. Catching All Errors with Effect.catchAll

Handle any error and convert it to a ZerospinError:

import { Effect } from 'effect';
import { ZerospinError } from '@zerospin/error';

const safeOperation = someEffect.pipe(
  Effect.catchAll(error => {
    return new ZerospinError({
      code: 'operation-failed',
      message: error.message,
      cause: error,
    });
  }),
);

Example from ZeroSpin:

// From packages/zerospin/src/batch/makeSafe.ts
const safe =
  yield *
  validateUnknown({
    onExcessProperty: 'preserve',
    schema,
    value: command,
  }).pipe(
    Effect.catchAll(error => {
      return new ZerospinError({
        code: 'failed-to-validate-command',
        message: error.message,
        cause: error,
      });
    }),
  );

4. Handling Promise Errors

When working with promises, catch errors and throw ZerospinError:

import { Effect } from 'effect';
import { ZerospinError } from '@zerospin/error';

const fetchData = Effect.tryPromise(async () => {
  try {
    const response = await fetch('/api/data');
    return await response.json();
  } catch (error) {
    throw new ZerospinError({
      code: 'fetch-failed',
      message: 'Failed to fetch data',
      cause: error,
    });
  }
});

Example from ZeroSpin:

// From packages/client/src/makeZerospinFetchFrontendApi.ts
const res =
  yield *
  Effect.tryPromise(async () => {
    return fetch(url, {
      body: JSON.stringify(body),
      headers: { 'Content-Type': 'application/json' },
      method: 'POST',
    }).catch((error: unknown) => {
      throw new ZerospinError({
        code: 'failed-to-fetch',
        message: 'Failed to fetch',
        cause: error,
      });
    });
  });

5. Schema Validation Errors

Convert schema validation errors to ZerospinError:

import { Effect, Schema } from 'effect';
import { ZerospinError } from '@zerospin/error';

const validateData = Schema.decode(Schema.String)(value).pipe(
  Effect.mapError(error => {
    return new ZerospinError({
      code: 'validation-failed',
      message: error.message,
      cause: error,
    });
  }),
);

Example from ZeroSpin:

// From packages/zerospin/src/utils/encodeUnknown.ts
export const encodeUnknown = Effect.fn('encodeUnknown')(<
  DECODED,
  ENCODED,
>(props: {
  schema: Schema.Schema<DECODED, ENCODED>;
  value: unknown;
  errorMessage?: string;
}): Effect.Effect<ENCODED, ZerospinError<'failed-to-encode-unknown'>> => {
  const { errorMessage, schema, value } = props;

  return Schema.encodeUnknown(schema)(value).pipe(
    Effect.mapError(error => {
      return new ZerospinError({
        code: 'failed-to-encode-unknown',
        message: errorMessage ?? error.message,
        cause: error,
      });
    }),
  );
});

Another example:

// From packages/zerospin/src/contract/makeContract.ts
return validateUnknown({
  onExcessProperty: 'error',
  schema: resultsSchema,
  value: results,
}).pipe(
  Effect.mapError(error => {
    return new ZerospinError({
      code: 'failed-to-validate-results',
      message: error.message,
      cause: error,
    });
  }),
);

6. Deserializing Errors from JSON

When receiving errors from APIs or serialized sources:

import { ZerospinError } from '@zerospin/error';

// From packages/client/src/makeZerospinFetchFrontendApi.ts
const payload =
  yield *
  Effect.tryPromise(() => {
    return res.json() as Promise<IAnyErrorJson | IJson>;
  });

if (res.status === 200) {
  return payload as IJson;
}

// Deserialize error from JSON
return yield * new ZerospinError(payload as IAnyErrorJson);

7. Error Handling in Route Handlers

Handle errors at the boundary and serialize for HTTP responses:

import { Effect, Exit, Cause } from 'effect';
import { ZerospinError } from '@zerospin/error';
import { NextResponse } from 'next/server';

const exit = await Effect.runPromiseExit(myEffect);

return Exit.match(exit, {
  onFailure: cause => {
    console.error(Cause.pretty(cause));
    if (Cause.isFailType(cause)) {
      const error = cause.error as ZerospinError;
      return NextResponse.json(error.serialize(), {
        status: error.status ?? 400,
      });
    }
    return NextResponse.json(
      new ZerospinError({
        code: 'unexpected-error',
        message: Cause.pretty(cause),
      }).serialize(),
      { status: 500 },
    );
  },
  onSuccess: value => {
    return NextResponse.json(value);
  },
});

Example from ZeroSpin:

// From packages/zerospin/src/rpc/makeNextRoute.ts
return Exit.match(exit, {
  onFailure: cause => {
    console.error(Cause.pretty(cause));
    if (Cause.isFailType(cause)) {
      const error = cause.error as ZerospinError;
      return NextResponse.json(error.serialize(), {
        status: error.status ?? 400,
      });
    }
    return NextResponse.json(
      new ZerospinError({
        code: 'unexpected-error',
        message: Cause.pretty(cause),
      }).serialize(),
      { status: 500 },
    );
  },
  onSuccess: value => {
    return NextResponse.json(value);
  },
});

Error Properties

Error Structure

interface IError<T extends string = string, E = unknown> {
  code: T; // Error code identifier
  status: null | number; // HTTP status code (optional)
  cause?: unknown; // Original error that caused this
  extra?: E; // Additional typed metadata
  message?: string; // Human-readable message
}

Type Safety with Error Codes

You can create type-safe error codes:

type MyErrorCodes =
  | 'failed-to-fetch'
  | 'validation-failed'
  | 'resource-not-found';

const error: ZerospinError<MyErrorCodes> = new ZerospinError({
  code: 'failed-to-fetch', // TypeScript will validate this
  message: 'Failed to fetch',
});

Utility Methods

Checking if Something is a ZerospinError

import { ZerospinError } from '@zerospin/error';

if (ZerospinError.isZerospinError(error)) {
  console.log(error.code);
  console.log(error.message);
}

Example from ZeroSpin:

// From packages/zerospin/src/ZerospinErrorLayer.ts
Exit.mapError(exit, error => {
  if (ZerospinError.isZerospinError(error)) {
    if (error.cause) {
      error.message += ` ${JSON.stringify(error.cause, null, 2)}`;
    }
    if (error.extra) {
      error.message += ` ${JSON.stringify(error.extra, null, 2)}`;
    }
  }
  return error;
});

Serializing Errors

Serialize errors for transmission over networks or storage:

const error = new ZerospinError({
  code: 'my-error',
  message: 'Something went wrong',
  extra: { actorId: '123' },
  status: 400,
});

// Serialize full error
const json = error.serialize();

// Serialize without certain fields
const jsonWithoutExtra = error.serialize(['extra']);

Best Practices

  1. Always provide meaningful error codes: Use descriptive, kebab-case codes like 'failed-to-fetch' instead of generic ones.

  2. Preserve original errors: Use the cause property to maintain error chains:

    new ZerospinError({
      code: 'operation-failed',
      message: 'Operation failed',
      cause: originalError, // Preserve the original error
    });
  3. Use typed extra data: Leverage TypeScript generics for type-safe metadata:

    new ZerospinError<ErrorCode, { actorId: string; action: string }>({
      code: 'permission-denied',
      extra: { actorId: '123', action: 'delete' },
    });
  4. Handle at boundaries: Convert to ZerospinError at Effect boundaries (promises, HTTP handlers, etc.) and let them propagate through your Effect pipeline.

  5. Serialize for APIs: Use serialize() when sending errors over HTTP or storing them.

API Reference

ZerospinError Class

class ZerospinError<T extends string = never, E = unknown>
  extends Data.TaggedError('ZerospinError')<IError<T, E>>

Constructor:

  • new ZerospinError(code: string) - Simple form
  • new ZerospinError(props: IProps<T, E>) - Full form

Static Methods:

  • ZerospinError.isZerospinError(data: unknown): data is ZerospinError - Type guard
  • ZerospinError.makeZerospinErrorJson(props): IAnyErrorJson - Create JSON representation

Instance Methods:

  • serialize(omit?: string[]): IAnyErrorJson - Serialize to JSON

Types

type IAnyError = ZerospinError<string>;
type IAnyErrorJson = Brand.Brand<'ZerospinErrorJson'> & {
  code: string;
  extra: unknown;
  message: string;
  status: null | number;
};