chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 极简 SSE(text/event-stream)解析器。
|
||||
*
|
||||
* 只依赖 Web 标准的 ReadableStream,可直接消费 fetch 响应体。
|
||||
* 上游既可能是标准 SSE,也可能在末尾缺少空行,因此以「块边界」为准做收尾。
|
||||
*/
|
||||
|
||||
export interface SseEvent {
|
||||
event?: string
|
||||
data: string
|
||||
}
|
||||
|
||||
export async function* parseSse(
|
||||
stream: ReadableStream<Uint8Array>
|
||||
): AsyncGenerator<SseEvent, void, void> {
|
||||
const reader = stream.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
|
||||
try {
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
|
||||
let index: number
|
||||
// 事件之间以空行分隔,兼容 \r\n 与 \n
|
||||
while ((index = findBoundary(buffer)) !== -1) {
|
||||
const raw = buffer.slice(0, index)
|
||||
buffer = buffer.slice(index).replace(/^(\r?\n){2}/, '')
|
||||
const parsed = parseBlock(raw)
|
||||
if (parsed) yield parsed
|
||||
}
|
||||
}
|
||||
|
||||
// 收尾:流结束时可能残留最后一个事件
|
||||
buffer += decoder.decode()
|
||||
const tail = parseBlock(buffer)
|
||||
if (tail) yield tail
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
}
|
||||
|
||||
function findBoundary(buffer: string): number {
|
||||
const a = buffer.indexOf('\n\n')
|
||||
const b = buffer.indexOf('\r\n\r\n')
|
||||
if (a === -1) return b
|
||||
if (b === -1) return a
|
||||
return Math.min(a, b)
|
||||
}
|
||||
|
||||
function parseBlock(block: string): SseEvent | null {
|
||||
const lines = block.split(/\r?\n/)
|
||||
const dataLines: string[] = []
|
||||
let event: string | undefined
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line || line.startsWith(':')) continue
|
||||
if (line.startsWith('event:')) {
|
||||
event = line.slice(6).trim()
|
||||
} else if (line.startsWith('data:')) {
|
||||
dataLines.push(line.slice(5).replace(/^ /, ''))
|
||||
}
|
||||
}
|
||||
|
||||
if (dataLines.length === 0) return null
|
||||
return { event, data: dataLines.join('\n') }
|
||||
}
|
||||
Reference in new issue
Block a user