platformservice/ts/errors/base.errors.ts

437 lines
9.9 KiB
TypeScript
Raw Normal View History

2025-05-08 12:46:10 +00:00
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<string, any>;
/** 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<string, any> {
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;
}
}