@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
JavaScript
;
/**
* 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();
}
});
});
}