type LayoutBatchCallback = () => void; export type CancelLayoutBatch = () => void; interface QueuedCallback { callback: LayoutBatchCallback; cancelled: boolean; } const measureQueue: QueuedCallback[] = []; const mutateQueue: QueuedCallback[] = []; let scheduled = false; let flushing = false; function requestFrame(callback: FrameRequestCallback): void { const raf = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function' ? window.requestAnimationFrame.bind(window) : typeof globalThis.requestAnimationFrame === 'function' ? globalThis.requestAnimationFrame.bind(globalThis) : undefined; if (raf) { raf(callback); return; } setTimeout(() => callback(Date.now()), 0); } function scheduleFlush(): void { if (scheduled || flushing) return; scheduled = true; requestFrame(flushQueues); } function enqueue(queue: QueuedCallback[], callback: LayoutBatchCallback): CancelLayoutBatch { const entry: QueuedCallback = { callback, cancelled: false }; queue.push(entry); scheduleFlush(); return () => { entry.cancelled = true; }; } function reportLayoutBatchError(error: unknown): void { if (typeof globalThis.reportError === 'function') { globalThis.reportError(error); return; } setTimeout(() => { throw error; }, 0); } function flushQueue(queue: QueuedCallback[]): void { const errors: unknown[] = []; for (const entry of queue) { if (entry.cancelled) continue; try { entry.callback(); } catch (error) { errors.push(error); } } for (const error of errors) reportLayoutBatchError(error); } function flushQueues(): void { scheduled = false; flushing = true; try { const measures = measureQueue.splice(0); flushQueue(measures); const mutates = mutateQueue.splice(0); flushQueue(mutates); } finally { flushing = false; if (measureQueue.length > 0 || mutateQueue.length > 0) scheduleFlush(); } } export function __resetForTest(): void { measureQueue.length = 0; mutateQueue.length = 0; scheduled = false; flushing = false; } export function measure(callback: LayoutBatchCallback): CancelLayoutBatch { return enqueue(measureQueue, callback); } export function mutate(callback: LayoutBatchCallback): CancelLayoutBatch { return enqueue(mutateQueue, callback); }