156 lines
3.5 KiB
TypeScript
156 lines
3.5 KiB
TypeScript
/**
|
|
* Error handling utilities for consistent error management.
|
|
* Provides centralized error handling logic across the application.
|
|
*/
|
|
|
|
/**
|
|
* Error kind for categorizing errors
|
|
*/
|
|
export type ErrorKind =
|
|
| 'render'
|
|
| 'network'
|
|
| 'validation'
|
|
| 'authentication'
|
|
| 'authorization'
|
|
| 'notFound'
|
|
| 'server'
|
|
| 'unknown'
|
|
|
|
/**
|
|
* Error object with kind and message
|
|
*/
|
|
export type AppError = {
|
|
kind: ErrorKind
|
|
message: string
|
|
details?: Record<string, unknown>
|
|
timestamp: number
|
|
}
|
|
|
|
/**
|
|
* Create an error object with consistent structure
|
|
*/
|
|
export function createAppError(
|
|
kind: ErrorKind,
|
|
message: string,
|
|
details?: Record<string, unknown>
|
|
): AppError {
|
|
return {
|
|
kind,
|
|
message,
|
|
details,
|
|
timestamp: Date.now(),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract error message from unknown error
|
|
*/
|
|
export function getErrorMessage(error: unknown): string {
|
|
if (error instanceof Error) {
|
|
return error.message
|
|
}
|
|
if (typeof error === 'string') {
|
|
return error
|
|
}
|
|
if (typeof error === 'object' && error !== null && 'message' in error) {
|
|
return String(error.message)
|
|
}
|
|
return 'An unknown error occurred'
|
|
}
|
|
|
|
/**
|
|
* Extract error kind from error
|
|
*/
|
|
export function getErrorKind(error: unknown): ErrorKind {
|
|
if (error instanceof Error) {
|
|
const message = error.message.toLowerCase()
|
|
if (message.includes('network')) return 'network'
|
|
if (message.includes('validation') || message.includes('invalid')) return 'validation'
|
|
if (message.includes('auth')) return 'authentication'
|
|
if (message.includes('forbidden') || message.includes('unauthorized')) return 'authorization'
|
|
if (message.includes('not found') || message.includes('404')) return 'notFound'
|
|
if (message.includes('server') || message.includes('500')) return 'server'
|
|
}
|
|
return 'unknown'
|
|
}
|
|
|
|
/**
|
|
* Format error for display
|
|
*/
|
|
export function formatError(error: AppError | Error | string): string {
|
|
if (typeof error === 'string') {
|
|
return error
|
|
}
|
|
if (error instanceof Error) {
|
|
return error.message
|
|
}
|
|
return error.message
|
|
}
|
|
|
|
/**
|
|
* Check if error is recoverable
|
|
*/
|
|
export function isRecoverableError(error: AppError | Error | string): boolean {
|
|
if (typeof error === 'string') return false
|
|
if (error instanceof Error) {
|
|
const message = error.message.toLowerCase()
|
|
return ['network', 'timeout', 'connection'].some((term) => message.includes(term))
|
|
}
|
|
// error is AppError at this point
|
|
return ['network', 'server'].includes((error as AppError).kind)
|
|
}
|
|
|
|
/**
|
|
* Update error state consistently
|
|
*/
|
|
export function updateErrorState<T extends { error?: unknown }>(
|
|
state: T,
|
|
setError: (error: null | { kind: string; message: string }) => void,
|
|
error: unknown
|
|
): void {
|
|
setError({
|
|
kind: 'render',
|
|
message: getErrorMessage(error),
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Clear error state
|
|
*/
|
|
export function clearErrorState<T extends { error?: unknown }>(
|
|
state: T,
|
|
setError: (error: null) => void
|
|
): void {
|
|
setError(null)
|
|
}
|
|
|
|
/**
|
|
* Handle error with consistent logging
|
|
*/
|
|
export function handleError(
|
|
error: unknown,
|
|
context?: Record<string, unknown>
|
|
): AppError {
|
|
const appError = createAppError(
|
|
getErrorKind(error),
|
|
getErrorMessage(error),
|
|
context
|
|
)
|
|
|
|
console.error('[Error]', appError, context)
|
|
return appError
|
|
}
|
|
|
|
/**
|
|
* Create error handler for async functions
|
|
*/
|
|
export function createErrorHandler<T>(
|
|
onError?: (error: AppError) => void
|
|
): (error: unknown) => T {
|
|
return (error: unknown) => {
|
|
const appError = handleError(error)
|
|
onError?.(appError)
|
|
throw error
|
|
}
|
|
}
|