Files
PodTui/src/stores/persist.ts
Michael Freno c2ec356a5f refactor(persist): one per-domain persist scheduler
stores/persist.ts: createPersistScheduler — trailing-edge debounce,
flush, per-domain isolation. search-history writes collapse from
per-keystroke to one debounced write; scope save stays direct.
2026-09-03 08:33:11 -04:00

58 lines
1.6 KiB
TypeScript

/**
* Persistence scheduler for PodTUI
* Per-domain trailing-edge debounced writes
*/
/** Debounced writer: rapid schedules collapse into one write per domain. */
export interface PersistScheduler {
/** Mark a domain dirty and (re)arm its trailing-edge write timer. */
schedule(domain: string): void;
/** Write the domain immediately if dirty; cancels any pending timer. */
flush(domain: string): void;
/** Write every dirty domain immediately. */
flushAll(): void;
}
/** Timer handle as returned by setTimeout in this runtime. */
type TimerHandle = ReturnType<typeof setTimeout>;
/** Build a per-domain trailing-edge debouncer. `write` is invoked with the
* domain name; callers read current state inside it, so a flush always
* lands the latest value. Rapid schedule() calls share one timer. */
export function createPersistScheduler(
write: (domain: string) => void,
debounceMs = 250,
): PersistScheduler {
const dirty = new Set<string>();
const timers = new Map<string, TimerHandle>();
const flush = (domain: string): void => {
const timer = timers.get(domain);
if (timer) {
clearTimeout(timer);
timers.delete(domain);
}
if (!dirty.has(domain)) return;
dirty.delete(domain);
write(domain);
};
const schedule = (domain: string): void => {
dirty.add(domain);
clearTimeout(timers.get(domain));
timers.set(
domain,
setTimeout(() => {
timers.delete(domain);
flush(domain);
}, debounceMs),
);
};
const flushAll = (): void => {
for (const domain of [...dirty]) flush(domain);
};
return { schedule, flush, flushAll };
}