/** * IndexedDBEventRepository * Offline-first repository using IndexedDB as single source of truth * * All CRUD operations: * - Save to IndexedDB immediately (always succeeds) * - Add to sync queue if source is 'local' * - Background SyncManager processes queue to sync with API */ export class IndexedDBEventRepository { constructor(indexedDB, queue) { this.indexedDB = indexedDB; this.queue = queue; } /** * Load all events from IndexedDB * Ensures IndexedDB is initialized and seeded on first call */ async loadEvents() { // Lazy initialization on first data load if (!this.indexedDB.isInitialized()) { await this.indexedDB.initialize(); await this.indexedDB.seedIfEmpty(); } return await this.indexedDB.getAllEvents(); } /** * Create a new event * - Generates ID * - Saves to IndexedDB * - Adds to queue if local (needs sync) */ async createEvent(event, source = 'local') { // Generate unique ID const id = this.generateEventId(); // Determine sync status based on source const syncStatus = source === 'local' ? 'pending' : 'synced'; // Create full event object const newEvent = { ...event, id, syncStatus }; // Save to IndexedDB await this.indexedDB.saveEvent(newEvent); // If local change, add to sync queue if (source === 'local') { await this.queue.enqueue({ type: 'create', eventId: id, data: newEvent, timestamp: Date.now(), retryCount: 0 }); } return newEvent; } /** * Update an existing event * - Updates in IndexedDB * - Adds to queue if local (needs sync) */ async updateEvent(id, updates, source = 'local') { // Get existing event const existingEvent = await this.indexedDB.getEvent(id); if (!existingEvent) { throw new Error(`Event with ID ${id} not found`); } // Determine sync status based on source const syncStatus = source === 'local' ? 'pending' : 'synced'; // Merge updates const updatedEvent = { ...existingEvent, ...updates, id, // Ensure ID doesn't change syncStatus }; // Save to IndexedDB await this.indexedDB.saveEvent(updatedEvent); // If local change, add to sync queue if (source === 'local') { await this.queue.enqueue({ type: 'update', eventId: id, data: updates, timestamp: Date.now(), retryCount: 0 }); } return updatedEvent; } /** * Delete an event * - Removes from IndexedDB * - Adds to queue if local (needs sync) */ async deleteEvent(id, source = 'local') { // Check if event exists const existingEvent = await this.indexedDB.getEvent(id); if (!existingEvent) { throw new Error(`Event with ID ${id} not found`); } // If local change, add to sync queue BEFORE deleting // (so we can send the delete operation to API later) if (source === 'local') { await this.queue.enqueue({ type: 'delete', eventId: id, data: {}, // No data needed for delete timestamp: Date.now(), retryCount: 0 }); } // Delete from IndexedDB await this.indexedDB.deleteEvent(id); } /** * Generate unique event ID * Format: {timestamp}-{random} */ generateEventId() { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 9); return `${timestamp}-${random}`; } } //# sourceMappingURL=IndexedDBEventRepository.js.map