import { Readable } from 'node:stream' export interface NodeReadableToWebStreamOptions { onCancel?: (reason?: unknown) => void | Promise } /** * 将 Node.js Readable 转换为受 Web Stream 消费速度约束的流。 * * 每次 pull 只向下游交付一个分块,避免 data 事件持续读取大文件、 * 而浏览器或代理来不及接收时在 Node 进程内堆积大量内存。 */ export function nodeReadableToWebStream( source: Readable, options: NodeReadableToWebStreamOptions = {}, ): ReadableStream { const iterator = source[Symbol.asyncIterator]() let finished = false return new ReadableStream( { async pull(controller) { if (finished) { return } try { const next = await iterator.next() if (next.done) { finished = true controller.close() return } const chunk = next.value controller.enqueue(chunk instanceof Uint8Array ? chunk : Buffer.from(chunk)) } catch (error) { finished = true if (!source.destroyed) { source.destroy() } controller.error(error) } }, async cancel(reason) { finished = true try { await options.onCancel?.(reason) } finally { if (!source.destroyed) { source.destroy() } } }, }, { highWaterMark: 1, size: () => 1, }, ) }