"use client"; /** * The derived sync key is kept in IndexedDB as a **non-extractable** * CryptoKey: it can encrypt/decrypt for this origin but its raw bytes can * never be read out (not even by a script that got in). If the browser * doesn't allow that, syncing asks for the passphrase again instead. */ const DB_NAME = "freezmo-sync"; const STORE = "keys"; const KEY_ID = "device-key"; function openDb(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, 1); request.onupgradeneeded = () => { request.result.createObjectStore(STORE); }; request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async function withStore(mode: IDBTransactionMode, run: (store: IDBObjectStore) => IDBRequest): Promise { const db = await openDb(); try { return await new Promise((resolve, reject) => { const transaction = db.transaction(STORE, mode); const request = run(transaction.objectStore(STORE)); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } finally { db.close(); } } export async function saveDeviceKey(key: CryptoKey): Promise { try { await withStore("readwrite", (store) => store.put(key, KEY_ID)); return true; } catch { return false; } } export async function loadDeviceKey(): Promise { try { const key = await withStore("readonly", (store) => store.get(KEY_ID)); return key ?? null; } catch { return null; } } export async function clearDeviceKey(): Promise { try { await withStore("readwrite", (store) => store.delete(KEY_ID)); } catch { /* ignore */ } }