resuma 1.2.1

Resuma — resumable SSR Rust web framework: zero hydration, islands, server actions, Flow (Axum).
Documentation
function J(e,t){let n=window.__resuma;if(!n?.action)throw new Error("Resuma core not loaded");return n.action(e,t)}var q=[],$=new Map,M=new Map,P=new WeakMap,k=new Map,L=new Set;function U(e){let t=P.get(e);return t||(t=new Set,P.set(e,t)),t}function D(e){let t=k.get(e);t&&(t.es?.close(),t.es=null,k.delete(e))}function E(e){e&&L.add(e);let t=k.get(e);t&&(t.terminal=!0,t.es?.close(),t.es=null)}function x(e){q.push(e)}function K(){for(;q.length;){let e=q.pop();try{e?.()}catch{}}}function H(e){return e.getAttribute("data-r-flow-graph")??e.getAttribute("data-r-event-stream")??e.getAttribute("data-r-worker-panel")??""}function F(e){return e.getAttribute("data-r-graph-token")??""}function j(e,t){let n;return((...s)=>{n!==void 0&&clearTimeout(n),n=setTimeout(()=>e(...s),t)})}function O(e){let t=e.type??"event",n=e.timestamp_ms??0;switch(t){case"log":return`${t}:${e.message??""}`;case"progress":return`${t}:${e.value??""}`;case"result":return`${t}:${JSON.stringify(e.data??null)}`;case"node_done":return`${t}:${e.duration_ms??n}`;case"graph_done":return`${t}:done`;default:return`${t}:${n}`}}function R(e){return e==="done"||e==="failed"||e==="paused"}function G(e){return typeof e=="string"?e:e&&typeof e=="object"&&"0"in e?String(e[0]):"node"}function I(e){let t=e.type??"event";switch(t){case"log":return`[log] ${e.message}`;case"progress":return`[progress] ${e.value}%`;case"ai_thinking":return`[ai] ${e.content}`;case"tool_call":return`[tool] ${e.tool}`;case"node_start":return`[start] ${G(e.node)}`;case"node_done":return`[done] ${G(e.node)} (${e.duration_ms}ms)`;case"node_failed":return`[error] ${e.error}`;case"result":return`[result] ${JSON.stringify(e.data)}`;case"graph_done":return"[graph] complete";default:return`[${t}]`}}function v(e,t){if(!t)return e;let n=e.includes("?")?"&":"?";return`${e}${n}token=${encodeURIComponent(t)}`}function b(e){let t={};return e&&(t["X-Resuma-Graph-Token"]=e),t}function Q(){let e=document.getElementById("resuma-state");if(!e?.textContent)return"";try{return JSON.parse(e.textContent).csrf_token??""}catch{return""}}function V(e){let t=b(e),n=Q();return n&&(t["x-resuma-csrf"]=n),t}function X(e,t){return`/_resuma/graph/${encodeURIComponent(e)}/${t}`}async function Y(e){try{let t=await e.json();if(t.error)return t.error}catch{}return e.status===422?"Graph already finished \u2014 run the worker again.":e.status===401||e.status===403?"Unauthorized \u2014 refresh the page and run the worker again.":`Control failed (HTTP ${e.status}).`}function Z(e){let t=Math.floor(e/1e3),n=Math.floor(t/3600),s=Math.floor(t%3600/60),r=t%60;return n>0?`${n}h ${s}m`:s>0?`${s}m ${r}s`:`${r}s`}function B(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}async function ee(){try{return await J("exec_status",[])}catch{}try{let e=await fetch("/_resuma/status",{credentials:"same-origin"});return e.ok?await e.json():null}catch{return null}}function N(e,t){let n=t.queues.reduce((a,o)=>a+o.pending,0),s=t.queues.reduce((a,o)=>a+o.processing,0),r=n>10?"r-flow-dash__badge r-flow-dash__badge--warn":t.ok?"r-flow-dash__badge":"r-flow-dash__badge r-flow-dash__badge--err",i=t.workers.names.length>0?t.workers.names.map(a=>`<span class="r-flow-dash__chip">${B(a)}</span>`).join(""):'<span class="r-flow-dash__chip">none registered</span>',c=t.queues.map(a=>{let o=a.pending+a.processing+a.done+a.failed||1,u=Math.round(a.processing/o*100);return`<tr>
        <td>${B(a.queue)}</td>
        <td>${a.pending}</td>
        <td>${a.processing}</td>
        <td>${a.done}</td>
        <td>${a.failed}</td>
        <td><div class="r-flow-dash__bar"><svg viewBox="0 0 100 6" preserveAspectRatio="none" aria-hidden="true"><rect class="r-flow-dash__bar-fill" width="${u}" height="6" rx="3"/></svg></div></td>
      </tr>`}).join("");e.innerHTML=`
    <header class="r-flow-dash__header">
      <h2 class="r-flow-dash__title">Resuma OS</h2>
      <span class="${r}">${t.ok?"healthy":"degraded"} \xB7 uptime ${Z(t.uptime_ms)}</span>
    </header>
    <div class="r-flow-dash__grid">
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Workers</p><p class="r-flow-dash__stat-value">${t.workers.registered}</p></div>
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Graphs running</p><p class="r-flow-dash__stat-value">${t.graphs.running}</p></div>
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Graphs paused</p><p class="r-flow-dash__stat-value">${t.graphs.paused}</p></div>
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Queue pending</p><p class="r-flow-dash__stat-value">${n}</p></div>
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Processing</p><p class="r-flow-dash__stat-value">${s}</p></div>
      <div class="r-flow-dash__stat"><p class="r-flow-dash__stat-label">Scheduler due</p><p class="r-flow-dash__stat-value">${t.scheduler.due}</p></div>
    </div>
    <section class="r-flow-dash__section">
      <h3>Workers</h3>
      <div class="r-flow-dash__chips">${i}</div>
    </section>
    <section class="r-flow-dash__section">
      <h3>Queues</h3>
      <table class="r-flow-dash__table">
        <thead><tr><th>Name</th><th>Pending</th><th>Active</th><th>Done</th><th>Failed</th><th>Load</th></tr></thead>
        <tbody>${c}</tbody>
      </table>
    </section>
    <section class="r-flow-dash__section">
      <h3>Scheduler</h3>
      <p style="margin:0;font-size:.8rem;color:#94a3b8">${t.scheduler.enabled} enabled \xB7 ${t.scheduler.total} total \xB7 ${t.scheduler.due} due now</p>
    </section>`}function te(e){let t=e.querySelector("[data-r-flow-dashboard-root]")??e,n=Number(e.getAttribute("data-r-flow-dashboard-poll")??"5000")||5e3,s=e.getAttribute("data-r-flow-dashboard-init"),r=async()=>{let a=await ee();a&&N(t,a)};if(s)try{N(t,JSON.parse(s))}catch{}r();let i=window.setInterval(()=>{r()},n),c=()=>clearInterval(i);e.addEventListener("resuma:disconnect",c,{once:!0}),x(c)}function W(e,t,n,s){if(!e||typeof EventSource>"u")return()=>{};if(L.has(e))return()=>{};let r=k.get(e);if(r||(r={listeners:new Set,es:null,refs:0,terminal:!1},k.set(e,r)),r.terminal)return()=>{};r.listeners.add(n),r.refs+=1,(()=>{if(r.terminal||r.es)return;let a=v(`/_resuma/graph/${encodeURIComponent(e)}/events`,t),o=new EventSource(a);r.es=o,o.onmessage=u=>{try{let l=JSON.parse(u.data);for(let m of r.listeners)m(l);l.type==="graph_done"&&(r.terminal=!0,o.close(),r.es=null)}catch{}},o.onerror=()=>{o.close(),r.es=null}})();let c=!1;return()=>{c||(c=!0,r.listeners.delete(n),r.refs-=1,r.refs<=0&&(r.es?.close(),k.delete(e)))}}function ne(e,t){let n=e.querySelector("[data-r-flow-graph-track]"),s=e.querySelector("[data-r-flow-graph-status]"),r=t.nodes??[];if(n&&(r.length?(n.innerHTML="",r.forEach((i,c)=>{if(c>0){let u=document.createElement("span");u.className="r-flow-graph__arrow",u.textContent="\u2192",n.appendChild(u)}let a=document.createElement("span");a.className=`r-flow-graph__node r-flow-graph__node--${i.status}`;let o=i.status==="done"?"\u2713":i.status==="running"?"\u25CF":i.status==="failed"?"\u2717":i.status==="paused"?"\u2016":"\u25CB";a.textContent=`${o} ${i.label}`,n.appendChild(a)})):n.textContent="No nodes"),s){let i=t.worker?` \xB7 ${t.worker}`:"",c=t.status??"unknown";s.textContent=`Status: ${c}${i}`}}async function C(e,t,n){let s=e.querySelector("[data-r-flow-graph-status]");if(L.has(t)){s&&(s.textContent="Graph finished.");return}let r=v(`/_resuma/graph/${encodeURIComponent(t)}`,n);try{let i=await fetch(r,{headers:b(n),credentials:"same-origin"});if(!i.ok){s&&(i.status===401||i.status===403?(E(t),s.textContent=L.has(t)?"Graph finished.":"Unauthorized \u2014 refresh the page and run the worker again."):s.textContent=`Failed to load graph (HTTP ${i.status}).`);return}let c=await i.json();ne(e,c),R(c.status)&&E(t)}catch{s&&(s.textContent="Network error loading graph.")}}function re(e){let t=H(e),n=F(e);if(!t)return;e.dataset.rFlowMounted==="1"&&e.dispatchEvent(new Event("resuma:disconnect")),e.dataset.rFlowMounted="1";let s=!1,r=null,i=()=>{s=!0,r?.(),r=null,delete e.dataset.rFlowMounted};e.addEventListener("resuma:disconnect",i,{once:!0}),x(i);let c=j(()=>{s||C(e,t,n)},400);(async()=>{if(!(s||(await C(e,t,n),s)||!(e.getAttribute("data-r-flow-graph-live")==="true"))){try{let o=v(`/_resuma/graph/${encodeURIComponent(t)}`,n),u=await fetch(o,{headers:b(n),credentials:"same-origin"});if(s)return;if(u.ok){let l=await u.json();if(R(l.status))return}}catch{}s||(r=W(t,n,o=>{if(!s&&!(o.type==="progress"||o.type==="log"||o.type==="ai_thinking")){if(o.type==="graph_done"||o.type==="node_done"||o.type==="node_start"||o.type==="node_failed"){C(e,t,n);return}c()}}))}})()}function z(e){return e.querySelector("[data-r-event-stream-viewport]")??e.querySelector(".r-event-stream__viewport")??e.querySelector(".r-event-stream-list")??e}function A(e,t=!0){let n=()=>{let s=e.scrollHeight;t&&"scrollTo"in e?e.scrollTo({top:s,behavior:"smooth"}):e.scrollTop=s};requestAnimationFrame(()=>{n(),requestAnimationFrame(n)})}function se(e,t){if(typeof ResizeObserver>"u")return()=>{};let n=()=>{e.scrollTop=e.scrollHeight},s=new ResizeObserver(n);return s.observe(t),()=>s.disconnect()}function ae(e){let t=H(e),n=F(e);if(!t)return;let s=z(e),r=e.querySelector("ul")??e,i=1e3,c=(M.get(t)??0)+1;M.set(t,c);let a=()=>M.get(t)===c,o=U(r),u=se(s,r),l=(p,w=!1)=>{if(!a())return;let g=document.createElement("li");for(g.textContent=p,r.appendChild(g);r.children.length>i;)r.removeChild(r.firstChild);A(s,w)},m=(p,w=!1)=>{if(!a()||e.dataset.rStreamTerminal==="1"&&p.type!=="graph_done")return;let g=O(p);o.has(g)||(o.add(g),l(I(p),w),p.type==="graph_done"&&(e.dataset.rStreamTerminal="1",E(t)))};$.get(t)?.(),$.delete(t),e.dataset.rFlowMounted==="1"&&e.dispatchEvent(new Event("resuma:disconnect")),e.dataset.rFlowMounted="1";let d=!1,f=null,h=()=>{d=!0,u(),f?.(),f=null,delete e.dataset.rFlowMounted,M.get(t)===c&&M.delete(t),$.get(t)===h&&$.delete(t)};$.set(t,h),e.addEventListener("resuma:disconnect",h,{once:!0}),x(h),(async()=>{let p=v(`/_resuma/graph/${encodeURIComponent(t)}/replay`,n),w=b(n),g=!1;try{let _=await fetch(p,{headers:w,credentials:"same-origin"});if(!a()||d)return;if(_.ok){let S=await _.json();r.innerHTML="",o.clear();for(let T of S)m(T,!1);A(s,!1),g=S.some(T=>T.type==="graph_done"),g&&(e.dataset.rStreamTerminal="1",E(t))}}catch{}if(!(!a()||d||g||L.has(t))){try{let _=v(`/_resuma/graph/${encodeURIComponent(t)}`,n),S=await fetch(_,{headers:w,credentials:"same-origin"});if(!a()||d)return;if(S.ok){let T=await S.json();if(R(T.status)){e.dataset.rStreamTerminal="1",E(t);return}}}catch{}!a()||d||(f=W(t,n,_=>{!a()||d||m(_)}))}})()}async function y(e,t,n,s){let r=e.querySelector("[data-r-worker-status]"),i=e.querySelector("[data-r-worker-pause]"),c=e.querySelector("[data-r-worker-resume]"),a=e.querySelector("[data-r-worker-cancel]"),o=e.querySelector("[data-r-worker-replay]"),u=v(`/_resuma/graph/${encodeURIComponent(t)}`,n);try{let l=await fetch(u,{headers:b(n),credentials:"same-origin"});if(!l.ok){r&&(r.textContent=s??`Could not load graph (HTTP ${l.status}).`);return}let d=(await l.json()).status??"unknown",f=d==="done"||d==="failed",h=d==="paused";i&&(i.disabled=f||h),c&&(c.disabled=!h),a&&(a.disabled=f),o&&(o.disabled=!1),r&&(s?r.textContent=s:f?r.textContent=`Graph ${d} \u2014 Pause/Cancel only work while running. Run again for a new graph.`:h?r.textContent="Paused \u2014 click Resume to continue from checkpoint.":r.textContent="Running \u2014 Pause stops cooperatively at the next checkpoint (~3s).")}catch{r&&s&&(r.textContent=s)}}function oe(e){let t=H(e),n=F(e);if(!t)return;e.dataset.rFlowMounted==="1"&&e.dispatchEvent(new Event("resuma:disconnect")),e.dataset.rFlowMounted="1";let s=!1,r=null,i=()=>{s=!0,r?.(),r=null,delete e.dataset.rFlowMounted};e.addEventListener("resuma:disconnect",i,{once:!0}),x(i),y(e,t,n);let c=j(()=>{s||y(e,t,n)},300);(async()=>{try{let l=v(`/_resuma/graph/${encodeURIComponent(t)}`,n),m=await fetch(l,{headers:b(n),credentials:"same-origin"});if(s)return;if(m.ok){let d=await m.json();if(R(d.status)){E(t),await y(e,t,n);return}}}catch{}s||(r=W(t,n,l=>{if(!s){if(l.type==="graph_done"){E(t),y(e,t,n);return}(l.type==="node_done"||l.type==="node_failed"||l.type==="progress")&&c()}}))})();let a=()=>({method:"POST",credentials:"same-origin",headers:V(n)}),o=e.closest("[data-r-flow-execution]"),u=async l=>{let m=e.querySelector("[data-r-worker-status]"),d=e.querySelector("[data-r-worker-pause]"),f=e.querySelector("[data-r-worker-resume]"),h=e.querySelector("[data-r-worker-cancel]");d&&(d.disabled=!0),f&&(f.disabled=!0),h&&(h.disabled=!0),m&&(m.textContent=`${l}\u2026`);let p=await fetch(X(t,l),a());if(s)return;if(!p.ok){let _=await Y(p);await y(e,t,n,_);return}let w=o?.querySelector("[data-r-flow-graph]");w&&C(w,t,n),await y(e,t,n,`${{pause:"Paused",resume:"Resumed",cancel:"Cancelled"}[l]} \u2014 refreshing graph\u2026`)};e.querySelector("[data-r-worker-pause]")?.addEventListener("click",()=>{u("pause")}),e.querySelector("[data-r-worker-resume]")?.addEventListener("click",()=>{u("resume")}),e.querySelector("[data-r-worker-cancel]")?.addEventListener("click",()=>{u("cancel")}),e.querySelector("[data-r-worker-replay]")?.addEventListener("click",async()=>{let l=await fetch(v(`/_resuma/graph/${encodeURIComponent(t)}/replay`,n),{headers:b(n),credentials:"same-origin"});if(!l.ok)return;let m=await l.json(),d=e.closest("[data-r-flow-execution]")?.querySelector("[data-r-event-stream]"),f=d?.querySelector("ul"),h=d?z(d):null;if(!f)return;let p=U(f);p.clear(),f.innerHTML="",delete d.dataset.rStreamTerminal;for(let w of m){let g=O(w);if(p.has(g))continue;p.add(g);let _=document.createElement("li");_.textContent=I(w),f.appendChild(_)}h&&A(h,!1),await y(e,t,n,"Replay loaded.")})}function ie(e=document){let t=new Set;e.querySelectorAll("[data-r-flow-dashboard], [data-r-flow-graph], [data-r-event-stream], [data-r-worker-panel]").forEach(n=>{let s=H(n);s&&t.add(s),n.dispatchEvent(new Event("resuma:disconnect"))});for(let n of t)D(n)}function ce(e=document,t){t?.flush!==!1?K():ie(e),e.querySelectorAll("[data-r-flow-dashboard]").forEach(te);let n=new Set,s=(r,i,c)=>{let a=H(i);if(!a){c(i);return}let o=`${r}:${a}`;n.has(o)||(n.add(o),c(i))};e.querySelectorAll("[data-r-flow-graph]").forEach(r=>{s("graph",r,re)}),e.querySelectorAll("[data-r-event-stream]").forEach(r=>{s("stream",r,ae)}),e.querySelectorAll("[data-r-worker-panel]").forEach(r=>{s("panel",r,oe)})}export{ie as disconnectFlowWidgets,ce as initFlowWidgets};