resuma 0.4.3

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