@symbo.ls/brender 3.14.667 → 3.14.668

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,19 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.668
4
+
5
+ ### Patch Changes
6
+
7
+ - XMA round 3 (performance): a pre-rendered page is adopted in short tasks; the first render puts rows on screen whole; the design system is prepared at build time and adopted by the page; `smbls build` adds V8 compile hints and (opt-in, `build.modulePreload`) lazy-module preloads; brender's preload goes after the import map; pre-built pages can be flagged stale (`fetch.seed.isStale` / `maxAge` / `onRefresh`, `<html data-smbls-stale>`); routes without their own page answer with 200.html on every deploy target; the app starts in the address's language (`addressLang`) and adopts the seeded answers; `DropdownDisclosure` and Dialog `locked`; frank-audit accepts DOM work on a bridged library's and a parsed document's own nodes; a server render waits for its own labels (label loads in flight are per app); mermaid renderProjectHtml takes a caller-resolved head (`options.metadata`) and `metadata.source` is never a tag.
8
+ - Updated dependencies
9
+ - @symbo.ls/css@3.14.780
10
+ - @symbo.ls/default-config@3.14.788
11
+ - @symbo.ls/element@3.14.806
12
+ - @symbo.ls/scratch@3.14.790
13
+ - smbls@3.14.840
14
+ - @symbo.ls/utils@3.14.789
15
+ - @symbo.ls/fetch@3.14.667
16
+
3
17
  ## 3.14.667
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -157,6 +157,8 @@ Options:
157
157
  | `adoptDom` | `false` | Ship the element-path registry, so the client adopts the markup instead of replacing it (`smbls build` passes it unless `brender: { hydrate: false }`) |
158
158
  | `isr` | — | `{ clientScript }`: a standalone document that boots this client bundle |
159
159
  | `hydrate` | `true` | With `isr`: boot the client over the markup (`false`: the legacy swap script) |
160
+ | `stamp` | the render time (ISO) | The build's data stamp, written into `<meta name="brender-stamp">` — a string or a number |
161
+ | `time` | now | The build time in ms, written as its `data-time`. A build passes one `stamp` and `time` to every page |
160
162
  | `nonce` | — | Content-Security-Policy nonce for the inline scripts the `isr` document still has (the label seed, the legacy swap script) — a page rendered into a shell has none |
161
163
  | `themeColor` | | See render.js. |
162
164
 
@@ -178,9 +180,12 @@ No inline script — a strict Content-Security-Policy (`script-src 'self'`, no `
178
180
  | `data-br="br-N"` | each server element node | The node's key |
179
181
  | `<script type="application/json" data-brender="registry">{…}</script>` | after the markup | Element path → `data-br` key (`__root` for the app's root); adoption pages only |
180
182
  | `<script type="application/json" data-fetch-seed>[…]</script>` | after the markup | The anonymous GET answers the render got (`@symbo.ls/fetch`), adopted by the client request that sends exactly the same request |
183
+ | `<meta name="brender-stamp" content="<stamp>" data-time="<ms>">` | `<head>`, after the mark | The build the page comes from: `stamp` (the `stamp` option, else the render time as an ISO string) and the render time in ms. App code reads it as `el.context.prerender` (`{ stamp, time }`) |
181
184
 
182
185
  The runtime also reads what pages from brender up to 3.14.664 carry — the inline `window.__BRENDER__` flag, `window.__BR_REGISTRY__`, `window.__BRENDER_HYDRATE__` — and, when a policy blocked that flag, takes a `data-br` element directly in the mount node for the mark. An smbls runtime older than this format does not know the `<meta>`: keep `smbls` and `@symbo.ls/brender` at the same release.
183
186
 
187
+ **The data is the build's.** A pre-rendered page shows the answers its render got until it is rendered again — a `smbls build` page until the next build. A page flagged stale (`<html data-smbls-stale>` from the host, or the project's `fetch.seed.isStale(stamp)` / `fetch.seed.maxAge`) shows them at once and then, after the adoption, asks again and swaps in what changed; `fetch.seed: false` keeps the answers out of the page. See `@symbo.ls/fetch`, "A pre-built page shows the data of its build".
188
+
184
189
  What is recorded for the seed: a GET whose only headers are `Accept` / `Accept-Language` — so an anonymous read of a keyed backend counts when `fetch.reads` moves its key into the query string (`reads: { query: { apikey: 'apikey' }, rpc: 'get' }`, read-only RPCs as GETs) — with an ok JSON or text answer, at most 256 KB each and 1 MB per page. Never a POST, a call with `Authorization` or another header, or anything from a render that overlapped another.
185
190
 
186
191
  ### `prefetchPageData(data, route, options?)` (deprecated)
@@ -288,16 +293,31 @@ Output directory defaults to `dist-brender` (or `brenderDistDir` from `symbols.j
288
293
 
289
294
  Param routes (e.g. `/blog/:id`) are skipped unless `brender.paths` lists addresses for them.
290
295
 
291
- `smbls brender` and `smbls build` share one route loop: the same `brender` settings from `symbols.json` (`context`, `paths`, `exclude`, `strict`, `eagerImages`, `timeout`), `file:` URLs resolved into the output (a `file:` URL that names no project file fails its route), and the same diagnostics. `smbls brender` writes standalone pages in a directory layout (`/about` → `about/index.html`), each with the CSS of every page it rendered.
296
+ `smbls brender` and `smbls build` share one route loop: the same `brender` settings from `symbols.json` (`context`, `paths`, `exclude`, `strict`, `eagerImages`, `timeout`, `stamp`), `file:` URLs resolved into the output (a `file:` URL that names no project file fails its route), and the same diagnostics. `smbls brender` writes standalone pages in a directory layout (`/about` → `about/index.html`), each with the CSS of every page it rendered.
292
297
 
293
298
  ### `smbls build`
294
299
 
295
300
  With brender on (the default), `smbls build` pre-renders every static route INTO the bundler's `dist/index.html`:
296
301
 
297
302
  - `/about` → `about.html`, `/` → `index.html`, `/*` → `404.html` rendered at `/404`. Two routes that end in one file (`/404` and `/*`) are written once — the first in page order — and the other is reported.
298
- - The bundler's own document is kept as **`200.html`** — serve it for client-side routes (an SPA rewrite, a Worker's catch-all), never `index.html`, which is now the home page. A `200.html` already in the output (`public/`) is not replaced.
303
+ - The bundler's own document is kept as **`200.html`** — serve it for client-side routes, never `index.html`, which is now the home page (see below). A `200.html` already in the output (`public/`) is not replaced.
299
304
  - Every `file:` URL in a page (a `new URL('…', import.meta.url)` asset) is mapped to the file the bundler emitted from the same source (found by content), or copied into the output under a content hash. A `file:` URL that names no project file fails the build.
300
305
  - A page made with failed data or label requests, or with raw label keys, is reported; `smbls build --strict` (or `brender.strict`) fails the build on it.
306
+ - Every page carries the data of the build time until the next build, and says which build that is: one stamp per build (`<meta name="brender-stamp">`). `brender.stamp` in `symbols.json` (a string or a number), else `brender.stamp` in the project's `config.js` — a value, or a function `({ time, data }) => stamp` run once per build (it may be async: the data's own version, such as the time of its latest change) — else the build time. A stamp function that throws fails the build. How a page learns that its data moved on: `@symbo.ls/fetch`, "A pre-built page shows the data of its build".
307
+
308
+ #### Routes without their own page: 200.html
309
+
310
+ **Serve 200.html, never another page, for a route without its own page** — a route with parameters that `brender.paths` does not list (`/campaigns/:id`), a page with `prerender: false`, an excluded or failed one. `index.html` is now the HOME page: a host's single-page fallback that serves it shows the home page first, then the client throws its nodes away and draws the right page (XMA: `removed: 439` on a campaign page). A host's 404 handling serves the pre-rendered not-found page, with a 404 status, under the right page's client. `smbls build` writes the rules into `<outDir>/_redirects` (after the project's own rules from `public/`; `brender.redirects: false` writes none). A pre-rendered address that a route pattern also matches gets a rule to itself first — Cloudflare follows a rule even where a file matches. The rule per host:
311
+
312
+ | Host | The rule | Written by |
313
+ |------|----------|------------|
314
+ | Cloudflare Workers (static assets) | `_redirects` in the output: each route without its own page → `/200` with status `200`; `assets.not_found_handling`: `"404-page"` with a 404 page, else `"none"` — never `"single-page-application"`, which serves index.html | `smbls build` (`_redirects`), `smbls deploy --target workers` (`wrangler.jsonc`) |
315
+ | Cloudflare Pages | the same `_redirects`; every other address gets 404.html. Without a 404.html, Pages falls back to index.html: give the project a `/*` page | `smbls build` |
316
+ | Netlify | the same `_redirects` (a file always wins over a rewrite there) | `smbls build` |
317
+ | Vercel | `vercel.json`: `"cleanUrls": true` and `"rewrites": [{ "source": "/(.*)", "destination": "/200" }]` — Vercel checks the filesystem first, so every pre-rendered page keeps its own file; an unknown address gets the app's not-found page with status 200 | `smbls deploy --provider vercel` |
318
+ | GitHub Pages | no rewrites: an address with no file gets 404.html (status 404). Without a 404 page of the project's own, copy 200.html to 404.html; better, pre-render the routes with parameters (`brender.paths`) | `smbls deploy --provider github-pages` (the workflow) |
319
+ | Surge | serves 200.html for every unknown address by itself | — |
320
+ | Your own server or Worker | try the file, then `<path>.html`, then 200.html — nginx: `try_files $uri $uri.html /200.html;`; a Worker: `env.ASSETS.fetch(new URL('/200', request.url))` | — |
301
321
 
302
322
  ### symbols.json
303
323
 
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{createEnv as e}from"./env.js";import{resetKeys as r,assignKeys as t,mapKeysToElements as o}from"./keys.js";import{loadProject as m,loadAndRenderAll as a}from"./load.js";import{render as p,renderElement as n,renderRoute as d,renderPage as i,resetGlobalCSSCache as l,getAccumulatedEmotionCSS as c,replaceEmotionCSS as f,adoptionScripts as s,prerenderParts as S}from"./render.js";import{extractMetadata as g,generateHeadHtml as h}from"./metadata.js";import{collectBrNodes as E,hydrate as P}from"./hydrate.js";import{generateSitemap as u}from"./sitemap.js";import{prefetchPageData as y,injectPrefetchedState as C}from"./prefetch.js";import{composeIntoShell as x}from"./shell.js";var G={createEnv:e,resetKeys:r,assignKeys:t,mapKeysToElements:o,loadProject:m,loadAndRenderAll:a,render:p,renderElement:n,renderRoute:d,renderPage:i,resetGlobalCSSCache:l,getAccumulatedEmotionCSS:c,replaceEmotionCSS:f,extractMetadata:g,generateHeadHtml:h,collectBrNodes:E,hydrate:P,generateSitemap:u,prefetchPageData:y,injectPrefetchedState:C,composeIntoShell:x,adoptionScripts:s,prerenderParts:S};export{s as adoptionScripts,t as assignKeys,E as collectBrNodes,x as composeIntoShell,e as createEnv,G as default,g as extractMetadata,h as generateHeadHtml,u as generateSitemap,c as getAccumulatedEmotionCSS,P as hydrate,C as injectPrefetchedState,a as loadAndRenderAll,m as loadProject,o as mapKeysToElements,y as prefetchPageData,S as prerenderParts,p as render,n as renderElement,i as renderPage,d as renderRoute,f as replaceEmotionCSS,l as resetGlobalCSSCache,r as resetKeys};
1
+ import{createEnv as e}from"./env.js";import{resetKeys as r,assignKeys as t,mapKeysToElements as o}from"./keys.js";import{loadProject as m,loadAndRenderAll as a}from"./load.js";import{render as p,renderElement as d,renderRoute as n,renderPage as i,resetGlobalCSSCache as l,getAccumulatedEmotionCSS as c,replaceEmotionCSS as f,adoptionScripts as s,prerenderParts as S,buildStamp as g}from"./render.js";import{extractMetadata as h,generateHeadHtml as u}from"./metadata.js";import{collectBrNodes as E,hydrate as P}from"./hydrate.js";import{generateSitemap as y}from"./sitemap.js";import{prefetchPageData as C,injectPrefetchedState as x}from"./prefetch.js";import{composeIntoShell as A}from"./shell.js";var I={createEnv:e,resetKeys:r,assignKeys:t,mapKeysToElements:o,loadProject:m,loadAndRenderAll:a,render:p,renderElement:d,renderRoute:n,renderPage:i,resetGlobalCSSCache:l,getAccumulatedEmotionCSS:c,replaceEmotionCSS:f,extractMetadata:h,generateHeadHtml:u,collectBrNodes:E,hydrate:P,generateSitemap:y,prefetchPageData:C,injectPrefetchedState:x,composeIntoShell:A,adoptionScripts:s,prerenderParts:S,buildStamp:g};export{s as adoptionScripts,t as assignKeys,g as buildStamp,E as collectBrNodes,A as composeIntoShell,e as createEnv,I as default,h as extractMetadata,u as generateHeadHtml,y as generateSitemap,c as getAccumulatedEmotionCSS,P as hydrate,x as injectPrefetchedState,a as loadAndRenderAll,m as loadProject,o as mapKeysToElements,C as prefetchPageData,S as prerenderParts,p as render,d as renderElement,i as renderPage,n as renderRoute,f as replaceEmotionCSS,l as resetGlobalCSSCache,r as resetKeys};
@@ -1,21 +1,21 @@
1
- import{resolve as u,join as Fe,dirname as j}from"path";import{existsSync as b,unlinkSync as Be,readFileSync as J,realpathSync as Ge}from"fs";import{resolvePackageEntry as Ke}from"./packageEntry.js";import{tmpdir as Ne}from"os";import{randomBytes as He}from"crypto";import{createRequire as oe}from"module";import{pathToFileURL as Ue}from"url";import{createEnv as ue}from"./env.js";import{resetKeys as de,assignKeys as me,mapKeysToElements as ge}from"./keys.js";import{extractMetadata as ne,generateHeadHtml as We}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Je}from"./shell.js";import{fetchSSRTranslations as ze}from"./prefetch.js";import{createEarlySeedScript as Ye}from"@symbo.ls/fetch";import{headerEntries as Xe}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as Ve,escapeRegExp as Qe,jsonForScript as Ze,polyglotFallbackLang as z,polyglotLangStorageKey as et,polyglotAddressLang as tt,polyglotStartupScript as ot}from"@symbo.ls/utils";let Y=null;const nt=async()=>{if(Y)return Y;try{return Y=(await import("@symbo.ls/funcql")).funcqlPlugin,Y}catch{return null}};import"linkedom";import{createEarlySeedBlock as st}from"@symbo.ls/fetch";import{reset as rt}from"@symbo.ls/css";const X=(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},se=(e,t)=>z(e?.lang,t),he=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const i=X(r,e);if(i!==void 0)return i}}if(n?.translations){const r=n.translations[t];if(r){const i=X(r,e);if(i!==void 0)return i}}const s=z(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const i=X(r,e);if(i!==void 0)return i}}},ct=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=he(e,t||se(s,n),n,s);if(r!==void 0)return r;const i=ie(ce);return n&&i&&i.labelKeys.add(e),e},ye=function(){return se(this?.state?.root,this?.context?.polyglot)},L=(e,t=new WeakMap)=>{if(e===null||typeof e!="object")return e;if(t.has(e))return t.get(e);if(Array.isArray(e)){const n=[];t.set(e,n);for(const s of e)n.push(typeof s=="object"&&s!==null?L(s,t):s);return n}const o={};t.set(e,o);for(const n of Object.keys(e)){const s=e[n];o[n]=typeof s=="object"&&s!==null?L(s,t):s}return o};let A=null;try{import.meta.url&&(A=oe(import.meta.url))}catch{}const it=()=>{if(!import.meta.url||!A)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=Ge(new URL(".",import.meta.url).pathname),t=u(e,"../..");if(b(u(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=A.resolve("smbls/package.json");n=j(l)}catch{let l=e;for(;l!==j(l);){const g=u(l,"node_modules","smbls");if(b(u(g,"package.json"))){n=g;break}l=j(l)}}let s=A;if(n)try{s=oe(u(n,"package.json"))}catch{}let r,i=process.cwd();for(;i!==j(i);){if(b(u(i,"package.json"))){r=i;break}i=j(i)}let d=s;if(r)try{d=oe(u(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:d}},O=(e,t)=>{const o=e.isMonorepo?[A]:[e._smblsRequire,e._projectRequire,A].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},yo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages",t,...o);const n=O(e,`${t}/package.json`);return n?u(j(n),...o):null},bo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"plugins",t,...o);const n=O(e,`@symbo.ls/${t}/package.json`);return n?u(j(n),...o):null},re=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=u(e.monorepoRoot,s,t,...o);if(b(r))return r}return null}const n=O(e,`@symbo.ls/${t}/package.json`);return n?u(j(n),...o):null},be=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages","domql","packages",t,...o);const n=O(e,`@symbo.ls/${t}/package.json`);return n?u(j(n),...o):null};let V=null;const Se="smbls/ssr",at=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},ke=()=>(V||(V=lt().catch(e=>{throw V=null,e})),V),lt=async()=>{const e=it();let t;if(A&&e.isMonorepo)t=u(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await at()}catch(a){f=a}if(!A)throw new Error(`brender: ${Se} is not available in this bundled runtime (${f.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`);const c=e.smblsRoot&&u(e.smblsRoot,"src","createDomql.js");if(c&&b(c))t=c;else throw new Error(`brender: cannot load ${Se} (${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=Fe(Ne(),`br_createDomql_${He(6).toString("hex")}.mjs`),s=Ke,r=e.isMonorepo?await import(Ue(u(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,i=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,d=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},c=>{if(d.test(c.path))return;const a=c.path.split("/"),p=c.path.startsWith("@")?a.slice(0,2).join("/"):a[0];if(!i.has(p))return;const m=r.resolveWorkspaceSource(c.path,c.importer||t);return m?{path:m}:{errors:[{text:`"${c.path}" names no source file in the workspace`}]}}),f.onLoad({filter:/[\\/]dist[\\/]/},c=>r.isWorkspaceDist(c.path)?{errors:[{text:`${c.path} is a BUILD of a workspace package \u2014 the render bundles source`}]}:void 0)),f.onResolve({filter:/^smbls/},c=>{const a=c.path.replace(/^smbls\/?/,""),p=e.isMonorepo?u(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!p)return;if(!a){const S=s(p);return S?{path:S}:void 0}const m=u(p,a);if(b(m))return{path:m};if(b(m+".js"))return{path:m+".js"};const y=u(m,"index.js");if(b(y))return{path:y}}),f.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const a=u(e.monorepoRoot,"packages","domql","src","index.js");if(b(a))return{path:a};const p=u(e.monorepoRoot,"packages","domql","index.js");if(b(p))return{path:p}}else try{const a=_require.resolve("domql/package.json"),p=s(j(a));if(p)return{path:p}}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 m of["packages","plugins"]){const y=u(e.monorepoRoot,m,a,"src","index.js");if(b(y))return{path:y};const S=u(e.monorepoRoot,m,a,"index.js");if(b(S))return{path:S}}const p=u(e.monorepoRoot,"packages","default-config","blank","index.js");if(a==="default-config"&&b(p))return{path:p}}else{const p=re(e,a,"src","index.js");if(p&&b(p))return{path:p};const m=re(e,a,"index.js");if(m&&b(m))return{path:m};if(a==="default-config"){const y=re(e,"default-config","blank","index.js");if(y&&b(y))return{path:y}}}}),f.onResolve({filter:/^@domql\//},c=>{const a=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const p=u(e.monorepoRoot,"packages","domql","packages",a,"src","index.js");if(b(p))return{path:p};const m=u(e.monorepoRoot,"packages","domql","packages",a,"index.js");if(b(m))return{path:m}}else{const p=be(e,a,"src","index.js");if(p&&b(p))return{path:p};const m=be(e,a,"index.js");if(m&&b(m))return{path:m}}}),f.onResolve({filter:/^css-in-props/},c=>{let a;if(e.isMonorepo)a=u(e.monorepoRoot,"packages","css-in-props");else{const y=O(e,"css-in-props/package.json");y&&(a=j(y))}if(!a)return;const p=c.path.replace(/^css-in-props\/?/,"");if(p){const y=u(a,p),S=u(y,"index.js");if(b(S))return{path:S};if(b(y+".js"))return{path:y+".js"};if(b(y))return{path:y}}const m=s(a);if(m)return{path:m}}),f.onResolve({filter:/^@emotion\//},c=>{let a;if(e.isMonorepo)a=u(e.monorepoRoot,"node_modules",c.path);else{const p=O(e,`${c.path}/package.json`);if(!p)return;a=j(p)}if(b(a)){const p=u(a,"package.json");if(b(p))try{const m=JSON.parse(J(p,"utf8")),y=m.module||m.main||"dist/emotion-css.esm.js";return{path:u(a,y)}}catch{}return{path:a}}}),f.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const a=u(c.resolveDir,c.path);if(b(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?[u(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[u(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[u(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[u(e.smblsRoot,"..","..","node_modules")]:[]].filter(f=>b(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{Be(n)}catch{}return g},ft={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"}},pt=(e,t)=>{const o={};return Re(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},Re=(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)},ut=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let P=0;const we=Symbol.for("brender.fetchTracked"),xe=()=>P,dt=256*1024,mt=1024*1024,gt=new Set(["accept-language","accept"]),je={renders:0,record:null},ht=50,ce={renders:0,record:null},_e=(e,t)=>{e.renders++,e.renders===1?e.record=t():e.record&&(e.record.tainted=!0);const o=e.record;return{record:o,close:()=>{e.renders--,e.record===o&&(e.record=null)}}},ie=e=>e.record&&!e.record.tainted?e.record:null,yt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},ae=(e,t,o)=>{!e||e.tainted||e.failures.length>=ht||e.failures.push({url:yt(t),reason:o})},Ee=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),bt=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=Ee(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),i={},d=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,c]of Xe(l))g&&c==null||(i[String(f).toLowerCase()]=String(c))};return n&&d(t.headers),o&&d(o.headers),{url:s,method:r,headers:i}},St=(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>dt||e.bytes+i.length>mt||(e.bytes+=i.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:i}))},()=>{}))},kt=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[we])return;const t=function(...o){P++;let n=!1;const s=()=>{n||(n=!0,P--)},r=c=>{P++,c.then(()=>{P--},()=>{P--})},i=ie(je),d=ie(ce);let l=null;if(i)try{l=bt(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!gt.has(c)))&&(l=null)}catch{l=null}const g=()=>Ee(o[0]);let f;try{f=e.apply(this,o)}catch(c){throw ae(d,g(),c&&c.message||"failed"),s(),c}return f&&typeof f.then=="function"?f.then(c=>{l&&St(i,l,c,r),d&&c&&c.ok===!1&&ae(d,g(),"HTTP "+c.status),s()},c=>{ae(d,g(),c&&c.message||"network error"),s()}):s(),f};t[we]=!0,globalThis.fetch=t},So=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=Ze(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
- `+Ye(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},Rt=3e4,wt=2147483647;let ve=Promise.resolve();const Te=()=>{},xt=e=>{const t=ve.then(e);return ve=t.then(Te,Te),t},jt=e=>{if(e===void 0)return Rt;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,wt)},_t=(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},Et=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},vt=e=>{let t=!1;return()=>{t||(t=!0,e())}},Le=(e,t={})=>{let o;try{o=jt(t.timeout)}catch(n){return Promise.reject(n)}return xt(()=>Tt(e,t,o))},Tt=async(e,t,o)=>{await ke();const n={closed:!1,undo:[]},s=Lt(e,t,n);if(!o)return s;let r;const i=new Promise((d,l)=>{r=setTimeout(()=>{Et(n),l(_t(t,o))},o)});try{return await Promise.race([s,i])}finally{clearTimeout(r)}},Lt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:i,prefetch:d=!1}=t,l=s||n;kt();const g=_e(ce,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await $t(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:i,prefetch:d},g.record,o)}finally{g.close()}},$t=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},i,d)=>{const l=()=>{if(d.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await ze(e)}catch(m){d.closed||console.warn("[brender] SSR translation fetch failed:",m.message)}l()}const{window:f,document:c}=ue(),a=c.body;f.location.pathname=o;const p=[Q("document",c),Q("location",f.location),Q("localStorage",f.localStorage),Q("sessionStorage",f.sessionStorage)].map(vt);d.undo.push(...p);try{const{createDomqlElement:m}=await ke();l();const y=L(e.app||{}),S={...e.config||{}};e.polyglot&&!S.polyglot&&(S.polyglot=e.polyglot),e.fetch&&!S.fetch&&(S.fetch=e.fetch),e.router&&!S.router&&(S.router=e.router);for(const k of Mt)e[k]!=null&&S[k]==null&&(S[k]=e[k]);const h=S.polyglot?{...S.polyglot}:void 0,q=tt(o,h);q&&f.localStorage.setItem(et(h),q),g&&h&&(h.translations={...h.translations||{},...g});const _=L(e.state||{});(g||h)&&(_.root||(_.root={}),h&&(_.root.lang=z(_.root.lang,h)),g&&(_.root.translations={..._.root.translations||{},...g})),rt();const F={};for(const k in e){if(ut.has(k))continue;const w=e[k];F[k]=w&&typeof w=="object"?L(w):w}const $={...F,state:_,...n?{state:{..._,...n}}:{},dependencies:L(e.dependencies||{}),components:L(e.components||{}),snippets:L(e.snippets||{}),pages:L(e.pages||{}),functions:{...e.functions||{},polyglot:ct,getActiveLang:ye,getLang:ye},methods:e.methods||{},designSystem:L(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...S,...h?{polyglot:h}:{},document:c,window:f,parent:{node:a},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,...s||{}},B=await nt();l(),B&&($.plugins=$.plugins||[],$.plugins.some(k=>k.name==="funcql")||$.plugins.push(B)),de();const G=_e(je,()=>({entries:[],bytes:0,tainted:!1})),D=G.record;let v;try{v=await m(y,$),l();const k=Date.now(),w=r?2e3:50,te=Oe=>new Promise(Pe=>setTimeout(Pe,Oe));for(await te(Math.min(50,w));r&&!d.closed&&xe()>0&&Date.now()-k<w;)await te(10),xe()===0&&await te(Math.max(0,Math.min(50,w-(Date.now()-k))));l()}finally{G.close()}const C=D&&!D.tainted?D.entries.slice():[];me(a);const K=ge(v),{elementPath:N}=await import("@symbo.ls/element");l();const M=pt(v,N),I=ne(e,t,v,v?.state),E=[],H=c.head||c.querySelector("head");if(H)for(const k of H.querySelectorAll("style")){if(k.sheet&&k.sheet.cssRules)for(const w of k.sheet.cssRules)w.cssText&&E.push(w);if(!E.length){const w=k.textContent||"";w&&E.push({cssText:w})}}const pe=E.map(k=>k.cssText),{globalCSS:R,classRules:x}=Ft(E);let T=qe(a.innerHTML);const U=v?.state?.root,W=se(U,h),ee=At(g,U);ee&&(T=Ae(T,$e(ee,W,h)));const Ie=i&&!i.tainted?{failedRequests:i.failures.slice(),rawLabelKeys:Dt(i.labelKeys,T,W,h,U)}:null;return{html:T,metadata:I,registry:K,brRegistry:M,element:v,emotionCSS:pe,globalCSS:R,classRules:x,fetchSeed:C,document:c,window:f,ssrTranslations:ee||void 0,activeLang:W,diagnostics:Ie}}finally{for(const m of p)m()}},Q=(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]})},le=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,fe=e=>e&&typeof e.parse=="function"?e.parse():e,At=(e,t)=>{const o=fe(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const i=fe(s[r]);i&&typeof i=="object"&&(n[r]={...n[r]||{},...i})}return Object.keys(n).length?n:null},$e=(e,t,o)=>e[t]||e[z(void 0,o)]||Object.values(e)[0]||{},Ae=(e,t)=>e.replace(le,(o,n)=>X(t,n.trim())??o),qt=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Ct=(e,t)=>new RegExp(`(?:^|[^\\w.-])${Qe(t)}(?![\\w-]|\\.\\w)`).test(e),Dt=(e,t,o,n,s)=>{const r=new Set,i=t.replace(qt,"");for(const d of e)/\s/.test(d)||he(d,o,n,s)===void 0&&Ct(i,d)&&r.add(d);for(const d of i.matchAll(le))r.add(d[1].trim());return[...r]},ko=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=ue(),r=s.body,{create:i}=await import("@symbo.ls/element"),d=await import("@symbo.ls/utils"),l={...ft,...o.components||{}},g={...d,...o.utils||{},...o.functions||{}};de();let f;try{f=i(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}me(r);const c=f?ge(f):{};return{html:qe(r.innerHTML),registry:c,element:f}},qe=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}),Mt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],It=/\.[A-Za-z_\\-]/,Ot=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!It.test(t)},Pt=/^\s*(:root|\[data-theme|\[data-skin)/,Ce=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 Ot(e.selectorText)?Pt.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(Ce));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Ft=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Ce(n);s?t[s].push(n.cssText):n&&n.cssText&&o.push(n.cssText)}return{globalCSS:{rootRule:t.root.join(`
1
+ import{resolve as u,join as Fe,dirname as _}from"path";import{existsSync as y,unlinkSync as Be,readFileSync as U,realpathSync as Ge}from"fs";import{resolvePackageEntry as Ke}from"./packageEntry.js";import{tmpdir as Ne}from"os";import{randomBytes as He}from"crypto";import{createRequire as re}from"module";import{pathToFileURL as Ue}from"url";import{createEnv as ge}from"./env.js";import{resetKeys as he,assignKeys as ye,mapKeysToElements as be}from"./keys.js";import{extractMetadata as ae,generateHeadHtml as We}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Je}from"./shell.js";import{fetchSSRTranslations as ze}from"./prefetch.js";import{createEarlySeedScript as Ye}from"@symbo.ls/fetch";import{headerEntries as Xe}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as Ve,brenderStampMeta as Qe,escapeRegExp as Ze,jsonForScript as et,polyglotFallbackLang as X,polyglotLangStorageKey as tt,polyglotAddressLang as ot,polyglotStartupScript as nt}from"@symbo.ls/utils";let V=null;const st=async()=>{if(V)return V;try{return V=(await import("@symbo.ls/funcql")).funcqlPlugin,V}catch{return null}};import"linkedom";import{createEarlySeedBlock as rt}from"@symbo.ls/fetch";import{reset as at}from"@symbo.ls/css";const Q=(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},Z=(e,t)=>X(e?.lang,t),Se=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const i=Q(r,e);if(i!==void 0)return i}}if(n?.translations){const r=n.translations[t];if(r){const i=Q(r,e);if(i!==void 0)return i}}const s=X(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const i=Q(r,e);if(i!==void 0)return i}}},it=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=Se(e,t||Z(s,n),n,s);if(r!==void 0)return r;const i=fe(le);return n&&i&&i.labelKeys.add(e),e},we=function(){return Z(this?.state?.root,this?.context?.polyglot)},T=(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?T(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?T(s,t):s}return o};let $=null;try{import.meta.url&&($=re(import.meta.url))}catch{}const ct=()=>{if(!import.meta.url||!$)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=Ge(new URL(".",import.meta.url).pathname),t=u(e,"../..");if(y(u(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=$.resolve("smbls/package.json");n=_(l)}catch{let l=e;for(;l!==_(l);){const g=u(l,"node_modules","smbls");if(y(u(g,"package.json"))){n=g;break}l=_(l)}}let s=$;if(n)try{s=re(u(n,"package.json"))}catch{}let r,i=process.cwd();for(;i!==_(i);){if(y(u(i,"package.json"))){r=i;break}i=_(i)}let d=s;if(r)try{d=re(u(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:d}},P=(e,t)=>{const o=e.isMonorepo?[$]:[e._smblsRequire,e._projectRequire,$].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},wo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages",t,...o);const n=P(e,`${t}/package.json`);return n?u(_(n),...o):null},ko=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"plugins",t,...o);const n=P(e,`@symbo.ls/${t}/package.json`);return n?u(_(n),...o):null},ie=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=u(e.monorepoRoot,s,t,...o);if(y(r))return r}return null}const n=P(e,`@symbo.ls/${t}/package.json`);return n?u(_(n),...o):null},ke=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages","domql","packages",t,...o);const n=P(e,`@symbo.ls/${t}/package.json`);return n?u(_(n),...o):null};let ee=null;const Re="smbls/ssr",lt=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},xe=()=>(ee||(ee=ft().catch(e=>{throw ee=null,e})),ee),ft=async()=>{const e=ct();let t;if($&&e.isMonorepo)t=u(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await lt()}catch(c){f=c}if(!$)throw new Error(`brender: ${Re} 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 a=e.smblsRoot&&u(e.smblsRoot,"src","createDomql.js");if(a&&y(a))t=a;else throw new Error(`brender: cannot load ${Re} (${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=Fe(Ne(),`br_createDomql_${He(6).toString("hex")}.mjs`),s=Ke,r=e.isMonorepo?await import(Ue(u(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,i=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,d=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},a=>{if(d.test(a.path))return;const c=a.path.split("/"),p=a.path.startsWith("@")?c.slice(0,2).join("/"):c[0];if(!i.has(p))return;const m=r.resolveWorkspaceSource(a.path,a.importer||t);return m?{path:m}:{errors:[{text:`"${a.path}" names no source file in the workspace`}]}}),f.onLoad({filter:/[\\/]dist[\\/]/},a=>r.isWorkspaceDist(a.path)?{errors:[{text:`${a.path} is a BUILD of a workspace package \u2014 the render bundles source`}]}:void 0)),f.onResolve({filter:/^smbls/},a=>{const c=a.path.replace(/^smbls\/?/,""),p=e.isMonorepo?u(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!p)return;if(!c){const S=s(p);return S?{path:S}:void 0}const m=u(p,c);if(y(m))return{path:m};if(y(m+".js"))return{path:m+".js"};const b=u(m,"index.js");if(y(b))return{path:b}}),f.onResolve({filter:/^domql$/},a=>{if(e.isMonorepo){const c=u(e.monorepoRoot,"packages","domql","src","index.js");if(y(c))return{path:c};const p=u(e.monorepoRoot,"packages","domql","index.js");if(y(p))return{path:p}}else try{const c=_require.resolve("domql/package.json"),p=s(_(c));if(p)return{path:p}}catch{}}),f.onResolve({filter:/^@symbo\.ls\//},a=>{const c=a.path.replace("@symbo.ls/","");if(c==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(e.isMonorepo){for(const m of["packages","plugins"]){const b=u(e.monorepoRoot,m,c,"src","index.js");if(y(b))return{path:b};const S=u(e.monorepoRoot,m,c,"index.js");if(y(S))return{path:S}}const p=u(e.monorepoRoot,"packages","default-config","blank","index.js");if(c==="default-config"&&y(p))return{path:p}}else{const p=ie(e,c,"src","index.js");if(p&&y(p))return{path:p};const m=ie(e,c,"index.js");if(m&&y(m))return{path:m};if(c==="default-config"){const b=ie(e,"default-config","blank","index.js");if(b&&y(b))return{path:b}}}}),f.onResolve({filter:/^@domql\//},a=>{const c=a.path.replace("@symbo.ls/","");if(e.isMonorepo){const p=u(e.monorepoRoot,"packages","domql","packages",c,"src","index.js");if(y(p))return{path:p};const m=u(e.monorepoRoot,"packages","domql","packages",c,"index.js");if(y(m))return{path:m}}else{const p=ke(e,c,"src","index.js");if(p&&y(p))return{path:p};const m=ke(e,c,"index.js");if(m&&y(m))return{path:m}}}),f.onResolve({filter:/^css-in-props/},a=>{let c;if(e.isMonorepo)c=u(e.monorepoRoot,"packages","css-in-props");else{const b=P(e,"css-in-props/package.json");b&&(c=_(b))}if(!c)return;const p=a.path.replace(/^css-in-props\/?/,"");if(p){const b=u(c,p),S=u(b,"index.js");if(y(S))return{path:S};if(y(b+".js"))return{path:b+".js"};if(y(b))return{path:b}}const m=s(c);if(m)return{path:m}}),f.onResolve({filter:/^@emotion\//},a=>{let c;if(e.isMonorepo)c=u(e.monorepoRoot,"node_modules",a.path);else{const p=P(e,`${a.path}/package.json`);if(!p)return;c=_(p)}if(y(c)){const p=u(c,"package.json");if(y(p))try{const m=JSON.parse(U(p,"utf8")),b=m.module||m.main||"dist/emotion-css.esm.js";return{path:u(c,b)}}catch{}return{path:c}}}),f.onResolve({filter:/\.json$/},a=>{if(a.resolveDir){const c=u(a.resolveDir,a.path);if(y(c))return{path:c}}}),f.onLoad({filter:/smbls\/.*options\.js$/},async a=>{if(!a.path.includes("smbls/")||!a.path.endsWith("options.js"))return;let c=U(a.path,"utf8");return c=c.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),c=c.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:c,loader:"js"}}),f.onLoad({filter:/smbls\/.*init\.js$/},async a=>{if(!a.path.includes("smbls/")||!a.path.endsWith("init.js"))return;let c=U(a.path,"utf8");return c=c.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:c,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 a=>{let c=U(a.path,"utf8");return c=c.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:c,loader:"js"}}),f.onLoad({filter:/fetchOnCreate\.js$/},async a=>{if(!a.path.includes("smbls/"))return;let c=U(a.path,"utf8");return c=c.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:c,loader:"js"}})}};await o.build({entryPoints:[t],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[l],nodePaths:e.isMonorepo?[u(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[u(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[u(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[u(e.smblsRoot,"..","..","node_modules")]:[]].filter(f=>y(f)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const g=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{Be(n)}catch{}return g},pt={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"}},ut=(e,t)=>{const o={};return je(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},je=(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)},dt=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let F=0;const _e=Symbol.for("brender.fetchTracked"),ce=()=>F,mt=256*1024,gt=1024*1024,ht=new Set(["accept-language","accept"]),Ee={renders:0,record:null},yt=50,le={renders:0,record:null},ve=(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)}}},fe=e=>e.record&&!e.record.tainted?e.record:null,bt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},pe=(e,t,o)=>{!e||e.tainted||e.failures.length>=yt||e.failures.push({url:bt(t),reason:o})},Te=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),St=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=Te(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),i={},d=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,a]of Xe(l))g&&a==null||(i[String(f).toLowerCase()]=String(a))};return n&&d(t.headers),o&&d(o.headers),{url:s,method:r,headers:i}},wt=(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>mt||e.bytes+i.length>gt||(e.bytes+=i.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:i}))},()=>{}))},kt=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[_e])return;const t=function(...o){F++;let n=!1;const s=()=>{n||(n=!0,F--)},r=a=>{F++,a.then(()=>{F--},()=>{F--})},i=fe(Ee),d=fe(le);let l=null;if(i)try{l=St(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(a=>!ht.has(a)))&&(l=null)}catch{l=null}const g=()=>Te(o[0]);let f;try{f=e.apply(this,o)}catch(a){throw pe(d,g(),a&&a.message||"failed"),s(),a}return f&&typeof f.then=="function"?f.then(a=>{l&&wt(i,l,a,r),d&&a&&a.ok===!1&&pe(d,g(),"HTTP "+a.status),s()},a=>{pe(d,g(),a&&a.message||"network error"),s()}):s(),f};t[_e]=!0,globalThis.fetch=t},Ro=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=et(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
+ `+Ye(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},Rt=3e4,xt=2147483647;let Le=Promise.resolve();const De=()=>{},jt=e=>{const t=Le.then(e);return Le=t.then(De,De),t},_t=e=>{if(e===void 0)return Rt;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,xt)},Et=(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},vt=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},Tt=e=>{let t=!1;return()=>{t||(t=!0,e())}},$e=(e,t={})=>{let o;try{o=_t(t.timeout)}catch(n){return Promise.reject(n)}return jt(()=>Lt(e,t,o))},Lt=async(e,t,o)=>{await xe();const n={closed:!1,undo:[]},s=Dt(e,t,n);if(!o)return s;let r;const i=new Promise((d,l)=>{r=setTimeout(()=>{vt(n),l(Et(t,o))},o)});try{return await Promise.race([s,i])}finally{clearTimeout(r)}},Dt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:i,prefetch:d=!1}=t,l=s||n;kt();const g=ve(le,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await $t(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:i,prefetch:d},g.record,o)}finally{g.close()}},$t=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},i,d)=>{const l=()=>{if(d.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await ze(e)}catch(m){d.closed||console.warn("[brender] SSR translation fetch failed:",m.message)}l()}const{window:f,document:a}=ge(),c=a.body;f.location.pathname=o;const p=[te("document",a),te("location",f.location),te("localStorage",f.localStorage),te("sessionStorage",f.sessionStorage)].map(Tt);d.undo.push(...p);try{const{createDomqlElement:m}=await xe();l();const b=T(e.app||{}),S={...e.config||{}};e.polyglot&&!S.polyglot&&(S.polyglot=e.polyglot),e.fetch&&!S.fetch&&(S.fetch=e.fetch),e.router&&!S.router&&(S.router=e.router);for(const k of Ot)e[k]!=null&&S[k]==null&&(S[k]=e[k]);const R=S.polyglot?{...S.polyglot}:void 0,W=ot(o,R);W&&f.localStorage.setItem(tt(R),W),g&&R&&(R.translations={...R.translations||{},...g});const h=T(e.state||{});(g||R)&&(h.root||(h.root={}),R&&(h.root.lang=X(h.root.lang,R)),g&&(h.root.translations={...h.root.translations||{},...g})),at();const A={};for(const k in e){if(dt.has(k))continue;const x=e[k];A[k]=x&&typeof x=="object"?T(x):x}const L={...A,state:h,...n?{state:{...h,...n}}:{},dependencies:T(e.dependencies||{}),components:T(e.components||{}),snippets:T(e.snippets||{}),pages:T(e.pages||{}),functions:{...e.functions||{},polyglot:it,getActiveLang:we,getLang:we},methods:e.methods||{},designSystem:T(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...S,...R?{polyglot:R}:{},document:a,window:f,parent:{node:c},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,captureDesignSystem:!0,...s||{}},B=await st();l(),B&&(L.plugins=L.plugins||[],L.plugins.some(k=>k.name==="funcql")||L.plugins.push(B)),he();const I=ve(Ee,()=>({entries:[],bytes:0,tainted:!1})),O=I.record;let E;try{E=await m(b,L),l();const k=Date.now(),x=r?2e3:50,H=se=>new Promise(Pe=>setTimeout(Pe,se));for(await H(Math.min(50,x));r&&!d.closed&&ce()>0&&Date.now()-k<x;)await H(10),ce()===0&&await H(Math.max(0,Math.min(50,x-(Date.now()-k))));if(l(),r&&await At(E,R)){l();const se=Date.now();for(await H(Math.min(50,x));!d.closed&&ce()>0&&Date.now()-se<x;)await H(10);l()}}finally{I.close()}const J=O&&!O.tainted&&!(S.fetch&&S.fetch.seed===!1)?O.entries.slice():[];ye(c);const z=be(E),{elementPath:q}=await import("@symbo.ls/element");l();const G=ut(E,q),K=ae(e,t,E,E?.state),v=[],C=a.head||a.querySelector("head");if(C)for(const k of C.querySelectorAll("style")){if(k.sheet&&k.sheet.cssRules)for(const x of k.sheet.cssRules)x.cssText&&v.push(x);if(!v.length){const x=k.textContent||"";x&&v.push({cssText:x})}}const M=v.map(k=>k.cssText),{globalCSS:Y,classRules:me}=Gt(v);let w=Ce(c.innerHTML);const j=E?.state?.root,D=Z(j,R),N=qt(g,j);N&&(w=qe(w,Ae(N,D,R)));const ne=i&&!i.tainted?{failedRequests:i.failures.slice(),rawLabelKeys:It(i.labelKeys,w,D,R,j)}:null;return{html:w,metadata:K,registry:z,brRegistry:G,element:E,emotionCSS:M,globalCSS:Y,classRules:me,fetchSeed:J,document:a,window:f,ssrTranslations:N||void 0,activeLang:D,diagnostics:ne,preparedDesignSystem:L.preparedDesignSystem||null}}finally{for(const m of p)m()}},te=(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]})},At=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=Z(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},ue=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,de=e=>e&&typeof e.parse=="function"?e.parse():e,qt=(e,t)=>{const o=de(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const i=de(s[r]);i&&typeof i=="object"&&(n[r]={...n[r]||{},...i})}return Object.keys(n).length?n:null},Ae=(e,t,o)=>e[t]||e[X(void 0,o)]||Object.values(e)[0]||{},qe=(e,t)=>e.replace(ue,(o,n)=>Q(t,n.trim())??o),Ct=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Mt=(e,t)=>new RegExp(`(?:^|[^\\w.-])${Ze(t)}(?![\\w-]|\\.\\w)`).test(e),It=(e,t,o,n,s)=>{const r=new Set,i=t.replace(Ct,"");for(const d of e)/\s/.test(d)||Se(d,o,n,s)===void 0&&Mt(i,d)&&r.add(d);for(const d of i.matchAll(ue))r.add(d[1].trim());return[...r]},xo=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=ge(),r=s.body,{create:i}=await import("@symbo.ls/element"),d=await import("@symbo.ls/utils"),l={...pt,...o.components||{}},g={...d,...o.utils||{},...o.functions||{}};he();let f;try{f=i(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}ye(r);const a=f?be(f):{};return{html:Ce(r.innerHTML),registry:a,element:f}},Ce=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}),Ot=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Pt=/\.[A-Za-z_\\-]/,Ft=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Pt.test(t)},Bt=/^\s*(:root|\[data-theme|\[data-skin)/,Me=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 Ft(e.selectorText)?Bt.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(Me));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Gt=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Me(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 Z=new Set;const Ro=()=>{Z=new Set},wo=()=>Array.from(Z).join(`
6
- `),xo=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
5
+ `),fontFaceCSS:""},classRules:o}};let oe=new Set;const jo=()=>{oe=new Set},_o=()=>Array.from(oe).join(`
6
+ `),Eo=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
7
7
  ${t}
8
- </style>`:""),jo=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,i=await Le(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!i)return null;const d=e.designSystem||{},l=i.globalCSS;let g=null;try{const c=Bt(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:Me(d),metadata:i.metadata||ne(e,o),brKeyCount:i.registry?Object.keys(i.registry).length:0,brRegistry:i.brRegistry||{},fetchSeed:i.fetchSeed||[],ssrTranslations:i.ssrTranslations,prefetchedState:g,activeLang:f,diagnostics:i.diagnostics}},Bt=e=>{const t={};return Re(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("~/"),d=i?r.slice(2):r,l=i?o.state.root:o.state,g=fe(l&&l[d]);d&&g!=null&&typeof g!="function"&&(t[d]=g)}}),t},_o=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:i=!0,prefetch:d=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:c=De,preloadScripts:a=!0,nonce:p,timeout:m}=o,y=Ut(p),S=n||e.state?.lang||e.app?.metadata?.lang||"en",h=await Le(e,{route:t,pathname:f,prefetch:d,timeout:m});if(!h)return null;const q={...h.metadata};if(s&&(q["theme-color"]=s),typeof l=="string"&&!e.name){const R="\0brender-undeclared-title\0";if(ne({...e,name:R},t,h.element,h.element?.state).title===R)for(const T of["title","og:title","twitter:title"])q[T]===h.metadata.title&&delete q[T]}const _=We(q);for(const R of h.classRules)R&&Z.add(R);const F=Array.from(Z).join(`
10
- `),$=h.globalCSS,B=`<style data-brender>
11
- ${$.rootRule||""}
12
- ${$.resetRules||""}
13
- ${$.keyframeRules||""}
14
- </style>`,G=F?`<style data-emotion="smbls">
15
- ${F}
16
- </style>`:"",D=Me(e.designSystem||{}),v=Object.keys(h.registry).length,C=Ht({brRegistry:h.brRegistry,fetchSeed:h.fetchSeed,adoptDom:g});let K="";if(r&&r.clientScript){const R=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,x=R>0?"../".repeat(R):"./";if(i){const T=ot({polyglot:e.polyglot||e.config?.polyglot,translations:h.ssrTranslations,lang:h.activeLang,pathname:f||t,attrs:y}),U=T?T+`
17
- `:"",W=C.blocks?C.blocks+`
18
- `:"";K=`${U}${W}<script type="module" src="${x}${r.clientScript}"${y}><\/script>`}else K=`<script type="module"${y}>
8
+ </style>`:""),vo=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,i=await $e(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!i)return null;const d=e.designSystem||{},l=i.globalCSS;let g=null;try{const a=Kt(i.element);Object.keys(a).length&&(g=a)}catch{}const f=i.activeLang;return{html:i.html,css:i.classRules.join(`
9
+ `),classRules:i.classRules.slice(),globalCSS:l,resetCss:l.resetRules,fontLinks:Oe(d),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,diagnostics:i.diagnostics,preparedDesignSystem:i.preparedDesignSystem||null}},Kt=e=>{const t={};return je(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("~/"),d=i?r.slice(2):r,l=i?o.state.root:o.state,g=de(l&&l[d]);d&&g!=null&&typeof g!="function"&&(t[d]=g)}}),t},To=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:i=!0,prefetch:d=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:a=Ie,preloadScripts:c=!0,nonce:p,timeout:m,stamp:b,time:S}=o,R=zt(p),W=n||e.state?.lang||e.app?.metadata?.lang||"en",h=await $e(e,{route:t,pathname:f,prefetch:d,timeout:m});if(!h)return null;const A={...h.metadata};if(s&&(A["theme-color"]=s),typeof l=="string"&&!e.name){const w="\0brender-undeclared-title\0";if(ae({...e,name:w},t,h.element,h.element?.state).title===w)for(const D of["title","og:title","twitter:title"])A[D]===h.metadata.title&&delete A[D]}const L=We(A);for(const w of h.classRules)w&&oe.add(w);const B=Array.from(oe).join(`
10
+ `),I=h.globalCSS,O=`<style data-brender>
11
+ ${I.rootRule||""}
12
+ ${I.resetRules||""}
13
+ ${I.keyframeRules||""}
14
+ </style>`,E=B?`<style data-emotion="smbls">
15
+ ${B}
16
+ </style>`:"",J=Oe(e.designSystem||{}),z=Object.keys(h.registry).length,q=Wt({brRegistry:h.brRegistry,fetchSeed:h.fetchSeed,adoptDom:g,stamp:b,time:S});let G="";if(r&&r.clientScript){const w=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,j=w>0?"../".repeat(w):"./";if(i){const D=nt({polyglot:e.polyglot||e.config?.polyglot,translations:h.ssrTranslations,lang:h.activeLang,pathname:f||t,attrs:R}),N=D?D+`
17
+ `:"",ne=q.blocks?q.blocks+`
18
+ `:"";G=`${N}${ne}<script type="module" src="${j}${r.clientScript}"${R}><\/script>`}else G=`<script type="module"${R}>
19
19
  {
20
20
  const brEls = document.querySelectorAll('body > :not(script):not(style)')
21
21
  const observer = new MutationObserver((mutations) => {
@@ -32,22 +32,23 @@ ${F}
32
32
  observer.observe(document.body, { childList: true })
33
33
  }
34
34
  <\/script>
35
- <script type="module" src="${x}${r.clientScript}"${y}><\/script>`}const N={...e.config||{}};e.polyglot&&!N.polyglot&&(N.polyglot=e.polyglot);const M=N.polyglot;let I=_;if(M){const R={...M.translations||{}};for(const x in h.ssrTranslations||{})R[x]={...R[x]||{},...h.ssrTranslations[x]};I=Ae(_,$e(R,h.activeLang,M))}const E=h.diagnostics;if(E)for(const R of I.matchAll(le)){const x=R[1].trim();E.rawLabelKeys.includes(x)||E.rawLabelKeys.push(x)}const H=Nt(h.html,c);return typeof l=="string"?{html:Je(l,{headTags:`${I}
36
- ${C.meta}`,headEnd:[D,B,G].filter(Boolean).join(`
37
- `),body:H,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:C.blocks,lang:n||(M?h.activeLang:void 0),preloadScripts:a}),route:t,brKeyCount:v,shell:!0,diagnostics:E}:{html:`<!DOCTYPE html>
38
- <html lang="${S}">
35
+ <script type="module" src="${j}${r.clientScript}"${R}><\/script>`}const K={...e.config||{}};e.polyglot&&!K.polyglot&&(K.polyglot=e.polyglot);const v=K.polyglot;let C=L;if(v){const w={...v.translations||{}};for(const j in h.ssrTranslations||{})w[j]={...w[j]||{},...h.ssrTranslations[j]};C=qe(L,Ae(w,h.activeLang,v))}const M=h.diagnostics;if(M)for(const w of C.matchAll(ue)){const j=w[1].trim();M.rawLabelKeys.includes(j)||M.rawLabelKeys.push(j)}const Y=Ut(h.html,a);return typeof l=="string"?{html:Je(l,{headTags:`${C}
36
+ ${q.meta}`,headEnd:[J,O,E].filter(Boolean).join(`
37
+ `),body:Y,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:q.blocks,lang:n||(v?h.activeLang:void 0),preloadScripts:c}),route:t,brKeyCount:z,shell:!0,diagnostics:M,preparedDesignSystem:h.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
38
+ <html lang="${W}">
39
39
  <head>
40
- ${I}${r&&r.clientScript&&i?`
41
- ${C.meta}`:""}
42
- ${D}
40
+ ${C}${r&&r.clientScript&&i?`
41
+ ${q.meta}`:""}
42
+ ${J}
43
43
 
44
- ${B}
45
- ${G}
44
+ ${O}
45
+ ${E}
46
46
  </head>
47
47
  <body>
48
- ${H}
49
- ${K}
48
+ ${Y}
49
+ ${G}
50
50
  </body>
51
- </html>`,route:t,brKeyCount:v,diagnostics:E}},De=2,Gt=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,Kt=/\sloading\s*=/i,Nt=(e,t=De)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(Gt,s=>n++<o||Kt.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Me=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
- `):""},Ht=({brRegistry:e,fetchSeed:t,adoptDom:o=!1}={})=>{const n=o&&e&&Object.keys(e).length?Ve(e):"",s=st(t);return{meta:`<meta name="brender" content="${n?"hydrate":"replace"}">`,blocks:[n,s].filter(Boolean).join(`
53
- `)}},Ut=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{De as EAGER_IMAGES_DEFAULT,Se as SMBLS_SSR_SPECIFIER,So as adoptionScripts,wo as getAccumulatedEmotionCSS,Nt as markLazyImages,Ht as prerenderParts,Le as render,ko as renderElement,_o as renderPage,jo as renderRoute,xo as replaceEmotionCSS,Ro as resetGlobalCSSCache,Ft as splitCapturedCSS};
51
+ </html>`,route:t,brKeyCount:z,diagnostics:M,preparedDesignSystem:h.preparedDesignSystem||null}},Ie=2,Nt=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,Ht=/\sloading\s*=/i,Ut=(e,t=Ie)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(Nt,s=>n++<o||Ht.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Oe=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
+ `):""},Wt=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s}={})=>{const r=o&&e&&Object.keys(e).length?Ve(e):"",i=rt(t);return{meta:`<meta name="brender" content="${r?"hydrate":"replace"}">
53
+ `+Qe(Jt(n,s)),blocks:[r,i].filter(Boolean).join(`
54
+ `)}},Jt=(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}},zt=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{Ie as EAGER_IMAGES_DEFAULT,Re as SMBLS_SSR_SPECIFIER,Ro as adoptionScripts,Jt as buildStamp,_o as getAccumulatedEmotionCSS,Ut as markLazyImages,Wt as prerenderParts,$e as render,xo as renderElement,To as renderPage,vo as renderRoute,Eo as replaceEmotionCSS,jo as resetGlobalCSSCache,Gt as splitCapturedCSS};
package/dist/esm/shell.js CHANGED
@@ -1,9 +1,9 @@
1
- const I=/<head(\s[^>]*)?>/i,P=/<body(\s[^>]*)?>/i,f=(e,n)=>{const t=new RegExp(`\\s${n}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,"i").exec(e);return t?t[2]??t[3]??t[4]??"":null},S=e=>{const n=e.slice(0,12).toLowerCase();if(n.startsWith("<title"))return"title";if(n.startsWith("<meta")){if(f(e,"charset")!==null)return"charset";for(const t of["name","property","http-equiv","itemprop"]){const r=f(e,t);if(r!==null)return`${t}:${r.toLowerCase()}`}return null}if(n.startsWith("<link")){const t=(f(e,"rel")||"").toLowerCase();return t==="canonical"?"link:canonical":t==="icon"||t==="shortcut icon"?`link:${t}:${f(e,"href")||""}`:t==="alternate"&&f(e,"hreflang")!==null?`link:alternate:${(f(e,"hreflang")||"").toLowerCase()}`:null}return null},v=/<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi,_=/<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi,x=e=>[...e.matchAll(_)].map(n=>[n.index,n.index+n[0].length,n[0]]),L=(e,n,t=r=>r)=>{let r="",i=0;for(const[o,l,a]of x(e))r+=n(e.slice(i,o))+t(a),i=l;return r+n(e.slice(i))},d=(e,n,t=0)=>{const r=x(e),i=new RegExp(n.source,n.flags.includes("g")?n.flags:n.flags+"g");i.lastIndex=t;let o;for(;o=i.exec(e);){const l=o.index,a=r.find(([s,u])=>l>s&&l<u);if(!a)return o;i.lastIndex=a[1]}return null},j=e=>!!e&&!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(e),E=(e,n)=>e.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi,(t,r,i,o,l)=>{const a=i?o:l;if(!j(a))return t;const s=i||"";return`${r}${s}${n}${a.replace(/^\.\//,"")}${s}`}),q=(e,n)=>{if(!n)return e;const t="../".repeat(n);return L(e,r=>r.replace(/<link\b[^>]*>/gi,i=>E(i,t)),r=>/^<script\b/i.test(r)?r.replace(/^<script\b[^>]*>/i,i=>E(i,t)):r)},W=new Set(["","module","text/javascript","application/javascript"]),m=(e,n)=>new RegExp(`\\s${n}(?:\\s*=|\\s|>|/)`,"i").test(e),B=e=>{const n=[],t=new Set;for(const[,,r]of x(e)){if(!/^<script\b/i.test(r))continue;const i=r.slice(0,r.indexOf(">")+1),o=f(i,"src");if(!o||t.has(o))continue;const l=(f(i,"type")||"").trim().toLowerCase();if(!W.has(l)||m(i,"async")||m(i,"nomodule"))continue;t.add(o);const a=["crossorigin","integrity","referrerpolicy"].map(u=>{const h=f(i,u);return h===null?m(i,u)?` ${u}`:"":` ${u}="${h.replace(/"/g,"&quot;")}"`}).join(""),s=o.replace(/"/g,"&quot;");n.push(l==="module"?`<link rel="modulepreload" href="${s}" fetchpriority="high"${a}>`:`<link rel="preload" as="script" href="${s}" fetchpriority="high"${a}>`)}return n.join(`
2
- `)},D=(e,n)=>{const t=d(e,/<html(\s[^>]*)?>/i);if(!t)return e;const r=t[0],i=String(n).replace(/"/g,"&quot;"),o=f(r,"lang")!==null?r.replace(/(\slang\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`$1"${i}"`):r.replace(/^<html/i,`<html lang="${i}"`);return e.slice(0,t.index)+o+e.slice(t.index+r.length)},H=(e,{headTags:n="",headEnd:t="",body:r="",depth:i=0,bootScripts:o="",lang:l,preloadScripts:a=!1}={})=>{if(typeof e!="string"||!e.trim())throw new Error("brender: the shell document is empty");let s=q(e,i);l&&(s=D(s,l));const u=a?B(s):"";u&&(t=t?`${u}
3
- ${t}`:u);const h=new Set;for(const c of n.matchAll(v)){const p=S(c[0]);p&&h.add(p)}const w=c=>L(c,p=>p.replace(v,R=>h.has(S(R))?"":R)),y=n?`
1
+ import{insertAfterImportMap as E}from"@symbo.ls/utils";const P=/<head(\s[^>]*)?>/i,_=/<body(\s[^>]*)?>/i,f=(e,n)=>{const t=new RegExp(`\\s${n}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,"i").exec(e);return t?t[2]??t[3]??t[4]??"":null},S=e=>{const n=e.slice(0,12).toLowerCase();if(n.startsWith("<title"))return"title";if(n.startsWith("<meta")){if(f(e,"charset")!==null)return"charset";for(const t of["name","property","http-equiv","itemprop"]){const r=f(e,t);if(r!==null)return`${t}:${r.toLowerCase()}`}return null}if(n.startsWith("<link")){const t=(f(e,"rel")||"").toLowerCase();return t==="canonical"?"link:canonical":t==="icon"||t==="shortcut icon"?`link:${t}:${f(e,"href")||""}`:t==="alternate"&&f(e,"hreflang")!==null?`link:alternate:${(f(e,"hreflang")||"").toLowerCase()}`:null}return null},v=/<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi,j=/<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi,x=e=>[...e.matchAll(j)].map(n=>[n.index,n.index+n[0].length,n[0]]),L=(e,n,t=r=>r)=>{let r="",o=0;for(const[i,l,a]of x(e))r+=n(e.slice(o,i))+t(a),o=l;return r+n(e.slice(o))},d=(e,n,t=0)=>{const r=x(e),o=new RegExp(n.source,n.flags.includes("g")?n.flags:n.flags+"g");o.lastIndex=t;let i;for(;i=o.exec(e);){const l=i.index,a=r.find(([s,p])=>l>s&&l<p);if(!a)return i;o.lastIndex=a[1]}return null},q=e=>!!e&&!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(e),I=(e,n)=>e.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi,(t,r,o,i,l)=>{const a=o?i:l;if(!q(a))return t;const s=o||"";return`${r}${s}${n}${a.replace(/^\.\//,"")}${s}`}),W=(e,n)=>{if(!n)return e;const t="../".repeat(n);return L(e,r=>r.replace(/<link\b[^>]*>/gi,o=>I(o,t)),r=>/^<script\b/i.test(r)?r.replace(/^<script\b[^>]*>/i,o=>I(o,t)):r)},B=new Set(["","module","text/javascript","application/javascript"]),m=(e,n)=>new RegExp(`\\s${n}(?:\\s*=|\\s|>|/)`,"i").test(e),D=e=>{const n=[],t=new Set;for(const[,,r]of x(e)){if(!/^<script\b/i.test(r))continue;const o=r.slice(0,r.indexOf(">")+1),i=f(o,"src");if(!i||t.has(i))continue;const l=(f(o,"type")||"").trim().toLowerCase();if(!B.has(l)||m(o,"async")||m(o,"nomodule"))continue;t.add(i);const a=["crossorigin","integrity","referrerpolicy"].map(p=>{const h=f(o,p);return h===null?m(o,p)?` ${p}`:"":` ${p}="${h.replace(/"/g,"&quot;")}"`}).join(""),s=i.replace(/"/g,"&quot;");n.push(l==="module"?`<link rel="modulepreload" href="${s}" fetchpriority="high"${a}>`:`<link rel="preload" as="script" href="${s}" fetchpriority="high"${a}>`)}return n.join(`
2
+ `)},H=(e,n)=>{const t=d(e,/<html(\s[^>]*)?>/i);if(!t)return e;const r=t[0],o=String(n).replace(/"/g,"&quot;"),i=f(r,"lang")!==null?r.replace(/(\slang\s*=\s*)(?:"[^"]*"|'[^']*'|[^\s>]+)/i,`$1"${o}"`):r.replace(/^<html/i,`<html lang="${o}"`);return e.slice(0,t.index)+i+e.slice(t.index+r.length)},z=(e,{headTags:n="",headEnd:t="",body:r="",depth:o=0,bootScripts:i="",lang:l,preloadScripts:a=!1}={})=>{if(typeof e!="string"||!e.trim())throw new Error("brender: the shell document is empty");let s=W(e,o);l&&(s=H(s,l));const p=a?D(s):"";if(p){const c=E(s,p);c!==null?s=c:t=t?`${p}
3
+ ${t}`:p}const h=new Set;for(const c of n.matchAll(v)){const u=S(c[0]);u&&h.add(u)}const w=c=>L(c,u=>u.replace(v,R=>h.has(S(R))?"":R)),y=n?`
4
4
  ${n}`:"",O=t?`
5
5
  ${t}
6
- `:"",g=d(s,I),b=g&&d(s,/<\/head\s*>/i,g.index);if(g&&b){const c=g.index+g[0].length,p=b.index;s=s.slice(0,c)+y+w(s.slice(c,p))+O+s.slice(p)}else{s=w(s);const c=d(s,/<html(\s[^>]*)?>/i)||d(s,/<!doctype[^>]*>/i),p=c?c.index+c[0].length:0;s=s.slice(0,p)+y+O+s.slice(p)}const k=`
6
+ `:"",g=d(s,P),b=g&&d(s,/<\/head\s*>/i,g.index);if(g&&b){const c=g.index+g[0].length,u=b.index;s=s.slice(0,c)+y+w(s.slice(c,u))+O+s.slice(u)}else{s=w(s);const c=d(s,/<html(\s[^>]*)?>/i)||d(s,/<!doctype[^>]*>/i),u=c?c.index+c[0].length:0;s=s.slice(0,u)+y+O+s.slice(u)}const k=`
7
7
  ${r}
8
- ${o?o+`
9
- `:""}`,$=d(s,P);if($){const c=$.index+$[0].length;return s.slice(0,c)+k+s.slice(c)}const C=d(s,/<\/(?:body|html)\s*>/i),A=C?C.index:s.length;return s.slice(0,A)+k+s.slice(A)};export{H as composeIntoShell,S as headTagKey,B as scriptPreloads};
8
+ ${i?i+`
9
+ `:""}`,$=d(s,_);if($){const c=$.index+$[0].length;return s.slice(0,c)+k+s.slice(c)}const A=d(s,/<\/(?:body|html)\s*>/i),C=A?A.index:s.length;return s.slice(0,C)+k+s.slice(C)};export{z as composeIntoShell,S as headTagKey,D as scriptPreloads};
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createEnv } from './env.js'
2
2
  import { resetKeys, assignKeys, mapKeysToElements } from './keys.js'
3
3
  import { loadProject, loadAndRenderAll } from './load.js'
4
- import { render, renderElement, renderRoute, renderPage, resetGlobalCSSCache, getAccumulatedEmotionCSS, replaceEmotionCSS, adoptionScripts, prerenderParts } from './render.js'
4
+ import { render, renderElement, renderRoute, renderPage, resetGlobalCSSCache, getAccumulatedEmotionCSS, replaceEmotionCSS, adoptionScripts, prerenderParts, buildStamp } from './render.js'
5
5
  import { extractMetadata, generateHeadHtml } from './metadata.js'
6
6
  import { collectBrNodes, hydrate } from './hydrate.js'
7
7
  import { generateSitemap } from './sitemap.js'
@@ -31,7 +31,8 @@ export {
31
31
  injectPrefetchedState,
32
32
  composeIntoShell,
33
33
  adoptionScripts,
34
- prerenderParts
34
+ prerenderParts,
35
+ buildStamp
35
36
  }
36
37
 
37
38
  export default {
@@ -57,5 +58,6 @@ export default {
57
58
  injectPrefetchedState,
58
59
  composeIntoShell,
59
60
  adoptionScripts,
60
- prerenderParts
61
+ prerenderParts,
62
+ buildStamp
61
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/brender",
3
- "version": "3.14.667",
3
+ "version": "3.14.668",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -46,18 +46,18 @@
46
46
  "prepublishOnly": "npm run build"
47
47
  },
48
48
  "dependencies": {
49
- "@symbo.ls/css": "^3.14.779",
50
- "@symbo.ls/default-config": "^3.14.787",
51
- "@symbo.ls/element": "^3.14.805",
52
- "@symbo.ls/fetch": "^3.14.666",
49
+ "@symbo.ls/css": "^3.14.780",
50
+ "@symbo.ls/default-config": "^3.14.788",
51
+ "@symbo.ls/element": "^3.14.806",
52
+ "@symbo.ls/fetch": "^3.14.667",
53
53
  "@symbo.ls/helmet": "^3.14.670",
54
- "@symbo.ls/scratch": "^3.14.789",
55
- "@symbo.ls/utils": "^3.14.788",
54
+ "@symbo.ls/scratch": "^3.14.790",
55
+ "@symbo.ls/utils": "^3.14.789",
56
56
  "esbuild": "^0.28.1",
57
57
  "linkedom": "^0.16.8"
58
58
  },
59
59
  "peerDependencies": {
60
- "smbls": "^3.14.839"
60
+ "smbls": "^3.14.840"
61
61
  },
62
62
  "optionalDependencies": {
63
63
  "@symbo.ls/funcql": "^3.14.0"
package/render.js CHANGED
@@ -13,7 +13,7 @@ import { composeIntoShell } from './shell.js'
13
13
  import { fetchSSRTranslations } from './prefetch.js'
14
14
  import { createEarlySeedScript } from '@symbo.ls/fetch'
15
15
  import { headerEntries } from '@symbo.ls/fetch/rest'
16
- import { brenderRegistryBlock, escapeRegExp, jsonForScript, polyglotFallbackLang, polyglotLangStorageKey, polyglotAddressLang, polyglotStartupScript } from '@symbo.ls/utils'
16
+ import { brenderRegistryBlock, brenderStampMeta, escapeRegExp, jsonForScript, polyglotFallbackLang, polyglotLangStorageKey, polyglotAddressLang, polyglotStartupScript } from '@symbo.ls/utils'
17
17
 
18
18
  // funcql plugin — enables evaluation of funcql schemas as property values
19
19
  // during SSR. Async-imported to avoid polluting non-funcql codepaths.
@@ -1262,6 +1262,12 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1262
1262
  // not carry them: the browser asked for them again after boot (xma.info,
1263
1263
  // 2026-10-16: index.html 10 seed entries, about.html 3).
1264
1264
  isolateFetchCache: true,
1265
+ // Keep the design system this render prepared — the processed config and
1266
+ // its global CSS — for the page to adopt instead of preparing it again
1267
+ // (smbls prepare.js, "The design system prepared at build time"; every
1268
+ // render returns it as `preparedDesignSystem`, and `smbls build` writes
1269
+ // it into one file each page preloads).
1270
+ captureDesignSystem: true,
1265
1271
  // Caller overrides
1266
1272
  ...(contextOverrides || {})
1267
1273
  }
@@ -1310,10 +1316,26 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1310
1316
  if (inFlightRequests() === 0) await sleep(Math.max(0, Math.min(50, flushCap - (Date.now() - flushStart))))
1311
1317
  }
1312
1318
  live()
1319
+
1320
+ // The page's labels, when the project's polyglot.fetch is still loading
1321
+ // them: the render waits for that load past the flush's cap — a page
1322
+ // without its labels is a failed page, not a slower one — bounded by the
1323
+ // render's own time limit (see "One render at a time"). See awaitLabels.
1324
+ if (prefetch && await awaitLabels(element, polyglotConfig)) {
1325
+ live()
1326
+ // what the labels' answer triggers: another quiet period, and the
1327
+ // requests it starts, under the flush's cap once more
1328
+ const labelsAt = Date.now()
1329
+ await sleep(Math.min(50, flushCap))
1330
+ while (!turn.closed && inFlightRequests() > 0 && Date.now() - labelsAt < flushCap) await sleep(10)
1331
+ live()
1332
+ }
1313
1333
  } finally {
1314
1334
  recording.close()
1315
1335
  }
1316
- const fetchSeed = seed && !seed.tainted ? seed.entries.slice() : []
1336
+ // `fetch.seed: false` — the project adopts no recorded answer (the client
1337
+ // asks for everything itself): the page carries none.
1338
+ const fetchSeed = seed && !seed.tainted && !(config.fetch && config.fetch.seed === false) ? seed.entries.slice() : []
1317
1339
 
1318
1340
  // Assign data-br keys for hydration
1319
1341
  assignKeys(body)
@@ -1375,7 +1397,7 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1375
1397
  }
1376
1398
  : null
1377
1399
 
1378
- return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics }
1400
+ return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics, preparedDesignSystem: ctx.preparedDesignSystem || null }
1379
1401
  } finally {
1380
1402
  for (const restore of restoreGlobals) restore()
1381
1403
  }
@@ -1398,6 +1420,39 @@ const swapGlobal = (name, value) => {
1398
1420
  }
1399
1421
  }
1400
1422
 
1423
+ // ── Labels the render is still loading ──────────────────────────────────────
1424
+ // A function `polyglot.fetch` loads the page's labels inside the render (the
1425
+ // polyglot plugin calls it with the root element, see fetchSSRTranslations).
1426
+ // The flush waits for requests up to its cap; the labels' load is more than
1427
+ // a request (the project's loader, its own awaits, the body, the answer into
1428
+ // the root state), and the first render of a process — a cold connection,
1429
+ // a cold JIT, a page that renders a lot — took longer than the cap: `smbls
1430
+ // build` of xma.info wrote `/` with 49 raw label keys while its labels landed
1431
+ // 2.9 s after the render had ended (2026-10-18). So when the root has no
1432
+ // labels for its language yet, the render waits for the load in flight for
1433
+ // this app's root: the polyglot plugin's `loadTranslations(lang, { inFlight:
1434
+ // true })` hands it back (@symbo.ls/polyglot loadsInFlight) and starts
1435
+ // nothing — a load that already ended, failed or answered "unchanged", is not
1436
+ // asked again. True when it asked (the render then gives the answer a quiet
1437
+ // period).
1438
+ const awaitLabels = async (element, poly) => {
1439
+ if (!poly || !poly.fetch || !element || typeof element.call !== 'function') return false
1440
+ const root = element.state && element.state.root
1441
+ if (!root) return false
1442
+ const lang = activeLangOf(root, poly)
1443
+ const have = root.translations && root.translations[lang]
1444
+ if (have && typeof have === 'object' && Object.keys(have).length) return false
1445
+ if (poly.translations && poly.translations[lang]) return false
1446
+ try {
1447
+ // undefined at once when no load is in flight
1448
+ await element.call('loadTranslations', lang, { inFlight: true })
1449
+ } catch (e) {
1450
+ // the load reports its own failure (polyglot logs it; the request shows
1451
+ // in the diagnostics); the raw keys check names what is missing
1452
+ }
1453
+ return true
1454
+ }
1455
+
1401
1456
  // ── Labels after a render ───────────────────────────────────────────────────
1402
1457
 
1403
1458
  const LABEL_TEMPLATE = /\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g
@@ -1702,7 +1757,11 @@ export const renderRoute = async (data, options = {}) => {
1702
1757
  activeLang,
1703
1758
  // failed requests and raw label keys (see `DIAG`); null when an
1704
1759
  // overlapping render made them unknowable
1705
- diagnostics: result.diagnostics
1760
+ diagnostics: result.diagnostics,
1761
+ // the design system this render prepared (smbls prepare.js), for the
1762
+ // caller to hand the page (@symbo.ls/utils designSystemLink /
1763
+ // designSystemBlock)
1764
+ preparedDesignSystem: result.preparedDesignSystem || null
1706
1765
  }
1707
1766
  }
1708
1767
 
@@ -1763,13 +1822,17 @@ const collectFetchedState = (element) => {
1763
1822
  * The result then carries `shell: true`.
1764
1823
  * @param {number|false} [options.timeout] - render()'s time limit (30 s by
1765
1824
  * default; 0 or false: none) — see render()
1825
+ * @param {string|number} [options.stamp] - The build's data stamp, written into
1826
+ * the page (see buildStamp); default: the render time as an ISO string
1827
+ * @param {number} [options.time] - The build time in ms (see buildStamp);
1828
+ * default: now. A build passes one stamp and time to every page.
1766
1829
  * @param {number} [options.depth] - Directory depth of the output file: rebases
1767
1830
  * the shell's relative asset URLs (with `shell`, default 0) and the ISR
1768
1831
  * client script path (default: derived from the route).
1769
1832
  * @returns {Promise<{ html: string, route: string, brKeyCount: number, shell?: true }>}
1770
1833
  */
1771
1834
  export const renderPage = async (data, route = '/', options = {}) => {
1772
- const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout } = options
1835
+ const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout, stamp, time } = options
1773
1836
  const nonceAttr = nonceAttribute(nonce)
1774
1837
 
1775
1838
  // Detect lang from project config, app metadata, or default
@@ -1828,7 +1891,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
1828
1891
  // and the data blocks after the markup — the registry under `adoptDom`,
1829
1892
  // and the recorded anonymous answers, so the client renders from the data
1830
1893
  // the server used instead of asking for all of it again.
1831
- const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom })
1894
+ const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time })
1832
1895
 
1833
1896
  // ISR: include client SPA bundle for hydration + data fetching
1834
1897
  let isrBody = ''
@@ -1936,7 +1999,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
1936
1999
  lang: lang || (polyglotCfg ? result.activeLang : undefined),
1937
2000
  preloadScripts
1938
2001
  })
1939
- return { html, route, brKeyCount, shell: true, diagnostics }
2002
+ return { html, route, brKeyCount, shell: true, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
1940
2003
  }
1941
2004
 
1942
2005
  // (The empty line after the font links held a @font-face <style> that
@@ -1956,7 +2019,7 @@ ${isrBody}
1956
2019
  </body>
1957
2020
  </html>`
1958
2021
 
1959
- return { html, route, brKeyCount, diagnostics }
2022
+ return { html, route, brKeyCount, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
1960
2023
  }
1961
2024
 
1962
2025
  // ── Images below the fold ───────────────────────────────────────────────────
@@ -2025,17 +2088,35 @@ const generateFontLinks = (ds) => {
2025
2088
  // again. Placed after the markup, so they never delay its paint;
2026
2089
  // the app's scripts run after them either way.
2027
2090
  // `adoptionScripts` above stays for older callers (the platform's renderer).
2028
- export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false } = {}) => {
2091
+ // The build's stamp (XMA round 3, item B) goes into <head> after the mark:
2092
+ // `<meta name="brender-stamp">` (@symbo.ls/utils brenderStampMeta) — read by
2093
+ // the client's fetch plugin, which decides whether the page is stale
2094
+ // (@symbo.ls/fetch early.js "Stale pages"), by app code
2095
+ // (`el.context.prerender`, smbls prerender.js) and by a host that compares
2096
+ // it with its data's last change.
2097
+ export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time } = {}) => {
2029
2098
  const registry = adoptDom && brRegistry && Object.keys(brRegistry).length
2030
2099
  ? brenderRegistryBlock(brRegistry)
2031
2100
  : ''
2032
2101
  const seed = createEarlySeedBlock(fetchSeed)
2033
2102
  return {
2034
- meta: `<meta name="brender" content="${registry ? 'hydrate' : 'replace'}">`,
2103
+ meta: `<meta name="brender" content="${registry ? 'hydrate' : 'replace'}">\n` + brenderStampMeta(buildStamp(stamp, time)),
2035
2104
  blocks: [registry, seed].filter(Boolean).join('\n')
2036
2105
  }
2037
2106
  }
2038
2107
 
2108
+ // ── The build's stamp ───────────────────────────────────────────────────────
2109
+ // A pre-rendered page carries the data of the time it was rendered until it
2110
+ // is rendered again (`smbls build` pages: until the next build). Each page
2111
+ // says when that was — `time`, ms since the epoch — and which data it holds:
2112
+ // `stamp`, a project's own version of its data (`brender.stamp`, run once
2113
+ // per build by the CLI), else the render time as an ISO string. One build
2114
+ // passes one stamp and time to every page.
2115
+ export const buildStamp = (stamp, time) => {
2116
+ const at = typeof time === 'number' && isFinite(time) ? Math.round(time) : Date.now()
2117
+ return { stamp: stamp != null && stamp !== '' ? String(stamp) : new Date(at).toISOString(), time: at }
2118
+ }
2119
+
2039
2120
  // ` nonce="…"` for an inline script, or '' — a value that could break out of
2040
2121
  // the attribute is refused.
2041
2122
  const nonceAttribute = (nonce) => {
package/shell.js CHANGED
@@ -32,6 +32,8 @@
32
32
  * DOM parse + serialize round-trip does not guarantee.
33
33
  */
34
34
 
35
+ import { insertAfterImportMap } from '@symbo.ls/utils'
36
+
35
37
  const HEAD_OPEN = /<head(\s[^>]*)?>/i
36
38
  const BODY_OPEN = /<body(\s[^>]*)?>/i
37
39
 
@@ -199,8 +201,16 @@ export const composeIntoShell = (shell, { headTags = '', headEnd = '', body = ''
199
201
  }
200
202
  let html = rebaseAssets(shell, depth)
201
203
  if (lang) html = setHtmlLang(html, lang)
204
+ // The app's preloads go right after the shell's import map when it has one
205
+ // (parcel's page does): a modulepreload before it gets the map rejected,
206
+ // and the app's lazy bundles resolve through it. Else first in what the
207
+ // render adds before </head>.
202
208
  const preloads = preloadScripts ? scriptPreloads(html) : ''
203
- if (preloads) headEnd = headEnd ? `${preloads}\n${headEnd}` : preloads
209
+ if (preloads) {
210
+ const placed = insertAfterImportMap(html, preloads)
211
+ if (placed !== null) html = placed
212
+ else headEnd = headEnd ? `${preloads}\n${headEnd}` : preloads
213
+ }
204
214
 
205
215
  const owned = new Set()
206
216
  for (const m of headTags.matchAll(HEAD_TAG)) {