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

553 lines (552 loc) 20.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ResourceManager = exports.ResourcePriority = exports.ResourceType = void 0; exports.createResourceManager = createResourceManager; exports.getGlobalResourceManager = getGlobalResourceManager; exports.setGlobalResourceManager = setGlobalResourceManager; const events_1 = require("events"); const fs = __importStar(require("fs-extra")); var ResourceType; (function (ResourceType) { ResourceType["FILE_HANDLE"] = "file_handle"; ResourceType["STREAM"] = "stream"; ResourceType["NETWORK_CONNECTION"] = "network_connection"; ResourceType["CHILD_PROCESS"] = "child_process"; ResourceType["TIMER"] = "timer"; ResourceType["EVENT_LISTENER"] = "event_listener"; ResourceType["TEMPORARY_FILE"] = "temporary_file"; ResourceType["TEMPORARY_DIRECTORY"] = "temporary_directory"; ResourceType["CACHE_ENTRY"] = "cache_entry"; ResourceType["MEMORY_BUFFER"] = "memory_buffer"; ResourceType["DATABASE_CONNECTION"] = "database_connection"; ResourceType["CUSTOM"] = "custom"; })(ResourceType || (exports.ResourceType = ResourceType = {})); var ResourcePriority; (function (ResourcePriority) { ResourcePriority[ResourcePriority["LOW"] = 0] = "LOW"; ResourcePriority[ResourcePriority["NORMAL"] = 1] = "NORMAL"; ResourcePriority[ResourcePriority["HIGH"] = 2] = "HIGH"; ResourcePriority[ResourcePriority["CRITICAL"] = 3] = "CRITICAL"; })(ResourcePriority || (exports.ResourcePriority = ResourcePriority = {})); class ResourceManager extends events_1.EventEmitter { constructor() { super(); this.resources = new Map(); this.stateSnapshots = new Map(); this.recoveryPlans = new Map(); this.activeOperations = new Map(); this.cleanupInProgress = false; this.shutdownHooksRegistered = false; this.registerShutdownHooks(); } registerShutdownHooks() { if (this.shutdownHooksRegistered) return; process.on('exit', () => { this.performEmergencyCleanup(); }); process.on('SIGINT', () => { this.performGracefulShutdown().then(() => process.exit(0)); }); process.on('SIGTERM', () => { this.performGracefulShutdown().then(() => process.exit(0)); }); process.on('uncaughtException', (error) => { console.error('Uncaught exception, performing emergency cleanup:', error); this.performEmergencyCleanup(); process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('Unhandled rejection, performing emergency cleanup:', reason); this.performEmergencyCleanup(); process.exit(1); }); this.shutdownHooksRegistered = true; } registerResource(type, resource, cleanup, options = {}) { const id = this.generateResourceId(); const now = new Date(); const handle = { id, type, resource, metadata: { created: now, lastAccessed: now, size: options.size, tags: options.tags || [], priority: options.priority || ResourcePriority.NORMAL }, cleanup }; this.resources.set(id, handle); this.emit('resource:registered', handle); return id; } unregisterResource(id) { const handle = this.resources.get(id); if (handle) { this.resources.delete(id); this.emit('resource:unregistered', handle); return true; } return false; } async cleanupResource(id, force = false) { const handle = this.resources.get(id); if (!handle) return false; try { this.emit('resource:cleanup:start', handle); if (force) { await this.forceCleanupResource(handle); } else { await this.gracefulCleanupResource(handle); } this.resources.delete(id); this.emit('resource:cleanup:success', handle); return true; } catch (error) { this.emit('resource:cleanup:error', { handle, error }); throw error; } } async gracefulCleanupResource(handle) { const timeout = this.getTimeoutForPriority(handle.metadata.priority); await this.withTimeout(async () => { await handle.cleanup(); }, timeout); } async forceCleanupResource(handle) { try { // Try graceful cleanup first with shorter timeout await this.withTimeout(async () => { await handle.cleanup(); }, 1000); } catch (error) { // Force cleanup for specific resource types await this.performForceCleanup(handle); } } async performForceCleanup(handle) { switch (handle.type) { case ResourceType.FILE_HANDLE: if (handle.resource && typeof handle.resource.close === 'function') { handle.resource.close(); } break; case ResourceType.STREAM: if (handle.resource && typeof handle.resource.destroy === 'function') { handle.resource.destroy(); } break; case ResourceType.CHILD_PROCESS: if (handle.resource && typeof handle.resource.kill === 'function') { handle.resource.kill('SIGKILL'); } break; case ResourceType.TIMER: if (handle.resource) { clearTimeout(handle.resource); clearInterval(handle.resource); } break; case ResourceType.TEMPORARY_FILE: try { await fs.unlink(handle.resource); } catch { // Ignore errors } break; case ResourceType.TEMPORARY_DIRECTORY: try { await fs.remove(handle.resource); } catch { // Ignore errors } break; default: // Custom cleanup try { await handle.cleanup(); } catch { // Ignore errors in force mode } break; } } getTimeoutForPriority(priority) { switch (priority) { case ResourcePriority.LOW: return 1000; case ResourcePriority.NORMAL: return 5000; case ResourcePriority.HIGH: return 10000; case ResourcePriority.CRITICAL: return 30000; default: return 5000; } } async cleanupByType(type, options = {}) { const handles = Array.from(this.resources.values()) .filter(h => h.type === type); if (options.dryRun) { this.emit('cleanup:dry_run', { type, count: handles.length }); return handles.length; } let cleaned = 0; for (const handle of handles) { try { await this.cleanupResource(handle.id); cleaned++; } catch (error) { this.emit('cleanup:error', { handle, error }); } } return cleaned; } async cleanupByTags(tags, options = {}) { const handles = Array.from(this.resources.values()) .filter(h => h.metadata.tags && tags.some(tag => h.metadata.tags.includes(tag))); if (options.dryRun) { this.emit('cleanup:dry_run', { tags, count: handles.length }); return handles.length; } let cleaned = 0; for (const handle of handles) { try { await this.cleanupResource(handle.id); cleaned++; } catch (error) { this.emit('cleanup:error', { handle, error }); } } return cleaned; } async cleanupAll(options = {}) { if (this.cleanupInProgress) { return 0; } this.cleanupInProgress = true; const defaultOptions = { gracefulTimeout: 30000, forceTimeout: 5000, priority: ResourcePriority.LOW, preserveTypes: [], dryRun: false }; const opts = { ...defaultOptions, ...options }; try { const handles = Array.from(this.resources.values()) .filter(h => !opts.preserveTypes.includes(h.type)) .filter(h => h.metadata.priority >= opts.priority) .sort((a, b) => b.metadata.priority - a.metadata.priority); if (opts.dryRun) { this.emit('cleanup:dry_run', { count: handles.length }); return handles.length; } this.emit('cleanup:start', { count: handles.length }); // Phase 1: Graceful cleanup let cleaned = 0; const gracefulPromises = handles.map(async (handle) => { try { await this.withTimeout(async () => { await this.cleanupResource(handle.id); }, opts.gracefulTimeout); cleaned++; } catch (error) { this.emit('cleanup:graceful_failed', { handle, error }); } }); await Promise.allSettled(gracefulPromises); // Phase 2: Force cleanup remaining resources const remainingHandles = Array.from(this.resources.values()) .filter(h => !opts.preserveTypes.includes(h.type)); if (remainingHandles.length > 0) { this.emit('cleanup:force_start', { count: remainingHandles.length }); const forcePromises = remainingHandles.map(async (handle) => { try { await this.cleanupResource(handle.id, true); cleaned++; } catch (error) { this.emit('cleanup:force_failed', { handle, error }); } }); await Promise.allSettled(forcePromises); } this.emit('cleanup:complete', { cleaned }); return cleaned; } finally { this.cleanupInProgress = false; } } performEmergencyCleanup() { const handles = Array.from(this.resources.values()); for (const handle of handles) { try { if (typeof handle.cleanup === 'function') { const result = handle.cleanup(); if (result && typeof result.then === 'function') { // Don't wait for async cleanup in emergency result.catch(() => { }); } } } catch { // Ignore errors in emergency cleanup } } this.resources.clear(); } async performGracefulShutdown() { try { await this.cleanupAll({ gracefulTimeout: 10000, forceTimeout: 2000, priority: ResourcePriority.LOW }); } catch (error) { console.error('Error during graceful shutdown:', error); this.performEmergencyCleanup(); } } // State management createSnapshot(operation, state) { const id = this.generateSnapshotId(); const resources = Array.from(this.resources.keys()); const checksum = this.calculateChecksum(state); const snapshot = { id, timestamp: new Date(), operation, state: this.deepClone(state), resources, checksum }; this.stateSnapshots.set(id, snapshot); this.emit('snapshot:created', snapshot); // Keep only recent snapshots this.cleanupOldSnapshots(); return id; } restoreSnapshot(id) { const snapshot = this.stateSnapshots.get(id); if (!snapshot) { throw new Error(`Snapshot ${id} not found`); } // Verify checksum const currentChecksum = this.calculateChecksum(snapshot.state); if (currentChecksum !== snapshot.checksum) { throw new Error(`Snapshot ${id} is corrupted`); } this.emit('snapshot:restored', snapshot); return this.deepClone(snapshot.state); } createRecoveryPlan(operation, steps) { const id = this.generateRecoveryPlanId(); const resources = Array.from(this.resources.keys()); const estimatedDuration = steps.reduce((total, step) => total + step.timeout, 0); const plan = { id, operation, steps, rollbackSteps: steps.filter(s => s.rollback).reverse(), resources, metadata: { created: new Date(), estimatedDuration, dependencies: [] } }; this.recoveryPlans.set(id, plan); this.emit('recovery_plan:created', plan); return id; } async executeRecoveryPlan(id) { const plan = this.recoveryPlans.get(id); if (!plan) { throw new Error(`Recovery plan ${id} not found`); } this.emit('recovery:start', plan); const executedSteps = []; try { for (const step of plan.steps) { this.emit('recovery:step:start', step); try { await this.withTimeout(step.action, step.timeout); executedSteps.push(step); this.emit('recovery:step:success', step); } catch (error) { this.emit('recovery:step:error', { step, error }); if (step.critical) { throw new Error(`Critical recovery step failed: ${step.description}`); } } } this.emit('recovery:success', plan); } catch (error) { this.emit('recovery:failure', { plan, error }); // Execute rollback await this.executeRollback(executedSteps); throw error; } } async executeRollback(executedSteps) { this.emit('rollback:start', { steps: executedSteps.length }); for (const step of executedSteps.reverse()) { if (step.rollback) { try { await this.withTimeout(step.rollback, step.timeout); this.emit('rollback:step:success', step); } catch (error) { this.emit('rollback:step:error', { step, error }); } } } this.emit('rollback:complete'); } // Utility methods generateResourceId() { return `res_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } generateSnapshotId() { return `snap_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } generateRecoveryPlanId() { return `plan_${Date.now()}_${Math.random().toString(36).substr(2, 8)}`; } calculateChecksum(data) { const crypto = require('crypto'); return crypto.createHash('sha256').update(JSON.stringify(data)).digest('hex'); } deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } cleanupOldSnapshots() { const snapshots = Array.from(this.stateSnapshots.entries()) .sort(([, a], [, b]) => b.timestamp.getTime() - a.timestamp.getTime()); // Keep only last 100 snapshots if (snapshots.length > 100) { const toDelete = snapshots.slice(100); for (const [id] of toDelete) { this.stateSnapshots.delete(id); } } } async withTimeout(operation, timeoutMs) { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error(`Operation timed out after ${timeoutMs}ms`)); }, timeoutMs); operation() .then(result => { clearTimeout(timer); resolve(result); }) .catch(error => { clearTimeout(timer); reject(error); }); }); } // Query methods getResourceCount() { return this.resources.size; } getResourcesByType(type) { return Array.from(this.resources.values()) .filter(h => h.type === type); } getResourcesByTags(tags) { return Array.from(this.resources.values()) .filter(h => h.metadata.tags && tags.some(tag => h.metadata.tags.includes(tag))); } getResourceStats() { const resources = Array.from(this.resources.values()); const byType = {}; const byPriority = {}; let totalSize = 0; for (const resource of resources) { byType[resource.type] = (byType[resource.type] || 0) + 1; byPriority[ResourcePriority[resource.metadata.priority]] = (byPriority[ResourcePriority[resource.metadata.priority]] || 0) + 1; if (resource.metadata.size) { totalSize += resource.metadata.size; } } return { total: resources.length, byType, byPriority, totalSize }; } getSnapshots() { return Array.from(this.stateSnapshots.values()) .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } getRecoveryPlans() { return Array.from(this.recoveryPlans.values()) .sort((a, b) => b.metadata.created.getTime() - a.metadata.created.getTime()); } } exports.ResourceManager = ResourceManager; // Global resource manager let globalResourceManager = null; function createResourceManager() { return new ResourceManager(); } function getGlobalResourceManager() { if (!globalResourceManager) { globalResourceManager = new ResourceManager(); } return globalResourceManager; } function setGlobalResourceManager(manager) { globalResourceManager = manager; }