You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

163 lines
4.2 KiB

/**
* Error Handling Module
*
* Centralized error handling for the DailyNotification plugin
* Maps native exceptions to canonical errors with user-friendly messages
*
* @author Matthew Raymer
* @version 1.0.0
*/
import { ErrorCode, type ErrorInfo } from './bridge'
export class ErrorHandler {
/**
* Map native error to canonical error
*/
mapNativeError(error: unknown): ErrorInfo {
const errorMessage = (error as { message?: string })?.message ||
(error as { toString?: () => string })?.toString?.() ||
'Unknown error'
// Map common error patterns
if (errorMessage.includes('Permission denied')) {
return {
code: ErrorCode.PERMISSION_DENIED,
message: 'Required permission not granted',
hint: 'Request permission in settings'
}
}
if (errorMessage.includes('Channel disabled')) {
return {
code: ErrorCode.CHANNEL_DISABLED,
message: 'Notification channel is disabled',
hint: 'Enable notifications in settings'
}
}
if (errorMessage.includes('Exact alarm')) {
return {
code: ErrorCode.EXACT_ALARM_DENIED,
message: 'Exact alarm permission denied',
hint: 'Grant exact alarm permission in settings'
}
}
if (errorMessage.includes('Doze')) {
return {
code: ErrorCode.DOZE_LIMIT,
message: 'Device in Doze mode',
hint: 'Expect delays; fallback taken'
}
}
if (errorMessage.includes('Invalid time')) {
return {
code: ErrorCode.INVALID_TIME,
message: 'Invalid time format',
hint: 'Use 24-hour HH:mm format'
}
}
if (errorMessage.includes('Title too long')) {
return {
code: ErrorCode.TITLE_TOO_LONG,
message: 'Title exceeds 100 characters',
hint: 'Trim title to 100 characters or less'
}
}
if (errorMessage.includes('Body too long')) {
return {
code: ErrorCode.BODY_TOO_LONG,
message: 'Body exceeds 500 characters',
hint: 'Trim body to 500 characters or less'
}
}
if (errorMessage.includes('Response too large')) {
return {
code: ErrorCode.RESPONSE_TOO_LARGE,
message: 'Response size exceeds limit',
hint: 'Response is too large to process'
}
}
if (errorMessage.includes('Insecure URL')) {
return {
code: ErrorCode.INSECURE_URL,
message: 'Only HTTPS URLs allowed',
hint: 'Use secure HTTPS URLs only'
}
}
if (errorMessage.includes('Schedule blocked')) {
return {
code: ErrorCode.SCHEDULE_BLOCKED,
message: 'Cannot schedule now',
hint: 'Check prerequisites and try again'
}
}
// Default error
return {
code: 'E_UNKNOWN',
message: errorMessage,
hint: 'Check logs for more details'
}
}
/**
* Create user-friendly error message
*/
createUserMessage(error: ErrorInfo): string {
let message = error.message
if (error.hint) {
message += ` (${error.hint})`
}
return message
}
/**
* Log error with context
*/
logError(error: unknown, context = 'DailyNotification') {
console.error(`[${context}] Error:`, error)
if ((error as { stack?: string })?.stack) {
console.error(`[${context}] Stack:`, (error as { stack: string }).stack)
}
}
/**
* Handle plugin method error
*/
handlePluginError(error: unknown, method: string): ErrorInfo {
this.logError(error, `Plugin.${method}`)
return this.mapNativeError(error)
}
}
// Singleton instance
export const errorHandler = new ErrorHandler()
// Utility functions
export function mapNativeError(error: unknown): ErrorInfo {
return errorHandler.mapNativeError(error)
}
export function createUserMessage(error: ErrorInfo): string {
return errorHandler.createUserMessage(error)
}
export function logError(error: unknown, context?: string): void {
errorHandler.logError(error, context)
}
export function handlePluginError(error: unknown, method: string): ErrorInfo {
return errorHandler.handlePluginError(error, method)
}