Files
zui/frontend/src/lib/errorHandling.ts
2026-03-20 00:04:46 +01:00

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
}
}