phase 5 part 1: typescript, vue, react, ssr(Next.js 16) topics with lessons, scenarios, interviews, verified examples
This commit is contained in:
1 parent
7a32d2c9a6
commit
7b5cae7d24
39 files changed
+2482
-48
No files matched your search
@@ -0,0 +1,51 @@
|
||||
// 增量 SSE 解析器:网络分块可能把一个事件切成两半,必须缓冲到空行再解析。
|
||||
// 浏览器的 EventSource 只支持 GET 且不能带自定义请求头,AI 对话通常用 fetch + POST,再自己解析 SSE。
|
||||
// 格式参考 HTML 标准:https://html.spec.whatwg.org/multipage/server-sent-events.html
|
||||
|
||||
export interface SSEEvent {
|
||||
event: string; // 默认 "message"
|
||||
data: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export function createSSEParser(onEvent: (e: SSEEvent) => void) {
|
||||
let buffer = "";
|
||||
return (chunk: string): void => {
|
||||
buffer += chunk.replace(/\r\n?/g, "\n");
|
||||
let sep: number;
|
||||
while ((sep = buffer.indexOf("\n\n")) >= 0) {
|
||||
const block = buffer.slice(0, sep);
|
||||
buffer = buffer.slice(sep + 2);
|
||||
const ev: SSEEvent = { event: "message", data: "" };
|
||||
const data: string[] = [];
|
||||
for (const line of block.split("\n")) {
|
||||
if (!line || line.startsWith(":")) continue; // 空行、注释(常用作心跳)
|
||||
const i = line.indexOf(":");
|
||||
const field = i < 0 ? line : line.slice(0, i);
|
||||
const value = i < 0 ? "" : line.slice(i + 1).replace(/^ /, "");
|
||||
if (field === "event") ev.event = value;
|
||||
else if (field === "data") data.push(value);
|
||||
else if (field === "id") ev.id = value;
|
||||
}
|
||||
if (data.length) {
|
||||
ev.data = data.join("\n");
|
||||
onEvent(ev);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 对话任务的状态:前端据此显示“连接中 / 生成中 / 完成 / 失败(可重试)/ 已停止”
|
||||
export type ChatStatus = "idle" | "connecting" | "streaming" | "done" | "error" | "aborted";
|
||||
|
||||
// 服务端约定的事件:token(增量文本)、progress(任务进度)、final(结构化结果)、error
|
||||
export interface FinalPayload {
|
||||
answer: string;
|
||||
citations: { id: number; title: string }[];
|
||||
}
|
||||
|
||||
export function parseFinal(data: string): FinalPayload {
|
||||
const v = JSON.parse(data) as Partial<FinalPayload>;
|
||||
if (typeof v.answer !== "string" || !Array.isArray(v.citations)) throw new Error("final 事件格式错误");
|
||||
return { answer: v.answer, citations: v.citations };
|
||||
}
|
||||
Reference in new issue
Block a user