import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import { existsSync } from 'node:fs'; import * as crypto from 'node:crypto'; export class FileCacheAdapter { private cacheDir: string; private prefix: string; private defaultTTL: number; constructor(config?: { cacheDir?: string; prefix?: string; defaultTTL?: number }) { this.cacheDir = config?.cacheDir || path.resolve(process.cwd(), '.cache'); this.prefix = config?.prefix || ''; this.defaultTTL = config?.defaultTTL || 3600; if (!existsSync(this.cacheDir)) { fs.mkdir(this.cacheDir, { recursive: true }).catch(() => { }); } } private sanitize(key: string): string { const clean = key.replace(/[^a-z0-9]/gi, '_'); if (clean.length > 64) { return crypto.createHash('md5').update(key).digest('hex'); } return clean; } private getFilePath(key: string): string { const safeKey = this.sanitize(`${this.prefix}${key}`).toLowerCase(); return path.join(this.cacheDir, `${safeKey}.json`); } async get(key: string): Promise { const filePath = this.getFilePath(key); try { if (!existsSync(filePath)) return null; const content = await fs.readFile(filePath, 'utf8'); const data = JSON.parse(content); if (data.expiry && Date.now() > data.expiry) { await this.del(key); return null; } return data.value; } catch (_error) { return null; // Keeping original return type Promise } } async set(key: string, value: T, ttl?: number): Promise { const filePath = this.getFilePath(key); const effectiveTTL = ttl !== undefined ? ttl : this.defaultTTL; const data = { value, expiry: effectiveTTL > 0 ? Date.now() + effectiveTTL * 1000 : null, updatedAt: new Date().toISOString() }; try { await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf8'); } catch (_error) { // Ignore } } async del(key: string): Promise { const filePath = this.getFilePath(key); try { if (existsSync(filePath)) { await fs.unlink(filePath); } } catch (_error) { // Ignored - best effort cleanup } } }