Skip to main content
Glama

Vibe Coder MCP

by freshtechbro
import-cycle-breaker.ts•7.2 kB
/** * Import Cycle Breaker Utility * * Detects and breaks circular import dependencies by tracking import stack * and providing safe fallbacks when circular imports are detected. * * This utility helps prevent infinite recursion during dynamic module loading * by maintaining a stack of currently importing modules and detecting cycles. */ import logger from '../logger.js'; /** * Import cycle detection and breaking utility */ export class ImportCycleBreaker { private static importStack = new Set<string>(); private static importHistory = new Map<string, { timestamp: number; success: boolean }>(); private static readonly IMPORT_TIMEOUT = 5000; // 5 seconds private static readonly HISTORY_CLEANUP_INTERVAL = 60000; // 1 minute private static cleanupTimer?: NodeJS.Timeout; /** * Safely import a module with circular dependency detection * * @param modulePath - The path to the module to import * @param importName - The specific export to import from the module * @returns The imported module/export or null if circular dependency detected */ static async safeImport<T>(modulePath: string, importName?: string): Promise<T | null> { const importKey = importName ? `${modulePath}:${importName}` : modulePath; // Check if this import is already in progress (circular dependency) 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; } // Check recent failed imports to avoid repeated failures 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; } // Add to import stack this.importStack.add(importKey); try { logger.debug({ modulePath, importName }, 'Starting safe import'); // Set timeout for import operation const importPromise = this.performImport<T>(modulePath, importName); const timeoutPromise = new Promise<never>((_, 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 { // Remove from import stack this.importStack.delete(importKey); // Start cleanup timer if not already running this.startCleanupTimer(); } } /** * Perform the actual module import */ private static async performImport<T>(modulePath: string, importName?: string): Promise<T> { 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; } /** * Record import attempt for history tracking */ private static recordImportAttempt(importKey: string, success: boolean): void { this.importHistory.set(importKey, { timestamp: Date.now(), success }); } /** * Start cleanup timer to remove old import history entries */ private static startCleanupTimer(): void { if (this.cleanupTimer) { return; } this.cleanupTimer = setTimeout(() => { this.cleanupImportHistory(); this.cleanupTimer = undefined; }, this.HISTORY_CLEANUP_INTERVAL); } /** * Clean up old import history entries */ private static cleanupImportHistory(): void { const now = Date.now(); const cutoff = now - (this.HISTORY_CLEANUP_INTERVAL * 2); // Keep 2 intervals worth 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'); } } /** * Get current import stack for debugging */ static getCurrentImportStack(): string[] { return Array.from(this.importStack); } /** * Get import history for debugging */ static getImportHistory(): Record<string, { timestamp: number; success: boolean }> { const history: Record<string, { timestamp: number; success: boolean }> = {}; for (const [key, value] of this.importHistory.entries()) { history[key] = value; } return history; } /** * Clear all import tracking (useful for testing) */ static clearAll(): void { this.importStack.clear(); this.importHistory.clear(); if (this.cleanupTimer) { clearTimeout(this.cleanupTimer); this.cleanupTimer = undefined; } } /** * Check if a specific import is currently in progress */ static isImportInProgress(modulePath: string, importName?: string): boolean { const importKey = importName ? `${modulePath}:${importName}` : modulePath; return this.importStack.has(importKey); } /** * Get statistics about import operations */ static getStatistics(): { currentImports: number; historyEntries: number; successfulImports: number; failedImports: number; } { 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 }; } /** * Create a safe import wrapper for a specific module * Useful for creating module-specific import functions */ static createModuleImporter(modulePath: string) { return async <T>(importName?: string): Promise<T | null> => { return this.safeImport<T>(modulePath, importName); }; } /** * Batch import multiple modules safely * Returns results in the same order as input, with null for failed imports */ static async safeBatchImport<T>( imports: Array<{ modulePath: string; importName?: string }> ): Promise<Array<T | null>> { const results = await Promise.allSettled( imports.map(({ modulePath, importName }) => this.safeImport<T>(modulePath, importName) ) ); return results.map(result => result.status === 'fulfilled' ? result.value : null ); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server