import { StreamEventName } from "@/lib/types"; type SSEEvent = { event: StreamEventName; data: Record; }; function parseEventBlock(block: string): SSEEvent | null { const lines = block.split("\n").map((line) => line.trim()).filter(Boolean); if (!lines.length) { return null; } let eventName: StreamEventName = "status"; const dataLines: string[] = []; for (const line of lines) { if (line.startsWith("event:")) { eventName = line.replace("event:", "").trim() as StreamEventName; continue; } if (line.startsWith("data:")) { dataLines.push(line.replace("data:", "").trim()); } } if (!dataLines.length) { return null; } const raw = dataLines.join("\n"); try { return { event: eventName, data: JSON.parse(raw) as Record }; } catch { return { event: eventName, data: { message: raw } }; } } export async function* parseSSEStream( stream: ReadableStream ): AsyncGenerator { const reader = stream.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { value, done } = await reader.read(); if (done) { break; } buffer += decoder.decode(value, { stream: true }); const blocks = buffer.split("\n\n"); buffer = blocks.pop() ?? ""; for (const block of blocks) { const parsed = parseEventBlock(block); if (parsed) { yield parsed; } } } if (buffer.trim()) { const parsed = parseEventBlock(buffer); if (parsed) { yield parsed; } } }