/** * @module @dotdo/postgres-shared/error-utils * * Lightweight error message extraction utilities. * Use these for simple error message handling without creating PostgresError objects. * * For full error handling with codes, context, and retryability, use the errors module. */ /** * Type guard to check if a value is an Error instance. * * @param value - The value to check * @returns True if the value is an Error instance * * @example * ```typescript * try { * await riskyOperation() * } catch (error) { * if (isError(error)) { * console.log(error.message) // TypeScript knows error is Error * console.log(error.stack) * } * } * ``` */ export function isError(value: unknown): value is Error { return value instanceof Error } /** * Extract an error message from any value, including nested errors. * * Handles various error types consistently: * - Error instances: returns error.message (optionally includes nested cause) * - Strings: returns the string directly * - Objects with message property: returns the message as string * - Everything else: converts to string * * @param error - The value to extract a message from * @param options - Optional configuration * @param options.includeNested - Whether to include nested error causes (default: false) * @param options.maxDepth - Maximum depth for nested errors (default: 3) * @returns The extracted error message * * @example * ```typescript * try { * await riskyOperation() * } catch (error) { * // Simple extraction * console.log(extractErrorMessage(error)) * * // With nested errors * console.log(extractErrorMessage(error, { includeNested: true })) * // "Connection failed: DNS lookup failed: Network unreachable" * } * ``` */ export function extractErrorMessage( error: unknown, options: { includeNested?: boolean; maxDepth?: number } = {} ): string { const { includeNested = false, maxDepth = 3 } = options // Get the base message let message: string if (error instanceof Error) { message = error.message } else if (typeof error === 'string') { message = error } else if (error && typeof error === 'object' && 'message' in error) { message = String((error as { message: unknown }).message) } else { message = String(error) } // Handle nested errors if requested if (includeNested && error instanceof Error && error.cause && maxDepth > 0) { const nestedMessage = extractErrorMessage(error.cause, { includeNested: true, maxDepth: maxDepth - 1, }) if (nestedMessage && nestedMessage !== message) { return `${message}: ${nestedMessage}` } } return message } /** * Extract an error message from any value. * * Handles various error types consistently: * - Error instances: returns error.message * - Strings: returns the string directly * - Objects with message property: returns the message as string * - Everything else: converts to string * * @example * ```typescript * try { * await riskyOperation() * } catch (error) { * console.log(formatError(error)) // Always returns a string * } * ``` */ export function formatError(error: unknown): string { if (error instanceof Error) return error.message if (typeof error === 'string') return error if (error && typeof error === 'object' && 'message' in error) { return String((error as { message: unknown }).message) } return String(error) } /** * Wrap any value as an Error instance with optional context. * * Unlike the wrapError in errors.ts which creates PostgresError instances, * this creates plain Error objects for simpler use cases. * * @param error - The value to wrap * @param context - Optional context to prepend to the error message * @returns An Error instance * * @example * ```typescript * try { * await fetchData() * } catch (error) { * throw asError(error, 'Failed to fetch data') * // Error: "Failed to fetch data: Connection refused" * } * ``` */ export function asError(error: unknown, context?: string): Error { const message = formatError(error) return new Error(context ? `${context}: ${message}` : message) } /** * Wrap any value as an Error instance with required context. * * This function is similar to asError but: * - Context is required (not optional) * - Preserves the original error as the cause for debugging * - Uses extractErrorMessage for better nested error handling * * @param error - The value to wrap * @param context - Context to prepend to the error message (required) * @returns An Error instance with the original error as cause * * @example * ```typescript * try { * await fetchData() * } catch (error) { * throw wrapError(error, 'Failed to fetch data') * // Error: "Failed to fetch data: Connection refused" * // with error.cause set to original error * } * ``` */ export function wrapError(error: unknown, context: string): Error { const message = extractErrorMessage(error) const wrappedError = new Error(`${context}: ${message}`) // Preserve the original error as the cause for debugging wrappedError.cause = error return wrappedError }