update
This commit is contained in:
193
ts/errors/index.ts
Normal file
193
ts/errors/index.ts
Normal file
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Platform Service Error System
|
||||
*
|
||||
* This module provides a comprehensive error handling system for the Platform Service,
|
||||
* with structured error types, error codes, and consistent patterns for logging and recovery.
|
||||
*/
|
||||
|
||||
// Export error codes and types
|
||||
export * from './error.codes.js';
|
||||
|
||||
// Export base error classes
|
||||
export * from './base.errors.js';
|
||||
|
||||
// Export domain-specific error classes
|
||||
export * from './email.errors.js';
|
||||
export * from './mta.errors.js';
|
||||
export * from './reputation.errors.js';
|
||||
|
||||
// Export utility function to create specific error types based on the error category
|
||||
import { getErrorClassForCategory } from './base.errors.js';
|
||||
export { getErrorClassForCategory };
|
||||
|
||||
/**
|
||||
* Create a typed error from a standard Error
|
||||
* Useful for converting errors from external libraries or APIs
|
||||
*
|
||||
* @param error Standard error to convert
|
||||
* @param code Error code to assign
|
||||
* @param contextData Additional context data
|
||||
* @returns Typed PlatformError
|
||||
*/
|
||||
export function fromError(
|
||||
error: Error,
|
||||
code: string,
|
||||
contextData: Record<string, any> = {}
|
||||
) {
|
||||
// Import and use PlatformError
|
||||
const { PlatformError } = require('./base.errors.js');
|
||||
const { ErrorSeverity, ErrorCategory, ErrorRecoverability } = require('./error.codes.js');
|
||||
|
||||
return new PlatformError(
|
||||
error.message,
|
||||
code,
|
||||
ErrorSeverity.MEDIUM,
|
||||
ErrorCategory.OPERATION,
|
||||
ErrorRecoverability.NON_RECOVERABLE,
|
||||
{
|
||||
data: {
|
||||
...contextData,
|
||||
originalError: {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an error is retryable
|
||||
*
|
||||
* @param error Error to check
|
||||
* @returns Boolean indicating if the error should be retried
|
||||
*/
|
||||
export function isRetryable(error: any): boolean {
|
||||
// If it's our platform error, use its recoverability property
|
||||
if (error && typeof error === 'object' && 'recoverability' in error) {
|
||||
const { ErrorRecoverability } = require('./error.codes.js');
|
||||
return error.recoverability === ErrorRecoverability.RECOVERABLE ||
|
||||
error.recoverability === ErrorRecoverability.MAYBE_RECOVERABLE ||
|
||||
error.recoverability === ErrorRecoverability.TRANSIENT;
|
||||
}
|
||||
|
||||
// Check if it's a network error (these are often transient)
|
||||
if (error && typeof error === 'object' && error.code) {
|
||||
const networkErrors = [
|
||||
'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EHOSTUNREACH',
|
||||
'ENETUNREACH', 'ENOTFOUND', 'EPROTO', 'ECONNABORTED'
|
||||
];
|
||||
|
||||
return networkErrors.includes(error.code);
|
||||
}
|
||||
|
||||
// By default, we can't determine if the error is retryable
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped version of a function that catches errors
|
||||
* and converts them to typed PlatformErrors
|
||||
*
|
||||
* @param fn Function to wrap
|
||||
* @param errorCode Default error code to use
|
||||
* @param contextData Additional context data
|
||||
* @returns Wrapped function
|
||||
*/
|
||||
export function withErrorHandling<T extends (...args: any[]) => Promise<any>>(
|
||||
fn: T,
|
||||
errorCode: string,
|
||||
contextData: Record<string, any> = {}
|
||||
): T {
|
||||
return (async function(...args: Parameters<T>): Promise<ReturnType<T>> {
|
||||
try {
|
||||
return await fn(...args);
|
||||
} catch (error) {
|
||||
if (error && typeof error === 'object' && 'code' in error) {
|
||||
// Already a typed error, rethrow
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw fromError(
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
errorCode,
|
||||
{
|
||||
...contextData,
|
||||
fnName: fn.name,
|
||||
args: args.map(arg =>
|
||||
typeof arg === 'object'
|
||||
? '[Object]'
|
||||
: String(arg).substring(0, 100)
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry a function with exponential backoff
|
||||
*
|
||||
* @param fn Function to retry
|
||||
* @param options Retry options
|
||||
* @returns Function result or throws after max retries
|
||||
*/
|
||||
export async function retry<T>(
|
||||
fn: () => Promise<T>,
|
||||
options: {
|
||||
maxRetries?: number;
|
||||
initialDelay?: number;
|
||||
maxDelay?: number;
|
||||
backoffFactor?: number;
|
||||
retryableErrors?: Array<string | RegExp>;
|
||||
} = {}
|
||||
): Promise<T> {
|
||||
const {
|
||||
maxRetries = 3,
|
||||
initialDelay = 1000,
|
||||
maxDelay = 30000,
|
||||
backoffFactor = 2,
|
||||
retryableErrors = []
|
||||
} = options;
|
||||
|
||||
let lastError: Error;
|
||||
|
||||
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
lastError = error instanceof Error
|
||||
? error
|
||||
: new Error(String(error));
|
||||
|
||||
// Check if we should retry
|
||||
const shouldRetry = attempt < maxRetries && (
|
||||
isRetryable(error) ||
|
||||
retryableErrors.some(pattern => {
|
||||
if (typeof pattern === 'string') {
|
||||
return lastError.message.includes(pattern);
|
||||
}
|
||||
return pattern.test(lastError.message);
|
||||
})
|
||||
);
|
||||
|
||||
if (!shouldRetry) {
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
// Calculate delay with exponential backoff
|
||||
const delay = Math.min(initialDelay * Math.pow(backoffFactor, attempt), maxDelay);
|
||||
|
||||
// Add jitter to prevent thundering herd problem (±20%)
|
||||
const jitter = 0.8 + Math.random() * 0.4;
|
||||
const actualDelay = Math.floor(delay * jitter);
|
||||
|
||||
// Wait before next retry
|
||||
await new Promise(resolve => setTimeout(resolve, actualDelay));
|
||||
}
|
||||
}
|
||||
|
||||
// This should never happen, but TypeScript needs it
|
||||
throw lastError!;
|
||||
}
|
Reference in New Issue
Block a user