export interface SnapshotSource { snapshot(): Readonly; subscribe(listener: () => void): () => void; } export interface SnapshotCache { getSnapshot(): Readonly; dispose(): void; } /** * Wraps a store instance so repeated `getSnapshot()` calls return the same * reference until the store notifies a change. `useSyncExternalStore` throws * and spins if given a fresh object each call, which `@wrnexus/store`'s * `readonlySnapshot` does by design. */ export function createSnapshotCache(source: SnapshotSource): SnapshotCache { let cached: Readonly | undefined; let dirty = true; const unsubscribe = source.subscribe(() => { dirty = true; }); return { getSnapshot() { if (dirty || cached === undefined) { cached = source.snapshot(); dirty = false; } return cached; }, dispose() { unsubscribe(); }, }; } /** * Memoizes a selector over a cached snapshot. Without this, any mutation * re-renders every island bound to the store, because snapshots are whole-state. */ export function createSelectorCache( getSnapshot: () => Readonly, selector: (state: Readonly) => R, isEqual: (a: R, b: R) => boolean = Object.is, ): () => R { let lastSnapshot: Readonly | undefined; let lastResult: R; let initialized = false; return () => { const snapshot = getSnapshot(); if (!initialized || snapshot !== lastSnapshot) { const next = selector(snapshot); if (!initialized || !isEqual(next, lastResult)) lastResult = next; lastSnapshot = snapshot; initialized = true; } return lastResult; }; }