import { ErrorSeverity, ErrorCategory, ErrorRecoverability } from './error.codes.js'; import { logger } from '../logger.js'; // Import TLogLevel from plugins import type { TLogLevel } from '../plugins.js'; /** * Context information added to structured errors */ export interface IErrorContext { /** Component or service where the error occurred */ component?: string; /** Operation that was being performed */ operation?: string; /** Unique request ID if available */ requestId?: string; /** Error occurred at timestamp */ timestamp?: number; /** User-visible message (safe to display to end-users) */ userMessage?: string; /** Additional structured data for debugging */ data?: Record; /** Related entity IDs if applicable */ entity?: { type: string; id: string | number; }; /** Stack trace (if enabled in configuration) */ stack?: string; /** Retry information if applicable */ retry?: { /** Maximum number of retries allowed */ maxRetries?: number; /** Current retry count */ currentRetry?: number; /** Next retry timestamp */ nextRetryAt?: number; /** Delay between retries (in ms) */ retryDelay?: number; }; } /** * Base class for all errors in the Platform Service * Adds structured error information, logging, and error tracking */ export class PlatformError extends Error { /** Error code identifying the specific error type */ public readonly code: string; /** Error severity level */ public readonly severity: ErrorSeverity; /** Error category for grouping related errors */ public readonly category: ErrorCategory; /** Whether the error can be recovered from automatically */ public readonly recoverability: ErrorRecoverability; /** Additional context information */ public readonly context: IErrorContext; /** * Creates a new PlatformError * * @param message Error message * @param code Error code from error.codes.ts * @param severity Error severity level * @param category Error category * @param recoverability Error recoverability indication * @param context Additional context information */ constructor( message: string, code: string, severity: ErrorSeverity = ErrorSeverity.MEDIUM, category: ErrorCategory = ErrorCategory.OTHER, recoverability: ErrorRecoverability = ErrorRecoverability.NON_RECOVERABLE, context: IErrorContext = {} ) { super(message); // Set error metadata this.name = this.constructor.name; this.code = code; this.severity = severity; this.category = category; this.recoverability = recoverability; // Add timestamp if not provided this.context = { ...context, timestamp: context.timestamp || Date.now(), }; // Capture stack trace Error.captureStackTrace(this, this.constructor); // Log the error automatically unless explicitly disabled if (!context.data?.skipLogging) { this.logError(); } } /** * Logs the error using the platform logger */ private logError(): void { const logLevel = this.getLogLevelFromSeverity() as TLogLevel; // Construct structured log entry const logData = { error_code: this.code, error_name: this.name, severity: this.severity, category: this.category, recoverability: this.recoverability, ...this.context }; // Log with appropriate level logger.log(logLevel, this.message, logData); } /** * Maps severity levels to log levels */ private getLogLevelFromSeverity(): string { switch (this.severity) { case ErrorSeverity.CRITICAL: case ErrorSeverity.HIGH: return 'error'; case ErrorSeverity.MEDIUM: return 'warn'; case ErrorSeverity.LOW: return 'info'; case ErrorSeverity.INFO: return 'debug'; default: return 'error'; } } /** * Returns a JSON representation of the error */ public toJSON(): Record { return { name: this.name, message: this.message, code: this.code, severity: this.severity, category: this.category, recoverability: this.recoverability, context: this.context, stack: process.env.NODE_ENV !== 'production' ? this.stack : undefined }; } /** * Creates an instance with retry information * * @param maxRetries Maximum number of retries * @param currentRetry Current retry count * @param retryDelay Delay between retries in ms */ public withRetry( maxRetries: number, currentRetry: number = 0, retryDelay: number = 1000 ): PlatformError { const nextRetryAt = Date.now() + retryDelay; // Create a new instance with the same parameters but updated context return new (this.constructor as typeof PlatformError)( this.message, this.code, this.severity, this.category, // If we can retry, the error is at least maybe recoverable currentRetry < maxRetries ? ErrorRecoverability.MAYBE_RECOVERABLE : this.recoverability, { ...this.context, retry: { maxRetries, currentRetry, nextRetryAt, retryDelay } } ); } /** * Checks if the error should be retried based on retry information */ public shouldRetry(): boolean { const { retry } = this.context; if (!retry) return false; return retry.currentRetry < retry.maxRetries; } /** * Returns a user-friendly message that is safe to display to end users */ public getUserMessage(): string { return this.context.userMessage || 'An unexpected error occurred.'; } } /** * Error class for validation errors */ export class ValidationError extends PlatformError { /** * Creates a new validation error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.LOW, ErrorCategory.VALIDATION, ErrorRecoverability.NON_RECOVERABLE, context ); } } /** * Error class for configuration errors */ export class ConfigurationError extends PlatformError { /** * Creates a new configuration error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.MEDIUM, ErrorCategory.CONFIGURATION, ErrorRecoverability.NON_RECOVERABLE, context ); } } /** * Error class for network-related errors */ export class NetworkError extends PlatformError { /** * Creates a new network error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.MEDIUM, ErrorCategory.CONNECTIVITY, ErrorRecoverability.MAYBE_RECOVERABLE, context ); } } /** * Error class for resource availability errors (rate limits, quotas) */ export class ResourceError extends PlatformError { /** * Creates a new resource error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.MEDIUM, ErrorCategory.RESOURCE, ErrorRecoverability.MAYBE_RECOVERABLE, context ); } } /** * Error class for authentication/authorization errors */ export class AuthenticationError extends PlatformError { /** * Creates a new authentication error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.HIGH, ErrorCategory.AUTHENTICATION, ErrorRecoverability.NON_RECOVERABLE, context ); } } /** * Error class for operation errors (API calls, processing) */ export class OperationError extends PlatformError { /** * Creates a new operation error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.MEDIUM, ErrorCategory.OPERATION, ErrorRecoverability.MAYBE_RECOVERABLE, context ); } } /** * Error class for critical system errors */ export class SystemError extends PlatformError { /** * Creates a new system error * * @param message Error message * @param code Error code * @param context Additional context */ constructor( message: string, code: string, context: IErrorContext = {} ) { super( message, code, ErrorSeverity.CRITICAL, ErrorCategory.OTHER, ErrorRecoverability.NON_RECOVERABLE, context ); } } /** * Helper to get the appropriate error class based on error category * * @param category Error category * @returns The appropriate error class */ export function getErrorClassForCategory(category: ErrorCategory): any { switch (category) { case ErrorCategory.VALIDATION: return ValidationError; case ErrorCategory.CONFIGURATION: return ConfigurationError; case ErrorCategory.CONNECTIVITY: return NetworkError; case ErrorCategory.RESOURCE: return ResourceError; case ErrorCategory.AUTHENTICATION: return AuthenticationError; case ErrorCategory.OPERATION: return OperationError; default: return PlatformError; } }