(onEvent: (event: SSEEvent) => void)
| 8 | } |
| 9 | |
| 10 | export function createParser(onEvent: (event: SSEEvent) => void) { |
| 11 | let buffer = ""; |
| 12 | let eventType = ""; |
| 13 | let eventData = ""; |
| 14 | |
| 15 | function dispatch() { |
| 16 | if (eventData !== "" || eventType !== "") { |
| 17 | onEvent({ |
| 18 | type: eventType || "message", |
| 19 | data: eventData, |
| 20 | }); |
| 21 | eventType = ""; |
| 22 | eventData = ""; |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | return { |
| 27 | feed(chunk: string) { |
| 28 | buffer += chunk; |
| 29 | const lines = buffer.split("\n"); |
| 30 | buffer = lines.pop() || ""; |
| 31 | |
| 32 | for (const line of lines) { |
| 33 | const trimmed = line.endsWith("\r") ? line.slice(0, -1) : line; |
| 34 | |
| 35 | if (trimmed === "") { |
| 36 | dispatch(); |
| 37 | } else if (trimmed.startsWith("data: ")) { |
| 38 | eventData += (eventData ? "\n" : "") + trimmed.slice(6); |
| 39 | } else if (trimmed.startsWith("event: ")) { |
| 40 | eventType = trimmed.slice(7); |
| 41 | } else if (trimmed.startsWith("id: ")) { |
| 42 | // ignore id |
| 43 | } else if (trimmed.startsWith("retry: ")) { |
| 44 | // ignore retry |
| 45 | } else if (trimmed.startsWith(":")) { |
| 46 | // comment, ignore |
| 47 | } |
| 48 | } |
| 49 | }, |
| 50 | }; |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * 读取 ReadableStream 并逐块喂给 SSE 解析器,最后 resolve |
no outgoing calls
no test coverage detected