import { ISync, SyncStatus, EntityType } from '../types/CalendarTypes'; /** * SyncPlugin - Pluggable sync functionality for entity services * * COMPOSITION PATTERN: * - Encapsulates all sync-related logic in separate class * - Composed into BaseEntityService (not inheritance) * - Allows sync functionality to be swapped/mocked for testing * - Single Responsibility: Only handles sync status management * * DESIGN: * - Takes reference to BaseEntityService for calling get/save * - Implements sync methods that delegate to service's CRUD * - Uses IndexedDB syncStatus index for efficient queries */ export class SyncPlugin { /** * @param service - Reference to BaseEntityService for CRUD operations */ constructor(private service: any) { // Type is 'any' to avoid circular dependency at compile time // Runtime: service is BaseEntityService } /** * Mark entity as successfully synced * Sets syncStatus = 'synced' and persists to IndexedDB * * @param id - Entity ID */ async markAsSynced(id: string): Promise { const entity = await this.service.get(id); if (entity) { entity.syncStatus = 'synced'; await this.service.save(entity); } } /** * Mark entity as sync error (max retries exceeded) * Sets syncStatus = 'error' and persists to IndexedDB * * @param id - Entity ID */ async markAsError(id: string): Promise { const entity = await this.service.get(id); if (entity) { entity.syncStatus = 'error'; await this.service.save(entity); } } /** * Get current sync status for an entity * * @param id - Entity ID * @returns SyncStatus or null if entity not found */ async getSyncStatus(id: string): Promise { const entity = await this.service.get(id); return entity ? entity.syncStatus : null; } /** * Get entities by sync status * Uses IndexedDB syncStatus index for efficient querying * * @param syncStatus - Sync status ('synced', 'pending', 'error') * @returns Array of entities with this sync status */ async getBySyncStatus(syncStatus: string): Promise { return new Promise((resolve, reject) => { const transaction = this.service.db.transaction([this.service.storeName], 'readonly'); const store = transaction.objectStore(this.service.storeName); const index = store.index('syncStatus'); const request = index.getAll(syncStatus); request.onsuccess = () => { const data = request.result as any[]; const entities = data.map(item => this.service.deserialize(item)); resolve(entities); }; request.onerror = () => { reject(new Error(`Failed to get ${this.service.entityType}s by sync status ${syncStatus}: ${request.error}`)); }; }); } }