76 lines
1.8 KiB
TypeScript
76 lines
1.8 KiB
TypeScript
import { windowsPathKey } from './pathing';
|
|
|
|
export interface CoalescedEventInput<T> {
|
|
projectRoot: string;
|
|
payload: T;
|
|
}
|
|
|
|
interface PendingEvent<T> {
|
|
timer: NodeJS.Timeout;
|
|
projectRoot: string;
|
|
payload: T;
|
|
}
|
|
|
|
export class ProjectEventCoalescer<T> {
|
|
private readonly pending = new Map<string, PendingEvent<T>>();
|
|
|
|
private readonly debounceMs: number;
|
|
|
|
private readonly onFlush: (event: CoalescedEventInput<T>) => void;
|
|
|
|
constructor(debounceMs: number, onFlush: (event: CoalescedEventInput<T>) => void) {
|
|
this.debounceMs = debounceMs;
|
|
this.onFlush = onFlush;
|
|
}
|
|
|
|
queue(projectRoot: string, payload: T): void {
|
|
const projectKey = windowsPathKey(projectRoot);
|
|
const existing = this.pending.get(projectKey);
|
|
if (existing) {
|
|
clearTimeout(existing.timer);
|
|
existing.projectRoot = projectRoot;
|
|
existing.payload = payload;
|
|
existing.timer = setTimeout(() => this.flush(projectKey), this.debounceMs);
|
|
return;
|
|
}
|
|
|
|
this.pending.set(projectKey, {
|
|
projectRoot,
|
|
payload,
|
|
timer: setTimeout(() => this.flush(projectKey), this.debounceMs),
|
|
});
|
|
}
|
|
|
|
cancel(projectRoot: string): void {
|
|
const projectKey = windowsPathKey(projectRoot);
|
|
const pending = this.pending.get(projectKey);
|
|
if (!pending) {
|
|
return;
|
|
}
|
|
clearTimeout(pending.timer);
|
|
this.pending.delete(projectKey);
|
|
}
|
|
|
|
cancelAll(): void {
|
|
for (const pending of this.pending.values()) {
|
|
clearTimeout(pending.timer);
|
|
}
|
|
this.pending.clear();
|
|
}
|
|
|
|
pendingCount(): number {
|
|
return this.pending.size;
|
|
}
|
|
|
|
private flush(projectKey: string): void {
|
|
const pending = this.pending.get(projectKey);
|
|
if (!pending) {
|
|
return;
|
|
}
|
|
this.pending.delete(projectKey);
|
|
this.onFlush({
|
|
projectRoot: pending.projectRoot,
|
|
payload: pending.payload,
|
|
});
|
|
}
|
|
}
|