UNPKG

ts-project-indexer-mcp

Version:

A powerful MCP (Model Context Protocol) server that indexes and analyzes TypeScript/JavaScript projects, providing intelligent code exploration, dependency tracking, method discovery, and project structure analysis for AI assistants and development tools.

131 lines 4.05 kB
import { promises as fs } from 'node:fs'; import { join } from 'node:path'; import { logger } from '../mcp-server.js'; export class CacheManager { cache = new Map(); cacheDir; defaultTtl = 1000 * 60 * 60 * 24; // 24 hours constructor(cacheDir = '.cache') { this.cacheDir = cacheDir; } async init() { try { await fs.mkdir(this.cacheDir, { recursive: true }); } catch (error) { // Directory already exists or other error, continue } } async get(key) { // Check memory cache first const memoryEntry = this.cache.get(key); if (memoryEntry && !this.isExpired(memoryEntry)) { return memoryEntry.data; } // Check disk cache try { const filePath = join(this.cacheDir, `${this.sanitizeKey(key)}.json`); const fileContent = await fs.readFile(filePath, 'utf-8'); const entry = JSON.parse(fileContent, (key, value) => { // Revive Date objects if (key === 'timestamp' || key === 'lastModified') { return new Date(value); } return value; }); if (!this.isExpired(entry)) { // Cache in memory for faster access this.cache.set(key, entry); return entry.data; } else { // Remove expired file await this.delete(key); } } catch (error) { // File doesn't exist or is corrupted } return null; } async set(key, data, ttl) { const entry = { data, timestamp: new Date(), ttl: ttl || this.defaultTtl, }; // Store in memory this.cache.set(key, entry); // Store on disk try { const filePath = join(this.cacheDir, `${this.sanitizeKey(key)}.json`); await fs.writeFile(filePath, JSON.stringify(entry, null, 2)); } catch (error) { logger.error(`Failed to write cache file for key ${key}: ${error}`); } } async delete(key) { // Remove from memory this.cache.delete(key); // Remove from disk try { const filePath = join(this.cacheDir, `${this.sanitizeKey(key)}.json`); await fs.unlink(filePath); } catch (error) { // File doesn't exist, ignore } } async clear() { // Clear memory cache this.cache.clear(); // Clear disk cache try { const files = await fs.readdir(this.cacheDir); await Promise.all(files .filter(file => file.endsWith('.json')) .map(file => fs.unlink(join(this.cacheDir, file)))); } catch (error) { // Directory doesn't exist or other error } } async has(key) { const data = await this.get(key); return data !== null; } async getFileChecksum(filePath) { try { const stats = await fs.stat(filePath); return `${stats.size}-${stats.mtime.getTime()}`; } catch (error) { return null; } } async isFileChanged(filePath, cachedChecksum) { const currentChecksum = await this.getFileChecksum(filePath); return currentChecksum !== cachedChecksum; } isExpired(entry) { if (!entry.ttl) return false; const now = Date.now(); const entryTime = entry.timestamp.getTime(); return now - entryTime > entry.ttl; } sanitizeKey(key) { return key .replace(/[^a-zA-Z0-9]/g, '_') .replace(/_+/g, '_') .substring(0, 200); // Limit filename length } getStats() { return { memoryEntries: this.cache.size, cacheDir: this.cacheDir, }; } } //# sourceMappingURL=cache-manager.js.map