[{"data":1,"prerenderedAt":291},["ShallowReactive",2],{"docs-protocol\u002Ftransport-zh":3},{"id":4,"title":5,"body":6,"description":15,"extension":284,"meta":285,"navigation":286,"path":287,"seo":288,"stem":289,"__hash__":290},"docs\u002Fzh\u002Fprotocol\u002Ftransport.md","传输规范",{"type":7,"value":8,"toc":277},"minimark",[9,12,16,21,37,47,50,56,114,117,123,241,245,248,251,273],[10,11,5],"h1",{"id":5},[13,14,15],"p",{},"A@UI 当前支持的传输格式为 SSE (Server-Sent Events)。",[17,18,20],"h2",{"id":19},"sse-格式","SSE 格式",[13,22,23,24,28,29,32,33,36],{},"每条 A@UI 命令以一行 ",[25,26,27],"code",{},"data: \u003Cjson>"," 发送，以 ",[25,30,31],{},"\\n\\n"," 结尾。命令流以 ",[25,34,35],{},"data: [DONE]"," 标记结束。",[38,39,45],"pre",{"className":40,"code":42,"language":43,"meta":44},[41],"language-text","data: {\"type\":\"render\",\"component\":\"SearchBox\",\"params\":{\"placeholder\":\"搜索项目\"}}\n\ndata: {\"type\":\"update\",\"widgetId\":\"widget-1\",\"params\":{\"value\":\"Mercury\"}}\n\ndata: [DONE]\n","text","",[25,46,42],{"__ignoreMap":44},[17,48,49],{"id":49},"流解析行为",[13,51,52,55],{},[25,53,54],{},"consumeAAtUIStream()"," 按以下规则解析 SSE 流：",[57,58,59,63,70,75,82,108],"ol",{},[60,61,62],"li",{},"逐行读取，过滤空行。",[60,64,65,66,69],{},"只处理以 ",[25,67,68],{},"data:"," 开头的行，其他行静默跳过。",[60,71,72,74],{},[25,73,68],{}," 后的空白会被 trim。",[60,76,77,78,81],{},"遇到 ",[25,79,80],{},"[DONE]"," 立即返回，不再处理后续数据。",[60,83,84,85,87,88,91,92],{},"每个 ",[25,86,68],{}," 行的 payload 会被 ",[25,89,90],{},"JSON.parse()"," 解析：\n",[93,94,95,102],"ul",{},[60,96,97,98,101],{},"解析成功 → 校验命令结构 → 合法则 dispatch，不合法则通过 ",[25,99,100],{},"onError"," 通道暴露",[60,103,104,105,107],{},"解析失败 → 通过 ",[25,106,100],{}," 通道暴露，附带原始 payload",[60,109,110,111,113],{},"流结束后如果缓冲区有残留内容，通过 ",[25,112,100],{}," 暴露 \"trailing partial SSE frame\"。",[17,115,116],{"id":116},"缓冲区处理",[13,118,119,120,122],{},"解析器内部维护一个行缓冲区，处理 ",[25,121,68],{}," 行被分割在多段 chunk 中的情况：",[38,124,128],{"className":125,"code":126,"language":127,"meta":44,"style":44},"language-ts shiki shiki-themes one-dark-pro","let buffer = '';\n\u002F\u002F 每次 chunk 到达时追加，按行分割\nbuffer += chunk;\nconst lines = buffer.split(\u002F\\r?\\n\u002F);\nbuffer = lines.pop() ?? ''; \u002F\u002F 最后一段可能不完整，留在缓冲中\n","ts",[25,129,130,155,162,176,213],{"__ignoreMap":44},[131,132,135,139,143,147,151],"span",{"class":133,"line":134},"line",1,[131,136,138],{"class":137},"seHd6","let",[131,140,142],{"class":141},"sVyAn"," buffer",[131,144,146],{"class":145},"sjrmR"," =",[131,148,150],{"class":149},"subq3"," ''",[131,152,154],{"class":153},"sn6KH",";\n",[131,156,158],{"class":133,"line":157},2,[131,159,161],{"class":160},"sV9Aq","\u002F\u002F 每次 chunk 到达时追加，按行分割\n",[131,163,165,168,171,174],{"class":133,"line":164},3,[131,166,167],{"class":141},"buffer",[131,169,170],{"class":145}," +=",[131,172,173],{"class":141}," chunk",[131,175,154],{"class":153},[131,177,179,182,186,188,190,193,197,200,203,207,210],{"class":133,"line":178},4,[131,180,181],{"class":137},"const",[131,183,185],{"class":184},"sU0A5"," lines",[131,187,146],{"class":145},[131,189,142],{"class":184},[131,191,192],{"class":153},".",[131,194,196],{"class":195},"sVbv2","split",[131,198,199],{"class":153},"(",[131,201,202],{"class":141},"\u002F\\r",[131,204,206],{"class":205},"sVC51","?",[131,208,209],{"class":141},"\\n\u002F",[131,211,212],{"class":153},");\n",[131,214,216,218,220,222,224,227,230,233,235,238],{"class":133,"line":215},5,[131,217,167],{"class":141},[131,219,146],{"class":145},[131,221,185],{"class":184},[131,223,192],{"class":153},[131,225,226],{"class":195},"pop",[131,228,229],{"class":153},"() ",[131,231,232],{"class":145},"??",[131,234,150],{"class":149},[131,236,237],{"class":153},"; ",[131,239,240],{"class":160},"\u002F\u002F 最后一段可能不完整，留在缓冲中\n",[17,242,244],{"id":243},"websocket-与-http-polling","WebSocket 与 HTTP Polling",[13,246,247],{},"WebSocket 和 HTTP Polling 是计划中的传输方式，当前尚未实现具体的前端运行时消费逻辑。如果通过其他方式将合法命令送入 Runtime API，Runtime 自身不关心字节来源。",[17,249,250],{"id":250},"参考",[93,252,253,259],{},[60,254,255,258],{},[25,256,257],{},"packages\u002Fa-at-ui\u002Fsrc\u002Fruntime\u002Fvue\u002Fstream.ts"," — SSE 流解析实现",[60,260,261,264,265,268,269,272],{},[25,262,263],{},"packages\u002Fa-at-ui\u002Fsrc\u002Fruntime\u002Fvue\u002Fadapter.ts"," — ",[25,266,267],{},"dispatch()"," 和 ",[25,270,271],{},"handleError()"," 接口",[274,275,276],"style",{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":44,"searchDepth":157,"depth":157,"links":278},[279,280,281,282,283],{"id":19,"depth":157,"text":20},{"id":49,"depth":157,"text":49},{"id":116,"depth":157,"text":116},{"id":243,"depth":157,"text":244},{"id":250,"depth":157,"text":250},"md",{},true,"\u002Fzh\u002Fprotocol\u002Ftransport",{"title":5,"description":15},"zh\u002Fprotocol\u002Ftransport","aooNqtohIsrkvaMY3z3wcC8WwBR0ZvyEwk998jZXS8U",1780547990951]