import { idbGet, idbSet, idbRemove } from './indexedDbStorage'; /** * Persistent cache for seen hashes (IndexedDB-backed). * Call init() before use. */ export class HashCache { private cache: Set = new Set(); private storageKey: string; private initialized = false; constructor(storageKey: string = 'userwallet_hash_cache') { this.storageKey = storageKey; } /** * Load cache from IndexedDB. Must be called before hasSeen / markSeen / markSeenBatch. */ async init(): Promise { if (this.initialized) { return; } await this.loadFromStorage(); this.initialized = true; } /** * Check if a hash has been seen. */ hasSeen(hash: string): boolean { return this.cache.has(hash); } /** * Mark a hash as seen (in-memory only). Use markSeenBatch to persist. */ markSeen(hash: string): void { this.cache.add(hash); } /** * Mark multiple hashes as seen and persist to IndexedDB. */ async markSeenBatch(hashes: string[]): Promise { for (const hash of hashes) { this.cache.add(hash); } await this.saveToStorage(); } /** * Clear the cache and remove from IndexedDB. */ async clear(): Promise { this.cache.clear(); await idbRemove(this.storageKey); } /** * Get cache size. */ size(): number { return this.cache.size; } private async loadFromStorage(): Promise { let stored = await idbGet(this.storageKey); if (stored === null && typeof localStorage !== 'undefined') { const legacy = localStorage.getItem(this.storageKey); if (legacy !== null) { await idbSet(this.storageKey, legacy); localStorage.removeItem(this.storageKey); stored = legacy; } } if (stored !== null) { const hashes = JSON.parse(stored) as string[]; this.cache = new Set(hashes); } } private async saveToStorage(): Promise { let hashes = Array.from(this.cache); if (hashes.length > 10000) { hashes = hashes.slice(-10000); this.cache = new Set(hashes); } await idbSet(this.storageKey, JSON.stringify(hashes)); } }