@uniweb/runtime 1.3.9 → 1.3.10
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/dist/ssr.js.map +1 -1
- package/package.json +3 -3
- package/src/components/WebsiteRenderer.jsx +7 -11
- package/src/document-tracking.js +157 -9
- package/src/hooks/useDocumentEvents.js +64 -0
- package/src/wire-foundation.js +5 -5
- package/src/hooks/useOutboundClicks.js +0 -64
- package/src/hooks/useSectionClicks.js +0 -100
package/dist/ssr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/background-shared.js","../src/background.js","../src/default-404.js","../src/wire-foundation.js","../src/sticky.js","../src/area-wrappers.js","../src/default-layout.js","../src/appearance.js","../src/document-title.js","../src/start-page.js","../src/resolve-page.js","../src/ssr-renderer.js","../src/default-fetcher.js","../src/require-transport.js","../src/page-blocks.js","../src/page-icons.js","../src/load-page-data.js","../src/page-renderer.js","../src/collect-records.js"],"sourcesContent":["/**\n * Props Preparation for Runtime Guarantees\n *\n * Prepares props for foundation components with:\n * - Param defaults from runtime schema\n * - Guaranteed content structure (no null checks needed)\n * - `content.data` holding the keys the component declares, each as it arrived\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\n/**\n * Guarantee item has flat content structure\n *\n * @param {Object} item - Raw item from parser\n * @returns {Object} Item with guaranteed flat structure\n */\nfunction guaranteeItemStructure(item) {\n return {\n title: item.title || '',\n pretitle: item.pretitle || '',\n subtitle: item.subtitle || '',\n paragraphs: item.paragraphs || [],\n links: item.links || [],\n images: item.images || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\n // ⛔ An entry's own insets were left out until 2026-09-29, as `tables` was: the\n // parser delivered them and this object, built key by key, did not carry them.\n insets: item.insets || [],\n media: item.media || [],\n snippets: item.snippets || [],\n buttons: item.buttons || [],\n data: item.data || {},\n cards: item.cards || [],\n documents: item.documents || [],\n forms: item.forms || [],\n quotes: item.quotes || [],\n headings: item.headings || [],\n ...(item.math && item.math.length ? { math: item.math } : {}),\n ...(item.tables && item.tables.length ? { tables: item.tables } : {}),\n }\n}\n\n/**\n * Guarantee content structure exists\n * Returns a flat content object with all standard fields guaranteed to exist\n *\n * @param {Object} parsedContent - Raw parsed content from semantic parser (flat structure)\n * @returns {Object} Content with guaranteed flat structure\n */\nexport function guaranteeContentStructure(parsedContent) {\n const content = parsedContent || {}\n\n return {\n // Flat header fields\n title: content.title || '',\n pretitle: content.pretitle || '',\n subtitle: content.subtitle || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n images: content.images || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\n insets: content.insets || [],\n // The section's visual media — images, videos and insets on their own line — in\n // the order written, each with its `kind`: what a `media` slot holds (2026-09-29).\n media: content.media || [],\n snippets: content.snippets || [],\n buttons: content.buttons || [],\n data: content.data || {},\n cards: content.cards || [],\n documents: content.documents || [],\n forms: content.forms || [],\n quotes: content.quotes || [],\n headings: content.headings || [],\n\n // Rare collections — surfaced only when present so pages that don't\n // use them don't pay the allocation cost. Foundations that need them\n // should check for presence (content.math?.length) or use\n // content.sequence for in-order rendering.\n //\n // ⛔ `tables` was not passed through until 2026-09-29: the parser delivered it and\n // this object, built key by key, left it out — so a component declaring `tables`\n // received nothing, while `math`, its twin, arrived.\n ...(content.math && content.math.length ? { math: content.math } : {}),\n ...(content.tables && content.tables.length ? { tables: content.tables } : {}),\n\n // Items with guaranteed structure\n items: (content.items || []).map(guaranteeItemStructure),\n\n // Sequence for ordered rendering\n sequence: content.sequence || [],\n\n // Preserve raw content if present\n raw: content.raw,\n }\n}\n\n/**\n * Apply param defaults from runtime schema\n *\n * @param {Object} params - Params from frontmatter\n * @param {Object} defaults - Default values from runtime schema\n * @returns {Object} Merged params with defaults applied\n */\nexport function applyDefaults(params, defaults) {\n if (!defaults || Object.keys(defaults).length === 0) {\n return params || {}\n }\n\n return {\n ...defaults,\n ...(params || {}),\n }\n}\n\n/**\n * ⭐ `content.data` HOLDS THE KEYS THE COMPONENT DECLARES, AND NOTHING ELSE — ruled 2026-09-14\n * [Diego]. Every key in its `meta.js` `data:` and its foundation's `main.js` `data:`\n * (`website.declaredKeys`), each filled, in order, from:\n *\n * 1. what the section holds under that key (`block.heldData`) — a tagged data block, or\n * its own fetch's answer a static build prerendered into it;\n * 2. what the entity store delivered for it — the fetch that fills it (`fillDeclaredKeys`);\n * 3. `null` — nothing fills it, or its fetch is still out (`block.dataLoading`) or failed\n * (`block.dataError[key]`). ⭐ Never `[]`, which is an answer with no records.\n *\n * A key declared `single: true` then holds one record, or `null` (`holdOneRecord`).\n *\n * A tagged block the section holds under a key the component does not declare is left out, and\n * said so in dev (`warnUndeclaredBlocks`); it stays in `content.sequence`. A block inside an item\n * is that item's (`content.items[n].data`), never held here. The data object is rebuilt on each\n * render from those sources, never from the last render's.\n *\n * ⛔ Until 2026-09-14 this merged every delivered key into what the section held, so a\n * section received every fetch that reached it and every tagged block it had.\n */\nfunction assembleData(block, meta, entityData) {\n const declared = block.website?.declaredKeys ? block.website.declaredKeys(meta) : []\n const held = block.heldData || {}\n const data = {}\n for (const [key] of declared) {\n if (held[key] !== undefined) data[key] = held[key]\n else if (entityData && entityData[key] !== undefined) data[key] = entityData[key]\n else data[key] = null\n }\n // What the section holds and this leaves out — the keys the dev warning reports.\n warnUndeclaredBlocks(block, Object.keys(held).filter((key) => !Object.hasOwn(data, key)))\n block.parsedContent.data = data\n return declared\n}\n\n/**\n * ⭐ A KEY DECLARED `single: true` HOLDS ONE RECORD — the first of the list that filled it, or `null`\n * when the list is empty (ruled 2026-10-07 [Diego]).\n * Every source answers a list: a query's records, a parametric page's record (a list of one), a\n * static build's prerendered answer. The store already asked for one (`@uniweb/core/page-data`'s\n * `keyProgram`). A value that is not a list is the record already — a data block holding it, or an\n * external source answering one — and is left as it is.\n *\n * Runs after the records are linked (`linkOwnRecords`), so the record keeps its `$route`. The data\n * object is replaced, never written into.\n */\nfunction holdOneRecord(block, declared) {\n const data = block.parsedContent.data\n let out = null\n for (const [key, , , single] of declared) {\n if (single !== true || !Array.isArray(data?.[key])) continue\n out = out || { ...data }\n out[key] = data[key][0] ?? null\n }\n if (out) block.parsedContent.data = out\n}\n\n/**\n * Say once, in dev, that a tagged block the section holds — a data block or a concept block — did\n * not reach its component: its key is not declared. `assembleData` passes the keys it left out, so\n * this names exactly what the component did not receive. The block is still in `content.sequence`,\n * where a component that renders the sequence finds it.\n *\n * ⭐ AN ITEM'S BLOCK IS NEVER ONE OF THEM. The parser keeps a block inside an item with that item\n * (`content.items[n].data`), which asks no declaration, and only the section's own blocks reach\n * `block.heldData`. ⛔ Until 2026-10-25 this walked `content.sequence`, which lists every element of\n * the section, its items' included, and so reported each card's `meta` block in a card grid as left\n * out of `content.data`, where it never belonged.\n */\nconst warnedBlocks = new Set()\nfunction warnUndeclaredBlocks(block, leftOut) {\n if (!leftOut.length || !block.website?.entityStore?.dev) return\n const sequence = block.parsedContent?.sequence || []\n for (const tag of leftOut) {\n const memo = `${block.type}::${tag}`\n if (warnedBlocks.has(memo)) continue\n // The section's own block under the tag is the first in the sequence: the parser cuts the\n // sequence into slices, and the section's own content is the first (`groups.js`). None means\n // the key holds a fetch's prerendered answer, which is not a block.\n // A data block (```yaml:tag) and a concept block (```md:tag) both land under their tag, so\n // both need the key declared. ⛔ Until 2026-09-29 only a data block was reported.\n const element = sequence.find((e) => (e?.type === 'dataBlock' || e?.type === 'concept_block') && e.tag === tag)\n if (!element) continue\n warnedBlocks.add(memo)\n const kind = element.type === 'dataBlock' ? 'data block' : 'concept block'\n console.warn(\n `[uniweb] ${block.type}: the \\`${tag}\\` ${kind} is not in content.data — ${block.type} does not ` +\n `declare \\`${tag}\\` in its meta.js \\`data:\\`. It stays in content.sequence.`\n )\n }\n}\n\n/**\n * Run the foundation-level data handler on a block, if one is\n * registered. Runs after entity data merge and before the content\n * handler — the handler sees the fully assembled data and can filter,\n * reshape, or augment it before Loom (or any content transform) runs.\n *\n * The handler receives `(data, block)` where data is\n * `block.parsedContent.data`. It returns a new data object, or\n * null/undefined for no change. The returned data replaces\n * `block.parsedContent.data` for all downstream processing — both\n * the content handler and the component see the transformed data.\n *\n * Skipped when the block is still waiting on async data\n * (`block.dataLoading`), or when no handler is registered.\n * Errors are logged and the original data is preserved.\n */\nfunction runDataHandler(block) {\n if (block.dataLoading) return\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.data\n if (typeof handler !== 'function') return\n\n try {\n const result = handler(block.parsedContent.data, block)\n if (result != null && result !== block.parsedContent.data) {\n block.parsedContent.data = result\n }\n } catch (err) {\n console.error('Foundation data handler failed:', err)\n }\n}\n\n/**\n * Run the foundation-level content handler on a block, if one is\n * registered. Runs at prop-preparation time — after the data handler\n * has had a chance to filter/reshape the data — so the handler sees\n * the fully assembled (and possibly filtered) data. Replaces\n * `block.parsedContent` in place with the re-parsed, instantiated\n * form. The handler receives `(data, block)` and reads raw\n * ProseMirror from `block.rawContent`.\n *\n * Skipped when the block is still waiting on async data\n * (`block.dataLoading`), when no handler is registered, when the\n * block has no raw content, when the handler returns a no-change\n * signal (undefined, null, or the same reference as rawContent), when\n * it returns anything but a ProseMirror document, or when the handler\n * throws. Errors are logged via `console.error`.\n *\n * ⛔ ONLY A DOCUMENT IS RE-PARSED (2026-10-05). The handler is given the section's\n * DATA, so the natural no-op — `(x) => x` — returns the data map. That is not\n * `rawContent`, so it passed the no-change check; `parseContent` takes a plain\n * object as content already parsed; and the section's content became its data\n * map — title, paragraphs and items gone — with `data.data` pointing back at\n * itself, so a `JSON.stringify(content.data)` threw far from the handler (and\n * failed a static build's prerender of the page). Any result that is not a\n * document is now ignored, and said once per handler.\n */\nfunction runContentHandler(block) {\n if (block.dataLoading) return\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.content\n if (typeof handler !== 'function') return\n if (!block.rawContent || Object.keys(block.rawContent).length === 0) return\n\n try {\n const transformed = handler(block.parsedContent.data, block)\n if (!transformed || transformed === block.rawContent) return\n if (!isDocument(transformed)) {\n warnNotADocument(handler, transformed, block.parsedContent.data)\n return\n }\n const reparsed = block.parseContent(transformed)\n reparsed.data = block.parsedContent.data\n block.parsedContent = reparsed\n block.items = reparsed.items || []\n } catch (err) {\n console.error('Foundation content handler failed:', err)\n }\n}\n\n/** A ProseMirror document — bare, or wrapped as `{ doc }`, the two forms `rawContent` takes. */\nfunction isDocument(value) {\n return value?.type === 'doc' || value?.doc?.type === 'doc'\n}\n\n/**\n * Say, once per handler, that a content handler returned something that is not a\n * document — naming the likely mistake when it returned the very data it was given.\n */\nconst warnedContentHandlers = new WeakSet()\nfunction warnNotADocument(handler, value, data) {\n if (warnedContentHandlers.has(handler)) return\n warnedContentHandlers.add(handler)\n const what =\n value === data\n ? 'the data it was given'\n : Array.isArray(value)\n ? 'an array'\n : `${typeof value === 'object' ? 'an object' : typeof value}`\n console.warn(\n `[uniweb] handlers.content returned ${what}, not a ProseMirror document — ignored, and the ` +\n `section keeps its content. The handler is called (data, block) and returns the section's ` +\n `raw content transformed (block.rawContent, as Loom's does), or null for no change.`\n )\n}\n\n/**\n * Run the foundation-level props handler on the final { content, params }\n * before they reach the component. Runs after content parsing, param\n * defaults and content guarantees — the handler sees the exact shape the\n * component would receive and can modify it.\n *\n * The handler receives `(content, params, block)` and returns a new\n * `{ content, params }` object, or null/undefined for no change.\n *\n * Use cases: post-parse content reshaping, computed fields derived\n * from both content and params, param-driven content reorganization.\n * Errors are logged and the original props are preserved.\n */\nfunction runPropsHandler(content, params, block) {\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.props\n if (typeof handler !== 'function') return null\n\n try {\n const result = handler(content, params, block)\n if (result && typeof result === 'object') return result\n } catch (err) {\n console.error('Foundation props handler failed:', err)\n }\n return null\n}\n\n/**\n * Prepare props for a component with runtime guarantees.\n *\n * Does the full content-assembly pipeline in one place so both\n * renderers (`BlockRenderer.jsx` CSR and `ssr-renderer.js` SSG) share\n * the same code path:\n *\n * 1. Assemble `block.parsedContent.data` from the keys the component\n * declares: what the section holds, else what EntityStore delivered,\n * else `null` (`assembleData`) — one record for a key declared\n * `single: true` (`holdOneRecord`).\n * 2. Run the foundation data handler (if registered) to filter or\n * reshape the assembled data.\n * 3. Run the foundation content handler (if registered) on the\n * block. This may replace `block.parsedContent` with a re-parsed,\n * instantiated version.\n * 4. Apply param defaults from meta.\n * 5. Build the guaranteed content structure.\n * 6. Run the foundation props handler (if registered) for\n * post-processing of the final { content, params }.\n *\n * Steps 1–3 mutate the block (vanilla JS layer). Steps 4–6 are\n * pure derivations of the block's now-assembled state.\n *\n * ⛔ No field defaults for `content.data` — ruled 2026-10-05 [Diego]. A step between 5 and 6\n * filled each missing field from its schema's `default`, and replaced a value its `enum`\n * rejected, from the `schemas` a foundation built before then still carries in its meta —\n * read no more. A record reaches the component as it is: an absent field is its own fact,\n * and what it renders as is the component's choice.\n *\n * @param {Object} block - The block instance\n * @param {Object} meta - Runtime metadata for the component (from meta[componentName])\n * @param {Object|null} [entityData] - Entity data resolved by EntityStore (null if none)\n * @returns {Object} Prepared props: { content, params }\n */\nexport function prepareProps(block, meta, entityData = null) {\n const declared = assembleData(block, meta, entityData)\n // ⭐ A list the section held before the store answered — its own fetch, prerendered\n // into its content by a static build — gets its records' `$route` by the store's rule,\n // which the store did not deliver it by (2026-09-14).\n block.website?.entityStore?.linkOwnRecords?.(block)\n holdOneRecord(block, declared)\n runDataHandler(block)\n runContentHandler(block)\n\n // Apply param defaults\n const defaults = meta?.defaults || {}\n const params = applyDefaults(block.properties, defaults)\n\n // Guarantee content structure\n const content = guaranteeContentStructure(block.parsedContent)\n\n // Post-process hook\n const adjusted = runPropsHandler(content, params, block)\n if (adjusted) {\n return {\n content: adjusted.content || content,\n params: adjusted.params || params,\n }\n }\n\n return { content, params }\n}\n\n/**\n * Get runtime metadata for a component from the global uniweb instance\n *\n * @param {string} componentName\n * @returns {Object|null}\n */\nexport function getComponentMeta(componentName) {\n return globalThis.uniweb?.getComponentMeta?.(componentName) || null\n}\n\n/**\n * Get default param values for a component\n *\n * @param {string} componentName\n * @returns {Object}\n */\nexport function getComponentDefaults(componentName) {\n return globalThis.uniweb?.getComponentDefaults?.(componentName) || {}\n}\n","/**\n * The two helpers the background renderers share — one home, because they are\n * not rendering.\n *\n * ## Why this module exists\n *\n * Backgrounds were an L3 pair until 2026-10-25: `components/Background.jsx`\n * rendered for the browser and `ssr-renderer.js` with `React.createElement`\n * only. ⛔ **Only the RENDERING was ever meant to be twinned — \"pure data →\n * L1, one home\" — and two pure helpers had been dragged across the seam with\n * it**: a base-path joiner and a colour-opacity converter, byte-identical in\n * both files. The rendering is one copy now too (`background.js`: it needs no\n * hooks or DOM); these stay apart from it because they are not rendering.\n *\n * ⚠️ **And the copies had already drifted from the rule they were copying.**\n * The base-path joiner is `@uniweb/core`'s `applyBasePath`, whose own docblock\n * says it moved down to core *\"rather than grow a second copy (the failure\n * `@uniweb/core/route-match` was created to end, after one matcher was\n * implemented twice and the copies diverged)\"*. Two copies grew anyway, in the\n * runtime, and **both were missing its protocol-relative guard** — so an author\n * writing `//cdn.example.com/hero.jpg` on a site with `base: /docs/` got\n * `/docs//cdn.example.com/hero.jpg`. Measured 2026-09-06; the exact garbage\n * `applyBasePath` documents itself as existing to prevent.\n *\n * ⇒ The lesson is narrower than \"don't duplicate\": **when a twinned pair needs a\n * helper, the helper is the thing that must not be twinned.** A twin is a\n * commitment to keep two renderers in step; every pure function pulled inside it\n * silently joins that commitment.\n */\n\nimport { applyBasePath } from '@uniweb/core/base-path'\n\n/**\n * A site-root-relative URL with the deployment base applied.\n *\n * The base comes off the active website rather than being passed in, because\n * both callers render from the singleton and neither has it to hand. The join\n * itself is core's — protocol-relative and absolute URLs pass through, and an\n * already-based path is not based twice.\n *\n * @param {string} url\n * @returns {string}\n */\nexport function siteUrl(url) {\n return applyBasePath(url, globalThis.uniweb?.activeWebsite?.basePath || '')\n}\n\n/**\n * A colour with an alpha applied, for an overlay drawn over a background.\n *\n * Hex and `rgb()` / `rgba()` are converted; anything else — a named colour, a\n * `var(--token)`, `oklch(…)` — is returned unchanged, because a wrong guess\n * here paints the wrong colour rather than failing.\n *\n * @param {string} color\n * @param {number} opacity\n * @returns {string}\n */\nexport function withOpacity(color, opacity) {\n if (typeof color !== 'string' || !color) return color\n if (color.startsWith('#')) {\n // `#rgb` and `#rgba` are written out first; an alpha pair is replaced by `opacity`.\n // ⛔ Until 2026-10-25 every hex was read as six digits, so `#000` gave `rgba(0, 0, NaN, …)`,\n // and a gradient using it was invalid CSS that drew nothing.\n let hex = color.slice(1)\n if (hex.length === 3 || hex.length === 4) hex = [...hex].map((c) => c + c).join('')\n if (!/^[0-9a-f]{6}([0-9a-f]{2})?$/i.test(hex)) return color\n const r = parseInt(hex.slice(0, 2), 16)\n const g = parseInt(hex.slice(2, 4), 16)\n const b = parseInt(hex.slice(4, 6), 16)\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\n }\n if (color.startsWith('rgb')) {\n const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (match) return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n return color\n}\n","/**\n * A section's background — the layer the runtime draws behind a section's content: a color, a\n * gradient, an image or a video, under an optional overlay. It reads `block.background`, which core\n * normalizes once (`Block.normalizeBackground`).\n *\n * ⭐ ONE COPY, FOR BOTH RENDERERS: the browser's (`components/Background.jsx`) and the prerender's\n * (`ssr-renderer.js`, behind `uniweb build` and a host's prerender alike). It needs no hooks, no\n * Router and no DOM, so it is written with `createElement` and both draw it — the L3 twins exist for\n * code that needs those (`default-layout.js` is the same rule).\n * ⛔ Until 2026-10-25 each renderer wrote its own, and they had drifted: the prerender read an\n * overlay's `angle: 0` and `endPosition: 0` as unset (`||`), drew no video background at all, and\n * dropped the `className` kit's `SectionBackground` passes. Each showed as the page changing when\n * the browser rendered it in place of the prerendered one. Pinned by `tests/page-parity.test.js`.\n *\n * ⭐ A VIDEO IS ITS POSTER UNTIL THE BROWSER HAS MOUNTED IT. Both renderers draw the poster\n * (`VideoPoster`); the browser then swaps in the playing `<video>` (`components/Background.jsx`),\n * unless the visitor prefers reduced motion and there is a poster to keep (`videoMoves`). That\n * preference is the one thing here that needs the DOM, so it is read in an effect, never in the first\n * render: read there, it made the browser's first render differ from the prerendered page for exactly\n * those visitors.\n *\n * ⭐ An unset value is `undefined` or `null`, never a falsy one: `0` is an angle, a position and an\n * opacity like any other (`??`, not `||`).\n */\n\nimport React from 'react'\nimport { siteUrl, withOpacity } from './background-shared.js'\n\nconst h = React.createElement\n\n/** The box every layer fills: the section's, behind its content (`BlockRenderer.jsx`). */\nconst FILL = { position: 'absolute', inset: 0 }\n\nfunction ColorLayer({ color }) {\n if (!color) return null\n return h('div', { className: 'background-color', style: { ...FILL, backgroundColor: color }, 'aria-hidden': 'true' })\n}\n\nfunction GradientLayer({ gradient }) {\n if (!gradient) return null\n\n // A CSS gradient as written (\"linear-gradient(to bottom, #000, #333)\")\n if (typeof gradient === 'string') {\n return h('div', { className: 'background-gradient', style: { ...FILL, background: gradient }, 'aria-hidden': 'true' })\n }\n\n const start = gradient.start ?? 'transparent'\n const end = gradient.end ?? 'transparent'\n const startOpacity = gradient.startOpacity ?? 1\n const endOpacity = gradient.endOpacity ?? 1\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n const background =\n `linear-gradient(${gradient.angle ?? 0}deg, ` +\n `${startColor} ${gradient.startPosition ?? 0}%, ${endColor} ${gradient.endPosition ?? 100}%)`\n\n return h('div', { className: 'background-gradient', style: { ...FILL, background }, 'aria-hidden': 'true' })\n}\n\nfunction ImageLayer({ image }) {\n if (!image?.src) return null\n const style = {\n ...FILL,\n backgroundImage: `url(${siteUrl(image.src)})`,\n backgroundPosition: image.position ?? 'center',\n backgroundSize: image.size ?? 'cover',\n backgroundRepeat: 'no-repeat',\n }\n return h('div', { className: 'background-image', style, 'aria-hidden': 'true' })\n}\n\n/**\n * A video background as both renderers first draw it: its poster, as an image layer — or nothing,\n * when it has none. The browser replaces it with `VideoElement` once mounted.\n */\nexport function VideoPoster({ video }) {\n if (!video?.src || !video.poster) return null\n return h(ImageLayer, { image: { src: video.poster, size: 'cover', position: 'center' } })\n}\n\n/**\n * Whether a mounted video background plays: always, unless the visitor prefers reduced motion and\n * there is a poster to show instead.\n *\n * @param {Object} video - `background.video`\n * @param {boolean} reducedMotion - the visitor's `prefers-reduced-motion`\n * @returns {boolean}\n */\nexport function videoMoves(video, reducedMotion) {\n if (!video?.src) return false\n return !(reducedMotion && video.poster)\n}\n\n/** The playing video, which only the browser draws, once mounted (`components/Background.jsx`). */\nexport function VideoElement({ video }) {\n if (!video?.src) return null\n const sources = (video.sources || inferSources(video.src)).map((s) => ({ ...s, src: siteUrl(s.src) }))\n return h(\n 'video',\n {\n className: 'background-video',\n style: { ...FILL, width: '100%', height: '100%', objectFit: 'cover' },\n autoPlay: true,\n loop: video.loop ?? true,\n muted: video.muted ?? true,\n playsInline: true,\n poster: siteUrl(video.poster),\n 'aria-hidden': 'true',\n },\n sources.map(({ src, type }, index) => h('source', { key: index, src, type })),\n )\n}\n\n/**\n * The formats to offer for a single `src`: an `.mp4` is offered as `.webm` first (better\n * compression), and the reverse; the browser plays the first it supports.\n */\nfunction inferSources(src) {\n const ext = src.split('.').pop()?.toLowerCase()\n const base = src.slice(0, src.lastIndexOf('.'))\n if (ext === 'mp4') return [{ src: `${base}.webm`, type: 'video/webm' }, { src, type: 'video/mp4' }]\n if (ext === 'webm') return [{ src, type: 'video/webm' }, { src: `${base}.mp4`, type: 'video/mp4' }]\n const type = src.endsWith('.ogg') || src.endsWith('.ogv') ? 'video/ogg' : 'video/mp4'\n return [{ src, type }]\n}\n\nfunction Overlay({ overlay }) {\n if (!overlay?.enabled) return null\n const opacity = overlay.opacity ?? 0.5\n\n if (overlay.gradient) {\n const g = overlay.gradient\n const background =\n `linear-gradient(${g.angle ?? 180}deg, ${g.start ?? 'rgba(0,0,0,0.7)'} ${g.startPosition ?? 0}%, ` +\n `${g.end ?? 'rgba(0,0,0,0)'} ${g.endPosition ?? 100}%)`\n return h('div', {\n className: 'background-overlay background-overlay--gradient',\n style: { ...FILL, background, opacity, pointerEvents: 'none' },\n 'aria-hidden': 'true',\n })\n }\n\n const base = overlay.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n return h('div', {\n className: 'background-overlay background-overlay--solid',\n style: { ...FILL, backgroundColor: `rgba(${base}, ${opacity})`, pointerEvents: 'none' },\n 'aria-hidden': 'true',\n })\n}\n\n/**\n * The background layer: `block.background`'s fields, and a `className` for the layer (kit's\n * `SectionBackground` passes one).\n *\n * @param {Object} props\n * @param {string} props.mode - 'color' | 'gradient' | 'image' | 'video'; nothing is drawn without one\n * @param {string} [props.color]\n * @param {string|Object} [props.gradient]\n * @param {Object} [props.image]\n * @param {Object} [props.video]\n * @param {Object} [props.overlay]\n * @param {string} [props.className]\n * @param {Function} [props.Video] - how a video background is drawn: its poster (the default, and the\n * prerender's), or the browser's, which plays it once mounted\n * @returns {React.ReactElement|null}\n */\nexport function Background({ mode, color, gradient, image, video, overlay, className = '', Video = VideoPoster }) {\n if (!mode) return null\n return h(\n 'div',\n {\n className: `background background--${mode} ${className}`.trim(),\n style: { ...FILL, overflow: 'hidden', zIndex: 0 },\n 'aria-hidden': 'true',\n },\n mode === 'color' ? h(ColorLayer, { color }) : null,\n mode === 'gradient' ? h(GradientLayer, { gradient }) : null,\n mode === 'image' ? h(ImageLayer, { image }) : null,\n mode === 'video' ? h(Video, { video }) : null,\n h(Overlay, { overlay }),\n )\n}\n","/**\n * Default 404 Page Content\n *\n * Single source of truth for the fallback 404 page shown when a site\n * has no custom 404 page defined. Used by:\n * - PageRenderer.jsx (client-side, as React elements)\n * - ssr-renderer.js generate404Html (build-time, as HTML string)\n *\n * The wrapper uses min-height + flex centering so the 404 content\n * renders at the same position regardless of parent layout context.\n * This prevents a visible flash when React hydrates over the SSR content.\n */\n\nimport React from 'react'\n\nconst styles = {\n wrapper: {\n minHeight: '80vh',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '2rem',\n textAlign: 'center',\n },\n heading: { fontSize: '3rem', fontWeight: 'bold', color: '#1f2937', marginBottom: '1rem' },\n message: { color: '#64748b', marginBottom: '2rem' },\n link: { color: '#3b82f6', textDecoration: 'underline' },\n}\n\n/**\n * React element for client-side rendering (PageRenderer).\n * Reads basePath from the runtime so the homepage link works\n * in subdirectory deployments (e.g., /sites/testproject).\n */\nexport function Default404() {\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n const homeHref = basePath ? `${basePath}/` : '/'\n return React.createElement('div', { className: 'page-not-found', style: styles.wrapper },\n React.createElement('h1', { style: styles.heading }, '404'),\n React.createElement('p', { style: styles.message }, 'Page not found'),\n React.createElement('a', { href: homeHref, style: styles.link }, 'Go to homepage')\n )\n}\n\n/**\n * Static HTML string for SSR injection (generate404Html).\n *\n * @param {string} [basePath] - Base path prefix for the homepage link (e.g., '/sites/testproject')\n */\nexport function default404Html(basePath = '') {\n const homeHref = basePath ? `${basePath}/` : '/'\n return (\n `<div class=\"page-not-found\" style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center\">` +\n `<h1 style=\"font-size:3rem;font-weight:bold;color:#1f2937;margin-bottom:1rem\">404</h1>` +\n `<p style=\"color:#64748b;margin-bottom:2rem\">Page not found</p>` +\n `<a href=\"${homeHref}\" style=\"color:#3b82f6;text-decoration:underline\">Go to homepage</a>` +\n `</div>`\n )\n}\n","/**\n * Layer-2 wiring helpers: runtime ↔ Uniweb singleton.\n *\n * After `createUniweb()` constructs the singleton, the runtime fills a\n * few declared slots on it before the first render — foundation\n * capabilities (`defaultInsets`, `xref.build()`), per-request data\n * hydration into `website.dataStore`, locale-scoped content slicing.\n * This step is identical in every environment (browser SPA, SSG\n * prerender, cloud SSR) because it's plain data manipulation on a JS\n * object: no React rendering happens here, no hooks are called, no DOM\n * is touched, no `react-dom/server` is needed.\n *\n * That's why these helpers live in one file imported by both\n * `setup.js` (browser boot) and `ssr-renderer.js` (SSG/cloud-SSR boot),\n * instead of being duplicated into each. Things that genuinely differ\n * between environments — routing components, icon-cache hydration from\n * the DOM, the per-page render loop — stay in the per-environment\n * entries; these helpers cover only the environment-agnostic L2 work.\n *\n * What this file keeps out of the SPA bundle is `react-dom/server`, not\n * React: `FallbackRef` below is `React.createElement` alone. `setup.js`\n * pulls `wire-foundation.js` directly, so this file must NOT transitively\n * pull `ssr-renderer.js` (which imports `react-dom/server`). The L2\n * helpers therefore live here, while the L3-composing\n * `initPrerenderForLocale` lives in `ssr-renderer.js`.\n *\n * Adding a new framework-level capability:\n * 1. Name it in `extractFoundationRuntime`'s lists (`@uniweb/build`'s\n * `src/runtime-schema.js`): the built entry carries only what they\n * name, so anything else never reaches this file.\n * 2. Read the foundation declaration via `foundation.default.capabilities.<name>`.\n * 3. Apply it to the uniweb singleton (set a slot, call a build hook,\n * register something on `activeWebsite`).\n * 4. Provide a runtime fallback if the capability is one foundations\n * may legitimately not declare (see `FallbackRef`).\n *\n * Foundation export shape contract: the runtime always loads the\n * **built** foundation artifact (`dist/entry.js`) via\n * `loadFoundation()` in `foundation-loader.js`, which does `import(url)`\n * and returns a module namespace. The build pipeline\n * (`@uniweb/build`'s `src/generate-entry.js`) writes what the runtime\n * reads of the foundation's source default export into\n * `default.capabilities.*`, so the runtime sees a single canonical shape\n * with no need for fallback chains. This\n * differs from `@uniweb/press` / `@uniweb/unipress`,\n * which DO need to handle a second shape because they're sometimes\n * called from inside a foundation bundle (where the foundation imports\n * its own source as a bare default object).\n */\n\nimport React from 'react'\nimport { deriveCacheKey, resolveDefaultLocale } from '@uniweb/core'\n// Leaf subpaths, not the package root: this file is pulled into the SSR/Worker\n// bundle, and `@uniweb/core` proper drags semantic-parser and theming with it.\nimport { resolveService, readServiceOptions } from '@uniweb/core/services'\nimport Tracker from '@uniweb/core/tracker'\nimport { buildTheme } from '@uniweb/theming'\n\n/**\n * Renders unhandled `[#id]` cross-reference markers as plain text. Used\n * when the active foundation didn't declare its own `<Ref>` via\n * `defaultInsets`. Pure `React.createElement` — safe in every\n * environment, including the hook-free SSR pipeline.\n *\n * Foundations that support cross-references override this by exporting\n * `defaultInsets: { Ref }` (with kit's xref-aware Ref) from their\n * source — the build pipeline carries it through into\n * `default.capabilities.defaultInsets`.\n */\nexport function FallbackRef({ params }) {\n return React.createElement(\n 'span',\n { className: 'xref xref--unhandled' },\n `[${params?.key || '?'}]`,\n )\n}\n\n/**\n * Wire foundation-declared capabilities onto a freshly constructed\n * Uniweb singleton. Called once, after `createUniweb()`, before any\n * rendering. Identical for SPA, SSG, and cloud SSR.\n *\n * @param {import('@uniweb/core').default} uniweb - From createUniweb(...).\n * @param {object} foundation - Loaded foundation module (built shape).\n */\nexport function wireFoundationCapabilities(uniweb, foundation) {\n const caps = foundation?.default?.capabilities || {}\n\n // defaultInsets: framework provides FallbackRef as the floor;\n // foundation overrides win. `getComponent()` on the Uniweb singleton\n // (core/uniweb.js) falls back to defaultInsets[name] when no\n // foundation/extension component matches — that's how `<Ref>` becomes\n // available to every foundation without each one having to register\n // it explicitly.\n uniweb.defaultInsets = { Ref: FallbackRef, ...(caps.defaultInsets || {}) }\n\n // xref: foundations supporting cross-references export\n // `xref.build(website, { foundationKinds })`. The runtime can't\n // import kit directly (kit is bundled into each foundation, not into\n // runtime, so only the foundations that use it pay for it), so it\n // dispatches through the foundation's reference. Foundations without\n // xref skip this entirely; kit's xref module never enters their\n // bundle thanks to tree-shaking at foundation-build time.\n if (caps.xref?.build && uniweb.activeWebsite) {\n caps.xref.build(uniweb.activeWebsite, {\n foundationKinds: caps.xref.kinds || {},\n })\n }\n}\n\n/**\n * Slice a multi-locale site-content payload to one locale.\n *\n * Sites published through the editor ship a single payload that carries\n * all locales nested under `content.locales[locale]` — `pages`, optional\n * `layouts`, and a `config` overlay. The default locale lives at the\n * top level (no nesting). This helper extracts the requested locale's\n * view as a fresh content object the rest of the runtime can consume\n * unchanged.\n *\n * Returns `content` as-is when `locale` is the default, missing, or not\n * present in `content.locales` — callers that already hand us locale-\n * scoped content (e.g., the framework's per-locale SSG path that loads\n * each `dist/{locale}/site-content.json` separately) get pass-through\n * behavior.\n *\n * The shape comes from the editor's publish payload, which is the canonical\n * form for multi-locale content — a server-side renderer consumes it\n * directly. Build-time SSG pre-flattens to one file per locale and so falls\n * into the pass-through case.\n *\n * @param {Object} content - Site content payload, possibly multi-locale.\n * @param {string} locale - Requested locale code.\n * @returns {Object} Content scoped to the requested locale.\n */\nexport function sliceContentForLocale(content, locale) {\n const defaultLang = resolveDefaultLocale(content?.config)\n const locData = content?.locales?.[locale]\n if (!locale || locale === defaultLang || !locData) return content\n return {\n pages: locData.pages,\n layouts: locData.layouts || content.layouts,\n config: {\n ...locData.config,\n i18n: content.config?.i18n,\n activeLocale: locale,\n },\n }\n}\n\n/**\n * Pre-populate a Website's DataStore from build-time / publish-time\n * fetched data so the dispatcher's first probe hits the cache instead\n * of refetching.\n *\n * The value MUST be wrapped as `{ data }`, or `cached.data` reads `undefined`. Three call sites\n * used to inline this loop independently (browser SPA, Node SSG, and a server-side renderer); one\n * of the three used the wrong shape, silently killing prefetched-data reuse wherever it ran. This\n * helper is the one canonical implementation.\n *\n * ⭐ **The KEY is the dispatcher's, not ours** (`fetcher.hydrate`). ⛔ This filed every entry under\n * `deriveCacheKey(entry.config)` until 2026-09-20, which is right for every request the framework's\n * own fetcher answers and wrong for one the site routes to a transport that declares its own\n * `cacheKey` — the documented case being a request whose identity depends on state its address does\n * not carry. Measured: such an entry landed where no `peek` looks, so every prerendered page of that\n * site rendered empty and the browser refetched it, with no error anywhere. One derivation, one\n * home — the dispatcher's, which is where selection happens.\n *\n * @param {import('@uniweb/core').Website} website\n * @param {Array<{config: Object, data: any}>} fetchedData\n */\nexport function hydrateDataStore(website, fetchedData) {\n if (!website?.dataStore || !fetchedData?.length) return\n for (const entry of fetchedData) {\n // A data step's list carries every declared config with an `outcome`; only what was actually\n // fetched enters the store. A list without outcomes (the SSG lane's) is all fetched.\n if (entry.outcome && entry.outcome !== 'fetched') continue\n // `meta` (the depth the records were fetched at) rides along, so the store\n // files them in its record index exactly as a runtime fetch would.\n const value = entry.meta ? { data: entry.data, meta: entry.meta } : { data: entry.data }\n if (typeof website.fetcher?.hydrate === 'function') {\n website.fetcher.hydrate(entry.config, value, { website })\n } else {\n // A graph with no dispatcher — a stub in a test, or a caller assembling one by hand.\n website.dataStore.set(deriveCacheKey(entry.config), value)\n }\n }\n}\n\n/**\n * Make sure the site's theme CSS exists on the graph, generating it from\n * the authored config when nothing upstream did.\n *\n * **The authored theme config is the source of truth in every lane;\n * generated CSS is a cache of it.** `uniweb build` fills that cache and\n * bakes the result into `<head>`, so this is a no-op on the static lane.\n * A lane that serves a site WITHOUT running the framework's build — a\n * backend-hosted SPA, a cloud shell-mode fallback — carries only the\n * authored `theme.yml` (that is the correct thing for a sync wire to\n * carry: `theme.css` is a build artifact, and with two publishers only\n * one of which computes it, shipping it would make a site's styling\n * depend on who published last). Without this helper those lanes render\n * with every semantic token unset — no colours, no backgrounds.\n *\n * Generating here rather than in a publish step is what keeps the\n * three-ingredient contract true: site + foundation + runtime converge\n * to a *styled* page with no fourth actor. It also stays one\n * implementation — the alternative was re-deriving the OKLCH shade math\n * in another language and keeping the two bit-compatible.\n *\n * L2, not L3: this reads and writes graph state and renders nothing, so\n * it has a single home here and both boot paths call it. **The\n * `@uniweb/theming` import is deliberately static.** An SSR isolate\n * loads a fixed modules map and cannot resolve a chunk graph, so the SSR\n * entry must include the generator statically; a lazy `import()` in the\n * browser entry only would mean two mechanisms for one behaviour,\n * drifting independently. Measured cost of the generator: ~4.9 KB gzip.\n *\n * Foundation-declared vars reach us through\n * `capabilities.vars` — emitted into `dist/entry.js` by\n * `@uniweb/build`'s `generate-entry.js`. Before that existed they lived\n * only in `dist/meta/schema.json` and a theme generated outside the\n * build silently lost every one of them.\n *\n * Callers own the \"should I?\" question, because it is environment-\n * specific: the browser entry skips this when the document already\n * carries a prerendered `<style id=\"uniweb-theme\">` (regenerating from\n * an already-processed config is wasted work at best), while the SSR\n * entry always runs it and lets `injectPageContent()` emit the result\n * idempotently.\n *\n * @param {import('@uniweb/core').default} uniweb - From createUniweb(...).\n * @param {object} foundation - Loaded foundation module (built shape).\n */\nexport function ensureThemeCss(uniweb, foundation) {\n const website = uniweb?.activeWebsite\n const themeData = website?.themeData\n if (!themeData || themeData.css) return\n\n const caps = foundation?.default?.capabilities || {}\n try {\n const { config, css, links } = buildTheme(themeData, {\n foundationVars: caps.vars || {},\n base: website.basePath || '/',\n })\n // Merge rather than replace: `config` is the processed superset (it\n // adds `palettes`, normalized `contexts`, resolved `fonts`), so this\n // also gives a build-less lane the same themeData shape the static\n // lane has — Theme.getPalette() and friends start working too.\n Object.assign(themeData, config, { css, links })\n } catch (err) {\n // This runs on the path taken when something upstream has already\n // gone wrong. A degraded render that is still legibly the site beats\n // one that looks broken, but neither is worth a boot crash.\n console.warn('[uniweb] theme CSS generation failed:', err?.message || err)\n }\n}\n\n/**\n * L2: give the site's tracker its destination.\n *\n * Replaces the disabled `Tracker` that `createUniweb` declares (see\n * `core/src/uniweb.js`) with a configured one, when — and only when — a\n * destination resolves. With none, the disabled default stays and every\n * `track()` call in the site remains a silent no-op, which is the default\n * state for the large majority of sites.\n *\n * ⛔ **WHY THE BASE PATH IS PASSED IN RATHER THAN READ OFF THE WEBSITE.**\n * `resolveService` joins a root-relative endpoint to `website.basePath`, and\n * that field is still `''` until `setBasePath()` runs — which happens later,\n * from `RuntimeProvider`. Resolving against the website as-is would silently\n * drop the prefix on every subdirectory deployment, and the symptom would be a\n * collector quietly receiving nothing. So the caller supplies the basename it\n * has already derived, and the lookup is done against that. `resolveService`\n * reads only `.config` and `.basePath`, so a plain object is a complete input.\n *\n * ⚖️ **Not called from the SSR path, deliberately.** The tracker is\n * browser-guarded, so wiring it there would produce a configured object that\n * can never emit — a slot that looks live and is not. The SSR twin has no\n * page-view effect either; suppression is structural rather than a flag.\n *\n * ## `scripts` — a vendor's own script, when the site declares one\n *\n * A second, independent path — vendor tags:\n * nothing is translated between our stream and theirs, and the framework never\n * learns which vendor it is. ⛔ **The loader is INJECTED rather than imported**,\n * because this file is pulled into the SSR/Worker bundle and a script loader is\n * DOM code. The browser entry passes one; the SSR path passes none, so there is\n * no branch to remember.\n *\n * @param {object} uniweb - the singleton\n * @param {object} [options]\n * @param {string} [options.basePath] - the deployment base (router basename)\n * @param {(urls: string[], opts: object) => void} [options.loadScripts] - DOM\n * loader for declared vendor scripts; omitted outside a browser entry\n */\n/**\n * What `tracking.emit` names, when a site names a preset rather than a list.\n *\n * ⭐ **`all` is deliberately ABSENT from this table.** It resolves to `null` —\n * *no narrowing* — so an event added in a later release is included without the\n * site republishing. A literal list would freeze `all` at the moment the site\n * was built and quietly stop meaning \"all\".\n *\n * ⚖️ **`standard` and `all` select the same events today, and that is not a\n * reason to drop one.** They diverge the moment a new automatic event ships:\n * `standard` is a curated set that a release cannot grow behind an operator's\n * back, `all` is the standing yes. The volume surprise is the thing being\n * avoided — a site that never changed should not start sending more.\n *\n * ⛔ **The curated set is the answer for a site that CONFIGURED ITS OWN\n * DESTINATION. It is NOT the answer for a site whose host supplies one** — see\n * `resolveEmit`, which is where absence stopped meaning one thing.\n */\nconst EMIT_PRESETS = {\n minimal: ['page_view'],\n standard: ['page_view', 'outbound_click', 'section_view']\n}\n\n/** The preset a site gets by declaring a destination and nothing else. */\nconst DEFAULT_EMIT = 'standard'\n\n/**\n * The site's own selection, as a list of event names or `null` for no narrowing.\n *\n * ⛔ **An unknown preset name resolves to the DEFAULT, not to nothing.** A typo\n * (`emit: sandard`) must not silently take a site dark: the failure mode of a\n * misread selection has to be \"you got the usual set\", never \"you got none and\n * nothing said so\".\n *\n * ## ⭐ ABSENCE MEANS TWO DIFFERENT THINGS, and this is where they part\n *\n * **A site that configured its own `endpoint` chose it.** Writing no `emit`\n * there means *\"the curated default\"*, and `standard` is exactly right — a\n * later framework release must not grow it behind that operator's back.\n *\n * **A site whose HOST supplies the collector has no endpoint of its own.** The\n * operator's whole relationship is *\"my host does analytics for me\"*, so\n * writing no `emit` there means **\"whatever my host offers\"** — not a list\n * frozen at the framework version the site was built against.\n *\n * ⇒ **Absent `emit` defers to the host's declared list when there is one, and\n * falls back to `standard` when there is not.** Returning `null` is how the\n * deferral is expressed: it is *no site-tier narrowing*, so `Tracker.arms()` is\n * left with the host's list as the only gate.\n *\n * ⭐ **Why this is a fix and not a relaxation.** §4 of the tracking design says\n * *\"the runtime emits what the SITE OWNER buys\"* — and before this, an owner\n * paying a host for analytics received a **framework-frozen subset** of what\n * that host stores and bills them for. The only way to close the gap was to\n * hand-edit YAML and republish, **a dependency with no symptom when forgotten**,\n * which is the precise failure that rule was written to reject.\n *\n * ⛔ **The fallback is NOT decoration — it is the standalone-first guarantee.**\n * A static host, a foreign backend, and any Uniweb backend predating the\n * `events` key all declare no list. Deferring unconditionally would arm *every*\n * event, forever, on exactly the sites the framework exists to serve without a\n * backend.\n *\n * ⚠️ **A host that declares an EMPTY list still means it** — `[]` is a\n * statement, not an absence, and it arms nothing. That is unchanged: `arms()`\n * has always read an empty host list that way. Only `undefined` means \"nothing\n * declared\".\n *\n * @param {string|string[]|undefined} emit - the site's own `tracking.emit`\n * @param {string[]|null} [hostEvents] - the host's declared list, or `null`\n * when the host declared none. **Only consulted when `emit` is absent**;\n * an author who names anything still wins.\n * @returns {string[]|null}\n */\nfunction resolveEmit(emit, hostEvents = null) {\n // ⛔ Absent is the ONLY branch that consults the host — this is a default,\n // never an override. `emit: minimal` on a host offering everything still\n // sends one event.\n if (emit == null) return hostEvents ? null : EMIT_PRESETS[DEFAULT_EMIT]\n if (Array.isArray(emit)) return emit\n if (emit === 'all') return null\n return EMIT_PRESETS[emit] || EMIT_PRESETS[DEFAULT_EMIT]\n}\n\nexport function wireTracker(uniweb, { basePath = '', loadScripts = null } = {}) {\n const website = uniweb?.activeWebsite\n if (!website) return\n\n // A plain lookup target: `resolveService` reads `.config` and `.basePath`\n // only, so this is the whole of what it needs and carries the *correct* base.\n const target = { config: website.config, basePath }\n\n const { url } = resolveService(target, 'tracking')\n const options = readServiceOptions(target, 'tracking')\n\n // Only whether any were declared — normalizing them is the loader's job, and\n // lives behind the loader's dynamic boundary so a site with none never\n // downloads that code either.\n const declaredScripts = options.scripts\n const hasScripts = Array.isArray(declaredScripts) ? declaredScripts.length > 0 : !!declaredScripts\n\n // Nothing declared on either count — keep the disabled default, nothing\n // armed, nothing queued. This is the state of the large majority of sites.\n if (!url && !hasScripts) return\n\n // The two narrowings, resolved here rather than in core: this is per-request\n // config reshaping, which is L2's job (see this file's header).\n //\n // ⛔ **`hostEvents` is read from the HOST tier only** — `config.services\n // .tracking.events`, never the merged view. A site cannot widen what a host\n // declined to store, and reading the merge would let it, silently, by writing\n // its own `events:` key.\n //\n // ⛔ **Absent stays absent.** No `events` from the host means NO NARROWING,\n // never an empty set: a host that sends no list is an older or simpler one,\n // and the other reading takes every site on it dark with every gate saying\n // yes. `?? null` rather than `?? []` is the whole of that guard.\n // ⛔ Each tier is read from ITS OWN key, not from the merged `options`. The\n // merge exists so a site's own option can override a host's — `consent`, say;\n // the address is not an option, and comes from `resolveService` above. These\n // two are not overrides of each other but answers to different questions, and\n // reading either off the merge would let one tier answer the other's — a site\n // writing `events:` would widen past what the host stores, silently.\n const hostTracking = website.config?.services?.tracking\n const siteTracking = website.config?.tracking\n const hostEvents =\n hostTracking && Array.isArray(hostTracking.events) ? hostTracking.events : null\n\n const tracker = new Tracker({\n endpoint: url,\n hostEvents,\n siteEmit: resolveEmit(siteTracking && siteTracking.emit, hostEvents),\n // ⭐ Read off the MERGED view, unlike the two above — and the difference is\n // the point. `events`/`emit` answer different questions per tier, so each is\n // read from its own key; this is one question with two possible answerers,\n // so the ordinary precedence applies: the host declares a batch window that\n // suits its collector, and a site's own `tracking:` overrides it. Absent on\n // both, `Tracker` keeps its default.\n //\n // ⛔ **A field being READABLE is not the same as it being AVAILABLE**, and\n // that is what made this line worth a test rather than a shrug.\n // `readServiceOptions` has always returned this key, so the plan read as\n // finished while nothing wrote the object being read — it would have shipped\n // as *\"we set the interval and it did nothing\"*, with all three lanes' suites\n // green. The value now has to reach `setInterval`, and a test asserts the\n // delay rather than the field.\n flushIntervalMs: options.flushIntervalMs,\n // Opt-in, not the default. Declaring a destination is itself the operator's\n // decision to track; requiring a second affirmative step would be the\n // framework presuming a jurisdiction on their behalf, which is exactly what\n // it must not do. A site that needs the gate asks for it.\n consentRequired: options.consent === 'required',\n debug: !!options.debug\n })\n uniweb.tracking = tracker\n\n if (!loadScripts || !hasScripts) return\n\n // The same suppression the tracker applies to its own events: a server render\n // or a framed authoring preview is not a visit, and a vendor's script must not\n // fire there either. One predicate in core, so the two cannot drift.\n if (!tracker.isLiveDocument()) return\n\n const load = () => loadScripts(declaredScripts, { basePath, debug: !!options.debug })\n if (tracker.consentStatus() === 'granted') load()\n else tracker.onGranted = load\n}\n","/**\n * Where a section's `sticky` setting lands — pure, so the browser's renderer\n * (`components/Layout.jsx`, `BlockRenderer.jsx`) and the prerender's (`ssr-renderer.js`) make the\n * same boxes sticky, and a prerendered page does not change when the browser renders over it.\n *\n * ⭐ A COMPONENT'S OUTERMOST ELEMENT CANNOT STICK. `position: sticky` moves an element only within\n * its parent's box, and a component sits in boxes as tall as itself: the section's wrapper, the\n * layout area's wrapper (`area-wrappers.js`), the built-in layout's zone (`default-layout.js`). (An\n * element inside a component can stick within a taller box the component draws — a side panel in\n * the row beside a list — which is the component's own layout and asks nothing of this module.)\n * So a section asks — `sticky:`, one of a section's own settings (`@uniweb/schemas/section`),\n * defaulted by its type's `meta.js` — and the runtime makes sticky the box that can:\n *\n * - in the body, the section's own wrapper, whose parent, the body, spans every section;\n * - a child section rendered as a section (`<ChildBlocks wrapAs>`, kit's `ChildGrid`), its own\n * wrapper too, which then stays in view within the box its parent places it in — in\n * `ChildGrid`, its cell, as tall as the row — and no further. Top-aligned, so a flex or grid\n * item has room. At one column (a phone) a child is its own row and does not stick, which is\n * what a side panel stacked under the content should do;\n * - in any other area, the area's box when any of its sections is sticky: the zone, in the\n * built-in layout; the area wrapper, in a foundation's layout. That box takes the area's\n * layer, and a sticky area's layer is above every area that scrolls under it\n * (`resolveLayoutLayers`).\n *\n * ⭐ A section stays in view BELOW what is stuck at the top. A sticky area sticks at `0px`; a\n * sticky section with no `top` of its own sticks at `--uw-sticky-top`, how far down the page's\n * stuck chrome reaches, which the browser measures (`sticky-top.js`) — so a side panel or a\n * sub-navigation clears a stuck header whatever its height. Unmeasured — a prerendered page\n * before the browser runs, a page with nothing stuck — it is `0px`.\n *\n * Every box this module makes sticky says so, `data-uw-sticky` = `area`, `section` or `child`,\n * for what reads them in the browser: the measurement, and `uniweb dev`'s check\n * (`sticky-check.js`).\n *\n * ⛔ A layout that wraps an area in an element of its own confines the area wrapper, as the zone\n * does in the built-in layout. That element is the layout's, so the layout makes it sticky, when\n * `page.isSticky(area)`.\n *\n * ⛔ Until 2026-10-19 there was no setting, and a header's own `sticky top-0` — the starter\n * templates' — scrolled away with the page. Until 2026-10-20 a child section's setting did\n * nothing, and a sticky section stuck at `0px`, under a stuck header.\n */\n\n/** The very top of the viewport — a sticky area with no `top` of its own. */\nexport const STICKY_TOP = '0px'\n\n/**\n * Where a sticky section with no `top` of its own stays: below what is stuck at the top\n * (`sticky-top.js` measures it), else the very top.\n */\nexport const STICKY_SECTION_TOP = 'var(--uw-sticky-top, 0px)'\n\n/**\n * The style that keeps a box in view.\n *\n * @param {string|null} [top] - how far below the viewport's top, a CSS length\n * @param {number|null} [zIndex] - its layer, when it has one\n * @returns {{ position: 'sticky', top: string, zIndex?: number }}\n */\nexport function stickyStyle(top, zIndex) {\n const style = { position: 'sticky', top: top || STICKY_TOP }\n if (zIndex != null) style.zIndex = zIndex\n return style\n}\n\n/**\n * Whether an area stays in view — when any of its sections is sticky — and how far below the\n * top: the first sticky section's.\n *\n * @param {Array<Object>|null|undefined} blocks - the area's blocks, their components initialized\n * @returns {{ top: string }|null}\n */\nexport function areaStickiness(blocks) {\n const first = (blocks || []).find((block) => block?.sticky)\n return first ? { top: first.stickyTop || STICKY_TOP } : null\n}\n\n/**\n * Each layout area's stickiness, read from its sections — what both renderers compute before the\n * areas' layers, since a sticky area is lifted above the rest (`resolveLayoutLayers`).\n *\n * @param {Object<string, Array<Object>>} areas - area name → its blocks, their components initialized\n * @returns {Object<string, { top: string }|null>}\n */\nexport function layoutStickiness(areas) {\n const sticky = {}\n for (const [name, blocks] of Object.entries(areas || {})) sticky[name] = areaStickiness(blocks)\n return sticky\n}\n\n/**\n * The props a built-in layout zone takes when its area sticks — or null.\n *\n * @param {{ top: string }|null|undefined} stickiness - the area's (`areaStickiness`)\n * @param {number|null} [layer] - the area's layer (`resolveLayoutLayers`)\n * @returns {{ style: Object, 'data-uw-sticky': 'area' }|null}\n */\nexport function zoneProps(stickiness, layer) {\n return stickiness ? { style: stickyStyle(stickiness.top, layer), 'data-uw-sticky': 'area' } : null\n}\n\n/**\n * The style a sticky section's own wrapper takes — a body section's, or a child section's — or\n * null. Below what is stuck at the top unless its type says how far down; layer 1, so it paints\n * over the sections after it, which are positioned only when they have a background; and\n * top-aligned, so that as a flex or grid item it is not stretched to the row, which would leave\n * it no room to move (in normal flow `align-self` does nothing).\n *\n * @param {Object} block - its component initialized\n * @returns {Object|null}\n */\nexport function sectionStickyStyle(block) {\n return block?.sticky ? { ...stickyStyle(block.stickyTop || STICKY_SECTION_TOP, 1), alignSelf: 'start' } : null\n}\n\n/**\n * Whether any area of a page's layout sticks — its sections' components initialized, as both\n * renderers do before they render.\n *\n * @param {Object} page\n * @returns {boolean}\n */\nexport function hasStickyArea(page) {\n const areas = page?.getLayoutAreas?.() || {}\n return Object.values(areas).some((blocks) => areaStickiness(blocks) !== null)\n}\n\n/**\n * Where a link to a section lands — below what is stuck at the top, `--uw-sticky-top` — given to a\n * body section's box, and a child section's, on a page whose layout has a sticky area. A link to\n * `#section-…` (a search result, a contents entry, a pasted deep link) scrolls the section's top\n * edge to the top of the viewport, and without the margin the stuck header covers it — measured\n * 2026-10-20: a section's top at 0px under an 80px header, at 80px with the margin. On other pages\n * the markup is unchanged, and unmeasured the margin is `0px`.\n *\n * @param {Object} block - its component initialized\n * @param {'section'|'child'|boolean} kind - a body section's box, or a child section's; else none\n * @returns {{ scrollMarginTop: string }|null}\n */\nexport function sectionScrollMargin(block, kind) {\n return kind && hasStickyArea(block?.page) ? { scrollMarginTop: STICKY_SECTION_TOP } : null\n}\n\n/**\n * What a section's wrapper adds when it sticks: its style, and which kind of box it is.\n *\n * @param {Object} block - its component initialized\n * @param {'section'|'child'|boolean} kind - a body section (`true` reads as one), or a child section\n * @returns {{ style: Object, 'data-uw-sticky': string }|null}\n */\nexport function sectionStickyProps(block, kind) {\n const style = kind ? sectionStickyStyle(block) : null\n return style ? { style, 'data-uw-sticky': kind === 'child' ? 'child' : 'section' } : null\n}\n","/**\n * What the runtime puts on a layout-area wrapper.\n *\n * When a foundation enables view transitions (the default), the runtime gives\n * each layout area a `view-transition-name` so the browser animates it on its\n * own — persistent chrome (header, sidebar, footer) morphs in place while\n * everything else, the body included, crossfades as the page. With no names the\n * whole layout (chrome included) would flicker on every navigation.\n *\n * ⛔ **The body takes no name by default, which is what keeps a phone usable.** A\n * named element is captured as one image. WebKit captures it whole, off-screen part\n * included, at device resolution, painting every filter in it on the main thread; the\n * page itself is captured at the size of the viewport. Measured 2026-10-30 on a live\n * site, in WebKit on a 3× phone viewport with a desktop CPU: a named body was a\n * 1179 × 22710 capture, 107 MB against 9 MB, and a tap stalled the main thread for up to\n * 842 ms, against 271 ms with no name. Chromium did not stall at all. On an iPhone, that\n * site froze for about 30 s on a tap. The body still crossfades, with the page.\n *\n * Naming is only half of it. A `view-transition-name` **makes its element a\n * stacking context**, so the moment the runtime adds these wrappers it has\n * decided how the areas paint relative to one another — and with no `z-index`\n * on them they all sit at `auto` and paint in DOM order, which puts the body\n * over the header on any layout that renders the header first.\n *\n * That is not theoretical. It is the same mechanism `@uniweb/kit`'s `Overlay`\n * exists for (a modal opened from the header, trapped inside `uw-header`), and\n * it made a real docs page's fixed header unclickable while the identical\n * header on the marketing layout was fine — because that layout's markup\n * happened to wrap its header area in `relative z-40`. A framework that\n * creates stacking contexts owes its users an order; leaving it to DOM order\n * means \"does my header work\" is answered by an accident of someone's JSX.\n *\n * So this module resolves BOTH halves of the wrapper — the transition name and\n * the stacking layer — and hands back the finished style. It is pure (no\n * React/DOM) so the SPA renderer (`components/Layout.jsx`) and the SSR renderer\n * (`ssr-renderer.js`) produce identical wrappers, keeping prerendered HTML and\n * the hydrated SPA aligned.\n */\n\nimport { stickyStyle } from './sticky.js'\n\n// Namespace so generated names can't collide with `view-transition-name`s a\n// foundation sets inside its own component CSS. The prefix also guarantees a\n// valid CSS <custom-ident> (starts with a letter).\nconst NS = 'uw-'\n\nconst toIdent = (name) => NS + String(name).replace(/[^a-zA-Z0-9_-]/g, '-')\n\n/**\n * Build the effective view-transition-name map for a layout.\n *\n * Default: every rendered area gets a stable, namespaced name (`uw-<area>`), so\n * same-named areas across layouts share a name and morph between layouts\n * automatically. The body gets none and crossfades as the page (the header above).\n *\n * The layout's `meta.js` `transitions` value overrides this:\n * - an object overrides per region (`{ left: 'sidebar' }` to group across\n * layouts, `{ left: null }` to opt one region out, or `{ body: 'main' }` for a\n * body that morphs between layouts, which WebKit captures whole);\n * - `false` opts the whole layout out (back to the full-page crossfade).\n *\n * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.transitions`.\n * @returns {Object|null} region → view-transition-name; `null` when opted out.\n * A region whose value is null/empty in the returned map gets no name.\n */\nexport function resolveLayoutTransitions(areaNames, explicit) {\n if (explicit === false) return null\n\n const transitions = {}\n for (const name of areaNames) transitions[name] = toIdent(name)\n\n return explicit ? { ...transitions, ...explicit } : transitions\n}\n\n/**\n * The stacking layer of each area's wrapper.\n *\n * Default: every area except the body gets `1`, and the body gets nothing —\n * content is the backdrop, chrome is above it — and a sticky area gets one more\n * than every area that does not stick (below). That is the whole of what the\n * framework claims to know, and it is deliberately not more.\n *\n * The body is left unstacked rather than pinned to `0` on purpose. A layer\n * brings `position: relative` with it (see `areaWrapperStyle`), and a\n * positioned body wrapper would become the containing block for every\n * absolutely-positioned descendant on the page — a real behaviour change across\n * every site, to buy an ordering that lifting the chrome already achieves. An\n * unlayered body stays a plain stacking context and paints below anything with\n * a positive z-index, which is exactly the intent.\n *\n * In particular there is no default ordering BETWEEN chrome areas. Area names\n * are free-form (`header`, `footer`, `left` and `right` are conventions the\n * docs promote, but a foundation may define `topbar`, `rail`, `statusbar`,\n * anything), so ranking `header` above `left` would be the framework reading\n * meaning into a string it does not own — and would then behave differently for\n * a layout that spelled the same idea another way. Where two pieces of chrome\n * genuinely overlap, which one wins is a design decision, and the layout says\n * so with `layers`.\n *\n * The shape mirrors `transitions` exactly, so there is one thing to learn:\n * - an object overrides per region (`{ footer: 0 }`, `{ header: 5 }`), and a\n * region may be set to `null` to leave it unstacked;\n * - `false` opts the whole layout out, and the runtime then emits no\n * stacking at all — for a foundation that would rather own it in its own\n * markup, which is exactly what the marketing layout above was doing.\n *\n * Layers do NOT depend on view transitions. \"Chrome paints above content\" is a\n * property of the layout, not of how it animates — and body sections routinely\n * form their own stacking contexts (a section with a background isolates so its\n * background layer stays contained), so a fixed header in an unstacked sibling\n * area is not guaranteed to win against them either way. Tying the two together\n * was what left `DefaultLayout` hand-rolling its own `z-index: 40` on the\n * header: a second mechanism for the same job, which then swallowed `layers`\n * whole — a foundation could set `layers: { header: 0 }` on the default layout\n * and measurably nothing happened.\n *\n * ⭐ The one ordering the framework does know is a sticky area's (`sticky.js` — any of its sections\n * is). It stays in view while the rest of the page scrolls under it, so it is lifted above every\n * area that does not stick: the runtime made it stick, so it owes it the layer. Left equal to them,\n * a footer taller than the space below a stuck header paints over the header at the bottom of the\n * page, since it comes later in the DOM (measured 2026-10-19: a 521px footer on a 480px-tall\n * phone). That is a fact about the box, not a reading of the area's name, and the layout's\n * `layers` still rank a sticky area it names.\n *\n * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.layers`.\n * @param {Object} [sticky] - region → its stickiness, or null (`layoutStickiness`).\n * @returns {Object} region → z-index. Empty when the layout opts out.\n */\nexport function resolveLayoutLayers(areaNames, explicit, sticky = null) {\n if (explicit === false) return {}\n\n const layers = {}\n for (const name of areaNames) layers[name] = 1\n if (explicit) Object.assign(layers, explicit)\n\n const lifted = areaNames.filter((name) => sticky?.[name] && !(explicit && name in explicit))\n if (lifted.length > 0) {\n const under = Object.entries(layers)\n .filter(([name, layer]) => !sticky?.[name] && Number.isFinite(layer))\n .map(([, layer]) => layer)\n const above = Math.max(0, ...under) + 1\n for (const name of lifted) layers[name] = above\n }\n\n return layers\n}\n\n/**\n * The finished inline style for one area's wrapper, or `null` when the region\n * needs no wrapper at all.\n *\n * Returning the whole style from one place is the point: the SPA and SSR\n * renderers each build these wrappers, and a rule applied in one and forgotten\n * in the other is invisible until a prerendered page and its hydrated self\n * disagree about what paints on top.\n *\n * `position: relative` rides along with a layer because `z-index` does nothing\n * on a static element. It is set only on regions that carry a layer, which is\n * why the default leaves the body at `0` rather than lifting everything: a\n * positioned body wrapper would become the containing block for every\n * absolutely-positioned descendant on the page, and the ordering does not need\n * it.\n *\n * A sticky area (`sticky.js` — any of its sections is) is `position: sticky` in place of\n * `relative`, keeping its layer and its transition name. Only a foundation's layout passes one: the\n * built-in layout's area wrapper sits in its zone, as tall as itself, so the zone sticks instead.\n *\n * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @param {{ top: string }|null} [sticky] - the area's stickiness, when its box is the one that sticks\n * @returns {Object|null} Inline style object, or null for no wrapper.\n */\nexport function areaWrapperStyle(region, transitions, layers, sticky = null) {\n const style = {}\n\n const name = transitions?.[region]\n if (name) style.viewTransitionName = name\n else if (transitions && region === 'body') style.isolation = 'isolate'\n\n const layer = layers?.[region]\n if (sticky) {\n Object.assign(style, stickyStyle(sticky.top, layer))\n } else if (layer != null) {\n style.position = 'relative'\n style.zIndex = layer\n }\n\n return Object.keys(style).length > 0 ? style : null\n}\n\n/**\n * The props for one area's wrapper element — its style, and `data-uw-sticky=\"area\"` when it is\n * the box that sticks (`sticky.js`) — or `null` when the region needs no wrapper. Both renderers\n * build the wrapper from this, so the prerendered page and the browser's agree.\n *\n * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @param {{ top: string }|null} [sticky] - the area's stickiness, when its box is the one that sticks\n * @returns {{ style: Object, 'data-uw-sticky'?: 'area' }|null}\n */\nexport function areaWrapperProps(region, transitions, layers, sticky = null) {\n const style = areaWrapperStyle(region, transitions, layers, sticky)\n if (!style) return null\n return sticky ? { style, 'data-uw-sticky': 'area' } : { style }\n}\n","/**\n * The built-in layout — what a page renders with when the foundation offers no layout for it\n * (`Page.getLayoutName()` is null, or names none the foundation has): the header, the body and\n * the footer in one full-height column. Any other area is passed in and not drawn.\n *\n * ⭐ ONE COPY, FOR BOTH RENDERERS: the browser's (`components/Layout.jsx`) and the prerender's\n * (`ssr-renderer.js`, behind `uniweb build` and a host's prerender alike). It needs no hooks, no\n * Router and no DOM, so it is written with `createElement` and both import it — the L3 twins\n * exist for code that needs those, and this needs none.\n * ⛔ Until 2026-10-07 each renderer wrote its own, and the prerender's had no column: the browser\n * replaces the prerendered HTML rather than hydrating it, so a short page's footer sat right under\n * its content in the first paint — and in what a crawler reads — and jumped to the bottom when the\n * browser's render took over. Pinned by `tests/default-layout-parity.test.js`.\n *\n * ⛔ No stacking is set here. The areas arrive ordered: the runtime gives each area wrapper its\n * layer (`area-wrappers.js`), so chrome paints above the body whether or not view transitions are\n * on, and a foundation can re-rank them with `layers` in its layout meta. This layout used to\n * hard-code `z-index: 40` on the header and `30` on the footer for that job; once the runtime owned\n * the ordering, those numbers were a second mechanism for it, and the older one won — a positioned\n * element here becomes a stacking context that seals the area's own layer inside it. Measured\n * 2026-07-31: `layers: { header: 0 }` on the default layout changed nothing.\n *\n * ⭐ A ZONE IS THE BOX THAT STICKS. When a section of the header (or footer) area is sticky, the\n * runtime hands this layout the zone's props (`zones`, from `zoneProps`): its parent is the page\n * column, so it can stay in view, where the area wrapper inside it, as tall as itself, cannot\n * (`sticky.js`). The style carries the area's layer, which the sticky zone then holds.\n */\n\nimport React from 'react'\n\nconst COLUMN = { display: 'flex', flexDirection: 'column', minHeight: '100vh' }\nconst GROW = { flex: 1 }\n\n/**\n * @param {Object} props\n * @param {React.ReactNode} [props.header]\n * @param {React.ReactNode} [props.body]\n * @param {React.ReactNode} [props.footer]\n * @param {{ header?: Object, footer?: Object }} [props.zones] - a zone's props, when it sticks\n * @returns {React.ReactElement}\n */\nexport function DefaultLayout({ header, body, footer, zones }) {\n return React.createElement(\n 'div',\n { style: COLUMN },\n header && React.createElement('header', zones?.header || null, header),\n body && React.createElement('main', { style: GROW }, body),\n footer && React.createElement('footer', zones?.footer || null, footer),\n )\n}\n","/**\n * Appearance — site-wide color scheme (light/dark).\n *\n * ONE resolver, reached two ways:\n *\n * 1. SPA boot — `initAppearance()` runs inside initRuntime, after initUniweb()\n * (so website.themeData.appearance is readable) and before\n * createRoot().render(). That position precedes React's first paint, so no\n * section renders with the wrong tokens and then flips, and it covers every\n * delivery mode because all three start() branches funnel into initRuntime.\n *\n * 2. Prerendered HTML — `renderAppearanceBootScript()` serializes the SAME\n * function into a synchronous <head> script. HTML that ships real body\n * content is styled from :root (light) tokens until a bundle loads, so\n * without this a dark visitor sees a flash of light. The script is emitted\n * by injectPageContent() in ssr-renderer.js, which every prerender lane\n * goes through — the framework's SSG and the cloud worker's JIT render\n * alike. Emitting it from a lane-specific injector is how the cloud lane\n * silently missed it once already.\n *\n * Why serialize instead of hand-writing the inline script: the two paths must\n * agree exactly. `applyBootScheme` is therefore written to be SELF-CONTAINED —\n * it references no module-scope binding, only its two arguments and the browser\n * globals it needs — so `Function.prototype.toString()` yields a script that\n * behaves identically to calling it directly. Keep it that way: an import, a\n * module const, or a helper call would survive `toString()` as an undefined\n * identifier at first paint. appearance.test.js pins the equivalence.\n *\n * Environment-neutral by construction. `applyBootScheme` no-ops its DOM writes\n * outside a browser, and `renderAppearanceBootScript` only stringifies — so\n * ssr-renderer.js can import this module in Node and in a server-side isolate.\n *\n * Two writers with independent resolution is the bug this replaced:\n * WebsiteRenderer used to re-apply `appearance.default` from an effect, and\n * because React runs child effects before parent effects it clobbered the\n * visitor's stored preference on every page load — the page came back light\n * while the toggle button still believed it was dark, making the next click a\n * no-op.\n */\n\nimport { hasDarkScheme } from '@uniweb/core'\n\nexport const APPEARANCE_STORAGE_KEY = 'uniweb-appearance'\nexport const DARK_SCHEME_CLASS = 'scheme-dark'\nexport const LIGHT_SCHEME_CLASS = 'scheme-light'\n\n/**\n * Resolve and apply the visitor's color scheme.\n *\n * Precedence: stored visitor preference → OS preference (when the site opts in)\n * → the theme's declared default.\n *\n * SELF-CONTAINED ON PURPOSE — see the module header. This function is both\n * called directly (SPA boot) and serialized with toString() into the pre-paint\n * <script> of prerendered HTML. It must never reference anything outside its own\n * arguments and the browser globals below; the storage key and class names are\n * inlined as literals rather than read from the exported constants for exactly\n * that reason.\n *\n * Written in ES5 so it needs no transpilation in the inline-script form, and\n * every browser access is guarded: Safari private mode throws on localStorage,\n * old webviews lack matchMedia, and Node has no document.\n *\n * @param {boolean} respectSystem - follow prefers-color-scheme when unset\n * @param {'light'|'dark'} fallback - the theme's declared default\n * @returns {'light'|'dark'} the scheme applied\n */\nexport function applyBootScheme(respectSystem, fallback) {\n var stored = null\n try {\n stored = localStorage.getItem('uniweb-appearance')\n } catch (e) {\n // Safari private mode and some embedded webviews throw on access\n }\n\n var hasStored = stored === 'light' || stored === 'dark'\n var scheme = hasStored ? stored : fallback\n\n if (!hasStored && respectSystem) {\n try {\n if (window.matchMedia('(prefers-color-scheme: dark)').matches) scheme = 'dark'\n } catch (e) {\n // No matchMedia — keep the declared default\n }\n }\n\n try {\n var root = document.documentElement\n // Always set an explicit class rather than relying on the absence of one.\n // `default: system` themes emit a `@media (prefers-color-scheme: dark)`\n // block scoped to `:root:not(.scheme-light)`, so forcing light on a dark OS\n // requires `scheme-light` to be present — removing `scheme-dark` alone would\n // leave the media query still applying dark tokens.\n if (scheme === 'dark') {\n root.classList.add('scheme-dark')\n root.classList.remove('scheme-light')\n } else {\n root.classList.add('scheme-light')\n root.classList.remove('scheme-dark')\n }\n } catch (e) {\n // No DOM (Node / prerender) — the resolved scheme is still returned\n }\n\n return scheme\n}\n\n/**\n * Reduce a theme's `appearance:` block to the two arguments applyBootScheme\n * takes, or null when the site can never show dark.\n *\n * THE ONLY PLACE `appearance.*` FIELDS ARE READ. Both the SPA boot and the\n * inline-script emitter go through here, so the two cannot disagree about what\n * `respectSystemPreference` defaults to. They used to: the runtime treated an\n * unset value as false while the script emitter and @uniweb/core's\n * Theme.getAppearance() treated it as true. Those agreed only by the grace of\n * @uniweb/theming's normalizeAppearance() always injecting the key — any path\n * handing raw theme.yml appearance to the runtime would have produced a\n * pre-paint script and a boot resolver that disagree, i.e. the exact\n * flash-then-flip this whole module exists to prevent. Unset means true, which\n * is what the docs promise and what core already did.\n *\n * The null gate is @uniweb/core's hasDarkScheme() — the same predicate\n * @uniweb/theming uses to decide whether `.scheme-dark` CSS is generated at all.\n * Sharing it means we can never apply a scheme that has no matching rules, and\n * a light-only site correctly gets no class and no inline script.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {{respectSystem: boolean, fallback: 'light'|'dark'}|null}\n */\nexport function resolveAppearanceBoot(appearance) {\n if (!appearance || !hasDarkScheme(appearance)) return null\n\n return {\n respectSystem: appearance.respectSystemPreference !== false,\n fallback: appearance.default === 'dark' ? 'dark' : 'light',\n }\n}\n\n/**\n * Resolve and apply the boot scheme in the browser. Called by initRuntime.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {'light'|'dark'|null} the applied scheme, or null when the site has\n * no dark scheme to switch to (nothing is written to the document)\n */\nexport function initAppearance(appearance) {\n const opts = resolveAppearanceBoot(appearance)\n if (!opts) return null\n\n return applyBootScheme(opts.respectSystem, opts.fallback)\n}\n\n/**\n * Emit the pre-paint <script> for prerendered HTML.\n *\n * Returns '' when the site has no dark scheme — a light-only page always renders\n * light, so there is nothing to correct before paint and no reason to ship the\n * bytes. Pure SPA builds don't need it either: the body is empty until the\n * bundle renders and initAppearance() runs before that first render.\n *\n * Only a boolean and a JSON-quoted 'light'/'dark' are interpolated, both derived\n * from resolveAppearanceBoot rather than taken from the theme verbatim, so\n * author-supplied theme.yml values cannot inject script.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {string} a `<script>` tag, or '' when no script is needed\n */\nexport function renderAppearanceBootScript(appearance) {\n const opts = resolveAppearanceBoot(appearance)\n if (!opts) return ''\n\n const call = `(${applyBootScheme.toString()})(${opts.respectSystem}, ${JSON.stringify(opts.fallback)})`\n\n return `<script id=\"uniweb-appearance\">${call}</script>`\n}\n","/**\n * The DOCUMENT TITLE — what a browser tab, a bookmark and a search result show for a page.\n *\n * ONE rule, reached two ways (the shape `appearance.js` has for the color scheme):\n *\n * 1. SPA — `useHeadMeta` sets `document.title` on every navigation.\n * 2. Prerendered HTML — `injectPageContent()` in ssr-renderer.js writes `<title>`, for\n * every prerender lane: the framework's SSG and a host's render alike.\n *\n * ⛔ Until 2026-09-14 each side carried its own rule. The SPA wrote `<page> | <site>` and\n * the prerender wrote `<page>` alone, so every prerendered page changed its title the\n * moment the bundle loaded (`Home` → `Home | Acme`), and a crawler that runs no script\n * indexed the shorter one.\n *\n * `<page title> | <site name>`; the page title alone when the site has no name; the empty\n * string when the page has no title — a caller then leaves the title it already has.\n *\n * @param {string|number|null|undefined} pageTitle - the page's display title (`page.getTitle()`)\n * @param {string|null|undefined} siteName - the site's name (`website.name`)\n * @returns {string}\n */\nexport function documentTitle(pageTitle, siteName) {\n if (!pageTitle) return ''\n const title = String(pageTitle)\n return siteName ? `${title} | ${siteName}` : title\n}\n","/**\n * The page a Website starts with as active — recorded when the SSR init builds the graph, and put\n * back before each render (`createPageRenderer`).\n *\n * ⭐ It is what the browser has on a URL that names no page. Core makes the site's index page (or\n * its first page) active when it builds the Website, and neither the boot nor `PageRenderer` makes\n * the not-found page active, since no route names it.\n *\n * ⛔ `renderPage` makes the page it renders active by its route, so for the not-found page it\n * changes nothing, and that page rendered with whatever page the Website rendered last: on a\n * Website reused across requests, the previous request's. Anything that adapts to the active page\n * — a header, a language switcher, a docs sidebar — showed that page.\n *\n * A WeakMap rather than a slot on core's sealed Website: it is SSR state, and core is in every site.\n */\nconst startPages = new WeakMap()\n\n/** Remember `website`'s active page as the one it starts with. Call it before anything renders. */\nexport function recordStartPage(website) {\n if (website && !startPages.has(website)) startPages.set(website, website.activePage)\n}\n\n/** Make `website`'s start page active again. A Website the SSR init did not build is left alone. */\nexport function restoreStartPage(website) {\n if (startPages.has(website)) website.activePage = startPages.get(website)\n}\n","/**\n * WHICH PAGE A ROUTE NAMES — the one lookup, importable without a renderer.\n *\n * ⭐ **It is one line, and it is a module because of who imports it.** `ssr-renderer.js` imports\n * `react-dom/server` at module scope, so anything reaching for this lookup through that file drags\n * the React server renderer into its own graph. The render's data step (`load-page-data.js`)\n * fetches and renders nothing, and did exactly that until 2026-09-20 — for `website.getPage`.\n *\n * ⚠️ **The docstring below already said \"no React … safe in a Worker isolate\", and was right.** It\n * was the FILE that made it false, which is the shape to watch for: a claim about a function's\n * dependencies is a claim about its module's imports, and nothing checks the two agree.\n * `tests/data-step-graph.test.js` checks it for the one caller that needs it.\n *\n * @module\n */\n\n/**\n * Resolve a route to the Page that should render it.\n *\n * Exists because `ssr-renderer.js` exported `renderPage(page, …)` and no supported way\n * to *get* a page — so every host rendering server-side wrote its own lookup,\n * and the obvious one (`website.pages.find(p => p.route === route)`) cannot\n * match a dynamic route, because the payload holds `/blog/:id` and the request\n * carries `/blog/1`. One host wrote that lookup three times in three files\n * before the gap was noticed. A renderer that takes a Page owes callers a Page.\n *\n * ⛔ Move it, never copy it: a second lookup here would be the fourth.\n *\n * This is `Website#getPage` — the same seven-step resolution the browser runs,\n * literally the same function, so a server-rendered page and the one hydrating\n * over it cannot disagree. Pure `@uniweb/core`: no React, no DOM, no DataStore\n * required, safe in a Worker isolate.\n *\n * @param {Website} website\n * @param {string} route - The requested path, e.g. `/blog/1`\n * @returns {Page|undefined} The page, or undefined when nothing matches — which\n * is a genuine 404 and the caller's to turn into one.\n */\nexport function resolvePage(website, route) {\n return website.getPage(route)\n}\n\n/**\n * The page a request for `route` renders, for the doors that render the page a visit starts on\n * (`createPageRenderer`, and its data step `loadPageData`): `resolvePage`, except a page served from\n * elsewhere.\n *\n * ⭐ A page with `rewrite:` is the host's to proxy, before the app loads. A request for one that\n * reaches a renderer was not proxied, so the doors answer it as the browser's first load of it does\n * (`PageRenderer.jsx`): with the site's not-found page, as a static build emits no file there.\n * ⛔ Until 2026-10-27 they rendered that page's own content as an ordinary page, and the browser\n * replaced it with the not-found page at boot.\n *\n * @param {Website} website\n * @param {string} route - The requested path\n * @returns {Page|undefined} undefined when the route names no page, or a page served from elsewhere\n */\nexport function resolveRequestedPage(website, route) {\n return website.resolveRoute(route).kind === 'rewrite' ? undefined : resolvePage(website, route)\n}\n","/**\n * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and server-side SSR.\n * Mirrors BlockRenderer.jsx using React.createElement directly — no hooks,\n * no JSX, no browser APIs. A section's background and the built-in layout are\n * not mirrored: both renderers draw the one copy (`background.js`,\n * `default-layout.js`).\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx, update `renderBlock` here to match.\n *\n * Exports three layers:\n * 1. Rendering functions (renderBlock, renderBlocks, renderLayout, renderBackground)\n * 2. Initialization (initPrerender, prefetchIcons)\n * 3. Per-page rendering (renderPage, classifyRenderError, injectPageContent, escapeHtml)\n */\n\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { createUniweb, resolveDefaultLocale } from '@uniweb/core'\nimport { sectionDomId } from '@uniweb/core/section-id'\nimport { Background } from './background.js'\nimport { routePatternToRegex, isDynamicRoute } from '@uniweb/core/route-match'\nimport { DEFAULT_ICON_BASE, iconUrl } from '@uniweb/core/icon-corpus'\nimport { buildSectionOverrides, FONT_LINKS_MARKER } from '@uniweb/theming'\nimport { prepareProps, getComponentMeta } from './prepare-props.js'\nimport { default404Html } from './default-404.js'\nimport {\n wireFoundationCapabilities,\n sliceContentForLocale,\n hydrateDataStore,\n ensureThemeCss,\n} from './wire-foundation.js'\nimport { resolveLayoutTransitions, resolveLayoutLayers, areaWrapperProps } from './area-wrappers.js'\nimport { layoutStickiness, sectionStickyProps, sectionScrollMargin, zoneProps } from './sticky.js'\nimport { DefaultLayout } from './default-layout.js'\nimport { renderAppearanceBootScript } from './appearance.js'\nimport { documentTitle } from './document-title.js'\nimport { recordStartPage } from './start-page.js'\nimport { escapeHtml } from '@uniweb/core/page-shell'\nimport { jsonForScript } from '@uniweb/core/data-script'\n\n// Re-export L2 helpers so the public `@uniweb/runtime/ssr` surface\n// carries everything an SSR consumer needs from one entry point.\nexport { sliceContentForLocale, hydrateDataStore }\n\n// ============================================================================\n// Layer 1: Rendering functions\n// ============================================================================\n\n/**\n * Valid color contexts for section theming\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration.\n * Mirrors getWrapperProps in BlockRenderer.jsx.\n */\nexport function getWrapperProps(block) {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Empty themeName = Auto → no context class → inherits tokens from :root\n // Non-empty = Pinned → context class sets tokens directly on the element\n let contextClass = ''\n if (theme && VALID_CONTEXTS.includes(theme)) {\n contextClass = `context-${theme}`\n }\n\n let className = contextClass\n if (blockClassName) {\n className = className ? `${className} ${blockClassName}` : blockClassName\n }\n\n // Normalized once by core (`block.background`) — the same value the SPA renderer reads.\n const background = block.background || {}\n const style = {}\n\n // If background has content, ensure relative positioning and a stacking context\n // so the background's z-index stays contained within this section.\n if (background.mode) {\n style.position = 'relative'\n style.isolation = 'isolate'\n }\n\n // The section's tokens in effect whatever the scheme, inline — as in the SPA renderer\n // (`BlockRenderer.jsx`); the rest of its theme is the page stylesheet's.\n if (block.contextOverrides) {\n for (const [key, value] of Object.entries(block.contextOverrides)) {\n style[`--${key}`] = value\n }\n }\n\n // Same rule as the SPA renderer and the search extractor — @uniweb/core/section-id.\n return { id: sectionDomId(block), style, className, background }\n}\n\n\n/**\n * Render a section's background for SSR: the one implementation both renderers draw\n * (`background.js`). A video background is drawn as its poster — what the browser draws too,\n * until it has mounted and starts the video.\n * ⛔ Until 2026-10-25 this was a copy of `Background.jsx`, and drew no video at all.\n *\n * @param {Object} background - `block.background`, normalized by core\n * @returns {React.ReactElement|null}\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n return React.createElement(Background, background)\n}\n\n/**\n * Render a single block for SSR.\n * Mirrors BlockRenderer.jsx but without hooks (no runtime data fetching).\n *\n * Two modes (mirrors client BlockRenderer):\n * - Bare (as=null/false): component only, no wrapper\n * - Section (as='section'/'div'/etc.): full treatment with wrapper, context, background\n *\n * @param {Block} block - Block instance to render\n * @param {Object} [options]\n * @param {string|null} [options.as='section'] - Wrapper element tag, or null/false for bare mode\n * @param {'section'|'child'|boolean} [options.stickySelf] - whether a sticky section's own wrapper\n * is the box that sticks: a body section's (`'section'`), or a child section's (`'child'`)\n * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { as = 'section', stickySelf = false } = {}) {\n const Component = block.initComponent()\n\n if (!Component) {\n return React.createElement('div', {\n className: 'block-error',\n style: { padding: '1rem', background: '#fef2f2', color: '#dc2626' },\n }, `Component not found: ${block.type}`)\n }\n\n // Resolve entity data synchronously (SSG has no async): EntityStore fills the keys\n // the component declares (`meta.data`) from the fetches that reach the block.\n const meta = getComponentMeta(block.type)\n const entityStore = block.website?.entityStore\n let entityData = null\n let pending = false\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready') entityData = resolved.data\n pending = resolved.status === 'pending'\n }\n\n // ⭐ A block whose data is still out is LOADING here, as in the browser, which sets the same flag\n // before `prepareProps` (`BlockRenderer.jsx`) and fetches the data once it boots. So the page\n // paints the component's loading state, the one the browser renders in its place.\n // ⛔ Until 2026-10-25 this was never set, and such a section prerendered its EMPTY state —\n // \"nothing found\", or an error — then switched to loading when the browser took over.\n block.dataLoading = pending\n\n // Build content and params with runtime guarantees.\n // prepareProps handles the full pipeline: entity data merge,\n // foundation data and content handlers, param defaults, guaranteed\n // content structure, and the props handler.\n // See prepare-props.js for the pipeline details.\n const prepared = prepareProps(block, meta, entityData)\n const params = prepared.params\n const content = { ...prepared.content, ...block.properties }\n\n const componentProps = { content, params, block }\n\n // Bare mode: component only, no wrapper or section chrome.\n // Used by ChildBlocks for grid cells, tab panels, inline children, insets.\n if (!as) {\n return React.createElement(Component, componentProps)\n }\n\n // Section mode: full treatment with wrapper, context classes, background.\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // A body section's wrapper, or a child section's, is the box that stays in view (`sticky.js`).\n const sticky = sectionStickyProps(block, stickySelf)\n if (sticky) {\n wrapperProps.style = { ...wrapperProps.style, ...sticky.style }\n wrapperProps['data-uw-sticky'] = sticky['data-uw-sticky']\n }\n // On a page whose layout has a sticky area, a link to this section lands below it.\n const margin = sectionScrollMargin(block, stickySelf)\n if (margin) wrapperProps.style = { ...wrapperProps.style, ...margin }\n\n // Merge Component.className (static classes declared on the component function)\n const componentClassName = Component.className\n if (componentClassName) {\n wrapperProps.className = wrapperProps.className\n ? `${wrapperProps.className} ${componentClassName}`\n : componentClassName\n }\n\n // Check if component handles its own background\n const hasBackground = background?.mode && meta?.background !== 'self'\n block.hasBackground = hasBackground\n\n // Determine wrapper element:\n // - Explicit as (not 'section') → use as prop directly\n // - Component.as → use component's declared tag (e.g., Header.as = 'header')\n // - fallback → 'section'\n const wrapperTag = as !== 'section' ? as : (Component.as || 'section')\n\n if (hasBackground) {\n return React.createElement(wrapperTag, wrapperProps,\n renderBackground(background),\n React.createElement('div', { style: { position: 'relative', zIndex: 10 } },\n React.createElement(Component, componentProps)\n )\n )\n }\n\n return React.createElement(wrapperTag, wrapperProps,\n React.createElement(Component, componentProps)\n )\n}\n\n/**\n * Render an array of blocks for SSR.\n */\nexport function renderBlocks(blocks, { stickySelf = false } = {}) {\n if (!blocks || blocks.length === 0) return null\n return blocks.map((block, index) =>\n React.createElement(React.Fragment, { key: block.id || index },\n renderBlock(block, { stickySelf })\n )\n )\n}\n\n/**\n * Render page layout for SSR.\n * Mirrors Layout.jsx but without hooks.\n */\nexport function renderLayout(page, website) {\n const layoutName = page.getLayoutName()\n const RemoteLayout = website.getRemoteLayout(layoutName)\n const layoutMeta = website.getLayoutMeta(layoutName)\n\n const bodyBlocks = page.getBodyBlocks()\n const areas = page.getLayoutAreas()\n\n // Mirror Layout.jsx: every block's component initialized before anything renders, so each\n // section's `sticky` — its type's default included — is known before the areas' layers are.\n for (const blocks of [bodyBlocks, ...Object.values(areas)]) {\n for (const block of blocks || []) block.initComponent()\n }\n\n // Mirror Layout.jsx: wrap the body and each area in a thin div, an area's carrying its\n // view-transition-name, so the prerendered HTML matches the page the SPA renders\n // in its place and the browser can animate regions independently on client navigation.\n const areaNames = Object.keys(areas)\n const transitions = website.viewTransitions\n ? resolveLayoutTransitions(areaNames, layoutMeta?.transitions)\n : null\n\n // Mirror Layout.jsx: an area stays in view when any of its sections is sticky — in a\n // foundation's layout its wrapper sticks, in the built-in one the zone around it — and is lifted\n // above the areas that scroll under it.\n const sticky = layoutStickiness(areas)\n const layers = resolveLayoutLayers(areaNames, layoutMeta?.layers, sticky)\n const wrapArea = (name, element) => {\n const props = areaWrapperProps(name, transitions, layers, RemoteLayout ? sticky[name] : null)\n return props ? React.createElement('div', props, element) : element\n }\n\n const bodyElement = bodyBlocks ? wrapArea('body', renderBlocks(bodyBlocks, { stickySelf: 'section' })) : null\n const areaElements = {}\n for (const [name, blocks] of Object.entries(areas)) {\n areaElements[name] = wrapArea(name, renderBlocks(blocks))\n }\n\n if (RemoteLayout) {\n const params = { ...(layoutMeta?.defaults || {}), ...(page.getLayoutParams() || {}) }\n return React.createElement(RemoteLayout, {\n page, website, params,\n body: bodyElement,\n ...areaElements,\n })\n }\n\n // The built-in layout — the browser's own copy, not a mirror of it (`default-layout.js`).\n // ⛔ Until 2026-10-07 this wrote its own, with no full-height column, so the prerendered\n // page differed from the one the browser renders over it.\n const zone = (name) => zoneProps(sticky[name], layers?.[name])\n return React.createElement(DefaultLayout, {\n body: bodyElement,\n ...areaElements,\n zones: { header: zone('header'), footer: zone('footer') },\n })\n}\n\n// ============================================================================\n// Layer 2: Initialization\n// ============================================================================\n\n/**\n * Construct a Uniweb singleton scoped to a single locale.\n *\n * Combines the three steps that every SSR consumer (browser SPA, Node\n * SSG, a server-side isolate) needs in the same order: slice the\n * multi-locale content payload, run `initPrerender` (which builds the\n * Website + wires foundation capabilities), then `setActiveLocale` so\n * `website.activeLang` stays in sync with what the page is rendering\n * for. Caller still owns DataStore hydration (per-request data differs\n * between requests; locale construction can be cached).\n *\n * @param {Object} content - Site content payload (possibly multi-locale).\n * @param {Object} foundation - Loaded foundation module.\n * @param {string} locale - Locale code to render in.\n * @param {Array<Object>|Object} [extensionsOrOptions] - Same shape as initPrerender's\n * third arg: an extensions array, or an options object when no extensions.\n * @param {Object} [maybeOptions] - Options object when extensions are passed.\n * @returns {import('@uniweb/core').default} The configured Uniweb singleton.\n */\nexport function initPrerenderForLocale(content, foundation, locale, extensionsOrOptions, maybeOptions) {\n const localeContent = sliceContentForLocale(content, locale)\n const uniweb = initPrerender(localeContent, foundation, extensionsOrOptions, maybeOptions)\n const defaultLang = resolveDefaultLocale(content?.config)\n if (locale && locale !== defaultLang && uniweb.activeWebsite?.setActiveLocale) {\n uniweb.activeWebsite.setActiveLocale(locale)\n }\n return uniweb\n}\n\n/**\n * Create and configure the Uniweb runtime for prerendering.\n *\n * Handles the full initialization sequence in the correct order:\n * createUniweb → setFoundation → capabilities → layoutMeta → basePath → childBlockRenderer.\n *\n * Returns the configured uniweb instance. Consumers can add extras after:\n * - Build: pre-populate DataStore, load extensions\n * - A server-side renderer: (none needed — the payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; a server-side renderer clones upfront).\n *\n * @param {Object} content - Site content JSON (pages, config, hierarchy)\n * @param {Object} foundation - Loaded foundation module\n * @param {Object} [options]\n * @param {function} [options.onProgress] - Progress callback\n * @returns {Object} Configured uniweb instance\n */\nexport function initPrerender(content, foundation, extensionsOrOptions, maybeOptions) {\n // Backwards-compatible arg shape: (content, foundation, options) or\n // (content, foundation, extensions, options). Extensions must be passed at\n // construction so the Website's FetcherDispatcher sees their routes.\n let extensions = []\n let options = {}\n if (Array.isArray(extensionsOrOptions)) {\n extensions = extensionsOrOptions\n options = maybeOptions || {}\n } else {\n options = extensionsOrOptions || {}\n }\n const { onProgress = () => {} } = options\n\n onProgress('Initializing runtime...')\n // Uniweb constructor wires foundation, capabilities, layoutMeta, handlers,\n // and extensions at construction time — see `@uniweb/core`'s src/uniweb.js.\n const uniweb = createUniweb(content, foundation, extensions)\n // Before anything renders: the active page every render starts from (`start-page.js`).\n recordStartPage(uniweb.activeWebsite)\n\n // Set base path from site config for subdirectory deployments\n if (content.config?.base && uniweb.activeWebsite?.setBasePath) {\n uniweb.activeWebsite.setBasePath(content.config.base)\n }\n\n // Set childBlockRenderer so ChildBlocks/Visual/Render work during prerender.\n // Mirrors the client's ChildBlocks component in PageRenderer.jsx:\n // - default bare rendering (no wrapAs) — component only, no wrapper\n // - pass wrapAs to opt into full section treatment\n uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, wrapAs }) {\n const blockList = blocks || from?.childBlocks || []\n return blockList.map((childBlock, index) =>\n React.createElement(React.Fragment, { key: childBlock.id || index },\n renderBlock(childBlock, { as: wrapAs || null, stickySelf: wrapAs ? 'child' : false })\n )\n )\n }\n\n // The same background a section gets, for kit's `SectionBackground` — a component that\n // paints its own (`background: 'self'`). The one implementation, which the browser draws\n // too (setup.js); its `className` is the layer's. ⛔ Until 2026-10-25 this dropped it.\n uniweb.backgroundRenderer = Background\n\n // L2 (singleton wiring): defaultInsets, xref.build(), and any future\n // framework-level capability bridge — shared with setup.js so both\n // boot paths apply the same foundation contract. See\n // wire-foundation.js — its header states the rule for what belongs in\n // that helper vs. here vs. setup.js.\n wireFoundationCapabilities(uniweb, foundation)\n\n // Site-wide theme CSS. Unconditional here: at this point there is no\n // <head> to inspect, and injectPageContent() emits the result\n // idempotently, so a lane that already baked the style tag is unaffected.\n ensureThemeCss(uniweb, foundation)\n\n // Register SSR-safe routing so useRouting()/useActiveRoute() work during prerender.\n // renderPage() calls website.setActivePage() before rendering each page,\n // so activePage.route reflects the page being rendered — except the site's\n // not-found page, which no route names. Through `createPageRenderer` it renders\n // with the page a route served from elsewhere names, else the page the Website\n // starts with (`start-page.js`); through `generate404Html`, with the page\n // rendered last.\n const website = uniweb.activeWebsite\n uniweb.routingComponents = {\n useLocation: () => {\n // ⛔ `activePage.route` IS ALREADY ROOT-RELATIVE — `/`, `/about`. This used to\n // return `'/' + route`, so every prerendered page reported a pathname with a\n // doubled leading slash: `//` on the homepage, `//about` elsewhere.\n //\n // It failed silently for as long as it existed, because nothing in the\n // runtime reads pathname — a FOUNDATION does. Measured 2026-09-18: a language\n // switcher passing `useLocation().pathname` to `getLocaleUrl` emitted\n // `href=\"//\"`, which a browser resolves as a PROTOCOL-RELATIVE url (host\n // empty), not as the site root. The SPA was unaffected, so it reproduced only\n // in the built output.\n const route = website?.activePage?.route || '/'\n const pathname = route.startsWith('/') ? route : `/${route}`\n return { pathname, search: '', hash: '', state: null, key: 'default' }\n },\n useParams: () => ({}),\n useNavigate: () => () => {},\n }\n\n return uniweb\n}\n\n/**\n * Pre-fetch icons from CDN and populate the Uniweb icon cache.\n * Stores the cache on siteContent._iconCache for embedding in HTML.\n *\n * @param {Object} siteContent - Site content JSON (mutated: _iconCache added)\n * @param {Object} uniweb - Configured uniweb instance\n * @param {function} [onProgress] - Progress callback\n * @returns {Promise<string[]>} the references that could not be fetched\n */\nexport async function prefetchIcons(siteContent, uniweb, onProgress = () => {}) {\n const icons = siteContent.icons?.used || []\n if (icons.length === 0) return []\n\n const cdnBase = siteContent.config?.icons?.base || DEFAULT_ICON_BASE\n\n onProgress(`Fetching ${icons.length} icons for SSR...`)\n\n const results = await Promise.allSettled(\n icons.map(async (iconRef) => {\n const [family, name] = iconRef.split(':')\n const url = iconUrl(family, name, cdnBase)\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n return [`${family}:${name}`, await response.text()]\n })\n )\n // ⭐ Cached in the order the build names the icons (`icons.used`, sorted), never the order their\n // fetches settle in: the cache is written into every page (`_iconCache`, `__ICON_CACHE__`), so\n // in arrival order two builds of one site wrote different pages. ⛔ Until 2026-10-25 each icon\n // was cached as it arrived (measured: three builds of one site, three orders).\n for (const result of results) {\n if (result.status === 'fulfilled') uniweb.iconCache.set(...result.value)\n }\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failedRefs = icons.filter((_, i) => results[i].status === 'rejected')\n if (failedRefs.length > 0) {\n // Named, so an author can fix the reference. ⛔ Until 2026-10-24 this said only \"(1 failed)\",\n // and a missing icon on a live page took a search through every reference to find.\n const shown = failedRefs.slice(0, 10).join(', ') + (failedRefs.length > 10 ? ', …' : '')\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failedRefs.length} failed: ${shown})`\n console.warn(`[prerender] ${msg}`)\n onProgress(` ${msg}`)\n }\n\n // Store icon cache on siteContent for embedding in HTML\n if (uniweb.iconCache.size > 0) {\n siteContent._iconCache = Object.fromEntries(uniweb.iconCache)\n }\n return failedRefs\n}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n// ⭐ `resolvePage` LIVES IN ITS OWN LEAF and is re-exported here, so the public\n// `@uniweb/runtime/ssr` surface is unchanged. It is `website.getPage` and needs\n// no renderer; this file imports `react-dom/server` at module scope, so a caller\n// that only wants the lookup used to get React's server renderer with it\n// (`./resolve-page.js`, 2026-09-20).\nexport { resolvePage } from './resolve-page.js'\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\nexport function classifyRenderError(err) {\n const msg = err.message || ''\n\n if (msg.includes('Invalid hook call') || msg.includes('useState') || msg.includes('useEffect')) {\n return {\n type: 'hooks',\n message: 'contains components with React hooks (renders client-side)',\n }\n }\n\n if (msg.includes('Element type is invalid') && msg.includes('null')) {\n return {\n type: 'null-component',\n message: 'a component resolved to null (often hook-related, renders client-side)',\n }\n }\n\n return {\n type: 'unknown',\n message: msg,\n }\n}\n\n/**\n * Render a single page to HTML.\n *\n * Handles the full per-page pipeline:\n * setActivePage → renderLayout → renderToString → error handling → section override CSS.\n *\n * @param {Page} page - Page instance to render\n * @param {Website} website - Website instance\n * @returns {{ renderedContent: string, sectionOverrideCSS: string } | { error: { type: string, message: string } }}\n */\nexport function renderPage(page, website) {\n // ⛔ The site's not-found page is never made active by its route. No route names it, and on a\n // site whose parametric page matches `/404` (a root `/:slug`) that route names THAT page, which\n // then rendered as the active one. It renders with the active page it finds: the Website's\n // first, through `createPageRenderer` (`start-page.js`), as in the browser.\n if (page !== website.getNotFoundPage()) website.setActivePage(page.route)\n\n // A page that claims content but yields no blocks has not been loaded — it is\n // not an empty page. `Page#bodyBlocks` returns [] when its sections are absent\n // from the payload (split content), on the understanding that a caller loads\n // them first: the SPA does, in PageRenderer and at boot. THIS path never has.\n //\n // Left alone, that renders a structurally valid, completely empty document and\n // reports success — which is the worst shape a failure can take, and it cost a\n // host most of a day chasing a renderer that was doing what it was told.\n // Distinguishing it here is cheap: a content-less container reports\n // hasContent() === false and is correctly empty, so the two never collide.\n if (page.hasContent?.() && page.getBodyBlocks().length === 0) {\n return {\n error: {\n type: 'content-not-loaded',\n message:\n `page \"${page.route}\" declares content but has no loaded sections — ` +\n 'its sections are not in the payload and this renderer does not fetch them',\n },\n }\n }\n\n const element = renderLayout(page, website)\n\n let renderedContent\n try {\n renderedContent = renderToString(element)\n } catch (err) {\n return { error: classifyRenderError(err) }\n }\n\n // Build per-page section override CSS (theme pinning, component vars)\n const appearance = website.themeData?.appearance\n // Every block the page renders — each layout area it shows, a sidebar too; the builder walks\n // their child sections. The SPA renderer passes the same (`PageRenderer.jsx`).\n const sectionOverrideCSS = buildSectionOverrides(page.getAllBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n// Escape HTML special characters — the one implementation, which a page's shell writes with too\n// (`@uniweb/core/page-shell`). Still exported from here, as it has been since 0.6.14.\nexport { escapeHtml }\n\n/**\n * Inject prerendered content into an HTML shell.\n *\n * THE SHARED PRERENDER SEAM. Every lane that turns a Page into HTML calls this:\n * the framework's SSG (@uniweb/build's prerender.js) and the cloud worker's\n * just-in-time render. Anything a page needs *because it was prerendered at all*\n * belongs here, so a new feature reaches both lanes at once.\n *\n * Common operations shared by both build and cloud:\n * - Replace #root div with rendered HTML\n * - Update page title\n * - Write the page's head tags — description, canonical, hreflang, Open Graph (`writeHeadMeta`)\n * - Inject section override CSS\n * - Inject the pre-paint appearance script\n *\n * Build layers its additional injections on top of this return value:\n * __SITE_CONTENT__ JSON, icon cache, theme CSS (build-specific).\n *\n * WHICH SIDE OF THE SEAM? Ask what the injection is derived from. If it needs\n * only the page/website graph — which every lane has — it goes here. If it needs\n * a build-only artifact (the emitted bundle, the collection JSON files, the\n * prefetched icon cache, the compiled theme CSS), it goes in the caller. Getting\n * this wrong is silent: the appearance boot script started life in\n * @uniweb/build's injectBuildData and therefore never reached cloud-rendered\n * pages, which flashed light for every dark-mode visitor.\n *\n * @param {string} html - HTML shell\n * @param {string} renderedContent - React renderToString output\n * @param {Object} page - Page data { title, description, route }\n * @param {Object} [options]\n * @param {string} [options.sectionOverrideCSS] - Per-page section override CSS\n * @returns {string} HTML with injected content\n */\nexport function injectPageContent(html, renderedContent, page, options = {}) {\n let result = html\n\n // Pre-paint appearance script. Prerendered HTML carries real content styled\n // from :root (light) tokens, so a dark visitor would see a flash of light\n // before the bundle boots and applies the class. Read off the page's\n // website back-ref rather than a parameter: every lane builds the same graph,\n // so this needs no plumbing and no per-lane opt-in. Idempotent, because\n // @uniweb/build's injectBuildData may run over this HTML afterwards.\n if (!result.includes('id=\"uniweb-appearance\"')) {\n const bootScript = renderAppearanceBootScript(page?.website?.themeData?.appearance)\n if (bootScript) {\n result = result.replace('</head>', ` ${bootScript}\\n</head>`)\n }\n }\n\n // Site-wide theme CSS. Derived from the website graph\n // (`website.themeData`), so it belongs on THIS side of the seam — every\n // lane builds that graph. It lived in @uniweb/build's injectBuildData\n // until 2026-07-28, which meant sites served by any lane that doesn't run\n // the framework's build rendered with every semantic token unset: no\n // colours, no backgrounds, no failure anywhere. That is the same mistake\n // the appearance script above was moved out of, four lines below the\n // comment warning about it — see the note in `@uniweb/build`'s src/prerender.js.\n // Idempotent, so a shell that already carries the tag is left alone.\n const themeData = page?.website?.themeData\n const themeCss = themeData?.css\n if (themeCss && !result.includes('id=\"uniweb-theme\"')) {\n result = result.replace(\n '</head>',\n ` <style id=\"uniweb-theme\">\\n${themeCss}\\n </style>\\n</head>`\n )\n }\n\n // The theme's font <link> tags — same seam, same reasoning. Graph-derived,\n // so a lane that never runs @uniweb/build still gets its webfonts instead of\n // falling back to system faces. Deduped on FONT_LINKS_MARKER rather than an\n // id because <link> tags have none; the marker is owned by @uniweb/theming,\n // which generates the block, so this and @uniweb/build read one literal.\n if (themeData?.links && !result.includes(FONT_LINKS_MARKER)) {\n result = result.replace(\n '</head>',\n ` ${FONT_LINKS_MARKER}\\n${themeData.links}\\n</head>`\n )\n }\n\n // Inject per-page section override CSS before </head>\n if (options.sectionOverrideCSS) {\n const overrideStyle = `<style id=\"uniweb-page-overrides\">\\n${options.sectionOverrideCSS}\\n</style>`\n result = result.replace('</head>', `${overrideStyle}\\n</head>`)\n }\n\n // The icons this page drew, which the browser reads before its first render (`setup.js`), so it\n // draws them too. A replacer function, since `$` in a replacement string is not literal.\n if (options.icons?.size && !result.includes('id=\"__ICON_CACHE__\"')) {\n const iconScript = `<script id=\"__ICON_CACHE__\" type=\"application/json\">${jsonForScript(Object.fromEntries(options.icons))}</script>`\n result = result.replace('</head>', () => ` ${iconScript}\\n</head>`)\n }\n\n // Replace the empty root div with pre-rendered content\n result = result.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${renderedContent}</div>`\n )\n\n // Update the document title — by the rule the SPA sets `document.title` with\n // (`documentTitle`: `<page> | <site>`), so the title a crawler reads here is the one a\n // visitor keeps once the bundle loads. ⛔ Until 2026-09-14 this wrote the page title\n // alone. getTitle() so isIndex pages inherit their parent's title.\n const title = documentTitle(page.getTitle?.() || page.title, page.website?.name)\n if (title) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(title)}</title>`\n )\n }\n\n // ⭐ THE PAGE'S HEAD TAGS — description, canonical, hreflang, Open Graph, Twitter — from the\n // page's own head metadata (`Page#getHeadMeta`, which the browser writes the same tags from in\n // `useHeadMeta`), so crawlers and social unfurlers, which run no script, read the page's own.\n // ⛔ Until 2026-10-25 these were APPENDED, after the static build had written the HOME page's\n // into the one shell every page is rendered into: each page named the home page its canonical\n // and carried two og:title and two og:url, the home page's first.\n result = writeHeadMeta(result, page)\n\n return result\n}\n\n/**\n * Write a page's head tags into `html`'s head. These tags are the framework's: each is set to the\n * page's value, replacing any of its name the shell carries, and removed when the page has none —\n * as the browser does on every navigation (`useHeadMeta`), so hydrating the page changes nothing a\n * crawler read.\n *\n * @param {string} html\n * @param {Object} page\n * @returns {string}\n */\nfunction writeHeadMeta(html, page) {\n // A Page states its own (`getHeadMeta`); plain page data, which callers may hand in, its fields.\n const meta = typeof page?.getHeadMeta === 'function' ? page.getHeadMeta() : { description: page?.description || null }\n const end = html.indexOf('</head>')\n if (!meta || end === -1) return html\n const og = meta.og || {}\n const keywords = Array.isArray(meta.keywords) ? meta.keywords.join(', ') : meta.keywords\n const metaTags = [\n ['name', 'description', meta.description],\n ['name', 'keywords', keywords],\n ['name', 'robots', meta.robots],\n ['property', 'og:type', 'website'],\n ['property', 'og:site_name', og.siteName],\n ['property', 'og:title', og.title],\n ['property', 'og:description', og.description],\n ['property', 'og:image', og.image],\n ['property', 'og:url', og.url],\n ['name', 'twitter:card', og.image ? 'summary_large_image' : 'summary'],\n ['name', 'twitter:site', meta.twitter?.site],\n ['name', 'twitter:title', og.title],\n ['name', 'twitter:description', og.description],\n ['name', 'twitter:image', og.image],\n ]\n // The head alone: the page's content is in the body, and none of it is edited here.\n let head = html.slice(0, end)\n const written = []\n for (const [attr, key, value] of metaTags) {\n head = head.replace(new RegExp(`<meta\\\\b[^>]*\\\\b(?:name|property)=[\"']${key}[\"'][^>]*>\\\\s*`, 'gi'), '')\n if (value) written.push(`<meta ${attr}=\"${key}\" content=\"${escapeHtml(value)}\">`)\n }\n head = head.replace(/<link\\b[^>]*\\brel=[\"']canonical[\"'][^>]*>\\s*/gi, '')\n if (meta.canonical) written.push(`<link rel=\"canonical\" href=\"${escapeHtml(meta.canonical)}\">`)\n head = head.replace(/<link\\b[^>]*\\bhreflang=[\"'][^\"']*[\"'][^>]*>\\s*/gi, '')\n for (const { hreflang, url } of meta.alternates || []) {\n written.push(`<link rel=\"alternate\" hreflang=\"${escapeHtml(hreflang)}\" href=\"${escapeHtml(url)}\">`)\n }\n return `${head}${written.join('\\n')}\\n${html.slice(end)}`\n}\n\n// ============================================================================\n// 404 fallback generation\n// ============================================================================\n\n/**\n * Generate 404.html content for static hosting fallback.\n *\n * Serves two purposes on static hosts (GitHub Pages, Cloudflare Pages, etc.):\n * 1. Real 404: pre-rendered custom 404 page content (or blank #root if none defined)\n * 2. Valid dynamic route (e.g. /blog/2): inline script clears #root so SPA renders fresh\n *\n * Flow: static host serves 404.html → inline script runs before React mounts →\n * - dynamic route: clears #root, React renders the page normally\n * - real 404: leaves #root with pre-rendered content, React re-renders same 404 page\n *\n * @param {Object} options\n * @param {string} options.baseHtml - Assembled HTML shell (with site content already injected)\n * @param {Object} options.website - Initialized Website instance (from initPrerender)\n * @param {Object} options.siteContent - Site content object (to find dynamic templates)\n * @returns {{ html: string, hasNotFoundPage: boolean }}\n */\nexport function generate404Html({ baseHtml, website, siteContent }) {\n // Extract patterns for routes that remain as dynamic templates (prerender: false)\n // '/blog/:id' → /^\\/blog\\/([^/]+)$/. Compiled by the shared matcher rather\n // than a second regex built here: this file used to build its own with\n // `:[^/]+`, which disagreed with core's `:(\\w+)` on any param name carrying a\n // non-word character. See @uniweb/core/route-match for the whole story.\n // ⛔ **ROUTE, NOT THE FLAG — a filter must not read absence as \"not parametric\".**\n // This filtered on `p.isDynamic` alone. A producer that marks only the bracket\n // page (or none at all) left every parametric route out of the list, so a cold\n // hit on one showed the 404 body and never cleared #root for the SPA. The flag\n // is a producer's claim; the `:` in the route is the thing itself, and every\n // flagged page has one — so this is a superset and cannot lose a route.\n const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic || isDynamicRoute(p.route)) || []\n const routePatterns = dynamicTemplates.map((p) => routePatternToRegex(p.route).regex.source)\n\n let html = baseHtml\n\n // Pre-render the custom 404 page content into #root (if the site defines one),\n // otherwise inject a default 404 message so the page isn't blank before JS loads\n const notFoundPage = website.getNotFoundPage()\n if (notFoundPage) {\n const notFoundResult = renderPage(notFoundPage, website)\n if (notFoundResult && !notFoundResult.error) {\n html = injectPageContent(html, notFoundResult.renderedContent, notFoundPage, {\n sectionOverrideCSS: notFoundResult.sectionOverrideCSS,\n })\n }\n } else {\n const basePath = website.basePath || ''\n html = html.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${default404Html(basePath)}</div>`\n )\n }\n\n // Inject inline script: if path matches a dynamic route, clear #root before React mounts\n // so the SPA renders the correct page rather than the 404 content\n if (routePatterns.length > 0) {\n const patternList = routePatterns.map((p) => `/${p}/`).join(',')\n // The path is normalized here rather than by making every pattern accept a\n // trailing slash — same rule the matcher applies, applied in one place.\n const dynamicScript =\n `<script>(function(){` +\n `var p=[${patternList}],r=window.location.pathname.replace(/\\\\/+$/,'')||'/';` +\n `if(p.some(function(x){return x.test(r)})){` +\n `var el=document.getElementById('root');if(el)el.innerHTML='';` +\n `}})()</script>`\n html = html.replace('</body>', `${dynamicScript}\\n</body>`)\n }\n\n return { html, hasNotFoundPage: !!notFoundPage }\n}\n","/**\n * Runtime default fetcher.\n *\n * Used as the FetcherDispatcher's terminal fallback when no foundation\n * transport claims the request. Sites that declare no transport at all —\n * starter/docs/marketing templates hitting /data/*.json — ride on this path\n * with zero config, and so does a site a host serves live.\n *\n * ⭐ It speaks exactly TWO lanes, and takes NO site-level vocabulary for a\n * backend of the author's own:\n *\n * - a compiled file — `path:` under the site's base (`/data/<query>.json`,\n * a per-record file), or a plain JSON `url:` the author wrote;\n * - the host's RECORDS SERVICE — `ask:`, one POST per tick carrying every\n * question the page asked, answered per key (the records contract, as this\n * client reads it).\n *\n * A query's set — `scope:` / `where:` / `sort:` / `limit:` — and the `narrow`\n * a fetch takes of it are evaluated HERE, locally, over what the first lane\n * returns — with `@uniweb/core`'s one evaluator (`evaluateQuery`), the same the\n * build uses — and at the source when asked. Nothing decides that per site: the\n * LANE decides.\n *\n * ⛔ RETIRED 2026-09-04 [Diego]: `fetcher.baseUrl`, `headers`, `envelope`,\n * `supports`, `request.style` / `request.rename` and the `json-body`\n * request-style registry. *\"3rd party endpoints must be supported at the\n * foundation level… making the runtime+core lean.\"* A backend with its own\n * base, headers, wire or query language is a TRANSPORT — a named\n * `{ resolve, cacheKey? }` the foundation (or an extension) registers and\n * the site selects per binding key (`as`) in `fetcher.transports`. The build warns once\n * and drops a retired key from the payload, so an author's backend does not\n * silently stop being reached.\n *\n * A request may carry `method: 'POST'` + `body:` for an endpoint that takes a\n * query in a body (GraphQL, a search endpoint) — an external query's, or its\n * `record:` request's — and `transform:`, a dot-path to the records in ONE\n * response. `{paramName}` placeholders in body strings are substituted from\n * `request.dynamicContext`, so a parametric page's record request can reference\n * its route param. ⛔ `envelope` (the object form of `detail:`) is retired with\n * it, 2026-09-13: a record request's own `transform` is `record.transform`.\n *\n * Exported from a subpath — `@uniweb/runtime/default-fetcher` — for\n * runtime-level callers (the editor's preview iframe, custom runtime\n * harnesses). **Foundations should not import this.** A foundation that\n * wants plain URL + JSON behavior simply omits its own transport; the\n * runtime installs this one automatically.\n *\n * Intentional omission: credentials / secrets. Any value the framework puts\n * into the served HTML is public to the browser. Sites needing private\n * credentials use a deployment-layer proxy — the site fetches a same-origin\n * URL, and a layer in front resolves the credential and forwards upstream.\n */\n\nimport {\n substitutePlaceholders,\n evaluateQuery,\n sortRecords,\n sortToWire,\n deriveCacheKey,\n resolveServiceUrl,\n} from '@uniweb/core'\n\n/**\n * @param {Object} [options]\n * @param {string} [options.basePath=''] - Prepended to local absolute paths\n * for subpath deployments. Remote URLs pass through unchanged.\n * @param {boolean} [options.dev=false] - Dev-mode diagnostics: a bad `sort:`\n * throws instead of delivering the records unsorted.\n * @param {Function|null} [options.fetch=null] - The transport. A host executing\n * fetches outside a browser (an SSR isolate) decides how a site-relative\n * address is dispatched — through its own origin or a service binding — and\n * hands that in. Defaults to the global `fetch`, resolved at call time so a\n * test stub installed later is honoured. ⛔ The entries a host calls\n * (`loadPageData`, `prefetchAndHydrate`, `collectSiteRecords`) never reach this\n * default: they refuse a missing transport (`require-transport.js`). It serves\n * the browser and a harness that builds its own fetcher.\n * @returns {{ cacheKey: (req: Object) => string, resolve: (req: Object, ctx: Object) => Promise<{ data, error?, meta? }> }}\n */\nexport function createDefaultFetcher({ basePath = '', dev = false, fetch: fetchImpl = null } = {}) {\n const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init)\n const pathPrefix = basePath && basePath !== '/' ? basePath.replace(/\\/$/, '') : ''\n\n\n // ⭐ THE RECORDS SERVICE — a batch of the misses, one POST, merged per key.\n //\n // The entity store dispatches every config a page needs in one synchronous\n // loop before awaiting any of them, so a question enqueued here and flushed\n // on the next microtask carries every miss of that page in one body. The\n // batch response is never cached as one: each request gets its own answer,\n // keyed by its own question.\n const askQueues = new Map()\n const askRecords = (request, ctx) => {\n // ⛔ A question needs the query's Model ref. A payload that offers the\n // service and carries no `config.queries` entry for the query cannot ask;\n // that is a producer defect and it is said here, per key, with no request.\n if (typeof request.schema !== 'string' || !request.schema) {\n return Promise.resolve({\n data: null,\n error: `the payload offers the records service but carries no Model ref for query ` +\n `\"${request.query ?? request.as}\" (config.queries) — it cannot be asked`,\n })\n }\n return new Promise((resolve) => {\n const url = resolveServiceUrl(request.ask, pathPrefix)\n let queue = askQueues.get(url)\n if (!queue) {\n queue = []\n askQueues.set(url, queue)\n queueMicrotask(() => {\n askQueues.delete(url)\n flushAsked(url, queue, doFetch)\n })\n }\n queue.push({ request, ctx, resolve })\n })\n }\n\n // ⭐ VIEWS OF ONE FILE SHARE ONE READ IN FLIGHT. Each view — its own `scope`,\n // `where`, `sort` or `limit` — is its own cache entry (`deriveCacheKey` hashes\n // the view) and is cut from the read; views asked together (a page's sections,\n // a host's prefetch) share one request. ⛔ Until 2026-09-11 the view was applied\n // before the dispatcher stored the answer under a key that left the view out, so\n // one file had ONE entry and whoever asked first decided what everyone got\n // (measured). A settled read is not kept: a view asked later reads again (a\n // static host's HTTP cache answers it), so dropping an entry and asking again\n // still reaches the source. The read carries no caller's abort signal — several\n // views may be waiting on it, and one navigating away must not cancel it for\n // the others.\n const reads = new Map()\n const readOnce = (target, init) => {\n const key = `${init.method} ${target} ${init.body ?? ''}`\n let pending = reads.get(key)\n if (!pending) {\n pending = readResponse(doFetch, target, init).finally(() => reads.delete(key))\n reads.set(key, pending)\n }\n return pending\n }\n\n return {\n /**\n * The cache identity is the request's ADDRESS and the view it takes of it —\n * or, when asked of the records service, the QUESTION (`deriveCacheKey`).\n * Two pages declaring different `where:` clauses against one path are two\n * entries, each cut from a read of the file (`readOnce`, above).\n */\n cacheKey(request) {\n return deriveCacheKey(request)\n },\n\n async resolve(request, ctx = {}) {\n if (!request) return { data: null }\n if (request.ask) return askRecords(request, ctx)\n const { path, url, transform, body: rawBody } = request\n\n // Normalize method. Only GET and POST are supported by the default\n // fetcher — mutations (PUT/PATCH/DELETE) are a different feature\n // (optimistic updates, action semantics) and don't belong here.\n let method = (request.method || 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'POST') {\n console.warn(`[default-fetcher] method \"${request.method}\" is not supported — falling back to GET.`)\n method = 'GET'\n }\n\n let target\n if (path) {\n // Local file under public/ — basePath applies for subpath deploys.\n target = pathPrefix && path.startsWith('/') && !path.startsWith('//')\n ? pathPrefix + path\n : path\n } else if (url) {\n // A URL the author wrote, sent exactly as written.\n target = url\n } else {\n return { data: [], error: 'No path, url or ask specified' }\n }\n\n const init = { method }\n\n if (method === 'POST') {\n // Substitute {paramName} placeholders in body strings using the\n // dynamic-route context. The helper expects a flat key→value map;\n // build it from dynamicContext's { paramName, paramValue } shape.\n // Strict-brace matcher: GraphQL selection sets pass through unchanged.\n const dc = request.dynamicContext\n const body = (rawBody !== undefined && rawBody !== null && dc && dc.paramName)\n ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false })\n : rawBody\n if (body !== undefined && body !== null) {\n init.headers = { 'Content-Type': 'application/json' }\n init.body = typeof body === 'string' ? body : JSON.stringify(body)\n }\n }\n\n try {\n const response = await readOnce(target, init)\n if (ctx.signal?.aborted) return { data: [], error: 'aborted' }\n\n if (!response.ok) {\n return { data: [], error: `HTTP ${response.status}: ${response.statusText}` }\n }\n\n let data = response.body\n\n // Unwrap the response: the request's own `transform:`, and nothing else — a\n // record request carries its `record.transform`, never the list's.\n if (transform && data !== null && data !== undefined) {\n data = getNestedValue(data, transform)\n }\n\n // Evaluate the query locally. Only applies to array data\n // (filtering/sorting/limiting a single record doesn't make sense).\n // For non-arrays, operators are ignored — the source returned what\n // it returned.\n data = applyOperators(data, request, { dev, locale: sortLocale(request, ctx) })\n\n // ⭐ Say whether WHOLE records were delivered, so the record index can\n // file them — what the config asked for, echoed: briefs when the query\n // has a per-record source, whole records otherwise. (The service reports\n // `whole` per\n // key and overrides this with what it actually served.)\n const whole = typeof request.whole === 'boolean' ? request.whole : undefined\n return whole === undefined ? { data: data ?? [] } : { data: data ?? [], meta: { whole } }\n } catch (error) {\n if (error?.name === 'AbortError') {\n return { data: [], error: 'aborted' }\n }\n return { data: [], error: error?.message || String(error) }\n }\n },\n }\n}\n\n/**\n * One question of a batch, in the records service's own vocabulary\n * (the records contract, §2).\n * ⭐ TWO LEVELS, ruled 2026-09-14 [Diego]:\n *\n * - **the top level is the query as saved** — `schema` (required), `scope` a bare\n * path, `where`, `sort` one key spelled `date` / `-date`, `limit` — and `whole`\n * when the whole record is wanted. Its records are the set, and the top level is\n * the same on every question about it;\n * - **`narrow`** — how this page takes less than the set: `where`, `match` (a\n * parametric page's record), `sort`, `cursor` (to resume a previous answer) and\n * `limit`. Absent when the question asks for the set as it is.\n *\n * ⛔ Until 2026-09-14 the two were one flat question — the page's `where` joined the\n * query's, its `sort` and `limit` replaced the query's — and `match` and `cursor` sat\n * at the top level, where the service no longer takes them. There is no fallback:\n * the service answers an unknown or misplaced field with a `400` for the request.\n *\n * ⭐ A where-object crosses exactly as authored: the evaluator and the service speak\n * one language (`@uniweb/core`'s where evaluator). ⛔ `nin` was respelled `not_in`\n * here until 2026-09-13, when the evaluator took `not_in` as its only spelling.\n * Anything the service does not answer (a retired operator) is sent as written and\n * answered there: never approximated.\n */\n// Exported so the question a parametric page's record is asked with can be generated as test\n// vectors: it is this body, and nothing else composes one.\nexport function toQuestion(request) {\n const q = { schema: request.schema }\n const where = request.where && typeof request.where === 'object' ? request.where : null\n // ⭐ `scope` crosses as authored. ⛔ A top-level `where.path.under` was respelled\n // into it until 2026-09-11, when that form was retired in favour of `scope:`\n // [Diego] — the build refuses it now, so there is nothing left to respell.\n const scope = typeof request.scope === 'string' && request.scope ? request.scope : null\n if (scope) q.scope = scope\n if (where) q.where = where\n const sort = wireSort(request.sort)\n if (sort) q.sort = sort\n if (typeof request.limit === 'number' && request.limit > 0) q.limit = request.limit\n // ⛔ ONLY WHEN TRUE. The brief is the default and absent means the brief, so\n // sending `whole: false` would be noise on every list question the client makes.\n if (request.whole === true) q.whole = true\n const narrow = toNarrow(request.narrow)\n if (narrow) q.narrow = narrow\n // ⛔ `exhaustive` and `maxPages` deliberately do NOT cross — they are client\n // instructions about how many times to ask, never part of what is being asked.\n return q\n}\n\n/**\n * A question's `narrow`, with only the fields the service takes inside it — any other\n * is a `400` for the whole request there — or `null` when nothing is left.\n *\n * - `where` crosses as authored;\n * - `match` — the record of a parametric page: one key and the URL's value, beside\n * the author's `where`, never merged into it (`buildDetailConfig`);\n * - `sort` in the service's spelling;\n * - ⭐ `cursor` — the ONLY field in a question that is not the author's: opaque, from a\n * previous answer's `cursors`, sent back on the same question and nothing else\n * changed. A cursor is a narrowing like the rest, so the top level stays the query as\n * saved on every page;\n * - `limit`, when it cuts.\n */\nfunction toNarrow(narrow) {\n if (!narrow || typeof narrow !== 'object') return null\n const n = {}\n if (narrow.where && typeof narrow.where === 'object') n.where = narrow.where\n if (narrow.match && typeof narrow.match === 'object') n.match = narrow.match\n const sort = wireSort(narrow.sort)\n if (sort) n.sort = sort\n if (typeof narrow.cursor === 'string' && narrow.cursor) n.cursor = narrow.cursor\n if (typeof narrow.limit === 'number' && narrow.limit > 0) n.limit = narrow.limit\n return Object.keys(n).length > 0 ? n : null\n}\n\n/**\n * A `sort` in the service's spelling — `date` / `-date` — or, for one outside the\n * language, the author's text AS WRITTEN, which the service answers by its own rule\n * (ignored: the author's arrangement). ⛔ Never a throw: this runs while a page's whole\n * batch is being composed, before any request, and until 2026-09-14 a sort `parseSort`\n * refuses — two keys, a bad direction — threw here and left every question of the batch\n * pending forever.\n */\nfunction wireSort(sort) {\n try {\n return sortToWire(sort)\n } catch {\n return typeof sort === 'string' && sort.trim() ? sort : null\n }\n}\n\n/**\n * Send one batch to the records service and hand each question its own answer.\n *\n * The response is `{ data, whole?, cursors?, partial? }` (backend's records-query\n * contract rev D, 2026-09-13), each map absent when empty: `data` answers exactly the\n * keys sent — `[]` when nothing matched, and `[]` too for a question an author got\n * wrong, since the service answers a mistake rather than refusing it; `whole[key]` says\n * a key was served as whole records; `cursors[key]` that more records remain past the\n * question's `narrow.limit` — a query's own `limit` defines the set and never produces\n * one; `partial[key]` that a source of the key did not answer. A key\n * missing from `data` is a protocol violation and is reported as an error, never as\n * silence. ⛔ `errors` and `limits` are not read: the service stopped sending them in\n * the same revision, and this client read both until 2026-09-14.\n *\n * ⭐ `cursors` ARE READ — 2026-09-06 [Diego], reversing the ruling that had them\n * \"received and IGNORED, because framework has no paging concept.\" ⛔ **That ruling\n * described our client and was silently wrong about our USERS:** a `cursors` entry is\n * how the service says there is more, and discarding it meant a hosted list could\n * render part of its records with no way for an author to tell that from the end of\n * the data. **The silent class, on a visitor's page.**\n *\n * Two behaviours, deliberately not one:\n *\n * - **a page render REPORTS** — `meta.partial` rides the answer, and nothing pages\n * automatically. Auto-paging here would put unbounded round trips in front of\n * paint for a section that may only show ten rows.\n * - **an exhaustive caller PAGES** — `request.exhaustive` follows `cursors`\n * until the service stops issuing them. `collectSiteRecords` is the caller\n * that wants it (a corpus is not a page), and `maxPages` bounds the loop so\n * a service that always answers with a cursor cannot spin.\n */\n\n/**\n * Pages an exhaustive request follows before stopping and reporting the answer\n * as partial. A caller sets its own with `request.maxPages`.\n *\n * ⚖️ **20 is a bound, not a target** — it is the value a real caller chose for a\n * real per-request budget (a search index, 20 × 100), taken as the default\n * because any bound prevents a runaway and a low one fails visibly rather than\n * expensively. A caller that knows its budget passes its own.\n */\nconst DEFAULT_MAX_PAGES = 20\nasync function flushAsked(url, queue, doFetch) {\n // One shared page loop: the batch is sent, and any entry that asked to be\n // exhaustive and came back with a cursor is re-sent alone until it is done.\n const pending = new Map()\n const body = {}\n const keys = []\n for (const entry of queue) {\n const base = entry.request.as || 'q'\n let key = base\n for (let n = 2; key in body; n += 1) key = `${base}#${n}`\n keys.push(key)\n body[key] = toQuestion(entry.request)\n }\n let parsed\n try {\n const response = await doFetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n // A protocol violation is refused for the WHOLE request with a problem body\n // whose `detail` names the key and the fault (an unknown operator, an empty\n // binding key, a non-BCP-47 locale segment…). Surface that sentence on every\n // key of the batch rather than the bare status: the author reads\n // `block.dataError` and the status alone says nothing they can act on.\n let detail = null\n try {\n const problem = safeParseJSON(await response.text())\n if (problem && typeof problem.detail === 'string' && problem.detail) detail = problem.detail\n } catch { /* an unreadable body falls back to the status line */ }\n const error = detail\n ? `HTTP ${response.status}: ${detail}`\n : `HTTP ${response.status}: ${response.statusText}`\n for (const entry of queue) entry.resolve({ data: null, error })\n return\n }\n parsed = await response.json()\n } catch (error) {\n const message = error?.name === 'AbortError' ? 'aborted' : (error?.message || String(error))\n // ⛔ AN ABORT KEEPS WHAT ARRIVED. On an exhaustive walk every in-flight key\n // fails at once here, so discarding held pages loses the whole corpus rather\n // than one key — the \"rejection that loses every key\" a caller cannot have.\n for (const entry of queue) {\n const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null\n entry.resolve(held\n ? { data: held, error: message, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error: message })\n }\n return\n }\n const data = parsed && typeof parsed.data === 'object' && parsed.data ? parsed.data : {}\n // ⭐ `whole[key]` is present only for keys delivered as WHOLE entities — so a\n // key's ABSENCE is the brief, and a key present that never asked is the\n // brief-less Model saying so. Absent entirely when every key is briefs.\n const wholes = parsed && typeof parsed.whole === 'object' && parsed.whole ? parsed.whole : {}\n const cursors = parsed && typeof parsed.cursors === 'object' && parsed.cursors ? parsed.cursors : {}\n // ⭐ `partial[key]` — `{ source, code, detail }`: a source of that key did not answer,\n // and the key carries what the others returned. Unread, a degraded key looked complete.\n const degraded = parsed && typeof parsed.partial === 'object' && parsed.partial ? parsed.partial : {}\n queue.forEach((entry, i) => {\n const key = keys[i]\n // ⛔ A FAILURE MUST NOT DISCARD PAGES ALREADY COLLECTED. An exhaustive walk\n // that fails on page 7 has six pages in hand, and a caller that asked for a\n // corpus would rather have them marked partial than lose the key — losing it\n // is indistinguishable from \"this query has no records\".\n const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null\n if (!(key in data)) {\n const error = `the records service answered without the key \"${key}\"`\n entry.resolve(held\n ? { data: held, error, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error })\n return\n }\n const whole = typeof wholes[key] === 'boolean'\n ? wholes[key]\n : (typeof entry.request.whole === 'boolean' ? entry.request.whole : undefined)\n\n const cursor = typeof cursors[key] === 'string' && cursors[key] ? cursors[key] : null\n const rows = Array.isArray(data[key]) ? data[key] : data[key]\n\n // An exhaustive caller collects the page and asks for the next one.\n if (cursor && entry.request.exhaustive && Array.isArray(rows)) {\n const acc = entry.collected ? entry.collected.concat(rows) : rows.slice()\n const page = (entry.page || 1) + 1\n const cap = typeof entry.request.maxPages === 'number' && entry.request.maxPages > 0\n ? entry.request.maxPages\n : DEFAULT_MAX_PAGES\n if (page <= cap) {\n pending.set(entry, { cursor, collected: acc, page, whole })\n return\n }\n // The caller's own bound, not the service's: report rather than spin.\n entry.resolve({ data: acc, meta: withMeta({ whole, partial: true, pages: cap }) })\n return\n }\n\n const collected = entry.collected ? entry.collected.concat(Array.isArray(rows) ? rows : []) : rows\n const meta = withMeta({\n whole,\n // ⭐ ONE FLAG, MEANING **NOT THE WHOLE POPULATION** — asked for by name, so\n // a caller has one boolean to branch on rather than several signals to\n // combine. It is set in every case that means it:\n // · a cursor came back and this caller does not page (a page render);\n // · a cursor came back and the caller's `maxPages` stopped the walk;\n // · the walk failed or aborted with pages already in hand;\n // · a source of the key did not answer (`partial[key]`), and `unavailable`\n // carries the service's own account of which one.\n // ⛔ Named `truncated` when it shipped in 0.17.0; renamed the same day, before\n // any consumer adopted it, because \"truncated\" says something was cut and this\n // also means \"there is more you did not ask for\".\n partial: (cursor || key in degraded) ? true : undefined,\n unavailable: key in degraded ? degraded[key] : undefined,\n pages: entry.page && entry.page > 1 ? entry.page : undefined,\n })\n entry.resolve(meta ? { data: collected, meta } : { data: collected })\n })\n\n if (pending.size === 0) return\n // Re-ask each unfinished key on its own — the cursor is per key, so a batch\n // would have to correlate several independent positions through one body.\n // ⭐ The cursor rides inside `narrow` and NOTHING ELSE in the question changes: the\n // service resumes only the question a cursor came from, and answers a cursor sent\n // with anything else changed with a `400`.\n await Promise.all([...pending].map(([entry, state]) => {\n const next = {\n ...entry,\n request: { ...entry.request, narrow: { ...(entry.request.narrow || {}), cursor: state.cursor } },\n collected: state.collected,\n page: state.page,\n }\n return flushAsked(url, [next], doFetch)\n }))\n}\n\n/**\n * Read one response and parse its body — the part of a fetch every view of an\n * address shares. A JSON body is parsed as JSON; anything else is tried as JSON\n * and kept as text when it is not. A failed response keeps its text, so a caller\n * can read a message out of it (the records service's problem details).\n *\n * ⛔ A WHOLE HTML DOCUMENT IS NOT DATA — it is a failed read. A host that serves a site\n * answers an address it holds no file for with the site's page, a `200` of `text/html`,\n * and kept as text that page reached a component as a query's records: the component\n * called `.map` on it and the page fell to its error boundary. Measured 2026-09-26 on a\n * site a local backend serves, `/es/data/team.json` answering with the page. A fragment of\n * HTML is still kept as text; only a document — `<!doctype html>` or `<html>` — is refused.\n */\nasync function readResponse(doFetch, target, init) {\n const response = await doFetch(target, init)\n if (!response.ok) {\n let text = null\n try {\n text = await response.text()\n } catch {\n // Body not readable — the status line is all there is.\n }\n return { ok: false, status: response.status, statusText: response.statusText, text }\n }\n const contentType = response.headers.get('content-type') || ''\n if (contentType.includes('application/json')) return { ok: true, body: await response.json() }\n const text = await response.text()\n try {\n return { ok: true, body: JSON.parse(text) }\n } catch {\n if (HTML_DOCUMENT.test(text)) {\n return { ok: false, status: response.status, statusText: 'an HTML page, not data', text: null }\n }\n return { ok: true, body: text }\n }\n}\n\nconst HTML_DOCUMENT = /^\\s*(?:<!doctype\\s+html|<html[\\s>])/i\n\n/** Build a `meta` from the fields that are actually present, or `undefined`. */\nfunction withMeta(fields) {\n const out = {}\n for (const [k, v] of Object.entries(fields)) if (v !== undefined) out[k] = v\n return Object.keys(out).length ? out : undefined\n}\n\n/**\n * The locale a list's texts are collated in: the page's (ruled 2026-09-14 [Diego]).\n * A request asked in a locale carries it; otherwise the caller's — the website's\n * active locale in the browser, the locale a host's prerender renders.\n */\nfunction sortLocale(request, ctx) {\n if (typeof request?.locale === 'string' && request.locale) return request.locale\n if (typeof ctx?.locale === 'string' && ctx.locale) return ctx.locale\n const active = ctx?.website?.getActiveLocale?.()\n return typeof active === 'string' && active ? active : null\n}\n\n/**\n * Evaluate the query over what the source returned — the ONE evaluator,\n * `@uniweb/core`'s `evaluateQuery`, so the browser orders and filters exactly as\n * the build does: the query's set first (`scope` over the branch a compiled record is\n * held with, `where`, `sort`, `limit`), then the fetch's `narrow` of it. What comes back\n * carries no branch — `evaluateQuery` drops it, so no component receives one. Only the\n * sort is this lane's own, for what it does with a bad `sort:` in production.\n */\nfunction applyOperators(data, request, { dev = false, locale = null } = {}) {\n return evaluateQuery(data, request, {\n locale,\n sort: (items, sortExpr, options) => applySort(items, sortExpr, dev, options.locale),\n })\n}\n\n/**\n * ⛔ This was a second sort implementation until 2026-09-04, and it honoured\n * a comma-separated MULTI-KEY sort the language does not have (single-key by\n * ruling). A bad `sort:` is an authoring error: dev throws so it is seen; in\n * production the records are delivered in source order and the reason is\n * logged once — a wrong order is not worth a broken page for a visitor.\n */\nconst warnedBadSorts = new Set()\nfunction applySort(items, sortExpr, dev, locale) {\n try {\n return sortRecords(items, sortExpr, { locale })\n } catch (err) {\n if (dev) throw err\n const key = String(sortExpr)\n if (!warnedBadSorts.has(key)) {\n warnedBadSorts.add(key)\n console.error(`[default-fetcher] ${err.message} Records delivered unsorted.`)\n }\n return items\n }\n}\n\n/**\n * Walk a dotted path into an object. Missing segments short-circuit to\n * `undefined` so callers can distinguish \"present and empty\" from \"not there.\"\n */\nfunction getNestedValue(obj, path) {\n if (!obj || !path) return obj\n let current = obj\n for (const part of path.split('.')) {\n if (current === null || current === undefined) return undefined\n current = current[part]\n }\n return current\n}\n\n/**\n * JSON.parse that returns `undefined` on failure instead of throwing.\n * Used when we want to probe a response body for an error path but don't\n * want a non-JSON body to surface as a parser exception.\n */\nfunction safeParseJSON(text) {\n try {\n return JSON.parse(text)\n } catch {\n return undefined\n }\n}\n","/**\n * THE HOST'S TRANSPORT, REQUIRED — on every entry a host calls to have the runtime fetch on its\n * behalf: `loadPageData`, `prefetchAndHydrate` and `collectSiteRecords`.\n *\n * ⛔ The failure this prevents is not \"no transport\". `createDefaultFetcher` resolves its transport\n * as `fetchImpl || globalThis.fetch`, so a missing one becomes the ambient network — in an isolate,\n * the isolate's own, outside the host's timeout and byte budget and outside its resolution of\n * site-relative addresses. With an absolute address the request does not even fail: it leaves from\n * the wrong place and comes back `fetched`. A wiring mistake wearing a success.\n *\n * Reachable, not hypothetical: a host measured against a real isolate loader (2026-09-03) that a\n * function passed through an entrypoint's `fetch(Request)` with a JSON body arrives `undefined`,\n * and survives only as an RPC method argument.\n *\n * ⛔ Until 2026-10-25 only `prefetchAndHydrate` refused one, while the entries a host's isolate\n * calls — the data step and the corpus entry — fell back to the ambient network silently.\n *\n * The default stays where it is right: in the browser, which has one network, and in a harness\n * that builds a default fetcher itself. A caller of these entries that wants the ambient fetch\n * passes `fetch: globalThis.fetch`, which is one word and says so.\n *\n * Zero-dependency leaf, so the data step's graph stays renderer-free (`tests/data-step-graph.test.js`).\n *\n * @param {*} fetch - what the caller passed as `fetch`\n * @param {string} entry - the entry's name, for the message\n * @returns {Function} the transport\n */\nexport function requireTransport(fetch, entry) {\n if (typeof fetch === 'function') return fetch\n throw new Error(\n `${entry}: \\`fetch\\` must be a function. A transport does not survive a ` +\n 'JSON-serialized isolate boundary — pass it as an RPC method argument. ' +\n 'To use the ambient fetch deliberately, pass `fetch: globalThis.fetch`.'\n )\n}\n","/**\n * Every block a page's render can reach: its body and the layout areas it uses (the runtime's own\n * layout choice — the page's `layout:`, else the foundation's default, else the site's default set,\n * minus any area the page hides), plus what those blocks hold.\n *\n * ⭐ One walk for the two that ask for a page's data before its render: a host's data step\n * (`load-page-data.js`) and the browser before it shows a page it navigates to (`navigation.js`).\n * So the two cannot disagree about which blocks a page reaches.\n *\n * @param {Object} page - a Page\n * @returns {Object[]} its blocks, each one once\n */\nexport function blocksOf(page) {\n const out = []\n const walk = (list) => {\n for (const block of list || []) {\n if (!block) continue\n out.push(block)\n walk(block.childBlocks)\n walk(block.insets)\n }\n }\n walk(page.getBodyBlocks?.() ?? page.bodyBlocks)\n for (const area of Object.values(page.getLayoutAreas?.() ?? {})) walk(area)\n return out\n}\n","/**\n * A PAGE'S ICONS, IN ITS PRERENDER.\n *\n * An icon is a file. On a page a host renders, nothing had fetched it by the time the page was\n * written, so the page held a blank box in its place until the browser had booted and fetched it:\n * about a second, on a fast connection (measured 2026-10-30). The render is synchronous and reads an\n * icon from `uniweb.iconCache` (`getIconSync`, kit's `<Icon>`), so what a page shows has to be there\n * before it starts, as with its data. Two halves:\n *\n * - The data step asks the host's transport for the icons the page's content names, and keeps them\n * on the graph (`loadPageIcons`).\n * - The render door notes which icons it drew (`whileReadingIcons`), and the page carries exactly\n * those in `__ICON_CACHE__`, which `setup.js` reads before the browser's first render. The browser\n * renders over the prerendered page, so it has to draw what the page drew, or it would blank them.\n *\n * ⛔ NOT AN ENTRY OF THE DATA STEP'S LIST. A host counts that list to decide how long to keep the\n * page: an icon in it would shorten the life of every page holding one, and a failed one could end\n * it. So nothing here is reported. A host carries nothing new and changes nothing.\n *\n * ⚖️ What the walk cannot find: an icon a foundation's own code names, or one in a fetched record.\n * Those stay as they were, the placeholder and then the browser's fetch. So does any icon that fails\n * or arrives late, because a page without an icon is a page that still works.\n *\n * Renderer-free, as the data step's graph is (`tests/data-step-graph.test.js`), and not in the\n * browser's bundle: nothing `setup.js` or `index.jsx` imports reaches it.\n */\nimport { DEFAULT_ICON_BASE, ICON_FAMILY_MAP, iconUrl } from '@uniweb/core/icon-corpus'\n\n/** How long a page waits for its icons. Past this it renders without the ones still on their way. */\nexport const ICON_WAIT_MS = 1000\n\n/** The most a page asks for in one step, so an icon gallery cannot spend a host's budget of requests. */\nexport const MAX_ICON_REQUESTS = 40\n\n/** A reference a link carries as its `icon`: `lu-play` or `lu:play`, as kit's `<Icon>` reads one. */\nfunction parseIconReference(ref) {\n if (typeof ref !== 'string') return null\n const colon = ref.indexOf(':')\n if (colon > 0) return { library: ref.slice(0, colon), name: ref.slice(colon + 1) }\n const dash = ref.indexOf('-')\n if (dash < 2 || dash > 3) return null\n const library = ref.slice(0, dash)\n return ICON_FAMILY_MAP[library] ? { library, name: ref.slice(dash + 1) } : null\n}\n\nfunction visit(node, found) {\n if (!node || typeof node !== 'object') return\n if (node.type === 'image' && node.attrs?.role === 'icon') found.push(node.attrs)\n for (const mark of node.marks || []) {\n const ref = parseIconReference(mark?.attrs?.icon)\n if (ref) found.push(ref)\n }\n if (Array.isArray(node.content)) for (const child of node.content) visit(child, found)\n}\n\n/**\n * The icons the content of these blocks names, each once.\n *\n * Keyed as `<Icon>` asks for one, by the library and name as written, since that is what\n * `getIconSync` looks up. The address uses the family's code and the corpus's spelling of the name.\n *\n * @param {Object[]} blocks\n * @returns {Map<string, { family: string, name: string }>}\n */\nexport function pageIcons(blocks) {\n const named = []\n for (const block of blocks) visit(block.rawContent?.doc ?? block.rawContent, named)\n const icons = new Map()\n for (const { library, name } of named) {\n const family = ICON_FAMILY_MAP[String(library).toLowerCase()]\n if (family && name) icons.set(`${library}:${name}`, { family, name })\n }\n return icons\n}\n\n/**\n * Fetch the icons these blocks name through the host's transport, into the graph's icon cache. One a\n * warm graph holds is not asked for again. Resolves when they are all in, or at `ICON_WAIT_MS`.\n *\n * @param {Object} options\n * @param {Object[]} options.blocks - the page's blocks (`blocksOf`)\n * @param {Object} options.website - the Website, whose `config.icons` says where the icons are\n * @param {Function} options.fetch - the transport\n * @param {Map<string, string>} options.cache - the graph's `iconCache`\n * @returns {Promise<void>}\n */\nexport async function loadPageIcons({ blocks, website, fetch, cache }) {\n const config = website.config?.icons\n if (config?.cdn === false) return\n const base = config?.base || DEFAULT_ICON_BASE\n const wanted = [...pageIcons(blocks)].filter(([key]) => !cache.has(key)).slice(0, MAX_ICON_REQUESTS)\n if (wanted.length === 0) return\n\n const all = Promise.all(wanted.map(async ([key, { family, name }]) => {\n try {\n const response = await fetch(iconUrl(family, name, base), { method: 'GET' })\n if (!response.ok) return\n const svg = await response.text()\n if (/<svg[\\s>]/i.test(svg)) cache.set(key, svg)\n } catch {\n // The transport may refuse an address. The icon stays on the browser's path.\n }\n }))\n let timer\n const waited = new Promise((resolve) => { timer = setTimeout(resolve, ICON_WAIT_MS) })\n await Promise.race([all, waited])\n clearTimeout(timer)\n}\n\n/**\n * Run a render and collect the icons it drew, so the page can carry exactly those. On a graph reused\n * across pages that can include one this page's walk did not find, which is why this reads the\n * render rather than the walk: whatever the HTML shows, the browser's first render can show too.\n *\n * The render is synchronous, so no other request can read through this while it runs.\n *\n * @param {Object|undefined} uniweb - what `globalThis.uniweb` names for this render\n * @param {Map<string, string>} read - filled with each icon drawn\n * @param {Function} draw - the render\n * @returns {*} what `draw` returned\n */\nexport function whileReadingIcons(uniweb, read, draw) {\n const cache = uniweb?.iconCache\n if (!cache) return draw()\n const { get } = cache\n cache.get = (key) => {\n const svg = get.call(cache, key)\n if (svg) read.set(key, svg)\n return svg\n }\n try {\n return draw()\n } finally {\n delete cache.get\n }\n}\n","/**\n * THE RENDER'S DATA STEP — the page a visit starts on, fetched by the rule that renders it.\n *\n * ⭐ **Why a step at all.** The server render is synchronous: while it renders, a section reads its\n * data from the cache and never waits for a request (`EntityStore.resolve`). So whatever a page\n * shows has to be there before the render starts. That is this, and nothing else: it is not an\n * optimization, it is how a server-rendered page has content.\n *\n * ⭐ **Why it runs the render's own rule.** It asks, block by block, exactly what the render will\n * look for — `EntityStore.fetch`, the same plan `resolve` reads back (`@uniweb/core/page-data`) —\n * through a dispatcher built from the site's own foundation, so a transport the site chose answers\n * here too. ⛔ Until 2026-09-19 a host's prefetch worked this out from the payload alone: it could\n * not know which keys a component declares, which layout the page draws, or which transport a key\n * is routed to, so it asked for every fetch on every level and still got four cases wrong.\n * A payload cannot answer those; the graph the\n * isolate is about to render with can.\n *\n * ⚖️ **What it still over-asks.** The blocks are the page's — body, the layout areas it uses, their\n * child blocks and insets. Whether a component actually draws its children, or a layout its area,\n * is known only by rendering; a block that declares keys and is never drawn is asked for anyway.\n * Bounded by what such a block declares, and cheaper than rendering the page twice.\n *\n * @module\n */\nimport DataStore, { deriveCacheKey } from '@uniweb/core/datastore'\nimport { resolveRequestedPage } from './resolve-page.js'\nimport { getComponentMeta } from './prepare-props.js'\nimport { createDefaultFetcher } from './default-fetcher.js'\nimport { requireTransport } from './require-transport.js'\nimport { blocksOf } from './page-blocks.js'\nimport { loadPageIcons } from './page-icons.js'\n\n/**\n * Fetch what one route's render will read.\n *\n * @param {Object} options\n * @param {Object} options.website - the initialized Website the render will use (`initPrerender`)\n * @param {string|Object} options.route - the route, or an already-resolved Page. A route that names\n * no page, or a page served from elsewhere (`rewrite:`), asks for the site's not-found page's\n * data, the page its render draws.\n * @param {Function} options.fetch - the transport, per request. Required: a missing one is refused,\n * never replaced by the ambient `fetch` (`require-transport.js`)\n * @param {boolean} [options.dev]\n * @param {string|null} [options.locale] - ⛔ NOT a locale to fetch in: the Website is already\n * locale-sliced (`initPrerenderForLocale`) and every address is resolved in ITS locale. Passing\n * one that disagrees throws, because the alternative is a page prerendered in one language with\n * the data of another, and nothing to see it.\n * @param {'always'|'author'} [options.prerender] - `'always'` asks for everything, which is what an\n * isolate rendering a visit's first page wants; `'author'` honours `prerender: false`.\n * @param {Object} [options.cache] - a `DataStore` to answer from and fill, shared across calls.\n * ⭐ For a caller that runs the step for MANY pages of one site — a static build — where every\n * page asks for the site's and its parent's data and reading each address once is the difference\n * between N reads and N×M. ⛔ Not for a host serving requests: there a page's data is asked\n * fresh, and the default (a cache per call) is what does that. A shared cache changes nothing\n * about what is ASKED or returned — every request is still recorded, hit or miss, and whether a\n * record is held whole already is answered from this call's own requests, never the cache's.\n * @param {boolean} [options.icons] - also ask for the icons the page's content names, and keep them on\n * the graph for the render (`page-icons.js`). They are not entries of the list this returns. A\n * caller that has its own answer for icons, as a static build does, says `false`.\n * @returns {Promise<Array<{ config: Object, outcome: 'fetched'|'failed'|'skipped', data: *, meta?: Object, error?: string }>>}\n * one entry per request the render will make, keyed downstream by `deriveCacheKey(config)`.\n * ⛔ The page's data and nothing else: a host counts this list to decide how long to keep the\n * page, so one more entry shortens its life, and one that fails can end it.\n */\nexport async function loadPageData({ website, route, fetch, dev = false, locale = null, prerender = 'always', cache = null, icons = true }) {\n if (prerender !== 'author' && prerender !== 'always') {\n throw new Error(`loadPageData: prerender must be 'author' or 'always', got ${JSON.stringify(prerender)}`)\n }\n // Before the route is resolved: a lost transport is the caller's wiring, and an unknown route\n // must not hide it.\n requireTransport(fetch, 'loadPageData')\n const active = website?.getActiveLocale?.() ?? null\n if (locale && active && locale !== active) {\n throw new Error(\n `loadPageData: this Website renders ${JSON.stringify(active)} and the caller asked for ` +\n `${JSON.stringify(locale)}. A Website is locale-sliced — build one per locale ` +\n '(`initPrerenderForLocale`) rather than asking this step for another language.'\n )\n }\n // ⭐ This graph is the one `globalThis.uniweb` names while the step reads it, which it does here,\n // before its first `await`: each block's component meta and the layout the page draws come\n // through the global, and a host holding several graphs in one isolate has it naming the newest\n // (`Website#makeCurrent`).\n website.makeCurrent()\n const iconCache = globalThis.uniweb?.iconCache\n const named = typeof route === 'string' ? resolveRequestedPage(website, route) : route\n // ⭐ A route that names no page, or a page served from elsewhere (`resolveRequestedPage`), renders\n // the site's not-found page (`createPageRenderer`), so it is that page's data the render reads.\n // With no such page: a genuine 404, the caller's to turn into one, and nothing to fetch.\n const resolved = named ?? (typeof route === 'string' ? website.getNotFoundPage() : null)\n if (!resolved) return []\n // ⭐ The same promotion the render applies: a content-less folder renders its designated child,\n // and it is that child's data the page needs.\n const page = typeof resolved.getRenderableSelf === 'function' ? resolved.getRenderableSelf() : resolved\n\n // ⛔ A real Page, not page data. `createPageRenderer` tolerates plain data because rendering it\n // still produces a page; here the same tolerance would walk raw sections, find no blocks and no\n // graph behind them, and fetch NOTHING — a clean run, an empty page, no error anywhere. That is\n // the failure this whole step exists to end, so it is refused.\n if (typeof page.getBodyBlocks !== 'function') {\n throw new Error('loadPageData: `route` must be a route or a Page from this Website — page data is not enough to say what its render reads.')\n }\n\n // A cache of this call's own unless the caller shares one: the graph's belongs to whatever it is\n // rendering, and a page's data is asked fresh per request. The site's transports still apply\n // (`dispatcherFor`).\n const dispatcher = website.dispatcherFor({\n dataStore: cache ?? new DataStore(),\n defaultFetcher: createDefaultFetcher({ basePath: website.config?.base || '', dev, fetch }),\n })\n\n /** One entry per request, in the order they were first asked for. */\n const entries = new Map()\n const record = (request, entry) => {\n const key = deriveCacheKey(request)\n if (!entries.has(key)) entries.set(key, entry)\n }\n\n // ⭐ WHAT THIS CALL'S OWN REQUESTS ANSWERED — the record index the plan asks \"held whole\n // already?\" before it asks for a record (R1). ⛔ Not the cache's: a shared one holds the records\n // other pages fetched, and a record found there was never asked for, so never recorded, and the\n // page carried nothing for it (2026-10-25). With a cache of this call's own, the two hold the\n // same records.\n const answered = new DataStore()\n\n const recording = {\n // ⭐ What the plan uses without asking (`keyProgram`'s held steps) is what THIS call's own\n // requests answered, as `peekRecord` is: the page carries every answer its render reads, so\n // it may use nothing it did not ask for itself — not another page's answer in a cache the\n // static build shares.\n peek: (request) => answered.get(deriveCacheKey(request)),\n peekRecord: (id) => answered.getRecord(id),\n dispatch: async (request, ctx) => {\n if (prerender === 'author' && request?.prerender === false) {\n // The author deferred this one to the browser and the caller honours that. Present, so a\n // host can count what was declared against what was tried; not fetched, not hydrated.\n record(request, { config: request, outcome: 'skipped', data: null })\n return { data: null }\n }\n const result = await dispatcher.dispatch(request, ctx)\n if (!result?.error && result?.data != null) {\n answered.set(deriveCacheKey(request), result.meta ? { data: result.data, meta: result.meta } : { data: result.data })\n }\n record(request, result?.error\n ? { config: request, outcome: 'failed', data: null, error: result.error }\n : { config: request, outcome: 'fetched', data: result?.data ?? null, ...(result?.meta ? { meta: result.meta } : {}) })\n return result\n },\n }\n\n const store = website.entityStore\n const blocks = blocksOf(page)\n // ⭐ Every block's first request goes out in the same tick, which is what lets a question door\n // answer a page in one POST. A step that depends on an answer takes a later one, and only its\n // own key waits.\n await Promise.all([\n // The page's own record: asked whether or not a section declares the route key, because the\n // page names itself from it and a URL naming no record is a 404.\n store.fetchPageRecord(page, { dispatcher: recording }),\n ...blocks.map((block) => store.fetch(block, getComponentMeta(block.type), { dispatcher: recording })),\n icons && iconCache && loadPageIcons({ blocks, website, fetch, cache: iconCache }),\n ])\n\n return [...entries.values()]\n}\n","/**\n * The composed render entry — resolve a route, render it, inject it into a shell.\n *\n * ⭐ **This exists because framework had two callers of one unshared sequence, not\n * because a consumer asked.** `@uniweb/runtime/ssr` exported every step of the\n * per-page render and never the sequence, so each host assembled it:\n *\n * - `@uniweb/build`'s `prerender.js` — `renderPage` → classify → `injectPageContent`,\n * once per page in its loop.\n * - an SSR isolate prerendering the page a visit starts on (the SPA renders every\n * page after it) — `resolvePage` → `renderPage` → `injectPageContent`, and it wrote\n * the route lookup three times in three files before `resolvePage` was exported at\n * all (see that function's header).\n *\n * Two unlike callers is what makes the interface honest: a build bakes files and an\n * isolate answers a request, so anything only one of them needs stayed out.\n *\n * ⛔ WHAT IS DELIBERATELY NOT IN HERE, and the boundary is the point:\n *\n * - **Shell assembly.** The shell arrives built. The import map, the CDN base and\n * cache headers are host layout, and a runtime that assembled them would be\n * modelling a deployment it cannot see (the host drew this line itself,\n * 2026-09-03: a serve location is read from the payload, never constructed here).\n * - **Init and the data step.** The two lanes differ REALLY here, not incidentally: a\n * build initializes once per locale and runs the data step for each page over one\n * cache for the whole run; an isolate initializes per locale and runs the step fresh\n * for each request. Folding either in would fit one caller and lie to the other.\n * `initPrerender` / `initPrerenderForLocale` / `loadPageData` / `hydrateDataStore`\n * stay separate exports, and `prefetchAndHydrate` below is the isolate's two-step,\n * not a general one. ⭐ What both lanes DO share is the last step: the render reads\n * exactly the entries its page carries. That is `render`'s `fetchedData`, since\n * 2026-10-25, and both lanes pass it.\n * - **Anything build-only.** `injectBuildData` stays in the build lane; it is the\n * other half of the head seam and has its own parity guard.\n */\nimport { renderPage, classifyRenderError, injectPageContent } from './ssr-renderer.js'\nimport { resolveRequestedPage } from './resolve-page.js'\nimport { hydrateDataStore } from './wire-foundation.js'\nimport { loadPageData } from './load-page-data.js'\nimport { requireTransport } from './require-transport.js'\nimport { restoreStartPage } from './start-page.js'\nimport { whileReadingIcons } from './page-icons.js'\n\n/**\n * A renderer bound to one initialized Website and one shell.\n *\n * Create it once per locale (the Website is already locale-sliced by\n * `initPrerenderForLocale`) and call `render` per route or per page.\n *\n * @param {Object} opts\n * @param {Object} opts.website - `uniweb.activeWebsite`, already initialized\n * @param {string} opts.shell - the HTML shell to inject into, taken as given\n * @returns {{ website: Object, render: Function }}\n */\nexport function createPageRenderer({ website, shell }) {\n if (!website) throw new Error('createPageRenderer: `website` is required')\n if (typeof shell !== 'string') throw new Error('createPageRenderer: `shell` must be an HTML string')\n\n /**\n * Render one page.\n *\n * ⭐ Returns an OUTCOME rather than throwing or returning a bare string, because\n * the two callers branch differently on the same three cases and neither wants an\n * exception: a build logs and keeps going so one broken section cannot fail a whole\n * site, an isolate decides a status code and a cache policy. Same reasoning as the\n * per-entry outcome `loadPageData` returns.\n *\n * @param {string|Object} target - a route (`/blog/1`, resolved through the same\n * matcher the browser uses, so a dynamic route works) or an already-resolved\n * Page, which the build lane already holds from its own loop. A route that names\n * no page renders the site's not-found page (`website.getNotFoundPage()`), as the\n * browser does for it, and so does a route naming a page served from elsewhere\n * (`rewrite:`): a request for it that reaches a renderer was not proxied, and the\n * browser's first load of it draws the not-found page too.\n * @param {Object} [options]\n * @param {Object} [options.inject] - extra options forwarded to `injectPageContent`\n * @param {Array<{config: Object, data: *, meta?: Object}>} [options.fetchedData] - the entries\n * the page carries: the `content.fetchedData` its HTML embeds, which the browser boots with.\n * ⭐ Given, the render reads exactly these. The Website forgets what it held, answers and the\n * parametric pages made from them (`Website#forgetData`), and files these, all in this\n * synchronous call, so a Website reused across requests renders each against its own and\n * no other request runs in between. Absent, the render reads whatever the store holds.\n * @returns {{ outcome: 'rendered'|'notFound'|'failed', html: string|null,\n * page: Object|null, error: {type: string, message: string}|null }}\n * `notFound` means the URL names nothing, and `html` says whether there is a page to\n * send for it. Send `html` with your not-found status when it is set, and the shell\n * when it is not:\n * - `page` set, `html` set — the site's not-found page, for a route that names no page\n * or a page served from elsewhere; or a page whose record does not exist, rendered in\n * its own not-found state.\n * - `page: null`, `html: null` — no route matched, or it names a page served from\n * elsewhere, and the site has no not-found page. The browser shows its default\n * not-found message.\n * - `page` set, `html: null`, `error` set — that page broke while rendering; `error`\n * says what broke.\n */\n function render(target, { inject = {}, fetchedData } = {}) {\n if (fetchedData !== undefined && !Array.isArray(fetchedData)) {\n throw new Error('createPageRenderer: `fetchedData` must be the array of entries the page carries')\n }\n // ⭐ FIRST, THIS GRAPH IS THE ONE `globalThis.uniweb` NAMES. A block's component, the props\n // pipeline and kit in the foundation reach the site through the global, and every graph built\n // since this one was points it elsewhere — a host holds several in one isolate. The render is\n // synchronous, so the value set here is the one it reads throughout (`Website#makeCurrent`).\n website.makeCurrent()\n // ⭐ BEFORE THE ROUTE IS RESOLVED: a parametric page is made from the store when it is first\n // resolved, so resolving first would make it from what an earlier request left there.\n // ⛔ Until 2026-10-25 a host that reused one Website hydrated each request's answers on top of\n // every earlier request's, and a section that reads the store itself (kit's `useFetched`)\n // could render answers its page did not carry, so the browser's first render differed.\n if (fetchedData !== undefined) {\n website.forgetData()\n hydrateDataStore(website, fetchedData)\n }\n const named = typeof target === 'string' ? resolveRequestedPage(website, target) : target\n // ⭐ A ROUTE THAT NAMES NO PAGE RENDERS THE SITE'S NOT-FOUND PAGE, as the browser does for\n // it (`PageRenderer.jsx`), and the data step asks for that page's data (`loadPageData`). No\n // route names it (`/404` is never routable), so this is the only way a route reaches it.\n // A route naming a page served from elsewhere is answered the same way: the host did not\n // proxy it (`resolveRequestedPage`).\n // ⛔ Until 2026-10-26 this answered `notFound` with nothing to send, and a host that wanted\n // the site's page had to fetch its data and render it as a Page itself.\n const resolved = named ?? (typeof target === 'string' ? website.getNotFoundPage() : null)\n\n // ⛔ Not an error: nothing matched and the site has no not-found page — a genuine 404,\n // and the caller's to turn into one: a build skips it, an isolate sends the shell with a\n // 404 status. Returning `failed` here would make those indistinguishable.\n if (!resolved) return { outcome: 'notFound', html: null, page: null, error: null }\n\n // ⭐ **THE INDEX-CHILD PROMOTION APPLIES TO BOTH TARGET FORMS** — ruled\n // 2026-09-12 [Diego]. A content-less folder with a designated `isIndex` child\n // renders that child. `Website#getPage` already did this for a ROUTE on its\n // exact-match branches, and nothing did it for an already-resolved Page, so the\n // same page rendered differently depending on which form the caller happened to\n // hold — silently, with no error on either path. `getRenderableSelf` is the one\n // rule for it and now has exactly one caller: here.\n // ⚠️ Guarded on the method rather than assumed: the documented target is a Page,\n // but a caller handing in plain page data should degrade to \"render what I gave\n // you\", not throw.\n const page = typeof resolved.getRenderableSelf === 'function' ? resolved.getRenderableSelf() : resolved\n\n // ⭐ A PAGE CAN RESOLVE AND STILL NAME NOTHING: a parametric page whose record does\n // not exist. Core marks it `notFound` only when the data answered definitively\n // (`Website#_createDynamicPage`), never on a failed or missing fetch. It renders the\n // page's own not-found state — a record miss is not a route miss — and the outcome\n // says `notFound` all the same, so a caller keying its status on the outcome sends a\n // not-found response without reading anything new. The site's not-found page, rendered\n // for a route that names no page, is `notFound` the same way. Known before rendering, so\n // it holds even when that render breaks. ⛔ Until 2026-09-30 this was `rendered`, and a\n // host that answered by the outcome sent 200 for a URL naming no record.\n const missing = !named || page?.notFound === true\n\n // ⭐ EVERY RENDER STARTS FROM THE ACTIVE PAGE THE WEBSITE STARTED WITH (`start-page.js`),\n // then makes active the page the route names, as the browser's boot does (`index.jsx`).\n // `renderPage` then makes the page it renders active by its route, and the not-found page\n // has none: it renders with the page a route served from elsewhere names, as in the browser,\n // or with the start page. ⛔ Until 2026-10-26 it rendered with whatever page this Website\n // rendered last, so on a Website reused across requests its header could show the previous\n // request's page.\n restoreStartPage(website)\n if (typeof target === 'string') website.setActivePage(target)\n // ⭐ The page carries the icons it drew, so the browser's first render draws them too.\n const icons = new Map()\n let result\n try {\n result = whileReadingIcons(globalThis.uniweb, icons, () => renderPage(page, website))\n } catch (err) {\n // `renderPage` handles its own errors, but a foundation can throw from\n // module scope in ways it does not catch. Classify rather than propagate,\n // so one page cannot take down a build loop or an isolate's request.\n return { outcome: missing ? 'notFound' : 'failed', html: null, page, error: classifyRenderError(err) }\n }\n\n if (result.error) return { outcome: missing ? 'notFound' : 'failed', html: null, page, error: result.error }\n\n // ⛔ `sectionOverrideCSS` LAST, so a caller's `inject` cannot displace it. It is\n // computed by `renderPage` for this page — theme pinning and component vars —\n // and a caller passing a same-named key would silently drop it, rendering a page\n // that looks fine and is unstyled in exactly the places the author pinned. That\n // is the empty-success shape this module keeps refusing elsewhere; the spread was\n // the other way round for one commit.\n const html = injectPageContent(shell, result.renderedContent, page, {\n ...inject,\n sectionOverrideCSS: result.sectionOverrideCSS,\n icons,\n })\n return { outcome: missing ? 'notFound' : 'rendered', html, page, error: null }\n }\n\n return { website, render }\n}\n\n/**\n * Prefetch a route's data and hydrate it onto the graph — the isolate's two-step.\n *\n * ⭐ Its whole purpose is that a host stops assembling our structure by hand. It\n * returns the prefetch outcomes rather than swallowing them, because a host reads\n * them to tell \"nothing was tried\" from \"everything tried failed\", which is a\n * different cache decision.\n *\n * ⭐ **Since 2026-09-19 it asks what the RENDER will read, block by block** (`loadPageData`),\n * on the Website it is handed: the keys each component declares, the layout the page actually\n * draws, and the transport the site chose for a key. `content` is no longer read — the graph\n * carries everything — and stays in the signature for callers that pass it.\n * ⛔ `prefetchPageData`, which it replaced, worked from the payload alone and could know none of\n * those: it asked for every fetch on every level and still got four cases wrong. It was removed\n * on 2026-09-20.\n *\n * ⛔ The build lane does NOT call this: it runs `loadPageData` itself for each page, with one\n * cache for the whole run (which this entry does not take) and `prerender: 'author'`, and keeps\n * the entries to embed in that page's HTML. That difference is why this is a named isolate\n * helper and not a step inside `render`.\n *\n * @returns {Promise<Array<{config: Object, outcome: string, data: any, error?: string}>>}\n */\nexport async function prefetchAndHydrate({ website, content, route, locale = null, fetch = null, dev = false, prerender = 'always' }) {\n // ⛔ Guarded for the same reason `createPageRenderer` is, and it was not for one\n // commit. `hydrateDataStore` no-ops on a graph with no `dataStore`, so a caller\n // passing the wrong object gets a successful-looking prefetch, an unhydrated graph\n // and a page that renders empty — no error anywhere. Fail where the mistake is.\n if (!website?.dataStore) {\n throw new Error('prefetchAndHydrate: `website` must be an initialized Website with a dataStore')\n }\n\n // ⛔ THE TRANSPORT IS REQUIRED, as on every entry a host calls to have the runtime fetch for\n // it: one that did not cross the isolate boundary is refused here, never replaced by the\n // isolate's own network (`require-transport.js`, which records the measurement). Checked here\n // as well as in `loadPageData` so the message names the entry the caller called.\n requireTransport(fetch, 'prefetchAndHydrate')\n // ⛔ `locale` is forwarded to be CHECKED, not applied: the Website is locale-sliced already, so a\n // caller whose two disagree gets an error rather than a page prerendered in one language with the\n // data of another.\n const fetched = await loadPageData({ website, route, fetch, dev, locale, prerender })\n hydrateDataStore(website, fetched)\n return fetched\n}\n","/**\n * Every record a site's declared queries return — for a host building something\n * over the whole corpus rather than rendering one page.\n *\n * ## Why this is framework's and not the caller's\n *\n * A host that indexes a site has to ask the same questions the site's pages ask.\n * ⛔ **If it composes its own, the index and the page can disagree**, and the way\n * they disagree is the worst one available: the index offers a result whose page\n * then renders empty. That is the two-answers-to-one-question failure the GET\n * lane was retired for, reintroduced one lane over.\n *\n * ⇒ So the composition is not duplicated here either. This walks the site's\n * `config.queries`, hands each one to `resolveFetchConfigs` — **the same rule a\n * page render uses**, applying each saved query's own `scope` and `where` — and\n * asks through the same client.\n *\n * ## ⭐ Each query is asked AS SAVED — its `limit` included\n *\n * A query defines its SET: the records its `scope` and `where` select, in its\n * `sort`, up to its `limit` (ruled 2026-09-14 [Diego]). A parametric page has a URL\n * for each record of its route query's set and no other, so the set is exactly the\n * population a site's pages reach — a field note older than a query's 100 most recent\n * has no page to index. Asked with no `narrow`, the service answers the set whole in\n * one answer, so a walk has nothing to page; it still follows a cursor if one ever\n * comes back, bounded by `maxPages`.\n *\n * ⛔ **Until 2026-09-14 this dropped the query's `limit`**, on the rule it replaced — a\n * `limit` was the list page's presentation and every record matching `scope` + `where`\n * had a page — which was right under that rule. ⚠️ And that rule's own history: the\n * first version, in 0.17.0, passed `limit` through while claiming the corpus was the\n * population detail pages reach, and the consumer found it (2026-09-06).\n *\n * ## What the caller supplies\n *\n * ⭐ **The transport, and nothing else.** A host outside a browser decides how a\n * site-relative address is dispatched — through its own origin, a service\n * binding, a forwarding table that turns `/_…` into an upstream — and hands that\n * in as `fetch`. **Framework decides what to ask and how to read the answer;\n * the host decides how the bytes travel.** That split is the whole of the\n * boundary here.\n *\n * ⛔ **A site with no records service is not an error.** It has no live records,\n * so this returns empty rather than throwing: a static site's corpus comes from\n * its own compiled artifacts, which the caller already has.\n */\n\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { resolveRecordsService } from '@uniweb/core/records-service'\nimport { createDefaultFetcher } from './default-fetcher.js'\nimport { requireTransport } from './require-transport.js'\n\n/**\n * Ask a site's records service for every record its declared queries return.\n *\n * @param {Object} content - the render payload (`config.services`, `config.queries`)\n * @param {Object} options\n * @param {string} options.locale - the locale to ask in; it is a route segment\n * of the service's address, so there is no asking without one\n * @param {Function} options.fetch - the transport, `(url, init) => Response`. Required: a missing\n * one is refused, never replaced by the ambient `fetch` (`require-transport.js`)\n * @param {AbortSignal} [options.signal]\n * @param {string[]} [options.only] - restrict to these query names\n * @param {boolean} [options.whole=false] - what to ask for. `false` is the brief a\n * list shows; **`true` — whole records — is what an index wants**: a brief index\n * cannot match body text the record's own detail page displays, and a reader who\n * finds a word on the page and not in search meets the inconsistency two\n * rankings would produce. The cost is the caller's, and bounded by each query's\n * own `limit`. An external query is never collected — it has no records service\n * to ask.\n * @param {number} [options.maxPages] - the caller's own bound on a walk that pages. A\n * question with no `narrow` is answered whole with no cursor, so today's walk is one\n * request per query; the bound holds should a service page anyway.\n * @returns {Promise<{records: Object, errors: Object|null, meta: Object}>}\n * `records` is keyed by query NAME, each a flat array; `errors` is keyed the\n * same and is null when nothing failed; `meta[name]`, when there is anything to\n * say, carries `{ whole?, pages?, partial?, unavailable? }`.\n *\n * ⭐ **`partial` means NOT THE WHOLE POPULATION**, and a key can be in BOTH\n * `records` and `errors`: a walk that failed or was aborted with pages already\n * in hand keeps them, marked. ⛔ Losing them would be indistinguishable from\n * \"this query has no records\", and on an abort every in-flight key fails at\n * once — so discarding would lose the corpus, not a key.\n */\nexport async function collectSiteRecords(\n content,\n { locale, fetch, signal, only = null, whole = false, maxPages } = {},\n) {\n // ⛔ Before the empties below: those are states of a site, while a lost transport is the caller's\n // wiring, and a site with no live records must not hide it.\n requireTransport(fetch, 'collectSiteRecords')\n\n const config = content?.config\n const services = config?.services ?? null\n const queries = config?.queries ?? null\n\n // No service, no queries, or no locale to ask in ⇒ nothing to collect. Each is\n // an ordinary state of a site, not a fault: the caller reads its own artifacts.\n if (!queries || typeof queries !== 'object') return empty()\n if (!resolveRecordsService(services, locale)) return empty()\n\n const names = Object.keys(queries).filter((n) => (only ? only.includes(n) : true))\n if (names.length === 0) return empty()\n\n // One synthetic source per query — `as` is the query's own name, so the answer\n // comes back under the name the caller asked by.\n const configs = resolveFetchConfigs(\n names.map((name) => ({ query: name, as: name })),\n { services, queries, locale, defaultLocale: config?.defaultLanguage ?? locale },\n )\n\n const fetcher = createDefaultFetcher({ fetch, basePath: config?.base ?? '' })\n const records = {}\n const errors = {}\n const meta = {}\n\n await Promise.all([...configs].map(async ([name, cfg]) => {\n // ⛔ Only a config the service answers. A query that resolved to a compiled\n // `path` has no live lane, and reading that file is the caller's business,\n // not ours — it is in the site's own URL space and they already serve it.\n if (!cfg.ask) return\n // The query as saved — the set its pages reach, its `limit` included; see the header.\n const asked = { ...cfg, whole, exhaustive: true }\n if (typeof maxPages === 'number' && maxPages > 0) asked.maxPages = maxPages\n\n const result = await fetcher.resolve(asked, { signal })\n if (result?.error) errors[name] = result.error\n // ⭐ Data and an error are not exclusive: a partial walk reports both, and\n // the caller decides whether partial is usable. Only a walk that collected\n // nothing leaves the key out of `records` entirely.\n if (Array.isArray(result?.data)) records[name] = result.data\n else if (!result?.error) records[name] = []\n if (result?.meta) meta[name] = result.meta\n }))\n\n return { records, errors: Object.keys(errors).length ? errors : null, meta }\n}\n\nfunction empty() {\n return { records: {}, errors: null, meta: {} }\n}\n"],"names":["text","fetch","deriveCacheKey"],"mappings":";;;;;;;;;;;;;;;;;;;AAiBA,SAAS,uBAAuB,MAAM;AACpC,SAAO;AAAA,IACL,OAAO,KAAK,SAAS;AAAA,IACrB,UAAU,KAAK,YAAY;AAAA,IAC3B,UAAU,KAAK,YAAY;AAAA,IAC3B,YAAY,KAAK,cAAc,CAAA;AAAA,IAC/B,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA;AAAA;AAAA,IAGvB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,UAAU,KAAK,YAAY,CAAA;AAAA,IAC3B,SAAS,KAAK,WAAW,CAAA;AAAA,IACzB,MAAM,KAAK,QAAQ,CAAA;AAAA,IACnB,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,WAAW,KAAK,aAAa,CAAA;AAAA,IAC7B,OAAO,KAAK,SAAS,CAAA;AAAA,IACrB,QAAQ,KAAK,UAAU,CAAA;AAAA,IACvB,UAAU,KAAK,YAAY,CAAA;AAAA,IAC3B,GAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,EAAE,MAAM,KAAK,KAAI,IAAK;IAC1D,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,EAAE,QAAQ,KAAK,OAAM,IAAK;EACtE;AACA;AASO,SAAS,0BAA0B,eAAe;AACvD,QAAM,UAAU,iBAAiB,CAAA;AAEjC,SAAO;AAAA;AAAA,IAEL,OAAO,QAAQ,SAAS;AAAA,IACxB,UAAU,QAAQ,YAAY;AAAA,IAC9B,UAAU,QAAQ,YAAY;AAAA,IAC9B,WAAW,QAAQ,aAAa;AAAA;AAAA,IAGhC,YAAY,QAAQ,cAAc,CAAA;AAAA,IAClC,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,QAAQ,QAAQ,UAAU,CAAA;AAAA;AAAA;AAAA,IAG1B,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,UAAU,QAAQ,YAAY,CAAA;AAAA,IAC9B,SAAS,QAAQ,WAAW,CAAA;AAAA,IAC5B,MAAM,QAAQ,QAAQ,CAAA;AAAA,IACtB,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,WAAW,QAAQ,aAAa,CAAA;AAAA,IAChC,OAAO,QAAQ,SAAS,CAAA;AAAA,IACxB,QAAQ,QAAQ,UAAU,CAAA;AAAA,IAC1B,UAAU,QAAQ,YAAY,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAU9B,GAAI,QAAQ,QAAQ,QAAQ,KAAK,SAAS,EAAE,MAAM,QAAQ,KAAI,IAAK;IACnE,GAAI,QAAQ,UAAU,QAAQ,OAAO,SAAS,EAAE,QAAQ,QAAQ,OAAM,IAAK;;IAG3E,QAAQ,QAAQ,SAAS,CAAA,GAAI,IAAI,sBAAsB;AAAA;AAAA,IAGvD,UAAU,QAAQ,YAAY,CAAA;AAAA;AAAA,IAG9B,KAAK,QAAQ;AAAA,EACjB;AACA;AASO,SAAS,cAAc,QAAQ,UAAU;AAC9C,MAAI,CAAC,YAAY,OAAO,KAAK,QAAQ,EAAE,WAAW,GAAG;AACnD,WAAO,UAAU,CAAA;AAAA,EACnB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,UAAU,CAAA;AAAA,EAClB;AACA;AAuBA,SAAS,aAAa,OAAO,MAAM,YAAY;AAC7C,QAAM,WAAW,MAAM,SAAS,eAAe,MAAM,QAAQ,aAAa,IAAI,IAAI,CAAA;AAClF,QAAM,OAAO,MAAM,YAAY,CAAA;AAC/B,QAAM,OAAO,CAAA;AACb,aAAW,CAAC,GAAG,KAAK,UAAU;AAC5B,QAAI,KAAK,GAAG,MAAM,OAAW,MAAK,GAAG,IAAI,KAAK,GAAG;AAAA,aACxC,cAAc,WAAW,GAAG,MAAM,OAAW,MAAK,GAAG,IAAI,WAAW,GAAG;AAAA,QAC3E,MAAK,GAAG,IAAI;AAAA,EACnB;AAEA,uBAAqB,OAAO,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,OAAO,MAAM,GAAG,CAAC,CAAC;AACxF,QAAM,cAAc,OAAO;AAC3B,SAAO;AACT;AAaA,SAAS,cAAc,OAAO,UAAU;AACtC,QAAM,OAAO,MAAM,cAAc;AACjC,MAAI,MAAM;AACV,aAAW,CAAC,SAAS,MAAM,KAAK,UAAU;AACxC,QAAI,WAAW,QAAQ,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,EAAG;AACpD,UAAM,OAAO,EAAE,GAAG,KAAI;AACtB,QAAI,GAAG,IAAI,KAAK,GAAG,EAAE,CAAC,KAAK;AAAA,EAC7B;AACA,MAAI,IAAK,OAAM,cAAc,OAAO;AACtC;AAcA,MAAM,eAAe,oBAAI,IAAG;AAC5B,SAAS,qBAAqB,OAAO,SAAS;AAC5C,MAAI,CAAC,QAAQ,UAAU,CAAC,MAAM,SAAS,aAAa,IAAK;AACzD,QAAM,WAAW,MAAM,eAAe,YAAY,CAAA;AAClD,aAAW,OAAO,SAAS;AACzB,UAAM,OAAO,GAAG,MAAM,IAAI,KAAK,GAAG;AAClC,QAAI,aAAa,IAAI,IAAI,EAAG;AAM5B,UAAM,UAAU,SAAS,KAAK,CAAC,OAAO,GAAG,SAAS,eAAe,GAAG,SAAS,oBAAoB,EAAE,QAAQ,GAAG;AAC9G,QAAI,CAAC,QAAS;AACd,iBAAa,IAAI,IAAI;AACrB,UAAM,OAAO,QAAQ,SAAS,cAAc,eAAe;AAC3D,YAAQ;AAAA,MACN,YAAY,MAAM,IAAI,WAAW,GAAG,MAAM,IAAI,6BAA6B,MAAM,IAAI,uBACtE,GAAG;AAAA,IACxB;AAAA,EACE;AACF;AAkBA,SAAS,eAAe,OAAO;AAC7B,MAAI,MAAM,YAAa;AACvB,QAAM,UAAU,WAAW,QAAQ,kBAAkB,UAAU;AAC/D,MAAI,OAAO,YAAY,WAAY;AAEnC,MAAI;AACF,UAAM,SAAS,QAAQ,MAAM,cAAc,MAAM,KAAK;AACtD,QAAI,UAAU,QAAQ,WAAW,MAAM,cAAc,MAAM;AACzD,YAAM,cAAc,OAAO;AAAA,IAC7B;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,mCAAmC,GAAG;AAAA,EACtD;AACF;AA2BA,SAAS,kBAAkB,OAAO;AAChC,MAAI,MAAM,YAAa;AACvB,QAAM,UAAU,WAAW,QAAQ,kBAAkB,UAAU;AAC/D,MAAI,OAAO,YAAY,WAAY;AACnC,MAAI,CAAC,MAAM,cAAc,OAAO,KAAK,MAAM,UAAU,EAAE,WAAW,EAAG;AAErE,MAAI;AACF,UAAM,cAAc,QAAQ,MAAM,cAAc,MAAM,KAAK;AAC3D,QAAI,CAAC,eAAe,gBAAgB,MAAM,WAAY;AACtD,QAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,uBAAiB,SAAS,aAAa,MAAM,cAAc,IAAI;AAC/D;AAAA,IACF;AACA,UAAM,WAAW,MAAM,aAAa,WAAW;AAC/C,aAAS,OAAO,MAAM,cAAc;AACpC,UAAM,gBAAgB;AACtB,UAAM,QAAQ,SAAS,SAAS,CAAA;AAAA,EAClC,SAAS,KAAK;AACZ,YAAQ,MAAM,sCAAsC,GAAG;AAAA,EACzD;AACF;AAGA,SAAS,WAAW,OAAO;AACzB,SAAO,OAAO,SAAS,SAAS,OAAO,KAAK,SAAS;AACvD;AAMA,MAAM,wBAAwB,oBAAI,QAAO;AACzC,SAAS,iBAAiB,SAAS,OAAO,MAAM;AAC9C,MAAI,sBAAsB,IAAI,OAAO,EAAG;AACxC,wBAAsB,IAAI,OAAO;AACjC,QAAM,OACJ,UAAU,OACN,0BACA,MAAM,QAAQ,KAAK,IACjB,aACA,GAAG,OAAO,UAAU,WAAW,cAAc,OAAO,KAAK;AACjE,UAAQ;AAAA,IACN,sCAAsC,IAAI;AAAA,EAG9C;AACA;AAeA,SAAS,gBAAgB,SAAS,QAAQ,OAAO;AAC/C,QAAM,UAAU,WAAW,QAAQ,kBAAkB,UAAU;AAC/D,MAAI,OAAO,YAAY,WAAY,QAAO;AAE1C,MAAI;AACF,UAAM,SAAS,QAAQ,SAAS,QAAQ,KAAK;AAC7C,QAAI,UAAU,OAAO,WAAW,SAAU,QAAO;AAAA,EACnD,SAAS,KAAK;AACZ,YAAQ,MAAM,oCAAoC,GAAG;AAAA,EACvD;AACA,SAAO;AACT;AAqCO,SAAS,aAAa,OAAO,MAAM,aAAa,MAAM;AAC3D,QAAM,WAAW,aAAa,OAAO,MAAM,UAAU;AAIrD,QAAM,SAAS,aAAa,iBAAiB,KAAK;AAClD,gBAAc,OAAO,QAAQ;AAC7B,iBAAe,KAAK;AACpB,oBAAkB,KAAK;AAGvB,QAAM,WAAW,MAAM,YAAY,CAAA;AACnC,QAAM,SAAS,cAAc,MAAM,YAAY,QAAQ;AAGvD,QAAM,UAAU,0BAA0B,MAAM,aAAa;AAG7D,QAAM,WAAW,gBAAgB,SAAS,QAAQ,KAAK;AACvD,MAAI,UAAU;AACZ,WAAO;AAAA,MACL,SAAS,SAAS,WAAW;AAAA,MAC7B,QAAQ,SAAS,UAAU;AAAA,IACjC;AAAA,EACE;AAEA,SAAO,EAAE,SAAS,OAAM;AAC1B;AAQO,SAAS,iBAAiB,eAAe;AAC9C,SAAO,WAAW,QAAQ,mBAAmB,aAAa,KAAK;AACjE;AAQO,SAAS,qBAAqB,eAAe;AAClD,SAAO,WAAW,QAAQ,uBAAuB,aAAa,KAAK,CAAA;AACrE;AChYO,SAAS,QAAQ,KAAK;AAC3B,SAAO,cAAc,KAAK,WAAW,QAAQ,eAAe,YAAY,EAAE;AAC5E;AAaO,SAAS,YAAY,OAAO,SAAS;AAC1C,MAAI,OAAO,UAAU,YAAY,CAAC,MAAO,QAAO;AAChD,MAAI,MAAM,WAAW,GAAG,GAAG;AAIzB,QAAI,MAAM,MAAM,MAAM,CAAC;AACvB,QAAI,IAAI,WAAW,KAAK,IAAI,WAAW,EAAG,OAAM,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE;AAClF,QAAI,CAAC,+BAA+B,KAAK,GAAG,EAAG,QAAO;AACtD,UAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,UAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,UAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,WAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,OAAO;AAAA,EAC1C;AACA,MAAI,MAAM,WAAW,KAAK,GAAG;AAC3B,UAAM,QAAQ,MAAM,MAAM,gCAAgC;AAC1D,QAAI,MAAO,QAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,OAAO;AAAA,EAC1E;AACA,SAAO;AACT;ACjDA,MAAM,IAAI,MAAM;AAGhB,MAAM,OAAO,EAAE,UAAU,YAAY,OAAO,EAAC;AAE7C,SAAS,WAAW,EAAE,SAAS;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,OAAO,EAAE,WAAW,oBAAoB,OAAO,EAAE,GAAG,MAAM,iBAAiB,SAAS,eAAe,OAAM,CAAE;AACtH;AAEA,SAAS,cAAc,EAAE,YAAY;AACnC,MAAI,CAAC,SAAU,QAAO;AAGtB,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO,EAAE,OAAO,EAAE,WAAW,uBAAuB,OAAO,EAAE,GAAG,MAAM,YAAY,YAAY,eAAe,OAAM,CAAE;AAAA,EACvH;AAEA,QAAM,QAAQ,SAAS,SAAS;AAChC,QAAM,MAAM,SAAS,OAAO;AAC5B,QAAM,eAAe,SAAS,gBAAgB;AAC9C,QAAM,aAAa,SAAS,cAAc;AAC1C,QAAM,aAAa,eAAe,IAAI,YAAY,OAAO,YAAY,IAAI;AACzE,QAAM,WAAW,aAAa,IAAI,YAAY,KAAK,UAAU,IAAI;AACjE,QAAM,aACJ,mBAAmB,SAAS,SAAS,CAAC,QACnC,UAAU,IAAI,SAAS,iBAAiB,CAAC,MAAM,QAAQ,IAAI,SAAS,eAAe,GAAG;AAE3F,SAAO,EAAE,OAAO,EAAE,WAAW,uBAAuB,OAAO,EAAE,GAAG,MAAM,cAAc,eAAe,OAAM,CAAE;AAC7G;AAEA,SAAS,WAAW,EAAE,SAAS;AAC7B,MAAI,CAAC,OAAO,IAAK,QAAO;AACxB,QAAM,QAAQ;AAAA,IACZ,GAAG;AAAA,IACH,iBAAiB,OAAO,QAAQ,MAAM,GAAG,CAAC;AAAA,IAC1C,oBAAoB,MAAM,YAAY;AAAA,IACtC,gBAAgB,MAAM,QAAQ;AAAA,IAC9B,kBAAkB;AAAA,EACtB;AACE,SAAO,EAAE,OAAO,EAAE,WAAW,oBAAoB,OAAO,eAAe,OAAM,CAAE;AACjF;AAMO,SAAS,YAAY,EAAE,SAAS;AACrC,MAAI,CAAC,OAAO,OAAO,CAAC,MAAM,OAAQ,QAAO;AACzC,SAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,MAAM,QAAQ,MAAM,SAAS,UAAU,SAAQ,EAAE,CAAE;AAC1F;AAgDA,SAAS,QAAQ,EAAE,WAAW;AAC5B,MAAI,CAAC,SAAS,QAAS,QAAO;AAC9B,QAAM,UAAU,QAAQ,WAAW;AAEnC,MAAI,QAAQ,UAAU;AACpB,UAAM,IAAI,QAAQ;AAClB,UAAM,aACJ,mBAAmB,EAAE,SAAS,GAAG,QAAQ,EAAE,SAAS,iBAAiB,IAAI,EAAE,iBAAiB,CAAC,MAC1F,EAAE,OAAO,eAAe,IAAI,EAAE,eAAe,GAAG;AACrD,WAAO,EAAE,OAAO;AAAA,MACd,WAAW;AAAA,MACX,OAAO,EAAE,GAAG,MAAM,YAAY,SAAS,eAAe,OAAM;AAAA,MAC5D,eAAe;AAAA,IACrB,CAAK;AAAA,EACH;AAEA,QAAM,OAAO,QAAQ,SAAS,UAAU,kBAAkB;AAC1D,SAAO,EAAE,OAAO;AAAA,IACd,WAAW;AAAA,IACX,OAAO,EAAE,GAAG,MAAM,iBAAiB,QAAQ,IAAI,KAAK,OAAO,KAAK,eAAe,OAAM;AAAA,IACrF,eAAe;AAAA,EACnB,CAAG;AACH;AAkBO,SAAS,WAAW,EAAE,MAAM,OAAO,UAAU,OAAO,OAAO,SAAS,YAAY,IAAI,QAAQ,YAAW,GAAI;AAChH,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,WAAW,0BAA0B,IAAI,IAAI,SAAS,GAAG,KAAI;AAAA,MAC7D,OAAO,EAAE,GAAG,MAAM,UAAU,UAAU,QAAQ,EAAC;AAAA,MAC/C,eAAe;AAAA,IACrB;AAAA,IACI,SAAS,UAAU,EAAE,YAAY,EAAE,MAAK,CAAE,IAAI;AAAA,IAC9C,SAAS,aAAa,EAAE,eAAe,EAAE,SAAQ,CAAE,IAAI;AAAA,IACvD,SAAS,UAAU,EAAE,YAAY,EAAE,MAAK,CAAE,IAAI;AAAA,IAC9C,SAAS,UAAU,EAAE,OAAO,EAAE,MAAK,CAAE,IAAI;AAAA,IACzC,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1B;AACA;ACnIO,SAAS,eAAe,WAAW,IAAI;AAC5C,QAAM,WAAW,WAAW,GAAG,QAAQ,MAAM;AAC7C,SACE,+TAGY,QAAQ;AAGxB;ACUO,SAAS,YAAY,EAAE,UAAU;AACtC,SAAO,MAAM;AAAA,IACX;AAAA,IACA,EAAE,WAAW,uBAAsB;AAAA,IACnC,IAAI,QAAQ,OAAO,GAAG;AAAA,EAC1B;AACA;AAUO,SAAS,2BAA2B,QAAQ,YAAY;AAC7D,QAAM,OAAO,YAAY,SAAS,gBAAgB,CAAA;AAQlD,SAAO,gBAAgB,EAAE,KAAK,aAAa,GAAI,KAAK,iBAAiB,GAAG;AASxE,MAAI,KAAK,MAAM,SAAS,OAAO,eAAe;AAC5C,SAAK,KAAK,MAAM,OAAO,eAAe;AAAA,MACpC,iBAAiB,KAAK,KAAK,SAAS,CAAA;AAAA,IAC1C,CAAK;AAAA,EACH;AACF;AA2BO,SAAS,sBAAsB,SAAS,QAAQ;AACrD,QAAM,cAAc,qBAAqB,SAAS,MAAM;AACxD,QAAM,UAAU,SAAS,UAAU,MAAM;AACzC,MAAI,CAAC,UAAU,WAAW,eAAe,CAAC,QAAS,QAAO;AAC1D,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,SAAS,QAAQ,WAAW,QAAQ;AAAA,IACpC,QAAQ;AAAA,MACN,GAAG,QAAQ;AAAA,MACX,MAAM,QAAQ,QAAQ;AAAA,MACtB,cAAc;AAAA,IACpB;AAAA,EACA;AACA;AAuBO,SAAS,iBAAiB,SAAS,aAAa;AACrD,MAAI,CAAC,SAAS,aAAa,CAAC,aAAa,OAAQ;AACjD,aAAW,SAAS,aAAa;AAG/B,QAAI,MAAM,WAAW,MAAM,YAAY,UAAW;AAGlD,UAAM,QAAQ,MAAM,OAAO,EAAE,MAAM,MAAM,MAAM,MAAM,MAAM,KAAI,IAAK,EAAE,MAAM,MAAM,KAAI;AACtF,QAAI,OAAO,QAAQ,SAAS,YAAY,YAAY;AAClD,cAAQ,QAAQ,QAAQ,MAAM,QAAQ,OAAO,EAAE,QAAO,CAAE;AAAA,IAC1D,OAAO;AAEL,cAAQ,UAAU,IAAI,eAAe,MAAM,MAAM,GAAG,KAAK;AAAA,IAC3D;AAAA,EACF;AACF;AA+CO,SAAS,eAAe,QAAQ,YAAY;AACjD,QAAM,UAAU,QAAQ;AACxB,QAAM,YAAY,SAAS;AAC3B,MAAI,CAAC,aAAa,UAAU,IAAK;AAEjC,QAAM,OAAO,YAAY,SAAS,gBAAgB,CAAA;AAClD,MAAI;AACF,UAAM,EAAE,QAAQ,KAAK,MAAK,IAAK,WAAW,WAAW;AAAA,MACnD,gBAAgB,KAAK,QAAQ,CAAA;AAAA,MAC7B,MAAM,QAAQ,YAAY;AAAA,IAChC,CAAK;AAKD,WAAO,OAAO,WAAW,QAAQ,EAAE,KAAK,MAAK,CAAE;AAAA,EACjD,SAAS,KAAK;AAIZ,YAAQ,KAAK,yCAAyC,KAAK,WAAW,GAAG;AAAA,EAC3E;AACF;ACpNO,MAAM,aAAa;AAMnB,MAAM,qBAAqB;AAS3B,SAAS,YAAY,KAAK,QAAQ;AACvC,QAAM,QAAQ,EAAE,UAAU,UAAU,KAAK,OAAO,WAAU;AAC1D,MAAI,UAAU,KAAM,OAAM,SAAS;AACnC,SAAO;AACT;AASO,SAAS,eAAe,QAAQ;AACrC,QAAM,SAAS,UAAU,CAAA,GAAI,KAAK,CAAC,UAAU,OAAO,MAAM;AAC1D,SAAO,QAAQ,EAAE,KAAK,MAAM,aAAa,WAAU,IAAK;AAC1D;AASO,SAAS,iBAAiB,OAAO;AACtC,QAAM,SAAS,CAAA;AACf,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,SAAS,CAAA,CAAE,EAAG,QAAO,IAAI,IAAI,eAAe,MAAM;AAC9F,SAAO;AACT;AASO,SAAS,UAAU,YAAY,OAAO;AAC3C,SAAO,aAAa,EAAE,OAAO,YAAY,WAAW,KAAK,KAAK,GAAG,kBAAkB,WAAW;AAChG;AAYO,SAAS,mBAAmB,OAAO;AACxC,SAAO,OAAO,SAAS,EAAE,GAAG,YAAY,MAAM,aAAa,oBAAoB,CAAC,GAAG,WAAW,QAAO,IAAK;AAC5G;AASO,SAAS,cAAc,MAAM;AAClC,QAAM,QAAQ,MAAM,sBAAsB,CAAA;AAC1C,SAAO,OAAO,OAAO,KAAK,EAAE,KAAK,CAAC,WAAW,eAAe,MAAM,MAAM,IAAI;AAC9E;AAcO,SAAS,oBAAoB,OAAO,MAAM;AAC/C,SAAO,QAAQ,cAAc,OAAO,IAAI,IAAI,EAAE,iBAAiB,uBAAuB;AACxF;AASO,SAAS,mBAAmB,OAAO,MAAM;AAC9C,QAAM,QAAQ,OAAO,mBAAmB,KAAK,IAAI;AACjD,SAAO,QAAQ,EAAE,OAAO,kBAAkB,SAAS,UAAU,UAAU,cAAc;AACvF;AC7GA,MAAM,KAAK;AAEX,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,IAAI,EAAE,QAAQ,mBAAmB,GAAG;AAoBnE,SAAS,yBAAyB,WAAW,UAAU;AAC5D,MAAI,aAAa,MAAO,QAAO;AAE/B,QAAM,cAAc,CAAA;AACpB,aAAW,QAAQ,UAAW,aAAY,IAAI,IAAI,QAAQ,IAAI;AAE9D,SAAO,WAAW,EAAE,GAAG,aAAa,GAAG,SAAQ,IAAK;AACtD;AAyDO,SAAS,oBAAoB,WAAW,UAAU,SAAS,MAAM;AACtE,MAAI,aAAa,MAAO,QAAO,CAAA;AAE/B,QAAM,SAAS,CAAA;AACf,aAAW,QAAQ,UAAW,QAAO,IAAI,IAAI;AAC7C,MAAI,SAAU,QAAO,OAAO,QAAQ,QAAQ;AAE5C,QAAM,SAAS,UAAU,OAAO,CAAC,SAAS,SAAS,IAAI,KAAK,EAAE,YAAY,QAAQ,SAAS;AAC3F,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,QAAQ,OAAO,QAAQ,MAAM,EAChC,OAAO,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,OAAO,SAAS,KAAK,CAAC,EACnE,IAAI,CAAC,GAAG,KAAK,MAAM,KAAK;AAC3B,UAAM,QAAQ,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI;AACtC,eAAW,QAAQ,OAAQ,QAAO,IAAI,IAAI;AAAA,EAC5C;AAEA,SAAO;AACT;AA4BO,SAAS,iBAAiB,QAAQ,aAAa,QAAQ,SAAS,MAAM;AAC3E,QAAM,QAAQ,CAAA;AAEd,QAAM,OAAO,cAAc,MAAM;AACjC,MAAI,KAAM,OAAM,qBAAqB;AAAA,WAC5B,eAAe,WAAW,OAAQ,OAAM,YAAY;AAE7D,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,QAAQ;AACV,WAAO,OAAO,OAAO,YAAY,OAAO,KAAK,KAAK,CAAC;AAAA,EACrD,WAAW,SAAS,MAAM;AACxB,UAAM,WAAW;AACjB,UAAM,SAAS;AAAA,EACjB;AAEA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AACjD;AAaO,SAAS,iBAAiB,QAAQ,aAAa,QAAQ,SAAS,MAAM;AAC3E,QAAM,QAAQ,iBAAiB,QAAQ,aAAa,QAAQ,MAAM;AAClE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,SAAS,EAAE,OAAO,kBAAkB,OAAM,IAAK,EAAE,MAAK;AAC/D;AClLA,MAAM,SAAS,EAAE,SAAS,QAAQ,eAAe,UAAU,WAAW,QAAO;AAC7E,MAAM,OAAO,EAAE,MAAM,EAAC;AAUf,SAAS,cAAc,EAAE,QAAQ,MAAM,QAAQ,MAAK,GAAI;AAC7D,SAAO,MAAM;AAAA,IACX;AAAA,IACA,EAAE,OAAO,OAAM;AAAA,IACf,UAAU,MAAM,cAAc,UAAU,OAAO,UAAU,MAAM,MAAM;AAAA,IACrE,QAAQ,MAAM,cAAc,QAAQ,EAAE,OAAO,KAAI,GAAI,IAAI;AAAA,IACzD,UAAU,MAAM,cAAc,UAAU,OAAO,UAAU,MAAM,MAAM;AAAA,EACzE;AACA;ACkBO,SAAS,gBAAgB,eAAe,UAAU;AACvD,MAAI,SAAS;AACb,MAAI;AACF,aAAS,aAAa,QAAQ,mBAAmB;AAAA,EACnD,SAAS,GAAG;AAAA,EAEZ;AAEA,MAAI,YAAY,WAAW,WAAW,WAAW;AACjD,MAAI,SAAS,YAAY,SAAS;AAElC,MAAI,CAAC,aAAa,eAAe;AAC/B,QAAI;AACF,UAAI,OAAO,WAAW,8BAA8B,EAAE,QAAS,UAAS;AAAA,IAC1E,SAAS,GAAG;AAAA,IAEZ;AAAA,EACF;AAEA,MAAI;AACF,QAAI,OAAO,SAAS;AAMpB,QAAI,WAAW,QAAQ;AACrB,WAAK,UAAU,IAAI,aAAa;AAChC,WAAK,UAAU,OAAO,cAAc;AAAA,IACtC,OAAO;AACL,WAAK,UAAU,IAAI,cAAc;AACjC,WAAK,UAAU,OAAO,aAAa;AAAA,IACrC;AAAA,EACF,SAAS,GAAG;AAAA,EAEZ;AAEA,SAAO;AACT;AAyBO,SAAS,sBAAsB,YAAY;AAChD,MAAI,CAAC,cAAc,CAAC,cAAc,UAAU,EAAG,QAAO;AAEtD,SAAO;AAAA,IACL,eAAe,WAAW,4BAA4B;AAAA,IACtD,UAAU,WAAW,YAAY,SAAS,SAAS;AAAA,EACvD;AACA;AA+BO,SAAS,2BAA2B,YAAY;AACrD,QAAM,OAAO,sBAAsB,UAAU;AAC7C,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,OAAO,IAAI,gBAAgB,SAAQ,CAAE,KAAK,KAAK,aAAa,KAAK,KAAK,UAAU,KAAK,QAAQ,CAAC;AAEpG,SAAO,kCAAkC,IAAI;AAC/C;AC1JO,SAAS,cAAc,WAAW,UAAU;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,QAAQ,OAAO,SAAS;AAC9B,SAAO,WAAW,GAAG,KAAK,MAAM,QAAQ,KAAK;AAC/C;ACVA,MAAM,aAAa,oBAAI,QAAO;AAGvB,SAAS,gBAAgB,SAAS;AACvC,MAAI,WAAW,CAAC,WAAW,IAAI,OAAO,EAAG,YAAW,IAAI,SAAS,QAAQ,UAAU;AACrF;AAGO,SAAS,iBAAiB,SAAS;AACxC,MAAI,WAAW,IAAI,OAAO,EAAG,SAAQ,aAAa,WAAW,IAAI,OAAO;AAC1E;ACaO,SAAS,YAAY,SAAS,OAAO;AAC1C,SAAO,QAAQ,QAAQ,KAAK;AAC9B;AAiBO,SAAS,qBAAqB,SAAS,OAAO;AACnD,SAAO,QAAQ,aAAa,KAAK,EAAE,SAAS,YAAY,SAAY,YAAY,SAAS,KAAK;AAChG;ACLA,MAAM,iBAAiB,CAAC,SAAS,UAAU,MAAM;AAM1C,SAAS,gBAAgB,OAAO;AACrC,QAAM,QAAQ,MAAM;AACpB,QAAM,iBAAiB,MAAM,OAAO,aAAa;AAIjD,MAAI,eAAe;AACnB,MAAI,SAAS,eAAe,SAAS,KAAK,GAAG;AAC3C,mBAAe,WAAW,KAAK;AAAA,EACjC;AAEA,MAAI,YAAY;AAChB,MAAI,gBAAgB;AAClB,gBAAY,YAAY,GAAG,SAAS,IAAI,cAAc,KAAK;AAAA,EAC7D;AAGA,QAAM,aAAa,MAAM,cAAc,CAAA;AACvC,QAAM,QAAQ,CAAA;AAId,MAAI,WAAW,MAAM;AACnB,UAAM,WAAW;AACjB,UAAM,YAAY;AAAA,EACpB;AAIA,MAAI,MAAM,kBAAkB;AAC1B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,gBAAgB,GAAG;AACjE,YAAM,KAAK,GAAG,EAAE,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,SAAO,EAAE,IAAI,aAAa,KAAK,GAAG,OAAO,WAAW,WAAU;AAChE;AAYO,SAAS,iBAAiB,YAAY;AAC3C,MAAI,CAAC,YAAY,KAAM,QAAO;AAC9B,SAAO,MAAM,cAAc,YAAY,UAAU;AACnD;AAiBO,SAAS,YAAY,OAAO,EAAE,KAAK,WAAW,aAAa,MAAK,IAAK,IAAI;AAC9E,QAAM,YAAY,MAAM,cAAa;AAErC,MAAI,CAAC,WAAW;AACd,WAAO,MAAM,cAAc,OAAO;AAAA,MAChC,WAAW;AAAA,MACX,OAAO,EAAE,SAAS,QAAQ,YAAY,WAAW,OAAO,UAAS;AAAA,IACvE,GAAO,wBAAwB,MAAM,IAAI,EAAE;AAAA,EACzC;AAIA,QAAM,OAAO,iBAAiB,MAAM,IAAI;AACxC,QAAM,cAAc,MAAM,SAAS;AACnC,MAAI,aAAa;AACjB,MAAI,UAAU;AACd,MAAI,aAAa;AACf,UAAM,WAAW,YAAY,QAAQ,OAAO,IAAI;AAChD,QAAI,SAAS,WAAW,QAAS,cAAa,SAAS;AACvD,cAAU,SAAS,WAAW;AAAA,EAChC;AAOA,QAAM,cAAc;AAOpB,QAAM,WAAW,aAAa,OAAO,MAAM,UAAU;AACrD,QAAM,SAAS,SAAS;AACxB,QAAM,UAAU,EAAE,GAAG,SAAS,SAAS,GAAG,MAAM,WAAU;AAE1D,QAAM,iBAAiB,EAAE,SAAS,QAAQ,MAAK;AAI/C,MAAI,CAAC,IAAI;AACP,WAAO,MAAM,cAAc,WAAW,cAAc;AAAA,EACtD;AAGA,QAAM,EAAE,YAAY,GAAG,aAAY,IAAK,gBAAgB,KAAK;AAG7D,QAAM,SAAS,mBAAmB,OAAO,UAAU;AACnD,MAAI,QAAQ;AACV,iBAAa,QAAQ,EAAE,GAAG,aAAa,OAAO,GAAG,OAAO,MAAK;AAC7D,iBAAa,gBAAgB,IAAI,OAAO,gBAAgB;AAAA,EAC1D;AAEA,QAAM,SAAS,oBAAoB,OAAO,UAAU;AACpD,MAAI,OAAQ,cAAa,QAAQ,EAAE,GAAG,aAAa,OAAO,GAAG,OAAM;AAGnE,QAAM,qBAAqB,UAAU;AACrC,MAAI,oBAAoB;AACtB,iBAAa,YAAY,aAAa,YAClC,GAAG,aAAa,SAAS,IAAI,kBAAkB,KAC/C;AAAA,EACN;AAGA,QAAM,gBAAgB,YAAY,QAAQ,MAAM,eAAe;AAC/D,QAAM,gBAAgB;AAMtB,QAAM,aAAa,OAAO,YAAY,KAAM,UAAU,MAAM;AAE5D,MAAI,eAAe;AACjB,WAAO,MAAM;AAAA,MAAc;AAAA,MAAY;AAAA,MACrC,iBAAiB,UAAU;AAAA,MAC3B,MAAM;AAAA,QAAc;AAAA,QAAO,EAAE,OAAO,EAAE,UAAU,YAAY,QAAQ,KAAI;AAAA,QACtE,MAAM,cAAc,WAAW,cAAc;AAAA,MACrD;AAAA,IACA;AAAA,EACE;AAEA,SAAO,MAAM;AAAA,IAAc;AAAA,IAAY;AAAA,IACrC,MAAM,cAAc,WAAW,cAAc;AAAA,EACjD;AACA;AAKO,SAAS,aAAa,QAAQ,EAAE,aAAa,MAAK,IAAK,CAAA,GAAI;AAChE,MAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO;AAC3C,SAAO,OAAO;AAAA,IAAI,CAAC,OAAO,UACxB,MAAM;AAAA,MAAc,MAAM;AAAA,MAAU,EAAE,KAAK,MAAM,MAAM,MAAK;AAAA,MAC1D,YAAY,OAAO,EAAE,WAAU,CAAE;AAAA,IACvC;AAAA,EACA;AACA;AAMO,SAAS,aAAa,MAAM,SAAS;AAC1C,QAAM,aAAa,KAAK,cAAa;AACrC,QAAM,eAAe,QAAQ,gBAAgB,UAAU;AACvD,QAAM,aAAa,QAAQ,cAAc,UAAU;AAEnD,QAAM,aAAa,KAAK,cAAa;AACrC,QAAM,QAAQ,KAAK,eAAc;AAIjC,aAAW,UAAU,CAAC,YAAY,GAAG,OAAO,OAAO,KAAK,CAAC,GAAG;AAC1D,eAAW,SAAS,UAAU,CAAA,EAAI,OAAM,cAAa;AAAA,EACvD;AAKA,QAAM,YAAY,OAAO,KAAK,KAAK;AACnC,QAAM,cAAc,QAAQ,kBACxB,yBAAyB,WAAW,YAAY,WAAW,IAC3D;AAKJ,QAAM,SAAS,iBAAiB,KAAK;AACrC,QAAM,SAAS,oBAAoB,WAAW,YAAY,QAAQ,MAAM;AACxE,QAAM,WAAW,CAAC,MAAM,YAAY;AAClC,UAAM,QAAQ,iBAAiB,MAAM,aAAa,QAAQ,eAAe,OAAO,IAAI,IAAI,IAAI;AAC5F,WAAO,QAAQ,MAAM,cAAc,OAAO,OAAO,OAAO,IAAI;AAAA,EAC9D;AAEA,QAAM,cAAc,aAAa,SAAS,QAAQ,aAAa,YAAY,EAAE,YAAY,UAAS,CAAE,CAAC,IAAI;AACzG,QAAM,eAAe,CAAA;AACrB,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,GAAG;AAClD,iBAAa,IAAI,IAAI,SAAS,MAAM,aAAa,MAAM,CAAC;AAAA,EAC1D;AAEA,MAAI,cAAc;AAChB,UAAM,SAAS,EAAE,GAAI,YAAY,YAAY,IAAK,GAAI,KAAK,gBAAe,KAAM,GAAG;AACnF,WAAO,MAAM,cAAc,cAAc;AAAA,MACvC;AAAA,MAAM;AAAA,MAAS;AAAA,MACf,MAAM;AAAA,MACN,GAAG;AAAA,IACT,CAAK;AAAA,EACH;AAKA,QAAM,OAAO,CAAC,SAAS,UAAU,OAAO,IAAI,GAAG,SAAS,IAAI,CAAC;AAC7D,SAAO,MAAM,cAAc,eAAe;AAAA,IACxC,MAAM;AAAA,IACN,GAAG;AAAA,IACH,OAAO,EAAE,QAAQ,KAAK,QAAQ,GAAG,QAAQ,KAAK,QAAQ,EAAC;AAAA,EAC3D,CAAG;AACH;AAyBO,SAAS,uBAAuB,SAAS,YAAY,QAAQ,qBAAqB,cAAc;AACrG,QAAM,gBAAgB,sBAAsB,SAAS,MAAM;AAC3D,QAAM,SAAS,cAAc,eAAe,YAAY,qBAAqB,YAAY;AACzF,QAAM,cAAc,qBAAqB,SAAS,MAAM;AACxD,MAAI,UAAU,WAAW,eAAe,OAAO,eAAe,iBAAiB;AAC7E,WAAO,cAAc,gBAAgB,MAAM;AAAA,EAC7C;AACA,SAAO;AACT;AAqBO,SAAS,cAAc,SAAS,YAAY,qBAAqB,cAAc;AAIpF,MAAI,aAAa,CAAA;AACjB,MAAI,UAAU,CAAA;AACd,MAAI,MAAM,QAAQ,mBAAmB,GAAG;AACtC,iBAAa;AACb,cAAU,gBAAgB,CAAA;AAAA,EAC5B,OAAO;AACL,cAAU,uBAAuB,CAAA;AAAA,EACnC;AACA,QAAM,EAAE,aAAa,MAAM;AAAA,EAAC,MAAM;AAElC,aAAW,yBAAyB;AAGpC,QAAM,SAAS,aAAa,SAAS,YAAY,UAAU;AAE3D,kBAAgB,OAAO,aAAa;AAGpC,MAAI,QAAQ,QAAQ,QAAQ,OAAO,eAAe,aAAa;AAC7D,WAAO,cAAc,YAAY,QAAQ,OAAO,IAAI;AAAA,EACtD;AAMA,SAAO,qBAAqB,SAAS,kBAAkB,EAAE,QAAQ,MAAM,UAAU;AAC/E,UAAM,YAAY,UAAU,MAAM,eAAe,CAAA;AACjD,WAAO,UAAU;AAAA,MAAI,CAAC,YAAY,UAChC,MAAM;AAAA,QAAc,MAAM;AAAA,QAAU,EAAE,KAAK,WAAW,MAAM,MAAK;AAAA,QAC/D,YAAY,YAAY,EAAE,IAAI,UAAU,MAAM,YAAY,SAAS,UAAU,MAAK,CAAE;AAAA,MAC5F;AAAA,IACA;AAAA,EACE;AAKA,SAAO,qBAAqB;AAO5B,6BAA2B,QAAQ,UAAU;AAK7C,iBAAe,QAAQ,UAAU;AASjC,QAAM,UAAU,OAAO;AACvB,SAAO,oBAAoB;AAAA,IACzB,aAAa,MAAM;AAWjB,YAAM,QAAQ,SAAS,YAAY,SAAS;AAC5C,YAAM,WAAW,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI,KAAK;AAC1D,aAAO,EAAE,UAAU,QAAQ,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,UAAS;AAAA,IACtE;AAAA,IACA,WAAW,OAAO,CAAA;AAAA,IAClB,aAAa,MAAM,MAAM;AAAA,IAAC;AAAA,EAC9B;AAEE,SAAO;AACT;AAWO,eAAe,cAAc,aAAa,QAAQ,aAAa,MAAM;AAAC,GAAG;AAC9E,QAAM,QAAQ,YAAY,OAAO,QAAQ,CAAA;AACzC,MAAI,MAAM,WAAW,EAAG,QAAO,CAAA;AAE/B,QAAM,UAAU,YAAY,QAAQ,OAAO,QAAQ;AAEnD,aAAW,YAAY,MAAM,MAAM,mBAAmB;AAEtD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,YAAY;AAC3B,YAAM,CAAC,QAAQ,IAAI,IAAI,QAAQ,MAAM,GAAG;AACxC,YAAM,MAAM,QAAQ,QAAQ,MAAM,OAAO;AACzC,YAAM,WAAW,MAAM,MAAM,GAAG;AAChC,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,aAAO,CAAC,GAAG,MAAM,IAAI,IAAI,IAAI,MAAM,SAAS,KAAI,CAAE;AAAA,IACpD,CAAC;AAAA,EACL;AAKE,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,WAAW,YAAa,QAAO,UAAU,IAAI,GAAG,OAAO,KAAK;AAAA,EACzE;AAEA,QAAM,YAAY,QAAQ,OAAO,OAAK,EAAE,WAAW,WAAW,EAAE;AAChE,QAAM,aAAa,MAAM,OAAO,CAAC,GAAG,MAAM,QAAQ,CAAC,EAAE,WAAW,UAAU;AAC1E,MAAI,WAAW,SAAS,GAAG;AAGzB,UAAM,QAAQ,WAAW,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,KAAK,WAAW,SAAS,KAAK,QAAQ;AACrF,UAAM,MAAM,WAAW,SAAS,IAAI,MAAM,MAAM,WAAW,WAAW,MAAM,YAAY,KAAK;AAC7F,YAAQ,KAAK,eAAe,GAAG,EAAE;AACjC,eAAW,KAAK,GAAG,EAAE;AAAA,EACvB;AAGA,MAAI,OAAO,UAAU,OAAO,GAAG;AAC7B,gBAAY,aAAa,OAAO,YAAY,OAAO,SAAS;AAAA,EAC9D;AACA,SAAO;AACT;AAmBO,SAAS,oBAAoB,KAAK;AACvC,QAAM,MAAM,IAAI,WAAW;AAE3B,MAAI,IAAI,SAAS,mBAAmB,KAAK,IAAI,SAAS,UAAU,KAAK,IAAI,SAAS,WAAW,GAAG;AAC9F,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACf;AAAA,EACE;AAEA,MAAI,IAAI,SAAS,yBAAyB,KAAK,IAAI,SAAS,MAAM,GAAG;AACnE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACf;AAAA,EACE;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,EACb;AACA;AAYO,SAAS,WAAW,MAAM,SAAS;AAKxC,MAAI,SAAS,QAAQ,gBAAe,EAAI,SAAQ,cAAc,KAAK,KAAK;AAYxE,MAAI,KAAK,kBAAkB,KAAK,cAAa,EAAG,WAAW,GAAG;AAC5D,WAAO;AAAA,MACL,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SACE,SAAS,KAAK,KAAK;AAAA,MAE7B;AAAA,IACA;AAAA,EACE;AAEA,QAAM,UAAU,aAAa,MAAM,OAAO;AAE1C,MAAI;AACJ,MAAI;AACF,sBAAkB,eAAe,OAAO;AAAA,EAC1C,SAAS,KAAK;AACZ,WAAO,EAAE,OAAO,oBAAoB,GAAG,EAAC;AAAA,EAC1C;AAGA,QAAM,aAAa,QAAQ,WAAW;AAGtC,QAAM,qBAAqB,sBAAsB,KAAK,aAAY,GAAI,UAAU;AAEhF,SAAO,EAAE,iBAAiB,mBAAkB;AAC9C;AA2CO,SAAS,kBAAkB,MAAM,iBAAiB,MAAM,UAAU,CAAA,GAAI;AAC3E,MAAI,SAAS;AAQb,MAAI,CAAC,OAAO,SAAS,wBAAwB,GAAG;AAC9C,UAAM,aAAa,2BAA2B,MAAM,SAAS,WAAW,UAAU;AAClF,QAAI,YAAY;AACd,eAAS,OAAO,QAAQ,WAAW,KAAK,UAAU;AAAA,QAAW;AAAA,IAC/D;AAAA,EACF;AAWA,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,WAAW,WAAW;AAC5B,MAAI,YAAY,CAAC,OAAO,SAAS,mBAAmB,GAAG;AACrD,aAAS,OAAO;AAAA,MACd;AAAA,MACA;AAAA,EAAgC,QAAQ;AAAA;AAAA;AAAA,IAC9C;AAAA,EACE;AAOA,MAAI,WAAW,SAAS,CAAC,OAAO,SAAS,iBAAiB,GAAG;AAC3D,aAAS,OAAO;AAAA,MACd;AAAA,MACA,KAAK,iBAAiB;AAAA,EAAK,UAAU,KAAK;AAAA;AAAA,IAChD;AAAA,EACE;AAGA,MAAI,QAAQ,oBAAoB;AAC9B,UAAM,gBAAgB;AAAA,EAAuC,QAAQ,kBAAkB;AAAA;AACvF,aAAS,OAAO,QAAQ,WAAW,GAAG,aAAa;AAAA,QAAW;AAAA,EAChE;AAIA,MAAI,QAAQ,OAAO,QAAQ,CAAC,OAAO,SAAS,qBAAqB,GAAG;AAClE,UAAM,aAAa,uDAAuD,cAAc,OAAO,YAAY,QAAQ,KAAK,CAAC,CAAC;AAC1H,aAAS,OAAO,QAAQ,WAAW,MAAM,KAAK,UAAU;AAAA,QAAW;AAAA,EACrE;AAGA,WAAS,OAAO;AAAA,IACd;AAAA,IACA,kBAAkB,eAAe;AAAA,EACrC;AAME,QAAM,QAAQ,cAAc,KAAK,WAAQ,KAAQ,KAAK,OAAO,KAAK,SAAS,IAAI;AAC/E,MAAI,OAAO;AACT,aAAS,OAAO;AAAA,MACd;AAAA,MACA,UAAU,WAAW,KAAK,CAAC;AAAA,IACjC;AAAA,EACE;AAQA,WAAS,cAAc,QAAQ,IAAI;AAEnC,SAAO;AACT;AAYA,SAAS,cAAc,MAAM,MAAM;AAEjC,QAAM,OAAO,OAAO,MAAM,gBAAgB,aAAa,KAAK,YAAW,IAAK,EAAE,aAAa,MAAM,eAAe,KAAI;AACpH,QAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,MAAI,CAAC,QAAQ,QAAQ,GAAI,QAAO;AAChC,QAAM,KAAK,KAAK,MAAM,CAAA;AACtB,QAAM,WAAW,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK;AAChF,QAAM,WAAW;AAAA,IACf,CAAC,QAAQ,eAAe,KAAK,WAAW;AAAA,IACxC,CAAC,QAAQ,YAAY,QAAQ;AAAA,IAC7B,CAAC,QAAQ,UAAU,KAAK,MAAM;AAAA,IAC9B,CAAC,YAAY,WAAW,SAAS;AAAA,IACjC,CAAC,YAAY,gBAAgB,GAAG,QAAQ;AAAA,IACxC,CAAC,YAAY,YAAY,GAAG,KAAK;AAAA,IACjC,CAAC,YAAY,kBAAkB,GAAG,WAAW;AAAA,IAC7C,CAAC,YAAY,YAAY,GAAG,KAAK;AAAA,IACjC,CAAC,YAAY,UAAU,GAAG,GAAG;AAAA,IAC7B,CAAC,QAAQ,gBAAgB,GAAG,QAAQ,wBAAwB,SAAS;AAAA,IACrE,CAAC,QAAQ,gBAAgB,KAAK,SAAS,IAAI;AAAA,IAC3C,CAAC,QAAQ,iBAAiB,GAAG,KAAK;AAAA,IAClC,CAAC,QAAQ,uBAAuB,GAAG,WAAW;AAAA,IAC9C,CAAC,QAAQ,iBAAiB,GAAG,KAAK;AAAA,EACtC;AAEE,MAAI,OAAO,KAAK,MAAM,GAAG,GAAG;AAC5B,QAAM,UAAU,CAAA;AAChB,aAAW,CAAC,MAAM,KAAK,KAAK,KAAK,UAAU;AACzC,WAAO,KAAK,QAAQ,IAAI,OAAO,yCAAyC,GAAG,kBAAkB,IAAI,GAAG,EAAE;AACtG,QAAI,MAAO,SAAQ,KAAK,SAAS,IAAI,KAAK,GAAG,cAAc,WAAW,KAAK,CAAC,IAAI;AAAA,EAClF;AACA,SAAO,KAAK,QAAQ,kDAAkD,EAAE;AACxE,MAAI,KAAK,UAAW,SAAQ,KAAK,+BAA+B,WAAW,KAAK,SAAS,CAAC,IAAI;AAC9F,SAAO,KAAK,QAAQ,oDAAoD,EAAE;AAC1E,aAAW,EAAE,UAAU,IAAG,KAAM,KAAK,cAAc,IAAI;AACrD,YAAQ,KAAK,mCAAmC,WAAW,QAAQ,CAAC,WAAW,WAAW,GAAG,CAAC,IAAI;AAAA,EACpG;AACA,SAAO,GAAG,IAAI,GAAG,QAAQ,KAAK,IAAI,CAAC;AAAA,EAAK,KAAK,MAAM,GAAG,CAAC;AACzD;AAuBO,SAAS,gBAAgB,EAAE,UAAU,SAAS,YAAW,GAAI;AAYlE,QAAM,mBAAmB,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,eAAe,EAAE,KAAK,CAAC,KAAK,CAAA;AACrG,QAAM,gBAAgB,iBAAiB,IAAI,CAAC,MAAM,oBAAoB,EAAE,KAAK,EAAE,MAAM,MAAM;AAE3F,MAAI,OAAO;AAIX,QAAM,eAAe,QAAQ,gBAAe;AAC5C,MAAI,cAAc;AAChB,UAAM,iBAAiB,WAAW,cAAc,OAAO;AACvD,QAAI,kBAAkB,CAAC,eAAe,OAAO;AAC3C,aAAO,kBAAkB,MAAM,eAAe,iBAAiB,cAAc;AAAA,QAC3E,oBAAoB,eAAe;AAAA,MAC3C,CAAO;AAAA,IACH;AAAA,EACF,OAAO;AACL,UAAM,WAAW,QAAQ,YAAY;AACrC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,kBAAkB,eAAe,QAAQ,CAAC;AAAA,IAChD;AAAA,EACE;AAIA,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,cAAc,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG;AAG/D,UAAM,gBACJ,8BACU,WAAW;AAIvB,WAAO,KAAK,QAAQ,WAAW,GAAG,aAAa;AAAA,QAAW;AAAA,EAC5D;AAEA,SAAO,EAAE,MAAM,iBAAiB,CAAC,CAAC,aAAY;AAChD;ACjvBO,SAAS,qBAAqB,EAAE,WAAW,IAAI,MAAM,OAAO,OAAO,YAAY,KAAI,IAAK,IAAI;AACjG,QAAM,UAAU,CAAC,OAAO,UAAU,aAAa,WAAW,OAAO,OAAO,IAAI;AAC5E,QAAM,aAAa,YAAY,aAAa,MAAM,SAAS,QAAQ,OAAO,EAAE,IAAI;AAUhF,QAAM,YAAY,oBAAI,IAAG;AACzB,QAAM,aAAa,CAAC,SAAS,QAAQ;AAInC,QAAI,OAAO,QAAQ,WAAW,YAAY,CAAC,QAAQ,QAAQ;AACzD,aAAO,QAAQ,QAAQ;AAAA,QACrB,MAAM;AAAA,QACN,OAAO,8EACD,QAAQ,SAAS,QAAQ,EAAE;AAAA,MACzC,CAAO;AAAA,IACH;AACA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,MAAM,kBAAkB,QAAQ,KAAK,UAAU;AACrD,UAAI,QAAQ,UAAU,IAAI,GAAG;AAC7B,UAAI,CAAC,OAAO;AACV,gBAAQ,CAAA;AACR,kBAAU,IAAI,KAAK,KAAK;AACxB,uBAAe,MAAM;AACnB,oBAAU,OAAO,GAAG;AACpB,qBAAW,KAAK,OAAO,OAAO;AAAA,QAChC,CAAC;AAAA,MACH;AACA,YAAM,KAAK,EAAE,SAAS,KAAK,QAAO,CAAE;AAAA,IACtC,CAAC;AAAA,EACH;AAaA,QAAM,QAAQ,oBAAI,IAAG;AACrB,QAAM,WAAW,CAAC,QAAQ,SAAS;AACjC,UAAM,MAAM,GAAG,KAAK,MAAM,IAAI,MAAM,IAAI,KAAK,QAAQ,EAAE;AACvD,QAAI,UAAU,MAAM,IAAI,GAAG;AAC3B,QAAI,CAAC,SAAS;AACZ,gBAAU,aAAa,SAAS,QAAQ,IAAI,EAAE,QAAQ,MAAM,MAAM,OAAO,GAAG,CAAC;AAC7E,YAAM,IAAI,KAAK,OAAO;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,SAAS,SAAS;AAChB,aAAO,eAAe,OAAO;AAAA,IAC/B;AAAA,IAEA,MAAM,QAAQ,SAAS,MAAM,IAAI;AAC/B,UAAI,CAAC,QAAS,QAAO,EAAE,MAAM,KAAI;AACjC,UAAI,QAAQ,IAAK,QAAO,WAAW,SAAS,GAAG;AAC/C,YAAM,EAAE,MAAM,KAAK,WAAW,MAAM,QAAO,IAAK;AAKhD,UAAI,UAAU,QAAQ,UAAU,OAAO,YAAW;AAClD,UAAI,WAAW,SAAS,WAAW,QAAQ;AACzC,gBAAQ,KAAK,6BAA6B,QAAQ,MAAM,2CAA2C;AACnG,iBAAS;AAAA,MACX;AAEA,UAAI;AACJ,UAAI,MAAM;AAER,iBAAS,cAAc,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,IAAI,IAChE,aAAa,OACb;AAAA,MACN,WAAW,KAAK;AAEd,iBAAS;AAAA,MACX,OAAO;AACL,eAAO,EAAE,MAAM,IAAI,OAAO,gCAA+B;AAAA,MAC3D;AAEA,YAAM,OAAO,EAAE,OAAM;AAErB,UAAI,WAAW,QAAQ;AAKrB,cAAM,KAAK,QAAQ;AACnB,cAAM,OAAQ,YAAY,UAAa,YAAY,QAAQ,MAAM,GAAG,YAChE,uBAAuB,SAAS,EAAE,CAAC,GAAG,SAAS,GAAG,GAAG,WAAU,GAAI,EAAE,QAAQ,MAAK,CAAE,IACpF;AACJ,YAAI,SAAS,UAAa,SAAS,MAAM;AACvC,eAAK,UAAU,EAAE,gBAAgB,mBAAkB;AACnD,eAAK,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAAA,QACnE;AAAA,MACF;AAEA,UAAI;AACF,cAAM,WAAW,MAAM,SAAS,QAAQ,IAAI;AAC5C,YAAI,IAAI,QAAQ,QAAS,QAAO,EAAE,MAAM,CAAA,GAAI,OAAO,UAAS;AAE5D,YAAI,CAAC,SAAS,IAAI;AAChB,iBAAO,EAAE,MAAM,IAAI,OAAO,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU,GAAE;AAAA,QAC7E;AAEA,YAAI,OAAO,SAAS;AAIpB,YAAI,aAAa,SAAS,QAAQ,SAAS,QAAW;AACpD,iBAAO,eAAe,MAAM,SAAS;AAAA,QACvC;AAMA,eAAO,eAAe,MAAM,SAAS,EAAE,KAAK,QAAQ,WAAW,SAAS,GAAG,EAAC,CAAE;AAO9E,cAAM,QAAQ,OAAO,QAAQ,UAAU,YAAY,QAAQ,QAAQ;AACnE,eAAO,UAAU,SAAY,EAAE,MAAM,QAAQ,CAAA,EAAE,IAAK,EAAE,MAAM,QAAQ,CAAA,GAAI,MAAM,EAAE,MAAK,EAAE;AAAA,MACzF,SAAS,OAAO;AACd,YAAI,OAAO,SAAS,cAAc;AAChC,iBAAO,EAAE,MAAM,IAAI,OAAO,UAAS;AAAA,QACrC;AACA,eAAO,EAAE,MAAM,IAAI,OAAO,OAAO,WAAW,OAAO,KAAK,EAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACJ;AACA;AA4BO,SAAS,WAAW,SAAS;AAClC,QAAM,IAAI,EAAE,QAAQ,QAAQ,OAAM;AAClC,QAAM,QAAQ,QAAQ,SAAS,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAInF,QAAM,QAAQ,OAAO,QAAQ,UAAU,YAAY,QAAQ,QAAQ,QAAQ,QAAQ;AACnF,MAAI,MAAO,GAAE,QAAQ;AACrB,MAAI,MAAO,GAAE,QAAQ;AACrB,QAAM,OAAO,SAAS,QAAQ,IAAI;AAClC,MAAI,KAAM,GAAE,OAAO;AACnB,MAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,QAAQ,EAAG,GAAE,QAAQ,QAAQ;AAG9E,MAAI,QAAQ,UAAU,KAAM,GAAE,QAAQ;AACtC,QAAM,SAAS,SAAS,QAAQ,MAAM;AACtC,MAAI,OAAQ,GAAE,SAAS;AAGvB,SAAO;AACT;AAgBA,SAAS,SAAS,QAAQ;AACxB,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI,CAAA;AACV,MAAI,OAAO,SAAS,OAAO,OAAO,UAAU,SAAU,GAAE,QAAQ,OAAO;AACvE,MAAI,OAAO,SAAS,OAAO,OAAO,UAAU,SAAU,GAAE,QAAQ,OAAO;AACvE,QAAM,OAAO,SAAS,OAAO,IAAI;AACjC,MAAI,KAAM,GAAE,OAAO;AACnB,MAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAQ,GAAE,SAAS,OAAO;AAC1E,MAAI,OAAO,OAAO,UAAU,YAAY,OAAO,QAAQ,EAAG,GAAE,QAAQ,OAAO;AAC3E,SAAO,OAAO,KAAK,CAAC,EAAE,SAAS,IAAI,IAAI;AACzC;AAUA,SAAS,SAAS,MAAM;AACtB,MAAI;AACF,WAAO,WAAW,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO,OAAO,SAAS,YAAY,KAAK,KAAI,IAAK,OAAO;AAAA,EAC1D;AACF;AA2CA,MAAM,oBAAoB;AAC1B,eAAe,WAAW,KAAK,OAAO,SAAS;AAG7C,QAAM,UAAU,oBAAI,IAAG;AACvB,QAAM,OAAO,CAAA;AACb,QAAM,OAAO,CAAA;AACb,aAAW,SAAS,OAAO;AACzB,UAAM,OAAO,MAAM,QAAQ,MAAM;AACjC,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,OAAO,MAAM,KAAK,EAAG,OAAM,GAAG,IAAI,IAAI,CAAC;AACvD,SAAK,KAAK,GAAG;AACb,SAAK,GAAG,IAAI,WAAW,MAAM,OAAO;AAAA,EACtC;AACA,MAAI;AACJ,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,MAClC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAkB;AAAA,MAC7C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC/B,CAAK;AACD,QAAI,CAAC,SAAS,IAAI;AAMhB,UAAI,SAAS;AACb,UAAI;AACF,cAAM,UAAU,cAAc,MAAM,SAAS,KAAI,CAAE;AACnD,YAAI,WAAW,OAAO,QAAQ,WAAW,YAAY,QAAQ,OAAQ,UAAS,QAAQ;AAAA,MACxF,QAAQ;AAAA,MAAyD;AACjE,YAAM,QAAQ,SACV,QAAQ,SAAS,MAAM,KAAK,MAAM,KAClC,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AACnD,iBAAW,SAAS,MAAO,OAAM,QAAQ,EAAE,MAAM,MAAM,MAAK,CAAE;AAC9D;AAAA,IACF;AACA,aAAS,MAAM,SAAS,KAAI;AAAA,EAC9B,SAAS,OAAO;AACd,UAAM,UAAU,OAAO,SAAS,eAAe,YAAa,OAAO,WAAW,OAAO,KAAK;AAI1F,eAAW,SAAS,OAAO;AACzB,YAAM,OAAO,MAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,UAAU,SAAS,MAAM,YAAY;AAC1F,YAAM,QAAQ,OACV,EAAE,MAAM,MAAM,OAAO,SAAS,MAAM,SAAS,EAAE,SAAS,MAAM,OAAO,MAAM,KAAI,CAAE,EAAC,IAClF,EAAE,MAAM,MAAM,OAAO,QAAO,CAAE;AAAA,IACpC;AACA;AAAA,EACF;AACA,QAAM,OAAO,UAAU,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,OAAO,OAAO,CAAA;AAItF,QAAM,SAAS,UAAU,OAAO,OAAO,UAAU,YAAY,OAAO,QAAQ,OAAO,QAAQ,CAAA;AAC3F,QAAM,UAAU,UAAU,OAAO,OAAO,YAAY,YAAY,OAAO,UAAU,OAAO,UAAU,CAAA;AAGlG,QAAM,WAAW,UAAU,OAAO,OAAO,YAAY,YAAY,OAAO,UAAU,OAAO,UAAU,CAAA;AACnG,QAAM,QAAQ,CAAC,OAAO,MAAM;AAC1B,UAAM,MAAM,KAAK,CAAC;AAKlB,UAAM,OAAO,MAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,UAAU,SAAS,MAAM,YAAY;AAC1F,QAAI,EAAE,OAAO,OAAO;AAClB,YAAM,QAAQ,iDAAiD,GAAG;AAClE,YAAM,QAAQ,OACV,EAAE,MAAM,MAAM,OAAO,MAAM,SAAS,EAAE,SAAS,MAAM,OAAO,MAAM,KAAI,CAAE,EAAC,IACzE,EAAE,MAAM,MAAM,MAAK,CAAE;AACzB;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,OAAO,GAAG,MAAM,YACjC,OAAO,GAAG,IACT,OAAO,MAAM,QAAQ,UAAU,YAAY,MAAM,QAAQ,QAAQ;AAEtE,UAAM,SAAS,OAAO,QAAQ,GAAG,MAAM,YAAY,QAAQ,GAAG,IAAI,QAAQ,GAAG,IAAI;AACjF,UAAM,OAAO,MAAM,QAAQ,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,KAAK,GAAG;AAG5D,QAAI,UAAU,MAAM,QAAQ,cAAc,MAAM,QAAQ,IAAI,GAAG;AAC7D,YAAM,MAAM,MAAM,YAAY,MAAM,UAAU,OAAO,IAAI,IAAI,KAAK,MAAK;AACvE,YAAM,QAAQ,MAAM,QAAQ,KAAK;AACjC,YAAM,MAAM,OAAO,MAAM,QAAQ,aAAa,YAAY,MAAM,QAAQ,WAAW,IAC/E,MAAM,QAAQ,WACd;AACJ,UAAI,QAAQ,KAAK;AACf,gBAAQ,IAAI,OAAO,EAAE,QAAQ,WAAW,KAAK,MAAM,MAAK,CAAE;AAC1D;AAAA,MACF;AAEA,YAAM,QAAQ,EAAE,MAAM,KAAK,MAAM,SAAS,EAAE,OAAO,SAAS,MAAM,OAAO,IAAG,CAAE,EAAC,CAAE;AACjF;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,YAAY,MAAM,UAAU,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAA,CAAE,IAAI;AAC9F,UAAM,OAAO,SAAS;AAAA,MACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYA,SAAU,UAAU,OAAO,WAAY,OAAO;AAAA,MAC9C,aAAa,OAAO,WAAW,SAAS,GAAG,IAAI;AAAA,MAC/C,OAAO,MAAM,QAAQ,MAAM,OAAO,IAAI,MAAM,OAAO;AAAA,IACzD,CAAK;AACD,UAAM,QAAQ,OAAO,EAAE,MAAM,WAAW,KAAI,IAAK,EAAE,MAAM,UAAS,CAAE;AAAA,EACtE,CAAC;AAED,MAAI,QAAQ,SAAS,EAAG;AAMxB,QAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,KAAK,MAAM;AACrD,UAAM,OAAO;AAAA,MACX,GAAG;AAAA,MACH,SAAS,EAAE,GAAG,MAAM,SAAS,QAAQ,EAAE,GAAI,MAAM,QAAQ,UAAU,CAAA,GAAK,QAAQ,MAAM,OAAM,EAAE;AAAA,MAC9F,WAAW,MAAM;AAAA,MACjB,MAAM,MAAM;AAAA,IAClB;AACI,WAAO,WAAW,KAAK,CAAC,IAAI,GAAG,OAAO;AAAA,EACxC,CAAC,CAAC;AACJ;AAeA,eAAe,aAAa,SAAS,QAAQ,MAAM;AACjD,QAAM,WAAW,MAAM,QAAQ,QAAQ,IAAI;AAC3C,MAAI,CAAC,SAAS,IAAI;AAChB,QAAIA,QAAO;AACX,QAAI;AACF,MAAAA,QAAO,MAAM,SAAS,KAAI;AAAA,IAC5B,QAAQ;AAAA,IAER;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,SAAS,QAAQ,YAAY,SAAS,YAAY,MAAAA,MAAI;AAAA,EACpF;AACA,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,MAAI,YAAY,SAAS,kBAAkB,EAAG,QAAO,EAAE,IAAI,MAAM,MAAM,MAAM,SAAS,KAAI,EAAE;AAC5F,QAAM,OAAO,MAAM,SAAS,KAAI;AAChC,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,MAAM,KAAK,MAAM,IAAI,EAAC;AAAA,EAC3C,QAAQ;AACN,QAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,aAAO,EAAE,IAAI,OAAO,QAAQ,SAAS,QAAQ,YAAY,0BAA0B,MAAM,KAAI;AAAA,IAC/F;AACA,WAAO,EAAE,IAAI,MAAM,MAAM,KAAI;AAAA,EAC/B;AACF;AAEA,MAAM,gBAAgB;AAGtB,SAAS,SAAS,QAAQ;AACxB,QAAM,MAAM,CAAA;AACZ,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,MAAM,OAAW,KAAI,CAAC,IAAI;AAC3E,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AACzC;AAOA,SAAS,WAAW,SAAS,KAAK;AAChC,MAAI,OAAO,SAAS,WAAW,YAAY,QAAQ,OAAQ,QAAO,QAAQ;AAC1E,MAAI,OAAO,KAAK,WAAW,YAAY,IAAI,OAAQ,QAAO,IAAI;AAC9D,QAAM,SAAS,KAAK,SAAS,kBAAe;AAC5C,SAAO,OAAO,WAAW,YAAY,SAAS,SAAS;AACzD;AAUA,SAAS,eAAe,MAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAI,IAAK,IAAI;AAC1E,SAAO,cAAc,MAAM,SAAS;AAAA,IAClC;AAAA,IACA,MAAM,CAAC,OAAO,UAAU,YAAY,UAAU,OAAO,UAAU,KAAK,QAAQ,MAAM;AAAA,EACtF,CAAG;AACH;AASA,MAAM,iBAAiB,oBAAI,IAAG;AAC9B,SAAS,UAAU,OAAO,UAAU,KAAK,QAAQ;AAC/C,MAAI;AACF,WAAO,YAAY,OAAO,UAAU,EAAE,OAAM,CAAE;AAAA,EAChD,SAAS,KAAK;AACZ,QAAI,IAAK,OAAM;AACf,UAAM,MAAM,OAAO,QAAQ;AAC3B,QAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC5B,qBAAe,IAAI,GAAG;AACtB,cAAQ,MAAM,qBAAqB,IAAI,OAAO,8BAA8B;AAAA,IAC9E;AACA,WAAO;AAAA,EACT;AACF;AAMA,SAAS,eAAe,KAAK,MAAM;AACjC,MAAI,CAAC,OAAO,CAAC,KAAM,QAAO;AAC1B,MAAI,UAAU;AACd,aAAW,QAAQ,KAAK,MAAM,GAAG,GAAG;AAClC,QAAI,YAAY,QAAQ,YAAY,OAAW,QAAO;AACtD,cAAU,QAAQ,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAOA,SAAS,cAAc,MAAM;AAC3B,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;ACjlBO,SAAS,iBAAiBC,QAAO,OAAO;AAC7C,MAAI,OAAOA,WAAU,WAAY,QAAOA;AACxC,QAAM,IAAI;AAAA,IACR,GAAG,KAAK;AAAA,EAGZ;AACA;ACtBO,SAAS,SAAS,MAAM;AAC7B,QAAM,MAAM,CAAA;AACZ,QAAM,OAAO,CAAC,SAAS;AACrB,eAAW,SAAS,QAAQ,IAAI;AAC9B,UAAI,CAAC,MAAO;AACZ,UAAI,KAAK,KAAK;AACd,WAAK,MAAM,WAAW;AACtB,WAAK,MAAM,MAAM;AAAA,IACnB;AAAA,EACF;AACA,OAAK,KAAK,gBAAa,KAAQ,KAAK,UAAU;AAC9C,aAAW,QAAQ,OAAO,OAAO,KAAK,sBAAsB,CAAA,CAAE,EAAG,MAAK,IAAI;AAC1E,SAAO;AACT;ACIO,MAAM,eAAe;AAGrB,MAAM,oBAAoB;AAGjC,SAAS,mBAAmB,KAAK;AAC/B,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,MAAI,QAAQ,EAAG,QAAO,EAAE,SAAS,IAAI,MAAM,GAAG,KAAK,GAAG,MAAM,IAAI,MAAM,QAAQ,CAAC,EAAC;AAChF,QAAM,OAAO,IAAI,QAAQ,GAAG;AAC5B,MAAI,OAAO,KAAK,OAAO,EAAG,QAAO;AACjC,QAAM,UAAU,IAAI,MAAM,GAAG,IAAI;AACjC,SAAO,gBAAgB,OAAO,IAAI,EAAE,SAAS,MAAM,IAAI,MAAM,OAAO,CAAC,MAAM;AAC7E;AAEA,SAAS,MAAM,MAAM,OAAO;AAC1B,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,MAAI,KAAK,SAAS,WAAW,KAAK,OAAO,SAAS,OAAQ,OAAM,KAAK,KAAK,KAAK;AAC/E,aAAW,QAAQ,KAAK,SAAS,CAAA,GAAI;AACnC,UAAM,MAAM,mBAAmB,MAAM,OAAO,IAAI;AAChD,QAAI,IAAK,OAAM,KAAK,GAAG;AAAA,EACzB;AACA,MAAI,MAAM,QAAQ,KAAK,OAAO,EAAG,YAAW,SAAS,KAAK,QAAS,OAAM,OAAO,KAAK;AACvF;AAWO,SAAS,UAAU,QAAQ;AAChC,QAAM,QAAQ,CAAA;AACd,aAAW,SAAS,OAAQ,OAAM,MAAM,YAAY,OAAO,MAAM,YAAY,KAAK;AAClF,QAAM,QAAQ,oBAAI,IAAG;AACrB,aAAW,EAAE,SAAS,KAAI,KAAM,OAAO;AACrC,UAAM,SAAS,gBAAgB,OAAO,OAAO,EAAE,YAAW,CAAE;AAC5D,QAAI,UAAU,KAAM,OAAM,IAAI,GAAG,OAAO,IAAI,IAAI,IAAI,EAAE,QAAQ,KAAI,CAAE;AAAA,EACtE;AACA,SAAO;AACT;AAaO,eAAe,cAAc,EAAE,QAAQ,SAAS,OAAAA,QAAO,MAAK,GAAI;AACrE,QAAM,SAAS,QAAQ,QAAQ;AAC/B,MAAI,QAAQ,QAAQ,MAAO;AAC3B,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,CAAC,GAAG,UAAU,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EAAE,MAAM,GAAG,iBAAiB;AACnG,MAAI,OAAO,WAAW,EAAG;AAEzB,QAAM,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,QAAQ,KAAI,CAAE,MAAM;AACpE,QAAI;AACF,YAAM,WAAW,MAAMA,OAAM,QAAQ,QAAQ,MAAM,IAAI,GAAG,EAAE,QAAQ,MAAK,CAAE;AAC3E,UAAI,CAAC,SAAS,GAAI;AAClB,YAAM,MAAM,MAAM,SAAS,KAAI;AAC/B,UAAI,aAAa,KAAK,GAAG,EAAG,OAAM,IAAI,KAAK,GAAG;AAAA,IAChD,QAAQ;AAAA,IAER;AAAA,EACF,CAAC,CAAC;AACF,MAAI;AACJ,QAAM,SAAS,IAAI,QAAQ,CAAC,YAAY;AAAE,YAAQ,WAAW,SAAS,YAAY;AAAA,EAAE,CAAC;AACrF,QAAM,QAAQ,KAAK,CAAC,KAAK,MAAM,CAAC;AAChC,eAAa,KAAK;AACpB;AAcO,SAAS,kBAAkB,QAAQ,MAAM,MAAM;AACpD,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,MAAO,QAAO,KAAI;AACvB,QAAM,EAAE,IAAG,IAAK;AAChB,QAAM,MAAM,CAAC,QAAQ;AACnB,UAAM,MAAM,IAAI,KAAK,OAAO,GAAG;AAC/B,QAAI,IAAK,MAAK,IAAI,KAAK,GAAG;AAC1B,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAI;AAAA,EACb,UAAC;AACC,WAAO,MAAM;AAAA,EACf;AACF;ACvEO,eAAe,aAAa,EAAE,SAAS,OAAO,OAAAA,QAAO,MAAM,OAAO,SAAS,MAAM,YAAY,UAAU,QAAQ,MAAM,QAAQ,QAAQ;AAC1I,MAAI,cAAc,YAAY,cAAc,UAAU;AACpD,UAAM,IAAI,MAAM,6DAA6D,KAAK,UAAU,SAAS,CAAC,EAAE;AAAA,EAC1G;AAGA,mBAAiBA,QAAO,cAAc;AACtC,QAAM,SAAS,SAAS,uBAAuB;AAC/C,MAAI,UAAU,UAAU,WAAW,QAAQ;AACzC,UAAM,IAAI;AAAA,MACR,sCAAsC,KAAK,UAAU,MAAM,CAAC,6BACzD,KAAK,UAAU,MAAM,CAAC;AAAA,IAE/B;AAAA,EACE;AAKA,UAAQ,YAAW;AACnB,QAAM,YAAY,WAAW,QAAQ;AACrC,QAAM,QAAQ,OAAO,UAAU,WAAW,qBAAqB,SAAS,KAAK,IAAI;AAIjF,QAAM,WAAW,UAAU,OAAO,UAAU,WAAW,QAAQ,gBAAe,IAAK;AACnF,MAAI,CAAC,SAAU,QAAO,CAAA;AAGtB,QAAM,OAAO,OAAO,SAAS,sBAAsB,aAAa,SAAS,sBAAsB;AAM/F,MAAI,OAAO,KAAK,kBAAkB,YAAY;AAC5C,UAAM,IAAI,MAAM,2HAA2H;AAAA,EAC7I;AAKA,QAAM,aAAa,QAAQ,cAAc;AAAA,IACvC,WAAW,SAAS,IAAI,UAAS;AAAA,IACjC,gBAAgB,qBAAqB,EAAE,UAAU,QAAQ,QAAQ,QAAQ,IAAI,KAAK,OAAAA,QAAO;AAAA,EAC7F,CAAG;AAGD,QAAM,UAAU,oBAAI,IAAG;AACvB,QAAM,SAAS,CAAC,SAAS,UAAU;AACjC,UAAM,MAAMC,iBAAe,OAAO;AAClC,QAAI,CAAC,QAAQ,IAAI,GAAG,EAAG,SAAQ,IAAI,KAAK,KAAK;AAAA,EAC/C;AAOA,QAAM,WAAW,IAAI,UAAS;AAE9B,QAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhB,MAAM,CAAC,YAAY,SAAS,IAAIA,iBAAe,OAAO,CAAC;AAAA,IACvD,YAAY,CAAC,OAAO,SAAS,UAAU,EAAE;AAAA,IACzC,UAAU,OAAO,SAAS,QAAQ;AAChC,UAAI,cAAc,YAAY,SAAS,cAAc,OAAO;AAG1D,eAAO,SAAS,EAAE,QAAQ,SAAS,SAAS,WAAW,MAAM,KAAI,CAAE;AACnE,eAAO,EAAE,MAAM,KAAI;AAAA,MACrB;AACA,YAAM,SAAS,MAAM,WAAW,SAAS,SAAS,GAAG;AACrD,UAAI,CAAC,QAAQ,SAAS,QAAQ,QAAQ,MAAM;AAC1C,iBAAS,IAAIA,iBAAe,OAAO,GAAG,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,KAAI,IAAK,EAAE,MAAM,OAAO,KAAI,CAAE;AAAA,MACtH;AACA,aAAO,SAAS,QAAQ,QACpB,EAAE,QAAQ,SAAS,SAAS,UAAU,MAAM,MAAM,OAAO,OAAO,MAAK,IACrE,EAAE,QAAQ,SAAS,SAAS,WAAW,MAAM,QAAQ,QAAQ,MAAM,GAAI,QAAQ,OAAO,EAAE,MAAM,OAAO,KAAI,IAAK,CAAA,EAAG,CAAE;AACvH,aAAO;AAAA,IACT;AAAA,EACJ;AAEE,QAAM,QAAQ,QAAQ;AACtB,QAAM,SAAS,SAAS,IAAI;AAI5B,QAAM,QAAQ,IAAI;AAAA;AAAA;AAAA,IAGhB,MAAM,gBAAgB,MAAM,EAAE,YAAY,UAAS,CAAE;AAAA,IACrD,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,MAAM,OAAO,iBAAiB,MAAM,IAAI,GAAG,EAAE,YAAY,UAAS,CAAE,CAAC;AAAA,IACpG,SAAS,aAAa,cAAc,EAAE,QAAQ,SAAS,OAAAD,QAAO,OAAO,WAAW;AAAA,EACpF,CAAG;AAED,SAAO,CAAC,GAAG,QAAQ,OAAM,CAAE;AAC7B;AC9GO,SAAS,mBAAmB,EAAE,SAAS,SAAS;AACrD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,2CAA2C;AACzE,MAAI,OAAO,UAAU,SAAU,OAAM,IAAI,MAAM,oDAAoD;AAwCnG,WAAS,OAAO,QAAQ,EAAE,SAAS,CAAA,GAAI,YAAW,IAAK,IAAI;AACzD,QAAI,gBAAgB,UAAa,CAAC,MAAM,QAAQ,WAAW,GAAG;AAC5D,YAAM,IAAI,MAAM,iFAAiF;AAAA,IACnG;AAKA,YAAQ,YAAW;AAMnB,QAAI,gBAAgB,QAAW;AAC7B,cAAQ,WAAU;AAClB,uBAAiB,SAAS,WAAW;AAAA,IACvC;AACA,UAAM,QAAQ,OAAO,WAAW,WAAW,qBAAqB,SAAS,MAAM,IAAI;AAQnF,UAAM,WAAW,UAAU,OAAO,WAAW,WAAW,QAAQ,gBAAe,IAAK;AAKpF,QAAI,CAAC,SAAU,QAAO,EAAE,SAAS,YAAY,MAAM,MAAM,MAAM,MAAM,OAAO,KAAI;AAYhF,UAAM,OAAO,OAAO,SAAS,sBAAsB,aAAa,SAAS,sBAAsB;AAW/F,UAAM,UAAU,CAAC,SAAS,MAAM,aAAa;AAS7C,qBAAiB,OAAO;AACxB,QAAI,OAAO,WAAW,SAAU,SAAQ,cAAc,MAAM;AAE5D,UAAM,QAAQ,oBAAI,IAAG;AACrB,QAAI;AACJ,QAAI;AACF,eAAS,kBAAkB,WAAW,QAAQ,OAAO,MAAM,WAAW,MAAM,OAAO,CAAC;AAAA,IACtF,SAAS,KAAK;AAIZ,aAAO,EAAE,SAAS,UAAU,aAAa,UAAU,MAAM,MAAM,MAAM,OAAO,oBAAoB,GAAG,EAAC;AAAA,IACtG;AAEA,QAAI,OAAO,MAAO,QAAO,EAAE,SAAS,UAAU,aAAa,UAAU,MAAM,MAAM,MAAM,OAAO,OAAO,MAAK;AAQ1G,UAAM,OAAO,kBAAkB,OAAO,OAAO,iBAAiB,MAAM;AAAA,MAClE,GAAG;AAAA,MACH,oBAAoB,OAAO;AAAA,MAC3B;AAAA,IACN,CAAK;AACD,WAAO,EAAE,SAAS,UAAU,aAAa,YAAY,MAAM,MAAM,OAAO,KAAI;AAAA,EAC9E;AAEA,SAAO,EAAE,SAAS,OAAM;AAC1B;AAyBO,eAAe,mBAAmB,EAAE,SAAS,SAAS,OAAO,SAAS,MAAM,OAAAA,SAAQ,MAAM,MAAM,OAAO,YAAY,SAAQ,GAAI;AAKpI,MAAI,CAAC,SAAS,WAAW;AACvB,UAAM,IAAI,MAAM,+EAA+E;AAAA,EACjG;AAMA,mBAAiBA,QAAO,oBAAoB;AAI5C,QAAM,UAAU,MAAM,aAAa,EAAE,SAAS,OAAO,OAAAA,QAAO,KAAK,QAAQ,UAAS,CAAE;AACpF,mBAAiB,SAAS,OAAO;AACjC,SAAO;AACT;ACvJO,eAAe,mBACpB,SACA,EAAE,QAAQ,OAAAA,QAAO,QAAQ,OAAO,MAAM,QAAQ,OAAO,SAAQ,IAAK,CAAA,GAClE;AAGA,mBAAiBA,QAAO,oBAAoB;AAE5C,QAAM,SAAS,SAAS;AACxB,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,UAAU,QAAQ,WAAW;AAInC,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO,MAAK;AACzD,MAAI,CAAC,sBAAsB,UAAU,MAAM,EAAG,QAAO,MAAK;AAE1D,QAAM,QAAQ,OAAO,KAAK,OAAO,EAAE,OAAO,CAAC,MAAO,OAAO,KAAK,SAAS,CAAC,IAAI,IAAK;AACjF,MAAI,MAAM,WAAW,EAAG,QAAO,MAAK;AAIpC,QAAM,UAAU;AAAA,IACd,MAAM,IAAI,CAAC,UAAU,EAAE,OAAO,MAAM,IAAI,KAAI,EAAG;AAAA,IAC/C,EAAE,UAAU,SAAS,QAAQ,eAAe,QAAQ,mBAAmB,OAAM;AAAA,EACjF;AAEE,QAAM,UAAU,qBAAqB,EAAE,OAAAA,QAAO,UAAU,QAAQ,QAAQ,GAAE,CAAE;AAC5E,QAAM,UAAU,CAAA;AAChB,QAAM,SAAS,CAAA;AACf,QAAM,OAAO,CAAA;AAEb,QAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,EAAE,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM;AAIxD,QAAI,CAAC,IAAI,IAAK;AAEd,UAAM,QAAQ,EAAE,GAAG,KAAK,OAAO,YAAY,KAAI;AAC/C,QAAI,OAAO,aAAa,YAAY,WAAW,EAAG,OAAM,WAAW;AAEnE,UAAM,SAAS,MAAM,QAAQ,QAAQ,OAAO,EAAE,OAAM,CAAE;AACtD,QAAI,QAAQ,MAAO,QAAO,IAAI,IAAI,OAAO;AAIzC,QAAI,MAAM,QAAQ,QAAQ,IAAI,EAAG,SAAQ,IAAI,IAAI,OAAO;AAAA,aAC/C,CAAC,QAAQ,MAAO,SAAQ,IAAI,IAAI,CAAA;AACzC,QAAI,QAAQ,KAAM,MAAK,IAAI,IAAI,OAAO;AAAA,EACxC,CAAC,CAAC;AAEF,SAAO,EAAE,SAAS,QAAQ,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS,MAAM,KAAI;AAC5E;AAEA,SAAS,QAAQ;AACf,SAAO,EAAE,SAAS,CAAA,GAAI,QAAQ,MAAM,MAAM,CAAA,EAAE;AAC9C;"}
|
|
1
|
+
{"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/background-shared.js","../src/background.js","../src/default-404.js","../src/wire-foundation.js","../src/sticky.js","../src/area-wrappers.js","../src/default-layout.js","../src/appearance.js","../src/document-title.js","../src/start-page.js","../src/resolve-page.js","../src/ssr-renderer.js","../src/default-fetcher.js","../src/require-transport.js","../src/page-blocks.js","../src/page-icons.js","../src/load-page-data.js","../src/page-renderer.js","../src/collect-records.js"],"sourcesContent":["/**\n * Props Preparation for Runtime Guarantees\n *\n * Prepares props for foundation components with:\n * - Param defaults from runtime schema\n * - Guaranteed content structure (no null checks needed)\n * - `content.data` holding the keys the component declares, each as it arrived\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\n/**\n * Guarantee item has flat content structure\n *\n * @param {Object} item - Raw item from parser\n * @returns {Object} Item with guaranteed flat structure\n */\nfunction guaranteeItemStructure(item) {\n return {\n title: item.title || '',\n pretitle: item.pretitle || '',\n subtitle: item.subtitle || '',\n paragraphs: item.paragraphs || [],\n links: item.links || [],\n images: item.images || [],\n lists: item.lists || [],\n icons: item.icons || [],\n videos: item.videos || [],\n // ⛔ An entry's own insets were left out until 2026-09-29, as `tables` was: the\n // parser delivered them and this object, built key by key, did not carry them.\n insets: item.insets || [],\n media: item.media || [],\n snippets: item.snippets || [],\n buttons: item.buttons || [],\n data: item.data || {},\n cards: item.cards || [],\n documents: item.documents || [],\n forms: item.forms || [],\n quotes: item.quotes || [],\n headings: item.headings || [],\n ...(item.math && item.math.length ? { math: item.math } : {}),\n ...(item.tables && item.tables.length ? { tables: item.tables } : {}),\n }\n}\n\n/**\n * Guarantee content structure exists\n * Returns a flat content object with all standard fields guaranteed to exist\n *\n * @param {Object} parsedContent - Raw parsed content from semantic parser (flat structure)\n * @returns {Object} Content with guaranteed flat structure\n */\nexport function guaranteeContentStructure(parsedContent) {\n const content = parsedContent || {}\n\n return {\n // Flat header fields\n title: content.title || '',\n pretitle: content.pretitle || '',\n subtitle: content.subtitle || '',\n alignment: content.alignment || null,\n\n // Flat body fields\n paragraphs: content.paragraphs || [],\n links: content.links || [],\n images: content.images || [],\n lists: content.lists || [],\n icons: content.icons || [],\n videos: content.videos || [],\n insets: content.insets || [],\n // The section's visual media — images, videos and insets on their own line — in\n // the order written, each with its `kind`: what a `media` slot holds (2026-09-29).\n media: content.media || [],\n snippets: content.snippets || [],\n buttons: content.buttons || [],\n data: content.data || {},\n cards: content.cards || [],\n documents: content.documents || [],\n forms: content.forms || [],\n quotes: content.quotes || [],\n headings: content.headings || [],\n\n // Rare collections — surfaced only when present so pages that don't\n // use them don't pay the allocation cost. Foundations that need them\n // should check for presence (content.math?.length) or use\n // content.sequence for in-order rendering.\n //\n // ⛔ `tables` was not passed through until 2026-09-29: the parser delivered it and\n // this object, built key by key, left it out — so a component declaring `tables`\n // received nothing, while `math`, its twin, arrived.\n ...(content.math && content.math.length ? { math: content.math } : {}),\n ...(content.tables && content.tables.length ? { tables: content.tables } : {}),\n\n // Items with guaranteed structure\n items: (content.items || []).map(guaranteeItemStructure),\n\n // Sequence for ordered rendering\n sequence: content.sequence || [],\n\n // Preserve raw content if present\n raw: content.raw,\n }\n}\n\n/**\n * Apply param defaults from runtime schema\n *\n * @param {Object} params - Params from frontmatter\n * @param {Object} defaults - Default values from runtime schema\n * @returns {Object} Merged params with defaults applied\n */\nexport function applyDefaults(params, defaults) {\n if (!defaults || Object.keys(defaults).length === 0) {\n return params || {}\n }\n\n return {\n ...defaults,\n ...(params || {}),\n }\n}\n\n/**\n * ⭐ `content.data` HOLDS THE KEYS THE COMPONENT DECLARES, AND NOTHING ELSE — ruled 2026-09-14\n * [Diego]. Every key in its `meta.js` `data:` and its foundation's `main.js` `data:`\n * (`website.declaredKeys`), each filled, in order, from:\n *\n * 1. what the section holds under that key (`block.heldData`) — a tagged data block, or\n * its own fetch's answer a static build prerendered into it;\n * 2. what the entity store delivered for it — the fetch that fills it (`fillDeclaredKeys`);\n * 3. `null` — nothing fills it, or its fetch is still out (`block.dataLoading`) or failed\n * (`block.dataError[key]`). ⭐ Never `[]`, which is an answer with no records.\n *\n * A key declared `single: true` then holds one record, or `null` (`holdOneRecord`).\n *\n * A tagged block the section holds under a key the component does not declare is left out, and\n * said so in dev (`warnUndeclaredBlocks`); it stays in `content.sequence`. A block inside an item\n * is that item's (`content.items[n].data`), never held here. The data object is rebuilt on each\n * render from those sources, never from the last render's.\n *\n * ⛔ Until 2026-09-14 this merged every delivered key into what the section held, so a\n * section received every fetch that reached it and every tagged block it had.\n */\nfunction assembleData(block, meta, entityData) {\n const declared = block.website?.declaredKeys ? block.website.declaredKeys(meta) : []\n const held = block.heldData || {}\n const data = {}\n for (const [key] of declared) {\n if (held[key] !== undefined) data[key] = held[key]\n else if (entityData && entityData[key] !== undefined) data[key] = entityData[key]\n else data[key] = null\n }\n // What the section holds and this leaves out — the keys the dev warning reports.\n warnUndeclaredBlocks(block, Object.keys(held).filter((key) => !Object.hasOwn(data, key)))\n block.parsedContent.data = data\n return declared\n}\n\n/**\n * ⭐ A KEY DECLARED `single: true` HOLDS ONE RECORD — the first of the list that filled it, or `null`\n * when the list is empty (ruled 2026-10-07 [Diego]).\n * Every source answers a list: a query's records, a parametric page's record (a list of one), a\n * static build's prerendered answer. The store already asked for one (`@uniweb/core/page-data`'s\n * `keyProgram`). A value that is not a list is the record already — a data block holding it, or an\n * external source answering one — and is left as it is.\n *\n * Runs after the records are linked (`linkOwnRecords`), so the record keeps its `$route`. The data\n * object is replaced, never written into.\n */\nfunction holdOneRecord(block, declared) {\n const data = block.parsedContent.data\n let out = null\n for (const [key, , , single] of declared) {\n if (single !== true || !Array.isArray(data?.[key])) continue\n out = out || { ...data }\n out[key] = data[key][0] ?? null\n }\n if (out) block.parsedContent.data = out\n}\n\n/**\n * Say once, in dev, that a tagged block the section holds — a data block or a concept block — did\n * not reach its component: its key is not declared. `assembleData` passes the keys it left out, so\n * this names exactly what the component did not receive. The block is still in `content.sequence`,\n * where a component that renders the sequence finds it.\n *\n * ⭐ AN ITEM'S BLOCK IS NEVER ONE OF THEM. The parser keeps a block inside an item with that item\n * (`content.items[n].data`), which asks no declaration, and only the section's own blocks reach\n * `block.heldData`. ⛔ Until 2026-10-25 this walked `content.sequence`, which lists every element of\n * the section, its items' included, and so reported each card's `meta` block in a card grid as left\n * out of `content.data`, where it never belonged.\n */\nconst warnedBlocks = new Set()\nfunction warnUndeclaredBlocks(block, leftOut) {\n if (!leftOut.length || !block.website?.entityStore?.dev) return\n const sequence = block.parsedContent?.sequence || []\n for (const tag of leftOut) {\n const memo = `${block.type}::${tag}`\n if (warnedBlocks.has(memo)) continue\n // The section's own block under the tag is the first in the sequence: the parser cuts the\n // sequence into slices, and the section's own content is the first (`groups.js`). None means\n // the key holds a fetch's prerendered answer, which is not a block.\n // A data block (```yaml:tag) and a concept block (```md:tag) both land under their tag, so\n // both need the key declared. ⛔ Until 2026-09-29 only a data block was reported.\n const element = sequence.find((e) => (e?.type === 'dataBlock' || e?.type === 'concept_block') && e.tag === tag)\n if (!element) continue\n warnedBlocks.add(memo)\n const kind = element.type === 'dataBlock' ? 'data block' : 'concept block'\n console.warn(\n `[uniweb] ${block.type}: the \\`${tag}\\` ${kind} is not in content.data — ${block.type} does not ` +\n `declare \\`${tag}\\` in its meta.js \\`data:\\`. It stays in content.sequence.`\n )\n }\n}\n\n/**\n * Run the foundation-level data handler on a block, if one is\n * registered. Runs after entity data merge and before the content\n * handler — the handler sees the fully assembled data and can filter,\n * reshape, or augment it before Loom (or any content transform) runs.\n *\n * The handler receives `(data, block)` where data is\n * `block.parsedContent.data`. It returns a new data object, or\n * null/undefined for no change. The returned data replaces\n * `block.parsedContent.data` for all downstream processing — both\n * the content handler and the component see the transformed data.\n *\n * Skipped when the block is still waiting on async data\n * (`block.dataLoading`), or when no handler is registered.\n * Errors are logged and the original data is preserved.\n */\nfunction runDataHandler(block) {\n if (block.dataLoading) return\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.data\n if (typeof handler !== 'function') return\n\n try {\n const result = handler(block.parsedContent.data, block)\n if (result != null && result !== block.parsedContent.data) {\n block.parsedContent.data = result\n }\n } catch (err) {\n console.error('Foundation data handler failed:', err)\n }\n}\n\n/**\n * Run the foundation-level content handler on a block, if one is\n * registered. Runs at prop-preparation time — after the data handler\n * has had a chance to filter/reshape the data — so the handler sees\n * the fully assembled (and possibly filtered) data. Replaces\n * `block.parsedContent` in place with the re-parsed, instantiated\n * form. The handler receives `(data, block)` and reads raw\n * ProseMirror from `block.rawContent`.\n *\n * Skipped when the block is still waiting on async data\n * (`block.dataLoading`), when no handler is registered, when the\n * block has no raw content, when the handler returns a no-change\n * signal (undefined, null, or the same reference as rawContent), when\n * it returns anything but a ProseMirror document, or when the handler\n * throws. Errors are logged via `console.error`.\n *\n * ⛔ ONLY A DOCUMENT IS RE-PARSED (2026-10-05). The handler is given the section's\n * DATA, so the natural no-op — `(x) => x` — returns the data map. That is not\n * `rawContent`, so it passed the no-change check; `parseContent` takes a plain\n * object as content already parsed; and the section's content became its data\n * map — title, paragraphs and items gone — with `data.data` pointing back at\n * itself, so a `JSON.stringify(content.data)` threw far from the handler (and\n * failed a static build's prerender of the page). Any result that is not a\n * document is now ignored, and said once per handler.\n */\nfunction runContentHandler(block) {\n if (block.dataLoading) return\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.content\n if (typeof handler !== 'function') return\n if (!block.rawContent || Object.keys(block.rawContent).length === 0) return\n\n try {\n const transformed = handler(block.parsedContent.data, block)\n if (!transformed || transformed === block.rawContent) return\n if (!isDocument(transformed)) {\n warnNotADocument(handler, transformed, block.parsedContent.data)\n return\n }\n const reparsed = block.parseContent(transformed)\n reparsed.data = block.parsedContent.data\n block.parsedContent = reparsed\n block.items = reparsed.items || []\n } catch (err) {\n console.error('Foundation content handler failed:', err)\n }\n}\n\n/** A ProseMirror document — bare, or wrapped as `{ doc }`, the two forms `rawContent` takes. */\nfunction isDocument(value) {\n return value?.type === 'doc' || value?.doc?.type === 'doc'\n}\n\n/**\n * Say, once per handler, that a content handler returned something that is not a\n * document — naming the likely mistake when it returned the very data it was given.\n */\nconst warnedContentHandlers = new WeakSet()\nfunction warnNotADocument(handler, value, data) {\n if (warnedContentHandlers.has(handler)) return\n warnedContentHandlers.add(handler)\n const what =\n value === data\n ? 'the data it was given'\n : Array.isArray(value)\n ? 'an array'\n : `${typeof value === 'object' ? 'an object' : typeof value}`\n console.warn(\n `[uniweb] handlers.content returned ${what}, not a ProseMirror document — ignored, and the ` +\n `section keeps its content. The handler is called (data, block) and returns the section's ` +\n `raw content transformed (block.rawContent, as Loom's does), or null for no change.`\n )\n}\n\n/**\n * Run the foundation-level props handler on the final { content, params }\n * before they reach the component. Runs after content parsing, param\n * defaults and content guarantees — the handler sees the exact shape the\n * component would receive and can modify it.\n *\n * The handler receives `(content, params, block)` and returns a new\n * `{ content, params }` object, or null/undefined for no change.\n *\n * Use cases: post-parse content reshaping, computed fields derived\n * from both content and params, param-driven content reorganization.\n * Errors are logged and the original props are preserved.\n */\nfunction runPropsHandler(content, params, block) {\n const handler = globalThis.uniweb?.foundationConfig?.handlers?.props\n if (typeof handler !== 'function') return null\n\n try {\n const result = handler(content, params, block)\n if (result && typeof result === 'object') return result\n } catch (err) {\n console.error('Foundation props handler failed:', err)\n }\n return null\n}\n\n/**\n * Prepare props for a component with runtime guarantees.\n *\n * Does the full content-assembly pipeline in one place so both\n * renderers (`BlockRenderer.jsx` CSR and `ssr-renderer.js` SSG) share\n * the same code path:\n *\n * 1. Assemble `block.parsedContent.data` from the keys the component\n * declares: what the section holds, else what EntityStore delivered,\n * else `null` (`assembleData`) — one record for a key declared\n * `single: true` (`holdOneRecord`).\n * 2. Run the foundation data handler (if registered) to filter or\n * reshape the assembled data.\n * 3. Run the foundation content handler (if registered) on the\n * block. This may replace `block.parsedContent` with a re-parsed,\n * instantiated version.\n * 4. Apply param defaults from meta.\n * 5. Build the guaranteed content structure.\n * 6. Run the foundation props handler (if registered) for\n * post-processing of the final { content, params }.\n *\n * Steps 1–3 mutate the block (vanilla JS layer). Steps 4–6 are\n * pure derivations of the block's now-assembled state.\n *\n * ⛔ No field defaults for `content.data` — ruled 2026-10-05 [Diego]. A step between 5 and 6\n * filled each missing field from its schema's `default`, and replaced a value its `enum`\n * rejected, from the `schemas` a foundation built before then still carries in its meta —\n * read no more. A record reaches the component as it is: an absent field is its own fact,\n * and what it renders as is the component's choice.\n *\n * @param {Object} block - The block instance\n * @param {Object} meta - Runtime metadata for the component (from meta[componentName])\n * @param {Object|null} [entityData] - Entity data resolved by EntityStore (null if none)\n * @returns {Object} Prepared props: { content, params }\n */\nexport function prepareProps(block, meta, entityData = null) {\n const declared = assembleData(block, meta, entityData)\n // ⭐ A list the section held before the store answered — its own fetch, prerendered\n // into its content by a static build — gets its records' `$route` by the store's rule,\n // which the store did not deliver it by (2026-09-14).\n block.website?.entityStore?.linkOwnRecords?.(block)\n holdOneRecord(block, declared)\n runDataHandler(block)\n runContentHandler(block)\n\n // Apply param defaults\n const defaults = meta?.defaults || {}\n const params = applyDefaults(block.properties, defaults)\n\n // Guarantee content structure\n const content = guaranteeContentStructure(block.parsedContent)\n\n // Post-process hook\n const adjusted = runPropsHandler(content, params, block)\n if (adjusted) {\n return {\n content: adjusted.content || content,\n params: adjusted.params || params,\n }\n }\n\n return { content, params }\n}\n\n/**\n * Get runtime metadata for a component from the global uniweb instance\n *\n * @param {string} componentName\n * @returns {Object|null}\n */\nexport function getComponentMeta(componentName) {\n return globalThis.uniweb?.getComponentMeta?.(componentName) || null\n}\n\n/**\n * Get default param values for a component\n *\n * @param {string} componentName\n * @returns {Object}\n */\nexport function getComponentDefaults(componentName) {\n return globalThis.uniweb?.getComponentDefaults?.(componentName) || {}\n}\n","/**\n * The two helpers the background renderers share — one home, because they are\n * not rendering.\n *\n * ## Why this module exists\n *\n * Backgrounds were an L3 pair until 2026-10-25: `components/Background.jsx`\n * rendered for the browser and `ssr-renderer.js` with `React.createElement`\n * only. ⛔ **Only the RENDERING was ever meant to be twinned — \"pure data →\n * L1, one home\" — and two pure helpers had been dragged across the seam with\n * it**: a base-path joiner and a colour-opacity converter, byte-identical in\n * both files. The rendering is one copy now too (`background.js`: it needs no\n * hooks or DOM); these stay apart from it because they are not rendering.\n *\n * ⚠️ **And the copies had already drifted from the rule they were copying.**\n * The base-path joiner is `@uniweb/core`'s `applyBasePath`, whose own docblock\n * says it moved down to core *\"rather than grow a second copy (the failure\n * `@uniweb/core/route-match` was created to end, after one matcher was\n * implemented twice and the copies diverged)\"*. Two copies grew anyway, in the\n * runtime, and **both were missing its protocol-relative guard** — so an author\n * writing `//cdn.example.com/hero.jpg` on a site with `base: /docs/` got\n * `/docs//cdn.example.com/hero.jpg`. Measured 2026-09-06; the exact garbage\n * `applyBasePath` documents itself as existing to prevent.\n *\n * ⇒ The lesson is narrower than \"don't duplicate\": **when a twinned pair needs a\n * helper, the helper is the thing that must not be twinned.** A twin is a\n * commitment to keep two renderers in step; every pure function pulled inside it\n * silently joins that commitment.\n */\n\nimport { applyBasePath } from '@uniweb/core/base-path'\n\n/**\n * A site-root-relative URL with the deployment base applied.\n *\n * The base comes off the active website rather than being passed in, because\n * both callers render from the singleton and neither has it to hand. The join\n * itself is core's — protocol-relative and absolute URLs pass through, and an\n * already-based path is not based twice.\n *\n * @param {string} url\n * @returns {string}\n */\nexport function siteUrl(url) {\n return applyBasePath(url, globalThis.uniweb?.activeWebsite?.basePath || '')\n}\n\n/**\n * A colour with an alpha applied, for an overlay drawn over a background.\n *\n * Hex and `rgb()` / `rgba()` are converted; anything else — a named colour, a\n * `var(--token)`, `oklch(…)` — is returned unchanged, because a wrong guess\n * here paints the wrong colour rather than failing.\n *\n * @param {string} color\n * @param {number} opacity\n * @returns {string}\n */\nexport function withOpacity(color, opacity) {\n if (typeof color !== 'string' || !color) return color\n if (color.startsWith('#')) {\n // `#rgb` and `#rgba` are written out first; an alpha pair is replaced by `opacity`.\n // ⛔ Until 2026-10-25 every hex was read as six digits, so `#000` gave `rgba(0, 0, NaN, …)`,\n // and a gradient using it was invalid CSS that drew nothing.\n let hex = color.slice(1)\n if (hex.length === 3 || hex.length === 4) hex = [...hex].map((c) => c + c).join('')\n if (!/^[0-9a-f]{6}([0-9a-f]{2})?$/i.test(hex)) return color\n const r = parseInt(hex.slice(0, 2), 16)\n const g = parseInt(hex.slice(2, 4), 16)\n const b = parseInt(hex.slice(4, 6), 16)\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\n }\n if (color.startsWith('rgb')) {\n const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (match) return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n return color\n}\n","/**\n * A section's background — the layer the runtime draws behind a section's content: a color, a\n * gradient, an image or a video, under an optional overlay. It reads `block.background`, which core\n * normalizes once (`Block.normalizeBackground`).\n *\n * ⭐ ONE COPY, FOR BOTH RENDERERS: the browser's (`components/Background.jsx`) and the prerender's\n * (`ssr-renderer.js`, behind `uniweb build` and a host's prerender alike). It needs no hooks, no\n * Router and no DOM, so it is written with `createElement` and both draw it — the L3 twins exist for\n * code that needs those (`default-layout.js` is the same rule).\n * ⛔ Until 2026-10-25 each renderer wrote its own, and they had drifted: the prerender read an\n * overlay's `angle: 0` and `endPosition: 0` as unset (`||`), drew no video background at all, and\n * dropped the `className` kit's `SectionBackground` passes. Each showed as the page changing when\n * the browser rendered it in place of the prerendered one. Pinned by `tests/page-parity.test.js`.\n *\n * ⭐ A VIDEO IS ITS POSTER UNTIL THE BROWSER HAS MOUNTED IT. Both renderers draw the poster\n * (`VideoPoster`); the browser then swaps in the playing `<video>` (`components/Background.jsx`),\n * unless the visitor prefers reduced motion and there is a poster to keep (`videoMoves`). That\n * preference is the one thing here that needs the DOM, so it is read in an effect, never in the first\n * render: read there, it made the browser's first render differ from the prerendered page for exactly\n * those visitors.\n *\n * ⭐ An unset value is `undefined` or `null`, never a falsy one: `0` is an angle, a position and an\n * opacity like any other (`??`, not `||`).\n */\n\nimport React from 'react'\nimport { siteUrl, withOpacity } from './background-shared.js'\n\nconst h = React.createElement\n\n/** The box every layer fills: the section's, behind its content (`BlockRenderer.jsx`). */\nconst FILL = { position: 'absolute', inset: 0 }\n\nfunction ColorLayer({ color }) {\n if (!color) return null\n return h('div', { className: 'background-color', style: { ...FILL, backgroundColor: color }, 'aria-hidden': 'true' })\n}\n\nfunction GradientLayer({ gradient }) {\n if (!gradient) return null\n\n // A CSS gradient as written (\"linear-gradient(to bottom, #000, #333)\")\n if (typeof gradient === 'string') {\n return h('div', { className: 'background-gradient', style: { ...FILL, background: gradient }, 'aria-hidden': 'true' })\n }\n\n const start = gradient.start ?? 'transparent'\n const end = gradient.end ?? 'transparent'\n const startOpacity = gradient.startOpacity ?? 1\n const endOpacity = gradient.endOpacity ?? 1\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n const background =\n `linear-gradient(${gradient.angle ?? 0}deg, ` +\n `${startColor} ${gradient.startPosition ?? 0}%, ${endColor} ${gradient.endPosition ?? 100}%)`\n\n return h('div', { className: 'background-gradient', style: { ...FILL, background }, 'aria-hidden': 'true' })\n}\n\nfunction ImageLayer({ image }) {\n if (!image?.src) return null\n const style = {\n ...FILL,\n backgroundImage: `url(${siteUrl(image.src)})`,\n backgroundPosition: image.position ?? 'center',\n backgroundSize: image.size ?? 'cover',\n backgroundRepeat: 'no-repeat',\n }\n return h('div', { className: 'background-image', style, 'aria-hidden': 'true' })\n}\n\n/**\n * A video background as both renderers first draw it: its poster, as an image layer — or nothing,\n * when it has none. The browser replaces it with `VideoElement` once mounted.\n */\nexport function VideoPoster({ video }) {\n if (!video?.src || !video.poster) return null\n return h(ImageLayer, { image: { src: video.poster, size: 'cover', position: 'center' } })\n}\n\n/**\n * Whether a mounted video background plays: always, unless the visitor prefers reduced motion and\n * there is a poster to show instead.\n *\n * @param {Object} video - `background.video`\n * @param {boolean} reducedMotion - the visitor's `prefers-reduced-motion`\n * @returns {boolean}\n */\nexport function videoMoves(video, reducedMotion) {\n if (!video?.src) return false\n return !(reducedMotion && video.poster)\n}\n\n/** The playing video, which only the browser draws, once mounted (`components/Background.jsx`). */\nexport function VideoElement({ video }) {\n if (!video?.src) return null\n const sources = (video.sources || inferSources(video.src)).map((s) => ({ ...s, src: siteUrl(s.src) }))\n return h(\n 'video',\n {\n className: 'background-video',\n style: { ...FILL, width: '100%', height: '100%', objectFit: 'cover' },\n autoPlay: true,\n loop: video.loop ?? true,\n muted: video.muted ?? true,\n playsInline: true,\n poster: siteUrl(video.poster),\n 'aria-hidden': 'true',\n },\n sources.map(({ src, type }, index) => h('source', { key: index, src, type })),\n )\n}\n\n/**\n * The formats to offer for a single `src`: an `.mp4` is offered as `.webm` first (better\n * compression), and the reverse; the browser plays the first it supports.\n */\nfunction inferSources(src) {\n const ext = src.split('.').pop()?.toLowerCase()\n const base = src.slice(0, src.lastIndexOf('.'))\n if (ext === 'mp4') return [{ src: `${base}.webm`, type: 'video/webm' }, { src, type: 'video/mp4' }]\n if (ext === 'webm') return [{ src, type: 'video/webm' }, { src: `${base}.mp4`, type: 'video/mp4' }]\n const type = src.endsWith('.ogg') || src.endsWith('.ogv') ? 'video/ogg' : 'video/mp4'\n return [{ src, type }]\n}\n\nfunction Overlay({ overlay }) {\n if (!overlay?.enabled) return null\n const opacity = overlay.opacity ?? 0.5\n\n if (overlay.gradient) {\n const g = overlay.gradient\n const background =\n `linear-gradient(${g.angle ?? 180}deg, ${g.start ?? 'rgba(0,0,0,0.7)'} ${g.startPosition ?? 0}%, ` +\n `${g.end ?? 'rgba(0,0,0,0)'} ${g.endPosition ?? 100}%)`\n return h('div', {\n className: 'background-overlay background-overlay--gradient',\n style: { ...FILL, background, opacity, pointerEvents: 'none' },\n 'aria-hidden': 'true',\n })\n }\n\n const base = overlay.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n return h('div', {\n className: 'background-overlay background-overlay--solid',\n style: { ...FILL, backgroundColor: `rgba(${base}, ${opacity})`, pointerEvents: 'none' },\n 'aria-hidden': 'true',\n })\n}\n\n/**\n * The background layer: `block.background`'s fields, and a `className` for the layer (kit's\n * `SectionBackground` passes one).\n *\n * @param {Object} props\n * @param {string} props.mode - 'color' | 'gradient' | 'image' | 'video'; nothing is drawn without one\n * @param {string} [props.color]\n * @param {string|Object} [props.gradient]\n * @param {Object} [props.image]\n * @param {Object} [props.video]\n * @param {Object} [props.overlay]\n * @param {string} [props.className]\n * @param {Function} [props.Video] - how a video background is drawn: its poster (the default, and the\n * prerender's), or the browser's, which plays it once mounted\n * @returns {React.ReactElement|null}\n */\nexport function Background({ mode, color, gradient, image, video, overlay, className = '', Video = VideoPoster }) {\n if (!mode) return null\n return h(\n 'div',\n {\n className: `background background--${mode} ${className}`.trim(),\n style: { ...FILL, overflow: 'hidden', zIndex: 0 },\n 'aria-hidden': 'true',\n },\n mode === 'color' ? h(ColorLayer, { color }) : null,\n mode === 'gradient' ? h(GradientLayer, { gradient }) : null,\n mode === 'image' ? h(ImageLayer, { image }) : null,\n mode === 'video' ? h(Video, { video }) : null,\n h(Overlay, { overlay }),\n )\n}\n","/**\n * Default 404 Page Content\n *\n * Single source of truth for the fallback 404 page shown when a site\n * has no custom 404 page defined. Used by:\n * - PageRenderer.jsx (client-side, as React elements)\n * - ssr-renderer.js generate404Html (build-time, as HTML string)\n *\n * The wrapper uses min-height + flex centering so the 404 content\n * renders at the same position regardless of parent layout context.\n * This prevents a visible flash when React hydrates over the SSR content.\n */\n\nimport React from 'react'\n\nconst styles = {\n wrapper: {\n minHeight: '80vh',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '2rem',\n textAlign: 'center',\n },\n heading: { fontSize: '3rem', fontWeight: 'bold', color: '#1f2937', marginBottom: '1rem' },\n message: { color: '#64748b', marginBottom: '2rem' },\n link: { color: '#3b82f6', textDecoration: 'underline' },\n}\n\n/**\n * React element for client-side rendering (PageRenderer).\n * Reads basePath from the runtime so the homepage link works\n * in subdirectory deployments (e.g., /sites/testproject).\n */\nexport function Default404() {\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n const homeHref = basePath ? `${basePath}/` : '/'\n return React.createElement('div', { className: 'page-not-found', style: styles.wrapper },\n React.createElement('h1', { style: styles.heading }, '404'),\n React.createElement('p', { style: styles.message }, 'Page not found'),\n React.createElement('a', { href: homeHref, style: styles.link }, 'Go to homepage')\n )\n}\n\n/**\n * Static HTML string for SSR injection (generate404Html).\n *\n * @param {string} [basePath] - Base path prefix for the homepage link (e.g., '/sites/testproject')\n */\nexport function default404Html(basePath = '') {\n const homeHref = basePath ? `${basePath}/` : '/'\n return (\n `<div class=\"page-not-found\" style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;text-align:center\">` +\n `<h1 style=\"font-size:3rem;font-weight:bold;color:#1f2937;margin-bottom:1rem\">404</h1>` +\n `<p style=\"color:#64748b;margin-bottom:2rem\">Page not found</p>` +\n `<a href=\"${homeHref}\" style=\"color:#3b82f6;text-decoration:underline\">Go to homepage</a>` +\n `</div>`\n )\n}\n","/**\n * Layer-2 wiring helpers: runtime ↔ Uniweb singleton.\n *\n * After `createUniweb()` constructs the singleton, the runtime fills a\n * few declared slots on it before the first render — foundation\n * capabilities (`defaultInsets`, `xref.build()`), per-request data\n * hydration into `website.dataStore`, locale-scoped content slicing.\n * This step is identical in every environment (browser SPA, SSG\n * prerender, cloud SSR) because it's plain data manipulation on a JS\n * object: no React rendering happens here, no hooks are called, no DOM\n * is touched, no `react-dom/server` is needed.\n *\n * That's why these helpers live in one file imported by both\n * `setup.js` (browser boot) and `ssr-renderer.js` (SSG/cloud-SSR boot),\n * instead of being duplicated into each. Things that genuinely differ\n * between environments — routing components, icon-cache hydration from\n * the DOM, the per-page render loop — stay in the per-environment\n * entries; these helpers cover only the environment-agnostic L2 work.\n *\n * What this file keeps out of the SPA bundle is `react-dom/server`, not\n * React: `FallbackRef` below is `React.createElement` alone. `setup.js`\n * pulls `wire-foundation.js` directly, so this file must NOT transitively\n * pull `ssr-renderer.js` (which imports `react-dom/server`). The L2\n * helpers therefore live here, while the L3-composing\n * `initPrerenderForLocale` lives in `ssr-renderer.js`.\n *\n * Adding a new framework-level capability:\n * 1. Name it in `extractFoundationRuntime`'s lists (`@uniweb/build`'s\n * `src/runtime-schema.js`): the built entry carries only what they\n * name, so anything else never reaches this file.\n * 2. Read the foundation declaration via `foundation.default.capabilities.<name>`.\n * 3. Apply it to the uniweb singleton (set a slot, call a build hook,\n * register something on `activeWebsite`).\n * 4. Provide a runtime fallback if the capability is one foundations\n * may legitimately not declare (see `FallbackRef`).\n *\n * Foundation export shape contract: the runtime always loads the\n * **built** foundation artifact (`dist/entry.js`) via\n * `loadFoundation()` in `foundation-loader.js`, which does `import(url)`\n * and returns a module namespace. The build pipeline\n * (`@uniweb/build`'s `src/generate-entry.js`) writes what the runtime\n * reads of the foundation's source default export into\n * `default.capabilities.*`, so the runtime sees a single canonical shape\n * with no need for fallback chains. This\n * differs from `@uniweb/press` / `@uniweb/unipress`,\n * which DO need to handle a second shape because they're sometimes\n * called from inside a foundation bundle (where the foundation imports\n * its own source as a bare default object).\n */\n\nimport React from 'react'\nimport { deriveCacheKey, resolveDefaultLocale } from '@uniweb/core'\n// Leaf subpaths, not the package root: this file is pulled into the SSR/Worker\n// bundle, and `@uniweb/core` proper drags semantic-parser and theming with it.\nimport { resolveService, readServiceOptions } from '@uniweb/core/services'\nimport Tracker from '@uniweb/core/tracker'\nimport { buildTheme } from '@uniweb/theming'\n\n/**\n * Renders unhandled `[#id]` cross-reference markers as plain text. Used\n * when the active foundation didn't declare its own `<Ref>` via\n * `defaultInsets`. Pure `React.createElement` — safe in every\n * environment, including the hook-free SSR pipeline.\n *\n * Foundations that support cross-references override this by exporting\n * `defaultInsets: { Ref }` (with kit's xref-aware Ref) from their\n * source — the build pipeline carries it through into\n * `default.capabilities.defaultInsets`.\n */\nexport function FallbackRef({ params }) {\n return React.createElement(\n 'span',\n { className: 'xref xref--unhandled' },\n `[${params?.key || '?'}]`,\n )\n}\n\n/**\n * Wire foundation-declared capabilities onto a freshly constructed\n * Uniweb singleton. Called once, after `createUniweb()`, before any\n * rendering. Identical for SPA, SSG, and cloud SSR.\n *\n * @param {import('@uniweb/core').default} uniweb - From createUniweb(...).\n * @param {object} foundation - Loaded foundation module (built shape).\n */\nexport function wireFoundationCapabilities(uniweb, foundation) {\n const caps = foundation?.default?.capabilities || {}\n\n // defaultInsets: framework provides FallbackRef as the floor;\n // foundation overrides win. `getComponent()` on the Uniweb singleton\n // (core/uniweb.js) falls back to defaultInsets[name] when no\n // foundation/extension component matches — that's how `<Ref>` becomes\n // available to every foundation without each one having to register\n // it explicitly.\n uniweb.defaultInsets = { Ref: FallbackRef, ...(caps.defaultInsets || {}) }\n\n // xref: foundations supporting cross-references export\n // `xref.build(website, { foundationKinds })`. The runtime can't\n // import kit directly (kit is bundled into each foundation, not into\n // runtime, so only the foundations that use it pay for it), so it\n // dispatches through the foundation's reference. Foundations without\n // xref skip this entirely; kit's xref module never enters their\n // bundle thanks to tree-shaking at foundation-build time.\n if (caps.xref?.build && uniweb.activeWebsite) {\n caps.xref.build(uniweb.activeWebsite, {\n foundationKinds: caps.xref.kinds || {},\n })\n }\n}\n\n/**\n * Slice a multi-locale site-content payload to one locale.\n *\n * Sites published through the editor ship a single payload that carries\n * all locales nested under `content.locales[locale]` — `pages`, optional\n * `layouts`, and a `config` overlay. The default locale lives at the\n * top level (no nesting). This helper extracts the requested locale's\n * view as a fresh content object the rest of the runtime can consume\n * unchanged.\n *\n * Returns `content` as-is when `locale` is the default, missing, or not\n * present in `content.locales` — callers that already hand us locale-\n * scoped content (e.g., the framework's per-locale SSG path that loads\n * each `dist/{locale}/site-content.json` separately) get pass-through\n * behavior.\n *\n * The shape comes from the editor's publish payload, which is the canonical\n * form for multi-locale content — a server-side renderer consumes it\n * directly. Build-time SSG pre-flattens to one file per locale and so falls\n * into the pass-through case.\n *\n * @param {Object} content - Site content payload, possibly multi-locale.\n * @param {string} locale - Requested locale code.\n * @returns {Object} Content scoped to the requested locale.\n */\nexport function sliceContentForLocale(content, locale) {\n const defaultLang = resolveDefaultLocale(content?.config)\n const locData = content?.locales?.[locale]\n if (!locale || locale === defaultLang || !locData) return content\n return {\n pages: locData.pages,\n layouts: locData.layouts || content.layouts,\n config: {\n ...locData.config,\n i18n: content.config?.i18n,\n activeLocale: locale,\n },\n }\n}\n\n/**\n * Pre-populate a Website's DataStore from build-time / publish-time\n * fetched data so the dispatcher's first probe hits the cache instead\n * of refetching.\n *\n * The value MUST be wrapped as `{ data }`, or `cached.data` reads `undefined`. Three call sites\n * used to inline this loop independently (browser SPA, Node SSG, and a server-side renderer); one\n * of the three used the wrong shape, silently killing prefetched-data reuse wherever it ran. This\n * helper is the one canonical implementation.\n *\n * ⭐ **The KEY is the dispatcher's, not ours** (`fetcher.hydrate`). ⛔ This filed every entry under\n * `deriveCacheKey(entry.config)` until 2026-09-20, which is right for every request the framework's\n * own fetcher answers and wrong for one the site routes to a transport that declares its own\n * `cacheKey` — the documented case being a request whose identity depends on state its address does\n * not carry. Measured: such an entry landed where no `peek` looks, so every prerendered page of that\n * site rendered empty and the browser refetched it, with no error anywhere. One derivation, one\n * home — the dispatcher's, which is where selection happens.\n *\n * @param {import('@uniweb/core').Website} website\n * @param {Array<{config: Object, data: any}>} fetchedData\n */\nexport function hydrateDataStore(website, fetchedData) {\n if (!website?.dataStore || !fetchedData?.length) return\n for (const entry of fetchedData) {\n // A data step's list carries every declared config with an `outcome`; only what was actually\n // fetched enters the store. A list without outcomes (the SSG lane's) is all fetched.\n if (entry.outcome && entry.outcome !== 'fetched') continue\n // `meta` (the depth the records were fetched at) rides along, so the store\n // files them in its record index exactly as a runtime fetch would.\n const value = entry.meta ? { data: entry.data, meta: entry.meta } : { data: entry.data }\n if (typeof website.fetcher?.hydrate === 'function') {\n website.fetcher.hydrate(entry.config, value, { website })\n } else {\n // A graph with no dispatcher — a stub in a test, or a caller assembling one by hand.\n website.dataStore.set(deriveCacheKey(entry.config), value)\n }\n }\n}\n\n/**\n * Make sure the site's theme CSS exists on the graph, generating it from\n * the authored config when nothing upstream did.\n *\n * **The authored theme config is the source of truth in every lane;\n * generated CSS is a cache of it.** `uniweb build` fills that cache and\n * bakes the result into `<head>`, so this is a no-op on the static lane.\n * A lane that serves a site WITHOUT running the framework's build — a\n * backend-hosted SPA, a cloud shell-mode fallback — carries only the\n * authored `theme.yml` (that is the correct thing for a sync wire to\n * carry: `theme.css` is a build artifact, and with two publishers only\n * one of which computes it, shipping it would make a site's styling\n * depend on who published last). Without this helper those lanes render\n * with every semantic token unset — no colours, no backgrounds.\n *\n * Generating here rather than in a publish step is what keeps the\n * three-ingredient contract true: site + foundation + runtime converge\n * to a *styled* page with no fourth actor. It also stays one\n * implementation — the alternative was re-deriving the OKLCH shade math\n * in another language and keeping the two bit-compatible.\n *\n * L2, not L3: this reads and writes graph state and renders nothing, so\n * it has a single home here and both boot paths call it. **The\n * `@uniweb/theming` import is deliberately static.** An SSR isolate\n * loads a fixed modules map and cannot resolve a chunk graph, so the SSR\n * entry must include the generator statically; a lazy `import()` in the\n * browser entry only would mean two mechanisms for one behaviour,\n * drifting independently. Measured cost of the generator: ~4.9 KB gzip.\n *\n * Foundation-declared vars reach us through\n * `capabilities.vars` — emitted into `dist/entry.js` by\n * `@uniweb/build`'s `generate-entry.js`. Before that existed they lived\n * only in `dist/meta/schema.json` and a theme generated outside the\n * build silently lost every one of them.\n *\n * Callers own the \"should I?\" question, because it is environment-\n * specific: the browser entry skips this when the document already\n * carries a prerendered `<style id=\"uniweb-theme\">` (regenerating from\n * an already-processed config is wasted work at best), while the SSR\n * entry always runs it and lets `injectPageContent()` emit the result\n * idempotently.\n *\n * @param {import('@uniweb/core').default} uniweb - From createUniweb(...).\n * @param {object} foundation - Loaded foundation module (built shape).\n */\nexport function ensureThemeCss(uniweb, foundation) {\n const website = uniweb?.activeWebsite\n const themeData = website?.themeData\n if (!themeData || themeData.css) return\n\n const caps = foundation?.default?.capabilities || {}\n try {\n const { config, css, links } = buildTheme(themeData, {\n foundationVars: caps.vars || {},\n base: website.basePath || '/',\n })\n // Merge rather than replace: `config` is the processed superset (it\n // adds `palettes`, normalized `contexts`, resolved `fonts`), so this\n // also gives a build-less lane the same themeData shape the static\n // lane has — Theme.getPalette() and friends start working too.\n Object.assign(themeData, config, { css, links })\n } catch (err) {\n // This runs on the path taken when something upstream has already\n // gone wrong. A degraded render that is still legibly the site beats\n // one that looks broken, but neither is worth a boot crash.\n console.warn('[uniweb] theme CSS generation failed:', err?.message || err)\n }\n}\n\n/**\n * L2: give the site's tracker its destination.\n *\n * Replaces the disabled `Tracker` that `createUniweb` declares (see\n * `core/src/uniweb.js`) with a configured one, when — and only when — a\n * destination resolves. With none, the disabled default stays and every\n * `track()` call in the site remains a silent no-op, which is the default\n * state for the large majority of sites.\n *\n * ⛔ **WHY THE BASE PATH IS PASSED IN RATHER THAN READ OFF THE WEBSITE.**\n * `resolveService` joins a root-relative endpoint to `website.basePath`, and\n * that field is still `''` until `setBasePath()` runs — which happens later,\n * from `RuntimeProvider`. Resolving against the website as-is would silently\n * drop the prefix on every subdirectory deployment, and the symptom would be a\n * collector quietly receiving nothing. So the caller supplies the basename it\n * has already derived, and the lookup is done against that. `resolveService`\n * reads only `.config` and `.basePath`, so a plain object is a complete input.\n *\n * ⚖️ **Not called from the SSR path, deliberately.** The tracker is\n * browser-guarded, so wiring it there would produce a configured object that\n * can never emit — a slot that looks live and is not. The SSR twin has no\n * page-view effect either; suppression is structural rather than a flag.\n *\n * ## `scripts` — a vendor's own script, when the site declares one\n *\n * A second, independent path — vendor tags:\n * nothing is translated between our stream and theirs, and the framework never\n * learns which vendor it is. ⛔ **The loader is INJECTED rather than imported**,\n * because this file is pulled into the SSR/Worker bundle and a script loader is\n * DOM code. The browser entry passes one; the SSR path passes none, so there is\n * no branch to remember.\n *\n * @param {object} uniweb - the singleton\n * @param {object} [options]\n * @param {string} [options.basePath] - the deployment base (router basename)\n * @param {(urls: string[], opts: object) => void} [options.loadScripts] - DOM\n * loader for declared vendor scripts; omitted outside a browser entry\n */\n/**\n * What `tracking.emit` names, when a site names a preset rather than a list.\n *\n * ⭐ **`all` is deliberately ABSENT from this table.** It resolves to `null` —\n * *no narrowing* — so an event added in a later release is included without the\n * site republishing. A literal list would freeze `all` at the moment the site\n * was built and quietly stop meaning \"all\".\n *\n * ⚖️ **`standard` and `all` differ, and a new automatic event only ever widens\n * the gap.** `standard` is a curated set that a release cannot grow behind an\n * operator's back, `all` is the standing yes: `section_click`, `time_on_page` and\n * `conversion` are `all`'s and not `standard`'s. The volume surprise is the thing\n * being avoided — a site that never changed should not start sending more.\n *\n * ⛔ **The curated set is the answer for a site that CONFIGURED ITS OWN\n * DESTINATION. It is NOT the answer for a site whose host supplies one** — see\n * `resolveEmit`, which is where absence stopped meaning one thing.\n */\nconst EMIT_PRESETS = {\n minimal: ['page_view'],\n standard: ['page_view', 'outbound_click', 'section_view']\n}\n\n/** The preset a site gets by declaring a destination and nothing else. */\nconst DEFAULT_EMIT = 'standard'\n\n/**\n * The site's own selection, as a list of event names or `null` for no narrowing.\n *\n * ⛔ **An unknown preset name resolves to the DEFAULT, not to nothing.** A typo\n * (`emit: sandard`) must not silently take a site dark: the failure mode of a\n * misread selection has to be \"you got the usual set\", never \"you got none and\n * nothing said so\".\n *\n * ## ⭐ ABSENCE MEANS TWO DIFFERENT THINGS, and this is where they part\n *\n * **A site that configured its own `endpoint` chose it.** Writing no `emit`\n * there means *\"the curated default\"*, and `standard` is exactly right — a\n * later framework release must not grow it behind that operator's back.\n *\n * **A site whose HOST supplies the collector has no endpoint of its own.** The\n * operator's whole relationship is *\"my host does analytics for me\"*, so\n * writing no `emit` there means **\"whatever my host offers\"** — not a list\n * frozen at the framework version the site was built against.\n *\n * ⇒ **Absent `emit` defers to the host's declared list when there is one, and\n * falls back to `standard` when there is not.** Returning `null` is how the\n * deferral is expressed: it is *no site-tier narrowing*, so `Tracker.arms()` is\n * left with the host's list as the only gate.\n *\n * ⭐ **Why this is a fix and not a relaxation.** §4 of the tracking design says\n * *\"the runtime emits what the SITE OWNER buys\"* — and before this, an owner\n * paying a host for analytics received a **framework-frozen subset** of what\n * that host stores and bills them for. The only way to close the gap was to\n * hand-edit YAML and republish, **a dependency with no symptom when forgotten**,\n * which is the precise failure that rule was written to reject.\n *\n * ⛔ **The fallback is NOT decoration — it is the standalone-first guarantee.**\n * A static host, a foreign backend, and any Uniweb backend predating the\n * `events` key all declare no list. Deferring unconditionally would arm *every*\n * event, forever, on exactly the sites the framework exists to serve without a\n * backend.\n *\n * ⚠️ **A host that declares an EMPTY list still means it** — `[]` is a\n * statement, not an absence, and it arms nothing. That is unchanged: `arms()`\n * has always read an empty host list that way. Only `undefined` means \"nothing\n * declared\".\n *\n * @param {string|string[]|undefined} emit - the site's own `tracking.emit`\n * @param {string[]|null} [hostEvents] - the host's declared list, or `null`\n * when the host declared none. **Only consulted when `emit` is absent**;\n * an author who names anything still wins.\n * @returns {string[]|null}\n */\nfunction resolveEmit(emit, hostEvents = null) {\n // ⛔ Absent is the ONLY branch that consults the host — this is a default,\n // never an override. `emit: minimal` on a host offering everything still\n // sends one event.\n if (emit == null) return hostEvents ? null : EMIT_PRESETS[DEFAULT_EMIT]\n if (Array.isArray(emit)) return emit\n if (emit === 'all') return null\n return EMIT_PRESETS[emit] || EMIT_PRESETS[DEFAULT_EMIT]\n}\n\nexport function wireTracker(uniweb, { basePath = '', loadScripts = null } = {}) {\n const website = uniweb?.activeWebsite\n if (!website) return\n\n // A plain lookup target: `resolveService` reads `.config` and `.basePath`\n // only, so this is the whole of what it needs and carries the *correct* base.\n const target = { config: website.config, basePath }\n\n const { url } = resolveService(target, 'tracking')\n const options = readServiceOptions(target, 'tracking')\n\n // Only whether any were declared — normalizing them is the loader's job, and\n // lives behind the loader's dynamic boundary so a site with none never\n // downloads that code either.\n const declaredScripts = options.scripts\n const hasScripts = Array.isArray(declaredScripts) ? declaredScripts.length > 0 : !!declaredScripts\n\n // Nothing declared on either count — keep the disabled default, nothing\n // armed, nothing queued. This is the state of the large majority of sites.\n if (!url && !hasScripts) return\n\n // The two narrowings, resolved here rather than in core: this is per-request\n // config reshaping, which is L2's job (see this file's header).\n //\n // ⛔ **`hostEvents` is read from the HOST tier only** — `config.services\n // .tracking.events`, never the merged view. A site cannot widen what a host\n // declined to store, and reading the merge would let it, silently, by writing\n // its own `events:` key.\n //\n // ⛔ **Absent stays absent.** No `events` from the host means NO NARROWING,\n // never an empty set: a host that sends no list is an older or simpler one,\n // and the other reading takes every site on it dark with every gate saying\n // yes. `?? null` rather than `?? []` is the whole of that guard.\n // ⛔ Each tier is read from ITS OWN key, not from the merged `options`. The\n // merge exists so a site's own option can override a host's — `consent`, say;\n // the address is not an option, and comes from `resolveService` above. These\n // two are not overrides of each other but answers to different questions, and\n // reading either off the merge would let one tier answer the other's — a site\n // writing `events:` would widen past what the host stores, silently.\n const hostTracking = website.config?.services?.tracking\n const siteTracking = website.config?.tracking\n const hostEvents =\n hostTracking && Array.isArray(hostTracking.events) ? hostTracking.events : null\n\n const tracker = new Tracker({\n endpoint: url,\n hostEvents,\n siteEmit: resolveEmit(siteTracking && siteTracking.emit, hostEvents),\n // ⭐ Read off the MERGED view, unlike the two above — and the difference is\n // the point. `events`/`emit` answer different questions per tier, so each is\n // read from its own key; this is one question with two possible answerers,\n // so the ordinary precedence applies: the host declares a batch window that\n // suits its collector, and a site's own `tracking:` overrides it. Absent on\n // both, `Tracker` keeps its default.\n //\n // ⛔ **A field being READABLE is not the same as it being AVAILABLE**, and\n // that is what made this line worth a test rather than a shrug.\n // `readServiceOptions` has always returned this key, so the plan read as\n // finished while nothing wrote the object being read — it would have shipped\n // as *\"we set the interval and it did nothing\"*, with all three lanes' suites\n // green. The value now has to reach `setInterval`, and a test asserts the\n // delay rather than the field.\n flushIntervalMs: options.flushIntervalMs,\n // Opt-in, not the default. Declaring a destination is itself the operator's\n // decision to track; requiring a second affirmative step would be the\n // framework presuming a jurisdiction on their behalf, which is exactly what\n // it must not do. A site that needs the gate asks for it.\n consentRequired: options.consent === 'required',\n debug: !!options.debug\n })\n uniweb.tracking = tracker\n\n if (!loadScripts || !hasScripts) return\n\n // The same suppression the tracker applies to its own events: a server render\n // or a framed authoring preview is not a visit, and a vendor's script must not\n // fire there either. One predicate in core, so the two cannot drift.\n if (!tracker.isLiveDocument()) return\n\n const load = () => loadScripts(declaredScripts, { basePath, debug: !!options.debug })\n if (tracker.consentStatus() === 'granted') load()\n else tracker.onGranted = load\n}\n","/**\n * Where a section's `sticky` setting lands — pure, so the browser's renderer\n * (`components/Layout.jsx`, `BlockRenderer.jsx`) and the prerender's (`ssr-renderer.js`) make the\n * same boxes sticky, and a prerendered page does not change when the browser renders over it.\n *\n * ⭐ A COMPONENT'S OUTERMOST ELEMENT CANNOT STICK. `position: sticky` moves an element only within\n * its parent's box, and a component sits in boxes as tall as itself: the section's wrapper, the\n * layout area's wrapper (`area-wrappers.js`), the built-in layout's zone (`default-layout.js`). (An\n * element inside a component can stick within a taller box the component draws — a side panel in\n * the row beside a list — which is the component's own layout and asks nothing of this module.)\n * So a section asks — `sticky:`, one of a section's own settings (`@uniweb/schemas/section`),\n * defaulted by its type's `meta.js` — and the runtime makes sticky the box that can:\n *\n * - in the body, the section's own wrapper, whose parent, the body, spans every section;\n * - a child section rendered as a section (`<ChildBlocks wrapAs>`, kit's `ChildGrid`), its own\n * wrapper too, which then stays in view within the box its parent places it in — in\n * `ChildGrid`, its cell, as tall as the row — and no further. Top-aligned, so a flex or grid\n * item has room. At one column (a phone) a child is its own row and does not stick, which is\n * what a side panel stacked under the content should do;\n * - in any other area, the area's box when any of its sections is sticky: the zone, in the\n * built-in layout; the area wrapper, in a foundation's layout. That box takes the area's\n * layer, and a sticky area's layer is above every area that scrolls under it\n * (`resolveLayoutLayers`).\n *\n * ⭐ A section stays in view BELOW what is stuck at the top. A sticky area sticks at `0px`; a\n * sticky section with no `top` of its own sticks at `--uw-sticky-top`, how far down the page's\n * stuck chrome reaches, which the browser measures (`sticky-top.js`) — so a side panel or a\n * sub-navigation clears a stuck header whatever its height. Unmeasured — a prerendered page\n * before the browser runs, a page with nothing stuck — it is `0px`.\n *\n * Every box this module makes sticky says so, `data-uw-sticky` = `area`, `section` or `child`,\n * for what reads them in the browser: the measurement, and `uniweb dev`'s check\n * (`sticky-check.js`).\n *\n * ⛔ A layout that wraps an area in an element of its own confines the area wrapper, as the zone\n * does in the built-in layout. That element is the layout's, so the layout makes it sticky, when\n * `page.isSticky(area)`.\n *\n * ⛔ Until 2026-10-19 there was no setting, and a header's own `sticky top-0` — the starter\n * templates' — scrolled away with the page. Until 2026-10-20 a child section's setting did\n * nothing, and a sticky section stuck at `0px`, under a stuck header.\n */\n\n/** The very top of the viewport — a sticky area with no `top` of its own. */\nexport const STICKY_TOP = '0px'\n\n/**\n * Where a sticky section with no `top` of its own stays: below what is stuck at the top\n * (`sticky-top.js` measures it), else the very top.\n */\nexport const STICKY_SECTION_TOP = 'var(--uw-sticky-top, 0px)'\n\n/**\n * The style that keeps a box in view.\n *\n * @param {string|null} [top] - how far below the viewport's top, a CSS length\n * @param {number|null} [zIndex] - its layer, when it has one\n * @returns {{ position: 'sticky', top: string, zIndex?: number }}\n */\nexport function stickyStyle(top, zIndex) {\n const style = { position: 'sticky', top: top || STICKY_TOP }\n if (zIndex != null) style.zIndex = zIndex\n return style\n}\n\n/**\n * Whether an area stays in view — when any of its sections is sticky — and how far below the\n * top: the first sticky section's.\n *\n * @param {Array<Object>|null|undefined} blocks - the area's blocks, their components initialized\n * @returns {{ top: string }|null}\n */\nexport function areaStickiness(blocks) {\n const first = (blocks || []).find((block) => block?.sticky)\n return first ? { top: first.stickyTop || STICKY_TOP } : null\n}\n\n/**\n * Each layout area's stickiness, read from its sections — what both renderers compute before the\n * areas' layers, since a sticky area is lifted above the rest (`resolveLayoutLayers`).\n *\n * @param {Object<string, Array<Object>>} areas - area name → its blocks, their components initialized\n * @returns {Object<string, { top: string }|null>}\n */\nexport function layoutStickiness(areas) {\n const sticky = {}\n for (const [name, blocks] of Object.entries(areas || {})) sticky[name] = areaStickiness(blocks)\n return sticky\n}\n\n/**\n * The props a built-in layout zone takes when its area sticks — or null.\n *\n * @param {{ top: string }|null|undefined} stickiness - the area's (`areaStickiness`)\n * @param {number|null} [layer] - the area's layer (`resolveLayoutLayers`)\n * @returns {{ style: Object, 'data-uw-sticky': 'area' }|null}\n */\nexport function zoneProps(stickiness, layer) {\n return stickiness ? { style: stickyStyle(stickiness.top, layer), 'data-uw-sticky': 'area' } : null\n}\n\n/**\n * The style a sticky section's own wrapper takes — a body section's, or a child section's — or\n * null. Below what is stuck at the top unless its type says how far down; layer 1, so it paints\n * over the sections after it, which are positioned only when they have a background; and\n * top-aligned, so that as a flex or grid item it is not stretched to the row, which would leave\n * it no room to move (in normal flow `align-self` does nothing).\n *\n * @param {Object} block - its component initialized\n * @returns {Object|null}\n */\nexport function sectionStickyStyle(block) {\n return block?.sticky ? { ...stickyStyle(block.stickyTop || STICKY_SECTION_TOP, 1), alignSelf: 'start' } : null\n}\n\n/**\n * Whether any area of a page's layout sticks — its sections' components initialized, as both\n * renderers do before they render.\n *\n * @param {Object} page\n * @returns {boolean}\n */\nexport function hasStickyArea(page) {\n const areas = page?.getLayoutAreas?.() || {}\n return Object.values(areas).some((blocks) => areaStickiness(blocks) !== null)\n}\n\n/**\n * Where a link to a section lands — below what is stuck at the top, `--uw-sticky-top` — given to a\n * body section's box, and a child section's, on a page whose layout has a sticky area. A link to\n * `#section-…` (a search result, a contents entry, a pasted deep link) scrolls the section's top\n * edge to the top of the viewport, and without the margin the stuck header covers it — measured\n * 2026-10-20: a section's top at 0px under an 80px header, at 80px with the margin. On other pages\n * the markup is unchanged, and unmeasured the margin is `0px`.\n *\n * @param {Object} block - its component initialized\n * @param {'section'|'child'|boolean} kind - a body section's box, or a child section's; else none\n * @returns {{ scrollMarginTop: string }|null}\n */\nexport function sectionScrollMargin(block, kind) {\n return kind && hasStickyArea(block?.page) ? { scrollMarginTop: STICKY_SECTION_TOP } : null\n}\n\n/**\n * What a section's wrapper adds when it sticks: its style, and which kind of box it is.\n *\n * @param {Object} block - its component initialized\n * @param {'section'|'child'|boolean} kind - a body section (`true` reads as one), or a child section\n * @returns {{ style: Object, 'data-uw-sticky': string }|null}\n */\nexport function sectionStickyProps(block, kind) {\n const style = kind ? sectionStickyStyle(block) : null\n return style ? { style, 'data-uw-sticky': kind === 'child' ? 'child' : 'section' } : null\n}\n","/**\n * What the runtime puts on a layout-area wrapper.\n *\n * When a foundation enables view transitions (the default), the runtime gives\n * each layout area a `view-transition-name` so the browser animates it on its\n * own — persistent chrome (header, sidebar, footer) morphs in place while\n * everything else, the body included, crossfades as the page. With no names the\n * whole layout (chrome included) would flicker on every navigation.\n *\n * ⛔ **The body takes no name by default, which is what keeps a phone usable.** A\n * named element is captured as one image. WebKit captures it whole, off-screen part\n * included, at device resolution, painting every filter in it on the main thread; the\n * page itself is captured at the size of the viewport. Measured 2026-10-30 on a live\n * site, in WebKit on a 3× phone viewport with a desktop CPU: a named body was a\n * 1179 × 22710 capture, 107 MB against 9 MB, and a tap stalled the main thread for up to\n * 842 ms, against 271 ms with no name. Chromium did not stall at all. On an iPhone, that\n * site froze for about 30 s on a tap. The body still crossfades, with the page.\n *\n * Naming is only half of it. A `view-transition-name` **makes its element a\n * stacking context**, so the moment the runtime adds these wrappers it has\n * decided how the areas paint relative to one another — and with no `z-index`\n * on them they all sit at `auto` and paint in DOM order, which puts the body\n * over the header on any layout that renders the header first.\n *\n * That is not theoretical. It is the same mechanism `@uniweb/kit`'s `Overlay`\n * exists for (a modal opened from the header, trapped inside `uw-header`), and\n * it made a real docs page's fixed header unclickable while the identical\n * header on the marketing layout was fine — because that layout's markup\n * happened to wrap its header area in `relative z-40`. A framework that\n * creates stacking contexts owes its users an order; leaving it to DOM order\n * means \"does my header work\" is answered by an accident of someone's JSX.\n *\n * So this module resolves BOTH halves of the wrapper — the transition name and\n * the stacking layer — and hands back the finished style. It is pure (no\n * React/DOM) so the SPA renderer (`components/Layout.jsx`) and the SSR renderer\n * (`ssr-renderer.js`) produce identical wrappers, keeping prerendered HTML and\n * the hydrated SPA aligned.\n */\n\nimport { stickyStyle } from './sticky.js'\n\n// Namespace so generated names can't collide with `view-transition-name`s a\n// foundation sets inside its own component CSS. The prefix also guarantees a\n// valid CSS <custom-ident> (starts with a letter).\nconst NS = 'uw-'\n\nconst toIdent = (name) => NS + String(name).replace(/[^a-zA-Z0-9_-]/g, '-')\n\n/**\n * Build the effective view-transition-name map for a layout.\n *\n * Default: every rendered area gets a stable, namespaced name (`uw-<area>`), so\n * same-named areas across layouts share a name and morph between layouts\n * automatically. The body gets none and crossfades as the page (the header above).\n *\n * The layout's `meta.js` `transitions` value overrides this:\n * - an object overrides per region (`{ left: 'sidebar' }` to group across\n * layouts, `{ left: null }` to opt one region out, or `{ body: 'main' }` for a\n * body that morphs between layouts, which WebKit captures whole);\n * - `false` opts the whole layout out (back to the full-page crossfade).\n *\n * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.transitions`.\n * @returns {Object|null} region → view-transition-name; `null` when opted out.\n * A region whose value is null/empty in the returned map gets no name.\n */\nexport function resolveLayoutTransitions(areaNames, explicit) {\n if (explicit === false) return null\n\n const transitions = {}\n for (const name of areaNames) transitions[name] = toIdent(name)\n\n return explicit ? { ...transitions, ...explicit } : transitions\n}\n\n/**\n * The stacking layer of each area's wrapper.\n *\n * Default: every area except the body gets `1`, and the body gets nothing —\n * content is the backdrop, chrome is above it — and a sticky area gets one more\n * than every area that does not stick (below). That is the whole of what the\n * framework claims to know, and it is deliberately not more.\n *\n * The body is left unstacked rather than pinned to `0` on purpose. A layer\n * brings `position: relative` with it (see `areaWrapperStyle`), and a\n * positioned body wrapper would become the containing block for every\n * absolutely-positioned descendant on the page — a real behaviour change across\n * every site, to buy an ordering that lifting the chrome already achieves. An\n * unlayered body stays a plain stacking context and paints below anything with\n * a positive z-index, which is exactly the intent.\n *\n * In particular there is no default ordering BETWEEN chrome areas. Area names\n * are free-form (`header`, `footer`, `left` and `right` are conventions the\n * docs promote, but a foundation may define `topbar`, `rail`, `statusbar`,\n * anything), so ranking `header` above `left` would be the framework reading\n * meaning into a string it does not own — and would then behave differently for\n * a layout that spelled the same idea another way. Where two pieces of chrome\n * genuinely overlap, which one wins is a design decision, and the layout says\n * so with `layers`.\n *\n * The shape mirrors `transitions` exactly, so there is one thing to learn:\n * - an object overrides per region (`{ footer: 0 }`, `{ header: 5 }`), and a\n * region may be set to `null` to leave it unstacked;\n * - `false` opts the whole layout out, and the runtime then emits no\n * stacking at all — for a foundation that would rather own it in its own\n * markup, which is exactly what the marketing layout above was doing.\n *\n * Layers do NOT depend on view transitions. \"Chrome paints above content\" is a\n * property of the layout, not of how it animates — and body sections routinely\n * form their own stacking contexts (a section with a background isolates so its\n * background layer stays contained), so a fixed header in an unstacked sibling\n * area is not guaranteed to win against them either way. Tying the two together\n * was what left `DefaultLayout` hand-rolling its own `z-index: 40` on the\n * header: a second mechanism for the same job, which then swallowed `layers`\n * whole — a foundation could set `layers: { header: 0 }` on the default layout\n * and measurably nothing happened.\n *\n * ⭐ The one ordering the framework does know is a sticky area's (`sticky.js` — any of its sections\n * is). It stays in view while the rest of the page scrolls under it, so it is lifted above every\n * area that does not stick: the runtime made it stick, so it owes it the layer. Left equal to them,\n * a footer taller than the space below a stuck header paints over the header at the bottom of the\n * page, since it comes later in the DOM (measured 2026-10-19: a 521px footer on a 480px-tall\n * phone). That is a fact about the box, not a reading of the area's name, and the layout's\n * `layers` still rank a sticky area it names.\n *\n * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.layers`.\n * @param {Object} [sticky] - region → its stickiness, or null (`layoutStickiness`).\n * @returns {Object} region → z-index. Empty when the layout opts out.\n */\nexport function resolveLayoutLayers(areaNames, explicit, sticky = null) {\n if (explicit === false) return {}\n\n const layers = {}\n for (const name of areaNames) layers[name] = 1\n if (explicit) Object.assign(layers, explicit)\n\n const lifted = areaNames.filter((name) => sticky?.[name] && !(explicit && name in explicit))\n if (lifted.length > 0) {\n const under = Object.entries(layers)\n .filter(([name, layer]) => !sticky?.[name] && Number.isFinite(layer))\n .map(([, layer]) => layer)\n const above = Math.max(0, ...under) + 1\n for (const name of lifted) layers[name] = above\n }\n\n return layers\n}\n\n/**\n * The finished inline style for one area's wrapper, or `null` when the region\n * needs no wrapper at all.\n *\n * Returning the whole style from one place is the point: the SPA and SSR\n * renderers each build these wrappers, and a rule applied in one and forgotten\n * in the other is invisible until a prerendered page and its hydrated self\n * disagree about what paints on top.\n *\n * `position: relative` rides along with a layer because `z-index` does nothing\n * on a static element. It is set only on regions that carry a layer, which is\n * why the default leaves the body at `0` rather than lifting everything: a\n * positioned body wrapper would become the containing block for every\n * absolutely-positioned descendant on the page, and the ordering does not need\n * it.\n *\n * A sticky area (`sticky.js` — any of its sections is) is `position: sticky` in place of\n * `relative`, keeping its layer and its transition name. Only a foundation's layout passes one: the\n * built-in layout's area wrapper sits in its zone, as tall as itself, so the zone sticks instead.\n *\n * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @param {{ top: string }|null} [sticky] - the area's stickiness, when its box is the one that sticks\n * @returns {Object|null} Inline style object, or null for no wrapper.\n */\nexport function areaWrapperStyle(region, transitions, layers, sticky = null) {\n const style = {}\n\n const name = transitions?.[region]\n if (name) style.viewTransitionName = name\n else if (transitions && region === 'body') style.isolation = 'isolate'\n\n const layer = layers?.[region]\n if (sticky) {\n Object.assign(style, stickyStyle(sticky.top, layer))\n } else if (layer != null) {\n style.position = 'relative'\n style.zIndex = layer\n }\n\n return Object.keys(style).length > 0 ? style : null\n}\n\n/**\n * The props for one area's wrapper element — its style, and `data-uw-sticky=\"area\"` when it is\n * the box that sticks (`sticky.js`) — or `null` when the region needs no wrapper. Both renderers\n * build the wrapper from this, so the prerendered page and the browser's agree.\n *\n * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @param {{ top: string }|null} [sticky] - the area's stickiness, when its box is the one that sticks\n * @returns {{ style: Object, 'data-uw-sticky'?: 'area' }|null}\n */\nexport function areaWrapperProps(region, transitions, layers, sticky = null) {\n const style = areaWrapperStyle(region, transitions, layers, sticky)\n if (!style) return null\n return sticky ? { style, 'data-uw-sticky': 'area' } : { style }\n}\n","/**\n * The built-in layout — what a page renders with when the foundation offers no layout for it\n * (`Page.getLayoutName()` is null, or names none the foundation has): the header, the body and\n * the footer in one full-height column. Any other area is passed in and not drawn.\n *\n * ⭐ ONE COPY, FOR BOTH RENDERERS: the browser's (`components/Layout.jsx`) and the prerender's\n * (`ssr-renderer.js`, behind `uniweb build` and a host's prerender alike). It needs no hooks, no\n * Router and no DOM, so it is written with `createElement` and both import it — the L3 twins\n * exist for code that needs those, and this needs none.\n * ⛔ Until 2026-10-07 each renderer wrote its own, and the prerender's had no column: the browser\n * replaces the prerendered HTML rather than hydrating it, so a short page's footer sat right under\n * its content in the first paint — and in what a crawler reads — and jumped to the bottom when the\n * browser's render took over. Pinned by `tests/default-layout-parity.test.js`.\n *\n * ⛔ No stacking is set here. The areas arrive ordered: the runtime gives each area wrapper its\n * layer (`area-wrappers.js`), so chrome paints above the body whether or not view transitions are\n * on, and a foundation can re-rank them with `layers` in its layout meta. This layout used to\n * hard-code `z-index: 40` on the header and `30` on the footer for that job; once the runtime owned\n * the ordering, those numbers were a second mechanism for it, and the older one won — a positioned\n * element here becomes a stacking context that seals the area's own layer inside it. Measured\n * 2026-07-31: `layers: { header: 0 }` on the default layout changed nothing.\n *\n * ⭐ A ZONE IS THE BOX THAT STICKS. When a section of the header (or footer) area is sticky, the\n * runtime hands this layout the zone's props (`zones`, from `zoneProps`): its parent is the page\n * column, so it can stay in view, where the area wrapper inside it, as tall as itself, cannot\n * (`sticky.js`). The style carries the area's layer, which the sticky zone then holds.\n */\n\nimport React from 'react'\n\nconst COLUMN = { display: 'flex', flexDirection: 'column', minHeight: '100vh' }\nconst GROW = { flex: 1 }\n\n/**\n * @param {Object} props\n * @param {React.ReactNode} [props.header]\n * @param {React.ReactNode} [props.body]\n * @param {React.ReactNode} [props.footer]\n * @param {{ header?: Object, footer?: Object }} [props.zones] - a zone's props, when it sticks\n * @returns {React.ReactElement}\n */\nexport function DefaultLayout({ header, body, footer, zones }) {\n return React.createElement(\n 'div',\n { style: COLUMN },\n header && React.createElement('header', zones?.header || null, header),\n body && React.createElement('main', { style: GROW }, body),\n footer && React.createElement('footer', zones?.footer || null, footer),\n )\n}\n","/**\n * Appearance — site-wide color scheme (light/dark).\n *\n * ONE resolver, reached two ways:\n *\n * 1. SPA boot — `initAppearance()` runs inside initRuntime, after initUniweb()\n * (so website.themeData.appearance is readable) and before\n * createRoot().render(). That position precedes React's first paint, so no\n * section renders with the wrong tokens and then flips, and it covers every\n * delivery mode because all three start() branches funnel into initRuntime.\n *\n * 2. Prerendered HTML — `renderAppearanceBootScript()` serializes the SAME\n * function into a synchronous <head> script. HTML that ships real body\n * content is styled from :root (light) tokens until a bundle loads, so\n * without this a dark visitor sees a flash of light. The script is emitted\n * by injectPageContent() in ssr-renderer.js, which every prerender lane\n * goes through — the framework's SSG and the cloud worker's JIT render\n * alike. Emitting it from a lane-specific injector is how the cloud lane\n * silently missed it once already.\n *\n * Why serialize instead of hand-writing the inline script: the two paths must\n * agree exactly. `applyBootScheme` is therefore written to be SELF-CONTAINED —\n * it references no module-scope binding, only its two arguments and the browser\n * globals it needs — so `Function.prototype.toString()` yields a script that\n * behaves identically to calling it directly. Keep it that way: an import, a\n * module const, or a helper call would survive `toString()` as an undefined\n * identifier at first paint. appearance.test.js pins the equivalence.\n *\n * Environment-neutral by construction. `applyBootScheme` no-ops its DOM writes\n * outside a browser, and `renderAppearanceBootScript` only stringifies — so\n * ssr-renderer.js can import this module in Node and in a server-side isolate.\n *\n * Two writers with independent resolution is the bug this replaced:\n * WebsiteRenderer used to re-apply `appearance.default` from an effect, and\n * because React runs child effects before parent effects it clobbered the\n * visitor's stored preference on every page load — the page came back light\n * while the toggle button still believed it was dark, making the next click a\n * no-op.\n */\n\nimport { hasDarkScheme } from '@uniweb/core'\n\nexport const APPEARANCE_STORAGE_KEY = 'uniweb-appearance'\nexport const DARK_SCHEME_CLASS = 'scheme-dark'\nexport const LIGHT_SCHEME_CLASS = 'scheme-light'\n\n/**\n * Resolve and apply the visitor's color scheme.\n *\n * Precedence: stored visitor preference → OS preference (when the site opts in)\n * → the theme's declared default.\n *\n * SELF-CONTAINED ON PURPOSE — see the module header. This function is both\n * called directly (SPA boot) and serialized with toString() into the pre-paint\n * <script> of prerendered HTML. It must never reference anything outside its own\n * arguments and the browser globals below; the storage key and class names are\n * inlined as literals rather than read from the exported constants for exactly\n * that reason.\n *\n * Written in ES5 so it needs no transpilation in the inline-script form, and\n * every browser access is guarded: Safari private mode throws on localStorage,\n * old webviews lack matchMedia, and Node has no document.\n *\n * @param {boolean} respectSystem - follow prefers-color-scheme when unset\n * @param {'light'|'dark'} fallback - the theme's declared default\n * @returns {'light'|'dark'} the scheme applied\n */\nexport function applyBootScheme(respectSystem, fallback) {\n var stored = null\n try {\n stored = localStorage.getItem('uniweb-appearance')\n } catch (e) {\n // Safari private mode and some embedded webviews throw on access\n }\n\n var hasStored = stored === 'light' || stored === 'dark'\n var scheme = hasStored ? stored : fallback\n\n if (!hasStored && respectSystem) {\n try {\n if (window.matchMedia('(prefers-color-scheme: dark)').matches) scheme = 'dark'\n } catch (e) {\n // No matchMedia — keep the declared default\n }\n }\n\n try {\n var root = document.documentElement\n // Always set an explicit class rather than relying on the absence of one.\n // `default: system` themes emit a `@media (prefers-color-scheme: dark)`\n // block scoped to `:root:not(.scheme-light)`, so forcing light on a dark OS\n // requires `scheme-light` to be present — removing `scheme-dark` alone would\n // leave the media query still applying dark tokens.\n if (scheme === 'dark') {\n root.classList.add('scheme-dark')\n root.classList.remove('scheme-light')\n } else {\n root.classList.add('scheme-light')\n root.classList.remove('scheme-dark')\n }\n } catch (e) {\n // No DOM (Node / prerender) — the resolved scheme is still returned\n }\n\n return scheme\n}\n\n/**\n * Reduce a theme's `appearance:` block to the two arguments applyBootScheme\n * takes, or null when the site can never show dark.\n *\n * THE ONLY PLACE `appearance.*` FIELDS ARE READ. Both the SPA boot and the\n * inline-script emitter go through here, so the two cannot disagree about what\n * `respectSystemPreference` defaults to. They used to: the runtime treated an\n * unset value as false while the script emitter and @uniweb/core's\n * Theme.getAppearance() treated it as true. Those agreed only by the grace of\n * @uniweb/theming's normalizeAppearance() always injecting the key — any path\n * handing raw theme.yml appearance to the runtime would have produced a\n * pre-paint script and a boot resolver that disagree, i.e. the exact\n * flash-then-flip this whole module exists to prevent. Unset means true, which\n * is what the docs promise and what core already did.\n *\n * The null gate is @uniweb/core's hasDarkScheme() — the same predicate\n * @uniweb/theming uses to decide whether `.scheme-dark` CSS is generated at all.\n * Sharing it means we can never apply a scheme that has no matching rules, and\n * a light-only site correctly gets no class and no inline script.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {{respectSystem: boolean, fallback: 'light'|'dark'}|null}\n */\nexport function resolveAppearanceBoot(appearance) {\n if (!appearance || !hasDarkScheme(appearance)) return null\n\n return {\n respectSystem: appearance.respectSystemPreference !== false,\n fallback: appearance.default === 'dark' ? 'dark' : 'light',\n }\n}\n\n/**\n * Resolve and apply the boot scheme in the browser. Called by initRuntime.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {'light'|'dark'|null} the applied scheme, or null when the site has\n * no dark scheme to switch to (nothing is written to the document)\n */\nexport function initAppearance(appearance) {\n const opts = resolveAppearanceBoot(appearance)\n if (!opts) return null\n\n return applyBootScheme(opts.respectSystem, opts.fallback)\n}\n\n/**\n * Emit the pre-paint <script> for prerendered HTML.\n *\n * Returns '' when the site has no dark scheme — a light-only page always renders\n * light, so there is nothing to correct before paint and no reason to ship the\n * bytes. Pure SPA builds don't need it either: the body is empty until the\n * bundle renders and initAppearance() runs before that first render.\n *\n * Only a boolean and a JSON-quoted 'light'/'dark' are interpolated, both derived\n * from resolveAppearanceBoot rather than taken from the theme verbatim, so\n * author-supplied theme.yml values cannot inject script.\n *\n * @param {Object} [appearance] - the resolved theme.yml `appearance:` block\n * @returns {string} a `<script>` tag, or '' when no script is needed\n */\nexport function renderAppearanceBootScript(appearance) {\n const opts = resolveAppearanceBoot(appearance)\n if (!opts) return ''\n\n const call = `(${applyBootScheme.toString()})(${opts.respectSystem}, ${JSON.stringify(opts.fallback)})`\n\n return `<script id=\"uniweb-appearance\">${call}</script>`\n}\n","/**\n * The DOCUMENT TITLE — what a browser tab, a bookmark and a search result show for a page.\n *\n * ONE rule, reached two ways (the shape `appearance.js` has for the color scheme):\n *\n * 1. SPA — `useHeadMeta` sets `document.title` on every navigation.\n * 2. Prerendered HTML — `injectPageContent()` in ssr-renderer.js writes `<title>`, for\n * every prerender lane: the framework's SSG and a host's render alike.\n *\n * ⛔ Until 2026-09-14 each side carried its own rule. The SPA wrote `<page> | <site>` and\n * the prerender wrote `<page>` alone, so every prerendered page changed its title the\n * moment the bundle loaded (`Home` → `Home | Acme`), and a crawler that runs no script\n * indexed the shorter one.\n *\n * `<page title> | <site name>`; the page title alone when the site has no name; the empty\n * string when the page has no title — a caller then leaves the title it already has.\n *\n * @param {string|number|null|undefined} pageTitle - the page's display title (`page.getTitle()`)\n * @param {string|null|undefined} siteName - the site's name (`website.name`)\n * @returns {string}\n */\nexport function documentTitle(pageTitle, siteName) {\n if (!pageTitle) return ''\n const title = String(pageTitle)\n return siteName ? `${title} | ${siteName}` : title\n}\n","/**\n * The page a Website starts with as active — recorded when the SSR init builds the graph, and put\n * back before each render (`createPageRenderer`).\n *\n * ⭐ It is what the browser has on a URL that names no page. Core makes the site's index page (or\n * its first page) active when it builds the Website, and neither the boot nor `PageRenderer` makes\n * the not-found page active, since no route names it.\n *\n * ⛔ `renderPage` makes the page it renders active by its route, so for the not-found page it\n * changes nothing, and that page rendered with whatever page the Website rendered last: on a\n * Website reused across requests, the previous request's. Anything that adapts to the active page\n * — a header, a language switcher, a docs sidebar — showed that page.\n *\n * A WeakMap rather than a slot on core's sealed Website: it is SSR state, and core is in every site.\n */\nconst startPages = new WeakMap()\n\n/** Remember `website`'s active page as the one it starts with. Call it before anything renders. */\nexport function recordStartPage(website) {\n if (website && !startPages.has(website)) startPages.set(website, website.activePage)\n}\n\n/** Make `website`'s start page active again. A Website the SSR init did not build is left alone. */\nexport function restoreStartPage(website) {\n if (startPages.has(website)) website.activePage = startPages.get(website)\n}\n","/**\n * WHICH PAGE A ROUTE NAMES — the one lookup, importable without a renderer.\n *\n * ⭐ **It is one line, and it is a module because of who imports it.** `ssr-renderer.js` imports\n * `react-dom/server` at module scope, so anything reaching for this lookup through that file drags\n * the React server renderer into its own graph. The render's data step (`load-page-data.js`)\n * fetches and renders nothing, and did exactly that until 2026-09-20 — for `website.getPage`.\n *\n * ⚠️ **The docstring below already said \"no React … safe in a Worker isolate\", and was right.** It\n * was the FILE that made it false, which is the shape to watch for: a claim about a function's\n * dependencies is a claim about its module's imports, and nothing checks the two agree.\n * `tests/data-step-graph.test.js` checks it for the one caller that needs it.\n *\n * @module\n */\n\n/**\n * Resolve a route to the Page that should render it.\n *\n * Exists because `ssr-renderer.js` exported `renderPage(page, …)` and no supported way\n * to *get* a page — so every host rendering server-side wrote its own lookup,\n * and the obvious one (`website.pages.find(p => p.route === route)`) cannot\n * match a dynamic route, because the payload holds `/blog/:id` and the request\n * carries `/blog/1`. One host wrote that lookup three times in three files\n * before the gap was noticed. A renderer that takes a Page owes callers a Page.\n *\n * ⛔ Move it, never copy it: a second lookup here would be the fourth.\n *\n * This is `Website#getPage` — the same seven-step resolution the browser runs,\n * literally the same function, so a server-rendered page and the one hydrating\n * over it cannot disagree. Pure `@uniweb/core`: no React, no DOM, no DataStore\n * required, safe in a Worker isolate.\n *\n * @param {Website} website\n * @param {string} route - The requested path, e.g. `/blog/1`\n * @returns {Page|undefined} The page, or undefined when nothing matches — which\n * is a genuine 404 and the caller's to turn into one.\n */\nexport function resolvePage(website, route) {\n return website.getPage(route)\n}\n\n/**\n * The page a request for `route` renders, for the doors that render the page a visit starts on\n * (`createPageRenderer`, and its data step `loadPageData`): `resolvePage`, except a page served from\n * elsewhere.\n *\n * ⭐ A page with `rewrite:` is the host's to proxy, before the app loads. A request for one that\n * reaches a renderer was not proxied, so the doors answer it as the browser's first load of it does\n * (`PageRenderer.jsx`): with the site's not-found page, as a static build emits no file there.\n * ⛔ Until 2026-10-27 they rendered that page's own content as an ordinary page, and the browser\n * replaced it with the not-found page at boot.\n *\n * @param {Website} website\n * @param {string} route - The requested path\n * @returns {Page|undefined} undefined when the route names no page, or a page served from elsewhere\n */\nexport function resolveRequestedPage(website, route) {\n return website.resolveRoute(route).kind === 'rewrite' ? undefined : resolvePage(website, route)\n}\n","/**\n * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and server-side SSR.\n * Mirrors BlockRenderer.jsx using React.createElement directly — no hooks,\n * no JSX, no browser APIs. A section's background and the built-in layout are\n * not mirrored: both renderers draw the one copy (`background.js`,\n * `default-layout.js`).\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx, update `renderBlock` here to match.\n *\n * Exports three layers:\n * 1. Rendering functions (renderBlock, renderBlocks, renderLayout, renderBackground)\n * 2. Initialization (initPrerender, prefetchIcons)\n * 3. Per-page rendering (renderPage, classifyRenderError, injectPageContent, escapeHtml)\n */\n\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { createUniweb, resolveDefaultLocale } from '@uniweb/core'\nimport { sectionDomId } from '@uniweb/core/section-id'\nimport { Background } from './background.js'\nimport { routePatternToRegex, isDynamicRoute } from '@uniweb/core/route-match'\nimport { DEFAULT_ICON_BASE, iconUrl } from '@uniweb/core/icon-corpus'\nimport { buildSectionOverrides, FONT_LINKS_MARKER } from '@uniweb/theming'\nimport { prepareProps, getComponentMeta } from './prepare-props.js'\nimport { default404Html } from './default-404.js'\nimport {\n wireFoundationCapabilities,\n sliceContentForLocale,\n hydrateDataStore,\n ensureThemeCss,\n} from './wire-foundation.js'\nimport { resolveLayoutTransitions, resolveLayoutLayers, areaWrapperProps } from './area-wrappers.js'\nimport { layoutStickiness, sectionStickyProps, sectionScrollMargin, zoneProps } from './sticky.js'\nimport { DefaultLayout } from './default-layout.js'\nimport { renderAppearanceBootScript } from './appearance.js'\nimport { documentTitle } from './document-title.js'\nimport { recordStartPage } from './start-page.js'\nimport { escapeHtml } from '@uniweb/core/page-shell'\nimport { jsonForScript } from '@uniweb/core/data-script'\n\n// Re-export L2 helpers so the public `@uniweb/runtime/ssr` surface\n// carries everything an SSR consumer needs from one entry point.\nexport { sliceContentForLocale, hydrateDataStore }\n\n// ============================================================================\n// Layer 1: Rendering functions\n// ============================================================================\n\n/**\n * Valid color contexts for section theming\n */\nconst VALID_CONTEXTS = ['light', 'medium', 'dark']\n\n/**\n * Build wrapper props from block configuration.\n * Mirrors getWrapperProps in BlockRenderer.jsx.\n */\nexport function getWrapperProps(block) {\n const theme = block.themeName\n const blockClassName = block.state?.className || ''\n\n // Empty themeName = Auto → no context class → inherits tokens from :root\n // Non-empty = Pinned → context class sets tokens directly on the element\n let contextClass = ''\n if (theme && VALID_CONTEXTS.includes(theme)) {\n contextClass = `context-${theme}`\n }\n\n let className = contextClass\n if (blockClassName) {\n className = className ? `${className} ${blockClassName}` : blockClassName\n }\n\n // Normalized once by core (`block.background`) — the same value the SPA renderer reads.\n const background = block.background || {}\n const style = {}\n\n // If background has content, ensure relative positioning and a stacking context\n // so the background's z-index stays contained within this section.\n if (background.mode) {\n style.position = 'relative'\n style.isolation = 'isolate'\n }\n\n // The section's tokens in effect whatever the scheme, inline — as in the SPA renderer\n // (`BlockRenderer.jsx`); the rest of its theme is the page stylesheet's.\n if (block.contextOverrides) {\n for (const [key, value] of Object.entries(block.contextOverrides)) {\n style[`--${key}`] = value\n }\n }\n\n // Same rule as the SPA renderer and the search extractor — @uniweb/core/section-id.\n return { id: sectionDomId(block), style, className, background }\n}\n\n\n/**\n * Render a section's background for SSR: the one implementation both renderers draw\n * (`background.js`). A video background is drawn as its poster — what the browser draws too,\n * until it has mounted and starts the video.\n * ⛔ Until 2026-10-25 this was a copy of `Background.jsx`, and drew no video at all.\n *\n * @param {Object} background - `block.background`, normalized by core\n * @returns {React.ReactElement|null}\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n return React.createElement(Background, background)\n}\n\n/**\n * Render a single block for SSR.\n * Mirrors BlockRenderer.jsx but without hooks (no runtime data fetching).\n *\n * Two modes (mirrors client BlockRenderer):\n * - Bare (as=null/false): component only, no wrapper\n * - Section (as='section'/'div'/etc.): full treatment with wrapper, context, background\n *\n * @param {Block} block - Block instance to render\n * @param {Object} [options]\n * @param {string|null} [options.as='section'] - Wrapper element tag, or null/false for bare mode\n * @param {'section'|'child'|boolean} [options.stickySelf] - whether a sticky section's own wrapper\n * is the box that sticks: a body section's (`'section'`), or a child section's (`'child'`)\n * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { as = 'section', stickySelf = false } = {}) {\n const Component = block.initComponent()\n\n if (!Component) {\n return React.createElement('div', {\n className: 'block-error',\n style: { padding: '1rem', background: '#fef2f2', color: '#dc2626' },\n }, `Component not found: ${block.type}`)\n }\n\n // Resolve entity data synchronously (SSG has no async): EntityStore fills the keys\n // the component declares (`meta.data`) from the fetches that reach the block.\n const meta = getComponentMeta(block.type)\n const entityStore = block.website?.entityStore\n let entityData = null\n let pending = false\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready') entityData = resolved.data\n pending = resolved.status === 'pending'\n }\n\n // ⭐ A block whose data is still out is LOADING here, as in the browser, which sets the same flag\n // before `prepareProps` (`BlockRenderer.jsx`) and fetches the data once it boots. So the page\n // paints the component's loading state, the one the browser renders in its place.\n // ⛔ Until 2026-10-25 this was never set, and such a section prerendered its EMPTY state —\n // \"nothing found\", or an error — then switched to loading when the browser took over.\n block.dataLoading = pending\n\n // Build content and params with runtime guarantees.\n // prepareProps handles the full pipeline: entity data merge,\n // foundation data and content handlers, param defaults, guaranteed\n // content structure, and the props handler.\n // See prepare-props.js for the pipeline details.\n const prepared = prepareProps(block, meta, entityData)\n const params = prepared.params\n const content = { ...prepared.content, ...block.properties }\n\n const componentProps = { content, params, block }\n\n // Bare mode: component only, no wrapper or section chrome.\n // Used by ChildBlocks for grid cells, tab panels, inline children, insets.\n if (!as) {\n return React.createElement(Component, componentProps)\n }\n\n // Section mode: full treatment with wrapper, context classes, background.\n const { background, ...wrapperProps } = getWrapperProps(block)\n\n // A body section's wrapper, or a child section's, is the box that stays in view (`sticky.js`).\n const sticky = sectionStickyProps(block, stickySelf)\n if (sticky) {\n wrapperProps.style = { ...wrapperProps.style, ...sticky.style }\n wrapperProps['data-uw-sticky'] = sticky['data-uw-sticky']\n }\n // On a page whose layout has a sticky area, a link to this section lands below it.\n const margin = sectionScrollMargin(block, stickySelf)\n if (margin) wrapperProps.style = { ...wrapperProps.style, ...margin }\n\n // Merge Component.className (static classes declared on the component function)\n const componentClassName = Component.className\n if (componentClassName) {\n wrapperProps.className = wrapperProps.className\n ? `${wrapperProps.className} ${componentClassName}`\n : componentClassName\n }\n\n // Check if component handles its own background\n const hasBackground = background?.mode && meta?.background !== 'self'\n block.hasBackground = hasBackground\n\n // Determine wrapper element:\n // - Explicit as (not 'section') → use as prop directly\n // - Component.as → use component's declared tag (e.g., Header.as = 'header')\n // - fallback → 'section'\n const wrapperTag = as !== 'section' ? as : (Component.as || 'section')\n\n if (hasBackground) {\n return React.createElement(wrapperTag, wrapperProps,\n renderBackground(background),\n React.createElement('div', { style: { position: 'relative', zIndex: 10 } },\n React.createElement(Component, componentProps)\n )\n )\n }\n\n return React.createElement(wrapperTag, wrapperProps,\n React.createElement(Component, componentProps)\n )\n}\n\n/**\n * Render an array of blocks for SSR.\n */\nexport function renderBlocks(blocks, { stickySelf = false } = {}) {\n if (!blocks || blocks.length === 0) return null\n return blocks.map((block, index) =>\n React.createElement(React.Fragment, { key: block.id || index },\n renderBlock(block, { stickySelf })\n )\n )\n}\n\n/**\n * Render page layout for SSR.\n * Mirrors Layout.jsx but without hooks.\n */\nexport function renderLayout(page, website) {\n const layoutName = page.getLayoutName()\n const RemoteLayout = website.getRemoteLayout(layoutName)\n const layoutMeta = website.getLayoutMeta(layoutName)\n\n const bodyBlocks = page.getBodyBlocks()\n const areas = page.getLayoutAreas()\n\n // Mirror Layout.jsx: every block's component initialized before anything renders, so each\n // section's `sticky` — its type's default included — is known before the areas' layers are.\n for (const blocks of [bodyBlocks, ...Object.values(areas)]) {\n for (const block of blocks || []) block.initComponent()\n }\n\n // Mirror Layout.jsx: wrap the body and each area in a thin div, an area's carrying its\n // view-transition-name, so the prerendered HTML matches the page the SPA renders\n // in its place and the browser can animate regions independently on client navigation.\n const areaNames = Object.keys(areas)\n const transitions = website.viewTransitions\n ? resolveLayoutTransitions(areaNames, layoutMeta?.transitions)\n : null\n\n // Mirror Layout.jsx: an area stays in view when any of its sections is sticky — in a\n // foundation's layout its wrapper sticks, in the built-in one the zone around it — and is lifted\n // above the areas that scroll under it.\n const sticky = layoutStickiness(areas)\n const layers = resolveLayoutLayers(areaNames, layoutMeta?.layers, sticky)\n const wrapArea = (name, element) => {\n const props = areaWrapperProps(name, transitions, layers, RemoteLayout ? sticky[name] : null)\n return props ? React.createElement('div', props, element) : element\n }\n\n const bodyElement = bodyBlocks ? wrapArea('body', renderBlocks(bodyBlocks, { stickySelf: 'section' })) : null\n const areaElements = {}\n for (const [name, blocks] of Object.entries(areas)) {\n areaElements[name] = wrapArea(name, renderBlocks(blocks))\n }\n\n if (RemoteLayout) {\n const params = { ...(layoutMeta?.defaults || {}), ...(page.getLayoutParams() || {}) }\n return React.createElement(RemoteLayout, {\n page, website, params,\n body: bodyElement,\n ...areaElements,\n })\n }\n\n // The built-in layout — the browser's own copy, not a mirror of it (`default-layout.js`).\n // ⛔ Until 2026-10-07 this wrote its own, with no full-height column, so the prerendered\n // page differed from the one the browser renders over it.\n const zone = (name) => zoneProps(sticky[name], layers?.[name])\n return React.createElement(DefaultLayout, {\n body: bodyElement,\n ...areaElements,\n zones: { header: zone('header'), footer: zone('footer') },\n })\n}\n\n// ============================================================================\n// Layer 2: Initialization\n// ============================================================================\n\n/**\n * Construct a Uniweb singleton scoped to a single locale.\n *\n * Combines the three steps that every SSR consumer (browser SPA, Node\n * SSG, a server-side isolate) needs in the same order: slice the\n * multi-locale content payload, run `initPrerender` (which builds the\n * Website + wires foundation capabilities), then `setActiveLocale` so\n * `website.activeLang` stays in sync with what the page is rendering\n * for. Caller still owns DataStore hydration (per-request data differs\n * between requests; locale construction can be cached).\n *\n * @param {Object} content - Site content payload (possibly multi-locale).\n * @param {Object} foundation - Loaded foundation module.\n * @param {string} locale - Locale code to render in.\n * @param {Array<Object>|Object} [extensionsOrOptions] - Same shape as initPrerender's\n * third arg: an extensions array, or an options object when no extensions.\n * @param {Object} [maybeOptions] - Options object when extensions are passed.\n * @returns {import('@uniweb/core').default} The configured Uniweb singleton.\n */\nexport function initPrerenderForLocale(content, foundation, locale, extensionsOrOptions, maybeOptions) {\n const localeContent = sliceContentForLocale(content, locale)\n const uniweb = initPrerender(localeContent, foundation, extensionsOrOptions, maybeOptions)\n const defaultLang = resolveDefaultLocale(content?.config)\n if (locale && locale !== defaultLang && uniweb.activeWebsite?.setActiveLocale) {\n uniweb.activeWebsite.setActiveLocale(locale)\n }\n return uniweb\n}\n\n/**\n * Create and configure the Uniweb runtime for prerendering.\n *\n * Handles the full initialization sequence in the correct order:\n * createUniweb → setFoundation → capabilities → layoutMeta → basePath → childBlockRenderer.\n *\n * Returns the configured uniweb instance. Consumers can add extras after:\n * - Build: pre-populate DataStore, load extensions\n * - A server-side renderer: (none needed — the payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; a server-side renderer clones upfront).\n *\n * @param {Object} content - Site content JSON (pages, config, hierarchy)\n * @param {Object} foundation - Loaded foundation module\n * @param {Object} [options]\n * @param {function} [options.onProgress] - Progress callback\n * @returns {Object} Configured uniweb instance\n */\nexport function initPrerender(content, foundation, extensionsOrOptions, maybeOptions) {\n // Backwards-compatible arg shape: (content, foundation, options) or\n // (content, foundation, extensions, options). Extensions must be passed at\n // construction so the Website's FetcherDispatcher sees their routes.\n let extensions = []\n let options = {}\n if (Array.isArray(extensionsOrOptions)) {\n extensions = extensionsOrOptions\n options = maybeOptions || {}\n } else {\n options = extensionsOrOptions || {}\n }\n const { onProgress = () => {} } = options\n\n onProgress('Initializing runtime...')\n // Uniweb constructor wires foundation, capabilities, layoutMeta, handlers,\n // and extensions at construction time — see `@uniweb/core`'s src/uniweb.js.\n const uniweb = createUniweb(content, foundation, extensions)\n // Before anything renders: the active page every render starts from (`start-page.js`).\n recordStartPage(uniweb.activeWebsite)\n\n // Set base path from site config for subdirectory deployments\n if (content.config?.base && uniweb.activeWebsite?.setBasePath) {\n uniweb.activeWebsite.setBasePath(content.config.base)\n }\n\n // Set childBlockRenderer so ChildBlocks/Visual/Render work during prerender.\n // Mirrors the client's ChildBlocks component in PageRenderer.jsx:\n // - default bare rendering (no wrapAs) — component only, no wrapper\n // - pass wrapAs to opt into full section treatment\n uniweb.childBlockRenderer = function InlineChildBlocks({ blocks, from, wrapAs }) {\n const blockList = blocks || from?.childBlocks || []\n return blockList.map((childBlock, index) =>\n React.createElement(React.Fragment, { key: childBlock.id || index },\n renderBlock(childBlock, { as: wrapAs || null, stickySelf: wrapAs ? 'child' : false })\n )\n )\n }\n\n // The same background a section gets, for kit's `SectionBackground` — a component that\n // paints its own (`background: 'self'`). The one implementation, which the browser draws\n // too (setup.js); its `className` is the layer's. ⛔ Until 2026-10-25 this dropped it.\n uniweb.backgroundRenderer = Background\n\n // L2 (singleton wiring): defaultInsets, xref.build(), and any future\n // framework-level capability bridge — shared with setup.js so both\n // boot paths apply the same foundation contract. See\n // wire-foundation.js — its header states the rule for what belongs in\n // that helper vs. here vs. setup.js.\n wireFoundationCapabilities(uniweb, foundation)\n\n // Site-wide theme CSS. Unconditional here: at this point there is no\n // <head> to inspect, and injectPageContent() emits the result\n // idempotently, so a lane that already baked the style tag is unaffected.\n ensureThemeCss(uniweb, foundation)\n\n // Register SSR-safe routing so useRouting()/useActiveRoute() work during prerender.\n // renderPage() calls website.setActivePage() before rendering each page,\n // so activePage.route reflects the page being rendered — except the site's\n // not-found page, which no route names. Through `createPageRenderer` it renders\n // with the page a route served from elsewhere names, else the page the Website\n // starts with (`start-page.js`); through `generate404Html`, with the page\n // rendered last.\n const website = uniweb.activeWebsite\n uniweb.routingComponents = {\n useLocation: () => {\n // ⛔ `activePage.route` IS ALREADY ROOT-RELATIVE — `/`, `/about`. This used to\n // return `'/' + route`, so every prerendered page reported a pathname with a\n // doubled leading slash: `//` on the homepage, `//about` elsewhere.\n //\n // It failed silently for as long as it existed, because nothing in the\n // runtime reads pathname — a FOUNDATION does. Measured 2026-09-18: a language\n // switcher passing `useLocation().pathname` to `getLocaleUrl` emitted\n // `href=\"//\"`, which a browser resolves as a PROTOCOL-RELATIVE url (host\n // empty), not as the site root. The SPA was unaffected, so it reproduced only\n // in the built output.\n const route = website?.activePage?.route || '/'\n const pathname = route.startsWith('/') ? route : `/${route}`\n return { pathname, search: '', hash: '', state: null, key: 'default' }\n },\n useParams: () => ({}),\n useNavigate: () => () => {},\n }\n\n return uniweb\n}\n\n/**\n * Pre-fetch icons from CDN and populate the Uniweb icon cache.\n * Stores the cache on siteContent._iconCache for embedding in HTML.\n *\n * @param {Object} siteContent - Site content JSON (mutated: _iconCache added)\n * @param {Object} uniweb - Configured uniweb instance\n * @param {function} [onProgress] - Progress callback\n * @returns {Promise<string[]>} the references that could not be fetched\n */\nexport async function prefetchIcons(siteContent, uniweb, onProgress = () => {}) {\n const icons = siteContent.icons?.used || []\n if (icons.length === 0) return []\n\n const cdnBase = siteContent.config?.icons?.base || DEFAULT_ICON_BASE\n\n onProgress(`Fetching ${icons.length} icons for SSR...`)\n\n const results = await Promise.allSettled(\n icons.map(async (iconRef) => {\n const [family, name] = iconRef.split(':')\n const url = iconUrl(family, name, cdnBase)\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n return [`${family}:${name}`, await response.text()]\n })\n )\n // ⭐ Cached in the order the build names the icons (`icons.used`, sorted), never the order their\n // fetches settle in: the cache is written into every page (`_iconCache`, `__ICON_CACHE__`), so\n // in arrival order two builds of one site wrote different pages. ⛔ Until 2026-10-25 each icon\n // was cached as it arrived (measured: three builds of one site, three orders).\n for (const result of results) {\n if (result.status === 'fulfilled') uniweb.iconCache.set(...result.value)\n }\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failedRefs = icons.filter((_, i) => results[i].status === 'rejected')\n if (failedRefs.length > 0) {\n // Named, so an author can fix the reference. ⛔ Until 2026-10-24 this said only \"(1 failed)\",\n // and a missing icon on a live page took a search through every reference to find.\n const shown = failedRefs.slice(0, 10).join(', ') + (failedRefs.length > 10 ? ', …' : '')\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failedRefs.length} failed: ${shown})`\n console.warn(`[prerender] ${msg}`)\n onProgress(` ${msg}`)\n }\n\n // Store icon cache on siteContent for embedding in HTML\n if (uniweb.iconCache.size > 0) {\n siteContent._iconCache = Object.fromEntries(uniweb.iconCache)\n }\n return failedRefs\n}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n// ⭐ `resolvePage` LIVES IN ITS OWN LEAF and is re-exported here, so the public\n// `@uniweb/runtime/ssr` surface is unchanged. It is `website.getPage` and needs\n// no renderer; this file imports `react-dom/server` at module scope, so a caller\n// that only wants the lookup used to get React's server renderer with it\n// (`./resolve-page.js`, 2026-09-20).\nexport { resolvePage } from './resolve-page.js'\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\nexport function classifyRenderError(err) {\n const msg = err.message || ''\n\n if (msg.includes('Invalid hook call') || msg.includes('useState') || msg.includes('useEffect')) {\n return {\n type: 'hooks',\n message: 'contains components with React hooks (renders client-side)',\n }\n }\n\n if (msg.includes('Element type is invalid') && msg.includes('null')) {\n return {\n type: 'null-component',\n message: 'a component resolved to null (often hook-related, renders client-side)',\n }\n }\n\n return {\n type: 'unknown',\n message: msg,\n }\n}\n\n/**\n * Render a single page to HTML.\n *\n * Handles the full per-page pipeline:\n * setActivePage → renderLayout → renderToString → error handling → section override CSS.\n *\n * @param {Page} page - Page instance to render\n * @param {Website} website - Website instance\n * @returns {{ renderedContent: string, sectionOverrideCSS: string } | { error: { type: string, message: string } }}\n */\nexport function renderPage(page, website) {\n // ⛔ The site's not-found page is never made active by its route. No route names it, and on a\n // site whose parametric page matches `/404` (a root `/:slug`) that route names THAT page, which\n // then rendered as the active one. It renders with the active page it finds: the Website's\n // first, through `createPageRenderer` (`start-page.js`), as in the browser.\n if (page !== website.getNotFoundPage()) website.setActivePage(page.route)\n\n // A page that claims content but yields no blocks has not been loaded — it is\n // not an empty page. `Page#bodyBlocks` returns [] when its sections are absent\n // from the payload (split content), on the understanding that a caller loads\n // them first: the SPA does, in PageRenderer and at boot. THIS path never has.\n //\n // Left alone, that renders a structurally valid, completely empty document and\n // reports success — which is the worst shape a failure can take, and it cost a\n // host most of a day chasing a renderer that was doing what it was told.\n // Distinguishing it here is cheap: a content-less container reports\n // hasContent() === false and is correctly empty, so the two never collide.\n if (page.hasContent?.() && page.getBodyBlocks().length === 0) {\n return {\n error: {\n type: 'content-not-loaded',\n message:\n `page \"${page.route}\" declares content but has no loaded sections — ` +\n 'its sections are not in the payload and this renderer does not fetch them',\n },\n }\n }\n\n const element = renderLayout(page, website)\n\n let renderedContent\n try {\n renderedContent = renderToString(element)\n } catch (err) {\n return { error: classifyRenderError(err) }\n }\n\n // Build per-page section override CSS (theme pinning, component vars)\n const appearance = website.themeData?.appearance\n // Every block the page renders — each layout area it shows, a sidebar too; the builder walks\n // their child sections. The SPA renderer passes the same (`PageRenderer.jsx`).\n const sectionOverrideCSS = buildSectionOverrides(page.getAllBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n// Escape HTML special characters — the one implementation, which a page's shell writes with too\n// (`@uniweb/core/page-shell`). Still exported from here, as it has been since 0.6.14.\nexport { escapeHtml }\n\n/**\n * Inject prerendered content into an HTML shell.\n *\n * THE SHARED PRERENDER SEAM. Every lane that turns a Page into HTML calls this:\n * the framework's SSG (@uniweb/build's prerender.js) and the cloud worker's\n * just-in-time render. Anything a page needs *because it was prerendered at all*\n * belongs here, so a new feature reaches both lanes at once.\n *\n * Common operations shared by both build and cloud:\n * - Replace #root div with rendered HTML\n * - Update page title\n * - Write the page's head tags — description, canonical, hreflang, Open Graph (`writeHeadMeta`)\n * - Inject section override CSS\n * - Inject the pre-paint appearance script\n *\n * Build layers its additional injections on top of this return value:\n * __SITE_CONTENT__ JSON, icon cache, theme CSS (build-specific).\n *\n * WHICH SIDE OF THE SEAM? Ask what the injection is derived from. If it needs\n * only the page/website graph — which every lane has — it goes here. If it needs\n * a build-only artifact (the emitted bundle, the collection JSON files, the\n * prefetched icon cache, the compiled theme CSS), it goes in the caller. Getting\n * this wrong is silent: the appearance boot script started life in\n * @uniweb/build's injectBuildData and therefore never reached cloud-rendered\n * pages, which flashed light for every dark-mode visitor.\n *\n * @param {string} html - HTML shell\n * @param {string} renderedContent - React renderToString output\n * @param {Object} page - Page data { title, description, route }\n * @param {Object} [options]\n * @param {string} [options.sectionOverrideCSS] - Per-page section override CSS\n * @returns {string} HTML with injected content\n */\nexport function injectPageContent(html, renderedContent, page, options = {}) {\n let result = html\n\n // Pre-paint appearance script. Prerendered HTML carries real content styled\n // from :root (light) tokens, so a dark visitor would see a flash of light\n // before the bundle boots and applies the class. Read off the page's\n // website back-ref rather than a parameter: every lane builds the same graph,\n // so this needs no plumbing and no per-lane opt-in. Idempotent, because\n // @uniweb/build's injectBuildData may run over this HTML afterwards.\n if (!result.includes('id=\"uniweb-appearance\"')) {\n const bootScript = renderAppearanceBootScript(page?.website?.themeData?.appearance)\n if (bootScript) {\n result = result.replace('</head>', ` ${bootScript}\\n</head>`)\n }\n }\n\n // Site-wide theme CSS. Derived from the website graph\n // (`website.themeData`), so it belongs on THIS side of the seam — every\n // lane builds that graph. It lived in @uniweb/build's injectBuildData\n // until 2026-07-28, which meant sites served by any lane that doesn't run\n // the framework's build rendered with every semantic token unset: no\n // colours, no backgrounds, no failure anywhere. That is the same mistake\n // the appearance script above was moved out of, four lines below the\n // comment warning about it — see the note in `@uniweb/build`'s src/prerender.js.\n // Idempotent, so a shell that already carries the tag is left alone.\n const themeData = page?.website?.themeData\n const themeCss = themeData?.css\n if (themeCss && !result.includes('id=\"uniweb-theme\"')) {\n result = result.replace(\n '</head>',\n ` <style id=\"uniweb-theme\">\\n${themeCss}\\n </style>\\n</head>`\n )\n }\n\n // The theme's font <link> tags — same seam, same reasoning. Graph-derived,\n // so a lane that never runs @uniweb/build still gets its webfonts instead of\n // falling back to system faces. Deduped on FONT_LINKS_MARKER rather than an\n // id because <link> tags have none; the marker is owned by @uniweb/theming,\n // which generates the block, so this and @uniweb/build read one literal.\n if (themeData?.links && !result.includes(FONT_LINKS_MARKER)) {\n result = result.replace(\n '</head>',\n ` ${FONT_LINKS_MARKER}\\n${themeData.links}\\n</head>`\n )\n }\n\n // Inject per-page section override CSS before </head>\n if (options.sectionOverrideCSS) {\n const overrideStyle = `<style id=\"uniweb-page-overrides\">\\n${options.sectionOverrideCSS}\\n</style>`\n result = result.replace('</head>', `${overrideStyle}\\n</head>`)\n }\n\n // The icons this page drew, which the browser reads before its first render (`setup.js`), so it\n // draws them too. A replacer function, since `$` in a replacement string is not literal.\n if (options.icons?.size && !result.includes('id=\"__ICON_CACHE__\"')) {\n const iconScript = `<script id=\"__ICON_CACHE__\" type=\"application/json\">${jsonForScript(Object.fromEntries(options.icons))}</script>`\n result = result.replace('</head>', () => ` ${iconScript}\\n</head>`)\n }\n\n // Replace the empty root div with pre-rendered content\n result = result.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${renderedContent}</div>`\n )\n\n // Update the document title — by the rule the SPA sets `document.title` with\n // (`documentTitle`: `<page> | <site>`), so the title a crawler reads here is the one a\n // visitor keeps once the bundle loads. ⛔ Until 2026-09-14 this wrote the page title\n // alone. getTitle() so isIndex pages inherit their parent's title.\n const title = documentTitle(page.getTitle?.() || page.title, page.website?.name)\n if (title) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(title)}</title>`\n )\n }\n\n // ⭐ THE PAGE'S HEAD TAGS — description, canonical, hreflang, Open Graph, Twitter — from the\n // page's own head metadata (`Page#getHeadMeta`, which the browser writes the same tags from in\n // `useHeadMeta`), so crawlers and social unfurlers, which run no script, read the page's own.\n // ⛔ Until 2026-10-25 these were APPENDED, after the static build had written the HOME page's\n // into the one shell every page is rendered into: each page named the home page its canonical\n // and carried two og:title and two og:url, the home page's first.\n result = writeHeadMeta(result, page)\n\n return result\n}\n\n/**\n * Write a page's head tags into `html`'s head. These tags are the framework's: each is set to the\n * page's value, replacing any of its name the shell carries, and removed when the page has none —\n * as the browser does on every navigation (`useHeadMeta`), so hydrating the page changes nothing a\n * crawler read.\n *\n * @param {string} html\n * @param {Object} page\n * @returns {string}\n */\nfunction writeHeadMeta(html, page) {\n // A Page states its own (`getHeadMeta`); plain page data, which callers may hand in, its fields.\n const meta = typeof page?.getHeadMeta === 'function' ? page.getHeadMeta() : { description: page?.description || null }\n const end = html.indexOf('</head>')\n if (!meta || end === -1) return html\n const og = meta.og || {}\n const keywords = Array.isArray(meta.keywords) ? meta.keywords.join(', ') : meta.keywords\n const metaTags = [\n ['name', 'description', meta.description],\n ['name', 'keywords', keywords],\n ['name', 'robots', meta.robots],\n ['property', 'og:type', 'website'],\n ['property', 'og:site_name', og.siteName],\n ['property', 'og:title', og.title],\n ['property', 'og:description', og.description],\n ['property', 'og:image', og.image],\n ['property', 'og:url', og.url],\n ['name', 'twitter:card', og.image ? 'summary_large_image' : 'summary'],\n ['name', 'twitter:site', meta.twitter?.site],\n ['name', 'twitter:title', og.title],\n ['name', 'twitter:description', og.description],\n ['name', 'twitter:image', og.image],\n ]\n // The head alone: the page's content is in the body, and none of it is edited here.\n let head = html.slice(0, end)\n const written = []\n for (const [attr, key, value] of metaTags) {\n head = head.replace(new RegExp(`<meta\\\\b[^>]*\\\\b(?:name|property)=[\"']${key}[\"'][^>]*>\\\\s*`, 'gi'), '')\n if (value) written.push(`<meta ${attr}=\"${key}\" content=\"${escapeHtml(value)}\">`)\n }\n head = head.replace(/<link\\b[^>]*\\brel=[\"']canonical[\"'][^>]*>\\s*/gi, '')\n if (meta.canonical) written.push(`<link rel=\"canonical\" href=\"${escapeHtml(meta.canonical)}\">`)\n head = head.replace(/<link\\b[^>]*\\bhreflang=[\"'][^\"']*[\"'][^>]*>\\s*/gi, '')\n for (const { hreflang, url } of meta.alternates || []) {\n written.push(`<link rel=\"alternate\" hreflang=\"${escapeHtml(hreflang)}\" href=\"${escapeHtml(url)}\">`)\n }\n return `${head}${written.join('\\n')}\\n${html.slice(end)}`\n}\n\n// ============================================================================\n// 404 fallback generation\n// ============================================================================\n\n/**\n * Generate 404.html content for static hosting fallback.\n *\n * Serves two purposes on static hosts (GitHub Pages, Cloudflare Pages, etc.):\n * 1. Real 404: pre-rendered custom 404 page content (or blank #root if none defined)\n * 2. Valid dynamic route (e.g. /blog/2): inline script clears #root so SPA renders fresh\n *\n * Flow: static host serves 404.html → inline script runs before React mounts →\n * - dynamic route: clears #root, React renders the page normally\n * - real 404: leaves #root with pre-rendered content, React re-renders same 404 page\n *\n * @param {Object} options\n * @param {string} options.baseHtml - Assembled HTML shell (with site content already injected)\n * @param {Object} options.website - Initialized Website instance (from initPrerender)\n * @param {Object} options.siteContent - Site content object (to find dynamic templates)\n * @returns {{ html: string, hasNotFoundPage: boolean }}\n */\nexport function generate404Html({ baseHtml, website, siteContent }) {\n // Extract patterns for routes that remain as dynamic templates (prerender: false)\n // '/blog/:id' → /^\\/blog\\/([^/]+)$/. Compiled by the shared matcher rather\n // than a second regex built here: this file used to build its own with\n // `:[^/]+`, which disagreed with core's `:(\\w+)` on any param name carrying a\n // non-word character. See @uniweb/core/route-match for the whole story.\n // ⛔ **ROUTE, NOT THE FLAG — a filter must not read absence as \"not parametric\".**\n // This filtered on `p.isDynamic` alone. A producer that marks only the bracket\n // page (or none at all) left every parametric route out of the list, so a cold\n // hit on one showed the 404 body and never cleared #root for the SPA. The flag\n // is a producer's claim; the `:` in the route is the thing itself, and every\n // flagged page has one — so this is a superset and cannot lose a route.\n const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic || isDynamicRoute(p.route)) || []\n const routePatterns = dynamicTemplates.map((p) => routePatternToRegex(p.route).regex.source)\n\n let html = baseHtml\n\n // Pre-render the custom 404 page content into #root (if the site defines one),\n // otherwise inject a default 404 message so the page isn't blank before JS loads\n const notFoundPage = website.getNotFoundPage()\n if (notFoundPage) {\n const notFoundResult = renderPage(notFoundPage, website)\n if (notFoundResult && !notFoundResult.error) {\n html = injectPageContent(html, notFoundResult.renderedContent, notFoundPage, {\n sectionOverrideCSS: notFoundResult.sectionOverrideCSS,\n })\n }\n } else {\n const basePath = website.basePath || ''\n html = html.replace(\n /<div id=\"root\">[\\s\\S]*?<\\/div>/,\n `<div id=\"root\">${default404Html(basePath)}</div>`\n )\n }\n\n // Inject inline script: if path matches a dynamic route, clear #root before React mounts\n // so the SPA renders the correct page rather than the 404 content\n if (routePatterns.length > 0) {\n const patternList = routePatterns.map((p) => `/${p}/`).join(',')\n // The path is normalized here rather than by making every pattern accept a\n // trailing slash — same rule the matcher applies, applied in one place.\n const dynamicScript =\n `<script>(function(){` +\n `var p=[${patternList}],r=window.location.pathname.replace(/\\\\/+$/,'')||'/';` +\n `if(p.some(function(x){return x.test(r)})){` +\n `var el=document.getElementById('root');if(el)el.innerHTML='';` +\n `}})()</script>`\n html = html.replace('</body>', `${dynamicScript}\\n</body>`)\n }\n\n return { html, hasNotFoundPage: !!notFoundPage }\n}\n","/**\n * Runtime default fetcher.\n *\n * Used as the FetcherDispatcher's terminal fallback when no foundation\n * transport claims the request. Sites that declare no transport at all —\n * starter/docs/marketing templates hitting /data/*.json — ride on this path\n * with zero config, and so does a site a host serves live.\n *\n * ⭐ It speaks exactly TWO lanes, and takes NO site-level vocabulary for a\n * backend of the author's own:\n *\n * - a compiled file — `path:` under the site's base (`/data/<query>.json`,\n * a per-record file), or a plain JSON `url:` the author wrote;\n * - the host's RECORDS SERVICE — `ask:`, one POST per tick carrying every\n * question the page asked, answered per key (the records contract, as this\n * client reads it).\n *\n * A query's set — `scope:` / `where:` / `sort:` / `limit:` — and the `narrow`\n * a fetch takes of it are evaluated HERE, locally, over what the first lane\n * returns — with `@uniweb/core`'s one evaluator (`evaluateQuery`), the same the\n * build uses — and at the source when asked. Nothing decides that per site: the\n * LANE decides.\n *\n * ⛔ RETIRED 2026-09-04 [Diego]: `fetcher.baseUrl`, `headers`, `envelope`,\n * `supports`, `request.style` / `request.rename` and the `json-body`\n * request-style registry. *\"3rd party endpoints must be supported at the\n * foundation level… making the runtime+core lean.\"* A backend with its own\n * base, headers, wire or query language is a TRANSPORT — a named\n * `{ resolve, cacheKey? }` the foundation (or an extension) registers and\n * the site selects per binding key (`as`) in `fetcher.transports`. The build warns once\n * and drops a retired key from the payload, so an author's backend does not\n * silently stop being reached.\n *\n * A request may carry `method: 'POST'` + `body:` for an endpoint that takes a\n * query in a body (GraphQL, a search endpoint) — an external query's, or its\n * `record:` request's — and `transform:`, a dot-path to the records in ONE\n * response. `{paramName}` placeholders in body strings are substituted from\n * `request.dynamicContext`, so a parametric page's record request can reference\n * its route param. ⛔ `envelope` (the object form of `detail:`) is retired with\n * it, 2026-09-13: a record request's own `transform` is `record.transform`.\n *\n * Exported from a subpath — `@uniweb/runtime/default-fetcher` — for\n * runtime-level callers (the editor's preview iframe, custom runtime\n * harnesses). **Foundations should not import this.** A foundation that\n * wants plain URL + JSON behavior simply omits its own transport; the\n * runtime installs this one automatically.\n *\n * Intentional omission: credentials / secrets. Any value the framework puts\n * into the served HTML is public to the browser. Sites needing private\n * credentials use a deployment-layer proxy — the site fetches a same-origin\n * URL, and a layer in front resolves the credential and forwards upstream.\n */\n\nimport {\n substitutePlaceholders,\n evaluateQuery,\n sortRecords,\n sortToWire,\n deriveCacheKey,\n resolveServiceUrl,\n} from '@uniweb/core'\n\n/**\n * @param {Object} [options]\n * @param {string} [options.basePath=''] - Prepended to local absolute paths\n * for subpath deployments. Remote URLs pass through unchanged.\n * @param {boolean} [options.dev=false] - Dev-mode diagnostics: a bad `sort:`\n * throws instead of delivering the records unsorted.\n * @param {Function|null} [options.fetch=null] - The transport. A host executing\n * fetches outside a browser (an SSR isolate) decides how a site-relative\n * address is dispatched — through its own origin or a service binding — and\n * hands that in. Defaults to the global `fetch`, resolved at call time so a\n * test stub installed later is honoured. ⛔ The entries a host calls\n * (`loadPageData`, `prefetchAndHydrate`, `collectSiteRecords`) never reach this\n * default: they refuse a missing transport (`require-transport.js`). It serves\n * the browser and a harness that builds its own fetcher.\n * @returns {{ cacheKey: (req: Object) => string, resolve: (req: Object, ctx: Object) => Promise<{ data, error?, meta? }> }}\n */\nexport function createDefaultFetcher({ basePath = '', dev = false, fetch: fetchImpl = null } = {}) {\n const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init)\n const pathPrefix = basePath && basePath !== '/' ? basePath.replace(/\\/$/, '') : ''\n\n\n // ⭐ THE RECORDS SERVICE — a batch of the misses, one POST, merged per key.\n //\n // The entity store dispatches every config a page needs in one synchronous\n // loop before awaiting any of them, so a question enqueued here and flushed\n // on the next microtask carries every miss of that page in one body. The\n // batch response is never cached as one: each request gets its own answer,\n // keyed by its own question.\n const askQueues = new Map()\n const askRecords = (request, ctx) => {\n // ⛔ A question needs the query's Model ref. A payload that offers the\n // service and carries no `config.queries` entry for the query cannot ask;\n // that is a producer defect and it is said here, per key, with no request.\n if (typeof request.schema !== 'string' || !request.schema) {\n return Promise.resolve({\n data: null,\n error: `the payload offers the records service but carries no Model ref for query ` +\n `\"${request.query ?? request.as}\" (config.queries) — it cannot be asked`,\n })\n }\n return new Promise((resolve) => {\n const url = resolveServiceUrl(request.ask, pathPrefix)\n let queue = askQueues.get(url)\n if (!queue) {\n queue = []\n askQueues.set(url, queue)\n queueMicrotask(() => {\n askQueues.delete(url)\n flushAsked(url, queue, doFetch)\n })\n }\n queue.push({ request, ctx, resolve })\n })\n }\n\n // ⭐ VIEWS OF ONE FILE SHARE ONE READ IN FLIGHT. Each view — its own `scope`,\n // `where`, `sort` or `limit` — is its own cache entry (`deriveCacheKey` hashes\n // the view) and is cut from the read; views asked together (a page's sections,\n // a host's prefetch) share one request. ⛔ Until 2026-09-11 the view was applied\n // before the dispatcher stored the answer under a key that left the view out, so\n // one file had ONE entry and whoever asked first decided what everyone got\n // (measured). A settled read is not kept: a view asked later reads again (a\n // static host's HTTP cache answers it), so dropping an entry and asking again\n // still reaches the source. The read carries no caller's abort signal — several\n // views may be waiting on it, and one navigating away must not cancel it for\n // the others.\n const reads = new Map()\n const readOnce = (target, init) => {\n const key = `${init.method} ${target} ${init.body ?? ''}`\n let pending = reads.get(key)\n if (!pending) {\n pending = readResponse(doFetch, target, init).finally(() => reads.delete(key))\n reads.set(key, pending)\n }\n return pending\n }\n\n return {\n /**\n * The cache identity is the request's ADDRESS and the view it takes of it —\n * or, when asked of the records service, the QUESTION (`deriveCacheKey`).\n * Two pages declaring different `where:` clauses against one path are two\n * entries, each cut from a read of the file (`readOnce`, above).\n */\n cacheKey(request) {\n return deriveCacheKey(request)\n },\n\n async resolve(request, ctx = {}) {\n if (!request) return { data: null }\n if (request.ask) return askRecords(request, ctx)\n const { path, url, transform, body: rawBody } = request\n\n // Normalize method. Only GET and POST are supported by the default\n // fetcher — mutations (PUT/PATCH/DELETE) are a different feature\n // (optimistic updates, action semantics) and don't belong here.\n let method = (request.method || 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'POST') {\n console.warn(`[default-fetcher] method \"${request.method}\" is not supported — falling back to GET.`)\n method = 'GET'\n }\n\n let target\n if (path) {\n // Local file under public/ — basePath applies for subpath deploys.\n target = pathPrefix && path.startsWith('/') && !path.startsWith('//')\n ? pathPrefix + path\n : path\n } else if (url) {\n // A URL the author wrote, sent exactly as written.\n target = url\n } else {\n return { data: [], error: 'No path, url or ask specified' }\n }\n\n const init = { method }\n\n if (method === 'POST') {\n // Substitute {paramName} placeholders in body strings using the\n // dynamic-route context. The helper expects a flat key→value map;\n // build it from dynamicContext's { paramName, paramValue } shape.\n // Strict-brace matcher: GraphQL selection sets pass through unchanged.\n const dc = request.dynamicContext\n const body = (rawBody !== undefined && rawBody !== null && dc && dc.paramName)\n ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false })\n : rawBody\n if (body !== undefined && body !== null) {\n init.headers = { 'Content-Type': 'application/json' }\n init.body = typeof body === 'string' ? body : JSON.stringify(body)\n }\n }\n\n try {\n const response = await readOnce(target, init)\n if (ctx.signal?.aborted) return { data: [], error: 'aborted' }\n\n if (!response.ok) {\n return { data: [], error: `HTTP ${response.status}: ${response.statusText}` }\n }\n\n let data = response.body\n\n // Unwrap the response: the request's own `transform:`, and nothing else — a\n // record request carries its `record.transform`, never the list's.\n if (transform && data !== null && data !== undefined) {\n data = getNestedValue(data, transform)\n }\n\n // Evaluate the query locally. Only applies to array data\n // (filtering/sorting/limiting a single record doesn't make sense).\n // For non-arrays, operators are ignored — the source returned what\n // it returned.\n data = applyOperators(data, request, { dev, locale: sortLocale(request, ctx) })\n\n // ⭐ Say whether WHOLE records were delivered, so the record index can\n // file them — what the config asked for, echoed: briefs when the query\n // has a per-record source, whole records otherwise. (The service reports\n // `whole` per\n // key and overrides this with what it actually served.)\n const whole = typeof request.whole === 'boolean' ? request.whole : undefined\n return whole === undefined ? { data: data ?? [] } : { data: data ?? [], meta: { whole } }\n } catch (error) {\n if (error?.name === 'AbortError') {\n return { data: [], error: 'aborted' }\n }\n return { data: [], error: error?.message || String(error) }\n }\n },\n }\n}\n\n/**\n * One question of a batch, in the records service's own vocabulary\n * (the records contract, §2).\n * ⭐ TWO LEVELS, ruled 2026-09-14 [Diego]:\n *\n * - **the top level is the query as saved** — `schema` (required), `scope` a bare\n * path, `where`, `sort` one key spelled `date` / `-date`, `limit` — and `whole`\n * when the whole record is wanted. Its records are the set, and the top level is\n * the same on every question about it;\n * - **`narrow`** — how this page takes less than the set: `where`, `match` (a\n * parametric page's record), `sort`, `cursor` (to resume a previous answer) and\n * `limit`. Absent when the question asks for the set as it is.\n *\n * ⛔ Until 2026-09-14 the two were one flat question — the page's `where` joined the\n * query's, its `sort` and `limit` replaced the query's — and `match` and `cursor` sat\n * at the top level, where the service no longer takes them. There is no fallback:\n * the service answers an unknown or misplaced field with a `400` for the request.\n *\n * ⭐ A where-object crosses exactly as authored: the evaluator and the service speak\n * one language (`@uniweb/core`'s where evaluator). ⛔ `nin` was respelled `not_in`\n * here until 2026-09-13, when the evaluator took `not_in` as its only spelling.\n * Anything the service does not answer (a retired operator) is sent as written and\n * answered there: never approximated.\n */\n// Exported so the question a parametric page's record is asked with can be generated as test\n// vectors: it is this body, and nothing else composes one.\nexport function toQuestion(request) {\n const q = { schema: request.schema }\n const where = request.where && typeof request.where === 'object' ? request.where : null\n // ⭐ `scope` crosses as authored. ⛔ A top-level `where.path.under` was respelled\n // into it until 2026-09-11, when that form was retired in favour of `scope:`\n // [Diego] — the build refuses it now, so there is nothing left to respell.\n const scope = typeof request.scope === 'string' && request.scope ? request.scope : null\n if (scope) q.scope = scope\n if (where) q.where = where\n const sort = wireSort(request.sort)\n if (sort) q.sort = sort\n if (typeof request.limit === 'number' && request.limit > 0) q.limit = request.limit\n // ⛔ ONLY WHEN TRUE. The brief is the default and absent means the brief, so\n // sending `whole: false` would be noise on every list question the client makes.\n if (request.whole === true) q.whole = true\n const narrow = toNarrow(request.narrow)\n if (narrow) q.narrow = narrow\n // ⛔ `exhaustive` and `maxPages` deliberately do NOT cross — they are client\n // instructions about how many times to ask, never part of what is being asked.\n return q\n}\n\n/**\n * A question's `narrow`, with only the fields the service takes inside it — any other\n * is a `400` for the whole request there — or `null` when nothing is left.\n *\n * - `where` crosses as authored;\n * - `match` — the record of a parametric page: one key and the URL's value, beside\n * the author's `where`, never merged into it (`buildDetailConfig`);\n * - `sort` in the service's spelling;\n * - ⭐ `cursor` — the ONLY field in a question that is not the author's: opaque, from a\n * previous answer's `cursors`, sent back on the same question and nothing else\n * changed. A cursor is a narrowing like the rest, so the top level stays the query as\n * saved on every page;\n * - `limit`, when it cuts.\n */\nfunction toNarrow(narrow) {\n if (!narrow || typeof narrow !== 'object') return null\n const n = {}\n if (narrow.where && typeof narrow.where === 'object') n.where = narrow.where\n if (narrow.match && typeof narrow.match === 'object') n.match = narrow.match\n const sort = wireSort(narrow.sort)\n if (sort) n.sort = sort\n if (typeof narrow.cursor === 'string' && narrow.cursor) n.cursor = narrow.cursor\n if (typeof narrow.limit === 'number' && narrow.limit > 0) n.limit = narrow.limit\n return Object.keys(n).length > 0 ? n : null\n}\n\n/**\n * A `sort` in the service's spelling — `date` / `-date` — or, for one outside the\n * language, the author's text AS WRITTEN, which the service answers by its own rule\n * (ignored: the author's arrangement). ⛔ Never a throw: this runs while a page's whole\n * batch is being composed, before any request, and until 2026-09-14 a sort `parseSort`\n * refuses — two keys, a bad direction — threw here and left every question of the batch\n * pending forever.\n */\nfunction wireSort(sort) {\n try {\n return sortToWire(sort)\n } catch {\n return typeof sort === 'string' && sort.trim() ? sort : null\n }\n}\n\n/**\n * Send one batch to the records service and hand each question its own answer.\n *\n * The response is `{ data, whole?, cursors?, partial? }` (backend's records-query\n * contract rev D, 2026-09-13), each map absent when empty: `data` answers exactly the\n * keys sent — `[]` when nothing matched, and `[]` too for a question an author got\n * wrong, since the service answers a mistake rather than refusing it; `whole[key]` says\n * a key was served as whole records; `cursors[key]` that more records remain past the\n * question's `narrow.limit` — a query's own `limit` defines the set and never produces\n * one; `partial[key]` that a source of the key did not answer. A key\n * missing from `data` is a protocol violation and is reported as an error, never as\n * silence. ⛔ `errors` and `limits` are not read: the service stopped sending them in\n * the same revision, and this client read both until 2026-09-14.\n *\n * ⭐ `cursors` ARE READ — 2026-09-06 [Diego], reversing the ruling that had them\n * \"received and IGNORED, because framework has no paging concept.\" ⛔ **That ruling\n * described our client and was silently wrong about our USERS:** a `cursors` entry is\n * how the service says there is more, and discarding it meant a hosted list could\n * render part of its records with no way for an author to tell that from the end of\n * the data. **The silent class, on a visitor's page.**\n *\n * Two behaviours, deliberately not one:\n *\n * - **a page render REPORTS** — `meta.partial` rides the answer, and nothing pages\n * automatically. Auto-paging here would put unbounded round trips in front of\n * paint for a section that may only show ten rows.\n * - **an exhaustive caller PAGES** — `request.exhaustive` follows `cursors`\n * until the service stops issuing them. `collectSiteRecords` is the caller\n * that wants it (a corpus is not a page), and `maxPages` bounds the loop so\n * a service that always answers with a cursor cannot spin.\n */\n\n/**\n * Pages an exhaustive request follows before stopping and reporting the answer\n * as partial. A caller sets its own with `request.maxPages`.\n *\n * ⚖️ **20 is a bound, not a target** — it is the value a real caller chose for a\n * real per-request budget (a search index, 20 × 100), taken as the default\n * because any bound prevents a runaway and a low one fails visibly rather than\n * expensively. A caller that knows its budget passes its own.\n */\nconst DEFAULT_MAX_PAGES = 20\nasync function flushAsked(url, queue, doFetch) {\n // One shared page loop: the batch is sent, and any entry that asked to be\n // exhaustive and came back with a cursor is re-sent alone until it is done.\n const pending = new Map()\n const body = {}\n const keys = []\n for (const entry of queue) {\n const base = entry.request.as || 'q'\n let key = base\n for (let n = 2; key in body; n += 1) key = `${base}#${n}`\n keys.push(key)\n body[key] = toQuestion(entry.request)\n }\n let parsed\n try {\n const response = await doFetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n // A protocol violation is refused for the WHOLE request with a problem body\n // whose `detail` names the key and the fault (an unknown operator, an empty\n // binding key, a non-BCP-47 locale segment…). Surface that sentence on every\n // key of the batch rather than the bare status: the author reads\n // `block.dataError` and the status alone says nothing they can act on.\n let detail = null\n try {\n const problem = safeParseJSON(await response.text())\n if (problem && typeof problem.detail === 'string' && problem.detail) detail = problem.detail\n } catch { /* an unreadable body falls back to the status line */ }\n const error = detail\n ? `HTTP ${response.status}: ${detail}`\n : `HTTP ${response.status}: ${response.statusText}`\n for (const entry of queue) entry.resolve({ data: null, error })\n return\n }\n parsed = await response.json()\n } catch (error) {\n const message = error?.name === 'AbortError' ? 'aborted' : (error?.message || String(error))\n // ⛔ AN ABORT KEEPS WHAT ARRIVED. On an exhaustive walk every in-flight key\n // fails at once here, so discarding held pages loses the whole corpus rather\n // than one key — the \"rejection that loses every key\" a caller cannot have.\n for (const entry of queue) {\n const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null\n entry.resolve(held\n ? { data: held, error: message, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error: message })\n }\n return\n }\n const data = parsed && typeof parsed.data === 'object' && parsed.data ? parsed.data : {}\n // ⭐ `whole[key]` is present only for keys delivered as WHOLE entities — so a\n // key's ABSENCE is the brief, and a key present that never asked is the\n // brief-less Model saying so. Absent entirely when every key is briefs.\n const wholes = parsed && typeof parsed.whole === 'object' && parsed.whole ? parsed.whole : {}\n const cursors = parsed && typeof parsed.cursors === 'object' && parsed.cursors ? parsed.cursors : {}\n // ⭐ `partial[key]` — `{ source, code, detail }`: a source of that key did not answer,\n // and the key carries what the others returned. Unread, a degraded key looked complete.\n const degraded = parsed && typeof parsed.partial === 'object' && parsed.partial ? parsed.partial : {}\n queue.forEach((entry, i) => {\n const key = keys[i]\n // ⛔ A FAILURE MUST NOT DISCARD PAGES ALREADY COLLECTED. An exhaustive walk\n // that fails on page 7 has six pages in hand, and a caller that asked for a\n // corpus would rather have them marked partial than lose the key — losing it\n // is indistinguishable from \"this query has no records\".\n const held = Array.isArray(entry.collected) && entry.collected.length ? entry.collected : null\n if (!(key in data)) {\n const error = `the records service answered without the key \"${key}\"`\n entry.resolve(held\n ? { data: held, error, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error })\n return\n }\n const whole = typeof wholes[key] === 'boolean'\n ? wholes[key]\n : (typeof entry.request.whole === 'boolean' ? entry.request.whole : undefined)\n\n const cursor = typeof cursors[key] === 'string' && cursors[key] ? cursors[key] : null\n const rows = Array.isArray(data[key]) ? data[key] : data[key]\n\n // An exhaustive caller collects the page and asks for the next one.\n if (cursor && entry.request.exhaustive && Array.isArray(rows)) {\n const acc = entry.collected ? entry.collected.concat(rows) : rows.slice()\n const page = (entry.page || 1) + 1\n const cap = typeof entry.request.maxPages === 'number' && entry.request.maxPages > 0\n ? entry.request.maxPages\n : DEFAULT_MAX_PAGES\n if (page <= cap) {\n pending.set(entry, { cursor, collected: acc, page, whole })\n return\n }\n // The caller's own bound, not the service's: report rather than spin.\n entry.resolve({ data: acc, meta: withMeta({ whole, partial: true, pages: cap }) })\n return\n }\n\n const collected = entry.collected ? entry.collected.concat(Array.isArray(rows) ? rows : []) : rows\n const meta = withMeta({\n whole,\n // ⭐ ONE FLAG, MEANING **NOT THE WHOLE POPULATION** — asked for by name, so\n // a caller has one boolean to branch on rather than several signals to\n // combine. It is set in every case that means it:\n // · a cursor came back and this caller does not page (a page render);\n // · a cursor came back and the caller's `maxPages` stopped the walk;\n // · the walk failed or aborted with pages already in hand;\n // · a source of the key did not answer (`partial[key]`), and `unavailable`\n // carries the service's own account of which one.\n // ⛔ Named `truncated` when it shipped in 0.17.0; renamed the same day, before\n // any consumer adopted it, because \"truncated\" says something was cut and this\n // also means \"there is more you did not ask for\".\n partial: (cursor || key in degraded) ? true : undefined,\n unavailable: key in degraded ? degraded[key] : undefined,\n pages: entry.page && entry.page > 1 ? entry.page : undefined,\n })\n entry.resolve(meta ? { data: collected, meta } : { data: collected })\n })\n\n if (pending.size === 0) return\n // Re-ask each unfinished key on its own — the cursor is per key, so a batch\n // would have to correlate several independent positions through one body.\n // ⭐ The cursor rides inside `narrow` and NOTHING ELSE in the question changes: the\n // service resumes only the question a cursor came from, and answers a cursor sent\n // with anything else changed with a `400`.\n await Promise.all([...pending].map(([entry, state]) => {\n const next = {\n ...entry,\n request: { ...entry.request, narrow: { ...(entry.request.narrow || {}), cursor: state.cursor } },\n collected: state.collected,\n page: state.page,\n }\n return flushAsked(url, [next], doFetch)\n }))\n}\n\n/**\n * Read one response and parse its body — the part of a fetch every view of an\n * address shares. A JSON body is parsed as JSON; anything else is tried as JSON\n * and kept as text when it is not. A failed response keeps its text, so a caller\n * can read a message out of it (the records service's problem details).\n *\n * ⛔ A WHOLE HTML DOCUMENT IS NOT DATA — it is a failed read. A host that serves a site\n * answers an address it holds no file for with the site's page, a `200` of `text/html`,\n * and kept as text that page reached a component as a query's records: the component\n * called `.map` on it and the page fell to its error boundary. Measured 2026-09-26 on a\n * site a local backend serves, `/es/data/team.json` answering with the page. A fragment of\n * HTML is still kept as text; only a document — `<!doctype html>` or `<html>` — is refused.\n */\nasync function readResponse(doFetch, target, init) {\n const response = await doFetch(target, init)\n if (!response.ok) {\n let text = null\n try {\n text = await response.text()\n } catch {\n // Body not readable — the status line is all there is.\n }\n return { ok: false, status: response.status, statusText: response.statusText, text }\n }\n const contentType = response.headers.get('content-type') || ''\n if (contentType.includes('application/json')) return { ok: true, body: await response.json() }\n const text = await response.text()\n try {\n return { ok: true, body: JSON.parse(text) }\n } catch {\n if (HTML_DOCUMENT.test(text)) {\n return { ok: false, status: response.status, statusText: 'an HTML page, not data', text: null }\n }\n return { ok: true, body: text }\n }\n}\n\nconst HTML_DOCUMENT = /^\\s*(?:<!doctype\\s+html|<html[\\s>])/i\n\n/** Build a `meta` from the fields that are actually present, or `undefined`. */\nfunction withMeta(fields) {\n const out = {}\n for (const [k, v] of Object.entries(fields)) if (v !== undefined) out[k] = v\n return Object.keys(out).length ? out : undefined\n}\n\n/**\n * The locale a list's texts are collated in: the page's (ruled 2026-09-14 [Diego]).\n * A request asked in a locale carries it; otherwise the caller's — the website's\n * active locale in the browser, the locale a host's prerender renders.\n */\nfunction sortLocale(request, ctx) {\n if (typeof request?.locale === 'string' && request.locale) return request.locale\n if (typeof ctx?.locale === 'string' && ctx.locale) return ctx.locale\n const active = ctx?.website?.getActiveLocale?.()\n return typeof active === 'string' && active ? active : null\n}\n\n/**\n * Evaluate the query over what the source returned — the ONE evaluator,\n * `@uniweb/core`'s `evaluateQuery`, so the browser orders and filters exactly as\n * the build does: the query's set first (`scope` over the branch a compiled record is\n * held with, `where`, `sort`, `limit`), then the fetch's `narrow` of it. What comes back\n * carries no branch — `evaluateQuery` drops it, so no component receives one. Only the\n * sort is this lane's own, for what it does with a bad `sort:` in production.\n */\nfunction applyOperators(data, request, { dev = false, locale = null } = {}) {\n return evaluateQuery(data, request, {\n locale,\n sort: (items, sortExpr, options) => applySort(items, sortExpr, dev, options.locale),\n })\n}\n\n/**\n * ⛔ This was a second sort implementation until 2026-09-04, and it honoured\n * a comma-separated MULTI-KEY sort the language does not have (single-key by\n * ruling). A bad `sort:` is an authoring error: dev throws so it is seen; in\n * production the records are delivered in source order and the reason is\n * logged once — a wrong order is not worth a broken page for a visitor.\n */\nconst warnedBadSorts = new Set()\nfunction applySort(items, sortExpr, dev, locale) {\n try {\n return sortRecords(items, sortExpr, { locale })\n } catch (err) {\n if (dev) throw err\n const key = String(sortExpr)\n if (!warnedBadSorts.has(key)) {\n warnedBadSorts.add(key)\n console.error(`[default-fetcher] ${err.message} Records delivered unsorted.`)\n }\n return items\n }\n}\n\n/**\n * Walk a dotted path into an object. Missing segments short-circuit to\n * `undefined` so callers can distinguish \"present and empty\" from \"not there.\"\n */\nfunction getNestedValue(obj, path) {\n if (!obj || !path) return obj\n let current = obj\n for (const part of path.split('.')) {\n if (current === null || current === undefined) return undefined\n current = current[part]\n }\n return current\n}\n\n/**\n * JSON.parse that returns `undefined` on failure instead of throwing.\n * Used when we want to probe a response body for an error path but don't\n * want a non-JSON body to surface as a parser exception.\n */\nfunction safeParseJSON(text) {\n try {\n return JSON.parse(text)\n } catch {\n return undefined\n }\n}\n","/**\n * THE HOST'S TRANSPORT, REQUIRED — on every entry a host calls to have the runtime fetch on its\n * behalf: `loadPageData`, `prefetchAndHydrate` and `collectSiteRecords`.\n *\n * ⛔ The failure this prevents is not \"no transport\". `createDefaultFetcher` resolves its transport\n * as `fetchImpl || globalThis.fetch`, so a missing one becomes the ambient network — in an isolate,\n * the isolate's own, outside the host's timeout and byte budget and outside its resolution of\n * site-relative addresses. With an absolute address the request does not even fail: it leaves from\n * the wrong place and comes back `fetched`. A wiring mistake wearing a success.\n *\n * Reachable, not hypothetical: a host measured against a real isolate loader (2026-09-03) that a\n * function passed through an entrypoint's `fetch(Request)` with a JSON body arrives `undefined`,\n * and survives only as an RPC method argument.\n *\n * ⛔ Until 2026-10-25 only `prefetchAndHydrate` refused one, while the entries a host's isolate\n * calls — the data step and the corpus entry — fell back to the ambient network silently.\n *\n * The default stays where it is right: in the browser, which has one network, and in a harness\n * that builds a default fetcher itself. A caller of these entries that wants the ambient fetch\n * passes `fetch: globalThis.fetch`, which is one word and says so.\n *\n * Zero-dependency leaf, so the data step's graph stays renderer-free (`tests/data-step-graph.test.js`).\n *\n * @param {*} fetch - what the caller passed as `fetch`\n * @param {string} entry - the entry's name, for the message\n * @returns {Function} the transport\n */\nexport function requireTransport(fetch, entry) {\n if (typeof fetch === 'function') return fetch\n throw new Error(\n `${entry}: \\`fetch\\` must be a function. A transport does not survive a ` +\n 'JSON-serialized isolate boundary — pass it as an RPC method argument. ' +\n 'To use the ambient fetch deliberately, pass `fetch: globalThis.fetch`.'\n )\n}\n","/**\n * Every block a page's render can reach: its body and the layout areas it uses (the runtime's own\n * layout choice — the page's `layout:`, else the foundation's default, else the site's default set,\n * minus any area the page hides), plus what those blocks hold.\n *\n * ⭐ One walk for the two that ask for a page's data before its render: a host's data step\n * (`load-page-data.js`) and the browser before it shows a page it navigates to (`navigation.js`).\n * So the two cannot disagree about which blocks a page reaches.\n *\n * @param {Object} page - a Page\n * @returns {Object[]} its blocks, each one once\n */\nexport function blocksOf(page) {\n const out = []\n const walk = (list) => {\n for (const block of list || []) {\n if (!block) continue\n out.push(block)\n walk(block.childBlocks)\n walk(block.insets)\n }\n }\n walk(page.getBodyBlocks?.() ?? page.bodyBlocks)\n for (const area of Object.values(page.getLayoutAreas?.() ?? {})) walk(area)\n return out\n}\n","/**\n * A PAGE'S ICONS, IN ITS PRERENDER.\n *\n * An icon is a file. On a page a host renders, nothing had fetched it by the time the page was\n * written, so the page held a blank box in its place until the browser had booted and fetched it:\n * about a second, on a fast connection (measured 2026-10-30). The render is synchronous and reads an\n * icon from `uniweb.iconCache` (`getIconSync`, kit's `<Icon>`), so what a page shows has to be there\n * before it starts, as with its data. Two halves:\n *\n * - The data step asks the host's transport for the icons the page's content names, and keeps them\n * on the graph (`loadPageIcons`).\n * - The render door notes which icons it drew (`whileReadingIcons`), and the page carries exactly\n * those in `__ICON_CACHE__`, which `setup.js` reads before the browser's first render. The browser\n * renders over the prerendered page, so it has to draw what the page drew, or it would blank them.\n *\n * ⛔ NOT AN ENTRY OF THE DATA STEP'S LIST. A host counts that list to decide how long to keep the\n * page: an icon in it would shorten the life of every page holding one, and a failed one could end\n * it. So nothing here is reported. A host carries nothing new and changes nothing.\n *\n * ⚖️ What the walk cannot find: an icon a foundation's own code names, or one in a fetched record.\n * Those stay as they were, the placeholder and then the browser's fetch. So does any icon that fails\n * or arrives late, because a page without an icon is a page that still works.\n *\n * Renderer-free, as the data step's graph is (`tests/data-step-graph.test.js`), and not in the\n * browser's bundle: nothing `setup.js` or `index.jsx` imports reaches it.\n */\nimport { DEFAULT_ICON_BASE, ICON_FAMILY_MAP, iconUrl } from '@uniweb/core/icon-corpus'\n\n/** How long a page waits for its icons. Past this it renders without the ones still on their way. */\nexport const ICON_WAIT_MS = 1000\n\n/** The most a page asks for in one step, so an icon gallery cannot spend a host's budget of requests. */\nexport const MAX_ICON_REQUESTS = 40\n\n/** A reference a link carries as its `icon`: `lu-play` or `lu:play`, as kit's `<Icon>` reads one. */\nfunction parseIconReference(ref) {\n if (typeof ref !== 'string') return null\n const colon = ref.indexOf(':')\n if (colon > 0) return { library: ref.slice(0, colon), name: ref.slice(colon + 1) }\n const dash = ref.indexOf('-')\n if (dash < 2 || dash > 3) return null\n const library = ref.slice(0, dash)\n return ICON_FAMILY_MAP[library] ? { library, name: ref.slice(dash + 1) } : null\n}\n\nfunction visit(node, found) {\n if (!node || typeof node !== 'object') return\n if (node.type === 'image' && node.attrs?.role === 'icon') found.push(node.attrs)\n for (const mark of node.marks || []) {\n const ref = parseIconReference(mark?.attrs?.icon)\n if (ref) found.push(ref)\n }\n if (Array.isArray(node.content)) for (const child of node.content) visit(child, found)\n}\n\n/**\n * The icons the content of these blocks names, each once.\n *\n * Keyed as `<Icon>` asks for one, by the library and name as written, since that is what\n * `getIconSync` looks up. The address uses the family's code and the corpus's spelling of the name.\n *\n * @param {Object[]} blocks\n * @returns {Map<string, { family: string, name: string }>}\n */\nexport function pageIcons(blocks) {\n const named = []\n for (const block of blocks) visit(block.rawContent?.doc ?? block.rawContent, named)\n const icons = new Map()\n for (const { library, name } of named) {\n const family = ICON_FAMILY_MAP[String(library).toLowerCase()]\n if (family && name) icons.set(`${library}:${name}`, { family, name })\n }\n return icons\n}\n\n/**\n * Fetch the icons these blocks name through the host's transport, into the graph's icon cache. One a\n * warm graph holds is not asked for again. Resolves when they are all in, or at `ICON_WAIT_MS`.\n *\n * @param {Object} options\n * @param {Object[]} options.blocks - the page's blocks (`blocksOf`)\n * @param {Object} options.website - the Website, whose `config.icons` says where the icons are\n * @param {Function} options.fetch - the transport\n * @param {Map<string, string>} options.cache - the graph's `iconCache`\n * @returns {Promise<void>}\n */\nexport async function loadPageIcons({ blocks, website, fetch, cache }) {\n const config = website.config?.icons\n if (config?.cdn === false) return\n const base = config?.base || DEFAULT_ICON_BASE\n const wanted = [...pageIcons(blocks)].filter(([key]) => !cache.has(key)).slice(0, MAX_ICON_REQUESTS)\n if (wanted.length === 0) return\n\n const all = Promise.all(wanted.map(async ([key, { family, name }]) => {\n try {\n const response = await fetch(iconUrl(family, name, base), { method: 'GET' })\n if (!response.ok) return\n const svg = await response.text()\n if (/<svg[\\s>]/i.test(svg)) cache.set(key, svg)\n } catch {\n // The transport may refuse an address. The icon stays on the browser's path.\n }\n }))\n let timer\n const waited = new Promise((resolve) => { timer = setTimeout(resolve, ICON_WAIT_MS) })\n await Promise.race([all, waited])\n clearTimeout(timer)\n}\n\n/**\n * Run a render and collect the icons it drew, so the page can carry exactly those. On a graph reused\n * across pages that can include one this page's walk did not find, which is why this reads the\n * render rather than the walk: whatever the HTML shows, the browser's first render can show too.\n *\n * The render is synchronous, so no other request can read through this while it runs.\n *\n * @param {Object|undefined} uniweb - what `globalThis.uniweb` names for this render\n * @param {Map<string, string>} read - filled with each icon drawn\n * @param {Function} draw - the render\n * @returns {*} what `draw` returned\n */\nexport function whileReadingIcons(uniweb, read, draw) {\n const cache = uniweb?.iconCache\n if (!cache) return draw()\n const { get } = cache\n cache.get = (key) => {\n const svg = get.call(cache, key)\n if (svg) read.set(key, svg)\n return svg\n }\n try {\n return draw()\n } finally {\n delete cache.get\n }\n}\n","/**\n * THE RENDER'S DATA STEP — the page a visit starts on, fetched by the rule that renders it.\n *\n * ⭐ **Why a step at all.** The server render is synchronous: while it renders, a section reads its\n * data from the cache and never waits for a request (`EntityStore.resolve`). So whatever a page\n * shows has to be there before the render starts. That is this, and nothing else: it is not an\n * optimization, it is how a server-rendered page has content.\n *\n * ⭐ **Why it runs the render's own rule.** It asks, block by block, exactly what the render will\n * look for — `EntityStore.fetch`, the same plan `resolve` reads back (`@uniweb/core/page-data`) —\n * through a dispatcher built from the site's own foundation, so a transport the site chose answers\n * here too. ⛔ Until 2026-09-19 a host's prefetch worked this out from the payload alone: it could\n * not know which keys a component declares, which layout the page draws, or which transport a key\n * is routed to, so it asked for every fetch on every level and still got four cases wrong.\n * A payload cannot answer those; the graph the\n * isolate is about to render with can.\n *\n * ⚖️ **What it still over-asks.** The blocks are the page's — body, the layout areas it uses, their\n * child blocks and insets. Whether a component actually draws its children, or a layout its area,\n * is known only by rendering; a block that declares keys and is never drawn is asked for anyway.\n * Bounded by what such a block declares, and cheaper than rendering the page twice.\n *\n * @module\n */\nimport DataStore, { deriveCacheKey } from '@uniweb/core/datastore'\nimport { resolveRequestedPage } from './resolve-page.js'\nimport { getComponentMeta } from './prepare-props.js'\nimport { createDefaultFetcher } from './default-fetcher.js'\nimport { requireTransport } from './require-transport.js'\nimport { blocksOf } from './page-blocks.js'\nimport { loadPageIcons } from './page-icons.js'\n\n/**\n * Fetch what one route's render will read.\n *\n * @param {Object} options\n * @param {Object} options.website - the initialized Website the render will use (`initPrerender`)\n * @param {string|Object} options.route - the route, or an already-resolved Page. A route that names\n * no page, or a page served from elsewhere (`rewrite:`), asks for the site's not-found page's\n * data, the page its render draws.\n * @param {Function} options.fetch - the transport, per request. Required: a missing one is refused,\n * never replaced by the ambient `fetch` (`require-transport.js`)\n * @param {boolean} [options.dev]\n * @param {string|null} [options.locale] - ⛔ NOT a locale to fetch in: the Website is already\n * locale-sliced (`initPrerenderForLocale`) and every address is resolved in ITS locale. Passing\n * one that disagrees throws, because the alternative is a page prerendered in one language with\n * the data of another, and nothing to see it.\n * @param {'always'|'author'} [options.prerender] - `'always'` asks for everything, which is what an\n * isolate rendering a visit's first page wants; `'author'` honours `prerender: false`.\n * @param {Object} [options.cache] - a `DataStore` to answer from and fill, shared across calls.\n * ⭐ For a caller that runs the step for MANY pages of one site — a static build — where every\n * page asks for the site's and its parent's data and reading each address once is the difference\n * between N reads and N×M. ⛔ Not for a host serving requests: there a page's data is asked\n * fresh, and the default (a cache per call) is what does that. A shared cache changes nothing\n * about what is ASKED or returned — every request is still recorded, hit or miss, and whether a\n * record is held whole already is answered from this call's own requests, never the cache's.\n * @param {boolean} [options.icons] - also ask for the icons the page's content names, and keep them on\n * the graph for the render (`page-icons.js`). They are not entries of the list this returns. A\n * caller that has its own answer for icons, as a static build does, says `false`.\n * @returns {Promise<Array<{ config: Object, outcome: 'fetched'|'failed'|'skipped', data: *, meta?: Object, error?: string }>>}\n * one entry per request the render will make, keyed downstream by `deriveCacheKey(config)`.\n * ⛔ The page's data and nothing else: a host counts this list to decide how long to keep the\n * page, so one more entry shortens its life, and one that fails can end it.\n */\nexport async function loadPageData({ website, route, fetch, dev = false, locale = null, prerender = 'always', cache = null, icons = true }) {\n if (prerender !== 'author' && prerender !== 'always') {\n throw new Error(`loadPageData: prerender must be 'author' or 'always', got ${JSON.stringify(prerender)}`)\n }\n // Before the route is resolved: a lost transport is the caller's wiring, and an unknown route\n // must not hide it.\n requireTransport(fetch, 'loadPageData')\n const active = website?.getActiveLocale?.() ?? null\n if (locale && active && locale !== active) {\n throw new Error(\n `loadPageData: this Website renders ${JSON.stringify(active)} and the caller asked for ` +\n `${JSON.stringify(locale)}. A Website is locale-sliced — build one per locale ` +\n '(`initPrerenderForLocale`) rather than asking this step for another language.'\n )\n }\n // ⭐ This graph is the one `globalThis.uniweb` names while the step reads it, which it does here,\n // before its first `await`: each block's component meta and the layout the page draws come\n // through the global, and a host holding several graphs in one isolate has it naming the newest\n // (`Website#makeCurrent`).\n website.makeCurrent()\n const iconCache = globalThis.uniweb?.iconCache\n const named = typeof route === 'string' ? resolveRequestedPage(website, route) : route\n // ⭐ A route that names no page, or a page served from elsewhere (`resolveRequestedPage`), renders\n // the site's not-found page (`createPageRenderer`), so it is that page's data the render reads.\n // With no such page: a genuine 404, the caller's to turn into one, and nothing to fetch.\n const resolved = named ?? (typeof route === 'string' ? website.getNotFoundPage() : null)\n if (!resolved) return []\n // ⭐ The same promotion the render applies: a content-less folder renders its designated child,\n // and it is that child's data the page needs.\n const page = typeof resolved.getRenderableSelf === 'function' ? resolved.getRenderableSelf() : resolved\n\n // ⛔ A real Page, not page data. `createPageRenderer` tolerates plain data because rendering it\n // still produces a page; here the same tolerance would walk raw sections, find no blocks and no\n // graph behind them, and fetch NOTHING — a clean run, an empty page, no error anywhere. That is\n // the failure this whole step exists to end, so it is refused.\n if (typeof page.getBodyBlocks !== 'function') {\n throw new Error('loadPageData: `route` must be a route or a Page from this Website — page data is not enough to say what its render reads.')\n }\n\n // A cache of this call's own unless the caller shares one: the graph's belongs to whatever it is\n // rendering, and a page's data is asked fresh per request. The site's transports still apply\n // (`dispatcherFor`).\n const dispatcher = website.dispatcherFor({\n dataStore: cache ?? new DataStore(),\n defaultFetcher: createDefaultFetcher({ basePath: website.config?.base || '', dev, fetch }),\n })\n\n /** One entry per request, in the order they were first asked for. */\n const entries = new Map()\n const record = (request, entry) => {\n const key = deriveCacheKey(request)\n if (!entries.has(key)) entries.set(key, entry)\n }\n\n // ⭐ WHAT THIS CALL'S OWN REQUESTS ANSWERED — the record index the plan asks \"held whole\n // already?\" before it asks for a record (R1). ⛔ Not the cache's: a shared one holds the records\n // other pages fetched, and a record found there was never asked for, so never recorded, and the\n // page carried nothing for it (2026-10-25). With a cache of this call's own, the two hold the\n // same records.\n const answered = new DataStore()\n\n const recording = {\n // ⭐ What the plan uses without asking (`keyProgram`'s held steps) is what THIS call's own\n // requests answered, as `peekRecord` is: the page carries every answer its render reads, so\n // it may use nothing it did not ask for itself — not another page's answer in a cache the\n // static build shares.\n peek: (request) => answered.get(deriveCacheKey(request)),\n peekRecord: (id) => answered.getRecord(id),\n dispatch: async (request, ctx) => {\n if (prerender === 'author' && request?.prerender === false) {\n // The author deferred this one to the browser and the caller honours that. Present, so a\n // host can count what was declared against what was tried; not fetched, not hydrated.\n record(request, { config: request, outcome: 'skipped', data: null })\n return { data: null }\n }\n const result = await dispatcher.dispatch(request, ctx)\n if (!result?.error && result?.data != null) {\n answered.set(deriveCacheKey(request), result.meta ? { data: result.data, meta: result.meta } : { data: result.data })\n }\n record(request, result?.error\n ? { config: request, outcome: 'failed', data: null, error: result.error }\n : { config: request, outcome: 'fetched', data: result?.data ?? null, ...(result?.meta ? { meta: result.meta } : {}) })\n return result\n },\n }\n\n const store = website.entityStore\n const blocks = blocksOf(page)\n // ⭐ Every block's first request goes out in the same tick, which is what lets a question door\n // answer a page in one POST. A step that depends on an answer takes a later one, and only its\n // own key waits.\n await Promise.all([\n // The page's own record: asked whether or not a section declares the route key, because the\n // page names itself from it and a URL naming no record is a 404.\n store.fetchPageRecord(page, { dispatcher: recording }),\n ...blocks.map((block) => store.fetch(block, getComponentMeta(block.type), { dispatcher: recording })),\n icons && iconCache && loadPageIcons({ blocks, website, fetch, cache: iconCache }),\n ])\n\n return [...entries.values()]\n}\n","/**\n * The composed render entry — resolve a route, render it, inject it into a shell.\n *\n * ⭐ **This exists because framework had two callers of one unshared sequence, not\n * because a consumer asked.** `@uniweb/runtime/ssr` exported every step of the\n * per-page render and never the sequence, so each host assembled it:\n *\n * - `@uniweb/build`'s `prerender.js` — `renderPage` → classify → `injectPageContent`,\n * once per page in its loop.\n * - an SSR isolate prerendering the page a visit starts on (the SPA renders every\n * page after it) — `resolvePage` → `renderPage` → `injectPageContent`, and it wrote\n * the route lookup three times in three files before `resolvePage` was exported at\n * all (see that function's header).\n *\n * Two unlike callers is what makes the interface honest: a build bakes files and an\n * isolate answers a request, so anything only one of them needs stayed out.\n *\n * ⛔ WHAT IS DELIBERATELY NOT IN HERE, and the boundary is the point:\n *\n * - **Shell assembly.** The shell arrives built. The import map, the CDN base and\n * cache headers are host layout, and a runtime that assembled them would be\n * modelling a deployment it cannot see (the host drew this line itself,\n * 2026-09-03: a serve location is read from the payload, never constructed here).\n * - **Init and the data step.** The two lanes differ REALLY here, not incidentally: a\n * build initializes once per locale and runs the data step for each page over one\n * cache for the whole run; an isolate initializes per locale and runs the step fresh\n * for each request. Folding either in would fit one caller and lie to the other.\n * `initPrerender` / `initPrerenderForLocale` / `loadPageData` / `hydrateDataStore`\n * stay separate exports, and `prefetchAndHydrate` below is the isolate's two-step,\n * not a general one. ⭐ What both lanes DO share is the last step: the render reads\n * exactly the entries its page carries. That is `render`'s `fetchedData`, since\n * 2026-10-25, and both lanes pass it.\n * - **Anything build-only.** `injectBuildData` stays in the build lane; it is the\n * other half of the head seam and has its own parity guard.\n */\nimport { renderPage, classifyRenderError, injectPageContent } from './ssr-renderer.js'\nimport { resolveRequestedPage } from './resolve-page.js'\nimport { hydrateDataStore } from './wire-foundation.js'\nimport { loadPageData } from './load-page-data.js'\nimport { requireTransport } from './require-transport.js'\nimport { restoreStartPage } from './start-page.js'\nimport { whileReadingIcons } from './page-icons.js'\n\n/**\n * A renderer bound to one initialized Website and one shell.\n *\n * Create it once per locale (the Website is already locale-sliced by\n * `initPrerenderForLocale`) and call `render` per route or per page.\n *\n * @param {Object} opts\n * @param {Object} opts.website - `uniweb.activeWebsite`, already initialized\n * @param {string} opts.shell - the HTML shell to inject into, taken as given\n * @returns {{ website: Object, render: Function }}\n */\nexport function createPageRenderer({ website, shell }) {\n if (!website) throw new Error('createPageRenderer: `website` is required')\n if (typeof shell !== 'string') throw new Error('createPageRenderer: `shell` must be an HTML string')\n\n /**\n * Render one page.\n *\n * ⭐ Returns an OUTCOME rather than throwing or returning a bare string, because\n * the two callers branch differently on the same three cases and neither wants an\n * exception: a build logs and keeps going so one broken section cannot fail a whole\n * site, an isolate decides a status code and a cache policy. Same reasoning as the\n * per-entry outcome `loadPageData` returns.\n *\n * @param {string|Object} target - a route (`/blog/1`, resolved through the same\n * matcher the browser uses, so a dynamic route works) or an already-resolved\n * Page, which the build lane already holds from its own loop. A route that names\n * no page renders the site's not-found page (`website.getNotFoundPage()`), as the\n * browser does for it, and so does a route naming a page served from elsewhere\n * (`rewrite:`): a request for it that reaches a renderer was not proxied, and the\n * browser's first load of it draws the not-found page too.\n * @param {Object} [options]\n * @param {Object} [options.inject] - extra options forwarded to `injectPageContent`\n * @param {Array<{config: Object, data: *, meta?: Object}>} [options.fetchedData] - the entries\n * the page carries: the `content.fetchedData` its HTML embeds, which the browser boots with.\n * ⭐ Given, the render reads exactly these. The Website forgets what it held, answers and the\n * parametric pages made from them (`Website#forgetData`), and files these, all in this\n * synchronous call, so a Website reused across requests renders each against its own and\n * no other request runs in between. Absent, the render reads whatever the store holds.\n * @returns {{ outcome: 'rendered'|'notFound'|'failed', html: string|null,\n * page: Object|null, error: {type: string, message: string}|null }}\n * `notFound` means the URL names nothing, and `html` says whether there is a page to\n * send for it. Send `html` with your not-found status when it is set, and the shell\n * when it is not:\n * - `page` set, `html` set — the site's not-found page, for a route that names no page\n * or a page served from elsewhere; or a page whose record does not exist, rendered in\n * its own not-found state.\n * - `page: null`, `html: null` — no route matched, or it names a page served from\n * elsewhere, and the site has no not-found page. The browser shows its default\n * not-found message.\n * - `page` set, `html: null`, `error` set — that page broke while rendering; `error`\n * says what broke.\n */\n function render(target, { inject = {}, fetchedData } = {}) {\n if (fetchedData !== undefined && !Array.isArray(fetchedData)) {\n throw new Error('createPageRenderer: `fetchedData` must be the array of entries the page carries')\n }\n // ⭐ FIRST, THIS GRAPH IS THE ONE `globalThis.uniweb` NAMES. A block's component, the props\n // pipeline and kit in the foundation reach the site through the global, and every graph built\n // since this one was points it elsewhere — a host holds several in one isolate. The render is\n // synchronous, so the value set here is the one it reads throughout (`Website#makeCurrent`).\n website.makeCurrent()\n // ⭐ BEFORE THE ROUTE IS RESOLVED: a parametric page is made from the store when it is first\n // resolved, so resolving first would make it from what an earlier request left there.\n // ⛔ Until 2026-10-25 a host that reused one Website hydrated each request's answers on top of\n // every earlier request's, and a section that reads the store itself (kit's `useFetched`)\n // could render answers its page did not carry, so the browser's first render differed.\n if (fetchedData !== undefined) {\n website.forgetData()\n hydrateDataStore(website, fetchedData)\n }\n const named = typeof target === 'string' ? resolveRequestedPage(website, target) : target\n // ⭐ A ROUTE THAT NAMES NO PAGE RENDERS THE SITE'S NOT-FOUND PAGE, as the browser does for\n // it (`PageRenderer.jsx`), and the data step asks for that page's data (`loadPageData`). No\n // route names it (`/404` is never routable), so this is the only way a route reaches it.\n // A route naming a page served from elsewhere is answered the same way: the host did not\n // proxy it (`resolveRequestedPage`).\n // ⛔ Until 2026-10-26 this answered `notFound` with nothing to send, and a host that wanted\n // the site's page had to fetch its data and render it as a Page itself.\n const resolved = named ?? (typeof target === 'string' ? website.getNotFoundPage() : null)\n\n // ⛔ Not an error: nothing matched and the site has no not-found page — a genuine 404,\n // and the caller's to turn into one: a build skips it, an isolate sends the shell with a\n // 404 status. Returning `failed` here would make those indistinguishable.\n if (!resolved) return { outcome: 'notFound', html: null, page: null, error: null }\n\n // ⭐ **THE INDEX-CHILD PROMOTION APPLIES TO BOTH TARGET FORMS** — ruled\n // 2026-09-12 [Diego]. A content-less folder with a designated `isIndex` child\n // renders that child. `Website#getPage` already did this for a ROUTE on its\n // exact-match branches, and nothing did it for an already-resolved Page, so the\n // same page rendered differently depending on which form the caller happened to\n // hold — silently, with no error on either path. `getRenderableSelf` is the one\n // rule for it and now has exactly one caller: here.\n // ⚠️ Guarded on the method rather than assumed: the documented target is a Page,\n // but a caller handing in plain page data should degrade to \"render what I gave\n // you\", not throw.\n const page = typeof resolved.getRenderableSelf === 'function' ? resolved.getRenderableSelf() : resolved\n\n // ⭐ A PAGE CAN RESOLVE AND STILL NAME NOTHING: a parametric page whose record does\n // not exist. Core marks it `notFound` only when the data answered definitively\n // (`Website#_createDynamicPage`), never on a failed or missing fetch. It renders the\n // page's own not-found state — a record miss is not a route miss — and the outcome\n // says `notFound` all the same, so a caller keying its status on the outcome sends a\n // not-found response without reading anything new. The site's not-found page, rendered\n // for a route that names no page, is `notFound` the same way. Known before rendering, so\n // it holds even when that render breaks. ⛔ Until 2026-09-30 this was `rendered`, and a\n // host that answered by the outcome sent 200 for a URL naming no record.\n const missing = !named || page?.notFound === true\n\n // ⭐ EVERY RENDER STARTS FROM THE ACTIVE PAGE THE WEBSITE STARTED WITH (`start-page.js`),\n // then makes active the page the route names, as the browser's boot does (`index.jsx`).\n // `renderPage` then makes the page it renders active by its route, and the not-found page\n // has none: it renders with the page a route served from elsewhere names, as in the browser,\n // or with the start page. ⛔ Until 2026-10-26 it rendered with whatever page this Website\n // rendered last, so on a Website reused across requests its header could show the previous\n // request's page.\n restoreStartPage(website)\n if (typeof target === 'string') website.setActivePage(target)\n // ⭐ The page carries the icons it drew, so the browser's first render draws them too.\n const icons = new Map()\n let result\n try {\n result = whileReadingIcons(globalThis.uniweb, icons, () => renderPage(page, website))\n } catch (err) {\n // `renderPage` handles its own errors, but a foundation can throw from\n // module scope in ways it does not catch. Classify rather than propagate,\n // so one page cannot take down a build loop or an isolate's request.\n return { outcome: missing ? 'notFound' : 'failed', html: null, page, error: classifyRenderError(err) }\n }\n\n if (result.error) return { outcome: missing ? 'notFound' : 'failed', html: null, page, error: result.error }\n\n // ⛔ `sectionOverrideCSS` LAST, so a caller's `inject` cannot displace it. It is\n // computed by `renderPage` for this page — theme pinning and component vars —\n // and a caller passing a same-named key would silently drop it, rendering a page\n // that looks fine and is unstyled in exactly the places the author pinned. That\n // is the empty-success shape this module keeps refusing elsewhere; the spread was\n // the other way round for one commit.\n const html = injectPageContent(shell, result.renderedContent, page, {\n ...inject,\n sectionOverrideCSS: result.sectionOverrideCSS,\n icons,\n })\n return { outcome: missing ? 'notFound' : 'rendered', html, page, error: null }\n }\n\n return { website, render }\n}\n\n/**\n * Prefetch a route's data and hydrate it onto the graph — the isolate's two-step.\n *\n * ⭐ Its whole purpose is that a host stops assembling our structure by hand. It\n * returns the prefetch outcomes rather than swallowing them, because a host reads\n * them to tell \"nothing was tried\" from \"everything tried failed\", which is a\n * different cache decision.\n *\n * ⭐ **Since 2026-09-19 it asks what the RENDER will read, block by block** (`loadPageData`),\n * on the Website it is handed: the keys each component declares, the layout the page actually\n * draws, and the transport the site chose for a key. `content` is no longer read — the graph\n * carries everything — and stays in the signature for callers that pass it.\n * ⛔ `prefetchPageData`, which it replaced, worked from the payload alone and could know none of\n * those: it asked for every fetch on every level and still got four cases wrong. It was removed\n * on 2026-09-20.\n *\n * ⛔ The build lane does NOT call this: it runs `loadPageData` itself for each page, with one\n * cache for the whole run (which this entry does not take) and `prerender: 'author'`, and keeps\n * the entries to embed in that page's HTML. That difference is why this is a named isolate\n * helper and not a step inside `render`.\n *\n * @returns {Promise<Array<{config: Object, outcome: string, data: any, error?: string}>>}\n */\nexport async function prefetchAndHydrate({ website, content, route, locale = null, fetch = null, dev = false, prerender = 'always' }) {\n // ⛔ Guarded for the same reason `createPageRenderer` is, and it was not for one\n // commit. `hydrateDataStore` no-ops on a graph with no `dataStore`, so a caller\n // passing the wrong object gets a successful-looking prefetch, an unhydrated graph\n // and a page that renders empty — no error anywhere. Fail where the mistake is.\n if (!website?.dataStore) {\n throw new Error('prefetchAndHydrate: `website` must be an initialized Website with a dataStore')\n }\n\n // ⛔ THE TRANSPORT IS REQUIRED, as on every entry a host calls to have the runtime fetch for\n // it: one that did not cross the isolate boundary is refused here, never replaced by the\n // isolate's own network (`require-transport.js`, which records the measurement). Checked here\n // as well as in `loadPageData` so the message names the entry the caller called.\n requireTransport(fetch, 'prefetchAndHydrate')\n // ⛔ `locale` is forwarded to be CHECKED, not applied: the Website is locale-sliced already, so a\n // caller whose two disagree gets an error rather than a page prerendered in one language with the\n // data of another.\n const fetched = await loadPageData({ website, route, fetch, dev, locale, prerender })\n hydrateDataStore(website, fetched)\n return fetched\n}\n","/**\n * Every record a site's declared queries return — for a host building something\n * over the whole corpus rather than rendering one page.\n *\n * ## Why this is framework's and not the caller's\n *\n * A host that indexes a site has to ask the same questions the site's pages ask.\n * ⛔ **If it composes its own, the index and the page can disagree**, and the way\n * they disagree is the worst one available: the index offers a result whose page\n * then renders empty. That is the two-answers-to-one-question failure the GET\n * lane was retired for, reintroduced one lane over.\n *\n * ⇒ So the composition is not duplicated here either. This walks the site's\n * `config.queries`, hands each one to `resolveFetchConfigs` — **the same rule a\n * page render uses**, applying each saved query's own `scope` and `where` — and\n * asks through the same client.\n *\n * ## ⭐ Each query is asked AS SAVED — its `limit` included\n *\n * A query defines its SET: the records its `scope` and `where` select, in its\n * `sort`, up to its `limit` (ruled 2026-09-14 [Diego]). A parametric page has a URL\n * for each record of its route query's set and no other, so the set is exactly the\n * population a site's pages reach — a field note older than a query's 100 most recent\n * has no page to index. Asked with no `narrow`, the service answers the set whole in\n * one answer, so a walk has nothing to page; it still follows a cursor if one ever\n * comes back, bounded by `maxPages`.\n *\n * ⛔ **Until 2026-09-14 this dropped the query's `limit`**, on the rule it replaced — a\n * `limit` was the list page's presentation and every record matching `scope` + `where`\n * had a page — which was right under that rule. ⚠️ And that rule's own history: the\n * first version, in 0.17.0, passed `limit` through while claiming the corpus was the\n * population detail pages reach, and the consumer found it (2026-09-06).\n *\n * ## What the caller supplies\n *\n * ⭐ **The transport, and nothing else.** A host outside a browser decides how a\n * site-relative address is dispatched — through its own origin, a service\n * binding, a forwarding table that turns `/_…` into an upstream — and hands that\n * in as `fetch`. **Framework decides what to ask and how to read the answer;\n * the host decides how the bytes travel.** That split is the whole of the\n * boundary here.\n *\n * ⛔ **A site with no records service is not an error.** It has no live records,\n * so this returns empty rather than throwing: a static site's corpus comes from\n * its own compiled artifacts, which the caller already has.\n */\n\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { resolveRecordsService } from '@uniweb/core/records-service'\nimport { createDefaultFetcher } from './default-fetcher.js'\nimport { requireTransport } from './require-transport.js'\n\n/**\n * Ask a site's records service for every record its declared queries return.\n *\n * @param {Object} content - the render payload (`config.services`, `config.queries`)\n * @param {Object} options\n * @param {string} options.locale - the locale to ask in; it is a route segment\n * of the service's address, so there is no asking without one\n * @param {Function} options.fetch - the transport, `(url, init) => Response`. Required: a missing\n * one is refused, never replaced by the ambient `fetch` (`require-transport.js`)\n * @param {AbortSignal} [options.signal]\n * @param {string[]} [options.only] - restrict to these query names\n * @param {boolean} [options.whole=false] - what to ask for. `false` is the brief a\n * list shows; **`true` — whole records — is what an index wants**: a brief index\n * cannot match body text the record's own detail page displays, and a reader who\n * finds a word on the page and not in search meets the inconsistency two\n * rankings would produce. The cost is the caller's, and bounded by each query's\n * own `limit`. An external query is never collected — it has no records service\n * to ask.\n * @param {number} [options.maxPages] - the caller's own bound on a walk that pages. A\n * question with no `narrow` is answered whole with no cursor, so today's walk is one\n * request per query; the bound holds should a service page anyway.\n * @returns {Promise<{records: Object, errors: Object|null, meta: Object}>}\n * `records` is keyed by query NAME, each a flat array; `errors` is keyed the\n * same and is null when nothing failed; `meta[name]`, when there is anything to\n * say, carries `{ whole?, pages?, partial?, unavailable? }`.\n *\n * ⭐ **`partial` means NOT THE WHOLE POPULATION**, and a key can be in BOTH\n * `records` and `errors`: a walk that failed or was aborted with pages already\n * in hand keeps them, marked. ⛔ Losing them would be indistinguishable from\n * \"this query has no records\", and on an abort every in-flight key fails at\n * once — so discarding would lose the corpus, not a key.\n */\nexport async function collectSiteRecords(\n content,\n { locale, fetch, signal, only = null, whole = false, maxPages } = {},\n) {\n // ⛔ Before the empties below: those are states of a site, while a lost transport is the caller's\n // wiring, and a site with no live records must not hide it.\n requireTransport(fetch, 'collectSiteRecords')\n\n const config = content?.config\n const services = config?.services ?? null\n const queries = config?.queries ?? null\n\n // No service, no queries, or no locale to ask in ⇒ nothing to collect. Each is\n // an ordinary state of a site, not a fault: the caller reads its own artifacts.\n if (!queries || typeof queries !== 'object') return empty()\n if (!resolveRecordsService(services, locale)) return empty()\n\n const names = Object.keys(queries).filter((n) => (only ? only.includes(n) : true))\n if (names.length === 0) return empty()\n\n // One synthetic source per query — `as` is the query's own name, so the answer\n // comes back under the name the caller asked by.\n const configs = resolveFetchConfigs(\n names.map((name) => ({ query: name, as: name })),\n { services, queries, locale, defaultLocale: config?.defaultLanguage ?? locale },\n )\n\n const fetcher = createDefaultFetcher({ fetch, basePath: config?.base ?? '' })\n const records = {}\n const errors = {}\n const meta = {}\n\n await Promise.all([...configs].map(async ([name, cfg]) => {\n // ⛔ Only a config the service answers. A query that resolved to a compiled\n // `path` has no live lane, and reading that file is the caller's business,\n // not ours — it is in the site's own URL space and they already serve it.\n if (!cfg.ask) return\n // The query as saved — the set its pages reach, its `limit` included; see the header.\n const asked = { ...cfg, whole, exhaustive: true }\n if (typeof maxPages === 'number' && maxPages > 0) asked.maxPages = maxPages\n\n const result = await fetcher.resolve(asked, { signal })\n if (result?.error) errors[name] = result.error\n // ⭐ Data and an error are not exclusive: a partial walk reports both, and\n // the caller decides whether partial is usable. Only a walk that collected\n // nothing leaves the key out of `records` entirely.\n if (Array.isArray(result?.data)) records[name] = result.data\n else if (!result?.error) records[name] = []\n if (result?.meta) meta[name] = result.meta\n }))\n\n return { records, errors: Object.keys(errors).length ? errors : null, meta }\n}\n\nfunction empty() {\n return { records: {}, errors: null, meta: {} }\n}\n"],"names":["text","fetch","deriveCacheKey"],"mappings":"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"}
|