/** * 把持续吐数据的 HTTP 响应体读成一行一行。 * * 内核的 /traffic、/memory、/logs 在没有 WebSocket 升级时退化成分块流,一行一个 * JSON。分块的边界和行的边界无关:一次 read 可能拿到半行,也可能拿到好几行。 * 这里把它们缝成完整的行再交出去,空行丢弃。 * * 停止读取的方式是中止 fetch(AbortController)或 break 出循环,两种都会走到 * finally 里把流取消掉,不留悬着的连接。 */ export async function* readLines(body: ReadableStream): AsyncGenerator { const reader = body.getReader() const decoder = new TextDecoder() let buffer = '' try { for (;;) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) const parts = buffer.split('\n') // 最后一段可能是半行,留在缓冲里等下一块补齐 buffer = parts.pop() ?? '' for (const part of parts) { const line = part.endsWith('\r') ? part.slice(0, -1) : part if (line) yield line } } const rest = buffer.trim() if (rest) yield rest } finally { await reader.cancel().catch(() => {}) } }