@symbo.ls/brender 3.14.667 → 3.14.669

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,34 @@
1
1
  # @symbo.ls/brender
2
2
 
3
+ ## 3.14.669
4
+
5
+ ### Patch Changes
6
+
7
+ - XMA round 5 ("smbls 3.14.843: what XMA still needs"): container and current CSS props + a dev warning for a dropped CSS prop (S); `_redirects` file rules on Cloudflare (T); `build.prepareDesignSystem: false` covers the pre-render app shell (U); preloads after the charset and `build.modulePreload: "strict"` (K); Dialog re-opens on an `if:` re-mount and hides a closed modal from its first moment (G, R); DropdownDisclosure `open`, `toggle` and `keepOpen` (H); the first render keeps a scrolled reader on an adopted page (P); `addressLang: 'strict'` and languages from a function (I); `fetch.early` chained requests and `afterFirstView` (J, 8); adoption keeps unchanged text, adopts in place, keeps the page's font faces, puts the app's sheets before the server's (F, O, Q, N); the head of a pre-rendered page resolves the page's own metadata (L). Plus the unreleased fixes on main since round 4b.
8
+ - Updated dependencies
9
+ - @symbo.ls/fetch@3.14.669
10
+ - @symbo.ls/utils@3.14.845
11
+ - @symbo.ls/scratch@3.14.792
12
+ - @symbo.ls/css@3.14.782
13
+ - @symbo.ls/element@3.14.809
14
+ - @symbo.ls/default-config@3.14.789
15
+ - @symbo.ls/helmet@3.14.671
16
+ - smbls@3.14.844
17
+
18
+ ## 3.14.668
19
+
20
+ ### Patch Changes
21
+
22
+ - 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.
23
+ - Updated dependencies
24
+ - @symbo.ls/css@3.14.780
25
+ - @symbo.ls/default-config@3.14.788
26
+ - @symbo.ls/element@3.14.806
27
+ - @symbo.ls/scratch@3.14.790
28
+ - smbls@3.14.840
29
+ - @symbo.ls/utils@3.14.789
30
+ - @symbo.ls/fetch@3.14.667
31
+
3
32
  ## 3.14.667
4
33
 
5
34
  ### 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 +1 @@
1
- const a=t=>typeof t=="function",S=t=>Array.isArray(t),g=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),_=(t,r)=>{if(t){if(a(t))try{return t({state:r||{},call:()=>{},__ref:{}},r||{})}catch{return}return t}},j=(t,r)=>{if(!t)return null;if(typeof t=="string")return{from:t,method:"select"};const n=a(t)?null:{...t};return!n||(n.method||(n.method="select"),a(n.params)&&(n.params=_(n.params,r)),n.method==="insert"||n.method==="update"||n.method==="upsert"||n.method==="delete")||n.on&&n.on!=="create"?null:n},b=(t,r="")=>{if(!t||typeof t!="object")return[];if(a(t))return[];const n=[],s=t.state||{};if(t.fetch){const e=S(t.fetch)?t.fetch:[t.fetch];for(const c of e){const o=j(c,s);o&&n.push({config:o,stateKey:o.as,path:r,elementState:s})}}for(const e in t)e==="fetch"||e==="state"||e==="props"||e==="attr"||e==="on"||e==="define"||e==="childExtends"||e==="childProps"||e==="childrenAs"||e.charAt(0)>="A"&&e.charAt(0)<="Z"&&g(t[e])&&n.push(...b(t[e],r?`${r}.${e}`:e));return n},M=["rest","local"],w=new WeakMap,A=async t=>{if(!t)return null;if(g(t)&&(a(t.select)||a(t.rpc)))return t;const r=typeof t=="string"?{adapter:t}:t;if(!g(r)||typeof r.adapter!="string")return null;if(w.has(r))return w.get(r);const n=(async()=>{let s;try{s=await import("@symbo.ls/fetch")}catch{return null}if(!(a(s.hasAdapter)?s.hasAdapter(r.adapter):M.includes(r.adapter)))return null;try{return await s.resolveDb(r)}catch(c){return console.warn(`[brender] SSR adapter "${r.adapter}" unavailable: ${c.message}`),null}})();return w.set(r,n),n},k=async(t,r)=>{try{const{method:n,from:s,params:e,transform:c,limit:o,offset:p,order:f,single:h,headers:d,baseUrl:l}=r;let i;if(n==="rpc"){if(!a(t.rpc))return null;i=await t.rpc({from:s,params:e,headers:d,baseUrl:l})}else{if(!a(t.select))return null;i=await t.select({from:s,select:r.select,params:e,limit:o,offset:p,order:f,single:h,headers:d,baseUrl:l})}let u=i?.data??null;if(i?.error)return null;if(u&&c&&a(c))try{u=c(u)}catch{}return u}catch{return null}},P=async(t,r="/",n={})=>{const e=(t.pages||{})[r];if(!e)return new Map;const c=t.config||t.settings||{},o=c.fetch||t.fetch||c.db||t.db;if(!o)return new Map;const p=await A(o);if(!p)return new Map;const f=b(e);if(!f.length)return new Map;const h=new Map,d=await Promise.allSettled(f.map(async({config:l,stateKey:i,path:u})=>{const y=await k(p,l);if(y!==null){const m=h.get(u)||{};i?m[i]=y:g(y)&&Object.assign(m,y),h.set(u,m)}}));return h},x=async t=>{const r=t.config||{},n=r.polyglot||t.polyglot;if(!n?.fetch||typeof n.fetch=="function")return null;const s=r.fetch||t.fetch||r.db||t.db;if(!s)return null;const e=await A(s);if(!e)return null;const c=n.fetch,o=c.rpc||c.from||"get_translations_if_changed",p=n.languages||[n.defaultLang||"en"],f={},h=await Promise.allSettled(p.map(async d=>{try{if(!a(e.rpc))return;const l=await e.rpc({from:o,params:{p_lang:d,p_cached_version:0}});if(l.error||!l.data)return;const i=l.data;i.translations&&(f[d]=i.translations)}catch(l){console.warn("[brender] SSR translation fetch failed:",l.message)}}));return Object.keys(f).length?f:null},D=(t,r)=>{if(!(!r||!r.size))for(const[n,s]of r){let e=t;if(n){const c=n.split(".");for(const o of c){if(!e||typeof e!="object")break;e=e[o]}}e&&typeof e=="object"&&((!e.state||typeof e.state!="object")&&(e.state={}),Object.assign(e.state,s))}};export{A as createSSRAdapter,x as fetchSSRTranslations,D as injectPrefetchedState,P as prefetchPageData};
1
+ const l=t=>typeof t=="function",S=t=>Array.isArray(t),g=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),_=(t,r)=>{if(t){if(l(t))try{return t({state:r||{},call:()=>{},__ref:{}},r||{})}catch{return}return t}},j=(t,r)=>{if(!t)return null;if(typeof t=="string")return{from:t,method:"select"};const n=l(t)?null:{...t};return!n||(n.method||(n.method="select"),l(n.params)&&(n.params=_(n.params,r)),n.method==="insert"||n.method==="update"||n.method==="upsert"||n.method==="delete")||n.on&&n.on!=="create"?null:n},b=(t,r="")=>{if(!t||typeof t!="object")return[];if(l(t))return[];const n=[],s=t.state||{};if(t.fetch){const e=S(t.fetch)?t.fetch:[t.fetch];for(const c of e){const a=j(c,s);a&&n.push({config:a,stateKey:a.as,path:r,elementState:s})}}for(const e in t)e==="fetch"||e==="state"||e==="props"||e==="attr"||e==="on"||e==="define"||e==="childExtends"||e==="childProps"||e==="childrenAs"||e.charAt(0)>="A"&&e.charAt(0)<="Z"&&g(t[e])&&n.push(...b(t[e],r?`${r}.${e}`:e));return n},M=["rest","local"],w=new WeakMap,A=async t=>{if(!t)return null;if(g(t)&&(l(t.select)||l(t.rpc)))return t;const r=typeof t=="string"?{adapter:t}:t;if(!g(r)||typeof r.adapter!="string")return null;if(w.has(r))return w.get(r);const n=(async()=>{let s;try{s=await import("@symbo.ls/fetch")}catch{return null}if(!(l(s.hasAdapter)?s.hasAdapter(r.adapter):M.includes(r.adapter)))return null;try{return await s.resolveDb(r)}catch(c){return console.warn(`[brender] SSR adapter "${r.adapter}" unavailable: ${c.message}`),null}})();return w.set(r,n),n},k=async(t,r)=>{try{const{method:n,from:s,params:e,transform:c,limit:a,offset:u,order:h,single:f,headers:d,baseUrl:p}=r;let o;if(n==="rpc"){if(!l(t.rpc))return null;o=await t.rpc({from:s,params:e,headers:d,baseUrl:p})}else{if(!l(t.select))return null;o=await t.select({from:s,select:r.select,params:e,limit:a,offset:u,order:h,single:f,headers:d,baseUrl:p})}let i=o?.data??null;if(o?.error)return null;if(i&&c&&l(c))try{i=c(i)}catch{}return i}catch{return null}},P=async(t,r="/",n={})=>{const e=(t.pages||{})[r];if(!e)return new Map;const c=t.config||t.settings||{},a=c.fetch||t.fetch||c.db||t.db;if(!a)return new Map;const u=await A(a);if(!u)return new Map;const h=b(e);if(!h.length)return new Map;const f=new Map,d=await Promise.allSettled(h.map(async({config:p,stateKey:o,path:i})=>{const y=await k(u,p);if(y!==null){const m=f.get(i)||{};o?m[o]=y:g(y)&&Object.assign(m,y),f.set(i,m)}}));return f},x=async t=>{const r=t.config||{},n=r.polyglot||t.polyglot;if(!n?.fetch||typeof n.fetch=="function")return null;const s=r.fetch||t.fetch||r.db||t.db;if(!s)return null;const e=await A(s);if(!e)return null;const c=n.fetch,a=c.rpc||c.from||"get_translations_if_changed",u=(await import("@symbo.ls/utils")).polyglotLanguages(n,!1),h=u.length?u:[n.defaultLang||"en"],f={},d=await Promise.allSettled(h.map(async p=>{try{if(!l(e.rpc))return;const o=await e.rpc({from:a,params:{p_lang:p,p_cached_version:0}});if(o.error||!o.data)return;const i=o.data;i.translations&&(f[p]=i.translations)}catch(o){console.warn("[brender] SSR translation fetch failed:",o.message)}}));return Object.keys(f).length?f:null},D=(t,r)=>{if(!(!r||!r.size))for(const[n,s]of r){let e=t;if(n){const c=n.split(".");for(const a of c){if(!e||typeof e!="object")break;e=e[a]}}e&&typeof e=="object"&&((!e.state||typeof e.state!="object")&&(e.state={}),Object.assign(e.state,s))}};export{A as createSSRAdapter,x as fetchSSRTranslations,D as injectPrefetchedState,P as prefetchPageData};
@@ -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 Ge,dirname as E}from"path";import{existsSync as y,unlinkSync as Ne,readFileSync as J,realpathSync as He}from"fs";import{resolvePackageEntry as Ue}from"./packageEntry.js";import{tmpdir as We}from"os";import{randomBytes as Je}from"crypto";import{createRequire as ie}from"module";import{pathToFileURL as ze}from"url";import{createEnv as ye}from"./env.js";import{resetKeys as be,assignKeys as Se,mapKeysToElements as we}from"./keys.js";import{extractMetadata as le,generateHeadHtml as Ye}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Xe}from"./shell.js";import{fetchSSRTranslations as Ve}from"./prefetch.js";import{createEarlySeedScript as Qe}from"@symbo.ls/fetch";import{headerEntries as Ze}from"@symbo.ls/fetch/rest";import{brenderRegistryBlock as et,brenderStampMeta as tt,escapeRegExp as ot,jsonForScript as nt,polyglotFallbackLang as V,polyglotLangStorageKey as st,polyglotAddressLang as rt,polyglotStartupScript as ct}from"@symbo.ls/utils";let Q=null;const at=async()=>{if(Q)return Q;try{return Q=(await import("@symbo.ls/funcql")).funcqlPlugin,Q}catch{return null}};import"linkedom";import{createEarlySeedBlock as it}from"@symbo.ls/fetch";import{reset as lt}from"@symbo.ls/css";const Z=(e,t)=>{if(!e||!t)return;if(e[t]!==void 0)return e[t];const o=t.split(".");let n=e;for(const s of o){if(n==null||typeof n!="object")return;n=n[s]}return n},ee=(e,t)=>V(e?.lang,t),ke=(e,t,o,n)=>{if(o?.translations){const r=o.translations[t];if(r){const a=Z(r,e);if(a!==void 0)return a}}if(n?.translations){const r=n.translations[t];if(r){const a=Z(r,e);if(a!==void 0)return a}}const s=V(void 0,o);if(s!==t&&o?.translations){const r=o.translations[s];if(r){const a=Z(r,e);if(a!==void 0)return a}}},ft=function(e,t){if(!e)return"";const o=this?.context,n=o?.polyglot,s=this?.state?.root||o?.state?.root,r=ke(e,t||ee(s,n),n,s);if(r!==void 0)return r;const a=de(ue);return n&&a&&a.labelKeys.add(e),e},Re=function(){return ee(this?.state?.root,this?.context?.polyglot)},v=(e,t=new WeakMap)=>{if(e===null||typeof e!="object")return e;if(t.has(e))return t.get(e);if(Array.isArray(e)){const n=[];t.set(e,n);for(const s of e)n.push(typeof s=="object"&&s!==null?v(s,t):s);return n}const o={};t.set(e,o);for(const n of Object.keys(e)){const s=e[n];o[n]=typeof s=="object"&&s!==null?v(s,t):s}return o};let $=null;try{import.meta.url&&($=ie(import.meta.url))}catch{}const pt=()=>{if(!import.meta.url||!$)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:l=>l};const e=He(new URL(".",import.meta.url).pathname),t=u(e,"../..");if(y(u(t,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:t};let n;try{const l=$.resolve("smbls/package.json");n=E(l)}catch{let l=e;for(;l!==E(l);){const g=u(l,"node_modules","smbls");if(y(u(g,"package.json"))){n=g;break}l=E(l)}}let s=$;if(n)try{s=ie(u(n,"package.json"))}catch{}let r,a=process.cwd();for(;a!==E(a);){if(y(u(a,"package.json"))){r=a;break}a=E(a)}let d=s;if(r)try{d=ie(u(r,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:e,projectRoot:r,_smblsRequire:s,_projectRequire:d}},B=(e,t)=>{const o=e.isMonorepo?[$]:[e._smblsRequire,e._projectRequire,$].filter(Boolean);for(const n of o)try{return n.resolve(t)}catch{}return null},xo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages",t,...o);const n=B(e,`${t}/package.json`);return n?u(E(n),...o):null},jo=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"plugins",t,...o);const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null},fe=(e,t,...o)=>{if(e.isMonorepo){for(const s of["packages","plugins"]){const r=u(e.monorepoRoot,s,t,...o);if(y(r))return r}return null}const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null},xe=(e,t,...o)=>{if(e.isMonorepo)return u(e.monorepoRoot,"packages","domql","packages",t,...o);const n=B(e,`@symbo.ls/${t}/package.json`);return n?u(E(n),...o):null};let te=null;const je="smbls/ssr",ut=async()=>{const e=await import("smbls/ssr");if(typeof e?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return e},_e=()=>(te||(te=dt().catch(e=>{throw te=null,e})),te),dt=async()=>{const e=pt();let t;if($&&e.isMonorepo)t=u(e.monorepoRoot,"packages","smbls","src","createDomql.js");else{let f;try{return await ut()}catch(i){f=i}if(!$)throw new Error(`brender: ${je} is not available in this bundled runtime (${f.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`);const c=e.smblsRoot&&u(e.smblsRoot,"src","createDomql.js");if(c&&y(c))t=c;else throw new Error(`brender: cannot load ${je} (${f.message}). Server rendering needs smbls installed in this project, at a release that exports smbls/ssr`+(e.smblsRoot?` (found smbls at ${e.smblsRoot}).`:" (no smbls package found)."))}const o=await import("esbuild"),n=Ge(We(),`br_createDomql_${Je(6).toString("hex")}.mjs`),s=Ue,r=e.isMonorepo?await import(ze(u(e.monorepoRoot,"scripts","vite-workspace-source.mjs")).href):null,a=r?new Set(r.workspacePackageRoots().map(f=>r.workspacePackageAt(f).name)):null,d=/^(?:smbls(?:\/.*)?|domql|css-in-props(?:\/.*)?|@symbo\.ls\/[^/]+)$/,l={name:"workspace-resolve",setup(f){r&&(f.onResolve({filter:/^[^./]/},c=>{if(d.test(c.path))return;const i=c.path.split("/"),p=c.path.startsWith("@")?i.slice(0,2).join("/"):i[0];if(!a.has(p))return;const m=r.resolveWorkspaceSource(c.path,c.importer||t);return m?{path:m}:{errors:[{text:`"${c.path}" names no source file in the workspace`}]}}),f.onLoad({filter:/[\\/]dist[\\/]/},c=>r.isWorkspaceDist(c.path)?{errors:[{text:`${c.path} is a BUILD of a workspace package \u2014 the render bundles source`}]}:void 0)),f.onResolve({filter:/^smbls/},c=>{const i=c.path.replace(/^smbls\/?/,""),p=e.isMonorepo?u(e.monorepoRoot,"packages","smbls"):e.smblsRoot;if(!p)return;if(!i){const S=s(p);return S?{path:S}:void 0}const m=u(p,i);if(y(m))return{path:m};if(y(m+".js"))return{path:m+".js"};const b=u(m,"index.js");if(y(b))return{path:b}}),f.onResolve({filter:/^domql$/},c=>{if(e.isMonorepo){const i=u(e.monorepoRoot,"packages","domql","src","index.js");if(y(i))return{path:i};const p=u(e.monorepoRoot,"packages","domql","index.js");if(y(p))return{path:p}}else try{const i=_require.resolve("domql/package.json"),p=s(E(i));if(p)return{path:p}}catch{}}),f.onResolve({filter:/^@symbo\.ls\//},c=>{const i=c.path.replace("@symbo.ls/","");if(i==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(e.isMonorepo){for(const m of["packages","plugins"]){const b=u(e.monorepoRoot,m,i,"src","index.js");if(y(b))return{path:b};const S=u(e.monorepoRoot,m,i,"index.js");if(y(S))return{path:S}}const p=u(e.monorepoRoot,"packages","default-config","blank","index.js");if(i==="default-config"&&y(p))return{path:p}}else{const p=fe(e,i,"src","index.js");if(p&&y(p))return{path:p};const m=fe(e,i,"index.js");if(m&&y(m))return{path:m};if(i==="default-config"){const b=fe(e,"default-config","blank","index.js");if(b&&y(b))return{path:b}}}}),f.onResolve({filter:/^@domql\//},c=>{const i=c.path.replace("@symbo.ls/","");if(e.isMonorepo){const p=u(e.monorepoRoot,"packages","domql","packages",i,"src","index.js");if(y(p))return{path:p};const m=u(e.monorepoRoot,"packages","domql","packages",i,"index.js");if(y(m))return{path:m}}else{const p=xe(e,i,"src","index.js");if(p&&y(p))return{path:p};const m=xe(e,i,"index.js");if(m&&y(m))return{path:m}}}),f.onResolve({filter:/^css-in-props/},c=>{let i;if(e.isMonorepo)i=u(e.monorepoRoot,"packages","css-in-props");else{const b=B(e,"css-in-props/package.json");b&&(i=E(b))}if(!i)return;const p=c.path.replace(/^css-in-props\/?/,"");if(p){const b=u(i,p),S=u(b,"index.js");if(y(S))return{path:S};if(y(b+".js"))return{path:b+".js"};if(y(b))return{path:b}}const m=s(i);if(m)return{path:m}}),f.onResolve({filter:/^@emotion\//},c=>{let i;if(e.isMonorepo)i=u(e.monorepoRoot,"node_modules",c.path);else{const p=B(e,`${c.path}/package.json`);if(!p)return;i=E(p)}if(y(i)){const p=u(i,"package.json");if(y(p))try{const m=JSON.parse(J(p,"utf8")),b=m.module||m.main||"dist/emotion-css.esm.js";return{path:u(i,b)}}catch{}return{path:i}}}),f.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const i=u(c.resolveDir,c.path);if(y(i))return{path:i}}}),f.onLoad({filter:/smbls\/.*options\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("options.js"))return;let i=J(c.path,"utf8");return i=i.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),i=i.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:i,loader:"js"}}),f.onLoad({filter:/smbls\/.*init\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("init.js"))return;let i=J(c.path,"utf8");return i=i.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:i,loader:"js"}}),f.onLoad({filter:/.*/,namespace:"brender-stub"},()=>({contents:"export const SyncComponent = {}; export const Inspect = {}; export const Notifications = {}; export const syncPlugin = () => ({}); export const notificationsPlugin = () => ({}); export const seedRawDesignSystem = () => {}; export const inspectPlugin = () => ({}); export default {}",loader:"js"})),f.onLoad({filter:/smbls\/src\/router\.js$/},async c=>{let i=J(c.path,"utf8");return i=i.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:i,loader:"js"}}),f.onLoad({filter:/fetchOnCreate\.js$/},async c=>{if(!c.path.includes("smbls/"))return;let i=J(c.path,"utf8");return i=i.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:i,loader:"js"}})}};await o.build({entryPoints:[t],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[l],nodePaths:e.isMonorepo?[u(e.monorepoRoot,"node_modules")]:[...e.smblsRoot?[u(e.smblsRoot,"node_modules")]:[],...e.projectRoot?[u(e.projectRoot,"node_modules")]:[],...e.smblsRoot?[u(e.smblsRoot,"..","..","node_modules")]:[]].filter(f=>y(f)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const g=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{Ne(n)}catch{}return g},mt={Box:{},Focusable:{},Block:{display:"block"},Inline:{display:"inline"},Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Link:{tag:"a",attr:{href:e=>e.href,target:e=>e.target,rel:e=>e.rel}},A:{extends:"Link"},RouteLink:{extends:"Link"},Img:{tag:"img",attr:{src:e=>{let t=e.src;return typeof t=="string"&&t.includes("{{")&&(t=e.call("replaceLiteralsWithObjectFields",t,e.state)),t},alt:e=>e.alt,loading:e=>e.loading}},Image:{extends:"Img"},Button:{tag:"button"},FocusableComponent:{tag:"button"},Form:{tag:"form"},Input:{tag:"input"},TextArea:{tag:"textarea"},Textarea:{tag:"textarea"},Select:{tag:"select"},Label:{tag:"label"},Iframe:{tag:"iframe"},Video:{tag:"video"},Audio:{tag:"audio"},Canvas:{tag:"canvas"},Span:{tag:"span"},P:{tag:"p"},H1:{tag:"h1"},H2:{tag:"h2"},H3:{tag:"h3"},H4:{tag:"h4"},H5:{tag:"h5"},H6:{tag:"h6"},Svg:{tag:"svg",attr:{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},Text:{tag:"span"}},gt=(e,t)=>{const o={};return Ee(e,(n,s)=>{if(!s.__brKey)return;const r=t(n);o[r===""?"__root":r]=s.__brKey}),o},Ee=(e,t)=>{const o=n=>{const s=n&&n.__ref;if(s&&(t(n,s),s.__children))for(const r of s.__children)o(n[r])};o(e)},ht=new Set(["app","config","pages","components","state","snippets","designSystem","dependencies","functions","methods","files","sharedLibraries"]);let K=0;const ve=Symbol.for("brender.fetchTracked"),pe=()=>K,yt=256*1024,bt=1024*1024,St=new Set(["accept-language","accept"]),Te={renders:0,record:null},wt=50,ue={renders:0,record:null},Le=(e,t)=>{e.renders++,e.renders===1?e.record=t():e.record&&(e.record.tainted=!0);const o=e.record;return{record:o,close:()=>{e.renders--,e.record===o&&(e.record=null)}}},de=e=>e.record&&!e.record.tainted?e.record:null,kt=e=>{try{const t=new URL(e);return t.origin+t.pathname}catch{return String(e).split("?")[0]}},me=(e,t,o)=>{!e||e.tainted||e.failures.length>=wt||e.failures.push({url:kt(t),reason:o})},De=e=>e&&typeof e=="object"&&typeof e.url=="string"?e.url:String(e),Rt=e=>{const[t,o]=e,n=t&&typeof t=="object"&&typeof t.url=="string",s=De(t),r=String(o&&o.method||n&&t.method||"GET").toUpperCase(),a={},d=l=>{if(!l)return;const g=!Array.isArray(l)&&typeof l.entries!="function";for(const[f,c]of Ze(l))g&&c==null||(a[String(f).toLowerCase()]=String(c))};return n&&d(t.headers),o&&d(o.headers),{url:s,method:r,headers:a}},xt=(e,t,o,n)=>{if(!o||!o.ok||typeof o.clone!="function")return;const s=o.headers&&o.headers.get&&o.headers.get("content-type")||"";if(!/json|^text\//i.test(s))return;let r;try{r=o.clone()}catch{return}n(r.text().then(a=>{e.tainted||a.length>yt||e.bytes+a.length>bt||(e.bytes+=a.length,e.entries.push({url:t.url,lang:t.headers["accept-language"]||"",status:o.status,type:s,body:a}))},()=>{}))},jt=()=>{const e=globalThis.fetch;if(typeof e!="function"||e[ve])return;const t=function(...o){K++;let n=!1;const s=()=>{n||(n=!0,K--)},r=c=>{K++,c.then(()=>{K--},()=>{K--})},a=de(Te),d=de(ue);let l=null;if(a)try{l=Rt(o),(l.method!=="GET"||!/^https?:/i.test(l.url)||Object.keys(l.headers).some(c=>!St.has(c)))&&(l=null)}catch{l=null}const g=()=>De(o[0]);let f;try{f=e.apply(this,o)}catch(c){throw me(d,g(),c&&c.message||"failed"),s(),c}return f&&typeof f.then=="function"?f.then(c=>{l&&xt(a,l,c,r),d&&c&&c.ok===!1&&me(d,g(),"HTTP "+c.status),s()},c=>{me(d,g(),c&&c.message||"network error"),s()}):s(),f};t[ve]=!0,globalThis.fetch=t},_o=({brRegistry:e,fetchSeed:t,hydrateFlag:o=!0}={})=>{if(!e||!Object.keys(e).length)return"";const n=nt(e);return o?`<script>window.__BR_REGISTRY__=${n};window.__BRENDER_HYDRATE__=true<\/script>`+(t&&t.length?`
2
+ `+Qe(t):""):`<script>window.__BR_REGISTRY__=${n}<\/script>`},_t=3e4,Et=2147483647;let $e=Promise.resolve();const Ae=()=>{},vt=e=>{const t=$e.then(e);return $e=t.then(Ae,Ae),t},Tt=e=>{if(e===void 0)return _t;if(e===0||e===!1||e===1/0)return 0;if(typeof e!="number"||!(e>0))throw new TypeError(`brender: options.timeout is a number of milliseconds, or 0 / false for no limit \u2014 not ${JSON.stringify(e)}`);return Math.min(e,Et)},Lt=(e,t)=>{const o=e.route||"/",n=e.pathname||o,s=n===o?o:`${n} (route ${o})`,r=new Error(`brender: the render of ${s} did not finish within ${t} ms and was abandoned, so the renders after it can run (options.timeout)`);return r.code="BRENDER_RENDER_TIMEOUT",r.route=o,r.pathname=n,r.timeout=t,r},Dt=e=>{e.closed=!0;for(const t of e.undo.splice(0))t()},$t=e=>{let t=!1;return()=>{t||(t=!0,e())}},qe=(e,t={})=>{let o;try{o=Tt(t.timeout)}catch(n){return Promise.reject(n)}return vt(()=>At(e,t,o))},At=async(e,t,o)=>{await _e();const n={closed:!1,undo:[]},s=qt(e,t,n);if(!o)return s;let r;const a=new Promise((d,l)=>{r=setTimeout(()=>{Dt(n),l(Lt(t,o))},o)});try{return await Promise.race([s,a])}finally{clearTimeout(r)}},qt=async(e,t,o)=>{const{route:n="/",pathname:s,state:r,context:a,prefetch:d=!1}=t,l=s||n;jt();const g=Le(ue,()=>({failures:[],labelKeys:new Set,tainted:!1}));try{return await Ct(e,{route:n,locationPath:l,stateOverrides:r,contextOverrides:a,prefetch:d},g.record,o)}finally{g.close()}},Ct=async(e,{route:t,locationPath:o,stateOverrides:n,contextOverrides:s,prefetch:r},a,d)=>{const l=()=>{if(d.closed)throw new Error("brender: this render was abandoned at its timeout")};let g;if(r){try{g=await Ve(e)}catch(m){d.closed||console.warn("[brender] SSR translation fetch failed:",m.message)}l()}const{window:f,document:c}=ye(),i=c.body;f.location.pathname=o;const p=[oe("document",c),oe("location",f.location),oe("localStorage",f.localStorage),oe("sessionStorage",f.sessionStorage)].map($t);d.undo.push(...p);try{const{createDomqlElement:m}=await _e();l();const b=v(e.app||{}),S={...e.config||{}};e.polyglot&&!S.polyglot&&(S.polyglot=e.polyglot),e.fetch&&!S.fetch&&(S.fetch=e.fetch),e.router&&!S.router&&(S.router=e.router);for(const w of Bt)e[w]!=null&&S[w]==null&&(S[w]=e[w]);const k=S.polyglot?{...S.polyglot}:void 0,z=rt(o,k);z&&f.localStorage.setItem(st(k),z),g&&k&&(k.translations={...k.translations||{},...g});const h=v(e.state||{});(g||k)&&(h.root||(h.root={}),k&&(h.root.lang=V(h.root.lang,k)),g&&(h.root.translations={...h.root.translations||{},...g})),lt();const A={};for(const w in e){if(ht.has(w))continue;const x=e[w];A[w]=x&&typeof x=="object"?v(x):x}const T={...A,state:h,...n?{state:{...h,...n}}:{},dependencies:v(e.dependencies||{}),components:v(e.components||{}),snippets:v(e.snippets||{}),pages:v(e.pages||{}),functions:{...e.functions||{},polyglot:ft,getActiveLang:Re,getLang:Re},methods:e.methods||{},designSystem:v(e.designSystem||{}),files:e.files||{},sharedLibraries:e.sharedLibraries||[],...S,...k?{polyglot:k}:{},document:c,window:f,parent:{node:i},initOptions:{},domqlOptions:{sourcemap:!1},firstRender:!1,sync:!1,inspect:!1,notifications:!1,isolateDesignSystem:!0,isolateFetchCache:!0,captureDesignSystem:!0,...s||{}},G=await at();l(),G&&(T.plugins=T.plugins||[],T.plugins.some(w=>w.name==="funcql")||T.plugins.push(G)),be();const I=Le(Te,()=>({entries:[],bytes:0,tainted:!1})),O=I.record;let j;try{j=await m(b,T),l();const w=Date.now(),x=r?2e3:50,W=ae=>new Promise(Ke=>setTimeout(Ke,ae));for(await W(Math.min(50,x));r&&!d.closed&&pe()>0&&Date.now()-w<x;)await W(10),pe()===0&&await W(Math.max(0,Math.min(50,x-(Date.now()-w))));if(l(),r&&await Mt(j,k)){l();const ae=Date.now();for(await W(Math.min(50,x));!d.closed&&pe()>0&&Date.now()-ae<x;)await W(10);l()}}finally{I.close()}const Y=O&&!O.tainted&&!(S.fetch&&S.fetch.seed===!1)?O.entries.slice():[];Se(i);const X=we(j),{elementPath:q}=await import("@symbo.ls/element");l();const N=gt(j,q),H=j?.context?.routerOptions?.contentElementKey||"content",L=j&&j[H],P=L&&typeof L=="object"&&L.__ref?L:void 0,C=le(e,t,j,j?.state,P),D=[],se=c.head||c.querySelector("head");if(se)for(const w of se.querySelectorAll("style")){if(w.sheet&&w.sheet.cssRules)for(const x of w.sheet.cssRules)x.cssText&&D.push(x);if(!D.length){const x=w.textContent||"";x&&D.push({cssText:x})}}const R=D.map(w=>w.cssText),{globalCSS:_,classRules:M}=Ht(D);let F=Ie(i.innerHTML);const U=j?.state?.root,re=ee(U,k),ce=It(g,U);ce&&(F=Me(F,Ce(ce,re,k)));const Be=a&&!a.tainted?{failedRequests:a.failures.slice(),rawLabelKeys:Ft(a.labelKeys,F,re,k,U)}:null;return{html:F,metadata:C,registry:X,brRegistry:N,element:j,emotionCSS:R,globalCSS:_,classRules:M,fetchSeed:Y,document:c,window:f,ssrTranslations:ce||void 0,activeLang:re,diagnostics:Be,preparedDesignSystem:T.preparedDesignSystem||null}}finally{for(const m of p)m()}},oe=(e,t)=>{const o=Object.getOwnPropertyDescriptor(globalThis,e);return o&&!o.configurable?!o.writable&&!o.set?()=>{}:(globalThis[e]=t,()=>{}):(Object.defineProperty(globalThis,e,{value:t,writable:!0,configurable:!0,enumerable:!0}),()=>{o?Object.defineProperty(globalThis,e,o):delete globalThis[e]})},Mt=async(e,t)=>{if(!t||!t.fetch||!e||typeof e.call!="function")return!1;const o=e.state&&e.state.root;if(!o)return!1;const n=ee(o,t),s=o.translations&&o.translations[n];if(s&&typeof s=="object"&&Object.keys(s).length||t.translations&&t.translations[n])return!1;try{await e.call("loadTranslations",n,{inFlight:!0})}catch{}return!0},ge=/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,he=e=>e&&typeof e.parse=="function"?e.parse():e,It=(e,t)=>{const o=he(t?.translations),n={};for(const s of[e,o])if(!(!s||typeof s!="object"))for(const r in s){const a=he(s[r]);a&&typeof a=="object"&&(n[r]={...n[r]||{},...a})}return Object.keys(n).length?n:null},Ce=(e,t,o)=>e[t]||e[V(void 0,o)]||Object.values(e)[0]||{},Me=(e,t)=>e.replace(ge,(o,n)=>Z(t,n.trim())??o),Ot=/<script\b[^>]*\btype\s*=\s*["']?application\/json["']?[^>]*>[\s\S]*?<\/script\s*>/gi,Pt=(e,t)=>new RegExp(`(?:^|[^\\w.-])${ot(t)}(?![\\w-]|\\.\\w)`).test(e),Ft=(e,t,o,n,s)=>{const r=new Set,a=t.replace(Ot,"");for(const d of e)/\s/.test(d)||ke(d,o,n,s)===void 0&&Pt(a,d)&&r.add(d);for(const d of a.matchAll(ge))r.add(d[1].trim());return[...r]},Eo=async(e,t={})=>{const{context:o={}}=t,{window:n,document:s}=ye(),r=s.body,{create:a}=await import("@symbo.ls/element"),d=await import("@symbo.ls/utils"),l={...mt,...o.components||{}},g={...d,...o.utils||{},...o.functions||{}};be();let f;try{f=a(e,{node:r},"root",{context:{document:s,window:n,...o,components:l,utils:g}})}catch{}Se(r);const c=f?we(f):{};return{html:Ie(r.innerHTML),registry:c,element:f}},Ie=e=>e.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(t,o,n,s)=>{if(n.includes("&lt;")){const r=n.replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'");return o+r+s}return t}),Bt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],Kt=/\.[A-Za-z_\\-]/,Gt=e=>{if(typeof e!="string"||!e)return!1;const t=e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,'""').replace(/\[[^\]]*\]/g,"[]");return!Kt.test(t)},Nt=/^\s*(:root|\[data-theme|\[data-skin)/,Oe=e=>{const t=e&&e.cssText;if(typeof t!="string")return null;if(/^@(-webkit-)?keyframes\b/.test(t))return"keyframes";if(t.startsWith("@font-face"))return null;if(typeof e.selectorText=="string")return Gt(e.selectorText)?Nt.test(e.selectorText)?"root":"reset":null;const o=e.cssRules&&Array.from(e.cssRules);if(o&&o.length&&/^@(media|supports|container)\b/.test(t)){const n=new Set(o.map(Oe));return n.has(null)||n.size!==1?n.has(null)?null:"reset":[...n][0]}return null},Ht=e=>{const t={root:[],reset:[],keyframes:[]},o=[];for(const n of e||[]){const s=Oe(n);s?t[s].push(n.cssText):n&&n.cssText&&o.push(n.cssText)}return{globalCSS:{rootRule:t.root.join(`
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 ne=new Set;const vo=()=>{ne=new Set},To=()=>Array.from(ne).join(`
6
+ `),Lo=(e,t)=>e.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,t?`<style data-emotion="smbls">
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>`:""),Do=async(e,t={})=>{const{route:o="/",pathname:n,prefetch:s=!0,timeout:r}=t,a=await qe(e,{route:o,pathname:n,prefetch:s,timeout:r});if(!a)return null;const d=e.designSystem||{},l=a.globalCSS;let g=null;try{const c=Ut(a.element);Object.keys(c).length&&(g=c)}catch{}const f=a.activeLang;return{html:a.html,css:a.classRules.join(`
9
+ `),classRules:a.classRules.slice(),globalCSS:l,resetCss:l.resetRules,fontLinks:Fe(d),metadata:a.metadata||le(e,o),brKeyCount:a.registry?Object.keys(a.registry).length:0,brRegistry:a.brRegistry||{},fetchSeed:a.fetchSeed||[],ssrTranslations:a.ssrTranslations,prefetchedState:g,activeLang:f,diagnostics:a.diagnostics,preparedDesignSystem:a.preparedDesignSystem||null}},Ut=e=>{const t={};return Ee(e,o=>{const n=o.fetch;if(!(!n||typeof n!="object"||!o.state))for(const s of Array.isArray(n)?n:[n]){const r=s&&typeof s=="object"&&typeof s.as=="string"?s.as:null;if(!r)continue;const a=r.startsWith("~/"),d=a?r.slice(2):r,l=a?o.state.root:o.state,g=he(l&&l[d]);d&&g!=null&&typeof g!="function"&&(t[d]=g)}}),t},$o=async(e,t="/",o={})=>{const{lang:n,themeColor:s,isr:r,hydrate:a=!0,prefetch:d=!0,shell:l,adoptDom:g=!1,pathname:f,eagerImages:c=Pe,preloadScripts:i=!0,nonce:p,timeout:m,stamp:b,time:S}=o,k=Vt(p),z=n||e.state?.lang||e.app?.metadata?.lang||"en",h=await qe(e,{route:t,pathname:f,prefetch:d,timeout:m});if(!h)return null;const A={...h.metadata};if(s&&(A["theme-color"]=s),typeof l=="string"&&!e.name){const R="\0brender-undeclared-title\0";if(le({...e,name:R},t,h.element,h.element?.state).title===R)for(const M of["title","og:title","twitter:title"])A[M]===h.metadata.title&&delete A[M]}const T=Ye(A);for(const R of h.classRules)R&&ne.add(R);const G=Array.from(ne).join(`
10
+ `),I=h.globalCSS,O=`<style data-brender>
11
+ ${I.rootRule||""}
12
+ ${I.resetRules||""}
13
+ ${I.keyframeRules||""}
14
+ </style>`,j=G?`<style data-emotion="smbls">
15
+ ${G}
16
+ </style>`:"",Y=Fe(e.designSystem||{}),X=Object.keys(h.registry).length,q=Yt({brRegistry:h.brRegistry,fetchSeed:h.fetchSeed,adoptDom:g,stamp:b,time:S});let N="";if(r&&r.clientScript){const R=Number.isInteger(o.depth)?o.depth:t==="/"?0:t.replace(/^\/|\/$/g,"").split("/").length,_=R>0?"../".repeat(R):"./";if(a){const M=ct({polyglot:e.polyglot||e.config?.polyglot,translations:h.ssrTranslations,lang:h.activeLang,pathname:f||t,attrs:k}),F=M?M+`
17
+ `:"",U=q.blocks?q.blocks+`
18
+ `:"";N=`${F}${U}<script type="module" src="${_}${r.clientScript}"${k}><\/script>`}else N=`<script type="module"${k}>
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="${_}${r.clientScript}"${k}><\/script>`}const H={...e.config||{}};e.polyglot&&!H.polyglot&&(H.polyglot=e.polyglot);const L=H.polyglot;let P=T;if(L){const R={...L.translations||{}};for(const _ in h.ssrTranslations||{})R[_]={...R[_]||{},...h.ssrTranslations[_]};P=Me(T,Ce(R,h.activeLang,L))}const C=h.diagnostics;if(C)for(const R of P.matchAll(ge)){const _=R[1].trim();C.rawLabelKeys.includes(_)||C.rawLabelKeys.push(_)}const D=zt(h.html,c);return typeof l=="string"?{html:Xe(l,{headTags:`${P}
36
+ ${q.meta}`,headEnd:[Y,O,j].filter(Boolean).join(`
37
+ `),body:D,depth:Number.isInteger(o.depth)?o.depth:0,bootScripts:q.blocks,lang:n||(L?h.activeLang:void 0),preloadScripts:i}),route:t,brKeyCount:X,shell:!0,diagnostics:C,preparedDesignSystem:h.preparedDesignSystem||null}:{html:`<!DOCTYPE html>
38
+ <html lang="${z}">
39
39
  <head>
40
- ${I}${r&&r.clientScript&&i?`
41
- ${C.meta}`:""}
42
- ${D}
40
+ ${P}${r&&r.clientScript&&a?`
41
+ ${q.meta}`:""}
42
+ ${Y}
43
43
 
44
- ${B}
45
- ${G}
44
+ ${O}
45
+ ${j}
46
46
  </head>
47
47
  <body>
48
- ${H}
49
- ${K}
48
+ ${D}
49
+ ${N}
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:X,diagnostics:C,preparedDesignSystem:h.preparedDesignSystem||null}},Pe=2,Wt=/<img\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi,Jt=/\sloading\s*=/i,zt=(e,t=Pe)=>{if(t===!1||t==null||typeof e!="string")return e;const o=Math.max(0,Number(t)||0);let n=0;return e.replace(Wt,s=>n++<o||Jt.test(s)?s:s.replace(/^<img\b/i,'<img loading="lazy"'))},Fe=e=>{if(!e)return"";const t=e.font_family||e.fontFamily||{},o=new Set;for(const s of Object.values(t)){if(typeof s!="string")continue;const r=s.match(/'([^']+)'/);r&&o.add(r[1])}return o.size?['<link rel="preconnect" href="https://fonts.googleapis.com">','<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',`<link href="https://fonts.googleapis.com/css2?${[...o].map(s=>`family=${s.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
52
+ `):""},Yt=({brRegistry:e,fetchSeed:t,adoptDom:o=!1,stamp:n,time:s}={})=>{const r=o&&e&&Object.keys(e).length?et(e):"",a=it(t);return{meta:`<meta name="brender" content="${r?"hydrate":"replace"}">
53
+ `+tt(Xt(n,s)),blocks:[r,a].filter(Boolean).join(`
54
+ `)}},Xt=(e,t)=>{const o=typeof t=="number"&&isFinite(t)?Math.round(t):Date.now();return{stamp:e!=null&&e!==""?String(e):new Date(o).toISOString(),time:o}},Vt=e=>{if(e==null||e==="")return"";const t=String(e);if(!/^[A-Za-z0-9+/=_-]+$/.test(t))throw new Error("brender: a nonce is base64 or base64url text");return` nonce="${t}"`};export{Pe as EAGER_IMAGES_DEFAULT,je as SMBLS_SSR_SPECIFIER,_o as adoptionScripts,Xt as buildStamp,To as getAccumulatedEmotionCSS,zt as markLazyImages,Yt as prerenderParts,qe as render,Eo as renderElement,$o as renderPage,Do as renderRoute,Lo as replaceEmotionCSS,vo as resetGlobalCSSCache,Ht as splitCapturedCSS};
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.669",
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",
53
- "@symbo.ls/helmet": "^3.14.670",
54
- "@symbo.ls/scratch": "^3.14.789",
55
- "@symbo.ls/utils": "^3.14.788",
49
+ "@symbo.ls/css": "^3.14.782",
50
+ "@symbo.ls/default-config": "^3.14.789",
51
+ "@symbo.ls/element": "^3.14.809",
52
+ "@symbo.ls/fetch": "^3.14.669",
53
+ "@symbo.ls/helmet": "^3.14.671",
54
+ "@symbo.ls/scratch": "^3.14.792",
55
+ "@symbo.ls/utils": "^3.14.845",
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.844"
61
61
  },
62
62
  "optionalDependencies": {
63
63
  "@symbo.ls/funcql": "^3.14.0"
package/prefetch.js CHANGED
@@ -269,7 +269,9 @@ export const fetchSSRTranslations = async (data) => {
269
269
 
270
270
  const fetchConfig = polyglot.fetch
271
271
  const rpcName = fetchConfig.rpc || fetchConfig.from || 'get_translations_if_changed'
272
- const languages = polyglot.languages || [polyglot.defaultLang || 'en']
272
+ // `languages` may be a function (utils polyglotLanguages); none: the default
273
+ const listed = (await import('@symbo.ls/utils')).polyglotLanguages(polyglot, false)
274
+ const languages = listed.length ? listed : [polyglot.defaultLang || 'en']
273
275
 
274
276
  const translations = {}
275
277
 
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)
@@ -1329,7 +1351,13 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1329
1351
  // Extract metadata for the rendered route
1330
1352
  // Pass the rendered element and its state so function-valued metadata
1331
1353
  // (e.g. detail page titles from fetched data) can be resolved
1332
- const metadata = extractMetadata(data, route, element, element?.state)
1354
+ // The routed page's own metadata resolves against the page element and its
1355
+ // state, where its fetched data lands — as the client's helmet resolves it
1356
+ // (XMA round 5, item L: the head carried the app's defaults).
1357
+ const contentKey = element?.context?.routerOptions?.contentElementKey || 'content'
1358
+ const routed = element && element[contentKey]
1359
+ const pageElement = routed && typeof routed === 'object' && routed.__ref ? routed : undefined
1360
+ const metadata = extractMetadata(data, route, element, element?.state, pageElement)
1333
1361
 
1334
1362
  // Extract CSS from style tags in virtual head (atomic CSS engine writes here)
1335
1363
  const capturedRules = []
@@ -1375,7 +1403,7 @@ const renderWithDiagnostics = async (data, { route, locationPath, stateOverrides
1375
1403
  }
1376
1404
  : null
1377
1405
 
1378
- return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics }
1406
+ return { html, metadata, registry, brRegistry, element, emotionCSS, globalCSS, classRules, fetchSeed, document, window, ssrTranslations: labels || undefined, activeLang, diagnostics, preparedDesignSystem: ctx.preparedDesignSystem || null }
1379
1407
  } finally {
1380
1408
  for (const restore of restoreGlobals) restore()
1381
1409
  }
@@ -1398,6 +1426,39 @@ const swapGlobal = (name, value) => {
1398
1426
  }
1399
1427
  }
1400
1428
 
1429
+ // ── Labels the render is still loading ──────────────────────────────────────
1430
+ // A function `polyglot.fetch` loads the page's labels inside the render (the
1431
+ // polyglot plugin calls it with the root element, see fetchSSRTranslations).
1432
+ // The flush waits for requests up to its cap; the labels' load is more than
1433
+ // a request (the project's loader, its own awaits, the body, the answer into
1434
+ // the root state), and the first render of a process — a cold connection,
1435
+ // a cold JIT, a page that renders a lot — took longer than the cap: `smbls
1436
+ // build` of xma.info wrote `/` with 49 raw label keys while its labels landed
1437
+ // 2.9 s after the render had ended (2026-10-18). So when the root has no
1438
+ // labels for its language yet, the render waits for the load in flight for
1439
+ // this app's root: the polyglot plugin's `loadTranslations(lang, { inFlight:
1440
+ // true })` hands it back (@symbo.ls/polyglot loadsInFlight) and starts
1441
+ // nothing — a load that already ended, failed or answered "unchanged", is not
1442
+ // asked again. True when it asked (the render then gives the answer a quiet
1443
+ // period).
1444
+ const awaitLabels = async (element, poly) => {
1445
+ if (!poly || !poly.fetch || !element || typeof element.call !== 'function') return false
1446
+ const root = element.state && element.state.root
1447
+ if (!root) return false
1448
+ const lang = activeLangOf(root, poly)
1449
+ const have = root.translations && root.translations[lang]
1450
+ if (have && typeof have === 'object' && Object.keys(have).length) return false
1451
+ if (poly.translations && poly.translations[lang]) return false
1452
+ try {
1453
+ // undefined at once when no load is in flight
1454
+ await element.call('loadTranslations', lang, { inFlight: true })
1455
+ } catch (e) {
1456
+ // the load reports its own failure (polyglot logs it; the request shows
1457
+ // in the diagnostics); the raw keys check names what is missing
1458
+ }
1459
+ return true
1460
+ }
1461
+
1401
1462
  // ── Labels after a render ───────────────────────────────────────────────────
1402
1463
 
1403
1464
  const LABEL_TEMPLATE = /\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g
@@ -1702,7 +1763,11 @@ export const renderRoute = async (data, options = {}) => {
1702
1763
  activeLang,
1703
1764
  // failed requests and raw label keys (see `DIAG`); null when an
1704
1765
  // overlapping render made them unknowable
1705
- diagnostics: result.diagnostics
1766
+ diagnostics: result.diagnostics,
1767
+ // the design system this render prepared (smbls prepare.js), for the
1768
+ // caller to hand the page (@symbo.ls/utils designSystemLink /
1769
+ // designSystemBlock)
1770
+ preparedDesignSystem: result.preparedDesignSystem || null
1706
1771
  }
1707
1772
  }
1708
1773
 
@@ -1763,13 +1828,17 @@ const collectFetchedState = (element) => {
1763
1828
  * The result then carries `shell: true`.
1764
1829
  * @param {number|false} [options.timeout] - render()'s time limit (30 s by
1765
1830
  * default; 0 or false: none) — see render()
1831
+ * @param {string|number} [options.stamp] - The build's data stamp, written into
1832
+ * the page (see buildStamp); default: the render time as an ISO string
1833
+ * @param {number} [options.time] - The build time in ms (see buildStamp);
1834
+ * default: now. A build passes one stamp and time to every page.
1766
1835
  * @param {number} [options.depth] - Directory depth of the output file: rebases
1767
1836
  * the shell's relative asset URLs (with `shell`, default 0) and the ISR
1768
1837
  * client script path (default: derived from the route).
1769
1838
  * @returns {Promise<{ html: string, route: string, brKeyCount: number, shell?: true }>}
1770
1839
  */
1771
1840
  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
1841
+ const { lang, themeColor, isr, hydrate = true, prefetch = true, shell, adoptDom = false, pathname, eagerImages = EAGER_IMAGES_DEFAULT, preloadScripts = true, nonce, timeout, stamp, time } = options
1773
1842
  const nonceAttr = nonceAttribute(nonce)
1774
1843
 
1775
1844
  // Detect lang from project config, app metadata, or default
@@ -1828,7 +1897,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
1828
1897
  // and the data blocks after the markup — the registry under `adoptDom`,
1829
1898
  // and the recorded anonymous answers, so the client renders from the data
1830
1899
  // the server used instead of asking for all of it again.
1831
- const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom })
1900
+ const prerender = prerenderParts({ brRegistry: result.brRegistry, fetchSeed: result.fetchSeed, adoptDom, stamp, time })
1832
1901
 
1833
1902
  // ISR: include client SPA bundle for hydration + data fetching
1834
1903
  let isrBody = ''
@@ -1936,7 +2005,7 @@ export const renderPage = async (data, route = '/', options = {}) => {
1936
2005
  lang: lang || (polyglotCfg ? result.activeLang : undefined),
1937
2006
  preloadScripts
1938
2007
  })
1939
- return { html, route, brKeyCount, shell: true, diagnostics }
2008
+ return { html, route, brKeyCount, shell: true, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
1940
2009
  }
1941
2010
 
1942
2011
  // (The empty line after the font links held a @font-face <style> that
@@ -1956,7 +2025,7 @@ ${isrBody}
1956
2025
  </body>
1957
2026
  </html>`
1958
2027
 
1959
- return { html, route, brKeyCount, diagnostics }
2028
+ return { html, route, brKeyCount, diagnostics, preparedDesignSystem: result.preparedDesignSystem || null }
1960
2029
  }
1961
2030
 
1962
2031
  // ── Images below the fold ───────────────────────────────────────────────────
@@ -2025,17 +2094,35 @@ const generateFontLinks = (ds) => {
2025
2094
  // again. Placed after the markup, so they never delay its paint;
2026
2095
  // the app's scripts run after them either way.
2027
2096
  // `adoptionScripts` above stays for older callers (the platform's renderer).
2028
- export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false } = {}) => {
2097
+ // The build's stamp (XMA round 3, item B) goes into <head> after the mark:
2098
+ // `<meta name="brender-stamp">` (@symbo.ls/utils brenderStampMeta) — read by
2099
+ // the client's fetch plugin, which decides whether the page is stale
2100
+ // (@symbo.ls/fetch early.js "Stale pages"), by app code
2101
+ // (`el.context.prerender`, smbls prerender.js) and by a host that compares
2102
+ // it with its data's last change.
2103
+ export const prerenderParts = ({ brRegistry, fetchSeed, adoptDom = false, stamp, time } = {}) => {
2029
2104
  const registry = adoptDom && brRegistry && Object.keys(brRegistry).length
2030
2105
  ? brenderRegistryBlock(brRegistry)
2031
2106
  : ''
2032
2107
  const seed = createEarlySeedBlock(fetchSeed)
2033
2108
  return {
2034
- meta: `<meta name="brender" content="${registry ? 'hydrate' : 'replace'}">`,
2109
+ meta: `<meta name="brender" content="${registry ? 'hydrate' : 'replace'}">\n` + brenderStampMeta(buildStamp(stamp, time)),
2035
2110
  blocks: [registry, seed].filter(Boolean).join('\n')
2036
2111
  }
2037
2112
  }
2038
2113
 
2114
+ // ── The build's stamp ───────────────────────────────────────────────────────
2115
+ // A pre-rendered page carries the data of the time it was rendered until it
2116
+ // is rendered again (`smbls build` pages: until the next build). Each page
2117
+ // says when that was — `time`, ms since the epoch — and which data it holds:
2118
+ // `stamp`, a project's own version of its data (`brender.stamp`, run once
2119
+ // per build by the CLI), else the render time as an ISO string. One build
2120
+ // passes one stamp and time to every page.
2121
+ export const buildStamp = (stamp, time) => {
2122
+ const at = typeof time === 'number' && isFinite(time) ? Math.round(time) : Date.now()
2123
+ return { stamp: stamp != null && stamp !== '' ? String(stamp) : new Date(at).toISOString(), time: at }
2124
+ }
2125
+
2039
2126
  // ` nonce="…"` for an inline script, or '' — a value that could break out of
2040
2127
  // the attribute is refused.
2041
2128
  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)) {