| // Copyright (C) 2026 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| /** | |
| * Parses a Server-Sent Events (SSE) stream from a fetch response body. SSE | |
| * events are blank-line-separated, each consisting of one or more `data: | |
| * <json>` lines. The terminal event is the literal `data: [DONE]`. | |
| * | |
| * @param body - The response body stream from a fetch request. | |
| * @param signal - Optional abort signal to cancel the stream early. | |
| * @yields The parsed JSON payload from each SSE event. | |
| */ | |
| export async function* parseSse( | |
| body: ReadableStream<Uint8Array>, | |
| signal?: AbortSignal, | |
| ): AsyncGenerator<unknown, void, void> { | |
| const reader = body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let buf = ''; | |
| for (;;) { | |
| if (signal?.aborted) return; | |
| const {value, done} = await reader.read(); | |
| if (done) break; | |
| buf += decoder.decode(value, {stream: true}); | |
| buf = buf.replace(/\r\n/g, '\n'); | |
| let sep: number; | |
| while ((sep = buf.indexOf('\n\n')) !== -1) { | |
| const event = buf.slice(0, sep); | |
| buf = buf.slice(sep + 2); | |
| const payload = event | |
| .split('\n') | |
| .filter((l) => l.startsWith('data:')) | |
| .map((l) => l.slice(5).trimStart()) | |
| .join(''); | |
| if (!payload || payload === '[DONE]') continue; | |
| try { | |
| yield JSON.parse(payload); | |
| } catch { | |
| // Ignore unparseable events (keepalives, partial buffers). | |
| } | |
| } | |
| } | |
| } |