@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
JavaScript
;
/**
* 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