UNPKG

bc-symbols-mcp

Version:

MCP server for analyzing Business Central .app files and BC object structures

212 lines 5.9 kB
export class MemoryCache { cache = {}; maxAge; constructor(maxAgeMinutes = 60) { this.maxAge = maxAgeMinutes * 60 * 1000; // Convert to milliseconds } /** * Store an app in the cache */ set(filePath, app) { this.cache[filePath] = { data: app, timestamp: Date.now(), fileHash: app.fileHash }; } /** * Retrieve an app from the cache * Returns null if not found, expired, or file hash doesn't match */ get(filePath, currentFileHash) { const entry = this.cache[filePath]; if (!entry) { return null; } // Check if entry has expired if (Date.now() - entry.timestamp > this.maxAge) { delete this.cache[filePath]; return null; } // Check if file hash matches (file hasn't been modified) if (entry.fileHash !== currentFileHash) { delete this.cache[filePath]; return null; } return entry.data; } /** * Check if an app is cached and valid */ has(filePath, currentFileHash) { return this.get(filePath, currentFileHash) !== null; } /** * Remove an app from the cache */ delete(filePath) { if (this.cache[filePath]) { delete this.cache[filePath]; return true; } return false; } /** * Clear all cached apps */ clear() { this.cache = {}; } /** * Get all cached file paths */ getCachedPaths() { return Object.keys(this.cache); } /** * Get all cached apps that are still valid */ getValidCachedApps() { const now = Date.now(); const validApps = []; for (const [filePath, entry] of Object.entries(this.cache)) { // Check if entry has expired if (now - entry.timestamp > this.maxAge) { delete this.cache[filePath]; continue; } validApps.push(entry.data); } return validApps; } /** * Clean up expired entries */ cleanup() { const now = Date.now(); let removedCount = 0; for (const [filePath, entry] of Object.entries(this.cache)) { if (now - entry.timestamp > this.maxAge) { delete this.cache[filePath]; removedCount++; } } return removedCount; } /** * Get cache statistics */ getStats() { const now = Date.now(); let validEntries = 0; let expiredEntries = 0; let totalMemoryUsage = 0; for (const entry of Object.values(this.cache)) { if (now - entry.timestamp > this.maxAge) { expiredEntries++; } else { validEntries++; } // Rough estimate of memory usage totalMemoryUsage += JSON.stringify(entry.data).length; } return { totalEntries: Object.keys(this.cache).length, validEntries, expiredEntries, totalMemoryUsage }; } /** * Find cached apps by criteria */ findApps(predicate) { const validApps = this.getValidCachedApps(); return validApps.filter(predicate); } /** * Find a cached app by app ID */ findAppById(appId) { const validApps = this.getValidCachedApps(); return validApps.find(app => app.id === appId) || null; } /** * Find cached apps by publisher */ findAppsByPublisher(publisher) { return this.findApps(app => app.publisher === publisher); } /** * Find cached apps that depend on a specific app */ findAppsDependingOn(targetAppId) { return this.findApps(app => app.dependencies.some(dep => dep.id === targetAppId)); } /** * Get dependency tree for an app */ getDependencyTree(appId) { const app = this.findAppById(appId); if (!app) { return []; } const dependencies = new Set(); const visited = new Set(); const collectDependencies = (currentAppId) => { if (visited.has(currentAppId)) { return; // Avoid circular dependencies } visited.add(currentAppId); const currentApp = this.findAppById(currentAppId); if (currentApp) { for (const dep of currentApp.dependencies) { dependencies.add(dep.id); collectDependencies(dep.id); } } }; collectDependencies(appId); return Array.from(dependencies); } /** * Get reverse dependency tree (apps that depend on this app) */ getReverseDependencyTree(appId) { const dependents = new Set(); const visited = new Set(); const collectDependents = (targetAppId) => { if (visited.has(targetAppId)) { return; } visited.add(targetAppId); const dependentApps = this.findAppsDependingOn(targetAppId); for (const app of dependentApps) { dependents.add(app.id); collectDependents(app.id); } }; collectDependents(appId); return Array.from(dependents); } /** * Set cache expiration time */ setMaxAge(maxAgeMinutes) { this.maxAge = maxAgeMinutes * 60 * 1000; } /** * Check if cache is empty */ isEmpty() { return Object.keys(this.cache).length === 0; } /** * Get cache size in entries */ size() { return Object.keys(this.cache).length; } } //# sourceMappingURL=memory-cache.js.map