SPB Git forge

spb/websensor

Public
33commits 1branches 0releases
3.4 MBsize
maindefault branch
10 days agolast push
TypeScript 55.4% Python 43.2% SQL 1.2%
1.3 KB · 37 lines typescript
Raw Blame History
1/**2 * Tiny in-process TTL cache with request coalescing (spec §59). Used for aggregates only3 * (stats, trending, pulse, radar, rankings) — never for the live feed itself.4 */5const store = new Map<string, { at: number; ttl: number; value: unknown; pending?: Promise<unknown> }>();67export async function cached<T>(key: string, ttlMs: number, fn: () => Promise<T>): Promise<T> {8  const now = Date.now();9  const hit = store.get(key);10  if (hit && now - hit.at < hit.ttl) return hit.value as T;11  if (hit?.pending) return hit.pending as Promise<T>;12  const pending = fn()13    .then((value) => {14      store.set(key, { at: Date.now(), ttl: ttlMs, value });15      return value;16    })17    .catch((e) => {18      // serve stale on failure when we have anything at all19      if (hit) {20        store.set(key, { ...hit, pending: undefined });21        return hit.value as T;22      }23      store.delete(key);24      throw e;25    });26  store.set(key, { at: hit?.at ?? 0, ttl: hit?.ttl ?? ttlMs, value: hit?.value, pending });27  return pending;28}2930export function cacheStats(): { entries: number; keys: string[] } {31  return { entries: store.size, keys: [...store.keys()].slice(0, 50) };32}3334export function invalidate(prefix: string): void {35  for (const k of store.keys()) if (k.startsWith(prefix)) store.delete(k);36}37