@symbo.ls/brender 3.14.669 → 3.14.671

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,39 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.671
4
+
5
+ ### Patch Changes
6
+
7
+ - XMA round 8 (page weight and core size). Default builds look and behave the same; opt-in options save more.
8
+
9
+ Built pages (brender), on by default with no visible change, each with an off switch:
10
+
11
+ - The inline sprite holds only the page's icons (`brender.spriteIcons: 'all'` restores all).
12
+ - Atomic CSS only for classes in the page's markup (`brender.cssClasses: 'all'`).
13
+ - The element-path registry ships as a tree when the client reads it (`brender.registryFormat: 'paths'`).
14
+
15
+ Opt-in:
16
+
17
+ - `build.prepareDesignSystem: { pages: false }`: built pages do not request the prepared design-system file (`false` keeps meaning `{ shell: false }`).
18
+ - `brender.cssVariables: 'page'`: a built page carries only the CSS variables it reads.
19
+ - `build.treeShakeFeatures: true | { keep: [...] }`: a production `smbls build` drops unused autoGrow, inertOutside, bridge, viewEvents, lazy route groups, viewTransition, live sync, the pre-rendered boot (kept when the build pre-renders) and getSafeInk. Using a dropped feature logs a loud error naming the option.
20
+
21
+ Core: the full `esm` dist no longer carries esbuild `__esm` wrappers (namespace objects marked pure): about 2 KB Brotli less in every build.
22
+
23
+ Fix (round 7 options): the build scan for `treeShakeComponents`, `treeShakeIcons`, `treeShakeLibraries` and `treeShakeFeatures` reads only what the app loads (the symbols dir without tests, the entry document, symbols.json, and what they import). Files in scripts/, tests/, tool configs and worker files no longer keep components alive, and they no longer change the build output.
24
+
25
+ - Updated dependencies
26
+ - smbls@3.14.848
27
+ - @symbo.ls/utils@3.14.848
28
+ - @symbo.ls/element@3.14.813
29
+ - @symbo.ls/scratch@3.14.795
30
+
31
+ ## 3.14.670
32
+
33
+ ### Patch Changes
34
+
35
+ - A page built ahead carries `data-theme` on `<html>` when `globalTheme` is a fixed theme (light, dark or a named scheme), so first paint uses the project theme and not the OS scheme. `auto`/`system`/unset keep the prefers-color-scheme behaviour. (XMA round 7, K)
36
+
3
37
  ## 3.14.669
4
38
 
5
39
  ### Patch Changes
@@ -0,0 +1 @@
1
+ const E=/<svg\b[^>]*\sid="svgSprite"[^>]*>/,x=/\s*<symbol\b[^>]*>[\s\S]*?<\/symbol>/y,m=/\s*<\/svg>/y,O=/^\s*<symbol\b[^>]*?\sid="([^"]+)"/,y=/#([^\s"'()<>&,;#]+)/g,w=(e,c)=>{if(typeof e!="string"||!e)return c;y.lastIndex=0;let t;for(;t=y.exec(e);)c.add(t[1]);return c},q=(e,c="")=>{const t={html:e,kept:0,dropped:0};if(typeof e!="string")return t;const s=E.exec(e);if(!s)return t;const n=s.index;let o=n+s[0].length;const r=[];for(;;){x.lastIndex=o;const f=x.exec(e);if(!f)break;const p=O.exec(f[0]);r.push({text:f[0],id:p?p[1]:null}),o=x.lastIndex}if(m.lastIndex=o,!m.exec(e))return t;const g=m.lastIndex,i=e.slice(0,n)+e.slice(g),u=w(c,w(i,new Set)),a=new Set,h=new Map;for(const f of r)f.id&&h.set(f.id,f);const S=[...u];for(;S.length;){const f=S.pop(),p=h.get(f);if(!(!p||a.has(p))){a.add(p);for(const k of w(p.text,new Set))h.has(k)&&S.push(k)}}const d=r.filter(f=>!f.id||a.has(f));if(d.length===r.length)return{html:e,kept:d.length,dropped:0};const v=s[0]+d.map(f=>f.text).join("")+e.slice(o,g);return{html:e.slice(0,n)+v+e.slice(g),kept:d.length,dropped:r.length-d.length}},M=e=>e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]"),T=/\.(-?(?:[_A-Za-z -￿]|\\.)(?:[\w -￿-]|\\.)*)/g,P=e=>e.replace(/\\(?:([0-9a-fA-F]{1,6})\s?|(.))/g,(c,t,s)=>t?String.fromCodePoint(parseInt(t,16)):s),L=e=>{const c=[];let t=0,s=null;for(let n=0;n<e.length;n++){const o=e[n];if(s){o==="\\"?n++:o===s&&(s=null);continue}o==='"'||o==="'"?s=o:o==="{"?(c.push(e.slice(t,n).trim()),t=n+1):(o==="}"||o===";")&&(t=n+1)}return c},C=e=>{const c=[];for(const t of L(String(e||""))){if(!t||t[0]==="@")continue;T.lastIndex=0;const s=M(t);let n;for(;n=T.exec(s);)c.push(P(n[1]))}return c},A=/\sclass\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/gi,F=(...e)=>{const c=new Set;for(const t of e){if(typeof t!="string"||!t)continue;A.lastIndex=0;let s;for(;s=A.exec(t);){const n=s[1]??s[2]??s[3]??"";for(const o of n.split(/\s+/))o&&c.add(o.replace(/&amp;/g,"&"))}}return c},N=(e,c)=>{const t=[];for(const s of e||[]){if(!s)continue;const n=C(s);(!n.length||n.some(o=>c.has(o)))&&t.push(s)}return t},b=/var\(\s*(--[\w-]+)/g,_=/^\s*(--[\w-]+)\s*:([\s\S]*)$/,I=(e,c)=>{if(typeof e!="string"||!e)return c;b.lastIndex=0;let t;for(;t=b.exec(e);)c.add(t[1]);return c},R=(e,c)=>{let t="",s=0,n=null,o=0;for(let r=0;r<e.length;r++){const l=e[r];if(n){l==="\\"?r++:l===n&&(n=null);continue}if(l==='"'||l==="'")n=l;else if(l==="(")o++;else if(l===")")o=Math.max(0,o-1);else{if(o)continue;l==="{"?(t+=e.slice(s,r+1),s=r+1):l===";"?(t+=c(e.slice(s,r),";"),s=r+1):l==="}"&&(t+=c(e.slice(s,r),"")+"}",s=r+1)}}return t+e.slice(s)},B=(e,...c)=>{if(typeof e!="string"||!e)return{css:e,kept:0,dropped:0};const t=new Map,s=new Set;R(e,i=>{const u=_.exec(i);if(u){const a=t.get(u[1])||[];a.push(u[2]),t.set(u[1],a)}else I(i,s);return""});for(const i of c)I(i,s);const n=new Set,o=[...s];for(;o.length;){const i=o.pop();if(!(n.has(i)||!t.has(i))){n.add(i);for(const u of t.get(i))for(const a of I(u,new Set))o.push(a)}}let r=0,l=0;return{css:R(e,(i,u)=>{const a=_.exec(i);return!a||n.has(a[1])?(a&&r++,i+u):(l++,"")}),kept:r,dropped:l}};export{N as classRulesForPage,F as markupClasses,C as ruleClasses,q as trimSpriteToPage,B as trimVariablesToPage};
@@ -1,21 +1,22 @@
1
- import{resolve as u,join as Ge,dirname as E}from"path";import{existsSync as y,unlinkSync as Ne,readFileSync as J,realpathSync as He}from"fs";import{resolvePackageEntry as Ue}from"./packageEntry.js";import{tmpdir as We}from"os";import{randomBytes as Je}from"crypto";import{createRequire as ie}from"module";import{pathToFileURL as ze}from"url";import{createEnv as ye}from"./env.js";import{resetKeys as be,assignKeys as Se,mapKeysToElements as we}from"./keys.js";import{extractMetadata as le,generateHeadHtml as Ye}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Xe}from"./shell.js";import{fetchSSRTranslations as Ve}from"./prefetch.js";import{createEarlySeedScript as Qe}from"@symbo.ls/fetch";import{headerEntries as Ze}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as et,brenderStampMeta as tt,escapeRegExp as ot,jsonForScript as nt,polyglotFallbackLang as V,polyglotLangStorageKey as st,polyglotAddressLang as rt,polyglotStartupScript as ct}from"@symbo.ls/utils";let Q=null;const at=async()=>{if(Q)return Q;try{return Q=(await import("@symbo.ls/funcql")).funcqlPlugin,Q}catch{return null}};import"linkedom";import{createEarlySeedBlock as it}from"@symbo.ls/fetch";import{reset as lt}from"@symbo.ls/css";const Z=(e,t)=>{if(!e||!t)return;if(e[t]!==void 0)return e[t];const o=t.split(".");let n=e;for(const s of o){if(n==null||typeof n!="object")return;n=n[s]}return n},ee=(e,t)=>V(e?.lang,t),ke=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const a=Z(r,e);if(a!==void 0)return a}}if(n?.translations){const r=n.translations[t];if(r){const a=Z(r,e);if(a!==void 0)return a}}const s=V(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const a=Z(r,e);if(a!==void 0)return a}}},ft=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=ke(e,t||ee(s,n),n,s);if(r!==void 0)return r;const a=de(ue);return n&&a&&a.labelKeys.add(e),e},Re=function(){return ee(this?.state?.root,this?.context?.polyglot)},v=(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 s of e)n.push(typeof s=="object"&&s!==null?v(s,t):s);return n}const o={};t.set(e,o);for(const n of Object.keys(e)){const s=e[n];o[n]=typeof s=="object"&&s!==null?v(s,t):s}return o};let $=null;try{import.meta.url&&($=ie(import.meta.url))}catch{}const pt=()=>{if(!import.meta.url||!$)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=He(new URL(".",import.meta.url).pathname),t=u(e,"../..");if(y(u(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=$.resolve("smbls/package.json");n=E(l)}catch{let l=e;for(;l!==E(l);){const g=u(l,"node_modules","smbls");if(y(u(g,"package.json"))){n=g;break}l=E(l)}}let s=$;if(n)try{s=ie(u(n,"package.json"))}catch{}let r,a=process.cwd();for(;a!==E(a);){if(y(u(a,"package.json"))){r=a;break}a=E(a)}let d=s;if(r)try{d=ie(u(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:d}},B=(e,t)=>{const o=e.isMonorepo?[$]:[e._smblsRequire,e._projectRequire,$].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},xo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages",t,...o);const n=B(e,`${t}/package.json`);return n?u(E(n),...o):null},jo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"plugins",t,...o);const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null},fe=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=u(e.monorepoRoot,s,t,...o);if(y(r))return r}return null}const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null},xe=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages","domql","packages",t,...o);const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null};let te=null;const je="smbls/ssr",ut=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},_e=()=>(te||(te=dt().catch(e=>{throw te=null,e})),te),dt=async()=>{const e=pt();let t;if($&&e.isMonorepo)t=u(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await ut()}catch(i){f=i}if(!$)throw new Error(`brender: ${je} is not available in this bundled runtime (${f.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`);const c=e.smblsRoot&&u(e.smblsRoot,"src","createDomql.js");if(c&&y(c))t=c;else throw new Error(`brender: cannot load ${je} (${f.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=Ge(We(),`br_createDomql_${Je(6).toString("hex")}.mjs`),s=Ue,r=e.isMonorepo?await import(ze(u(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,a=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,d=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},c=>{if(d.test(c.path))return;const i=c.path.split("/"),p=c.path.startsWith("@")?i.slice(0,2).join("/"):i[0];if(!a.has(p))return;const m=r.resolveWorkspaceSource(c.path,c.importer||t);return m?{path:m}:{errors:[{text:`"${c.path}" names no source file in the workspace`}]}}),f.onLoad({filter:/[\\/]dist[\\/]/},c=>r.isWorkspaceDist(c.path)?{errors:[{text:`${c.path} is a BUILD of a workspace package \u2014 the render bundles source`}]}:void 0)),f.onResolve({filter:/^smbls/},c=>{const i=c.path.replace(/^smbls\/?/,""),p=e.isMonorepo?u(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!p)return;if(!i){const S=s(p);return S?{path:S}:void 0}const m=u(p,i);if(y(m))return{path:m};if(y(m+".js"))return{path:m+".js"};const b=u(m,"index.js");if(y(b))return{path:b}}),f.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const i=u(e.monorepoRoot,"packages","domql","src","index.js");if(y(i))return{path:i};const p=u(e.monorepoRoot,"packages","domql","index.js");if(y(p))return{path:p}}else try{const i=_require.resolve("domql/package.json"),p=s(E(i));if(p)return{path:p}}catch{}}),f.onResolve({filter:/^@symbo\.ls\//},c=>{const i=c.path.replace("@symbo.ls/","");if(i==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(e.isMonorepo){for(const m of["packages","plugins"]){const b=u(e.monorepoRoot,m,i,"src","index.js");if(y(b))return{path:b};const S=u(e.monorepoRoot,m,i,"index.js");if(y(S))return{path:S}}const p=u(e.monorepoRoot,"packages","default-config","blank","index.js");if(i==="default-config"&&y(p))return{path:p}}else{const p=fe(e,i,"src","index.js");if(p&&y(p))return{path:p};const m=fe(e,i,"index.js");if(m&&y(m))return{path:m};if(i==="default-config"){const b=fe(e,"default-config","blank","index.js");if(b&&y(b))return{path:b}}}}),f.onResolve({filter:/^@domql\//},c=>{const i=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const p=u(e.monorepoRoot,"packages","domql","packages",i,"src","index.js");if(y(p))return{path:p};const m=u(e.monorepoRoot,"packages","domql","packages",i,"index.js");if(y(m))return{path:m}}else{const p=xe(e,i,"src","index.js");if(p&&y(p))return{path:p};const m=xe(e,i,"index.js");if(m&&y(m))return{path:m}}}),f.onResolve({filter:/^css-in-props/},c=>{let i;if(e.isMonorepo)i=u(e.monorepoRoot,"packages","css-in-props");else{const b=B(e,"css-in-props/package.json");b&&(i=E(b))}if(!i)return;const p=c.path.replace(/^css-in-props\/?/,"");if(p){const b=u(i,p),S=u(b,"index.js");if(y(S))return{path:S};if(y(b+".js"))return{path:b+".js"};if(y(b))return{path:b}}const m=s(i);if(m)return{path:m}}),f.onResolve({filter:/^@emotion\//},c=>{let i;if(e.isMonorepo)i=u(e.monorepoRoot,"node_modules",c.path);else{const p=B(e,`${c.path}/package.json`);if(!p)return;i=E(p)}if(y(i)){const p=u(i,"package.json");if(y(p))try{const m=JSON.parse(J(p,"utf8")),b=m.module||m.main||"dist/emotion-css.esm.js";return{path:u(i,b)}}catch{}return{path:i}}}),f.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const i=u(c.resolveDir,c.path);if(y(i))return{path:i}}}),f.onLoad({filter:/smbls\/.*options\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("options.js"))return;let i=J(c.path,"utf8");return i=i.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),i=i.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:i,loader:"js"}}),f.onLoad({filter:/smbls\/.*init\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("init.js"))return;let i=J(c.path,"utf8");return i=i.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:i,loader:"js"}}),f.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"})),f.onLoad({filter:/smbls\/src\/router\.js$/},async c=>{let i=J(c.path,"utf8");return i=i.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:i,loader:"js"}}),f.onLoad({filter:/fetchOnCreate\.js$/},async c=>{if(!c.path.includes("smbls/"))return;let i=J(c.path,"utf8");return i=i.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:i,loader:"js"}})}};await o.build({entryPoints:[t],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[l],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(f=>y(f)),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 g=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{Ne(n)}catch{}return g},mt={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"}},gt=(e,t)=>{const o={};return Ee(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},Ee=(e,t)=>{const o=n=>{const s=n&&n.__ref;if(s&&(t(n,s),s.__children))for(const r of s.__children)o(n[r])};o(e)},ht=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let K=0;const ve=Symbol.for("brender.fetchTracked"),pe=()=>K,yt=256*1024,bt=1024*1024,St=new Set(["accept-language","accept"]),Te={renders:0,record:null},wt=50,ue={renders:0,record:null},Le=(e,t)=>{e.renders++,e.renders===1?e.record=t():e.record&&(e.record.tainted=!0);const o=e.record;return{record:o,close:()=>{e.renders--,e.record===o&&(e.record=null)}}},de=e=>e.record&&!e.record.tainted?e.record:null,kt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},me=(e,t,o)=>{!e||e.tainted||e.failures.length>=wt||e.failures.push({url:kt(t),reason:o})},De=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),Rt=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=De(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),a={},d=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,c]of Ze(l))g&&c==null||(a[String(f).toLowerCase()]=String(c))};return n&&d(t.headers),o&&d(o.headers),{url:s,method:r,headers:a}},xt=(e,t,o,n)=>{if(!o||!o.ok||typeof o.clone!="function")return;const s=o.headers&&o.headers.get&&o.headers.get("content-type")||"";if(!/json|^text\//i.test(s))return;let r;try{r=o.clone()}catch{return}n(r.text().then(a=>{e.tainted||a.length>yt||e.bytes+a.length>bt||(e.bytes+=a.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:a}))},()=>{}))},jt=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[ve])return;const t=function(...o){K++;let n=!1;const s=()=>{n||(n=!0,K--)},r=c=>{K++,c.then(()=>{K--},()=>{K--})},a=de(Te),d=de(ue);let l=null;if(a)try{l=Rt(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!St.has(c)))&&(l=null)}catch{l=null}const g=()=>De(o[0]);let f;try{f=e.apply(this,o)}catch(c){throw me(d,g(),c&&c.message||"failed"),s(),c}return f&&typeof f.then=="function"?f.then(c=>{l&&xt(a,l,c,r),d&&c&&c.ok===!1&&me(d,g(),"HTTP "+c.status),s()},c=>{me(d,g(),c&&c.message||"network error"),s()}):s(),f};t[ve]=!0,globalThis.fetch=t},_o=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=nt(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
- `+Qe(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},_t=3e4,Et=2147483647;let $e=Promise.resolve();const Ae=()=>{},vt=e=>{const t=$e.then(e);return $e=t.then(Ae,Ae),t},Tt=e=>{if(e===void 0)return _t;if(e===0||e===!1||e===1/0)return 0;if(typeof e!="number"||!(e>0))throw new TypeError(`brender: options.timeout is a number of milliseconds, or 0 / false for no limit \u2014 not ${JSON.stringify(e)}`);return Math.min(e,Et)},Lt=(e,t)=>{const o=e.route||"/",n=e.pathname||o,s=n===o?o:`${n} (route ${o})`,r=new Error(`brender: the render of ${s} did not finish within ${t} ms and was abandoned, so the renders after it can run (options.timeout)`);return r.code="BRENDER_RENDER_TIMEOUT",r.route=o,r.pathname=n,r.timeout=t,r},Dt=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},$t=e=>{let t=!1;return()=>{t||(t=!0,e())}},qe=(e,t={})=>{let o;try{o=Tt(t.timeout)}catch(n){return Promise.reject(n)}return vt(()=>At(e,t,o))},At=async(e,t,o)=>{await _e();const n={closed:!1,undo:[]},s=qt(e,t,n);if(!o)return s;let r;const a=new Promise((d,l)=>{r=setTimeout(()=>{Dt(n),l(Lt(t,o))},o)});try{return await Promise.race([s,a])}finally{clearTimeout(r)}},qt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:a,prefetch:d=!1}=t,l=s||n;jt();const g=Le(ue,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await Ct(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:a,prefetch:d},g.record,o)}finally{g.close()}},Ct=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},a,d)=>{const l=()=>{if(d.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await Ve(e)}catch(m){d.closed||console.warn("[brender] SSR translation fetch failed:",m.message)}l()}const{window:f,document:c}=ye(),i=c.body;f.location.pathname=o;const p=[oe("document",c),oe("location",f.location),oe("localStorage",f.localStorage),oe("sessionStorage",f.sessionStorage)].map($t);d.undo.push(...p);try{const{createDomqlElement:m}=await _e();l();const b=v(e.app||{}),S={...e.config||{}};e.polyglot&&!S.polyglot&&(S.polyglot=e.polyglot),e.fetch&&!S.fetch&&(S.fetch=e.fetch),e.router&&!S.router&&(S.router=e.router);for(const w of Bt)e[w]!=null&&S[w]==null&&(S[w]=e[w]);const k=S.polyglot?{...S.polyglot}:void 0,z=rt(o,k);z&&f.localStorage.setItem(st(k),z),g&&k&&(k.translations={...k.translations||{},...g});const h=v(e.state||{});(g||k)&&(h.root||(h.root={}),k&&(h.root.lang=V(h.root.lang,k)),g&&(h.root.translations={...h.root.translations||{},...g})),lt();const A={};for(const w in e){if(ht.has(w))continue;const x=e[w];A[w]=x&&typeof x=="object"?v(x):x}const T={...A,state:h,...n?{state:{...h,...n}}:{},dependencies:v(e.dependencies||{}),components:v(e.components||{}),snippets:v(e.snippets||{}),pages:v(e.pages||{}),functions:{...e.functions||{},polyglot:ft,getActiveLang:Re,getLang:Re},methods:e.methods||{},designSystem:v(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...S,...k?{polyglot:k}:{},document:c,window:f,parent:{node:i},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,captureDesignSystem:!0,...s||{}},G=await at();l(),G&&(T.plugins=T.plugins||[],T.plugins.some(w=>w.name==="funcql")||T.plugins.push(G)),be();const I=Le(Te,()=>({entries:[],bytes:0,tainted:!1})),O=I.record;let j;try{j=await m(b,T),l();const w=Date.now(),x=r?2e3:50,W=ae=>new Promise(Ke=>setTimeout(Ke,ae));for(await W(Math.min(50,x));r&&!d.closed&&pe()>0&&Date.now()-w<x;)await W(10),pe()===0&&await W(Math.max(0,Math.min(50,x-(Date.now()-w))));if(l(),r&&await Mt(j,k)){l();const ae=Date.now();for(await W(Math.min(50,x));!d.closed&&pe()>0&&Date.now()-ae<x;)await W(10);l()}}finally{I.close()}const Y=O&&!O.tainted&&!(S.fetch&&S.fetch.seed===!1)?O.entries.slice():[];Se(i);const X=we(j),{elementPath:q}=await import("@symbo.ls/element");l();const N=gt(j,q),H=j?.context?.routerOptions?.contentElementKey||"content",L=j&&j[H],P=L&&typeof L=="object"&&L.__ref?L:void 0,C=le(e,t,j,j?.state,P),D=[],se=c.head||c.querySelector("head");if(se)for(const w of se.querySelectorAll("style")){if(w.sheet&&w.sheet.cssRules)for(const x of w.sheet.cssRules)x.cssText&&D.push(x);if(!D.length){const x=w.textContent||"";x&&D.push({cssText:x})}}const R=D.map(w=>w.cssText),{globalCSS:_,classRules:M}=Ht(D);let F=Ie(i.innerHTML);const U=j?.state?.root,re=ee(U,k),ce=It(g,U);ce&&(F=Me(F,Ce(ce,re,k)));const Be=a&&!a.tainted?{failedRequests:a.failures.slice(),rawLabelKeys:Ft(a.labelKeys,F,re,k,U)}:null;return{html:F,metadata:C,registry:X,brRegistry:N,element:j,emotionCSS:R,globalCSS:_,classRules:M,fetchSeed:Y,document:c,window:f,ssrTranslations:ce||void 0,activeLang:re,diagnostics:Be,preparedDesignSystem:T.preparedDesignSystem||null}}finally{for(const m of p)m()}},oe=(e,t)=>{const o=Object.getOwnPropertyDescriptor(globalThis,e);return o&&!o.configurable?!o.writable&&!o.set?()=>{}:(globalThis[e]=t,()=>{}):(Object.defineProperty(globalThis,e,{value:t,writable:!0,configurable:!0,enumerable:!0}),()=>{o?Object.defineProperty(globalThis,e,o):delete globalThis[e]})},Mt=async(e,t)=>{if(!t||!t.fetch||!e||typeof e.call!="function")return!1;const o=e.state&&e.state.root;if(!o)return!1;const n=ee(o,t),s=o.translations&&o.translations[n];if(s&&typeof s=="object"&&Object.keys(s).length||t.translations&&t.translations[n])return!1;try{await e.call("loadTranslations",n,{inFlight:!0})}catch{}return!0},ge=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,he=e=>e&&typeof e.parse=="function"?e.parse():e,It=(e,t)=>{const o=he(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const a=he(s[r]);a&&typeof a=="object"&&(n[r]={...n[r]||{},...a})}return Object.keys(n).length?n:null},Ce=(e,t,o)=>e[t]||e[V(void 0,o)]||Object.values(e)[0]||{},Me=(e,t)=>e.replace(ge,(o,n)=>Z(t,n.trim())??o),Ot=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Pt=(e,t)=>new RegExp(`(?:^|[^\\w.-])${ot(t)}(?![\\w-]|\\.\\w)`).test(e),Ft=(e,t,o,n,s)=>{const r=new Set,a=t.replace(Ot,"");for(const d of e)/\s/.test(d)||ke(d,o,n,s)===void 0&&Pt(a,d)&&r.add(d);for(const d of a.matchAll(ge))r.add(d[1].trim());return[...r]},Eo=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=ye(),r=s.body,{create:a}=await import("@symbo.ls/element"),d=await import("@symbo.ls/utils"),l={...mt,...o.components||{}},g={...d,...o.utils||{},...o.functions||{}};be();let f;try{f=a(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}Se(r);const c=f?we(f):{};return{html:Ie(r.innerHTML),registry:c,element:f}},Ie=e=>e.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(t,o,n,s)=>{if(n.includes("&lt;")){const r=n.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'");return o+r+s}return t}),Bt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Kt=/\.[A-Za-z_\\-]/,Gt=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Kt.test(t)},Nt=/^\s*(:root|\[data-theme|\[data-skin)/,Oe=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 Gt(e.selectorText)?Nt.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(Oe));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Ht=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Oe(n);s?t[s].push(n.cssText):n&&n.cssText&&o.push(n.cssText)}return{globalCSS:{rootRule:t.root.join(`
1
+ import{resolve as m,join as He,dirname as _}from"path";import{existsSync as h,unlinkSync as Ue,readFileSync as z,realpathSync as We}from"fs";import{resolvePackageEntry as Je}from"./packageEntry.js";import{tmpdir as Ye}from"os";import{randomBytes as ze}from"crypto";import{createRequire as le}from"module";import{pathToFileURL as Ve}from"url";import{createEnv as Se}from"./env.js";import{resetKeys as Re,assignKeys as ke,mapKeysToElements as we}from"./keys.js";import{extractMetadata as pe,generateHeadHtml as Xe}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Qe}from"./shell.js";import{trimSpriteToPage as Ze,classRulesForPage as et,markupClasses as tt,trimVariablesToPage as ot}from"./pageWeight.js";import{fetchSSRTranslations as nt}from"./prefetch.js";import{createEarlySeedScript as st}from"@symbo.ls/fetch";import{headerEntries as rt}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as ct,brenderRegistryTreeBlock as it,brenderStampMeta as at,escapeRegExp as lt,jsonForScript as pt,polyglotFallbackLang as oe,polyglotLangStorageKey as ft,polyglotAddressLang as ut,polyglotStartupScript as mt}from"@symbo.ls/utils";let ne=null;const dt=async()=>{if(ne)return ne;try{return ne=(await import("@symbo.ls/funcql")).funcqlPlugin,ne}catch{return null}};import"linkedom";import{createEarlySeedBlock as gt}from"@symbo.ls/fetch";import{reset as ht}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 s of o){if(n==null||typeof n!="object")return;n=n[s]}return n},re=(e,t)=>oe(e?.lang,t),xe=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const i=se(r,e);if(i!==void 0)return i}}if(n?.translations){const r=n.translations[t];if(r){const i=se(r,e);if(i!==void 0)return i}}const s=oe(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const i=se(r,e);if(i!==void 0)return i}}},yt=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=xe(e,t||re(s,n),n,s);if(r!==void 0)return r;const i=de(me);return n&&i&&i.labelKeys.add(e),e},je=function(){return re(this?.state?.root,this?.context?.polyglot)},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 s of e)n.push(typeof s=="object"&&s!==null?L(s,t):s);return n}const o={};t.set(e,o);for(const n of Object.keys(e)){const s=e[n];o[n]=typeof s=="object"&&s!==null?L(s,t):s}return o};let O=null;try{import.meta.url&&(O=le(import.meta.url))}catch{}const bt=()=>{if(!import.meta.url||!O)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=We(new URL(".",import.meta.url).pathname),t=m(e,"../..");if(h(m(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=O.resolve("smbls/package.json");n=_(l)}catch{let l=e;for(;l!==_(l);){const g=m(l,"node_modules","smbls");if(h(m(g,"package.json"))){n=g;break}l=_(l)}}let s=O;if(n)try{s=le(m(n,"package.json"))}catch{}let r,i=process.cwd();for(;i!==_(i);){if(h(m(i,"package.json"))){r=i;break}i=_(i)}let f=s;if(r)try{f=le(m(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:f}},H=(e,t)=>{const o=e.isMonorepo?[O]:[e._smblsRequire,e._projectRequire,O].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},Co=(e,t,...o)=>{if(e.isMonorepo)return m(e.monorepoRoot,"packages",t,...o);const n=H(e,`${t}/package.json`);return n?m(_(n),...o):null},qo=(e,t,...o)=>{if(e.isMonorepo)return m(e.monorepoRoot,"plugins",t,...o);const n=H(e,`@symbo.ls/${t}/package.json`);return n?m(_(n),...o):null},fe=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=m(e.monorepoRoot,s,t,...o);if(h(r))return r}return null}const n=H(e,`@symbo.ls/${t}/package.json`);return n?m(_(n),...o):null},Ee=(e,t,...o)=>{if(e.isMonorepo)return m(e.monorepoRoot,"packages","domql","packages",t,...o);const n=H(e,`@symbo.ls/${t}/package.json`);return n?m(_(n),...o):null};let ce=null;const Te="smbls/ssr",St=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},_e=()=>(ce||(ce=Rt().catch(e=>{throw ce=null,e})),ce),Rt=async()=>{const e=bt();let t;if(O&&e.isMonorepo)t=m(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let p;try{return await St()}catch(a){p=a}if(!O)throw new Error(`brender: ${Te} is not available in this bundled runtime (${p.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`);const c=e.smblsRoot&&m(e.smblsRoot,"src","createDomql.js");if(c&&h(c))t=c;else throw new Error(`brender: cannot load ${Te} (${p.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=He(Ye(),`br_createDomql_${ze(6).toString("hex")}.mjs`),s=Je,r=e.isMonorepo?await import(Ve(m(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,i=r?new Set(r.workspacePackageRoots().map(p=>r.workspacePackageAt(p).name)):null,f=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(p){r&&(p.onResolve({filter:/^[^./]/},c=>{if(f.test(c.path))return;const a=c.path.split("/"),u=c.path.startsWith("@")?a.slice(0,2).join("/"):a[0];if(!i.has(u))return;const d=r.resolveWorkspaceSource(c.path,c.importer||t);return d?{path:d}:{errors:[{text:`"${c.path}" names no source file in the workspace`}]}}),p.onLoad({filter:/[\\/]dist[\\/]/},c=>r.isWorkspaceDist(c.path)?{errors:[{text:`${c.path} is a BUILD of a workspace package \u2014 the render bundles source`}]}:void 0)),p.onResolve({filter:/^smbls/},c=>{const a=c.path.replace(/^smbls\/?/,""),u=e.isMonorepo?m(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!u)return;if(!a){const E=s(u);return E?{path:E}:void 0}const d=m(u,a);if(h(d))return{path:d};if(h(d+".js"))return{path:d+".js"};const b=m(d,"index.js");if(h(b))return{path:b}}),p.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const a=m(e.monorepoRoot,"packages","domql","src","index.js");if(h(a))return{path:a};const u=m(e.monorepoRoot,"packages","domql","index.js");if(h(u))return{path:u}}else try{const a=_require.resolve("domql/package.json"),u=s(_(a));if(u)return{path:u}}catch{}}),p.onResolve({filter:/^@symbo\.ls\//},c=>{const a=c.path.replace("@symbo.ls/","");if(a==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(e.isMonorepo){for(const d of["packages","plugins"]){const b=m(e.monorepoRoot,d,a,"src","index.js");if(h(b))return{path:b};const E=m(e.monorepoRoot,d,a,"index.js");if(h(E))return{path:E}}const u=m(e.monorepoRoot,"packages","default-config","blank","index.js");if(a==="default-config"&&h(u))return{path:u}}else{const u=fe(e,a,"src","index.js");if(u&&h(u))return{path:u};const d=fe(e,a,"index.js");if(d&&h(d))return{path:d};if(a==="default-config"){const b=fe(e,"default-config","blank","index.js");if(b&&h(b))return{path:b}}}}),p.onResolve({filter:/^@domql\//},c=>{const a=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const u=m(e.monorepoRoot,"packages","domql","packages",a,"src","index.js");if(h(u))return{path:u};const d=m(e.monorepoRoot,"packages","domql","packages",a,"index.js");if(h(d))return{path:d}}else{const u=Ee(e,a,"src","index.js");if(u&&h(u))return{path:u};const d=Ee(e,a,"index.js");if(d&&h(d))return{path:d}}}),p.onResolve({filter:/^css-in-props/},c=>{let a;if(e.isMonorepo)a=m(e.monorepoRoot,"packages","css-in-props");else{const b=H(e,"css-in-props/package.json");b&&(a=_(b))}if(!a)return;const u=c.path.replace(/^css-in-props\/?/,"");if(u){const b=m(a,u),E=m(b,"index.js");if(h(E))return{path:E};if(h(b+".js"))return{path:b+".js"};if(h(b))return{path:b}}const d=s(a);if(d)return{path:d}}),p.onResolve({filter:/^@emotion\//},c=>{let a;if(e.isMonorepo)a=m(e.monorepoRoot,"node_modules",c.path);else{const u=H(e,`${c.path}/package.json`);if(!u)return;a=_(u)}if(h(a)){const u=m(a,"package.json");if(h(u))try{const d=JSON.parse(z(u,"utf8")),b=d.module||d.main||"dist/emotion-css.esm.js";return{path:m(a,b)}}catch{}return{path:a}}}),p.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const a=m(c.resolveDir,c.path);if(h(a))return{path:a}}}),p.onLoad({filter:/smbls\/.*options\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("options.js"))return;let a=z(c.path,"utf8");return a=a.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),a=a.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:a,loader:"js"}}),p.onLoad({filter:/smbls\/.*init\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("init.js"))return;let a=z(c.path,"utf8");return a=a.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:a,loader:"js"}}),p.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"})),p.onLoad({filter:/smbls\/src\/router\.js$/},async c=>{let a=z(c.path,"utf8");return a=a.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:a,loader:"js"}}),p.onLoad({filter:/fetchOnCreate\.js$/},async c=>{if(!c.path.includes("smbls/"))return;let a=z(c.path,"utf8");return a=a.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:a,loader:"js"}})}};await o.build({entryPoints:[t],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[l],nodePaths:e.isMonorepo?[m(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[m(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[m(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[m(e.smblsRoot,"..","..","node_modules")]:[]].filter(p=>h(p)),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 g=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{Ue(n)}catch{}return g},kt={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"}},wt=(e,t)=>{const o={};return ve(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},ve=(e,t)=>{const o=n=>{const s=n&&n.__ref;if(s&&(t(n,s),s.__children))for(const r of s.__children)o(n[r])};o(e)},xt=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let U=0;const Le=Symbol.for("brender.fetchTracked"),ue=()=>U,jt=256*1024,Et=1024*1024,Tt=new Set(["accept-language","accept"]),De={renders:0,record:null},_t=50,me={renders:0,record:null},$e=(e,t)=>{e.renders++,e.renders===1?e.record=t():e.record&&(e.record.tainted=!0);const o=e.record;return{record:o,close:()=>{e.renders--,e.record===o&&(e.record=null)}}},de=e=>e.record&&!e.record.tainted?e.record:null,vt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},ge=(e,t,o)=>{!e||e.tainted||e.failures.length>=_t||e.failures.push({url:vt(t),reason:o})},Ae=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),Lt=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=Ae(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),i={},f=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[p,c]of rt(l))g&&c==null||(i[String(p).toLowerCase()]=String(c))};return n&&f(t.headers),o&&f(o.headers),{url:s,method:r,headers:i}},Dt=(e,t,o,n)=>{if(!o||!o.ok||typeof o.clone!="function")return;const s=o.headers&&o.headers.get&&o.headers.get("content-type")||"";if(!/json|^text\//i.test(s))return;let r;try{r=o.clone()}catch{return}n(r.text().then(i=>{e.tainted||i.length>jt||e.bytes+i.length>Et||(e.bytes+=i.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:i}))},()=>{}))},$t=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[Le])return;const t=function(...o){U++;let n=!1;const s=()=>{n||(n=!0,U--)},r=c=>{U++,c.then(()=>{U--},()=>{U--})},i=de(De),f=de(me);let l=null;if(i)try{l=Lt(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!Tt.has(c)))&&(l=null)}catch{l=null}const g=()=>Ae(o[0]);let p;try{p=e.apply(this,o)}catch(c){throw ge(f,g(),c&&c.message||"failed"),s(),c}return p&&typeof p.then=="function"?p.then(c=>{l&&Dt(i,l,c,r),f&&c&&c.ok===!1&&ge(f,g(),"HTTP "+c.status),s()},c=>{ge(f,g(),c&&c.message||"network error"),s()}):s(),p};t[Le]=!0,globalThis.fetch=t},Mo=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=pt(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
+ `+st(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},At=3e4,Ct=2147483647;let Ce=Promise.resolve();const qe=()=>{},qt=e=>{const t=Ce.then(e);return Ce=t.then(qe,qe),t},Mt=e=>{if(e===void 0)return At;if(e===0||e===!1||e===1/0)return 0;if(typeof e!="number"||!(e>0))throw new TypeError(`brender: options.timeout is a number of milliseconds, or 0 / false for no limit \u2014 not ${JSON.stringify(e)}`);return Math.min(e,Ct)},It=(e,t)=>{const o=e.route||"/",n=e.pathname||o,s=n===o?o:`${n} (route ${o})`,r=new Error(`brender: the render of ${s} did not finish within ${t} ms and was abandoned, so the renders after it can run (options.timeout)`);return r.code="BRENDER_RENDER_TIMEOUT",r.route=o,r.pathname=n,r.timeout=t,r},Ot=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},Pt=e=>{let t=!1;return()=>{t||(t=!0,e())}},Me=(e,t={})=>{let o;try{o=Mt(t.timeout)}catch(n){return Promise.reject(n)}return qt(()=>Ft(e,t,o))},Ft=async(e,t,o)=>{await _e();const n={closed:!1,undo:[]},s=Bt(e,t,n);if(!o)return s;let r;const i=new Promise((f,l)=>{r=setTimeout(()=>{Ot(n),l(It(t,o))},o)});try{return await Promise.race([s,i])}finally{clearTimeout(r)}},Bt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:i,prefetch:f=!1}=t,l=s||n;$t();const g=$e(me,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await Kt(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:i,prefetch:f},g.record,o)}finally{g.close()}},Kt=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},i,f)=>{const l=()=>{if(f.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await nt(e)}catch(d){f.closed||console.warn("[brender] SSR translation fetch failed:",d.message)}l()}const{window:p,document:c}=Se(),a=c.body;p.location.pathname=o;const u=[ie("document",c),ie("location",p.location),ie("localStorage",p.localStorage),ie("sessionStorage",p.sessionStorage)].map(Pt);f.undo.push(...u);try{const{createDomqlElement:d,PRERENDER_REGISTRY_FORMATS:b}=await _e();l();const E=L(e.app||{}),x={...e.config||{}};e.polyglot&&!x.polyglot&&(x.polyglot=e.polyglot),e.fetch&&!x.fetch&&(x.fetch=e.fetch),e.router&&!x.router&&(x.router=e.router);for(const y of Jt)e[y]!=null&&x[y]==null&&(x[y]=e[y]);const w=x.polyglot?{...x.polyglot}:void 0,X=ut(o,w);X&&p.localStorage.setItem(ft(w),X),g&&w&&(w.translations={...w.translations||{},...g});const v=L(e.state||{});(g||w)&&(v.root||(v.root={}),w&&(v.root.lang=oe(v.root.lang,w)),g&&(v.root.translations={...v.root.translations||{},...g})),ht();const P={};for(const y in e){if(xt.has(y))continue;const k=e[y];P[y]=k&&typeof k=="object"?L(k):k}const A={...P,state:v,...n?{state:{...v,...n}}:{},dependencies:L(e.dependencies||{}),components:L(e.components||{}),snippets:L(e.snippets||{}),pages:L(e.pages||{}),functions:{...e.functions||{},polyglot:yt,getActiveLang:je,getLang:je},methods:e.methods||{},designSystem:L(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...x,...w?{polyglot:w}:{},document:c,window:p,parent:{node:a},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,captureDesignSystem:!0,...s||{}},G=await dt();l(),G&&(A.plugins=A.plugins||[],A.plugins.some(y=>y.name==="funcql")||A.plugins.push(G)),Re();const S=$e(De,()=>({entries:[],bytes:0,tainted:!1})),C=S.record;let j;try{j=await d(E,A),l();const y=Date.now(),k=r?2e3:50,K=ae=>new Promise(Ne=>setTimeout(Ne,ae));for(await K(Math.min(50,k));r&&!f.closed&&ue()>0&&Date.now()-y<k;)await K(10),ue()===0&&await K(Math.max(0,Math.min(50,k-(Date.now()-y))));if(l(),r&&await Gt(j,w)){l();const ae=Date.now();for(await K(Math.min(50,k));!f.closed&&ue()>0&&Date.now()-ae<k;)await K(10);l()}}finally{S.close()}const q=C&&!C.tainted&&!(x.fetch&&x.fetch.seed===!1)?C.entries.slice():[];ke(a);const W=we(j),{elementPath:M}=await import("@symbo.ls/element");l();const be=wt(j,M),Q=j?.context?.routerOptions?.contentElementKey||"content",F=j&&j[Q],Z=F&&typeof F=="object"&&F.__ref?F:void 0,ee=pe(e,t,j,j?.state,Z),B=[],I=c.head||c.querySelector("head");if(I)for(const y of I.querySelectorAll("style")){if(y.sheet&&y.sheet.cssRules)for(const k of y.sheet.cssRules)k.cssText&&B.push(k);if(!B.length){const k=y.textContent||"";k&&B.push({cssText:k})}}const J=B.map(y=>y.cssText),{globalCSS:Y,classRules:N}=Zt(B);let D=Pe(a.innerHTML);const $=j?.state?.root,te=re($,w),R=Nt(g,$);R&&(D=Oe(D,Ie(R,te,w)));const T=i&&!i.tainted?{failedRequests:i.failures.slice(),rawLabelKeys:Wt(i.labelKeys,D,te,w,$)}:null;return{html:D,metadata:ee,registry:W,brRegistry:be,element:j,emotionCSS:J,globalCSS:Y,classRules:N,fetchSeed:q,document:c,window:p,ssrTranslations:R||void 0,activeLang:te,diagnostics:T,preparedDesignSystem:A.preparedDesignSystem||null,registryFormats:Array.isArray(b)?b.slice():["paths"]}}finally{for(const d of u)d()}},ie=(e,t)=>{const o=Object.getOwnPropertyDescriptor(globalThis,e);return o&&!o.configurable?!o.writable&&!o.set?()=>{}:(globalThis[e]=t,()=>{}):(Object.defineProperty(globalThis,e,{value:t,writable:!0,configurable:!0,enumerable:!0}),()=>{o?Object.defineProperty(globalThis,e,o):delete globalThis[e]})},Gt=async(e,t)=>{if(!t||!t.fetch||!e||typeof e.call!="function")return!1;const o=e.state&&e.state.root;if(!o)return!1;const n=re(o,t),s=o.translations&&o.translations[n];if(s&&typeof s=="object"&&Object.keys(s).length||t.translations&&t.translations[n])return!1;try{await e.call("loadTranslations",n,{inFlight:!0})}catch{}return!0},he=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,ye=e=>e&&typeof e.parse=="function"?e.parse():e,Nt=(e,t)=>{const o=ye(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const i=ye(s[r]);i&&typeof i=="object"&&(n[r]={...n[r]||{},...i})}return Object.keys(n).length?n:null},Ie=(e,t,o)=>e[t]||e[oe(void 0,o)]||Object.values(e)[0]||{},Oe=(e,t)=>e.replace(he,(o,n)=>se(t,n.trim())??o),Ht=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Ut=(e,t)=>new RegExp(`(?:^|[^\\w.-])${lt(t)}(?![\\w-]|\\.\\w)`).test(e),Wt=(e,t,o,n,s)=>{const r=new Set,i=t.replace(Ht,"");for(const f of e)/\s/.test(f)||xe(f,o,n,s)===void 0&&Ut(i,f)&&r.add(f);for(const f of i.matchAll(he))r.add(f[1].trim());return[...r]},Io=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=Se(),r=s.body,{create:i}=await import("@symbo.ls/element"),f=await import("@symbo.ls/utils"),l={...kt,...o.components||{}},g={...f,...o.utils||{},...o.functions||{}};Re();let p;try{p=i(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}ke(r);const c=p?we(p):{};return{html:Pe(r.innerHTML),registry:c,element:p}},Pe=e=>e.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(t,o,n,s)=>{if(n.includes("&lt;")){const r=n.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'");return o+r+s}return t}),Jt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Yt=new Set(["auto","system"]),Fe=e=>{if(!e||typeof e!="object")return null;const t=e.config&&typeof e.config=="object"?e.config:{};if((t.themeRoot!=null?t.themeRoot:e.themeRoot)!=null)return null;const n=t.globalTheme!=null?t.globalTheme:e.globalTheme;if(typeof n!="string")return null;const s=n.trim();return!s||Yt.has(s)?null:s},zt=e=>String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),Vt=/\.[A-Za-z_\\-]/,Xt=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Vt.test(t)},Qt=/^\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 Xt(e.selectorText)?Qt.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},Zt=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Be(n);s?t[s].push(n.cssText):n&&n.cssText&&o.push(n.cssText)}return{globalCSS:{rootRule:t.root.join(`
3
3
  `),resetRules:t.reset.join(`
4
4
  `),keyframeRules:t.keyframes.join(`
5
- `),fontFaceCSS:""},classRules:o}};let ne=new Set;const vo=()=>{ne=new Set},To=()=>Array.from(ne).join(`
6
- `),Lo=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
5
+ `),fontFaceCSS:""},classRules:o}};let V=new Set;const Oo=()=>{V=new Set},Po=()=>Array.from(V).join(`
6
+ `),Fo=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
7
7
  ${t}
8
- </style>`:""),Do=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,a=await qe(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!a)return null;const d=e.designSystem||{},l=a.globalCSS;let g=null;try{const c=Ut(a.element);Object.keys(c).length&&(g=c)}catch{}const f=a.activeLang;return{html:a.html,css:a.classRules.join(`
9
- `),classRules:a.classRules.slice(),globalCSS:l,resetCss:l.resetRules,fontLinks:Fe(d),metadata:a.metadata||le(e,o),brKeyCount:a.registry?Object.keys(a.registry).length:0,brRegistry:a.brRegistry||{},fetchSeed:a.fetchSeed||[],ssrTranslations:a.ssrTranslations,prefetchedState:g,activeLang:f,diagnostics:a.diagnostics,preparedDesignSystem:a.preparedDesignSystem||null}},Ut=e=>{const t={};return Ee(e,o=>{const n=o.fetch;if(!(!n||typeof n!="object"||!o.state))for(const s of Array.isArray(n)?n:[n]){const r=s&&typeof s=="object"&&typeof s.as=="string"?s.as:null;if(!r)continue;const a=r.startsWith("~/"),d=a?r.slice(2):r,l=a?o.state.root:o.state,g=he(l&&l[d]);d&&g!=null&&typeof g!="function"&&(t[d]=g)}}),t},$o=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:a=!0,prefetch:d=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:c=Pe,preloadScripts:i=!0,nonce:p,timeout:m,stamp:b,time:S}=o,k=Vt(p),z=n||e.state?.lang||e.app?.metadata?.lang||"en",h=await qe(e,{route:t,pathname:f,prefetch:d,timeout:m});if(!h)return null;const A={...h.metadata};if(s&&(A["theme-color"]=s),typeof l=="string"&&!e.name){const R="\0brender-undeclared-title\0";if(le({...e,name:R},t,h.element,h.element?.state).title===R)for(const M of["title","og:title","twitter:title"])A[M]===h.metadata.title&&delete A[M]}const T=Ye(A);for(const R of h.classRules)R&&ne.add(R);const G=Array.from(ne).join(`
10
- `),I=h.globalCSS,O=`<style data-brender>
11
- ${I.rootRule||""}
12
- ${I.resetRules||""}
13
- ${I.keyframeRules||""}
14
- </style>`,j=G?`<style data-emotion="smbls">
15
- ${G}
16
- </style>`:"",Y=Fe(e.designSystem||{}),X=Object.keys(h.registry).length,q=Yt({brRegistry:h.brRegistry,fetchSeed:h.fetchSeed,adoptDom:g,stamp:b,time:S});let N="";if(r&&r.clientScript){const R=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,_=R>0?"../".repeat(R):"./";if(a){const M=ct({polyglot:e.polyglot||e.config?.polyglot,translations:h.ssrTranslations,lang:h.activeLang,pathname:f||t,attrs:k}),F=M?M+`
17
- `:"",U=q.blocks?q.blocks+`
18
- `:"";N=`${F}${U}<script type="module" src="${_}${r.clientScript}"${k}><\/script>`}else N=`<script type="module"${k}>
8
+ </style>`:""),Bo=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,i=await Me(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!i)return null;const f=e.designSystem||{},l=i.globalCSS;let g=null;try{const c=eo(i.element);Object.keys(c).length&&(g=c)}catch{}const p=i.activeLang;return{html:i.html,css:i.classRules.join(`
9
+ `),classRules:i.classRules.slice(),globalCSS:l,resetCss:l.resetRules,fontLinks:Ge(f),metadata:i.metadata||pe(e,o),brKeyCount:i.registry?Object.keys(i.registry).length:0,brRegistry:i.brRegistry||{},fetchSeed:i.fetchSeed||[],ssrTranslations:i.ssrTranslations,prefetchedState:g,activeLang:p,theme:Fe(e),diagnostics:i.diagnostics,preparedDesignSystem:i.preparedDesignSystem||null}},eo=e=>{const t={};return ve(e,o=>{const n=o.fetch;if(!(!n||typeof n!="object"||!o.state))for(const s of Array.isArray(n)?n:[n]){const r=s&&typeof s=="object"&&typeof s.as=="string"?s.as:null;if(!r)continue;const i=r.startsWith("~/"),f=i?r.slice(2):r,l=i?o.state.root:o.state,g=ye(l&&l[f]);f&&g!=null&&typeof g!="function"&&(t[f]=g)}}),t},Ko=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:i=!0,prefetch:f=!0,shell:l,adoptDom:g=!1,pathname:p,eagerImages:c=Ke,preloadScripts:a=!0,nonce:u,timeout:d,stamp:b,time:E,spriteIcons:x="all",cssClasses:w="all",cssVariables:X="all",registryFormat:v="paths"}=o,P=co(u),A=n||e.state?.lang||e.app?.metadata?.lang||"en",G=Fe(e),S=await Me(e,{route:t,pathname:p,prefetch:f,timeout:d});if(!S)return null;const C={...S.metadata};if(s&&(C["theme-color"]=s),typeof l=="string"&&!e.name){const R="\0brender-undeclared-title\0";if(pe({...e,name:R},t,S.element,S.element?.state).title===R)for(const y of["title","og:title","twitter:title"])C[y]===S.metadata.title&&delete C[y]}const j=Xe(C);for(const R of S.classRules)R&&V.add(R);let q=no(S.html,c);x==="page"&&(q=Ze(q,typeof l=="string"?l:"").html);const W=w==="page"?et(Array.from(V),tt(q,typeof l=="string"?l:"")).join(`
10
+ `):Array.from(V).join(`
11
+ `),M=S.globalCSS,Q=`<style data-brender>
12
+ ${(X==="page"?ot(M.rootRule||"",M.resetRules,M.keyframeRules,W,q,typeof l=="string"?l:"").css:M.rootRule)||""}
13
+ ${M.resetRules||""}
14
+ ${M.keyframeRules||""}
15
+ </style>`,F=W?`<style data-emotion="smbls">
16
+ ${W}
17
+ </style>`:"",Z=Ge(e.designSystem||{}),ee=Object.keys(S.registry).length,B=v==="tree"||v==="auto"&&(S.registryFormats||[]).includes("tree"),I=so({brRegistry:S.brRegistry,fetchSeed:S.fetchSeed,adoptDom:g,stamp:b,time:E,registryFormat:B?"tree":"paths"});let J="";if(r&&r.clientScript){const R=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,T=R>0?"../".repeat(R):"./";if(i){const y=mt({polyglot:e.polyglot||e.config?.polyglot,translations:S.ssrTranslations,lang:S.activeLang,pathname:p||t,attrs:P}),k=y?y+`
18
+ `:"",K=I.blocks?I.blocks+`
19
+ `:"";J=`${k}${K}<script type="module" src="${T}${r.clientScript}"${P}><\/script>`}else J=`<script type="module"${P}>
19
20
  {
20
21
  const brEls = document.querySelectorAll('body > :not(script):not(style)')
21
22
  const observer = new MutationObserver((mutations) => {
@@ -32,23 +33,23 @@ ${G}
32
33
  observer.observe(document.body, { childList: true })
33
34
  }
34
35
  <\/script>
35
- <script type="module" src="${_}${r.clientScript}"${k}><\/script>`}const H={...e.config||{}};e.polyglot&&!H.polyglot&&(H.polyglot=e.polyglot);const L=H.polyglot;let P=T;if(L){const R={...L.translations||{}};for(const _ in h.ssrTranslations||{})R[_]={...R[_]||{},...h.ssrTranslations[_]};P=Me(T,Ce(R,h.activeLang,L))}const C=h.diagnostics;if(C)for(const R of P.matchAll(ge)){const _=R[1].trim();C.rawLabelKeys.includes(_)||C.rawLabelKeys.push(_)}const D=zt(h.html,c);return typeof l=="string"?{html:Xe(l,{headTags:`${P}
36
- ${q.meta}`,headEnd:[Y,O,j].filter(Boolean).join(`
37
- `),body:D,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:q.blocks,lang:n||(L?h.activeLang:void 0),preloadScripts:i}),route:t,brKeyCount:X,shell:!0,diagnostics:C,preparedDesignSystem:h.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
38
- <html lang="${z}">
36
+ <script type="module" src="${T}${r.clientScript}"${P}><\/script>`}const Y={...e.config||{}};e.polyglot&&!Y.polyglot&&(Y.polyglot=e.polyglot);const N=Y.polyglot;let D=j;if(N){const R={...N.translations||{}};for(const T in S.ssrTranslations||{})R[T]={...R[T]||{},...S.ssrTranslations[T]};D=Oe(j,Ie(R,S.activeLang,N))}const $=S.diagnostics;if($)for(const R of D.matchAll(he)){const T=R[1].trim();$.rawLabelKeys.includes(T)||$.rawLabelKeys.push(T)}return typeof l=="string"?{html:Qe(l,{headTags:`${D}
37
+ ${I.meta}`,headEnd:[Z,Q,F].filter(Boolean).join(`
38
+ `),body:q,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:I.blocks,lang:n||(N?S.activeLang:void 0),theme:G,preloadScripts:a}),route:t,brKeyCount:ee,shell:!0,diagnostics:$,preparedDesignSystem:S.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
39
+ <html lang="${A}"${G?` data-theme="${zt(G)}"`:""}>
39
40
  <head>
40
- ${P}${r&&r.clientScript&&a?`
41
- ${q.meta}`:""}
42
- ${Y}
41
+ ${D}${r&&r.clientScript&&i?`
42
+ ${I.meta}`:""}
43
+ ${Z}
43
44
 
44
- ${O}
45
- ${j}
45
+ ${Q}
46
+ ${F}
46
47
  </head>
47
48
  <body>
48
- ${D}
49
- ${N}
49
+ ${q}
50
+ ${J}
50
51
  </body>
51
- </html>`,route:t,brKeyCount:X,diagnostics:C,preparedDesignSystem:h.preparedDesignSystem||null}},Pe=2,Wt=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,Jt=/\sloading\s*=/i,zt=(e,t=Pe)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(Wt,s=>n++<o||Jt.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Fe=e=>{if(!e)return"";const t=e.font_family||e.fontFamily||{},o=new Set;for(const s of Object.values(t)){if(typeof s!="string")continue;const r=s.match(/'([^']+)'/);r&&o.add(r[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(s=>`family=${s.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
52
- `):""},Yt=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s}={})=>{const r=o&&e&&Object.keys(e).length?et(e):"",a=it(t);return{meta:`<meta name="brender" content="${r?"hydrate":"replace"}">
53
- `+tt(Xt(n,s)),blocks:[r,a].filter(Boolean).join(`
54
- `)}},Xt=(e,t)=>{const o=typeof t=="number"&&isFinite(t)?Math.round(t):Date.now();return{stamp:e!=null&&e!==""?String(e):new Date(o).toISOString(),time:o}},Vt=e=>{if(e==null||e==="")return"";const t=String(e);if(!/^[A-Za-z0-9+/=_-]+$/.test(t))throw new Error("brender: a nonce is base64 or base64url text");return` nonce="${t}"`};export{Pe as EAGER_IMAGES_DEFAULT,je as SMBLS_SSR_SPECIFIER,_o as adoptionScripts,Xt as buildStamp,To as getAccumulatedEmotionCSS,zt as markLazyImages,Yt as prerenderParts,qe as render,Eo as renderElement,$o as renderPage,Do as renderRoute,Lo as replaceEmotionCSS,vo as resetGlobalCSSCache,Ht as splitCapturedCSS};
52
+ </html>`,route:t,brKeyCount:ee,diagnostics:$,preparedDesignSystem:S.preparedDesignSystem||null}},Ke=2,to=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,oo=/\sloading\s*=/i,no=(e,t=Ke)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(to,s=>n++<o||oo.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Ge=e=>{if(!e)return"";const t=e.font_family||e.fontFamily||{},o=new Set;for(const s of Object.values(t)){if(typeof s!="string")continue;const r=s.match(/'([^']+)'/);r&&o.add(r[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(s=>`family=${s.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
53
+ `):""},so=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s,registryFormat:r="paths"}={})=>{const f=o&&e&&Object.keys(e).length?r==="tree"&&it(e)||ct(e):"",l=gt(t);return{meta:`<meta name="brender" content="${f?"hydrate":"replace"}">
54
+ `+at(ro(n,s)),blocks:[f,l].filter(Boolean).join(`
55
+ `)}},ro=(e,t)=>{const o=typeof t=="number"&&isFinite(t)?Math.round(t):Date.now();return{stamp:e!=null&&e!==""?String(e):new Date(o).toISOString(),time:o}},co=e=>{if(e==null||e==="")return"";const t=String(e);if(!/^[A-Za-z0-9+/=_-]+$/.test(t))throw new Error("brender: a nonce is base64 or base64url text");return` nonce="${t}"`};export{Ke as EAGER_IMAGES_DEFAULT,Te as SMBLS_SSR_SPECIFIER,Mo as adoptionScripts,ro as buildStamp,Fe as fixedThemeOf,Po as getAccumulatedEmotionCSS,no as markLazyImages,so as prerenderParts,Me as render,Io as renderElement,Ko as renderPage,Bo as renderRoute,Fo as replaceEmotionCSS,Oo as resetGlobalCSSCache,Zt as splitCapturedCSS};
package/dist/esm/shell.js CHANGED
@@ -1,9 +1,9 @@
1
- import{insertAfterImportMap as E}from"@symbo.ls/utils";const P=/<head(\s[^>]*)?>/i,_=/<body(\s[^>]*)?>/i,f=(e,n)=>{const t=new RegExp(`\\s${n}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,"i").exec(e);return t?t[2]??t[3]??t[4]??"":null},S=e=>{const n=e.slice(0,12).toLowerCase();if(n.startsWith("<title"))return"title";if(n.startsWith("<meta")){if(f(e,"charset")!==null)return"charset";for(const t of["name","property","http-equiv","itemprop"]){const r=f(e,t);if(r!==null)return`${t}:${r.toLowerCase()}`}return null}if(n.startsWith("<link")){const t=(f(e,"rel")||"").toLowerCase();return t==="canonical"?"link:canonical":t==="icon"||t==="shortcut icon"?`link:${t}:${f(e,"href")||""}`:t==="alternate"&&f(e,"hreflang")!==null?`link:alternate:${(f(e,"hreflang")||"").toLowerCase()}`:null}return null},v=/<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi,j=/<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi,x=e=>[...e.matchAll(j)].map(n=>[n.index,n.index+n[0].length,n[0]]),L=(e,n,t=r=>r)=>{let r="",o=0;for(const[i,l,a]of x(e))r+=n(e.slice(o,i))+t(a),o=l;return r+n(e.slice(o))},d=(e,n,t=0)=>{const r=x(e),o=new RegExp(n.source,n.flags.includes("g")?n.flags:n.flags+"g");o.lastIndex=t;let i;for(;i=o.exec(e);){const l=i.index,a=r.find(([s,p])=>l>s&&l<p);if(!a)return i;o.lastIndex=a[1]}return null},q=e=>!!e&&!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(e),I=(e,n)=>e.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi,(t,r,o,i,l)=>{const a=o?i:l;if(!q(a))return t;const s=o||"";return`${r}${s}${n}${a.replace(/^\.\//,"")}${s}`}),W=(e,n)=>{if(!n)return e;const t="../".repeat(n);return L(e,r=>r.replace(/<link\b[^>]*>/gi,o=>I(o,t)),r=>/^<script\b/i.test(r)?r.replace(/^<script\b[^>]*>/i,o=>I(o,t)):r)},B=new Set(["","module","text/javascript","application/javascript"]),m=(e,n)=>new RegExp(`\\s${n}(?:\\s*=|\\s|>|/)`,"i").test(e),D=e=>{const n=[],t=new Set;for(const[,,r]of x(e)){if(!/^<script\b/i.test(r))continue;const o=r.slice(0,r.indexOf(">")+1),i=f(o,"src");if(!i||t.has(i))continue;const l=(f(o,"type")||"").trim().toLowerCase();if(!B.has(l)||m(o,"async")||m(o,"nomodule"))continue;t.add(i);const a=["crossorigin","integrity","referrerpolicy"].map(p=>{const h=f(o,p);return h===null?m(o,p)?` ${p}`:"":` ${p}="${h.replace(/"/g,"&quot;")}"`}).join(""),s=i.replace(/"/g,"&quot;");n.push(l==="module"?`<link rel="modulepreload" href="${s}" fetchpriority="high"${a}>`:`<link rel="preload" as="script" href="${s}" fetchpriority="high"${a}>`)}return n.join(`
2
- `)},H=(e,n)=>{const t=d(e,/<html(\s[^>]*)?>/i);if(!t)return e;const r=t[0],o=String(n).replace(/"/g,"&quot;"),i=f(r,"lang")!==null?r.replace(/(\slang\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`$1"${o}"`):r.replace(/^<html/i,`<html lang="${o}"`);return e.slice(0,t.index)+i+e.slice(t.index+r.length)},z=(e,{headTags:n="",headEnd:t="",body:r="",depth:o=0,bootScripts:i="",lang:l,preloadScripts:a=!1}={})=>{if(typeof e!="string"||!e.trim())throw new Error("brender: the shell document is empty");let s=W(e,o);l&&(s=H(s,l));const p=a?D(s):"";if(p){const c=E(s,p);c!==null?s=c:t=t?`${p}
3
- ${t}`:p}const h=new Set;for(const c of n.matchAll(v)){const u=S(c[0]);u&&h.add(u)}const w=c=>L(c,u=>u.replace(v,R=>h.has(S(R))?"":R)),y=n?`
4
- ${n}`:"",O=t?`
5
- ${t}
6
- `:"",g=d(s,P),b=g&&d(s,/<\/head\s*>/i,g.index);if(g&&b){const c=g.index+g[0].length,u=b.index;s=s.slice(0,c)+y+w(s.slice(c,u))+O+s.slice(u)}else{s=w(s);const c=d(s,/<html(\s[^>]*)?>/i)||d(s,/<!doctype[^>]*>/i),u=c?c.index+c[0].length:0;s=s.slice(0,u)+y+O+s.slice(u)}const k=`
1
+ import{insertAfterImportMap as _}from"@symbo.ls/utils";const j=/<head(\s[^>]*)?>/i,q=/<body(\s[^>]*)?>/i,u=(e,t)=>{const s=new RegExp(`\\s${t}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,"i").exec(e);return s?s[2]??s[3]??s[4]??"":null},v=e=>{const t=e.slice(0,12).toLowerCase();if(t.startsWith("<title"))return"title";if(t.startsWith("<meta")){if(u(e,"charset")!==null)return"charset";for(const s of["name","property","http-equiv","itemprop"]){const r=u(e,s);if(r!==null)return`${s}:${r.toLowerCase()}`}return null}if(t.startsWith("<link")){const s=(u(e,"rel")||"").toLowerCase();return s==="canonical"?"link:canonical":s==="icon"||s==="shortcut icon"?`link:${s}:${u(e,"href")||""}`:s==="alternate"&&u(e,"hreflang")!==null?`link:alternate:${(u(e,"hreflang")||"").toLowerCase()}`:null}return null},E=/<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi,W=/<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi,x=e=>[...e.matchAll(W)].map(t=>[t.index,t.index+t[0].length,t[0]]),I=(e,t,s=r=>r)=>{let r="",i=0;for(const[o,l,a]of x(e))r+=t(e.slice(i,o))+s(a),i=l;return r+t(e.slice(i))},h=(e,t,s=0)=>{const r=x(e),i=new RegExp(t.source,t.flags.includes("g")?t.flags:t.flags+"g");i.lastIndex=s;let o;for(;o=i.exec(e);){const l=o.index,a=r.find(([f,n])=>l>f&&l<n);if(!a)return o;i.lastIndex=a[1]}return null},B=e=>!!e&&!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(e),L=(e,t)=>e.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi,(s,r,i,o,l)=>{const a=i?o:l;if(!B(a))return s;const f=i||"";return`${r}${f}${t}${a.replace(/^\.\//,"")}${f}`}),D=(e,t)=>{if(!t)return e;const s="../".repeat(t);return I(e,r=>r.replace(/<link\b[^>]*>/gi,i=>L(i,s)),r=>/^<script\b/i.test(r)?r.replace(/^<script\b[^>]*>/i,i=>L(i,s)):r)},H=new Set(["","module","text/javascript","application/javascript"]),m=(e,t)=>new RegExp(`\\s${t}(?:\\s*=|\\s|>|/)`,"i").test(e),T=e=>{const t=[],s=new Set;for(const[,,r]of x(e)){if(!/^<script\b/i.test(r))continue;const i=r.slice(0,r.indexOf(">")+1),o=u(i,"src");if(!o||s.has(o))continue;const l=(u(i,"type")||"").trim().toLowerCase();if(!H.has(l)||m(i,"async")||m(i,"nomodule"))continue;s.add(o);const a=["crossorigin","integrity","referrerpolicy"].map(n=>{const d=u(i,n);return d===null?m(i,n)?` ${n}`:"":` ${n}="${d.replace(/"/g,"&quot;")}"`}).join(""),f=o.replace(/"/g,"&quot;");t.push(l==="module"?`<link rel="modulepreload" href="${f}" fetchpriority="high"${a}>`:`<link rel="preload" as="script" href="${f}" fetchpriority="high"${a}>`)}return t.join(`
2
+ `)},P=(e,t,s)=>{const r=h(e,/<html(\s[^>]*)?>/i);if(!r)return e;const i=r[0],o=String(s).replace(/&/g,"&amp;").replace(/"/g,"&quot;"),l=u(i,t)!==null?i.replace(new RegExp(`(\\s${t}\\s*=\\s*)(?:"[^"]*"|'[^']*'|[^\\s>]+)`,"i"),`$1"${o}"`):i.replace(/^<html/i,`<html ${t}="${o}"`);return e.slice(0,r.index)+l+e.slice(r.index+i.length)},K=(e,{headTags:t="",headEnd:s="",body:r="",depth:i=0,bootScripts:o="",lang:l,theme:a,preloadScripts:f=!1}={})=>{if(typeof e!="string"||!e.trim())throw new Error("brender: the shell document is empty");let n=D(e,i);l&&(n=P(n,"lang",l)),a&&(n=P(n,"data-theme",a));const d=f?T(n):"";if(d){const c=_(n,d);c!==null?n=c:s=s?`${d}
3
+ ${s}`:d}const w=new Set;for(const c of t.matchAll(E)){const p=v(c[0]);p&&w.add(p)}const y=c=>I(c,p=>p.replace(E,S=>w.has(v(S))?"":S)),O=t?`
4
+ ${t}`:"",b=s?`
5
+ ${s}
6
+ `:"",g=h(n,j),k=g&&h(n,/<\/head\s*>/i,g.index);if(g&&k){const c=g.index+g[0].length,p=k.index;n=n.slice(0,c)+O+y(n.slice(c,p))+b+n.slice(p)}else{n=y(n);const c=h(n,/<html(\s[^>]*)?>/i)||h(n,/<!doctype[^>]*>/i),p=c?c.index+c[0].length:0;n=n.slice(0,p)+O+b+n.slice(p)}const A=`
7
7
  ${r}
8
- ${i?i+`
9
- `:""}`,$=d(s,_);if($){const c=$.index+$[0].length;return s.slice(0,c)+k+s.slice(c)}const A=d(s,/<\/(?:body|html)\s*>/i),C=A?A.index:s.length;return s.slice(0,C)+k+s.slice(C)};export{z as composeIntoShell,S as headTagKey,D as scriptPreloads};
8
+ ${o?o+`
9
+ `:""}`,$=h(n,q);if($){const c=$.index+$[0].length;return n.slice(0,c)+A+n.slice(c)}const C=h(n,/<\/(?:body|html)\s*>/i),R=C?C.index:n.length;return n.slice(0,R)+A+n.slice(R)};export{K as composeIntoShell,v as headTagKey,T as scriptPreloads};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/brender",
3
- "version": "3.14.669",
3
+ "version": "3.14.671",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -48,16 +48,16 @@
48
48
  "dependencies": {
49
49
  "@symbo.ls/css": "^3.14.782",
50
50
  "@symbo.ls/default-config": "^3.14.789",
51
- "@symbo.ls/element": "^3.14.809",
51
+ "@symbo.ls/element": "^3.14.813",
52
52
  "@symbo.ls/fetch": "^3.14.669",
53
53
  "@symbo.ls/helmet": "^3.14.671",
54
- "@symbo.ls/scratch": "^3.14.792",
55
- "@symbo.ls/utils": "^3.14.845",
54
+ "@symbo.ls/scratch": "^3.14.795",
55
+ "@symbo.ls/utils": "^3.14.848",
56
56
  "esbuild": "^0.28.1",
57
57
  "linkedom": "^0.16.8"
58
58
  },
59
59
  "peerDependencies": {
60
- "smbls": "^3.14.844"
60
+ "smbls": "^3.14.848"
61
61
  },
62
62
  "optionalDependencies": {
63
63
  "@symbo.ls/funcql": "^3.14.0"
package/pageWeight.js ADDED
@@ -0,0 +1,284 @@
1
+ // ── The weight of a page built ahead (XMA round 8) ─────────────────────────
2
+ // Every page `smbls build` writes carried the WHOLE project's share of three
3
+ // things the page itself never reads before the app boots: the icon sprite
4
+ // (every icon of the design system — 66 on XMA, 10 on its home), the atomic
5
+ // rules of every page rendered before it in the build, and the design
6
+ // system's every CSS variable. The client needs none of them from the page:
7
+ // its design-system init fills the sprite with every icon (smbls init.js
8
+ // applyDesignSystem → scratch appendSvgIconsSprite, which adds the symbols a
9
+ // sprite lacks), its atomic engine inserts the rules of every element it
10
+ // creates into its own sheets, and it injects every variable itself — the
11
+ // server's styles go once the hydration window closes (smbls prerender.js
12
+ // removeServerStyles). So what a page must carry is what it paints before
13
+ // the boot: the icons its markup uses, the rules of the classes in its
14
+ // markup, the variables its CSS reads. Each trim below keeps that and drops
15
+ // the rest. Pure string work on the page's own serialized output; anything
16
+ // the parser does not recognise is left as it is (over-keeping costs bytes,
17
+ // under-keeping would cost a page).
18
+
19
+ // ── The icon sprite (E6) ────────────────────────────────────────────────────
20
+
21
+ // scratch createSVGSpriteElement: `<svg … id="svgSprite" …>` holding one
22
+ // `<symbol id>` per icon. Symbols never nest, so each runs to its own
23
+ // `</symbol>`.
24
+ const SPRITE_OPEN = /<svg\b[^>]*\sid="svgSprite"[^>]*>/
25
+ const SYMBOL_AT = /\s*<symbol\b[^>]*>[\s\S]*?<\/symbol>/y
26
+ const SPRITE_CLOSE_AT = /\s*<\/svg>/y
27
+ const SYMBOL_ID = /^\s*<symbol\b[^>]*?\sid="([^"]+)"/
28
+ // A fragment reference — `href="#x"`, `xlink:href="#x"`, `url(#x)` — or any
29
+ // `#token`: a hex colour that happens to spell an icon's name keeps it, which
30
+ // is the safe direction.
31
+ const FRAGMENT = /#([^\s"'()<>&,;#]+)/g
32
+
33
+ const fragmentsIn = (text, into) => {
34
+ if (typeof text !== 'string' || !text) return into
35
+ FRAGMENT.lastIndex = 0
36
+ let m
37
+ while ((m = FRAGMENT.exec(text))) into.add(m[1])
38
+ return into
39
+ }
40
+
41
+ /**
42
+ * The page's icon sprite with only the symbols the page references (and the
43
+ * symbols those reference). The client's design-system init adds every other
44
+ * icon of the design system to the same sprite before the app is created, so
45
+ * an icon the page shows later (after a click, on new data) is there when it
46
+ * renders. Returns `html` unchanged when it has no sprite or the sprite holds
47
+ * anything but symbols.
48
+ * @param {string} html - the page's body markup
49
+ * @param {string} [context] - other markup of the same document (the shell)
50
+ * @returns {{ html: string, kept: number, dropped: number }}
51
+ */
52
+ export const trimSpriteToPage = (html, context = '') => {
53
+ const none = { html, kept: 0, dropped: 0 }
54
+ if (typeof html !== 'string') return none
55
+ const open = SPRITE_OPEN.exec(html)
56
+ if (!open) return none
57
+ const start = open.index
58
+ let at = start + open[0].length
59
+ const symbols = []
60
+ for (;;) {
61
+ SYMBOL_AT.lastIndex = at
62
+ const m = SYMBOL_AT.exec(html)
63
+ if (!m) break
64
+ const id = SYMBOL_ID.exec(m[0])
65
+ symbols.push({ text: m[0], id: id ? id[1] : null })
66
+ at = SYMBOL_AT.lastIndex
67
+ }
68
+ SPRITE_CLOSE_AT.lastIndex = at
69
+ const close = SPRITE_CLOSE_AT.exec(html)
70
+ // Something other than a symbol inside the sprite: left alone.
71
+ if (!close) return none
72
+ const end = SPRITE_CLOSE_AT.lastIndex
73
+
74
+ const outside = html.slice(0, start) + html.slice(end)
75
+ const wanted = fragmentsIn(context, fragmentsIn(outside, new Set()))
76
+ // A kept symbol can use another one: keep what it references too.
77
+ const keep = new Set()
78
+ const byId = new Map()
79
+ for (const s of symbols) if (s.id) byId.set(s.id, s)
80
+ const queue = [...wanted]
81
+ while (queue.length) {
82
+ const id = queue.pop()
83
+ const s = byId.get(id)
84
+ if (!s || keep.has(s)) continue
85
+ keep.add(s)
86
+ for (const ref of fragmentsIn(s.text, new Set())) if (byId.has(ref)) queue.push(ref)
87
+ }
88
+ // An anonymous symbol is kept: nothing can name it, but nothing proves it
89
+ // unused either.
90
+ const kept = symbols.filter((s) => !s.id || keep.has(s))
91
+ if (kept.length === symbols.length) return { html, kept: kept.length, dropped: 0 }
92
+ const sprite = open[0] + kept.map((s) => s.text).join('') + html.slice(at, end)
93
+ return { html: html.slice(0, start) + sprite + html.slice(end), kept: kept.length, dropped: symbols.length - kept.length }
94
+ }
95
+
96
+ // ── The atomic rules (E12) ──────────────────────────────────────────────────
97
+
98
+ // Quoted strings and attribute brackets, masked before a selector is read:
99
+ // a dot inside `[href$=".pdf"]` is not a class (render.js isGlobalSelector).
100
+ const maskSelector = (text) => text
101
+ .replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, '""')
102
+ .replace(/\[[^\]]*\]/g, '[]')
103
+ const CLASS_TOKEN = /\.(-?(?:[_A-Za-z -￿]|\\.)(?:[\w -￿-]|\\.)*)/g
104
+ const unescapeClass = (name) => name.replace(/\\(?:([0-9a-fA-F]{1,6})\s?|(.))/g, (m, hex, ch) => (hex ? String.fromCodePoint(parseInt(hex, 16)) : ch))
105
+
106
+ // The preludes of a CSS text — every selector list and at-rule prelude in
107
+ // front of a `{` — at any depth; declaration blocks are skipped.
108
+ const preludesOf = (css) => {
109
+ const out = []
110
+ let start = 0
111
+ let quote = null
112
+ for (let i = 0; i < css.length; i++) {
113
+ const ch = css[i]
114
+ if (quote) {
115
+ if (ch === '\\') i++
116
+ else if (ch === quote) quote = null
117
+ continue
118
+ }
119
+ if (ch === '"' || ch === "'") quote = ch
120
+ else if (ch === '{') {
121
+ out.push(css.slice(start, i).trim())
122
+ start = i + 1
123
+ } else if (ch === '}' || ch === ';') start = i + 1
124
+ }
125
+ return out
126
+ }
127
+
128
+ /**
129
+ * The class names a CSS rule's selectors name (at-rule preludes aside).
130
+ * @param {string} cssText
131
+ * @returns {string[]}
132
+ */
133
+ export const ruleClasses = (cssText) => {
134
+ const names = []
135
+ for (const prelude of preludesOf(String(cssText || ''))) {
136
+ if (!prelude || prelude[0] === '@') continue
137
+ CLASS_TOKEN.lastIndex = 0
138
+ const masked = maskSelector(prelude)
139
+ let m
140
+ while ((m = CLASS_TOKEN.exec(masked))) names.push(unescapeClass(m[1]))
141
+ }
142
+ return names
143
+ }
144
+
145
+ const CLASS_ATTR = /\sclass\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/gi
146
+
147
+ /**
148
+ * Every class name the markup's `class` attributes carry.
149
+ * @param {...string} texts
150
+ * @returns {Set<string>}
151
+ */
152
+ export const markupClasses = (...texts) => {
153
+ const out = new Set()
154
+ for (const text of texts) {
155
+ if (typeof text !== 'string' || !text) continue
156
+ CLASS_ATTR.lastIndex = 0
157
+ let m
158
+ while ((m = CLASS_ATTR.exec(text))) {
159
+ const value = m[1] ?? m[2] ?? m[3] ?? ''
160
+ for (const name of value.split(/\s+/)) if (name) out.add(name.replace(/&amp;/g, '&'))
161
+ }
162
+ }
163
+ return out
164
+ }
165
+
166
+ /**
167
+ * The atomic rules a page's markup can match: each rule that names a class
168
+ * the page carries, and each rule that names none (an @font-face). The
169
+ * client's atomic engine writes the rules of every element it creates into
170
+ * its own sheets (an element created after the boot — a click, new data —
171
+ * gets its rules there), and the server's sheet goes when the hydration
172
+ * window closes, so the page only needs what styles its own markup until
173
+ * then. Order is kept: the cascade between kept rules does not change.
174
+ * @param {string[]} rules - cssText of each rule (renderPage's accumulated set)
175
+ * @param {Set<string>} classes - markupClasses of the page
176
+ * @returns {string[]}
177
+ */
178
+ export const classRulesForPage = (rules, classes) => {
179
+ const out = []
180
+ for (const rule of rules || []) {
181
+ if (!rule) continue
182
+ const names = ruleClasses(rule)
183
+ if (!names.length || names.some((name) => classes.has(name))) out.push(rule)
184
+ }
185
+ return out
186
+ }
187
+
188
+ // ── The CSS variables (E7) ──────────────────────────────────────────────────
189
+
190
+ const VAR_REF = /var\(\s*(--[\w-]+)/g
191
+ const CUSTOM_DECLARATION = /^\s*(--[\w-]+)\s*:([\s\S]*)$/
192
+
193
+ const varRefsIn = (text, into) => {
194
+ if (typeof text !== 'string' || !text) return into
195
+ VAR_REF.lastIndex = 0
196
+ let m
197
+ while ((m = VAR_REF.exec(text))) into.add(m[1])
198
+ return into
199
+ }
200
+
201
+ // Walk a CSS text and hand each declaration (the text between `{`/`;` and
202
+ // `;`/`}` inside a block) to `onDeclaration`, which returns what to write in
203
+ // its place. Preludes, braces and everything else are copied as they are.
204
+ // Strings and parentheses (a `;` inside `url(data:…;base64,…)`) are skipped.
205
+ const mapDeclarations = (css, onDeclaration) => {
206
+ let out = ''
207
+ let start = 0
208
+ let quote = null
209
+ let parens = 0
210
+ for (let i = 0; i < css.length; i++) {
211
+ const ch = css[i]
212
+ if (quote) {
213
+ if (ch === '\\') i++
214
+ else if (ch === quote) quote = null
215
+ continue
216
+ }
217
+ if (ch === '"' || ch === "'") quote = ch
218
+ else if (ch === '(') parens++
219
+ else if (ch === ')') parens = Math.max(0, parens - 1)
220
+ else if (parens) continue
221
+ else if (ch === '{') {
222
+ out += css.slice(start, i + 1)
223
+ start = i + 1
224
+ } else if (ch === ';') {
225
+ out += onDeclaration(css.slice(start, i), ';')
226
+ start = i + 1
227
+ } else if (ch === '}') {
228
+ out += onDeclaration(css.slice(start, i), '') + '}'
229
+ start = i + 1
230
+ }
231
+ }
232
+ return out + css.slice(start)
233
+ }
234
+
235
+ /**
236
+ * The page's variable blocks (`:root`, the `[data-theme]` / skin blocks, the
237
+ * `prefers-color-scheme` fallbacks) with only the custom properties the page
238
+ * reads: the ones its CSS and markup name in `var(--…)`, and the ones those
239
+ * name in turn, in every block that defines them. Every other declaration
240
+ * stays. The client injects every variable of the design system as it boots,
241
+ * before the server's styles go (smbls prerender.js), so an element created
242
+ * later still resolves the ones dropped here — but a stylesheet the page
243
+ * links (public CSS, a CDN) is not seen, which is why `smbls build` only
244
+ * does this when the project asks (`brender.cssVariables: 'page'`).
245
+ * @param {string} rootCss - the variable blocks (globalCSS.rootRule)
246
+ * @param {...string} readers - every other text of the page: its other CSS,
247
+ * its markup, the shell
248
+ * @returns {{ css: string, kept: number, dropped: number }}
249
+ */
250
+ export const trimVariablesToPage = (rootCss, ...readers) => {
251
+ if (typeof rootCss !== 'string' || !rootCss) return { css: rootCss, kept: 0, dropped: 0 }
252
+ const values = new Map()
253
+ const wanted = new Set()
254
+ mapDeclarations(rootCss, (text) => {
255
+ const m = CUSTOM_DECLARATION.exec(text)
256
+ if (m) {
257
+ const list = values.get(m[1]) || []
258
+ list.push(m[2])
259
+ values.set(m[1], list)
260
+ } else varRefsIn(text, wanted)
261
+ return ''
262
+ })
263
+ for (const text of readers) varRefsIn(text, wanted)
264
+ const keep = new Set()
265
+ const queue = [...wanted]
266
+ while (queue.length) {
267
+ const name = queue.pop()
268
+ if (keep.has(name) || !values.has(name)) continue
269
+ keep.add(name)
270
+ for (const value of values.get(name)) for (const ref of varRefsIn(value, new Set())) queue.push(ref)
271
+ }
272
+ let kept = 0
273
+ let dropped = 0
274
+ const css = mapDeclarations(rootCss, (text, end) => {
275
+ const m = CUSTOM_DECLARATION.exec(text)
276
+ if (!m || keep.has(m[1])) {
277
+ if (m) kept++
278
+ return text + end
279
+ }
280
+ dropped++
281
+ return ''
282
+ })
283
+ return { css, kept, dropped }
284
+ }
package/render.js CHANGED
@@ -10,10 +10,11 @@ import { resetKeys, assignKeys, mapKeysToElements } from './keys.js'
10
10
  import { extractMetadata, generateHeadHtml } from './metadata.js'
11
11
  import { hydrate } from './hydrate.js'
12
12
  import { composeIntoShell } from './shell.js'
13
+ import { trimSpriteToPage, classRulesForPage, markupClasses, trimVariablesToPage } from './pageWeight.js'
13
14
  import { fetchSSRTranslations } from './prefetch.js'
14
15
  import { createEarlySeedScript } from '@symbo.ls/fetch'
15
16
  import { headerEntries } from '@symbo.ls/fetch/rest'
16
- import { brenderRegistryBlock, brenderStampMeta, escapeRegExp, jsonForScript, polyglotFallbackLang, polyglotLangStorageKey, polyglotAddressLang, polyglotStartupScript } from '@symbo.ls/utils'
17
+ import { brenderRegistryBlock, brenderRegistryTreeBlock, brenderStampMeta, escapeRegExp, jsonForScript, polyglotFallbackLang, polyglotLangStorageKey, polyglotAddressLang, polyglotStartupScript } from '@symbo.ls/utils'
17
18
 
18
19
  // funcql plugin — enables evaluation of funcql schemas as property values
19
20
  // during SSR. Async-imported to avoid polluting non-funcql codepaths.
@@ -1141,7 +1142,7 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1141
1142
  // Import createDomqlElement via bundled smbls source.
1142
1143
  // The smbls monorepo uses extensionless/directory imports that Node.js ESM
1143
1144
  // can't resolve natively, so we bundle it with esbuild first.
1144
- const { createDomqlElement } = await bundleCreateDomql()
1145
+ const { createDomqlElement, PRERENDER_REGISTRY_FORMATS } = await bundleCreateDomql()
1145
1146
  live()
1146
1147
 
1147
1148
  const app = structuredCloneDeep(data.app || {})
@@ -1403,7 +1404,10 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1403
1404
  }
1404
1405
  : null
1405
1406
 
1406
- return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics, preparedDesignSystem: ctx.preparedDesignSystem || null }
1407
+ return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics, preparedDesignSystem: ctx.preparedDesignSystem || null,
1408
+ // the registry encodings the client of this smbls reads (smbls
1409
+ // prerender.js PRERENDER_REGISTRY_FORMATS); an older smbls reads paths
1410
+ registryFormats: Array.isArray(PRERENDER_REGISTRY_FORMATS) ? PRERENDER_REGISTRY_FORMATS.slice() : ['paths'] }
1407
1411
  } finally {
1408
1412
  for (const restore of restoreGlobals) restore()
1409
1413
  }
@@ -1601,6 +1605,37 @@ const SCRATCH_CONFIG_FLAGS = [
1601
1605
  'useThemeSuffixedVars', 'verbose', 'semanticIcons',
1602
1606
  ]
1603
1607
 
1608
+ // The theme a built page's `<html data-theme>` carries — the project's
1609
+ // `globalTheme` when it is a FIXED theme ('light', 'dark' or any named
1610
+ // scheme), else null. The global CSS keys every scheme on `[data-theme=…]`
1611
+ // and falls back to `@media (prefers-color-scheme) { :root:not([data-theme]) }`,
1612
+ // so a page with no attribute painted the OS scheme until the client's boot
1613
+ // (smbls resolveAndApplyTheme, scratch applyThemeRoot) stamped the project's
1614
+ // one: on a `globalTheme: 'light'` project, a visitor in OS dark mode saw the
1615
+ // dark tokens from first paint to boot (xma.info: white ink on the white
1616
+ // header, 237–771 ms). The client writes the same value at boot, so the
1617
+ // attribute is never removed or changed on adoption.
1618
+ //
1619
+ // 'auto' (and 'system', unset) keeps the OS fallback: the client resolves it
1620
+ // from prefers-color-scheme, or from a choice stored under `themeStorageKey`
1621
+ // — which smbls consults ONLY for 'auto', so a fixed theme has no stored
1622
+ // choice that could differ. A config with its own `themeRoot` stamps that
1623
+ // node, not `<html>`, so it gets none.
1624
+ const UNFORCED_THEMES = new Set(['auto', 'system'])
1625
+ export const fixedThemeOf = (data) => {
1626
+ if (!data || typeof data !== 'object') return null
1627
+ const config = data.config && typeof data.config === 'object' ? data.config : {}
1628
+ const themeRoot = config.themeRoot != null ? config.themeRoot : data.themeRoot
1629
+ if (themeRoot != null) return null
1630
+ const theme = config.globalTheme != null ? config.globalTheme : data.globalTheme
1631
+ if (typeof theme !== 'string') return null
1632
+ const value = theme.trim()
1633
+ if (!value || UNFORCED_THEMES.has(value)) return null
1634
+ return value
1635
+ }
1636
+
1637
+ const escapeAttr = (value) => String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;')
1638
+
1604
1639
  // ── Global CSS: the render's own capture, split ────────────────────────────
1605
1640
  // The rules smbls init injects into the virtual <head> during render() ARE
1606
1641
  // the page's global CSS — the :root variables, scheme and skin blocks, the
@@ -1761,6 +1796,9 @@ export const renderRoute = async (data, options = {}) => {
1761
1796
  ssrTranslations: result.ssrTranslations,
1762
1797
  prefetchedState,
1763
1798
  activeLang,
1799
+ // the `<html data-theme>` a page of this render carries (fixedThemeOf);
1800
+ // null when the project follows the OS
1801
+ theme: fixedThemeOf(data),
1764
1802
  // failed requests and raw label keys (see `DIAG`); null when an
1765
1803
  // overlapping render made them unknowable
1766
1804
  diagnostics: result.diagnostics,
@@ -1835,14 +1873,33 @@ const collectFetchedState = (element) => {
1835
1873
  * @param {number} [options.depth] - Directory depth of the output file: rebases
1836
1874
  * the shell's relative asset URLs (with `shell`, default 0) and the ISR
1837
1875
  * client script path (default: derived from the route).
1876
+ * @param {'all'|'page'} [options.spriteIcons='all'] - 'page': the icon sprite
1877
+ * keeps only the icons the page references (pageWeight.js
1878
+ * trimSpriteToPage) — for a page whose client boots with the design
1879
+ * system's icons (`smbls build`); 'all' keeps every icon
1880
+ * @param {'all'|'page'} [options.cssClasses='all'] - 'page': the atomic rules
1881
+ * are the ones the page's markup can match (pageWeight.js
1882
+ * classRulesForPage) instead of every rule the pages rendered so far in
1883
+ * this process needed
1884
+ * @param {'all'|'page'} [options.cssVariables='all'] - 'page': the variable
1885
+ * blocks keep the custom properties the page's CSS and markup read
1886
+ * (pageWeight.js trimVariablesToPage) — a stylesheet the page links is
1887
+ * not read, so it is the project's choice (`brender.cssVariables`)
1888
+ * @param {'paths'|'tree'|'auto'} [options.registryFormat='paths'] - with
1889
+ * `adoptDom`: the element-path registry as paths, as a tree (the same data
1890
+ * in a third of the bytes — @symbo.ls/utils encodeBrenderRegistry), or the
1891
+ * tree when the smbls the page renders with reads it
1838
1892
  * @returns {Promise<{ html: string, route: string, brKeyCount: number, shell?: true }>}
1839
1893
  */
1840
1894
  export const renderPage = async (data, route = '/', options = {}) => {
1841
- const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout, stamp, time } = options
1895
+ const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout, stamp, time, spriteIcons = 'all', cssClasses = 'all', cssVariables = 'all', registryFormat = 'paths' } = options
1842
1896
  const nonceAttr = nonceAttribute(nonce)
1843
1897
 
1844
1898
  // Detect lang from project config, app metadata, or default
1845
1899
  const htmlLang = lang || data.state?.lang || data.app?.metadata?.lang || 'en'
1900
+ // The project's fixed theme on <html>, so the first paint uses its tokens
1901
+ // and not the OS scheme's (see fixedThemeOf).
1902
+ const htmlTheme = fixedThemeOf(data)
1846
1903
 
1847
1904
  // Use the full smbls pipeline for rendering. `pathname` is the address the
1848
1905
  // page is rendered at — the router matches it, location and every
@@ -1880,12 +1937,32 @@ export const renderPage = async (data, route = '/', options = {}) => {
1880
1937
  for (const rule of result.classRules) {
1881
1938
  if (rule) _accumulatedEmotionCSS.add(rule)
1882
1939
  }
1883
- const emotionCSS = Array.from(_accumulatedEmotionCSS).join('\n')
1940
+
1941
+ // Images past the first `eagerImages` load lazily unless they say how to
1942
+ // load (see markLazyImages).
1943
+ let bodyHtml = markLazyImages(result.html, eagerImages)
1944
+ // `spriteIcons: 'page'` — the page's own icons only (pageWeight.js, XMA
1945
+ // round 8, E6): the client adds the rest of the design system's icons to
1946
+ // the same sprite when it boots, before any of them can be rendered.
1947
+ if (spriteIcons === 'page') bodyHtml = trimSpriteToPage(bodyHtml, typeof shell === 'string' ? shell : '').html
1948
+
1949
+ // `cssClasses: 'page'` — of the rules every page so far needed, the ones
1950
+ // this page's markup can match (pageWeight.js classRulesForPage, XMA round
1951
+ // 8, E12): the client writes the rules of each element it creates itself.
1952
+ const emotionCSS = cssClasses === 'page'
1953
+ ? classRulesForPage(Array.from(_accumulatedEmotionCSS), markupClasses(bodyHtml, typeof shell === 'string' ? shell : '')).join('\n')
1954
+ : Array.from(_accumulatedEmotionCSS).join('\n')
1884
1955
 
1885
1956
  // Global CSS (variables, reset, keyframes): this render's own init output.
1886
1957
  // The style tags, built once for both documents below.
1887
1958
  const globalCSS = result.globalCSS
1888
- const brenderStyle = `<style data-brender>\n${globalCSS.rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`
1959
+ // `cssVariables: 'page'` — the custom properties this page's CSS and
1960
+ // markup read (pageWeight.js trimVariablesToPage, XMA round 8, E7); the
1961
+ // client injects every variable itself as it boots.
1962
+ const rootRule = cssVariables === 'page'
1963
+ ? trimVariablesToPage(globalCSS.rootRule || '', globalCSS.resetRules, globalCSS.keyframeRules, emotionCSS, bodyHtml, typeof shell === 'string' ? shell : '').css
1964
+ : globalCSS.rootRule
1965
+ const brenderStyle = `<style data-brender>\n${rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`
1889
1966
  const emotionStyle = emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''
1890
1967
 
1891
1968
  // Generate font links from design system
@@ -1897,7 +1974,10 @@ export const renderPage = async (data, route = '/', options = {}) => {
1897
1974
  // and the data blocks after the markup — the registry under `adoptDom`,
1898
1975
  // and the recorded anonymous answers, so the client renders from the data
1899
1976
  // the server used instead of asking for all of it again.
1900
- const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time })
1977
+ // `registryFormat: 'auto'` — the tree when the smbls this page renders
1978
+ // with (the page's own client) reads it, else paths (XMA round 8, E13).
1979
+ const treeRegistry = registryFormat === 'tree' || (registryFormat === 'auto' && (result.registryFormats || []).includes('tree'))
1980
+ const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time, registryFormat: treeRegistry ? 'tree' : 'paths' })
1901
1981
 
1902
1982
  // ISR: include client SPA bundle for hydration + data fetching
1903
1983
  let isrBody = ''
@@ -1984,9 +2064,6 @@ export const renderPage = async (data, route = '/', options = {}) => {
1984
2064
  }
1985
2065
  }
1986
2066
 
1987
- // Images past the first `eagerImages` load lazily unless they say how to
1988
- // load (see markLazyImages).
1989
- const bodyHtml = markLazyImages(result.html, eagerImages)
1990
2067
 
1991
2068
  // Build path: render INTO the app's own built document (shell.js) — the
1992
2069
  // standalone document below carries no app code unless `isr` adds a
@@ -2003,6 +2080,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
2003
2080
  // boot — a page rendered for /en/… kept the shell's `lang="ka"`.
2004
2081
  // Only when the project has languages (or the caller names one).
2005
2082
  lang: lang || (polyglotCfg ? result.activeLang : undefined),
2083
+ theme: htmlTheme,
2006
2084
  preloadScripts
2007
2085
  })
2008
2086
  return { html, route, brKeyCount, shell: true, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
@@ -2011,7 +2089,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
2011
2089
  // (The empty line after the font links held a @font-face <style> that
2012
2090
  // splitCapturedCSS never fills — @font-face stays in the class stream.)
2013
2091
  const html = `<!DOCTYPE html>
2014
- <html lang="${htmlLang}">
2092
+ <html lang="${htmlLang}"${htmlTheme ? ` data-theme="${escapeAttr(htmlTheme)}"` : ''}>
2015
2093
  <head>
2016
2094
  ${resolvedHeadTags}${isr && isr.clientScript && hydrate ? `\n${prerender.meta}` : ''}
2017
2095
  ${fontLinks}
@@ -2100,9 +2178,13 @@ const generateFontLinks = (ds) => {
2100
2178
  // (@symbo.ls/fetch early.js "Stale pages"), by app code
2101
2179
  // (`el.context.prerender`, smbls prerender.js) and by a host that compares
2102
2180
  // it with its data's last change.
2103
- export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time } = {}) => {
2104
- const registry = adoptDom && brRegistry && Object.keys(brRegistry).length
2105
- ? brenderRegistryBlock(brRegistry)
2181
+ // `registryFormat: 'tree'` writes the registry as a tree (@symbo.ls/utils
2182
+ // encodeBrenderRegistry, XMA round 8, E13) — the same data, a third of the
2183
+ // bytes — in its own block; a registry that cannot be one stays paths.
2184
+ export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time, registryFormat = 'paths' } = {}) => {
2185
+ const has = adoptDom && brRegistry && Object.keys(brRegistry).length
2186
+ const registry = has
2187
+ ? (registryFormat === 'tree' && brenderRegistryTreeBlock(brRegistry)) || brenderRegistryBlock(brRegistry)
2106
2188
  : ''
2107
2189
  const seed = createEarlySeedBlock(fetchSeed)
2108
2190
  return {
package/shell.js CHANGED
@@ -168,15 +168,16 @@ export const scriptPreloads = (html) => {
168
168
  return links.join('\n')
169
169
  }
170
170
 
171
- // `<html lang>` set to the page's language (kept when it already says so).
172
- const setHtmlLang = (html, lang) => {
171
+ // One attribute on `<html>` set to `value` — replaced when the shell already
172
+ // carries it, added otherwise.
173
+ const setHtmlAttr = (html, name, value) => {
173
174
  const htmlOpen = findOutside(html, /<html(\s[^>]*)?>/i)
174
175
  if (!htmlOpen) return html
175
176
  const tag = htmlOpen[0]
176
- const value = String(lang).replace(/"/g, '&quot;')
177
- const next = attr(tag, 'lang') !== null
178
- ? tag.replace(/(\slang\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s>]+)/i, `$1"${value}"`)
179
- : tag.replace(/^<html/i, `<html lang="${value}"`)
177
+ const escaped = String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;')
178
+ const next = attr(tag, name) !== null
179
+ ? tag.replace(new RegExp(`(\\s${name}\\s*=\\s*)(?:"[^"]*"|'[^']*'|[^\\s>]+)`, 'i'), `$1"${escaped}"`)
180
+ : tag.replace(/^<html/i, `<html ${name}="${escaped}"`)
180
181
  return html.slice(0, htmlOpen.index) + next + html.slice(htmlOpen.index + tag.length)
181
182
  }
182
183
 
@@ -191,16 +192,19 @@ const setHtmlLang = (html, lang) => {
191
192
  * markup (the hydration registry and the fetch seed — renderPage
192
193
  * prerenderParts)
193
194
  * @param {string} [parts.lang] - the page's language, written to `<html lang>`
195
+ * @param {string} [parts.theme] - the project's fixed theme, written to
196
+ * `<html data-theme>` (see render.js fixedThemeOf); none for 'auto'
194
197
  * @param {boolean} [parts.preloadScripts=false] - preload the shell's own
195
198
  * scripts with high priority (see scriptPreloads)
196
199
  * @returns {string}
197
200
  */
198
- export const composeIntoShell = (shell, { headTags = '', headEnd = '', body = '', depth = 0, bootScripts = '', lang, preloadScripts = false } = {}) => {
201
+ export const composeIntoShell = (shell, { headTags = '', headEnd = '', body = '', depth = 0, bootScripts = '', lang, theme, preloadScripts = false } = {}) => {
199
202
  if (typeof shell !== 'string' || !shell.trim()) {
200
203
  throw new Error('brender: the shell document is empty')
201
204
  }
202
205
  let html = rebaseAssets(shell, depth)
203
- if (lang) html = setHtmlLang(html, lang)
206
+ if (lang) html = setHtmlAttr(html, 'lang', lang)
207
+ if (theme) html = setHtmlAttr(html, 'data-theme', theme)
204
208
  // The app's preloads go right after the shell's import map when it has one
205
209
  // (parcel's page does): a modulepreload before it gets the map rejected,
206
210
  // and the app's lazy bundles resolve through it. Else first in what the