/**
* Custom error types for MCP Server ROI
*/
export abstract class BaseError extends Error {
public readonly code: string;
public readonly statusCode: number;
public readonly isOperational: boolean;
public readonly context?: Record<string, any>;
constructor(
message: string,
code: string,
statusCode: number,
isOperational: boolean = true,
context?: Record<string, any>
) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.statusCode = statusCode;
this.isOperational = isOperational;
this.context = context;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* Validation errors for input data
*/
export class ValidationError extends BaseError {
constructor(message: string, context?: Record<string, any>) {
super(message, 'VALIDATION_ERROR', 400, true, context);
}
}
/**
* Database-related errors
*/
export class DatabaseError extends BaseError {
constructor(message: string, context?: Record<string, any>) {
super(message, 'DATABASE_ERROR', 500, false, context);
}
}
/**
* Worker thread errors
*/
export class WorkerError extends BaseError {
constructor(message: string, context?: Record<string, any>) {
super(message, 'WORKER_ERROR', 500, false, context);
}
}
/**
* Calculation errors (e.g., IRR convergence failure)
*/
export class CalculationError extends BaseError {
constructor(message: string, context?: Record<string, any>) {
super(message, 'CALCULATION_ERROR', 422, true, context);
}
}
/**
* Configuration errors
*/
export class ConfigurationError extends BaseError {
constructor(message: string, context?: Record<string, any>) {
super(message, 'CONFIGURATION_ERROR', 500, false, context);
}
}
/**
* Resource not found errors
*/
export class NotFoundError extends BaseError {
constructor(resource: string, identifier: string | number) {
super(
`${resource} not found: ${identifier}`,
'NOT_FOUND',
404,
true,
{ resource, identifier }
);
}
}
/**
* Rate limiting errors
*/
export class RateLimitError extends BaseError {
constructor(limit: number, window: string) {
super(
`Rate limit exceeded: ${limit} requests per ${window}`,
'RATE_LIMIT_EXCEEDED',
429,
true,
{ limit, window }
);
}
}
/**
* Timeout errors
*/
export class TimeoutError extends BaseError {
constructor(operation: string, timeoutMs: number) {
super(
`Operation timed out: ${operation} (${timeoutMs}ms)`,
'TIMEOUT_ERROR',
408,
true,
{ operation, timeoutMs }
);
}
}
/**
* Input validation specific errors
*/
export class InputValidationError extends ValidationError {
constructor(field: string, value: any, reason: string) {
super(
`Invalid input for field '${field}': ${reason}`,
{ field, value, reason }
);
}
}
/**
* Range validation errors
*/
export class RangeError extends ValidationError {
constructor(field: string, value: number, min?: number, max?: number) {
const constraints: string[] = [];
if (min !== undefined) constraints.push(`min: ${min}`);
if (max !== undefined) constraints.push(`max: ${max}`);
super(
`Value ${value} for field '${field}' is out of range (${constraints.join(', ')})`,
{ field, value, min, max }
);
}
}
/**
* Type guard to check if an error is operational
*/
export function isOperationalError(error: Error): error is BaseError {
return error instanceof BaseError && error.isOperational;
}
/**
* Error serializer for API responses
*/
export function serializeError(error: Error): {
name: string;
message: string;
code?: string;
statusCode?: number;
context?: Record<string, any>;
stack?: string;
} {
const serialized: any = {
name: error.name,
message: error.message
};
if (error instanceof BaseError) {
serialized.code = error.code;
serialized.statusCode = error.statusCode;
serialized.context = error.context;
}
// Include stack trace in development
if (process.env.NODE_ENV !== 'production') {
serialized.stack = error.stack;
}
return serialized;
}