const LS = String.fromCharCode(0x2028);
const PS = String.fromCharCode(0x2029);
function safeJsonForScript(v: unknown): string {
return JSON.stringify(v)
.split("<").join("\\u003C")
.split(">").join("\\u003E")
.split("&").join("\\u0026")
.split(LS).join("\\u2028")
.split(PS).join("\\u2029");
}
export function liveSubscribeScript(args: {
/** Topic name, e.g. "repo:abc" or "user:42" */
topic: string;
/** id of the DOM element that receives appended event HTML */
targetElementId: string;
/** Optional JS function body taking (event) and returning an HTML string.
* If omitted, a default escapes the JSON payload as text. */
formatFn?: string;
}): string {
const topic = safeJsonForScript(args.topic);
const targetId = safeJsonForScript(args.targetElementId);
const formatFnBody =
args.formatFn ??
"return '<li>' + String(event && event.data || '').replace(/[&<>\"']/g,function(c){return {'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c];}) + '</li>';";
return (
"(function(){try{" +
"if(typeof EventSource==='undefined')return;" +
"var t=" + topic + ",id=" + targetId + ";" +
"var el=document.getElementById(id);if(!el)return;" +
"function fmt(event){" + formatFnBody + "}" +
"var es,delay=1000;" +
"function connect(){" +
"try{es=new EventSource('/live-events/'+encodeURIComponent(t));}catch(e){setTimeout(connect,delay);return;}" +
"es.onmessage=function(m){try{var d=JSON.parse(m.data);var h=fmt(d);if(h&&el)el.insertAdjacentHTML('beforeend',h);}catch(e){}};" +
"es.onerror=function(){try{es.close();}catch(e){}setTimeout(connect,delay);};" +
"}connect();}catch(e){}})();"
);
}
export function liveCommentBannerScript(args: {
topic: string;
bannerElementId: string;
}): string {
const topic = safeJsonForScript(args.topic);
const id = safeJsonForScript(args.bannerElementId);
return (
"(function(){try{" +
"if(typeof EventSource==='undefined')return;" +
"var t=" + topic + ",bid=" + id + ";" +
"var b=document.getElementById(bid);if(!b)return;" +
"var n=0;var es,delay=1000;" +
"function show(){if(b.style)b.style.display='';" +
"var c=b.querySelector('.js-live-count');" +
"if(c)c.textContent=String(n);" +
"var lk=b.querySelector('.js-live-link');" +
"if(lk)lk.setAttribute('href',window.location.pathname+window.location.search);}" +
"function connect(){try{es=new EventSource('/live-events/'+encodeURIComponent(t));}catch(e){setTimeout(connect,delay);return;}" +
"es.onmessage=function(){n++;show();};" +
"es.onerror=function(){try{es.close();}catch(e){}setTimeout(connect,delay);};" +
"}connect();}catch(e){}})();"
);
}
export function liveLogTailScript(opts: {
topic: string;
targetElementId: string;
jobId?: string;
onRunDone?: string;
}): string {
const topic = safeJsonForScript(opts.topic);
const targetId = safeJsonForScript(opts.targetElementId);
const jobFilter = safeJsonForScript(opts.jobId ?? "");
const onRunDone = opts.onRunDone ? String(opts.onRunDone) : "";
const onRunDoneJson = safeJsonForScript(onRunDone);
return (
"(function(){try{" +
"if(typeof EventSource==='undefined')return;" +
"var t=" + topic + ",id=" + targetId + ",jf=" + jobFilter + ",onDone=" + onRunDoneJson + ";" +
"var el=document.getElementById(id);if(!el)return;" +
"var status=document.getElementById(id+'-status');" +
"function esc(s){return String(s==null?'':s).replace(/[&<>\"']/g,function(c){return {'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c];});}" +
"function setStatus(s){if(status)status.textContent=s;}" +
"function scroll(){try{el.scrollTop=el.scrollHeight;}catch(e){}}" +
"function append(txt){el.insertAdjacentHTML('beforeend',esc(txt));scroll();}" +
"function match(d){if(!jf)return true;return d&&d.jobId===jf;}" +
"var es;" +
"try{es=new EventSource('/live-events/'+encodeURIComponent(t));}catch(e){return;}" +
"es.addEventListener('open',function(){setStatus('live');});" +
"es.addEventListener('step-log',function(m){try{var d=JSON.parse(m.data);if(!match(d))return;" +
"var prefix='[step '+d.stepIndex+' '+(d.stream||'stdout')+'] ';" +
"var chunk=String(d.chunk==null?'':d.chunk);" +
"var lines=chunk.split('\\n');" +
"for(var i=0;i<lines.length;i++){if(i===lines.length-1&&lines[i]==='')continue;append(prefix+lines[i]+'\\n');}" +
"}catch(e){}});" +
"es.addEventListener('step-start',function(m){try{var d=JSON.parse(m.data);if(!match(d))return;" +
"append('>>> step '+d.stepIndex+' ('+(d.name||'')+') started\\n');" +
"}catch(e){}});" +
"es.addEventListener('step-done',function(m){try{var d=JSON.parse(m.data);if(!match(d))return;" +
"var dur=typeof d.durationMs==='number'?(d.durationMs<1000?d.durationMs+'ms':(d.durationMs/1000).toFixed(1)+'s'):'';" +
"append('<<< step '+d.stepIndex+' done (exit '+d.exitCode+(dur?', '+dur:'')+')\\n');" +
"}catch(e){}});" +
"es.addEventListener('run-done',function(m){try{setStatus('done');try{es.close();}catch(e){}if(onDone){try{(new Function(onDone))();}catch(e){}}}catch(e){}});" +
"es.onerror=function(){setStatus('disconnected');};" +
"}catch(e){}})();"
);
}
|