export type TippleCache = Record< string, { data: any; refetch: boolean; domains: D[] } >; export interface TippleClientOptions { baseUrl?: string; fetchOptions?: ((o: RequestInit) => RequestInit) | RequestInit; initialCache?: TippleCache; } export interface TippleClient { config: TippleClientOptions; /** Callback for cache updates. */ addCacheWatcher: (callback: (cache: TippleCache) => void) => () => void; /** Additions to cache. */ addResponse: (arg: { key: string; data: any; domains: D[] }) => void; /** Invalidation of cache. */ clearDomains: (domains: D[]) => void; } export const createClient = ( config: TippleClientOptions = {} ): TippleClient => { let cache: TippleCache = config.initialCache || {}; let cacheWatchers: Array<(c: typeof cache) => void> = []; const addCacheWatcher: TippleClient['addCacheWatcher'] = callback => { cacheWatchers = [...cacheWatchers, callback]; return () => (cacheWatchers = cacheWatchers.filter(f => f === callback)); }; const addResponse: TippleClient['addResponse'] = ({ key, data, domains, }) => { cache = { ...cache, [key]: { data, domains, refetch: false } }; cacheWatchers.forEach(c => c(cache)); }; const clearDomains: TippleClient['clearDomains'] = domains => { cache = Object.entries(cache).reduce( (c, [key, value]) => ({ ...c, [key]: { ...value, refetch: value.domains.some(d => domains.includes(d)), }, }), {} ); cacheWatchers.forEach(c => c(cache)); }; return { config, addCacheWatcher, addResponse, clearDomains, }; };