import { clearConvKeyCache } from '@chat-app/shared/chat'; import { devLocalSecretStore } from './secretStore'; // Aggressively scrub local crypto + chat caches on sign-out (and on // app-close if the user opted in). Preserves things that aren't sensitive // and would be annoying to lose (theme, locale, install-id). // // We can't enumerate IndexedDB names without async + the indexedDB API, // so we list the ones we know about explicitly. Adding a new local store // later? Append to LOCAL_DBS. const LOCAL_DBS = ['soundboard', 'message-cache', 'chatapp-attachments']; const PRESERVE_LOCAL_STORAGE = new Set([ 'chatapp.theme', 'chatapp.locale', 'chatapp.installId', 'chatapp.wipeOnClose.v1', 'chatapp.autoLockMinutes.v1', 'i18nextLng', ]); export async function wipeLocalState(userId: string | null): Promise { // 1. Per-conversation key cache (in-memory). try { clearConvKeyCache(); } catch { /* never throws but be defensive */ } // 2. Stronghold / secret-store: drop the user-priv blob for this user. if (userId) { try { await devLocalSecretStore.removeSecret('chatapp.userpriv.' + userId); } catch (err) { console.warn('[wipe] userpriv remove failed', err); } } // 3. localStorage — preserve only the explicit whitelist. try { const keysToDrop: string[] = []; for (let i = 0; i < window.localStorage.length; i++) { const k = window.localStorage.key(i); if (k && !PRESERVE_LOCAL_STORAGE.has(k)) keysToDrop.push(k); } for (const k of keysToDrop) window.localStorage.removeItem(k); } catch (err) { console.warn('[wipe] localStorage clear failed', err); } // 4. sessionStorage — always full. try { window.sessionStorage.clear(); } catch { /* ignored */ } // 5. IndexedDB — delete known databases. Resolves even when blocked so // we don't hang sign-out forever. await Promise.allSettled(LOCAL_DBS.map((name) => new Promise((resolve) => { try { const req = window.indexedDB.deleteDatabase(name); req.onsuccess = () => resolve(); req.onerror = () => resolve(); req.onblocked = () => resolve(); } catch { resolve(); } }))); }