Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237export type ErrorCategory = | "network" | "not-found" | "forbidden" | "validation" | "server" | "unknown";
export type ErrorDetails = { readonly category: ErrorCategory; readonly message: string; readonly isRetryable: boolean; readonly showToUser: boolean; readonly originalError: unknown;};
type TaggedError = { readonly _tag: string; readonly message?: string;};
function isTaggedError(error: unknown): error is TaggedError { return ( typeof error === "object" && error !== null && "_tag" in error && typeof (error as TaggedError)._tag === "string" );}
function isError(error: unknown): error is Error { return error instanceof Error;}
const NETWORK_ERROR_PATTERNS = [ "network", "fetch", "failed to fetch", "networkerror", "connection", "timeout", "econnrefused", "enotfound", "cors", "abort", "offline",] as const;
const NOT_FOUND_PATTERNS = ["not found", "notfound", "404", "does not exist", "missing"] as const;
const FORBIDDEN_PATTERNS = [ "forbidden", "unauthorized", "401", "403", "permission", "access denied", "not allowed",] as const;
const SERVER_ERROR_PATTERNS = [ "internal server", "500", "502", "503", "504", "server error",] as const;
const VALIDATION_PATTERNS = [ "validation", "invalid", "required", "must be", "cannot be", "expected",] as const;
function matchesPatterns(message: string, patterns: readonly string[]): boolean { const lowerMessage = message.toLowerCase(); return patterns.some((pattern) => lowerMessage.includes(pattern));}
const TAG_TO_CATEGORY: Record<string, ErrorCategory> = { NetworkError: "network", TimeoutError: "network", ConnectionError: "network",
NotFoundError: "not-found", GoalMapNotFoundError: "not-found", AssignmentNotFoundError: "not-found", KitNotFoundError: "not-found", UserNotFoundError: "not-found", ResourceNotFoundError: "not-found",
ForbiddenError: "forbidden", UnauthorizedError: "forbidden", AuthenticationError: "forbidden", PermissionError: "forbidden",
ValidationError: "validation", ParseError: "validation", DecodeError: "validation", SchemaError: "validation",
ServerError: "server", InternalError: "server", DatabaseError: "server",};
export function categorizeError(error: unknown): ErrorCategory { if (isTaggedError(error)) { const tagCategory = TAG_TO_CATEGORY[error._tag]; if (tagCategory) { return tagCategory; }
const message = error.message || error._tag; return categorizeByMessage(message); }
if (isError(error)) { return categorizeByMessage(error.message); }
if (typeof error === "string") { return categorizeByMessage(error); }
if ( typeof error === "object" && error !== null && "error" in error && typeof (error as { error: string }).error === "string" ) { return categorizeByMessage((error as { error: string }).error); }
return "unknown";}
function categorizeByMessage(message: string): ErrorCategory { if (matchesPatterns(message, NETWORK_ERROR_PATTERNS)) { return "network"; } if (matchesPatterns(message, NOT_FOUND_PATTERNS)) { return "not-found"; } if (matchesPatterns(message, FORBIDDEN_PATTERNS)) { return "forbidden"; } if (matchesPatterns(message, SERVER_ERROR_PATTERNS)) { return "server"; } if (matchesPatterns(message, VALIDATION_PATTERNS)) { return "validation"; } return "unknown";}
const DEFAULT_MESSAGES: Record<ErrorCategory, string> = { network: "Unable to connect. Please check your internet connection and try again.", "not-found": "The requested resource could not be found.", forbidden: "You don't have permission to perform this action.", validation: "The provided data is invalid. Please check your input.", server: "Something went wrong on our end. Please try again later.", unknown: "An unexpected error occurred. Please try again.",};
// Only network and server errors are automatically retryablefunction isRetryableCategory(category: ErrorCategory): boolean { return category === "network" || category === "server";}
function extractMessage(error: unknown): string { if (isTaggedError(error)) { return error.message || error._tag; } if (isError(error)) { return error.message; } if (typeof error === "string") { return error; } if ( typeof error === "object" && error !== null && "error" in error && typeof (error as { error: string }).error === "string" ) { return (error as { error: string }).error; } return "An unknown error occurred";}
export function getErrorDetails(error: unknown): ErrorDetails { const category = categorizeError(error); const rawMessage = extractMessage(error);
const isInternalMessage = rawMessage.toLowerCase().includes("internal") || rawMessage === "An unknown error occurred";
const message = isInternalMessage ? DEFAULT_MESSAGES[category] : rawMessage;
return { category, message, isRetryable: isRetryableCategory(category), showToUser: true, originalError: error, };}
export function isNetworkError(error: unknown): boolean { return categorizeError(error) === "network";}
export function isNotFoundError(error: unknown): boolean { return categorizeError(error) === "not-found";}
export function isForbiddenError(error: unknown): boolean { return categorizeError(error) === "forbidden";}
export function isValidationError(error: unknown): boolean { return categorizeError(error) === "validation";}
export function isServerError(error: unknown): boolean { return categorizeError(error) === "server";}
export function isRetryableError(error: unknown): boolean { return getErrorDetails(error).isRetryable;}