what-server 0.11.6 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{signal as p,batch as v}from"what-core";var h=null,k=!(typeof process<"u");async function S(e,t){if(h&&h.revalidatePath)return h.revalidatePath(e,t);k&&console.warn(`[what] revalidatePath('${e}') had no effect: no cache engine is bound. Create a what-isr engine and bind it in your adapter (setRevalidationHandler).`)}async function b(e,t){if(h&&h.revalidateTag)return h.revalidateTag(e,t);k&&console.warn(`[what] revalidateTag('${e}') had no effect: no cache engine is bound.`)}var A=new Map;function P(){if(typeof document<"u"){let e=document.querySelector('meta[name="what-csrf-token"]');if(e)return e.getAttribute("content");let t=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(t)return decodeURIComponent(t[1])}return null}function I(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function R(e,t){if(!e||!t||e.length!==t.length)return!1;let r=0;for(let n=0;n<e.length;n++)r|=e.charCodeAt(n)^t.charCodeAt(n);return r===0}function U(e){return`<meta name="what-csrf-token" content="${String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var T=0;function C(){return`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),t=>t.toString(16).padStart(2,"0")).join(""):`c${(++T).toString(36)}_${Date.now().toString(36)}`}`}function O(e,t={}){let r=t.id||C(),{onError:n,onSuccess:l,revalidate:o}=t;typeof window>"u"&&A.set(r,{fn:e,options:t});async function a(...i){if(typeof window>"u")return e(...i);let s=t.timeout||3e4,u=new AbortController,c=setTimeout(()=>u.abort(),s);try{let f=P(),d={"Content-Type":"application/json","X-What-Action":r};f&&(d["X-CSRF-Token"]=f);let g=await fetch("/__what_action",{method:"POST",headers:d,credentials:"same-origin",signal:u.signal,body:JSON.stringify({args:i})});if(!g.ok){let w=await g.json().catch(()=>({message:"Action failed"}));throw new Error(w.message||"Action failed")}let y=await g.json();if(l&&l(y),o)for(let w of o)_(w);return y}catch(f){if(f.name==="AbortError"){let d=new Error(`Action "${r}" timed out after ${s}ms`);throw d.code="TIMEOUT",n&&n(d),d}throw n&&n(f),f}finally{clearTimeout(c)}}return a._actionId=r,a._isAction=!0,a}function x(e,t={}){let{onSuccess:r,onError:n,resetOnSuccess:l=!0}=t;return async o=>{let a,i;o instanceof Event?(o.preventDefault(),i=o.target,a=new FormData(i)):a=o;let s={},u=!1;for(let[c,f]of a.entries())typeof File<"u"&&f instanceof File&&(u=!0),s[c]?Array.isArray(s[c])?s[c].push(f):s[c]=[s[c],f]:s[c]=f;try{let c=u?await e(s,a):await e(s);return r&&r(c,i),l&&i&&i.reset(),c}catch(c){throw n&&n(c,i),c}}}function F(e){let t=p(!1),r=p(null),n=p(null);async function l(...o){t.set(!0),r.set(null);try{let a=await e(...o);return n.set(a),a}catch(a){throw r.set(a),a}finally{t.set(!1)}}return{trigger:l,isPending:()=>t(),error:()=>r(),data:()=>n(),reset:()=>{r.set(null),n.set(null)}}}function j(e,t={}){let{resetOnSuccess:r=!0}=t,n={current:null},l=F(x(e,{resetOnSuccess:r}));function o(a){a.preventDefault();let i=new FormData(a.target);return n.current=a.target,l.trigger(i)}return{...l,handleSubmit:o,formRef:n}}function M(e,t){let r=p(e),n=p([]),l=p(e);function o(u){let c=t(r.peek(),u);v(()=>{n.set([...n.peek(),u]),r.set(c)})}function a(u,c){v(()=>{if(n.set(n.peek().filter(f=>f!==u)),c!==void 0){l.set(c);let f=c;for(let d of n.peek())f=t(f,d);r.set(f)}})}function i(u,c){v(()=>{let f=n.peek().filter(y=>y!==u);n.set(f);let d=c!==void 0?c:l.peek();l.set(d);let g=d;for(let y of f)g=t(g,y);r.set(g)})}async function s(u,c){o(u);try{let f=await c();return a(u,f),f}catch(f){throw i(u),f}}return{value:()=>r(),isPending:()=>n().length>0,addOptimistic:o,resolve:a,rollback:i,withOptimistic:s,set:u=>{r.set(u),l.set(u)}}}var m=new Map;function N(e,t){return m.has(e)||m.set(e,new Set),m.get(e).add(t),()=>{m.get(e)?.delete(t)}}function _(e){let t=m.get(e);if(t)for(let r of t)try{r()}catch(n){console.error("[what] Revalidation error:",n)}}function H(e,t,r,n={}){let{csrfToken:l,skipCsrf:o=!1}=n;if(!o){if(!l)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let i=e?.headers?.["x-csrf-token"]||e?.headers?.["X-CSRF-Token"];if(!R(i,l))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=A.get(t);return a?Array.isArray(r)?a.fn(...r).then(async i=>{let s=a.options||{};if(Array.isArray(s.revalidate))for(let u of s.revalidate)await S(u);if(Array.isArray(s.revalidateTags))for(let u of s.revalidateTags)await b(u);return{status:200,body:i}}).catch(i=>(console.error(`[what] Action "${t}" error:`,i),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function V(){return[...A.keys()]}function X(e,t={}){let{onSuccess:r,onError:n,onSettled:l}=t,o={isPending:p(!1),error:p(null),data:p(null)};async function a(...i){o.isPending.set(!0),o.error.set(null);try{let s=await e(...i);return o.data.set(s),r&&r(s,...i),s}catch(s){throw o.error.set(s),n&&n(s,...i),s}finally{o.isPending.set(!1),l&&l(o.data.peek(),o.error.peek(),...i)}}return{mutate:a,isPending:()=>o.isPending(),error:()=>o.error(),data:()=>o.data(),reset:()=>{o.error.set(null),o.data.set(null)}}}export{O as action,U as csrfMetaTag,x as formAction,I as generateCsrfToken,V as getRegisteredActions,H as handleActionRequest,_ as invalidatePath,N as onRevalidate,F as useAction,j as useFormAction,X as useMutation,M as useOptimistic,R as validateCsrfToken};
1
+ import{signal as p,batch as v}from"what-core";var m=null,k=!(typeof process<"u");async function b(e,t){if(m&&m.revalidatePath)return m.revalidatePath(e,t);k&&console.warn(`[what] revalidatePath('${e}') had no effect: no cache engine is bound. Create a what-isr engine and bind it in your adapter (setRevalidationHandler).`)}async function S(e,t){if(m&&m.revalidateTag)return m.revalidateTag(e,t);k&&console.warn(`[what] revalidateTag('${e}') had no effect: no cache engine is bound.`)}var A=new Map;function P(){if(typeof document<"u"){let e=document.querySelector('meta[name="what-csrf-token"]');if(e)return e.getAttribute("content");let t=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(t)return decodeURIComponent(t[1])}return null}function U(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function R(e,t){if(!e||!t||e.length!==t.length)return!1;let r=0;for(let n=0;n<e.length;n++)r|=e.charCodeAt(n)^t.charCodeAt(n);return r===0}function O(e){return`<meta name="what-csrf-token" content="${String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var T=0,x=!1;function C(){return!x&&typeof process<"u",`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),t=>t.toString(16).padStart(2,"0")).join(""):`c${(++T).toString(36)}_${Date.now().toString(36)}`}`}function N(e,t={}){let r=t.id||C(),{onError:n,onSuccess:f,revalidate:o}=t;typeof window>"u"&&A.set(r,{fn:e,options:t});async function a(...s){if(typeof window>"u")return e(...s);let i=t.timeout||3e4,u=new AbortController,c=setTimeout(()=>u.abort(),i);try{let l=P(),d={"Content-Type":"application/json","X-What-Action":r};l&&(d["X-CSRF-Token"]=l);let g=await fetch("/__what_action",{method:"POST",headers:d,credentials:"same-origin",signal:u.signal,body:JSON.stringify({args:s})});if(!g.ok){let w=await g.json().catch(()=>({message:"Action failed"}));throw new Error(w.message||"Action failed")}let h=await g.json();if(f&&f(h),o)for(let w of o)D(w);return h}catch(l){if(l.name==="AbortError"){let d=new Error(`Action "${r}" timed out after ${i}ms`);throw d.code="TIMEOUT",n&&n(d),d}throw n&&n(l),l}finally{clearTimeout(c)}}return a._actionId=r,a._isAction=!0,a}function _(e,t={}){let{onSuccess:r,onError:n,resetOnSuccess:f=!0}=t;return async o=>{let a,s;o instanceof Event?(o.preventDefault(),s=o.target,a=new FormData(s)):a=o;let i={},u=!1;for(let[c,l]of a.entries())typeof File<"u"&&l instanceof File&&(u=!0),i[c]?Array.isArray(i[c])?i[c].push(l):i[c]=[i[c],l]:i[c]=l;try{let c=u?await e(i,a):await e(i);return r&&r(c,s),f&&s&&s.reset(),c}catch(c){throw n&&n(c,s),c}}}function E(e){let t=p(!1),r=p(null),n=p(null);async function f(...o){t.set(!0),r.set(null);try{let a=await e(...o);return n.set(a),a}catch(a){throw r.set(a),a}finally{t.set(!1)}}return{trigger:f,isPending:()=>t(),error:()=>r(),data:()=>n(),reset:()=>{r.set(null),n.set(null)}}}function j(e,t={}){let{resetOnSuccess:r=!0}=t,n={current:null},f=E(_(e,{resetOnSuccess:r}));function o(a){a.preventDefault();let s=new FormData(a.target);return n.current=a.target,f.trigger(s)}return{...f,handleSubmit:o,formRef:n}}function M(e,t){let r=p(e),n=p([]),f=p(e);function o(u){let c=t(r.peek(),u);v(()=>{n.set([...n.peek(),u]),r.set(c)})}function a(u,c){v(()=>{if(n.set(n.peek().filter(l=>l!==u)),c!==void 0){f.set(c);let l=c;for(let d of n.peek())l=t(l,d);r.set(l)}})}function s(u,c){v(()=>{let l=n.peek().filter(h=>h!==u);n.set(l);let d=c!==void 0?c:f.peek();f.set(d);let g=d;for(let h of l)g=t(g,h);r.set(g)})}async function i(u,c){o(u);try{let l=await c();return a(u,l),l}catch(l){throw s(u),l}}return{value:()=>r(),isPending:()=>n().length>0,addOptimistic:o,resolve:a,rollback:s,withOptimistic:i,set:u=>{r.set(u),f.set(u)}}}var y=new Map;function V(e,t){return y.has(e)||y.set(e,new Set),y.get(e).add(t),()=>{y.get(e)?.delete(t)}}function D(e){let t=y.get(e);if(t)for(let r of t)try{r()}catch(n){console.error("[what] Revalidation error:",n)}}function H(e,t,r,n={}){let{csrfToken:f,skipCsrf:o=!1}=n;if(!o){if(!f)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let s=e?.headers?.["x-csrf-token"]||e?.headers?.["X-CSRF-Token"];if(!R(s,f))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=A.get(t);return a?Array.isArray(r)?a.fn(...r).then(async s=>{let i=a.options||{};if(Array.isArray(i.revalidate))for(let u of i.revalidate)await b(u);if(Array.isArray(i.revalidateTags))for(let u of i.revalidateTags)await S(u);return{status:200,body:s}}).catch(s=>(console.error(`[what] Action "${t}" error:`,s),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function W(){return[...A.keys()]}function X(e,t={}){let{onSuccess:r,onError:n,onSettled:f}=t,o={isPending:p(!1),error:p(null),data:p(null)};async function a(...s){o.isPending.set(!0),o.error.set(null);try{let i=await e(...s);return o.data.set(i),r&&r(i,...s),i}catch(i){throw o.error.set(i),n&&n(i,...s),i}finally{o.isPending.set(!1),f&&f(o.data.peek(),o.error.peek(),...s)}}return{mutate:a,isPending:()=>o.isPending(),error:()=>o.error(),data:()=>o.data(),reset:()=>{o.error.set(null),o.data.set(null)}}}export{N as action,O as csrfMetaTag,_ as formAction,U as generateCsrfToken,W as getRegisteredActions,H as handleActionRequest,D as invalidatePath,V as onRevalidate,E as useAction,j as useFormAction,X as useMutation,M as useOptimistic,R as validateCsrfToken};
package/dist/index.min.js CHANGED
@@ -1,24 +1,24 @@
1
- import{h as St,runWithServerContext as H,beginHeadCollection as ie,endHeadCollection as tt}from"what-core";var kt=new RegExp("[<>&\\u2028\\u2029]","g"),Rt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function x(t){return JSON.stringify(t).replace(kt,e=>Rt[e.charCodeAt(0)])}import{mount as Se,signal as Ae,batch as ke}from"what-core";var _t=new Map;function it(){let t={};for(let[e,n]of _t)t[e]=n._getSnapshot();return t}import{signal as S,batch as z}from"what-core";var A=null,ct=!(typeof process<"u");function V(t){A=t}function Ct(){return A}async function J(t,e){if(A&&A.revalidatePath)return A.revalidatePath(t,e);ct&&console.warn(`[what] revalidatePath('${t}') had no effect: no cache engine is bound. Create a what-isr engine and bind it in your adapter (setRevalidationHandler).`)}async function W(t,e){if(A&&A.revalidateTag)return A.revalidateTag(t,e);ct&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var X=new Map;function Et(){if(typeof document<"u"){let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(e)return decodeURIComponent(e[1])}return null}function Y(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let t=new Uint8Array(16);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function ut(t,e){if(!t||!e||t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function G(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var vt=0;function xt(){return`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),e=>e.toString(16).padStart(2,"0")).join(""):`c${(++vt).toString(36)}_${Date.now().toString(36)}`}`}function Pt(t,e={}){let n=e.id||xt(),{onError:r,onSuccess:o,revalidate:a}=e;typeof window>"u"&&X.set(n,{fn:t,options:e});async function s(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let l=Et(),f={"Content-Type":"application/json","X-What-Action":n};l&&(f["X-CSRF-Token"]=l);let p=await fetch("/__what_action",{method:"POST",headers:f,credentials:"same-origin",signal:u.signal,body:JSON.stringify({args:i})});if(!p.ok){let m=await p.json().catch(()=>({message:"Action failed"}));throw new Error(m.message||"Action failed")}let h=await p.json();if(o&&o(h),a)for(let m of a)ft(m);return h}catch(l){if(l.name==="AbortError"){let f=new Error(`Action "${n}" timed out after ${c}ms`);throw f.code="TIMEOUT",r&&r(f),f}throw r&&r(l),l}finally{clearTimeout(d)}}return s._actionId=n,s._isAction=!0,s}function dt(t,e={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=e;return async a=>{let s,i;a instanceof Event?(a.preventDefault(),i=a.target,s=new FormData(i)):s=a;let c={},u=!1;for(let[d,l]of s.entries())typeof File<"u"&&l instanceof File&&(u=!0),c[d]?Array.isArray(c[d])?c[d].push(l):c[d]=[c[d],l]:c[d]=l;try{let d=u?await t(c,s):await t(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function lt(t){let e=S(!1),n=S(null),r=S(null);async function o(...a){e.set(!0),n.set(null);try{let s=await t(...a);return r.set(s),s}catch(s){throw n.set(s),s}finally{e.set(!1)}}return{trigger:o,isPending:()=>e(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function Ot(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=lt(dt(t,{resetOnSuccess:n}));function a(s){s.preventDefault();let i=new FormData(s.target);return r.current=s.target,o.trigger(i)}return{...o,handleSubmit:a,formRef:r}}function $t(t,e){let n=S(t),r=S([]),o=S(t);function a(u){let d=e(n.peek(),u);z(()=>{r.set([...r.peek(),u]),n.set(d)})}function s(u,d){z(()=>{if(r.set(r.peek().filter(l=>l!==u)),d!==void 0){o.set(d);let l=d;for(let f of r.peek())l=e(l,f);n.set(l)}})}function i(u,d){z(()=>{let l=r.peek().filter(h=>h!==u);r.set(l);let f=d!==void 0?d:o.peek();o.set(f);let p=f;for(let h of l)p=e(p,h);n.set(p)})}async function c(u,d){a(u);try{let l=await d();return s(u,l),l}catch(l){throw i(u),l}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:a,resolve:s,rollback:i,withOptimistic:c,set:u=>{n.set(u),o.set(u)}}}var P=new Map;function Ht(t,e){return P.has(t)||P.set(t,new Set),P.get(t).add(e),()=>{P.get(t)?.delete(e)}}function ft(t){let e=P.get(t);if(e)for(let n of e)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function L(t,e,n,r={}){let{csrfToken:o,skipCsrf:a=!1}=r;if(!a){if(!o)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let i=t?.headers?.["x-csrf-token"]||t?.headers?.["X-CSRF-Token"];if(!ut(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let s=X.get(e);return s?Array.isArray(n)?s.fn(...n).then(async i=>{let c=s.options||{};if(Array.isArray(c.revalidate))for(let u of c.revalidate)await J(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await W(u);return{status:200,body:i}}).catch(i=>(console.error(`[what] Action "${e}" error:`,i),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function It(){return[...X.keys()]}function jt(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,a={isPending:S(!1),error:S(null),data:S(null)};async function s(...i){a.isPending.set(!0),a.error.set(null);try{let c=await t(...i);return a.data.set(c),n&&n(c,...i),c}catch(c){throw a.error.set(c),r&&r(c,...i),c}finally{a.isPending.set(!1),o&&o(a.data.peek(),a.error.peek(),...i)}}return{mutate:s,isPending:()=>a.isPending(),error:()=>a.error(),data:()=>a.data(),reset:()=>{a.error.set(null),a.data.set(null)}}}var Lt="/__what_action",ht=1024*1024;function Nt(t){if(!t)return{};if(typeof t.forEach=="function"&&typeof t.get=="function"){let n={};return t.forEach((r,o)=>{n[o.toLowerCase()]=r}),n}let e={};for(let n in t)e[n.toLowerCase()]=t[n];return e}function N(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function Z(t,e){return{status:t,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${t}</h1><p>${String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</p></body></html>`}}function pt(t){if(typeof t!="string"||!t.startsWith("/")||/^[/\\]{2}/.test(t)||t.includes("\\"))return null;try{let e=new URL(t,"http://localhost");return e.origin!=="http://localhost"?null:e.pathname+e.search}catch{return null}}function Dt(t,e){let n=pt(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),a=pt(o.pathname+o.search);if(a)return a}catch{}return"/"}var Mt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function O(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return N(405,{message:"Method Not Allowed"});let s=Nt(o.headers),i=s["x-what-action"],c=s["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:e?await e(o):void 0;if(u){let h=o.body||{},m=h._action||h["data-action"]||o.query&&o.query.action;if(!m)return Z(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let w={...s},_=h._csrf??h["what-csrf-token"];if(_&&!w["x-csrf-token"]&&(w["x-csrf-token"]=String(_)),!n&&e&&!d)return Z(403,"Missing CSRF token");let C={};for(let[E,U]of Object.entries(h))Mt.has(E)||(C[E]=U);let R=await L({headers:w},m,[C],{csrfToken:d,skipCsrf:n});return R.status===200?{status:303,headers:{location:Dt(h,s)},body:""}:Z(R.status,R.body&&R.body.message||"Action failed")}if(!i)return N(400,{message:"Missing X-What-Action header"});if(!n&&e&&!d)return N(403,{message:"Missing CSRF token"});let f=(o.body||{}).args,p=await L({headers:s},i,f,{csrfToken:d,skipCsrf:n});return N(p.status,p.body)}}function Ft(t={}){let e=t.basePath||Lt,n=O(t);return async function(o,a,s){let[i,c]=(o.url||"").split("?");if(i!==e||(o.method||"").toUpperCase()!=="POST")return s?s():void 0;let u;try{let f=await Ut(o);u=D(f,o.headers["content-type"]||"")}catch(f){a.writeHead(f.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),a.end(JSON.stringify({message:f.code==="BODY_TOO_LARGE"?"Payload too large":"Invalid request body"}));return}let d=Object.fromEntries(new URLSearchParams(c||"")),l=await n({method:o.method,headers:o.headers,body:u,query:d});a.writeHead(l.status,l.headers),a.end(l.body)}}function D(t,e){if((e||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(t)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return t==null||t===""?{}:JSON.parse(String(t))}async function K(t,e=ht){let n=Number(t.headers.get("content-length"));if(Number.isFinite(n)&&n>e)return{tooLarge:!0};let r=t.body;if(!r||typeof r.getReader!="function"){let i=await t.text();return Buffer.byteLength(i,"utf8")>e?{tooLarge:!0}:{raw:i}}let o=r.getReader(),a=[],s=0;for(;;){let{done:i,value:c}=await o.read();if(i)break;if(c){if(s+=c.byteLength,s>e){try{await o.cancel()}catch{}return{tooLarge:!0}}a.push(c)}}return{raw:Buffer.concat(a.map(i=>Buffer.from(i))).toString("utf8")}}function Ut(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",a=>{if(r+=a.length,r>ht){let s=new Error("Body too large");s.code="BODY_TOO_LARGE",n(s),t.destroy?.();return}o.push(a)}),t.on("end",()=>{if(o.length===0)return e("");e(Buffer.concat(o).toString("utf8"))}),t.on("error",n)})}function Bt(t={}){let e=O(t);return async function(n){let r={};try{let s=await K(n);if(s.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=D(s.raw,n.headers.get("content-type")||"")}catch{r={}}let o={};try{o=Object.fromEntries(new URL(n.url,"http://localhost").searchParams)}catch{}let a=await e({method:n.method,headers:n.headers,body:r,query:o});return new Response(a.body,{status:a.status,headers:a.headers})}}import{matchRoute as Vt,parseQuery as Jt}from"what-router/match";var Wt="/__what_action",zt="/__what_revalidate",Q="what-csrf";function M(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function mt(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function Xt(t){try{let e=await K(t);return e.tooLarge?{tooLarge:!0}:D(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function Yt(t){try{return await t.json()}catch{return{}}}function Gt(t){return async function(n){let{route:r,params:o,query:a,request:s}=n,i={default:r.component,loader:r.loader},c=n.csrfToken?{...t,csrfToken:n.csrfToken}:t;return{html:await F(i,{params:o,query:a,request:s},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function k(t={}){let{routes:e=[],cache:n,render:r,revalidateWebhook:o,document:a={},notFound:s,basePath:i="",csrf:c=!0}=t,u=c!==!1&&!t.actionHandler,d=t.actionHandler||O(u?{getCsrfToken:f=>mt(f.headers&&f.headers.cookie,Q)}:{skipCsrf:!0}),l=r||Gt(a);return n&&(n.revalidatePath||n.revalidateTag)&&V({revalidatePath:n.revalidatePath,revalidateTag:n.revalidateTag}),async function(p){let h=new URL(p.url,"http://localhost"),m=h.pathname;if(i&&m.startsWith(i)&&(m=m.slice(i.length)||"/"),p.method==="POST"&&m===Wt){let y=await Xt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let T=await d({method:"POST",headers:M(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(T.body,{status:T.status,headers:T.headers})}let w=null,_=null;if(u&&(w=mt(M(p.headers).cookie,Q),!w)){w=Y();let T=M(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;_=`${Q}=${encodeURIComponent(w)}; Path=/; SameSite=Lax`+(T?"; Secure":"")}let C=y=>(_&&(y["set-cookie"]=_),y);if(p.method==="POST"&&m===zt&&o){let y=await Yt(p),T=await o({headers:M(p.headers),body:y});return new Response(JSON.stringify(T.body),{status:T.status,headers:{"content-type":"application/json"}})}let R=Vt(m,e);if(!R){let y=s?s():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(y,{status:404,headers:C({"content-type":"text/html; charset=utf-8"})})}let{route:E,params:U}=R,B=E.page||{mode:E.mode||"client"},j={path:m,query:Jt(h.search),config:B,route:E,params:U,request:p};if(n&&B.mode!=="server"){let y=await n.handle(j,()=>l(j));return new Response(y.html,{status:y.status||200,headers:C({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}w&&(j.csrfToken=w);let at=await l(j),st=C({"content-type":"text/html; charset=utf-8"});return B.mode==="server"&&(st["Cache-Control"]="private, no-store"),new Response(at.html,{status:at.status||200,headers:st})}}import Zt from"node:http";async function yt(t){let n=`http://${t.headers.host||"localhost"}${t.url}`,r=new Headers;for(let[a,s]of Object.entries(t.headers))s!=null&&r.set(a,Array.isArray(s)?s.join(", "):String(s));let o;if(t.method!=="GET"&&t.method!=="HEAD"){let a=[];for await(let s of t)a.push(s);a.length&&(o=Buffer.concat(a))}return new Request(n,{method:t.method,headers:r,body:o})}async function gt(t,e){t.statusCode=e.status,e.headers.forEach((r,o)=>t.setHeader(o,r));let n=await e.text();t.end(n)}function wt(t){return async function(n,r){try{let o=await yt(n),a=await t(o);await gt(r,a)}catch(o){r.headersSent||r.writeHead(500,{"content-type":"text/html; charset=utf-8"}),r.end("<!DOCTYPE html><html><body><h1>500 \u2014 Server Error</h1></body></html>"),console.error("[what-server] request error:",o)}}}function Kt(t={}){let e=k(t);return async function(r,o,a){let s=await yt(r),i=await e(s);if(i.status===404&&typeof a=="function")return a();await gt(o,i)}}function Qt(t={}){let e=k(t),n=Zt.createServer(wt(e)),{scheduler:r}=t;if(r){r.start();let o=()=>{try{r.stop()}catch{}n.close()};process.once("SIGTERM",o),process.once("SIGINT",o)}return n}import{mkdir as qt,writeFile as bt}from"node:fs/promises";import{join as q}from"node:path";import{matchRoute as te}from"what-router/match";function ee(t){return t.includes(":")||t.includes("*")||t.includes("[")}function ne(t,e){return t.replace(/\[\.\.\.(\w+)\]/g,(n,r)=>e[r]??"").replace(/\[(\w+)\]/g,(n,r)=>e[r]??"").replace(/[:*](\w+)/g,(n,r)=>e[r]??"")}async function re({routes:t=[],outDir:e,render:n,documentOptions:r={}}={}){let o=[];for(let a of t){let s=a.page&&a.page.mode||a.mode;if(s!=="static"&&s!=="hybrid")continue;let i={default:a.component,loader:a.loader},c=[a.path];if(ee(a.path)){if(typeof a.getStaticPaths!="function")continue;c=((await a.getStaticPaths()).paths||[]).map(d=>ne(a.path,d.params||{}))}for(let u of c){let d=te(u,[a]),f={params:d?d.params:{},query:{}},p=n?await n(i,f):await F(i,f,r),h=q(e,u==="/"?"":u);if(await qt(h,{recursive:!0}),await bt(q(h,"index.html"),p),typeof a.loader=="function"){let m=await a.loader(f);await bt(q(h,"__what_data.json"),x({loaderData:m}))}o.push(u)}}return{pages:o}}function oe(t={}){let e=k(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function ae(t={}){return k(t)}async function se({outDir:t=".vercel/output",functionName:e="render",runtime:n="nodejs22.x",files:r=null,handler:o="index.mjs",staticDir:a=null}={}){let{mkdir:s,writeFile:i,cp:c}=await import("node:fs/promises"),{join:u,dirname:d}=await import("node:path");await s(t,{recursive:!0});let l={version:3,routes:[{handle:"filesystem"},{src:"/.*",dest:`/${e}`}]};await i(u(t,"config.json"),JSON.stringify(l,null,2)),a&&await c(a,u(t,"static"),{recursive:!0});let f=null;if(r&&typeof r=="object"){f=u(t,"functions",`${e}.func`),await s(f,{recursive:!0});let p={runtime:n,handler:o,launcherType:"Nodejs",shouldAddHelpers:!1,supportsResponseStreaming:!0};await i(u(f,".vc-config.json"),JSON.stringify(p,null,2));for(let[h,m]of Object.entries(r)){let w=u(f,h);await s(d(w),{recursive:!0}),await i(w,m)}o in r||console.warn(`[what-server] buildVercelOutput: files does not include the handler entry "${o}" \u2014 the deploy will 500 until your build emits it.`)}return{config:l,outDir:t,functionDir:f}}function I(t){return{head:ie(),loaderData:t,resources:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var Tt=0;function ce(){Tt=0}function ue(){return"h"+Tt++}function qe(t){return ce(),v(t)}function v(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return g(String(t));if(typeof t=="function"&&t._signal)return`<!--$-->${v(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${v(t())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${t.map(v).join("")}<!--/[]-->`;if(typeof t.tag=="function"){let c=ue(),u=t.tag({...t.props,children:t.children}),d=v(u);return de(d,c)}let{tag:e,props:n,children:r}=t,o=rt(n||{}),a=`<${e}${o}>`;if(ot.has(e))return a;let s=nt(n),i=s!=null?String(s):r.map(v).join("");return`${a}${i}</${e}>`}function de(t,e){let n=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],a=r.length+1+o.length;return t.slice(0,a)+` data-hk="${e}"`+t.slice(a)}return t}function b(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return g(String(t));if(typeof t=="function"&&t._signal)return b(t());if(typeof t=="function")try{return b(t())}catch{return typeof process<"u",""}if(Array.isArray(t))return t.map(b).join("");if(t.tag==="__suspense")try{return(t.children||[]).map(b).join("")}catch(c){if(c&&typeof c.then=="function")return b(t.props&&t.props.fallback);throw c}if(typeof t.tag=="function"){let c=t.tag({...t.props,children:t.children});return b(c)}let{tag:e,props:n,children:r}=t,o=rt(n||{}),a=`<${e}${o}>`;if(ot.has(e))return a;let s=nt(n),i=s!=null?String(s):r.map(b).join("");return`${a}${i}</${e}>`}function tn(t){let e=I(void 0);return{body:H(e,()=>b(t)),head:tt(e.head)}}async function en(t,e={}){let n=t.default||t,r=typeof t.loader=="function"?await t.loader(e):void 0,o=I(r),a=e.params||{};return{body:H(o,()=>b(St(n,{...a,loaderData:r}))),head:tt(o.head),loaderData:r}}var At=12;async function le(t,e){e||(e=I(void 0));let n="";for(let o=0;o<At;o++){n=H(e,()=>b(t));let a=[...e.resources.values()].filter(s=>s.status==="pending").map(s=>s.promise);if(a.length===0)break;await Promise.all(a)}let r={};for(let[o,a]of e.resources)a.status==="ready"&&(r[o]=a.value);return{body:n,head:tt(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function F(t,e={},n={}){let r=t.default||t,o=typeof t.loader=="function"?await t.loader(e):void 0,a=I(o),s=e.params||{},{body:i,head:c,resources:u}=await le(St(r,{...s,loaderData:o}),a),d={loaderData:o??null,resources:u,islandStores:it()};return fe({body:i,head:c,payload:d,options:n})}function fe({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",a=`<script id="__what_data" type="application/json">${x(n)}<\/script>`,s=r.clientEntry?`<script type="module" src="${g(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?G(r.csrfToken):"")+(r.head||""),u=r.bodyClass?` class="${g(r.bodyClass)}"`:"";return`<!DOCTYPE html><html lang="${g(o)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${e||""}${c}</head><body${u}>${t}${a}${s}</body></html>`}async function*$(t,e){if(e===void 0&&(e=I(void 0)),t==null||t===!1||t===!0)return;if(typeof t=="string"||typeof t=="number"){yield g(String(t));return}if(typeof t=="function"&&t._signal){yield*$(t(),e);return}if(typeof t=="function"){try{yield*$(t(),e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let s of t)yield*$(s,e);return}if(t.tag==="__suspense"){let s=null;for(let i=0;i<At&&s===null;i++){let c=null;try{s=H(e,()=>(t.children||[]).map(b).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(s===null){let u=[...e.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}s===null&&(s=H(e,()=>b(t.props&&t.props.fallback))),yield s;return}if(typeof t.tag=="function"){try{let s=t.tag({...t.props,children:t.children}),i=s instanceof Promise?await s:s;yield*$(i,e)}catch(s){typeof process<"u",yield et?`<!-- SSR Error: ${g(s.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t,a=rt(r||{});if(yield`<${n}${a}>`,!ot.has(n)){let s=nt(r);if(s!=null)yield String(s);else for(let i of o)yield*$(i,e);yield`</${n}>`}}function nn(t){return{mode:"static",...t}}function rn(t,e={}){let n=t.component(e),r=b(n),o=t.islands||[];return pe({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function pe({title:t,meta:e,body:n,islands:r,scripts:o,styles:a,mode:s}){let i=Object.entries(e).map(([f,p])=>`<meta name="${g(f)}" content="${g(p)}">`).join(`
2
- `),c=a.map(f=>`<link rel="stylesheet" href="${g(f)}">`).join(`
1
+ import{h as yt,getServerContext as mt,runWithServerContext as S,beginHeadCollection as Zt,endHeadCollection as Q}from"what-core";var St=new RegExp("[<>&\\u2028\\u2029]","g"),bt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function F(t){return JSON.stringify(t).replace(St,e=>bt[e.charCodeAt(0)])}import{mount as de,signal as le,batch as pe,getServerContext as At}from"what-core";var Tt=new Map;function kt(t){let e=t||At();return e&&e.islandStores instanceof Map?e.islandStores:null}function st(t){let e=typeof document<"u"?Tt:kt(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as A,batch as J}from"what-core";var k=null,at=!(typeof process<"u");function U(t){k=t}function _t(){return k}async function B(t,e){if(k&&k.revalidatePath)return k.revalidatePath(t,e);at&&console.warn(`[what] revalidatePath('${t}') had no effect: no cache engine is bound. Create a what-isr engine and bind it in your adapter (setRevalidationHandler).`)}async function W(t,e){if(k&&k.revalidateTag)return k.revalidateTag(t,e);at&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var V=new Map;function Rt(){if(typeof document<"u"){let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(e)return decodeURIComponent(e[1])}return null}function z(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let t=new Uint8Array(16);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function it(t,e){if(!t||!e||t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function X(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Ct=0,Et=!1;function vt(){return!Et&&typeof process<"u",`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),e=>e.toString(16).padStart(2,"0")).join(""):`c${(++Ct).toString(36)}_${Date.now().toString(36)}`}`}function Pt(t,e={}){let n=e.id||vt(),{onError:r,onSuccess:o,revalidate:a}=e;typeof window>"u"&&V.set(n,{fn:t,options:e});async function s(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let f=Rt(),l={"Content-Type":"application/json","X-What-Action":n};f&&(l["X-CSRF-Token"]=f);let p=await fetch("/__what_action",{method:"POST",headers:l,credentials:"same-origin",signal:u.signal,body:JSON.stringify({args:i})});if(!p.ok){let g=await p.json().catch(()=>({message:"Action failed"}));throw new Error(g.message||"Action failed")}let h=await p.json();if(o&&o(h),a)for(let g of a)dt(g);return h}catch(f){if(f.name==="AbortError"){let l=new Error(`Action "${n}" timed out after ${c}ms`);throw l.code="TIMEOUT",r&&r(l),l}throw r&&r(f),f}finally{clearTimeout(d)}}return s._actionId=n,s._isAction=!0,s}function ct(t,e={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=e;return async a=>{let s,i;a instanceof Event?(a.preventDefault(),i=a.target,s=new FormData(i)):s=a;let c={},u=!1;for(let[d,f]of s.entries())typeof File<"u"&&f instanceof File&&(u=!0),c[d]?Array.isArray(c[d])?c[d].push(f):c[d]=[c[d],f]:c[d]=f;try{let d=u?await t(c,s):await t(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function ut(t){let e=A(!1),n=A(null),r=A(null);async function o(...a){e.set(!0),n.set(null);try{let s=await t(...a);return r.set(s),s}catch(s){throw n.set(s),s}finally{e.set(!1)}}return{trigger:o,isPending:()=>e(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function xt(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=ut(ct(t,{resetOnSuccess:n}));function a(s){s.preventDefault();let i=new FormData(s.target);return r.current=s.target,o.trigger(i)}return{...o,handleSubmit:a,formRef:r}}function Ot(t,e){let n=A(t),r=A([]),o=A(t);function a(u){let d=e(n.peek(),u);J(()=>{r.set([...r.peek(),u]),n.set(d)})}function s(u,d){J(()=>{if(r.set(r.peek().filter(f=>f!==u)),d!==void 0){o.set(d);let f=d;for(let l of r.peek())f=e(f,l);n.set(f)}})}function i(u,d){J(()=>{let f=r.peek().filter(h=>h!==u);r.set(f);let l=d!==void 0?d:o.peek();o.set(l);let p=l;for(let h of f)p=e(p,h);n.set(p)})}async function c(u,d){a(u);try{let f=await d();return s(u,f),f}catch(f){throw i(u),f}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:a,resolve:s,rollback:i,withOptimistic:c,set:u=>{n.set(u),o.set(u)}}}var x=new Map;function $t(t,e){return x.has(t)||x.set(t,new Set),x.get(t).add(e),()=>{x.get(t)?.delete(e)}}function dt(t){let e=x.get(t);if(e)for(let n of e)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function I(t,e,n,r={}){let{csrfToken:o,skipCsrf:a=!1}=r;if(!a){if(!o)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let i=t?.headers?.["x-csrf-token"]||t?.headers?.["X-CSRF-Token"];if(!it(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let s=V.get(e);return s?Array.isArray(n)?s.fn(...n).then(async i=>{let c=s.options||{};if(Array.isArray(c.revalidate))for(let u of c.revalidate)await B(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await W(u);return{status:200,body:i}}).catch(i=>(console.error(`[what] Action "${e}" error:`,i),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function Ht(){return[...V.keys()]}function It(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,a={isPending:A(!1),error:A(null),data:A(null)};async function s(...i){a.isPending.set(!0),a.error.set(null);try{let c=await t(...i);return a.data.set(c),n&&n(c,...i),c}catch(c){throw a.error.set(c),r&&r(c,...i),c}finally{a.isPending.set(!1),o&&o(a.data.peek(),a.error.peek(),...i)}}return{mutate:s,isPending:()=>a.isPending(),error:()=>a.error(),data:()=>a.data(),reset:()=>{a.error.set(null),a.data.set(null)}}}var Dt="/__what_action",lt=1024*1024;function Lt(t){if(!t)return{};if(typeof t.forEach=="function"&&typeof t.get=="function"){let n={};return t.forEach((r,o)=>{n[o.toLowerCase()]=r}),n}let e={};for(let n in t)e[n.toLowerCase()]=t[n];return e}function D(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function Y(t,e){return{status:t,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${t}</h1><p>${String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</p></body></html>`}}function ft(t){if(typeof t!="string"||!t.startsWith("/")||/^[/\\]{2}/.test(t)||t.includes("\\"))return null;try{let e=new URL(t,"http://localhost");return e.origin!=="http://localhost"?null:e.pathname+e.search}catch{return null}}function jt(t,e){let n=ft(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),a=ft(o.pathname+o.search);if(a)return a}catch{}return"/"}var Mt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function O(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return D(405,{message:"Method Not Allowed"});let s=Lt(o.headers),i=s["x-what-action"],c=s["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:e?await e(o):void 0;if(u){let h=o.body||{},g=h._action||h["data-action"]||o.query&&o.query.action;if(!g)return Y(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let b={...s},C=h._csrf??h["what-csrf-token"];if(C&&!b["x-csrf-token"]&&(b["x-csrf-token"]=String(C)),!n&&e&&!d)return Y(403,"Missing CSRF token");let E={};for(let[v,M]of Object.entries(h))Mt.has(v)||(E[v]=M);let R=await I({headers:b},g,[E],{csrfToken:d,skipCsrf:n});return R.status===200?{status:303,headers:{location:jt(h,s)},body:""}:Y(R.status,R.body&&R.body.message||"Action failed")}if(!i)return D(400,{message:"Missing X-What-Action header"});if(!n&&e&&!d)return D(403,{message:"Missing CSRF token"});let l=(o.body||{}).args,p=await I({headers:s},i,l,{csrfToken:d,skipCsrf:n});return D(p.status,p.body)}}function Nt(t={}){let e=t.basePath||Dt,n=O(t);return async function(o,a,s){let[i,c]=(o.url||"").split("?");if(i!==e||(o.method||"").toUpperCase()!=="POST")return s?s():void 0;let u;try{let l=await Ft(o);u=L(l,o.headers["content-type"]||"")}catch(l){a.writeHead(l.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),a.end(JSON.stringify({message:l.code==="BODY_TOO_LARGE"?"Payload too large":"Invalid request body"}));return}let d=Object.fromEntries(new URLSearchParams(c||"")),f=await n({method:o.method,headers:o.headers,body:u,query:d});a.writeHead(f.status,f.headers),a.end(f.body)}}function L(t,e){if((e||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(t)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return t==null||t===""?{}:JSON.parse(String(t))}async function K(t,e=lt){let n=Number(t.headers.get("content-length"));if(Number.isFinite(n)&&n>e)return{tooLarge:!0};let r=t.body;if(!r||typeof r.getReader!="function"){let i=await t.text();return Buffer.byteLength(i,"utf8")>e?{tooLarge:!0}:{raw:i}}let o=r.getReader(),a=[],s=0;for(;;){let{done:i,value:c}=await o.read();if(i)break;if(c){if(s+=c.byteLength,s>e){try{await o.cancel()}catch{}return{tooLarge:!0}}a.push(c)}}return{raw:Buffer.concat(a.map(i=>Buffer.from(i))).toString("utf8")}}function Ft(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",a=>{if(r+=a.length,r>lt){let s=new Error("Body too large");s.code="BODY_TOO_LARGE",n(s),t.destroy?.();return}o.push(a)}),t.on("end",()=>{if(o.length===0)return e("");e(Buffer.concat(o).toString("utf8"))}),t.on("error",n)})}function Ut(t={}){let e=O(t);return async function(n){let r={};try{let s=await K(n);if(s.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=L(s.raw,n.headers.get("content-type")||"")}catch{r={}}let o={};try{o=Object.fromEntries(new URL(n.url,"http://localhost").searchParams)}catch{}let a=await e({method:n.method,headers:n.headers,body:r,query:o});return new Response(a.body,{status:a.status,headers:a.headers})}}import{matchRoute as Bt,parseQuery as Wt}from"what-router/match";var Jt="/__what_action",Vt="/__what_revalidate",Z="what-csrf";function j(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function pt(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function zt(t){try{let e=await K(t);return e.tooLarge?{tooLarge:!0}:L(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function Xt(t){try{return await t.json()}catch{return{}}}function Yt(t){return async function(n){let{route:r,params:o,query:a,request:s}=n,i={default:r.component,loader:r.loader},c=n.csrfToken?{...t,csrfToken:n.csrfToken}:t;return{html:await ht(i,{params:o,query:a,request:s},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function G(t={}){let{routes:e=[],cache:n,render:r,revalidateWebhook:o,document:a={},notFound:s,basePath:i="",csrf:c=!0}=t,u=c!==!1&&!t.actionHandler,d=t.actionHandler||O(u?{getCsrfToken:l=>pt(l.headers&&l.headers.cookie,Z)}:{skipCsrf:!0}),f=r||Yt(a);return n&&(n.revalidatePath||n.revalidateTag)&&U({revalidatePath:n.revalidatePath,revalidateTag:n.revalidateTag}),async function(p){let h=new URL(p.url,"http://localhost"),g=h.pathname;if(i&&g.startsWith(i)&&(g=g.slice(i.length)||"/"),p.method==="POST"&&g===Jt){let y=await zt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let T=await d({method:"POST",headers:j(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(T.body,{status:T.status,headers:T.headers})}let b=null,C=null;if(u&&(b=pt(j(p.headers).cookie,Z),!b)){b=z();let T=j(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;C=`${Z}=${encodeURIComponent(b)}; Path=/; SameSite=Lax`+(T?"; Secure":"")}let E=y=>(C&&(y["set-cookie"]=C),y);if(p.method==="POST"&&g===Vt&&o){let y=await Xt(p),T=await o({headers:j(p.headers),body:y});return new Response(JSON.stringify(T.body),{status:T.status,headers:{"content-type":"application/json"}})}let R=Bt(g,e);if(!R){let y=s?s():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(y,{status:404,headers:E({"content-type":"text/html; charset=utf-8"})})}let{route:v,params:M}=R,N=v.page||{mode:v.mode||"client"},H={path:g,query:Wt(h.search),config:N,route:v,params:M,request:p};if(n&&N.mode!=="server"){let y=await n.handle(H,()=>f(H));return new Response(y.html,{status:y.status||200,headers:E({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}b&&(H.csrfToken=b);let rt=await f(H),ot=E({"content-type":"text/html; charset=utf-8"});return N.mode==="server"&&(ot["Cache-Control"]="private, no-store"),new Response(rt.html,{status:rt.status||200,headers:ot})}}function Kt(t={}){let e=G(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function _(t){return{head:Zt(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var gt=0;function Gt(){gt=0}function Qt(){return"h"+gt++}function qt(t){if(typeof document>"u"&&!mt()){let e=_(void 0);return S(e,()=>qt(t))}return Gt(),P(t)}function P(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return m(String(t));if(typeof t=="function"&&t._signal)return`<!--$-->${P(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${P(t())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${t.map(P).join("")}<!--/[]-->`;if(typeof t.tag=="function"){let c=Qt(),u=t.tag({...t.props,children:t.children}),d=P(u);return te(d,c)}let{tag:e,props:n,children:r}=t,o=et(n||{}),a=`<${e}${o}>`;if(nt.has(e))return a;let s=tt(n),i=s!=null?String(s):r.map(P).join("");return`${a}${i}</${e}>`}function te(t,e){let n=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],a=r.length+1+o.length;return t.slice(0,a)+` data-hk="${e}"`+t.slice(a)}return t}function w(t){if(typeof document>"u"&&!mt()){let c=_(void 0);return S(c,()=>w(t))}if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return m(String(t));if(typeof t=="function"&&t._signal)return w(t());if(typeof t=="function")try{return w(t())}catch{return typeof process<"u",""}if(Array.isArray(t))return t.map(w).join("");if(t.tag==="__suspense")try{return(t.children||[]).map(w).join("")}catch(c){if(c&&typeof c.then=="function")return w(t.props&&t.props.fallback);throw c}if(typeof t.tag=="function"){let c=t.tag({...t.props,children:t.children});return w(c)}let{tag:e,props:n,children:r}=t,o=et(n||{}),a=`<${e}${o}>`;if(nt.has(e))return a;let s=tt(n),i=s!=null?String(s):r.map(w).join("");return`${a}${i}</${e}>`}function He(t){let e=_(void 0);return{body:S(e,()=>w(t)),head:Q(e.head)}}async function Ie(t,e={}){let n=t.default||t,r=typeof t.loader=="function"?await t.loader(e):void 0,o=_(r),a=e.params||{};return{body:S(o,()=>w(yt(n,{...a,loaderData:r}))),head:Q(o.head),loaderData:r}}var wt=12;async function ee(t,e){e||(e=_(void 0));let n="";for(let o=0;o<wt;o++){n=S(e,()=>w(t));let a=[...e.resources.values()].filter(s=>s.status==="pending").map(s=>s.promise);if(a.length===0)break;await Promise.all(a)}let r={};for(let[o,a]of e.resources)a.status==="ready"&&(r[o]=a.value);return{body:n,head:Q(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function ht(t,e={},n={}){let r=t.default||t,o=typeof t.loader=="function"?await t.loader(e):void 0,a=_(o),s=e.params||{},{body:i,head:c,resources:u}=await ee(yt(r,{...s,loaderData:o}),a),d={loaderData:o??null,resources:u,islandStores:st(a)};return ne({body:i,head:c,payload:d,options:n})}function ne({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",a=`<script id="__what_data" type="application/json">${F(n)}<\/script>`,s=r.clientEntry?`<script type="module" src="${m(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?X(r.csrfToken):"")+(r.head||""),u=r.bodyClass?` class="${m(r.bodyClass)}"`:"";return`<!DOCTYPE html><html lang="${m(o)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${e||""}${c}</head><body${u}>${t}${a}${s}</body></html>`}async function*$(t,e){if(e===void 0&&(e=_(void 0)),t==null||t===!1||t===!0)return;if(typeof t=="string"||typeof t=="number"){yield m(String(t));return}if(typeof t=="function"&&t._signal){let s=S(e,()=>t());yield*$(s,e);return}if(typeof t=="function"){try{let s=S(e,()=>t());yield*$(s,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let s of t)yield*$(s,e);return}if(t.tag==="__suspense"){let s=null;for(let i=0;i<wt&&s===null;i++){let c=null;try{s=S(e,()=>(t.children||[]).map(w).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(s===null){let u=[...e.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}s===null&&(s=S(e,()=>w(t.props&&t.props.fallback))),yield s;return}if(typeof t.tag=="function"){try{let s=S(e,()=>t.tag({...t.props,children:t.children})),i=s instanceof Promise?await s:s;yield*$(i,e)}catch(s){typeof process<"u",yield q?`<!-- SSR Error: ${m(s.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t,a=et(r||{});if(yield`<${n}${a}>`,!nt.has(n)){let s=tt(r);if(s!=null)yield String(s);else for(let i of o)yield*$(i,e);yield`</${n}>`}}function De(t){return{mode:"static",...t}}function Le(t,e={}){let n=_(e),r=S(n,()=>{let a=t.component(e);return w(a)}),o=t.islands||[];return re({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function re({title:t,meta:e,body:n,islands:r,scripts:o,styles:a,mode:s}){let i=Object.entries(e).map(([l,p])=>`<meta name="${m(l)}" content="${m(p)}">`).join(`
2
+ `),c=a.map(l=>`<link rel="stylesheet" href="${m(l)}">`).join(`
3
3
  `),u=r.length>0?`
4
4
  <script type="module">
5
5
  import { hydrateIslands } from '/@what/islands.js';
6
6
  hydrateIslands();
7
- <\/script>`:"",d=o.map(f=>`<script type="module" src="${g(f)}"><\/script>`).join(`
8
- `),l=s==="client"?`
7
+ <\/script>`:"",d=o.map(l=>`<script type="module" src="${m(l)}"><\/script>`).join(`
8
+ `),f=s==="client"?`
9
9
  <script type="module" src="/@what/client.js"><\/script>`:"";return`<!DOCTYPE html>
10
10
  <html lang="en">
11
11
  <head>
12
12
  <meta charset="UTF-8">
13
13
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
14
  ${i}
15
- <title>${g(t)}</title>
15
+ <title>${m(t)}</title>
16
16
  ${c}
17
17
  </head>
18
18
  <body>
19
19
  <div id="app">${n}</div>
20
20
  ${u}
21
21
  ${d}
22
- ${l}
22
+ ${f}
23
23
  </body>
24
- </html>`}function on(t){return t._server=!0,t}var et=!(typeof process<"u");function nt(t){return t?t.dangerouslySetInnerHTML?t.dangerouslySetInnerHTML.__html??null:t.innerHTML&&typeof t.innerHTML=="object"&&"__html"in t.innerHTML?t.innerHTML.__html??null:(t.innerHTML!=null&&typeof t.innerHTML=="string"&&et&&console.warn("[what-server] innerHTML received a raw string. This is a security risk (XSS). Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead."),null):null}var he=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/;function rt(t){let e="";for(let[n,r]of Object.entries(t))if(!(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")&&!(n.startsWith("on")&&n.length>2)&&!(r===!1||r==null)){if(!he.test(n)){et&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")e+=` class="${g(String(r))}"`;else if(n==="style"&&typeof r=="object"){let o=Object.entries(r).map(([a,s])=>`${ge(a)}:${s}`).join(";");e+=` style="${g(o)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?e+=` ${n}="true"`:e+=` ${n}`;else{if(me(n,r))continue;e+=` ${n}="${g(String(r))}"`}}return e}function me(t,e){let n=t.toLowerCase();if(!ye.has(n))return!1;let r=String(e).trim().replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();return r.startsWith("javascript:")||r.startsWith("vbscript:")||r.startsWith("data:")}var ye=new Set(["href","src","action","formaction","xlink:href"]);function g(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function ge(t){return t.startsWith("--")?t:t.replace(/([A-Z])/g,"-$1").toLowerCase()}var ot=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);export{Pt as action,se as buildVercelOutput,O as createActionHandler,oe as createCloudflareHandler,k as createRequestHandler,Qt as createServer,ae as createVercelHandler,G as csrfMetaTag,nn as definePage,re as exportStatic,Bt as fetchActionHandler,dt as formAction,Y as generateCsrfToken,rn as generateStaticPage,It as getRegisteredActions,Ct as getRevalidationHandler,L as handleActionRequest,ft as invalidatePath,Ft as nodeActionMiddleware,Ht as onRevalidate,F as renderDocument,en as renderPage,qe as renderToHydratableString,$ as renderToStream,b as renderToString,le as renderToStringAsync,tn as renderToStringWithHead,J as revalidatePath,W as revalidateTag,x as serializeState,on as server,V as setRevalidationHandler,wt as toNodeListener,lt as useAction,Ot as useFormAction,jt as useMutation,$t as useOptimistic,ut as validateCsrfToken,Kt as whatMiddleware};
24
+ </html>`}function je(t){return t._server=!0,t}var q=!(typeof process<"u");function tt(t){return t?t.dangerouslySetInnerHTML?t.dangerouslySetInnerHTML.__html??null:t.innerHTML&&typeof t.innerHTML=="object"&&"__html"in t.innerHTML?t.innerHTML.__html??null:(t.innerHTML!=null&&typeof t.innerHTML=="string"&&q&&console.warn("[what-server] innerHTML received a raw string. This is a security risk (XSS). Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead."),null):null}var oe=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/;function et(t){let e="";for(let[n,r]of Object.entries(t))if(!(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")&&!(n.startsWith("on")&&n.length>2)&&!(r===!1||r==null)){if(!oe.test(n)){q&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")e+=` class="${m(String(r))}"`;else if(n==="style"&&typeof r=="object"){let o=Object.entries(r).map(([a,s])=>`${ie(a)}:${s}`).join(";");e+=` style="${m(o)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?e+=` ${n}="true"`:e+=` ${n}`;else{if(se(n,r))continue;e+=` ${n}="${m(String(r))}"`}}return e}function se(t,e){let n=t.toLowerCase();if(!ae.has(n))return!1;let r=String(e).trim().replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();return r.startsWith("javascript:")||r.startsWith("vbscript:")||r.startsWith("data:")}var ae=new Set(["href","src","action","formaction","xlink:href"]);function m(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function ie(t){return t.startsWith("--")?t:t.replace(/([A-Z])/g,"-$1").toLowerCase()}var nt=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);export{Pt as action,O as createActionHandler,Kt as createCloudflareHandler,G as createRequestHandler,X as csrfMetaTag,De as definePage,Ut as fetchActionHandler,ct as formAction,z as generateCsrfToken,Le as generateStaticPage,Ht as getRegisteredActions,_t as getRevalidationHandler,I as handleActionRequest,dt as invalidatePath,Nt as nodeActionMiddleware,$t as onRevalidate,ht as renderDocument,Ie as renderPage,qt as renderToHydratableString,$ as renderToStream,w as renderToString,ee as renderToStringAsync,He as renderToStringWithHead,B as revalidatePath,W as revalidateTag,F as serializeState,je as server,U as setRevalidationHandler,ut as useAction,xt as useFormAction,It as useMutation,Ot as useOptimistic,it as validateCsrfToken};
@@ -1 +1 @@
1
- import{mount as C,signal as O,batch as S}from"what-core";var E=new RegExp("[<>&\\u2028\\u2029]","g"),I={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function v(e){return JSON.stringify(e).replace(E,o=>I[o.charCodeAt(0)])}var y=new Map,m=new Set,l=[],b=!1,p=new Map;function q(e,o){if(p.has(e))return p.get(e);let t={},s={};for(let[n,r]of Object.entries(o))s[n]=O(r),Object.defineProperty(t,n,{get:()=>s[n](),set:a=>s[n].set(a),enumerable:!0});return t._signals=s,t._subscribe=(n,r)=>{if(s[n])return s[n].subscribe(r)},t._batch=n=>S(n),t._getSnapshot=()=>{let n={};for(let[r,a]of Object.entries(s))n[r]=a.peek();return n},t._hydrate=n=>{S(()=>{for(let[r,a]of Object.entries(n))s[r]&&s[r].set(a)})},p.set(e,t),t}function w(e,o={}){return p.has(e)?p.get(e):q(e,o)}function J(){return v(A())}function A(){let e={};for(let[o,t]of p)e[o]=t._getSnapshot();return e}function M(e){try{let o=typeof e=="string"?JSON.parse(e):e;for(let[t,s]of Object.entries(o))w(t,s)._hydrate(s)}catch(o){console.warn("[what] Failed to hydrate island stores:",o)}}function P(e,o,t={}){y.set(e,{loader:o,mode:t.mode||"idle",media:t.media||null,priority:t.priority||0,stores:t.stores||[]})}function T({name:e,props:o={},children:t,mode:s,priority:n,stores:r}){let a=y.get(e),i=s||a?.mode||"idle",d=n??a?.priority??0,c=r||a?.stores||[];return{tag:"div",props:{"data-island":e,"data-island-mode":i,"data-island-props":JSON.stringify(o),"data-island-priority":d,"data-island-stores":JSON.stringify(c)},children:t||[],key:null,_vnode:!0}}function f(e){let o=!1;for(let t=0;t<l.length;t++)if(e.priority>l[t].priority){l.splice(t,0,e),o=!0;break}o||l.push(e),x()}function x(){if(b||l.length===0)return;b=!0;let e=l.shift();Promise.resolve(e.hydrate()).catch(o=>console.error("[what] Island hydration failed:",e.name,o)).finally(()=>{b=!1,queueMicrotask(x)})}function D(e,o=100){for(let t of l)if(t.name===e){t.priority=o,l.sort((s,n)=>n.priority-s.priority);break}}function k(){let e=document.querySelector("script[data-island-stores]");e&&M(e.textContent);let o=document.querySelectorAll("[data-island]");for(let t of o){let s=t.dataset.island,n=t.dataset.islandMode||"idle",r=JSON.parse(t.dataset.islandProps||"{}"),a=parseInt(t.dataset.islandPriority||"0",10),i=JSON.parse(t.dataset.islandStores||"[]"),d=y.get(s);if(!d){console.warn(`[what] Island "${s}" not registered`);continue}m.has(t)||N(t,d,r,n,a,s,i)}}function N(e,o,t,s,n,r,a){let i=async()=>{if(m.has(e))return;m.add(e);let d=await o.loader(),c=d.default||d,u={};for(let g of a)u[g]=w(g);let h=c({...t,...u});e.childNodes.length>0?i(h,e):C(h,e),e.removeAttribute("data-island"),e.removeAttribute("data-island-mode"),e.removeAttribute("data-island-props"),e.removeAttribute("data-island-priority"),e.removeAttribute("data-island-stores"),e.dispatchEvent(new CustomEvent("island:hydrated",{bubbles:!0,detail:{name:r,mode:s}}))};switch(s){case"load":f({name:r,priority:n+1e3,hydrate:i});break;case"idle":"requestIdleCallback"in window?requestIdleCallback(()=>{f({name:r,priority:n,hydrate:i})}):setTimeout(()=>{f({name:r,priority:n,hydrate:i})},200);break;case"visible":{new IntersectionObserver((c,u)=>{for(let h of c)if(h.isIntersecting){u.disconnect(),f({name:r,priority:n,hydrate:i});break}},{rootMargin:"200px"}).observe(e);break}case"media":{let d=window.matchMedia(o.media||"(max-width: 768px)");d.matches?f({name:r,priority:n,hydrate:i}):d.addEventListener("change",c=>{c.matches&&f({name:r,priority:n,hydrate:i})},{once:!0});break}case"action":{let d=["click","focus","mouseover","touchstart"],c=()=>{d.forEach(u=>e.removeEventListener(u,c)),f({name:r,priority:n+500,hydrate:i})};d.forEach(u=>e.addEventListener(u,c,{once:!0,passive:!0}));break}case"static":break;default:f({name:r,priority:n,hydrate:i})}}function H(e){for(let[o,t]of Object.entries(e))P(o,t.loader||t,{mode:t.mode||"idle",media:t.media,priority:t.priority||0,stores:t.stores||[]});typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",k):k())}function _(e,o){if(typeof document>"u")return;let t=document.querySelectorAll(e);for(let s of t)s.dataset.enhanced||(s.dataset.enhanced="true",o(s))}function Q(e="form[data-enhance]"){_(e,o=>{o.addEventListener("submit",async t=>{t.preventDefault();let s=new FormData(o),n=o.method.toUpperCase()||"POST",r=o.action||location.href;try{let a=document.querySelector('meta[name="csrf-token"]')||document.querySelector('meta[name="what-csrf-token"]'),i=a?a.getAttribute("content"):null,d=o.getAttribute("data-no-csrf")==="true";if(!i&&!d){console.warn('[what] Form submission blocked: no CSRF token found. Add a <meta name="csrf-token"> tag or set data-no-csrf="true" on the form to opt out.'),o.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:new Error("Missing CSRF token")}}));return}let c={"X-Requested-With":"XMLHttpRequest"};i&&(c["X-CSRF-Token"]=i);let u=await fetch(r,{method:n,body:n==="GET"?void 0:s,headers:c});o.dispatchEvent(new CustomEvent("form:response",{bubbles:!0,detail:{response:u,ok:u.ok}}))}catch(a){o.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:a}}))}})})}function X(){return{registered:[...y.keys()],hydrated:m.size,pending:l.length,queue:l.map(o=>({name:o.name,priority:o.priority})),stores:[...p.keys()]}}export{T as Island,H as autoIslands,D as boostIslandPriority,q as createIslandStore,_ as enhance,Q as enhanceForms,X as getIslandStatus,A as getIslandStoresSnapshot,M as hydrateIslandStores,k as hydrateIslands,P as island,J as serializeIslandStores,w as useIslandStore};
1
+ import{mount as R,signal as q,batch as E,getServerContext as _}from"what-core";var A=new RegExp("[<>&\\u2028\\u2029]","g"),P={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function x(e){return JSON.stringify(e).replace(A,r=>P[r.charCodeAt(0)])}var S=new Map,b=new Set,f=[],w=!1,v=new Map,p=new Map;function g(e){if(typeof structuredClone=="function")try{return structuredClone(e)}catch{}return Array.isArray(e)?e.map(g):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([r,t])=>[r,g(t)])):e}function h(e,r,t){if(e.has(r))return e.get(r);let n={},s={};for(let[o,a]of Object.entries(g(t)))s[o]=q(a),Object.defineProperty(n,o,{get:()=>s[o](),set:i=>s[o].set(i),enumerable:!0});return n._signals=s,n._subscribe=(o,a)=>{if(s[o])return s[o].subscribe(a)},n._batch=o=>E(o),n._getSnapshot=()=>{let o={};for(let[a,i]of Object.entries(s))o[a]=i.peek();return o},n._hydrate=o=>{E(()=>{for(let[a,i]of Object.entries(o))s[a]&&s[a].set(i)})},e.set(r,n),n}function y(e){let r=e||_();return r&&r.islandStores instanceof Map?r.islandStores:null}function I(e,r){if(p.has(e))return p.get(e).handle;let t={initialState:g(r),handle:null},n=()=>{let s=y();if(!s)throw new Error(`[what-server] Island store "${e}" was accessed outside an active server render. Read or write module-scoped island stores from a component rendered by renderDocument/renderPage.`);return h(s,e,t.initialState)};return t.handle=new Proxy({},{get(s,o){return Reflect.get(n(),o)},set(s,o,a){return Reflect.set(n(),o,a)},has(s,o){return Reflect.has(n(),o)},ownKeys(){return Reflect.ownKeys(n())},getOwnPropertyDescriptor(s,o){let a=Reflect.getOwnPropertyDescriptor(n(),o);return a?{...a,configurable:!0}:void 0}}),p.set(e,t),t.handle}function X(e,r){if(typeof document<"u")return h(v,e,r);let t=y();if(t){let n=p.get(e);return h(t,e,n?.initialState??r)}return I(e,r)}function O(e,r={}){if(typeof document<"u")return h(v,e,r);let t=y();if(t){let n=p.get(e);return h(t,e,n?.initialState??r)}return I(e,r)}function z(){return x(j())}function j(e){let r=typeof document<"u"?v:y(e),t={};for(let[n,s]of r||[])t[n]=s._getSnapshot();return t}function D(e){try{let r=typeof e=="string"?JSON.parse(e):e;for(let[t,n]of Object.entries(r))O(t,n)._hydrate(n)}catch(r){console.warn("[what] Failed to hydrate island stores:",r)}}function N(e,r,t={}){S.set(e,{loader:r,mode:t.mode||"idle",media:t.media||null,priority:t.priority||0,stores:t.stores||[]})}function K({name:e,props:r={},children:t,mode:n,priority:s,stores:o}){let a=S.get(e),i=n||a?.mode||"idle",c=s??a?.priority??0,d=o||a?.stores||[];return{tag:"div",props:{"data-island":e,"data-island-mode":i,"data-island-props":JSON.stringify(r),"data-island-priority":c,"data-island-stores":JSON.stringify(d)},children:t||[],key:null,_vnode:!0}}function l(e){let r=!1;for(let t=0;t<f.length;t++)if(e.priority>f[t].priority){f.splice(t,0,e),r=!0;break}r||f.push(e),M()}function M(){if(w||f.length===0)return;w=!0;let e=f.shift();Promise.resolve(e.hydrate()).catch(r=>console.error("[what] Island hydration failed:",e.name,r)).finally(()=>{w=!1,queueMicrotask(M)})}function U(e,r=100){for(let t of f)if(t.name===e){t.priority=r,f.sort((n,s)=>s.priority-n.priority);break}}function C(){let e=document.querySelector("script[data-island-stores]");e&&D(e.textContent);let r=document.querySelectorAll("[data-island]");for(let t of r){let n=t.dataset.island,s=t.dataset.islandMode||"idle",o=JSON.parse(t.dataset.islandProps||"{}"),a=parseInt(t.dataset.islandPriority||"0",10),i=JSON.parse(t.dataset.islandStores||"[]"),c=S.get(n);if(!c){console.warn(`[what] Island "${n}" not registered`);continue}b.has(t)||F(t,c,o,s,a,n,i)}}function F(e,r,t,n,s,o,a){let i=async()=>{if(b.has(e))return;b.add(e);let c=await r.loader(),d=c.default||c,u={};for(let k of a)u[k]=O(k);let m=d({...t,...u});e.childNodes.length>0?i(m,e):R(m,e),e.removeAttribute("data-island"),e.removeAttribute("data-island-mode"),e.removeAttribute("data-island-props"),e.removeAttribute("data-island-priority"),e.removeAttribute("data-island-stores"),e.dispatchEvent(new CustomEvent("island:hydrated",{bubbles:!0,detail:{name:o,mode:n}}))};switch(n){case"load":l({name:o,priority:s+1e3,hydrate:i});break;case"idle":"requestIdleCallback"in window?requestIdleCallback(()=>{l({name:o,priority:s,hydrate:i})}):setTimeout(()=>{l({name:o,priority:s,hydrate:i})},200);break;case"visible":{new IntersectionObserver((d,u)=>{for(let m of d)if(m.isIntersecting){u.disconnect(),l({name:o,priority:s,hydrate:i});break}},{rootMargin:"200px"}).observe(e);break}case"media":{let c=window.matchMedia(r.media||"(max-width: 768px)");c.matches?l({name:o,priority:s,hydrate:i}):c.addEventListener("change",d=>{d.matches&&l({name:o,priority:s,hydrate:i})},{once:!0});break}case"action":{let c=["click","focus","mouseover","touchstart"],d=()=>{c.forEach(u=>e.removeEventListener(u,d)),l({name:o,priority:s+500,hydrate:i})};c.forEach(u=>e.addEventListener(u,d,{once:!0,passive:!0}));break}case"static":break;default:l({name:o,priority:s,hydrate:i})}}function $(e){for(let[r,t]of Object.entries(e))N(r,t.loader||t,{mode:t.mode||"idle",media:t.media,priority:t.priority||0,stores:t.stores||[]});typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",C):C())}function L(e,r){if(typeof document>"u")return;let t=document.querySelectorAll(e);for(let n of t)n.dataset.enhanced||(n.dataset.enhanced="true",r(n))}function G(e="form[data-enhance]"){L(e,r=>{r.addEventListener("submit",async t=>{t.preventDefault();let n=new FormData(r),s=r.method.toUpperCase()||"POST",o=r.action||location.href;try{let a=document.querySelector('meta[name="csrf-token"]')||document.querySelector('meta[name="what-csrf-token"]'),i=a?a.getAttribute("content"):null,c=r.getAttribute("data-no-csrf")==="true";if(!i&&!c){console.warn('[what] Form submission blocked: no CSRF token found. Add a <meta name="csrf-token"> tag or set data-no-csrf="true" on the form to opt out.'),r.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:new Error("Missing CSRF token")}}));return}let d={"X-Requested-With":"XMLHttpRequest"};i&&(d["X-CSRF-Token"]=i);let u=await fetch(o,{method:s,body:s==="GET"?void 0:n,headers:d});r.dispatchEvent(new CustomEvent("form:response",{bubbles:!0,detail:{response:u,ok:u.ok}}))}catch(a){r.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:a}}))}})})}function W(){let e=typeof document<"u"?[...v.keys()]:[...y()?.keys()||p.keys()];return{registered:[...S.keys()],hydrated:b.size,pending:f.length,queue:f.map(t=>({name:t.name,priority:t.priority})),stores:e}}export{K as Island,$ as autoIslands,U as boostIslandPriority,X as createIslandStore,L as enhance,G as enhanceForms,W as getIslandStatus,j as getIslandStoresSnapshot,D as hydrateIslandStores,C as hydrateIslands,N as island,z as serializeIslandStores,O as useIslandStore};
@@ -0,0 +1,24 @@
1
+ import{AsyncLocalStorage as _e}from"node:async_hooks";import{__installServerContextStorage as ke}from"what-core";import{h as yt,getServerContext as gt,runWithServerContext as b,beginHeadCollection as ee,endHeadCollection as q}from"what-core";var kt=new RegExp("[<>&\\u2028\\u2029]","g"),Rt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function P(t){return JSON.stringify(t).replace(kt,e=>Rt[e.charCodeAt(0)])}import{mount as Ce,signal as Ee,batch as Oe,getServerContext as vt}from"what-core";var Ct=new Map;function xt(t){let e=t||vt();return e&&e.islandStores instanceof Map?e.islandStores:null}function it(t){let e=typeof document<"u"?Ct:xt(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as A,batch as z}from"what-core";var _=null,ct=!(typeof process<"u");function V(t){_=t}function Et(){return _}async function W(t,e){if(_&&_.revalidatePath)return _.revalidatePath(t,e);ct&&console.warn(`[what] revalidatePath('${t}') had no effect: no cache engine is bound. Create a what-isr engine and bind it in your adapter (setRevalidationHandler).`)}async function J(t,e){if(_&&_.revalidateTag)return _.revalidateTag(t,e);ct&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var X=new Map;function Ot(){if(typeof document<"u"){let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(e)return decodeURIComponent(e[1])}return null}function Y(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let t=new Uint8Array(16);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function ut(t,e){if(!t||!e||t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}function G(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Pt=0,$t=!1;function Ht(){return!$t&&typeof process<"u",`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),e=>e.toString(16).padStart(2,"0")).join(""):`c${(++Pt).toString(36)}_${Date.now().toString(36)}`}`}function jt(t,e={}){let n=e.id||Ht(),{onError:r,onSuccess:o,revalidate:s}=e;typeof window>"u"&&X.set(n,{fn:t,options:e});async function a(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let l=Ot(),f={"Content-Type":"application/json","X-What-Action":n};l&&(f["X-CSRF-Token"]=l);let p=await fetch("/__what_action",{method:"POST",headers:f,credentials:"same-origin",signal:u.signal,body:JSON.stringify({args:i})});if(!p.ok){let m=await p.json().catch(()=>({message:"Action failed"}));throw new Error(m.message||"Action failed")}let h=await p.json();if(o&&o(h),s)for(let m of s)ft(m);return h}catch(l){if(l.name==="AbortError"){let f=new Error(`Action "${n}" timed out after ${c}ms`);throw f.code="TIMEOUT",r&&r(f),f}throw r&&r(l),l}finally{clearTimeout(d)}}return a._actionId=n,a._isAction=!0,a}function dt(t,e={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=e;return async s=>{let a,i;s instanceof Event?(s.preventDefault(),i=s.target,a=new FormData(i)):a=s;let c={},u=!1;for(let[d,l]of a.entries())typeof File<"u"&&l instanceof File&&(u=!0),c[d]?Array.isArray(c[d])?c[d].push(l):c[d]=[c[d],l]:c[d]=l;try{let d=u?await t(c,a):await t(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function lt(t){let e=A(!1),n=A(null),r=A(null);async function o(...s){e.set(!0),n.set(null);try{let a=await t(...s);return r.set(a),a}catch(a){throw n.set(a),a}finally{e.set(!1)}}return{trigger:o,isPending:()=>e(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function It(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=lt(dt(t,{resetOnSuccess:n}));function s(a){a.preventDefault();let i=new FormData(a.target);return r.current=a.target,o.trigger(i)}return{...o,handleSubmit:s,formRef:r}}function Dt(t,e){let n=A(t),r=A([]),o=A(t);function s(u){let d=e(n.peek(),u);z(()=>{r.set([...r.peek(),u]),n.set(d)})}function a(u,d){z(()=>{if(r.set(r.peek().filter(l=>l!==u)),d!==void 0){o.set(d);let l=d;for(let f of r.peek())l=e(l,f);n.set(l)}})}function i(u,d){z(()=>{let l=r.peek().filter(h=>h!==u);r.set(l);let f=d!==void 0?d:o.peek();o.set(f);let p=f;for(let h of l)p=e(p,h);n.set(p)})}async function c(u,d){s(u);try{let l=await d();return a(u,l),l}catch(l){throw i(u),l}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:s,resolve:a,rollback:i,withOptimistic:c,set:u=>{n.set(u),o.set(u)}}}var $=new Map;function Lt(t,e){return $.has(t)||$.set(t,new Set),$.get(t).add(e),()=>{$.get(t)?.delete(e)}}function ft(t){let e=$.get(t);if(e)for(let n of e)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function D(t,e,n,r={}){let{csrfToken:o,skipCsrf:s=!1}=r;if(!s){if(!o)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let i=t?.headers?.["x-csrf-token"]||t?.headers?.["X-CSRF-Token"];if(!ut(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=X.get(e);return a?Array.isArray(n)?a.fn(...n).then(async i=>{let c=a.options||{};if(Array.isArray(c.revalidate))for(let u of c.revalidate)await W(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await J(u);return{status:200,body:i}}).catch(i=>(console.error(`[what] Action "${e}" error:`,i),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function Mt(){return[...X.keys()]}function Nt(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,s={isPending:A(!1),error:A(null),data:A(null)};async function a(...i){s.isPending.set(!0),s.error.set(null);try{let c=await t(...i);return s.data.set(c),n&&n(c,...i),c}catch(c){throw s.error.set(c),r&&r(c,...i),c}finally{s.isPending.set(!1),o&&o(s.data.peek(),s.error.peek(),...i)}}return{mutate:a,isPending:()=>s.isPending(),error:()=>s.error(),data:()=>s.data(),reset:()=>{s.error.set(null),s.data.set(null)}}}var Ft="/__what_action",ht=1024*1024;function Ut(t){if(!t)return{};if(typeof t.forEach=="function"&&typeof t.get=="function"){let n={};return t.forEach((r,o)=>{n[o.toLowerCase()]=r}),n}let e={};for(let n in t)e[n.toLowerCase()]=t[n];return e}function L(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function K(t,e){return{status:t,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${t}</h1><p>${String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</p></body></html>`}}function pt(t){if(typeof t!="string"||!t.startsWith("/")||/^[/\\]{2}/.test(t)||t.includes("\\"))return null;try{let e=new URL(t,"http://localhost");return e.origin!=="http://localhost"?null:e.pathname+e.search}catch{return null}}function Bt(t,e){let n=pt(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),s=pt(o.pathname+o.search);if(s)return s}catch{}return"/"}var Vt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function H(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return L(405,{message:"Method Not Allowed"});let a=Ut(o.headers),i=a["x-what-action"],c=a["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:e?await e(o):void 0;if(u){let h=o.body||{},m=h._action||h["data-action"]||o.query&&o.query.action;if(!m)return K(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let S={...a},C=h._csrf??h["what-csrf-token"];if(C&&!S["x-csrf-token"]&&(S["x-csrf-token"]=String(C)),!n&&e&&!d)return K(403,"Missing CSRF token");let x={};for(let[E,U]of Object.entries(h))Vt.has(E)||(x[E]=U);let v=await D({headers:S},m,[x],{csrfToken:d,skipCsrf:n});return v.status===200?{status:303,headers:{location:Bt(h,a)},body:""}:K(v.status,v.body&&v.body.message||"Action failed")}if(!i)return L(400,{message:"Missing X-What-Action header"});if(!n&&e&&!d)return L(403,{message:"Missing CSRF token"});let f=(o.body||{}).args,p=await D({headers:a},i,f,{csrfToken:d,skipCsrf:n});return L(p.status,p.body)}}function Wt(t={}){let e=t.basePath||Ft,n=H(t);return async function(o,s,a){let[i,c]=(o.url||"").split("?");if(i!==e||(o.method||"").toUpperCase()!=="POST")return a?a():void 0;let u;try{let f=await Jt(o);u=M(f,o.headers["content-type"]||"")}catch(f){s.writeHead(f.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),s.end(JSON.stringify({message:f.code==="BODY_TOO_LARGE"?"Payload too large":"Invalid request body"}));return}let d=Object.fromEntries(new URLSearchParams(c||"")),l=await n({method:o.method,headers:o.headers,body:u,query:d});s.writeHead(l.status,l.headers),s.end(l.body)}}function M(t,e){if((e||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(t)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return t==null||t===""?{}:JSON.parse(String(t))}async function Z(t,e=ht){let n=Number(t.headers.get("content-length"));if(Number.isFinite(n)&&n>e)return{tooLarge:!0};let r=t.body;if(!r||typeof r.getReader!="function"){let i=await t.text();return Buffer.byteLength(i,"utf8")>e?{tooLarge:!0}:{raw:i}}let o=r.getReader(),s=[],a=0;for(;;){let{done:i,value:c}=await o.read();if(i)break;if(c){if(a+=c.byteLength,a>e){try{await o.cancel()}catch{}return{tooLarge:!0}}s.push(c)}}return{raw:Buffer.concat(s.map(i=>Buffer.from(i))).toString("utf8")}}function Jt(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",s=>{if(r+=s.length,r>ht){let a=new Error("Body too large");a.code="BODY_TOO_LARGE",n(a),t.destroy?.();return}o.push(s)}),t.on("end",()=>{if(o.length===0)return e("");e(Buffer.concat(o).toString("utf8"))}),t.on("error",n)})}function zt(t={}){let e=H(t);return async function(n){let r={};try{let a=await Z(n);if(a.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=M(a.raw,n.headers.get("content-type")||"")}catch{r={}}let o={};try{o=Object.fromEntries(new URL(n.url,"http://localhost").searchParams)}catch{}let s=await e({method:n.method,headers:n.headers,body:r,query:o});return new Response(s.body,{status:s.status,headers:s.headers})}}import{matchRoute as Xt,parseQuery as Yt}from"what-router/match";var Gt="/__what_action",Kt="/__what_revalidate",Q="what-csrf";function N(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function mt(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function Zt(t){try{let e=await Z(t);return e.tooLarge?{tooLarge:!0}:M(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function Qt(t){try{return await t.json()}catch{return{}}}function qt(t){return async function(n){let{route:r,params:o,query:s,request:a}=n,i={default:r.component,loader:r.loader},c=n.csrfToken?{...t,csrfToken:n.csrfToken}:t;return{html:await F(i,{params:o,query:s,request:a},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function k(t={}){let{routes:e=[],cache:n,render:r,revalidateWebhook:o,document:s={},notFound:a,basePath:i="",csrf:c=!0}=t,u=c!==!1&&!t.actionHandler,d=t.actionHandler||H(u?{getCsrfToken:f=>mt(f.headers&&f.headers.cookie,Q)}:{skipCsrf:!0}),l=r||qt(s);return n&&(n.revalidatePath||n.revalidateTag)&&V({revalidatePath:n.revalidatePath,revalidateTag:n.revalidateTag}),async function(p){let h=new URL(p.url,"http://localhost"),m=h.pathname;if(i&&m.startsWith(i)&&(m=m.slice(i.length)||"/"),p.method==="POST"&&m===Gt){let y=await Zt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let T=await d({method:"POST",headers:N(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(T.body,{status:T.status,headers:T.headers})}let S=null,C=null;if(u&&(S=mt(N(p.headers).cookie,Q),!S)){S=Y();let T=N(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;C=`${Q}=${encodeURIComponent(S)}; Path=/; SameSite=Lax`+(T?"; Secure":"")}let x=y=>(C&&(y["set-cookie"]=C),y);if(p.method==="POST"&&m===Kt&&o){let y=await Qt(p),T=await o({headers:N(p.headers),body:y});return new Response(JSON.stringify(T.body),{status:T.status,headers:{"content-type":"application/json"}})}let v=Xt(m,e);if(!v){let y=a?a():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(y,{status:404,headers:x({"content-type":"text/html; charset=utf-8"})})}let{route:E,params:U}=v,B=E.page||{mode:E.mode||"client"},I={path:m,query:Yt(h.search),config:B,route:E,params:U,request:p};if(n&&B.mode!=="server"){let y=await n.handle(I,()=>l(I));return new Response(y.html,{status:y.status||200,headers:x({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}S&&(I.csrfToken=S);let st=await l(I),at=x({"content-type":"text/html; charset=utf-8"});return B.mode==="server"&&(at["Cache-Control"]="private, no-store"),new Response(st.html,{status:st.status||200,headers:at})}}function te(t={}){let e=k(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function R(t){return{head:ee(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var wt=0;function ne(){wt=0}function re(){return"h"+wt++}function oe(t){if(typeof document>"u"&&!gt()){let e=R(void 0);return b(e,()=>oe(t))}return ne(),O(t)}function O(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return g(String(t));if(typeof t=="function"&&t._signal)return`<!--$-->${O(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${O(t())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${t.map(O).join("")}<!--/[]-->`;if(typeof t.tag=="function"){let c=re(),u=t.tag({...t.props,children:t.children}),d=O(u);return se(d,c)}let{tag:e,props:n,children:r}=t,o=nt(n||{}),s=`<${e}${o}>`;if(rt.has(e))return s;let a=et(n),i=a!=null?String(a):r.map(O).join("");return`${s}${i}</${e}>`}function se(t,e){let n=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],s=r.length+1+o.length;return t.slice(0,s)+` data-hk="${e}"`+t.slice(s)}return t}function w(t){if(typeof document>"u"&&!gt()){let c=R(void 0);return b(c,()=>w(t))}if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return g(String(t));if(typeof t=="function"&&t._signal)return w(t());if(typeof t=="function")try{return w(t())}catch{return typeof process<"u",""}if(Array.isArray(t))return t.map(w).join("");if(t.tag==="__suspense")try{return(t.children||[]).map(w).join("")}catch(c){if(c&&typeof c.then=="function")return w(t.props&&t.props.fallback);throw c}if(typeof t.tag=="function"){let c=t.tag({...t.props,children:t.children});return w(c)}let{tag:e,props:n,children:r}=t,o=nt(n||{}),s=`<${e}${o}>`;if(rt.has(e))return s;let a=et(n),i=a!=null?String(a):r.map(w).join("");return`${s}${i}</${e}>`}function Ke(t){let e=R(void 0);return{body:b(e,()=>w(t)),head:q(e.head)}}async function Ze(t,e={}){let n=t.default||t,r=typeof t.loader=="function"?await t.loader(e):void 0,o=R(r),s=e.params||{};return{body:b(o,()=>w(yt(n,{...s,loaderData:r}))),head:q(o.head),loaderData:r}}var St=12;async function ae(t,e){e||(e=R(void 0));let n="";for(let o=0;o<St;o++){n=b(e,()=>w(t));let s=[...e.resources.values()].filter(a=>a.status==="pending").map(a=>a.promise);if(s.length===0)break;await Promise.all(s)}let r={};for(let[o,s]of e.resources)s.status==="ready"&&(r[o]=s.value);return{body:n,head:q(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function F(t,e={},n={}){let r=t.default||t,o=typeof t.loader=="function"?await t.loader(e):void 0,s=R(o),a=e.params||{},{body:i,head:c,resources:u}=await ae(yt(r,{...a,loaderData:o}),s),d={loaderData:o??null,resources:u,islandStores:it(s)};return ie({body:i,head:c,payload:d,options:n})}function ie({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",s=`<script id="__what_data" type="application/json">${P(n)}<\/script>`,a=r.clientEntry?`<script type="module" src="${g(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?G(r.csrfToken):"")+(r.head||""),u=r.bodyClass?` class="${g(r.bodyClass)}"`:"";return`<!DOCTYPE html><html lang="${g(o)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${e||""}${c}</head><body${u}>${t}${s}${a}</body></html>`}async function*j(t,e){if(e===void 0&&(e=R(void 0)),t==null||t===!1||t===!0)return;if(typeof t=="string"||typeof t=="number"){yield g(String(t));return}if(typeof t=="function"&&t._signal){let a=b(e,()=>t());yield*j(a,e);return}if(typeof t=="function"){try{let a=b(e,()=>t());yield*j(a,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let a of t)yield*j(a,e);return}if(t.tag==="__suspense"){let a=null;for(let i=0;i<St&&a===null;i++){let c=null;try{a=b(e,()=>(t.children||[]).map(w).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(a===null){let u=[...e.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}a===null&&(a=b(e,()=>w(t.props&&t.props.fallback))),yield a;return}if(typeof t.tag=="function"){try{let a=b(e,()=>t.tag({...t.props,children:t.children})),i=a instanceof Promise?await a:a;yield*j(i,e)}catch(a){typeof process<"u",yield tt?`<!-- SSR Error: ${g(a.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t,s=nt(r||{});if(yield`<${n}${s}>`,!rt.has(n)){let a=et(r);if(a!=null)yield String(a);else for(let i of o)yield*j(i,e);yield`</${n}>`}}function Qe(t){return{mode:"static",...t}}function qe(t,e={}){let n=R(e),r=b(n,()=>{let s=t.component(e);return w(s)}),o=t.islands||[];return ce({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function ce({title:t,meta:e,body:n,islands:r,scripts:o,styles:s,mode:a}){let i=Object.entries(e).map(([f,p])=>`<meta name="${g(f)}" content="${g(p)}">`).join(`
2
+ `),c=s.map(f=>`<link rel="stylesheet" href="${g(f)}">`).join(`
3
+ `),u=r.length>0?`
4
+ <script type="module">
5
+ import { hydrateIslands } from '/@what/islands.js';
6
+ hydrateIslands();
7
+ <\/script>`:"",d=o.map(f=>`<script type="module" src="${g(f)}"><\/script>`).join(`
8
+ `),l=a==="client"?`
9
+ <script type="module" src="/@what/client.js"><\/script>`:"";return`<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ ${i}
15
+ <title>${g(t)}</title>
16
+ ${c}
17
+ </head>
18
+ <body>
19
+ <div id="app">${n}</div>
20
+ ${u}
21
+ ${d}
22
+ ${l}
23
+ </body>
24
+ </html>`}function tn(t){return t._server=!0,t}var tt=!(typeof process<"u");function et(t){return t?t.dangerouslySetInnerHTML?t.dangerouslySetInnerHTML.__html??null:t.innerHTML&&typeof t.innerHTML=="object"&&"__html"in t.innerHTML?t.innerHTML.__html??null:(t.innerHTML!=null&&typeof t.innerHTML=="string"&&tt&&console.warn("[what-server] innerHTML received a raw string. This is a security risk (XSS). Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead."),null):null}var ue=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/;function nt(t){let e="";for(let[n,r]of Object.entries(t))if(!(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")&&!(n.startsWith("on")&&n.length>2)&&!(r===!1||r==null)){if(!ue.test(n)){tt&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")e+=` class="${g(String(r))}"`;else if(n==="style"&&typeof r=="object"){let o=Object.entries(r).map(([s,a])=>`${fe(s)}:${a}`).join(";");e+=` style="${g(o)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?e+=` ${n}="true"`:e+=` ${n}`;else{if(de(n,r))continue;e+=` ${n}="${g(String(r))}"`}}return e}function de(t,e){let n=t.toLowerCase();if(!le.has(n))return!1;let r=String(e).trim().replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();return r.startsWith("javascript:")||r.startsWith("vbscript:")||r.startsWith("data:")}var le=new Set(["href","src","action","formaction","xlink:href"]);function g(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function fe(t){return t.startsWith("--")?t:t.replace(/([A-Z])/g,"-$1").toLowerCase()}var rt=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);import pe from"node:http";async function bt(t){let n=`http://${t.headers.host||"localhost"}${t.url}`,r=new Headers;for(let[s,a]of Object.entries(t.headers))a!=null&&r.set(s,Array.isArray(a)?a.join(", "):String(a));let o;if(t.method!=="GET"&&t.method!=="HEAD"){let s=[];for await(let a of t)s.push(a);s.length&&(o=Buffer.concat(s))}return new Request(n,{method:t.method,headers:r,body:o})}async function At(t,e){t.statusCode=e.status,e.headers.forEach((r,o)=>t.setHeader(o,r));let n=await e.text();t.end(n)}function Tt(t){return async function(n,r){try{let o=await bt(n),s=await t(o);await At(r,s)}catch(o){r.headersSent||r.writeHead(500,{"content-type":"text/html; charset=utf-8"}),r.end("<!DOCTYPE html><html><body><h1>500 \u2014 Server Error</h1></body></html>"),console.error("[what-server] request error:",o)}}}function he(t={}){let e=k(t);return async function(r,o,s){let a=await bt(r),i=await e(a);if(i.status===404&&typeof s=="function")return s();await At(o,i)}}function me(t={}){let e=k(t),n=pe.createServer(Tt(e)),{scheduler:r}=t;if(r){r.start();let o=()=>{try{r.stop()}catch{}n.close()};process.once("SIGTERM",o),process.once("SIGINT",o)}return n}import{mkdir as ye,writeFile as _t}from"node:fs/promises";import{join as ot}from"node:path";import{matchRoute as ge}from"what-router/match";function we(t){return t.includes(":")||t.includes("*")||t.includes("[")}function Se(t,e){return t.replace(/\[\.\.\.(\w+)\]/g,(n,r)=>e[r]??"").replace(/\[(\w+)\]/g,(n,r)=>e[r]??"").replace(/[:*](\w+)/g,(n,r)=>e[r]??"")}async function be({routes:t=[],outDir:e,render:n,documentOptions:r={}}={}){let o=[];for(let s of t){let a=s.page&&s.page.mode||s.mode;if(a!=="static"&&a!=="hybrid")continue;let i={default:s.component,loader:s.loader},c=[s.path];if(we(s.path)){if(typeof s.getStaticPaths!="function")continue;c=((await s.getStaticPaths()).paths||[]).map(d=>Se(s.path,d.params||{}))}for(let u of c){let d=ge(u,[s]),f={params:d?d.params:{},query:{}},p=n?await n(i,f):await F(i,f,r),h=ot(e,u==="/"?"":u);if(await ye(h,{recursive:!0}),await _t(ot(h,"index.html"),p),typeof s.loader=="function"){let m=await s.loader(f);await _t(ot(h,"__what_data.json"),P({loaderData:m}))}o.push(u)}}return{pages:o}}function Ae(t={}){return k(t)}async function Te({outDir:t=".vercel/output",functionName:e="render",runtime:n="nodejs22.x",files:r=null,handler:o="index.mjs",staticDir:s=null}={}){let{mkdir:a,writeFile:i,cp:c}=await import("node:fs/promises"),{join:u,dirname:d}=await import("node:path");await a(t,{recursive:!0});let l={version:3,routes:[{handle:"filesystem"},{src:"/.*",dest:`/${e}`}]};await i(u(t,"config.json"),JSON.stringify(l,null,2)),s&&await c(s,u(t,"static"),{recursive:!0});let f=null;if(r&&typeof r=="object"){f=u(t,"functions",`${e}.func`),await a(f,{recursive:!0});let p={runtime:n,handler:o,launcherType:"Nodejs",shouldAddHelpers:!1,supportsResponseStreaming:!0};await i(u(f,".vc-config.json"),JSON.stringify(p,null,2));for(let[h,m]of Object.entries(r)){let S=u(f,h);await a(d(S),{recursive:!0}),await i(S,m)}o in r||console.warn(`[what-server] buildVercelOutput: files does not include the handler entry "${o}" \u2014 the deploy will 500 until your build emits it.`)}return{config:l,outDir:t,functionDir:f}}ke(new _e);export{jt as action,Te as buildVercelOutput,H as createActionHandler,te as createCloudflareHandler,k as createRequestHandler,me as createServer,Ae as createVercelHandler,G as csrfMetaTag,Qe as definePage,be as exportStatic,zt as fetchActionHandler,dt as formAction,Y as generateCsrfToken,qe as generateStaticPage,Mt as getRegisteredActions,Et as getRevalidationHandler,D as handleActionRequest,ft as invalidatePath,Wt as nodeActionMiddleware,Lt as onRevalidate,F as renderDocument,Ze as renderPage,oe as renderToHydratableString,j as renderToStream,w as renderToString,ae as renderToStringAsync,Ke as renderToStringWithHead,W as revalidatePath,J as revalidateTag,P as serializeState,tn as server,V as setRevalidationHandler,Tt as toNodeListener,lt as useAction,It as useFormAction,Nt as useMutation,Dt as useOptimistic,ut as validateCsrfToken,he as whatMiddleware};
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // What Framework Server - TypeScript Definitions
2
2
 
3
- import { VNode, VNodeChild, Signal } from '../core';
3
+ import { VNode, VNodeChild, Signal } from 'what-core';
4
4
 
5
5
  // --- SSR ---
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-server",
3
- "version": "0.11.6",
3
+ "version": "0.11.7",
4
4
  "description": "What Framework - SSR, islands architecture, static generation",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -8,6 +8,11 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./index.d.ts",
11
+ "node": {
12
+ "production": "./dist/node.min.js",
13
+ "import": "./src/node.js",
14
+ "default": "./src/node.js"
15
+ },
11
16
  "production": "./dist/index.min.js",
12
17
  "import": "./src/index.js",
13
18
  "default": "./src/index.js"
@@ -28,7 +33,10 @@
28
33
  "dist/**/*.min.js",
29
34
  "index.d.ts"
30
35
  ],
31
- "sideEffects": false,
36
+ "sideEffects": [
37
+ "./src/node.js",
38
+ "./dist/node.min.js"
39
+ ],
32
40
  "keywords": [
33
41
  "ssr",
34
42
  "islands",
@@ -39,8 +47,8 @@
39
47
  "author": "ZVN DEV (https://zvndev.com)",
40
48
  "license": "MIT",
41
49
  "peerDependencies": {
42
- "what-core": "^0.11.6",
43
- "what-router": "^0.11.6"
50
+ "what-core": "^0.11.7",
51
+ "what-router": "^0.11.7"
44
52
  },
45
53
  "peerDependenciesMeta": {
46
54
  "what-router": {
package/src/actions.js CHANGED
@@ -78,11 +78,24 @@ export function csrfMetaTag(token) {
78
78
  // --- Define a server action ---
79
79
 
80
80
  let _actionCounter = 0;
81
+ let _fallbackWarningShown = false;
81
82
 
82
83
  function generateActionId() {
83
- // Generate a deterministic ID — prefer crypto.getRandomValues, fall back to a
84
- // monotonic counter (never Math.random, which is not cryptographically safe and
85
- // produces predictable IDs in some runtimes).
84
+ if (
85
+ !_fallbackWarningShown
86
+ && typeof process !== 'undefined'
87
+ && process.env?.NODE_ENV !== 'production'
88
+ ) {
89
+ _fallbackWarningShown = true;
90
+ console.warn(
91
+ '[what-server] action() is using a runtime-generated ID because compiler metadata is missing. ' +
92
+ 'Build with what-compiler/vite-plugin-what or pass an explicit { id } so client and server bundles agree.'
93
+ );
94
+ }
95
+ // Compatibility fallback for direct, uncompiled script users. Normal builds
96
+ // receive a deterministic compiler ID derived from source path + binding.
97
+ // Never use Math.random here; crypto avoids predictable collisions, and the
98
+ // monotonic fallback keeps older runtimes functional.
86
99
  const rand = typeof crypto !== 'undefined' && crypto.getRandomValues
87
100
  ? Array.from(crypto.getRandomValues(new Uint8Array(6)), b => b.toString(16).padStart(2, '0')).join('')
88
101
  : `c${(++_actionCounter).toString(36)}_${Date.now().toString(36)}`;
package/src/index.js CHANGED
@@ -2,7 +2,13 @@
2
2
  // SSR, static site generation, server components.
3
3
  // Zero-JS pages by default. Islands opt-in to client JS.
4
4
 
5
- import { h, runWithServerContext, beginHeadCollection, endHeadCollection } from 'what-core';
5
+ import {
6
+ h,
7
+ getServerContext,
8
+ runWithServerContext,
9
+ beginHeadCollection,
10
+ endHeadCollection,
11
+ } from 'what-core';
6
12
  import { serializeState } from './serialize.js';
7
13
  import { getIslandStoresSnapshot } from './islands.js';
8
14
  import { csrfMetaTag } from './actions.js';
@@ -13,6 +19,7 @@ function createRenderContext(loaderData) {
13
19
  head: beginHeadCollection(),
14
20
  loaderData,
15
21
  resources: new Map(),
22
+ islandStores: new Map(),
16
23
  resourceCounter: 0,
17
24
  boundaryCounter: 0,
18
25
  suspended: [],
@@ -35,6 +42,10 @@ function nextHydrationId() {
35
42
  // so the client can reuse the server-rendered DOM.
36
43
 
37
44
  export function renderToHydratableString(vnode) {
45
+ if (typeof document === 'undefined' && !getServerContext()) {
46
+ const ctx = createRenderContext(undefined);
47
+ return runWithServerContext(ctx, () => renderToHydratableString(vnode));
48
+ }
38
49
  resetHydrationId();
39
50
  return _renderHydratable(vnode);
40
51
  }
@@ -108,6 +119,10 @@ function injectHydrationKey(html, hkId) {
108
119
  // Renders a VNode tree to an HTML string. Used for SSR and static gen.
109
120
 
110
121
  export function renderToString(vnode) {
122
+ if (typeof document === 'undefined' && !getServerContext()) {
123
+ const ctx = createRenderContext(undefined);
124
+ return runWithServerContext(ctx, () => renderToString(vnode));
125
+ }
111
126
  if (vnode == null || vnode === false || vnode === true) return '';
112
127
 
113
128
  // Text
@@ -243,7 +258,7 @@ export async function renderDocument(pageModule, reqCtx = {}, options = {}) {
243
258
  const payload = {
244
259
  loaderData: loaderData ?? null,
245
260
  resources,
246
- islandStores: getIslandStoresSnapshot(),
261
+ islandStores: getIslandStoresSnapshot(ctx),
247
262
  };
248
263
  return wrapHtmlDocument({ body, head, payload, options });
249
264
  }
@@ -283,14 +298,16 @@ export async function* renderToStream(vnode, ctx) {
283
298
 
284
299
  // Signal — unwrap by calling it
285
300
  if (typeof vnode === 'function' && vnode._signal) {
286
- yield* renderToStream(vnode(), ctx);
301
+ const value = runWithServerContext(ctx, () => vnode());
302
+ yield* renderToStream(value, ctx);
287
303
  return;
288
304
  }
289
305
 
290
306
  // Reactive function child — call to get value
291
307
  if (typeof vnode === 'function') {
292
308
  try {
293
- yield* renderToStream(vnode(), ctx);
309
+ const value = runWithServerContext(ctx, () => vnode());
310
+ yield* renderToStream(value, ctx);
294
311
  } catch (e) {
295
312
  if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
296
313
  console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);
@@ -333,7 +350,10 @@ export async function* renderToStream(vnode, ctx) {
333
350
 
334
351
  if (typeof vnode.tag === 'function') {
335
352
  try {
336
- const result = vnode.tag({ ...vnode.props, children: vnode.children });
353
+ const result = runWithServerContext(
354
+ ctx,
355
+ () => vnode.tag({ ...vnode.props, children: vnode.children })
356
+ );
337
357
  // Support async components
338
358
  const resolved = result instanceof Promise ? await result : result;
339
359
  yield* renderToStream(resolved, ctx);
@@ -380,8 +400,11 @@ export function definePage(config) {
380
400
 
381
401
  // Generate static HTML for a page
382
402
  export function generateStaticPage(page, data = {}) {
383
- const vnode = page.component(data);
384
- const html = renderToString(vnode);
403
+ const ctx = createRenderContext(data);
404
+ const html = runWithServerContext(ctx, () => {
405
+ const vnode = page.component(data);
406
+ return renderToString(vnode);
407
+ });
385
408
  const islands = page.islands || [];
386
409
 
387
410
  return wrapDocument({
@@ -589,12 +612,11 @@ export {
589
612
  getRevalidationHandler,
590
613
  } from './revalidation-registry.js';
591
614
 
592
- // Deploy adapters — framework-agnostic core + Node / static / edge wrappers
615
+ // Runtime-neutral deploy adapters. Node-only adapters are re-exported by the
616
+ // package's conditional Node entry so browser/edge bundles never resolve Node
617
+ // builtins merely because they import a client-safe server action.
593
618
  export { createRequestHandler } from './adapter/core.js';
594
- export { createServer, toNodeListener, whatMiddleware } from './adapter/node.js';
595
- export { exportStatic } from './adapter/static.js';
596
619
  export { createCloudflareHandler } from './adapter/cloudflare.js';
597
- export { createVercelHandler, buildVercelOutput } from './adapter/vercel.js';
598
620
 
599
621
  // Safe state serialization for inlining into <script> tags (AUDIT-2026-06-06 M13)
600
622
  export { serializeState } from './serialize.js';
package/src/islands.js CHANGED
@@ -16,7 +16,7 @@
16
16
  // 'media' - Hydrate when media query matches (e.g., mobile-only)
17
17
  // 'action' - Hydrate on first user interaction (click, focus, hover)
18
18
 
19
- import { mount, hydrate, signal, batch } from 'what-core';
19
+ import { mount, hydrate, signal, batch, getServerContext } from 'what-core';
20
20
  import { serializeState } from './serialize.js';
21
21
 
22
22
  const islandRegistry = new Map();
@@ -25,20 +25,41 @@ const hydrationQueue = [];
25
25
  let isProcessingQueue = false;
26
26
 
27
27
  // --- Shared Island State ---
28
- // Global reactive store that persists across islands and page navigations
29
-
30
- const sharedStores = new Map();
31
-
32
- export function createIslandStore(name, initialState) {
33
- if (sharedStores.has(name)) {
34
- return sharedStores.get(name);
28
+ // Browser stores intentionally persist across islands and client navigations.
29
+ // Server stores belong to the active render context so one request can never
30
+ // observe another request's state. Module-scoped server declarations receive a
31
+ // lightweight handle that resolves to the current request's concrete store.
32
+
33
+ const browserStores = new Map();
34
+ const serverStoreDefinitions = new Map();
35
+
36
+ function cloneInitialState(value) {
37
+ if (typeof structuredClone === 'function') {
38
+ try {
39
+ return structuredClone(value);
40
+ } catch {
41
+ // Fall through for values structuredClone cannot represent. Island
42
+ // state is expected to be serializable, but preserving a shallow copy is
43
+ // a safer compatibility fallback than sharing the original object.
44
+ }
45
+ }
46
+ if (Array.isArray(value)) return value.map(cloneInitialState);
47
+ if (value && typeof value === 'object') {
48
+ return Object.fromEntries(
49
+ Object.entries(value).map(([key, item]) => [key, cloneInitialState(item)])
50
+ );
35
51
  }
52
+ return value;
53
+ }
54
+
55
+ function createConcreteStore(storeMap, name, initialState) {
56
+ if (storeMap.has(name)) return storeMap.get(name);
36
57
 
37
58
  const store = {};
38
59
  const signals = {};
39
60
 
40
61
  // Create signals for each key in initial state
41
- for (const [key, value] of Object.entries(initialState)) {
62
+ for (const [key, value] of Object.entries(cloneInitialState(initialState))) {
42
63
  signals[key] = signal(value);
43
64
  Object.defineProperty(store, key, {
44
65
  get: () => signals[key](),
@@ -72,16 +93,86 @@ export function createIslandStore(name, initialState) {
72
93
  });
73
94
  };
74
95
 
75
- sharedStores.set(name, store);
96
+ storeMap.set(name, store);
76
97
  return store;
77
98
  }
78
99
 
100
+ function activeServerStoreMap(context) {
101
+ const ctx = context || getServerContext();
102
+ return ctx && ctx.islandStores instanceof Map ? ctx.islandStores : null;
103
+ }
104
+
105
+ function serverStoreHandle(name, initialState) {
106
+ if (serverStoreDefinitions.has(name)) {
107
+ return serverStoreDefinitions.get(name).handle;
108
+ }
109
+
110
+ const definition = {
111
+ initialState: cloneInitialState(initialState),
112
+ handle: null,
113
+ };
114
+
115
+ const resolve = () => {
116
+ const storeMap = activeServerStoreMap();
117
+ if (!storeMap) {
118
+ throw new Error(
119
+ `[what-server] Island store "${name}" was accessed outside an active server render. ` +
120
+ 'Read or write module-scoped island stores from a component rendered by renderDocument/renderPage.'
121
+ );
122
+ }
123
+ return createConcreteStore(storeMap, name, definition.initialState);
124
+ };
125
+
126
+ definition.handle = new Proxy({}, {
127
+ get(_target, property) {
128
+ return Reflect.get(resolve(), property);
129
+ },
130
+ set(_target, property, value) {
131
+ return Reflect.set(resolve(), property, value);
132
+ },
133
+ has(_target, property) {
134
+ return Reflect.has(resolve(), property);
135
+ },
136
+ ownKeys() {
137
+ return Reflect.ownKeys(resolve());
138
+ },
139
+ getOwnPropertyDescriptor(_target, property) {
140
+ const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), property);
141
+ return descriptor ? { ...descriptor, configurable: true } : undefined;
142
+ },
143
+ });
144
+
145
+ serverStoreDefinitions.set(name, definition);
146
+ return definition.handle;
147
+ }
148
+
149
+ export function createIslandStore(name, initialState) {
150
+ if (typeof document !== 'undefined') {
151
+ return createConcreteStore(browserStores, name, initialState);
152
+ }
153
+
154
+ const storeMap = activeServerStoreMap();
155
+ if (storeMap) {
156
+ const definition = serverStoreDefinitions.get(name);
157
+ return createConcreteStore(storeMap, name, definition?.initialState ?? initialState);
158
+ }
159
+
160
+ return serverStoreHandle(name, initialState);
161
+ }
162
+
79
163
  // Get or create a shared store
80
164
  export function useIslandStore(name, fallbackInitial = {}) {
81
- if (sharedStores.has(name)) {
82
- return sharedStores.get(name);
165
+ if (typeof document !== 'undefined') {
166
+ return createConcreteStore(browserStores, name, fallbackInitial);
167
+ }
168
+
169
+ const storeMap = activeServerStoreMap();
170
+ if (storeMap) {
171
+ const definition = serverStoreDefinitions.get(name);
172
+ return createConcreteStore(storeMap, name, definition?.initialState ?? fallbackInitial);
83
173
  }
84
- return createIslandStore(name, fallbackInitial);
174
+
175
+ return serverStoreHandle(name, fallbackInitial);
85
176
  }
86
177
 
87
178
  // Serialize all shared stores for SSR.
@@ -94,9 +185,12 @@ export function serializeIslandStores() {
94
185
 
95
186
  // Raw (unserialized) snapshot of all shared island stores, so renderDocument can
96
187
  // merge it into the single consolidated #__what_data payload (one serialize pass).
97
- export function getIslandStoresSnapshot() {
188
+ export function getIslandStoresSnapshot(context) {
189
+ const storeMap = typeof document !== 'undefined'
190
+ ? browserStores
191
+ : activeServerStoreMap(context);
98
192
  const data = {};
99
- for (const [name, store] of sharedStores) {
193
+ for (const [name, store] of storeMap || []) {
100
194
  data[name] = store._getSnapshot();
101
195
  }
102
196
  return data;
@@ -428,12 +522,15 @@ export function enhanceForms(selector = 'form[data-enhance]') {
428
522
  // --- Debugging ---
429
523
 
430
524
  export function getIslandStatus() {
525
+ const stores = typeof document !== 'undefined'
526
+ ? [...browserStores.keys()]
527
+ : [...(activeServerStoreMap()?.keys() || serverStoreDefinitions.keys())];
431
528
  const status = {
432
529
  registered: [...islandRegistry.keys()],
433
530
  hydrated: hydratedIslands.size,
434
531
  pending: hydrationQueue.length,
435
532
  queue: hydrationQueue.map(t => ({ name: t.name, priority: t.priority })),
436
- stores: [...sharedStores.keys()],
533
+ stores,
437
534
  };
438
535
  return status;
439
536
  }
package/src/node.js ADDED
@@ -0,0 +1,18 @@
1
+ // What Framework - Node server runtime.
2
+ //
3
+ // Keep Node async context and Node-only deployment adapters behind the
4
+ // package's `node` export condition. The default entry remains safe to resolve
5
+ // in browser and non-Node edge bundles, including client server-action builds.
6
+
7
+ import { AsyncLocalStorage } from 'node:async_hooks';
8
+ import { __installServerContextStorage } from 'what-core';
9
+
10
+ // Async components may access loader/resource/island state after an `await`.
11
+ // One process-local provider keeps those reads bound to the request that
12
+ // invoked the component even when multiple streams interleave.
13
+ __installServerContextStorage(new AsyncLocalStorage());
14
+
15
+ export * from './index.js';
16
+ export { createServer, toNodeListener, whatMiddleware } from './adapter/node.js';
17
+ export { exportStatic } from './adapter/static.js';
18
+ export { createVercelHandler, buildVercelOutput } from './adapter/vercel.js';
package/src/serialize.js CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Stateless on purpose: this module holds no shared state, so it is safe for
4
4
  // the bundler to inline into multiple server entry points without creating
5
- // divergent instances (unlike islands.js's sharedStores).
5
+ // divergent instances. Island state itself is scoped by the render context.
6
6
  //
7
7
  // JSON.stringify alone is NOT safe to drop inside <script>...</script>: a value
8
8
  // containing "</script>" (or "<!--", "<script") breaks out of the element and