# `sync-queue/sync-queue.ts` _(no submodules)_ ## Purpose Offline-first request queue that persists requests while the device is offline and replays them when connectivity returns. The queue is backend-agnostic: it delegates persistence to any `SyncQueueStore` (memory, `localStorage`, or IndexedDB), validates every entry through `SyncQueueEntrySchema` at the enqueue boundary, runs an exponential-backoff retry loop, and emits OTel counters and histograms covering enqueue / processed / failed counts plus flush duration. Migration from older schema versions happens automatically on every load. ## Features - Pluggable store (`MemoryStore`, `LocalStorageStore`, `IndexedDbStore`). - Zod boundary validation on every enqueue; failures raise `PwaError(SYNC_QUEUE_VALIDATION)`. - Capacity enforcement via `PwaError(SYNC_QUEUE_FULL)` when `maxSize` is hit. - Auto-migration of older schema versions on every load (idempotent). - Single-flight `process()` — concurrent invocations short-circuit to `0`. - Exponential-backoff `retryDelayFor(retryCount)` accessor. - Injectable `Clock` and `IdGenerator` for deterministic tests. - OTel emission: `pwa_sync_enqueued_total`, `pwa_sync_processed_total`, `pwa_sync_failed_total`, `pwa_sync_flush_ms`. ## Usage: Basic ```ts import { SyncQueue, IndexedDbStore } from "@suaveplan/pwa"; const queue = new SyncQueue({ store: new IndexedDbStore() }); await queue.enqueue("/api/v1/orders", "POST", { body: JSON.stringify({ sku: "abc", qty: 2 }), headers: { "content-type": "application/json" }, }); await queue.process(); ``` ## Usage: Advanced Inject a clock + id generator + custom processor + failure hook for tests: ```ts import { SyncQueue, MemoryStore, type Clock } from "@suaveplan/pwa"; const clock: Clock = { now: () => 1_700_000_000_000 }; let n = 0; const queue = new SyncQueue({ store: new MemoryStore(), clock, idGenerator: () => `sq_${++n}`, maxRetries: 2, retryDelay: 100, retryBackoffFactor: 3, onProcess: async (entry) => entry.url !== "/fail", onFailure: (entry) => console.error("permanently failed", entry.id), }); ``` ## API Reference - `new SyncQueue(options: SyncQueueOptions)`. - `enqueue(url, method, options?): Promise`. - `remove(id): Promise`. - `getAll(): Promise`. - `size(): Promise`. - `clear(): Promise`. - `process(): Promise`. - `retryDelayFor(retryCount): number`. - `isProcessing: boolean` (readonly). ## Implementation Notes The queue serialises the entire snapshot on every mutation rather than performing per-entry CRUD against the underlying store. This keeps the `SyncQueueStore` contract minimal (`load` / `save`) and lets every store implementation guarantee atomicity simply by wrapping `save()` in its native transaction primitive. The `process()` body avoids `Promise.all` so failures in one entry cannot cancel sibling work; instead each entry is awaited sequentially and its outcome recorded. The histogram records the entire flush duration including loads and saves so the observed value matches user-felt latency rather than CPU-only time.