Clov Packages
    Preparing search index...

    Module @clov-std/error - v5.0.0

    Clov Error logo

    ๐Ÿž Clov Error

    If you've ever debugged a production incident with nothing but a generic Error("something went wrong"), you know the pain.
    This package gives your errors structure, every exception carries a machine-readable code, so you can always branch on what happened instead of grepping a message string.

    Vanilla Error objects lack context.
    Catching one leaves you matching on error.message strings, which breaks the day someone rewords the message.

    @clov-std/error solves that with two classes:

    • Exception - a richer base error carrying a stable code and a typed cause.
    • HttpException - the same, plus a resolved HTTP status.

    The code is the point. A low-level library throws Exception with code: 'jwt.token.expired' without knowing anything about transports or locales, and the layer that does know maps that code to a wire format. One direction of dependency, no HTTP concern leaking into a token signer.

    No dependencies, no bloat. Just structured errors that make your life easier.

    Looking for localized messages? That's @clov-std/i18n.

    • ๐Ÿ”‘ Stable Error Codes : Branch on error.code, never on error.message.
    • ๐ŸŒ HTTP When You Need It : HttpException resolves a status from a name ('NOT_FOUND') or a number.
    • ๐Ÿ”— Typed Cause : cause keeps its shape through the generic, so wrapping an error doesn't lose its type.
    • ๐Ÿงน Clean Stack Traces : The constructor frame is stripped, and name reflects the actual subclass.
    • ๐Ÿ“ฆ Zero Dependencies : Pure TypeScript, no runtime globals, tiny footprint.
    bun add @clov-std/error
    

    Use Exception whenever you need a traceable error with more context than a plain Error.

    import { Exception } from '@clov-std/error';

    throw new Exception('Configuration file not found', {
    code: 'config.file.not-found'
    });

    The code is what callers should switch on:

    try {
    await verifyToken(token);
    } catch (err) {
    if (err instanceof Exception && err.code === 'jwt.token.expired') return refresh();
    throw err;
    }

    You can also wrap a root cause to preserve the original error:

    import { Exception } from '@clov-std/error';

    try {
    await db.save(user);
    } catch (err) {
    throw new Exception('Failed to persist user', { cause: err });
    }

    No request id, no timestamp. Both belong to the layer that handles the error: a request handler already has an x-request-id (or mints one), and a log sink already stamps every line with a time. Putting them on the error means every throw pays for identity nobody reads.

    If an application genuinely needs them on the instance, that's a subclass:

    class TracedException extends Exception {
    public readonly uuid: string = crypto.randomUUID();
    public readonly date: Date = new Date();
    }

    When a failure has a status, throw HttpException. It takes a status name or a number, and resolves it to error.status:

    import { HttpException } from '@clov-std/error';

    throw new HttpException('Account not found', {
    code: 'auth.account.not-found',
    status: 'NOT_FOUND' // or 404
    });

    status is required on purpose: an HTTP exception without one is a bug, and a silent 500 would hide it.

    Nothing about RFC 9457 lives here. Your presentation layer maps code to the wire format it needs, which keeps this package usable from a CLI or a worker:

    // One adapter, at the boundary
    problem({ type: error.code, status: error.status, detail: error.message });

    For translated messages, @clov-std/i18n builds on Exception with catalogs of localized templates.

    Full docs: https://clovlabs.github.io/std/

    MIT - Feel free to use it.

    Exception
    HttpException
    ExceptionOptions
    HttpExceptionOptions
    HttpStatusCode
    HttpStatusKey
    HTTP_STATUS_CODES