Skip to content

feat: implement multi-layer offline-first cache with IndexedDB, background sync, and conflict resolution - #762

Merged
Chucks1093 merged 1 commit into
accesslayerorg:devfrom
unlimitedengineer:feat/offline-cache-754
Aug 24, 2026
Merged

feat: implement multi-layer offline-first cache with IndexedDB, background sync, and conflict resolution#762
Chucks1093 merged 1 commit into
accesslayerorg:devfrom
unlimitedengineer:feat/offline-cache-754

Conversation

@unlimitedengineer

Copy link
Copy Markdown
Contributor

Closes #754

Summary

  • Adds IndexedDBCache class (src/lib/indexedDBCache.ts) wrapping the browser IndexedDB API with typed get, set, delete, clear, getAll, and evictStale methods. All operations are async and non-throwing — any failure falls back silently to the in-memory React Query cache.
  • Adds src/lib/queryPersistence.ts with restorePersistedQueries (loads all IndexedDB entries younger than 24 hours into the QueryClient on startup so the UI renders immediately with stale data) and subscribeToQueryCache (subscribes to QueryClient cache events and persists every data update to IndexedDB).
  • Adds src/lib/conflictResolution.ts with resolveConflict implementing last-write-wins merge: server data wins when it is newer, but fields listed in optimisticFields are preserved from the local IndexedDB entry (e.g. positions on portfolio queries made while offline).
  • Adds public/service-worker.js that intercepts failed GET requests to /api/creators/ and /api/wallet/ during offline periods, queues them in a sync-queue IndexedDB store, and drains the queue via the Background Sync API when connectivity is restored. Queued requests older than 1 hour are discarded without replay.
  • Adds useOfflineStatus hook and OfflineBanner component that show a persistent alert banner when navigator.onLine is false and dismiss it automatically when connectivity is restored.
  • Adds StaleBadge component that renders a 'Data may be outdated' badge on any data card whose dataUpdatedAt timestamp is older than 5 minutes.
  • Wires everything up in src/main.tsx (restore cache, subscribe to updates, register service worker) and mounts OfflineBanner in App.tsx.
  • Adds 23 unit tests: 13 for IndexedDBCache (get, set, delete, clear, getAll, evictStale, and silent error fallback) and 10 for resolveConflict (server newer, local newer, equal timestamps, multiple optimistic fields, no mutation of inputs).

Test plan

  • App renders immediately with cached data on hard reload before any network response
  • Queries older than 24 hours are not restored from IndexedDB on startup
  • Going offline shows the 'You are offline' banner; restoring connection hides it
  • Data cards show the 'Data may be outdated' badge when cache entry is older than 5 minutes
  • Failed API requests while offline are queued and replayed when connectivity returns
  • Queued requests older than 1 hour are discarded on sync drain
  • All 23 unit tests pass

@Chucks1093

Copy link
Copy Markdown
Member

The non-throwing fallback on IndexedDB operations is a good defensive choice since IndexedDB can fail in private browsing or when storage quota is exceeded and silently degrading to the in-memory cache means the app stays functional. The optimisticFields preserve on conflict resolution is also thoughtful because without it a sync drain could wipe out local position changes the user made while offline.

@Chucks1093
Chucks1093 merged commit 62ad368 into accesslayerorg:dev Aug 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants