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