// Persists uploaded tables (and their optional ROM zip) in the browser via // IndexedDB, so a returning visitor can replay a table without re-uploading // it. Blobs are stored as-is (File objects are Blobs) rather than converted // to ArrayBuffer up front — IndexedDB clones Blobs without loading their // bytes into JS memory, so a large library doesn't sit resident until a // specific entry is actually played. const DB_NAME = "vpinball-library"; const DB_VERSION = 1; const STORE = "tables"; export interface LibraryEntry { id: string; name: string; sizeBytes: number; addedAt: number; vpxBlob: Blob; romFileName?: string; romBlob?: Blob; } export type LibraryEntryMeta = Omit; function openDb(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onupgradeneeded = () => { request.result.createObjectStore(STORE, { keyPath: "id" }); }; request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async function withStore(mode: IDBTransactionMode, fn: (store: IDBObjectStore) => IDBRequest): Promise { const db = await openDb(); try { return await new Promise((resolve, reject) => { const tx = db.transaction(STORE, mode); const request = fn(tx.objectStore(STORE)); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } finally { db.close(); } } export async function saveToLibrary(entry: LibraryEntry): Promise { await withStore("readwrite", (store) => store.put(entry)); } export async function listLibrary(): Promise { const entries = await withStore("readonly", (store) => store.getAll()); return entries .map((entry): LibraryEntryMeta => ({ id: entry.id, name: entry.name, sizeBytes: entry.sizeBytes, addedAt: entry.addedAt, romFileName: entry.romFileName, })) .sort((a, b) => b.addedAt - a.addedAt); } export async function getLibraryEntry(id: string): Promise { return withStore("readonly", (store) => store.get(id)); } export async function deleteFromLibrary(id: string): Promise { await withStore("readwrite", (store) => store.delete(id)); }