@webtypen/webframez-react 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -1 +1 @@
1
- var se=Object.create;var y=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var ue=Object.getPrototypeOf,le=Object.prototype.hasOwnProperty;var fe=(e,t)=>{for(var n in t)y(e,n,{get:t[n],enumerable:!0})},L=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of de(t))!le.call(e,r)&&r!==n&&y(e,r,{get:()=>t[r],enumerable:!(o=ce(t,r))||o.enumerable});return e};var H=(e,t,n)=>(n=e!=null?se(ue(e)):{},L(t||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e)),pe=e=>L(y({},"__esModule",{value:!0}),e);var be={};fe(be,{mountWebframezClient:()=>ve,useCookie:()=>Ee,useRouter:()=>Ae});module.exports=pe(be);var s=H(require("react"),1),Z=require("react-dom/client"),R=require("react-server-dom-webpack/client"),K=require("@webtypen/webframez-react/route-slot");var me=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function u(e){if(typeof e!="string")return;let t=e.trim();return!t||t==="/"?"":t.replace(/\/+$/,"")}function U(e,t){let n=e.trim(),o=u(t);return!n||!o||me.test(n)||n.startsWith("/")||o!=="/"&&(n===o||n.startsWith(`${o}/`))?n:o==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${o}${n}`:`${o}/${n}`}function S(e,t){if(!e)return;let n=u(e.basename)??u(t),o=u(e.routeBasePath),r=u(e.transportBasePath),a={...e,...n!==void 0?{basename:n}:{},...o!==void 0?{routeBasePath:o}:{},...r!==void 0?{transportBasePath:r}:{}};return a.favicon&&(a.favicon=U(a.favicon,n)),a.links&&(a.links=a.links.map(c=>({...c,href:U(c.href,n)}))),a}var m=H(require("react"),1),$="webframez-route-children",W="__webframezRouteChildren",D="WebframezRouteChildren",N=()=>m.default.createElement($);N.displayName=D;N[W]=!0;var Re=N;function z(e){if(e===$||e===Re)return!0;if(!e||typeof e!="function"&&typeof e!="object")return!1;try{let t=e;return t[W]===!0||t.displayName===D||t.name==="RouteChildren"}catch{return!1}}function M(e){if(!e||typeof e!="object")return!1;try{return"type"in e&&"props"in e}catch{return!1}}function E(e,t){if(e==null||typeof e=="boolean")return e;if(Array.isArray(e)){let l=!1,g=e.map(p=>{let h=E(p,t);return h!==p&&(l=!0),h});return l?g:e}if(M(e)&&z(e.type))return t;let n=m.default.isValidElement(e);if(!n&&!M(e))return e;if(n&&z(e.type))return t;let o=e.props??{};if(!("children"in o))return e;let r=E(o.children,t);if(r===o.children)return e;if(n)return Array.isArray(r)?m.default.cloneElement(e,void 0,...r):m.default.cloneElement(e,void 0,r);let a=e,c={...a.props??{},children:r,...a.key!==void 0&&a.key!==null?{key:a.key}:{}};return m.default.createElement(a.type,c)}var d=require("react/jsx-runtime"),ge={push:()=>{},replace:()=>{},refresh:()=>{},refreshContext:()=>{}},A=typeof s.default.createContext=="function"?s.default.createContext(null):null,V="__WEBFRAMEZ_ROUTER__",he="[data-webframez-head='true']";function F(){if(typeof window>"u"||typeof document>"u")return;let e=()=>{window.scrollTo({top:0,left:0}),document.scrollingElement&&(document.scrollingElement.scrollTop=0),document.documentElement.scrollTop=0,document.body&&(document.body.scrollTop=0)};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(e);return}window.setTimeout(e,0)}function v(e){return u(e)??""}function we(e,t){return t?e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length)||"/":e||"/":e||"/"}function X(e,t){let n=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return e?n==="/"?e:`${e}${n}`:n}function _e(){return v(typeof window>"u"?globalThis.__RSC_ROUTE_BASE_PATH:window.__RSC_ROUTE_BASE_PATH)}function J(e){if(typeof window>"u")return e||"/";let t=v(window.__RSC_BASENAME),n=_e(),o=we(e||"/",t);return X(n,o)}function Q(e){if(typeof window>"u")return e;let t=window.__RSC_ENDPOINT;return typeof t=="string"&&t.trim()!==""?t:e}function Ce(){return typeof window>"u"?null:window[V]??null}function ye(e){typeof window>"u"||(window[V]=e)}function j(){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("="),a=r>=0?o.slice(0,r).trim():o,c=r>=0?o.slice(r+1):"";a&&(e[a]=decodeURIComponent(c))}return e}function q(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 Ee(){return s.default.useMemo(()=>({all:()=>j(),get:e=>j()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=q(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=q(e,"",{...t??{},maxAge:0}))}}),[])}function Ae(){let e=A?s.default.useContext(A):null;if(!e){let t=Ce();if(t)return t;if(typeof window>"u")return ge;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function Te({active:e}){return(0,d.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 G(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 Y(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 x(e){if(typeof document>"u")return;let t=S(e)??e;ee(t),document.title=t.title||"Webframez React";for(let n of document.head.querySelectorAll(he))n.remove();t.description&&G({name:"description",content:t.description}),t.favicon&&Y({rel:"icon",href:t.favicon});for(let n of t.meta??[])G(n);for(let n of t.links??[])Y(n)}function ee(e){if(typeof window>"u")return;let t=S(e)??e,n=u(t.basename),o=u(t.routeBasePath),r=u(t.transportBasePath);window.__RSC_BASENAME=n??"",window.__RSC_ROUTE_BASE_PATH=o??"",r!==void 0&&(window.__RSC_ENDPOINT=X(r,"/rsc"))}function Pe(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function Se(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,(0,R.createFromReadableStream)(Pe(e)))}function Ne(e){let t=Q(e),n=J(window.location.pathname);return Se()??(0,R.createFromFetch)(fetch(`${t}?path=${encodeURIComponent(n)}&search=${encodeURIComponent(window.location.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}function xe(e,t){return function(){let o=s.default.use(e),r=typeof o.contextModel<"u"&&typeof o.pageModel<"u",[a,c]=(0,s.useState)(o.model),[l,g]=(0,s.useState)(o.contextModel),[p,h]=(0,s.useState)(o.pageModel),[b,I]=(0,s.useState)(o.head),[te,w]=(0,s.useState)(!1),[ne,oe]=(0,s.useState)(!1),[re,T]=(0,s.useState)(r);async function O(i){let C=Q(t),f=J(i.pathname);return await(0,R.createFromFetch)(fetch(`${C}?path=${encodeURIComponent(f)}&search=${encodeURIComponent(i.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}async function _(i,C="push"){w(!0);try{let f=await O(i);x(f.head),I(f.head),c(f.model),g(f.contextModel),h(f.pageModel),T(!0);let P=`${i.pathname}${i.search}${i.hash}`;C==="replace"?(history.replaceState(null,"",P),F()):C==="push"&&(history.pushState(null,"",P),F())}catch(f){console.error("[webframez-react] Failed to render route",f),c((0,d.jsx)("p",{children:"Failed to load route."}))}finally{w(!1)}}async function ie(){w(!0);try{let i=await O(new URL(window.location.href));if(x(i.head),I(i.head),Object.prototype.hasOwnProperty.call(i,"contextModel")){g(i.contextModel),T(!0);return}c(i.model),T(!1)}catch(i){console.error("[webframez-react] Failed to refresh route context",i)}finally{w(!1)}}(0,s.useEffect)(()=>{x(b)},[b]),(0,s.useEffect)(()=>{oe(!0);let i=()=>{_(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let k=s.default.useMemo(()=>({push:i=>{_(new URL(i,window.location.origin),"push")},replace:i=>{_(new URL(i,window.location.origin),"replace")},refresh:()=>{_(new URL(window.location.href),"none")},refreshContext:()=>{ie()}}),[]);ye(k);let ae=re&&typeof p<"u"?l?(0,d.jsx)(K.RouteChildrenSlotProvider,{page:p,children:E(l,p)}):p:a,B=(0,d.jsxs)(d.Fragment,{children:[ne?(0,d.jsx)(Te,{active:te}):null,ae??(0,d.jsx)("p",{style:{padding:24},children:"Loading..."})]});return A?(0,d.jsx)(A.Provider,{value:k,children:B}):B}}function ve(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",a=Promise.resolve(Ne(r)).then(l=>(l?.head&&ee(l.head),l)),c=xe(a,r);return(0,Z.hydrateRoot)(n,(0,d.jsx)(c,{}))}
1
+ var ce=Object.create;var E=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var le=Object.getPrototypeOf,fe=Object.prototype.hasOwnProperty;var pe=(e,t)=>{for(var n in t)E(e,n,{get:t[n],enumerable:!0})},U=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ue(t))!fe.call(e,r)&&r!==n&&E(e,r,{get:()=>t[r],enumerable:!(o=de(t,r))||o.enumerable});return e};var H=(e,t,n)=>(n=e!=null?ce(le(e)):{},U(t||!e||!e.__esModule?E(n,"default",{value:e,enumerable:!0}):n,e)),me=e=>U(E({},"__esModule",{value:!0}),e);var ke={};pe(ke,{mountWebframezClient:()=>Oe,useCookie:()=>Ne,useRouter:()=>ve});module.exports=me(ke);var s=H(require("react"),1),K=require("react-dom/client"),w=require("react-server-dom-webpack/client"),V=require("@webtypen/webframez-react/route-slot");var Re=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function u(e){if(typeof e!="string")return;let t=e.trim();return!t||t==="/"?"":t.replace(/\/+$/,"")}function z(e,t){let n=e.trim(),o=u(t);return!n||!o||Re.test(n)||n.startsWith("/")||o!=="/"&&(n===o||n.startsWith(`${o}/`))?n:o==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${o}${n}`:`${o}/${n}`}function S(e,t){if(!e)return;let n=u(e.basename)??u(t),o=u(e.routeBasePath),r=u(e.transportBasePath),a={...e,...n!==void 0?{basename:n}:{},...o!==void 0?{routeBasePath:o}:{},...r!==void 0?{transportBasePath:r}:{}};return a.favicon&&(a.favicon=z(a.favicon,n)),a.links&&(a.links=a.links.map(c=>({...c,href:z(c.href,n)}))),a}var m=H(require("react"),1),D="webframez-route-children",W="__webframezRouteChildren",F="WebframezRouteChildren",N=()=>m.default.createElement(D);N.displayName=F;N[W]=!0;var we=N;function M(e){if(e===D||e===we)return!0;if(!e||typeof e!="function"&&typeof e!="object")return!1;try{let t=e;return t[W]===!0||t.displayName===F||t.name==="RouteChildren"}catch{return!1}}function $(e){if(!e||typeof e!="object")return!1;try{return"type"in e&&"props"in e}catch{return!1}}function A(e,t){if(e==null||typeof e=="boolean")return e;if(Array.isArray(e)){let l=!1,_=e.map(p=>{let h=A(p,t);return h!==p&&(l=!0),h});return l?_:e}if($(e)&&M(e.type))return t;let n=m.default.isValidElement(e);if(!n&&!$(e))return e;if(n&&M(e.type))return t;let o=e.props??{};if(!("children"in o))return e;let r=A(o.children,t);if(r===o.children)return e;if(n)return Array.isArray(r)?m.default.cloneElement(e,void 0,...r):m.default.cloneElement(e,void 0,r);let a=e,c={...a.props??{},children:r,...a.key!==void 0&&a.key!==null?{key:a.key}:{}};return m.default.createElement(a.type,c)}var d=require("react/jsx-runtime"),_e={push:()=>{},replace:()=>{},refresh:()=>{},refreshContext:()=>{}},T=typeof s.default.createContext=="function"?s.default.createContext(null):null,X="__WEBFRAMEZ_ROUTER__",he="__WEBFRAMEZ_REACT_BUILD_ID",ge="x-webframez-react-build",Ce="[data-webframez-head='true']";function j(){if(typeof window>"u"||typeof document>"u")return;let e=()=>{window.scrollTo({top:0,left:0}),document.scrollingElement&&(document.scrollingElement.scrollTop=0),document.documentElement.scrollTop=0,document.body&&(document.body.scrollTop=0)};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(e);return}window.setTimeout(e,0)}function I(e){return u(e)??""}function ye(e,t){return t?e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length)||"/":e||"/":e||"/"}function J(e,t){let n=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return e?n==="/"?e:`${e}${n}`:n}function Ee(){return I(typeof window>"u"?globalThis.__RSC_ROUTE_BASE_PATH:window.__RSC_ROUTE_BASE_PATH)}function Q(e){if(typeof window>"u")return e||"/";let t=I(window.__RSC_BASENAME),n=Ee(),o=ye(e||"/",t);return J(n,o)}function ee(e){if(typeof window>"u")return e;let t=window.__RSC_ENDPOINT;return typeof t=="string"&&t.trim()!==""?t:e}function Ae(){if(typeof window>"u")return"";let e=window[he];return typeof e=="string"?e:""}function Te(e){let t=Ae();return!!t&&!!e&&t!==e}function v(e){typeof window>"u"||window.location.assign(`${e.pathname}${e.search}${e.hash}`)}function Pe(){return typeof window>"u"?null:window[X]??null}function Se(e){typeof window>"u"||(window[X]=e)}function G(){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("="),a=r>=0?o.slice(0,r).trim():o,c=r>=0?o.slice(r+1):"";a&&(e[a]=decodeURIComponent(c))}return e}function q(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 Ne(){return s.default.useMemo(()=>({all:()=>G(),get:e=>G()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=q(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=q(e,"",{...t??{},maxAge:0}))}}),[])}function ve(){let e=T?s.default.useContext(T):null;if(!e){let t=Pe();if(t)return t;if(typeof window>"u")return _e;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function xe({active:e}){return(0,d.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 Y(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 Z(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 x(e){if(typeof document>"u")return;let t=S(e)??e;te(t),document.body.className=t.bodyClassName||"",document.title=t.title||"Webframez React";for(let n of document.head.querySelectorAll(Ce))n.remove();t.description&&Y({name:"description",content:t.description}),t.favicon&&Z({rel:"icon",href:t.favicon});for(let n of t.meta??[])Y(n);for(let n of t.links??[])Z(n)}function te(e){if(typeof window>"u")return;let t=S(e)??e,n=u(t.basename),o=u(t.routeBasePath),r=u(t.transportBasePath);window.__RSC_BASENAME=n??"",window.__RSC_ROUTE_BASE_PATH=o??"",r!==void 0&&(window.__RSC_ENDPOINT=J(r,"/rsc"))}function Ie(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function Be(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,(0,w.createFromReadableStream)(Ie(e)))}function be(e){let t=ee(e),n=Q(window.location.pathname);return Be()??(0,w.createFromFetch)(fetch(`${t}?path=${encodeURIComponent(n)}&search=${encodeURIComponent(window.location.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}function Le(e,t){return function(){let o=s.default.use(e),r=typeof o.contextModel<"u"&&typeof o.pageModel<"u",[a,c]=(0,s.useState)(o.model),[l,_]=(0,s.useState)(o.contextModel),[p,h]=(0,s.useState)(o.pageModel),[B,b]=(0,s.useState)(o.head),[ne,g]=(0,s.useState)(!1),[oe,re]=(0,s.useState)(!1),[ie,P]=(0,s.useState)(r);async function L(i){let y=ee(t),f=Q(i.pathname),R=await fetch(`${y}?path=${encodeURIComponent(f)}&search=${encodeURIComponent(i.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}});return Te(R.headers.get(ge))?(v(i),await new Promise(()=>{})):await(0,w.createFromFetch)(Promise.resolve(R))}async function C(i,y="push"){g(!0);try{let f=await L(i);x(f.head),b(f.head),c(f.model),_(f.contextModel),h(f.pageModel),P(!0);let R=`${i.pathname}${i.search}${i.hash}`;y==="replace"?(history.replaceState(null,"",R),j()):y==="push"&&(history.pushState(null,"",R),j())}catch(f){console.error("[webframez-react] Failed to render route",f),v(i),c((0,d.jsx)("p",{children:"Failed to load route."}))}finally{g(!1)}}async function ae(){g(!0);try{let i=await L(new URL(window.location.href));if(x(i.head),b(i.head),Object.prototype.hasOwnProperty.call(i,"contextModel")){_(i.contextModel),P(!0);return}c(i.model),P(!1)}catch(i){console.error("[webframez-react] Failed to refresh route context",i),v(new URL(window.location.href))}finally{g(!1)}}(0,s.useEffect)(()=>{x(B)},[B]),(0,s.useEffect)(()=>{re(!0);let i=()=>{C(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let O=s.default.useMemo(()=>({push:i=>{C(new URL(i,window.location.origin),"push")},replace:i=>{C(new URL(i,window.location.origin),"replace")},refresh:()=>{C(new URL(window.location.href),"none")},refreshContext:()=>{ae()}}),[]);Se(O);let se=ie&&typeof p<"u"?l?(0,d.jsx)(V.RouteChildrenSlotProvider,{page:p,children:A(l,p)}):p:a,k=(0,d.jsxs)(d.Fragment,{children:[oe?(0,d.jsx)(xe,{active:ne}):null,se??(0,d.jsx)("p",{style:{padding:24},children:"Loading..."})]});return T?(0,d.jsx)(T.Provider,{value:O,children:k}):k}}function Oe(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",a=Promise.resolve(be(r)).then(l=>(l?.head&&te(l.head),l)),c=Le(a,r);return(0,K.hydrateRoot)(n,(0,d.jsx)(c,{}))}
package/dist/client.js CHANGED
@@ -1 +1 @@
1
- import m,{useEffect as $,useState as f}from"react";import{hydrateRoot as ae}from"react-dom/client";import{createFromFetch as G,createFromReadableStream as se}from"react-server-dom-webpack/client";import{RouteChildrenSlotProvider as ce}from"@webtypen/webframez-react/route-slot";var re=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function c(e){if(typeof e!="string")return;let t=e.trim();return!t||t==="/"?"":t.replace(/\/+$/,"")}function B(e,t){let n=e.trim(),o=c(t);return!n||!o||re.test(n)||n.startsWith("/")||o!=="/"&&(n===o||n.startsWith(`${o}/`))?n:o==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${o}${n}`:`${o}/${n}`}function P(e,t){if(!e)return;let n=c(e.basename)??c(t),o=c(e.routeBasePath),r=c(e.transportBasePath),a={...e,...n!==void 0?{basename:n}:{},...o!==void 0?{routeBasePath:o}:{},...r!==void 0?{transportBasePath:r}:{}};return a.favicon&&(a.favicon=B(a.favicon,n)),a.links&&(a.links=a.links.map(s=>({...s,href:B(s.href,n)}))),a}import R from"react";var U="webframez-route-children",z="__webframezRouteChildren",M="WebframezRouteChildren",S=()=>R.createElement(U);S.displayName=M;S[z]=!0;var ie=S;function L(e){if(e===U||e===ie)return!0;if(!e||typeof e!="function"&&typeof e!="object")return!1;try{let t=e;return t[z]===!0||t.displayName===M||t.name==="RouteChildren"}catch{return!1}}function H(e){if(!e||typeof e!="object")return!1;try{return"type"in e&&"props"in e}catch{return!1}}function y(e,t){if(e==null||typeof e=="boolean")return e;if(Array.isArray(e)){let d=!1,g=e.map(l=>{let h=y(l,t);return h!==l&&(d=!0),h});return d?g:e}if(H(e)&&L(e.type))return t;let n=R.isValidElement(e);if(!n&&!H(e))return e;if(n&&L(e.type))return t;let o=e.props??{};if(!("children"in o))return e;let r=y(o.children,t);if(r===o.children)return e;if(n)return Array.isArray(r)?R.cloneElement(e,void 0,...r):R.cloneElement(e,void 0,r);let a=e,s={...a.props??{},children:r,...a.key!==void 0&&a.key!==null?{key:a.key}:{}};return R.createElement(a.type,s)}import{Fragment as Ce,jsx as p,jsxs as ye}from"react/jsx-runtime";var de={push:()=>{},replace:()=>{},refresh:()=>{},refreshContext:()=>{}},E=typeof m.createContext=="function"?m.createContext(null):null,Y="__WEBFRAMEZ_ROUTER__",ue="[data-webframez-head='true']";function W(){if(typeof window>"u"||typeof document>"u")return;let e=()=>{window.scrollTo({top:0,left:0}),document.scrollingElement&&(document.scrollingElement.scrollTop=0),document.documentElement.scrollTop=0,document.body&&(document.body.scrollTop=0)};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(e);return}window.setTimeout(e,0)}function x(e){return c(e)??""}function le(e,t){return t?e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length)||"/":e||"/":e||"/"}function Z(e,t){let n=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return e?n==="/"?e:`${e}${n}`:n}function fe(){return x(typeof window>"u"?globalThis.__RSC_ROUTE_BASE_PATH:window.__RSC_ROUTE_BASE_PATH)}function K(e){if(typeof window>"u")return e||"/";let t=x(window.__RSC_BASENAME),n=fe(),o=le(e||"/",t);return Z(n,o)}function V(e){if(typeof window>"u")return e;let t=window.__RSC_ENDPOINT;return typeof t=="string"&&t.trim()!==""?t:e}function pe(){return typeof window>"u"?null:window[Y]??null}function me(e){typeof window>"u"||(window[Y]=e)}function D(){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("="),a=r>=0?o.slice(0,r).trim():o,s=r>=0?o.slice(r+1):"";a&&(e[a]=decodeURIComponent(s))}return e}function F(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 Ie(){return m.useMemo(()=>({all:()=>D(),get:e=>D()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=F(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=F(e,"",{...t??{},maxAge:0}))}}),[])}function Oe(){let e=E?m.useContext(E):null;if(!e){let t=pe();if(t)return t;if(typeof window>"u")return de;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function Re({active:e}){return p("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 j(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 q(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 N(e){if(typeof document>"u")return;let t=P(e)??e;X(t),document.title=t.title||"Webframez React";for(let n of document.head.querySelectorAll(ue))n.remove();t.description&&j({name:"description",content:t.description}),t.favicon&&q({rel:"icon",href:t.favicon});for(let n of t.meta??[])j(n);for(let n of t.links??[])q(n)}function X(e){if(typeof window>"u")return;let t=P(e)??e,n=c(t.basename),o=c(t.routeBasePath),r=c(t.transportBasePath);window.__RSC_BASENAME=n??"",window.__RSC_ROUTE_BASE_PATH=o??"",r!==void 0&&(window.__RSC_ENDPOINT=Z(r,"/rsc"))}function ge(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function he(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,se(ge(e)))}function we(e){let t=V(e),n=K(window.location.pathname);return he()??G(fetch(`${t}?path=${encodeURIComponent(n)}&search=${encodeURIComponent(window.location.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}function _e(e,t){return function(){let o=m.use(e),r=typeof o.contextModel<"u"&&typeof o.pageModel<"u",[a,s]=f(o.model),[d,g]=f(o.contextModel),[l,h]=f(o.pageModel),[v,b]=f(o.head),[J,w]=f(!1),[Q,ee]=f(!1),[te,A]=f(r);async function I(i){let C=V(t),u=K(i.pathname);return await G(fetch(`${C}?path=${encodeURIComponent(u)}&search=${encodeURIComponent(i.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}async function _(i,C="push"){w(!0);try{let u=await I(i);N(u.head),b(u.head),s(u.model),g(u.contextModel),h(u.pageModel),A(!0);let T=`${i.pathname}${i.search}${i.hash}`;C==="replace"?(history.replaceState(null,"",T),W()):C==="push"&&(history.pushState(null,"",T),W())}catch(u){console.error("[webframez-react] Failed to render route",u),s(p("p",{children:"Failed to load route."}))}finally{w(!1)}}async function ne(){w(!0);try{let i=await I(new URL(window.location.href));if(N(i.head),b(i.head),Object.prototype.hasOwnProperty.call(i,"contextModel")){g(i.contextModel),A(!0);return}s(i.model),A(!1)}catch(i){console.error("[webframez-react] Failed to refresh route context",i)}finally{w(!1)}}$(()=>{N(v)},[v]),$(()=>{ee(!0);let i=()=>{_(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let O=m.useMemo(()=>({push:i=>{_(new URL(i,window.location.origin),"push")},replace:i=>{_(new URL(i,window.location.origin),"replace")},refresh:()=>{_(new URL(window.location.href),"none")},refreshContext:()=>{ne()}}),[]);me(O);let oe=te&&typeof l<"u"?d?p(ce,{page:l,children:y(d,l)}):l:a,k=ye(Ce,{children:[Q?p(Re,{active:J}):null,oe??p("p",{style:{padding:24},children:"Loading..."})]});return E?p(E.Provider,{value:O,children:k}):k}}function ke(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",a=Promise.resolve(we(r)).then(d=>(d?.head&&X(d.head),d)),s=_e(a,r);return ae(n,p(s,{}))}export{ke as mountWebframezClient,Ie as useCookie,Oe as useRouter};
1
+ import m,{useEffect as D,useState as f}from"react";import{hydrateRoot as se}from"react-dom/client";import{createFromFetch as Y,createFromReadableStream as ce}from"react-server-dom-webpack/client";import{RouteChildrenSlotProvider as de}from"@webtypen/webframez-react/route-slot";var ie=/^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;function c(e){if(typeof e!="string")return;let t=e.trim();return!t||t==="/"?"":t.replace(/\/+$/,"")}function k(e,t){let n=e.trim(),o=c(t);return!n||!o||ie.test(n)||n.startsWith("/")||o!=="/"&&(n===o||n.startsWith(`${o}/`))?n:o==="/"?n.startsWith("/")?n:`/${n}`:n.startsWith("/")?`${o}${n}`:`${o}/${n}`}function P(e,t){if(!e)return;let n=c(e.basename)??c(t),o=c(e.routeBasePath),r=c(e.transportBasePath),a={...e,...n!==void 0?{basename:n}:{},...o!==void 0?{routeBasePath:o}:{},...r!==void 0?{transportBasePath:r}:{}};return a.favicon&&(a.favicon=k(a.favicon,n)),a.links&&(a.links=a.links.map(s=>({...s,href:k(s.href,n)}))),a}import w from"react";var z="webframez-route-children",M="__webframezRouteChildren",$="WebframezRouteChildren",S=()=>w.createElement(z);S.displayName=$;S[M]=!0;var ae=S;function U(e){if(e===z||e===ae)return!0;if(!e||typeof e!="function"&&typeof e!="object")return!1;try{let t=e;return t[M]===!0||t.displayName===$||t.name==="RouteChildren"}catch{return!1}}function H(e){if(!e||typeof e!="object")return!1;try{return"type"in e&&"props"in e}catch{return!1}}function E(e,t){if(e==null||typeof e=="boolean")return e;if(Array.isArray(e)){let d=!1,_=e.map(l=>{let h=E(l,t);return h!==l&&(d=!0),h});return d?_:e}if(H(e)&&U(e.type))return t;let n=w.isValidElement(e);if(!n&&!H(e))return e;if(n&&U(e.type))return t;let o=e.props??{};if(!("children"in o))return e;let r=E(o.children,t);if(r===o.children)return e;if(n)return Array.isArray(r)?w.cloneElement(e,void 0,...r):w.cloneElement(e,void 0,r);let a=e,s={...a.props??{},children:r,...a.key!==void 0&&a.key!==null?{key:a.key}:{}};return w.createElement(a.type,s)}import{Fragment as Pe,jsx as p,jsxs as Se}from"react/jsx-runtime";var ue={push:()=>{},replace:()=>{},refresh:()=>{},refreshContext:()=>{}},A=typeof m.createContext=="function"?m.createContext(null):null,Z="__WEBFRAMEZ_ROUTER__",le="__WEBFRAMEZ_REACT_BUILD_ID",fe="x-webframez-react-build",pe="[data-webframez-head='true']";function W(){if(typeof window>"u"||typeof document>"u")return;let e=()=>{window.scrollTo({top:0,left:0}),document.scrollingElement&&(document.scrollingElement.scrollTop=0),document.documentElement.scrollTop=0,document.body&&(document.body.scrollTop=0)};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(e);return}window.setTimeout(e,0)}function x(e){return c(e)??""}function me(e,t){return t?e===t?"/":e.startsWith(`${t}/`)?e.slice(t.length)||"/":e||"/":e||"/"}function K(e,t){let n=!t||t==="/"?"/":t.startsWith("/")?t:`/${t}`;return e?n==="/"?e:`${e}${n}`:n}function Re(){return x(typeof window>"u"?globalThis.__RSC_ROUTE_BASE_PATH:window.__RSC_ROUTE_BASE_PATH)}function V(e){if(typeof window>"u")return e||"/";let t=x(window.__RSC_BASENAME),n=Re(),o=me(e||"/",t);return K(n,o)}function X(e){if(typeof window>"u")return e;let t=window.__RSC_ENDPOINT;return typeof t=="string"&&t.trim()!==""?t:e}function we(){if(typeof window>"u")return"";let e=window[le];return typeof e=="string"?e:""}function _e(e){let t=we();return!!t&&!!e&&t!==e}function N(e){typeof window>"u"||window.location.assign(`${e.pathname}${e.search}${e.hash}`)}function he(){return typeof window>"u"?null:window[Z]??null}function ge(e){typeof window>"u"||(window[Z]=e)}function F(){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("="),a=r>=0?o.slice(0,r).trim():o,s=r>=0?o.slice(r+1):"";a&&(e[a]=decodeURIComponent(s))}return e}function j(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 He(){return m.useMemo(()=>({all:()=>F(),get:e=>F()[e],set:(e,t,n)=>{typeof document>"u"||(document.cookie=j(e,t,n))},remove:(e,t)=>{typeof document>"u"||(document.cookie=j(e,"",{...t??{},maxAge:0}))}}),[])}function ze(){let e=A?m.useContext(A):null;if(!e){let t=he();if(t)return t;if(typeof window>"u")return ue;throw new Error("useRouter must be used inside mountWebframezClient()")}return e}function Ce({active:e}){return p("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 G(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 q(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 v(e){if(typeof document>"u")return;let t=P(e)??e;J(t),document.body.className=t.bodyClassName||"",document.title=t.title||"Webframez React";for(let n of document.head.querySelectorAll(pe))n.remove();t.description&&G({name:"description",content:t.description}),t.favicon&&q({rel:"icon",href:t.favicon});for(let n of t.meta??[])G(n);for(let n of t.links??[])q(n)}function J(e){if(typeof window>"u")return;let t=P(e)??e,n=c(t.basename),o=c(t.routeBasePath),r=c(t.transportBasePath);window.__RSC_BASENAME=n??"",window.__RSC_ROUTE_BASE_PATH=o??"",r!==void 0&&(window.__RSC_ENDPOINT=K(r,"/rsc"))}function ye(e){let t=new TextEncoder;return new ReadableStream({start(n){n.enqueue(t.encode(e)),n.close()}})}function Ee(){if(typeof window>"u")return null;let e=window.__RSC_INITIAL_PAYLOAD;return typeof e!="string"||e===""?null:(delete window.__RSC_INITIAL_PAYLOAD,ce(ye(e)))}function Ae(e){let t=X(e),n=V(window.location.pathname);return Ee()??Y(fetch(`${t}?path=${encodeURIComponent(n)}&search=${encodeURIComponent(window.location.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}}))}function Te(e,t){return function(){let o=m.use(e),r=typeof o.contextModel<"u"&&typeof o.pageModel<"u",[a,s]=f(o.model),[d,_]=f(o.contextModel),[l,h]=f(o.pageModel),[I,B]=f(o.head),[Q,g]=f(!1),[ee,te]=f(!1),[ne,T]=f(r);async function b(i){let y=X(t),u=V(i.pathname),R=await fetch(`${y}?path=${encodeURIComponent(u)}&search=${encodeURIComponent(i.search)}`,{cache:"no-store",credentials:"same-origin",headers:{Accept:"text/x-component"}});return _e(R.headers.get(fe))?(N(i),await new Promise(()=>{})):await Y(Promise.resolve(R))}async function C(i,y="push"){g(!0);try{let u=await b(i);v(u.head),B(u.head),s(u.model),_(u.contextModel),h(u.pageModel),T(!0);let R=`${i.pathname}${i.search}${i.hash}`;y==="replace"?(history.replaceState(null,"",R),W()):y==="push"&&(history.pushState(null,"",R),W())}catch(u){console.error("[webframez-react] Failed to render route",u),N(i),s(p("p",{children:"Failed to load route."}))}finally{g(!1)}}async function oe(){g(!0);try{let i=await b(new URL(window.location.href));if(v(i.head),B(i.head),Object.prototype.hasOwnProperty.call(i,"contextModel")){_(i.contextModel),T(!0);return}s(i.model),T(!1)}catch(i){console.error("[webframez-react] Failed to refresh route context",i),N(new URL(window.location.href))}finally{g(!1)}}D(()=>{v(I)},[I]),D(()=>{te(!0);let i=()=>{C(new URL(window.location.href),"none")};return window.addEventListener("popstate",i),()=>{window.removeEventListener("popstate",i)}},[]);let L=m.useMemo(()=>({push:i=>{C(new URL(i,window.location.origin),"push")},replace:i=>{C(new URL(i,window.location.origin),"replace")},refresh:()=>{C(new URL(window.location.href),"none")},refreshContext:()=>{oe()}}),[]);ge(L);let re=ne&&typeof l<"u"?d?p(de,{page:l,children:E(d,l)}):l:a,O=Se(Pe,{children:[ee?p(Ce,{active:Q}):null,re??p("p",{style:{padding:24},children:"Loading..."})]});return A?p(A.Provider,{value:L,children:O}):O}}function Me(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",a=Promise.resolve(Ae(r)).then(d=>(d?.head&&J(d.head),d)),s=Te(a,r);return se(n,p(s,{}))}export{Me as mountWebframezClient,He as useCookie,ze as useRouter};
package/dist/http.cjs CHANGED
@@ -52,7 +52,9 @@ function createHTMLShell(options = {}) {
52
52
  title = "RSC App",
53
53
  rscEndpoint = "/rsc",
54
54
  clientScriptUrl = "/client.js",
55
+ buildId = "",
55
56
  headTags = "",
57
+ bodyClassName = "",
56
58
  rootHtml = "",
57
59
  initialFlightData = "",
58
60
  basename = "",
@@ -101,11 +103,12 @@ function createHTMLShell(options = {}) {
101
103
  <title>${title}</title>
102
104
  ${headTags}
103
105
  </head>
104
- <body>
106
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
105
107
  <div id="root">${rootHtml}</div>
106
108
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
107
109
  <script>window.__RSC_BASENAME = "${basename}";</script>
108
110
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
111
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
109
112
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
110
113
  <script type="module" src="${clientScriptUrl}"></script>
111
114
  ${liveReloadScript}
@@ -470,6 +473,9 @@ function mergeHead(...configs) {
470
473
  if (config.description) {
471
474
  merged.description = config.description;
472
475
  }
476
+ if (hasOwn("bodyClassName")) {
477
+ merged.bodyClassName = config.bodyClassName;
478
+ }
473
479
  if (hasOwn("basename")) {
474
480
  merged.basename = config.basename;
475
481
  }
@@ -801,6 +807,15 @@ function createClientAssetVersion(distRootDir) {
801
807
  return "";
802
808
  }
803
809
  }
810
+ function createBuildId(distRootDir, manifestPath) {
811
+ const clientVersion = createClientAssetVersion(distRootDir);
812
+ try {
813
+ const stat = import_node_fs2.default.statSync(manifestPath);
814
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
815
+ } catch {
816
+ return clientVersion;
817
+ }
818
+ }
804
819
  function appendAssetVersion(url, version) {
805
820
  if (!version) {
806
821
  return url;
@@ -1494,6 +1509,30 @@ function createServerConsumerManifest(manifest) {
1494
1509
  }
1495
1510
  return consumerManifest;
1496
1511
  }
1512
+ function createManifestLoader(options) {
1513
+ let cache = null;
1514
+ return () => {
1515
+ const stat = import_node_fs2.default.statSync(options.manifestPath);
1516
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1517
+ if (cache && cache.cacheKey === cacheKey) {
1518
+ return cache;
1519
+ }
1520
+ const moduleMap = normalizeClientManifest(
1521
+ JSON.parse(import_node_fs2.default.readFileSync(options.manifestPath, "utf-8")),
1522
+ {
1523
+ distRootDir: options.distRootDir,
1524
+ cwd: options.cwd
1525
+ }
1526
+ );
1527
+ cache = {
1528
+ cacheKey,
1529
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1530
+ moduleMap,
1531
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1532
+ };
1533
+ return cache;
1534
+ };
1535
+ }
1497
1536
  function createNodeRequestHandler(options) {
1498
1537
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1499
1538
  const distRootDir = import_node_path3.default.resolve(options.distRootDir);
@@ -1511,14 +1550,11 @@ function createNodeRequestHandler(options) {
1511
1550
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1512
1551
  const liveReloadClients = /* @__PURE__ */ new Set();
1513
1552
  const router = createFileRouter({ pagesDir });
1514
- const moduleMap = normalizeClientManifest(
1515
- JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8")),
1516
- {
1517
- distRootDir,
1518
- cwd: process.cwd()
1519
- }
1520
- );
1521
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1553
+ const getManifestState = createManifestLoader({
1554
+ distRootDir,
1555
+ manifestPath,
1556
+ cwd: process.cwd()
1557
+ });
1522
1558
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1523
1559
  const disposeInitialHtmlWorker = () => {
1524
1560
  initialHtmlWorker.dispose();
@@ -1573,6 +1609,7 @@ function createNodeRequestHandler(options) {
1573
1609
  return;
1574
1610
  }
1575
1611
  if (url.pathname === rscPath) {
1612
+ const manifestState2 = getManifestState();
1576
1613
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1577
1614
  const requestContext = createRouteRequestContext(
1578
1615
  req,
@@ -1595,8 +1632,9 @@ function createNodeRequestHandler(options) {
1595
1632
  pageModel: resolved2.pageModel,
1596
1633
  head: resolved2.head
1597
1634
  };
1635
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1598
1636
  sendRSC(res, payload, {
1599
- moduleMap,
1637
+ moduleMap: manifestState2.moduleMap,
1600
1638
  statusCode: resolved2.statusCode
1601
1639
  });
1602
1640
  return;
@@ -1666,8 +1704,9 @@ function createNodeRequestHandler(options) {
1666
1704
  model: resolved.model,
1667
1705
  head: resolved.head
1668
1706
  };
1707
+ const manifestState = getManifestState();
1669
1708
  const initialFlightData = await renderRSCToString(initialPayload, {
1670
- moduleMap
1709
+ moduleMap: manifestState.moduleMap
1671
1710
  });
1672
1711
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1673
1712
  const shellClientScriptUrl = appendAssetVersion(
@@ -1684,7 +1723,7 @@ function createNodeRequestHandler(options) {
1684
1723
  try {
1685
1724
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1686
1725
  flightData: initialFlightData,
1687
- moduleMap: serverConsumerModuleMap
1726
+ moduleMap: manifestState.serverConsumerModuleMap
1688
1727
  });
1689
1728
  } catch (error) {
1690
1729
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1692,7 +1731,7 @@ function createNodeRequestHandler(options) {
1692
1731
  initialHtmlWorker.dispose();
1693
1732
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1694
1733
  flightData: initialFlightData,
1695
- moduleMap: serverConsumerModuleMap
1734
+ moduleMap: manifestState.serverConsumerModuleMap
1696
1735
  });
1697
1736
  } catch (retryError) {
1698
1737
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1700,7 +1739,7 @@ function createNodeRequestHandler(options) {
1700
1739
  initialHtmlWorker.dispose();
1701
1740
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1702
1741
  flightData: initialFlightData,
1703
- moduleMap: serverConsumerModuleMap
1742
+ moduleMap: manifestState.serverConsumerModuleMap
1704
1743
  });
1705
1744
  } catch (flightRenderError) {
1706
1745
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1724,7 +1763,9 @@ function createNodeRequestHandler(options) {
1724
1763
  createHTMLShell({
1725
1764
  title: resolved.head.title || "Webframez React",
1726
1765
  headTags: renderHeadToString(resolved.head),
1766
+ bodyClassName: resolved.head.bodyClassName || "",
1727
1767
  clientScriptUrl: shellClientScriptUrl,
1768
+ buildId: manifestState.buildId,
1728
1769
  rscEndpoint: shellRscEndpoint,
1729
1770
  rootHtml,
1730
1771
  initialFlightData,
package/dist/http.js CHANGED
@@ -32,7 +32,9 @@ function createHTMLShell(options = {}) {
32
32
  title = "RSC App",
33
33
  rscEndpoint = "/rsc",
34
34
  clientScriptUrl = "/client.js",
35
+ buildId = "",
35
36
  headTags = "",
37
+ bodyClassName = "",
36
38
  rootHtml = "",
37
39
  initialFlightData = "",
38
40
  basename = "",
@@ -81,11 +83,12 @@ function createHTMLShell(options = {}) {
81
83
  <title>${title}</title>
82
84
  ${headTags}
83
85
  </head>
84
- <body>
86
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
85
87
  <div id="root">${rootHtml}</div>
86
88
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
87
89
  <script>window.__RSC_BASENAME = "${basename}";</script>
88
90
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
91
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
89
92
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
90
93
  <script type="module" src="${clientScriptUrl}"></script>
91
94
  ${liveReloadScript}
@@ -450,6 +453,9 @@ function mergeHead(...configs) {
450
453
  if (config.description) {
451
454
  merged.description = config.description;
452
455
  }
456
+ if (hasOwn("bodyClassName")) {
457
+ merged.bodyClassName = config.bodyClassName;
458
+ }
453
459
  if (hasOwn("basename")) {
454
460
  merged.basename = config.basename;
455
461
  }
@@ -781,6 +787,15 @@ function createClientAssetVersion(distRootDir) {
781
787
  return "";
782
788
  }
783
789
  }
790
+ function createBuildId(distRootDir, manifestPath) {
791
+ const clientVersion = createClientAssetVersion(distRootDir);
792
+ try {
793
+ const stat = fs2.statSync(manifestPath);
794
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
795
+ } catch {
796
+ return clientVersion;
797
+ }
798
+ }
784
799
  function appendAssetVersion(url, version) {
785
800
  if (!version) {
786
801
  return url;
@@ -1474,6 +1489,30 @@ function createServerConsumerManifest(manifest) {
1474
1489
  }
1475
1490
  return consumerManifest;
1476
1491
  }
1492
+ function createManifestLoader(options) {
1493
+ let cache = null;
1494
+ return () => {
1495
+ const stat = fs2.statSync(options.manifestPath);
1496
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1497
+ if (cache && cache.cacheKey === cacheKey) {
1498
+ return cache;
1499
+ }
1500
+ const moduleMap = normalizeClientManifest(
1501
+ JSON.parse(fs2.readFileSync(options.manifestPath, "utf-8")),
1502
+ {
1503
+ distRootDir: options.distRootDir,
1504
+ cwd: options.cwd
1505
+ }
1506
+ );
1507
+ cache = {
1508
+ cacheKey,
1509
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1510
+ moduleMap,
1511
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1512
+ };
1513
+ return cache;
1514
+ };
1515
+ }
1477
1516
  function createNodeRequestHandler(options) {
1478
1517
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1479
1518
  const distRootDir = path3.resolve(options.distRootDir);
@@ -1491,14 +1530,11 @@ function createNodeRequestHandler(options) {
1491
1530
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1492
1531
  const liveReloadClients = /* @__PURE__ */ new Set();
1493
1532
  const router = createFileRouter({ pagesDir });
1494
- const moduleMap = normalizeClientManifest(
1495
- JSON.parse(fs2.readFileSync(manifestPath, "utf-8")),
1496
- {
1497
- distRootDir,
1498
- cwd: process.cwd()
1499
- }
1500
- );
1501
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1533
+ const getManifestState = createManifestLoader({
1534
+ distRootDir,
1535
+ manifestPath,
1536
+ cwd: process.cwd()
1537
+ });
1502
1538
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1503
1539
  const disposeInitialHtmlWorker = () => {
1504
1540
  initialHtmlWorker.dispose();
@@ -1553,6 +1589,7 @@ function createNodeRequestHandler(options) {
1553
1589
  return;
1554
1590
  }
1555
1591
  if (url.pathname === rscPath) {
1592
+ const manifestState2 = getManifestState();
1556
1593
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1557
1594
  const requestContext = createRouteRequestContext(
1558
1595
  req,
@@ -1575,8 +1612,9 @@ function createNodeRequestHandler(options) {
1575
1612
  pageModel: resolved2.pageModel,
1576
1613
  head: resolved2.head
1577
1614
  };
1615
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1578
1616
  sendRSC(res, payload, {
1579
- moduleMap,
1617
+ moduleMap: manifestState2.moduleMap,
1580
1618
  statusCode: resolved2.statusCode
1581
1619
  });
1582
1620
  return;
@@ -1646,8 +1684,9 @@ function createNodeRequestHandler(options) {
1646
1684
  model: resolved.model,
1647
1685
  head: resolved.head
1648
1686
  };
1687
+ const manifestState = getManifestState();
1649
1688
  const initialFlightData = await renderRSCToString(initialPayload, {
1650
- moduleMap
1689
+ moduleMap: manifestState.moduleMap
1651
1690
  });
1652
1691
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1653
1692
  const shellClientScriptUrl = appendAssetVersion(
@@ -1664,7 +1703,7 @@ function createNodeRequestHandler(options) {
1664
1703
  try {
1665
1704
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1666
1705
  flightData: initialFlightData,
1667
- moduleMap: serverConsumerModuleMap
1706
+ moduleMap: manifestState.serverConsumerModuleMap
1668
1707
  });
1669
1708
  } catch (error) {
1670
1709
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1672,7 +1711,7 @@ function createNodeRequestHandler(options) {
1672
1711
  initialHtmlWorker.dispose();
1673
1712
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1674
1713
  flightData: initialFlightData,
1675
- moduleMap: serverConsumerModuleMap
1714
+ moduleMap: manifestState.serverConsumerModuleMap
1676
1715
  });
1677
1716
  } catch (retryError) {
1678
1717
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1680,7 +1719,7 @@ function createNodeRequestHandler(options) {
1680
1719
  initialHtmlWorker.dispose();
1681
1720
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1682
1721
  flightData: initialFlightData,
1683
- moduleMap: serverConsumerModuleMap
1722
+ moduleMap: manifestState.serverConsumerModuleMap
1684
1723
  });
1685
1724
  } catch (flightRenderError) {
1686
1725
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1704,7 +1743,9 @@ function createNodeRequestHandler(options) {
1704
1743
  createHTMLShell({
1705
1744
  title: resolved.head.title || "Webframez React",
1706
1745
  headTags: renderHeadToString(resolved.head),
1746
+ bodyClassName: resolved.head.bodyClassName || "",
1707
1747
  clientScriptUrl: shellClientScriptUrl,
1748
+ buildId: manifestState.buildId,
1708
1749
  rscEndpoint: shellRscEndpoint,
1709
1750
  rootHtml,
1710
1751
  initialFlightData,
package/dist/index.cjs CHANGED
@@ -59,7 +59,9 @@ function createHTMLShell(options = {}) {
59
59
  title = "RSC App",
60
60
  rscEndpoint = "/rsc",
61
61
  clientScriptUrl = "/client.js",
62
+ buildId = "",
62
63
  headTags = "",
64
+ bodyClassName = "",
63
65
  rootHtml = "",
64
66
  initialFlightData = "",
65
67
  basename = "",
@@ -108,11 +110,12 @@ function createHTMLShell(options = {}) {
108
110
  <title>${title}</title>
109
111
  ${headTags}
110
112
  </head>
111
- <body>
113
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
112
114
  <div id="root">${rootHtml}</div>
113
115
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
114
116
  <script>window.__RSC_BASENAME = "${basename}";</script>
115
117
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
118
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
116
119
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
117
120
  <script type="module" src="${clientScriptUrl}"></script>
118
121
  ${liveReloadScript}
@@ -505,6 +508,9 @@ function mergeHead(...configs) {
505
508
  if (config.description) {
506
509
  merged.description = config.description;
507
510
  }
511
+ if (hasOwn("bodyClassName")) {
512
+ merged.bodyClassName = config.bodyClassName;
513
+ }
508
514
  if (hasOwn("basename")) {
509
515
  merged.basename = config.basename;
510
516
  }
@@ -841,6 +847,15 @@ function createClientAssetVersion(distRootDir) {
841
847
  return "";
842
848
  }
843
849
  }
850
+ function createBuildId(distRootDir, manifestPath) {
851
+ const clientVersion = createClientAssetVersion(distRootDir);
852
+ try {
853
+ const stat = import_node_fs2.default.statSync(manifestPath);
854
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
855
+ } catch {
856
+ return clientVersion;
857
+ }
858
+ }
844
859
  function appendAssetVersion(url, version) {
845
860
  if (!version) {
846
861
  return url;
@@ -1534,6 +1549,30 @@ function createServerConsumerManifest(manifest) {
1534
1549
  }
1535
1550
  return consumerManifest;
1536
1551
  }
1552
+ function createManifestLoader(options) {
1553
+ let cache = null;
1554
+ return () => {
1555
+ const stat = import_node_fs2.default.statSync(options.manifestPath);
1556
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1557
+ if (cache && cache.cacheKey === cacheKey) {
1558
+ return cache;
1559
+ }
1560
+ const moduleMap = normalizeClientManifest(
1561
+ JSON.parse(import_node_fs2.default.readFileSync(options.manifestPath, "utf-8")),
1562
+ {
1563
+ distRootDir: options.distRootDir,
1564
+ cwd: options.cwd
1565
+ }
1566
+ );
1567
+ cache = {
1568
+ cacheKey,
1569
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1570
+ moduleMap,
1571
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1572
+ };
1573
+ return cache;
1574
+ };
1575
+ }
1537
1576
  function createNodeRequestHandler(options) {
1538
1577
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1539
1578
  const distRootDir = import_node_path3.default.resolve(options.distRootDir);
@@ -1551,14 +1590,11 @@ function createNodeRequestHandler(options) {
1551
1590
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1552
1591
  const liveReloadClients = /* @__PURE__ */ new Set();
1553
1592
  const router = createFileRouter({ pagesDir });
1554
- const moduleMap = normalizeClientManifest(
1555
- JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8")),
1556
- {
1557
- distRootDir,
1558
- cwd: process.cwd()
1559
- }
1560
- );
1561
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1593
+ const getManifestState = createManifestLoader({
1594
+ distRootDir,
1595
+ manifestPath,
1596
+ cwd: process.cwd()
1597
+ });
1562
1598
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1563
1599
  const disposeInitialHtmlWorker = () => {
1564
1600
  initialHtmlWorker.dispose();
@@ -1613,6 +1649,7 @@ function createNodeRequestHandler(options) {
1613
1649
  return;
1614
1650
  }
1615
1651
  if (url.pathname === rscPath) {
1652
+ const manifestState2 = getManifestState();
1616
1653
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1617
1654
  const requestContext = createRouteRequestContext(
1618
1655
  req,
@@ -1635,8 +1672,9 @@ function createNodeRequestHandler(options) {
1635
1672
  pageModel: resolved2.pageModel,
1636
1673
  head: resolved2.head
1637
1674
  };
1675
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1638
1676
  sendRSC(res, payload, {
1639
- moduleMap,
1677
+ moduleMap: manifestState2.moduleMap,
1640
1678
  statusCode: resolved2.statusCode
1641
1679
  });
1642
1680
  return;
@@ -1706,8 +1744,9 @@ function createNodeRequestHandler(options) {
1706
1744
  model: resolved.model,
1707
1745
  head: resolved.head
1708
1746
  };
1747
+ const manifestState = getManifestState();
1709
1748
  const initialFlightData = await renderRSCToString(initialPayload, {
1710
- moduleMap
1749
+ moduleMap: manifestState.moduleMap
1711
1750
  });
1712
1751
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1713
1752
  const shellClientScriptUrl = appendAssetVersion(
@@ -1724,7 +1763,7 @@ function createNodeRequestHandler(options) {
1724
1763
  try {
1725
1764
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1726
1765
  flightData: initialFlightData,
1727
- moduleMap: serverConsumerModuleMap
1766
+ moduleMap: manifestState.serverConsumerModuleMap
1728
1767
  });
1729
1768
  } catch (error) {
1730
1769
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1732,7 +1771,7 @@ function createNodeRequestHandler(options) {
1732
1771
  initialHtmlWorker.dispose();
1733
1772
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1734
1773
  flightData: initialFlightData,
1735
- moduleMap: serverConsumerModuleMap
1774
+ moduleMap: manifestState.serverConsumerModuleMap
1736
1775
  });
1737
1776
  } catch (retryError) {
1738
1777
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1740,7 +1779,7 @@ function createNodeRequestHandler(options) {
1740
1779
  initialHtmlWorker.dispose();
1741
1780
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1742
1781
  flightData: initialFlightData,
1743
- moduleMap: serverConsumerModuleMap
1782
+ moduleMap: manifestState.serverConsumerModuleMap
1744
1783
  });
1745
1784
  } catch (flightRenderError) {
1746
1785
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1764,7 +1803,9 @@ function createNodeRequestHandler(options) {
1764
1803
  createHTMLShell({
1765
1804
  title: resolved.head.title || "Webframez React",
1766
1805
  headTags: renderHeadToString(resolved.head),
1806
+ bodyClassName: resolved.head.bodyClassName || "",
1767
1807
  clientScriptUrl: shellClientScriptUrl,
1808
+ buildId: manifestState.buildId,
1768
1809
  rscEndpoint: shellRscEndpoint,
1769
1810
  rootHtml,
1770
1811
  initialFlightData,
package/dist/index.js CHANGED
@@ -20,7 +20,9 @@ function createHTMLShell(options = {}) {
20
20
  title = "RSC App",
21
21
  rscEndpoint = "/rsc",
22
22
  clientScriptUrl = "/client.js",
23
+ buildId = "",
23
24
  headTags = "",
25
+ bodyClassName = "",
24
26
  rootHtml = "",
25
27
  initialFlightData = "",
26
28
  basename = "",
@@ -69,11 +71,12 @@ function createHTMLShell(options = {}) {
69
71
  <title>${title}</title>
70
72
  ${headTags}
71
73
  </head>
72
- <body>
74
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
73
75
  <div id="root">${rootHtml}</div>
74
76
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
75
77
  <script>window.__RSC_BASENAME = "${basename}";</script>
76
78
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
79
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
77
80
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
78
81
  <script type="module" src="${clientScriptUrl}"></script>
79
82
  ${liveReloadScript}
@@ -466,6 +469,9 @@ function mergeHead(...configs) {
466
469
  if (config.description) {
467
470
  merged.description = config.description;
468
471
  }
472
+ if (hasOwn("bodyClassName")) {
473
+ merged.bodyClassName = config.bodyClassName;
474
+ }
469
475
  if (hasOwn("basename")) {
470
476
  merged.basename = config.basename;
471
477
  }
@@ -807,6 +813,15 @@ function createClientAssetVersion(distRootDir) {
807
813
  return "";
808
814
  }
809
815
  }
816
+ function createBuildId(distRootDir, manifestPath) {
817
+ const clientVersion = createClientAssetVersion(distRootDir);
818
+ try {
819
+ const stat = fs2.statSync(manifestPath);
820
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
821
+ } catch {
822
+ return clientVersion;
823
+ }
824
+ }
810
825
  function appendAssetVersion(url, version) {
811
826
  if (!version) {
812
827
  return url;
@@ -1500,6 +1515,30 @@ function createServerConsumerManifest(manifest) {
1500
1515
  }
1501
1516
  return consumerManifest;
1502
1517
  }
1518
+ function createManifestLoader(options) {
1519
+ let cache = null;
1520
+ return () => {
1521
+ const stat = fs2.statSync(options.manifestPath);
1522
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1523
+ if (cache && cache.cacheKey === cacheKey) {
1524
+ return cache;
1525
+ }
1526
+ const moduleMap = normalizeClientManifest(
1527
+ JSON.parse(fs2.readFileSync(options.manifestPath, "utf-8")),
1528
+ {
1529
+ distRootDir: options.distRootDir,
1530
+ cwd: options.cwd
1531
+ }
1532
+ );
1533
+ cache = {
1534
+ cacheKey,
1535
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1536
+ moduleMap,
1537
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1538
+ };
1539
+ return cache;
1540
+ };
1541
+ }
1503
1542
  function createNodeRequestHandler(options) {
1504
1543
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1505
1544
  const distRootDir = path3.resolve(options.distRootDir);
@@ -1517,14 +1556,11 @@ function createNodeRequestHandler(options) {
1517
1556
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1518
1557
  const liveReloadClients = /* @__PURE__ */ new Set();
1519
1558
  const router = createFileRouter({ pagesDir });
1520
- const moduleMap = normalizeClientManifest(
1521
- JSON.parse(fs2.readFileSync(manifestPath, "utf-8")),
1522
- {
1523
- distRootDir,
1524
- cwd: process.cwd()
1525
- }
1526
- );
1527
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1559
+ const getManifestState = createManifestLoader({
1560
+ distRootDir,
1561
+ manifestPath,
1562
+ cwd: process.cwd()
1563
+ });
1528
1564
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1529
1565
  const disposeInitialHtmlWorker = () => {
1530
1566
  initialHtmlWorker.dispose();
@@ -1579,6 +1615,7 @@ function createNodeRequestHandler(options) {
1579
1615
  return;
1580
1616
  }
1581
1617
  if (url.pathname === rscPath) {
1618
+ const manifestState2 = getManifestState();
1582
1619
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1583
1620
  const requestContext = createRouteRequestContext(
1584
1621
  req,
@@ -1601,8 +1638,9 @@ function createNodeRequestHandler(options) {
1601
1638
  pageModel: resolved2.pageModel,
1602
1639
  head: resolved2.head
1603
1640
  };
1641
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1604
1642
  sendRSC(res, payload, {
1605
- moduleMap,
1643
+ moduleMap: manifestState2.moduleMap,
1606
1644
  statusCode: resolved2.statusCode
1607
1645
  });
1608
1646
  return;
@@ -1672,8 +1710,9 @@ function createNodeRequestHandler(options) {
1672
1710
  model: resolved.model,
1673
1711
  head: resolved.head
1674
1712
  };
1713
+ const manifestState = getManifestState();
1675
1714
  const initialFlightData = await renderRSCToString(initialPayload, {
1676
- moduleMap
1715
+ moduleMap: manifestState.moduleMap
1677
1716
  });
1678
1717
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1679
1718
  const shellClientScriptUrl = appendAssetVersion(
@@ -1690,7 +1729,7 @@ function createNodeRequestHandler(options) {
1690
1729
  try {
1691
1730
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1692
1731
  flightData: initialFlightData,
1693
- moduleMap: serverConsumerModuleMap
1732
+ moduleMap: manifestState.serverConsumerModuleMap
1694
1733
  });
1695
1734
  } catch (error) {
1696
1735
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1698,7 +1737,7 @@ function createNodeRequestHandler(options) {
1698
1737
  initialHtmlWorker.dispose();
1699
1738
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1700
1739
  flightData: initialFlightData,
1701
- moduleMap: serverConsumerModuleMap
1740
+ moduleMap: manifestState.serverConsumerModuleMap
1702
1741
  });
1703
1742
  } catch (retryError) {
1704
1743
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1706,7 +1745,7 @@ function createNodeRequestHandler(options) {
1706
1745
  initialHtmlWorker.dispose();
1707
1746
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1708
1747
  flightData: initialFlightData,
1709
- moduleMap: serverConsumerModuleMap
1748
+ moduleMap: manifestState.serverConsumerModuleMap
1710
1749
  });
1711
1750
  } catch (flightRenderError) {
1712
1751
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1730,7 +1769,9 @@ function createNodeRequestHandler(options) {
1730
1769
  createHTMLShell({
1731
1770
  title: resolved.head.title || "Webframez React",
1732
1771
  headTags: renderHeadToString(resolved.head),
1772
+ bodyClassName: resolved.head.bodyClassName || "",
1733
1773
  clientScriptUrl: shellClientScriptUrl,
1774
+ buildId: manifestState.buildId,
1734
1775
  rscEndpoint: shellRscEndpoint,
1735
1776
  rootHtml,
1736
1777
  initialFlightData,
package/dist/router.cjs CHANGED
@@ -357,6 +357,9 @@ function mergeHead(...configs) {
357
357
  if (config.description) {
358
358
  merged.description = config.description;
359
359
  }
360
+ if (hasOwn("bodyClassName")) {
361
+ merged.bodyClassName = config.bodyClassName;
362
+ }
360
363
  if (hasOwn("basename")) {
361
364
  merged.basename = config.basename;
362
365
  }
package/dist/router.js CHANGED
@@ -327,6 +327,9 @@ function mergeHead(...configs) {
327
327
  if (config.description) {
328
328
  merged.description = config.description;
329
329
  }
330
+ if (hasOwn("bodyClassName")) {
331
+ merged.bodyClassName = config.bodyClassName;
332
+ }
330
333
  if (hasOwn("basename")) {
331
334
  merged.basename = config.basename;
332
335
  }
package/dist/types.d.ts CHANGED
@@ -60,6 +60,7 @@ export type HeadLinkTag = {
60
60
  export type HeadConfig = {
61
61
  title?: string;
62
62
  description?: string;
63
+ bodyClassName?: string;
63
64
  basename?: string;
64
65
  routeBasePath?: string;
65
66
  transportBasePath?: string;
@@ -111,7 +112,9 @@ export type CreateHtmlShellOptions = {
111
112
  title?: string;
112
113
  rscEndpoint?: string;
113
114
  clientScriptUrl?: string;
115
+ buildId?: string;
114
116
  headTags?: string;
117
+ bodyClassName?: string;
115
118
  rootHtml?: string;
116
119
  initialFlightData?: string;
117
120
  basename?: string;
@@ -59,7 +59,9 @@ function createHTMLShell(options = {}) {
59
59
  title = "RSC App",
60
60
  rscEndpoint = "/rsc",
61
61
  clientScriptUrl = "/client.js",
62
+ buildId = "",
62
63
  headTags = "",
64
+ bodyClassName = "",
63
65
  rootHtml = "",
64
66
  initialFlightData = "",
65
67
  basename = "",
@@ -108,11 +110,12 @@ function createHTMLShell(options = {}) {
108
110
  <title>${title}</title>
109
111
  ${headTags}
110
112
  </head>
111
- <body>
113
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
112
114
  <div id="root">${rootHtml}</div>
113
115
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
114
116
  <script>window.__RSC_BASENAME = "${basename}";</script>
115
117
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
118
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
116
119
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
117
120
  <script type="module" src="${clientScriptUrl}"></script>
118
121
  ${liveReloadScript}
@@ -477,6 +480,9 @@ function mergeHead(...configs) {
477
480
  if (config.description) {
478
481
  merged.description = config.description;
479
482
  }
483
+ if (hasOwn("bodyClassName")) {
484
+ merged.bodyClassName = config.bodyClassName;
485
+ }
480
486
  if (hasOwn("basename")) {
481
487
  merged.basename = config.basename;
482
488
  }
@@ -808,6 +814,15 @@ function createClientAssetVersion(distRootDir) {
808
814
  return "";
809
815
  }
810
816
  }
817
+ function createBuildId(distRootDir, manifestPath) {
818
+ const clientVersion = createClientAssetVersion(distRootDir);
819
+ try {
820
+ const stat = import_node_fs2.default.statSync(manifestPath);
821
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
822
+ } catch {
823
+ return clientVersion;
824
+ }
825
+ }
811
826
  function appendAssetVersion(url, version) {
812
827
  if (!version) {
813
828
  return url;
@@ -1501,6 +1516,30 @@ function createServerConsumerManifest(manifest) {
1501
1516
  }
1502
1517
  return consumerManifest;
1503
1518
  }
1519
+ function createManifestLoader(options) {
1520
+ let cache = null;
1521
+ return () => {
1522
+ const stat = import_node_fs2.default.statSync(options.manifestPath);
1523
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1524
+ if (cache && cache.cacheKey === cacheKey) {
1525
+ return cache;
1526
+ }
1527
+ const moduleMap = normalizeClientManifest(
1528
+ JSON.parse(import_node_fs2.default.readFileSync(options.manifestPath, "utf-8")),
1529
+ {
1530
+ distRootDir: options.distRootDir,
1531
+ cwd: options.cwd
1532
+ }
1533
+ );
1534
+ cache = {
1535
+ cacheKey,
1536
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1537
+ moduleMap,
1538
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1539
+ };
1540
+ return cache;
1541
+ };
1542
+ }
1504
1543
  function createNodeRequestHandler(options) {
1505
1544
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1506
1545
  const distRootDir = import_node_path3.default.resolve(options.distRootDir);
@@ -1518,14 +1557,11 @@ function createNodeRequestHandler(options) {
1518
1557
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1519
1558
  const liveReloadClients = /* @__PURE__ */ new Set();
1520
1559
  const router = createFileRouter({ pagesDir });
1521
- const moduleMap = normalizeClientManifest(
1522
- JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8")),
1523
- {
1524
- distRootDir,
1525
- cwd: process.cwd()
1526
- }
1527
- );
1528
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1560
+ const getManifestState = createManifestLoader({
1561
+ distRootDir,
1562
+ manifestPath,
1563
+ cwd: process.cwd()
1564
+ });
1529
1565
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1530
1566
  const disposeInitialHtmlWorker = () => {
1531
1567
  initialHtmlWorker.dispose();
@@ -1580,6 +1616,7 @@ function createNodeRequestHandler(options) {
1580
1616
  return;
1581
1617
  }
1582
1618
  if (url.pathname === rscPath) {
1619
+ const manifestState2 = getManifestState();
1583
1620
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1584
1621
  const requestContext = createRouteRequestContext(
1585
1622
  req,
@@ -1602,8 +1639,9 @@ function createNodeRequestHandler(options) {
1602
1639
  pageModel: resolved2.pageModel,
1603
1640
  head: resolved2.head
1604
1641
  };
1642
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1605
1643
  sendRSC(res, payload, {
1606
- moduleMap,
1644
+ moduleMap: manifestState2.moduleMap,
1607
1645
  statusCode: resolved2.statusCode
1608
1646
  });
1609
1647
  return;
@@ -1673,8 +1711,9 @@ function createNodeRequestHandler(options) {
1673
1711
  model: resolved.model,
1674
1712
  head: resolved.head
1675
1713
  };
1714
+ const manifestState = getManifestState();
1676
1715
  const initialFlightData = await renderRSCToString(initialPayload, {
1677
- moduleMap
1716
+ moduleMap: manifestState.moduleMap
1678
1717
  });
1679
1718
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1680
1719
  const shellClientScriptUrl = appendAssetVersion(
@@ -1691,7 +1730,7 @@ function createNodeRequestHandler(options) {
1691
1730
  try {
1692
1731
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1693
1732
  flightData: initialFlightData,
1694
- moduleMap: serverConsumerModuleMap
1733
+ moduleMap: manifestState.serverConsumerModuleMap
1695
1734
  });
1696
1735
  } catch (error) {
1697
1736
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1699,7 +1738,7 @@ function createNodeRequestHandler(options) {
1699
1738
  initialHtmlWorker.dispose();
1700
1739
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1701
1740
  flightData: initialFlightData,
1702
- moduleMap: serverConsumerModuleMap
1741
+ moduleMap: manifestState.serverConsumerModuleMap
1703
1742
  });
1704
1743
  } catch (retryError) {
1705
1744
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1707,7 +1746,7 @@ function createNodeRequestHandler(options) {
1707
1746
  initialHtmlWorker.dispose();
1708
1747
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1709
1748
  flightData: initialFlightData,
1710
- moduleMap: serverConsumerModuleMap
1749
+ moduleMap: manifestState.serverConsumerModuleMap
1711
1750
  });
1712
1751
  } catch (flightRenderError) {
1713
1752
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1731,7 +1770,9 @@ function createNodeRequestHandler(options) {
1731
1770
  createHTMLShell({
1732
1771
  title: resolved.head.title || "Webframez React",
1733
1772
  headTags: renderHeadToString(resolved.head),
1773
+ bodyClassName: resolved.head.bodyClassName || "",
1734
1774
  clientScriptUrl: shellClientScriptUrl,
1775
+ buildId: manifestState.buildId,
1735
1776
  rscEndpoint: shellRscEndpoint,
1736
1777
  rootHtml,
1737
1778
  initialFlightData,
@@ -35,7 +35,9 @@ function createHTMLShell(options = {}) {
35
35
  title = "RSC App",
36
36
  rscEndpoint = "/rsc",
37
37
  clientScriptUrl = "/client.js",
38
+ buildId = "",
38
39
  headTags = "",
40
+ bodyClassName = "",
39
41
  rootHtml = "",
40
42
  initialFlightData = "",
41
43
  basename = "",
@@ -84,11 +86,12 @@ function createHTMLShell(options = {}) {
84
86
  <title>${title}</title>
85
87
  ${headTags}
86
88
  </head>
87
- <body>
89
+ <body${bodyClassName ? ` class="${bodyClassName.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}"` : ""}>
88
90
  <div id="root">${rootHtml}</div>
89
91
  <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
90
92
  <script>window.__RSC_BASENAME = "${basename}";</script>
91
93
  <script>window.__RSC_ROUTE_BASE_PATH = "${routeBasePath}";</script>
94
+ <script>window.__WEBFRAMEZ_REACT_BUILD_ID = "${buildId}";</script>
92
95
  <script>window.__RSC_INITIAL_PAYLOAD = ${escapedInitialFlightData};</script>
93
96
  <script type="module" src="${clientScriptUrl}"></script>
94
97
  ${liveReloadScript}
@@ -453,6 +456,9 @@ function mergeHead(...configs) {
453
456
  if (config.description) {
454
457
  merged.description = config.description;
455
458
  }
459
+ if (hasOwn("bodyClassName")) {
460
+ merged.bodyClassName = config.bodyClassName;
461
+ }
456
462
  if (hasOwn("basename")) {
457
463
  merged.basename = config.basename;
458
464
  }
@@ -784,6 +790,15 @@ function createClientAssetVersion(distRootDir) {
784
790
  return "";
785
791
  }
786
792
  }
793
+ function createBuildId(distRootDir, manifestPath) {
794
+ const clientVersion = createClientAssetVersion(distRootDir);
795
+ try {
796
+ const stat = fs2.statSync(manifestPath);
797
+ return `${clientVersion}:${Math.floor(stat.mtimeMs)}-${stat.size}`;
798
+ } catch {
799
+ return clientVersion;
800
+ }
801
+ }
787
802
  function appendAssetVersion(url, version) {
788
803
  if (!version) {
789
804
  return url;
@@ -1477,6 +1492,30 @@ function createServerConsumerManifest(manifest) {
1477
1492
  }
1478
1493
  return consumerManifest;
1479
1494
  }
1495
+ function createManifestLoader(options) {
1496
+ let cache = null;
1497
+ return () => {
1498
+ const stat = fs2.statSync(options.manifestPath);
1499
+ const cacheKey = `${Math.floor(stat.mtimeMs)}-${stat.size}`;
1500
+ if (cache && cache.cacheKey === cacheKey) {
1501
+ return cache;
1502
+ }
1503
+ const moduleMap = normalizeClientManifest(
1504
+ JSON.parse(fs2.readFileSync(options.manifestPath, "utf-8")),
1505
+ {
1506
+ distRootDir: options.distRootDir,
1507
+ cwd: options.cwd
1508
+ }
1509
+ );
1510
+ cache = {
1511
+ cacheKey,
1512
+ buildId: createBuildId(options.distRootDir, options.manifestPath),
1513
+ moduleMap,
1514
+ serverConsumerModuleMap: createServerConsumerManifest(moduleMap)
1515
+ };
1516
+ return cache;
1517
+ };
1518
+ }
1480
1519
  function createNodeRequestHandler(options) {
1481
1520
  const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1482
1521
  const distRootDir = path3.resolve(options.distRootDir);
@@ -1494,14 +1533,11 @@ function createNodeRequestHandler(options) {
1494
1533
  const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
1495
1534
  const liveReloadClients = /* @__PURE__ */ new Set();
1496
1535
  const router = createFileRouter({ pagesDir });
1497
- const moduleMap = normalizeClientManifest(
1498
- JSON.parse(fs2.readFileSync(manifestPath, "utf-8")),
1499
- {
1500
- distRootDir,
1501
- cwd: process.cwd()
1502
- }
1503
- );
1504
- const serverConsumerModuleMap = createServerConsumerManifest(moduleMap);
1536
+ const getManifestState = createManifestLoader({
1537
+ distRootDir,
1538
+ manifestPath,
1539
+ cwd: process.cwd()
1540
+ });
1505
1541
  const initialHtmlWorker = createInitialHtmlWorker(pagesDir);
1506
1542
  const disposeInitialHtmlWorker = () => {
1507
1543
  initialHtmlWorker.dispose();
@@ -1556,6 +1592,7 @@ function createNodeRequestHandler(options) {
1556
1592
  return;
1557
1593
  }
1558
1594
  if (url.pathname === rscPath) {
1595
+ const manifestState2 = getManifestState();
1559
1596
  const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
1560
1597
  const requestContext = createRouteRequestContext(
1561
1598
  req,
@@ -1578,8 +1615,9 @@ function createNodeRequestHandler(options) {
1578
1615
  pageModel: resolved2.pageModel,
1579
1616
  head: resolved2.head
1580
1617
  };
1618
+ res.setHeader("X-Webframez-React-Build", manifestState2.buildId);
1581
1619
  sendRSC(res, payload, {
1582
- moduleMap,
1620
+ moduleMap: manifestState2.moduleMap,
1583
1621
  statusCode: resolved2.statusCode
1584
1622
  });
1585
1623
  return;
@@ -1649,8 +1687,9 @@ function createNodeRequestHandler(options) {
1649
1687
  model: resolved.model,
1650
1688
  head: resolved.head
1651
1689
  };
1690
+ const manifestState = getManifestState();
1652
1691
  const initialFlightData = await renderRSCToString(initialPayload, {
1653
- moduleMap
1692
+ moduleMap: manifestState.moduleMap
1654
1693
  });
1655
1694
  const transportBasePath = normalizeRuntimeBasePath(resolved.head.transportBasePath) ?? normalizeRuntimeBasePath(basePath) ?? "";
1656
1695
  const shellClientScriptUrl = appendAssetVersion(
@@ -1667,7 +1706,7 @@ function createNodeRequestHandler(options) {
1667
1706
  try {
1668
1707
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1669
1708
  flightData: initialFlightData,
1670
- moduleMap: serverConsumerModuleMap
1709
+ moduleMap: manifestState.serverConsumerModuleMap
1671
1710
  });
1672
1711
  } catch (error) {
1673
1712
  console.error("[webframez-react] Failed to render initial HTML", error);
@@ -1675,7 +1714,7 @@ function createNodeRequestHandler(options) {
1675
1714
  initialHtmlWorker.dispose();
1676
1715
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1677
1716
  flightData: initialFlightData,
1678
- moduleMap: serverConsumerModuleMap
1717
+ moduleMap: manifestState.serverConsumerModuleMap
1679
1718
  });
1680
1719
  } catch (retryError) {
1681
1720
  console.error("[webframez-react] Retry for initial HTML failed", retryError);
@@ -1683,7 +1722,7 @@ function createNodeRequestHandler(options) {
1683
1722
  initialHtmlWorker.dispose();
1684
1723
  rootHtml = await initialHtmlWorker.renderFromFlightData({
1685
1724
  flightData: initialFlightData,
1686
- moduleMap: serverConsumerModuleMap
1725
+ moduleMap: manifestState.serverConsumerModuleMap
1687
1726
  });
1688
1727
  } catch (flightRenderError) {
1689
1728
  console.error("[webframez-react] Flight-to-HTML render failed", flightRenderError);
@@ -1707,7 +1746,9 @@ function createNodeRequestHandler(options) {
1707
1746
  createHTMLShell({
1708
1747
  title: resolved.head.title || "Webframez React",
1709
1748
  headTags: renderHeadToString(resolved.head),
1749
+ bodyClassName: resolved.head.bodyClassName || "",
1710
1750
  clientScriptUrl: shellClientScriptUrl,
1751
+ buildId: manifestState.buildId,
1711
1752
  rscEndpoint: shellRscEndpoint,
1712
1753
  rootHtml,
1713
1754
  initialFlightData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-react",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "TypeScript React RSC addition for @webtypen/webframez-core",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "author": {