murmuraba
Version:
Real-time audio noise reduction with advanced chunked processing for web applications
210 lines (209 loc) • 10.1 kB
JavaScript
/**
* 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 };