@webtypen/webframez-react 0.0.25 → 0.0.27

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 `assetsBaseUrl`.
136
+ That base URL is applied to `favicon` and `links[].href`, which is useful for CSS
137
+ files and other head assets served from a CDN or shared asset host.
138
+
139
+ ```ts
140
+ export function Head() {
141
+ return {
142
+ title: "Dashboard",
143
+ assetsBaseUrl: "https://cdn.example.com/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 H=Object.create;var f=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var W=(e,n)=>{for(var t in n)f(e,t,{get:n[t],enumerable:!0})},v=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of z(n))!M.call(e,r)&&r!==t&&f(e,r,{get:()=>n[r],enumerable:!(o=N(n,r))||o.enumerable});return e};var D=(e,n,t)=>(t=e!=null?H(B(e)):{},v(n||!e||!e.__esModule?f(t,"default",{value:e,enumerable:!0}):t,e)),F=e=>v(f({},"__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"),u=require("react-server-dom-webpack/client");var G=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function g(e){let n=e?.trim();if(n)return n==="/"?"/":n.replace(/\/+$/,"")}function x(e,n){let t=e.trim(),o=g(n);return!t||!o||G.test(t)||o!=="/"&&(t===o||t.startsWith(`${o}/`))?t:o==="/"?t.startsWith("/")?t:`/${t}`:t.startsWith("/")?`${o}${t}`:`${o}/${t}`}function E(e,n){if(!e)return;let t=g(e.assetsBaseUrl)??g(n),o={...e,...t?{assetsBaseUrl:t}:{}};return o.favicon&&(o.favicon=x(o.favicon,t)),o.links&&(o.links=o.links.map(r=>({...r,href:x(r.href,t)}))),o}var s=require("react/jsx-runtime"),Y={push:()=>{},replace:()=>{},refresh:()=>{}},p=typeof i.default.createContext=="function"?i.default.createContext(null):null,U="__WEBFRAMEZ_ROUTER__",Z="[data-webframez-head='true']";function q(){return typeof window>"u"?null:window[U]??null}function j(e){typeof window>"u"||(window[U]=e)}function S(){let e={},n=typeof document>"u"?"":document.cookie;if(!n||n.trim()==="")return e;for(let t of n.split(";")){let o=t.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 k(e,n,t={}){let o=[`${e}=${encodeURIComponent(n)}`];return t.path&&o.push(`Path=${t.path}`),t.domain&&o.push(`Domain=${t.domain}`),typeof t.maxAge=="number"&&o.push(`Max-Age=${Math.floor(t.maxAge)}`),t.expires&&o.push(`Expires=${t.expires.toUTCString()}`),t.sameSite&&o.push(`SameSite=${t.sameSite}`),t.secure&&o.push("Secure"),o.join("; ")}function X(){return i.default.useMemo(()=>({all:()=>S(),get:e=>S()[e],set:(e,n,t)=>{typeof document>"u"||(document.cookie=k(e,n,t))},remove:(e,n)=>{typeof document>"u"||(document.cookie=k(e,"",{...n??{},maxAge:0}))}}),[])}function K(){let e=p?i.default.useContext(p):null;if(!e){let n=q();if(n)return n;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 I(e){let n=document.createElement("meta");n.setAttribute("data-webframez-head","true");let t=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of t)n.setAttribute(o,String(r));document.head.appendChild(n)}function b(e){let n=document.createElement("link");n.setAttribute("data-webframez-head","true");let t=Object.entries(e).filter(([,o])=>!!o);for(let[o,r]of t)n.setAttribute(o,String(r));document.head.appendChild(n)}function P(e){if(typeof document>"u")return;let n=E(e)??e;document.title=n.title||"Webframez React";for(let t of document.head.querySelectorAll(Z))t.remove();n.description&&I({name:"description",content:n.description}),n.favicon&&b({rel:"icon",href:n.favicon});for(let t of n.meta??[])I(t);for(let t of n.links??[])b(t)}function J(e){let n=new TextEncoder;return new ReadableStream({start(t){t.enqueue(n.encode(e)),t.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,u.createFromReadableStream)(J(e)))}function ee(e){return Q()??(0,u.createFromFetch)(fetch(`${e}?path=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}`,{headers:{Accept:"text/x-component"}}))}function te(e,n){return function(){let o=i.default.use(e),[r,d]=(0,i.useState)(o.model),[c,h]=(0,i.useState)(o.head),[O,w]=(0,i.useState)(!1),[T,$]=(0,i.useState)(!1);async function l(a,A="push"){w(!0);try{let m=await(0,u.createFromFetch)(fetch(`${n}?path=${encodeURIComponent(a.pathname)}&search=${encodeURIComponent(a.search)}`,{headers:{Accept:"text/x-component"}}));P(m.head),h(m.head),d(m.model);let _=`${a.pathname}${a.search}`;A==="replace"?history.replaceState(null,"",_):A==="push"&&history.pushState(null,"",_)}catch(y){console.error("[webframez-react] Failed to render route",y),d((0,s.jsx)("p",{children:"Failed to load route."}))}finally{w(!1)}}(0,i.useEffect)(()=>{P(c)},[c]),(0,i.useEffect)(()=>{$(!0);let a=()=>{l(new URL(window.location.href),"none")};return window.addEventListener("popstate",a),()=>{window.removeEventListener("popstate",a)}},[]);let R=i.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")}}),[]);j(R);let C=(0,s.jsxs)(s.Fragment,{children:[T?(0,s.jsx)(V,{active:O}):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 n=e.rootId??"root",t=document.getElementById(n);if(!t)throw new Error(`Missing #${n} 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)(t,(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 l}from"react";import{hydrateRoot as H}from"react-dom/client";import{createFromFetch as P,createFromReadableStream as N}from"react-server-dom-webpack/client";var $=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function m(t){let n=t?.trim();if(n)return n==="/"?"/":n.replace(/\/+$/,"")}function _(t,n){let e=t.trim(),o=m(n);return!e||!o||$.test(e)||o!=="/"&&(e===o||e.startsWith(`${o}/`))?e:o==="/"?e.startsWith("/")?e:`/${e}`:e.startsWith("/")?`${o}${e}`:`${o}/${e}`}function v(t,n){if(!t)return;let e=m(t.assetsBaseUrl)??m(n),o={...t,...e?{assetsBaseUrl:e}:{}};return o.favicon&&(o.favicon=_(o.favicon,e)),o.links&&(o.links=o.links.map(r=>({...r,href:_(r.href,e)}))),o}import{Fragment as q,jsx as d,jsxs as j}from"react/jsx-runtime";var z={push:()=>{},replace:()=>{},refresh:()=>{}},f=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={},n=typeof document>"u"?"":document.cookie;if(!n||n.trim()==="")return t;for(let e of n.split(";")){let o=e.trim();if(!o)continue;let r=o.indexOf("="),a=r>=0?o.slice(0,r).trim():o,s=r>=0?o.slice(r+1):"";a&&(t[a]=decodeURIComponent(s))}return t}function S(t,n,e={}){let o=[`${t}=${encodeURIComponent(n)}`];return e.path&&o.push(`Path=${e.path}`),e.domain&&o.push(`Domain=${e.domain}`),typeof e.maxAge=="number"&&o.push(`Max-Age=${Math.floor(e.maxAge)}`),e.expires&&o.push(`Expires=${e.expires.toUTCString()}`),e.sameSite&&o.push(`SameSite=${e.sameSite}`),e.secure&&o.push("Secure"),o.join("; ")}function ee(){return c.useMemo(()=>({all:()=>E(),get:t=>E()[t],set:(t,n,e)=>{typeof document>"u"||(document.cookie=S(t,n,e))},remove:(t,n)=>{typeof document>"u"||(document.cookie=S(t,"",{...n??{},maxAge:0}))}}),[])}function te(){let t=f?c.useContext(f):null;if(!t){let n=M();if(n)return n;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 k(t){let n=document.createElement("meta");n.setAttribute("data-webframez-head","true");let e=Object.entries(t).filter(([,o])=>!!o);for(let[o,r]of e)n.setAttribute(o,String(r));document.head.appendChild(n)}function I(t){let n=document.createElement("link");n.setAttribute("data-webframez-head","true");let e=Object.entries(t).filter(([,o])=>!!o);for(let[o,r]of e)n.setAttribute(o,String(r));document.head.appendChild(n)}function b(t){if(typeof document>"u")return;let n=v(t)??t;document.title=n.title||"Webframez React";for(let e of document.head.querySelectorAll(B))e.remove();n.description&&k({name:"description",content:n.description}),n.favicon&&I({rel:"icon",href:n.favicon});for(let e of n.meta??[])k(e);for(let e of n.links??[])I(e)}function F(t){let n=new TextEncoder;return new ReadableStream({start(e){e.enqueue(n.encode(t)),e.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,N(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,n){return function(){let o=c.use(t),[r,a]=l(o.model),[s,g]=l(o.head),[U,h]=l(!1),[O,T]=l(!1);async function u(i,C="push"){h(!0);try{let p=await P(fetch(`${n}?path=${encodeURIComponent(i.pathname)}&search=${encodeURIComponent(i.search)}`,{headers:{Accept:"text/x-component"}}));b(p.head),g(p.head),a(p.model);let y=`${i.pathname}${i.search}`;C==="replace"?history.replaceState(null,"",y):C==="push"&&history.pushState(null,"",y)}catch(A){console.error("[webframez-react] Failed to render route",A),a(d("p",{children:"Failed to load route."}))}finally{h(!1)}}x(()=>{b(s)},[s]),x(()=>{T(!0);let i=()=>{u(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let w=c.useMemo(()=>({push:i=>{u(new URL(i,window.location.origin),"push")},replace:i=>{u(new URL(i,window.location.origin),"replace")},refresh:()=>{u(new URL(window.location.href),"none")}}),[]);W(w);let R=j(q,{children:[O?d(D,{active:U}):null,r??d("p",{style:{padding:24},children:"Loading..."})]});return f?d(f.Provider,{value:w,children:R}):R}}function ne(t={}){let n=t.rootId??"root",e=document.getElementById(n);if(!e)throw new Error(`Missing #${n} element`);let o=typeof window<"u"&&window.__RSC_ENDPOINT,r=t.rscEndpoint??o??"/rsc",a=Y(r),s=Z(a,r);return H(e,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 normalizeAssetsBaseUrl(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, assetsBaseUrl) {
166
+ const normalizedAssetUrl = assetUrl.trim();
167
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
168
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
169
+ return normalizedAssetUrl;
170
+ }
171
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
172
+ return normalizedAssetUrl;
173
+ }
174
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
175
+ return normalizedAssetUrl;
176
+ }
177
+ if (normalizedAssetsBaseUrl === "/") {
178
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
179
+ }
180
+ if (normalizedAssetUrl.startsWith("/")) {
181
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
182
+ }
183
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
184
+ }
185
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
186
+ if (!head) {
187
+ return void 0;
188
+ }
189
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
190
+ const normalizedHead = {
191
+ ...head,
192
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
193
+ };
194
+ if (normalizedHead.favicon) {
195
+ normalizedHead.favicon = resolveHeadAssetUrl(
196
+ normalizedHead.favicon,
197
+ effectiveAssetsBaseUrl
198
+ );
199
+ }
200
+ if (normalizedHead.links) {
201
+ normalizedHead.links = normalizedHead.links.map((link) => ({
202
+ ...link,
203
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
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.assetsBaseUrl);
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.assetsBaseUrl) {
436
+ merged.assetsBaseUrl = config.assetsBaseUrl;
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
  }
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 normalizeAssetsBaseUrl(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, assetsBaseUrl) {
141
+ const normalizedAssetUrl = assetUrl.trim();
142
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
143
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
144
+ return normalizedAssetUrl;
145
+ }
146
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
147
+ return normalizedAssetUrl;
148
+ }
149
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
150
+ return normalizedAssetUrl;
151
+ }
152
+ if (normalizedAssetsBaseUrl === "/") {
153
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
154
+ }
155
+ if (normalizedAssetUrl.startsWith("/")) {
156
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
157
+ }
158
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
159
+ }
160
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
161
+ if (!head) {
162
+ return void 0;
163
+ }
164
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
165
+ const normalizedHead = {
166
+ ...head,
167
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
168
+ };
169
+ if (normalizedHead.favicon) {
170
+ normalizedHead.favicon = resolveHeadAssetUrl(
171
+ normalizedHead.favicon,
172
+ effectiveAssetsBaseUrl
173
+ );
174
+ }
175
+ if (normalizedHead.links) {
176
+ normalizedHead.links = normalizedHead.links.map((link) => ({
177
+ ...link,
178
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
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.assetsBaseUrl);
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.assetsBaseUrl) {
411
+ merged.assetsBaseUrl = config.assetsBaseUrl;
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
  }
package/dist/index.cjs CHANGED
@@ -183,6 +183,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
183
183
  var import_node_module2 = __toESM(require("node:module"), 1);
184
184
  var import_node_path2 = __toESM(require("node:path"), 1);
185
185
 
186
+ // src/head.ts
187
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
188
+ function normalizeAssetsBaseUrl(value) {
189
+ const trimmed = value?.trim();
190
+ if (!trimmed) {
191
+ return void 0;
192
+ }
193
+ if (trimmed === "/") {
194
+ return "/";
195
+ }
196
+ return trimmed.replace(/\/+$/, "");
197
+ }
198
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
199
+ const normalizedAssetUrl = assetUrl.trim();
200
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
201
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
202
+ return normalizedAssetUrl;
203
+ }
204
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
205
+ return normalizedAssetUrl;
206
+ }
207
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
208
+ return normalizedAssetUrl;
209
+ }
210
+ if (normalizedAssetsBaseUrl === "/") {
211
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
212
+ }
213
+ if (normalizedAssetUrl.startsWith("/")) {
214
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
215
+ }
216
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
217
+ }
218
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
219
+ if (!head) {
220
+ return void 0;
221
+ }
222
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
223
+ const normalizedHead = {
224
+ ...head,
225
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
226
+ };
227
+ if (normalizedHead.favicon) {
228
+ normalizedHead.favicon = resolveHeadAssetUrl(
229
+ normalizedHead.favicon,
230
+ effectiveAssetsBaseUrl
231
+ );
232
+ }
233
+ if (normalizedHead.links) {
234
+ normalizedHead.links = normalizedHead.links.map((link) => ({
235
+ ...link,
236
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
237
+ }));
238
+ }
239
+ return normalizedHead;
240
+ }
241
+
186
242
  // src/router-runtime.tsx
187
243
  var import_react = __toESM(require("react"), 1);
188
244
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -398,7 +454,8 @@ function mergeHead(...configs) {
398
454
  meta: [],
399
455
  links: []
400
456
  };
401
- for (const config of configs) {
457
+ for (const candidate of configs) {
458
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
402
459
  if (!config) {
403
460
  continue;
404
461
  }
@@ -408,6 +465,9 @@ function mergeHead(...configs) {
408
465
  if (config.description) {
409
466
  merged.description = config.description;
410
467
  }
468
+ if (config.assetsBaseUrl) {
469
+ merged.assetsBaseUrl = config.assetsBaseUrl;
470
+ }
411
471
  if (config.favicon) {
412
472
  merged.favicon = config.favicon;
413
473
  }
@@ -421,22 +481,23 @@ function mergeHead(...configs) {
421
481
  return merged;
422
482
  }
423
483
  function renderHeadToString(head) {
484
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
424
485
  const tags = [];
425
- if (head.description) {
486
+ if (normalizedHead.description) {
426
487
  tags.push(
427
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
488
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
428
489
  );
429
490
  }
430
- if (head.favicon) {
491
+ if (normalizedHead.favicon) {
431
492
  tags.push(
432
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
493
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
433
494
  );
434
495
  }
435
- for (const meta of head.meta ?? []) {
496
+ for (const meta of normalizedHead.meta ?? []) {
436
497
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
437
498
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
438
499
  }
439
- for (const link of head.links ?? []) {
500
+ for (const link of normalizedHead.links ?? []) {
440
501
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
441
502
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
442
503
  }
package/dist/index.js CHANGED
@@ -147,6 +147,62 @@ import fs from "node:fs";
147
147
  import Module from "node:module";
148
148
  import path2 from "node:path";
149
149
 
150
+ // src/head.ts
151
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
152
+ function normalizeAssetsBaseUrl(value) {
153
+ const trimmed = value?.trim();
154
+ if (!trimmed) {
155
+ return void 0;
156
+ }
157
+ if (trimmed === "/") {
158
+ return "/";
159
+ }
160
+ return trimmed.replace(/\/+$/, "");
161
+ }
162
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
163
+ const normalizedAssetUrl = assetUrl.trim();
164
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
165
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
166
+ return normalizedAssetUrl;
167
+ }
168
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
169
+ return normalizedAssetUrl;
170
+ }
171
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
172
+ return normalizedAssetUrl;
173
+ }
174
+ if (normalizedAssetsBaseUrl === "/") {
175
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
176
+ }
177
+ if (normalizedAssetUrl.startsWith("/")) {
178
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
179
+ }
180
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
181
+ }
182
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
183
+ if (!head) {
184
+ return void 0;
185
+ }
186
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
187
+ const normalizedHead = {
188
+ ...head,
189
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
190
+ };
191
+ if (normalizedHead.favicon) {
192
+ normalizedHead.favicon = resolveHeadAssetUrl(
193
+ normalizedHead.favicon,
194
+ effectiveAssetsBaseUrl
195
+ );
196
+ }
197
+ if (normalizedHead.links) {
198
+ normalizedHead.links = normalizedHead.links.map((link) => ({
199
+ ...link,
200
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
201
+ }));
202
+ }
203
+ return normalizedHead;
204
+ }
205
+
150
206
  // src/router-runtime.tsx
151
207
  import React from "react";
152
208
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -362,7 +418,8 @@ function mergeHead(...configs) {
362
418
  meta: [],
363
419
  links: []
364
420
  };
365
- for (const config of configs) {
421
+ for (const candidate of configs) {
422
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
366
423
  if (!config) {
367
424
  continue;
368
425
  }
@@ -372,6 +429,9 @@ function mergeHead(...configs) {
372
429
  if (config.description) {
373
430
  merged.description = config.description;
374
431
  }
432
+ if (config.assetsBaseUrl) {
433
+ merged.assetsBaseUrl = config.assetsBaseUrl;
434
+ }
375
435
  if (config.favicon) {
376
436
  merged.favicon = config.favicon;
377
437
  }
@@ -385,22 +445,23 @@ function mergeHead(...configs) {
385
445
  return merged;
386
446
  }
387
447
  function renderHeadToString(head) {
448
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
388
449
  const tags = [];
389
- if (head.description) {
450
+ if (normalizedHead.description) {
390
451
  tags.push(
391
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
452
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
392
453
  );
393
454
  }
394
- if (head.favicon) {
455
+ if (normalizedHead.favicon) {
395
456
  tags.push(
396
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
457
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
397
458
  );
398
459
  }
399
- for (const meta of head.meta ?? []) {
460
+ for (const meta of normalizedHead.meta ?? []) {
400
461
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
401
462
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
402
463
  }
403
- for (const link of head.links ?? []) {
464
+ for (const link of normalizedHead.links ?? []) {
404
465
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
405
466
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
406
467
  }
package/dist/router.cjs CHANGED
@@ -41,6 +41,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
41
41
  var import_node_module = __toESM(require("node:module"), 1);
42
42
  var import_node_path = __toESM(require("node:path"), 1);
43
43
 
44
+ // src/head.ts
45
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
46
+ function normalizeAssetsBaseUrl(value) {
47
+ const trimmed = value?.trim();
48
+ if (!trimmed) {
49
+ return void 0;
50
+ }
51
+ if (trimmed === "/") {
52
+ return "/";
53
+ }
54
+ return trimmed.replace(/\/+$/, "");
55
+ }
56
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
57
+ const normalizedAssetUrl = assetUrl.trim();
58
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
59
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
60
+ return normalizedAssetUrl;
61
+ }
62
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
63
+ return normalizedAssetUrl;
64
+ }
65
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
66
+ return normalizedAssetUrl;
67
+ }
68
+ if (normalizedAssetsBaseUrl === "/") {
69
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
70
+ }
71
+ if (normalizedAssetUrl.startsWith("/")) {
72
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
73
+ }
74
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
75
+ }
76
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
77
+ if (!head) {
78
+ return void 0;
79
+ }
80
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
81
+ const normalizedHead = {
82
+ ...head,
83
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
84
+ };
85
+ if (normalizedHead.favicon) {
86
+ normalizedHead.favicon = resolveHeadAssetUrl(
87
+ normalizedHead.favicon,
88
+ effectiveAssetsBaseUrl
89
+ );
90
+ }
91
+ if (normalizedHead.links) {
92
+ normalizedHead.links = normalizedHead.links.map((link) => ({
93
+ ...link,
94
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
95
+ }));
96
+ }
97
+ return normalizedHead;
98
+ }
99
+
44
100
  // src/router-runtime.tsx
45
101
  var import_react = __toESM(require("react"), 1);
46
102
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -256,7 +312,8 @@ function mergeHead(...configs) {
256
312
  meta: [],
257
313
  links: []
258
314
  };
259
- for (const config of configs) {
315
+ for (const candidate of configs) {
316
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
260
317
  if (!config) {
261
318
  continue;
262
319
  }
@@ -266,6 +323,9 @@ function mergeHead(...configs) {
266
323
  if (config.description) {
267
324
  merged.description = config.description;
268
325
  }
326
+ if (config.assetsBaseUrl) {
327
+ merged.assetsBaseUrl = config.assetsBaseUrl;
328
+ }
269
329
  if (config.favicon) {
270
330
  merged.favicon = config.favicon;
271
331
  }
@@ -279,22 +339,23 @@ function mergeHead(...configs) {
279
339
  return merged;
280
340
  }
281
341
  function renderHeadToString(head) {
342
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
282
343
  const tags = [];
283
- if (head.description) {
344
+ if (normalizedHead.description) {
284
345
  tags.push(
285
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
346
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
286
347
  );
287
348
  }
288
- if (head.favicon) {
349
+ if (normalizedHead.favicon) {
289
350
  tags.push(
290
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
351
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
291
352
  );
292
353
  }
293
- for (const meta of head.meta ?? []) {
354
+ for (const meta of normalizedHead.meta ?? []) {
294
355
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
295
356
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
296
357
  }
297
- for (const link of head.links ?? []) {
358
+ for (const link of normalizedHead.links ?? []) {
298
359
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
299
360
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
300
361
  }
package/dist/router.js CHANGED
@@ -11,6 +11,62 @@ import fs from "node:fs";
11
11
  import Module from "node:module";
12
12
  import path from "node:path";
13
13
 
14
+ // src/head.ts
15
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
16
+ function normalizeAssetsBaseUrl(value) {
17
+ const trimmed = value?.trim();
18
+ if (!trimmed) {
19
+ return void 0;
20
+ }
21
+ if (trimmed === "/") {
22
+ return "/";
23
+ }
24
+ return trimmed.replace(/\/+$/, "");
25
+ }
26
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
27
+ const normalizedAssetUrl = assetUrl.trim();
28
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
29
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
30
+ return normalizedAssetUrl;
31
+ }
32
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
33
+ return normalizedAssetUrl;
34
+ }
35
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
36
+ return normalizedAssetUrl;
37
+ }
38
+ if (normalizedAssetsBaseUrl === "/") {
39
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
40
+ }
41
+ if (normalizedAssetUrl.startsWith("/")) {
42
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
43
+ }
44
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
45
+ }
46
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
47
+ if (!head) {
48
+ return void 0;
49
+ }
50
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
51
+ const normalizedHead = {
52
+ ...head,
53
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
54
+ };
55
+ if (normalizedHead.favicon) {
56
+ normalizedHead.favicon = resolveHeadAssetUrl(
57
+ normalizedHead.favicon,
58
+ effectiveAssetsBaseUrl
59
+ );
60
+ }
61
+ if (normalizedHead.links) {
62
+ normalizedHead.links = normalizedHead.links.map((link) => ({
63
+ ...link,
64
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
65
+ }));
66
+ }
67
+ return normalizedHead;
68
+ }
69
+
14
70
  // src/router-runtime.tsx
15
71
  import React from "react";
16
72
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -226,7 +282,8 @@ function mergeHead(...configs) {
226
282
  meta: [],
227
283
  links: []
228
284
  };
229
- for (const config of configs) {
285
+ for (const candidate of configs) {
286
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
230
287
  if (!config) {
231
288
  continue;
232
289
  }
@@ -236,6 +293,9 @@ function mergeHead(...configs) {
236
293
  if (config.description) {
237
294
  merged.description = config.description;
238
295
  }
296
+ if (config.assetsBaseUrl) {
297
+ merged.assetsBaseUrl = config.assetsBaseUrl;
298
+ }
239
299
  if (config.favicon) {
240
300
  merged.favicon = config.favicon;
241
301
  }
@@ -249,22 +309,23 @@ function mergeHead(...configs) {
249
309
  return merged;
250
310
  }
251
311
  function renderHeadToString(head) {
312
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
252
313
  const tags = [];
253
- if (head.description) {
314
+ if (normalizedHead.description) {
254
315
  tags.push(
255
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
316
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
256
317
  );
257
318
  }
258
- if (head.favicon) {
319
+ if (normalizedHead.favicon) {
259
320
  tags.push(
260
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
321
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
261
322
  );
262
323
  }
263
- for (const meta of head.meta ?? []) {
324
+ for (const meta of normalizedHead.meta ?? []) {
264
325
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
265
326
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
266
327
  }
267
- for (const link of head.links ?? []) {
328
+ for (const link of normalizedHead.links ?? []) {
268
329
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
269
330
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
270
331
  }
package/dist/types.d.ts CHANGED
@@ -9,7 +9,7 @@ export type AbortRouteOptions = {
9
9
  payload?: unknown;
10
10
  };
11
11
 
12
- export type RouteContext<TData = unknown> = {
12
+ export type RouteContext<TData = any> = {
13
13
  pathname: string;
14
14
  params: RouteParams;
15
15
  searchParams: RouteSearchParams;
@@ -22,11 +22,11 @@ export type InferPageData<TDataResolver> =
22
22
  TDataResolver extends (...args: any[]) => any
23
23
  ? Awaited<ReturnType<TDataResolver>>
24
24
  : TDataResolver;
25
- export type PageProps<TData = unknown> = RouteContext<InferPageData<TData>>;
25
+ export type PageProps<TData = any> = RouteContext<InferPageData<TData>>;
26
26
  export type PagePropsFromData<TDataResolver extends (...args: any[]) => any> =
27
27
  PageProps<InferPageData<TDataResolver>>;
28
28
 
29
- export type ErrorPageProps<TData = unknown> = RouteContext<TData> & {
29
+ export type ErrorPageProps<TData = any> = RouteContext<TData> & {
30
30
  statusCode: number;
31
31
  message: string;
32
32
  payload?: unknown;
@@ -51,6 +51,7 @@ export type HeadLinkTag = {
51
51
  export type HeadConfig = {
52
52
  title?: string;
53
53
  description?: string;
54
+ assetsBaseUrl?: string;
54
55
  favicon?: string;
55
56
  meta?: HeadMetaTag[];
56
57
  links?: HeadLinkTag[];
@@ -65,18 +66,18 @@ export type HeadResolver<TContext = RouteContext> = (
65
66
  context: TContext
66
67
  ) => HeadConfig | Promise<HeadConfig>;
67
68
 
68
- export type PageDataResolver<TData = unknown> = (
69
+ export type PageDataResolver<TData = any> = (
69
70
  context: RouteContext
70
71
  ) => TData | Promise<TData>;
71
72
 
72
73
  export type RouteMiddlewareResult = Record<string, unknown> | undefined | void;
73
- export type RouteMiddlewareResolver<TData = unknown> = (
74
+ export type RouteMiddlewareResolver<TData = any> = (
74
75
  context: RouteContext<TData>
75
76
  ) => RouteMiddlewareResult | Promise<RouteMiddlewareResult>;
76
77
  export type RouteMiddlewareRegistry = Record<string, RouteMiddlewareResolver<any>>;
77
78
  export type RouteMiddlewareConfig = string | string[];
78
79
 
79
- export type PageModule<TData = unknown> = {
80
+ export type PageModule<TData = any> = {
80
81
  default: (props: PageProps<TData>) => ReactNode | Promise<ReactNode>;
81
82
  Head?: HeadResolver<PageProps<TData>>;
82
83
  Data?: PageDataResolver<TData>;
@@ -153,6 +153,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
153
153
  var import_node_module2 = __toESM(require("node:module"), 1);
154
154
  var import_node_path2 = __toESM(require("node:path"), 1);
155
155
 
156
+ // src/head.ts
157
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
158
+ function normalizeAssetsBaseUrl(value) {
159
+ const trimmed = value?.trim();
160
+ if (!trimmed) {
161
+ return void 0;
162
+ }
163
+ if (trimmed === "/") {
164
+ return "/";
165
+ }
166
+ return trimmed.replace(/\/+$/, "");
167
+ }
168
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
169
+ const normalizedAssetUrl = assetUrl.trim();
170
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
171
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
172
+ return normalizedAssetUrl;
173
+ }
174
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
175
+ return normalizedAssetUrl;
176
+ }
177
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
178
+ return normalizedAssetUrl;
179
+ }
180
+ if (normalizedAssetsBaseUrl === "/") {
181
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
182
+ }
183
+ if (normalizedAssetUrl.startsWith("/")) {
184
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
185
+ }
186
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
187
+ }
188
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
189
+ if (!head) {
190
+ return void 0;
191
+ }
192
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
193
+ const normalizedHead = {
194
+ ...head,
195
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
196
+ };
197
+ if (normalizedHead.favicon) {
198
+ normalizedHead.favicon = resolveHeadAssetUrl(
199
+ normalizedHead.favicon,
200
+ effectiveAssetsBaseUrl
201
+ );
202
+ }
203
+ if (normalizedHead.links) {
204
+ normalizedHead.links = normalizedHead.links.map((link) => ({
205
+ ...link,
206
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
207
+ }));
208
+ }
209
+ return normalizedHead;
210
+ }
211
+
156
212
  // src/router-runtime.tsx
157
213
  var import_react = __toESM(require("react"), 1);
158
214
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -368,7 +424,8 @@ function mergeHead(...configs) {
368
424
  meta: [],
369
425
  links: []
370
426
  };
371
- for (const config of configs) {
427
+ for (const candidate of configs) {
428
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
372
429
  if (!config) {
373
430
  continue;
374
431
  }
@@ -378,6 +435,9 @@ function mergeHead(...configs) {
378
435
  if (config.description) {
379
436
  merged.description = config.description;
380
437
  }
438
+ if (config.assetsBaseUrl) {
439
+ merged.assetsBaseUrl = config.assetsBaseUrl;
440
+ }
381
441
  if (config.favicon) {
382
442
  merged.favicon = config.favicon;
383
443
  }
@@ -391,22 +451,23 @@ function mergeHead(...configs) {
391
451
  return merged;
392
452
  }
393
453
  function renderHeadToString(head) {
454
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
394
455
  const tags = [];
395
- if (head.description) {
456
+ if (normalizedHead.description) {
396
457
  tags.push(
397
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
458
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
398
459
  );
399
460
  }
400
- if (head.favicon) {
461
+ if (normalizedHead.favicon) {
401
462
  tags.push(
402
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
463
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
403
464
  );
404
465
  }
405
- for (const meta of head.meta ?? []) {
466
+ for (const meta of normalizedHead.meta ?? []) {
406
467
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
407
468
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
408
469
  }
409
- for (const link of head.links ?? []) {
470
+ for (const link of normalizedHead.links ?? []) {
410
471
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
411
472
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
412
473
  }
@@ -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 normalizeAssetsBaseUrl(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, assetsBaseUrl) {
141
+ const normalizedAssetUrl = assetUrl.trim();
142
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
143
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
144
+ return normalizedAssetUrl;
145
+ }
146
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
147
+ return normalizedAssetUrl;
148
+ }
149
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
150
+ return normalizedAssetUrl;
151
+ }
152
+ if (normalizedAssetsBaseUrl === "/") {
153
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
154
+ }
155
+ if (normalizedAssetUrl.startsWith("/")) {
156
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
157
+ }
158
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
159
+ }
160
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
161
+ if (!head) {
162
+ return void 0;
163
+ }
164
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
165
+ const normalizedHead = {
166
+ ...head,
167
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
168
+ };
169
+ if (normalizedHead.favicon) {
170
+ normalizedHead.favicon = resolveHeadAssetUrl(
171
+ normalizedHead.favicon,
172
+ effectiveAssetsBaseUrl
173
+ );
174
+ }
175
+ if (normalizedHead.links) {
176
+ normalizedHead.links = normalizedHead.links.map((link) => ({
177
+ ...link,
178
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
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.assetsBaseUrl);
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.assetsBaseUrl) {
411
+ merged.assetsBaseUrl = config.assetsBaseUrl;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-react",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "TypeScript React RSC addition for @webtypen/webframez-core",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "author": {
@@ -78,9 +78,9 @@
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@webtypen/webframez-core": "^0.3.30",
81
- "react": "0.0.0-experimental-95ffd6cd-20260205",
82
- "react-dom": "0.0.0-experimental-95ffd6cd-20260205",
83
- "react-server-dom-webpack": "0.0.0-experimental-95ffd6cd-20260205"
81
+ "react": "^19.0.0",
82
+ "react-dom": "^19.0.0",
83
+ "react-server-dom-webpack": "^19.0.0"
84
84
  },
85
85
  "peerDependenciesMeta": {
86
86
  "@webtypen/webframez-core": {
@@ -89,8 +89,8 @@
89
89
  },
90
90
  "devDependencies": {
91
91
  "esbuild": "^0.20.2",
92
- "react": "0.0.0-experimental-95ffd6cd-20260205",
93
- "react-dom": "0.0.0-experimental-95ffd6cd-20260205",
94
- "react-server-dom-webpack": "0.0.0-experimental-95ffd6cd-20260205"
92
+ "react": "19.1.1",
93
+ "react-dom": "19.1.1",
94
+ "react-server-dom-webpack": "19.1.1"
95
95
  }
96
96
  }