UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

144 lines (143 loc) 5.16 kB
import logger from '../logger.js'; export class ImportCycleBreaker { static importStack = new Set(); static importHistory = new Map(); static IMPORT_TIMEOUT = 5000; static HISTORY_CLEANUP_INTERVAL = 60000; static cleanupTimer; static async safeImport(modulePath, importName) { const importKey = importName ? `${modulePath}:${importName}` : modulePath; if (this.importStack.has(importKey)) { logger.warn({ modulePath, importName, currentStack: Array.from(this.importStack) }, 'Circular import detected, using fallback'); this.recordImportAttempt(importKey, false); return null; } const recentAttempt = this.importHistory.get(importKey); if (recentAttempt && !recentAttempt.success && (Date.now() - recentAttempt.timestamp) < this.IMPORT_TIMEOUT) { logger.debug({ modulePath, importName, lastAttempt: recentAttempt.timestamp }, 'Skipping recent failed import attempt'); return null; } this.importStack.add(importKey); try { logger.debug({ modulePath, importName }, 'Starting safe import'); const importPromise = this.performImport(modulePath, importName); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Import timeout')), this.IMPORT_TIMEOUT); }); const module = await Promise.race([importPromise, timeoutPromise]); logger.debug({ modulePath, importName }, 'Safe import completed successfully'); this.recordImportAttempt(importKey, true); return module; } catch (error) { logger.warn({ err: error, modulePath, importName }, 'Safe import failed'); this.recordImportAttempt(importKey, false); return null; } finally { this.importStack.delete(importKey); this.startCleanupTimer(); } } static async performImport(modulePath, importName) { const module = await import(modulePath); if (importName) { if (!(importName in module)) { throw new Error(`Export '${importName}' not found in module '${modulePath}'`); } return module[importName]; } return module; } static recordImportAttempt(importKey, success) { this.importHistory.set(importKey, { timestamp: Date.now(), success }); } static startCleanupTimer() { if (this.cleanupTimer) { return; } this.cleanupTimer = setTimeout(() => { this.cleanupImportHistory(); this.cleanupTimer = undefined; }, this.HISTORY_CLEANUP_INTERVAL); } static cleanupImportHistory() { const now = Date.now(); const cutoff = now - (this.HISTORY_CLEANUP_INTERVAL * 2); let cleanedCount = 0; for (const [key, entry] of this.importHistory.entries()) { if (entry.timestamp < cutoff) { this.importHistory.delete(key); cleanedCount++; } } if (cleanedCount > 0) { logger.debug({ cleanedCount }, 'Cleaned up old import history entries'); } } static getCurrentImportStack() { return Array.from(this.importStack); } static getImportHistory() { const history = {}; for (const [key, value] of this.importHistory.entries()) { history[key] = value; } return history; } static clearAll() { this.importStack.clear(); this.importHistory.clear(); if (this.cleanupTimer) { clearTimeout(this.cleanupTimer); this.cleanupTimer = undefined; } } static isImportInProgress(modulePath, importName) { const importKey = importName ? `${modulePath}:${importName}` : modulePath; return this.importStack.has(importKey); } static getStatistics() { let successfulImports = 0; let failedImports = 0; for (const entry of this.importHistory.values()) { if (entry.success) { successfulImports++; } else { failedImports++; } } return { currentImports: this.importStack.size, historyEntries: this.importHistory.size, successfulImports, failedImports }; } static createModuleImporter(modulePath) { return async (importName) => { return this.safeImport(modulePath, importName); }; } static async safeBatchImport(imports) { const results = await Promise.allSettled(imports.map(({ modulePath, importName }) => this.safeImport(modulePath, importName))); return results.map(result => result.status === 'fulfilled' ? result.value : null); } }