"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResourceLimiter = void 0;
/**
 * Production-grade resource limiting and monitoring
 * Prevents memory exhaustion and resource leaks
 */
const process = __importStar(require("process"));
/**
 * Monitor and enforce resource limits to prevent exhaustion
 */
class ResourceLimiter {
    constructor(limits = {}) {
        this.warningCallbacks = [];
        this.moduleCount = 0;
        this.fileHandleCount = 0;
        const defaultMaxMemory = 1024 * 1024 * 1024; // 1GB default
        this.limits = {
            maxMemoryBytes: limits.maxMemoryBytes ?? defaultMaxMemory,
            maxFileHandles: limits.maxFileHandles ?? 1000,
            maxCachedModules: limits.maxCachedModules ?? 10000,
            checkInterval: limits.checkInterval ?? 5000,
        };
    }
    /**
     * Start monitoring resources
     */
    start() {
        if (this.checkIntervalId) {
            return; // Already started
        }
        this.checkIntervalId = setInterval(() => {
            this.checkLimits();
        }, this.limits.checkInterval);
        // Don't prevent process exit - critical for tests and production
        // unref() allows Node to exit even if interval is active
        if (this.checkIntervalId.unref) {
            this.checkIntervalId.unref();
        }
    }
    /**
     * Stop monitoring resources
     */
    stop() {
        if (this.checkIntervalId) {
            clearInterval(this.checkIntervalId);
            this.checkIntervalId = undefined;
        }
    }
    /**
     * Register a callback for resource warnings
     */
    onWarning(callback) {
        this.warningCallbacks.push(callback);
    }
    /**
     * Check if a module can be loaded (respects cache limit)
     */
    canLoadModule() {
        return this.moduleCount < this.limits.maxCachedModules;
    }
    /**
     * Increment module count
     */
    incrementModules() {
        this.moduleCount++;
    }
    /**
     * Decrement module count
     */
    decrementModules() {
        this.moduleCount = Math.max(0, this.moduleCount - 1);
    }
    /**
     * Set module count explicitly
     */
    setModuleCount(count) {
        this.moduleCount = Math.max(0, count);
    }
    /**
     * Check if a file handle can be opened
     */
    canOpenFile() {
        return this.fileHandleCount < this.limits.maxFileHandles;
    }
    /**
     * Increment file handle count
     */
    incrementFileHandles() {
        this.fileHandleCount++;
    }
    /**
     * Decrement file handle count
     */
    decrementFileHandles() {
        this.fileHandleCount = Math.max(0, this.fileHandleCount - 1);
    }
    /**
     * Get current resource usage
     */
    getUsage() {
        const memoryUsage = process.memoryUsage();
        return {
            memoryUsed: memoryUsage.rss,
            memoryLimit: this.limits.maxMemoryBytes,
            memoryPercent: (memoryUsage.rss / this.limits.maxMemoryBytes) * 100,
            heapUsed: memoryUsage.heapUsed,
            heapTotal: memoryUsage.heapTotal,
            fileHandles: this.fileHandleCount,
            cachedModules: this.moduleCount,
        };
    }
    /**
     * Check resource limits and trigger warnings
     */
    checkLimits() {
        const usage = this.getUsage();
        // Check memory limit (90% threshold for warning)
        if (usage.memoryPercent > 90) {
            this.triggerWarning(usage, 'memory');
        }
        // Check file handle limit (90% threshold for warning)
        const fileHandlePercent = (usage.fileHandles / this.limits.maxFileHandles) * 100;
        if (fileHandlePercent > 90) {
            this.triggerWarning(usage, 'file_handles');
        }
        // Check cached modules limit (90% threshold for warning)
        const modulePercent = (usage.cachedModules / this.limits.maxCachedModules) * 100;
        if (modulePercent > 90) {
            this.triggerWarning(usage, 'cached_modules');
        }
    }
    /**
     * Trigger warning callbacks
     */
    triggerWarning(usage, limit) {
        for (const callback of this.warningCallbacks) {
            try {
                callback(usage, limit);
            }
            catch (error) {
                // Ignore callback errors to prevent cascading failures
                console.error('Resource warning callback failed:', error);
            }
        }
    }
    /**
     * Force garbage collection if available
     */
    forceGC() {
        if (global.gc) {
            global.gc();
        }
    }
}
exports.ResourceLimiter = ResourceLimiter;
//# sourceMappingURL=resource-limiter.js.map