@symbo.ls/brender 3.14.670 → 3.14.672

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,61 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.672
4
+
5
+ ### Patch Changes
6
+
7
+ - 77f0e31: Release note (XMA, round 8 E13): the element-path registry of a built page has a new tag. A step after `smbls build` that looks for the old tag does not find it.
8
+
9
+ - Old tag: `<script type="application/json" data-brender="registry">`. Its content is an object, element path → `br-<n>`.
10
+ - New tag, written by `smbls build` (`renderPage({ registryFormat: 'auto' })`) when the page's own smbls reads it (smbls 3.14.848 or later, cli 3.14.814 or later): `<script type="application/json" data-brender="registry-tree">`. Its content is the same registry as a tree, `{ "f": "tree", "e": [...] }` (`@symbo.ls/utils` `encodeBrenderRegistry` / `decodeBrenderRegistry`). It is about a third of the bytes.
11
+ - A registry that cannot be a tree (a value that is not `br-<n>`) still ships in the old tag. `renderPage` called directly still writes the old tag by default (`registryFormat: 'paths'`).
12
+ - To keep the old tag in `smbls build`, set `"brender": { "registryFormat": "paths" }` in `symbols.json`.
13
+
14
+ How a post-build step finds the end of the page markup:
15
+
16
+ - The markup is inserted right after `<body>`. The page's data blocks come directly after it, then the shell's own body content.
17
+ - The first data block after the markup is one of these: `<script type="application/json" data-brender="registry">`, `<script type="application/json" data-brender="registry-tree">`, or `<script type="application/json" data-fetch-seed>` (the recorded answers). A page with `brender.hydrate: false` has no registry block. A page that made no anonymous request has no fetch-seed block.
18
+ - Match all three, not one full tag. For example: `/<script type="application\/json" data-(?:brender="registry(?:-tree)?"|fetch-seed\b)/`. Do not match the full old tag text.
19
+ - When none of them is there, the markup ends where the shell's own body content starts (its first `<script`). Another way: read the elements that carry `data-br`. Every element of the markup carries one.
20
+
21
+ - 75fc737: Round 8, XMA ("on a slow desktop, data holds still"): the text of a page built ahead no longer falls back to the system font while the app takes the page over.
22
+
23
+ - Cause: Chrome makes new, unloaded `FontFace` objects when the style sheet that holds a page's `@font-face` rules changes. The takeover changed it twice. First, smbls inserted the framework's default `SourceSans` face, a face with no file (`src: ;`). Second, it removed the server's styles and copied their faces into a new `<style data-smbls-faces>`. Each time the text drew in the fallback font for one to three frames, and every text block changed size. Measured on a fixture page of 40 data blocks, desktop 1440 px, CPU 6x slower: 45,828 px of layout change in 2 frames with `build.prepareDesignSystem` on, 68,742 px in 3 frames with `{ shell: false, pages: false }`. Now 0 px in both, 4 of 4 runs.
24
+ - `@symbo.ls/brender` `renderPage` writes the page's `@font-face` rules in a `<style data-smbls-faces>` of their own, ahead of `<style data-brender>`. They are no longer in `<style data-emotion="smbls">`. The takeover does not select that sheet, so it stays for the page's life and keeps its faces loaded. `splitCapturedCSS` does not change.
25
+ - `smbls` `applyFontFaces` does not insert a face that names no file.
26
+ - A post-build step that moves the `@font-face` rules out of the server's styles finds none there now. It can leave `<style data-smbls-faces>` where it is.
27
+
28
+ - Updated dependencies [75fc737]
29
+ - smbls@3.14.850
30
+
31
+ ## 3.14.671
32
+
33
+ ### Patch Changes
34
+
35
+ - XMA round 8 (page weight and core size). Default builds look and behave the same; opt-in options save more.
36
+
37
+ Built pages (brender), on by default with no visible change, each with an off switch:
38
+
39
+ - The inline sprite holds only the page's icons (`brender.spriteIcons: 'all'` restores all).
40
+ - Atomic CSS only for classes in the page's markup (`brender.cssClasses: 'all'`).
41
+ - The element-path registry ships as a tree when the client reads it (`brender.registryFormat: 'paths'`).
42
+
43
+ Opt-in:
44
+
45
+ - `build.prepareDesignSystem: { pages: false }`: built pages do not request the prepared design-system file (`false` keeps meaning `{ shell: false }`).
46
+ - `brender.cssVariables: 'page'`: a built page carries only the CSS variables it reads.
47
+ - `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.
48
+
49
+ Core: the full `esm` dist no longer carries esbuild `__esm` wrappers (namespace objects marked pure): about 2 KB Brotli less in every build.
50
+
51
+ 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.
52
+
53
+ - Updated dependencies
54
+ - smbls@3.14.848
55
+ - @symbo.ls/utils@3.14.848
56
+ - @symbo.ls/element@3.14.813
57
+ - @symbo.ls/scratch@3.14.795
58
+
3
59
  ## 3.14.670
4
60
 
5
61
  ### 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,25 @@
1
- import{resolve as p,join as Ne,dirname as T}from"path";import{existsSync as h,unlinkSync as He,readFileSync as J,realpathSync as Ue}from"fs";import{resolvePackageEntry as We}from"./packageEntry.js";import{tmpdir as Je}from"os";import{randomBytes as ze}from"crypto";import{createRequire as ie}from"module";import{pathToFileURL as Ye}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 ae,generateHeadHtml as Xe}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Ve}from"./shell.js";import{fetchSSRTranslations as Qe}from"./prefetch.js";import{createEarlySeedScript as Ze}from"@symbo.ls/fetch";import{headerEntries as et}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as tt,brenderStampMeta as ot,escapeRegExp as nt,jsonForScript as st,polyglotFallbackLang as Q,polyglotLangStorageKey as rt,polyglotAddressLang as ct,polyglotStartupScript as it}from"@symbo.ls/utils";let Z=null;const at=async()=>{if(Z)return Z;try{return Z=(await import("@symbo.ls/funcql")).funcqlPlugin,Z}catch{return null}};import"linkedom";import{createEarlySeedBlock as lt}from"@symbo.ls/fetch";import{reset as ft}from"@symbo.ls/css";const ee=(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},te=(e,t)=>Q(e?.lang,t),Re=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const i=ee(r,e);if(i!==void 0)return i}}if(n?.translations){const r=n.translations[t];if(r){const i=ee(r,e);if(i!==void 0)return i}}const s=Q(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const i=ee(r,e);if(i!==void 0)return i}}},ut=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=Re(e,t||te(s,n),n,s);if(r!==void 0)return r;const i=pe(ue);return n&&i&&i.labelKeys.add(e),e},ke=function(){return te(this?.state?.root,this?.context?.polyglot)},$=(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?$(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?$(s,t):s}return o};let q=null;try{import.meta.url&&(q=ie(import.meta.url))}catch{}const pt=()=>{if(!import.meta.url||!q)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=Ue(new URL(".",import.meta.url).pathname),t=p(e,"../..");if(h(p(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=q.resolve("smbls/package.json");n=T(l)}catch{let l=e;for(;l!==T(l);){const g=p(l,"node_modules","smbls");if(h(p(g,"package.json"))){n=g;break}l=T(l)}}let s=q;if(n)try{s=ie(p(n,"package.json"))}catch{}let r,i=process.cwd();for(;i!==T(i);){if(h(p(i,"package.json"))){r=i;break}i=T(i)}let m=s;if(r)try{m=ie(p(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:m}},P=(e,t)=>{const o=e.isMonorepo?[q]:[e._smblsRequire,e._projectRequire,q].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},Eo=(e,t,...o)=>{if(e.isMonorepo)return p(e.monorepoRoot,"packages",t,...o);const n=P(e,`${t}/package.json`);return n?p(T(n),...o):null},To=(e,t,...o)=>{if(e.isMonorepo)return p(e.monorepoRoot,"plugins",t,...o);const n=P(e,`@symbo.ls/${t}/package.json`);return n?p(T(n),...o):null},le=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=p(e.monorepoRoot,s,t,...o);if(h(r))return r}return null}const n=P(e,`@symbo.ls/${t}/package.json`);return n?p(T(n),...o):null},xe=(e,t,...o)=>{if(e.isMonorepo)return p(e.monorepoRoot,"packages","domql","packages",t,...o);const n=P(e,`@symbo.ls/${t}/package.json`);return n?p(T(n),...o):null};let oe=null;const je="smbls/ssr",mt=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},_e=()=>(oe||(oe=dt().catch(e=>{throw oe=null,e})),oe),dt=async()=>{const e=pt();let t;if(q&&e.isMonorepo)t=p(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await mt()}catch(a){f=a}if(!q)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&&p(e.smblsRoot,"src","createDomql.js");if(c&&h(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=Ne(Je(),`br_createDomql_${ze(6).toString("hex")}.mjs`),s=We,r=e.isMonorepo?await import(Ye(p(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,i=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,m=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},c=>{if(m.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`}]}}),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 a=c.path.replace(/^smbls\/?/,""),u=e.isMonorepo?p(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!u)return;if(!a){const b=s(u);return b?{path:b}:void 0}const d=p(u,a);if(h(d))return{path:d};if(h(d+".js"))return{path:d+".js"};const y=p(d,"index.js");if(h(y))return{path:y}}),f.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const a=p(e.monorepoRoot,"packages","domql","src","index.js");if(h(a))return{path:a};const u=p(e.monorepoRoot,"packages","domql","index.js");if(h(u))return{path:u}}else try{const a=_require.resolve("domql/package.json"),u=s(T(a));if(u)return{path:u}}catch{}}),f.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 y=p(e.monorepoRoot,d,a,"src","index.js");if(h(y))return{path:y};const b=p(e.monorepoRoot,d,a,"index.js");if(h(b))return{path:b}}const u=p(e.monorepoRoot,"packages","default-config","blank","index.js");if(a==="default-config"&&h(u))return{path:u}}else{const u=le(e,a,"src","index.js");if(u&&h(u))return{path:u};const d=le(e,a,"index.js");if(d&&h(d))return{path:d};if(a==="default-config"){const y=le(e,"default-config","blank","index.js");if(y&&h(y))return{path:y}}}}),f.onResolve({filter:/^@domql\//},c=>{const a=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const u=p(e.monorepoRoot,"packages","domql","packages",a,"src","index.js");if(h(u))return{path:u};const d=p(e.monorepoRoot,"packages","domql","packages",a,"index.js");if(h(d))return{path:d}}else{const u=xe(e,a,"src","index.js");if(u&&h(u))return{path:u};const d=xe(e,a,"index.js");if(d&&h(d))return{path:d}}}),f.onResolve({filter:/^css-in-props/},c=>{let a;if(e.isMonorepo)a=p(e.monorepoRoot,"packages","css-in-props");else{const y=P(e,"css-in-props/package.json");y&&(a=T(y))}if(!a)return;const u=c.path.replace(/^css-in-props\/?/,"");if(u){const y=p(a,u),b=p(y,"index.js");if(h(b))return{path:b};if(h(y+".js"))return{path:y+".js"};if(h(y))return{path:y}}const d=s(a);if(d)return{path:d}}),f.onResolve({filter:/^@emotion\//},c=>{let a;if(e.isMonorepo)a=p(e.monorepoRoot,"node_modules",c.path);else{const u=P(e,`${c.path}/package.json`);if(!u)return;a=T(u)}if(h(a)){const u=p(a,"package.json");if(h(u))try{const d=JSON.parse(J(u,"utf8")),y=d.module||d.main||"dist/emotion-css.esm.js";return{path:p(a,y)}}catch{}return{path:a}}}),f.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const a=p(c.resolveDir,c.path);if(h(a))return{path:a}}}),f.onLoad({filter:/smbls\/.*options\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("options.js"))return;let a=J(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"}}),f.onLoad({filter:/smbls\/.*init\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("init.js"))return;let a=J(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"}}),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 a=J(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"}}),f.onLoad({filter:/fetchOnCreate\.js$/},async c=>{if(!c.path.includes("smbls/"))return;let a=J(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?[p(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[p(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[p(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[p(e.smblsRoot,"..","..","node_modules")]:[]].filter(f=>h(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{He(n)}catch{}return g},gt={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"}},ht=(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)},yt=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let F=0;const Te=Symbol.for("brender.fetchTracked"),fe=()=>F,bt=256*1024,St=1024*1024,wt=new Set(["accept-language","accept"]),ve={renders:0,record:null},Rt=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)}}},pe=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>=Rt||e.failures.push({url:kt(t),reason:o})},De=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),xt=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(),i={},m=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,c]of et(l))g&&c==null||(i[String(f).toLowerCase()]=String(c))};return n&&m(t.headers),o&&m(o.headers),{url:s,method:r,headers:i}},jt=(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>bt||e.bytes+i.length>St||(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[Te])return;const t=function(...o){F++;let n=!1;const s=()=>{n||(n=!0,F--)},r=c=>{F++,c.then(()=>{F--},()=>{F--})},i=pe(ve),m=pe(ue);let l=null;if(i)try{l=xt(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!wt.has(c)))&&(l=null)}catch{l=null}const g=()=>De(o[0]);let f;try{f=e.apply(this,o)}catch(c){throw me(m,g(),c&&c.message||"failed"),s(),c}return f&&typeof f.then=="function"?f.then(c=>{l&&jt(i,l,c,r),m&&c&&c.ok===!1&&me(m,g(),"HTTP "+c.status),s()},c=>{me(m,g(),c&&c.message||"network error"),s()}):s(),f};t[Te]=!0,globalThis.fetch=t},vo=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=st(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
- `+Ze(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},Et=3e4,Tt=2147483647;let $e=Promise.resolve();const Ae=()=>{},vt=e=>{const t=$e.then(e);return $e=t.then(Ae,Ae),t},Lt=e=>{if(e===void 0)return Et;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,Tt)},Dt=(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},$t=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},At=e=>{let t=!1;return()=>{t||(t=!0,e())}},qe=(e,t={})=>{let o;try{o=Lt(t.timeout)}catch(n){return Promise.reject(n)}return vt(()=>qt(e,t,o))},qt=async(e,t,o)=>{await _e();const n={closed:!1,undo:[]},s=Ct(e,t,n);if(!o)return s;let r;const i=new Promise((m,l)=>{r=setTimeout(()=>{$t(n),l(Dt(t,o))},o)});try{return await Promise.race([s,i])}finally{clearTimeout(r)}},Ct=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:i,prefetch:m=!1}=t,l=s||n;_t();const g=Le(ue,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await Mt(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:i,prefetch:m},g.record,o)}finally{g.close()}},Mt=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},i,m)=>{const l=()=>{if(m.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await Qe(e)}catch(d){m.closed||console.warn("[brender] SSR translation fetch failed:",d.message)}l()}const{window:f,document:c}=ye(),a=c.body;f.location.pathname=o;const u=[ne("document",c),ne("location",f.location),ne("localStorage",f.localStorage),ne("sessionStorage",f.sessionStorage)].map(At);m.undo.push(...u);try{const{createDomqlElement:d}=await _e();l();const y=$(e.app||{}),b={...e.config||{}};e.polyglot&&!b.polyglot&&(b.polyglot=e.polyglot),e.fetch&&!b.fetch&&(b.fetch=e.fetch),e.router&&!b.router&&(b.router=e.router);for(const w of Kt)e[w]!=null&&b[w]==null&&(b[w]=e[w]);const R=b.polyglot?{...b.polyglot}:void 0,z=ct(o,R);z&&f.localStorage.setItem(rt(R),z),g&&R&&(R.translations={...R.translations||{},...g});const _=$(e.state||{});(g||R)&&(_.root||(_.root={}),R&&(_.root.lang=Q(_.root.lang,R)),g&&(_.root.translations={..._.root.translations||{},...g})),ft();const S={};for(const w in e){if(yt.has(w))continue;const x=e[w];S[w]=x&&typeof x=="object"?$(x):x}const v={...S,state:_,...n?{state:{..._,...n}}:{},dependencies:$(e.dependencies||{}),components:$(e.components||{}),snippets:$(e.snippets||{}),pages:$(e.pages||{}),functions:{...e.functions||{},polyglot:ut,getActiveLang:ke,getLang:ke},methods:e.methods||{},designSystem:$(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...b,...R?{polyglot:R}:{},document:c,window:f,parent:{node:a},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,captureDesignSystem:!0,...s||{}},B=await at();l(),B&&(v.plugins=v.plugins||[],v.plugins.some(w=>w.name==="funcql")||v.plugins.push(B)),be();const K=Le(ve,()=>({entries:[],bytes:0,tainted:!1})),C=K.record;let j;try{j=await d(y,v),l();const w=Date.now(),x=r?2e3:50,W=ce=>new Promise(Ge=>setTimeout(Ge,ce));for(await W(Math.min(50,x));r&&!m.closed&&fe()>0&&Date.now()-w<x;)await W(10),fe()===0&&await W(Math.max(0,Math.min(50,x-(Date.now()-w))));if(l(),r&&await It(j,R)){l();const ce=Date.now();for(await W(Math.min(50,x));!m.closed&&fe()>0&&Date.now()-ce<x;)await W(10);l()}}finally{K.close()}const Y=C&&!C.tainted&&!(b.fetch&&b.fetch.seed===!1)?C.entries.slice():[];Se(a);const X=we(j),{elementPath:V}=await import("@symbo.ls/element");l();const M=ht(j,V),G=j?.context?.routerOptions?.contentElementKey||"content",A=j&&j[G],I=A&&typeof A=="object"&&A.__ref?A:void 0,O=ae(e,t,j,j?.state,I),L=[],N=c.head||c.querySelector("head");if(N)for(const w of N.querySelectorAll("style")){if(w.sheet&&w.sheet.cssRules)for(const x of w.sheet.cssRules)x.cssText&&L.push(x);if(!L.length){const x=w.textContent||"";x&&L.push({cssText:x})}}const he=L.map(w=>w.cssText),{globalCSS:k,classRules:E}=Jt(L);let D=Ie(a.innerHTML);const H=j?.state?.root,U=te(H,R),re=Ot(g,H);re&&(D=Me(D,Ce(re,U,R)));const Ke=i&&!i.tainted?{failedRequests:i.failures.slice(),rawLabelKeys:Bt(i.labelKeys,D,U,R,H)}:null;return{html:D,metadata:O,registry:X,brRegistry:M,element:j,emotionCSS:he,globalCSS:k,classRules:E,fetchSeed:Y,document:c,window:f,ssrTranslations:re||void 0,activeLang:U,diagnostics:Ke,preparedDesignSystem:v.preparedDesignSystem||null}}finally{for(const d of u)d()}},ne=(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]})},It=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=te(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},de=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,ge=e=>e&&typeof e.parse=="function"?e.parse():e,Ot=(e,t)=>{const o=ge(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const i=ge(s[r]);i&&typeof i=="object"&&(n[r]={...n[r]||{},...i})}return Object.keys(n).length?n:null},Ce=(e,t,o)=>e[t]||e[Q(void 0,o)]||Object.values(e)[0]||{},Me=(e,t)=>e.replace(de,(o,n)=>ee(t,n.trim())??o),Pt=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Ft=(e,t)=>new RegExp(`(?:^|[^\\w.-])${nt(t)}(?![\\w-]|\\.\\w)`).test(e),Bt=(e,t,o,n,s)=>{const r=new Set,i=t.replace(Pt,"");for(const m of e)/\s/.test(m)||Re(m,o,n,s)===void 0&&Ft(i,m)&&r.add(m);for(const m of i.matchAll(de))r.add(m[1].trim());return[...r]},Lo=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=ye(),r=s.body,{create:i}=await import("@symbo.ls/element"),m=await import("@symbo.ls/utils"),l={...gt,...o.components||{}},g={...m,...o.utils||{},...o.functions||{}};be();let f;try{f=i(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}),Kt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Gt=new Set(["auto","system"]),Oe=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||Gt.has(s)?null:s},Nt=e=>String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),Ht=/\.[A-Za-z_\\-]/,Ut=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Ht.test(t)},Wt=/^\s*(:root|\[data-theme|\[data-skin)/,Pe=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 Ut(e.selectorText)?Wt.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(Pe));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Jt=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Pe(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 Je,dirname as _}from"path";import{existsSync as h,unlinkSync as Ye,readFileSync as X,realpathSync as ze}from"fs";import{resolvePackageEntry as Ve}from"./packageEntry.js";import{tmpdir as Xe}from"os";import{randomBytes as Qe}from"crypto";import{createRequire as fe}from"module";import{pathToFileURL as Ze}from"url";import{createEnv as Re}from"./env.js";import{resetKeys as ke,assignKeys as we,mapKeysToElements as xe}from"./keys.js";import{extractMetadata as pe,generateHeadHtml as et}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as tt}from"./shell.js";import{trimSpriteToPage as ot,classRulesForPage as nt,markupClasses as st,trimVariablesToPage as rt}from"./pageWeight.js";import{fetchSSRTranslations as ct}from"./prefetch.js";import{createEarlySeedScript as at}from"@symbo.ls/fetch";import{headerEntries as it}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as lt,brenderRegistryTreeBlock as ft,brenderStampMeta as pt,escapeRegExp as ut,jsonForScript as mt,polyglotFallbackLang as oe,polyglotLangStorageKey as dt,polyglotAddressLang as gt,polyglotStartupScript as ht}from"@symbo.ls/utils";let ne=null;const yt=async()=>{if(ne)return ne;try{return ne=(await import("@symbo.ls/funcql")).funcqlPlugin,ne}catch{return null}};import"linkedom";import{createEarlySeedBlock as bt}from"@symbo.ls/fetch";import{reset as St}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),je=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const a=se(r,e);if(a!==void 0)return a}}if(n?.translations){const r=n.translations[t];if(r){const a=se(r,e);if(a!==void 0)return a}}const s=oe(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const a=se(r,e);if(a!==void 0)return a}}},Rt=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=je(e,t||re(s,n),n,s);if(r!==void 0)return r;const a=ge(de);return n&&a&&a.labelKeys.add(e),e},Te=function(){return re(this?.state?.root,this?.context?.polyglot)},$=(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?$(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?$(s,t):s}return o};let O=null;try{import.meta.url&&(O=fe(import.meta.url))}catch{}const kt=()=>{if(!import.meta.url||!O)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=ze(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=fe(m(n,"package.json"))}catch{}let r,a=process.cwd();for(;a!==_(a);){if(h(m(a,"package.json"))){r=a;break}a=_(a)}let p=s;if(r)try{p=fe(m(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:p}},U=(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},Io=(e,t,...o)=>{if(e.isMonorepo)return m(e.monorepoRoot,"packages",t,...o);const n=U(e,`${t}/package.json`);return n?m(_(n),...o):null},Oo=(e,t,...o)=>{if(e.isMonorepo)return m(e.monorepoRoot,"plugins",t,...o);const n=U(e,`@symbo.ls/${t}/package.json`);return n?m(_(n),...o):null},ue=(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=U(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=U(e,`@symbo.ls/${t}/package.json`);return n?m(_(n),...o):null};let ce=null;const _e="smbls/ssr",wt=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},ve=()=>(ce||(ce=xt().catch(e=>{throw ce=null,e})),ce),xt=async()=>{const e=kt();let t;if(O&&e.isMonorepo)t=m(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await wt()}catch(i){f=i}if(!O)throw new Error(`brender: ${_e} 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&&m(e.smblsRoot,"src","createDomql.js");if(c&&h(c))t=c;else throw new Error(`brender: cannot load ${_e} (${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=Je(Xe(),`br_createDomql_${Qe(6).toString("hex")}.mjs`),s=Ve,r=e.isMonorepo?await import(Ze(m(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,a=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,p=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},c=>{if(p.test(c.path))return;const i=c.path.split("/"),u=c.path.startsWith("@")?i.slice(0,2).join("/"):i[0];if(!a.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`}]}}),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\/?/,""),u=e.isMonorepo?m(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!u)return;if(!i){const E=s(u);return E?{path:E}:void 0}const d=m(u,i);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}}),f.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const i=m(e.monorepoRoot,"packages","domql","src","index.js");if(h(i))return{path:i};const u=m(e.monorepoRoot,"packages","domql","index.js");if(h(u))return{path:u}}else try{const i=_require.resolve("domql/package.json"),u=s(_(i));if(u)return{path:u}}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 d of["packages","plugins"]){const b=m(e.monorepoRoot,d,i,"src","index.js");if(h(b))return{path:b};const E=m(e.monorepoRoot,d,i,"index.js");if(h(E))return{path:E}}const u=m(e.monorepoRoot,"packages","default-config","blank","index.js");if(i==="default-config"&&h(u))return{path:u}}else{const u=ue(e,i,"src","index.js");if(u&&h(u))return{path:u};const d=ue(e,i,"index.js");if(d&&h(d))return{path:d};if(i==="default-config"){const b=ue(e,"default-config","blank","index.js");if(b&&h(b))return{path:b}}}}),f.onResolve({filter:/^@domql\//},c=>{const i=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const u=m(e.monorepoRoot,"packages","domql","packages",i,"src","index.js");if(h(u))return{path:u};const d=m(e.monorepoRoot,"packages","domql","packages",i,"index.js");if(h(d))return{path:d}}else{const u=Ee(e,i,"src","index.js");if(u&&h(u))return{path:u};const d=Ee(e,i,"index.js");if(d&&h(d))return{path:d}}}),f.onResolve({filter:/^css-in-props/},c=>{let i;if(e.isMonorepo)i=m(e.monorepoRoot,"packages","css-in-props");else{const b=U(e,"css-in-props/package.json");b&&(i=_(b))}if(!i)return;const u=c.path.replace(/^css-in-props\/?/,"");if(u){const b=m(i,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(i);if(d)return{path:d}}),f.onResolve({filter:/^@emotion\//},c=>{let i;if(e.isMonorepo)i=m(e.monorepoRoot,"node_modules",c.path);else{const u=U(e,`${c.path}/package.json`);if(!u)return;i=_(u)}if(h(i)){const u=m(i,"package.json");if(h(u))try{const d=JSON.parse(X(u,"utf8")),b=d.module||d.main||"dist/emotion-css.esm.js";return{path:m(i,b)}}catch{}return{path:i}}}),f.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const i=m(c.resolveDir,c.path);if(h(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=X(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=X(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=X(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=X(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?[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(f=>h(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{Ye(n)}catch{}return g},jt={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"}},Tt=(e,t)=>{const o={};return Le(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},Le=(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)},Et=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let W=0;const $e=Symbol.for("brender.fetchTracked"),me=()=>W,_t=256*1024,vt=1024*1024,Lt=new Set(["accept-language","accept"]),De={renders:0,record:null},$t=50,de={renders:0,record:null},Ae=(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)}}},ge=e=>e.record&&!e.record.tainted?e.record:null,Dt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},he=(e,t,o)=>{!e||e.tainted||e.failures.length>=$t||e.failures.push({url:Dt(t),reason:o})},Ce=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),At=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=Ce(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),a={},p=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,c]of it(l))g&&c==null||(a[String(f).toLowerCase()]=String(c))};return n&&p(t.headers),o&&p(o.headers),{url:s,method:r,headers:a}},Ct=(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>_t||e.bytes+a.length>vt||(e.bytes+=a.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:a}))},()=>{}))},qt=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[$e])return;const t=function(...o){W++;let n=!1;const s=()=>{n||(n=!0,W--)},r=c=>{W++,c.then(()=>{W--},()=>{W--})},a=ge(De),p=ge(de);let l=null;if(a)try{l=At(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!Lt.has(c)))&&(l=null)}catch{l=null}const g=()=>Ce(o[0]);let f;try{f=e.apply(this,o)}catch(c){throw he(p,g(),c&&c.message||"failed"),s(),c}return f&&typeof f.then=="function"?f.then(c=>{l&&Ct(a,l,c,r),p&&c&&c.ok===!1&&he(p,g(),"HTTP "+c.status),s()},c=>{he(p,g(),c&&c.message||"network error"),s()}):s(),f};t[$e]=!0,globalThis.fetch=t},Po=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=mt(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
+ `+at(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},Mt=3e4,It=2147483647;let qe=Promise.resolve();const Me=()=>{},Ot=e=>{const t=qe.then(e);return qe=t.then(Me,Me),t},Pt=e=>{if(e===void 0)return Mt;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,It)},Ft=(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},Bt=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},Kt=e=>{let t=!1;return()=>{t||(t=!0,e())}},Ie=(e,t={})=>{let o;try{o=Pt(t.timeout)}catch(n){return Promise.reject(n)}return Ot(()=>Gt(e,t,o))},Gt=async(e,t,o)=>{await ve();const n={closed:!1,undo:[]},s=Nt(e,t,n);if(!o)return s;let r;const a=new Promise((p,l)=>{r=setTimeout(()=>{Bt(n),l(Ft(t,o))},o)});try{return await Promise.race([s,a])}finally{clearTimeout(r)}},Nt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:a,prefetch:p=!1}=t,l=s||n;qt();const g=Ae(de,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await Ht(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:a,prefetch:p},g.record,o)}finally{g.close()}},Ht=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},a,p)=>{const l=()=>{if(p.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await ct(e)}catch(d){p.closed||console.warn("[brender] SSR translation fetch failed:",d.message)}l()}const{window:f,document:c}=Re(),i=c.body;f.location.pathname=o;const u=[ae("document",c),ae("location",f.location),ae("localStorage",f.localStorage),ae("sessionStorage",f.sessionStorage)].map(Kt);p.undo.push(...u);try{const{createDomqlElement:d,PRERENDER_REGISTRY_FORMATS:b}=await ve();l();const E=$(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 Vt)e[y]!=null&&x[y]==null&&(x[y]=e[y]);const w=x.polyglot?{...x.polyglot}:void 0,Q=gt(o,w);Q&&f.localStorage.setItem(dt(w),Q),g&&w&&(w.translations={...w.translations||{},...g});const v=$(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})),St();const P={};for(const y in e){if(Et.has(y))continue;const k=e[y];P[y]=k&&typeof k=="object"?$(k):k}const D={...P,state:v,...n?{state:{...v,...n}}:{},dependencies:$(e.dependencies||{}),components:$(e.components||{}),snippets:$(e.snippets||{}),pages:$(e.pages||{}),functions:{...e.functions||{},polyglot:Rt,getActiveLang:Te,getLang:Te},methods:e.methods||{},designSystem:$(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...x,...w?{polyglot:w}:{},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||{}},N=await yt();l(),N&&(D.plugins=D.plugins||[],D.plugins.some(y=>y.name==="funcql")||D.plugins.push(N)),ke();const S=Ae(De,()=>({entries:[],bytes:0,tainted:!1})),A=S.record;let j;try{j=await d(E,D),l();const y=Date.now(),k=r?2e3:50,R=T=>new Promise(G=>setTimeout(G,T));for(await R(Math.min(50,k));r&&!p.closed&&me()>0&&Date.now()-y<k;)await R(10),me()===0&&await R(Math.max(0,Math.min(50,k-(Date.now()-y))));if(l(),r&&await Ut(j,w)){l();const T=Date.now();for(await R(Math.min(50,k));!p.closed&&me()>0&&Date.now()-T<k;)await R(10);l()}}finally{S.close()}const C=A&&!A.tainted&&!(x.fetch&&x.fetch.seed===!1)?A.entries.slice():[];we(i);const J=xe(j),{elementPath:Y}=await import("@symbo.ls/element");l();const Z=Tt(j,Y),z=j?.context?.routerOptions?.contentElementKey||"content",F=j&&j[z],q=F&&typeof F=="object"&&F.__ref?F:void 0,Se=pe(e,t,j,j?.state,q),M=[],V=c.head||c.querySelector("head");if(V)for(const y of V.querySelectorAll("style")){if(y.sheet&&y.sheet.cssRules)for(const k of y.sheet.cssRules)k.cssText&&M.push(k);if(!M.length){const k=y.textContent||"";k&&M.push({cssText:k})}}const ee=M.map(y=>y.cssText),{globalCSS:te,classRules:le}=oo(M);let L=Fe(i.innerHTML);const B=j?.state?.root,K=re(B,w),I=Wt(g,B);I&&(L=Pe(L,Oe(I,K,w)));const H=a&&!a.tainted?{failedRequests:a.failures.slice(),rawLabelKeys:zt(a.labelKeys,L,K,w,B)}:null;return{html:L,metadata:Se,registry:J,brRegistry:Z,element:j,emotionCSS:ee,globalCSS:te,classRules:le,fetchSeed:C,document:c,window:f,ssrTranslations:I||void 0,activeLang:K,diagnostics:H,preparedDesignSystem:D.preparedDesignSystem||null,registryFormats:Array.isArray(b)?b.slice():["paths"]}}finally{for(const d of u)d()}},ae=(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]})},Ut=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},ye=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,be=e=>e&&typeof e.parse=="function"?e.parse():e,Wt=(e,t)=>{const o=be(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const a=be(s[r]);a&&typeof a=="object"&&(n[r]={...n[r]||{},...a})}return Object.keys(n).length?n:null},Oe=(e,t,o)=>e[t]||e[oe(void 0,o)]||Object.values(e)[0]||{},Pe=(e,t)=>e.replace(ye,(o,n)=>se(t,n.trim())??o),Jt=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Yt=(e,t)=>new RegExp(`(?:^|[^\\w.-])${ut(t)}(?![\\w-]|\\.\\w)`).test(e),zt=(e,t,o,n,s)=>{const r=new Set,a=t.replace(Jt,"");for(const p of e)/\s/.test(p)||je(p,o,n,s)===void 0&&Yt(a,p)&&r.add(p);for(const p of a.matchAll(ye))r.add(p[1].trim());return[...r]},Fo=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=Re(),r=s.body,{create:a}=await import("@symbo.ls/element"),p=await import("@symbo.ls/utils"),l={...jt,...o.components||{}},g={...p,...o.utils||{},...o.functions||{}};ke();let f;try{f=a(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}we(r);const c=f?xe(f):{};return{html:Fe(r.innerHTML),registry:c,element:f}},Fe=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}),Vt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Xt=new Set(["auto","system"]),Be=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||Xt.has(s)?null:s},Qt=e=>String(e).replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;"),Zt=/\.[A-Za-z_\\-]/,eo=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Zt.test(t)},to=/^\s*(:root|\[data-theme|\[data-skin)/,Ke=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 eo(e.selectorText)?to.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(Ke));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Ge=e=>typeof e=="string"&&/^\s*@font-face\b/i.test(e),oo=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Ke(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 se=new Set;const Do=()=>{se=new Set},$o=()=>Array.from(se).join(`
6
- `),Ao=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
5
+ `),fontFaceCSS:""},classRules:o}};let ie=new Set;const Bo=()=>{ie=new Set},Ko=()=>Array.from(ie).join(`
6
+ `),Go=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
7
7
  ${t}
8
- </style>`:""),qo=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,i=await qe(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!i)return null;const m=e.designSystem||{},l=i.globalCSS;let g=null;try{const c=zt(i.element);Object.keys(c).length&&(g=c)}catch{}const f=i.activeLang;return{html:i.html,css:i.classRules.join(`
9
- `),classRules:i.classRules.slice(),globalCSS:l,resetCss:l.resetRules,fontLinks:Be(m),metadata:i.metadata||ae(e,o),brKeyCount:i.registry?Object.keys(i.registry).length:0,brRegistry:i.brRegistry||{},fetchSeed:i.fetchSeed||[],ssrTranslations:i.ssrTranslations,prefetchedState:g,activeLang:f,theme:Oe(e),diagnostics:i.diagnostics,preparedDesignSystem:i.preparedDesignSystem||null}},zt=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 i=r.startsWith("~/"),m=i?r.slice(2):r,l=i?o.state.root:o.state,g=ge(l&&l[m]);m&&g!=null&&typeof g!="function"&&(t[m]=g)}}),t},Co=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:i=!0,prefetch:m=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:c=Fe,preloadScripts:a=!0,nonce:u,timeout:d,stamp:y,time:b}=o,R=eo(u),z=n||e.state?.lang||e.app?.metadata?.lang||"en",_=Oe(e),S=await qe(e,{route:t,pathname:f,prefetch:m,timeout:d});if(!S)return null;const v={...S.metadata};if(s&&(v["theme-color"]=s),typeof l=="string"&&!e.name){const k="\0brender-undeclared-title\0";if(ae({...e,name:k},t,S.element,S.element?.state).title===k)for(const D of["title","og:title","twitter:title"])v[D]===S.metadata.title&&delete v[D]}const B=Xe(v);for(const k of S.classRules)k&&se.add(k);const K=Array.from(se).join(`
10
- `),C=S.globalCSS,j=`<style data-brender>
11
- ${C.rootRule||""}
12
- ${C.resetRules||""}
13
- ${C.keyframeRules||""}
14
- </style>`,Y=K?`<style data-emotion="smbls">
15
- ${K}
16
- </style>`:"",X=Be(e.designSystem||{}),V=Object.keys(S.registry).length,M=Qt({brRegistry:S.brRegistry,fetchSeed:S.fetchSeed,adoptDom:g,stamp:y,time:b});let G="";if(r&&r.clientScript){const k=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,E=k>0?"../".repeat(k):"./";if(i){const D=it({polyglot:e.polyglot||e.config?.polyglot,translations:S.ssrTranslations,lang:S.activeLang,pathname:f||t,attrs:R}),H=D?D+`
17
- `:"",U=M.blocks?M.blocks+`
18
- `:"";G=`${H}${U}<script type="module" src="${E}${r.clientScript}"${R}><\/script>`}else G=`<script type="module"${R}>
8
+ </style>`:""),No=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,a=await Ie(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!a)return null;const p=e.designSystem||{},l=a.globalCSS;let g=null;try{const c=no(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:He(p),metadata:a.metadata||pe(e,o),brKeyCount:a.registry?Object.keys(a.registry).length:0,brRegistry:a.brRegistry||{},fetchSeed:a.fetchSeed||[],ssrTranslations:a.ssrTranslations,prefetchedState:g,activeLang:f,theme:Be(e),diagnostics:a.diagnostics,preparedDesignSystem:a.preparedDesignSystem||null}},no=e=>{const t={};return Le(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("~/"),p=a?r.slice(2):r,l=a?o.state.root:o.state,g=be(l&&l[p]);p&&g!=null&&typeof g!="function"&&(t[p]=g)}}),t},Ho=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:a=!0,prefetch:p=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:c=Ne,preloadScripts:i=!0,nonce:u,timeout:d,stamp:b,time:E,spriteIcons:x="all",cssClasses:w="all",cssVariables:Q="all",registryFormat:v="paths"}=o,P=lo(u),D=n||e.state?.lang||e.app?.metadata?.lang||"en",N=Be(e),S=await Ie(e,{route:t,pathname:f,prefetch:p,timeout:d});if(!S)return null;const A={...S.metadata};if(s&&(A["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 G of["title","og:title","twitter:title"])A[G]===S.metadata.title&&delete A[G]}const j=et(A);for(const R of S.classRules)R&&ie.add(R);let C=co(S.html,c);x==="page"&&(C=ot(C,typeof l=="string"?l:"").html);const J=Array.from(ie),Y=J.filter(Ge),Z=Y.length?J.filter(R=>!Ge(R)):J,z=w==="page"?nt(Z,st(C,typeof l=="string"?l:"")).join(`
10
+ `):Z.join(`
11
+ `),F=Y.length?`<style data-smbls-faces>
12
+ ${Y.join(`
13
+ `)}
14
+ </style>`:"",q=S.globalCSS,M=`<style data-brender>
15
+ ${(Q==="page"?rt(q.rootRule||"",q.resetRules,q.keyframeRules,z,C,typeof l=="string"?l:"").css:q.rootRule)||""}
16
+ ${q.resetRules||""}
17
+ ${q.keyframeRules||""}
18
+ </style>`,V=z?`<style data-emotion="smbls">
19
+ ${z}
20
+ </style>`:"",ee=He(e.designSystem||{}),te=Object.keys(S.registry).length,le=v==="tree"||v==="auto"&&(S.registryFormats||[]).includes("tree"),L=ao({brRegistry:S.brRegistry,fetchSeed:S.fetchSeed,adoptDom:g,stamp:b,time:E,registryFormat:le?"tree":"paths"});let B="";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(a){const G=ht({polyglot:e.polyglot||e.config?.polyglot,translations:S.ssrTranslations,lang:S.activeLang,pathname:f||t,attrs:P}),Ue=G?G+`
21
+ `:"",We=L.blocks?L.blocks+`
22
+ `:"";B=`${Ue}${We}<script type="module" src="${T}${r.clientScript}"${P}><\/script>`}else B=`<script type="module"${P}>
19
23
  {
20
24
  const brEls = document.querySelectorAll('body > :not(script):not(style)')
21
25
  const observer = new MutationObserver((mutations) => {
@@ -32,23 +36,23 @@ ${K}
32
36
  observer.observe(document.body, { childList: true })
33
37
  }
34
38
  <\/script>
35
- <script type="module" src="${E}${r.clientScript}"${R}><\/script>`}const A={...e.config||{}};e.polyglot&&!A.polyglot&&(A.polyglot=e.polyglot);const I=A.polyglot;let O=B;if(I){const k={...I.translations||{}};for(const E in S.ssrTranslations||{})k[E]={...k[E]||{},...S.ssrTranslations[E]};O=Me(B,Ce(k,S.activeLang,I))}const L=S.diagnostics;if(L)for(const k of O.matchAll(de)){const E=k[1].trim();L.rawLabelKeys.includes(E)||L.rawLabelKeys.push(E)}const N=Vt(S.html,c);return typeof l=="string"?{html:Ve(l,{headTags:`${O}
36
- ${M.meta}`,headEnd:[X,j,Y].filter(Boolean).join(`
37
- `),body:N,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:M.blocks,lang:n||(I?S.activeLang:void 0),theme:_,preloadScripts:a}),route:t,brKeyCount:V,shell:!0,diagnostics:L,preparedDesignSystem:S.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
38
- <html lang="${z}"${_?` data-theme="${Nt(_)}"`:""}>
39
+ <script type="module" src="${T}${r.clientScript}"${P}><\/script>`}const K={...e.config||{}};e.polyglot&&!K.polyglot&&(K.polyglot=e.polyglot);const I=K.polyglot;let H=j;if(I){const R={...I.translations||{}};for(const T in S.ssrTranslations||{})R[T]={...R[T]||{},...S.ssrTranslations[T]};H=Pe(j,Oe(R,S.activeLang,I))}const y=S.diagnostics;if(y)for(const R of H.matchAll(ye)){const T=R[1].trim();y.rawLabelKeys.includes(T)||y.rawLabelKeys.push(T)}return typeof l=="string"?{html:tt(l,{headTags:`${H}
40
+ ${L.meta}`,headEnd:[ee,F,M,V].filter(Boolean).join(`
41
+ `),body:C,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:L.blocks,lang:n||(I?S.activeLang:void 0),theme:N,preloadScripts:i}),route:t,brKeyCount:te,shell:!0,diagnostics:y,preparedDesignSystem:S.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
42
+ <html lang="${D}"${N?` data-theme="${Qt(N)}"`:""}>
39
43
  <head>
40
- ${O}${r&&r.clientScript&&i?`
41
- ${M.meta}`:""}
42
- ${X}
43
-
44
- ${j}
45
- ${Y}
44
+ ${H}${r&&r.clientScript&&a?`
45
+ ${L.meta}`:""}
46
+ ${ee}
47
+ ${F}
48
+ ${M}
49
+ ${V}
46
50
  </head>
47
51
  <body>
48
- ${N}
49
- ${G}
52
+ ${C}
53
+ ${B}
50
54
  </body>
51
- </html>`,route:t,brKeyCount:V,diagnostics:L,preparedDesignSystem:S.preparedDesignSystem||null}},Fe=2,Yt=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,Xt=/\sloading\s*=/i,Vt=(e,t=Fe)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(Yt,s=>n++<o||Xt.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Be=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
- `):""},Qt=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s}={})=>{const r=o&&e&&Object.keys(e).length?tt(e):"",i=lt(t);return{meta:`<meta name="brender" content="${r?"hydrate":"replace"}">
53
- `+ot(Zt(n,s)),blocks:[r,i].filter(Boolean).join(`
54
- `)}},Zt=(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}},eo=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{Fe as EAGER_IMAGES_DEFAULT,je as SMBLS_SSR_SPECIFIER,vo as adoptionScripts,Zt as buildStamp,Oe as fixedThemeOf,$o as getAccumulatedEmotionCSS,Vt as markLazyImages,Qt as prerenderParts,qe as render,Lo as renderElement,Co as renderPage,qo as renderRoute,Ao as replaceEmotionCSS,Do as resetGlobalCSSCache,Jt as splitCapturedCSS};
55
+ </html>`,route:t,brKeyCount:te,diagnostics:y,preparedDesignSystem:S.preparedDesignSystem||null}},Ne=2,so=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,ro=/\sloading\s*=/i,co=(e,t=Ne)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(so,s=>n++<o||ro.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},He=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(`
56
+ `):""},ao=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s,registryFormat:r="paths"}={})=>{const p=o&&e&&Object.keys(e).length?r==="tree"&&ft(e)||lt(e):"",l=bt(t);return{meta:`<meta name="brender" content="${p?"hydrate":"replace"}">
57
+ `+pt(io(n,s)),blocks:[p,l].filter(Boolean).join(`
58
+ `)}},io=(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}},lo=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{Ne as EAGER_IMAGES_DEFAULT,_e as SMBLS_SSR_SPECIFIER,Po as adoptionScripts,io as buildStamp,Be as fixedThemeOf,Ko as getAccumulatedEmotionCSS,co as markLazyImages,ao as prerenderParts,Ie as render,Fo as renderElement,Ho as renderPage,No as renderRoute,Go as replaceEmotionCSS,Bo as resetGlobalCSSCache,oo as splitCapturedCSS};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/brender",
3
- "version": "3.14.670",
3
+ "version": "3.14.672",
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.850"
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
  }
@@ -1689,6 +1693,9 @@ const classifyRule = (rule) => {
1689
1693
  return null
1690
1694
  }
1691
1695
 
1696
+ // An @font-face rule's text (renderPage keeps them apart, see facesStyle).
1697
+ const isFontFaceText = (text) => typeof text === 'string' && /^\s*@font-face\b/i.test(text)
1698
+
1692
1699
  export const splitCapturedCSS = (rules) => {
1693
1700
  const groups = { root: [], reset: [], keyframes: [] }
1694
1701
  const rest = []
@@ -1869,10 +1876,26 @@ const collectFetchedState = (element) => {
1869
1876
  * @param {number} [options.depth] - Directory depth of the output file: rebases
1870
1877
  * the shell's relative asset URLs (with `shell`, default 0) and the ISR
1871
1878
  * client script path (default: derived from the route).
1879
+ * @param {'all'|'page'} [options.spriteIcons='all'] - 'page': the icon sprite
1880
+ * keeps only the icons the page references (pageWeight.js
1881
+ * trimSpriteToPage) — for a page whose client boots with the design
1882
+ * system's icons (`smbls build`); 'all' keeps every icon
1883
+ * @param {'all'|'page'} [options.cssClasses='all'] - 'page': the atomic rules
1884
+ * are the ones the page's markup can match (pageWeight.js
1885
+ * classRulesForPage) instead of every rule the pages rendered so far in
1886
+ * this process needed
1887
+ * @param {'all'|'page'} [options.cssVariables='all'] - 'page': the variable
1888
+ * blocks keep the custom properties the page's CSS and markup read
1889
+ * (pageWeight.js trimVariablesToPage) — a stylesheet the page links is
1890
+ * not read, so it is the project's choice (`brender.cssVariables`)
1891
+ * @param {'paths'|'tree'|'auto'} [options.registryFormat='paths'] - with
1892
+ * `adoptDom`: the element-path registry as paths, as a tree (the same data
1893
+ * in a third of the bytes — @symbo.ls/utils encodeBrenderRegistry), or the
1894
+ * tree when the smbls the page renders with reads it
1872
1895
  * @returns {Promise<{ html: string, route: string, brKeyCount: number, shell?: true }>}
1873
1896
  */
1874
1897
  export const renderPage = async (data, route = '/', options = {}) => {
1875
- const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout, stamp, time } = options
1898
+ 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
1876
1899
  const nonceAttr = nonceAttribute(nonce)
1877
1900
 
1878
1901
  // Detect lang from project config, app metadata, or default
@@ -1917,12 +1940,48 @@ export const renderPage = async (data, route = '/', options = {}) => {
1917
1940
  for (const rule of result.classRules) {
1918
1941
  if (rule) _accumulatedEmotionCSS.add(rule)
1919
1942
  }
1920
- const emotionCSS = Array.from(_accumulatedEmotionCSS).join('\n')
1943
+
1944
+ // Images past the first `eagerImages` load lazily unless they say how to
1945
+ // load (see markLazyImages).
1946
+ let bodyHtml = markLazyImages(result.html, eagerImages)
1947
+ // `spriteIcons: 'page'` — the page's own icons only (pageWeight.js, XMA
1948
+ // round 8, E6): the client adds the rest of the design system's icons to
1949
+ // the same sprite when it boots, before any of them can be rendered.
1950
+ if (spriteIcons === 'page') bodyHtml = trimSpriteToPage(bodyHtml, typeof shell === 'string' ? shell : '').html
1951
+
1952
+ // `cssClasses: 'page'` — of the rules every page so far needed, the ones
1953
+ // this page's markup can match (pageWeight.js classRulesForPage, XMA round
1954
+ // 8, E12): the client writes the rules of each element it creates itself.
1955
+ //
1956
+ // The @font-face rules go in a <style data-smbls-faces> of their own, ahead
1957
+ // of the server's styles (XMA round 8, "data holds still" on a slow
1958
+ // desktop). The app removes the server's styles once its own are in, and
1959
+ // smbls kept the faces by moving them into a new <style data-smbls-faces> —
1960
+ // a new style sheet, so Chrome made new, unloaded FontFace objects for them:
1961
+ // the text fell back to the system font for a frame or more while the files
1962
+ // were read again, and every block of text changed size during the takeover.
1963
+ // Deleting the other rules from the server's sheet re-creates them too. A
1964
+ // sheet the takeover never touches keeps them loaded; smbls applyFontFaces
1965
+ // finds them declared and adds none of its own (smbls prerender.js
1966
+ // serverStyles does not select it, so it stays for the page's life).
1967
+ const accumulated = Array.from(_accumulatedEmotionCSS)
1968
+ const faceRules = accumulated.filter(isFontFaceText)
1969
+ const classRules = faceRules.length ? accumulated.filter((rule) => !isFontFaceText(rule)) : accumulated
1970
+ const emotionCSS = cssClasses === 'page'
1971
+ ? classRulesForPage(classRules, markupClasses(bodyHtml, typeof shell === 'string' ? shell : '')).join('\n')
1972
+ : classRules.join('\n')
1973
+ const facesStyle = faceRules.length ? `<style data-smbls-faces>\n${faceRules.join('\n')}\n</style>` : ''
1921
1974
 
1922
1975
  // Global CSS (variables, reset, keyframes): this render's own init output.
1923
1976
  // The style tags, built once for both documents below.
1924
1977
  const globalCSS = result.globalCSS
1925
- const brenderStyle = `<style data-brender>\n${globalCSS.rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`
1978
+ // `cssVariables: 'page'` — the custom properties this page's CSS and
1979
+ // markup read (pageWeight.js trimVariablesToPage, XMA round 8, E7); the
1980
+ // client injects every variable itself as it boots.
1981
+ const rootRule = cssVariables === 'page'
1982
+ ? trimVariablesToPage(globalCSS.rootRule || '', globalCSS.resetRules, globalCSS.keyframeRules, emotionCSS, bodyHtml, typeof shell === 'string' ? shell : '').css
1983
+ : globalCSS.rootRule
1984
+ const brenderStyle = `<style data-brender>\n${rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`
1926
1985
  const emotionStyle = emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''
1927
1986
 
1928
1987
  // Generate font links from design system
@@ -1934,7 +1993,10 @@ export const renderPage = async (data, route = '/', options = {}) => {
1934
1993
  // and the data blocks after the markup — the registry under `adoptDom`,
1935
1994
  // and the recorded anonymous answers, so the client renders from the data
1936
1995
  // the server used instead of asking for all of it again.
1937
- const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time })
1996
+ // `registryFormat: 'auto'` — the tree when the smbls this page renders
1997
+ // with (the page's own client) reads it, else paths (XMA round 8, E13).
1998
+ const treeRegistry = registryFormat === 'tree' || (registryFormat === 'auto' && (result.registryFormats || []).includes('tree'))
1999
+ const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time, registryFormat: treeRegistry ? 'tree' : 'paths' })
1938
2000
 
1939
2001
  // ISR: include client SPA bundle for hydration + data fetching
1940
2002
  let isrBody = ''
@@ -2021,9 +2083,6 @@ export const renderPage = async (data, route = '/', options = {}) => {
2021
2083
  }
2022
2084
  }
2023
2085
 
2024
- // Images past the first `eagerImages` load lazily unless they say how to
2025
- // load (see markLazyImages).
2026
- const bodyHtml = markLazyImages(result.html, eagerImages)
2027
2086
 
2028
2087
  // Build path: render INTO the app's own built document (shell.js) — the
2029
2088
  // standalone document below carries no app code unless `isr` adds a
@@ -2032,7 +2091,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
2032
2091
  if (typeof shell === 'string') {
2033
2092
  const html = composeIntoShell(shell, {
2034
2093
  headTags: `${resolvedHeadTags}\n${prerender.meta}`,
2035
- headEnd: [fontLinks, brenderStyle, emotionStyle].filter(Boolean).join('\n'),
2094
+ headEnd: [fontLinks, facesStyle, brenderStyle, emotionStyle].filter(Boolean).join('\n'),
2036
2095
  body: bodyHtml,
2037
2096
  depth: Number.isInteger(options.depth) ? options.depth : 0,
2038
2097
  bootScripts: prerender.blocks,
@@ -2046,14 +2105,14 @@ export const renderPage = async (data, route = '/', options = {}) => {
2046
2105
  return { html, route, brKeyCount, shell: true, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
2047
2106
  }
2048
2107
 
2049
- // (The empty line after the font links held a @font-face <style> that
2050
- // splitCapturedCSS never fills — @font-face stays in the class stream.)
2108
+ // The line after the font links holds the page's @font-face rules in a
2109
+ // <style data-smbls-faces> of their own (see facesStyle above).
2051
2110
  const html = `<!DOCTYPE html>
2052
2111
  <html lang="${htmlLang}"${htmlTheme ? ` data-theme="${escapeAttr(htmlTheme)}"` : ''}>
2053
2112
  <head>
2054
2113
  ${resolvedHeadTags}${isr && isr.clientScript && hydrate ? `\n${prerender.meta}` : ''}
2055
2114
  ${fontLinks}
2056
-
2115
+ ${facesStyle}
2057
2116
  ${brenderStyle}
2058
2117
  ${emotionStyle}
2059
2118
  </head>
@@ -2138,9 +2197,13 @@ const generateFontLinks = (ds) => {
2138
2197
  // (@symbo.ls/fetch early.js "Stale pages"), by app code
2139
2198
  // (`el.context.prerender`, smbls prerender.js) and by a host that compares
2140
2199
  // it with its data's last change.
2141
- export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time } = {}) => {
2142
- const registry = adoptDom && brRegistry && Object.keys(brRegistry).length
2143
- ? brenderRegistryBlock(brRegistry)
2200
+ // `registryFormat: 'tree'` writes the registry as a tree (@symbo.ls/utils
2201
+ // encodeBrenderRegistry, XMA round 8, E13) — the same data, a third of the
2202
+ // bytes — in its own block; a registry that cannot be one stays paths.
2203
+ export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time, registryFormat = 'paths' } = {}) => {
2204
+ const has = adoptDom && brRegistry && Object.keys(brRegistry).length
2205
+ const registry = has
2206
+ ? (registryFormat === 'tree' && brenderRegistryTreeBlock(brRegistry)) || brenderRegistryBlock(brRegistry)
2144
2207
  : ''
2145
2208
  const seed = createEarlySeedBlock(fetchSeed)
2146
2209
  return {