UNPKG

thrilled-be-core

Version:

Core Express backend package with middleware, logging, security, and base application setup

350 lines (349 loc) 11.9 kB
"use strict"; /** * Custom Error Classes for Enterprise Applications * * This file defines a comprehensive set of custom error classes that provide * better error handling, logging, and debugging capabilities throughout applications. * These errors follow HTTP status codes and provide structured error information * for both development and production environments. * * @package be-core * @since 1.0.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ErrorUtils = exports.RequestTimeoutError = exports.InternalServerError = exports.SystemError = exports.ConfigurationError = exports.RateLimitError = exports.ThirdPartyAPIError = exports.ServiceUnavailableError = exports.ExternalServiceError = exports.DuplicateResourceError = exports.ConflictError = exports.NotFoundError = exports.ResourceError = exports.BusinessRuleValidationError = exports.SchemaValidationError = exports.ValidationError = exports.SessionExpiredError = exports.InvalidTokenError = exports.TokenExpiredError = exports.AuthorizationError = exports.AuthenticationError = exports.TransactionError = exports.DatabaseConstraintError = exports.QueryTimeoutError = exports.DatabaseConnectionError = exports.DatabaseError = exports.AppError = void 0; exports.handleErrors = handleErrors; exports.asyncHandler = asyncHandler; const thrilled_be_types_1 = require("thrilled-be-types"); /** * Base application error class * All custom errors should extend this class */ class AppError extends thrilled_be_types_1.HttpException { timestamp; context; code; isOperational; status; // Override readonly status from HttpException constructor(message, status, code, context, isOperational = true) { super(status, message); this.name = this.constructor.name; this.timestamp = new Date(); this.context = context; this.code = code; this.isOperational = isOperational; this.status = status; // Set our mutable status // Maintains proper stack trace for where our error was thrown if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } /** * Convert error to JSON representation */ toJSON() { return { name: this.name, message: this.message, status: this.status, code: this.code, timestamp: this.timestamp.toISOString(), context: this.context, isOperational: this.isOperational, }; } } exports.AppError = AppError; /** * Database-related errors */ class DatabaseError extends AppError { constructor(message, context, originalError) { super(message, 500, 'DATABASE_ERROR', { ...context, originalError: originalError?.message }, true); } } exports.DatabaseError = DatabaseError; class DatabaseConnectionError extends DatabaseError { constructor(message = 'Database connection failed', context) { super(message, context); this.code = 'DATABASE_CONNECTION_ERROR'; } } exports.DatabaseConnectionError = DatabaseConnectionError; class QueryTimeoutError extends DatabaseError { constructor(message = 'Database query timed out', context) { super(message, context); this.code = 'QUERY_TIMEOUT_ERROR'; this.status = 504; // Gateway timeout } } exports.QueryTimeoutError = QueryTimeoutError; class DatabaseConstraintError extends DatabaseError { constructor(message, constraint, context) { super(message, { ...context, constraint }); this.code = 'DATABASE_CONSTRAINT_ERROR'; this.status = 400; // Bad request } } exports.DatabaseConstraintError = DatabaseConstraintError; class TransactionError extends DatabaseError { constructor(message = 'Transaction failed', context) { super(message, context); this.code = 'TRANSACTION_ERROR'; } } exports.TransactionError = TransactionError; /** * Authentication and Authorization errors */ class AuthenticationError extends AppError { constructor(message = 'Authentication failed', context) { super(message, 401, 'AUTHENTICATION_ERROR', context); } } exports.AuthenticationError = AuthenticationError; class AuthorizationError extends AppError { constructor(message = 'Access denied', context) { super(message, 403, 'AUTHORIZATION_ERROR', context); } } exports.AuthorizationError = AuthorizationError; class TokenExpiredError extends AuthenticationError { constructor(message = 'Token has expired', context) { super(message, context); this.code = 'TOKEN_EXPIRED_ERROR'; } } exports.TokenExpiredError = TokenExpiredError; class InvalidTokenError extends AuthenticationError { constructor(message = 'Invalid token provided', context) { super(message, context); this.code = 'INVALID_TOKEN_ERROR'; } } exports.InvalidTokenError = InvalidTokenError; class SessionExpiredError extends AuthenticationError { constructor(message = 'Session has expired', context) { super(message, context); this.code = 'SESSION_EXPIRED_ERROR'; } } exports.SessionExpiredError = SessionExpiredError; /** * Validation errors */ class ValidationError extends AppError { fields; constructor(message, fields, context) { super(message, 400, 'VALIDATION_ERROR', context); this.fields = fields; } } exports.ValidationError = ValidationError; class SchemaValidationError extends ValidationError { constructor(message, fields, context) { super(message, fields, context); this.code = 'SCHEMA_VALIDATION_ERROR'; } } exports.SchemaValidationError = SchemaValidationError; class BusinessRuleValidationError extends ValidationError { constructor(message, rule, context) { super(message, undefined, { ...context, rule }); this.code = 'BUSINESS_RULE_VALIDATION_ERROR'; this.status = 422; // Unprocessable Entity } } exports.BusinessRuleValidationError = BusinessRuleValidationError; /** * Resource errors */ class ResourceError extends AppError { constructor(message, status, code, context) { super(message, status, code, context); } } exports.ResourceError = ResourceError; class NotFoundError extends ResourceError { constructor(resource, identifier, context) { const message = identifier ? `${resource} with ID '${identifier}' not found` : `${resource} not found`; super(message, 404, 'NOT_FOUND_ERROR', { ...context, resource, identifier }); } } exports.NotFoundError = NotFoundError; class ConflictError extends ResourceError { constructor(message, resource, context) { super(message, 409, 'CONFLICT_ERROR', { ...context, resource }); } } exports.ConflictError = ConflictError; class DuplicateResourceError extends ConflictError { constructor(resource, field, value, context) { super(`${resource} with ${field} '${value}' already exists`, resource, { ...context, field, value }); this.code = 'DUPLICATE_RESOURCE_ERROR'; } } exports.DuplicateResourceError = DuplicateResourceError; /** * External service errors */ class ExternalServiceError extends AppError { constructor(message, service, context) { super(message, 503, 'EXTERNAL_SERVICE_ERROR', { ...context, service }); } } exports.ExternalServiceError = ExternalServiceError; class ServiceUnavailableError extends ExternalServiceError { constructor(message = 'Service temporarily unavailable', service, context) { super(message, service || 'unknown', context); this.code = 'SERVICE_UNAVAILABLE_ERROR'; } } exports.ServiceUnavailableError = ServiceUnavailableError; class ThirdPartyAPIError extends ExternalServiceError { constructor(message, apiName, statusCode, context) { super(message, apiName, { ...context, statusCode }); this.code = 'THIRD_PARTY_API_ERROR'; } } exports.ThirdPartyAPIError = ThirdPartyAPIError; /** * Rate limiting errors */ class RateLimitError extends AppError { constructor(message = 'Rate limit exceeded', context) { super(message, 429, 'RATE_LIMIT_ERROR', context); } } exports.RateLimitError = RateLimitError; /** * Configuration errors */ class ConfigurationError extends AppError { constructor(message, configKey, context) { super(message, 500, 'CONFIGURATION_ERROR', { ...context, configKey }, false); } } exports.ConfigurationError = ConfigurationError; /** * System errors */ class SystemError extends AppError { constructor(message, context) { super(message, 500, 'SYSTEM_ERROR', context, false); } } exports.SystemError = SystemError; class InternalServerError extends SystemError { constructor(message = 'Internal server error', context) { super(message, context); this.code = 'INTERNAL_SERVER_ERROR'; } } exports.InternalServerError = InternalServerError; class RequestTimeoutError extends AppError { constructor(message = 'Request timeout', context) { super(message, 408, 'REQUEST_TIMEOUT_ERROR', context); } } exports.RequestTimeoutError = RequestTimeoutError; /** * Utility functions for error handling */ class ErrorUtils { /** * Check if error is operational (expected) or programming error */ static isOperationalError(error) { if (error instanceof AppError) { return error.isOperational; } return false; } /** * Convert any error to AppError instance */ static toAppError(error) { if (error instanceof AppError) { return error; } if (error instanceof Error) { return new InternalServerError(error.message, { originalError: error.name }); } return new InternalServerError('Unknown error occurred', { originalError: String(error) }); } /** * Check if error should be logged */ static shouldLog(error) { if (error instanceof AppError) { // Don't log validation errors and expected operational errors return !(error instanceof ValidationError || error instanceof NotFoundError); } return true; } /** * Get error message safe for production */ static getPublicMessage(error) { if (error instanceof AppError) { return error.message; } // Don't expose internal error details in production if (process.env.NODE_ENV === 'production') { return 'Internal server error'; } return error.message; } /** * Extract error context for logging */ static getErrorContext(error) { if (error instanceof AppError) { return { name: error.name, code: error.code, status: error.status, context: error.context, timestamp: error.timestamp, isOperational: error.isOperational, }; } return { name: error.name, message: error.message, stack: error.stack, }; } } exports.ErrorUtils = ErrorUtils; /** * Error handler decorator for async functions */ function handleErrors(target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = async function (...args) { try { return await originalMethod.apply(this, args); } catch (error) { throw ErrorUtils.toAppError(error); } }; return descriptor; } /** * Async wrapper for error handling */ function asyncHandler(fn) { return async (...args) => { try { return await fn(...args); } catch (error) { throw ErrorUtils.toAppError(error); } }; }