what-server 0.11.8 → 0.12.1
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 +2 -2
- package/dist/islands.min.js +1 -1
- package/dist/node.min.js +6 -6
- package/index.d.ts +129 -28
- package/node.d.ts +44 -0
- package/package.json +5 -3
- package/src/form.js +92 -0
- package/src/index.js +23 -2
- package/src/islands.js +7 -1
package/dist/index.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{h as mt,getServerContext as gt,runWithServerContext as S,beginHeadCollection as Gt,endHeadCollection as q}from"what-core";var bt=new RegExp("[<>&\\u2028\\u2029]","g"),At={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function B(t){return JSON.stringify(t).replace(bt,e=>At[e.charCodeAt(0)])}import{mount as pe,hydrate as he,signal as ye,batch as me,getServerContext as _t}from"what-core";var Tt=new Map;function kt(t){let e=t||_t();return e&&e.islandStores instanceof Map?e.islandStores:null}function st(t){let e=typeof document<"u"?Tt:kt(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as A,batch as z}from"what-core";var T=null,it=!(typeof process<"u");function J(t){T=t}function Rt(){return T}async function W(t,e){if(T&&T.revalidatePath)return T.revalidatePath(t,e);it&&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 V(t,e){if(T&&T.revalidateTag)return T.revalidateTag(t,e);it&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var X=new Map;function Et(){if(typeof document<"u"){let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(e)return decodeURIComponent(e[1])}return null}function Y(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let t=new Uint8Array(16);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function ct(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 K(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}">`}var Ct=0,vt=!1;function Ot(){return!vt&&typeof process<"u",`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),e=>e.toString(16).padStart(2,"0")).join(""):`c${(++Ct).toString(36)}_${Date.now().toString(36)}`}`}function xt(t,e={}){let n=e.id||Ot(),{onError:r,onSuccess:o,revalidate:s}=e;typeof window>"u"&&X.set(n,{fn:t,options:e});async function a(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,d=new AbortController,u=setTimeout(()=>d.abort(),c);try{let f=Et(),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)ft(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 dt(t,e={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=e;return async s=>{let a,i;s instanceof Event?(s.preventDefault(),i=s.target,a=new FormData(i)):a=s;let c={},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 ut(t){let e=A(!1),n=A(null),r=A(null);async function o(...s){e.set(!0),n.set(null);try{let a=await t(...s);return r.set(a),a}catch(a){throw n.set(a),a}finally{e.set(!1)}}return{trigger:o,isPending:()=>e(),error:()=>n(),data:()=>r(),reset:()=>{n.set(null),r.set(null)}}}function Pt(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=ut(dt(t,{resetOnSuccess:n}));function s(a){a.preventDefault();let i=new FormData(a.target);return r.current=a.target,o.trigger(i)}return{...o,handleSubmit:s,formRef:r}}function $t(t,e){let n=A(t),r=A([]),o=A(t);function s(d){let u=e(n.peek(),d);z(()=>{r.set([...r.peek(),d]),n.set(u)})}function a(d,u){z(()=>{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){z(()=>{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 Ht(t,e){return P.has(t)||P.set(t,new Set),P.get(t).add(e),()=>{P.get(t)?.delete(e)}}function ft(t){let e=P.get(t);if(e)for(let n of e)try{n()}catch(r){console.error("[what] Revalidation error:",r)}}function L(t,e,n,r={}){let{csrfToken:o,skipCsrf: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(!ct(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=X.get(e);return a?Array.isArray(n)?a.fn(...n).then(async i=>{let c=a.options||{};if(Array.isArray(c.revalidate))for(let d of c.revalidate)await W(d);if(Array.isArray(c.revalidateTags))for(let d of c.revalidateTags)await V(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 It(){return[...X.keys()]}function Lt(t,e={}){let{onSuccess:n,onError:r,onSettled:o}=e,s={isPending:A(!1),error:A(null),data:A(null)};async function a(...i){s.isPending.set(!0),s.error.set(null);try{let c=await t(...i);return s.data.set(c),n&&n(c,...i),c}catch(c){throw s.error.set(c),r&&r(c,...i),c}finally{s.isPending.set(!1),o&&o(s.data.peek(),s.error.peek(),...i)}}return{mutate:a,isPending:()=>s.isPending(),error:()=>s.error(),data:()=>s.data(),reset:()=>{s.error.set(null),s.data.set(null)}}}var jt="/__what_action",pt=1024*1024;function Dt(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 Z(t,e){return{status:t,headers:{"content-type":"text/html; charset=utf-8"},body:`<!DOCTYPE html><html><body><h1>${t}</h1><p>${String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}</p></body></html>`}}function lt(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 Nt(t,e){let n=lt(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),s=lt(o.pathname+o.search);if(s)return s}catch{}return"/"}var Mt=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=Dt(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 Z(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 Z(403,"Missing CSRF token");let C={};for(let[v,F]of Object.entries(h))Mt.has(v)||(C[v]=F);let R=await L({headers:b},g,[C],{csrfToken:u,skipCsrf:n});return R.status===200?{status:303,headers:{location:Nt(h,a)},body:""}:Z(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 L({headers:a},i,l,{csrfToken:u,skipCsrf:n});return j(p.status,p.body)}}function Ft(t={}){let e=t.basePath||jt,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 Ut(o);d=D(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 D(t,e){if((e||"").includes("application/x-www-form-urlencoded")){let n={};for(let[r,o]of new URLSearchParams(String(t)))n[r]===void 0?n[r]=o:Array.isArray(n[r])?n[r].push(o):n[r]=[n[r],o];return n}return t==null||t===""?{}:JSON.parse(String(t))}async function N(t,e=pt){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 Ut(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",s=>{if(r+=s.length,r>pt){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 Bt(t={}){let e=$(t);return async function(n){let r={};try{let a=await N(n);if(a.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=D(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 Jt,parseQuery as Wt}from"what-router/match";var Vt="/__what_action",zt="/__what_revalidate",G="what-csrf";function H(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function ht(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function Xt(t){try{let e=await N(t);return e.tooLarge?{tooLarge:!0}:D(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function Yt(t){let e;try{e=await N(t)}catch{return{json:{}}}if(e.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(e.raw)}}catch{return{json:{}}}}function Kt(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 yt(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=>ht(l.headers&&l.headers.cookie,G)}:{skipCsrf:!0}),f=r||Kt(s);return n&&(n.revalidatePath||n.revalidateTag)&&J({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===Vt){let y=await Xt(p);if(y&&y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let _=await u({method:"POST",headers:H(p.headers),body:y,query:Object.fromEntries(h.searchParams)});return new Response(_.body,{status:_.status,headers:_.headers})}let b=null,E=null;if(d&&(b=ht(H(p.headers).cookie,G),!b)){b=Y();let _=H(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;E=`${G}=${encodeURIComponent(b)}; Path=/; SameSite=Lax`+(_?"; Secure":"")}let C=y=>(E&&(y["set-cookie"]=E),y);if(p.method==="POST"&&g===zt&&o){let y=await Yt(p);if(y.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});let _=await o({headers:H(p.headers),body:y.json});return new Response(JSON.stringify(_.body),{status:_.status,headers:{"content-type":"application/json"}})}let R=Jt(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:F}=R,U=v.page||{mode:v.mode||"client"},x={path:g,query:Wt(h.search),config:U,route:v,params:F,request:p};if(n&&U.mode!=="server"){x.varyHeaders=H(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 ot=await f(x),at=C({"content-type":"text/html; charset=utf-8"});return U.mode==="server"&&(at["Cache-Control"]="private, no-store"),new Response(ot.html,{status:ot.status||200,headers:at})}}function Zt(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:Gt(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var wt=0;function Qt(){wt=0}function qt(){return"h"+wt++}function te(t){if(typeof document>"u"&&!gt()){let e=k(void 0);return S(e,()=>te(t))}return Qt(),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=qt(),d=t.tag({...t.props,children:t.children}),u=O(d);return ee(u,c)}let{tag:e,props:n,children:r}=t;et(e);let o=nt(n||{}),s=`<${e}${o}>`;if(rt.has(e))return s;let a=tt(n),i=a!=null?String(a):r.map(O).join("");return`${s}${i}</${e}>`}function ee(t,e){let n=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],s=r.length+1+o.length;return t.slice(0,s)+` data-hk="${e}"`+t.slice(s)}return t}function w(t){if(typeof document>"u"&&!gt()){let c=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;et(e);let o=nt(n||{}),s=`<${e}${o}>`;if(rt.has(e))return s;let a=tt(n),i=a!=null?String(a):r.map(w).join("");return`${s}${i}</${e}>`}function je(t){let e=k(void 0);return{body:S(e,()=>w(t)),head:q(e.head)}}async function De(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(mt(n,{...s,loaderData:r}))),head:q(o.head),loaderData:r}}var St=12;async function ne(t,e){e||(e=k(void 0));let n="";for(let o=0;o<St;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:q(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function yt(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 ne(mt(r,{...a,loaderData:o}),s),u={loaderData:o??null,resources:d,islandStores:st(s)};return re({body:i,head:c,payload:u,options:n})}function re({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",s=`<script id="__what_data" type="application/json">${B(n)}<\/script>`,a=r.clientEntry?`<script type="module" src="${m(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?K(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*I(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*I(a,e);return}if(typeof t=="function"){try{let a=S(e,()=>t());yield*I(a,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let a of t)yield*I(a,e);return}if(t.tag==="__suspense"){let a=null;for(let i=0;i<St&&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*I(i,e)}catch(a){typeof process<"u",yield M?`<!-- SSR Error: ${m(a.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t;et(n);let s=nt(r||{});if(yield`<${n}${s}>`,!rt.has(n)){let a=tt(r);if(a!=null)yield String(a);else for(let i of o)yield*I(i,e);yield`</${n}>`}}function Ne(t){return{mode:"static",...t}}function Me(t,e={}){let n=k(e),r=S(n,()=>{let s=t.component(e);return w(s)}),o=t.islands||[];return oe({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function oe({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(`
|
|
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,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}">`}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,"&").replace(/</g,"<").replace(/>/g,">")}</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
2
|
`),c=s.map(l=>`<link rel="stylesheet" href="${m(l)}">`).join(`
|
|
3
3
|
`),d=r.length>0?`
|
|
4
4
|
<script type="module">
|
|
@@ -21,4 +21,4 @@ import{h as mt,getServerContext as gt,runWithServerContext as S,beginHeadCollect
|
|
|
21
21
|
${u}
|
|
22
22
|
${f}
|
|
23
23
|
</body>
|
|
24
|
-
</html>`}function
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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};
|
package/dist/islands.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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){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="csrf-token"> tag or set data-no-csrf="true" on the form to opt out.'),r.dispatchEvent(new CustomEvent("form:error",{bubbles:!0,detail:{error:new Error("Missing CSRF token")}}));return}let 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 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};
|
package/dist/node.min.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{AsyncLocalStorage as Pe}from"node:async_hooks";import{__installServerContextStorage as $e}from"what-core";import{h as St,getServerContext as bt,runWithServerContext as b,beginHeadCollection as ae,endHeadCollection as nt}from"what-core";var Ct=new RegExp("[<>&\\u2028\\u2029]","g"),xt={60:"\\u003c",62:"\\u003e",38:"\\u0026",8232:"\\u2028",8233:"\\u2029"};function $(t){return JSON.stringify(t).replace(Ct,e=>xt[e.charCodeAt(0)])}import{mount as Ie,hydrate as Ne,signal as Le,batch as De,getServerContext as Ot}from"what-core";var Pt=new Map;function $t(t){let e=t||Ot();return e&&e.islandStores instanceof Map?e.islandStores:null}function lt(t){let e=typeof document<"u"?Pt:$t(t),n={};for(let[r,o]of e||[])n[r]=o._getSnapshot();return n}import{signal as _,batch as K}from"what-core";var R=null,ft=!(typeof process<"u");function Y(t){R=t}function Ht(){return R}async function X(t,e){if(R&&R.revalidatePath)return R.revalidatePath(t,e);ft&&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 G(t,e){if(R&&R.revalidateTag)return R.revalidateTag(t,e);ft&&console.warn(`[what] revalidateTag('${t}') had no effect: no cache engine is bound.`)}var Z=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 pt(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 q(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}">`}var It=0,Nt=!1;function Lt(){return!Nt&&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${(++It).toString(36)}_${Date.now().toString(36)}`}`}function Dt(t,e={}){let n=e.id||Lt(),{onError:r,onSuccess:o,revalidate:s}=e;typeof window>"u"&&Z.set(n,{fn:t,options:e});async function a(...i){if(typeof window>"u")return t(...i);let c=e.timeout||3e4,u=new AbortController,d=setTimeout(()=>u.abort(),c);try{let l=jt(),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 y=await p.json().catch(()=>({message:"Action failed"}));throw new Error(y.message||"Action failed")}let h=await p.json();if(o&&o(h),s)for(let y of s)mt(y);return h}catch(l){if(l.name==="AbortError"){let f=new Error(`Action "${n}" timed out after ${c}ms`);throw f.code="TIMEOUT",r&&r(f),f}throw r&&r(l),l}finally{clearTimeout(d)}}return a._actionId=n,a._isAction=!0,a}function ht(t,e={}){let{onSuccess:n,onError:r,resetOnSuccess:o=!0}=e;return async s=>{let a,i;s instanceof Event?(s.preventDefault(),i=s.target,a=new FormData(i)):a=s;let c={},u=!1;for(let[d,l]of a.entries())typeof File<"u"&&l instanceof File&&(u=!0),c[d]?Array.isArray(c[d])?c[d].push(l):c[d]=[c[d],l]:c[d]=l;try{let d=u?await t(c,a):await t(c);return n&&n(d,i),o&&i&&i.reset(),d}catch(d){throw r&&r(d,i),d}}}function yt(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 Mt(t,e={}){let{resetOnSuccess:n=!0}=e,r={current:null},o=yt(ht(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 Ft(t,e){let n=_(t),r=_([]),o=_(t);function s(u){let d=e(n.peek(),u);K(()=>{r.set([...r.peek(),u]),n.set(d)})}function a(u,d){K(()=>{if(r.set(r.peek().filter(l=>l!==u)),d!==void 0){o.set(d);let l=d;for(let f of r.peek())l=e(l,f);n.set(l)}})}function i(u,d){K(()=>{let l=r.peek().filter(h=>h!==u);r.set(l);let f=d!==void 0?d:o.peek();o.set(f);let p=f;for(let h of l)p=e(p,h);n.set(p)})}async function c(u,d){s(u);try{let l=await d();return a(u,l),l}catch(l){throw i(u),l}}return{value:()=>n(),isPending:()=>r().length>0,addOptimistic:s,resolve:a,rollback:i,withOptimistic:c,set:u=>{n.set(u),o.set(u)}}}var H=new Map;function Ut(t,e){return H.has(t)||H.set(t,new Set),H.get(t).add(e),()=>{H.get(t)?.delete(e)}}function mt(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: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(!pt(i,o))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let a=Z.get(e);return a?Array.isArray(n)?a.fn(...n).then(async i=>{let c=a.options||{};if(Array.isArray(c.revalidate))for(let u of c.revalidate)await X(u);if(Array.isArray(c.revalidateTags))for(let u of c.revalidateTags)await G(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 Bt(){return[...Z.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)}}}var Vt="/__what_action",j=1024*1024;function Wt(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 M(t,e){return{status:t,headers:{"content-type":"application/json"},body:JSON.stringify(e)}}function tt(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,"&").replace(/</g,"<").replace(/>/g,">")}</p></body></html>`}}function gt(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=gt(t&&t._redirect);if(n)return n;let r=e.referer||e.referrer;if(r)try{let o=new URL(r,"http://localhost"),s=gt(o.pathname+o.search);if(s)return s}catch{}return"/"}var Yt=new Set(["_action","data-action","_csrf","what-csrf-token","_redirect"]);function I(t={}){let{getCsrfToken:e,skipCsrf:n=!1}=t;return async function(o){if((o.method||"POST").toUpperCase()!=="POST")return M(405,{message:"Method Not Allowed"});let a=Wt(o.headers),i=a["x-what-action"],c=a["content-type"]||"",u=!i&&c.includes("application/x-www-form-urlencoded"),d=n?void 0:e?await e(o):void 0;if(u){let h=o.body||{},y=h._action||h["data-action"]||o.query&&o.query.action;if(!y)return tt(400,'Missing action name (add a hidden "_action" field or ?action= query param)');let S={...a},v=h._csrf??h["what-csrf-token"];if(v&&!S["x-csrf-token"]&&(S["x-csrf-token"]=String(v)),!n&&e&&!d)return tt(403,"Missing CSRF token");let C={};for(let[x,W]of Object.entries(h))Yt.has(x)||(C[x]=W);let E=await D({headers:S},y,[C],{csrfToken:d,skipCsrf:n});return E.status===200?{status:303,headers:{location:zt(h,a)},body:""}:tt(E.status,E.body&&E.body.message||"Action failed")}if(!i)return M(400,{message:"Missing X-What-Action header"});if(!n&&e&&!d)return M(403,{message:"Missing CSRF token"});let f=(o.body||{}).args,p=await D({headers:a},i,f,{csrfToken:d,skipCsrf:n});return M(p.status,p.body)}}function Xt(t={}){let e=t.basePath||Vt,n=I(t);return async function(o,s,a){let[i,c]=(o.url||"").split("?");if(i!==e||(o.method||"").toUpperCase()!=="POST")return a?a():void 0;let u;try{let f=await Gt(o);u=F(f,o.headers["content-type"]||"")}catch(f){s.writeHead(f.code==="BODY_TOO_LARGE"?413:400,{"content-type":"application/json"}),s.end(JSON.stringify({message:f.code==="BODY_TOO_LARGE"?"Payload too large":"Invalid request body"}));return}let d=Object.fromEntries(new URLSearchParams(c||"")),l=await n({method:o.method,headers:o.headers,body:u,query:d});s.writeHead(l.status,l.headers),s.end(l.body)}}function F(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 U(t,e=j){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 Gt(t){return new Promise((e,n)=>{let r=0,o=[];t.on("data",s=>{if(r+=s.length,r>j){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 Kt(t={}){let e=I(t);return async function(n){let r={};try{let a=await U(n);if(a.tooLarge)return new Response(JSON.stringify({message:"Payload too large"}),{status:413,headers:{"content-type":"application/json"}});r=F(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 Zt,parseQuery as Qt}from"what-router/match";var qt="/__what_action",te="/__what_revalidate",et="what-csrf";function N(t){let e={};return t&&typeof t.forEach=="function"&&t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e}function wt(t,e){if(!t)return null;let n=String(t).match(new RegExp(`(?:^|;\\s*)${e}=([^;]+)`));return n?decodeURIComponent(n[1]):null}async function ee(t){try{let e=await U(t);return e.tooLarge?{tooLarge:!0}:F(e.raw,t.headers.get("content-type")||"")}catch{return{}}}async function ne(t){let e;try{e=await U(t)}catch{return{json:{}}}if(e.tooLarge)return{tooLarge:!0};try{return{json:JSON.parse(e.raw)}}catch{return{json:{}}}}function re(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 B(i,{params:o,query:s,request:a},c),status:200,tags:n.config&&n.config.tags||[],path:n.path}}}function T(t={}){let{routes:e=[],cache:n,render:r,revalidateWebhook:o,document:s={},notFound:a,basePath:i="",csrf:c=!0}=t,u=c!==!1&&!t.actionHandler,d=t.actionHandler||I(u?{getCsrfToken:f=>wt(f.headers&&f.headers.cookie,et)}:{skipCsrf:!0}),l=r||re(s);return n&&(n.revalidatePath||n.revalidateTag)&&Y({revalidatePath:n.revalidatePath,revalidateTag:n.revalidateTag}),async function(p){let h=new URL(p.url,"http://localhost"),y=h.pathname;if(i&&y.startsWith(i)&&(y=y.slice(i.length)||"/"),p.method==="POST"&&y===qt){let m=await ee(p);if(m&&m.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:m,query:Object.fromEntries(h.searchParams)});return new Response(A.body,{status:A.status,headers:A.headers})}let S=null,v=null;if(u&&(S=wt(N(p.headers).cookie,et),!S)){S=Q();let A=N(p.headers)["x-forwarded-proto"]==="https"||h.protocol==="https:"||!0;v=`${et}=${encodeURIComponent(S)}; Path=/; SameSite=Lax`+(A?"; Secure":"")}let C=m=>(v&&(m["set-cookie"]=v),m);if(p.method==="POST"&&y===te&&o){let m=await ne(p);if(m.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:m.json});return new Response(JSON.stringify(A.body),{status:A.status,headers:{"content-type":"application/json"}})}let E=Zt(y,e);if(!E){let m=a?a():"<!DOCTYPE html><html><body><h1>404 \u2014 Not Found</h1></body></html>";return new Response(m,{status:404,headers:C({"content-type":"text/html; charset=utf-8"})})}let{route:x,params:W}=E,z=x.page||{mode:x.mode||"client"},P={path:y,query:Qt(h.search),config:z,route:x,params:W,request:p};if(n&&z.mode!=="server"){P.varyHeaders=N(p.headers);let m=await n.handle(P,()=>l(P));return new Response(m.html,{status:m.status||200,headers:C({"content-type":"text/html; charset=utf-8",...m.headers||{}})})}S&&(P.csrfToken=S);let ut=await l(P),dt=C({"content-type":"text/html; charset=utf-8"});return z.mode==="server"&&(dt["Cache-Control"]="private, no-store"),new Response(ut.html,{status:ut.status||200,headers:dt})}}function oe(t={}){let e=T(t);return{async fetch(n,r,o){return r&&(n.__env=r),o&&(n.__ctx=o),e(n)}}}function k(t){return{head:ae(),loaderData:t,resources:new Map,islandStores:new Map,resourceCounter:0,boundaryCounter:0,suspended:[]}}var _t=0;function se(){_t=0}function ie(){return"h"+_t++}function ce(t){if(typeof document>"u"&&!bt()){let e=k(void 0);return b(e,()=>ce(t))}return se(),O(t)}function O(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return g(String(t));if(typeof t=="function"&&t._signal)return`<!--$-->${O(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${O(t())}<!--/$-->`}catch{return typeof process<"u","<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${t.map(O).join("")}<!--/[]-->`;if(typeof t.tag=="function"){let c=ie(),u=t.tag({...t.props,children:t.children}),d=O(u);return ue(d,c)}let{tag:e,props:n,children:r}=t;ot(e);let o=at(n||{}),s=`<${e}${o}>`;if(st.has(e))return s;let a=rt(n),i=a!=null?String(a):r.map(O).join("");return`${s}${i}</${e}>`}function ue(t,e){let n=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(n){let r=n[1],o=n[2],s=r.length+1+o.length;return t.slice(0,s)+` data-hk="${e}"`+t.slice(s)}return t}function w(t){if(typeof document>"u"&&!bt()){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;ot(e);let o=at(n||{}),s=`<${e}${o}>`;if(st.has(e))return s;let a=rt(n),i=a!=null?String(a):r.map(w).join("");return`${s}${i}</${e}>`}function on(t){let e=k(void 0);return{body:b(e,()=>w(t)),head:nt(e.head)}}async function an(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:b(o,()=>w(St(n,{...s,loaderData:r}))),head:nt(o.head),loaderData:r}}var At=12;async function de(t,e){e||(e=k(void 0));let n="";for(let o=0;o<At;o++){n=b(e,()=>w(t));let s=[...e.resources.values()].filter(a=>a.status==="pending").map(a=>a.promise);if(s.length===0)break;await Promise.all(s)}let r={};for(let[o,s]of e.resources)s.status==="ready"&&(r[o]=s.value);return{body:n,head:nt(e.head),loaderData:e.loaderData,resources:r,ctx:e}}async function B(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:u}=await de(St(r,{...a,loaderData:o}),s),d={loaderData:o??null,resources:u,islandStores:lt(s)};return le({body:i,head:c,payload:d,options:n})}function le({body:t,head:e,payload:n,options:r={}}){let o=r.lang||"en",s=`<script id="__what_data" type="application/json">${$(n)}<\/script>`,a=r.clientEntry?`<script type="module" src="${g(r.clientEntry)}"><\/script>`:"",c=(r.csrfToken?q(r.csrfToken):"")+(r.head||""),u=r.bodyClass?` class="${g(r.bodyClass)}"`:"";return`<!DOCTYPE html><html lang="${g(o)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${e||""}${c}</head><body${u}>${t}${s}${a}</body></html>`}async function*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 a=b(e,()=>t());yield*L(a,e);return}if(typeof t=="function"){try{let a=b(e,()=>t());yield*L(a,e)}catch{typeof process<"u"}return}if(Array.isArray(t)){for(let a of t)yield*L(a,e);return}if(t.tag==="__suspense"){let a=null;for(let i=0;i<At&&a===null;i++){let c=null;try{a=b(e,()=>(t.children||[]).map(w).join(""))}catch(u){if(u&&typeof u.then=="function")c=u;else throw u}if(a===null){let u=[...e.resources.values()].filter(d=>d.status==="pending").map(d=>d.promise);await Promise.all([c,...u].filter(Boolean))}}a===null&&(a=b(e,()=>w(t.props&&t.props.fallback))),yield a;return}if(typeof t.tag=="function"){try{let a=b(e,()=>t.tag({...t.props,children:t.children})),i=a instanceof Promise?await a:a;yield*L(i,e)}catch(a){typeof process<"u",yield J?`<!-- SSR Error: ${g(a.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:n,props:r,children:o}=t;ot(n);let s=at(r||{});if(yield`<${n}${s}>`,!st.has(n)){let a=rt(r);if(a!=null)yield String(a);else for(let i of o)yield*L(i,e);yield`</${n}>`}}function sn(t){return{mode:"static",...t}}function cn(t,e={}){let n=k(e),r=b(n,()=>{let s=t.component(e);return w(s)}),o=t.islands||[];return fe({title:t.title||"",meta:t.meta||{},body:r,islands:o,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode})}function fe({title:t,meta:e,body:n,islands:r,scripts:o,styles:s,mode:a}){let i=Object.entries(e).map(([f,p])=>`<meta name="${g(f)}" content="${g(p)}">`).join(`
|
|
2
|
-
`),c=
|
|
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,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}">`}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,"&").replace(/</g,"<").replace(/>/g,">")}</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(`
|
|
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(
|
|
8
|
-
`),
|
|
7
|
+
<\/script>`:"",d=o.map(l=>`<script type="module" src="${g(l)}"><\/script>`).join(`
|
|
8
|
+
`),f=s==="client"?`
|
|
9
9
|
<script type="module" src="/@what/client.js"><\/script>`:"";return`<!DOCTYPE html>
|
|
10
10
|
<html lang="en">
|
|
11
11
|
<head>
|
|
@@ -19,6 +19,6 @@ import{AsyncLocalStorage as Pe}from"node:async_hooks";import{__installServerCont
|
|
|
19
19
|
<div id="app">${n}</div>
|
|
20
20
|
${u}
|
|
21
21
|
${d}
|
|
22
|
-
${
|
|
22
|
+
${f}
|
|
23
23
|
</body>
|
|
24
|
-
</html>`}function
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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};
|
package/index.d.ts
CHANGED
|
@@ -54,35 +54,14 @@ export function server<P>(component: (props: P) => VNode): (props: P) => VNode;
|
|
|
54
54
|
//
|
|
55
55
|
// The island runtime (island, Island, hydrateIslands, createIslandStore, …)
|
|
56
56
|
// is exported from the `what-server/islands` subpath, not from this entry.
|
|
57
|
-
// See ./islands.d.ts. The shapes
|
|
58
|
-
// that cross the SSR boundary and are referenced by root-entry consumers.
|
|
59
|
-
|
|
60
|
-
export interface IslandOptions {
|
|
61
|
-
/** Hydration mode */
|
|
62
|
-
mode?: 'static' | 'idle' | 'visible' | 'load' | 'media' | 'action';
|
|
63
|
-
/** Media query for 'media' mode */
|
|
64
|
-
media?: string;
|
|
65
|
-
/** Priority (higher = hydrate first) */
|
|
66
|
-
priority?: number;
|
|
67
|
-
/** Shared stores this island uses */
|
|
68
|
-
stores?: string[];
|
|
69
|
-
}
|
|
57
|
+
// See ./islands.d.ts. The shapes are re-exported here because they describe
|
|
58
|
+
// values that cross the SSR boundary and are referenced by root-entry consumers.
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
_hydrate: (data: Partial<T>) => void;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface IslandStatus {
|
|
80
|
-
registered: string[];
|
|
81
|
-
hydrated: number;
|
|
82
|
-
pending: number;
|
|
83
|
-
queue: { name: string; priority: number }[];
|
|
84
|
-
stores: string[];
|
|
85
|
-
}
|
|
60
|
+
// One definition each, owned by ./islands. Re-exporting rather than
|
|
61
|
+
// redeclaring is what keeps `export * from 'what-server'` and
|
|
62
|
+
// `export * from 'what-server/islands'` in the same file from colliding, which
|
|
63
|
+
// is exactly the ambiguity what-framework/server hits.
|
|
64
|
+
export type { IslandOptions, IslandStore, IslandStatus } from './islands.js';
|
|
86
65
|
|
|
87
66
|
// --- Server Actions ---
|
|
88
67
|
|
|
@@ -189,3 +168,125 @@ export function handleActionRequest(
|
|
|
189
168
|
): Promise<ActionResponse>;
|
|
190
169
|
|
|
191
170
|
export function getRegisteredActions(): string[];
|
|
171
|
+
|
|
172
|
+
// --- Additional SSR entry points ---
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Render with hydration markers (data-hk attributes and comment boundaries) so
|
|
176
|
+
* the client can adopt the server DOM instead of recreating it.
|
|
177
|
+
*/
|
|
178
|
+
export function renderToHydratableString(vnode: VNode): string;
|
|
179
|
+
|
|
180
|
+
/** Render, and return the <head> tags collected during that render alongside the body. */
|
|
181
|
+
export function renderToStringWithHead(vnode: VNode): { body: string; head: string };
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Render, awaiting any suspended resources so their data is resolved in the
|
|
185
|
+
* output. `resources` is the payload the client reuses so it does not refetch.
|
|
186
|
+
*/
|
|
187
|
+
export function renderToStringAsync(
|
|
188
|
+
vnode: VNode,
|
|
189
|
+
ctx?: unknown,
|
|
190
|
+
): Promise<{ body: string; head: string; resources: Record<string, unknown> }>;
|
|
191
|
+
|
|
192
|
+
export interface DocumentOptions {
|
|
193
|
+
lang?: string;
|
|
194
|
+
head?: string;
|
|
195
|
+
bodyAttrs?: string;
|
|
196
|
+
scripts?: string[];
|
|
197
|
+
styles?: string[];
|
|
198
|
+
[key: string]: any;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Render a page module to a complete HTML document: runs its loader, renders the
|
|
203
|
+
* component, collects head tags, and inlines the hydration payload.
|
|
204
|
+
*/
|
|
205
|
+
export function renderDocument(
|
|
206
|
+
pageModule: { default: (props: any) => VNode; loader?: (ctx: RenderRequestContext) => any } | ((props: any) => VNode),
|
|
207
|
+
reqCtx?: RenderRequestContext,
|
|
208
|
+
options?: DocumentOptions,
|
|
209
|
+
): Promise<string>;
|
|
210
|
+
|
|
211
|
+
/** Render a PageConfig to a complete static HTML document. */
|
|
212
|
+
export function generateStaticPage(page: PageConfig, data?: any): string;
|
|
213
|
+
|
|
214
|
+
// --- <Form> ---
|
|
215
|
+
// A real <form method="post"> that posts to the action endpoint, so it submits
|
|
216
|
+
// with JavaScript disabled and is enhanced to a fetch when JS is present.
|
|
217
|
+
|
|
218
|
+
export interface FormProps {
|
|
219
|
+
/** A server action (from `action()`) or its id. */
|
|
220
|
+
action: ((...args: any[]) => any) | string;
|
|
221
|
+
/** Per-request CSRF token. Required for SSR; read from the page when omitted. */
|
|
222
|
+
csrfToken?: string | null;
|
|
223
|
+
/** Where to send the browser after a no-JS submit. */
|
|
224
|
+
redirect?: string;
|
|
225
|
+
method?: string;
|
|
226
|
+
/** Set false to leave a plain HTML form the client enhancer ignores. */
|
|
227
|
+
enhance?: boolean;
|
|
228
|
+
children?: VNodeChild;
|
|
229
|
+
[attr: string]: any;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function Form(props: FormProps): VNode;
|
|
233
|
+
|
|
234
|
+
/** The endpoint server actions post to. */
|
|
235
|
+
export const ACTION_ENDPOINT: string;
|
|
236
|
+
|
|
237
|
+
// --- CSRF ---
|
|
238
|
+
|
|
239
|
+
/** Generate a CSRF token. Call once per session/request. */
|
|
240
|
+
export function generateCsrfToken(): string;
|
|
241
|
+
|
|
242
|
+
/** Constant-time comparison of a request token against the session token. */
|
|
243
|
+
export function validateCsrfToken(requestToken: string | null | undefined, sessionToken: string | null | undefined): boolean;
|
|
244
|
+
|
|
245
|
+
/** The `<meta name="what-csrf-token">` tag to inline into the document head. */
|
|
246
|
+
export function csrfMetaTag(token: string): string;
|
|
247
|
+
|
|
248
|
+
// --- Action handlers ---
|
|
249
|
+
// Runtime-neutral core, plus the two host bindings.
|
|
250
|
+
|
|
251
|
+
export interface ActionHandlerOptions {
|
|
252
|
+
csrfSecret?: string;
|
|
253
|
+
onError?: (error: unknown) => void;
|
|
254
|
+
[key: string]: any;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function createActionHandler(options?: ActionHandlerOptions): (request: any) => Promise<any>;
|
|
258
|
+
export function nodeActionMiddleware(options?: ActionHandlerOptions): (req: any, res: any, next?: () => void) => void;
|
|
259
|
+
export function fetchActionHandler(options?: ActionHandlerOptions): (request: Request) => Promise<Response>;
|
|
260
|
+
|
|
261
|
+
// --- Deploy adapters ---
|
|
262
|
+
|
|
263
|
+
export interface RequestHandlerOptions {
|
|
264
|
+
routes?: any[];
|
|
265
|
+
documentOptions?: DocumentOptions;
|
|
266
|
+
[key: string]: any;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Runtime-neutral request handler: Request in, Response out. */
|
|
270
|
+
export function createRequestHandler(options?: RequestHandlerOptions): (request: Request, env?: any, ctx?: any) => Promise<Response>;
|
|
271
|
+
|
|
272
|
+
/** Cloudflare Workers entry wrapping createRequestHandler. */
|
|
273
|
+
export function createCloudflareHandler(options?: RequestHandlerOptions): { fetch: (request: Request, env?: any, ctx?: any) => Promise<Response> };
|
|
274
|
+
|
|
275
|
+
// --- Revalidation registry ---
|
|
276
|
+
// App code calls revalidatePath/revalidateTag; the deploy adapter binds a
|
|
277
|
+
// what-isr engine through setRevalidationHandler.
|
|
278
|
+
|
|
279
|
+
export interface RevalidationHandler {
|
|
280
|
+
revalidatePath?: (path: string, options?: any) => any;
|
|
281
|
+
revalidateTag?: (tag: string, options?: any) => any;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function setRevalidationHandler(handler: RevalidationHandler | null): void;
|
|
285
|
+
export function getRevalidationHandler(): RevalidationHandler | null;
|
|
286
|
+
export function revalidatePath(path: string, options?: any): Promise<any>;
|
|
287
|
+
export function revalidateTag(tag: string, options?: any): Promise<any>;
|
|
288
|
+
|
|
289
|
+
// --- Serialization ---
|
|
290
|
+
|
|
291
|
+
/** Serialize a value for safe inlining into a <script> tag (escapes `</script`). */
|
|
292
|
+
export function serializeState(value: unknown): string;
|
package/node.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// what-server/node — declarations for the package's `node` export condition
|
|
2
|
+
// (src/node.js). These entry points import node:http and node:fs, so they exist
|
|
3
|
+
// only under Node. Declaring them in index.d.ts instead would promise every
|
|
4
|
+
// browser and edge consumer six functions their runtime does not have.
|
|
5
|
+
|
|
6
|
+
export * from './index.js';
|
|
7
|
+
import type { RequestHandlerOptions, RenderRequestContext, DocumentOptions } from './index.js';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/** Convert a Web-Fetch handler into a Node (req, res) listener. */
|
|
11
|
+
export function toNodeListener(
|
|
12
|
+
handler: (request: Request) => Promise<Response> | Response,
|
|
13
|
+
): (req: any, res: any) => Promise<void>;
|
|
14
|
+
|
|
15
|
+
/** connect/express middleware. Calls next() on a 404 so other routes can answer. */
|
|
16
|
+
export function whatMiddleware(options?: RequestHandlerOptions): (req: any, res: any, next?: () => void) => Promise<void>;
|
|
17
|
+
|
|
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;
|
|
20
|
+
|
|
21
|
+
/** Vercel Functions entry: a Web-Fetch handler. */
|
|
22
|
+
export function createVercelHandler(options?: RequestHandlerOptions): (request: Request) => Promise<Response>;
|
|
23
|
+
|
|
24
|
+
export interface VercelOutputOptions {
|
|
25
|
+
outDir?: string;
|
|
26
|
+
functionName?: string;
|
|
27
|
+
runtime?: string;
|
|
28
|
+
files?: Record<string, string> | null;
|
|
29
|
+
handler?: string;
|
|
30
|
+
staticDir?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Write a Vercel Build Output API v3 directory. */
|
|
34
|
+
export function buildVercelOutput(options?: VercelOutputOptions): Promise<{ outDir: string; written: string[] }>;
|
|
35
|
+
|
|
36
|
+
export interface ExportStaticOptions {
|
|
37
|
+
routes?: any[];
|
|
38
|
+
outDir: string;
|
|
39
|
+
render?: (pageModule: any, reqCtx: RenderRequestContext) => Promise<string> | string;
|
|
40
|
+
documentOptions?: DocumentOptions;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Pre-render every `static` and `hybrid` route to files under `outDir`. */
|
|
44
|
+
export function exportStatic(options?: ExportStaticOptions): Promise<string[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "What Framework - SSR, islands architecture, static generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./index.d.ts",
|
|
11
11
|
"node": {
|
|
12
|
+
"types": "./node.d.ts",
|
|
12
13
|
"production": "./dist/node.min.js",
|
|
13
14
|
"import": "./src/node.js",
|
|
14
15
|
"default": "./src/node.js"
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"src",
|
|
34
35
|
"dist/**/*.min.js",
|
|
35
36
|
"index.d.ts",
|
|
37
|
+
"node.d.ts",
|
|
36
38
|
"islands.d.ts"
|
|
37
39
|
],
|
|
38
40
|
"sideEffects": [
|
|
@@ -49,8 +51,8 @@
|
|
|
49
51
|
"author": "ZVN DEV (https://zvndev.com)",
|
|
50
52
|
"license": "MIT",
|
|
51
53
|
"peerDependencies": {
|
|
52
|
-
"what-core": "^0.
|
|
53
|
-
"what-router": "^0.
|
|
54
|
+
"what-core": "^0.12.1",
|
|
55
|
+
"what-router": "^0.12.1"
|
|
54
56
|
},
|
|
55
57
|
"peerDependenciesMeta": {
|
|
56
58
|
"what-router": {
|
package/src/form.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// What Framework - <Form>
|
|
2
|
+
//
|
|
3
|
+
// The no-JS server-action path works and is covered by the scaffold smoke test,
|
|
4
|
+
// but reaching it meant hand-writing four exact things per form: the endpoint,
|
|
5
|
+
// the `_action` id, the `what-csrf-token` field, and the `_redirect`. Getting the
|
|
6
|
+
// CSRF field name wrong is silent: the form posts, the double-submit check
|
|
7
|
+
// rejects it, and nothing on the page says why. This component is the one piece
|
|
8
|
+
// that was missing between the forms module and a path that already worked.
|
|
9
|
+
//
|
|
10
|
+
// <Form action={createPost} redirect="/">
|
|
11
|
+
// <input name="title" required />
|
|
12
|
+
// <button>Publish</button>
|
|
13
|
+
// </Form>
|
|
14
|
+
//
|
|
15
|
+
// Renders a real <form method="post">, so it submits with JavaScript disabled.
|
|
16
|
+
// With JS, the client enhancer intercepts it and posts without a page reload.
|
|
17
|
+
|
|
18
|
+
import { h } from 'what-core';
|
|
19
|
+
|
|
20
|
+
export const ACTION_ENDPOINT = '/__what_action';
|
|
21
|
+
|
|
22
|
+
// The action id lives on the callable wrapper that action() returns. Accepting
|
|
23
|
+
// the function itself is the point: passing the id as a string means keeping two
|
|
24
|
+
// spellings in sync by hand, which is the failure this component exists to stop.
|
|
25
|
+
function resolveActionId(action) {
|
|
26
|
+
if (typeof action === 'string') return action;
|
|
27
|
+
if (typeof action === 'function') {
|
|
28
|
+
const id = action._actionId;
|
|
29
|
+
if (id) return id;
|
|
30
|
+
throw new Error(
|
|
31
|
+
'[what] <Form action={fn}>: that function is not a server action. ' +
|
|
32
|
+
'Wrap it with action() from what-server, or pass the action id as a string.'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
throw new Error('[what] <Form> requires an `action` prop: a server action or its id.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// On the server there is no document, so the per-request token must be handed in
|
|
39
|
+
// (typically from a loader). On the client it can be recovered from the page.
|
|
40
|
+
function readClientCsrfToken() {
|
|
41
|
+
if (typeof document === 'undefined') return null;
|
|
42
|
+
const meta = document.querySelector('meta[name="what-csrf-token"]');
|
|
43
|
+
if (meta) return meta.getAttribute('content');
|
|
44
|
+
const match = document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);
|
|
45
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function Form({
|
|
49
|
+
action,
|
|
50
|
+
csrfToken,
|
|
51
|
+
redirect,
|
|
52
|
+
method = 'post',
|
|
53
|
+
enhance = true,
|
|
54
|
+
children,
|
|
55
|
+
...rest
|
|
56
|
+
}) {
|
|
57
|
+
const actionId = resolveActionId(action);
|
|
58
|
+
const token = csrfToken !== undefined ? csrfToken : readClientCsrfToken();
|
|
59
|
+
|
|
60
|
+
if (
|
|
61
|
+
token == null
|
|
62
|
+
&& typeof process !== 'undefined'
|
|
63
|
+
&& process.env?.NODE_ENV !== 'production'
|
|
64
|
+
) {
|
|
65
|
+
console.warn(
|
|
66
|
+
`[what] <Form action="${actionId}"> has no CSRF token. Server-rendered forms must ` +
|
|
67
|
+
'receive one (`csrfToken` is passed to loaders), otherwise the double-submit ' +
|
|
68
|
+
'check rejects the POST and the failure is silent.'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const hidden = [
|
|
73
|
+
h('input', { type: 'hidden', name: '_action', value: actionId }),
|
|
74
|
+
h('input', { type: 'hidden', name: 'what-csrf-token', value: token || '' }),
|
|
75
|
+
];
|
|
76
|
+
if (redirect) {
|
|
77
|
+
hidden.push(h('input', { type: 'hidden', name: '_redirect', value: redirect }));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const props = {
|
|
81
|
+
...rest,
|
|
82
|
+
method,
|
|
83
|
+
action: rest.action || ACTION_ENDPOINT,
|
|
84
|
+
'data-action': actionId,
|
|
85
|
+
};
|
|
86
|
+
// `data-enhance` is the selector enhanceForms() looks for. Opting out leaves a
|
|
87
|
+
// plain HTML form, which still works: that is the whole point of the design.
|
|
88
|
+
if (enhance) props['data-enhance'] = '';
|
|
89
|
+
|
|
90
|
+
const kids = children == null ? [] : (Array.isArray(children) ? children : [children]);
|
|
91
|
+
return h('form', props, ...hidden, ...kids);
|
|
92
|
+
}
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
h,
|
|
7
|
+
_isAriaAttr,
|
|
7
8
|
getServerContext,
|
|
8
9
|
runWithServerContext,
|
|
9
10
|
beginHeadCollection,
|
|
@@ -42,7 +43,14 @@ function nextHydrationId() {
|
|
|
42
43
|
// so the client can reuse the server-rendered DOM.
|
|
43
44
|
|
|
44
45
|
export function renderToHydratableString(vnode) {
|
|
45
|
-
|
|
46
|
+
// Establish a render scope whenever one isn't already active. This used to be
|
|
47
|
+
// gated on `typeof document === 'undefined'` as a proxy for "are we on the
|
|
48
|
+
// server", but renderToString IS the server render, and the proxy is wrong
|
|
49
|
+
// under any DOM shim (jsdom, happy-dom, a Workers polyfill) where SSR ran with
|
|
50
|
+
// no scope at all and render-scoped state fell back to module globals. Every
|
|
51
|
+
// other consumer of the context checks `typeof document` itself before reading
|
|
52
|
+
// it, so a scope existing on the client changes nothing for them.
|
|
53
|
+
if (!getServerContext()) {
|
|
46
54
|
const ctx = createRenderContext(undefined);
|
|
47
55
|
return runWithServerContext(ctx, () => renderToHydratableString(vnode));
|
|
48
56
|
}
|
|
@@ -120,7 +128,9 @@ function injectHydrationKey(html, hkId) {
|
|
|
120
128
|
// Renders a VNode tree to an HTML string. Used for SSR and static gen.
|
|
121
129
|
|
|
122
130
|
export function renderToString(vnode) {
|
|
123
|
-
|
|
131
|
+
// See renderToHydratableString: the scope is established by "no scope yet",
|
|
132
|
+
// not by "no document".
|
|
133
|
+
if (!getServerContext()) {
|
|
124
134
|
const ctx = createRenderContext(undefined);
|
|
125
135
|
return runWithServerContext(ctx, () => renderToString(vnode));
|
|
126
136
|
}
|
|
@@ -533,6 +543,14 @@ function renderAttrs(props) {
|
|
|
533
543
|
if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;
|
|
534
544
|
const lowerKey = key.toLowerCase();
|
|
535
545
|
if (lowerKey.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR
|
|
546
|
+
// aria-*/role are enumerated, so `false` is a real value and must survive:
|
|
547
|
+
// an absent `aria-expanded` means "unsupported", `aria-expanded="false"`
|
|
548
|
+
// means "collapsed". Every other attribute keeps HTML boolean semantics,
|
|
549
|
+
// where false means omit. Checked before the falsy skip for that reason.
|
|
550
|
+
if (val === false && _isAriaAttr(key)) {
|
|
551
|
+
out += ` ${key}="false"`;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
536
554
|
if (val === false || val == null) continue;
|
|
537
555
|
if (!SAFE_ATTR_NAME.test(key)) {
|
|
538
556
|
if (_isDevMode) {
|
|
@@ -625,6 +643,9 @@ export {
|
|
|
625
643
|
csrfMetaTag,
|
|
626
644
|
} from './actions.js';
|
|
627
645
|
|
|
646
|
+
// <Form>: the progressive-enhancement form that posts to the action endpoint.
|
|
647
|
+
export { Form, ACTION_ENDPOINT } from './form.js';
|
|
648
|
+
|
|
628
649
|
// Served server actions: wire the /__what_action route (Node + fetch adapters)
|
|
629
650
|
export {
|
|
630
651
|
createActionHandler,
|
package/src/islands.js
CHANGED
|
@@ -311,6 +311,11 @@ export function hydrateIslands() {
|
|
|
311
311
|
const islands = document.querySelectorAll('[data-island]');
|
|
312
312
|
|
|
313
313
|
for (const el of islands) {
|
|
314
|
+
// Compiler-emitted islands (`<Counter client:idle />`) carry a direct
|
|
315
|
+
// component reference and schedule their own hydration, so they are not in
|
|
316
|
+
// the registry and must not be claimed here.
|
|
317
|
+
if (el.dataset.islandSelf) continue;
|
|
318
|
+
|
|
314
319
|
const name = el.dataset.island;
|
|
315
320
|
const mode = el.dataset.islandMode || 'idle';
|
|
316
321
|
const props = JSON.parse(el.dataset.islandProps || '{}');
|
|
@@ -489,7 +494,8 @@ export function enhanceForms(selector = 'form[data-enhance]') {
|
|
|
489
494
|
if (!csrfToken && !noCsrf) {
|
|
490
495
|
console.warn(
|
|
491
496
|
'[what] Form submission blocked: no CSRF token found. ' +
|
|
492
|
-
'Add a <meta name="csrf-token"> tag
|
|
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.'
|
|
493
499
|
);
|
|
494
500
|
form.dispatchEvent(new CustomEvent('form:error', {
|
|
495
501
|
bubbles: true,
|