import { deepEqual } from '@/utils/deepEqual' import { executeRequest } from './executeRequest' const cacheStore = new Map() /** * Cache-first, stale-while-revalidate data fetcher. * * Supports: * - URL string (GET) * - Request object (GET / POST) * - Custom async fetcher function * * @param {Object} options * @param {string} options.key - Unique cache key * @param {string|Object} [options.request] - URL or request config * @param {Function} [options.fetcher] - Custom async function * @param {Function} options.onUpdate - Callback with data * * @example * fetchWithCache({ * key: 'users.list', * request: '/api/users', * onUpdate: (data, source) => { * users.value = data * } * }) */ export async function fetchWithCache({ key, request = null, fetcher = null, onUpdate = null }) { if (!key) { throw new Error('fetchWithCache requires a cache key') } // 1️⃣ Emit cached data immediately if (cacheStore.has(key)) { onUpdate?.(cacheStore.get(key), 'cache') } // 2️⃣ Build fetcher if not provided let resolvedFetcher = fetcher if (!resolvedFetcher && request) { resolvedFetcher = () => executeRequest(request) } if (typeof resolvedFetcher !== 'function') { throw new Error('fetchWithCache requires either fetcher or request') } // 3️⃣ Fetch fresh data try { const fresh = await resolvedFetcher() const cached = cacheStore.get(key) if (!cached || !deepEqual(cached, fresh)) { cacheStore.set(key, fresh) onUpdate?.(fresh, 'fresh') } } catch (err) { console.error('[fetchWithCache] fetch failed:', err) } }