UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

889 lines (883 loc) 30.1 kB
'use strict'; var global$1 = require('../../types/global.js'); var types = require('./types.js'); var eventManager = require('./event-manager.js'); var configManager = require('./config-manager.js'); var referenceTracker = require('./reference-tracker.js'); var memoryPool = require('./memory-pool.js'); /*************************************************************************** * FortifyJS - Secure Array Types * * This file contains type definitions for the SecureArray modular architecture * * @author Nehonix * * @license MIT * * Copyright (c) 2025 Nehonix. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ***************************************************************************** */ // Initialize polyfills for WeakRef and FinalizationRegistry global$1.initializePolyfills(); /** * Advanced Memory Manager * * Main orchestrator for the memory management system with all components */ class AdvancedMemoryManager { constructor(config = {}) { this.pools = new Map(); // Object collection tracking this.objectTracker = new Map(); this.collectionHistory = []; // Initialize state this.state = { isRunning: false, lastGC: Date.now(), nextScheduledGC: 0, activeMonitors: 0, errorCount: 0, uptime: Date.now(), }; // Initialize stats this.stats = { totalAllocated: 0, totalFreed: 0, currentUsage: 0, peakUsage: 0, gcCount: 0, leakCount: 0, pressure: 0, poolStats: [], trackedObjects: 0, lastGC: Date.now(), averageGCTime: 0, }; // Initialize performance metrics this.performanceMetrics = { averageGCTime: 0, gcFrequency: 0, memoryEfficiency: 0, poolHitRates: {}, leakDetectionAccuracy: 0, systemLoad: 0, }; // Initialize components in order this.eventManager = new eventManager.MemoryEventManager(this.getDefaultConfig()); this.configManager = new configManager.ConfigurationManager(config, this.eventManager); this.referenceTracker = new referenceTracker.AdvancedReferenceTracker(this.eventManager, this.configManager.getConfig()); // Initialize object collection tracking this.initializeObjectTracking(); // Start the memory manager this.start(); } /** * Get singleton instance */ static getInstance(config) { if (!AdvancedMemoryManager.instance) { AdvancedMemoryManager.instance = new AdvancedMemoryManager(config); } return AdvancedMemoryManager.instance; } /** * Get default configuration */ getDefaultConfig() { return { maxMemory: 100 * 1024 * 1024, // 100MB gcThreshold: 0.8, gcInterval: 30000, // 30 seconds enableLeakDetection: true, enablePerformanceMonitoring: true, enableEventLogging: false, autoCleanupInterval: 60000, // 1 minute maxPoolAge: 300000, // 5 minutes leakDetectionThreshold: 300000, // 5 minutes maxEventHistory: 1000, }; } /** * Start the memory manager */ start() { if (this.state.isRunning) { return; } this.state.isRunning = true; this.state.uptime = Date.now(); const config = this.configManager.getConfig(); // Start garbage collection monitoring this.startGCMonitoring(); // Start performance monitoring if enabled if (config.enablePerformanceMonitoring) { this.startPerformanceMonitoring(); } // Start cleanup monitoring this.startCleanupMonitoring(); this.eventManager.emit(types.MemoryEventType.CONFIG_UPDATED, { action: "started", config: config, }); } /** * Start garbage collection monitoring */ startGCMonitoring() { const config = this.configManager.getConfig(); if (this.gcInterval) { clearInterval(this.gcInterval); } this.gcInterval = setInterval(() => { this.checkMemoryPressure(); }, config.gcInterval); this.state.nextScheduledGC = Date.now() + config.gcInterval; } /** * Start performance monitoring */ startPerformanceMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); } this.monitoringInterval = setInterval(() => { this.updatePerformanceMetrics(); this.updateMemoryStats(); }, 5000); // Update every 5 seconds this.state.activeMonitors++; } /** * Start cleanup monitoring */ startCleanupMonitoring() { const config = this.configManager.getConfig(); if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.cleanupInterval = setInterval(() => { this.performCleanup(); }, config.autoCleanupInterval); this.state.activeMonitors++; } /** * Check memory pressure and trigger GC if needed */ checkMemoryPressure() { try { const usage = this.getCurrentMemoryUsage(); const config = this.configManager.getConfig(); this.stats.currentUsage = usage.estimatedSize; this.stats.pressure = usage.estimatedSize / config.maxMemory; const pressureInfo = this.getMemoryPressureInfo(); if (pressureInfo.shouldTriggerGC) { this.triggerGarbageCollection(); } // Emit memory pressure event if significant if (pressureInfo.level !== types.MemoryPressureLevel.LOW) { this.eventManager.emit(types.MemoryEventType.MEMORY_PRESSURE, pressureInfo); } } catch (error) { this.handleError("checkMemoryPressure", error); } } /** * Get current memory usage estimation */ getCurrentMemoryUsage() { // Try Node.js process.memoryUsage first if (typeof process !== "undefined" && process.memoryUsage) { const usage = process.memoryUsage(); return { estimatedSize: usage.heapUsed, confidence: 0.95, method: "node", breakdown: { heap: usage.heapUsed, external: usage.external, }, }; } // Browser fallback - estimate based on tracked objects const trackerStats = this.referenceTracker.getStats(); return { estimatedSize: trackerStats.totalEstimatedSize || 0, confidence: 0.6, method: "browser", breakdown: { tracked: trackerStats.totalEstimatedSize || 0, }, }; } /** * Get memory pressure information */ getMemoryPressureInfo() { const config = this.configManager.getConfig(); const pressure = this.stats.pressure; let level; let recommendation; let shouldTriggerGC; if (pressure < 0.5) { level = types.MemoryPressureLevel.LOW; recommendation = "Memory usage is optimal"; shouldTriggerGC = false; } else if (pressure < config.gcThreshold) { level = types.MemoryPressureLevel.MEDIUM; recommendation = "Monitor memory usage"; shouldTriggerGC = false; } else if (pressure < 0.95) { level = types.MemoryPressureLevel.HIGH; recommendation = "Consider triggering garbage collection"; shouldTriggerGC = true; } else { level = types.MemoryPressureLevel.CRITICAL; recommendation = "Immediate garbage collection required"; shouldTriggerGC = true; } // Also check time since last GC const timeSinceLastGC = Date.now() - this.state.lastGC; if (timeSinceLastGC >= config.gcInterval) { shouldTriggerGC = true; } return { level, pressure, recommendation, shouldTriggerGC, }; } /** * Trigger garbage collection */ triggerGarbageCollection() { const startTime = Date.now(); const beforeUsage = this.stats.currentUsage; this.eventManager.emit(types.MemoryEventType.GC_TRIGGERED, { beforeUsage, pressure: this.stats.pressure, trigger: "automatic", }); try { // Clean up dead references this.referenceTracker.cleanup(); // Clean up memory pools let poolsCleanedUp = 0; for (const pool of this.pools.values()) { // Remove old items from pools const sizeBefore = pool.size; // Pool cleanup is handled internally if (pool.size < sizeBefore) { poolsCleanedUp++; } } // Force GC if available (Node.js) if (typeof global !== "undefined" && global.gc) { global.gc(); } // Update stats const afterUsage = this.getCurrentMemoryUsage().estimatedSize; const duration = Date.now() - startTime; const freedMemory = Math.max(0, beforeUsage - afterUsage); this.stats.totalFreed += freedMemory; this.stats.gcCount++; this.stats.lastGC = Date.now(); this.state.lastGC = Date.now(); // Update average GC time this.stats.averageGCTime = (this.stats.averageGCTime * (this.stats.gcCount - 1) + duration) / this.stats.gcCount; // Perform sophisticated object collection tracking const objectsCollected = this.trackObjectCollection(beforeUsage, afterUsage); const result = { beforeUsage, afterUsage, freedMemory, duration, objectsCollected, poolsCleanedUp, success: true, }; this.eventManager.emit(types.MemoryEventType.GC_COMPLETED, result); return result; } catch (error) { const result = { beforeUsage, afterUsage: beforeUsage, freedMemory: 0, duration: Date.now() - startTime, objectsCollected: 0, poolsCleanedUp: 0, success: false, error: error instanceof Error ? error.message : String(error), }; this.handleError("triggerGarbageCollection", error); return result; } } /** * Perform routine cleanup */ performCleanup() { try { // Clean up reference tracker this.referenceTracker.cleanup(); // Detect memory leaks if enabled const config = this.configManager.getConfig(); if (config.enableLeakDetection) { const leaks = this.referenceTracker.detectLeaks(); this.stats.leakCount = leaks.length; } // Update tracked objects count this.stats.trackedObjects = this.referenceTracker.getTrackedObjects().length; } catch (error) { this.handleError("performCleanup", error); } } /** * Update performance metrics */ updatePerformanceMetrics() { try { // Update pool hit rates const poolHitRates = {}; for (const [name, pool] of this.pools.entries()) { const stats = pool.getStats(); poolHitRates[name] = stats.hitRate; } this.performanceMetrics.poolHitRates = poolHitRates; // Calculate GC frequency (GCs per hour) const uptime = Date.now() - this.state.uptime; this.performanceMetrics.gcFrequency = (this.stats.gcCount / uptime) * 3600000; // Calculate memory efficiency const config = this.configManager.getConfig(); this.performanceMetrics.memoryEfficiency = 1 - this.stats.currentUsage / config.maxMemory; // Update average GC time this.performanceMetrics.averageGCTime = this.stats.averageGCTime; } catch (error) { this.handleError("updatePerformanceMetrics", error); } } /** * Update memory statistics */ updateMemoryStats() { try { const usage = this.getCurrentMemoryUsage(); this.stats.currentUsage = usage.estimatedSize; this.stats.peakUsage = Math.max(this.stats.peakUsage, usage.estimatedSize); const config = this.configManager.getConfig(); this.stats.pressure = usage.estimatedSize / config.maxMemory; // Update pool stats this.stats.poolStats = Array.from(this.pools.values()).map((pool) => pool.getStats()); } catch (error) { this.handleError("updateMemoryStats", error); } } /** * Handle errors with proper logging and recovery */ handleError(operation, error) { this.state.errorCount++; const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Memory Manager Error in ${operation}:`, errorMessage); this.eventManager.emit(types.MemoryEventType.ERROR_OCCURRED, { operation, error: errorMessage, timestamp: Date.now(), errorCount: this.state.errorCount, }); } // Public API methods /** * Register an object for memory tracking */ registerObject(obj, id) { try { this.referenceTracker.addReference(obj, id); this.stats.totalAllocated += this.estimateObjectSize(obj); } catch (error) { this.handleError("registerObject", error); } } /** * Unregister an object from memory tracking */ unregisterObject(id) { try { this.referenceTracker.removeReference(id); this.stats.trackedObjects = Math.max(0, this.stats.trackedObjects - 1); } catch (error) { this.handleError("unregisterObject", error); } } /** * Add reference to an object */ addReference(id) { try { this.referenceTracker.addReference({}, id); // This needs to be fixed in the interface } catch (error) { this.handleError("addReference", error); } } /** * Remove reference from an object */ removeReference(id) { try { this.referenceTracker.removeReference(id); } catch (error) { this.handleError("removeReference", error); } } /** * Create a memory pool for object reuse */ createPool(config) { try { if (this.pools.has(config.name)) { throw new Error(`Pool with name '${config.name}' already exists`); } const pool = new memoryPool.AdvancedMemoryPool(config, this.eventManager); this.pools.set(config.name, pool); return pool; } catch (error) { this.handleError("createPool", error); throw error; } } /** * Get memory pool by name */ getPool(name) { return this.pools.get(name); } /** * Remove a memory pool */ removePool(name) { const pool = this.pools.get(name); if (pool && "destroy" in pool && typeof pool.destroy === "function") { pool.destroy(); } return this.pools.delete(name); } /** * Get current memory statistics */ getStats() { this.updateMemoryStats(); return { ...this.stats }; } /** * Get performance metrics */ getPerformanceMetrics() { this.updatePerformanceMetrics(); return { ...this.performanceMetrics }; } /** * Get memory manager state */ getState() { return { ...this.state }; } /** * Set memory limits */ setLimits(maxMemory, gcThreshold = 0.8) { try { this.configManager.updateConfig({ maxMemory, gcThreshold }); // Restart monitoring with new configuration this.startGCMonitoring(); } catch (error) { this.handleError("setLimits", error); throw error; } } /** * Force garbage collection */ forceGC() { this.eventManager.emit(types.MemoryEventType.GC_TRIGGERED, { trigger: "manual", timestamp: Date.now(), }); return this.triggerGarbageCollection(); } /** * Estimate object size in bytes */ estimateObjectSize(obj) { if (obj === null || obj === undefined) return 0; const type = typeof obj; switch (type) { case "boolean": return 4; case "number": return 8; case "string": return obj.length * 2; // UTF-16 case "object": if (obj instanceof ArrayBuffer) return obj.byteLength; if (obj instanceof Uint8Array) return obj.byteLength; if (Array.isArray(obj)) { return obj.reduce((sum, item) => sum + this.estimateObjectSize(item), 0); } return Object.keys(obj).length * 64; // Rough estimate default: return 64; // Default estimate } } /** * Get memory usage report */ getMemoryReport() { const stats = this.getStats(); const state = this.getState(); const config = this.configManager.getConfig(); return ` Memory Manager Report: ===================== Current Usage: ${this.formatBytes(stats.currentUsage)} Peak Usage: ${this.formatBytes(stats.peakUsage)} Total Allocated: ${this.formatBytes(stats.totalAllocated)} Total Freed: ${this.formatBytes(stats.totalFreed)} Memory Pressure: ${(stats.pressure * 100).toFixed(1)}% GC Count: ${stats.gcCount} Average GC Time: ${stats.averageGCTime.toFixed(2)}ms Tracked Objects: ${stats.trackedObjects} Memory Pools: ${this.pools.size} Leak Count: ${stats.leakCount} Configuration: - Max Memory: ${this.formatBytes(config.maxMemory)} - GC Threshold: ${(config.gcThreshold * 100).toFixed(1)}% - GC Interval: ${(config.gcInterval / 1000).toFixed(1)}s - Leak Detection: ${config.enableLeakDetection ? "Enabled" : "Disabled"} - Performance Monitoring: ${config.enablePerformanceMonitoring ? "Enabled" : "Disabled"} State: - Running: ${state.isRunning} - Uptime: ${this.formatDuration(Date.now() - state.uptime)} - Active Monitors: ${state.activeMonitors} - Error Count: ${state.errorCount} `.trim(); } /** * Format bytes for human-readable output */ formatBytes(bytes) { const units = ["B", "KB", "MB", "GB"]; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } /** * Format duration for human-readable output */ formatDuration(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`; if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`; if (minutes > 0) return `${minutes}m ${seconds % 60}s`; return `${seconds}s`; } /** * Update configuration */ updateConfig(updates) { try { this.configManager.updateConfig(updates); // Update components with new configuration const newConfig = this.configManager.getConfig(); this.referenceTracker.updateConfig(newConfig); this.eventManager.updateConfig(newConfig); // Restart monitoring if intervals changed if (updates.gcInterval !== undefined) { this.startGCMonitoring(); } if (updates.autoCleanupInterval !== undefined) { this.startCleanupMonitoring(); } } catch (error) { this.handleError("updateConfig", error); throw error; } } /** * Get configuration */ getConfig() { return this.configManager.getConfig(); } /** * Add event listener */ on(type, listener) { this.eventManager.on(type, listener); } /** * Remove event listener */ off(type, listener) { this.eventManager.off(type, listener); } /** * Stop the memory manager */ stop() { if (!this.state.isRunning) { return; } this.state.isRunning = false; // Clear all intervals if (this.gcInterval) { clearInterval(this.gcInterval); this.gcInterval = undefined; } if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = undefined; } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } this.state.activeMonitors = 0; this.eventManager.emit(types.MemoryEventType.CONFIG_UPDATED, { action: "stopped", timestamp: Date.now(), }); } /** * Restart the memory manager */ restart() { this.stop(); this.start(); } /** * Destroy the memory manager and clean up all resources */ destroy() { this.stop(); // Destroy all pools for (const [, pool] of this.pools.entries()) { if ("destroy" in pool && typeof pool.destroy === "function") { pool.destroy(); } } this.pools.clear(); // Destroy components this.referenceTracker.destroy(); this.eventManager.destroy(); // Reset singleton instance AdvancedMemoryManager.instance = null; } /** * Initialize object collection tracking system */ initializeObjectTracking() { // Initialize FinalizationRegistry if available if (typeof FinalizationRegistry !== "undefined") { this.finalizationRegistry = new FinalizationRegistry((objectId) => { this.handleObjectFinalization(objectId); }); } // Set up periodic object tracking cleanup setInterval(() => { this.cleanupObjectTracker(); }, 60000); // Every minute } /** * Track object collection during garbage collection */ trackObjectCollection(beforeUsage, afterUsage) { const memoryFreed = beforeUsage - afterUsage; const gcStartTime = Date.now(); // Count objects that are no longer reachable let objectsCollected = 0; const typesCollected = new Map(); // Check weak references to see which objects were collected for (const [objectId, tracker] of this.objectTracker.entries()) { let collectedCount = 0; // Filter out collected weak references tracker.weakRefs = tracker.weakRefs.filter((weakRef) => { const obj = weakRef.deref(); if (obj === undefined) { collectedCount++; return false; } return true; }); if (collectedCount > 0) { objectsCollected += collectedCount; tracker.count -= collectedCount; // Track by type const currentTypeCount = typesCollected.get(tracker.type) || 0; typesCollected.set(tracker.type, currentTypeCount + collectedCount); // Remove tracker if no objects remain if (tracker.count <= 0) { this.objectTracker.delete(objectId); } } } // Estimate objects collected based on memory freed if no direct tracking if (objectsCollected === 0 && memoryFreed > 0) { // Rough estimate: assume average object size of 1KB objectsCollected = Math.floor(memoryFreed / 1024); } // Record collection history const gcDuration = Date.now() - gcStartTime; this.collectionHistory.push({ timestamp: Date.now(), objectsCollected, typesCollected, memoryFreed, gcDuration, }); // Limit history size if (this.collectionHistory.length > 100) { this.collectionHistory.shift(); } return objectsCollected; } /** * Register an object for tracking */ trackObject(obj, type = "unknown", estimatedSize = 0) { const objectId = this.generateObjectId(); let tracker = this.objectTracker.get(type); if (!tracker) { tracker = { count: 0, totalSize: 0, lastSeen: Date.now(), type, weakRefs: [], }; this.objectTracker.set(type, tracker); } // Create weak reference to track the object const weakRef = new WeakRef(obj); tracker.weakRefs.push(weakRef); tracker.count++; tracker.totalSize += estimatedSize; tracker.lastSeen = Date.now(); // Register with FinalizationRegistry if available if (this.finalizationRegistry) { this.finalizationRegistry.register(obj, objectId); } return objectId; } /** * Handle object finalization */ handleObjectFinalization(objectId) { // This is called when an object is finalized by the garbage collector // We can use this to get more accurate collection statistics console.debug(`Object finalized: ${objectId}`); } /** * Clean up object tracker by removing stale entries */ cleanupObjectTracker() { const now = Date.now(); const staleThreshold = 300000; // 5 minutes for (const [type, tracker] of this.objectTracker.entries()) { // Remove stale weak references tracker.weakRefs = tracker.weakRefs.filter((weakRef) => { return weakRef.deref() !== undefined; }); // Update count based on remaining weak references tracker.count = tracker.weakRefs.length; // Remove tracker if no objects remain and it's stale if (tracker.count === 0 && now - tracker.lastSeen > staleThreshold) { this.objectTracker.delete(type); } } } /** * Generate unique object ID */ generateObjectId() { return `obj_${Date.now()}_${Math.random() .toString(36) .substring(2, 11)}`; } /** * Get object collection statistics */ getObjectCollectionStats() { const totalTracked = Array.from(this.objectTracker.values()).reduce((sum, tracker) => sum + tracker.count, 0); const byType = new Map(); for (const [type, tracker] of this.objectTracker.entries()) { byType.set(type, { count: tracker.count, totalSize: tracker.totalSize, }); } // Calculate average collection rate from recent history const recentCollections = this.collectionHistory.slice(-10); const averageCollectionRate = recentCollections.length > 0 ? recentCollections.reduce((sum, entry) => sum + entry.objectsCollected, 0) / recentCollections.length : 0; return { totalTracked, byType, recentCollections: this.collectionHistory.slice(-20), // Last 20 collections averageCollectionRate, }; } } exports.AdvancedMemoryManager = AdvancedMemoryManager; //# sourceMappingURL=memory-manager.js.map