/** * @ai-summary IndexedDB storage adapter for Auth0 and Zustand persistence * @ai-context Replaces localStorage with IndexedDB for mobile browser compatibility */ interface StorageAdapter { getItem(key: string): string | null; setItem(key: string, value: string): void; removeItem(key: string): void; clear(): void; key(index: number): string | null; readonly length: number; } interface Auth0Cache { get(key: string): Promise; set(key: string, value: any): Promise; remove(key: string): Promise; } class IndexedDBStorage implements StorageAdapter, Auth0Cache { private dbName = 'motovaultpro-storage'; private dbVersion = 1; private storeName = 'keyvalue'; private db: IDBDatabase | null = null; private memoryCache = new Map(); private initPromise: Promise; private isReady = false; constructor() { this.initPromise = this.initialize(); } private async initialize(): Promise { try { this.db = await this.openDatabase(); await this.loadCacheFromDB(); this.isReady = true; console.log('[IndexedDB] Storage initialized successfully'); } catch (error) { console.error('[IndexedDB] Initialization failed, using memory only:', error); this.isReady = false; } } private openDatabase(): Promise { return new Promise((resolve) => { const request = indexedDB.open(this.dbName, this.dbVersion); request.onerror = () => { console.error(`IndexedDB open failed: ${request.error?.message}`); resolve(null as any); // Fallback to memory-only mode }; request.onsuccess = () => { resolve(request.result); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName); } }; }); } private async loadCacheFromDB(): Promise { if (!this.db) return; return new Promise((resolve) => { const transaction = this.db!.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.getAll(); request.onsuccess = () => { const results = request.result; this.memoryCache.clear(); for (const item of results) { if (item.key && typeof item.value === 'string') { this.memoryCache.set(item.key, item.value); } } console.log(`[IndexedDB] Loaded ${this.memoryCache.size} items into cache`); resolve(); }; request.onerror = () => { console.warn('[IndexedDB] Failed to load cache from DB:', request.error); resolve(); // Don't fail initialization }; }); } private async persistToDB(key: string, value: string | null): Promise { if (!this.db) return; return new Promise((resolve) => { const transaction = this.db!.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); if (value === null) { const request = store.delete(key); request.onsuccess = () => resolve(); request.onerror = () => { console.warn(`[IndexedDB] Failed to delete ${key}:`, request.error); resolve(); }; } else { const request = store.put(value, key); request.onsuccess = () => resolve(); request.onerror = () => { console.warn(`[IndexedDB] Failed to persist ${key}:`, request.error); resolve(); }; } }); } // Synchronous Storage interface (uses memory cache) getItem(key: string): string | null { return this.memoryCache.get(key) || null; } setItem(key: string, value: string): void { this.memoryCache.set(key, value); // Async persist to IndexedDB (non-blocking) if (this.isReady) { this.persistToDB(key, value).catch(error => { console.warn(`[IndexedDB] Background persist failed for ${key}:`, error); }); } } removeItem(key: string): void { this.memoryCache.delete(key); // Async remove from IndexedDB (non-blocking) if (this.isReady) { this.persistToDB(key, null).catch(error => { console.warn(`[IndexedDB] Background removal failed for ${key}:`, error); }); } } clear(): void { this.memoryCache.clear(); // Async clear IndexedDB (non-blocking) if (this.db) { const transaction = this.db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); store.clear(); } } key(index: number): string | null { const keys = Array.from(this.memoryCache.keys()); return keys[index] || null; } get length(): number { return this.memoryCache.size; } // Auth0 Cache interface implementation async get(key: string): Promise { await this.initPromise; const value = this.getItem(key); return value ? JSON.parse(value) : undefined; } async set(key: string, value: any): Promise { await this.initPromise; this.setItem(key, JSON.stringify(value)); } async remove(key: string): Promise { await this.initPromise; this.removeItem(key); } // Additional methods for enhanced functionality async waitForReady(): Promise { return this.initPromise; } get isInitialized(): boolean { return this.isReady; } // For debugging getStats() { return { cacheSize: this.memoryCache.size, isReady: this.isReady, hasDB: !!this.db }; } } // Create singleton instance export const indexedDBStorage = new IndexedDBStorage(); // For Auth0 compatibility - ensure storage is ready before use export const createIndexedDBAdapter = () => { return indexedDBStorage; };