diff --git a/src/main/runtime/rpc/methods/file-watch-event-batcher.test.ts b/src/main/runtime/rpc/methods/file-watch-event-batcher.test.ts new file mode 100644 index 00000000000..ee46aa8f66c --- /dev/null +++ b/src/main/runtime/rpc/methods/file-watch-event-batcher.test.ts @@ -0,0 +1,38 @@ +import { tmpdir } from 'os' +import * as path from 'path' +import { describe, expect, it, vi } from 'vitest' +import type { FsChangeEvent } from '../../../../shared/types' +import { createFileWatchEventBatcher } from './file-watch-event-batcher' + +const LARGE_EVENT_COUNT = 150_000 + +function buildFileWatchEvents(count: number): FsChangeEvent[] { + const events: FsChangeEvent[] = [] + const root = path.join(tmpdir(), 'orca-file-watch-batch') + for (let index = 0; index < count; index += 1) { + events.push({ + kind: 'update', + absolutePath: path.join(root, `file-${index}.txt`) + }) + } + return events +} + +describe('createFileWatchEventBatcher', () => { + it('accepts large watcher event bursts', () => { + const emit = vi.fn() + const batcher = createFileWatchEventBatcher(path.join(tmpdir(), 'worktree'), emit) + + batcher.push(buildFileWatchEvents(LARGE_EVENT_COUNT)) + batcher.flush() + + const payload = emit.mock.calls[0]?.[0] as + | { type: string; worktree: string; events: FsChangeEvent[] } + | undefined + expect(payload?.type).toBe('changed') + expect(payload?.events).toHaveLength(LARGE_EVENT_COUNT) + expect(payload?.events.at(-1)?.absolutePath).toBe( + path.join(tmpdir(), 'orca-file-watch-batch', `file-${LARGE_EVENT_COUNT - 1}.txt`) + ) + }) +}) diff --git a/src/main/runtime/rpc/methods/file-watch-event-batcher.ts b/src/main/runtime/rpc/methods/file-watch-event-batcher.ts index 146ed251b0b..642d8e10741 100644 --- a/src/main/runtime/rpc/methods/file-watch-event-batcher.ts +++ b/src/main/runtime/rpc/methods/file-watch-event-batcher.ts @@ -38,7 +38,11 @@ export function createFileWatchEventBatcher( if (nextEvents.length === 0) { return } - events.push(...nextEvents) + // Why: file watchers can report huge bursts; spreading them into push can + // exceed JavaScript's argument limit before the batch has a chance to flush. + for (const event of nextEvents) { + events.push(event) + } const now = Date.now() if (firstEventAt === 0) { firstEventAt = now