UNPKG

murmuraba

Version:

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

168 lines (167 loc) 5.38 kB
/** * Secure Event Bridge for internal communication * Replaces the insecure global __murmurabaTDDBridge */ import { EventEmitter } from './event-emitter'; class SecureEventBridge extends EventEmitter { constructor() { super(); this.chunkProcessor = null; this.recordingManagers = new Map(); // Generate a unique access token for this session this.accessToken = this.generateAccessToken(); } /** * Get the singleton instance */ static getInstance() { if (!SecureEventBridge.instance) { SecureEventBridge.instance = new SecureEventBridge(); } return SecureEventBridge.instance; } /** * Reset the singleton (mainly for testing) */ static reset() { if (SecureEventBridge.instance) { SecureEventBridge.instance.removeAllListeners(); SecureEventBridge.instance.recordingManagers.clear(); SecureEventBridge.instance = null; } } /** * Register a chunk processor with validation */ registerChunkProcessor(processor, token) { if (!this.validateToken(token)) { throw new Error('Invalid access token for chunk processor registration'); } this.chunkProcessor = processor; } /** * Register a recording manager with validation */ registerRecordingManager(id, manager, token) { if (!this.validateToken(token)) { throw new Error('Invalid access token for recording manager registration'); } if (!id || typeof id !== 'string') { throw new Error('Invalid recording manager ID'); } this.recordingManagers.set(id, manager); this.emit('recording-manager-registered', id); } /** * Unregister a recording manager */ unregisterRecordingManager(id, token) { if (!this.validateToken(token)) { throw new Error('Invalid access token for recording manager unregistration'); } this.recordingManagers.delete(id); this.emit('recording-manager-unregistered', id); } /** * Notify metrics to all registered managers */ notifyMetrics(metrics, token) { if (!this.validateToken(token)) { throw new Error('Invalid access token for metrics notification'); } // Validate metrics object if (!this.validateMetrics(metrics)) { console.warn('Invalid metrics object received'); return; } // Emit metrics event this.emit('metrics', metrics); // Notify all registered recording managers this.recordingManagers.forEach((manager) => { try { if (manager && typeof manager.notifyMetrics === 'function') { manager.notifyMetrics(metrics); } } catch (error) { console.error('Error notifying recording manager:', error); } }); } /** * Get the access token (only for internal use) */ getAccessToken() { return this.accessToken; } /** * Get registered recording managers count */ getRecordingManagersCount() { return this.recordingManagers.size; } /** * Generate a secure access token */ generateAccessToken() { const array = new Uint8Array(32); if (typeof window !== 'undefined' && window.crypto) { window.crypto.getRandomValues(array); } else if (typeof global !== 'undefined' && global.crypto) { global.crypto.getRandomValues(array); } else { // Fallback for environments without crypto for (let i = 0; i < array.length; i++) { array[i] = Math.floor(Math.random() * 256); } } return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join(''); } /** * Validate access token */ validateToken(token) { return token === this.accessToken; } /** * Validate metrics object */ validateMetrics(metrics) { if (!metrics || typeof metrics !== 'object') { return false; } // Check required fields const requiredFields = [ 'noiseReductionLevel', 'processingLatency', 'inputLevel', 'outputLevel', 'frameCount', 'droppedFrames', 'timestamp' ]; for (const field of requiredFields) { if (!(field in metrics)) { return false; } // Validate numeric fields if (field !== 'timestamp' && (typeof metrics[field] !== 'number' || isNaN(metrics[field]) || !isFinite(metrics[field]))) { return false; } } // Validate ranges if (metrics.inputLevel < 0 || metrics.inputLevel > 1) return false; if (metrics.outputLevel < 0 || metrics.outputLevel > 1) return false; if (metrics.noiseReductionLevel < 0 || metrics.noiseReductionLevel > 100) return false; if (metrics.processingLatency < 0) return false; return true; } } SecureEventBridge.instance = null; export { SecureEventBridge };