UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

245 lines (244 loc) 7.06 kB
"use strict"; /** * Resource management and cleanup utilities */ Object.defineProperty(exports, "__esModule", { value: true }); exports.resourceManager = exports.ResourceManager = void 0; exports.trackFileWatcher = trackFileWatcher; exports.trackTimer = trackTimer; exports.trackInterval = trackInterval; exports.trackProcess = trackProcess; exports.trackServer = trackServer; class ResourceManager { constructor() { this.resources = new Map(); this.setupEventHandlers(); this.startMemoryMonitoring(); this.startPeriodicCleanup(); } static getInstance() { if (!ResourceManager.instance) { ResourceManager.instance = new ResourceManager(); } return ResourceManager.instance; } /** * Register a resource for tracking and cleanup */ register(id, type, resource, cleanup) { this.resources.set(id, { type, resource, cleanup, timestamp: Date.now() }); } /** * Unregister and cleanup a specific resource */ async unregister(id) { const tracker = this.resources.get(id); if (tracker) { try { await tracker.cleanup(); } catch (error) { if (process.env.DEBUG) { console.error(`Failed to cleanup resource ${id}:`, error); } } this.resources.delete(id); } } /** * Get current memory usage */ getMemoryUsage() { return process.memoryUsage(); } /** * Get memory usage in human readable format */ getFormattedMemoryUsage() { const usage = this.getMemoryUsage(); const formatMB = (bytes) => `${(bytes / 1024 / 1024).toFixed(2)}MB`; return `Heap: ${formatMB(usage.heapUsed)}/${formatMB(usage.heapTotal)}, ` + `RSS: ${formatMB(usage.rss)}, External: ${formatMB(usage.external)}`; } /** * Force garbage collection if available */ forceGC() { if (global.gc) { global.gc(); } } /** * Check if memory usage is high */ isMemoryHigh() { const usage = this.getMemoryUsage(); const heapUsedMB = usage.heapUsed / 1024 / 1024; // Consider memory high if heap usage > 100MB return heapUsedMB > 100; } /** * Get resource statistics */ getResourceStats() { const byType = {}; for (const tracker of this.resources.values()) { byType[tracker.type] = (byType[tracker.type] || 0) + 1; } return { total: this.resources.size, byType }; } /** * Cleanup old or unused resources */ async cleanupOldResources(maxAge = 3600000) { const now = Date.now(); const toCleanup = []; for (const [id, tracker] of this.resources) { if (now - tracker.timestamp > maxAge) { toCleanup.push(id); } } for (const id of toCleanup) { await this.unregister(id); } } /** * Cleanup all resources */ async cleanup() { const cleanupPromises = []; for (const [id, tracker] of this.resources) { cleanupPromises.push((async () => { try { await tracker.cleanup(); } catch (error) { if (process.env.DEBUG) { console.error(`Failed to cleanup resource ${id}:`, error); } } })()); } await Promise.all(cleanupPromises); this.resources.clear(); // Stop monitoring if (this.memoryMonitor) { clearInterval(this.memoryMonitor); } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } } /** * Setup event handlers for graceful shutdown */ setupEventHandlers() { const cleanup = async () => { await this.cleanup(); process.exit(0); }; process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); process.on('exit', () => { // Synchronous cleanup only for (const tracker of this.resources.values()) { try { const result = tracker.cleanup(); if (result && typeof result.then === 'function') { // Can't wait for async cleanup on exit console.warn('Async cleanup detected on exit, may not complete'); } } catch { // Ignore cleanup errors on exit } } }); } /** * Start memory monitoring */ startMemoryMonitoring() { if (process.env.NODE_ENV === 'development' || process.env.DEBUG) { this.memoryMonitor = setInterval(() => { if (this.isMemoryHigh()) { console.warn(`High memory usage detected: ${this.getFormattedMemoryUsage()}`); // Force GC if available this.forceGC(); // Cleanup old resources this.cleanupOldResources(1800000); // 30 minutes } }, 30000); // Check every 30 seconds } } /** * Start periodic cleanup */ startPeriodicCleanup() { this.cleanupInterval = setInterval(() => { this.cleanupOldResources(); }, 300000); // Cleanup every 5 minutes } } exports.ResourceManager = ResourceManager; // Utility functions for common resource types exports.resourceManager = ResourceManager.getInstance(); /** * Track a file watcher */ function trackFileWatcher(id, watcher) { exports.resourceManager.register(id, 'file-watcher', watcher, () => { if (watcher && typeof watcher.close === 'function') { watcher.close(); } }); } /** * Track a timer */ function trackTimer(id, timer) { exports.resourceManager.register(id, 'timer', timer, () => { clearTimeout(timer); }); } /** * Track an interval */ function trackInterval(id, interval) { exports.resourceManager.register(id, 'interval', interval, () => { clearInterval(interval); }); } /** * Track a process */ function trackProcess(id, proc) { exports.resourceManager.register(id, 'process', proc, () => { if (proc && typeof proc.kill === 'function') { proc.kill(); } }); } /** * Track a server */ function trackServer(id, server) { exports.resourceManager.register(id, 'server', server, () => { return new Promise((resolve) => { if (server && typeof server.close === 'function') { server.close(() => resolve()); } else { resolve(); } }); }); }