/** * Standardized error classes and utilities for model operations * Provides consistent error handling across all models */ class ModelError extends Error { constructor(message, code = 'MODEL_ERROR', context = {}) { super(message); this.name = 'ModelError'; this.code = code; this.context = context; this.timestamp = new Date().toISOString(); } toJSON() { return { name: this.name, message: this.message, code: this.code, context: this.context, timestamp: this.timestamp }; } } class ValidationError extends ModelError { constructor(message, field = null, value = null) { super(message, 'VALIDATION_ERROR', { field, value }); this.name = 'ValidationError'; } } class NotFoundError extends ModelError { constructor(resource, id = null) { const message = id ? `${resource} with id '${id}' not found` : `${resource} not found`; super(message, 'NOT_FOUND', { resource, id }); this.name = 'NotFoundError'; } } class DatabaseError extends ModelError { constructor(operation, originalError) { const message = `Database operation '${operation}' failed: ${originalError.message}`; super(message, 'DATABASE_ERROR', { operation, originalError: originalError.message, stack: originalError.stack }); this.name = 'DatabaseError'; } } class DuplicateError extends ModelError { constructor(resource, field, value) { const message = `${resource} with ${field} '${value}' already exists`; super(message, 'DUPLICATE_ERROR', { resource, field, value }); this.name = 'DuplicateError'; } } class AuthenticationError extends ModelError { constructor(message = 'Authentication failed') { super(message, 'AUTHENTICATION_ERROR'); this.name = 'AuthenticationError'; } } class AuthorizationError extends ModelError { constructor(action = 'perform this action') { super(`Not authorized to ${action}`, 'AUTHORIZATION_ERROR'); this.name = 'AuthorizationError'; } } /** * Error logging utility with context */ function logModelError(error, operation, model, additionalContext = {}) { const errorInfo = { timestamp: new Date().toISOString(), model, operation, error: { name: error.name, message: error.message, code: error.code, context: error.context }, ...additionalContext }; console.error(`[MODEL_ERROR] ${model}.${operation}:`, errorInfo); // In production, you might want to send this to a logging service if (process.env.NODE_ENV === 'production') { // Production logging logic here console.error('Production error logged:', JSON.stringify(errorInfo, null, 2)); } } /** * Wrapper for async model operations with consistent error handling */ async function withErrorHandling(operation, errorContext) { try { return await operation(); } catch (error) { // Always log the error for debugging logModelError(error, errorContext.operation, errorContext.model, errorContext); // If it's already one of our custom errors, just rethrow if (error instanceof ModelError) { throw error; } // For backward compatibility, rethrow the original error // This ensures existing tests and code continue to work throw error; } } /** * Create error context object for consistent logging */ function createErrorContext(model, operation, additional = {}) { return { model, operation, timestamp: new Date().toISOString(), ...additional }; } module.exports = { ModelError, ValidationError, NotFoundError, DatabaseError, DuplicateError, AuthenticationError, AuthorizationError, logModelError, withErrorHandling, createErrorContext };