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.

190 lines (189 loc) 5.27 kB
import logger from '../../../logger.js'; export class LRUCache { name; map = new Map(); head = null; tail = null; size = 0; totalSize = 0; hits = 0; misses = 0; evictions = 0; options; static DEFAULT_OPTIONS = { maxEntries: 1000, maxAge: 60 * 60 * 1000, sizeCalculator: () => 1, maxSize: 100000 }; constructor(options) { this.name = options.name; this.options = { ...LRUCache.DEFAULT_OPTIONS, name: options.name, maxEntries: options.maxEntries ?? LRUCache.DEFAULT_OPTIONS.maxEntries, maxAge: options.maxAge ?? LRUCache.DEFAULT_OPTIONS.maxAge, sizeCalculator: options.sizeCalculator ?? LRUCache.DEFAULT_OPTIONS.sizeCalculator, maxSize: options.maxSize ?? LRUCache.DEFAULT_OPTIONS.maxSize, dispose: options.dispose ?? ((_key, _value) => { }) }; logger.debug(`Created LRU cache "${this.name}" with max entries: ${this.options.maxEntries}, max size: ${this.options.maxSize}`); } get(key) { const entry = this.map.get(key); if (!entry) { this.misses++; return undefined; } if (entry.expiry < Date.now()) { this.delete(key); this.misses++; return undefined; } this.moveToFront(entry); this.hits++; return entry.value; } set(key, value, ttl) { const existingEntry = this.map.get(key); if (existingEntry) { this.totalSize -= existingEntry.size; existingEntry.value = value; existingEntry.timestamp = Date.now(); existingEntry.expiry = Date.now() + (ttl ?? this.options.maxAge); existingEntry.size = this.options.sizeCalculator(value); this.totalSize += existingEntry.size; this.moveToFront(existingEntry); } else { const now = Date.now(); const entry = { key, value, timestamp: now, expiry: now + (ttl ?? this.options.maxAge), size: this.options.sizeCalculator(value), prev: null, next: null }; this.map.set(key, entry); this.addToFront(entry); this.size++; this.totalSize += entry.size; } this.prune(); } has(key) { const entry = this.map.get(key); if (!entry) { return false; } if (entry.expiry < Date.now()) { this.delete(key); return false; } return true; } delete(key) { const entry = this.map.get(key); if (!entry) { return false; } this.map.delete(key); this.removeFromList(entry); this.size--; this.totalSize -= entry.size; this.options.dispose(key, entry.value); return true; } clear() { for (const [key, entry] of this.map.entries()) { this.options.dispose(key, entry.value); } this.map.clear(); this.head = null; this.tail = null; this.size = 0; this.totalSize = 0; logger.debug(`Cleared LRU cache "${this.name}"`); } getSize() { return this.size; } getTotalSize() { return this.totalSize; } getMaxEntries() { return this.options.maxEntries; } getStats() { return { size: this.size, totalSize: this.totalSize, hits: this.hits, misses: this.misses, evictions: this.evictions }; } prune() { while (this.size > this.options.maxEntries) { this.evictLRU(); } while (this.totalSize > this.options.maxSize && this.size > 0) { this.evictLRU(); } } evictLRU() { if (!this.tail) { return; } const key = this.tail.key; this.delete(key); this.evictions++; } addToFront(entry) { if (!this.head) { this.head = entry; this.tail = entry; } else { entry.next = this.head; this.head.prev = entry; this.head = entry; } } moveToFront(entry) { if (entry === this.head) { return; } this.removeFromList(entry); this.addToFront(entry); } removeFromList(entry) { if (entry === this.head) { this.head = entry.next; if (this.head) { this.head.prev = null; } } else if (entry === this.tail) { this.tail = entry.prev; if (this.tail) { this.tail.next = null; } } else { if (entry.prev) { entry.prev.next = entry.next; } if (entry.next) { entry.next.prev = entry.prev; } } if (entry === this.tail) { this.tail = entry.prev; } entry.prev = null; entry.next = null; } }