Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/nperez0111/bookhive. Track your books, share your shelves, see what others are reading bookhive.buzz
atproto bluesky books bookshelf goodreads management-system
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156import { NetworkError } from "@/components/NetworkErrorView";
export interface ApiError { message: string; status?: number; code?: string; name?: string;}
export const classifyNetworkError = (error: any): NetworkError => { // Handle fetch errors if (error.name === "TypeError" && error.message.includes("fetch")) { return { type: "network", message: "Unable to connect to the server. Please check your internet connection.", retryable: true, }; }
// Handle timeout errors if (error.name === "AbortError" || error.message.includes("timeout")) { return { type: "timeout", message: "The request timed out. Please try again.", retryable: true, }; }
// Handle HTTP status codes if (error.status) { switch (error.status) { case 401: return { type: "unauthorized", message: "Your session has expired. Please sign in again.", retryable: false, statusCode: 401, }; case 403: return { type: "unauthorized", message: "You do not have permission to access this resource.", retryable: false, statusCode: 403, }; case 404: return { type: "not_found", message: "The requested resource was not found.", retryable: false, statusCode: 404, }; case 408: return { type: "timeout", message: "The request timed out. Please try again.", retryable: true, statusCode: 408, }; case 429: return { type: "server", message: "Too many requests. Please wait a moment and try again.", retryable: true, statusCode: 429, }; case 500: return { type: "server", message: "Internal server error. Please try again later.", retryable: true, statusCode: 500, }; case 502: case 503: case 504: return { type: "server", message: "Service temporarily unavailable. Please try again later.", retryable: true, statusCode: error.status, }; default: return { type: "server", message: `Server error (${error.status}). Please try again.`, retryable: error.status >= 500, statusCode: error.status, }; } }
// Handle network connectivity issues if ( error.message?.includes("Network request failed") || error.message?.includes("Network Error") || error.message?.includes("ERR_NETWORK") ) { return { type: "network", message: "No internet connection. Please check your network settings.", retryable: true, }; }
// Handle DNS resolution errors if (error.message?.includes("ENOTFOUND") || error.message?.includes("DNS")) { return { type: "network", message: "Unable to reach the server. Please check your internet connection.", retryable: true, }; }
// Handle SSL/TLS errors if (error.message?.includes("SSL") || error.message?.includes("TLS")) { return { type: "network", message: "Secure connection failed. Please try again.", retryable: true, }; }
// Default unknown error return { type: "unknown", message: error.message || "An unexpected error occurred. Please try again.", retryable: true, };};
export const isRetryableError = (error: NetworkError): boolean => { return error.retryable;};
export const getRetryDelay = (attempt: number): number => { // Exponential backoff with jitter const baseDelay = Math.min(1000 * Math.pow(2, attempt), 30000); const jitter = Math.random() * 0.1 * baseDelay; return baseDelay + jitter;};
export const shouldRetry = ( error: NetworkError, attempt: number, maxAttempts: number = 3,): boolean => { if (!isRetryableError(error)) return false; if (attempt >= maxAttempts) return false;
// Don't retry authentication errors if (error.type === "unauthorized") return false;
return true;};