import type { File } from './types'; const DB_NAME = 'csound-files-db'; const DB_VERSION = 3; const STORE_NAME = 'files'; /** * Database wrapper for IndexedDB operations */ class FileDatabase { private db: IDBDatabase | null = null; private initPromise: Promise | null = null; /** * Initialize the database connection */ async init(): Promise { if (this.db) { return; } if (this.initPromise) { return this.initPromise; } this.initPromise = new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => { reject(new Error(`Failed to open database: ${request.error?.message}`)); }; request.onsuccess = () => { this.db = request.result; resolve(); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; // Delete old stores if they exist if (db.objectStoreNames.contains('projects')) { db.deleteObjectStore('projects'); } if (db.objectStoreNames.contains('workspaces')) { db.deleteObjectStore('workspaces'); } // Create files store if it doesn't exist if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME, { keyPath: 'id', }); } }; }); return this.initPromise; } /** * Ensure database is initialized */ private async ensureDb(): Promise { await this.init(); if (!this.db) { throw new Error('Database not initialized'); } return this.db; } /** * Get a file by ID */ async get(id: string): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.get(id); request.onsuccess = () => { resolve(request.result || null); }; request.onerror = () => { reject(new Error(`Failed to get file: ${request.error?.message}`)); }; }); } /** * Get all files */ async getAll(): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.getAll(); request.onsuccess = () => { resolve(request.result || []); }; request.onerror = () => { reject(new Error(`Failed to get all files: ${request.error?.message}`)); }; }); } /** * Save or update a file */ async put(file: File): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.put(file); request.onsuccess = () => { resolve(); }; request.onerror = () => { reject(new Error(`Failed to save file: ${request.error?.message}`)); }; }); } /** * Delete a file by ID */ async delete(id: string): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.delete(id); request.onsuccess = () => { resolve(); }; request.onerror = () => { reject(new Error(`Failed to delete file: ${request.error?.message}`)); }; }); } /** * Clear all files (use with caution!) */ async clear(): Promise { const db = await this.ensureDb(); return new Promise((resolve, reject) => { const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.clear(); request.onsuccess = () => { resolve(); }; request.onerror = () => { reject(new Error(`Failed to clear files: ${request.error?.message}`)); }; }); } /** * Close the database connection */ close(): void { if (this.db) { this.db.close(); this.db = null; this.initPromise = null; } } } export { FileDatabase };