JSPM

@dang33zz/error-registry

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q31397F
  • License MIT

A configurable error handling system with registry pattern for flexible error code lookup, custom error classes, and global error handlers

Package Exports

  • @dang33zz/error-registry

Readme

Error Registry

A configurable error handling system with registry pattern for flexible error code lookup, custom error classes, and global error handlers.

Features

  • 🎯 Flexible Error Code Lookup - Customizable property seekers for finding error codes
  • 🔧 Registry Pattern - Map error codes to custom error classes
  • 🌐 Global Error Handlers - Centralized error handling with handler registry
  • 🔄 Runtime Registration - Dynamically register errors and handlers
  • 📦 TypeScript Support - Full TypeScript support with generic types
  • 🎨 Customizable Base Errors - Use your own base error class or factory

Installation

npm install @dang33zz/error-registry

Quick Start

import { createErrorRegistry } from '@dang33zz/error-registry';

// Define custom error classes
class NotFoundError extends Error {
  constructor(data: any) {
    super(data.message);
    this.name = 'NotFoundError';
  }
}

class ValidationError extends Error {
  constructor(data: any) {
    super(data.message);
    this.name = 'ValidationError';
  }
}

// Create error registry
const errorRegistry = createErrorRegistry({
  seekers: ['code', 'status'], // Properties to search for error codes
  errors: {
    'NOT_FOUND': NotFoundError,
    404: NotFoundError,
    'VALIDATION_ERROR': ValidationError,
    400: ValidationError,
  },
  handlers: {
    404: (error) => console.error('Not found:', error),
    400: (error) => console.error('Validation error:', error),
  },
});

// Create errors
const error1 = errorRegistry.createError({ code: 'NOT_FOUND', message: 'User not found' });
console.log(error1 instanceof NotFoundError); // true

const error2 = errorRegistry.createError({ status: 404, message: 'Resource not found' });
console.log(error2 instanceof NotFoundError); // true

// Handle errors
await errorRegistry.handleError(error1);
await errorRegistry.handleError(error2);

API

createErrorRegistry<TErrorData, TError>(config?)

Creates a configurable error registry instance.

Configuration Options

interface Config<TErrorData = unknown, TError extends Error = Error> {
  /**
   * Array of property names to search for error codes in error data.
   * Default: ["code", "status"]
   */
  seekers?: string[];

  /**
   * Factory function or class constructor to create the base error class
   * when no custom error is found in the registry.
   */
  baseError?: ((data: TErrorData) => TError) | (new (data: TErrorData) => TError);

  /**
   * Registry mapping error codes or status codes to custom error classes.
   */
  errors?: Record<string | number, new (data: TErrorData) => TError>;

  /**
   * Registry mapping error codes or status codes to handler functions.
   */
  handlers?: Record<string | number, (error: TError, ctx?: unknown) => void | Promise<void>>;
}

Returns

An ErrorRegistry instance with the following methods:

  • createError(data: TErrorData): TError - Creates an error instance from error data. The system searches for error codes using the configured seekers, then checks the error registry. Falls back to baseError if provided, or generic Error as last resort.
  • handleError(error: TError, ctx?: unknown): Promise<void> - Handles an error by invoking the registered handler. The system searches for a handler by checking each seeker property on the error object. If no handler is found via seekers, it falls back to using the error's constructor name.
  • registerError(key: string | number, ctor: new (data: TErrorData) => TError): void - Registers a custom error class
  • registerHandler(key: string | number, fn: (error: TError, ctx?: unknown) => void | Promise<void>): void - Registers a handler function

Examples

Using with Custom Base Error Class

class ApiError extends Error {
  code: string;
  statusCode: number;
  
  constructor(data: { code: string; statusCode: number; message: string }) {
    super(data.message);
    this.code = data.code;
    this.statusCode = data.statusCode;
    this.name = 'ApiError';
  }
}

const errorRegistry = createErrorRegistry({
  seekers: ['code'],
  baseError: ApiError,
  errors: {
    'NOT_FOUND': NotFoundError,
    'UNAUTHORIZED': UnauthorizedError,
  },
});

Using with Factory Function

const errorRegistry = createErrorRegistry({
  seekers: ['errorCode'],
  baseError: (data: { errorCode: string; message: string }) => {
    return new CustomError(data.errorCode, data.message);
  },
});

Dynamic Registration

const errorRegistry = createErrorRegistry();

// Register errors at runtime
errorRegistry.registerError('OUT_OF_STOCK', OutOfStockError);
errorRegistry.registerError(500, ServerError);

// Register handlers at runtime
errorRegistry.registerHandler(401, async (error) => {
  localStorage.removeItem('token');
  window.location.href = '/login';
});

errorRegistry.registerHandler('OUT_OF_STOCK', (error) => {
  showNotification('Product is out of stock');
});

Error Handling with Context

const errorRegistry = createErrorRegistry({
  handlers: {
    500: async (error, ctx) => {
      // ctx can contain additional context like request ID, user info, etc.
      await logToSentry(error, { context: ctx });
    },
  },
});

await errorRegistry.handleError(error, { requestId: 'abc123', userId: 'user456' });

Framework Integration

Quick examples for common frameworks. For more detailed examples, see EXAMPLES.md.

Axios Integration
import axios from 'axios';
import { createErrorRegistry } from '@dang33zz/error-registry';

// Custom base error as factory function
const createApiError = (data: any) => {
  const error = new Error(data.message || 'API Error');
  (error as any).statusCode = data.statusCode || data.status || 500;
  (error as any).code = data.code;
  error.name = 'ApiError';
  return error;
};

const errorRegistry = createErrorRegistry({
  seekers: ['errorCode', 'statusCode', 'status'], // Custom seekers
  baseError: createApiError, // Custom base error as factory
  // Error mapping is optional - only handlers for global error handling
  handlers: {
    'NOT_FOUND': (error) => console.log('Resource not found'),
    404: (error) => console.log('404 handler'),
    'UNAUTHORIZED': () => redirectToLogin(),
    401: () => redirectToLogin(),
  },
});

// Global error handling
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const apiError = errorRegistry.createError({
      errorCode: error.response?.data?.errorCode,
      statusCode: error.response?.status,
      message: error.response?.data?.message || error.message,
    });
    await errorRegistry.handleError(apiError);
    return Promise.reject(apiError);
  }
);
Express Integration
import express from 'express';
import { createErrorRegistry } from '@dang33zz/error-registry';

// Custom base error
class HttpError extends Error {
  statusCode: number;
  code?: string;
  
  constructor(data: any) {
    super(data.message || 'HTTP Error');
    this.statusCode = data.statusCode || data.status || 500;
    this.code = data.code;
    this.name = 'HttpError';
  }
}

const errorRegistry = createErrorRegistry({
  seekers: ['code', 'status'],
  baseError: HttpError, // Custom base error
  errors: {
    'NOT_FOUND': NotFoundError, // Optional error mapping with code
    404: NotFoundError, // Optional error mapping with status
    'VALIDATION_ERROR': ValidationError,
    400: ValidationError,
  },
  handlers: {
    'NOT_FOUND': (error, ctx) => console.log('Not found'),
    404: (error, ctx) => console.log('404 handler'),
    'VALIDATION_ERROR': (error, ctx) => console.log('Validation failed'),
    400: (error, ctx) => console.log('400 handler'),
  },
});

const app = express();

// Global error handling
app.use((err, req, res, next) => {
  const error = errorRegistry.createError({
    status: err.status || 500,
    code: err.code,
    message: err.message,
  });
  
  errorRegistry.handleError(error, { req, res });
  res.status(error.statusCode || 500).json({ error: error.message });
});

// Dynamic registration
errorRegistry.registerError('FORBIDDEN', ForbiddenError);
errorRegistry.registerHandler('FORBIDDEN', (error) => {
  console.log('Access forbidden');
});

// Creating errors in routes
app.get('/users/:id', (req, res, next) => {
  const error = errorRegistry.createError({
    code: 'NOT_FOUND',
    message: 'User not found',
  });
  next(error);
});
Next.js Integration
// lib/errorRegistry.ts
import { createErrorRegistry } from '@dang33zz/error-registry';

// Custom base error as factory function
const createNextApiError = (data: any) => {
  const error = new Error(data.message || 'API Error');
  (error as any).statusCode = data.statusCode || data.status || 500;
  (error as any).code = data.code;
  error.name = 'NextApiError';
  return error;
};

export const errorRegistry = createErrorRegistry({
  seekers: ['code', 'status'],
  baseError: createNextApiError, // Custom base error as factory
  // Error mapping is optional - can use just handlers for global error handling
  handlers: {
    'NOT_FOUND': (error) => console.log('Not found'),
    404: (error) => console.log('404 handler'),
    'UNAUTHORIZED': (error) => console.log('Unauthorized'),
    401: (error) => console.log('401 handler'),
  },
});

// app/api/route.ts or pages/api/route.ts
import { errorRegistry } from '@/lib/errorRegistry';

export async function GET() {
  try {
    // Creating errors
    const error = errorRegistry.createError({
      code: 'NOT_FOUND',
      message: 'Resource not found',
    });
    throw error;
  } catch (error) {
    const apiError = errorRegistry.createError({
      status: error.status || 500,
      code: error.code,
      message: error.message,
    });
    await errorRegistry.handleError(apiError);
    return Response.json({ error: apiError.message }, { status: apiError.statusCode || 500 });
  }
}

TypeScript

Full TypeScript support with generics:

interface MyErrorData {
  code: string;
  message: string;
  timestamp: number;
}

class MyError extends Error {
  code: string;
  timestamp: number;
  
  constructor(data: MyErrorData) {
    super(data.message);
    this.code = data.code;
    this.timestamp = data.timestamp;
  }
}

const errorRegistry = createErrorRegistry<MyErrorData, MyError>({
  seekers: ['code'],
  baseError: MyError,
});

License

MIT