Files
orca/src/shared/quick-open-subprocess-path-accumulator.ts
T
Jinwoo Hong 74856c83b3 fix(dev): keep Node-only Quick Open path accumulator out of the renderer (#27372)
The renderer imports quick-open-listing-limits for the result cap, but the
same module also held a Buffer-backed accumulator that imports the CommonJS
@orca/process-host dist. Vite dev serves that dist as raw ESM, so the named
import failed and the workspace workbench and right sidebar crashed. Move the
accumulator to its own main-only module.
2026-10-11 00:22:18 -04:00

69 lines
2.1 KiB
TypeScript

import { GrowingByteBuffer } from '@orca/process-host/growing-byte-buffer'
const QUICK_OPEN_LISTING_MAX_PATH_BYTES = 64 * 1024
export type QuickOpenPathAccumulatorResult = 'continue' | 'stopped' | 'path-too-large'
export class QuickOpenSubprocessPathAccumulator {
private readonly field = new GrowingByteBuffer()
constructor(
private readonly delimiter: number,
private readonly maxPathBytes = QUICK_OPEN_LISTING_MAX_PATH_BYTES
) {
if (!Number.isInteger(delimiter) || delimiter < 0 || delimiter > 0xff) {
throw new RangeError('Quick Open path delimiter must be one byte')
}
if (!Number.isSafeInteger(maxPathBytes) || maxPathBytes < 0) {
throw new RangeError('Quick Open path limit must be a non-negative safe integer')
}
}
push(
rawChunk: Buffer | string,
onPath: (path: string) => boolean
): QuickOpenPathAccumulatorResult {
const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk, 'utf8')
let cursor = 0
while (cursor < chunk.length) {
const delimiter = chunk.indexOf(this.delimiter, cursor)
const end = delimiter === -1 ? chunk.length : delimiter
const segmentBytes = end - cursor
if (this.field.byteLength + segmentBytes > this.maxPathBytes) {
this.clear()
return 'path-too-large'
}
if (delimiter !== -1 && this.field.byteLength === 0) {
if (!onPath(chunk.toString('utf8', cursor, end))) {
return 'stopped'
}
} else if (segmentBytes > 0) {
// Why: copying prevents a short residual path from retaining the whole read buffer.
this.field.append(chunk.subarray(cursor, end))
if (delimiter !== -1 && !onPath(this.take())) {
return 'stopped'
}
} else if (delimiter !== -1 && !onPath(this.take())) {
return 'stopped'
}
if (delimiter === -1) {
return 'continue'
}
cursor = delimiter + 1
}
return 'continue'
}
finish(): string | null {
return this.field.byteLength > 0 ? this.take() : null
}
clear(): void {
this.field.clear()
}
private take(): string {
return this.field.takeString()
}
}