pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
64 lines (63 loc) • 2.56 kB
JavaScript
/**
* Generalized debounced-persist coalescing scheduler (#348 phase 2), factored
* out of the review graph's #260 circuit-breaker so a second warm cache
* (the word index) doesn't grow a parallel copy of the same discipline:
* 1. Coalesce: a burst of edits schedules ONE write after a quiet window,
* instead of one write per edit.
* 2. Best-effort teardown flush: a process-exit hook flushes any pending
* writes synchronously so a debounced payload is never silently lost.
*
* What stayed OUT of this module (left to each caller, deliberately not
* generalized): the actual serialize+write. The review graph writes its own
* cache file (tmp+rename, size-cap check, git-stamp) while the word index
* merges into the shared project-snapshot file via `saveProjectSnapshot`
* (preserving unrelated fields, honoring the seq-laundering guard). Those
* writers are different enough (different files, different merge semantics)
* that forcing them through one write function would either leak
* graph-specific concerns into the snapshot path or vice versa. The
* scheduler only owns "when", never "how".
*/
/**
* Build a debounce scheduler over `write(key, payload)`. `debounceMs()` is
* read fresh on every schedule call (not captured once) so a test that
* flips its env override mid-run takes effect immediately, same as the
* graph's `graphPersistDebounceMs()`.
*/
export function createDebounceScheduler(args) {
const { write, debounceMs } = args;
const pending = new Map();
const timers = new Map();
function flush(key) {
const payload = pending.get(key);
if (payload === undefined)
return;
pending.delete(key);
const timer = timers.get(key);
if (timer) {
clearTimeout(timer);
timers.delete(key);
}
write(key, payload);
}
function flushAll() {
for (const key of [...pending.keys()])
flush(key);
}
function schedule(key, payload) {
pending.set(key, payload);
const debounce = debounceMs();
const existing = timers.get(key);
if (existing)
clearTimeout(existing);
if (debounce === 0) {
flush(key);
return;
}
const timer = setTimeout(() => flush(key), debounce);
// Don't keep the event loop alive solely for a debounced write.
if (typeof timer.unref === "function")
timer.unref();
timers.set(key, timer);
}
return { schedule, flush, flushAll };
}