UNPKG

behemoth-cli

Version:

🌍 BEHEMOTH CLIv3.760.4 - Level 50+ POST-SINGULARITY Intelligence Trading AI

303 lines (257 loc) 8.05 kB
/** * Resource Cleanup Manager * Handles proper cleanup of event listeners, timeouts, intervals, and streams */ import { EventEmitter } from 'events'; import { logDebug, logWarn } from './error-handler.js'; export interface CleanupResource { id: string; type: 'timeout' | 'interval' | 'listener' | 'stream' | 'custom'; cleanup: () => void; description?: string; } export class ResourceCleanupManager { private resources: Map<string, CleanupResource> = new Map(); private isCleaningUp = false; /** * Register a timeout for cleanup */ registerTimeout(timeoutId: NodeJS.Timeout, description?: string): string { const id = `timeout_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.resources.set(id, { id, type: 'timeout', cleanup: () => clearTimeout(timeoutId), description }); logDebug('Registered timeout for cleanup', undefined, { component: 'ResourceCleanupManager', operation: 'registerTimeout', metadata: { id, description } }); return id; } /** * Register an interval for cleanup */ registerInterval(intervalId: NodeJS.Timeout, description?: string): string { const id = `interval_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.resources.set(id, { id, type: 'interval', cleanup: () => clearInterval(intervalId), description }); logDebug('Registered interval for cleanup', undefined, { component: 'ResourceCleanupManager', operation: 'registerInterval', metadata: { id, description } }); return id; } /** * Register an event listener for cleanup */ registerEventListener( emitter: EventEmitter | NodeJS.Process, event: string, listener: (...args: any[]) => void, description?: string ): string { const id = `listener_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.resources.set(id, { id, type: 'listener', cleanup: () => emitter.removeListener(event, listener), description: description || `${event} listener` }); logDebug('Registered event listener for cleanup', undefined, { component: 'ResourceCleanupManager', operation: 'registerEventListener', metadata: { id, event, description } }); return id; } /** * Register a custom cleanup function */ registerCustomCleanup( cleanupFn: () => void, description?: string ): string { const id = `custom_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.resources.set(id, { id, type: 'custom', cleanup: cleanupFn, description }); logDebug('Registered custom cleanup function', undefined, { component: 'ResourceCleanupManager', operation: 'registerCustomCleanup', metadata: { id, description } }); return id; } /** * Unregister a specific resource */ unregister(id: string): boolean { const resource = this.resources.get(id); if (!resource) { return false; } try { resource.cleanup(); this.resources.delete(id); logDebug('Unregistered resource', undefined, { component: 'ResourceCleanupManager', operation: 'unregister', metadata: { id, type: resource.type, description: resource.description } }); return true; } catch (error) { logWarn('Failed to cleanup resource', error, { component: 'ResourceCleanupManager', operation: 'unregister', metadata: { id, type: resource.type } }); // Remove from registry even if cleanup failed this.resources.delete(id); return false; } } /** * Clean up all registered resources */ cleanupAll(): void { if (this.isCleaningUp) { return; // Prevent recursive cleanup } this.isCleaningUp = true; const resourceCount = this.resources.size; logDebug(`Starting cleanup of ${resourceCount} resources`, undefined, { component: 'ResourceCleanupManager', operation: 'cleanupAll' }); let cleanedCount = 0; let failedCount = 0; for (const [id, resource] of this.resources.entries()) { try { resource.cleanup(); cleanedCount++; logDebug('Successfully cleaned up resource', undefined, { component: 'ResourceCleanupManager', operation: 'cleanupAll', metadata: { id, type: resource.type, description: resource.description } }); } catch (error) { failedCount++; logWarn('Failed to cleanup resource', error, { component: 'ResourceCleanupManager', operation: 'cleanupAll', metadata: { id, type: resource.type } }); } } this.resources.clear(); this.isCleaningUp = false; logDebug(`Cleanup completed: ${cleanedCount} successful, ${failedCount} failed`, undefined, { component: 'ResourceCleanupManager', operation: 'cleanupAll', metadata: { totalResources: resourceCount, cleaned: cleanedCount, failed: failedCount } }); } /** * Get count of registered resources by type */ getResourceCounts(): Record<string, number> { const counts: Record<string, number> = {}; for (const resource of this.resources.values()) { counts[resource.type] = (counts[resource.type] || 0) + 1; } return counts; } /** * Check if there are resources that need cleanup */ hasResources(): boolean { return this.resources.size > 0; } } // Global instance for application-wide resource management export const globalResourceManager = new ResourceCleanupManager(); /** * Enhanced timeout wrapper with automatic cleanup registration */ export function managedSetTimeout( callback: () => void, delay: number, description?: string, manager: ResourceCleanupManager = globalResourceManager ): { timeoutId: NodeJS.Timeout; cleanupId: string } { const timeoutId = setTimeout(() => { // Auto-unregister when timeout executes manager.unregister(cleanupId); callback(); }, delay); const cleanupId = manager.registerTimeout(timeoutId, description); return { timeoutId, cleanupId }; } /** * Enhanced interval wrapper with automatic cleanup registration */ export function managedSetInterval( callback: () => void, delay: number, description?: string, manager: ResourceCleanupManager = globalResourceManager ): { intervalId: NodeJS.Timeout; cleanupId: string } { const intervalId = setInterval(callback, delay); const cleanupId = manager.registerInterval(intervalId, description); return { intervalId, cleanupId }; } /** * Enhanced event listener registration with automatic cleanup */ export function managedEventListener<T extends EventEmitter | NodeJS.Process>( emitter: T, event: string, listener: (...args: any[]) => void, description?: string, manager: ResourceCleanupManager = globalResourceManager ): string { emitter.on(event as any, listener); return manager.registerEventListener(emitter, event, listener, description); } /** * Setup process exit handlers for cleanup */ export function setupProcessCleanupHandlers(manager: ResourceCleanupManager = globalResourceManager): void { const cleanupHandler = () => { if (manager.hasResources()) { console.log('🧹 Cleaning up resources...'); manager.cleanupAll(); } }; // Handle various exit scenarios process.on('exit', cleanupHandler); process.on('SIGINT', () => { cleanupHandler(); process.exit(0); }); process.on('SIGTERM', () => { cleanupHandler(); process.exit(0); }); process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); cleanupHandler(); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); cleanupHandler(); process.exit(1); }); }