UNPKG

@aurite-ai/api-client

Version:

Production-ready TypeScript client for the Aurite Framework API with comprehensive error handling and retry logic

278 lines 8.89 kB
"use strict"; /** * Error handling utilities for UI applications * * This module provides utilities for handling API errors in user interfaces, * including error categorization, user-friendly messaging, and retry logic. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.globalErrorBoundary = exports.ErrorBoundary = exports.RetryHandler = void 0; exports.createErrorNotification = createErrorNotification; exports.getErrorColor = getErrorColor; exports.getErrorIcon = getErrorIcon; exports.logError = logError; const types_1 = require("../types"); /** * Default error display configuration */ const DEFAULT_ERROR_CONFIG = { showTechnicalDetails: false, showRetryButton: true, autoDismiss: { enabled: true, categories: [types_1.ErrorCategory.VALIDATION, types_1.ErrorCategory.NOT_FOUND], delay: 5000, }, }; /** * Converts an API error to a user-friendly notification */ function createErrorNotification(error, config = DEFAULT_ERROR_CONFIG) { // Generate unique ID const id = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Handle non-API errors if (!(error instanceof types_1.ApiError) && !(error instanceof types_1.TimeoutError) && !(error instanceof types_1.CancellationError)) { return { id, title: 'Unexpected Error', message: 'An unexpected error occurred. Please try again.', severity: types_1.ErrorSeverity.ERROR, category: types_1.ErrorCategory.UNKNOWN, retryable: false, autoDismiss: false, technicalDetails: config.showTechnicalDetails ? { message: error.message } : undefined, originalError: new types_1.ApiError(error.message || 'Unknown error', 0), }; } const apiError = error; // Get custom message if configured const customMessage = config.customMessages?.[apiError.category]; const message = customMessage || apiError.getDisplayMessage(); // Determine if should auto-dismiss const shouldAutoDismiss = config.autoDismiss?.enabled && config.autoDismiss.categories.includes(apiError.category); return { id, title: getErrorTitle(apiError.category), message, severity: apiError.severity, category: apiError.category, retryable: apiError.retryable && Boolean(config.showRetryButton ?? true), retryDelay: apiError.retryDelay, autoDismiss: shouldAutoDismiss ?? false, autoDismissDelay: shouldAutoDismiss ? config.autoDismiss?.delay : undefined, technicalDetails: config.showTechnicalDetails ? apiError.toJSON() : undefined, originalError: apiError, }; } /** * Gets appropriate title for error category */ function getErrorTitle(category) { switch (category) { case types_1.ErrorCategory.NETWORK: return 'Connection Error'; case types_1.ErrorCategory.AUTH: return 'Authentication Error'; case types_1.ErrorCategory.VALIDATION: return 'Validation Error'; case types_1.ErrorCategory.SERVER: return 'Server Error'; case types_1.ErrorCategory.TIMEOUT: return 'Request Timeout'; case types_1.ErrorCategory.CANCELLED: return 'Request Cancelled'; case types_1.ErrorCategory.RATE_LIMIT: return 'Rate Limit Exceeded'; case types_1.ErrorCategory.NOT_FOUND: return 'Not Found'; default: return 'Error'; } } /** * Determines the appropriate UI color/theme for an error severity */ function getErrorColor(severity) { switch (severity) { case types_1.ErrorSeverity.INFO: return 'blue'; case types_1.ErrorSeverity.WARNING: return 'yellow'; case types_1.ErrorSeverity.ERROR: return 'red'; case types_1.ErrorSeverity.CRITICAL: return 'red'; default: return 'gray'; } } /** * Determines the appropriate icon for an error category */ function getErrorIcon(category) { switch (category) { case types_1.ErrorCategory.NETWORK: return '🌐'; case types_1.ErrorCategory.AUTH: return '🔒'; case types_1.ErrorCategory.VALIDATION: return '⚠️'; case types_1.ErrorCategory.SERVER: return '🔧'; case types_1.ErrorCategory.TIMEOUT: return '⏱️'; case types_1.ErrorCategory.CANCELLED: return '❌'; case types_1.ErrorCategory.RATE_LIMIT: return '🚦'; case types_1.ErrorCategory.NOT_FOUND: return '🔍'; default: return '❗'; } } /** * Retry handler with exponential backoff */ class RetryHandler { constructor(maxRetries = 3, baseDelay = 1000) { this.retryCount = 0; this.maxRetries = maxRetries; this.baseDelay = baseDelay; } /** * Executes a function with retry logic */ async execute(fn, shouldRetry) { while (this.retryCount <= this.maxRetries) { try { const result = await fn(); this.retryCount = 0; // Reset on success return result; } catch (error) { this.retryCount++; // Check if we should retry const canRetry = this.retryCount <= this.maxRetries; const errorAllowsRetry = error instanceof types_1.ApiError ? error.shouldRetry() : false; const customRetryLogic = shouldRetry ? shouldRetry(error) : true; if (!canRetry || !errorAllowsRetry || !customRetryLogic) { this.retryCount = 0; // Reset for next use throw error; } // Calculate delay with exponential backoff const delay = error instanceof types_1.ApiError ? error.getRetryDelay() : this.baseDelay * Math.pow(2, this.retryCount - 1); await new Promise(resolve => setTimeout(resolve, delay)); } } throw new Error('Maximum retries exceeded'); } /** * Resets the retry counter */ reset() { this.retryCount = 0; } /** * Gets current retry count */ getCurrentRetryCount() { return this.retryCount; } } exports.RetryHandler = RetryHandler; /** * Error boundary helper for React-like error handling */ class ErrorBoundary { constructor() { this.errorHandlers = []; } /** * Adds an error handler */ addErrorHandler(handler) { this.errorHandlers.push(handler); } /** * Removes an error handler */ removeErrorHandler(handler) { const index = this.errorHandlers.indexOf(handler); if (index > -1) { this.errorHandlers.splice(index, 1); } } /** * Handles an error by calling all registered handlers */ handleError(error, errorInfo) { this.errorHandlers.forEach(handler => { try { handler(error, errorInfo); } catch (e) { // eslint-disable-next-line no-console console.error('Error in error handler:', e); } }); } /** * Wraps a function to catch and handle errors */ wrap(fn) { return ((...args) => { try { const result = fn(...args); // Handle async functions if (result instanceof Promise) { return result.catch(e => { this.handleError(e); throw e; }); } return result; } catch (e) { this.handleError(e); throw e; } }); } } exports.ErrorBoundary = ErrorBoundary; /** * Logs errors with structured information for debugging */ function logError(error, context) { const timestamp = new Date().toISOString(); if (error instanceof types_1.ApiError) { // eslint-disable-next-line no-console console.error('API Error:', { timestamp, context, error: error.toJSON(), }); } else { // eslint-disable-next-line no-console console.error('API Error:', { timestamp, context, error: { name: error.name, message: error.message, stack: error.stack, }, }); } } /** * Global error boundary instance */ exports.globalErrorBoundary = new ErrorBoundary(); //# sourceMappingURL=errorHandling.js.map