API reference
DataCache
Section titled “DataCache”Own one per domain in a singleton API service.
Signature
Section titled “Signature”class DataCache { readonly name: string // devtools label (auto-generated if omitted) readonly version: Signal<number> // auto-increments on invalidate() and clear() readonly staleTime: number // resolved config value readonly expireTime: number // resolved config value
constructor(options?: { name?: string staleTime?: number expireTime?: number cleanupInterval?: number // ms between auto-eviction sweeps maxEntries?: number // LRU cap, oldest evicted on write })
get<T>(key: string[], options?: { staleTime?: number; expireTime?: number }): { data: T; fresh: boolean } | null set<T>(key: string[], data: T): void invalidate(prefix: string[]): void // marks stale + bumps version wrap<T>(key: string[], obs$: Observable<T>): Observable<T> deduplicate<T>(key: string[], fn: () => Promise<T>): Promise<T> prefetch<T>(key: string[], fn: () => Promise<T>): Promise<void> clear(): void cleanup(): number // evict expired entries, return count inspect(): CacheInspection<unknown> // point-in-time snapshot for devtools}readonly cache = new DataCache({ name: 'orders', maxEntries: 100 })
// Read from cacheconst entry = this.cache.get(['order', 'details', '42'])if (entry?.fresh) return entry.data
// Invalidate all "order" entriesthis.cache.invalidate(['order']) // prefix matchcachedResource()
Section titled “cachedResource()”Extends resource() with cache awareness while keeping the same mental model.
Signature
Section titled “Signature”function cachedResource<T, P extends object>(options: { cache: DataCache cacheKey: string[] | ((params: P) => string[]) params?: () => P | undefined // undefined = idle loader: (ctx: { params: P; abortSignal: AbortSignal }) => Observable<T> | Promise<T> defaultValue?: NoInfer<T> // value before the first load; narrows value() to Signal<T> staleTime?: number expireTime?: number retry?: number | RetryConfig // auto-retry with exponential backoff refetchInterval?: number | (() => number | false) // polling, ignores staleTime refetchOnWindowFocus?: boolean // revalidate on tab focus, respects staleTime refetchOnReconnect?: boolean // revalidate when the network returns id?: string // reuse the SSR value via TransferState}): CachedResourceRef<T>Returned reference
Section titled “Returned reference”interface CachedResourceRef<T> { readonly value: Signal<T | undefined> // preserves last cached value on error readonly status: Signal<ResourceStatus> readonly error: Signal<Error | undefined> readonly isLoading: Signal<boolean> readonly isStale: Signal<boolean> // SWR in progress readonly isInitialLoading: Signal<boolean> // true only on cold cache hasValue(): this is Omit<CachedResourceRef<T>, 'value'> & { readonly value: Signal<T> } // type guard, narrows value() reload(): boolean // refetches now, bypassing staleTime destroy(): void set(value: T): void update(updater: (prev: T | undefined) => T): void}
interface RetryConfig { maxRetries: number baseDelay?: number // upper bound of retry 1, jittered. default: 1_000 ms maxDelay?: number // ceiling for the backoff. default: 30_000 ms retryIf?: (error: unknown) => boolean // default: retry all}cachedMutation()
Section titled “cachedMutation()”Wraps any mutation with a signal lifecycle, optimistic updates, and automatic cache invalidation.
Signature
Section titled “Signature”function cachedMutation<A = void, R = void, C = void>(options: { mutationFn: (args: A) => Observable<R> | Promise<R> cache?: { invalidate(prefix: string[]): void } invalidateKeys?: (args: A, result: R) => string[][] onMutate?: (args: A) => C | Promise<C> // runs before the API call; its return value becomes "context" in onError onSuccess?: (result: R, args: A) => void onError?: (error: unknown, args: A, context: C | undefined) => void}): CachedMutationRef<A, R>
// Lifecycle: onMutate → mutationFn → (onSuccess → invalidateKeys) | onError// mutate() never rejects; errors are captured in the error signalReturned reference and usage
Section titled “Returned reference and usage”interface CachedMutationRef<A, R> { mutate(...args: A extends void ? [] : [args: A]): Promise<R | undefined> readonly status: Signal<CachedMutationStatus> // 'idle' | 'pending' | 'success' | 'error' readonly isPending: Signal<boolean> readonly error: Signal<unknown> readonly data: Signal<R | undefined> reset(): void}
// Optimistic delete with rollbackreadonly deleteMutation = cachedMutation({ mutationFn: (id: string) => this.#api.delete$(id), cache: this.#api.cache, invalidateKeys: (id) => [['order']], onMutate: (id) => { const prev = this.orders.value() // 1. snapshot this.orders.update(list => (list ?? []).filter(o => o.id !== id)) // 2. optimistic update return prev // 3. → becomes "context" in onError }, onError: (_err, _id, prev) => { if (prev) this.orders.set(prev) // 4. rollback on failure },})anyLoading()
Section titled “anyLoading()”Returns true when any input loading signal is true.
Signature
Section titled “Signature”function anyLoading(...signals: Signal<boolean>[]): Signal<boolean>// Implementation: computed(() => signals.some(s => s()))// Combine loading states from multiple resourcesreadonly isLoading = anyLoading( this.orders.isLoading, this.products.isLoading, this.deleteMutation.isPending,)
// In template@if (store.isLoading()) { <app-progress-bar />}provideZiflux()
Section titled “provideZiflux()”Global configuration. Add it once in app.config.ts.
Signature
Section titled “Signature”provideZiflux({ staleTime: 30_000, // ms before fresh → stale (default: 30s) expireTime: 300_000, // ms before stale → evicted (default: 5min)})export const appConfig: ApplicationConfig = { providers: [ provideZiflux({ staleTime: 30_000, expireTime: 300_000 }), ],}
// Priority: constructor arg > global provider > defaultsreadonly cache = new DataCache({ staleTime: 60_000 })withDevtools()
Section titled “withDevtools()”Enables the cache inspector and structured console logging. It is active only in development mode.
Signature
Section titled “Signature”function withDevtools(config?: DevtoolsConfig): ZifluxFeature
interface DevtoolsConfig { logOperations?: boolean // default: true in dev mode}provideZiflux( { staleTime: 30_000, expireTime: 300_000 }, withDevtools() // no config needed)
// Or with custom configprovideZiflux( { staleTime: 30_000 }, withDevtools({ logOperations: false }))ZifluxDevtoolsComponent
Section titled “ZifluxDevtoolsComponent”Standalone floating overlay for inspecting live cache state.
Signature
Section titled “Signature”@Component({ selector: 'ziflux-devtools', standalone: true,})export class ZifluxDevtoolsComponent<!-- In your root component template --><ziflux-devtools />
<!-- Toggle with Ctrl+Shift+Z (Cmd+Shift+Z on Mac) --><!-- Shows per-cache entries, freshness state, TTL, and in-flight requests -->ZIFLUX_CONFIG
Section titled “ZIFLUX_CONFIG”Injection token holding the resolved global config.
Signature
Section titled “Signature”const ZIFLUX_CONFIG: InjectionToken<ZifluxConfig>// Read global config in any injection contextconst config = inject(ZIFLUX_CONFIG)console.log(config.staleTime, config.expireTime)CacheRegistry
Section titled “CacheRegistry”Advanced API. Most applications do not need it directly. It is the global
registry of all DataCache instances.
Signature
Section titled “Signature”class CacheRegistry { readonly caches: Signal<Map<string, DataCache>> inspectAll(): { name: string; inspection: CacheInspection<unknown> }[]}// Auto-managed when withDevtools() is enabled// Useful for building custom monitoring dashboards
const registry = inject(CacheRegistry)const allCaches = registry.inspectAll()