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.
377 lines (374 loc) • 13.1 kB
JavaScript
import { EventEmitter } from 'events';
import { NehoID } from 'nehoid';
import { PluginEventType, PluginType } from './types/PluginTypes.js';
/**
* Ultra-Fast Plugin Execution Engine
*
* High-performance plugin execution engine designed to achieve <1ms overhead
* while maintaining security and comprehensive error handling.
*/
/**
* Ultra-fast plugin execution engine with intelligent optimization
*/
class PluginEngine extends EventEmitter {
constructor(registry, cache, cluster) {
super();
this.executionPool = new Map();
this.warmupCache = new Map();
// Performance optimization: Object pooling for contexts
this.contextPool = [];
this.MAX_POOL_SIZE = 100;
// Circuit breaker for failing plugins
this.circuitBreakers = new Map();
this.registry = registry;
this.cache = cache;
this.cluster = cluster;
// Listen to registry events
this.setupRegistryEventHandlers();
}
/**
* Execute plugins for a specific type with ultra-fast performance
*/
async executePlugins(type, req, res, next) {
const startTime = performance.now();
const executionId = NehoID.generate({ prefix: "plug.exec", size: 8 });
try {
// Get plugins for this type (pre-sorted by priority)
const plugins = this.registry.getPluginsByType(type);
if (plugins.length === 0) {
return true; // Continue execution if no plugins
}
// Create or reuse execution context
const context = this.createExecutionContext(req, res, next, executionId, startTime);
// Execute plugins based on type strategy
const success = await this.executePluginChain(plugins, context);
// Update performance metrics
const totalExecutionTime = performance.now() - startTime;
this.updatePerformanceMetrics(type, totalExecutionTime, success);
// Return context to pool
this.returnContextToPool(context);
return success;
}
catch (error) {
const executionTime = performance.now() - startTime;
this.handleExecutionError(type, executionId, error, executionTime);
return false;
}
}
/**
* Execute a single plugin with comprehensive error handling
*/
async executePlugin(plugin, context) {
const startTime = performance.now();
try {
// Check circuit breaker
if (this.isCircuitBreakerOpen(plugin.id)) {
return {
success: false,
executionTime: 0,
error: new Error(`Circuit breaker open for plugin ${plugin.id}`),
shouldContinue: true,
};
}
// Warm up plugin if needed
await this.warmupPlugin(plugin, context);
// Execute plugin with timeout
const result = await this.executeWithTimeout(plugin, context);
const executionTime = performance.now() - startTime;
result.executionTime = executionTime;
// Update plugin statistics
this.registry.updateStats(plugin.id, executionTime, result.success);
// Reset circuit breaker on success
if (result.success) {
this.resetCircuitBreaker(plugin.id);
}
else {
this.updateCircuitBreaker(plugin.id);
}
// Emit execution event
this.emitPluginEvent(PluginEventType.PLUGIN_EXECUTED, plugin.id, {
executionTime,
success: result.success,
type: plugin.type,
});
return result;
}
catch (error) {
const executionTime = performance.now() - startTime;
// Update circuit breaker
this.updateCircuitBreaker(plugin.id);
// Update error statistics
this.registry.updateStats(plugin.id, executionTime, false);
// Emit error event
this.emitPluginEvent(PluginEventType.PLUGIN_ERROR, plugin.id, {
error: error.message,
executionTime,
type: plugin.type,
});
return {
success: false,
executionTime,
error,
shouldContinue: true,
};
}
}
/**
* Execute plugin chain with intelligent optimization
*/
async executePluginChain(plugins, context) {
// For critical performance plugins, execute in parallel
if (plugins.length > 0 && plugins[0].type === PluginType.PERFORMANCE) {
return await this.executePluginsParallel(plugins, context);
}
// For security and cache plugins, execute sequentially
return await this.executePluginsSequential(plugins, context);
}
/**
* Execute plugins sequentially (for security, cache operations)
*/
async executePluginsSequential(plugins, context) {
for (const plugin of plugins) {
const result = await this.executePlugin(plugin, context);
if (!result.success && plugin.type === PluginType.SECURITY) {
// Security plugins must succeed
return false;
}
if (!result.shouldContinue) {
// Plugin requested to stop execution
return false;
}
// Store plugin result data
if (result.data) {
context.pluginData.set(plugin.id, result.data);
}
// Handle cache data
if (result.cacheData) {
await this.cache.set(result.cacheData.key, result.cacheData.value, { ttl: result.cacheData.ttl });
}
}
return true;
}
/**
* Execute plugins in parallel (for performance monitoring)
*/
async executePluginsParallel(plugins, context) {
const promises = plugins.map((plugin) => this.executePlugin(plugin, context));
const results = await Promise.allSettled(promises);
let allSuccessful = true;
results.forEach((result, index) => {
if (result.status === "fulfilled") {
const pluginResult = result.value;
if (!pluginResult.success) {
allSuccessful = false;
}
// Store plugin result data
if (pluginResult.data) {
context.pluginData.set(plugins[index].id, pluginResult.data);
}
}
else {
allSuccessful = false;
}
});
return allSuccessful;
}
/**
* Execute plugin with timeout protection
*/
async executeWithTimeout(plugin, context) {
const timeoutMs = plugin.maxExecutionTime;
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.emitPluginEvent(PluginEventType.PLUGIN_TIMEOUT, plugin.id, {
timeout: timeoutMs,
type: plugin.type,
});
reject(new Error(`Plugin ${plugin.id} timed out after ${timeoutMs}ms`));
}, timeoutMs);
// Execute plugin
const execution = plugin.isAsync
? plugin.execute(context)
: Promise.resolve(plugin.execute(context));
execution
.then((result) => {
clearTimeout(timeout);
resolve(result);
})
.catch((error) => {
clearTimeout(timeout);
reject(error);
});
});
}
/**
* Create optimized execution context with object pooling
*/
createExecutionContext(req, res, next, executionId, startTime) {
// Try to reuse context from pool
let context = this.contextPool.pop();
if (!context) {
context = {
req,
res,
next,
startTime,
executionId,
cache: this.cache,
cluster: this.cluster,
pluginData: new Map(),
security: {
isAuthenticated: false,
roles: [],
permissions: [],
},
metrics: {
requestStartTime: startTime,
pluginExecutionTimes: new Map(),
cacheHits: 0,
cacheMisses: 0,
},
};
}
else {
// Reset reused context
context.req = req;
context.res = res;
context.next = next;
context.startTime = startTime;
context.executionId = executionId;
context.pluginData.clear();
context.metrics.pluginExecutionTimes.clear();
context.metrics.requestStartTime = startTime;
context.metrics.cacheHits = 0;
context.metrics.cacheMisses = 0;
}
return context;
}
/**
* Return context to pool for reuse
*/
returnContextToPool(context) {
if (this.contextPool.length < this.MAX_POOL_SIZE) {
// Clear sensitive data before returning to pool
context.pluginData.clear();
context.metrics.pluginExecutionTimes.clear();
this.contextPool.push(context);
}
}
/**
* Warm up plugin for optimal performance
*/
async warmupPlugin(plugin, context) {
if (this.warmupCache.has(plugin.id)) {
return; // Already warmed up
}
if (plugin.warmup) {
try {
await plugin.warmup(context);
this.warmupCache.set(plugin.id, true);
}
catch (error) {
// Warmup failure is not critical
console.warn(`Plugin ${plugin.id} warmup failed:`, error);
}
}
}
/**
* Circuit breaker management
*/
isCircuitBreakerOpen(pluginId) {
const breaker = this.circuitBreakers.get(pluginId);
if (!breaker)
return false;
// Reset circuit breaker after 60 seconds
if (breaker.isOpen && Date.now() - breaker.lastFailure > 60000) {
breaker.isOpen = false;
breaker.failures = 0;
}
return breaker.isOpen;
}
updateCircuitBreaker(pluginId) {
const breaker = this.circuitBreakers.get(pluginId) || {
failures: 0,
lastFailure: 0,
isOpen: false,
};
breaker.failures++;
breaker.lastFailure = Date.now();
// Open circuit breaker after 5 failures
if (breaker.failures >= 5) {
breaker.isOpen = true;
}
this.circuitBreakers.set(pluginId, breaker);
}
resetCircuitBreaker(pluginId) {
this.circuitBreakers.delete(pluginId);
}
/**
* Setup registry event handlers
*/
setupRegistryEventHandlers() {
this.registry.on(PluginEventType.PLUGIN_UNREGISTERED, (event) => {
// Clean up plugin-specific data
this.circuitBreakers.delete(event.pluginId);
this.warmupCache.delete(event.pluginId);
});
}
/**
* Update performance metrics
*/
updatePerformanceMetrics(type, executionTime, success) {
// Emit performance metrics for monitoring
this.emit("performance", {
type,
executionTime,
success,
timestamp: Date.now(),
});
}
/**
* Handle execution errors
*/
handleExecutionError(type, executionId, error, executionTime) {
console.error(`Plugin execution error [${type}] [${executionId}]:`, error);
this.emit("error", {
type,
executionId,
error,
executionTime,
timestamp: Date.now(),
});
}
/**
* Emit plugin event
*/
emitPluginEvent(type, pluginId, data) {
const event = {
type,
pluginId,
timestamp: new Date(),
data,
};
this.emit(type, event);
}
/**
* Get engine statistics (ultra-fast optimized)
*/
getEngineStats() {
// Ultra-fast: Count open circuit breakers without creating arrays
let circuitBreakersOpen = 0;
this.circuitBreakers.forEach((breaker) => {
if (breaker.isOpen)
circuitBreakersOpen++;
});
return {
contextPoolSize: this.contextPool.length,
circuitBreakersOpen,
warmedUpPlugins: this.warmupCache.size,
activeExecutions: this.executionPool.size,
};
}
}
export { PluginEngine };
//# sourceMappingURL=PluginEngine.js.map