const DB_NAME = 'modrinth-moderation' const DB_VERSION = 2 function hasIndexedDb(): boolean { return typeof window !== 'undefined' && typeof indexedDB !== 'undefined' } function openDatabase(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION) request.onupgradeneeded = (event) => { const db = request.result if (event.oldVersion < 2 && db.objectStoreNames.contains('kv')) { db.deleteObjectStore('kv') } if (!db.objectStoreNames.contains('checklist')) { db.createObjectStore('checklist') } if (!db.objectStoreNames.contains('queue')) { db.createObjectStore('queue') } } request.onsuccess = () => resolve(request.result) request.onerror = () => reject(request.error ?? new Error('Failed to open IndexedDB')) request.onblocked = () => reject(new Error('IndexedDB open request blocked')) }) } function requestToPromise(request: IDBRequest): Promise { return new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result) request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed')) }) } export async function dbGet(store: string, key: IDBValidKey): Promise { if (!hasIndexedDb()) return null const db = await openDatabase() try { const tx = db.transaction(store, 'readonly') const result = await requestToPromise(tx.objectStore(store).get(key)) return result ?? null } finally { db.close() } } export async function dbPut(store: string, key: IDBValidKey, value: T): Promise { if (!hasIndexedDb()) return const db = await openDatabase() try { const tx = db.transaction(store, 'readwrite') tx.objectStore(store).put(value, key) await new Promise((resolve, reject) => { tx.oncomplete = () => resolve() tx.onerror = () => reject(tx.error ?? new Error('IndexedDB transaction failed')) }) } finally { db.close() } } export async function dbDelete(store: string, key: IDBValidKey): Promise { if (!hasIndexedDb()) return const db = await openDatabase() try { const tx = db.transaction(store, 'readwrite') tx.objectStore(store).delete(key) await new Promise((resolve, reject) => { tx.oncomplete = () => resolve() tx.onerror = () => reject(tx.error ?? new Error('IndexedDB transaction failed')) }) } finally { db.close() } } export async function dbScan(store: string): Promise<{ key: IDBValidKey; value: T }[]> { if (!hasIndexedDb()) return [] const db = await openDatabase() try { const tx = db.transaction(store, 'readonly') const s = tx.objectStore(store) const [keys, values] = await Promise.all([ requestToPromise(s.getAllKeys()), requestToPromise(s.getAll()), ]) return keys.map((key, i) => ({ key, value: values[i] as T })) } finally { db.close() } }