test(lib): cover SSE event-stream framing (20→29 tests)

Extract the SSE chunk-buffering from sdk.ts into a pure SSEParser (sdk.ts delegates;
behavior unchanged) and pin the framing rules that are easy to break when an event
splits across network reads: partial trailing lines held until completed, frames
reassembled across 2-3 reads, [DONE] sentinel and empty/non-data lines filtered,
each frame emitted exactly once. typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
engineer
2026-06-03 13:58:19 -07:00
parent 3499167b60
commit dd55ca471d
3 changed files with 101 additions and 15 deletions

View File

@@ -4,6 +4,7 @@
// expo/fetch provides WinterCG-compliant fetch with ReadableStream support for SSE
import { fetch as expoFetch } from "expo/fetch"
import { buildRequestHeaders } from "./headers"
import { SSEParser } from "./sse"
export interface ClientConfig {
baseUrl: string
@@ -220,27 +221,18 @@ export function createClient(config: ClientConfig) {
const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ""
const parser = new SSEParser()
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split("\n")
buffer = lines.pop() || ""
for (const line of lines) {
if (line.startsWith("data: ")) {
const data = line.slice(6)
if (data && data !== "[DONE]") {
try {
yield JSON.parse(data)
} catch (err) {
console.warn("[SSE] Failed to parse event:", data.slice(0, 200), err)
}
}
for (const data of parser.push(decoder.decode(value, { stream: true }))) {
try {
yield JSON.parse(data)
} catch (err) {
console.warn("[SSE] Failed to parse event:", data.slice(0, 200), err)
}
}
}