78 lines
2.2 KiB
JavaScript
78 lines
2.2 KiB
JavaScript
/* Generic SSE client wrapper for novel-app streams.
|
|
*
|
|
* 后端事件契约:
|
|
* token: {text:string}
|
|
* section_start: {section:string} (拆书)
|
|
* section_end: {section:string, content:string}
|
|
* context: {summary:string} (写章节)
|
|
* done: {content:string, word_count:number}
|
|
* error: {error:string}
|
|
*
|
|
* handlers 全部可选:
|
|
* {onToken, onSectionStart, onSectionEnd, onContext, onDone, onError}
|
|
* 返回句柄 {close()}, close() 幂等. done / error 帧后自动 close.
|
|
*/
|
|
|
|
const EVENTS = ["token", "section_start", "section_end", "context", "done", "error"];
|
|
|
|
export function createSSEStream(url, handlers = {}) {
|
|
let closed = false;
|
|
const es = new EventSource(url);
|
|
|
|
function close() {
|
|
if (closed) return;
|
|
closed = true;
|
|
es.close();
|
|
}
|
|
|
|
function parse(e) {
|
|
try {
|
|
return JSON.parse(e.data);
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
es.addEventListener("token", (e) => {
|
|
const d = parse(e);
|
|
if (d && handlers.onToken) handlers.onToken(d.text || "");
|
|
});
|
|
|
|
es.addEventListener("section_start", (e) => {
|
|
const d = parse(e);
|
|
if (d && handlers.onSectionStart) handlers.onSectionStart(d.section);
|
|
});
|
|
|
|
es.addEventListener("section_end", (e) => {
|
|
const d = parse(e);
|
|
if (d && handlers.onSectionEnd) handlers.onSectionEnd(d.section, d.content || "");
|
|
close();
|
|
});
|
|
|
|
es.addEventListener("context", (e) => {
|
|
const d = parse(e);
|
|
if (d && handlers.onContext) handlers.onContext(d.summary || "");
|
|
});
|
|
|
|
es.addEventListener("done", (e) => {
|
|
const d = parse(e);
|
|
if (d && handlers.onDone) handlers.onDone(d.content || "", d.word_count || 0);
|
|
close();
|
|
});
|
|
|
|
es.addEventListener("error", (e) => {
|
|
const d = parse(e);
|
|
if (handlers.onError) handlers.onError(d && d.error ? d.error : "未知错误", "server");
|
|
close();
|
|
});
|
|
|
|
es.onerror = () => {
|
|
if (!closed) {
|
|
if (handlers.onError) handlers.onError("连接错误, 请检查后端日志", "connection");
|
|
close();
|
|
}
|
|
};
|
|
|
|
return { close };
|
|
}
|