/** * @file Persistent Offline Queue * @description IndexedDB-backed queue for offline request persistence * * This module provides a robust offline queue that persists requests to * IndexedDB, ensuring they survive page refreshes and are automatically * processed when the network becomes available. */ /** * Queue item status */ export type QueueItemStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'expired'; /** * Serialized request for storage */ export interface QueuedRequest { /** Unique request ID */ id: string; /** Request URL */ url: string; /** HTTP method */ method: string; /** Request headers */ headers: Record; /** Request body (serialized) */ body: string | null; /** Creation timestamp */ createdAt: number; /** Expiration timestamp */ expiresAt: number; /** Request priority (higher = more important) */ priority: number; /** Current retry count */ retryCount: number; /** Maximum retries allowed */ maxRetries: number; /** Current status */ status: QueueItemStatus; /** Last error message if failed */ lastError?: string | undefined; /** Additional metadata */ metadata?: Record | undefined; } /** * Queue options */ export interface OfflineQueueOptions { /** Database name */ dbName?: string; /** Store name */ storeName?: string; /** Default expiration time in ms (default: 24 hours) */ defaultExpiration?: number; /** Maximum queue size */ maxQueueSize?: number; /** Maximum retries per request */ maxRetries?: number; /** Base retry delay in ms */ retryDelayBase?: number; /** Number of requests to process at once */ batchSize?: number; /** Automatically process queue when online */ autoProcess?: boolean; } /** * Enqueue options */ export interface EnqueueOptions { /** Request priority (default: 0) */ priority?: number; /** Custom expiration time in ms */ expiration?: number; /** Custom max retries */ maxRetries?: number; /** Additional metadata to store */ metadata?: Record; } /** * Queue statistics */ export interface QueueStats { pending: number; processing: number; completed: number; failed: number; total: number; } /** * Queue events for the event bus */ export interface OfflineQueueEvents { 'offlineQueue:enqueued': { id: string; url: string; }; 'offlineQueue:completed': { id: string; url: string; }; 'offlineQueue:failed': { id: string; url: string; error: string; retriesExhausted?: boolean; }; 'offlineQueue:expired': { id: string; url: string; }; 'offlineQueue:processing': { count: number; }; } /** * Persistent offline queue backed by IndexedDB * * @example * ```tsx * const queue = new PersistentOfflineQueue(); * await queue.init(); * * // Enqueue a request when offline * const request = new Request('/api/data', { * method: 'POST', * body: JSON.stringify(data) * }); * await queue.enqueue(request, { priority: 1 }); * * // Queue is automatically processed when online * ``` */ export declare class PersistentOfflineQueue { private options; private db; private isProcessing; private unsubscribe; private initialized; constructor(options?: OfflineQueueOptions); /** * Initialize the queue (must be called before use) */ init(): Promise; /** * Add a request to the queue * * @param request - Request to queue * @param options - Enqueue options * @returns The queued request ID */ enqueue(request: Request, options?: EnqueueOptions): Promise; /** * Process pending requests in the queue */ processQueue(): Promise; /** * Get count of pending items */ getPendingCount(): Promise; /** * Get all items in the queue */ getAll(): Promise; /** * Get queue statistics */ getStats(): Promise; /** * Remove an item from the queue */ remove(id: string): Promise; /** * Clear all items from the queue */ clear(): Promise; /** * Retry a failed item */ retryFailed(id: string): Promise; /** * Retry all failed items */ retryAllFailed(): Promise; /** * Check if the queue is empty */ isEmpty(): Promise; /** * Check if queue is currently processing */ isQueueProcessing(): boolean; /** * Force process the queue (even if already processing) */ forceProcess(): Promise; /** * Dispose resources */ dispose(): void; /** * Ensure the queue is initialized */ private ensureInitialized; /** * Get the database instance (throws if not initialized) */ private getDb; /** * Process a single queue item * * Note: This method intentionally uses raw fetch() rather than apiClient because: * 1. Queued requests store their own headers/method/body from the original request * 2. The offline queue replays exact requests that were made when offline * 3. Using apiClient would re-apply interceptors/auth that may have changed * * @see {@link @/lib/api/api-client} for making new API calls */ private processItem; /** * Handle retry logic for failed requests */ private handleRetry; /** * Update item status */ private updateStatus; /** * Clean expired items */ private cleanExpired; } /** * Global offline queue instance */ export declare const offlineQueue: PersistentOfflineQueue;