/** * Error Mapper - Pattern-based error translation engine * * Converts protocol-specific errors into structured CoachingError instances * using declarative error patterns. Provides a cleaner alternative to scattered * if-else error handling logic throughout adapter code. * * ## Key Features * * - **Declarative patterns**: Define error scenarios upfront, not scattered in code * - **Three matching strategies**: String, RegExp, or custom function * - **Template interpolation**: Insert error properties into messages with {field} syntax * - **Context extraction**: Pull relevant debugging info from error objects * - **Fallback handling**: Graceful defaults when no pattern matches * * ## Design Benefits * * 1. **Visibility**: All error scenarios visible at a glance * 2. **Maintainability**: Add new patterns without touching control flow * 3. **Consistency**: All errors transformed through same pipeline * 4. **Testability**: Easy to test patterns in isolation * * ## Usage Pattern * * 1. Create mapper with defaults * 2. Add patterns for known errors * 3. Use in try-catch blocks to translate errors * * @example Basic usage * ```typescript * const mapper = new ErrorMapper( * [ * { * match: "ECONNREFUSED", * code: "CONNECTION_FAILED", * hint: "Server is not reachable", * fix: "Check server is running" * } * ], * { * defaultCode: "UNKNOWN_ERROR", * defaultHint: "An unexpected error occurred" * } * ); * * try { * await connect(); * } catch (error) { * throw mapper.map(error, { operation: "connect" }); * } * ``` * * @example Regex matching with template * ```typescript * mapper.addPattern({ * match: /timeout|timed out/i, * code: "OPERATION_TIMEOUT", * messageTemplate: "Operation timed out: {message}", * hint: "The operation took too long", * fix: "Increase timeout in configuration" * }); * ``` * * @example Custom predicate with context extraction * ```typescript * mapper.addPattern({ * match: (error) => { * return error.code === "ENOTFOUND" || error.code === "ECONNREFUSED"; * }, * code: "ADAPTER_CONNECTION_FAILED", * hint: "Network connection failed", * fix: "Check network connectivity and server address", * extractContext: (error) => ({ * code: (error as any).code, * syscall: (error as any).syscall, * hostname: (error as any).hostname * }) * }); * ``` * * @module @wavespec/kit/error */ import type { ErrorPattern, ErrorMapperConfig, ErrorContext } from './types.js'; import { CoachingError } from '@wavespec/types'; /** * ErrorMapper - Pattern-based error translation * * Translates protocol-specific errors to CoachingErrors using declarative patterns. * Patterns are matched in order (first match wins), so arrange specific patterns * before generic ones. * * The mapper follows a simple pipeline: * 1. Normalize the error to an Error object * 2. Try each pattern in order until one matches * 3. Build CoachingError from the matched pattern * 4. Fall back to default error if no pattern matches * * @example Simple string matching * ```typescript * const mapper = new ErrorMapper([], { * defaultCode: "UNKNOWN", * defaultHint: "Unexpected error" * }); * * mapper.addPattern({ * match: "ECONNREFUSED", * code: "CONNECTION_FAILED", * hint: "Server refused connection", * fix: "Verify server is running" * }); * * const coachingError = mapper.map( * new Error("connect ECONNREFUSED 127.0.0.1:8080"), * { operation: "connect", port: 8080 } * ); * ``` * * @example Regex matching with severity * ```typescript * mapper.addPattern({ * match: /deprecated|deprecation/i, * code: "DEPRECATED_FEATURE", * hint: "You're using a deprecated feature", * fix: "Migrate to the recommended alternative", * severity: "warning" * }); * ``` */ export declare class ErrorMapper { private readonly patterns; private readonly config; /** * Create a new ErrorMapper * * @param patterns - Initial error patterns (optional, can add more later) * @param config - Mapper configuration with defaults for unmatched errors * * @example With initial patterns * ```typescript * const mapper = new ErrorMapper( * [ * { match: "ECONNREFUSED", code: "CONNECTION_FAILED", hint: "..." } * ], * { defaultCode: "UNKNOWN", defaultHint: "..." } * ); * ``` * * @example Empty mapper (add patterns later) * ```typescript * const mapper = new ErrorMapper([], { defaultCode: "UNKNOWN", defaultHint: "..." }); * mapper.addPattern({ ... }); * ``` */ constructor(patterns: ErrorPattern[], config?: ErrorMapperConfig); /** * Add a single error pattern * * Patterns are matched in the order they're added. Add more specific * patterns first, then generic patterns as fallbacks. * * @param pattern - Error pattern to add * @returns this (for method chaining) * * @example Chaining patterns * ```typescript * mapper * .addPattern({ match: "ECONNREFUSED", code: "CONNECTION_REFUSED", ... }) * .addPattern({ match: "ENOTFOUND", code: "HOST_NOT_FOUND", ... }) * .addPattern({ match: /timeout/i, code: "TIMEOUT", ... }); * ``` */ addPattern(pattern: ErrorPattern): this; /** * Add multiple error patterns at once * * Convenience method for bulk pattern registration. * Patterns are added in array order. * * @param patterns - Array of error patterns to add * @returns this (for method chaining) * * @example Bulk pattern registration * ```typescript * const networkPatterns: ErrorPattern[] = [ * { match: "ECONNREFUSED", code: "CONNECTION_REFUSED", ... }, * { match: "ENOTFOUND", code: "HOST_NOT_FOUND", ... }, * { match: "ETIMEDOUT", code: "TIMEOUT", ... } * ]; * * mapper.addPatterns(networkPatterns); * ``` */ addPatterns(patterns: ErrorPattern[]): this; /** * Map an error to CoachingError using patterns * * This is the main entry point for error translation. It: * 1. Normalizes the error to an Error object * 2. Tries each pattern in order until one matches * 3. Builds a CoachingError from the matched pattern * 4. Falls back to default error if no pattern matches * * The returned CoachingError includes: * - Structured error code for programmatic handling * - Human-readable message (optionally interpolated) * - Hint explaining what went wrong * - Fix suggestion for how to resolve it * - Context combining provided context + extracted context * - Recovery strategy (if pattern defines one) * * @param error - The error to translate (Error, string, or unknown) * @param context - Additional context about the error (operation, entity names, etc.) * @returns CoachingError with actionable guidance * * @example Basic translation * ```typescript * try { * await client.connect(); * } catch (error) { * throw mapper.map(error, { operation: "connect" }); * } * ``` * * @example With entity context * ```typescript * try { * await client.callTool("weather", args); * } catch (error) { * throw mapper.map(error, { * operation: "call_tool", * toolName: "weather" * }); * } * ``` */ map(error: unknown, context?: ErrorContext): CoachingError; /** * Check if an error matches a pattern * * Supports three matching strategies: * 1. String: Case-insensitive substring match in error.message * 2. RegExp: Pattern match in error.message * 3. Function: Custom predicate (error: Error) => boolean * * @param error - The error to check * @param pattern - The pattern to match against * @returns true if the error matches the pattern */ private matchesPattern; /** * Build CoachingError from a matched pattern * * Constructs the final CoachingError by: * 1. Using pattern's messageTemplate (with interpolation) or original error.message * 2. Extracting context from error if pattern defines extractContext * 3. Merging provided context + extracted context + original error info * 4. Including all pattern metadata (code, hint, fix, severity, etc.) * * @param error - The original error * @param pattern - The matched pattern * @param context - Provided context from caller * @returns CoachingError with complete error information */ private buildError; /** * Build default error when no pattern matches * * Falls back to configured defaults when the error doesn't match * any pattern. This ensures we always return a structured error, * even for unexpected error scenarios. * * @param error - The original error * @param context - Provided context from caller * @returns CoachingError with default values */ private buildDefaultError; /** * Interpolate template string with error properties * * Replaces {field} placeholders with values from the error object. * Common fields: * - {message}: error.message * - {name}: error.name * - {code}: error.code (for network errors) * - {syscall}: error.syscall * - Any custom property on the error object * * If a field doesn't exist, the placeholder is left as-is: {field} * * @param template - Template string with {field} placeholders * @param error - Error object with properties to interpolate * @returns Interpolated string * * @example * ```typescript * interpolate("Connection failed: {message}", error) * // => "Connection failed: ECONNREFUSED" * ``` */ private interpolate; /** * Normalize unknown error to Error object * * Handles various error formats: * - Error instances: Use as-is * - Strings: Wrap in Error * - Objects with message: Convert to Error * - Other: Convert to string, then wrap in Error * * This ensures we always have a consistent Error object to work with, * regardless of what was thrown. * * @param error - Error in any format * @returns Normalized Error object */ private normalizeError; } //# sourceMappingURL=mapper.d.ts.map