@webtypen/webframez-react 0.0.25 → 0.0.28

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/README.md CHANGED
@@ -14,7 +14,7 @@ React Server Components (RSC) extension for `@webtypen/webframez-core`.
14
14
 
15
15
  - Node.js `>= 20`
16
16
  - `@webtypen/webframez-core`
17
- - React Experimental (matching the package peer dependencies)
17
+ - React 19.x with matching `react-dom` and `react-server-dom-webpack`
18
18
 
19
19
  ## Installation
20
20
 
@@ -130,6 +130,25 @@ Route.renderReact("/app", {
130
130
  - Additional route options forwarded to `webframez-core`.
131
131
  - Typical use case: middleware.
132
132
 
133
+ ## `Head()` Assets
134
+
135
+ Inside a page, layout, or error `Head()` method you can set `basename`.
136
+ That basename is applied to `favicon` and `links[].href`, and also updates the
137
+ router basename used by `Link`, `Redirect`, and client-side navigation.
138
+
139
+ ```ts
140
+ export function Head() {
141
+ return {
142
+ title: "Dashboard",
143
+ basename: "/app",
144
+ favicon: "/favicon.ico",
145
+ links: [{ rel: "stylesheet", href: "/styles/dashboard.css" }],
146
+ };
147
+ }
148
+ ```
149
+
150
+ Absolute URLs like `https://...` or `//...` stay unchanged.
151
+
133
152
  ## Recommended Project Structure
134
153
 
135
154
  ```txt
package/dist/client.cjs CHANGED
@@ -1 +1 @@
1
- var T=Object.create;var f=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var H=(e,t)=>{for(var n in t)f(e,n,{get:t[n],enumerable:!0})},v=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of U(t))!M.call(e,i)&&i!==n&&f(e,i,{get:()=>t[i],enumerable:!(o=N(t,i))||o.enumerable});return e};var W=(e,t,n)=>(n=e!=null?T($(e)):{},v(t||!e||!e.__esModule?f(n,"default",{value:e,enumerable:!0}):n,e)),D=e=>v(f({},"__esModule",{value:!0}),e);var Q={};H(Q,{mountWebframezClient:()=>J,useCookie:()=>Y,useRouter:()=>q});module.exports=D(Q);var r=W(require("react"),1),S=require("react-dom/client"),u=require("react-server-dom-webpack/client"),s=require("react/jsx-runtime"),F={push:()=>{},replace:()=>{},refresh:()=>{}},p=typeof r.default.createContext=="function"?r.default.createContext(null):null,k="__WEBFRAMEZ_ROUTER__",B="[data-webframez-head='true']";function z(){return typeof window>"u"?null:window[k]??null}function G(e){typeof window>"u"||(window[k]=e)}function A(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let i=o.indexOf("="),c=i>=0?o.slice(0,i).trim():o,d=i>=0?o.slice(i+1):"";c&&(e[c]=decodeURIComponent(d))}return e}function x(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function Y(){return r.default.useMemo(()=>({all:()=>A(),get:e=>A()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=x(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=x(e,"",{...t??{},maxAge:0}))}}),[])}function q(){let e=p?r.default.useContext(p):null;if(!e){let t=z();if(t)return t;if(typeof window>"u")return F;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function Z({active:e}){return(0,s.jsx)("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function E(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function I(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function b(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(B))t.remove();e.description&&E({name:"description",content:e.description}),e.favicon&&I({rel:"icon",href:e.favicon});for(let t of e.meta??[])E(t);for(let t of e.links??[])I(t)}}function j(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function X(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,(0,u.createFromReadableStream)(j(e)))}function K(e){return X()??(0,u.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))}function V(e,t){return function(){let o=r.default.use(e),[i,c]=(0,r.useState)(o.model),[d,g]=(0,r.useState)(o.head),[P,w]=(0,r.useState)(!1),[L,O]=(0,r.useState)(!1);async function l(a,C="push"){w(!0);try{let m=await(0,u.createFromFetch)(fetch(`${t}?path=${encodeURIComponent(a.pathname)}&search=${encodeURIComponent(a.search)}`,{headers:{Accept:"text/x-component"}}));b(m.head),g(m.head),c(m.model);let _=`${a.pathname}${a.search}`;C==="replace"?history.replaceState(null,"",_):C==="push"&&history.pushState(null,"",_)}catch(y){console.error("[webframez-react] Failed to render route",y),c((0,s.jsx)("p",{children:"Failed to load route."}))}finally{w(!1)}}(0,r.useEffect)(()=>{b(d)},[d]),(0,r.useEffect)(()=>{O(!0);let a=()=>{l(new URL(window.location.href),"none")};return window.addEventListener("popstate",a),()=>{window.removeEventListener("popstate",a)}},[]);let R=r.default.useMemo(()=>({push:a=>{l(new URL(a,window.location.origin),"push")},replace:a=>{l(new URL(a,window.location.origin),"replace")},refresh:()=>{l(new URL(window.location.href),"none")}}),[]);G(R);let h=(0,s.jsxs)(s.Fragment,{children:[L?(0,s.jsx)(Z,{active:P}):null,i??(0,s.jsx)("p",{style:{padding:24},children:"Loading..."})]});return p?(0,s.jsx)(p.Provider,{value:R,children:h}):h}}function J(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,i=e.rscEndpoint??o??"/rsc",c=K(i),d=V(c,i);return(0,S.hydrateRoot)(n,(0,s.jsx)(d,{}))}
1
+ var N=Object.create;var m=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var n in t)m(e,n,{get:t[n],enumerable:!0})},v=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of z(t))!M.call(e,r)&&r!==n&&m(e,r,{get:()=>t[r],enumerable:!(o=U(t,r))||o.enumerable});return e};var D=(e,t,n)=>(n=e!=null?N(B(e)):{},v(t||!e||!e.__esModule?m(n,"default",{value:e,enumerable:!0}):n,e)),F=e=>v(m({},"__esModule",{value:!0}),e);var oe={};W(oe,{mountWebframezClient:()=>ne,useCookie:()=>X,useRouter:()=>K});module.exports=F(oe);var i=D(require("react"),1),L=require("react-dom/client"),l=require("react-server-dom-webpack/client");var G=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function u(e){let t=e?.trim();if(t)return t==="/"?"/":t.replace(/\/+$/,"")}function x(e,t){let n=e.trim(),o=u(t);return!n||!o||G.test(n)||o!=="/"&&(n===o||n.startsWith(`${o}/`))?n:o==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${o}${n}`:`${o}/${n}`}function E(e,t){if(!e)return;let n=u(e.basename)??u(t),o={...e,...n?{basename:n}:{}};return o.favicon&&(o.favicon=x(o.favicon,n)),o.links&&(o.links=o.links.map(r=>({...r,href:x(r.href,n)}))),o}var s=require("react/jsx-runtime"),Y={push:()=>{},replace:()=>{},refresh:()=>{}},p=typeof i.default.createContext=="function"?i.default.createContext(null):null,O="__WEBFRAMEZ_ROUTER__",Z="[data-webframez-head='true']";function q(){return typeof window>"u"?null:window[O]??null}function j(e){typeof window>"u"||(window[O]=e)}function S(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let r=o.indexOf("="),d=r>=0?o.slice(0,r).trim():o,c=r>=0?o.slice(r+1):"";d&&(e[d]=decodeURIComponent(c))}return e}function b(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function X(){return i.default.useMemo(()=>({all:()=>S(),get:e=>S()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=b(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=b(e,"",{...t??{},maxAge:0}))}}),[])}function K(){let e=p?i.default.useContext(p):null;if(!e){let t=q();if(t)return t;if(typeof window>"u")return Y;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function V({active:e}){return(0,s.jsx)("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function k(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of n)t.setAttribute(o,String(r));document.head.appendChild(t)}function I(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of n)t.setAttribute(o,String(r));document.head.appendChild(t)}function P(e){if(typeof document>"u")return;let t=E(e)??e,n=u(t.basename);window.__RSC_BASENAME=n??"",document.title=t.title||"Webframez React";for(let o of document.head.querySelectorAll(Z))o.remove();t.description&&k({name:"description",content:t.description}),t.favicon&&I({rel:"icon",href:t.favicon});for(let o of t.meta??[])k(o);for(let o of t.links??[])I(o)}function J(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function Q(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,(0,l.createFromReadableStream)(J(e)))}function ee(e){return Q()??(0,l.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))}function te(e,t){return function(){let o=i.default.use(e),[r,d]=(0,i.useState)(o.model),[c,w]=(0,i.useState)(o.head),[T,h]=(0,i.useState)(!1),[$,H]=(0,i.useState)(!1);async function f(a,_="push"){h(!0);try{let g=await(0,l.createFromFetch)(fetch(`${t}?path=${encodeURIComponent(a.pathname)}&search=${encodeURIComponent(a.search)}`,{headers:{Accept:"text/x-component"}}));P(g.head),w(g.head),d(g.model);let y=`${a.pathname}${a.search}`;_==="replace"?history.replaceState(null,"",y):_==="push"&&history.pushState(null,"",y)}catch(A){console.error("[webframez-react] Failed to render route",A),d((0,s.jsx)("p",{children:"Failed to load route."}))}finally{h(!1)}}(0,i.useEffect)(()=>{P(c)},[c]),(0,i.useEffect)(()=>{H(!0);let a=()=>{f(new URL(window.location.href),"none")};return window.addEventListener("popstate",a),()=>{window.removeEventListener("popstate",a)}},[]);let R=i.default.useMemo(()=>({push:a=>{f(new URL(a,window.location.origin),"push")},replace:a=>{f(new URL(a,window.location.origin),"replace")},refresh:()=>{f(new URL(window.location.href),"none")}}),[]);j(R);let C=(0,s.jsxs)(s.Fragment,{children:[$?(0,s.jsx)(V,{active:T}):null,r??(0,s.jsx)("p",{style:{padding:24},children:"Loading..."})]});return p?(0,s.jsx)(p.Provider,{value:R,children:C}):C}}function ne(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,r=e.rscEndpoint??o??"/rsc",d=ee(r),c=te(d,r);return(0,L.hydrateRoot)(n,(0,s.jsx)(c,{}))}
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- import d,{useEffect as _,useState as l}from"react";import{hydrateRoot as O}from"react-dom/client";import{createFromFetch as b,createFromReadableStream as T}from"react-server-dom-webpack/client";import{Fragment as z,jsx as c,jsxs as G}from"react/jsx-runtime";var N={push:()=>{},replace:()=>{},refresh:()=>{}},f=typeof d.createContext=="function"?d.createContext(null):null,S="__WEBFRAMEZ_ROUTER__",U="[data-webframez-head='true']";function $(){return typeof window>"u"?null:window[S]??null}function M(e){typeof window>"u"||(window[S]=e)}function v(){let e={},t=typeof document>"u"?"":document.cookie;if(!t||t.trim()==="")return e;for(let n of t.split(";")){let o=n.trim();if(!o)continue;let i=o.indexOf("="),a=i>=0?o.slice(0,i).trim():o,s=i>=0?o.slice(i+1):"";a&&(e[a]=decodeURIComponent(s))}return e}function A(e,t,n={}){let o=[`${e}=${encodeURIComponent(t)}`];return n.path&&o.push(`Path=${n.path}`),n.domain&&o.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&o.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&o.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&o.push(`SameSite=${n.sameSite}`),n.secure&&o.push("Secure"),o.join("; ")}function j(){return d.useMemo(()=>({all:()=>v(),get:e=>v()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=A(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=A(e,"",{...t??{},maxAge:0}))}}),[])}function X(){let e=f?d.useContext(f):null;if(!e){let t=$();if(t)return t;if(typeof window>"u")return N;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function H({active:e}){return c("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:e?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function x(e){let t=document.createElement("meta");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function E(e){let t=document.createElement("link");t.setAttribute("data-webframez-head","true");let n=Object.entries(e).filter(([,o])=>!!o);for(let[o,i]of n)t.setAttribute(o,String(i));document.head.appendChild(t)}function I(e){if(!(typeof document>"u")){document.title=e.title||"Webframez React";for(let t of document.head.querySelectorAll(U))t.remove();e.description&&x({name:"description",content:e.description}),e.favicon&&E({rel:"icon",href:e.favicon});for(let t of e.meta??[])x(t);for(let t of e.links??[])E(t)}}function W(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function D(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,T(W(e)))}function F(e){return D()??b(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))}function B(e,t){return function(){let o=d.use(e),[i,a]=l(o.model),[s,m]=l(o.head),[k,g]=l(!1),[P,L]=l(!1);async function u(r,h="push"){g(!0);try{let p=await b(fetch(`${t}?path=${encodeURIComponent(r.pathname)}&search=${encodeURIComponent(r.search)}`,{headers:{Accept:"text/x-component"}}));I(p.head),m(p.head),a(p.model);let y=`${r.pathname}${r.search}`;h==="replace"?history.replaceState(null,"",y):h==="push"&&history.pushState(null,"",y)}catch(C){console.error("[webframez-react] Failed to render route",C),a(c("p",{children:"Failed to load route."}))}finally{g(!1)}}_(()=>{I(s)},[s]),_(()=>{L(!0);let r=()=>{u(new URL(window.location.href),"none")};return window.addEventListener("popstate",r),()=>{window.removeEventListener("popstate",r)}},[]);let w=d.useMemo(()=>({push:r=>{u(new URL(r,window.location.origin),"push")},replace:r=>{u(new URL(r,window.location.origin),"replace")},refresh:()=>{u(new URL(window.location.href),"none")}}),[]);M(w);let R=G(z,{children:[P?c(H,{active:k}):null,i??c("p",{style:{padding:24},children:"Loading..."})]});return f?c(f.Provider,{value:w,children:R}):R}}function K(e={}){let t=e.rootId??"root",n=document.getElementById(t);if(!n)throw new Error(`Missing #${t} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,i=e.rscEndpoint??o??"/rsc",a=F(i),s=B(a,i);return O(n,c(s,{}))}export{K as mountWebframezClient,j as useCookie,X as useRouter};
1
+ import c,{useEffect as x,useState as f}from"react";import{hydrateRoot as N}from"react-dom/client";import{createFromFetch as P,createFromReadableStream as U}from"react-server-dom-webpack/client";var H=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function u(t){let o=t?.trim();if(o)return o==="/"?"/":o.replace(/\/+$/,"")}function y(t,o){let n=t.trim(),e=u(o);return!n||!e||H.test(n)||e!=="/"&&(n===e||n.startsWith(`${e}/`))?n:e==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${e}${n}`:`${e}/${n}`}function v(t,o){if(!t)return;let n=u(t.basename)??u(o),e={...t,...n?{basename:n}:{}};return e.favicon&&(e.favicon=y(e.favicon,n)),e.links&&(e.links=e.links.map(r=>({...r,href:y(r.href,n)}))),e}import{Fragment as q,jsx as d,jsxs as j}from"react/jsx-runtime";var z={push:()=>{},replace:()=>{},refresh:()=>{}},m=typeof c.createContext=="function"?c.createContext(null):null,L="__WEBFRAMEZ_ROUTER__",B="[data-webframez-head='true']";function M(){return typeof window>"u"?null:window[L]??null}function W(t){typeof window>"u"||(window[L]=t)}function E(){let t={},o=typeof document>"u"?"":document.cookie;if(!o||o.trim()==="")return t;for(let n of o.split(";")){let e=n.trim();if(!e)continue;let r=e.indexOf("="),a=r>=0?e.slice(0,r).trim():e,s=r>=0?e.slice(r+1):"";a&&(t[a]=decodeURIComponent(s))}return t}function S(t,o,n={}){let e=[`${t}=${encodeURIComponent(o)}`];return n.path&&e.push(`Path=${n.path}`),n.domain&&e.push(`Domain=${n.domain}`),typeof n.maxAge=="number"&&e.push(`Max-Age=${Math.floor(n.maxAge)}`),n.expires&&e.push(`Expires=${n.expires.toUTCString()}`),n.sameSite&&e.push(`SameSite=${n.sameSite}`),n.secure&&e.push("Secure"),e.join("; ")}function ee(){return c.useMemo(()=>({all:()=>E(),get:t=>E()[t],set:(t,o,n)=>{typeof document>"u"||(document.cookie=S(t,o,n))},remove:(t,o)=>{typeof document>"u"||(document.cookie=S(t,"",{...o??{},maxAge:0}))}}),[])}function te(){let t=m?c.useContext(m):null;if(!t){let o=M();if(o)return o;if(typeof window>"u")return z;throw new Error("useRouter must be used inside mountWebframezClient()")}return t}function D({active:t}){return d("div",{style:{position:"fixed",top:0,left:0,width:"100%",height:3,zIndex:9999,transformOrigin:"left",transform:t?"scaleX(1)":"scaleX(0)",transition:"transform 180ms ease",background:"linear-gradient(90deg, #0ea5e9, #22c55e)"}})}function b(t){let o=document.createElement("meta");o.setAttribute("data-webframez-head","true");let n=Object.entries(t).filter(([,e])=>!!e);for(let[e,r]of n)o.setAttribute(e,String(r));document.head.appendChild(o)}function k(t){let o=document.createElement("link");o.setAttribute("data-webframez-head","true");let n=Object.entries(t).filter(([,e])=>!!e);for(let[e,r]of n)o.setAttribute(e,String(r));document.head.appendChild(o)}function I(t){if(typeof document>"u")return;let o=v(t)??t,n=u(o.basename);window.__RSC_BASENAME=n??"",document.title=o.title||"Webframez React";for(let e of document.head.querySelectorAll(B))e.remove();o.description&&b({name:"description",content:o.description}),o.favicon&&k({rel:"icon",href:o.favicon});for(let e of o.meta??[])b(e);for(let e of o.links??[])k(e)}function F(t){let o=new TextEncoder;return new ReadableStream({start(n){n.enqueue(o.encode(t)),n.close()}})}function G(){if(typeof window>"u")return null;let t=window.__RSC_INITIAL_PAYLOAD;return typeof t!="string"||t===""?null:(delete window.__RSC_INITIAL_PAYLOAD,U(F(t)))}function Y(t){return G()??P(fetch(`${t}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))}function Z(t,o){return function(){let e=c.use(t),[r,a]=f(e.model),[s,g]=f(e.head),[O,w]=f(!1),[T,$]=f(!1);async function l(i,C="push"){w(!0);try{let p=await P(fetch(`${o}?path=${encodeURIComponent(i.pathname)}&search=${encodeURIComponent(i.search)}`,{headers:{Accept:"text/x-component"}}));I(p.head),g(p.head),a(p.model);let A=`${i.pathname}${i.search}`;C==="replace"?history.replaceState(null,"",A):C==="push"&&history.pushState(null,"",A)}catch(_){console.error("[webframez-react] Failed to render route",_),a(d("p",{children:"Failed to load route."}))}finally{w(!1)}}x(()=>{I(s)},[s]),x(()=>{$(!0);let i=()=>{l(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let h=c.useMemo(()=>({push:i=>{l(new URL(i,window.location.origin),"push")},replace:i=>{l(new URL(i,window.location.origin),"replace")},refresh:()=>{l(new URL(window.location.href),"none")}}),[]);W(h);let R=j(q,{children:[T?d(D,{active:O}):null,r??d("p",{style:{padding:24},children:"Loading..."})]});return m?d(m.Provider,{value:h,children:R}):R}}function ne(t={}){let o=t.rootId??"root",n=document.getElementById(o);if(!n)throw new Error(`Missing #${o} element`);let e=typeof window<"u"&&window.__RSC_ENDPOINT,r=t.rscEndpoint??e??"/rsc",a=Y(r),s=Z(a,r);return N(n,d(s,{}))}export{ne as mountWebframezClient,ee as useCookie,te as useRouter};
package/dist/http.cjs CHANGED
@@ -150,6 +150,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
150
150
  var import_node_module2 = __toESM(require("node:module"), 1);
151
151
  var import_node_path2 = __toESM(require("node:path"), 1);
152
152
 
153
+ // src/head.ts
154
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
155
+ function normalizeHeadBasename(value) {
156
+ const trimmed = value?.trim();
157
+ if (!trimmed) {
158
+ return void 0;
159
+ }
160
+ if (trimmed === "/") {
161
+ return "/";
162
+ }
163
+ return trimmed.replace(/\/+$/, "");
164
+ }
165
+ function resolveHeadAssetUrl(assetUrl, basename) {
166
+ const normalizedAssetUrl = assetUrl.trim();
167
+ const normalizedBasename = normalizeHeadBasename(basename);
168
+ if (!normalizedAssetUrl || !normalizedBasename) {
169
+ return normalizedAssetUrl;
170
+ }
171
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
172
+ return normalizedAssetUrl;
173
+ }
174
+ if (normalizedBasename !== "/" && (normalizedAssetUrl === normalizedBasename || normalizedAssetUrl.startsWith(`${normalizedBasename}/`))) {
175
+ return normalizedAssetUrl;
176
+ }
177
+ if (normalizedBasename === "/") {
178
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
179
+ }
180
+ if (normalizedAssetUrl.startsWith("/")) {
181
+ return `${normalizedBasename}${normalizedAssetUrl}`;
182
+ }
183
+ return `${normalizedBasename}/${normalizedAssetUrl}`;
184
+ }
185
+ function normalizeHeadConfig(head, inheritedBasename) {
186
+ if (!head) {
187
+ return void 0;
188
+ }
189
+ const effectiveBasename = normalizeHeadBasename(head.basename) ?? normalizeHeadBasename(inheritedBasename);
190
+ const normalizedHead = {
191
+ ...head,
192
+ ...effectiveBasename ? { basename: effectiveBasename } : {}
193
+ };
194
+ if (normalizedHead.favicon) {
195
+ normalizedHead.favicon = resolveHeadAssetUrl(
196
+ normalizedHead.favicon,
197
+ effectiveBasename
198
+ );
199
+ }
200
+ if (normalizedHead.links) {
201
+ normalizedHead.links = normalizedHead.links.map((link) => ({
202
+ ...link,
203
+ href: resolveHeadAssetUrl(link.href, effectiveBasename)
204
+ }));
205
+ }
206
+ return normalizedHead;
207
+ }
208
+
153
209
  // src/router-runtime.tsx
154
210
  var import_react = __toESM(require("react"), 1);
155
211
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -365,7 +421,8 @@ function mergeHead(...configs) {
365
421
  meta: [],
366
422
  links: []
367
423
  };
368
- for (const config of configs) {
424
+ for (const candidate of configs) {
425
+ const config = normalizeHeadConfig(candidate, merged.basename);
369
426
  if (!config) {
370
427
  continue;
371
428
  }
@@ -375,6 +432,9 @@ function mergeHead(...configs) {
375
432
  if (config.description) {
376
433
  merged.description = config.description;
377
434
  }
435
+ if (config.basename) {
436
+ merged.basename = config.basename;
437
+ }
378
438
  if (config.favicon) {
379
439
  merged.favicon = config.favicon;
380
440
  }
@@ -388,22 +448,23 @@ function mergeHead(...configs) {
388
448
  return merged;
389
449
  }
390
450
  function renderHeadToString(head) {
451
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
391
452
  const tags = [];
392
- if (head.description) {
453
+ if (normalizedHead.description) {
393
454
  tags.push(
394
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
455
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
395
456
  );
396
457
  }
397
- if (head.favicon) {
458
+ if (normalizedHead.favicon) {
398
459
  tags.push(
399
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
460
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
400
461
  );
401
462
  }
402
- for (const meta of head.meta ?? []) {
463
+ for (const meta of normalizedHead.meta ?? []) {
403
464
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
404
465
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
405
466
  }
406
- for (const link of head.links ?? []) {
467
+ for (const link of normalizedHead.links ?? []) {
407
468
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
408
469
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
409
470
  }
@@ -619,6 +680,12 @@ function createFileRouter(options) {
619
680
  params: {},
620
681
  searchParams: input.searchParams,
621
682
  cookies: input.cookies ?? {},
683
+ request: input.request ?? {
684
+ host: null,
685
+ pathname,
686
+ originalPathname: pathname,
687
+ headers: {}
688
+ },
622
689
  abort: (options2) => {
623
690
  throw createAbort(options2);
624
691
  }
@@ -846,6 +913,60 @@ function normalizeBasePath(basePath) {
846
913
  const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
847
914
  return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
848
915
  }
916
+ function normalizeRequestHostValue(value) {
917
+ const trimmed = value.trim();
918
+ if (!trimmed) {
919
+ return null;
920
+ }
921
+ const firstValue = trimmed.split(",")[0]?.trim() || "";
922
+ if (!firstValue) {
923
+ return null;
924
+ }
925
+ if (firstValue.startsWith("[")) {
926
+ const closingIndex = firstValue.indexOf("]");
927
+ if (closingIndex > 0) {
928
+ return firstValue.slice(1, closingIndex).toLowerCase();
929
+ }
930
+ }
931
+ return firstValue.replace(/:\d+$/, "").toLowerCase();
932
+ }
933
+ function getRequestHost(req) {
934
+ const candidates = [
935
+ req.headers["x-forwarded-host"],
936
+ req.headers["x-original-host"],
937
+ req.headers.host
938
+ ];
939
+ for (const candidate of candidates) {
940
+ if (Array.isArray(candidate)) {
941
+ for (const singleValue of candidate) {
942
+ if (typeof singleValue !== "string") {
943
+ continue;
944
+ }
945
+ const normalized2 = normalizeRequestHostValue(singleValue);
946
+ if (normalized2) {
947
+ return normalized2;
948
+ }
949
+ }
950
+ continue;
951
+ }
952
+ if (typeof candidate !== "string") {
953
+ continue;
954
+ }
955
+ const normalized = normalizeRequestHostValue(candidate);
956
+ if (normalized) {
957
+ return normalized;
958
+ }
959
+ }
960
+ return null;
961
+ }
962
+ function createRouteRequestContext(req, pathname, originalPathname) {
963
+ return {
964
+ host: getRequestHost(req),
965
+ pathname,
966
+ originalPathname,
967
+ headers: req.headers
968
+ };
969
+ }
849
970
  function stripBasePath(pathname, basePath) {
850
971
  if (!basePath) {
851
972
  return pathname;
@@ -1191,13 +1312,19 @@ function createNodeRequestHandler(options) {
1191
1312
  }
1192
1313
  if (url.pathname === rscPath) {
1193
1314
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1315
+ const requestContext = createRouteRequestContext(
1316
+ req,
1317
+ pathname,
1318
+ url.searchParams.get("path") || "/"
1319
+ );
1194
1320
  const search = new URLSearchParams(url.searchParams.get("search") || "");
1195
1321
  const resolved2 = await withRequestBasename(
1196
1322
  basePath,
1197
1323
  () => router.resolve({
1198
1324
  pathname,
1199
1325
  searchParams: parseSearchParams(search),
1200
- cookies: requestCookies
1326
+ cookies: requestCookies,
1327
+ request: requestContext
1201
1328
  })
1202
1329
  );
1203
1330
  const payload = {
@@ -1237,7 +1364,12 @@ function createNodeRequestHandler(options) {
1237
1364
  () => router.resolve({
1238
1365
  pathname: stripBasePath(url.pathname, basePath),
1239
1366
  searchParams: parseSearchParams(url.searchParams),
1240
- cookies: requestCookies
1367
+ cookies: requestCookies,
1368
+ request: createRouteRequestContext(
1369
+ req,
1370
+ stripBasePath(url.pathname, basePath),
1371
+ url.pathname
1372
+ )
1241
1373
  })
1242
1374
  );
1243
1375
  const initialPayload = {
@@ -1281,7 +1413,7 @@ function createNodeRequestHandler(options) {
1281
1413
  rscEndpoint: rscPath,
1282
1414
  rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
1283
1415
  initialFlightData,
1284
- basename: basePath,
1416
+ basename: resolved?.head?.basename ?? basePath,
1285
1417
  liveReloadPath: liveReloadPath || void 0,
1286
1418
  liveReloadServerId: liveReloadPath ? devServerId : void 0
1287
1419
  })
@@ -1300,7 +1432,7 @@ function createNodeRequestHandler(options) {
1300
1432
  rscEndpoint: rscPath,
1301
1433
  rootHtml,
1302
1434
  initialFlightData,
1303
- basename: basePath,
1435
+ basename: resolved.head.basename ?? basePath,
1304
1436
  liveReloadPath: liveReloadPath || void 0,
1305
1437
  liveReloadServerId: liveReloadPath ? devServerId : void 0
1306
1438
  })
package/dist/http.js CHANGED
@@ -125,6 +125,62 @@ import fs from "node:fs";
125
125
  import Module from "node:module";
126
126
  import path2 from "node:path";
127
127
 
128
+ // src/head.ts
129
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
130
+ function normalizeHeadBasename(value) {
131
+ const trimmed = value?.trim();
132
+ if (!trimmed) {
133
+ return void 0;
134
+ }
135
+ if (trimmed === "/") {
136
+ return "/";
137
+ }
138
+ return trimmed.replace(/\/+$/, "");
139
+ }
140
+ function resolveHeadAssetUrl(assetUrl, basename) {
141
+ const normalizedAssetUrl = assetUrl.trim();
142
+ const normalizedBasename = normalizeHeadBasename(basename);
143
+ if (!normalizedAssetUrl || !normalizedBasename) {
144
+ return normalizedAssetUrl;
145
+ }
146
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
147
+ return normalizedAssetUrl;
148
+ }
149
+ if (normalizedBasename !== "/" && (normalizedAssetUrl === normalizedBasename || normalizedAssetUrl.startsWith(`${normalizedBasename}/`))) {
150
+ return normalizedAssetUrl;
151
+ }
152
+ if (normalizedBasename === "/") {
153
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
154
+ }
155
+ if (normalizedAssetUrl.startsWith("/")) {
156
+ return `${normalizedBasename}${normalizedAssetUrl}`;
157
+ }
158
+ return `${normalizedBasename}/${normalizedAssetUrl}`;
159
+ }
160
+ function normalizeHeadConfig(head, inheritedBasename) {
161
+ if (!head) {
162
+ return void 0;
163
+ }
164
+ const effectiveBasename = normalizeHeadBasename(head.basename) ?? normalizeHeadBasename(inheritedBasename);
165
+ const normalizedHead = {
166
+ ...head,
167
+ ...effectiveBasename ? { basename: effectiveBasename } : {}
168
+ };
169
+ if (normalizedHead.favicon) {
170
+ normalizedHead.favicon = resolveHeadAssetUrl(
171
+ normalizedHead.favicon,
172
+ effectiveBasename
173
+ );
174
+ }
175
+ if (normalizedHead.links) {
176
+ normalizedHead.links = normalizedHead.links.map((link) => ({
177
+ ...link,
178
+ href: resolveHeadAssetUrl(link.href, effectiveBasename)
179
+ }));
180
+ }
181
+ return normalizedHead;
182
+ }
183
+
128
184
  // src/router-runtime.tsx
129
185
  import React from "react";
130
186
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -340,7 +396,8 @@ function mergeHead(...configs) {
340
396
  meta: [],
341
397
  links: []
342
398
  };
343
- for (const config of configs) {
399
+ for (const candidate of configs) {
400
+ const config = normalizeHeadConfig(candidate, merged.basename);
344
401
  if (!config) {
345
402
  continue;
346
403
  }
@@ -350,6 +407,9 @@ function mergeHead(...configs) {
350
407
  if (config.description) {
351
408
  merged.description = config.description;
352
409
  }
410
+ if (config.basename) {
411
+ merged.basename = config.basename;
412
+ }
353
413
  if (config.favicon) {
354
414
  merged.favicon = config.favicon;
355
415
  }
@@ -363,22 +423,23 @@ function mergeHead(...configs) {
363
423
  return merged;
364
424
  }
365
425
  function renderHeadToString(head) {
426
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
366
427
  const tags = [];
367
- if (head.description) {
428
+ if (normalizedHead.description) {
368
429
  tags.push(
369
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
430
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
370
431
  );
371
432
  }
372
- if (head.favicon) {
433
+ if (normalizedHead.favicon) {
373
434
  tags.push(
374
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
435
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
375
436
  );
376
437
  }
377
- for (const meta of head.meta ?? []) {
438
+ for (const meta of normalizedHead.meta ?? []) {
378
439
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
379
440
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
380
441
  }
381
- for (const link of head.links ?? []) {
442
+ for (const link of normalizedHead.links ?? []) {
382
443
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
383
444
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
384
445
  }
@@ -594,6 +655,12 @@ function createFileRouter(options) {
594
655
  params: {},
595
656
  searchParams: input.searchParams,
596
657
  cookies: input.cookies ?? {},
658
+ request: input.request ?? {
659
+ host: null,
660
+ pathname,
661
+ originalPathname: pathname,
662
+ headers: {}
663
+ },
597
664
  abort: (options2) => {
598
665
  throw createAbort(options2);
599
666
  }
@@ -821,6 +888,60 @@ function normalizeBasePath(basePath) {
821
888
  const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
822
889
  return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
823
890
  }
891
+ function normalizeRequestHostValue(value) {
892
+ const trimmed = value.trim();
893
+ if (!trimmed) {
894
+ return null;
895
+ }
896
+ const firstValue = trimmed.split(",")[0]?.trim() || "";
897
+ if (!firstValue) {
898
+ return null;
899
+ }
900
+ if (firstValue.startsWith("[")) {
901
+ const closingIndex = firstValue.indexOf("]");
902
+ if (closingIndex > 0) {
903
+ return firstValue.slice(1, closingIndex).toLowerCase();
904
+ }
905
+ }
906
+ return firstValue.replace(/:\d+$/, "").toLowerCase();
907
+ }
908
+ function getRequestHost(req) {
909
+ const candidates = [
910
+ req.headers["x-forwarded-host"],
911
+ req.headers["x-original-host"],
912
+ req.headers.host
913
+ ];
914
+ for (const candidate of candidates) {
915
+ if (Array.isArray(candidate)) {
916
+ for (const singleValue of candidate) {
917
+ if (typeof singleValue !== "string") {
918
+ continue;
919
+ }
920
+ const normalized2 = normalizeRequestHostValue(singleValue);
921
+ if (normalized2) {
922
+ return normalized2;
923
+ }
924
+ }
925
+ continue;
926
+ }
927
+ if (typeof candidate !== "string") {
928
+ continue;
929
+ }
930
+ const normalized = normalizeRequestHostValue(candidate);
931
+ if (normalized) {
932
+ return normalized;
933
+ }
934
+ }
935
+ return null;
936
+ }
937
+ function createRouteRequestContext(req, pathname, originalPathname) {
938
+ return {
939
+ host: getRequestHost(req),
940
+ pathname,
941
+ originalPathname,
942
+ headers: req.headers
943
+ };
944
+ }
824
945
  function stripBasePath(pathname, basePath) {
825
946
  if (!basePath) {
826
947
  return pathname;
@@ -1166,13 +1287,19 @@ function createNodeRequestHandler(options) {
1166
1287
  }
1167
1288
  if (url.pathname === rscPath) {
1168
1289
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1290
+ const requestContext = createRouteRequestContext(
1291
+ req,
1292
+ pathname,
1293
+ url.searchParams.get("path") || "/"
1294
+ );
1169
1295
  const search = new URLSearchParams(url.searchParams.get("search") || "");
1170
1296
  const resolved2 = await withRequestBasename(
1171
1297
  basePath,
1172
1298
  () => router.resolve({
1173
1299
  pathname,
1174
1300
  searchParams: parseSearchParams(search),
1175
- cookies: requestCookies
1301
+ cookies: requestCookies,
1302
+ request: requestContext
1176
1303
  })
1177
1304
  );
1178
1305
  const payload = {
@@ -1212,7 +1339,12 @@ function createNodeRequestHandler(options) {
1212
1339
  () => router.resolve({
1213
1340
  pathname: stripBasePath(url.pathname, basePath),
1214
1341
  searchParams: parseSearchParams(url.searchParams),
1215
- cookies: requestCookies
1342
+ cookies: requestCookies,
1343
+ request: createRouteRequestContext(
1344
+ req,
1345
+ stripBasePath(url.pathname, basePath),
1346
+ url.pathname
1347
+ )
1216
1348
  })
1217
1349
  );
1218
1350
  const initialPayload = {
@@ -1256,7 +1388,7 @@ function createNodeRequestHandler(options) {
1256
1388
  rscEndpoint: rscPath,
1257
1389
  rootHtml: createInitialHtmlErrorMarkup("Initial HTML render failed."),
1258
1390
  initialFlightData,
1259
- basename: basePath,
1391
+ basename: resolved?.head?.basename ?? basePath,
1260
1392
  liveReloadPath: liveReloadPath || void 0,
1261
1393
  liveReloadServerId: liveReloadPath ? devServerId : void 0
1262
1394
  })
@@ -1275,7 +1407,7 @@ function createNodeRequestHandler(options) {
1275
1407
  rscEndpoint: rscPath,
1276
1408
  rootHtml,
1277
1409
  initialFlightData,
1278
- basename: basePath,
1410
+ basename: resolved.head.basename ?? basePath,
1279
1411
  liveReloadPath: liveReloadPath || void 0,
1280
1412
  liveReloadServerId: liveReloadPath ? devServerId : void 0
1281
1413
  })