@symbo.ls/brender 3.14.663 → 3.14.664

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.664
4
+
5
+ ### Patch Changes
6
+
7
+ - Performance pass on the 3.14.827–3.14.832 batch, and opt-in View Transitions.
8
+
9
+ - Element create is faster than 3.14.826 again (kekela-like 4,068-element tree: create CPU min 1,265–1,355 ms vs 1,361–1,532 ms on 3.14.826). Per-definition caching of the extends merge layers (46,330 → 13,600 entries), no per-element source-text reads, one attribute writer.
10
+ - `css()` 3x faster (21 → 7 µs per call), byte-identical CSS output.
11
+ - Helmet renders a head layer only when its values change (20 `root.update({})` → 0 head renders, was 40). One runtime head writer: `applyElementMetadata`.
12
+ - brender/mermaid: registry script is escaped on the ISR and non-adopt paths; head/SSR output byte-identical otherwise.
13
+ - View Transitions for page switches (opt-in), `viewTransitionName` / `viewTransitionClass` flat props, `designSystem.viewTransition` animations.
14
+ - utils exports shared helpers used by helmet, frank, fetch and polyglot (escapeHtml, jsonForScript, alternateLinkKey, faviconLinkKey, bareLibraryKey, librarySpecsFor, polyglotLangStorageKey, ...).
15
+
16
+ - Updated dependencies
17
+ - smbls@3.14.833
18
+ - @symbo.ls/element@3.14.803
19
+ - @symbo.ls/css@3.14.777
20
+ - @symbo.ls/scratch@3.14.788
21
+ - @symbo.ls/utils@3.14.786
22
+ - @symbo.ls/fetch@3.14.664
23
+ - @symbo.ls/helmet@3.14.670
24
+
3
25
  ## 3.14.663
4
26
 
5
27
  ### Patch Changes
@@ -1,25 +1,22 @@
1
- import{resolve as d,join as At,dirname as T}from"path";import{existsSync as y,unlinkSync as Pt,readFileSync as V,realpathSync as qt}from"fs";import{resolvePackageEntry as It}from"./packageEntry.js";import{tmpdir as vt}from"os";import{randomBytes as Dt}from"crypto";import{createRequire as ot}from"module";import{createEnv as at}from"./env.js";import{resetKeys as ft,assignKeys as ut,mapKeysToElements as pt}from"./keys.js";import{extractMetadata as st,generateHeadHtml as Mt}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Bt}from"./shell.js";import{prefetchPageData as Ft,injectPrefetchedState as Nt,fetchSSRTranslations as Wt}from"./prefetch.js";import{createEarlySeedScript as Ht}from"@symbo.ls/fetch";let X=null;const Gt=async()=>{if(X)return X;try{return X=(await import("@symbo.ls/funcql")).funcqlPlugin,X}catch{return null}};import"linkedom";import{reset as Jt}from"@symbo.ls/css";const rt=(t,e)=>{if(!t||!e)return;if(t[e]!==void 0)return t[e];const n=e.split(".");let o=t;for(const i of n){if(o==null||typeof o!="object")return;o=o[i]}return o},Kt=function(t,e){if(!t)return"";const n=this?.context,o=n?.polyglot,i=e||o?.defaultLang||"en";if(o?.translations){const a=o.translations[i];if(a){const r=rt(a,t);if(r!==void 0)return r}}const c=this?.state?.root||n?.state?.root;if(c?.translations){const a=c.translations[i];if(a){const r=rt(a,t);if(r!==void 0)return r}}const u=o?.defaultLang||"en";if(u!==i&&o?.translations){const a=o.translations[u];if(a){const r=rt(a,t);if(r!==void 0)return r}}return t},dt=function(){const t=this?.context;return this?.state?.root?.lang||t?.polyglot?.defaultLang||"en"},A=(t,e=new WeakMap)=>{if(t===null||typeof t!="object")return t;if(e.has(t))return e.get(t);if(Array.isArray(t)){const o=[];e.set(t,o);for(const i of t)o.push(typeof i=="object"&&i!==null?A(i,e):i);return o}const n={};e.set(t,n);for(const o of Object.keys(t)){const i=t[o];n[o]=typeof i=="object"&&i!==null?A(i,e):i}return n};let F=null;try{import.meta.url&&(F=ot(import.meta.url))}catch{}const zt=()=>{if(!import.meta.url||!F)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:r=>r};const t=qt(new URL(".",import.meta.url).pathname),e=d(t,"../..");if(y(d(e,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:e};let o;try{const r=F.resolve("smbls/package.json");o=T(r)}catch{let r=t;for(;r!==T(r);){const s=d(r,"node_modules","smbls");if(y(d(s,"package.json"))){o=s;break}r=T(r)}}let i=F;if(o)try{i=ot(d(o,"package.json"))}catch{}let c,u=process.cwd();for(;u!==T(u);){if(y(d(u,"package.json"))){c=u;break}u=T(u)}let a=i;if(c)try{a=ot(d(c,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:o,brenderDir:t,projectRoot:c,_smblsRequire:i,_projectRequire:a}},K=(t,e)=>{const n=t.isMonorepo?[F]:[t._smblsRequire,t._projectRequire,F].filter(Boolean);for(const o of n)try{return o.resolve(e)}catch{}return null},qe=(t,e,...n)=>{if(t.isMonorepo)return d(t.monorepoRoot,"packages",e,...n);const o=K(t,`${e}/package.json`);return o?d(T(o),...n):null},Ie=(t,e,...n)=>{if(t.isMonorepo)return d(t.monorepoRoot,"plugins",e,...n);const o=K(t,`@symbo.ls/${e}/package.json`);return o?d(T(o),...n):null},it=(t,e,...n)=>{if(t.isMonorepo){for(const i of["packages","plugins"]){const c=d(t.monorepoRoot,i,e,...n);if(y(c))return c}return null}const o=K(t,`@symbo.ls/${e}/package.json`);return o?d(T(o),...n):null},mt=(t,e,...n)=>{if(t.isMonorepo)return d(t.monorepoRoot,"packages","domql","packages",e,...n);const o=K(t,`@symbo.ls/${e}/package.json`);return o?d(T(o),...n):null};let N=null;const gt="smbls/ssr",ht=async()=>{const t=await import("smbls/ssr");if(typeof t?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return t},Ut=async()=>{if(N)return N;const t=zt();if(!F)try{return N=await ht(),N}catch(a){throw new Error(`brender: ${gt} is not available in this bundled runtime (${a.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`)}let e;if(t.isMonorepo)e=d(t.monorepoRoot,"packages","smbls","src","createDomql.js");else{let a;try{return N=await ht(),N}catch(s){a=s}const r=t.smblsRoot&&d(t.smblsRoot,"src","createDomql.js");if(r&&y(r))e=r;else throw new Error(`brender: cannot load ${gt} (${a.message}). Server rendering needs smbls installed in this project, at a release that exports smbls/ssr`+(t.smblsRoot?` (found smbls at ${t.smblsRoot}).`:" (no smbls package found)."))}const n=await import("esbuild"),o=At(vt(),`br_createDomql_${Dt(6).toString("hex")}.mjs`),i=It,c={name:"workspace-resolve",setup(a){a.onResolve({filter:/^smbls/},r=>{const s=r.path.replace(/^smbls\/?/,""),f=t.isMonorepo?d(t.monorepoRoot,"packages","smbls"):t.smblsRoot;if(!f)return;if(!s){const h=i(f);return h?{path:h}:void 0}const l=d(f,s);if(y(l))return{path:l};if(y(l+".js"))return{path:l+".js"};const p=d(l,"index.js");if(y(p))return{path:p}}),a.onResolve({filter:/^domql$/},r=>{if(t.isMonorepo){const s=d(t.monorepoRoot,"packages","domql","src","index.js");if(y(s))return{path:s};const f=d(t.monorepoRoot,"packages","domql","index.js");if(y(f))return{path:f}}else try{const s=_require.resolve("domql/package.json"),f=i(T(s));if(f)return{path:f}}catch{}}),a.onResolve({filter:/^@symbo\.ls\//},r=>{const s=r.path.replace("@symbo.ls/","");if(s==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(t.isMonorepo){for(const l of["packages","plugins"]){const p=d(t.monorepoRoot,l,s,"src","index.js");if(y(p))return{path:p};const h=d(t.monorepoRoot,l,s,"index.js");if(y(h))return{path:h}}const f=d(t.monorepoRoot,"packages","default-config","blank","index.js");if(s==="default-config"&&y(f))return{path:f}}else{const f=it(t,s,"src","index.js");if(f&&y(f))return{path:f};const l=it(t,s,"index.js");if(l&&y(l))return{path:l};if(s==="default-config"){const p=it(t,"default-config","blank","index.js");if(p&&y(p))return{path:p}}}}),a.onResolve({filter:/^@domql\//},r=>{const s=r.path.replace("@symbo.ls/","");if(t.isMonorepo){const f=d(t.monorepoRoot,"packages","domql","packages",s,"src","index.js");if(y(f))return{path:f};const l=d(t.monorepoRoot,"packages","domql","packages",s,"index.js");if(y(l))return{path:l}}else{const f=mt(t,s,"src","index.js");if(f&&y(f))return{path:f};const l=mt(t,s,"index.js");if(l&&y(l))return{path:l}}}),a.onResolve({filter:/^css-in-props/},r=>{let s;if(t.isMonorepo)s=d(t.monorepoRoot,"packages","css-in-props");else{const p=K(t,"css-in-props/package.json");p&&(s=T(p))}if(!s)return;const f=r.path.replace(/^css-in-props\/?/,"");if(f){const p=d(s,f),h=d(p,"index.js");if(y(h))return{path:h};if(y(p+".js"))return{path:p+".js"};if(y(p))return{path:p}}const l=i(s);if(l)return{path:l}}),a.onResolve({filter:/^@emotion\//},r=>{let s;if(t.isMonorepo)s=d(t.monorepoRoot,"node_modules",r.path);else{const f=K(t,`${r.path}/package.json`);if(!f)return;s=T(f)}if(y(s)){const f=d(s,"package.json");if(y(f))try{const l=JSON.parse(V(f,"utf8")),p=l.module||l.main||"dist/emotion-css.esm.js";return{path:d(s,p)}}catch{}return{path:s}}}),a.onResolve({filter:/\.json$/},r=>{if(r.resolveDir){const s=d(r.resolveDir,r.path);if(y(s))return{path:s}}}),a.onLoad({filter:/smbls\/.*options\.js$/},async r=>{if(!r.path.includes("smbls/")||!r.path.endsWith("options.js"))return;let s=V(r.path,"utf8");return s=s.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),a.onLoad({filter:/smbls\/.*init\.js$/},async r=>{if(!r.path.includes("smbls/")||!r.path.endsWith("init.js"))return;let s=V(r.path,"utf8");return s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),a.onLoad({filter:/.*/,namespace:"brender-stub"},()=>({contents:"export const SyncComponent = {}; export const Inspect = {}; export const Notifications = {}; export const syncPlugin = () => ({}); export const notificationsPlugin = () => ({}); export const seedRawDesignSystem = () => {}; export const inspectPlugin = () => ({}); export default {}",loader:"js"})),a.onLoad({filter:/smbls\/src\/router\.js$/},async r=>{let s=V(r.path,"utf8");return s=s.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:s,loader:"js"}}),a.onLoad({filter:/fetchOnCreate\.js$/},async r=>{if(!r.path.includes("smbls/"))return;let s=V(r.path,"utf8");return s=s.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:s,loader:"js"}})}};await n.build({entryPoints:[e],bundle:!0,format:"esm",platform:"node",outfile:o,write:!0,logLevel:"warning",plugins:[c],nodePaths:t.isMonorepo?[d(t.monorepoRoot,"node_modules")]:[...t.smblsRoot?[d(t.smblsRoot,"node_modules")]:[],...t.projectRoot?[d(t.projectRoot,"node_modules")]:[],...t.smblsRoot?[d(t.smblsRoot,"..","..","node_modules")]:[]].filter(a=>y(a)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const u=await import(`file://${o}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",o);else try{Pt(o)}catch{}return N=u,u},Yt={Box:{},Focusable:{},Block:{display:"block"},Inline:{display:"inline"},Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Link:{tag:"a",attr:{href:t=>t.href,target:t=>t.target,rel:t=>t.rel}},A:{extends:"Link"},RouteLink:{extends:"Link"},Img:{tag:"img",attr:{src:t=>{let e=t.src;return typeof e=="string"&&e.includes("{{")&&(e=t.call("replaceLiteralsWithObjectFields",e,t.state)),e},alt:t=>t.alt,loading:t=>t.loading}},Image:{extends:"Img"},Button:{tag:"button"},FocusableComponent:{tag:"button"},Form:{tag:"form"},Input:{tag:"input"},TextArea:{tag:"textarea"},Textarea:{tag:"textarea"},Select:{tag:"select"},Label:{tag:"label"},Iframe:{tag:"iframe"},Video:{tag:"video"},Audio:{tag:"audio"},Canvas:{tag:"canvas"},Span:{tag:"span"},P:{tag:"p"},H1:{tag:"h1"},H2:{tag:"h2"},H3:{tag:"h3"},H4:{tag:"h4"},H5:{tag:"h5"},H6:{tag:"h6"},Svg:{tag:"svg",attr:{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},Text:{tag:"span"}},yt=(t,e="")=>{if(!t||!t.__ref)return{};const n={},o=t.__ref.__brKey;if(o&&(n[e===""?"__root":e]=o),t.__ref.__children)for(const i of t.__ref.__children){const c=t[i];if(c&&c.__ref){const u=e===""?i:`${e}.${i}`;Object.assign(n,yt(c,u))}}return n};let z=0;const bt=Symbol.for("brender.fetchTracked"),St=()=>z,Vt=256*1024,Xt=1024*1024,Zt=new Set(["accept-language","accept"]);let lt=0,q=null;const Qt=t=>{const[e,n]=t,o=e&&typeof e=="object"&&typeof e.url=="string",i=o?e.url:String(e),c=String(n&&n.method||o&&e.method||"GET").toUpperCase(),u={},a=r=>{if(r)if(typeof r.forEach=="function"&&!Array.isArray(r))r.forEach((s,f)=>{u[String(f).toLowerCase()]=String(s)});else if(Array.isArray(r))for(const[s,f]of r)u[String(s).toLowerCase()]=String(f);else for(const s in r)r[s]!=null&&(u[s.toLowerCase()]=String(r[s]))};return o&&a(e.headers),n&&a(n.headers),{url:i,method:c,headers:u}},te=(t,e,n,o)=>{if(!n||!n.ok||typeof n.clone!="function")return;const i=n.headers&&n.headers.get&&n.headers.get("content-type")||"";if(!/json|^text\//i.test(i))return;let c;try{c=n.clone()}catch{return}o(c.text().then(u=>{t.tainted||u.length>Vt||t.bytes+u.length>Xt||(t.bytes+=u.length,t.entries.push({url:e.url,lang:e.headers["accept-language"]||"",status:n.status,type:i,body:u}))},()=>{}))},ee=()=>{const t=globalThis.fetch;if(typeof t!="function"||t[bt])return;const e=function(...n){z++;let o=!1;const i=()=>{o||(o=!0,z--)},c=s=>{z++,s.then(()=>{z--},()=>{z--})},u=q&&!q.tainted?q:null;let a=null;if(u)try{a=Qt(n),(a.method!=="GET"||!/^https?:/i.test(a.url)||Object.keys(a.headers).some(s=>!Zt.has(s)))&&(a=null)}catch{a=null}let r;try{r=t.apply(this,n)}catch(s){throw i(),s}return r&&typeof r.then=="function"?r.then(s=>{a&&te(u,a,s,c),i()},i):i(),r};e[bt]=!0,globalThis.fetch=e},ct=({brRegistry:t,fetchSeed:e}={})=>!t||!Object.keys(t).length?"":`<script>window.__BR_REGISTRY__=${JSON.stringify(t).replace(/</g,"\\u003c")};window.__BRENDER_HYDRATE__=true<\/script>`+(e&&e.length?`
2
- `+Ht(e):""),wt=async(t,e={})=>{const{route:n="/",pathname:o,state:i,context:c,prefetch:u=!1}=e,a=o||n,r=globalThis.location,s=globalThis.window;(!globalThis.location||globalThis.location.pathname!==a)&&(globalThis.location={pathname:a,href:a,search:"",hash:"",origin:""}),globalThis.window||(globalThis.window={location:globalThis.location});let f;if(u)try{const g=t.pages||{};f={...g};const b=await Ft(t,n);if(b.size){const P=JSON.parse(JSON.stringify(g[n],(C,D)=>{if(typeof D!="function")return D})),J=(C,D)=>{if(!(!C||!D))for(const O in C)typeof C[O]=="function"?D[O]=C[O]:typeof C[O]=="object"&&C[O]!==null&&!Array.isArray(C[O])&&typeof D[O]=="object"&&D[O]!==null&&J(C[O],D[O])};J(g[n],P),Nt(P,b),f[n]=P}}catch(g){console.error("[brender] Prefetch error:",g),f=t.pages}let l;if(u)try{l=await Wt(t)}catch(g){console.warn("[brender] SSR translation fetch failed:",g.message)}r!==void 0?globalThis.location=r:delete globalThis.location,s!==void 0?globalThis.window=s:delete globalThis.window;const{window:p,document:h}=at(),S=h.body;p.location.pathname=a;const w=globalThis.document,m=globalThis.location;globalThis.document=h,globalThis.location=p.location;const{createDomqlElement:R}=await Ut(),E=A(t.app||{}),x={...t.config||{}};t.polyglot&&!x.polyglot&&(x.polyglot=t.polyglot),t.fetch&&!x.fetch&&(x.fetch=t.fetch),t.router&&!x.router&&(x.router=t.router);for(const g of ne)t[g]!=null&&x[g]==null&&(x[g]=t[g]);const _=x.polyglot?{...x.polyglot}:void 0;l&&_&&(_.translations={..._.translations||{},...l});const j=A(t.state||{});(l||_)&&(j.root||(j.root={}),_&&(j.root.lang=j.root.lang||_.defaultLang||"en"),l&&(j.root.translations={...j.root.translations||{},...l})),Jt();const W={};for(const g in t){if(g==="app"||g==="config"||g==="pages"||g==="components"||g==="state"||g==="snippets"||g==="designSystem"||g==="dependencies")continue;const b=t[g];W[g]=b&&typeof b=="object"?A(b):b}const H={...W,state:j,...i?{state:{...j,...i}}:{},dependencies:A(t.dependencies||{}),components:A(t.components||{}),snippets:A(t.snippets||{}),pages:A(f||t.pages||{}),functions:{...t.functions||{},polyglot:Kt,getActiveLang:dt,getLang:dt},methods:t.methods||{},designSystem:A(t.designSystem||{}),files:t.files||{},sharedLibraries:t.sharedLibraries||[],...x,..._?{polyglot:_}:{},document:h,window:p,parent:{node:S},initOptions:{},domqlOptions:{sourcemap:!1},sync:!1,inspect:!1,notifications:!1,...c||{}},k=await Gt();k&&(H.plugins=H.plugins||[],H.plugins.some(g=>g.name==="funcql")||H.plugins.push(k)),ft(),ee(),lt++,lt===1?q={entries:[],bytes:0,tainted:!1}:q&&(q.tainted=!0);const L=q;let $;try{$=await R(E,H);const g=Date.now(),b=u?2e3:50,P=J=>new Promise(C=>setTimeout(C,J));for(await P(Math.min(50,b));u&&St()>0&&Date.now()-g<b;){for(;St()>0&&Date.now()-g<b;)await P(10);await P(Math.max(0,Math.min(50,b-(Date.now()-g))))}}finally{lt--,q===L&&(q=null)}const G=L&&!L.tainted?L.entries.slice():[];ut(S);const U=pt($),M=yt($),et=st(t,n,$,$?.state),I=[],v=[],B=h.head||h.querySelector("head");if(B)for(const g of B.querySelectorAll("style")){if(g.sheet&&g.sheet.cssRules)for(const b of g.sheet.cssRules)b.cssText&&(I.push(b.cssText),v.push(b));if(!I.length){const b=g.textContent||"";b&&(I.push(b),v.push({cssText:b}))}}const{globalCSS:Y,classRules:Ot}=ie(v);let nt=Rt(S.innerHTML);if(l){const g=_?.defaultLang||"en",b=l[g]||Object.values(l)[0]||{};nt=nt.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(P,J)=>{const C=J.trim();return b[C]??P})}return w!==void 0?globalThis.document=w:delete globalThis.document,m!==void 0?globalThis.location=m:delete globalThis.location,{html:nt,metadata:et,registry:U,brRegistry:M,element:$,emotionCSS:I,globalCSS:Y,classRules:Ot,fetchSeed:G,document:h,window:p,ssrTranslations:l,prefetchedPages:f}},ve=async(t,e={})=>{const{context:n={}}=e,{window:o,document:i}=at(),c=i.body,{create:u}=await import("@symbo.ls/element"),a=await import("@symbo.ls/utils"),r={...Yt,...n.components||{}},s={...a,...n.utils||{},...n.functions||{}};ft();let f;try{f=u(t,{node:c},"root",{context:{document:i,window:o,...n,components:r,utils:s}})}catch{}ut(c);const l=f?pt(f):{};return{html:Rt(c.innerHTML),registry:l,element:f}},Rt=t=>t.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(e,n,o,i)=>{if(o.includes("&lt;")){const c=o.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'");return n+c+i}return e}),ne=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],xt=(t,e,n)=>{const o=Object.entries(e||{}).map(([i,c])=>`${n} ${i}: ${c}`).join(`;
3
- `);return o?`${n}${t} {
4
- ${o};
5
- ${n}}`:""},De=t=>Object.entries(t||{}).map(([e,n])=>{if(!e.startsWith("@"))return xt(e,n,"");const o=Object.entries(n||{}).map(([i,c])=>xt(i,c," ")).filter(Boolean).join(`
6
- `);return o?`${e} {
7
- ${o}
8
- }`:""}).filter(Boolean).join(`
9
-
10
- `),oe=/\.[A-Za-z_\\-]/,se=t=>{if(typeof t!="string"||!t)return!1;const e=t.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!oe.test(e)},re=/^\s*(:root|\[data-theme|\[data-skin)/,_t=t=>{const e=t&&t.cssText;if(typeof e!="string")return null;if(/^@(-webkit-)?keyframes\b/.test(e))return"keyframes";if(e.startsWith("@font-face"))return null;if(typeof t.selectorText=="string")return se(t.selectorText)?re.test(t.selectorText)?"root":"reset":null;const n=t.cssRules&&Array.from(t.cssRules);if(n&&n.length&&/^@(media|supports|container)\b/.test(e)){const o=new Set(n.map(_t));return o.has(null)||o.size!==1?o.has(null)?null:"reset":[...o][0]}return null},ie=t=>{const e={root:[],reset:[],keyframes:[]},n=[];for(const o of t||[]){const i=_t(o);i?e[i].push(o.cssText):o&&o.cssText&&n.push(o.cssText)}return{globalCSS:{rootRule:e.root.join(`
11
- `),resetRules:e.reset.join(`
12
- `),keyframeRules:e.keyframes.join(`
13
- `),fontFaceCSS:""},classRules:n}};let Z=new Set;const Me=()=>{Z=new Set},Be=()=>Array.from(Z).join(`
14
- `),Fe=(t,e)=>t.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,e?`<style data-emotion="smbls">
15
- ${e}
16
- </style>`:""),Ne=async(t,e={})=>{const{route:n="/",pathname:o,prefetch:i=!0}=e,c=await wt(t,{route:n,pathname:o,prefetch:i});if(!c)return null;const u=t.designSystem||{},a=c.globalCSS;let r=null,s=null;try{const f=c.element,l=f?.context?.polyglot||t.polyglot||t.config?.polyglot;if(s=f?.state?.root?.lang||l?.defaultLang||"en",c.prefetchedPages&&c.prefetchedPages[n]){const p=c.prefetchedPages[n],h=(S,w={})=>{if(!S||typeof S!="object")return w;if(S.state&&typeof S.state=="object")for(const[m,R]of Object.entries(S.state))R!=null&&typeof R!="function"&&(w[m]=R);for(const[m,R]of Object.entries(S))m==="state"||m==="props"||m==="attr"||m==="on"||m==="define"||m==="__ref"||m.startsWith("__")||R&&typeof R=="object"&&!Array.isArray(R)&&h(R,w);return w};r=h(p)}}catch{}return{html:c.html,css:c.classRules.join(`
17
- `),classRules:c.classRules.slice(),globalCSS:a,resetCss:a.resetRules,fontLinks:Lt(u),metadata:c.metadata||st(t,n),brKeyCount:c.registry?Object.keys(c.registry).length:0,brRegistry:c.brRegistry||{},fetchSeed:c.fetchSeed||[],ssrTranslations:c.ssrTranslations,prefetchedState:r,activeLang:s}},We=async(t,e="/",n={})=>{const{lang:o,themeColor:i,isr:c,hydrate:u=!0,prefetch:a=!0,shell:r,adoptDom:s=!1}=n,f=o||t.state?.lang||t.app?.metadata?.lang||"en",l=await wt(t,{route:e,prefetch:a});if(!l)return null;const p={...l.metadata};if(i&&(p["theme-color"]=i),typeof r=="string"&&!t.name){const k="\0brender-undeclared-title\0";if(st({...t,name:k},e,l.element,l.element?.state).title===k)for(const $ of["title","og:title","twitter:title"])p[$]===l.metadata.title&&delete p[$]}const h=Mt(p);for(const k of l.classRules)k&&Z.add(k);const S=Array.from(Z).join(`
18
- `),w=t.designSystem||{},m=l.globalCSS,R=Lt(w),E=Object.keys(l.registry).length;let x="";if(c&&c.clientScript){const k=Number.isInteger(n.depth)?n.depth:e==="/"?0:e.replace(/^\/|\/$/g,"").split("/").length,L=k>0?"../".repeat(k):"./";if(u){let $="";if(l.ssrTranslations){const M=t.polyglot||t.config?.polyglot,et=M?.storagePrefix||"",I=M?.storageLangKey||"",v=[];for(const B in l.ssrTranslations){const Y=l.ssrTranslations[B];Y&&typeof Y=="object"&&v.push(`localStorage.setItem(${JSON.stringify(et+B)},${JSON.stringify(JSON.stringify(Y))})`)}if(I){const B=M?.defaultLang||"en";v.push(`if(!localStorage.getItem(${JSON.stringify(I)}))localStorage.setItem(${JSON.stringify(I)},${JSON.stringify(B)})`)}v.length&&($=`<script>try{${v.join(";")}}catch(e){}<\/script>
19
- `)}const G=l.brRegistry&&Object.keys(l.brRegistry).length?JSON.stringify(l.brRegistry):null,U=s?ct(l)?ct(l)+`
20
- `:"":G?`<script>window.__BR_REGISTRY__=${G}<\/script>
21
- `:"";x=`${$}${U}<script>window.__BRENDER__=true<\/script>
22
- <script type="module" src="${L}${c.clientScript}"><\/script>`}else x=`<script type="module">
1
+ import{resolve as u,join as ke,dirname as v}from"path";import{existsSync as d,unlinkSync as we,readFileSync as V,realpathSync as xe}from"fs";import{resolvePackageEntry as _e}from"./packageEntry.js";import{tmpdir as je}from"os";import{randomBytes as ve}from"crypto";import{createRequire as oe}from"module";import{createEnv as ie}from"./env.js";import{resetKeys as ae,assignKeys as ce,mapKeysToElements as fe}from"./keys.js";import{extractMetadata as ne,generateHeadHtml as Te}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Ee}from"./shell.js";import{prefetchPageData as Le,injectPrefetchedState as Ce,fetchSSRTranslations as qe}from"./prefetch.js";import{createEarlySeedScript as $e}from"@symbo.ls/fetch";import{headerEntries as Pe}from"@symbo.ls/fetch/rest";import{jsonForScript as De}from"@symbo.ls/utils";let Q=null;const Ae=async()=>{if(Q)return Q;try{return Q=(await import("@symbo.ls/funcql")).funcqlPlugin,Q}catch{return null}};import"linkedom";import{reset as Me}from"@symbo.ls/css";const se=(e,t)=>{if(!e||!t)return;if(e[t]!==void 0)return e[t];const o=t.split(".");let n=e;for(const l of o){if(n==null||typeof n!="object")return;n=n[l]}return n},Oe=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,l=t||n?.defaultLang||"en";if(n?.translations){const c=n.translations[l];if(c){const r=se(c,e);if(r!==void 0)return r}}const a=this?.state?.root||o?.state?.root;if(a?.translations){const c=a.translations[l];if(c){const r=se(c,e);if(r!==void 0)return r}}const p=n?.defaultLang||"en";if(p!==l&&n?.translations){const c=n.translations[p];if(c){const r=se(c,e);if(r!==void 0)return r}}return e},pe=function(){const e=this?.context;return this?.state?.root?.lang||e?.polyglot?.defaultLang||"en"},L=(e,t=new WeakMap)=>{if(e===null||typeof e!="object")return e;if(t.has(e))return t.get(e);if(Array.isArray(e)){const n=[];t.set(e,n);for(const l of e)n.push(typeof l=="object"&&l!==null?L(l,t):l);return n}const o={};t.set(e,o);for(const n of Object.keys(e)){const l=e[n];o[n]=typeof l=="object"&&l!==null?L(l,t):l}return o};let M=null;try{import.meta.url&&(M=oe(import.meta.url))}catch{}const Ie=()=>{if(!import.meta.url||!M)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:r=>r};const e=xe(new URL(".",import.meta.url).pathname),t=u(e,"../..");if(d(u(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const r=M.resolve("smbls/package.json");n=v(r)}catch{let r=e;for(;r!==v(r);){const s=u(r,"node_modules","smbls");if(d(u(s,"package.json"))){n=s;break}r=v(r)}}let l=M;if(n)try{l=oe(u(n,"package.json"))}catch{}let a,p=process.cwd();for(;p!==v(p);){if(d(u(p,"package.json"))){a=p;break}p=v(p)}let c=l;if(a)try{c=oe(u(a,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:a,_smblsRequire:l,_projectRequire:c}},K=(e,t)=>{const o=e.isMonorepo?[M]:[e._smblsRequire,e._projectRequire,M].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},_t=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages",t,...o);const n=K(e,`${t}/package.json`);return n?u(v(n),...o):null},jt=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"plugins",t,...o);const n=K(e,`@symbo.ls/${t}/package.json`);return n?u(v(n),...o):null},re=(e,t,...o)=>{if(e.isMonorepo){for(const l of["packages","plugins"]){const a=u(e.monorepoRoot,l,t,...o);if(d(a))return a}return null}const n=K(e,`@symbo.ls/${t}/package.json`);return n?u(v(n),...o):null},ue=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages","domql","packages",t,...o);const n=K(e,`@symbo.ls/${t}/package.json`);return n?u(v(n),...o):null};let X=null;const me="smbls/ssr",Fe=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},Ne=async()=>{if(X)return X;const e=Ie();let t;if(M&&e.isMonorepo)t=u(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let c;try{return X=await Fe(),X}catch(s){c=s}if(!M)throw new Error(`brender: ${me} is not available in this bundled runtime (${c.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`);const r=e.smblsRoot&&u(e.smblsRoot,"src","createDomql.js");if(r&&d(r))t=r;else throw new Error(`brender: cannot load ${me} (${c.message}). Server rendering needs smbls installed in this project, at a release that exports smbls/ssr`+(e.smblsRoot?` (found smbls at ${e.smblsRoot}).`:" (no smbls package found)."))}const o=await import("esbuild"),n=ke(je(),`br_createDomql_${ve(6).toString("hex")}.mjs`),l=_e,a={name:"workspace-resolve",setup(c){c.onResolve({filter:/^smbls/},r=>{const s=r.path.replace(/^smbls\/?/,""),f=e.isMonorepo?u(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!f)return;if(!s){const h=l(f);return h?{path:h}:void 0}const i=u(f,s);if(d(i))return{path:i};if(d(i+".js"))return{path:i+".js"};const m=u(i,"index.js");if(d(m))return{path:m}}),c.onResolve({filter:/^domql$/},r=>{if(e.isMonorepo){const s=u(e.monorepoRoot,"packages","domql","src","index.js");if(d(s))return{path:s};const f=u(e.monorepoRoot,"packages","domql","index.js");if(d(f))return{path:f}}else try{const s=_require.resolve("domql/package.json"),f=l(v(s));if(f)return{path:f}}catch{}}),c.onResolve({filter:/^@symbo\.ls\//},r=>{const s=r.path.replace("@symbo.ls/","");if(s==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(e.isMonorepo){for(const i of["packages","plugins"]){const m=u(e.monorepoRoot,i,s,"src","index.js");if(d(m))return{path:m};const h=u(e.monorepoRoot,i,s,"index.js");if(d(h))return{path:h}}const f=u(e.monorepoRoot,"packages","default-config","blank","index.js");if(s==="default-config"&&d(f))return{path:f}}else{const f=re(e,s,"src","index.js");if(f&&d(f))return{path:f};const i=re(e,s,"index.js");if(i&&d(i))return{path:i};if(s==="default-config"){const m=re(e,"default-config","blank","index.js");if(m&&d(m))return{path:m}}}}),c.onResolve({filter:/^@domql\//},r=>{const s=r.path.replace("@symbo.ls/","");if(e.isMonorepo){const f=u(e.monorepoRoot,"packages","domql","packages",s,"src","index.js");if(d(f))return{path:f};const i=u(e.monorepoRoot,"packages","domql","packages",s,"index.js");if(d(i))return{path:i}}else{const f=ue(e,s,"src","index.js");if(f&&d(f))return{path:f};const i=ue(e,s,"index.js");if(i&&d(i))return{path:i}}}),c.onResolve({filter:/^css-in-props/},r=>{let s;if(e.isMonorepo)s=u(e.monorepoRoot,"packages","css-in-props");else{const m=K(e,"css-in-props/package.json");m&&(s=v(m))}if(!s)return;const f=r.path.replace(/^css-in-props\/?/,"");if(f){const m=u(s,f),h=u(m,"index.js");if(d(h))return{path:h};if(d(m+".js"))return{path:m+".js"};if(d(m))return{path:m}}const i=l(s);if(i)return{path:i}}),c.onResolve({filter:/^@emotion\//},r=>{let s;if(e.isMonorepo)s=u(e.monorepoRoot,"node_modules",r.path);else{const f=K(e,`${r.path}/package.json`);if(!f)return;s=v(f)}if(d(s)){const f=u(s,"package.json");if(d(f))try{const i=JSON.parse(V(f,"utf8")),m=i.module||i.main||"dist/emotion-css.esm.js";return{path:u(s,m)}}catch{}return{path:s}}}),c.onResolve({filter:/\.json$/},r=>{if(r.resolveDir){const s=u(r.resolveDir,r.path);if(d(s))return{path:s}}}),c.onLoad({filter:/smbls\/.*options\.js$/},async r=>{if(!r.path.includes("smbls/")||!r.path.endsWith("options.js"))return;let s=V(r.path,"utf8");return s=s.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),c.onLoad({filter:/smbls\/.*init\.js$/},async r=>{if(!r.path.includes("smbls/")||!r.path.endsWith("init.js"))return;let s=V(r.path,"utf8");return s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),c.onLoad({filter:/.*/,namespace:"brender-stub"},()=>({contents:"export const SyncComponent = {}; export const Inspect = {}; export const Notifications = {}; export const syncPlugin = () => ({}); export const notificationsPlugin = () => ({}); export const seedRawDesignSystem = () => {}; export const inspectPlugin = () => ({}); export default {}",loader:"js"})),c.onLoad({filter:/smbls\/src\/router\.js$/},async r=>{let s=V(r.path,"utf8");return s=s.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:s,loader:"js"}}),c.onLoad({filter:/fetchOnCreate\.js$/},async r=>{if(!r.path.includes("smbls/"))return;let s=V(r.path,"utf8");return s=s.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:s,loader:"js"}})}};await o.build({entryPoints:[t],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[a],nodePaths:e.isMonorepo?[u(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[u(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[u(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[u(e.smblsRoot,"..","..","node_modules")]:[]].filter(c=>d(c)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const p=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{we(n)}catch{}return X=p,p},Be={Box:{},Focusable:{},Block:{display:"block"},Inline:{display:"inline"},Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Link:{tag:"a",attr:{href:e=>e.href,target:e=>e.target,rel:e=>e.rel}},A:{extends:"Link"},RouteLink:{extends:"Link"},Img:{tag:"img",attr:{src:e=>{let t=e.src;return typeof t=="string"&&t.includes("{{")&&(t=e.call("replaceLiteralsWithObjectFields",t,e.state)),t},alt:e=>e.alt,loading:e=>e.loading}},Image:{extends:"Img"},Button:{tag:"button"},FocusableComponent:{tag:"button"},Form:{tag:"form"},Input:{tag:"input"},TextArea:{tag:"textarea"},Textarea:{tag:"textarea"},Select:{tag:"select"},Label:{tag:"label"},Iframe:{tag:"iframe"},Video:{tag:"video"},Audio:{tag:"audio"},Canvas:{tag:"canvas"},Span:{tag:"span"},P:{tag:"p"},H1:{tag:"h1"},H2:{tag:"h2"},H3:{tag:"h3"},H4:{tag:"h4"},H5:{tag:"h5"},H6:{tag:"h6"},Svg:{tag:"svg",attr:{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},Text:{tag:"span"}},He=(e,t)=>{const o={},n=l=>{const a=l&&l.__ref;if(a){if(a.__brKey){const p=t(l);o[p===""?"__root":p]=a.__brKey}if(a.__children)for(const p of a.__children)n(l[p])}};return n(e),o},Je=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let G=0;const ge=Symbol.for("brender.fetchTracked"),de=()=>G,Ke=256*1024,Ge=1024*1024,We=new Set(["accept-language","accept"]);let le=0,D=null;const Ue=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",l=n?t.url:String(t),a=String(o&&o.method||n&&t.method||"GET").toUpperCase(),p={},c=r=>{if(!r)return;const s=!Array.isArray(r)&&typeof r.entries!="function";for(const[f,i]of Pe(r))s&&i==null||(p[String(f).toLowerCase()]=String(i))};return n&&c(t.headers),o&&c(o.headers),{url:l,method:a,headers:p}},Ye=(e,t,o,n)=>{if(!o||!o.ok||typeof o.clone!="function")return;const l=o.headers&&o.headers.get&&o.headers.get("content-type")||"";if(!/json|^text\//i.test(l))return;let a;try{a=o.clone()}catch{return}n(a.text().then(p=>{e.tainted||p.length>Ke||e.bytes+p.length>Ge||(e.bytes+=p.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:l,body:p}))},()=>{}))},ze=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[ge])return;const t=function(...o){G++;let n=!1;const l=()=>{n||(n=!0,G--)},a=s=>{G++,s.then(()=>{G--},()=>{G--})},p=D&&!D.tainted?D:null;let c=null;if(p)try{c=Ue(o),(c.method!=="GET"||!/^https?:/i.test(c.url)||Object.keys(c.headers).some(s=>!We.has(s)))&&(c=null)}catch{c=null}let r;try{r=e.apply(this,o)}catch(s){throw l(),s}return r&&typeof r.then=="function"?r.then(s=>{c&&Ye(p,c,s,a),l()},l):l(),r};t[ge]=!0,globalThis.fetch=t},Ve=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=De(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
+ `+$e(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},he=async(e,t={})=>{const{route:o="/",pathname:n,state:l,context:a,prefetch:p=!1}=t,c=n||o,r=globalThis.location,s=globalThis.window;(!globalThis.location||globalThis.location.pathname!==c)&&(globalThis.location={pathname:c,href:c,search:"",hash:"",origin:""}),globalThis.window||(globalThis.window={location:globalThis.location});let f;if(p)try{const g=e.pages||{};f={...g};const y=await Le(e,o);if(y.size){const P=JSON.parse(JSON.stringify(g[o],(_,A)=>{if(typeof A!="function")return A})),J=(_,A)=>{if(!(!_||!A))for(const E in _)typeof _[E]=="function"?A[E]=_[E]:typeof _[E]=="object"&&_[E]!==null&&!Array.isArray(_[E])&&typeof A[E]=="object"&&A[E]!==null&&J(_[E],A[E])};J(g[o],P),Ce(P,y),f[o]=P}}catch(g){console.error("[brender] Prefetch error:",g),f=e.pages}let i;if(p)try{i=await qe(e)}catch(g){console.warn("[brender] SSR translation fetch failed:",g.message)}r!==void 0?globalThis.location=r:delete globalThis.location,s!==void 0?globalThis.window=s:delete globalThis.window;const{window:m,document:h}=ie(),w=h.body;m.location.pathname=c;const T=globalThis.document,S=globalThis.location;globalThis.document=h,globalThis.location=m.location;const{createDomqlElement:R}=await Ne(),Z=L(e.app||{}),k={...e.config||{}};e.polyglot&&!k.polyglot&&(k.polyglot=e.polyglot),e.fetch&&!k.fetch&&(k.fetch=e.fetch),e.router&&!k.router&&(k.router=e.router);for(const g of Xe)e[g]!=null&&k[g]==null&&(k[g]=e[g]);const x=k.polyglot?{...k.polyglot}:void 0;i&&x&&(x.translations={...x.translations||{},...i});const j=L(e.state||{});(i||x)&&(j.root||(j.root={}),x&&(j.root.lang=j.root.lang||x.defaultLang||"en"),i&&(j.root.translations={...j.root.translations||{},...i})),Me();const F={};for(const g in e){if(Je.has(g))continue;const y=e[g];F[g]=y&&typeof y=="object"?L(y):y}const C={...F,state:j,...l?{state:{...j,...l}}:{},dependencies:L(e.dependencies||{}),components:L(e.components||{}),snippets:L(e.snippets||{}),pages:L(f||e.pages||{}),functions:{...e.functions||{},polyglot:Oe,getActiveLang:pe,getLang:pe},methods:e.methods||{},designSystem:L(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...k,...x?{polyglot:x}:{},document:h,window:m,parent:{node:w},initOptions:{},domqlOptions:{sourcemap:!1},sync:!1,inspect:!1,notifications:!1,...a||{}},N=await Ae();N&&(C.plugins=C.plugins||[],C.plugins.some(g=>g.name==="funcql")||C.plugins.push(N)),ae(),ze(),le++,le===1?D={entries:[],bytes:0,tainted:!1}:D&&(D.tainted=!0);const W=D;let b;try{b=await R(Z,C);const g=Date.now(),y=p?2e3:50,P=J=>new Promise(_=>setTimeout(_,J));for(await P(Math.min(50,y));p&&de()>0&&Date.now()-g<y;)await P(10),de()===0&&await P(Math.max(0,Math.min(50,y-(Date.now()-g))))}finally{le--,D===W&&(D=null)}const B=W&&!W.tainted?W.entries.slice():[];ce(w);const q=fe(b),{elementPath:U}=await import("@symbo.ls/element"),O=He(b,U),Y=ne(e,o,b,b?.state),$=[],I=h.head||h.querySelector("head");if(I)for(const g of I.querySelectorAll("style")){if(g.sheet&&g.sheet.cssRules)for(const y of g.sheet.cssRules)y.cssText&&$.push(y);if(!$.length){const y=g.textContent||"";y&&$.push({cssText:y})}}const H=$.map(g=>g.cssText),{globalCSS:z,classRules:Re}=tt($);let te=ye(w.innerHTML);if(i){const g=x?.defaultLang||"en",y=i[g]||Object.values(i)[0]||{};te=te.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(P,J)=>{const _=J.trim();return y[_]??P})}return T!==void 0?globalThis.document=T:delete globalThis.document,S!==void 0?globalThis.location=S:delete globalThis.location,{html:te,metadata:Y,registry:q,brRegistry:O,element:b,emotionCSS:H,globalCSS:z,classRules:Re,fetchSeed:B,document:h,window:m,ssrTranslations:i,prefetchedPages:f}},vt=async(e,t={})=>{const{context:o={}}=t,{window:n,document:l}=ie(),a=l.body,{create:p}=await import("@symbo.ls/element"),c=await import("@symbo.ls/utils"),r={...Be,...o.components||{}},s={...c,...o.utils||{},...o.functions||{}};ae();let f;try{f=p(e,{node:a},"root",{context:{document:l,window:n,...o,components:r,utils:s}})}catch{}ce(a);const i=f?fe(f):{};return{html:ye(a.innerHTML),registry:i,element:f}},ye=e=>e.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(t,o,n,l)=>{if(n.includes("&lt;")){const a=n.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'");return o+a+l}return t}),Xe=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Ze=/\.[A-Za-z_\\-]/,Qe=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Ze.test(t)},et=/^\s*(:root|\[data-theme|\[data-skin)/,be=e=>{const t=e&&e.cssText;if(typeof t!="string")return null;if(/^@(-webkit-)?keyframes\b/.test(t))return"keyframes";if(t.startsWith("@font-face"))return null;if(typeof e.selectorText=="string")return Qe(e.selectorText)?et.test(e.selectorText)?"root":"reset":null;const o=e.cssRules&&Array.from(e.cssRules);if(o&&o.length&&/^@(media|supports|container)\b/.test(t)){const n=new Set(o.map(be));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},tt=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const l=be(n);l?t[l].push(n.cssText):n&&n.cssText&&o.push(n.cssText)}return{globalCSS:{rootRule:t.root.join(`
3
+ `),resetRules:t.reset.join(`
4
+ `),keyframeRules:t.keyframes.join(`
5
+ `),fontFaceCSS:""},classRules:o}};let ee=new Set;const Tt=()=>{ee=new Set},Et=()=>Array.from(ee).join(`
6
+ `),Lt=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
7
+ ${t}
8
+ </style>`:""),Ct=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:l=!0}=t,a=await he(e,{route:o,pathname:n,prefetch:l});if(!a)return null;const p=e.designSystem||{},c=a.globalCSS;let r=null,s=null;try{const f=a.element,i=f?.context?.polyglot||e.polyglot||e.config?.polyglot;if(s=f?.state?.root?.lang||i?.defaultLang||"en",a.prefetchedPages&&a.prefetchedPages[o]){const m=a.prefetchedPages[o],h=(w,T={})=>{if(!w||typeof w!="object")return T;if(w.state&&typeof w.state=="object")for(const[S,R]of Object.entries(w.state))R!=null&&typeof R!="function"&&(T[S]=R);for(const[S,R]of Object.entries(w))S==="state"||S==="props"||S==="attr"||S==="on"||S==="define"||S==="__ref"||S.startsWith("__")||R&&typeof R=="object"&&!Array.isArray(R)&&h(R,T);return T};r=h(m)}}catch{}return{html:a.html,css:a.classRules.join(`
9
+ `),classRules:a.classRules.slice(),globalCSS:c,resetCss:c.resetRules,fontLinks:Se(p),metadata:a.metadata||ne(e,o),brKeyCount:a.registry?Object.keys(a.registry).length:0,brRegistry:a.brRegistry||{},fetchSeed:a.fetchSeed||[],ssrTranslations:a.ssrTranslations,prefetchedState:r,activeLang:s}},qt=async(e,t="/",o={})=>{const{lang:n,themeColor:l,isr:a,hydrate:p=!0,prefetch:c=!0,shell:r,adoptDom:s=!1}=o,f=n||e.state?.lang||e.app?.metadata?.lang||"en",i=await he(e,{route:t,prefetch:c});if(!i)return null;const m={...i.metadata};if(l&&(m["theme-color"]=l),typeof r=="string"&&!e.name){const b="\0brender-undeclared-title\0";if(ne({...e,name:b},t,i.element,i.element?.state).title===b)for(const q of["title","og:title","twitter:title"])m[q]===i.metadata.title&&delete m[q]}const h=Te(m);for(const b of i.classRules)b&&ee.add(b);const w=Array.from(ee).join(`
10
+ `),T=i.globalCSS,S=`<style data-brender>
11
+ ${T.rootRule||""}
12
+ ${T.resetRules||""}
13
+ ${T.keyframeRules||""}
14
+ </style>`,R=w?`<style data-emotion="smbls">
15
+ ${w}
16
+ </style>`:"",Z=Se(e.designSystem||{}),k=Object.keys(i.registry).length,x=Ve({brRegistry:i.brRegistry,fetchSeed:i.fetchSeed,hydrateFlag:s});let j="";if(a&&a.clientScript){const b=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,B=b>0?"../".repeat(b):"./";if(p){let q="";if(i.ssrTranslations){const O=e.polyglot||e.config?.polyglot,Y=O?.storagePrefix||"",$=O?.storageLangKey||"",I=[];for(const H in i.ssrTranslations){const z=i.ssrTranslations[H];z&&typeof z=="object"&&I.push(`localStorage.setItem(${JSON.stringify(Y+H)},${JSON.stringify(JSON.stringify(z))})`)}if($){const H=O?.defaultLang||"en";I.push(`if(!localStorage.getItem(${JSON.stringify($)}))localStorage.setItem(${JSON.stringify($)},${JSON.stringify(H)})`)}I.length&&(q=`<script>try{${I.join(";")}}catch(e){}<\/script>
17
+ `)}const U=x?x+`
18
+ `:"";j=`${q}${U}<script>window.__BRENDER__=true<\/script>
19
+ <script type="module" src="${B}${a.clientScript}"><\/script>`}else j=`<script type="module">
23
20
  {
24
21
  const brEls = document.querySelectorAll('body > :not(script):not(style)')
25
22
  const observer = new MutationObserver((mutations) => {
@@ -36,36 +33,19 @@ ${e}
36
33
  observer.observe(document.body, { childList: true })
37
34
  }
38
35
  <\/script>
39
- <script type="module" src="${L}${c.clientScript}"><\/script>`}const _={...t.config||{}};t.polyglot&&!_.polyglot&&(_.polyglot=t.polyglot);const j=_.polyglot;let W=h;if(j){const k=j.defaultLang||"en",$={...j.translations||{},...l.ssrTranslations||{}}[k]||{};W=h.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(G,U)=>{const M=U.trim();return $[M]??G})}if(typeof r=="string"){const k=[m.fontFaceCSS?`<style data-brender>${m.fontFaceCSS}</style>`:"",`<style data-brender>
40
- ${m.rootRule||""}
41
- ${m.resetRules||""}
42
- ${m.keyframeRules||""}
43
- </style>`,S?`<style data-emotion="smbls">
44
- ${S}
45
- </style>`:""];return{html:Bt(r,{headTags:W,headEnd:[R,...k].filter(Boolean).join(`
46
- `),body:l.html,depth:Number.isInteger(n.depth)?n.depth:0,bootScripts:s?ct(l):""}),route:e,brKeyCount:E,shell:!0}}return{html:`<!DOCTYPE html>
36
+ <script type="module" src="${B}${a.clientScript}"><\/script>`}const F={...e.config||{}};e.polyglot&&!F.polyglot&&(F.polyglot=e.polyglot);const C=F.polyglot;let N=h;if(C){const b=C.defaultLang||"en",q={...C.translations||{},...i.ssrTranslations||{}}[b]||{};N=h.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(U,O)=>{const Y=O.trim();return q[Y]??U})}return typeof r=="string"?{html:Ee(r,{headTags:N,headEnd:[Z,S,R].filter(Boolean).join(`
37
+ `),body:i.html,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:s?x:""}),route:t,brKeyCount:k,shell:!0}:{html:`<!DOCTYPE html>
47
38
  <html lang="${f}">
48
39
  <head>
49
- ${W}
50
- ${R}
51
- ${m.fontFaceCSS?`<style data-brender>${m.fontFaceCSS}</style>`:""}
52
- <style data-brender>
53
- ${m.rootRule||""}
54
- ${m.resetRules||""}
55
- ${m.keyframeRules||""}
56
- </style>
57
- ${S?`<style data-emotion="smbls">
40
+ ${N}
41
+ ${Z}
42
+
58
43
  ${S}
59
- </style>`:""}
44
+ ${R}
60
45
  </head>
61
46
  <body>
62
- ${l.html}
63
- ${x}
47
+ ${i.html}
48
+ ${j}
64
49
  </body>
65
- </html>`,route:e,brKeyCount:E}},le={U:-6,V:-5,W:-4,X:-3,Y:-2,Z:-1,A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12,N:13,O:14,P:15},jt=new Set(["padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingBlock","paddingInline","paddingBlockStart","paddingBlockEnd","paddingInlineStart","paddingInlineEnd","margin","marginTop","marginRight","marginBottom","marginLeft","marginBlock","marginInline","marginBlockStart","marginBlockEnd","marginInlineStart","marginInlineEnd","gap","rowGap","columnGap","top","right","bottom","left","width","height","minWidth","maxWidth","minHeight","maxHeight","flexBasis","fontSize","lineHeight","letterSpacing","borderWidth","borderRadius","outlineWidth","outlineOffset","inset","insetBlock","insetInline","boxSize","round"]),kt=(t,e)=>{if(!t||typeof t!="string"||!e)return null;const n=e.base||16,o=e.ratio||1.618,i=e.unit||"px",c=e.subSequence!==!1;if(t.includes(" "))return t.split(" ").map(m=>m==="-"||m===""?m:kt(m,e)||m).join(" ");if(/^(none|auto|inherit|initial|unset|0)$/i.test(t)||/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(t)||/^(#|rgb|hsl|var\()/i.test(t))return null;const u=t.startsWith("-"),r=(u?t.slice(1):t).match(/^([A-Z])(\d)?$/i);if(!r)return null;const s=r[1].toUpperCase(),f=r[2]?parseInt(r[2]):0,l=le[s];if(l===void 0)return null;let p=n*Math.pow(o,l);if(f>0&&c){const w=n*Math.pow(o,l+1),R=(w-p)/o,E=w-R,x=p+R,_=(E+x)/2,j=~~w-~~p>16?[E,_,x]:[E,x];f<=j.length&&(p=j[f-1])}const h=Math.round(p*100)/100;return`${u?"-":""}${h}${i}`},ce=new Set([...jt].map(t=>t.replace(/[A-Z]/g,e=>"-"+e.toLowerCase()))),Q=(t,e,n)=>{if(typeof e!="string")return e;if(ae.has(t)){const o=n?.color||{};if(o[e])return o[e]}if(jt.has(t)||ce.has(t)){const o=n?.spacing||{},i=kt(e,o);if(i)return i}return e},ae=new Set(["color","background","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor","fill","stroke"]),$t=new Set(["href","src","alt","title","id","name","type","value","placeholder","target","rel","loading","srcset","sizes","media","role","tabindex","for","action","method","enctype","autocomplete","autofocus","theme","__element","update","childrenAs","childExtends","childProps","children"]),tt=t=>t.replace(/[A-Z]/g,e=>"-"+e.toLowerCase()),Ct=(t,e)=>{if(typeof e>"u"||e===null)return null;if(t==="flow"&&typeof e=="string"){let[n,o]=(e||"row").split(" ");return(e.startsWith("x")||e==="row")&&(n="row"),(e.startsWith("y")||e==="column")&&(n="column"),{display:"flex","flex-flow":(n||"")+" "+(o||"")}}if(t==="wrap")return{display:"flex","flex-wrap":e};if((t==="align"||t==="flexAlign")&&typeof e=="string"){const[n,o]=e.split(" "),i={display:"flex","align-items":n};return o&&(i["justify-content"]=o),i}if(t==="gridAlign"&&typeof e=="string"){const[n,o]=e.split(" "),i={display:"grid","align-items":n};return o&&(i["justify-content"]=o),i}if(t==="flexFlow"&&typeof e=="string"){let[n,o]=(e||"row").split(" ");return(e.startsWith("x")||e==="row")&&(n="row"),(e.startsWith("y")||e==="column")&&(n="column"),{display:"flex","flex-flow":(n||"")+" "+(o||"")}}if(t==="flexWrap")return{display:"flex","flex-wrap":e};if(t==="backgroundImage"&&typeof e=="string"&&!e.startsWith("url(")&&!e.startsWith("linear-gradient")&&!e.startsWith("radial-gradient")&&!e.startsWith("none"))return{"background-image":`url(${e})`};if(t==="round"||t==="borderRadius"&&e)return{"border-radius":typeof e=="number"?e+"px":e};if(t==="boxSize"&&typeof e=="string"){const[n,o]=e.split(" ");return{height:n,width:o||n}}if(t==="widthRange"&&typeof e=="string"){const[n,o]=e.split(" ");return{"min-width":n,"max-width":o||n}}if(t==="heightRange"&&typeof e=="string"){const[n,o]=e.split(" ");return{"min-height":n,"max-height":o||n}}return t==="column"?{"grid-column":e}:t==="columns"?{"grid-template-columns":e}:t==="templateColumns"?{"grid-template-columns":e}:t==="row"?{"grid-row":e}:t==="rows"?{"grid-template-rows":e}:t==="templateRows"?{"grid-template-rows":e}:t==="area"?{"grid-area":e}:t==="template"?{"grid-template":e}:t==="templateAreas"?{"grid-template-areas":e}:t==="autoColumns"?{"grid-auto-columns":e}:t==="autoRows"?{"grid-auto-rows":e}:t==="autoFlow"?{"grid-auto-flow":e}:t==="columnStart"?{"grid-column-start":e}:t==="rowStart"?{"grid-row-start":e}:null},Tt=(t,e)=>{const n={};for(const o in t){const i=t[o],c=Ct(o,i);if(c){for(const u in c)n[u]=Q(u,c[u],e);continue}typeof i!="string"&&typeof i!="number"||(n[tt(o)]=Q(o,i,e))}return n},fe=(t,e,n)=>{const o={},i={},c={};for(const u in t){const a=t[u];if(u.charCodeAt(0)===64&&typeof a=="object"){const s=n?.[u.slice(1)];if(s){const f=Tt(a,e);Object.keys(f).length&&(i[s]=f)}continue}if(u.charCodeAt(0)===58&&typeof a=="object"){const s=Tt(a,e);Object.keys(s).length&&(c[u]=s);continue}if(typeof a!="string"&&typeof a!="number"||u.charCodeAt(0)>=65&&u.charCodeAt(0)<=90||$t.has(u))continue;const r=Ct(u,a);if(r){for(const s in r)o[s]=Q(s,r[s],e);continue}o[tt(u)]=Q(u,a,e)}return{base:o,mediaRules:i,pseudoRules:c}},ue=(t,{base:e,mediaRules:n,pseudoRules:o})=>{const i=[],c=Object.entries(e).map(([u,a])=>`${u}: ${a}`).join("; ");c&&i.push(`${t} { ${c}; }`);for(const[u,a]of Object.entries(o)){const r=Object.entries(a).map(([s,f])=>`${s}: ${f}`).join("; ");r&&i.push(`${t}${u} { ${r}; }`)}for(const[u,a]of Object.entries(n)){const r=Object.entries(a).map(([f,l])=>`${f}: ${l}`).join("; "),s=u.startsWith("@")?u:`@media ${u}`;r&&i.push(`${s} { ${t} { ${r}; } }`)}return i.join(`
66
- `)},Et={Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Block:{display:"block"},Inline:{display:"inline"}},pe=t=>{const e=t.__ref?.__extends;if(!e||!Array.isArray(e))return null;for(const n of e)if(Et[n])return Et[n];return null},de=t=>{let e;for(const n in t){if(typeof t[n]!="function"||$t.has(n)||n.charCodeAt(0)>=65&&n.charCodeAt(0)<=90||n.startsWith("on")||n.startsWith("__"))continue;e||(e={});let o;try{o=t[n](t,t.state||{})}catch{try{const i={root:{},...t.state||{}};o=t[n](t,i)}catch{}}o!=null&&o!==!1&&(e[n]=o)}return e||t},He=(t,e)=>{const n=e?.media||{},o=e?.animation||{},i=[],c=new Set,u=new Set,a=s=>{if(!s||!s.__ref)return;const f=de(s);if(f&&s.node){const l=s.node.getAttribute?.("class");if(l&&!c.has(l)){c.add(l);const p=fe(f,e,n),h=pe(s);if(h)for(const[m,R]of Object.entries(h)){const E=tt(m);p.base[E]||(p.base[E]=R)}(Object.keys(p.base).length||Object.keys(p.mediaRules).length||Object.keys(p.pseudoRules).length)&&i.push(ue("."+l.split(" ")[0],p));const w=f.animation||f.animationName;if(typeof w=="string"){const m=w.split(" ")[0];o[m]&&u.add(m)}}}if(s.__ref?.__children)for(const l of s.__ref.__children)s[l]?.__ref&&a(s[l])};a(t);const r=[];for(const s of u){const f=o[s],l=Object.entries(f).map(([p,h])=>{const S=Object.entries(h).map(([w,m])=>`${tt(w)}: ${m}`).join("; ");return` ${p} { ${S}; }`}).join(`
67
- `);r.push(`@keyframes ${s} {
68
- ${l}
69
- }`)}return[...r,...i].join(`
70
- `)},Lt=t=>{if(!t)return"";const e=t.font_family||t.fontFamily||{},n=new Set;for(const i of Object.values(e)){if(typeof i!="string")continue;const c=i.match(/'([^']+)'/);c&&n.add(c[1])}return n.size?['<link rel="preconnect" href="https://fonts.googleapis.com">','<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',`<link href="https://fonts.googleapis.com/css2?${[...n].map(i=>`family=${i.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
71
- `):""};export{gt as SMBLS_SSR_SPECIFIER,ct as adoptionScripts,Be as getAccumulatedEmotionCSS,wt as render,ve as renderElement,We as renderPage,Ne as renderRoute,Fe as replaceEmotionCSS,Me as resetGlobalCSSCache,De as skinRulesToCSS,ie as splitCapturedCSS};
50
+ </html>`,route:t,brKeyCount:k}},Se=e=>{if(!e)return"";const t=e.font_family||e.fontFamily||{},o=new Set;for(const l of Object.values(t)){if(typeof l!="string")continue;const a=l.match(/'([^']+)'/);a&&o.add(a[1])}return o.size?['<link rel="preconnect" href="https://fonts.googleapis.com">','<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',`<link href="https://fonts.googleapis.com/css2?${[...o].map(l=>`family=${l.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
51
+ `):""};export{me as SMBLS_SSR_SPECIFIER,Ve as adoptionScripts,Et as getAccumulatedEmotionCSS,he as render,vt as renderElement,qt as renderPage,Ct as renderRoute,Lt as replaceEmotionCSS,Tt as resetGlobalCSSCache,tt as splitCapturedCSS};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/brender",
3
- "version": "3.14.663",
3
+ "version": "3.14.664",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -46,18 +46,18 @@
46
46
  "prepublishOnly": "npm run build"
47
47
  },
48
48
  "dependencies": {
49
- "@symbo.ls/css": "^3.14.775",
49
+ "@symbo.ls/css": "^3.14.777",
50
50
  "@symbo.ls/default-config": "^3.14.785",
51
- "@symbo.ls/element": "^3.14.800",
52
- "@symbo.ls/fetch": "^3.14.663",
53
- "@symbo.ls/helmet": "^3.14.669",
54
- "@symbo.ls/scratch": "^3.14.786",
55
- "@symbo.ls/utils": "^3.14.784",
51
+ "@symbo.ls/element": "^3.14.803",
52
+ "@symbo.ls/fetch": "^3.14.664",
53
+ "@symbo.ls/helmet": "^3.14.670",
54
+ "@symbo.ls/scratch": "^3.14.788",
55
+ "@symbo.ls/utils": "^3.14.786",
56
56
  "esbuild": "^0.28.1",
57
57
  "linkedom": "^0.16.8"
58
58
  },
59
59
  "peerDependencies": {
60
- "smbls": "^3.14.828"
60
+ "smbls": "^3.14.833"
61
61
  },
62
62
  "optionalDependencies": {
63
63
  "@symbo.ls/funcql": "^3.14.0"
package/render.js CHANGED
@@ -11,6 +11,8 @@ import { hydrate } from './hydrate.js'
11
11
  import { composeIntoShell } from './shell.js'
12
12
  import { prefetchPageData, injectPrefetchedState, fetchSSRTranslations } from './prefetch.js'
13
13
  import { createEarlySeedScript } from '@symbo.ls/fetch'
14
+ import { headerEntries } from '@symbo.ls/fetch/rest'
15
+ import { jsonForScript } from '@symbo.ls/utils'
14
16
 
15
17
  // funcql plugin — enables evaluation of funcql schemas as property values
16
18
  // during SSR. Async-imported to avoid polluting non-funcql codepaths.
@@ -290,18 +292,10 @@ const bundleCreateDomql = async () => {
290
292
 
291
293
  const ws = detectWorkspace()
292
294
 
293
- if (!_brenderRequire) {
294
- try {
295
- _cachedCreateDomql = await importSmblsSsr()
296
- return _cachedCreateDomql
297
- } catch (err) {
298
- throw new Error(`brender: ${SMBLS_SSR_SPECIFIER} is not available in this bundled runtime (${err.message}). Bundle smbls ≥ the release that adds the smbls/ssr export alongside @symbo.ls/brender.`)
299
- }
300
- }
301
-
302
- // Resolve entry point
295
+ // Resolve entry point: the monorepo's source, else `smbls/ssr` (one import
296
+ // for both a bundled runtime and an installed smbls).
303
297
  let entry
304
- if (ws.isMonorepo) {
298
+ if (_brenderRequire && ws.isMonorepo) {
305
299
  entry = resolve(ws.monorepoRoot, 'packages', 'smbls', 'src', 'createDomql.js')
306
300
  } else {
307
301
  let ssrError
@@ -311,6 +305,9 @@ const bundleCreateDomql = async () => {
311
305
  } catch (err) {
312
306
  ssrError = err
313
307
  }
308
+ if (!_brenderRequire) {
309
+ throw new Error(`brender: ${SMBLS_SSR_SPECIFIER} is not available in this bundled runtime (${ssrError.message}). Bundle smbls ≥ the release that adds the smbls/ssr export alongside @symbo.ls/brender.`)
310
+ }
314
311
  // Legacy: an smbls that predates the ssr entry but still ships its source.
315
312
  const srcEntry = ws.smblsRoot && resolve(ws.smblsRoot, 'src', 'createDomql.js')
316
313
  if (srcEntry && existsSync(srcEntry)) {
@@ -630,24 +627,23 @@ const UIKIT_STUBS = {
630
627
  *
631
628
  * The registry is serialized into the pre-rendered HTML so the browser can
632
629
  * assign the correct brKey to each skeleton element during true hydration.
630
+ * A path is @symbo.ls/element elementPath — the very function the hydrating
631
+ * client looks each element up with (adoptHydrationNode), so the two sides
632
+ * cannot spell a path differently. One flat object, filled in place (the
633
+ * recursive walk merged a fresh object per element into its parent's).
633
634
  */
634
- const buildPathRegistry = (element, path = '') => {
635
- if (!element || !element.__ref) return {}
635
+ const buildPathRegistry = (element, elementPath) => {
636
636
  const registry = {}
637
-
638
- const brKey = element.__ref.__brKey
639
- if (brKey) registry[path === '' ? '__root' : path] = brKey
640
-
641
- if (element.__ref.__children) {
642
- for (const childKey of element.__ref.__children) {
643
- const child = element[childKey]
644
- if (child && child.__ref) {
645
- const childPath = path === '' ? childKey : `${path}.${childKey}`
646
- Object.assign(registry, buildPathRegistry(child, childPath))
647
- }
637
+ const walk = (el) => {
638
+ const ref = el && el.__ref
639
+ if (!ref) return
640
+ if (ref.__brKey) {
641
+ const path = elementPath(el)
642
+ registry[path === '' ? '__root' : path] = ref.__brKey
648
643
  }
644
+ if (ref.__children) for (const childKey of ref.__children) walk(el[childKey])
649
645
  }
650
-
646
+ walk(element)
651
647
  return registry
652
648
  }
653
649
 
@@ -666,6 +662,15 @@ const buildPathRegistry = (element, path = '') => {
666
662
  * @param {object} [options.context] - Additional context overrides
667
663
  * @returns {Promise<{ html: string, metadata: object, registry: object, element: object }>}
668
664
  */
665
+ // The payload keys render() puts on the context itself (below `ctx`): the
666
+ // generic copy of every other top-level key skips them — cloning one only for
667
+ // the explicit key to replace it was a deep clone of `functions`, `methods`
668
+ // and `files` thrown away on every render.
669
+ const CONTEXT_KEYS_SET_BELOW = new Set([
670
+ 'app', 'config', 'pages', 'components', 'state', 'snippets', 'designSystem', 'dependencies',
671
+ 'functions', 'methods', 'files', 'sharedLibraries'
672
+ ])
673
+
669
674
  // ── In-flight request tracking (the post-create flush) ──────────────────────
670
675
  // The render's flush waits for the requests a page starts while it renders
671
676
  // (fetch plugin, SDK, project code — all end in globalThis.fetch). One
@@ -701,11 +706,15 @@ const requestShape = (args) => {
701
706
  const url = isRequest ? input.url : String(input)
702
707
  const method = String((init && init.method) || (isRequest && input.method) || 'GET').toUpperCase()
703
708
  const headers = {}
709
+ // Any fetch HeadersInit, read like the REST adapter reads one (headerEntries).
710
+ // A plain object's null / undefined value is no header.
704
711
  const add = (h) => {
705
712
  if (!h) return
706
- if (typeof h.forEach === 'function' && !Array.isArray(h)) h.forEach((v, k) => { headers[String(k).toLowerCase()] = String(v) })
707
- else if (Array.isArray(h)) for (const [k, v] of h) headers[String(k).toLowerCase()] = String(v)
708
- else for (const k in h) if (h[k] != null) headers[k.toLowerCase()] = String(h[k])
713
+ const plain = !Array.isArray(h) && typeof h.entries !== 'function'
714
+ for (const [k, v] of headerEntries(h)) {
715
+ if (plain && v == null) continue
716
+ headers[String(k).toLowerCase()] = String(v)
717
+ }
709
718
  }
710
719
  if (isRequest) add(input.headers)
711
720
  if (init) add(init.headers)
@@ -769,9 +778,14 @@ const trackFetches = () => {
769
778
  // (@symbo.ls/fetch createEarlySeedScript). Default off everywhere — a page
770
779
  // without these keeps the fresh client render.
771
780
  // FW-BRENDER-HYDRATION-AND-CF-DEPLOY-PROVIDER-1.
772
- export const adoptionScripts = ({ brRegistry, fetchSeed } = {}) => {
781
+ // `hydrateFlag: false` — the registry alone, for a client that replaces the
782
+ // server DOM but still reads the registry (renderPage's default ISR path). It
783
+ // inlined its own JSON.stringify there, without the `<` escape, so an element
784
+ // key carrying `</script>` closed the tag.
785
+ export const adoptionScripts = ({ brRegistry, fetchSeed, hydrateFlag = true } = {}) => {
773
786
  if (!brRegistry || !Object.keys(brRegistry).length) return ''
774
- const registry = JSON.stringify(brRegistry).replace(/</g, '\\u003c')
787
+ const registry = jsonForScript(brRegistry)
788
+ if (!hydrateFlag) return `<script>window.__BR_REGISTRY__=${registry}</script>`
775
789
  return `<script>window.__BR_REGISTRY__=${registry};window.__BRENDER_HYDRATE__=true</script>` +
776
790
  (fetchSeed && fetchSeed.length ? '\n' + createEarlySeedScript(fetchSeed) : '')
777
791
  }
@@ -913,8 +927,7 @@ export const render = async (data, options = {}) => {
913
927
  // build reuses. The explicit keys below still win.
914
928
  const projectContext = {}
915
929
  for (const key in data) {
916
- if (key === 'app' || key === 'config' || key === 'pages' || key === 'components' ||
917
- key === 'state' || key === 'snippets' || key === 'designSystem' || key === 'dependencies') continue
930
+ if (CONTEXT_KEYS_SET_BELOW.has(key)) continue
918
931
  const value = data[key]
919
932
  projectContext[key] = value && typeof value === 'object' ? structuredCloneDeep(value) : value
920
933
  }
@@ -1001,8 +1014,9 @@ export const render = async (data, options = {}) => {
1001
1014
  const sleep = (ms) => new Promise(r => setTimeout(r, ms))
1002
1015
  await sleep(Math.min(50, flushCap))
1003
1016
  while (prefetch && inFlightRequests() > 0 && Date.now() - flushStart < flushCap) {
1004
- while (inFlightRequests() > 0 && Date.now() - flushStart < flushCap) await sleep(10)
1005
- await sleep(Math.max(0, Math.min(50, flushCap - (Date.now() - flushStart))))
1017
+ await sleep(10)
1018
+ // a settled batch: another quiet period for what it triggers
1019
+ if (inFlightRequests() === 0) await sleep(Math.max(0, Math.min(50, flushCap - (Date.now() - flushStart))))
1006
1020
  }
1007
1021
  } finally {
1008
1022
  _activeRenders--
@@ -1017,7 +1031,8 @@ export const render = async (data, options = {}) => {
1017
1031
 
1018
1032
  // Build element path → brKey registry for client-side true hydration.
1019
1033
  // Walks the element tree after mapKeysToElements has set __brKey on each element.
1020
- const brRegistry = buildPathRegistry(element)
1034
+ const { elementPath } = await import('@symbo.ls/element')
1035
+ const brRegistry = buildPathRegistry(element, elementPath)
1021
1036
 
1022
1037
  // Extract metadata for the rendered route
1023
1038
  // Pass the rendered element and its state so function-valued metadata
@@ -1025,28 +1040,22 @@ export const render = async (data, options = {}) => {
1025
1040
  const metadata = extractMetadata(data, route, element, element?.state)
1026
1041
 
1027
1042
  // Extract CSS from style tags in virtual head (atomic CSS engine writes here)
1028
- const emotionCSS = []
1029
1043
  const capturedRules = []
1030
1044
  const head = document.head || document.querySelector('head')
1031
1045
  if (head) {
1032
1046
  for (const style of head.querySelectorAll('style')) {
1033
1047
  if (style.sheet && style.sheet.cssRules) {
1034
1048
  for (const rule of style.sheet.cssRules) {
1035
- if (rule.cssText) {
1036
- emotionCSS.push(rule.cssText)
1037
- capturedRules.push(rule)
1038
- }
1049
+ if (rule.cssText) capturedRules.push(rule)
1039
1050
  }
1040
1051
  }
1041
- if (!emotionCSS.length) {
1052
+ if (!capturedRules.length) {
1042
1053
  const content = style.textContent || ''
1043
- if (content) {
1044
- emotionCSS.push(content)
1045
- capturedRules.push({ cssText: content })
1046
- }
1054
+ if (content) capturedRules.push({ cssText: content })
1047
1055
  }
1048
1056
  }
1049
1057
  }
1058
+ const emotionCSS = capturedRules.map((rule) => rule.cssText)
1050
1059
  // globalCSS: the init-injected globals; classRules: everything else, in
1051
1060
  // capture order (see splitCapturedCSS).
1052
1061
  const { globalCSS, classRules } = splitCapturedCSS(capturedRules)
@@ -1156,27 +1165,6 @@ const SCRATCH_CONFIG_FLAGS = [
1156
1165
  'useDocumentTheme', 'useDefaultConfig', 'useDefaultIcons',
1157
1166
  'useThemeSuffixedVars', 'verbose', 'semanticIcons',
1158
1167
  ]
1159
- // Serialise scratch `buildSkinVarStyles` output — `{ selector: { var: value },
1160
- // '@media …': { selector: { var: value } } }` — as CSS text
1161
- // (FW-SCRATCH-SKINS-DIMENSION-1).
1162
- const declarationBlock = (selector, vars, indent) => {
1163
- const decls = Object.entries(vars || {})
1164
- .map(([k, v]) => `${indent} ${k}: ${v}`)
1165
- .join(';\n')
1166
- return decls ? `${indent}${selector} {\n${decls};\n${indent}}` : ''
1167
- }
1168
-
1169
- export const skinRulesToCSS = (rules) => Object.entries(rules || {})
1170
- .map(([key, body]) => {
1171
- if (!key.startsWith('@')) return declarationBlock(key, body, '')
1172
- const inner = Object.entries(body || {})
1173
- .map(([selector, vars]) => declarationBlock(selector, vars, ' '))
1174
- .filter(Boolean)
1175
- .join('\n')
1176
- return inner ? `${key} {\n${inner}\n}` : ''
1177
- })
1178
- .filter(Boolean)
1179
- .join('\n\n')
1180
1168
 
1181
1169
  // ── Global CSS: the render's own capture, split ────────────────────────────
1182
1170
  // The rules smbls init injects into the virtual <head> during render() ARE
@@ -1432,15 +1420,21 @@ export const renderPage = async (data, route = '/', options = {}) => {
1432
1420
  }
1433
1421
  const emotionCSS = Array.from(_accumulatedEmotionCSS).join('\n')
1434
1422
 
1435
- // Global CSS (variables, reset, keyframes): this render's own init output
1436
- const ds = data.designSystem || {}
1423
+ // Global CSS (variables, reset, keyframes): this render's own init output.
1424
+ // The style tags, built once for both documents below.
1437
1425
  const globalCSS = result.globalCSS
1426
+ const brenderStyle = `<style data-brender>\n${globalCSS.rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`
1427
+ const emotionStyle = emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''
1438
1428
 
1439
1429
  // Generate font links from design system
1440
- const fontLinks = generateFontLinks(ds)
1430
+ const fontLinks = generateFontLinks(data.designSystem || {})
1441
1431
 
1442
1432
  const brKeyCount = Object.keys(result.registry).length
1443
1433
 
1434
+ // The element-path registry — with the opt-in hydration flag and the
1435
+ // recorded fetch answers under `adoptDom` (adoptionScripts).
1436
+ const bootScripts = adoptionScripts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, hydrateFlag: adoptDom })
1437
+
1444
1438
  // ISR: include client SPA bundle for hydration + data fetching
1445
1439
  let isrBody = ''
1446
1440
  if (isr && isr.clientScript) {
@@ -1481,14 +1475,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
1481
1475
  }
1482
1476
  }
1483
1477
  // Embed BR registry for true DOM-adoption hydration
1484
- const brRegistryJson = result.brRegistry && Object.keys(result.brRegistry).length
1485
- ? JSON.stringify(result.brRegistry)
1486
- : null
1487
- const brRegistryScript = adoptDom
1488
- ? (adoptionScripts(result) ? adoptionScripts(result) + '\n' : '')
1489
- : brRegistryJson
1490
- ? `<script>window.__BR_REGISTRY__=${brRegistryJson}</script>\n`
1491
- : ''
1478
+ const brRegistryScript = bootScripts ? bootScripts + '\n' : ''
1492
1479
  isrBody = `${translationSeed}${brRegistryScript}<script>window.__BRENDER__=true</script>
1493
1480
  <script type="module" src="${prefix}${isr.clientScript}"></script>`
1494
1481
  } else {
@@ -1538,33 +1525,26 @@ export const renderPage = async (data, route = '/', options = {}) => {
1538
1525
  // client bundle, so writing it over a bundler's dist/index.html shipped
1539
1526
  // pages that never booted.
1540
1527
  if (typeof shell === 'string') {
1541
- const styles = [
1542
- globalCSS.fontFaceCSS ? `<style data-brender>${globalCSS.fontFaceCSS}</style>` : '',
1543
- `<style data-brender>\n${globalCSS.rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`,
1544
- emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''
1545
- ]
1546
1528
  const html = composeIntoShell(shell, {
1547
1529
  headTags: resolvedHeadTags,
1548
- headEnd: [fontLinks, ...styles].filter(Boolean).join('\n'),
1530
+ headEnd: [fontLinks, brenderStyle, emotionStyle].filter(Boolean).join('\n'),
1549
1531
  body: result.html,
1550
1532
  depth: Number.isInteger(options.depth) ? options.depth : 0,
1551
- bootScripts: adoptDom ? adoptionScripts(result) : ''
1533
+ bootScripts: adoptDom ? bootScripts : ''
1552
1534
  })
1553
1535
  return { html, route, brKeyCount, shell: true }
1554
1536
  }
1555
1537
 
1538
+ // (The empty line after the font links held a @font-face <style> that
1539
+ // splitCapturedCSS never fills — @font-face stays in the class stream.)
1556
1540
  const html = `<!DOCTYPE html>
1557
1541
  <html lang="${htmlLang}">
1558
1542
  <head>
1559
1543
  ${resolvedHeadTags}
1560
1544
  ${fontLinks}
1561
- ${globalCSS.fontFaceCSS ? `<style data-brender>${globalCSS.fontFaceCSS}</style>` : ''}
1562
- <style data-brender>
1563
- ${globalCSS.rootRule || ''}
1564
- ${globalCSS.resetRules || ''}
1565
- ${globalCSS.keyframeRules || ''}
1566
- </style>
1567
- ${emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''}
1545
+
1546
+ ${brenderStyle}
1547
+ ${emotionStyle}
1568
1548
  </head>
1569
1549
  <body>
1570
1550
  ${result.html}
@@ -1575,398 +1555,6 @@ ${isrBody}
1575
1555
  return { html, route, brKeyCount }
1576
1556
  }
1577
1557
 
1578
- // ── Design system token resolution ──────────────────────────────────────────
1579
-
1580
- const LETTER_TO_INDEX = {
1581
- U: -6, V: -5, W: -4, X: -3, Y: -2, Z: -1,
1582
- A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9,
1583
- K: 10, L: 11, M: 12, N: 13, O: 14, P: 15
1584
- }
1585
-
1586
- const SPACING_PROPS = new Set([
1587
- 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
1588
- 'paddingBlock', 'paddingInline', 'paddingBlockStart', 'paddingBlockEnd',
1589
- 'paddingInlineStart', 'paddingInlineEnd',
1590
- 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
1591
- 'marginBlock', 'marginInline', 'marginBlockStart', 'marginBlockEnd',
1592
- 'marginInlineStart', 'marginInlineEnd',
1593
- 'gap', 'rowGap', 'columnGap',
1594
- 'top', 'right', 'bottom', 'left',
1595
- 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight',
1596
- 'flexBasis', 'fontSize', 'lineHeight', 'letterSpacing',
1597
- 'borderWidth', 'borderRadius', 'outlineWidth', 'outlineOffset',
1598
- 'inset', 'insetBlock', 'insetInline',
1599
- 'boxSize', 'round'
1600
- ])
1601
-
1602
- /**
1603
- * Resolves a spacing token like 'B2', 'A', 'E3' to a px/em value.
1604
- * Uses base * ratio^index for main steps (A=0, B=1, etc.)
1605
- * and sub-ratio interpolation for sub-steps (B1, B2, B3).
1606
- */
1607
- const resolveSpacingToken = (token, spacingConfig) => {
1608
- if (!token || typeof token !== 'string') return null
1609
- if (!spacingConfig) return null
1610
-
1611
- const base = spacingConfig.base || 16
1612
- const ratio = spacingConfig.ratio || 1.618
1613
- const unit = spacingConfig.unit || 'px'
1614
- const hasSubSequence = spacingConfig.subSequence !== false
1615
-
1616
- // Handle compound values like 'B2 - -' or 'A1 B C1'
1617
- if (token.includes(' ')) {
1618
- const parts = token.split(' ').map(part => {
1619
- if (part === '-' || part === '') return part
1620
- return resolveSpacingToken(part, spacingConfig) || part
1621
- })
1622
- return parts.join(' ')
1623
- }
1624
-
1625
- // Skip CSS keywords and values with units
1626
- if (/^(none|auto|inherit|initial|unset|0)$/i.test(token)) return null
1627
- if (/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(token)) return null
1628
- // Skip hex colors, rgb(), etc.
1629
- if (/^(#|rgb|hsl|var\()/i.test(token)) return null
1630
-
1631
- const isNegative = token.startsWith('-')
1632
- const abs = isNegative ? token.slice(1) : token
1633
-
1634
- // Match letter + optional digit: A, B, B2, E3, etc.
1635
- const m = abs.match(/^([A-Z])(\d)?$/i)
1636
- if (!m) return null
1637
-
1638
- const letter = m[1].toUpperCase()
1639
- const subStep = m[2] ? parseInt(m[2]) : 0
1640
- const idx = LETTER_TO_INDEX[letter]
1641
- if (idx === undefined) return null
1642
-
1643
- let value = base * Math.pow(ratio, idx)
1644
-
1645
- if (subStep > 0 && hasSubSequence) {
1646
- const next = base * Math.pow(ratio, idx + 1)
1647
- const diff = next - value
1648
- const subRatio = diff / ratio
1649
- // Sub-steps: 1 = value + (diff - subRatio), 2 = midpoint, 3 = value + subRatio
1650
- const first = next - subRatio
1651
- const second = value + subRatio
1652
- const middle = (first + second) / 2
1653
- const subs = (~~next - ~~value > 16) ? [first, middle, second] : [first, second]
1654
- if (subStep <= subs.length) {
1655
- value = subs[subStep - 1]
1656
- }
1657
- }
1658
-
1659
- const rounded = Math.round(value * 100) / 100
1660
- const sign = isNegative ? '-' : ''
1661
- return `${sign}${rounded}${unit}`
1662
- }
1663
-
1664
- // Kebab-case versions of spacing props for post-shorthand resolution
1665
- const SPACING_PROPS_KEBAB = new Set(
1666
- [...SPACING_PROPS].map(k => k.replace(/[A-Z]/g, m => '-' + m.toLowerCase()))
1667
- )
1668
-
1669
- /**
1670
- * Try to resolve a CSS value through the design system.
1671
- * Returns the resolved value or the original if not a token.
1672
- */
1673
- const resolveDSValue = (key, val, ds) => {
1674
- if (typeof val !== 'string') return val
1675
-
1676
- // Color resolution
1677
- if (CSS_COLOR_PROPS.has(key)) {
1678
- const colorMap = ds?.color || {}
1679
- if (colorMap[val]) return colorMap[val]
1680
- }
1681
-
1682
- // Spacing resolution (check both camelCase and kebab-case keys)
1683
- if (SPACING_PROPS.has(key) || SPACING_PROPS_KEBAB.has(key)) {
1684
- const spacing = ds?.spacing || {}
1685
- const resolved = resolveSpacingToken(val, spacing)
1686
- if (resolved) return resolved
1687
- }
1688
-
1689
- return val
1690
- }
1691
-
1692
- // ── CSS helpers ─────────────────────────────────────────────────────────────
1693
-
1694
- const CSS_COLOR_PROPS = new Set([
1695
- 'color', 'background', 'backgroundColor', 'borderColor',
1696
- 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor',
1697
- 'outlineColor', 'fill', 'stroke'
1698
- ])
1699
-
1700
- const NON_CSS_PROPS = new Set([
1701
- 'href', 'src', 'alt', 'title', 'id', 'name', 'type', 'value', 'placeholder',
1702
- 'target', 'rel', 'loading', 'srcset', 'sizes', 'media', 'role', 'tabindex',
1703
- 'for', 'action', 'method', 'enctype', 'autocomplete', 'autofocus',
1704
- 'theme', '__element', 'update',
1705
- 'childrenAs', 'childExtends', 'childProps', 'children'
1706
- ])
1707
-
1708
- const camelToKebab = (str) => str.replace(/[A-Z]/g, m => '-' + m.toLowerCase())
1709
-
1710
- const resolveShorthand = (key, val) => {
1711
- if (typeof val === 'undefined' || val === null) return null
1712
-
1713
- // Flex shorthands
1714
- if (key === 'flow' && typeof val === 'string') {
1715
- let [direction, wrap] = (val || 'row').split(' ')
1716
- if (val.startsWith('x') || val === 'row') direction = 'row'
1717
- if (val.startsWith('y') || val === 'column') direction = 'column'
1718
- return { display: 'flex', 'flex-flow': (direction || '') + ' ' + (wrap || '') }
1719
- }
1720
- if (key === 'wrap') {
1721
- return { display: 'flex', 'flex-wrap': val }
1722
- }
1723
- if ((key === 'align' || key === 'flexAlign') && typeof val === 'string') {
1724
- const [alignItems, justifyContent] = val.split(' ')
1725
- const result = { display: 'flex', 'align-items': alignItems }
1726
- if (justifyContent) result['justify-content'] = justifyContent
1727
- return result
1728
- }
1729
- if (key === 'gridAlign' && typeof val === 'string') {
1730
- const [alignItems, justifyContent] = val.split(' ')
1731
- const result = { display: 'grid', 'align-items': alignItems }
1732
- if (justifyContent) result['justify-content'] = justifyContent
1733
- return result
1734
- }
1735
- if (key === 'flexFlow' && typeof val === 'string') {
1736
- let [direction, wrap] = (val || 'row').split(' ')
1737
- if (val.startsWith('x') || val === 'row') direction = 'row'
1738
- if (val.startsWith('y') || val === 'column') direction = 'column'
1739
- return { display: 'flex', 'flex-flow': (direction || '') + ' ' + (wrap || '') }
1740
- }
1741
- if (key === 'flexWrap') {
1742
- return { display: 'flex', 'flex-wrap': val }
1743
- }
1744
-
1745
- // Background image shorthand
1746
- if (key === 'backgroundImage' && typeof val === 'string' && !val.startsWith('url(') && !val.startsWith('linear-gradient') && !val.startsWith('radial-gradient') && !val.startsWith('none')) {
1747
- return { 'background-image': `url(${val})` }
1748
- }
1749
-
1750
- // Box/size shorthands
1751
- if (key === 'round' || (key === 'borderRadius' && val)) {
1752
- return { 'border-radius': typeof val === 'number' ? val + 'px' : val }
1753
- }
1754
- if (key === 'boxSize' && typeof val === 'string') {
1755
- const [height, width] = val.split(' ')
1756
- return { height, width: width || height }
1757
- }
1758
- if (key === 'widthRange' && typeof val === 'string') {
1759
- const [minWidth, maxWidth] = val.split(' ')
1760
- return { 'min-width': minWidth, 'max-width': maxWidth || minWidth }
1761
- }
1762
- if (key === 'heightRange' && typeof val === 'string') {
1763
- const [minHeight, maxHeight] = val.split(' ')
1764
- return { 'min-height': minHeight, 'max-height': maxHeight || minHeight }
1765
- }
1766
-
1767
- // Grid aliases
1768
- if (key === 'column') return { 'grid-column': val }
1769
- if (key === 'columns') return { 'grid-template-columns': val }
1770
- if (key === 'templateColumns') return { 'grid-template-columns': val }
1771
- if (key === 'row') return { 'grid-row': val }
1772
- if (key === 'rows') return { 'grid-template-rows': val }
1773
- if (key === 'templateRows') return { 'grid-template-rows': val }
1774
- if (key === 'area') return { 'grid-area': val }
1775
- if (key === 'template') return { 'grid-template': val }
1776
- if (key === 'templateAreas') return { 'grid-template-areas': val }
1777
- if (key === 'autoColumns') return { 'grid-auto-columns': val }
1778
- if (key === 'autoRows') return { 'grid-auto-rows': val }
1779
- if (key === 'autoFlow') return { 'grid-auto-flow': val }
1780
- if (key === 'columnStart') return { 'grid-column-start': val }
1781
- if (key === 'rowStart') return { 'grid-row-start': val }
1782
-
1783
- return null
1784
- }
1785
-
1786
- const resolveInnerProps = (obj, ds) => {
1787
- const result = {}
1788
- for (const k in obj) {
1789
- const v = obj[k]
1790
- const expanded = resolveShorthand(k, v)
1791
- if (expanded) {
1792
- for (const ek in expanded) {
1793
- result[ek] = resolveDSValue(ek, expanded[ek], ds)
1794
- }
1795
- continue
1796
- }
1797
- if (typeof v !== 'string' && typeof v !== 'number') continue
1798
- result[camelToKebab(k)] = resolveDSValue(k, v, ds)
1799
- }
1800
- return result
1801
- }
1802
-
1803
- const buildCSSFromProps = (props, ds, mediaMap) => {
1804
- const base = {}
1805
- const mediaRules = {}
1806
- const pseudoRules = {}
1807
-
1808
- for (const key in props) {
1809
- const val = props[key]
1810
-
1811
- if (key.charCodeAt(0) === 64 && typeof val === 'object') {
1812
- const bp = mediaMap?.[key.slice(1)]
1813
- if (bp) {
1814
- const inner = resolveInnerProps(val, ds)
1815
- if (Object.keys(inner).length) mediaRules[bp] = inner
1816
- }
1817
- continue
1818
- }
1819
-
1820
- if (key.charCodeAt(0) === 58 && typeof val === 'object') {
1821
- const inner = resolveInnerProps(val, ds)
1822
- if (Object.keys(inner).length) pseudoRules[key] = inner
1823
- continue
1824
- }
1825
-
1826
- if (typeof val !== 'string' && typeof val !== 'number') continue
1827
- if (key.charCodeAt(0) >= 65 && key.charCodeAt(0) <= 90) continue
1828
- if (NON_CSS_PROPS.has(key)) continue
1829
-
1830
- const expanded = resolveShorthand(key, val)
1831
- if (expanded) {
1832
- for (const ek in expanded) {
1833
- base[ek] = resolveDSValue(ek, expanded[ek], ds)
1834
- }
1835
- continue
1836
- }
1837
-
1838
- base[camelToKebab(key)] = resolveDSValue(key, val, ds)
1839
- }
1840
-
1841
- return { base, mediaRules, pseudoRules }
1842
- }
1843
-
1844
- const renderCSSRule = (selector, { base, mediaRules, pseudoRules }) => {
1845
- const lines = []
1846
- const baseDecls = Object.entries(base).map(([k, v]) => `${k}: ${v}`).join('; ')
1847
- if (baseDecls) lines.push(`${selector} { ${baseDecls}; }`)
1848
-
1849
- for (const [pseudo, p] of Object.entries(pseudoRules)) {
1850
- const decls = Object.entries(p).map(([k, v]) => `${k}: ${v}`).join('; ')
1851
- if (decls) lines.push(`${selector}${pseudo} { ${decls}; }`)
1852
- }
1853
-
1854
- for (const [query, p] of Object.entries(mediaRules)) {
1855
- const decls = Object.entries(p).map(([k, v]) => `${k}: ${v}`).join('; ')
1856
- const mq = query.startsWith('@') ? query : `@media ${query}`
1857
- if (decls) lines.push(`${mq} { ${selector} { ${decls}; } }`)
1858
- }
1859
-
1860
- return lines.join('\n')
1861
- }
1862
-
1863
- // Map of component names to their implicit CSS from extends
1864
- const EXTENDS_CSS = {
1865
- Flex: { display: 'flex' },
1866
- InlineFlex: { display: 'inline-flex' },
1867
- Grid: { display: 'grid' },
1868
- InlineGrid: { display: 'inline-grid' },
1869
- Block: { display: 'block' },
1870
- Inline: { display: 'inline' }
1871
- }
1872
-
1873
- const getExtendsCSS = (el) => {
1874
- const exts = el.__ref?.__extends
1875
- if (!exts || !Array.isArray(exts)) return null
1876
- for (const ext of exts) {
1877
- if (EXTENDS_CSS[ext]) return EXTENDS_CSS[ext]
1878
- }
1879
- return null
1880
- }
1881
-
1882
- /**
1883
- * Resolve function-valued CSS props by evaluating them with (element, state).
1884
- * In SSR, this gives correct initial values (e.g. display: 'none' when no auth).
1885
- * Uses fallback mock state if element state is incomplete.
1886
- */
1887
- const resolveElementProps = (el) => {
1888
- let resolved
1889
- for (const key in el) {
1890
- if (typeof el[key] !== 'function') continue
1891
- // Skip non-CSS props and component children
1892
- if (NON_CSS_PROPS.has(key)) continue
1893
- if (key.charCodeAt(0) >= 65 && key.charCodeAt(0) <= 90) continue
1894
- if (key.startsWith('on')) continue
1895
- if (key.startsWith('__')) continue
1896
- if (!resolved) resolved = {}
1897
- let result
1898
- try {
1899
- result = el[key](el, el.state || {})
1900
- } catch {
1901
- // State prototype chain may be incomplete in SSR — try with mock
1902
- try {
1903
- const mockState = { root: {}, ...(el.state || {}) }
1904
- result = el[key](el, mockState)
1905
- } catch { /* skip prop */ }
1906
- }
1907
- if (result !== undefined && result !== null && result !== false) {
1908
- resolved[key] = result
1909
- }
1910
- }
1911
- return resolved || el
1912
- }
1913
-
1914
- const extractCSS = (element, ds) => {
1915
- const mediaMap = ds?.media || {}
1916
- const animations = ds?.animation || {}
1917
- const rules = []
1918
- const seen = new Set()
1919
- const usedAnimations = new Set()
1920
-
1921
- const walk = (el) => {
1922
- if (!el || !el.__ref) return
1923
- const props = resolveElementProps(el)
1924
- if (props && el.node) {
1925
- const cls = el.node.getAttribute?.('class')
1926
- if (cls && !seen.has(cls)) {
1927
- seen.add(cls)
1928
- const cssResult = buildCSSFromProps(props, ds, mediaMap)
1929
-
1930
- // Inject CSS from extends chain (e.g. extends: 'Flex' → display: flex)
1931
- const extsCss = getExtendsCSS(el)
1932
- if (extsCss) {
1933
- for (const [k, v] of Object.entries(extsCss)) {
1934
- const kebab = camelToKebab(k)
1935
- if (!cssResult.base[kebab]) cssResult.base[kebab] = v
1936
- }
1937
- }
1938
-
1939
- const has = Object.keys(cssResult.base).length || Object.keys(cssResult.mediaRules).length || Object.keys(cssResult.pseudoRules).length
1940
- if (has) rules.push(renderCSSRule('.' + cls.split(' ')[0], cssResult))
1941
-
1942
- const anim = props.animation || props.animationName
1943
- if (typeof anim === 'string') {
1944
- const name = anim.split(' ')[0]
1945
- if (animations[name]) usedAnimations.add(name)
1946
- }
1947
- }
1948
- }
1949
- if (el.__ref?.__children) {
1950
- for (const ck of el.__ref.__children) {
1951
- if (el[ck]?.__ref) walk(el[ck])
1952
- }
1953
- }
1954
- }
1955
- walk(element)
1956
-
1957
- const keyframes = []
1958
- for (const name of usedAnimations) {
1959
- const frames = animations[name]
1960
- const frameRules = Object.entries(frames).map(([step, p]) => {
1961
- const decls = Object.entries(p).map(([k, v]) => `${camelToKebab(k)}: ${v}`).join('; ')
1962
- return ` ${step} { ${decls}; }`
1963
- }).join('\n')
1964
- keyframes.push(`@keyframes ${name} {\n${frameRules}\n}`)
1965
- }
1966
-
1967
- return [...keyframes, ...rules].join('\n')
1968
- }
1969
-
1970
1558
  const generateFontLinks = (ds) => {
1971
1559
  if (!ds) return ''
1972
1560
  const families = ds.font_family || ds.fontFamily || {}