UNPKG

murmuraba

Version:

Real-time audio noise reduction with advanced chunked processing for web applications

210 lines (209 loc) 10.1 kB
/** * Centralized Error Handling Utility * Eliminates duplicate error creation patterns across the codebase */ // Error Types export var ErrorType; (function (ErrorType) { ErrorType["AUDIO_CONTEXT"] = "AUDIO_CONTEXT_ERROR"; ErrorType["MEDIA_RECORDER"] = "MEDIA_RECORDER_ERROR"; ErrorType["WASM_MODULE"] = "WASM_MODULE_ERROR"; ErrorType["AUDIO_PROCESSING"] = "AUDIO_PROCESSING_ERROR"; ErrorType["VALIDATION"] = "VALIDATION_ERROR"; ErrorType["INITIALIZATION"] = "INITIALIZATION_ERROR"; ErrorType["NETWORK"] = "NETWORK_ERROR"; ErrorType["FILE_SYSTEM"] = "FILE_SYSTEM_ERROR"; ErrorType["PERMISSION"] = "PERMISSION_ERROR"; ErrorType["UNSUPPORTED"] = "UNSUPPORTED_ERROR"; })(ErrorType || (ErrorType = {})); // Base Error Class export class MurmurabaError extends Error { constructor(type, message, code, details) { super(message); this.name = 'MurmurabaError'; this.type = type; this.code = code || type; this.details = details; this.timestamp = Date.now(); // Maintain proper stack trace if (Error.captureStackTrace) { Error.captureStackTrace(this, MurmurabaError); } } toJSON() { return { name: this.name, type: this.type, code: this.code, message: this.message, details: this.details, timestamp: this.timestamp, stack: this.stack, }; } } // Error Factory Class export class ErrorFactory { // Audio Context Errors static audioContextNotSupported(details) { return new MurmurabaError(ErrorType.AUDIO_CONTEXT, 'AudioContext is not supported in this browser', 'AUDIO_CONTEXT_NOT_SUPPORTED', details); } static audioContextSuspended(details) { return new MurmurabaError(ErrorType.AUDIO_CONTEXT, 'AudioContext is suspended. User interaction required.', 'AUDIO_CONTEXT_SUSPENDED', details); } static audioContextCreationFailed(originalError, details) { return new MurmurabaError(ErrorType.AUDIO_CONTEXT, `Failed to create AudioContext: ${originalError?.message || 'Unknown error'}`, 'AUDIO_CONTEXT_CREATION_FAILED', { ...details, originalError: originalError?.message }); } // Media Recorder Errors static mediaRecorderNotSupported(details) { return new MurmurabaError(ErrorType.MEDIA_RECORDER, 'MediaRecorder is not supported in this browser', 'MEDIA_RECORDER_NOT_SUPPORTED', details); } static mediaRecorderStartFailed(originalError, details) { return new MurmurabaError(ErrorType.MEDIA_RECORDER, `Failed to start MediaRecorder: ${originalError?.message || 'Unknown error'}`, 'MEDIA_RECORDER_START_FAILED', { ...details, originalError: originalError?.message }); } static mediaRecorderInvalidState(currentState, expectedState, details) { return new MurmurabaError(ErrorType.MEDIA_RECORDER, `MediaRecorder is in invalid state. Current: ${currentState}, Expected: ${expectedState}`, 'MEDIA_RECORDER_INVALID_STATE', { ...details, currentState, expectedState }); } // WASM Module Errors static wasmModuleNotLoaded(details) { return new MurmurabaError(ErrorType.WASM_MODULE, 'WASM module is not loaded or initialized', 'WASM_MODULE_NOT_LOADED', details); } static wasmModuleLoadFailed(originalError, details) { return new MurmurabaError(ErrorType.WASM_MODULE, `Failed to load WASM module: ${originalError?.message || 'Unknown error'}`, 'WASM_MODULE_LOAD_FAILED', { ...details, originalError: originalError?.message }); } static wasmProcessingFailed(originalError, details) { return new MurmurabaError(ErrorType.WASM_MODULE, `WASM processing failed: ${originalError?.message || 'Unknown error'}`, 'WASM_PROCESSING_FAILED', { ...details, originalError: originalError?.message }); } // Audio Processing Errors static audioProcessingFailed(stage, originalError, details) { return new MurmurabaError(ErrorType.AUDIO_PROCESSING, `Audio processing failed at stage: ${stage}. ${originalError?.message || 'Unknown error'}`, 'AUDIO_PROCESSING_FAILED', { ...details, stage, originalError: originalError?.message }); } static invalidAudioFormat(expectedFormat, receivedFormat, details) { return new MurmurabaError(ErrorType.AUDIO_PROCESSING, `Invalid audio format. Expected: ${expectedFormat}, Received: ${receivedFormat}`, 'INVALID_AUDIO_FORMAT', { ...details, expectedFormat, receivedFormat }); } static audioBufferTooSmall(minSize, actualSize, details) { return new MurmurabaError(ErrorType.AUDIO_PROCESSING, `Audio buffer too small. Minimum: ${minSize}, Actual: ${actualSize}`, 'AUDIO_BUFFER_TOO_SMALL', { ...details, minSize, actualSize }); } // Validation Errors static invalidParameter(paramName, expectedType, receivedValue, details) { return new MurmurabaError(ErrorType.VALIDATION, `Invalid parameter '${paramName}'. Expected: ${expectedType}, Received: ${typeof receivedValue}`, 'INVALID_PARAMETER', { ...details, paramName, expectedType, receivedType: typeof receivedValue, receivedValue }); } static parameterOutOfRange(paramName, value, min, max, details) { return new MurmurabaError(ErrorType.VALIDATION, `Parameter '${paramName}' out of range. Value: ${value}, Range: [${min}, ${max}]`, 'PARAMETER_OUT_OF_RANGE', { ...details, paramName, value, min, max }); } static requiredParameterMissing(paramName, details) { return new MurmurabaError(ErrorType.VALIDATION, `Required parameter '${paramName}' is missing`, 'REQUIRED_PARAMETER_MISSING', { ...details, paramName }); } // Initialization Errors static initializationFailed(component, originalError, details) { return new MurmurabaError(ErrorType.INITIALIZATION, `Failed to initialize ${component}: ${originalError?.message || 'Unknown error'}`, 'INITIALIZATION_FAILED', { ...details, component, originalError: originalError?.message }); } static componentAlreadyInitialized(component, details) { return new MurmurabaError(ErrorType.INITIALIZATION, `Component '${component}' is already initialized`, 'COMPONENT_ALREADY_INITIALIZED', { ...details, component }); } // Permission Errors static microphonePermissionDenied(details) { return new MurmurabaError(ErrorType.PERMISSION, 'Microphone permission denied by user', 'MICROPHONE_PERMISSION_DENIED', details); } static audioPermissionNotGranted(details) { return new MurmurabaError(ErrorType.PERMISSION, 'Audio permission not granted. User interaction required.', 'AUDIO_PERMISSION_NOT_GRANTED', details); } // Unsupported Feature Errors static featureNotSupported(feature, details) { return new MurmurabaError(ErrorType.UNSUPPORTED, `Feature '${feature}' is not supported in this browser`, 'FEATURE_NOT_SUPPORTED', { ...details, feature }); } static browserNotSupported(requiredFeatures, details) { return new MurmurabaError(ErrorType.UNSUPPORTED, `Browser does not support required features: ${requiredFeatures.join(', ')}`, 'BROWSER_NOT_SUPPORTED', { ...details, requiredFeatures }); } // Generic Error Wrapper static wrapError(originalError, type, additionalMessage, details) { const message = additionalMessage ? `${additionalMessage}: ${originalError.message}` : originalError.message; const wrappedError = new MurmurabaError(type, message, 'WRAPPED_ERROR', { ...details, originalError: originalError.message, originalStack: originalError.stack, }); // Preserve the original stack trace wrappedError.stack = originalError.stack; return wrappedError; } } // Error Handler Class export class ErrorHandler { // Register error callback for specific error type static onError(type, callback) { if (!this.errorCallbacks.has(type)) { this.errorCallbacks.set(type, []); } this.errorCallbacks.get(type).push(callback); } // Register global error callback static onAnyError(callback) { this.globalErrorCallback = callback; } // Handle error with callbacks static handle(error) { // Call type-specific callbacks const typeCallbacks = this.errorCallbacks.get(error.type); if (typeCallbacks) { typeCallbacks.forEach(callback => { try { callback(error); } catch (callbackError) { console.error('Error in error callback:', callbackError); } }); } // Call global callback if (this.globalErrorCallback) { try { this.globalErrorCallback(error); } catch (callbackError) { console.error('Error in global error callback:', callbackError); } } return error; } // Create and handle error in one call static create(type, message, code, details) { const error = new MurmurabaError(type, message, code, details); return this.handle(error); } // Clear all callbacks static clearCallbacks() { this.errorCallbacks.clear(); this.globalErrorCallback = undefined; } } ErrorHandler.errorCallbacks = new Map(); // Utility functions for common error patterns export const throwIf = (condition, errorFactory) => { if (condition) { throw ErrorHandler.handle(errorFactory()); } }; export const throwIfNot = (condition, errorFactory) => { throwIf(!condition, errorFactory); }; export const wrapAsync = async (asyncFn, errorFactory) => { try { return await asyncFn(); } catch (error) { throw ErrorHandler.handle(errorFactory(error)); } }; export const wrapSync = (syncFn, errorFactory) => { try { return syncFn(); } catch (error) { throw ErrorHandler.handle(errorFactory(error)); } }; // Export everything export { MurmurabaError as default };