what-server 0.12.1 → 0.12.3

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.
package/dist/index.min.js CHANGED
@@ -1,10 +1,10 @@
1
- import{h as wt,_isAriaAttr as ne,getServerContext as St,runWithServerContext as S,beginHeadCollection as re,endHeadCollection as tt}from"what-core";var At=new RegExp("[<>&\\u2028\\u2029]","g"),Tt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function J(t){return JSON.stringify(t).replace(At,e=>Tt[e.charCodeAt(0)])}import{mount as Se,hydrate as be,signal as _e,batch as Ae,getServerContext as kt}from"what-core";var Rt=new Map;function Et(t){let e=t||kt();return e&&e.islandStores instanceof Map?e.islandStores:null}function it(t){let e=typeof document<"u"?Rt:Et(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as _,batch as X}from"what-core";var T=null,ct=!(typeof process<"u");function W(t){T=t}function Ct(){return T}async function V(t,e){if(T&&T.revalidatePath)return T.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 z(t,e){if(T&&T.revalidateTag)return T.revalidateTag(t,e);ct&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var Y=new Map;function vt(){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 K(){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 dt(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 Z(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Ot=0,xt=!1;function Pt(){return!xt&&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${(++Ot).toString(36)}_${Date.now().toString(36)}`}`}function $t(t,e={}){let n=e.id||Pt(),{onError:r,onSuccess:o,revalidate:s}=e;typeof window>"u"&&Y.set(n,{fn:t,options:e});async function a(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,d=new AbortController,u=setTimeout(()=>d.abort(),c);try{let f=vt(),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:d.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),s)for(let g of s)lt(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(u)}}return a._actionId=n,a._isAction=!0,a}function ut(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={},d=!1;for(let[u,f]of a.entries())typeof File<"u"&&f instanceof File&&(d=!0),c[u]?Array.isArray(c[u])?c[u].push(f):c[u]=[c[u],f]:c[u]=f;try{let u=d?await t(c,a):await t(c);return n&&n(u,i),o&&i&&i.reset(),u}catch(u){throw r&&r(u,i),u}}}function ft(t){let e=_(!1),n=_(null),r=_(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=ft(ut(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 Ht(t,e){let n=_(t),r=_([]),o=_(t);function s(d){let u=e(n.peek(),d);X(()=>{r.set([...r.peek(),d]),n.set(u)})}function a(d,u){X(()=>{if(r.set(r.peek().filter(f=>f!==d)),u!==void 0){o.set(u);let f=u;for(let l of r.peek())f=e(f,l);n.set(f)}})}function i(d,u){X(()=>{let f=r.peek().filter(h=>h!==d);r.set(f);let l=u!==void 0?u:o.peek();o.set(l);let p=l;for(let h of f)p=e(p,h);n.set(p)})}async function c(d,u){s(d);try{let f=await u();return a(d,f),f}catch(f){throw i(d),f}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:s,resolve:a,rollback:i,withOptimistic:c,set:d=>{n.set(d),o.set(d)}}}var P=new Map;function Nt(t,e){return P.has(t)||P.set(t,new Set),P.get(t).add(e),()=>{P.get(t)?.delete(e)}}function lt(t){let e=P.get(t);if(e)for(let n of e)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function N(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(!dt(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=Y.get(e);return a?Array.isArray(n)?a.fn(...n).then(async i=>{let c=a.options||{};if(Array.isArray(c.revalidate))for(let d of c.revalidate)await V(d);if(Array.isArray(c.revalidateTags))for(let d of c.revalidateTags)await z(d);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 Dt(){return[...Y.keys()]}function jt(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,s={isPending:_(!1),error:_(null),data:_(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)}}}import{h as D}from"what-core";var pt="/__what_action";function Lt(t){if(typeof t=="string")return t;if(typeof t=="function"){let e=t._actionId;if(e)return e;throw new Error("[what] <Form action={fn}>: that function is not a server action. Wrap it with action() from what-server, or pass the action id as a string.")}throw new Error("[what] <Form> requires an `action` prop: a server action or its id.")}function Mt(){if(typeof document>"u")return null;let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);return e?decodeURIComponent(e[1]):null}function Ft({action:t,csrfToken:e,redirect:n,method:r="post",enhance:o=!0,children:s,...a}){let i=Lt(t),c=e!==void 0?e:Mt();c==null&&typeof process<"u";let d=[D("input",{type:"hidden",name:"_action",value:i}),D("input",{type:"hidden",name:"what-csrf-token",value:c||""})];n&&d.push(D("input",{type:"hidden",name:"_redirect",value:n}));let u={...a,method:r,action:a.action||pt,"data-action":i};o&&(u["data-enhance"]="");let f=s==null?[]:Array.isArray(s)?s:[s];return D("form",u,...d,...f)}var Ut="/__what_action",yt=1024*1024;function Bt(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 j(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function G(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 ht(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=ht(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),s=ht(o.pathname+o.search);if(s)return s}catch{}return"/"}var Wt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function $(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return j(405,{message:"Method Not Allowed"});let a=Bt(o.headers),i=a["x-what-action"],c=a["content-type"]||"",d=!i&&c.includes("application/x-www-form-urlencoded"),u=n?void 0:e?await e(o):void 0;if(d){let h=o.body||{},g=h._action||h["data-action"]||o.query&&o.query.action;if(!g)return G(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let b={...a},E=h._csrf??h["what-csrf-token"];if(E&&!b["x-csrf-token"]&&(b["x-csrf-token"]=String(E)),!n&&e&&!u)return G(403,"Missing CSRF token");let C={};for(let[v,U]of Object.entries(h))Wt.has(v)||(C[v]=U);let R=await N({headers:b},g,[C],{csrfToken:u,skipCsrf:n});return R.status===200?{status:303,headers:{location:Jt(h,a)},body:""}:G(R.status,R.body&&R.body.message||"Action failed")}if(!i)return j(400,{message:"Missing X-What-Action header"});if(!n&&e&&!u)return j(403,{message:"Missing CSRF token"});let l=(o.body||{}).args,p=await N({headers:a},i,l,{csrfToken:u,skipCsrf:n});return j(p.status,p.body)}}function Vt(t={}){let e=t.basePath||Ut,n=$(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 d;try{let l=await zt(o);d=L(l,o.headers["content-type"]||"")}catch(l){s.writeHead(l.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),s.end(JSON.stringify({message:l.code==="BODY_TOO_LARGE"?"Payload too large":"Invalid request body"}));return}let u=Object.fromEntries(new URLSearchParams(c||"")),f=await n({method:o.method,headers:o.headers,body:d,query:u});s.writeHead(f.status,f.headers),s.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 M(t,e=yt){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 zt(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",s=>{if(r+=s.length,r>yt){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 Xt(t={}){let e=$(t);return async function(n){let r={};try{let a=await M(n);if(a.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=L(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 Yt,parseQuery as Kt}from"what-router/match";var Zt="/__what_action",Gt="/__what_revalidate",Q="what-csrf";function I(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 Qt(t){try{let e=await M(t);return e.tooLarge?{tooLarge:!0}:L(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function qt(t){let e;try{e=await M(t)}catch{return{json:{}}}if(e.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(e.raw)}}catch{return{json:{}}}}function te(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 gt(i,{params:o,query:s,request:a},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function q(t={}){let{routes:e=[],cache:n,render:r,revalidateWebhook:o,document:s={},notFound:a,basePath:i="",csrf:c=!0}=t,d=c!==!1&&!t.actionHandler,u=t.actionHandler||$(d?{getCsrfToken:l=>mt(l.headers&&l.headers.cookie,Q)}:{skipCsrf:!0}),f=r||te(s);return n&&(n.revalidatePath||n.revalidateTag)&&W({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===Zt){let y=await Qt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await u({method:"POST",headers:I(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(A.body,{status:A.status,headers:A.headers})}let b=null,E=null;if(d&&(b=mt(I(p.headers).cookie,Q),!b)){b=K();let A=I(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;E=`${Q}=${encodeURIComponent(b)}; Path=/; SameSite=Lax`+(A?"; Secure":"")}let C=y=>(E&&(y["set-cookie"]=E),y);if(p.method==="POST"&&g===Gt&&o){let y=await qt(p);if(y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await o({headers:I(p.headers),body:y.json});return new Response(JSON.stringify(A.body),{status:A.status,headers:{"content-type":"application/json"}})}let R=Yt(g,e);if(!R){let y=a?a():"<!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:v,params:U}=R,B=v.page||{mode:v.mode||"client"},x={path:g,query:Kt(h.search),config:B,route:v,params:U,request:p};if(n&&B.mode!=="server"){x.varyHeaders=I(p.headers);let y=await n.handle(x,()=>f(x));return new Response(y.html,{status:y.status||200,headers:C({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}b&&(x.csrfToken=b);let at=await f(x),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})}}function ee(t={}){let e=q(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function k(t){return{head:re(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var bt=0;function oe(){bt=0}function ae(){return"h"+bt++}function se(t){if(!St()){let e=k(void 0);return S(e,()=>se(t))}return oe(),O(t)}function O(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`<!--$-->${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=ae(),d=t.tag({...t.props,children:t.children}),u=O(d);return ie(u,c)}let{tag:e,props:n,children:r}=t;nt(e);let o=rt(n||{}),s=`<${e}${o}>`;if(ot.has(e))return s;let a=et(n),i=a!=null?String(a):r.map(O).join("");return`${s}${i}</${e}>`}function ie(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(!St()){let c=k(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;nt(e);let o=rt(n||{}),s=`<${e}${o}>`;if(ot.has(e))return s;let a=et(n),i=a!=null?String(a):r.map(w).join("");return`${s}${i}</${e}>`}function We(t){let e=k(void 0);return{body:S(e,()=>w(t)),head:tt(e.head)}}async function Ve(t,e={}){let n=t.default||t,r=typeof t.loader=="function"?await t.loader(e):void 0,o=k(r),s=e.params||{};return{body:S(o,()=>w(wt(n,{...s,loaderData:r}))),head:tt(o.head),loaderData:r}}var _t=12;async function ce(t,e){e||(e=k(void 0));let n="";for(let o=0;o<_t;o++){n=S(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:tt(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function gt(t,e={},n={}){let r=t.default||t,o=typeof t.loader=="function"?await t.loader(e):void 0,s=k(o),a=e.params||{},{body:i,head:c,resources:d}=await ce(wt(r,{...a,loaderData:o}),s),u={loaderData:o??null,resources:d,islandStores:it(s)};return de({body:i,head:c,payload:u,options:n})}function de({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",s=`<script id="__what_data" type="application/json">${J(n)}<\/script>`,a=r.clientEntry?`<script type="module" src="${m(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?Z(r.csrfToken):"")+(r.head||""),d=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${d}>${t}${s}${a}</body></html>`}async function*H(t,e){if(e===void 0&&(e=k(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 a=S(e,()=>t());yield*H(a,e);return}if(typeof t=="function"){try{let a=S(e,()=>t());yield*H(a,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let a of t)yield*H(a,e);return}if(t.tag==="__suspense"){let a=null;for(let i=0;i<_t&&a===null;i++){let c=null;try{a=S(e,()=>(t.children||[]).map(w).join(""))}catch(d){if(d&&typeof d.then=="function")c=d;else throw d}if(a===null){let d=[...e.resources.values()].filter(u=>u.status==="pending").map(u=>u.promise);await Promise.all([c,...d].filter(Boolean))}}a===null&&(a=S(e,()=>w(t.props&&t.props.fallback))),yield a;return}if(typeof t.tag=="function"){try{let a=S(e,()=>t.tag({...t.props,children:t.children})),i=a instanceof Promise?await a:a;yield*H(i,e)}catch(a){typeof process<"u",yield F?`<!-- SSR Error: ${m(a.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t;nt(n);let s=rt(r||{});if(yield`<${n}${s}>`,!ot.has(n)){let a=et(r);if(a!=null)yield String(a);else for(let i of o)yield*H(i,e);yield`</${n}>`}}function ze(t){return{mode:"static",...t}}function Xe(t,e={}){let n=k(e),r=S(n,()=>{let s=t.component(e);return w(s)}),o=t.islands||[];return ue({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function ue({title:t,meta:e,body:n,islands:r,scripts:o,styles:s,mode:a}){let i=Object.entries(e).map(([l,p])=>`<meta name="${m(l)}" content="${m(p)}">`).join(`
2
- `),c=s.map(l=>`<link rel="stylesheet" href="${m(l)}">`).join(`
3
- `),d=r.length>0?`
1
+ import{h as _e,_isAriaAttr as at,_beginComponentSSR as ee,_endComponentSSR as te,_mapArrayToArray as ne,getServerContext as Ae,runWithServerContext as S,beginHeadCollection as st,endHeadCollection as re}from"what-core";var Ee=new RegExp("[<>&\\u2028\\u2029]","g"),Re={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function J(e){return JSON.stringify(e).replace(Ee,t=>Re[t.charCodeAt(0)])}import{mount as At,hydrate as kt,signal as Tt,batch as Et,getServerContext as Ce}from"what-core";var ve=new Map;function Oe(e){let t=e||Ce();return t&&t.islandStores instanceof Map?t.islandStores:null}function de(e){let t=typeof document<"u"?ve:Oe(e),n={};for(let[r,o]of t||[])n[r]=o._getSnapshot();return n}import{signal as _,batch as X}from"what-core";var k=null,fe=!(typeof process<"u");function V(e){k=e}function $e(){return k}async function W(e,t){if(k&&k.revalidatePath)return k.revalidatePath(e,t);fe&&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 z(e,t){if(k&&k.revalidateTag)return k.revalidateTag(e,t);fe&&console.warn(`[what] revalidateTag('${e}') had no effect: no cache engine is bound.`)}var Y=new Map;function xe(){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 K(){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 le(e,t){if(!e||!t||e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function Z(e){return`<meta name="what-csrf-token" content="${String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Pe=0,Ie=!1;function Ne(){return!Ie&&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${(++Pe).toString(36)}_${Date.now().toString(36)}`}`}function He(e,t={}){let n=t.id||Ne(),{onError:r,onSuccess:o,revalidate:s}=t;typeof window>"u"&&Y.set(n,{fn:e,options:t});async function a(...i){if(typeof window>"u")return e(...i);let c=t.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let f=xe(),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 w=await p.json().catch(()=>({message:"Action failed"}));throw new Error(w.message||"Action failed")}let h=await p.json();if(o&&o(h),s)for(let w of s)ye(w);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 a._actionId=n,a._isAction=!0,a}function pe(e,t={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=t;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,f]of a.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 e(c,a):await e(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function he(e){let t=_(!1),n=_(null),r=_(null);async function o(...s){t.set(!0),n.set(null);try{let a=await e(...s);return r.set(a),a}catch(a){throw n.set(a),a}finally{t.set(!1)}}return{trigger:o,isPending:()=>t(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function Le(e,t={}){let{resetOnSuccess:n=!0}=t,r={current:null},o=he(pe(e,{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 De(e,t){let n=_(e),r=_([]),o=_(e);function s(u){let d=t(n.peek(),u);X(()=>{r.set([...r.peek(),u]),n.set(d)})}function a(u,d){X(()=>{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=t(f,l);n.set(f)}})}function i(u,d){X(()=>{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=t(p,h);n.set(p)})}async function c(u,d){s(u);try{let f=await d();return a(u,f),f}catch(f){throw i(u),f}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:s,resolve:a,rollback:i,withOptimistic:c,set:u=>{n.set(u),o.set(u)}}}var P=new Map;function je(e,t){return P.has(e)||P.set(e,new Set),P.get(e).add(t),()=>{P.get(e)?.delete(t)}}function ye(e){let t=P.get(e);if(t)for(let n of t)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function H(e,t,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=e?.headers?.["x-csrf-token"]||e?.headers?.["X-CSRF-Token"];if(!le(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=Y.get(t);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 z(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 Me(){return[...Y.keys()]}function Fe(e,t={}){let{onSuccess:n,onError:r,onSettled:o}=t,s={isPending:_(!1),error:_(null),data:_(null)};async function a(...i){s.isPending.set(!0),s.error.set(null);try{let c=await e(...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)}}}import{h as L}from"what-core";var me="/__what_action";function Ue(e){if(typeof e=="string")return e;if(typeof e=="function"){let t=e._actionId;if(t)return t;throw new Error("[what] <Form action={fn}>: that function is not a server action. Wrap it with action() from what-server, or pass the action id as a string.")}throw new Error("[what] <Form> requires an `action` prop: a server action or its id.")}function Be(){if(typeof document>"u")return null;let e=document.querySelector('meta[name="what-csrf-token"]');if(e)return e.getAttribute("content");let t=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);return t?decodeURIComponent(t[1]):null}function Je({action:e,csrfToken:t,redirect:n,method:r="post",enhance:o=!0,children:s,...a}){let i=Ue(e),c=t!==void 0?t:Be();c==null&&typeof process<"u";let u=[L("input",{type:"hidden",name:"_action",value:i}),L("input",{type:"hidden",name:"what-csrf-token",value:c||""})];n&&u.push(L("input",{type:"hidden",name:"_redirect",value:n}));let d={...a,method:r,action:a.action||me,"data-action":i};o&&(d["data-enhance"]="");let f=s==null?[]:Array.isArray(s)?s:[s];return L("form",d,...u,...f)}var Ve="/__what_action",we=1024*1024;function We(e){if(!e)return{};if(typeof e.forEach=="function"&&typeof e.get=="function"){let n={};return e.forEach((r,o)=>{n[o.toLowerCase()]=r}),n}let t={};for(let n in e)t[n.toLowerCase()]=e[n];return t}function D(e,t){return{status:e,headers:{"content-type":"application/json"},body:JSON.stringify(t)}}function G(e,t){return{status:e,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${e}</h1><p>${String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</p></body></html>`}}function ge(e){if(typeof e!="string"||!e.startsWith("/")||/^[/\\]{2}/.test(e)||e.includes("\\"))return null;try{let t=new URL(e,"http://localhost");return t.origin!=="http://localhost"?null:t.pathname+t.search}catch{return null}}function ze(e,t){let n=ge(e&&e._redirect);if(n)return n;let r=t.referer||t.referrer;if(r)try{let o=new URL(r,"http://localhost"),s=ge(o.pathname+o.search);if(s)return s}catch{}return"/"}var Xe=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function I(e={}){let{getCsrfToken:t,skipCsrf:n=!1}=e;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return D(405,{message:"Method Not Allowed"});let a=We(o.headers),i=a["x-what-action"],c=a["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:t?await t(o):void 0;if(u){let h=o.body||{},w=h._action||h["data-action"]||o.query&&o.query.action;if(!w)return G(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let b={...a},C=h._csrf??h["what-csrf-token"];if(C&&!b["x-csrf-token"]&&(b["x-csrf-token"]=String(C)),!n&&t&&!d)return G(403,"Missing CSRF token");let v={};for(let[O,U]of Object.entries(h))Xe.has(O)||(v[O]=U);let E=await H({headers:b},w,[v],{csrfToken:d,skipCsrf:n});return E.status===200?{status:303,headers:{location:ze(h,a)},body:""}:G(E.status,E.body&&E.body.message||"Action failed")}if(!i)return D(400,{message:"Missing X-What-Action header"});if(!n&&t&&!d)return D(403,{message:"Missing CSRF token"});let l=(o.body||{}).args,p=await H({headers:a},i,l,{csrfToken:d,skipCsrf:n});return D(p.status,p.body)}}function Ye(e={}){let t=e.basePath||Ve,n=I(e);return async function(o,s,a){let[i,c]=(o.url||"").split("?");if(i!==t||(o.method||"").toUpperCase()!=="POST")return a?a():void 0;let u;try{let l=await Ke(o);u=j(l,o.headers["content-type"]||"")}catch(l){s.writeHead(l.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),s.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});s.writeHead(f.status,f.headers),s.end(f.body)}}function j(e,t){if((t||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(e)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return e==null||e===""?{}:JSON.parse(String(e))}async function M(e,t=we){let n=Number(e.headers.get("content-length"));if(Number.isFinite(n)&&n>t)return{tooLarge:!0};let r=e.body;if(!r||typeof r.getReader!="function"){let i=await e.text();return Buffer.byteLength(i,"utf8")>t?{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>t){try{await o.cancel()}catch{}return{tooLarge:!0}}s.push(c)}}return{raw:Buffer.concat(s.map(i=>Buffer.from(i))).toString("utf8")}}function Ke(e){return new Promise((t,n)=>{let r=0,o=[];e.on("data",s=>{if(r+=s.length,r>we){let a=new Error("Body too large");a.code="BODY_TOO_LARGE",n(a),e.destroy?.();return}o.push(s)}),e.on("end",()=>{if(o.length===0)return t("");t(Buffer.concat(o).toString("utf8"))}),e.on("error",n)})}function Ze(e={}){let t=I(e);return async function(n){let r={};try{let a=await M(n);if(a.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=j(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 t({method:n.method,headers:n.headers,body:r,query:o});return new Response(s.body,{status:s.status,headers:s.headers})}}import{matchRoute as Ge,parseQuery as Qe}from"what-router/match";var qe="/__what_action",et="/__what_revalidate",Q="what-csrf";function N(e){let t={};return e&&typeof e.forEach=="function"&&e.forEach((n,r)=>{t[r.toLowerCase()]=n}),t}function Se(e,t){if(!e)return null;let n=String(e).match(new RegExp(`(?:^|;\\s*)${t}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function tt(e){try{let t=await M(e);return t.tooLarge?{tooLarge:!0}:j(t.raw,e.headers.get("content-type")||"")}catch{return{}}}async function nt(e){let t;try{t=await M(e)}catch{return{json:{}}}if(t.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(t.raw)}}catch{return{json:{}}}}function rt(e){return async function(n){let{route:r,params:o,query:s,request:a}=n,i={default:r.component,loader:r.loader},c=n.csrfToken?{...e,csrfToken:n.csrfToken}:e;return{html:await be(i,{params:o,query:s,request:a},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function q(e={}){let{routes:t=[],cache:n,render:r,revalidateWebhook:o,document:s={},notFound:a,basePath:i="",csrf:c=!0}=e,u=c!==!1&&!e.actionHandler,d=e.actionHandler||I(u?{getCsrfToken:l=>Se(l.headers&&l.headers.cookie,Q)}:{skipCsrf:!0}),f=r||rt(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"),w=h.pathname;if(i&&w.startsWith(i)&&(w=w.slice(i.length)||"/"),p.method==="POST"&&w===qe){let y=await tt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await d({method:"POST",headers:N(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(A.body,{status:A.status,headers:A.headers})}let b=null,C=null;if(u&&(b=Se(N(p.headers).cookie,Q),!b)){b=K();let A=N(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;C=`${Q}=${encodeURIComponent(b)}; Path=/; SameSite=Lax`+(A?"; Secure":"")}let v=y=>(C&&(y["set-cookie"]=C),y);if(p.method==="POST"&&w===et&&o){let y=await nt(p);if(y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await o({headers:N(p.headers),body:y.json});return new Response(JSON.stringify(A.body),{status:A.status,headers:{"content-type":"application/json"}})}let E=Ge(w,t);if(!E){let y=a?a():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(y,{status:404,headers:v({"content-type":"text/html; charset=utf-8"})})}let{route:O,params:U}=E,B=O.page||{mode:O.mode||"client"},x={path:w,query:Qe(h.search),config:B,route:O,params:U,request:p};if(n&&B.mode!=="server"){x.varyHeaders=N(p.headers);let y=await n.handle(x,()=>f(x));return new Response(y.html,{status:y.status||200,headers:v({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}b&&(x.csrfToken=b);let ce=await f(x),ue=v({"content-type":"text/html; charset=utf-8"});return B.mode==="server"&&(ue["Cache-Control"]="private, no-store"),new Response(ce.html,{status:ce.status||200,headers:ue})}}function ot(e={}){let t=q(e);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),t(n)}}}function T(e){return{head:st(),loaderData:e,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var ke=0;function it(){ke=0}function ct(){return"h"+ke++}function ut(e){if(!Ae()){let t=T(void 0);return S(t,()=>ut(e))}return it(),R(e)}function R(e){if(e==null||e===!1||e===!0)return"";if(typeof e=="string"||typeof e=="number")return g(String(e));if(typeof e=="function"&&e._signal)return`<!--$-->${R(e())}<!--/$-->`;if(typeof e=="function"&&e._mapArray)try{return`<!--$-->${R(ne(e))}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(typeof e=="function")try{return`<!--$-->${R(e())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(e))return`<!--[]-->${e.map(R).join("")}<!--/[]-->`;if(typeof e.tag=="function"){let c=ct(),u=ee(e.tag),d;try{d=R(e.tag({...e.props,children:e.children}))}finally{te(u)}return dt(d,c)}let{tag:t,props:n,children:r}=e;ae(t);let o=se(n||{}),s=`<${t}${o}>`;if(ie.has(t))return s;let a=oe(n),i=a!=null?String(a):r.map(R).join("");return`${s}${i}</${t}>`}function dt(e,t){let n=e.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],s=r.length+1+o.length;return e.slice(0,s)+` data-hk="${t}"`+e.slice(s)}return e}function m(e){if(!Ae()){let c=T(void 0);return S(c,()=>m(e))}if(e==null||e===!1||e===!0)return"";if(typeof e=="string"||typeof e=="number")return g(String(e));if(typeof e=="function"&&e._signal)return m(e());if(typeof e=="function"&&e._mapArray)try{return m(ne(e))}catch{return typeof process<"u",""}if(typeof e=="function")try{return m(e())}catch{return typeof process<"u",""}if(Array.isArray(e))return e.map(m).join("");if(e.tag==="__suspense")try{return(e.children||[]).map(m).join("")}catch(c){if(c&&typeof c.then=="function")return m(e.props&&e.props.fallback);throw c}if(typeof e.tag=="function"){let c=ee(e.tag);try{return m(e.tag({...e.props,children:e.children}))}finally{te(c)}}let{tag:t,props:n,children:r}=e;ae(t);let o=se(n||{}),s=`<${t}${o}>`;if(ie.has(t))return s;let a=oe(n),i=a!=null?String(a):r.map(m).join("");return`${s}${i}</${t}>`}function Xt(e){let t=T(void 0);return{body:S(t,()=>m(e)),head:re(t.head)}}async function Yt(e,t={}){let n=e.default||e,r=typeof e.loader=="function"?await e.loader(t):void 0,o=T(r),s=t.params||{};return{body:S(o,()=>m(_e(n,{...s,loaderData:r}))),head:re(o.head),loaderData:r}}var Te=12;async function ft(e,t){t||(t=T(void 0));let n="";for(let o=0;o<Te;o++){n=S(t,()=>m(e));let s=[...t.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 t.resources)s.status==="ready"&&(r[o]=s.value);return{body:n,head:re(t.head),loaderData:t.loaderData,resources:r,ctx:t}}async function be(e,t={},n={}){let r=e.default||e,o=typeof e.loader=="function"?await e.loader(t):void 0,s=T(o),a=t.params||{},{body:i,head:c,resources:u}=await ft(_e(r,{...a,loaderData:o}),s),d={loaderData:o??null,resources:u,islandStores:de(s)};return lt({body:i,head:c,payload:d,options:n})}function lt({body:e,head:t,payload:n,options:r={}}){let o=r.lang||"en",s=`<script id="__what_data" type="application/json">${J(n)}<\/script>`,a=r.clientEntry?`<script type="module" src="${g(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?Z(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">${t||""}${c}</head><body${u}>${e}${s}${a}</body></html>`}async function*$(e,t){if(t===void 0&&(t=T(void 0)),e==null||e===!1||e===!0)return;if(typeof e=="string"||typeof e=="number"){yield g(String(e));return}if(typeof e=="function"&&e._signal){let a=S(t,()=>e());yield*$(a,t);return}if(typeof e=="function"&&e._mapArray){let a=null;try{a=S(t,()=>ne(e))}catch{typeof process<"u"}a&&(yield*$(a,t));return}if(typeof e=="function"){try{let a=S(t,()=>e());yield*$(a,t)}catch{typeof process<"u"}return}if(Array.isArray(e)){for(let a of e)yield*$(a,t);return}if(e.tag==="__suspense"){let a=null;for(let i=0;i<Te&&a===null;i++){let c=null;try{a=S(t,()=>(e.children||[]).map(m).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(a===null){let u=[...t.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}a===null&&(a=S(t,()=>m(e.props&&e.props.fallback))),yield a;return}if(typeof e.tag=="function"){let a=ee(e.tag);try{let i=S(t,()=>e.tag({...e.props,children:e.children})),c=i instanceof Promise?await i:i;yield*$(c,t)}catch(i){typeof process<"u",yield F?`<!-- SSR Error: ${g(i.message||"Component error")} -->`:"<!-- SSR Error -->"}finally{te(a)}return}let{tag:n,props:r,children:o}=e;ae(n);let s=se(r||{});if(yield`<${n}${s}>`,!ie.has(n)){let a=oe(r);if(a!=null)yield String(a);else for(let i of o)yield*$(i,t);yield`</${n}>`}}function Kt(e){return{mode:"static",...e}}function Zt(e,t={}){let n=T(t),r=S(n,()=>{let s=e.component(t);return m(s)}),o=e.islands||[];return pt({title:e.title||"",meta:e.meta||{},body:r,islands:o,scripts:e.mode==="static"?[]:e.scripts||[],styles:e.styles||[],mode:e.mode})}function pt({title:e,meta:t,body:n,islands:r,scripts:o,styles:s,mode:a}){let i=Object.entries(t).map(([l,p])=>`<meta name="${g(l)}" content="${g(p)}">`).join(`
2
+ `),c=s.map(l=>`<link rel="stylesheet" href="${g(l)}">`).join(`
3
+ `),u=r.length>0?`
4
4
  <script type="module">
5
5
  import { hydrateIslands } from '/@what/islands.js';
6
6
  hydrateIslands();
7
- <\/script>`:"",u=o.map(l=>`<script type="module" src="${m(l)}"><\/script>`).join(`
7
+ <\/script>`:"",d=o.map(l=>`<script type="module" src="${g(l)}"><\/script>`).join(`
8
8
  `),f=a==="client"?`
9
9
  <script type="module" src="/@what/client.js"><\/script>`:"";return`<!DOCTYPE html>
10
10
  <html lang="en">
@@ -12,13 +12,13 @@ import{h as wt,_isAriaAttr as ne,getServerContext as St,runWithServerContext as
12
12
  <meta charset="UTF-8">
13
13
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
14
  ${i}
15
- <title>${m(t)}</title>
15
+ <title>${g(e)}</title>
16
16
  ${c}
17
17
  </head>
18
18
  <body>
19
19
  <div id="app">${n}</div>
20
- ${d}
21
20
  ${u}
21
+ ${d}
22
22
  ${f}
23
23
  </body>
24
- </html>`}function Ye(t){return t._server=!0,t}var F=!(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"&&F&&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 fe=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/,le=/^[a-zA-Z_][a-zA-Z0-9._:-]*$/;function nt(t){if(typeof t!="string"||!le.test(t))throw new Error(`[what-server] Invalid tag name in SSR: ${JSON.stringify(t)}`)}function rt(t){let e="";for(let[n,r]of Object.entries(t)){if(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")continue;let o=n.toLowerCase();if(!(o.startsWith("on")&&n.length>2)){if(r===!1&&ne(n)){e+=` ${n}="false"`;continue}if(!(r===!1||r==null)){if(!fe.test(n)){F&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(ye.has(o)){F&&console.warn(`[what-server] Skipping unsafe attribute in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")e+=` class="${m(String(r))}"`;else if(n==="style"&&typeof r=="object"){let s=Object.entries(r).map(([a,i])=>`${me(a)}:${i}`).join(";");e+=` style="${m(s)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?e+=` ${n}="true"`:e+=` ${n}`;else{if(pe(n,r))continue;e+=` ${n}="${m(String(r))}"`}}}}return e}function pe(t,e){let n=t.toLowerCase();if(!he.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 he=new Set(["href","src","action","formaction","data","ping","xlink:href"]),ye=new Set(["srcdoc"]);function m(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function me(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_ENDPOINT,Ft as Form,$t as action,$ as createActionHandler,ee as createCloudflareHandler,q as createRequestHandler,Z as csrfMetaTag,ze as definePage,Xt as fetchActionHandler,ut as formAction,K as generateCsrfToken,Xe as generateStaticPage,Dt as getRegisteredActions,Ct as getRevalidationHandler,N as handleActionRequest,lt as invalidatePath,Vt as nodeActionMiddleware,Nt as onRevalidate,gt as renderDocument,Ve as renderPage,se as renderToHydratableString,H as renderToStream,w as renderToString,ce as renderToStringAsync,We as renderToStringWithHead,V as revalidatePath,z as revalidateTag,J as serializeState,Ye as server,W as setRevalidationHandler,ft as useAction,It as useFormAction,jt as useMutation,Ht as useOptimistic,dt as validateCsrfToken};
24
+ </html>`}function Gt(e){return e._server=!0,e}var F=!(typeof process<"u");function oe(e){return e?e.dangerouslySetInnerHTML?e.dangerouslySetInnerHTML.__html??null:e.innerHTML&&typeof e.innerHTML=="object"&&"__html"in e.innerHTML?e.innerHTML.__html??null:(e.innerHTML!=null&&typeof e.innerHTML=="string"&&F&&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 ht=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/,yt=/^[a-zA-Z_][a-zA-Z0-9._:-]*$/;function ae(e){if(typeof e!="string"||!yt.test(e))throw new Error(`[what-server] Invalid tag name in SSR: ${JSON.stringify(e)}`)}function se(e){let t="";for(let[n,r]of Object.entries(e)){if(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")continue;let o=n.toLowerCase();if(!(o.startsWith("on")&&n.length>2)){if(r===!1&&at(n)){t+=` ${n}="false"`;continue}if(!(r===!1||r==null)){if(!ht.test(n)){F&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(wt.has(o)){F&&console.warn(`[what-server] Skipping unsafe attribute in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")t+=` class="${g(String(r))}"`;else if(n==="style"&&typeof r=="object"){let s=Object.entries(r).map(([a,i])=>`${St(a)}:${i}`).join(";");t+=` style="${g(s)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?t+=` ${n}="true"`:t+=` ${n}`;else{if(mt(n,r))continue;t+=` ${n}="${g(String(r))}"`}}}}return t}function mt(e,t){let n=e.toLowerCase();if(!gt.has(n))return!1;let r=String(t).trim().replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();return r.startsWith("javascript:")||r.startsWith("vbscript:")||r.startsWith("data:")}var gt=new Set(["href","src","action","formaction","data","ping","xlink:href"]),wt=new Set(["srcdoc"]);function g(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function St(e){return e.startsWith("--")?e:e.replace(/([A-Z])/g,"-$1").toLowerCase()}var ie=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);export{me as ACTION_ENDPOINT,Je as Form,He as action,I as createActionHandler,ot as createCloudflareHandler,q as createRequestHandler,Z as csrfMetaTag,Kt as definePage,Ze as fetchActionHandler,pe as formAction,K as generateCsrfToken,Zt as generateStaticPage,Me as getRegisteredActions,$e as getRevalidationHandler,H as handleActionRequest,ye as invalidatePath,Ye as nodeActionMiddleware,je as onRevalidate,be as renderDocument,Yt as renderPage,ut as renderToHydratableString,$ as renderToStream,m as renderToString,ft as renderToStringAsync,Xt as renderToStringWithHead,W as revalidatePath,z as revalidateTag,J as serializeState,Gt as server,V as setRevalidationHandler,he as useAction,Le as useFormAction,Fe as useMutation,De as useOptimistic,le as validateCsrfToken};
@@ -1 +1,2 @@
1
- import{mount as P,hydrate as R,signal as q,batch as E,getServerContext as N}from"what-core";var _=new RegExp("[<>&\\u2028\\u2029]","g"),A={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function x(e){return JSON.stringify(e).replace(_,r=>A[r.charCodeAt(0)])}var S=new Map,b=new Set,f=[],v=!1,w=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||N();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(w,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(w,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"?w: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 F(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",d=s??a?.priority??0,c=o||a?.stores||[];return{tag:"div",props:{"data-island":e,"data-island-mode":i,"data-island-props":JSON.stringify(r),"data-island-priority":d,"data-island-stores":JSON.stringify(c)},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(v||f.length===0)return;v=!0;let e=f.shift();Promise.resolve(e.hydrate()).catch(r=>console.error("[what] Island hydration failed:",e.name,r)).finally(()=>{v=!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.getElementById("__what_data");if(e)try{let t=JSON.parse(e.textContent||"{}");t&&t.islandStores&&D(t.islandStores)}catch(t){console.warn("[what] Failed to parse hydration payload:",t)}let r=document.querySelectorAll("[data-island]");for(let t of r){if(t.dataset.islandSelf)continue;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||"[]"),d=S.get(n);if(!d){console.warn(`[what] Island "${n}" not registered`);continue}b.has(t)||J(t,d,o,s,a,n,i)}}function J(e,r,t,n,s,o,a){let i=async()=>{if(b.has(e))return;b.add(e);let d=await r.loader(),c=d.default||d,u={};for(let k of a)u[k]=O(k);let m=c({...t,...u});e.childNodes.length>0?R(m,e):P(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((c,u)=>{for(let m of c)if(m.isIntersecting){u.disconnect(),l({name:o,priority:s,hydrate:i});break}},{rootMargin:"200px"}).observe(e);break}case"media":{let d=window.matchMedia(r.media||"(max-width: 768px)");d.matches?l({name:o,priority:s,hydrate:i}):d.addEventListener("change",c=>{c.matches&&l({name:o,priority:s,hydrate:i})},{once:!0});break}case"action":{let d=["click","focus","mouseover","touchstart"],c=()=>{d.forEach(u=>e.removeEventListener(u,c)),l({name:o,priority:s+500,hydrate:i})};d.forEach(u=>e.addEventListener(u,c,{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))F(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 B(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,d=r.getAttribute("data-no-csrf")==="true";if(!i&&!d){console.warn('[what] Form submission blocked: no CSRF token found. Add a <meta name="what-csrf-token"> tag (csrfMetaTag() emits it) 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 c={"X-Requested-With":"XMLHttpRequest"};i&&(c["X-CSRF-Token"]=i);let u=await fetch(o,{method:s,body:s==="GET"?void 0:n,headers:c});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 G(){let e=typeof document<"u"?[...w.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,B as enhanceForms,G as getIslandStatus,j as getIslandStoresSnapshot,D as hydrateIslandStores,C as hydrateIslands,F as island,z as serializeIslandStores,O as useIslandStore};
1
+ import{mount as L,hydrate as N,signal as T,batch as O,getServerContext as j}from"what-core";var q=new RegExp("[<>&\\u2028\\u2029]","g"),F={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function I(e){return JSON.stringify(e).replace(q,n=>F[n.charCodeAt(0)])}var x=new Map,v=new Set,f=[],C=!1,E=new Map,g=new Map;function k(e){if(typeof structuredClone=="function")try{return structuredClone(e)}catch{}return Array.isArray(e)?e.map(k):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([n,t])=>[n,k(t)])):e}function b(e,n,t){if(e.has(n))return e.get(n);let r={},s={};for(let[o,a]of Object.entries(k(t)))s[o]=T(a),Object.defineProperty(r,o,{get:()=>s[o](),set:i=>s[o].set(i),enumerable:!0});return r._signals=s,r._subscribe=(o,a)=>{if(s[o])return s[o].subscribe(a)},r._batch=o=>O(o),r._getSnapshot=()=>{let o={};for(let[a,i]of Object.entries(s))o[a]=i.peek();return o},r._hydrate=o=>{O(()=>{for(let[a,i]of Object.entries(o))s[a]&&s[a].set(i)})},e.set(n,r),r}function w(e){let n=e||j();return n&&n.islandStores instanceof Map?n.islandStores:null}function M(e,n){if(g.has(e))return g.get(e).handle;let t={initialState:k(n),handle:null},r=()=>{let s=w();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 b(s,e,t.initialState)};return t.handle=new Proxy({},{get(s,o){return Reflect.get(r(),o)},set(s,o,a){return Reflect.set(r(),o,a)},has(s,o){return Reflect.has(r(),o)},ownKeys(){return Reflect.ownKeys(r())},getOwnPropertyDescriptor(s,o){let a=Reflect.getOwnPropertyDescriptor(r(),o);return a?{...a,configurable:!0}:void 0}}),g.set(e,t),t.handle}function W(e,n){if(typeof document<"u")return b(E,e,n);let t=w();if(t){let r=g.get(e);return b(t,e,r?.initialState??n)}return M(e,n)}function R(e,n={}){if(typeof document<"u")return b(E,e,n);let t=w();if(t){let r=g.get(e);return b(t,e,r?.initialState??n)}return M(e,n)}function V(){return I(D())}function D(e){let n=typeof document<"u"?E:w(e),t={};for(let[r,s]of n||[])t[r]=s._getSnapshot();return t}function J(e){try{let n=typeof e=="string"?JSON.parse(e):e;for(let[t,r]of Object.entries(n))R(t,r)._hydrate(r)}catch(n){console.warn("[what] Failed to hydrate island stores:",n)}}function U(e,n,t={}){x.set(e,{loader:n,mode:t.mode||"idle",media:t.media||null,priority:t.priority||0,stores:t.stores||[]})}function Y({name:e,props:n={},children:t,mode:r,priority:s,stores:o}){let a=x.get(e),i=r||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(n),"data-island-priority":c,"data-island-stores":JSON.stringify(d)},children:t||[],key:null,_vnode:!0}}function h(e){let n=!1;for(let t=0;t<f.length;t++)if(e.priority>f[t].priority){f.splice(t,0,e),n=!0;break}n||f.push(e),_()}function _(){if(C||f.length===0)return;C=!0;let e=f.shift();Promise.resolve(e.hydrate()).catch(n=>console.error("[what] Island hydration failed:",e.name,n)).finally(()=>{C=!1,queueMicrotask(_)})}function Z(e,n=100){for(let t of f)if(t.name===e){t.priority=n,f.sort((r,s)=>s.priority-r.priority);break}}function A(){let e=document.getElementById("__what_data");if(e)try{let t=JSON.parse(e.textContent||"{}");t&&t.islandStores&&J(t.islandStores)}catch(t){console.warn("[what] Failed to parse hydration payload:",t)}let n=document.querySelectorAll("[data-island]");for(let t of n){if(t.dataset.islandSelf)continue;let r=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=x.get(r);if(!c){console.warn(`[what] Island "${r}" not registered`);continue}v.has(t)||H(t,c,o,s,a,r,i)}}function H(e,n,t,r,s,o,a){let i=async()=>{if(v.has(e))return;v.add(e);let c=await n.loader(),d=c.default||c,u={};for(let y of a)u[y]=R(y);let p=d({...t,...u});e.childNodes.length>0?N(p,e):L(p,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:r}}))};switch(r){case"load":h({name:o,priority:s+1e3,hydrate:i});break;case"idle":"requestIdleCallback"in window?requestIdleCallback(()=>{h({name:o,priority:s,hydrate:i})}):setTimeout(()=>{h({name:o,priority:s,hydrate:i})},200);break;case"visible":{new IntersectionObserver((d,u)=>{for(let p of d)if(p.isIntersecting){u.disconnect(),h({name:o,priority:s,hydrate:i});break}},{rootMargin:"200px"}).observe(e);break}case"media":{let c=window.matchMedia(n.media||"(max-width: 768px)");c.matches?h({name:o,priority:s,hydrate:i}):c.addEventListener("change",d=>{d.matches&&h({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)),h({name:o,priority:s+500,hydrate:i})};c.forEach(u=>e.addEventListener(u,d,{once:!0,passive:!0}));break}case"static":break;default:h({name:o,priority:s,hydrate:i})}}function ee(e){for(let[n,t]of Object.entries(e))U(n,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",A):A())}function $(e,n){if(typeof document>"u")return;let t=document.querySelectorAll(e);for(let r of t)r.dataset.enhanced||(r.dataset.enhanced="true",n(r))}function Q(e){let n=document.querySelector('meta[name="csrf-token"]')||document.querySelector('meta[name="what-csrf-token"]');if(n)return n.getAttribute("content");let t=e.querySelector('input[name="what-csrf-token"], input[name="_csrf"]');if(t&&t.value)return t.value;let r=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);return r?decodeURIComponent(r[1]):null}function X(e,n,t){let r=new FormData(e);if(n&&n.name&&r.append(n.name,n.value??""),t)return r;let s=new URLSearchParams,o=null;for(let[a,i]of r)typeof i=="string"?s.append(a,i.replace(/\r\n|\r|\n/g,`\r
2
+ `)):(o=o||a,s.append(a,i.name??""));return o&&typeof console<"u"&&console.warn(`[what] Form field "${o}" holds a file, but the form is not enctype="multipart/form-data", so only the file NAME is sent. This is what a plain HTML submit does too. Add enctype="multipart/form-data" to upload the bytes.`),s}function te(e="form[data-enhance]"){$(e,n=>{n.addEventListener("submit",async t=>{t.preventDefault();try{let r=t.submitter||null,s=l=>r&&r.getAttribute(`form${l}`)||n.getAttribute(l),o=(s("method")||"get").toUpperCase(),a=new URL(s("action")||location.href,location.href),c=(s("enctype")||"").toLowerCase()==="multipart/form-data",d=X(n,r,c),u=a.origin===location.origin,p={"X-Requested-With":"XMLHttpRequest"};if(u){let l=Q(n),S=n.getAttribute("data-no-csrf")==="true";if(!l&&!S){console.warn('[what] Form submission blocked: no CSRF token found. Add a <meta name="what-csrf-token"> tag (csrfMetaTag() emits it) or set data-no-csrf="true" on the form to opt out.'),n.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:new Error("Missing CSRF token")}}));return}l&&(p["X-CSRF-Token"]=l)}let y;o==="GET"?a.search=String(d):c?y=d:(y=d,p["Content-Type"]="application/x-www-form-urlencoded;charset=UTF-8");let m=await fetch(a.href,{method:o,body:y,headers:p,credentials:"same-origin"}),P=new CustomEvent("form:response",{bubbles:!0,cancelable:!0,detail:{response:m,ok:m.ok,redirected:m.redirected}});if(n.dispatchEvent(P)&&m.ok&&m.redirected){let l=null;try{let S=new URL(m.url,location.href);S.origin===location.origin&&(l=S.href)}catch{}l&&location.assign(l)}}catch(r){n.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:r}}))}})})}function ne(){let e=typeof document<"u"?[...E.keys()]:[...w()?.keys()||g.keys()];return{registered:[...x.keys()],hydrated:v.size,pending:f.length,queue:f.map(t=>({name:t.name,priority:t.priority})),stores:e}}export{Y as Island,ee as autoIslands,Z as boostIslandPriority,W as createIslandStore,$ as enhance,te as enhanceForms,ne as getIslandStatus,D as getIslandStoresSnapshot,J as hydrateIslandStores,A as hydrateIslands,U as island,V as serializeIslandStores,R as useIslandStore};
package/dist/node.min.js CHANGED
@@ -1,24 +1,24 @@
1
- import{AsyncLocalStorage as Le}from"node:async_hooks";import{__installServerContextStorage as De}from"what-core";import{h as _t,_isAriaAttr as de,getServerContext as At,runWithServerContext as b,beginHeadCollection as fe,endHeadCollection as rt}from"what-core";var xt=new RegExp("[<>&\\u2028\\u2029]","g"),Pt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function $(t){return JSON.stringify(t).replace(xt,e=>Pt[e.charCodeAt(0)])}import{mount as Ue,hydrate as Be,signal as Je,batch as Ve,getServerContext as $t}from"what-core";var Ht=new Map;function It(t){let e=t||$t();return e&&e.islandStores instanceof Map?e.islandStores:null}function lt(t){let e=typeof document<"u"?Ht:It(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as _,batch as Z}from"what-core";var T=null,pt=!(typeof process<"u");function X(t){T=t}function Nt(){return T}async function G(t,e){if(T&&T.revalidatePath)return T.revalidatePath(t,e);pt&&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 K(t,e){if(T&&T.revalidateTag)return T.revalidateTag(t,e);pt&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var Q=new Map;function jt(){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 q(){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 ht(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 tt(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Lt=0,Dt=!1;function Mt(){return!Dt&&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${(++Lt).toString(36)}_${Date.now().toString(36)}`}`}function Ft(t,e={}){let n=e.id||Mt(),{onError:r,onSuccess:o,revalidate:a}=e;typeof window>"u"&&Q.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=jt(),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 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)gt(m);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 mt(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 yt(t){let e=_(!1),n=_(null),r=_(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 Ut(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=yt(mt(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 Bt(t,e){let n=_(t),r=_([]),o=_(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(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){Z(()=>{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 H=new Map;function Jt(t,e){return H.has(t)||H.set(t,new Set),H.get(t).add(e),()=>{H.get(t)?.delete(e)}}function gt(t){let e=H.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: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(!ht(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let s=Q.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 G(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await K(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 Vt(){return[...Q.keys()]}function Wt(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,a={isPending:_(!1),error:_(null),data:_(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)}}}import{h as M}from"what-core";var wt="/__what_action";function zt(t){if(typeof t=="string")return t;if(typeof t=="function"){let e=t._actionId;if(e)return e;throw new Error("[what] <Form action={fn}>: that function is not a server action. Wrap it with action() from what-server, or pass the action id as a string.")}throw new Error("[what] <Form> requires an `action` prop: a server action or its id.")}function Yt(){if(typeof document>"u")return null;let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);return e?decodeURIComponent(e[1]):null}function Xt({action:t,csrfToken:e,redirect:n,method:r="post",enhance:o=!0,children:a,...s}){let i=zt(t),c=e!==void 0?e:Yt();c==null&&typeof process<"u";let u=[M("input",{type:"hidden",name:"_action",value:i}),M("input",{type:"hidden",name:"what-csrf-token",value:c||""})];n&&u.push(M("input",{type:"hidden",name:"_redirect",value:n}));let d={...s,method:r,action:s.action||wt,"data-action":i};o&&(d["data-enhance"]="");let f=a==null?[]:Array.isArray(a)?a:[a];return M("form",d,...u,...f)}var Gt="/__what_action",I=1024*1024;function Kt(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 F(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function et(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 St(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 Zt(t,e){let n=St(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),a=St(o.pathname+o.search);if(a)return a}catch{}return"/"}var Qt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function N(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return F(405,{message:"Method Not Allowed"});let s=Kt(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 et(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let S={...s},E=h._csrf??h["what-csrf-token"];if(E&&!S["x-csrf-token"]&&(S["x-csrf-token"]=String(E)),!n&&e&&!d)return et(403,"Missing CSRF token");let C={};for(let[O,z]of Object.entries(h))Qt.has(O)||(C[O]=z);let v=await D({headers:S},m,[C],{csrfToken:d,skipCsrf:n});return v.status===200?{status:303,headers:{location:Zt(h,s)},body:""}:et(v.status,v.body&&v.body.message||"Action failed")}if(!i)return F(400,{message:"Missing X-What-Action header"});if(!n&&e&&!d)return F(403,{message:"Missing CSRF token"});let l=(o.body||{}).args,p=await D({headers:s},i,l,{csrfToken:d,skipCsrf:n});return F(p.status,p.body)}}function qt(t={}){let e=t.basePath||Gt,n=N(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 te(o);u=U(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 U(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 B(t,e=I){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 te(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",a=>{if(r+=a.length,r>I){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 ee(t={}){let e=N(t);return async function(n){let r={};try{let s=await B(n);if(s.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=U(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 ne,parseQuery as re}from"what-router/match";var oe="/__what_action",ae="/__what_revalidate",nt="what-csrf";function j(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function bt(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function se(t){try{let e=await B(t);return e.tooLarge?{tooLarge:!0}:U(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function ie(t){let e;try{e=await B(t)}catch{return{json:{}}}if(e.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(e.raw)}}catch{return{json:{}}}}function ce(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 J(i,{params:o,query:a,request:s},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function R(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||N(u?{getCsrfToken:l=>bt(l.headers&&l.headers.cookie,nt)}:{skipCsrf:!0}),f=r||ce(a);return n&&(n.revalidatePath||n.revalidateTag)&&X({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===oe){let y=await se(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await d({method:"POST",headers:j(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(A.body,{status:A.status,headers:A.headers})}let S=null,E=null;if(u&&(S=bt(j(p.headers).cookie,nt),!S)){S=q();let A=j(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;E=`${nt}=${encodeURIComponent(S)}; Path=/; SameSite=Lax`+(A?"; Secure":"")}let C=y=>(E&&(y["set-cookie"]=E),y);if(p.method==="POST"&&m===ae&&o){let y=await ie(p);if(y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await o({headers:j(p.headers),body:y.json});return new Response(JSON.stringify(A.body),{status:A.status,headers:{"content-type":"application/json"}})}let v=ne(m,e);if(!v){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:O,params:z}=v,Y=O.page||{mode:O.mode||"client"},P={path:m,query:re(h.search),config:Y,route:O,params:z,request:p};if(n&&Y.mode!=="server"){P.varyHeaders=j(p.headers);let y=await n.handle(P,()=>f(P));return new Response(y.html,{status:y.status||200,headers:C({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}S&&(P.csrfToken=S);let dt=await f(P),ft=C({"content-type":"text/html; charset=utf-8"});return Y.mode==="server"&&(ft["Cache-Control"]="private, no-store"),new Response(dt.html,{status:dt.status||200,headers:ft})}}function ue(t={}){let e=R(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function k(t){return{head:fe(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var Tt=0;function le(){Tt=0}function pe(){return"h"+Tt++}function he(t){if(!At()){let e=k(void 0);return b(e,()=>he(t))}return le(),x(t)}function x(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`<!--$-->${x(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${x(t())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${t.map(x).join("")}<!--/[]-->`;if(typeof t.tag=="function"){let c=pe(),u=t.tag({...t.props,children:t.children}),d=x(u);return me(d,c)}let{tag:e,props:n,children:r}=t;at(e);let o=st(n||{}),a=`<${e}${o}>`;if(it.has(e))return a;let s=ot(n),i=s!=null?String(s):r.map(x).join("");return`${a}${i}</${e}>`}function me(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(!At()){let c=k(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;at(e);let o=st(n||{}),a=`<${e}${o}>`;if(it.has(e))return a;let s=ot(n),i=s!=null?String(s):r.map(w).join("");return`${a}${i}</${e}>`}function pn(t){let e=k(void 0);return{body:b(e,()=>w(t)),head:rt(e.head)}}async function hn(t,e={}){let n=t.default||t,r=typeof t.loader=="function"?await t.loader(e):void 0,o=k(r),a=e.params||{};return{body:b(o,()=>w(_t(n,{...a,loaderData:r}))),head:rt(o.head),loaderData:r}}var Rt=12;async function ye(t,e){e||(e=k(void 0));let n="";for(let o=0;o<Rt;o++){n=b(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:rt(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function J(t,e={},n={}){let r=t.default||t,o=typeof t.loader=="function"?await t.loader(e):void 0,a=k(o),s=e.params||{},{body:i,head:c,resources:u}=await ye(_t(r,{...s,loaderData:o}),a),d={loaderData:o??null,resources:u,islandStores:lt(a)};return ge({body:i,head:c,payload:d,options:n})}function ge({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",a=`<script id="__what_data" type="application/json">${$(n)}<\/script>`,s=r.clientEntry?`<script type="module" src="${g(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?tt(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*L(t,e){if(e===void 0&&(e=k(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 s=b(e,()=>t());yield*L(s,e);return}if(typeof t=="function"){try{let s=b(e,()=>t());yield*L(s,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let s of t)yield*L(s,e);return}if(t.tag==="__suspense"){let s=null;for(let i=0;i<Rt&&s===null;i++){let c=null;try{s=b(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=b(e,()=>w(t.props&&t.props.fallback))),yield s;return}if(typeof t.tag=="function"){try{let s=b(e,()=>t.tag({...t.props,children:t.children})),i=s instanceof Promise?await s:s;yield*L(i,e)}catch(s){typeof process<"u",yield V?`<!-- SSR Error: ${g(s.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t;at(n);let a=st(r||{});if(yield`<${n}${a}>`,!it.has(n)){let s=ot(r);if(s!=null)yield String(s);else for(let i of o)yield*L(i,e);yield`</${n}>`}}function mn(t){return{mode:"static",...t}}function yn(t,e={}){let n=k(e),r=b(n,()=>{let a=t.component(e);return w(a)}),o=t.islands||[];return we({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function we({title:t,meta:e,body:n,islands:r,scripts:o,styles:a,mode:s}){let i=Object.entries(e).map(([l,p])=>`<meta name="${g(l)}" content="${g(p)}">`).join(`
2
- `),c=a.map(l=>`<link rel="stylesheet" href="${g(l)}">`).join(`
1
+ import{AsyncLocalStorage as Ft}from"node:async_hooks";import{__installServerContextStorage as Ut}from"what-core";import{h as Re,_isAriaAttr as pt,_beginComponentSSR as re,_endComponentSSR as oe,_mapArrayToArray as ae,getServerContext as ke,runWithServerContext as b,beginHeadCollection as ht,endHeadCollection as se}from"what-core";var Ne=new RegExp("[<>&\\u2028\\u2029]","g"),He={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function N(e){return JSON.stringify(e).replace(Ne,t=>He[t.charCodeAt(0)])}import{mount as Jt,hydrate as Wt,signal as zt,batch as Yt,getServerContext as Ie}from"what-core";var je=new Map;function Le(e){let t=e||Ie();return t&&t.islandStores instanceof Map?t.islandStores:null}function me(e){let t=typeof document<"u"?je:Le(e),n={};for(let[r,o]of t||[])n[r]=o._getSnapshot();return n}import{signal as _,batch as Z}from"what-core";var T=null,ye=!(typeof process<"u");function X(e){T=e}function De(){return T}async function G(e,t){if(T&&T.revalidatePath)return T.revalidatePath(e,t);ye&&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 K(e,t){if(T&&T.revalidateTag)return T.revalidateTag(e,t);ye&&console.warn(`[what] revalidateTag('${e}') had no effect: no cache engine is bound.`)}var Q=new Map;function Me(){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 q(){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 ge(e,t){if(!e||!t||e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e.charCodeAt(r)^t.charCodeAt(r);return n===0}function ee(e){return`<meta name="what-csrf-token" content="${String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}">`}var Fe=0,Ue=!1;function Be(){return!Ue&&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${(++Fe).toString(36)}_${Date.now().toString(36)}`}`}function Ve(e,t={}){let n=t.id||Be(),{onError:r,onSuccess:o,revalidate:a}=t;typeof window>"u"&&Q.set(n,{fn:e,options:t});async function s(...i){if(typeof window>"u")return e(...i);let c=t.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let l=Me(),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)be(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 we(e,t={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=t;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 e(c,s):await e(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function Se(e){let t=_(!1),n=_(null),r=_(null);async function o(...a){t.set(!0),n.set(null);try{let s=await e(...a);return r.set(s),s}catch(s){throw n.set(s),s}finally{t.set(!1)}}return{trigger:o,isPending:()=>t(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function Je(e,t={}){let{resetOnSuccess:n=!0}=t,r={current:null},o=Se(we(e,{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 We(e,t){let n=_(e),r=_([]),o=_(e);function a(u){let d=t(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=t(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=t(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 H=new Map;function ze(e,t){return H.has(e)||H.set(e,new Set),H.get(e).add(t),()=>{H.get(e)?.delete(t)}}function be(e){let t=H.get(e);if(t)for(let n of t)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function D(e,t,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=e?.headers?.["x-csrf-token"]||e?.headers?.["X-CSRF-Token"];if(!ge(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let s=Q.get(t);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 G(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await K(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 Ye(){return[...Q.keys()]}function Xe(e,t={}){let{onSuccess:n,onError:r,onSettled:o}=t,a={isPending:_(!1),error:_(null),data:_(null)};async function s(...i){a.isPending.set(!0),a.error.set(null);try{let c=await e(...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)}}}import{h as M}from"what-core";var _e="/__what_action";function Ge(e){if(typeof e=="string")return e;if(typeof e=="function"){let t=e._actionId;if(t)return t;throw new Error("[what] <Form action={fn}>: that function is not a server action. Wrap it with action() from what-server, or pass the action id as a string.")}throw new Error("[what] <Form> requires an `action` prop: a server action or its id.")}function Ke(){if(typeof document>"u")return null;let e=document.querySelector('meta[name="what-csrf-token"]');if(e)return e.getAttribute("content");let t=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);return t?decodeURIComponent(t[1]):null}function Ze({action:e,csrfToken:t,redirect:n,method:r="post",enhance:o=!0,children:a,...s}){let i=Ge(e),c=t!==void 0?t:Ke();c==null&&typeof process<"u";let u=[M("input",{type:"hidden",name:"_action",value:i}),M("input",{type:"hidden",name:"what-csrf-token",value:c||""})];n&&u.push(M("input",{type:"hidden",name:"_redirect",value:n}));let d={...s,method:r,action:s.action||_e,"data-action":i};o&&(d["data-enhance"]="");let l=a==null?[]:Array.isArray(a)?a:[a];return M("form",d,...u,...l)}var Qe="/__what_action",I=1024*1024;function qe(e){if(!e)return{};if(typeof e.forEach=="function"&&typeof e.get=="function"){let n={};return e.forEach((r,o)=>{n[o.toLowerCase()]=r}),n}let t={};for(let n in e)t[n.toLowerCase()]=e[n];return t}function F(e,t){return{status:e,headers:{"content-type":"application/json"},body:JSON.stringify(t)}}function te(e,t){return{status:e,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${e}</h1><p>${String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</p></body></html>`}}function Ae(e){if(typeof e!="string"||!e.startsWith("/")||/^[/\\]{2}/.test(e)||e.includes("\\"))return null;try{let t=new URL(e,"http://localhost");return t.origin!=="http://localhost"?null:t.pathname+t.search}catch{return null}}function et(e,t){let n=Ae(e&&e._redirect);if(n)return n;let r=t.referer||t.referrer;if(r)try{let o=new URL(r,"http://localhost"),a=Ae(o.pathname+o.search);if(a)return a}catch{}return"/"}var tt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function j(e={}){let{getCsrfToken:t,skipCsrf:n=!1}=e;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return F(405,{message:"Method Not Allowed"});let s=qe(o.headers),i=s["x-what-action"],c=s["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:t?await t(o):void 0;if(u){let h=o.body||{},m=h._action||h["data-action"]||o.query&&o.query.action;if(!m)return te(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let S={...s},C=h._csrf??h["what-csrf-token"];if(C&&!S["x-csrf-token"]&&(S["x-csrf-token"]=String(C)),!n&&t&&!d)return te(403,"Missing CSRF token");let O={};for(let[x,z]of Object.entries(h))tt.has(x)||(O[x]=z);let E=await D({headers:S},m,[O],{csrfToken:d,skipCsrf:n});return E.status===200?{status:303,headers:{location:et(h,s)},body:""}:te(E.status,E.body&&E.body.message||"Action failed")}if(!i)return F(400,{message:"Missing X-What-Action header"});if(!n&&t&&!d)return F(403,{message:"Missing CSRF token"});let f=(o.body||{}).args,p=await D({headers:s},i,f,{csrfToken:d,skipCsrf:n});return F(p.status,p.body)}}function nt(e={}){let t=e.basePath||Qe,n=j(e);return async function(o,a,s){let[i,c]=(o.url||"").split("?");if(i!==t||(o.method||"").toUpperCase()!=="POST")return s?s():void 0;let u;try{let f=await rt(o);u=U(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 U(e,t){if((t||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(e)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return e==null||e===""?{}:JSON.parse(String(e))}async function B(e,t=I){let n=Number(e.headers.get("content-length"));if(Number.isFinite(n)&&n>t)return{tooLarge:!0};let r=e.body;if(!r||typeof r.getReader!="function"){let i=await e.text();return Buffer.byteLength(i,"utf8")>t?{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>t){try{await o.cancel()}catch{}return{tooLarge:!0}}a.push(c)}}return{raw:Buffer.concat(a.map(i=>Buffer.from(i))).toString("utf8")}}function rt(e){return new Promise((t,n)=>{let r=0,o=[];e.on("data",a=>{if(r+=a.length,r>I){let s=new Error("Body too large");s.code="BODY_TOO_LARGE",n(s),e.destroy?.();return}o.push(a)}),e.on("end",()=>{if(o.length===0)return t("");t(Buffer.concat(o).toString("utf8"))}),e.on("error",n)})}function ot(e={}){let t=j(e);return async function(n){let r={};try{let s=await B(n);if(s.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=U(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 t({method:n.method,headers:n.headers,body:r,query:o});return new Response(a.body,{status:a.status,headers:a.headers})}}import{matchRoute as at,parseQuery as st}from"what-router/match";var it="/__what_action",ct="/__what_revalidate",ne="what-csrf";function L(e){let t={};return e&&typeof e.forEach=="function"&&e.forEach((n,r)=>{t[r.toLowerCase()]=n}),t}function Te(e,t){if(!e)return null;let n=String(e).match(new RegExp(`(?:^|;\\s*)${t}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function ut(e){try{let t=await B(e);return t.tooLarge?{tooLarge:!0}:U(t.raw,e.headers.get("content-type")||"")}catch{return{}}}async function dt(e){let t;try{t=await B(e)}catch{return{json:{}}}if(t.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(t.raw)}}catch{return{json:{}}}}function lt(e){return async function(n){let{route:r,params:o,query:a,request:s}=n,i={default:r.component,loader:r.loader},c=n.csrfToken?{...e,csrfToken:n.csrfToken}:e;return{html:await V(i,{params:o,query:a,request:s},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function R(e={}){let{routes:t=[],cache:n,render:r,revalidateWebhook:o,document:a={},notFound:s,basePath:i="",csrf:c=!0}=e,u=c!==!1&&!e.actionHandler,d=e.actionHandler||j(u?{getCsrfToken:f=>Te(f.headers&&f.headers.cookie,ne)}:{skipCsrf:!0}),l=r||lt(a);return n&&(n.revalidatePath||n.revalidateTag)&&X({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===it){let y=await ut(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await d({method:"POST",headers:L(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(A.body,{status:A.status,headers:A.headers})}let S=null,C=null;if(u&&(S=Te(L(p.headers).cookie,ne),!S)){S=q();let A=L(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;C=`${ne}=${encodeURIComponent(S)}; Path=/; SameSite=Lax`+(A?"; Secure":"")}let O=y=>(C&&(y["set-cookie"]=C),y);if(p.method==="POST"&&m===ct&&o){let y=await dt(p);if(y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let A=await o({headers:L(p.headers),body:y.json});return new Response(JSON.stringify(A.body),{status:A.status,headers:{"content-type":"application/json"}})}let E=at(m,t);if(!E){let y=s?s():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(y,{status:404,headers:O({"content-type":"text/html; charset=utf-8"})})}let{route:x,params:z}=E,Y=x.page||{mode:x.mode||"client"},P={path:m,query:st(h.search),config:Y,route:x,params:z,request:p};if(n&&Y.mode!=="server"){P.varyHeaders=L(p.headers);let y=await n.handle(P,()=>l(P));return new Response(y.html,{status:y.status||200,headers:O({"content-type":"text/html; charset=utf-8",...y.headers||{}})})}S&&(P.csrfToken=S);let pe=await l(P),he=O({"content-type":"text/html; charset=utf-8"});return Y.mode==="server"&&(he["Cache-Control"]="private, no-store"),new Response(pe.html,{status:pe.status||200,headers:he})}}function ft(e={}){let t=R(e);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),t(n)}}}function k(e){return{head:ht(),loaderData:e,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var Ee=0;function mt(){Ee=0}function yt(){return"h"+Ee++}function gt(e){if(!ke()){let t=k(void 0);return b(t,()=>gt(e))}return mt(),v(e)}function v(e){if(e==null||e===!1||e===!0)return"";if(typeof e=="string"||typeof e=="number")return w(String(e));if(typeof e=="function"&&e._signal)return`<!--$-->${v(e())}<!--/$-->`;if(typeof e=="function"&&e._mapArray)try{return`<!--$-->${v(ae(e))}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(typeof e=="function")try{return`<!--$-->${v(e())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(e))return`<!--[]-->${e.map(v).join("")}<!--/[]-->`;if(typeof e.tag=="function"){let c=yt(),u=re(e.tag),d;try{d=v(e.tag({...e.props,children:e.children}))}finally{oe(u)}return wt(d,c)}let{tag:t,props:n,children:r}=e;ce(t);let o=ue(n||{}),a=`<${t}${o}>`;if(de.has(t))return a;let s=ie(n),i=s!=null?String(s):r.map(v).join("");return`${a}${i}</${t}>`}function wt(e,t){let n=e.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],a=r.length+1+o.length;return e.slice(0,a)+` data-hk="${t}"`+e.slice(a)}return e}function g(e){if(!ke()){let c=k(void 0);return b(c,()=>g(e))}if(e==null||e===!1||e===!0)return"";if(typeof e=="string"||typeof e=="number")return w(String(e));if(typeof e=="function"&&e._signal)return g(e());if(typeof e=="function"&&e._mapArray)try{return g(ae(e))}catch{return typeof process<"u",""}if(typeof e=="function")try{return g(e())}catch{return typeof process<"u",""}if(Array.isArray(e))return e.map(g).join("");if(e.tag==="__suspense")try{return(e.children||[]).map(g).join("")}catch(c){if(c&&typeof c.then=="function")return g(e.props&&e.props.fallback);throw c}if(typeof e.tag=="function"){let c=re(e.tag);try{return g(e.tag({...e.props,children:e.children}))}finally{oe(c)}}let{tag:t,props:n,children:r}=e;ce(t);let o=ue(n||{}),a=`<${t}${o}>`;if(de.has(t))return a;let s=ie(n),i=s!=null?String(s):r.map(g).join("");return`${a}${i}</${t}>`}function yn(e){let t=k(void 0);return{body:b(t,()=>g(e)),head:se(t.head)}}async function gn(e,t={}){let n=e.default||e,r=typeof e.loader=="function"?await e.loader(t):void 0,o=k(r),a=t.params||{};return{body:b(o,()=>g(Re(n,{...a,loaderData:r}))),head:se(o.head),loaderData:r}}var ve=12;async function St(e,t){t||(t=k(void 0));let n="";for(let o=0;o<ve;o++){n=b(t,()=>g(e));let a=[...t.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 t.resources)a.status==="ready"&&(r[o]=a.value);return{body:n,head:se(t.head),loaderData:t.loaderData,resources:r,ctx:t}}async function V(e,t={},n={}){let r=e.default||e,o=typeof e.loader=="function"?await e.loader(t):void 0,a=k(o),s=t.params||{},{body:i,head:c,resources:u}=await St(Re(r,{...s,loaderData:o}),a),d={loaderData:o??null,resources:u,islandStores:me(a)};return bt({body:i,head:c,payload:d,options:n})}function bt({body:e,head:t,payload:n,options:r={}}){let o=r.lang||"en",a=`<script id="__what_data" type="application/json">${N(n)}<\/script>`,s=r.clientEntry?`<script type="module" src="${w(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?ee(r.csrfToken):"")+(r.head||""),u=r.bodyClass?` class="${w(r.bodyClass)}"`:"";return`<!DOCTYPE html><html lang="${w(o)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${t||""}${c}</head><body${u}>${e}${a}${s}</body></html>`}async function*$(e,t){if(t===void 0&&(t=k(void 0)),e==null||e===!1||e===!0)return;if(typeof e=="string"||typeof e=="number"){yield w(String(e));return}if(typeof e=="function"&&e._signal){let s=b(t,()=>e());yield*$(s,t);return}if(typeof e=="function"&&e._mapArray){let s=null;try{s=b(t,()=>ae(e))}catch{typeof process<"u"}s&&(yield*$(s,t));return}if(typeof e=="function"){try{let s=b(t,()=>e());yield*$(s,t)}catch{typeof process<"u"}return}if(Array.isArray(e)){for(let s of e)yield*$(s,t);return}if(e.tag==="__suspense"){let s=null;for(let i=0;i<ve&&s===null;i++){let c=null;try{s=b(t,()=>(e.children||[]).map(g).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(s===null){let u=[...t.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}s===null&&(s=b(t,()=>g(e.props&&e.props.fallback))),yield s;return}if(typeof e.tag=="function"){let s=re(e.tag);try{let i=b(t,()=>e.tag({...e.props,children:e.children})),c=i instanceof Promise?await i:i;yield*$(c,t)}catch(i){typeof process<"u",yield J?`<!-- SSR Error: ${w(i.message||"Component error")} -->`:"<!-- SSR Error -->"}finally{oe(s)}return}let{tag:n,props:r,children:o}=e;ce(n);let a=ue(r||{});if(yield`<${n}${a}>`,!de.has(n)){let s=ie(r);if(s!=null)yield String(s);else for(let i of o)yield*$(i,t);yield`</${n}>`}}function wn(e){return{mode:"static",...e}}function Sn(e,t={}){let n=k(t),r=b(n,()=>{let a=e.component(t);return g(a)}),o=e.islands||[];return _t({title:e.title||"",meta:e.meta||{},body:r,islands:o,scripts:e.mode==="static"?[]:e.scripts||[],styles:e.styles||[],mode:e.mode})}function _t({title:e,meta:t,body:n,islands:r,scripts:o,styles:a,mode:s}){let i=Object.entries(t).map(([f,p])=>`<meta name="${w(f)}" content="${w(p)}">`).join(`
2
+ `),c=a.map(f=>`<link rel="stylesheet" href="${w(f)}">`).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(l=>`<script type="module" src="${g(l)}"><\/script>`).join(`
8
- `),f=s==="client"?`
7
+ <\/script>`:"",d=o.map(f=>`<script type="module" src="${w(f)}"><\/script>`).join(`
8
+ `),l=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>${w(e)}</title>
16
16
  ${c}
17
17
  </head>
18
18
  <body>
19
19
  <div id="app">${n}</div>
20
20
  ${u}
21
21
  ${d}
22
- ${f}
22
+ ${l}
23
23
  </body>
24
- </html>`}function gn(t){return t._server=!0,t}var V=!(typeof process<"u");function ot(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"&&V&&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 Se=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/,be=/^[a-zA-Z_][a-zA-Z0-9._:-]*$/;function at(t){if(typeof t!="string"||!be.test(t))throw new Error(`[what-server] Invalid tag name in SSR: ${JSON.stringify(t)}`)}function st(t){let e="";for(let[n,r]of Object.entries(t)){if(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")continue;let o=n.toLowerCase();if(!(o.startsWith("on")&&n.length>2)){if(r===!1&&de(n)){e+=` ${n}="false"`;continue}if(!(r===!1||r==null)){if(!Se.test(n)){V&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(Te.has(o)){V&&console.warn(`[what-server] Skipping unsafe attribute in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")e+=` class="${g(String(r))}"`;else if(n==="style"&&typeof r=="object"){let a=Object.entries(r).map(([s,i])=>`${Re(s)}:${i}`).join(";");e+=` style="${g(a)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?e+=` ${n}="true"`:e+=` ${n}`;else{if(_e(n,r))continue;e+=` ${n}="${g(String(r))}"`}}}}return e}function _e(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","data","ping","xlink:href"]),Te=new Set(["srcdoc"]);function g(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function Re(t){return t.startsWith("--")?t:t.replace(/([A-Z])/g,"-$1").toLowerCase()}var it=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);import ke from"node:http";var ct=Symbol("what.bodyTooLarge"),kt=()=>new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});async function vt(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=Number(t.headers["content-length"]),s=Number.isFinite(a)&&a>I,i=[],c=0;for await(let u of t)if(!s){if(c+=u.length,c>I){s=!0,i.length=0;continue}i.push(u)}if(s)return ct;i.length&&(o=Buffer.concat(i))}return new Request(n,{method:t.method,headers:r,body:o})}async function W(t,e){t.statusCode=e.status,e.headers.forEach((r,o)=>t.setHeader(o,r));let n=await e.text();t.end(n)}function Et(t){return async function(n,r){try{let o=await vt(n);if(o===ct)return await W(r,kt());let a=await t(o);await W(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 ve(t={}){let e=R(t);return async function(r,o,a){let s=await vt(r);if(s===ct)return await W(o,kt());let i=await e(s);if(i.status===404&&typeof a=="function")return a();await W(o,i)}}function Ee(t={}){let e=R(t),n=ke.createServer(Et(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 Ce,writeFile as Ct}from"node:fs/promises";import{join as ut,resolve as Ot,sep as Oe}from"node:path";import{matchRoute as xe}from"what-router/match";function Pe(t){return t.includes(":")||t.includes("*")||t.includes("[")}function $e(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]??"")}function He(t,e){let n=Ot(t),r=Ot(ut(t,e==="/"?"":e));if(r!==n&&!r.startsWith(n+Oe))throw new Error(`[what-server] Refusing to write outside outDir: ${e}`);return r}async function Ie({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(Pe(a.path)){if(typeof a.getStaticPaths!="function")continue;c=((await a.getStaticPaths()).paths||[]).map(d=>$e(a.path,d.params||{}))}for(let u of c){let d=He(e,u),f=xe(u,[a]),p={params:f?f.params:{},query:{}},h=n?await n(i,p):await J(i,p,r);if(await Ce(d,{recursive:!0}),await Ct(ut(d,"index.html"),h),typeof a.loader=="function"){let m=await a.loader(p);await Ct(ut(d,"__what_data.json"),$({loaderData:m}))}o.push(u)}}return{pages:o}}function Ne(t={}){return R(t)}async function je({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 f={version:3,routes:[{handle:"filesystem"},{src:"/.*",dest:`/${e}`}]};await i(u(t,"config.json"),JSON.stringify(f,null,2)),a&&await c(a,u(t,"static"),{recursive:!0});let l=null;if(r&&typeof r=="object"){l=u(t,"functions",`${e}.func`),await s(l,{recursive:!0});let p={runtime:n,handler:o,launcherType:"Nodejs",shouldAddHelpers:!1,supportsResponseStreaming:!0};await i(u(l,".vc-config.json"),JSON.stringify(p,null,2));for(let[h,m]of Object.entries(r)){let S=u(l,h);await s(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:f,outDir:t,functionDir:l}}De(new Le);export{wt as ACTION_ENDPOINT,Xt as Form,Ft as action,je as buildVercelOutput,N as createActionHandler,ue as createCloudflareHandler,R as createRequestHandler,Ee as createServer,Ne as createVercelHandler,tt as csrfMetaTag,mn as definePage,Ie as exportStatic,ee as fetchActionHandler,mt as formAction,q as generateCsrfToken,yn as generateStaticPage,Vt as getRegisteredActions,Nt as getRevalidationHandler,D as handleActionRequest,gt as invalidatePath,qt as nodeActionMiddleware,Jt as onRevalidate,J as renderDocument,hn as renderPage,he as renderToHydratableString,L as renderToStream,w as renderToString,ye as renderToStringAsync,pn as renderToStringWithHead,G as revalidatePath,K as revalidateTag,$ as serializeState,gn as server,X as setRevalidationHandler,Et as toNodeListener,yt as useAction,Ut as useFormAction,Wt as useMutation,Bt as useOptimistic,ht as validateCsrfToken,ve as whatMiddleware};
24
+ </html>`}function bn(e){return e._server=!0,e}var J=!(typeof process<"u");function ie(e){return e?e.dangerouslySetInnerHTML?e.dangerouslySetInnerHTML.__html??null:e.innerHTML&&typeof e.innerHTML=="object"&&"__html"in e.innerHTML?e.innerHTML.__html??null:(e.innerHTML!=null&&typeof e.innerHTML=="string"&&J&&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 At=/^[a-zA-Z_:][a-zA-Z0-9:._-]*$/,Tt=/^[a-zA-Z_][a-zA-Z0-9._:-]*$/;function ce(e){if(typeof e!="string"||!Tt.test(e))throw new Error(`[what-server] Invalid tag name in SSR: ${JSON.stringify(e)}`)}function ue(e){let t="";for(let[n,r]of Object.entries(e)){if(n==="key"||n==="ref"||n==="children"||n==="dangerouslySetInnerHTML"||n==="innerHTML")continue;let o=n.toLowerCase();if(!(o.startsWith("on")&&n.length>2)){if(r===!1&&pt(n)){t+=` ${n}="false"`;continue}if(!(r===!1||r==null)){if(!At.test(n)){J&&console.warn(`[what-server] Skipping invalid attribute name in SSR: ${JSON.stringify(n)}`);continue}if(Et.has(o)){J&&console.warn(`[what-server] Skipping unsafe attribute in SSR: ${JSON.stringify(n)}`);continue}if(n==="className"||n==="class")t+=` class="${w(String(r))}"`;else if(n==="style"&&typeof r=="object"){let a=Object.entries(r).map(([s,i])=>`${vt(s)}:${i}`).join(";");t+=` style="${w(a)}"`}else if(r===!0)n.startsWith("aria-")||n==="role"?t+=` ${n}="true"`:t+=` ${n}`;else{if(Rt(n,r))continue;t+=` ${n}="${w(String(r))}"`}}}}return t}function Rt(e,t){let n=e.toLowerCase();if(!kt.has(n))return!1;let r=String(t).trim().replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();return r.startsWith("javascript:")||r.startsWith("vbscript:")||r.startsWith("data:")}var kt=new Set(["href","src","action","formaction","data","ping","xlink:href"]),Et=new Set(["srcdoc"]);function w(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function vt(e){return e.startsWith("--")?e:e.replace(/([A-Z])/g,"-$1").toLowerCase()}var de=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);import Ct from"node:http";var le=Symbol("what.bodyTooLarge"),Ce=()=>new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});async function Oe(e){let n=`http://${e.headers.host||"localhost"}${e.url}`,r=new Headers;for(let[a,s]of Object.entries(e.headers))s!=null&&r.set(a,Array.isArray(s)?s.join(", "):String(s));let o;if(e.method!=="GET"&&e.method!=="HEAD"){let a=Number(e.headers["content-length"]),s=Number.isFinite(a)&&a>I,i=[],c=0;for await(let u of e)if(!s){if(c+=u.length,c>I){s=!0,i.length=0;continue}i.push(u)}if(s)return le;i.length&&(o=Buffer.concat(i))}return new Request(n,{method:e.method,headers:r,body:o})}async function W(e,t){e.statusCode=t.status,t.headers.forEach((r,o)=>e.setHeader(o,r));let n=await t.text();e.end(n)}function xe(e){return async function(n,r){try{let o=await Oe(n);if(o===le)return await W(r,Ce());let a=await e(o);await W(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 Ot(e={}){let t=R(e);return async function(r,o,a){let s=await Oe(r);if(s===le)return await W(o,Ce());let i=await t(s);if(i.status===404&&typeof a=="function")return a();await W(o,i)}}function xt(e={}){let t=R(e),n=Ct.createServer(xe(t)),{scheduler:r}=e;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 $t,writeFile as $e}from"node:fs/promises";import{join as fe,resolve as Pe,sep as Pt}from"node:path";import{matchRoute as Nt}from"what-router/match";function Ht(e){return e.includes(":")||e.includes("*")||e.includes("[")}function It(e,t){return e.replace(/\[\.\.\.(\w+)\]/g,(n,r)=>t[r]??"").replace(/\[(\w+)\]/g,(n,r)=>t[r]??"").replace(/[:*](\w+)/g,(n,r)=>t[r]??"")}function jt(e,t){let n=Pe(e),r=Pe(fe(e,t==="/"?"":t));if(r!==n&&!r.startsWith(n+Pt))throw new Error(`[what-server] Refusing to write outside outDir: ${t}`);return r}async function Lt({routes:e=[],outDir:t,render:n,documentOptions:r={}}={}){let o=[];for(let a of e){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(Ht(a.path)){if(typeof a.getStaticPaths!="function")continue;c=((await a.getStaticPaths()).paths||[]).map(d=>It(a.path,d.params||{}))}for(let u of c){let d=jt(t,u),l=Nt(u,[a]),p={params:l?l.params:{},query:{}},h=n?await n(i,p):await V(i,p,r);if(await $t(d,{recursive:!0}),await $e(fe(d,"index.html"),h),typeof a.loader=="function"){let m=await a.loader(p);await $e(fe(d,"__what_data.json"),N({loaderData:m}))}o.push(u)}}return{pages:o}}function Dt(e={}){return R(e)}async function Mt({outDir:e=".vercel/output",functionName:t="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(e,{recursive:!0});let l={version:3,routes:[{handle:"filesystem"},{src:"/.*",dest:`/${t}`}]};await i(u(e,"config.json"),JSON.stringify(l,null,2)),a&&await c(a,u(e,"static"),{recursive:!0});let f=null;if(r&&typeof r=="object"){f=u(e,"functions",`${t}.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 S=u(f,h);await s(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:e,functionDir:f}}Ut(new Ft);export{_e as ACTION_ENDPOINT,Ze as Form,Ve as action,Mt as buildVercelOutput,j as createActionHandler,ft as createCloudflareHandler,R as createRequestHandler,xt as createServer,Dt as createVercelHandler,ee as csrfMetaTag,wn as definePage,Lt as exportStatic,ot as fetchActionHandler,we as formAction,q as generateCsrfToken,Sn as generateStaticPage,Ye as getRegisteredActions,De as getRevalidationHandler,D as handleActionRequest,be as invalidatePath,nt as nodeActionMiddleware,ze as onRevalidate,V as renderDocument,gn as renderPage,gt as renderToHydratableString,$ as renderToStream,g as renderToString,St as renderToStringAsync,yn as renderToStringWithHead,G as revalidatePath,K as revalidateTag,N as serializeState,bn as server,X as setRevalidationHandler,xe as toNodeListener,Se as useAction,Je as useFormAction,Xe as useMutation,We as useOptimistic,ge as validateCsrfToken,Ot as whatMiddleware};
package/node.d.ts CHANGED
@@ -16,7 +16,18 @@ export function toNodeListener(
16
16
  export function whatMiddleware(options?: RequestHandlerOptions): (req: any, res: any, next?: () => void) => Promise<void>;
17
17
 
18
18
  /** A ready-to-listen node:http server. Starts `scheduler` and stops it on SIGTERM/SIGINT. */
19
- export function createServer(options?: RequestHandlerOptions & { scheduler?: { start(): void; stop(): void } }): import('node:http').Server;
19
+ // Structural, not `import('node:http').Server`. Referencing a Node builtin here
20
+ // would make @types/node a hard requirement for every consumer of what-server,
21
+ // including the browser and edge ones that never touch this entry point. The
22
+ // runtime value IS an http.Server; this types what callers actually do with it.
23
+ export interface NodeHttpServer {
24
+ listen(...args: any[]): NodeHttpServer;
25
+ close(callback?: (err?: Error) => void): NodeHttpServer;
26
+ address(): string | { address: string; family: string; port: number } | null;
27
+ on(event: string, listener: (...args: any[]) => void): NodeHttpServer;
28
+ }
29
+
30
+ export function createServer(options?: RequestHandlerOptions & { scheduler?: { start(): void; stop(): void } }): NodeHttpServer;
20
31
 
21
32
  /** Vercel Functions entry: a Web-Fetch handler. */
22
33
  export function createVercelHandler(options?: RequestHandlerOptions): (request: Request) => Promise<Response>;
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "what-server",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "What Framework - SSR, islands architecture, static generation",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "types": "index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./index.d.ts",
11
10
  "node": {
12
11
  "types": "./node.d.ts",
13
12
  "production": "./dist/node.min.js",
14
13
  "import": "./src/node.js",
15
14
  "default": "./src/node.js"
16
15
  },
16
+ "types": "./index.d.ts",
17
17
  "production": "./dist/index.min.js",
18
18
  "import": "./src/index.js",
19
19
  "default": "./src/index.js"
@@ -51,8 +51,8 @@
51
51
  "author": "ZVN DEV (https://zvndev.com)",
52
52
  "license": "MIT",
53
53
  "peerDependencies": {
54
- "what-core": "^0.12.1",
55
- "what-router": "^0.12.1"
54
+ "what-core": "^0.12.3",
55
+ "what-router": "^0.12.3"
56
56
  },
57
57
  "peerDependenciesMeta": {
58
58
  "what-router": {
package/src/index.js CHANGED
@@ -5,6 +5,9 @@
5
5
  import {
6
6
  h,
7
7
  _isAriaAttr,
8
+ _beginComponentSSR,
9
+ _endComponentSSR,
10
+ _mapArrayToArray,
8
11
  getServerContext,
9
12
  runWithServerContext,
10
13
  beginHeadCollection,
@@ -71,6 +74,20 @@ function _renderHydratable(vnode) {
71
74
  return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;
72
75
  }
73
76
 
77
+ // Compiled keyed list: an inserter taking (parent, marker), not a thunk.
78
+ // Calling it with no arguments threw and SSR swallowed it, so every compiled
79
+ // keyed list server-rendered as an empty container. See _mapArrayToArray.
80
+ if (typeof vnode === 'function' && vnode._mapArray) {
81
+ try {
82
+ return `<!--$-->${_renderHydratable(_mapArrayToArray(vnode))}<!--/$-->`;
83
+ } catch (e) {
84
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
85
+ console.warn('[what-server] Error rendering keyed list in SSR:', e.message);
86
+ }
87
+ return '<!--$--><!--/$-->';
88
+ }
89
+ }
90
+
74
91
  // Reactive function child — wrap in dynamic content markers
75
92
  if (typeof vnode === 'function') {
76
93
  try {
@@ -91,8 +108,13 @@ function _renderHydratable(vnode) {
91
108
  // Component — add hydration key to root element
92
109
  if (typeof vnode.tag === 'function') {
93
110
  const hkId = nextHydrationId();
94
- const result = vnode.tag({ ...vnode.props, children: vnode.children });
95
- const html = _renderHydratable(result);
111
+ const ctx = _beginComponentSSR(vnode.tag);
112
+ let html;
113
+ try {
114
+ html = _renderHydratable(vnode.tag({ ...vnode.props, children: vnode.children }));
115
+ } finally {
116
+ _endComponentSSR(ctx);
117
+ }
96
118
  // Inject data-hk into the first element tag if present
97
119
  return injectHydrationKey(html, hkId);
98
120
  }
@@ -146,6 +168,18 @@ export function renderToString(vnode) {
146
168
  return renderToString(vnode());
147
169
  }
148
170
 
171
+ // Compiled keyed list: see _renderHydratable above.
172
+ if (typeof vnode === 'function' && vnode._mapArray) {
173
+ try {
174
+ return renderToString(_mapArrayToArray(vnode));
175
+ } catch (e) {
176
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
177
+ console.warn('[what-server] Error rendering keyed list in SSR:', e.message);
178
+ }
179
+ return '';
180
+ }
181
+ }
182
+
149
183
  // Reactive function child — call to get value
150
184
  if (typeof vnode === 'function') {
151
185
  try {
@@ -178,9 +212,18 @@ export function renderToString(vnode) {
178
212
  }
179
213
 
180
214
  // Component
215
+ //
216
+ // Run it under a component context. Calling it bare left the component stack
217
+ // empty, so every context-dependent hook threw and the render failed outright
218
+ // rather than degrading. The context has to stay on the stack while the
219
+ // result is rendered, because useContext resolves by walking parent contexts.
181
220
  if (typeof vnode.tag === 'function') {
182
- const result = vnode.tag({ ...vnode.props, children: vnode.children });
183
- return renderToString(result);
221
+ const ctx = _beginComponentSSR(vnode.tag);
222
+ try {
223
+ return renderToString(vnode.tag({ ...vnode.props, children: vnode.children }));
224
+ } finally {
225
+ _endComponentSSR(ctx);
226
+ }
184
227
  }
185
228
 
186
229
  // Element
@@ -315,6 +358,20 @@ export async function* renderToStream(vnode, ctx) {
315
358
  return;
316
359
  }
317
360
 
361
+ // Compiled keyed list: see _renderHydratable above.
362
+ if (typeof vnode === 'function' && vnode._mapArray) {
363
+ let rows = null;
364
+ try {
365
+ rows = runWithServerContext(ctx, () => _mapArrayToArray(vnode));
366
+ } catch (e) {
367
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
368
+ console.warn('[what-server] Error rendering keyed list in stream SSR:', e.message);
369
+ }
370
+ }
371
+ if (rows) yield* renderToStream(rows, ctx);
372
+ return;
373
+ }
374
+
318
375
  // Reactive function child — call to get value
319
376
  if (typeof vnode === 'function') {
320
377
  try {
@@ -361,6 +418,10 @@ export async function* renderToStream(vnode, ctx) {
361
418
  }
362
419
 
363
420
  if (typeof vnode.tag === 'function') {
421
+ // The frame stays open across the yields below: useContext resolves by
422
+ // walking parent contexts, so a Provider's context has to outlive the
423
+ // streaming of its own subtree.
424
+ const componentCtx = _beginComponentSSR(vnode.tag);
364
425
  try {
365
426
  const result = runWithServerContext(
366
427
  ctx,
@@ -376,6 +437,8 @@ export async function* renderToStream(vnode, ctx) {
376
437
  yield _isDevMode
377
438
  ? `<!-- SSR Error: ${escapeHtml(e.message || 'Component error')} -->`
378
439
  : `<!-- SSR Error -->`;
440
+ } finally {
441
+ _endComponentSSR(componentCtx);
379
442
  }
380
443
  return;
381
444
  }
package/src/islands.js CHANGED
@@ -473,54 +473,180 @@ export function enhance(selector, handler) {
473
473
  }
474
474
  }
475
475
 
476
- // Form enhancement: submit via fetch instead of page reload
476
+ /**
477
+ * Recover the double-submit CSRF token for a form.
478
+ *
479
+ * The meta tag alone is not enough. A cached page (mode 'static' or 'hybrid') is
480
+ * shared between visitors, so the adapter deliberately does NOT embed a
481
+ * per-visitor token in it; the token lives only in the cookie. A form inside
482
+ * such a page therefore has no meta tag to read, and blocking on that alone
483
+ * refused perfectly valid submissions. `<Form>` also emits the token as a hidden
484
+ * field, which is the same value, so all three sources are checked.
485
+ */
486
+ function readFormCsrfToken(form) {
487
+ const meta = document.querySelector('meta[name="csrf-token"]')
488
+ || document.querySelector('meta[name="what-csrf-token"]');
489
+ if (meta) return meta.getAttribute('content');
490
+
491
+ const field = form.querySelector('input[name="what-csrf-token"], input[name="_csrf"]');
492
+ if (field && field.value) return field.value;
493
+
494
+ const cookie = document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);
495
+ return cookie ? decodeURIComponent(cookie[1]) : null;
496
+ }
497
+
498
+ /**
499
+ * Serialize a form the way the browser would, so an enhanced submit and a plain
500
+ * one are indistinguishable to the server.
501
+ *
502
+ * Two details that look pedantic and are not:
503
+ * - newlines in every text entry normalize to CRLF. The urlencoded and
504
+ * multipart serializers both do this per spec; URLSearchParams does not, so
505
+ * a <textarea> round-tripped different bytes with JS on than with JS off.
506
+ * - a File under a non-multipart encoding contributes only its NAME, which is
507
+ * what a native submit sends. Sending "[object File]" would be worse than
508
+ * useless, and silently sending nothing hides a real mistake.
509
+ */
510
+ function formEntries(form, submitter, multipart) {
511
+ const data = new FormData(form);
512
+
513
+ // FormData(form) never includes the submit button, so a multi-button form
514
+ // could not tell the server which button was pressed. Native submits include
515
+ // the submitter's name/value.
516
+ if (submitter && submitter.name) {
517
+ data.append(submitter.name, submitter.value ?? '');
518
+ }
519
+
520
+ if (multipart) return data;
521
+
522
+ const params = new URLSearchParams();
523
+ let droppedFile = null;
524
+ for (const [key, value] of data) {
525
+ if (typeof value === 'string') {
526
+ params.append(key, value.replace(/\r\n|\r|\n/g, '\r\n'));
527
+ } else {
528
+ droppedFile = droppedFile || key;
529
+ params.append(key, value.name ?? '');
530
+ }
531
+ }
532
+
533
+ if (droppedFile && typeof console !== 'undefined') {
534
+ console.warn(
535
+ `[what] Form field "${droppedFile}" holds a file, but the form is not ` +
536
+ 'enctype="multipart/form-data", so only the file NAME is sent. This is what ' +
537
+ 'a plain HTML submit does too. Add enctype="multipart/form-data" to upload ' +
538
+ 'the bytes.'
539
+ );
540
+ }
541
+ return params;
542
+ }
543
+
544
+ /**
545
+ * Form enhancement: submit via fetch instead of a full page load.
546
+ *
547
+ * The encoding is not a detail. `/__what_action` parses
548
+ * application/x-www-form-urlencoded or JSON, never multipart, so an enhanced
549
+ * submit of a default form has to use the same encoding as the plain HTML submit
550
+ * it replaces. Posting a FormData object unconditionally produced a multipart
551
+ * body the endpoint could not parse: the action id went missing and every
552
+ * enhanced `<Form>` failed with 400 while the no-JS path kept working.
553
+ *
554
+ * The encoding now follows the form's own `enctype`, exactly as the browser
555
+ * would, so a form that declares multipart still uploads its files.
556
+ *
557
+ * On success the action endpoint answers 303 to the form's `_redirect` target,
558
+ * which fetch follows. The page is then navigated there so the enhanced path
559
+ * lands where the unenhanced one would. Cancel the `form:response` event to keep
560
+ * the page put and handle the response yourself.
561
+ */
477
562
  export function enhanceForms(selector = 'form[data-enhance]') {
478
563
  enhance(selector, (form) => {
479
- form.addEventListener('submit', async (e) => {
480
- e.preventDefault();
481
-
482
- const formData = new FormData(form);
483
- const method = form.method.toUpperCase() || 'POST';
484
- const action = form.action || location.href;
564
+ form.addEventListener('submit', async (event) => {
565
+ event.preventDefault();
485
566
 
486
567
  try {
487
- // Read CSRF token from meta tag
488
- const csrfMeta = document.querySelector('meta[name="csrf-token"]') ||
489
- document.querySelector('meta[name="what-csrf-token"]');
490
- const csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : null;
491
-
492
- // If no CSRF token and form hasn't opted out, block submission
493
- const noCsrf = form.getAttribute('data-no-csrf') === 'true';
494
- if (!csrfToken && !noCsrf) {
495
- console.warn(
496
- '[what] Form submission blocked: no CSRF token found. ' +
497
- 'Add a <meta name="what-csrf-token"> tag (csrfMetaTag() emits it) ' +
498
- 'or set data-no-csrf="true" on the form to opt out.'
499
- );
500
- form.dispatchEvent(new CustomEvent('form:error', {
501
- bubbles: true,
502
- detail: { error: new Error('Missing CSRF token') },
503
- }));
504
- return;
568
+ // getAttribute, never the properties. HTMLFormElement is
569
+ // [LegacyOverrideBuiltIns]: a field named "method" or "action" SHADOWS
570
+ // form.method / form.action with the input element itself. Reading
571
+ // `form.method.toUpperCase()` then threw a TypeError after
572
+ // preventDefault() had already run, so the submit produced no fetch, no
573
+ // form:error, and no native fallback. It just did nothing.
574
+ const submitter = event.submitter || null;
575
+ const attr = (name) => (submitter && submitter.getAttribute(`form${name}`))
576
+ || form.getAttribute(name);
577
+
578
+ const method = (attr('method') || 'get').toUpperCase();
579
+ const action = new URL(attr('action') || location.href, location.href);
580
+ const enctype = (attr('enctype') || '').toLowerCase();
581
+ const multipart = enctype === 'multipart/form-data';
582
+
583
+ const entries = formEntries(form, submitter, multipart);
584
+
585
+ // CSRF is about protecting OUR endpoint. A form aimed at another origin
586
+ // must neither be blocked by our token policy nor be handed our token:
587
+ // attaching it would export the visitor's double-submit secret to a
588
+ // third party.
589
+ const sameOrigin = action.origin === location.origin;
590
+ const headers = { 'X-Requested-With': 'XMLHttpRequest' };
591
+
592
+ if (sameOrigin) {
593
+ const csrfToken = readFormCsrfToken(form);
594
+ const noCsrf = form.getAttribute('data-no-csrf') === 'true';
595
+ if (!csrfToken && !noCsrf) {
596
+ console.warn(
597
+ '[what] Form submission blocked: no CSRF token found. ' +
598
+ 'Add a <meta name="what-csrf-token"> tag (csrfMetaTag() emits it) ' +
599
+ 'or set data-no-csrf="true" on the form to opt out.'
600
+ );
601
+ form.dispatchEvent(new CustomEvent('form:error', {
602
+ bubbles: true,
603
+ detail: { error: new Error('Missing CSRF token') },
604
+ }));
605
+ return;
606
+ }
607
+ if (csrfToken) headers['X-CSRF-Token'] = csrfToken;
505
608
  }
506
609
 
507
- const headers = {
508
- 'X-Requested-With': 'XMLHttpRequest',
509
- };
510
- if (csrfToken) {
511
- headers['X-CSRF-Token'] = csrfToken;
610
+ let body;
611
+ if (method === 'GET') {
612
+ // A native GET submit REPLACES the query string with the form data.
613
+ // The previous code built the params and then threw them away, so an
614
+ // enhanced GET form fetched a bare URL with none of its fields.
615
+ action.search = String(entries);
616
+ } else if (multipart) {
617
+ // Hand FormData straight to fetch so the browser writes the boundary.
618
+ body = entries;
619
+ } else {
620
+ body = entries;
621
+ headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
512
622
  }
513
623
 
514
- const response = await fetch(action, {
624
+ const response = await fetch(action.href, {
515
625
  method,
516
- body: method === 'GET' ? undefined : formData,
626
+ body,
517
627
  headers,
628
+ credentials: 'same-origin',
518
629
  });
519
630
 
520
- form.dispatchEvent(new CustomEvent('form:response', {
631
+ const responseEvent = new CustomEvent('form:response', {
521
632
  bubbles: true,
522
- detail: { response, ok: response.ok },
523
- }));
633
+ cancelable: true,
634
+ detail: { response, ok: response.ok, redirected: response.redirected },
635
+ });
636
+ const proceed = form.dispatchEvent(responseEvent);
637
+
638
+ // Same-origin only. A native submit would follow an off-site redirect,
639
+ // but a framework default that can navigate the page to another origin
640
+ // on a server's say-so is not a default worth having. Listen for
641
+ // form:response if you need that.
642
+ if (proceed && response.ok && response.redirected) {
643
+ let target = null;
644
+ try {
645
+ const url = new URL(response.url, location.href);
646
+ if (url.origin === location.origin) target = url.href;
647
+ } catch { /* unparseable: do not navigate */ }
648
+ if (target) location.assign(target);
649
+ }
524
650
  } catch (error) {
525
651
  form.dispatchEvent(new CustomEvent('form:error', {
526
652
  bubbles: true,