resuma 0.4.7

Resuma — resumable SSR Rust web framework: zero hydration, islands, server actions, Flow (Axum).
Documentation
1
function m(t){let e=new Map;for(let r of t){let n=d(r.id);e.set(n,U(n,r.value))}return e}function d(t){return typeof t=="string"?t:typeof t=="number"?`s${t}`:`s${t[0]}`}function U(t,e){let r=e,n=new Set,o={id:t,get value(){return r},set value(s){o.set(s)},set(s){Object.is(s,r)||(r=s,n.forEach(a=>a(r)))},update(s){let a=s(r);a!==void 0?o.set(a):n.forEach(i=>i(r))},subscribe(s){return n.add(s),()=>n.delete(s)}};return o}var B="RESUMA-DYN";function f(t,e){t.querySelectorAll(B.toLowerCase()).forEach(n=>{let o=n.getAttribute("data-r-signal");if(!o)return;let s=e.get(o);s&&s.subscribe(a=>{n.textContent=S(a)})})}function g(t,e){t.querySelectorAll("[data-r-bind]").forEach(n=>k(n,e)),V(t,e)}function V(t,e){let r=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT),n=r.currentNode;for(;n;)n instanceof HTMLElement&&k(n,e),n=r.nextNode()}function k(t,e){for(let r of Array.from(t.attributes)){let n=r.name;if(!n.startsWith("data-r-bind:"))continue;let o=n.slice(12),[s,a="{}"]=r.value.split("|"),i=e.get(s);if(!i)continue;let c=l=>{let G=a.replace("{}",S(l));t.setAttribute(o,G)};c(i.value),i.subscribe(c)}}function S(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);try{return JSON.stringify(t)}catch{return String(t)}}function R(){let t=window.__resuma;if(!t)return;let e=document.getElementById("resuma-root")??document.body;f(e,t.signals),g(e,t.signals)}var J="resuma-island";function p(t,e){t.querySelectorAll(J).forEach(n=>{let o=n.getAttribute("data-r-chunk");if(!o)return;if((n.getAttribute("data-r-load")??"eager")==="visible"&&"IntersectionObserver"in window){new IntersectionObserver((i,c)=>{for(let l of i)l.isIntersecting&&(c.unobserve(l.target),E(n,o,e))},{rootMargin:"100px"}).observe(n);return}E(n,o,e)})}async function E(t,e,r){let n=t.getAttribute("data-r-props")??"{}",o={};try{o=JSON.parse(n)}catch{}await z(e,o,t,r)}async function z(t,e,r,n){try{let o=await import(`/_resuma/island-chunk/${t}.js`);typeof o.resume=="function"&&o.resume(e,n,r)}catch(o){console.debug("[resuma] island chunk unavailable, staying static",t,o)}}function K(t,e,r){let n=Object.create(r.state);if(!t)return n;for(let[o,s]of Object.entries(t)){let a=e.get(d(s));a&&(n[o]=a)}return n}function _(t,e,r){for(let n of t)try{let o=K(n.captures,e,r),s=new Function(`return (${n.body});`)(),a=n.target!=null?e.get(d(n.target))??null:null,i=()=>{try{let c=s(o,r);a&&c!==void 0&&a.set(c)}catch(c){console.error("[resuma] effect",n.id,c)}};i();for(let c of n.deps)e.get(d(c))?.subscribe(()=>i())}catch(o){console.error("[resuma] effect init",n.id,o)}}function T(t){let e=window.__resuma;e&&(e.loaded.has(t)||import(`/_resuma/handler/${t}.js`).then(r=>{e.loaded.set(t,r)}).catch(()=>{}))}function C(t,e){let r=[...new Set(t.filter(o=>o&&o!=="__page__"))];if(!r.length)return;if(!("IntersectionObserver"in window)){for(let o of r)T(o);return}let n=new IntersectionObserver((o,s)=>{for(let a of o){if(!a.isIntersecting)continue;let i=a.target.dataset.rChunk;i&&T(i),s.unobserve(a.target)}},{rootMargin:"120px"});for(let o of e.querySelectorAll("resuma-boundary[data-r-chunk]"))n.observe(o);for(let o of r){let s=document.createElement("resuma-boundary");s.hidden=!0,s.dataset.rChunk=o,e.appendChild(s),n.observe(s)}}var M=new Map;async function L(t,e){if(e){let i=M.get(e);return i||(i=x(e),M.set(e,i)),i}let[r,n]=t.split("#"),o=window.__resuma;if(r==="__page__"){let i=o.handlers[r]?.[n];if(i)return x(i)}let s=o.loaded.get(r);s||(s=await import(`/_resuma/handler/${r}.js`),o.loaded.set(r,s));let a=s[n];if(!a)throw new Error(`[resuma] handler ${n} not found in chunk ${r}`);return a}function x(t){let e=t.trim(),n=e.startsWith("(")||e.startsWith("function")||e.startsWith("async")?`return (${t});`:`return (async (event, state, __resuma) => { ${t} });`;return new Function(n)()}var I="resuma-root",h="resuma-state",b=null;function H(t){b=t}function v(){return document.getElementById(I)??document.body}function X(t){if(!t?.textContent)return{signals:[],handlers:{},islands:[],actions:[]};try{return JSON.parse(t.textContent)}catch(e){return console.error("[resuma] failed to parse state payload",e),{signals:[],handlers:{},islands:[],actions:[]}}}function Y(t,e){if(t===e)return!0;let r="http://resuma.local",n=new URL(t,r),o=new URL(e,r);if(n.search)return n.pathname+n.search===o.pathname+o.search;if(n.pathname===o.pathname)return!0;if(n.pathname!=="/"&&o.pathname.startsWith(n.pathname)){let s=o.pathname.charCodeAt(n.pathname.length);return s===void 0||s===47}return!1}function A(t){document.querySelectorAll("a[data-r-nav]").forEach(e=>{let r=e.getAttribute("href");if(!r)return;let n=e.getAttribute("data-r-active-class");if(!n)return;let o=(e.getAttribute("data-r-base-class")??e.className).split(/\s+/).filter(s=>s&&s!==n).join(" ");e.setAttribute("data-r-base-class",o),e.className=Y(r,t)?`${o} ${n}`.trim():o})}function Q(){if(!window.__resuma){window.location.reload();return}if(b){b(),A(location.pathname+location.search);return}let t=X(document.getElementById(h)),e=m(t.signals),r={};for(let[a,i]of e)r[a]=i;let n=window.__resuma,o={state:r,signals:e,handlers:t.handlers,contexts:t.contexts??{},loaded:n.loaded??new Map,action:n.action,safeAction:n.safeAction,refreshIsland:n.refreshIsland,context:a=>o.contexts[a],navigate:u,buildUrl:w};window.__resuma=o;let s=v();f(s,e),g(s,e),p(s,e),A(location.pathname+location.search)}function Z(){let t=v(),e=t.querySelector("[autofocus]")??t.querySelector("h1, [role='heading'], main")??t;e.hasAttribute("tabindex")||e.setAttribute("tabindex","-1");try{e.focus({preventScroll:!0})}catch{e.focus()}}function w(t,e){let r=new URL(t,location.origin);if(e)for(let[n,o]of Object.entries(e))o!=null&&o!==""&&r.searchParams.set(n,o);return r.pathname+r.search}async function u(t,e=!0){try{let r=await fetch(t,{headers:{Accept:"text/html"},credentials:"same-origin"});if(!r.ok){window.location.href=t;return}let n=await r.text(),o=new DOMParser().parseFromString(n,"text/html"),s=o.getElementById(I),a=o.getElementById(h);if(!s||!a?.textContent){window.location.href=t;return}v().innerHTML=s.innerHTML;let i=document.getElementById(h);i&&(i.textContent=a.textContent),o.title&&(document.title=o.title),e&&history.pushState({resumaNav:!0},"",t),Q(),e&&window.scrollTo(0,0),Z()}catch(r){console.error("[resuma] navigation failed",r),window.location.href=t}}function y(t){t.startsWith("/")&&!t.startsWith("//")?u(t):window.location.assign(t)}function tt(t,e){if(e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||t.target&&t.target!=="_self")return!1;let r=t.getAttribute("href");if(!r||r.startsWith("#")||r.startsWith("javascript:"))return!1;if(r.startsWith("http://")||r.startsWith("https://"))try{return new URL(r).origin===location.origin}catch{return!1}return!0}function P(){document.addEventListener("click",t=>{let e=t.target;if(!(e instanceof Element))return;let r=e.closest("a[data-r-nav]");if(!r||!tt(r,t))return;let n=r.getAttribute("href");n&&(t.preventDefault(),u(n))}),window.addEventListener("popstate",()=>{u(location.pathname+location.search,!1)})}var $="resuma-state",et="resuma-root";function nt(){let t=document.getElementById($);if(!t?.textContent)return"";try{return JSON.parse(t.textContent).csrf_token??""}catch{return""}}function j(t={}){let e={...t},r=nt();return r&&(e["x-resuma-csrf"]=r),e}var W=()=>document.getElementById(et)??document.body;function rt(){let t=document.getElementById($);if(!t||!t.textContent)return{signals:[],handlers:{},islands:[],actions:[]};try{return JSON.parse(t.textContent)}catch(e){return console.error("[resuma] failed to parse state payload",e),{signals:[],handlers:{},islands:[],actions:[]}}}var O=!1;function F(){let t=rt(),e=m(t.signals),r={};for(let[a,i]of e)r[a]=i;let n=window.__resuma,o={state:r,signals:e,handlers:t.handlers,contexts:t.contexts??{},loaded:n?.loaded??new Map,action:N,safeAction:ft,refreshIsland:q,context:a=>o.contexts[a],navigate:u,buildUrl:w};window.__resuma=o;let s=W();f(s,e),g(s,e),p(s,e),ct(s),lt(s),ut(s),dt(t.visible_tasks??{},r),_(t.effects??[],e,o),C(t.lazy_chunks??[],s)}async function xt(){O||(O=!0,H(F),F(),st(),it(),P(),gt())}function ot(t){let e=window.__resuma;if(!t.length)return e.state;let r={};for(let n of t){let[o,s]=n.split(":"),a=s??o,i=e.signals.get(a);i&&(r[o]=i)}return Object.assign(Object.create(e.state),r)}async function Lt(t,e,r,n){let o=await L(t,e),s=ot(n);await o(r,s,window.__resuma)}function st(){document.addEventListener("submit",async t=>{if(!(t.target instanceof HTMLFormElement))return;let e=t.target;if(!e.getAttribute("data-r-submit"))return;t.preventDefault();let r=e.getAttribute("data-r-submit"),n=new FormData(e),o={};n.forEach((a,i)=>{o[i]=String(a)});let s=new URLSearchParams(o);try{let a=await fetch(e.action||`/_resuma/submit/${encodeURIComponent(r)}`,{method:"POST",credentials:"same-origin",headers:j({"content-type":"application/x-www-form-urlencoded",accept:"application/json"}),body:s.toString()}),i=await a.json();if(!a.ok||i.ok===!1){at(e,i.field_errors??{}),(a.status>=500||!i.field_errors)&&console.error("[resuma] submit error",i.error??`submit ${r} failed`);return}D(e),i.redirect&&y(i.redirect)}catch(a){console.error("[resuma] submit error",a)}},!0)}function at(t,e){D(t);for(let[r,n]of Object.entries(e)){let o=t.querySelector(`[name="${r}"]`);if(!o)continue;let s=document.createElement("span");s.className="resuma-field-error",s.setAttribute("data-r-field-error",r),s.textContent=n,o.insertAdjacentElement("afterend",s)}}function D(t){t.querySelectorAll("[data-r-field-error]").forEach(e=>e.remove())}function it(){document.addEventListener("submit",t=>{if(!(t.target instanceof HTMLFormElement))return;let e=t.target;if(!e.hasAttribute("data-r-loader-refresh")||e.getAttribute("data-r-on:submit")||e.querySelector("[data-r-on\\:submit]"))return;t.preventDefault();let r=new FormData(e),n={};r.forEach((s,a)=>{let i=String(s);i&&(n[a]=i)});let o=e.getAttribute("action")||location.pathname;window.__resuma?.navigate(w(o,n))},!0)}function ct(t){t.querySelectorAll("template[data-r-stream-chunk]").forEach(e=>{let r=e.getAttribute("data-r-stream-chunk");if(!r)return;let n=t.querySelector(`template[data-r-stream="${r}"]`);if(!n||!n.parentElement)return;let o=e.innerHTML,s=document.createRange().createContextualFragment(o);n.replaceWith(s),e.remove()})}function lt(t){t.querySelectorAll("template[data-r-portal]").forEach(e=>{let r=e.getAttribute("data-r-portal");if(!r)return;let n=document.getElementById(r)??document.querySelector(`[data-r-portal-target="${r}"]`);if(!n)return;let o=document.createDocumentFragment();for(;e.content.firstChild;)o.appendChild(e.content.firstChild);n.appendChild(o),e.remove()})}function ut(t){"startViewTransition"in document&&t.querySelectorAll("[data-r-vt]").forEach(e=>{e.addEventListener("click",r=>{let n=r.target?.closest("a[href]");if(!n||n.getAttribute("target")==="_blank")return;let o=n.getAttribute("href");if(!o||o.startsWith("#")||o.startsWith("javascript:"))return;r.preventDefault();let s=()=>{window.location.href=o};document.startViewTransition?.(s)})})}function dt(t,e){let r=Object.entries(t);if(!r.length)return;let n=(o,s)=>{try{let a=new Function("state","__resuma",`return ${s}`);Promise.resolve(a(e,window.__resuma))}catch(a){console.error("[resuma] visible task",o,a)}};if("IntersectionObserver"in window){let o=new IntersectionObserver((s,a)=>{for(let i of s){if(!i.isIntersecting)continue;let c=i.target.dataset.rVisibleTask,l=c?t[c]:void 0;l&&n(c,l),a.unobserve(i.target)}},{rootMargin:"50px"});for(let[s]of r){let a=document.createElement("span");a.hidden=!0,a.dataset.rVisibleTask=s,W().appendChild(a),o.observe(a)}}else for(let[o,s]of r)n(o,s)}async function N(t,e){let r=await fetch(`/_resuma/action/${encodeURIComponent(t)}`,{method:"POST",credentials:"same-origin",headers:j({"content-type":"application/json"}),body:JSON.stringify({args:e})});if(!r.ok)throw new Error(`[resuma] action ${t} failed: ${r.status}`);let n=await r.json();if(n.ok===!1)throw new Error(n.error??"action failed");return n.redirect&&y(n.redirect),n.value}async function ft(t,e){try{return{ok:!0,value:await N(t,e)}}catch(r){return{ok:!1,error:r instanceof Error?r.message:String(r)}}}async function q(t){let e=await fetch(`/_resuma/island/${encodeURIComponent(t)}`);if(!e.ok)return;let r=await e.text(),n=document.querySelector(`resuma-island[data-r-instance="${t}"]`);n&&(n.outerHTML=r),R()}function gt(){if(!window.__resumaDev||typeof WebSocket>"u")return;let t=location.protocol==="https:"?"wss":"ws",e=!1,r=()=>{let n=new WebSocket(`${t}://${location.host}/_resuma/dev/ws`);n.addEventListener("open",()=>{if(e){location.reload();return}e=!0}),n.addEventListener("message",o=>{let s=String(o.data);if(s==="reload"){location.reload();return}s.startsWith("island:")&&q(s.slice(7))}),n.addEventListener("close",()=>{setTimeout(r,500)}),n.addEventListener("error",()=>{n.close()})};r()}export{xt as bootstrap,ot as buildLocalState,F as mountPage,Lt as runHandler};