@uniweb/runtime 0.19.3 → 0.19.5
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/wire-foundation.js +2 -1
package/dist/ssr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/background-shared.js","../src/default-404.js","../src/wire-foundation.js","../src/area-wrappers.js","../src/appearance.js","../src/ssr-renderer.js","../src/default-fetcher.js","../src/prefetch.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 * - Field defaults applied to `content.data` items from the bound schemas\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\nimport { isRichSchema } from '@uniweb/core'\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 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 }\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 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 ...(content.math && content.math.length ? { math: content.math } : {}),\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 a schema to a single object\n * Only processes fields defined in the schema, preserves unknown fields\n *\n * @param {Object} obj - The object to process\n * @param {Object} schema - Schema definition (fieldName -> fieldDef)\n * @returns {Object} Object with schema defaults applied\n */\nfunction applySchemaToObject(obj, schema) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {\n return obj\n }\n\n const result = { ...obj }\n\n for (const [field, fieldDef] of Object.entries(schema)) {\n // Get the default value - handle both shorthand and full form\n const defaultValue = typeof fieldDef === 'object' ? fieldDef.default : undefined\n\n // Apply default if field is missing and default exists\n if (result[field] === undefined && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n\n // Bare type strings ('string', 'decimal', …) carry nothing more to apply.\n if (typeof fieldDef !== 'object') continue\n\n // Inline picklist (`enum`): if the value is set but not among the allowed\n // values, fall back to the default.\n if (Array.isArray(fieldDef.enum)) {\n if (result[field] !== undefined && !fieldDef.enum.includes(result[field]) && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n }\n\n // Nested object → recurse into its field map.\n if (fieldDef.type === 'object' && fieldDef.fields && result[field]) {\n result[field] = applySchemaToObject(result[field], fieldDef.fields)\n }\n\n // Array of objects → apply the element field map to each item.\n if (fieldDef.type === 'array' && fieldDef.items && Array.isArray(result[field])) {\n const items = fieldDef.items\n if (items && typeof items === 'object' && items.type === 'object' && items.fields) {\n result[field] = result[field].map((item) => applySchemaToObject(item, items.fields))\n }\n }\n }\n\n return result\n}\n\n/**\n * Apply a schema to a value (object or array of objects)\n *\n * @param {Object|Array} value - The value to process\n * @param {Object} schema - Schema definition\n * @returns {Object|Array} Value with schema defaults applied\n */\nfunction applySchemaToValue(value, schema) {\n if (Array.isArray(value)) {\n return value.map(item => applySchemaToObject(item, schema))\n }\n return applySchemaToObject(value, schema)\n}\n\n/**\n * Apply field defaults from a rich form `fields` array to an object.\n *\n * Recurses into `type: 'form'` (composite arrays with childSchema) and\n * `type: 'nestedObject'` / `type: 'object'` (single nested objects).\n *\n * Conditional visibility (`field.condition`) is not yet applied here —\n * components receive all fields the author filled plus defaults; hiding\n * is a later pass that requires the shared evaluateCondition util.\n *\n * @param {Object} obj - Row data (object keyed by field id)\n * @param {Array} fields - Rich field definitions\n * @returns {Object} - obj with defaults filled in\n */\nfunction applyRichFieldDefaults(obj, fields) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj\n if (!Array.isArray(fields)) return obj\n\n const result = { ...obj }\n\n for (const field of fields) {\n if (!field || typeof field !== 'object' || !field.id) continue\n const id = field.id\n\n if (result[id] === undefined && field.default !== undefined) {\n result[id] = field.default\n }\n\n if (field.type === 'form' && field.childSchema && Array.isArray(result[id])) {\n result[id] = result[id].map(item =>\n applyRichFieldDefaults(item, field.childSchema.fields)\n )\n } else if (\n (field.type === 'nestedObject' || field.type === 'object') &&\n Array.isArray(field.fields) &&\n result[id] &&\n typeof result[id] === 'object'\n ) {\n result[id] = applyRichFieldDefaults(result[id], field.fields)\n }\n }\n\n return result\n}\n\n/**\n * Apply a rich form schema to its stored value.\n *\n * Shape rules:\n * - composite (isComposite=true) → value is array of childSchema rows\n * - when `childRecords` is set, value may be `{ [childRecords]: [...] }`\n * - non-composite → value is a single object keyed by field id\n */\nfunction applyRichSchemaToValue(value, schema) {\n if (value == null) return value\n\n if (schema.isComposite && schema.childSchema) {\n const childFields = schema.childSchema.fields\n const queryKey = schema.childRecords\n\n if (queryKey && value && typeof value === 'object' && !Array.isArray(value)) {\n const arr = Array.isArray(value[queryKey]) ? value[queryKey] : []\n return {\n ...value,\n [queryKey]: arr.map(row => applyRichFieldDefaults(row, childFields)),\n }\n }\n\n if (Array.isArray(value)) {\n return value.map(row => applyRichFieldDefaults(row, childFields))\n }\n\n return value\n }\n\n if (Array.isArray(schema.fields)) {\n return applyRichFieldDefaults(value, schema.fields)\n }\n\n return value\n}\n\n/**\n * Apply schemas to content.data\n * Only processes tags that have a matching schema, leaves others untouched\n *\n * ## Two orders of schema — what a `data:` declaration may describe\n *\n * A component's `data:` is 1st order: a DEVELOPER says what shape the section\n * consumes. An authored form (```yaml:form```) is 2nd order: an AUTHOR says what\n * shape a VISITOR will submit. It is schema-shaped, but it is content.\n *\n * Declaring a schema for such a tag is legitimate, and it is worth being precise\n * about what it may describe:\n *\n * OK the DEFINITION's envelope — `title?`, `description?`, `fields: <map>`.\n * That asks \"is this a well-formed form?\", which is what build-time\n * validation is for (`build/src/validate-data.js` pairs a section's data\n * input with the schema its meta.js binds to that key).\n * WRONG a schema whose fields are THE FORM'S fields (`name`, `email`, …).\n * Those are author-chosen and unknowable at build time. A form-rendering\n * component receives its fields; it does not declare them.\n *\n * The mechanism is bounded and does not punish the mistake loudly:\n * `applySchemaToObject` recurses only where the schema declares structure\n * (`type: object` + `fields`, `type: array` + `items.fields`), so a\n * form-definition schema — which cannot name the author's fields — can never\n * reach into them. It fills the envelope defaults its own author declared.\n *\n * (Established with the editor team, 2026-07-31, channel frontend↔framework.\n * The editor shadows a foundation's `form` declaration with its own builder via\n * `builtinSchemas()`; that is about the EDITING UI and is orthogonal to whether a\n * foundation declares a schema for validation.)\n *\n * @param {Object} data - The data object from content\n * @param {Object} schemas - Schema definitions from runtime meta\n * @returns {Object} Data with schemas applied\n */\nexport function applySchemas(data, schemas) {\n if (!schemas || !data || typeof data !== 'object') {\n return data || {}\n }\n\n const result = { ...data }\n\n for (const [tag, rawValue] of Object.entries(data)) {\n const schema = schemas[tag]\n if (!schema) continue // No schema for this tag - leave as-is\n\n result[tag] = isRichSchema(schema)\n ? applyRichSchemaToValue(rawValue, schema)\n : applySchemaToValue(rawValue, schema)\n }\n\n return result\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 * Merge entity data onto a block's parsedContent.data.\n *\n * Section-level data already on the block (from prerender fetches via\n * blockData.parsedContent.data in the Block constructor) takes priority;\n * entity data only fills missing keys. Mutates `block.parsedContent.data`\n * in place so the vanilla JS layer holds the assembled data and\n * subsequent reads see the same shape.\n */\nfunction mergeEntityData(block, entityData) {\n if (!entityData) return\n const current = block.parsedContent.data || {}\n let changed = false\n const merged = { ...current }\n for (const key of Object.keys(entityData)) {\n if (merged[key] === undefined) {\n merged[key] = entityData[key]\n changed = true\n }\n }\n if (changed) {\n block.parsedContent.data = merged\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), or\n * when the handler throws. Errors are logged via `console.error`.\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 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/**\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, content guarantees, and schema application — the handler\n * sees the exact shape the 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. Merge entity data (resolved by EntityStore) onto\n * `block.parsedContent.data`.\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. Apply schemas to content.data.\n * 7. 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–7 are\n * pure derivations of the block's now-assembled state.\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 mergeEntityData(block, entityData)\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 let content = guaranteeContentStructure(block.parsedContent)\n\n // Apply schemas to content.data\n const schemas = meta?.schemas || null\n if (schemas && content.data) {\n content.data = applySchemas(content.data, schemas)\n }\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 are an L3 pair by design: `components/Background.jsx` renders\n * with hooks and DOM, `ssr-renderer.js` renders with `React.createElement`\n * only, and the runtime-layer rule (framework `CLAUDE.md` gotcha #2) says that\n * pair is twinned on purpose. ⛔ **What it also says is that only the RENDERING\n * is twinned — \"pure data → L1, one home\" — and two pure helpers had been\n * dragged across the seam with it**: a base-path joiner and a colour-opacity\n * converter, byte-identical in both files.\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 const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 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 * 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 * Keeping this file React-free matters for the SPA bundle: `setup.js`\n * pulls `wire-foundation.js` directly, but it 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. Read the foundation declaration via `foundation.default.capabilities.<name>`.\n * 2. Apply it to the uniweb singleton (set a slot, call a build hook,\n * register something on `activeWebsite`).\n * 3. 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`) wraps the foundation's\n * source default export under `default.capabilities.*`, so the runtime\n * sees a single canonical shape 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 cache key MUST go through `deriveCacheKey(entry.config)` and the\n * value MUST be wrapped as `{ data }` — otherwise the dispatcher's\n * lookup at `_dataStore.get(deriveCacheKey(request))` misses every\n * time and `cached.data` reads `undefined`. Three call sites used to\n * inline this loop independently (browser SPA, Node SSG, and a server-side\n * renderer); one of the three was using the wrong shape, silently killing\n * prefetched-data reuse wherever it ran. This helper is the one canonical\n * implementation.\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 `prefetchPageData` list carries every declared config with an `outcome`; only what was\n // actually 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 website.dataStore.set(deriveCacheKey(entry.config), entry.meta ? { data: entry.data, meta: entry.meta } : { data: entry.data })\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 can override a host's `consent` or `endpoint`; 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 * 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 region a `view-transition-name` so the browser animates them\n * independently — persistent chrome (header, sidebar, footer) morphs in place\n * while the body crossfades. Without per-region names the browser falls back to\n * a single full-page crossfade, which makes the whole layout (chrome included)\n * flicker on every navigation.\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\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 plus the implicit `body` gets a stable,\n * namespaced name (`uw-<area>`, `uw-body`). Same-named areas across layouts\n * therefore share a name and morph between layouts automatically.\n *\n * The layout's `meta.js` `transitions` value overrides this:\n * - an object overrides per region (`{ left: 'sidebar' }` to group across\n * layouts, or `{ left: null }` to opt one region out);\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 = { body: toIdent('body') }\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. 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 * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.layers`.\n * @returns {Object} region → z-index. Empty when the layout opts out.\n */\nexport function resolveLayoutLayers(areaNames, explicit) {\n if (explicit === false) return {}\n\n const defaults = {}\n for (const name of areaNames) defaults[name] = 1\n\n return explicit ? { ...defaults, ...explicit } : defaults\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 * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @returns {Object|null} Inline style object, or null for no wrapper.\n */\nexport function areaWrapperStyle(region, transitions, layers) {\n const style = {}\n\n const name = transitions?.[region]\n if (name) style.viewTransitionName = name\n\n const layer = layers?.[region]\n 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 * 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 * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and server-side SSR.\n * Mirrors BlockRenderer.jsx + Background.jsx using React.createElement\n * directly — no hooks, no JSX, no browser APIs.\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx or Background.jsx, update this file 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 { siteUrl, withOpacity } from './background-shared.js'\nimport { routePatternToRegex } 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, areaWrapperStyle } from './area-wrappers.js'\nimport { renderAppearanceBootScript } from './appearance.js'\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 const { background = {} } = block.standardOptions\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 // Apply context overrides as inline CSS custom properties\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 background element for SSR.\n * Mirrors Background.jsx (color, gradient, image — not video).\n * Video backgrounds require JS for autoplay and are skipped during SSR.\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: '0',\n overflow: 'hidden',\n zIndex: 0,\n }\n\n const children = []\n\n // Color background\n if (background.mode === 'color' && background.color) {\n children.push(\n React.createElement('div', {\n key: 'bg-color',\n className: 'background-color',\n style: { position: 'absolute', inset: '0', backgroundColor: background.color },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Gradient background (supports string or object with opacity)\n if (background.mode === 'gradient' && background.gradient) {\n const g = background.gradient\n\n let bgValue\n if (typeof g === 'string') {\n bgValue = g\n } else {\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = g\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n bgValue = `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-gradient',\n className: 'background-gradient',\n style: { position: 'absolute', inset: '0', background: bgValue },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Image background\n if (background.mode === 'image' && background.image?.src) {\n const img = background.image\n children.push(\n React.createElement('div', {\n key: 'bg-image',\n className: 'background-image',\n style: {\n position: 'absolute',\n inset: '0',\n backgroundImage: `url(${siteUrl(img.src)})`,\n backgroundPosition: img.position || 'center',\n backgroundSize: img.size || 'cover',\n backgroundRepeat: 'no-repeat',\n },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Overlay (gradient or solid)\n if (background.overlay?.enabled) {\n const ov = background.overlay\n let overlayStyle\n\n if (ov.gradient) {\n const g = ov.gradient\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n background: `linear-gradient(${g.angle || 180}deg, ${g.start || 'rgba(0,0,0,0.7)'} ${g.startPosition || 0}%, ${g.end || 'rgba(0,0,0,0)'} ${g.endPosition || 100}%)`,\n opacity: ov.opacity ?? 0.5,\n }\n } else {\n const baseColor = ov.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n backgroundColor: `rgba(${baseColor}, ${ov.opacity ?? 0.5})`,\n }\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-overlay',\n className: ov.gradient ? 'background-overlay background-overlay--gradient' : 'background-overlay background-overlay--solid',\n style: overlayStyle,\n 'aria-hidden': 'true',\n })\n )\n }\n\n if (children.length === 0) return null\n\n return React.createElement('div', {\n className: `background background--${background.mode}`,\n style: containerStyle,\n 'aria-hidden': 'true',\n }, ...children)\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 * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { as = 'section' } = {}) {\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 inherited entity data synchronously (SSG has no async).\n // EntityStore walks page/site hierarchy to find data matching meta.inheritData.\n const meta = getComponentMeta(block.type)\n const entityStore = block.website?.entityStore\n let entityData = null\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready') entityData = resolved.data\n }\n\n // Build content and params with runtime guarantees.\n // prepareProps handles the full pipeline: entity data merge,\n // foundation content handler invocation, guaranteed content\n // structure, schema application, and param defaults.\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 // 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) {\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)\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: wrap body + each area in a thin div carrying its\n // view-transition-name, so the prerendered HTML matches what the SPA hydrates\n // 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 const layers = resolveLayoutLayers(areaNames, layoutMeta?.layers)\n const wrapArea = (name, element) => {\n const style = areaWrapperStyle(name, transitions, layers)\n return style ? React.createElement('div', { style }, element) : element\n }\n\n const bodyElement = bodyBlocks ? wrapArea('body', renderBlocks(bodyBlocks)) : 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 // Default layout — mirror DefaultLayout in Layout.jsx, including its lack of\n // stacking: the area wrappers already carry their layers, and a positioned\n // element here would seal those layers inside it.\n return React.createElement(React.Fragment, null,\n areaElements.header && React.createElement('header', null, areaElements.header),\n bodyElement && React.createElement('main', null, bodyElement),\n areaElements.footer && React.createElement('footer', null, areaElements.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\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 })\n )\n )\n }\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 always reflects the page being rendered.\n const website = uniweb.activeWebsite\n uniweb.routingComponents = {\n useLocation: () => {\n const route = website?.activePage?.route || ''\n return { pathname: '/' + route, 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 */\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?.cdnUrl || 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 const svg = await response.text()\n uniweb.iconCache.set(`${family}:${name}`, svg)\n })\n )\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failed = results.filter(r => r.status === 'rejected').length\n if (failed > 0) {\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failed} failed)`\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}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\n/**\n * Resolve a route to the Page that should render it.\n *\n * Exists because this module 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 * 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\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 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 const sectionOverrideCSS = buildSectionOverrides(page.getPageBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str) {\n if (!str) return ''\n return String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\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 * - Add/update meta description\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 hydrates 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 // 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 page title (use getTitle() so isIndex pages inherit parent title)\n const pageTitle = page.getTitle?.() || page.title\n if (pageTitle) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(pageTitle)}</title>`\n )\n }\n\n // Add/update meta description\n if (page.description) {\n const metaDesc = `<meta name=\"description\" content=\"${escapeHtml(page.description)}\">`\n if (result.includes('<meta name=\"description\"')) {\n result = result.replace(/<meta name=\"description\"[^>]*>/, metaDesc)\n } else {\n result = result.replace('</head>', `${metaDesc}\\n</head>`)\n }\n }\n\n // Social / SEO meta from the page's effective head metadata (page seo\n // cascading over site-level seo — see Page.getHeadMeta). The SPA emits these\n // client-side via useHeadMeta; this is the SSR twin, so crawlers and social\n // unfurlers (which don't run JS) get them in the static HTML too.\n const headMeta = page.getHeadMeta?.()\n if (headMeta) {\n const og = headMeta.og || {}\n const keywords = Array.isArray(headMeta.keywords)\n ? headMeta.keywords.join(', ')\n : headMeta.keywords\n const tags = []\n if (keywords) tags.push(`<meta name=\"keywords\" content=\"${escapeHtml(keywords)}\">`)\n if (headMeta.robots) tags.push(`<meta name=\"robots\" content=\"${escapeHtml(headMeta.robots)}\">`)\n if (og.title) tags.push(`<meta property=\"og:title\" content=\"${escapeHtml(og.title)}\">`)\n if (og.description) tags.push(`<meta property=\"og:description\" content=\"${escapeHtml(og.description)}\">`)\n if (og.image) tags.push(`<meta property=\"og:image\" content=\"${escapeHtml(og.image)}\">`)\n if (og.url) tags.push(`<meta property=\"og:url\" content=\"${escapeHtml(og.url)}\">`)\n tags.push('<meta property=\"og:type\" content=\"website\">')\n tags.push(`<meta name=\"twitter:card\" content=\"${og.image ? 'summary_large_image' : 'summary'}\">`)\n if (og.title) tags.push(`<meta name=\"twitter:title\" content=\"${escapeHtml(og.title)}\">`)\n if (og.description) tags.push(`<meta name=\"twitter:description\" content=\"${escapeHtml(og.description)}\">`)\n if (og.image) tags.push(`<meta name=\"twitter:image\" content=\"${escapeHtml(og.image)}\">`)\n if (headMeta.canonical) tags.push(`<link rel=\"canonical\" href=\"${escapeHtml(headMeta.canonical)}\">`)\n if (tags.length) result = result.replace('</head>', `${tags.join('\\n')}\\n</head>`)\n }\n\n return result\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 const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic) || []\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 * `where:` / `sort:` / `limit:` are evaluated HERE, locally, over what the\n * first lane returns — with `@uniweb/core`'s one evaluator, the same the build\n * uses to materialize a file — and at the source when asked. Nothing decides\n * that per site: the 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 schema 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 * Per-fetch, the request may still carry `method: 'POST'` + `body:` for a\n * backend that takes a query in a body (GraphQL, a search endpoint);\n * `{paramName}` placeholders in body strings are substituted from\n * `request.dynamicContext`, so a template page's detail query can reference\n * its route param. `transform:` and the object form of `detail:` (its own\n * `envelope`) stay per fetch too — they describe ONE response, not a backend.\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 matchWhere,\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.\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 return {\n /**\n * The cache identity is the request's ADDRESS — or, when asked of the\n * records service, the QUESTION (`deriveCacheKey` hashes every operator of an address-less\n * request). Operators evaluated here run over a shared cached value and\n * must NOT split the cache: two pages declaring different `where:` clauses\n * against the same path share one entry — the file is fetched once and\n * each page filters its own copy.\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 = { signal: ctx.signal, 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 doFetch(target, init)\n\n // A per-request envelope (set by the object form of `detail:`) describes\n // this one response.\n const envelope = (request.envelope && typeof request.envelope === 'object')\n ? request.envelope\n : {}\n\n if (!response.ok) {\n // If `envelope.error` names a path, try to extract a human message\n // from the parsed body; fall back to status text if the path is\n // missing or the body isn't JSON.\n let extracted\n if (envelope.error) {\n try {\n const text = await response.text()\n const body = safeParseJSON(text)\n if (body !== undefined) {\n const candidate = getNestedValue(body, envelope.error)\n if (typeof candidate === 'string' && candidate.length) {\n extracted = candidate\n }\n }\n } catch {\n // Body not readable — fall through to status-text fallback.\n }\n }\n return {\n data: [],\n error: extracted ?? `HTTP ${response.status}: ${response.statusText}`,\n }\n }\n\n const contentType = response.headers.get('content-type') || ''\n let data\n if (contentType.includes('application/json')) {\n data = await response.json()\n } else {\n const text = await response.text()\n try {\n data = JSON.parse(text)\n } catch {\n data = text\n }\n }\n\n // Unwrap the response. Per-fetch `transform:` wins; otherwise the\n // envelope's `item` path on a single-record request, `list` on a list.\n const isDetailRequest = !!request.dynamicContext\n const effectiveTransform =\n transform\n || (isDetailRequest ? envelope.item : envelope.list)\n if (effectiveTransform && data !== null && data !== undefined) {\n data = getNestedValue(data, effectiveTransform)\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 })\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): `schema` required, `scope` a bare path, `sort`\n * one key spelled `date` / `-date`, `depth` brief or full. The where-object\n * crosses as authored except for the two spellings the language settled\n * differently from the evaluator's: `nin` is `not_in` there, and a top-level\n * `path: { under }` — the file lane's way of naming a folder branch — is\n * `scope`. Anything the service does not accept (`like`, a dotted path) is\n * sent as written and refused there by name: loud, never approximated.\n */\nfunction toQuestion(request) {\n const q = { schema: request.schema }\n let where = request.where && typeof request.where === 'object' ? request.where : null\n let scope = typeof request.scope === 'string' && request.scope ? request.scope : null\n if (where && !scope && where.path && typeof where.path === 'object' && typeof where.path.under === 'string' && where.path.under) {\n const { path, ...rest } = where\n scope = path.under\n where = Object.keys(rest).length ? rest : null\n }\n if (scope) q.scope = scope\n if (where) q.where = renameOperators(where)\n const sort = sortToWire(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 // ⭐ `cursor` is the ONLY field here that is not the author's: it is opaque and\n // it comes from a previous answer's `cursors` (the records contract §2). ⛔ And\n // `exhaustive` deliberately does NOT cross — it is a client instruction about\n // how many times to ask, not part of the question being asked.\n if (typeof request.cursor === 'string' && request.cursor) q.cursor = request.cursor\n // ⛔ `maxPages` does not cross either, for the same reason `exhaustive` does not:\n // both say how many times to ask, never what is being asked.\n return q\n}\n\nconst OPERATOR_ALIAS = { nin: 'not_in' }\nfunction renameOperators(where) {\n if (Array.isArray(where)) return where.map(renameOperators)\n if (!where || typeof where !== 'object') return where\n const out = {}\n for (const [key, value] of Object.entries(where)) {\n out[OPERATOR_ALIAS[key] ?? key] = value && typeof value === 'object' ? renameOperators(value) : value\n }\n return out\n}\n\n/**\n * Send one batch to the records service and hand each question its own answer.\n *\n * The response is `{ data, depths?, errors?, cursors?, limits? }` (contract §5):\n * `data` answers exactly the keys sent, `[]` when nothing matched; a key that\n * ERRORED is absent from `data` and present in `errors`; `depths` says what was\n * actually served, which the record index files rather than what was asked for.\n * A key missing from both is a protocol violation and is reported as an error,\n * never as silence.\n *\n * ⭐ `cursors` AND `limits` ARE READ — 2026-09-06 [Diego], reversing the ruling\n * that had them \"received and IGNORED, because framework has no paging concept.\"\n * ⛔ **That ruling described our client and was silently wrong about our USERS:**\n * the service bounds every answer at 100 (the records contract §4.2/§5), and a\n * `cursors` entry is how it says there is more. Discarding both meant a hosted\n * list of 500 rendered 100 — no error, no warning, no way for an author to tell\n * a bound from the end of 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` and `meta.bound` ride the\n * answer, and nothing pages automatically. Auto-paging here would put\n * unbounded round trips in front of paint for a section that may only show\n * 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 const errors = parsed && typeof parsed.errors === 'object' && parsed.errors ? parsed.errors : {}\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 // Both absent when empty, never `{}` (the records contract §5).\n const cursors = parsed && typeof parsed.cursors === 'object' && parsed.cursors ? parsed.cursors : {}\n const limits = parsed && typeof parsed.limits === 'object' && parsed.limits ? parsed.limits : {}\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 errors) {\n // A per-key error is `{ code, detail }` — `schema_not_found`,\n // `field_not_in_brief`, `scope_not_found`… The sentence is `detail`; `code`\n // rides beside it for a reader that wants to branch on it.\n const e = errors[key]\n const detail = typeof e === 'string' ? e : (e?.detail || e?.message || JSON.stringify(e))\n const out = held\n ? { data: held, error: detail, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error: detail }\n if (e && typeof e === 'object' && typeof e.code === 'string') out.code = e.code\n entry.resolve(out)\n return\n }\n if (!(key in data)) {\n entry.resolve({ data: null, error: `the records service answered without the key \"${key}\"` })\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 bound = typeof limits[key] === 'number' ? limits[key] : undefined\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, bound })\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, bound, 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 bound,\n // ⭐ ONE FLAG, MEANING **NOT THE WHOLE POPULATION** — asked for by name, so\n // a caller has one boolean to branch on rather than three 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 // · the service reported it BOUNDED the answer and offered no cursor.\n // ⚠️ The last is why `limits` is read at all: a cursor is the signal for a\n // query that declared no `limit`, and `limits` is the signal for one whose\n // author limit was clamped (the records contract §5). Neither alone covers\n // both. ⛔ Named `truncated` when it shipped in 0.17.0 this morning; renamed\n // the same day, before any consumer adopted it, because \"truncated\" says\n // something was cut and this also means \"there is more you did not ask for\".\n partial: (cursor || bound !== undefined) ? true : 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 await Promise.all([...pending].map(([entry, state]) => {\n const next = {\n ...entry,\n request: { ...entry.request, cursor: state.cursor },\n collected: state.collected,\n page: state.page,\n }\n return flushAsked(url, [next], doFetch)\n }))\n}\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 * Evaluate the query over what the source returned — the ONE evaluator,\n * `@uniweb/core`'s, so the browser orders and filters exactly as the build\n * did when it materialized `/data/<name>.json`.\n */\nfunction applyOperators(data, request, { dev = false } = {}) {\n if (!Array.isArray(data)) return data\n let result = data\n if (request.where) result = matchWhere(request.where, result)\n if (request.sort) result = applySort(result, request.sort, dev)\n if (typeof request.limit === 'number' && request.limit > 0) result = result.slice(0, request.limit)\n return result\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) {\n try {\n return sortRecords(items, sortExpr)\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 * Server-side data prefetch — the runtime executing a page's fetches for a host.\n *\n * L2 (graph state, no React): reads a payload, resolves the fetch configs the way the\n * entity store does at render time, executes them through the runtime's own default\n * fetcher, and returns the `[{ config, data }]` list `hydrateDataStore` expects.\n *\n * ⭐ Why this exists — one implementation of the fetch, in the runtime. A host that renders\n * pages in an isolate hands the isolate `fetchedData`. Until this module the host had to\n * compute that itself: resolve the configs, issue the requests, unwrap the responses in the\n * shape the datastore expects — a copy of the runtime's logic, in another repo, drifting\n * (the records envelope went silently unread that way on 2026-09-02). [Diego, 2026-09-03]:\n * *the backend sets the records service; the fetch comes from the runtime.* The host now calls\n * this and carries no copy; the host that renders in an isolate agreed to exactly\n * that shape the same day.\n *\n * ⛔ Contract with the host, deliberately small:\n * - `content` the render payload (`site-content.json` / `__DATA__`), config included —\n * `config.services` and `config.base` are read from it.\n * - `route` the page to prefetch for; a `[slug]` template resolves through the same\n * matcher the SPA uses, so `/blog/post-1` finds `/blog/:slug`.\n * - `fetch` how to dispatch a request. The runtime composes the address; the host\n * decides how a site-relative one is reached (its origin, a binding).\n * ⛔ **Crossing an isolate boundary, this survives only as an RPC method\n * argument.** Through an entrypoint's `fetch(Request)` with a serialized\n * body it arrives `undefined` (measured by a host against a real\n * isolate loader, 2026-09-03) — and the fetcher then falls\n * back to `globalThis.fetch`, so the request leaves from the isolate,\n * outside whatever budget the host wrapped around it. `prefetchAndHydrate`\n * refuses a non-function for exactly this reason; this entry keeps the\n * permissive default because the build and browser lanes call it in-process.\n * - `prerender` whether a fetch is tried — `'always'` (default) tries every config; `'author'`\n * honours the author's `prerender: false`. ⛔ The default is `'always'` because this\n * entry has exactly one kind of caller: an isolate rendering per request, where the\n * flag means nothing and always prerendering is the product ([Diego, 2026-07-28 and\n * 2026-09-03]: \"`prerender: false` is not for the isolate\"). The build lane, which\n * bakes static artifacts and does honour the flag, uses its own executor\n * (`build/src/prerender.js`) and never calls this. `'author'` is the explicit opt-in\n * for a caller that bakes; omitting the option must not silently reproduce the\n * 2026-07-28 outcome — prefetch a no-op on a live-data template, page still 200.\n * - returns one entry per DECLARED config, `{ config, outcome, data, meta?, error? }`, keyed\n * downstream by `deriveCacheKey(config)`. `outcome` is `fetched`, `failed`\n * (transport or HTTP error, `error` says which) or `skipped` (the author\n * deferred it to the browser with `prerender: false`). `hydrateDataStore`\n * takes the list as-is and hydrates only `fetched` entries — a host reads the\n * outcomes to tell \"nothing was tried\" from \"everything tried failed\", which\n * is a different cache decision (measured by a host, 2026-09-03).\n *\n * It resolves nothing the host owns and models no host route layout: every address is\n * `{base}/…` from the payload, or the records service the host itself\n * published at `config.services.records`.\n */\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { deriveCacheKey } from '@uniweb/core/datastore'\nimport { routePatternToRegex, decodeRouteValue, splitPathCapture } from '@uniweb/core/route-match'\nimport { buildDetailConfig } from '@uniweb/core/detail-url'\nimport { resolveDefaultLocale } from '@uniweb/core/locale-config'\nimport { createDefaultFetcher } from './default-fetcher.js'\n\nconst isRefinement = (f) => f && typeof f === 'object' && f.refine === true\n\n/**\n * The page a route names — exact first, then the `[slug]` / `[...path]` templates, like\n * the SPA. Captured params are decoded the way `matchDynamicRoute` decodes them (a\n * catch-all per segment), so the values are what the site's query is bound against.\n */\nexport function findPageForRoute(content, route) {\n const pages = content?.pages || []\n const exact = pages.find((p) => p.route === route)\n if (exact) return { page: exact, params: {} }\n for (const page of pages) {\n if (!page.isDynamic || !page.route) continue\n const compiled = routePatternToRegex(page.route)\n const m = compiled?.regex ? compiled.regex.exec(route) : null\n if (m) {\n const params = {}\n ;(compiled.paramNames || []).forEach((n, i) => {\n const raw = m[i + 1]\n params[n] = n === compiled.catchAll ? raw.split('/').map(decodeRouteValue).join('/') : decodeRouteValue(raw)\n })\n return { page, params }\n }\n }\n return { page: null, params: {} }\n}\n\n/**\n * The route's variables and the delivery param for a matched template page — the same\n * binding the SPA makes in `Website._createDynamicPage`: `[slug]` binds the one capture\n * under the folder's own name; `[...path]` splits its capture into `path` / `dir` /\n * `slug` and delivers by `slug`, the record's handle.\n */\nfunction routeBinding(page, params) {\n const { catchAll } = routePatternToRegex(page.route)\n if (catchAll && params[catchAll] !== undefined) {\n const parts = splitPathCapture(params[catchAll])\n const paramName = page.paramName || 'slug'\n return { paramName, paramValue: parts.slug, variables: { ...params, ...parts } }\n }\n const paramName = page.paramName || Object.keys(params)[0]\n return { paramName, paramValue: params[paramName], variables: { ...params } }\n}\n\n/**\n * Every fetch config a page will need at render time, resolved once and de-duplicated by\n * cache key: the site-level fetch, the page's, its parent's, and each section's own\n * (including nested sections), each through `resolveFetchConfigs` — the same resolver the\n * entity store uses, so a host prefetches exactly what the render will ask for.\n *\n * @returns {Object[]} resolved fetch configs\n */\nexport function resolvePageFetchConfigs(content, route, { locale = null } = {}) {\n const { page, params } = findPageForRoute(content, route)\n if (!page) return []\n const pages = content?.pages || []\n const parent = page.parent ? pages.find((p) => p.route === page.parent) : null\n const binding = page.isDynamic && Object.keys(params).length ? routeBinding(page, params) : null\n const options = {\n locale,\n defaultLocale: resolveDefaultLocale(content?.config) ?? null,\n queries: content?.config?.queries ?? null,\n services: content?.config?.services ?? null,\n variables: binding?.variables ?? null,\n }\n const out = new Map()\n const add = (sources) => {\n for (const cfg of resolveFetchConfigs(sources, options).values()) {\n const key = deriveCacheKey(cfg)\n if (!out.has(key)) out.set(key, cfg)\n }\n }\n // The cascade a block sees: its own fetch (unless a refinement), page, parent, site.\n add([page.fetch ?? null, parent?.fetch ?? null, content?.config?.fetch ?? null])\n const walk = (sections) => {\n for (const s of sections || []) {\n if (s?.fetch && !isRefinement(s.fetch)) add([s.fetch, page.fetch ?? null, parent?.fetch ?? null, content?.config?.fetch ?? null])\n if (s?.subsections) walk(s.subsections)\n }\n }\n walk(page.sections)\n\n // ⭐ A template page is ABOUT one record, and the record is a fetch of its own.\n // The list the page inherits is what the entity store matches the route param\n // against; when that query has a per-record source (a live lane's record address,\n // a `deferred:` query's per-record file), the record itself comes from a second\n // request — which this helper never built, so a host prerendering a template page\n // got the BRIEF and the body arrived after hydration as a client fetch. The\n // detail config is built by the one rule the\n // entity store uses (`buildDetailConfig`), keyed by the route's param.\n if (binding && binding.paramValue !== undefined && page.parentSchema) {\n const listCfg = [...out.values()].find((cfg) => cfg.as === page.parentSchema && cfg.detail)\n const detailCfg = listCfg ? buildDetailConfig(listCfg, { paramName: binding.paramName, paramValue: String(binding.paramValue) }) : null\n if (detailCfg) {\n const key = deriveCacheKey(detailCfg)\n if (!out.has(key)) out.set(key, detailCfg)\n }\n }\n return [...out.values()]\n}\n\n/**\n * Execute resolved fetch configs through the runtime's default fetcher.\n *\n * @param {Object[]} configs resolved configs (from `resolvePageFetchConfigs` or the host's own\n * call to `resolveFetchConfigs`)\n * @param {Object} opts\n * @param {Object} opts.content the payload — `config.base`, `config.services`\n * @param {Function} [opts.fetch] the transport; defaults to the global `fetch`\n * @param {boolean} [opts.dev]\n * @returns {Promise<Array<{ config: Object, outcome: 'fetched'|'failed'|'skipped', data: any, error?: string }>>}\n */\nexport async function executeFetchConfigs(configs, { content, fetch = null, dev = false, prerender = 'always' } = {}) {\n if (prerender !== 'author' && prerender !== 'always') {\n throw new Error(`executeFetchConfigs: prerender must be 'author' or 'always', got ${JSON.stringify(prerender)}`)\n }\n const fetcher = createDefaultFetcher({\n basePath: content?.config?.base || '',\n dev,\n fetch,\n })\n const ctx = { website: null }\n // Dispatched together, not one after another: the records service batches the\n // requests issued in one tick into one POST, and a page's configs are\n // independent of each other. Order is preserved in the result.\n return Promise.all((configs || []).filter(Boolean).map(async (config) => {\n if (prerender === 'author' && config.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 hydrated.\n return { config, outcome: 'skipped', data: null }\n }\n const result = await fetcher.resolve(config, ctx)\n if (result?.error) return { config, outcome: 'failed', data: null, error: result.error }\n return { config, outcome: 'fetched', data: result?.data ?? null, ...(result?.meta ? { meta: result.meta } : {}) }\n }))\n}\n\n/** Resolve and execute in one call: what a host passes the isolate as `fetchedData`. */\nexport async function prefetchPageData({ content, route, locale = null, fetch = null, dev = false, prerender = 'always' }) {\n const configs = resolvePageFetchConfigs(content, route, { locale })\n return executeFetchConfigs(configs, { content, fetch, dev, prerender })\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 rendering per request — `resolvePage` → `renderPage` →\n * `injectPageContent`, and it wrote the route lookup three times in three files\n * before `resolvePage` was exported at 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 hydration.** The two lanes differ REALLY here, not incidentally: a\n * build initializes once and hydrates every collection up front, an isolate\n * initializes per locale and prefetches per route. Folding either in would fit\n * one caller and lie to the other. `initPrerender` / `initPrerenderForLocale` /\n * `prefetchPageData` / `hydrateDataStore` stay separate exports, and\n * `prefetchAndHydrate` below is the isolate's two-step, not a general one.\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 { resolvePage, renderPage, classifyRenderError, injectPageContent } from './ssr-renderer.js'\nimport { hydrateDataStore } from './wire-foundation.js'\nimport { prefetchPageData } from './prefetch.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\n * `prefetchPageData`'s per-entry outcome.\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.\n * @param {Object} [options]\n * @param {Object} [options.inject] - extra options forwarded to `injectPageContent`\n * @returns {{ outcome: 'rendered'|'notFound'|'failed', html: string|null,\n * page: Object|null, error: {type: string, message: string}|null }}\n */\n function render(target, { inject = {} } = {}) {\n const page = typeof target === 'string' ? resolvePage(website, target) : target\n\n // ⛔ Not an error: nothing matched, which is a genuine 404 and the caller's to\n // turn into one — a build skips it, an isolate serves its 404 page with a 404\n // status. Returning `failed` here would make those indistinguishable.\n if (!page) return { outcome: 'notFound', html: null, page: null, error: null }\n\n let result\n try {\n result = 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: 'failed', html: null, page, error: classifyRenderError(err) }\n }\n\n if (result.error) return { outcome: '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 })\n return { outcome: '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 * ⛔ The build lane does NOT call this: it hydrates every collection once, before\n * its page loop, from its own executor that honours the author's `prerender:` flag.\n * That difference is why this is a named isolate helper and not a step inside\n * `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 HERE, unlike on `prefetchPageData`, and this is the\n // one place the difference matters.\n //\n // A function does NOT survive every isolate boundary. Measured by a host against\n // a real isolate loader, 2026-09-03: passed through an\n // entrypoint's `fetch(Request)` with a JSON body the transport arrives\n // **`undefined`**; passed as an argument to an RPC method it arrives as a callable\n // function and the isolate invokes it. Only the RPC shape carries it.\n //\n // ⚠️ And `undefined` is not where it stops, which is the part their measurement\n // could not see from outside our code. `createDefaultFetcher` resolves the\n // transport as `fetchImpl || globalThis.fetch`, so a transport that failed to cross\n // silently becomes THE ISOLATE'S OWN NETWORK — outside the host's timeout, byte\n // budget and site-relative address resolution. With an absolute address it does not\n // even fail: the request goes out from the wrong place and comes back\n // `outcome: 'fetched'`. A wiring mistake wearing a success.\n //\n // ⇒ So the entry whose only caller crosses that boundary demands a real function\n // rather than defaulting. A Node or browser caller that genuinely wants the global\n // passes `fetch: globalThis.fetch` — one word, and it says so.\n if (typeof fetch !== 'function') {\n throw new Error(\n 'prefetchAndHydrate: `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 const fetched = await prefetchPageData({ content, route, locale, fetch, dev, 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 * ## ⛔ `limit` IS DROPPED, and it is the one place a corpus must diverge\n *\n * A saved query's `limit` is the LIST PAGE's presentation: `limit: 20` means the\n * page shows twenty. **Its detail pages still exist for every record matching\n * `scope` + `where`** — so a corpus that honoured `limit` would index twenty and\n * miss every page beyond them, which is worse than indexing nothing because the\n * gap is invisible.\n *\n * ⚠️ **This shipped wrong in 0.17.0 and was found by the consumer, not by us**\n * (2026-09-06): the config was passed through unchanged, `limit` crossed as the\n * question's own, and the corpus was capped. The claim that this surface met\n * \"the population its detail pages can reach\" was made *\"read charitably\"* — a\n * phrase doing work that one `sed` would have done better.\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'\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`\n * @param {AbortSignal} [options.signal]\n * @param {string[]} [options.only] - restrict to these query names\n * @param {'brief'|'full'} [options.depth='brief'] - what to ask for. `brief` is\n * what a list shows; **`full` is what an index wants** — a brief index cannot\n * 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 is bounded by `maxPages`.\n * @param {number} [options.maxPages] - the caller's own bound on the walk. The\n * default is a bound, not a target; a caller that knows its per-request budget\n * passes its own.\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]` carries\n * `{ depth, pages, partial?, bound? }`.\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 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 // `limit` is the list page's, never the corpus's — see the header.\n const { limit, ...population } = cfg\n const asked = { ...population, 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":["paramName","resolveDefaultLocale","deriveCacheKey","fetch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../src/background-shared.js","../src/default-404.js","../src/wire-foundation.js","../src/area-wrappers.js","../src/appearance.js","../src/ssr-renderer.js","../src/default-fetcher.js","../src/prefetch.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 * - Field defaults applied to `content.data` items from the bound schemas\n *\n * This enables simpler component code by ensuring predictable prop shapes.\n */\n\nimport { isRichSchema } from '@uniweb/core'\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 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 }\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 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 ...(content.math && content.math.length ? { math: content.math } : {}),\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 a schema to a single object\n * Only processes fields defined in the schema, preserves unknown fields\n *\n * @param {Object} obj - The object to process\n * @param {Object} schema - Schema definition (fieldName -> fieldDef)\n * @returns {Object} Object with schema defaults applied\n */\nfunction applySchemaToObject(obj, schema) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {\n return obj\n }\n\n const result = { ...obj }\n\n for (const [field, fieldDef] of Object.entries(schema)) {\n // Get the default value - handle both shorthand and full form\n const defaultValue = typeof fieldDef === 'object' ? fieldDef.default : undefined\n\n // Apply default if field is missing and default exists\n if (result[field] === undefined && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n\n // Bare type strings ('string', 'decimal', …) carry nothing more to apply.\n if (typeof fieldDef !== 'object') continue\n\n // Inline picklist (`enum`): if the value is set but not among the allowed\n // values, fall back to the default.\n if (Array.isArray(fieldDef.enum)) {\n if (result[field] !== undefined && !fieldDef.enum.includes(result[field]) && defaultValue !== undefined) {\n result[field] = defaultValue\n }\n }\n\n // Nested object → recurse into its field map.\n if (fieldDef.type === 'object' && fieldDef.fields && result[field]) {\n result[field] = applySchemaToObject(result[field], fieldDef.fields)\n }\n\n // Array of objects → apply the element field map to each item.\n if (fieldDef.type === 'array' && fieldDef.items && Array.isArray(result[field])) {\n const items = fieldDef.items\n if (items && typeof items === 'object' && items.type === 'object' && items.fields) {\n result[field] = result[field].map((item) => applySchemaToObject(item, items.fields))\n }\n }\n }\n\n return result\n}\n\n/**\n * Apply a schema to a value (object or array of objects)\n *\n * @param {Object|Array} value - The value to process\n * @param {Object} schema - Schema definition\n * @returns {Object|Array} Value with schema defaults applied\n */\nfunction applySchemaToValue(value, schema) {\n if (Array.isArray(value)) {\n return value.map(item => applySchemaToObject(item, schema))\n }\n return applySchemaToObject(value, schema)\n}\n\n/**\n * Apply field defaults from a rich form `fields` array to an object.\n *\n * Recurses into `type: 'form'` (composite arrays with childSchema) and\n * `type: 'nestedObject'` / `type: 'object'` (single nested objects).\n *\n * Conditional visibility (`field.condition`) is not yet applied here —\n * components receive all fields the author filled plus defaults; hiding\n * is a later pass that requires the shared evaluateCondition util.\n *\n * @param {Object} obj - Row data (object keyed by field id)\n * @param {Array} fields - Rich field definitions\n * @returns {Object} - obj with defaults filled in\n */\nfunction applyRichFieldDefaults(obj, fields) {\n if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj\n if (!Array.isArray(fields)) return obj\n\n const result = { ...obj }\n\n for (const field of fields) {\n if (!field || typeof field !== 'object' || !field.id) continue\n const id = field.id\n\n if (result[id] === undefined && field.default !== undefined) {\n result[id] = field.default\n }\n\n if (field.type === 'form' && field.childSchema && Array.isArray(result[id])) {\n result[id] = result[id].map(item =>\n applyRichFieldDefaults(item, field.childSchema.fields)\n )\n } else if (\n (field.type === 'nestedObject' || field.type === 'object') &&\n Array.isArray(field.fields) &&\n result[id] &&\n typeof result[id] === 'object'\n ) {\n result[id] = applyRichFieldDefaults(result[id], field.fields)\n }\n }\n\n return result\n}\n\n/**\n * Apply a rich form schema to its stored value.\n *\n * Shape rules:\n * - composite (isComposite=true) → value is array of childSchema rows\n * - when `childRecords` is set, value may be `{ [childRecords]: [...] }`\n * - non-composite → value is a single object keyed by field id\n */\nfunction applyRichSchemaToValue(value, schema) {\n if (value == null) return value\n\n if (schema.isComposite && schema.childSchema) {\n const childFields = schema.childSchema.fields\n const queryKey = schema.childRecords\n\n if (queryKey && value && typeof value === 'object' && !Array.isArray(value)) {\n const arr = Array.isArray(value[queryKey]) ? value[queryKey] : []\n return {\n ...value,\n [queryKey]: arr.map(row => applyRichFieldDefaults(row, childFields)),\n }\n }\n\n if (Array.isArray(value)) {\n return value.map(row => applyRichFieldDefaults(row, childFields))\n }\n\n return value\n }\n\n if (Array.isArray(schema.fields)) {\n return applyRichFieldDefaults(value, schema.fields)\n }\n\n return value\n}\n\n/**\n * Apply schemas to content.data\n * Only processes tags that have a matching schema, leaves others untouched\n *\n * ## Two orders of schema — what a `data:` declaration may describe\n *\n * A component's `data:` is 1st order: a DEVELOPER says what shape the section\n * consumes. An authored form (```yaml:form```) is 2nd order: an AUTHOR says what\n * shape a VISITOR will submit. It is schema-shaped, but it is content.\n *\n * Declaring a schema for such a tag is legitimate, and it is worth being precise\n * about what it may describe:\n *\n * OK the DEFINITION's envelope — `title?`, `description?`, `fields: <map>`.\n * That asks \"is this a well-formed form?\", which is what build-time\n * validation is for (`build/src/validate-data.js` pairs a section's data\n * input with the schema its meta.js binds to that key).\n * WRONG a schema whose fields are THE FORM'S fields (`name`, `email`, …).\n * Those are author-chosen and unknowable at build time. A form-rendering\n * component receives its fields; it does not declare them.\n *\n * The mechanism is bounded and does not punish the mistake loudly:\n * `applySchemaToObject` recurses only where the schema declares structure\n * (`type: object` + `fields`, `type: array` + `items.fields`), so a\n * form-definition schema — which cannot name the author's fields — can never\n * reach into them. It fills the envelope defaults its own author declared.\n *\n * (Established with the editor team, 2026-07-31, channel frontend↔framework.\n * The editor shadows a foundation's `form` declaration with its own builder via\n * `builtinSchemas()`; that is about the EDITING UI and is orthogonal to whether a\n * foundation declares a schema for validation.)\n *\n * @param {Object} data - The data object from content\n * @param {Object} schemas - Schema definitions from runtime meta\n * @returns {Object} Data with schemas applied\n */\nexport function applySchemas(data, schemas) {\n if (!schemas || !data || typeof data !== 'object') {\n return data || {}\n }\n\n const result = { ...data }\n\n for (const [tag, rawValue] of Object.entries(data)) {\n const schema = schemas[tag]\n if (!schema) continue // No schema for this tag - leave as-is\n\n result[tag] = isRichSchema(schema)\n ? applyRichSchemaToValue(rawValue, schema)\n : applySchemaToValue(rawValue, schema)\n }\n\n return result\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 * Merge entity data onto a block's parsedContent.data.\n *\n * Section-level data already on the block (from prerender fetches via\n * blockData.parsedContent.data in the Block constructor) takes priority;\n * entity data only fills missing keys. Mutates `block.parsedContent.data`\n * in place so the vanilla JS layer holds the assembled data and\n * subsequent reads see the same shape.\n */\nfunction mergeEntityData(block, entityData) {\n if (!entityData) return\n const current = block.parsedContent.data || {}\n let changed = false\n const merged = { ...current }\n for (const key of Object.keys(entityData)) {\n if (merged[key] === undefined) {\n merged[key] = entityData[key]\n changed = true\n }\n }\n if (changed) {\n block.parsedContent.data = merged\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), or\n * when the handler throws. Errors are logged via `console.error`.\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 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/**\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, content guarantees, and schema application — the handler\n * sees the exact shape the 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. Merge entity data (resolved by EntityStore) onto\n * `block.parsedContent.data`.\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. Apply schemas to content.data.\n * 7. 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–7 are\n * pure derivations of the block's now-assembled state.\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 mergeEntityData(block, entityData)\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 let content = guaranteeContentStructure(block.parsedContent)\n\n // Apply schemas to content.data\n const schemas = meta?.schemas || null\n if (schemas && content.data) {\n content.data = applySchemas(content.data, schemas)\n }\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 are an L3 pair by design: `components/Background.jsx` renders\n * with hooks and DOM, `ssr-renderer.js` renders with `React.createElement`\n * only, and the runtime-layer rule (framework `CLAUDE.md` gotcha #2) says that\n * pair is twinned on purpose. ⛔ **What it also says is that only the RENDERING\n * is twinned — \"pure data → L1, one home\" — and two pure helpers had been\n * dragged across the seam with it**: a base-path joiner and a colour-opacity\n * converter, byte-identical in both files.\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 const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 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 * 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 * Keeping this file React-free matters for the SPA bundle: `setup.js`\n * pulls `wire-foundation.js` directly, but it 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. Read the foundation declaration via `foundation.default.capabilities.<name>`.\n * 2. Apply it to the uniweb singleton (set a slot, call a build hook,\n * register something on `activeWebsite`).\n * 3. 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`) wraps the foundation's\n * source default export under `default.capabilities.*`, so the runtime\n * sees a single canonical shape 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 cache key MUST go through `deriveCacheKey(entry.config)` and the\n * value MUST be wrapped as `{ data }` — otherwise the dispatcher's\n * lookup at `_dataStore.get(deriveCacheKey(request))` misses every\n * time and `cached.data` reads `undefined`. Three call sites used to\n * inline this loop independently (browser SPA, Node SSG, and a server-side\n * renderer); one of the three was using the wrong shape, silently killing\n * prefetched-data reuse wherever it ran. This helper is the one canonical\n * implementation.\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 `prefetchPageData` list carries every declared config with an `outcome`; only what was\n // actually 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 website.dataStore.set(deriveCacheKey(entry.config), entry.meta ? { data: entry.data, meta: entry.meta } : { data: entry.data })\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 * 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 region a `view-transition-name` so the browser animates them\n * independently — persistent chrome (header, sidebar, footer) morphs in place\n * while the body crossfades. Without per-region names the browser falls back to\n * a single full-page crossfade, which makes the whole layout (chrome included)\n * flicker on every navigation.\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\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 plus the implicit `body` gets a stable,\n * namespaced name (`uw-<area>`, `uw-body`). Same-named areas across layouts\n * therefore share a name and morph between layouts automatically.\n *\n * The layout's `meta.js` `transitions` value overrides this:\n * - an object overrides per region (`{ left: 'sidebar' }` to group across\n * layouts, or `{ left: null }` to opt one region out);\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 = { body: toIdent('body') }\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. 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 * @param {string[]} areaNames - Names of the areas rendered for this page (excludes `body`).\n * @param {Object|false|null|undefined} explicit - `layoutMeta.layers`.\n * @returns {Object} region → z-index. Empty when the layout opts out.\n */\nexport function resolveLayoutLayers(areaNames, explicit) {\n if (explicit === false) return {}\n\n const defaults = {}\n for (const name of areaNames) defaults[name] = 1\n\n return explicit ? { ...defaults, ...explicit } : defaults\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 * @param {string} region - Area name, or `body`.\n * @param {Object|null} transitions - region → view-transition-name.\n * @param {Object} layers - region → z-index.\n * @returns {Object|null} Inline style object, or null for no wrapper.\n */\nexport function areaWrapperStyle(region, transitions, layers) {\n const style = {}\n\n const name = transitions?.[region]\n if (name) style.viewTransitionName = name\n\n const layer = layers?.[region]\n 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 * 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 * SSR Renderer\n *\n * Hook-free rendering pipeline for SSG (build) and server-side SSR.\n * Mirrors BlockRenderer.jsx + Background.jsx using React.createElement\n * directly — no hooks, no JSX, no browser APIs.\n *\n * This is the single source of truth for how blocks render during prerender.\n * When modifying BlockRenderer.jsx or Background.jsx, update this file 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 { siteUrl, withOpacity } from './background-shared.js'\nimport { routePatternToRegex } 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, areaWrapperStyle } from './area-wrappers.js'\nimport { renderAppearanceBootScript } from './appearance.js'\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 const { background = {} } = block.standardOptions\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 // Apply context overrides as inline CSS custom properties\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 background element for SSR.\n * Mirrors Background.jsx (color, gradient, image — not video).\n * Video backgrounds require JS for autoplay and are skipped during SSR.\n */\nexport function renderBackground(background) {\n if (!background?.mode) return null\n\n const containerStyle = {\n position: 'absolute',\n inset: '0',\n overflow: 'hidden',\n zIndex: 0,\n }\n\n const children = []\n\n // Color background\n if (background.mode === 'color' && background.color) {\n children.push(\n React.createElement('div', {\n key: 'bg-color',\n className: 'background-color',\n style: { position: 'absolute', inset: '0', backgroundColor: background.color },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Gradient background (supports string or object with opacity)\n if (background.mode === 'gradient' && background.gradient) {\n const g = background.gradient\n\n let bgValue\n if (typeof g === 'string') {\n bgValue = g\n } else {\n const {\n start = 'transparent',\n end = 'transparent',\n angle = 0,\n startPosition = 0,\n endPosition = 100,\n startOpacity = 1,\n endOpacity = 1,\n } = g\n const startColor = startOpacity < 1 ? withOpacity(start, startOpacity) : start\n const endColor = endOpacity < 1 ? withOpacity(end, endOpacity) : end\n bgValue = `linear-gradient(${angle}deg, ${startColor} ${startPosition}%, ${endColor} ${endPosition}%)`\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-gradient',\n className: 'background-gradient',\n style: { position: 'absolute', inset: '0', background: bgValue },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Image background\n if (background.mode === 'image' && background.image?.src) {\n const img = background.image\n children.push(\n React.createElement('div', {\n key: 'bg-image',\n className: 'background-image',\n style: {\n position: 'absolute',\n inset: '0',\n backgroundImage: `url(${siteUrl(img.src)})`,\n backgroundPosition: img.position || 'center',\n backgroundSize: img.size || 'cover',\n backgroundRepeat: 'no-repeat',\n },\n 'aria-hidden': 'true',\n })\n )\n }\n\n // Overlay (gradient or solid)\n if (background.overlay?.enabled) {\n const ov = background.overlay\n let overlayStyle\n\n if (ov.gradient) {\n const g = ov.gradient\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n background: `linear-gradient(${g.angle || 180}deg, ${g.start || 'rgba(0,0,0,0.7)'} ${g.startPosition || 0}%, ${g.end || 'rgba(0,0,0,0)'} ${g.endPosition || 100}%)`,\n opacity: ov.opacity ?? 0.5,\n }\n } else {\n const baseColor = ov.type === 'light' ? '255, 255, 255' : '0, 0, 0'\n overlayStyle = {\n position: 'absolute', inset: '0', pointerEvents: 'none',\n backgroundColor: `rgba(${baseColor}, ${ov.opacity ?? 0.5})`,\n }\n }\n\n children.push(\n React.createElement('div', {\n key: 'bg-overlay',\n className: ov.gradient ? 'background-overlay background-overlay--gradient' : 'background-overlay background-overlay--solid',\n style: overlayStyle,\n 'aria-hidden': 'true',\n })\n )\n }\n\n if (children.length === 0) return null\n\n return React.createElement('div', {\n className: `background background--${background.mode}`,\n style: containerStyle,\n 'aria-hidden': 'true',\n }, ...children)\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 * @returns {React.ReactElement}\n */\nexport function renderBlock(block, { as = 'section' } = {}) {\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 inherited entity data synchronously (SSG has no async).\n // EntityStore walks page/site hierarchy to find data matching meta.inheritData.\n const meta = getComponentMeta(block.type)\n const entityStore = block.website?.entityStore\n let entityData = null\n if (entityStore) {\n const resolved = entityStore.resolve(block, meta)\n if (resolved.status === 'ready') entityData = resolved.data\n }\n\n // Build content and params with runtime guarantees.\n // prepareProps handles the full pipeline: entity data merge,\n // foundation content handler invocation, guaranteed content\n // structure, schema application, and param defaults.\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 // 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) {\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)\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: wrap body + each area in a thin div carrying its\n // view-transition-name, so the prerendered HTML matches what the SPA hydrates\n // 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 const layers = resolveLayoutLayers(areaNames, layoutMeta?.layers)\n const wrapArea = (name, element) => {\n const style = areaWrapperStyle(name, transitions, layers)\n return style ? React.createElement('div', { style }, element) : element\n }\n\n const bodyElement = bodyBlocks ? wrapArea('body', renderBlocks(bodyBlocks)) : 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 // Default layout — mirror DefaultLayout in Layout.jsx, including its lack of\n // stacking: the area wrappers already carry their layers, and a positioned\n // element here would seal those layers inside it.\n return React.createElement(React.Fragment, null,\n areaElements.header && React.createElement('header', null, areaElements.header),\n bodyElement && React.createElement('main', null, bodyElement),\n areaElements.footer && React.createElement('footer', null, areaElements.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\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 })\n )\n )\n }\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 always reflects the page being rendered.\n const website = uniweb.activeWebsite\n uniweb.routingComponents = {\n useLocation: () => {\n const route = website?.activePage?.route || ''\n return { pathname: '/' + route, 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 */\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?.cdnUrl || 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 const svg = await response.text()\n uniweb.iconCache.set(`${family}:${name}`, svg)\n })\n )\n\n const succeeded = results.filter(r => r.status === 'fulfilled').length\n const failed = results.filter(r => r.status === 'rejected').length\n if (failed > 0) {\n const msg = `Fetched ${succeeded}/${icons.length} icons (${failed} failed)`\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}\n\n// ============================================================================\n// Layer 3: Per-page rendering\n// ============================================================================\n\n/**\n * Classify an SSR rendering error.\n *\n * @param {Error} err\n * @returns {{ type: 'hooks'|'null-component'|'unknown', message: string }}\n */\n/**\n * Resolve a route to the Page that should render it.\n *\n * Exists because this module 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 * 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\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 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 const sectionOverrideCSS = buildSectionOverrides(page.getPageBlocks(), appearance)\n\n return { renderedContent, sectionOverrideCSS }\n}\n\n// ============================================================================\n// HTML injection\n// ============================================================================\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str) {\n if (!str) return ''\n return String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\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 * - Add/update meta description\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 hydrates 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 // 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 page title (use getTitle() so isIndex pages inherit parent title)\n const pageTitle = page.getTitle?.() || page.title\n if (pageTitle) {\n result = result.replace(\n /<title>.*?<\\/title>/,\n `<title>${escapeHtml(pageTitle)}</title>`\n )\n }\n\n // Add/update meta description\n if (page.description) {\n const metaDesc = `<meta name=\"description\" content=\"${escapeHtml(page.description)}\">`\n if (result.includes('<meta name=\"description\"')) {\n result = result.replace(/<meta name=\"description\"[^>]*>/, metaDesc)\n } else {\n result = result.replace('</head>', `${metaDesc}\\n</head>`)\n }\n }\n\n // Social / SEO meta from the page's effective head metadata (page seo\n // cascading over site-level seo — see Page.getHeadMeta). The SPA emits these\n // client-side via useHeadMeta; this is the SSR twin, so crawlers and social\n // unfurlers (which don't run JS) get them in the static HTML too.\n const headMeta = page.getHeadMeta?.()\n if (headMeta) {\n const og = headMeta.og || {}\n const keywords = Array.isArray(headMeta.keywords)\n ? headMeta.keywords.join(', ')\n : headMeta.keywords\n const tags = []\n if (keywords) tags.push(`<meta name=\"keywords\" content=\"${escapeHtml(keywords)}\">`)\n if (headMeta.robots) tags.push(`<meta name=\"robots\" content=\"${escapeHtml(headMeta.robots)}\">`)\n if (og.title) tags.push(`<meta property=\"og:title\" content=\"${escapeHtml(og.title)}\">`)\n if (og.description) tags.push(`<meta property=\"og:description\" content=\"${escapeHtml(og.description)}\">`)\n if (og.image) tags.push(`<meta property=\"og:image\" content=\"${escapeHtml(og.image)}\">`)\n if (og.url) tags.push(`<meta property=\"og:url\" content=\"${escapeHtml(og.url)}\">`)\n tags.push('<meta property=\"og:type\" content=\"website\">')\n tags.push(`<meta name=\"twitter:card\" content=\"${og.image ? 'summary_large_image' : 'summary'}\">`)\n if (og.title) tags.push(`<meta name=\"twitter:title\" content=\"${escapeHtml(og.title)}\">`)\n if (og.description) tags.push(`<meta name=\"twitter:description\" content=\"${escapeHtml(og.description)}\">`)\n if (og.image) tags.push(`<meta name=\"twitter:image\" content=\"${escapeHtml(og.image)}\">`)\n if (headMeta.canonical) tags.push(`<link rel=\"canonical\" href=\"${escapeHtml(headMeta.canonical)}\">`)\n if (tags.length) result = result.replace('</head>', `${tags.join('\\n')}\\n</head>`)\n }\n\n return result\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 const dynamicTemplates = siteContent.pages?.filter((p) => p.isDynamic) || []\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 * `where:` / `sort:` / `limit:` are evaluated HERE, locally, over what the\n * first lane returns — with `@uniweb/core`'s one evaluator, the same the build\n * uses to materialize a file — and at the source when asked. Nothing decides\n * that per site: the 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 schema 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 * Per-fetch, the request may still carry `method: 'POST'` + `body:` for a\n * backend that takes a query in a body (GraphQL, a search endpoint);\n * `{paramName}` placeholders in body strings are substituted from\n * `request.dynamicContext`, so a template page's detail query can reference\n * its route param. `transform:` and the object form of `detail:` (its own\n * `envelope`) stay per fetch too — they describe ONE response, not a backend.\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 matchWhere,\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.\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 return {\n /**\n * The cache identity is the request's ADDRESS — or, when asked of the\n * records service, the QUESTION (`deriveCacheKey` hashes every operator of an address-less\n * request). Operators evaluated here run over a shared cached value and\n * must NOT split the cache: two pages declaring different `where:` clauses\n * against the same path share one entry — the file is fetched once and\n * each page filters its own copy.\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 = { signal: ctx.signal, 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 doFetch(target, init)\n\n // A per-request envelope (set by the object form of `detail:`) describes\n // this one response.\n const envelope = (request.envelope && typeof request.envelope === 'object')\n ? request.envelope\n : {}\n\n if (!response.ok) {\n // If `envelope.error` names a path, try to extract a human message\n // from the parsed body; fall back to status text if the path is\n // missing or the body isn't JSON.\n let extracted\n if (envelope.error) {\n try {\n const text = await response.text()\n const body = safeParseJSON(text)\n if (body !== undefined) {\n const candidate = getNestedValue(body, envelope.error)\n if (typeof candidate === 'string' && candidate.length) {\n extracted = candidate\n }\n }\n } catch {\n // Body not readable — fall through to status-text fallback.\n }\n }\n return {\n data: [],\n error: extracted ?? `HTTP ${response.status}: ${response.statusText}`,\n }\n }\n\n const contentType = response.headers.get('content-type') || ''\n let data\n if (contentType.includes('application/json')) {\n data = await response.json()\n } else {\n const text = await response.text()\n try {\n data = JSON.parse(text)\n } catch {\n data = text\n }\n }\n\n // Unwrap the response. Per-fetch `transform:` wins; otherwise the\n // envelope's `item` path on a single-record request, `list` on a list.\n const isDetailRequest = !!request.dynamicContext\n const effectiveTransform =\n transform\n || (isDetailRequest ? envelope.item : envelope.list)\n if (effectiveTransform && data !== null && data !== undefined) {\n data = getNestedValue(data, effectiveTransform)\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 })\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): `schema` required, `scope` a bare path, `sort`\n * one key spelled `date` / `-date`, `depth` brief or full. The where-object\n * crosses as authored except for the two spellings the language settled\n * differently from the evaluator's: `nin` is `not_in` there, and a top-level\n * `path: { under }` — the file lane's way of naming a folder branch — is\n * `scope`. Anything the service does not accept (`like`, a dotted path) is\n * sent as written and refused there by name: loud, never approximated.\n */\nfunction toQuestion(request) {\n const q = { schema: request.schema }\n let where = request.where && typeof request.where === 'object' ? request.where : null\n let scope = typeof request.scope === 'string' && request.scope ? request.scope : null\n if (where && !scope && where.path && typeof where.path === 'object' && typeof where.path.under === 'string' && where.path.under) {\n const { path, ...rest } = where\n scope = path.under\n where = Object.keys(rest).length ? rest : null\n }\n if (scope) q.scope = scope\n if (where) q.where = renameOperators(where)\n const sort = sortToWire(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 // ⭐ `cursor` is the ONLY field here that is not the author's: it is opaque and\n // it comes from a previous answer's `cursors` (the records contract §2). ⛔ And\n // `exhaustive` deliberately does NOT cross — it is a client instruction about\n // how many times to ask, not part of the question being asked.\n if (typeof request.cursor === 'string' && request.cursor) q.cursor = request.cursor\n // ⛔ `maxPages` does not cross either, for the same reason `exhaustive` does not:\n // both say how many times to ask, never what is being asked.\n return q\n}\n\nconst OPERATOR_ALIAS = { nin: 'not_in' }\nfunction renameOperators(where) {\n if (Array.isArray(where)) return where.map(renameOperators)\n if (!where || typeof where !== 'object') return where\n const out = {}\n for (const [key, value] of Object.entries(where)) {\n out[OPERATOR_ALIAS[key] ?? key] = value && typeof value === 'object' ? renameOperators(value) : value\n }\n return out\n}\n\n/**\n * Send one batch to the records service and hand each question its own answer.\n *\n * The response is `{ data, depths?, errors?, cursors?, limits? }` (contract §5):\n * `data` answers exactly the keys sent, `[]` when nothing matched; a key that\n * ERRORED is absent from `data` and present in `errors`; `depths` says what was\n * actually served, which the record index files rather than what was asked for.\n * A key missing from both is a protocol violation and is reported as an error,\n * never as silence.\n *\n * ⭐ `cursors` AND `limits` ARE READ — 2026-09-06 [Diego], reversing the ruling\n * that had them \"received and IGNORED, because framework has no paging concept.\"\n * ⛔ **That ruling described our client and was silently wrong about our USERS:**\n * the service bounds every answer at 100 (the records contract §4.2/§5), and a\n * `cursors` entry is how it says there is more. Discarding both meant a hosted\n * list of 500 rendered 100 — no error, no warning, no way for an author to tell\n * a bound from the end of 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` and `meta.bound` ride the\n * answer, and nothing pages automatically. Auto-paging here would put\n * unbounded round trips in front of paint for a section that may only show\n * 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 const errors = parsed && typeof parsed.errors === 'object' && parsed.errors ? parsed.errors : {}\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 // Both absent when empty, never `{}` (the records contract §5).\n const cursors = parsed && typeof parsed.cursors === 'object' && parsed.cursors ? parsed.cursors : {}\n const limits = parsed && typeof parsed.limits === 'object' && parsed.limits ? parsed.limits : {}\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 errors) {\n // A per-key error is `{ code, detail }` — `schema_not_found`,\n // `field_not_in_brief`, `scope_not_found`… The sentence is `detail`; `code`\n // rides beside it for a reader that wants to branch on it.\n const e = errors[key]\n const detail = typeof e === 'string' ? e : (e?.detail || e?.message || JSON.stringify(e))\n const out = held\n ? { data: held, error: detail, meta: withMeta({ partial: true, pages: entry.page }) }\n : { data: null, error: detail }\n if (e && typeof e === 'object' && typeof e.code === 'string') out.code = e.code\n entry.resolve(out)\n return\n }\n if (!(key in data)) {\n entry.resolve({ data: null, error: `the records service answered without the key \"${key}\"` })\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 bound = typeof limits[key] === 'number' ? limits[key] : undefined\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, bound })\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, bound, 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 bound,\n // ⭐ ONE FLAG, MEANING **NOT THE WHOLE POPULATION** — asked for by name, so\n // a caller has one boolean to branch on rather than three 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 // · the service reported it BOUNDED the answer and offered no cursor.\n // ⚠️ The last is why `limits` is read at all: a cursor is the signal for a\n // query that declared no `limit`, and `limits` is the signal for one whose\n // author limit was clamped (the records contract §5). Neither alone covers\n // both. ⛔ Named `truncated` when it shipped in 0.17.0 this morning; renamed\n // the same day, before any consumer adopted it, because \"truncated\" says\n // something was cut and this also means \"there is more you did not ask for\".\n partial: (cursor || bound !== undefined) ? true : 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 await Promise.all([...pending].map(([entry, state]) => {\n const next = {\n ...entry,\n request: { ...entry.request, cursor: state.cursor },\n collected: state.collected,\n page: state.page,\n }\n return flushAsked(url, [next], doFetch)\n }))\n}\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 * Evaluate the query over what the source returned — the ONE evaluator,\n * `@uniweb/core`'s, so the browser orders and filters exactly as the build\n * did when it materialized `/data/<name>.json`.\n */\nfunction applyOperators(data, request, { dev = false } = {}) {\n if (!Array.isArray(data)) return data\n let result = data\n if (request.where) result = matchWhere(request.where, result)\n if (request.sort) result = applySort(result, request.sort, dev)\n if (typeof request.limit === 'number' && request.limit > 0) result = result.slice(0, request.limit)\n return result\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) {\n try {\n return sortRecords(items, sortExpr)\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 * Server-side data prefetch — the runtime executing a page's fetches for a host.\n *\n * L2 (graph state, no React): reads a payload, resolves the fetch configs the way the\n * entity store does at render time, executes them through the runtime's own default\n * fetcher, and returns the `[{ config, data }]` list `hydrateDataStore` expects.\n *\n * ⭐ Why this exists — one implementation of the fetch, in the runtime. A host that renders\n * pages in an isolate hands the isolate `fetchedData`. Until this module the host had to\n * compute that itself: resolve the configs, issue the requests, unwrap the responses in the\n * shape the datastore expects — a copy of the runtime's logic, in another repo, drifting\n * (the records envelope went silently unread that way on 2026-09-02). [Diego, 2026-09-03]:\n * *the backend sets the records service; the fetch comes from the runtime.* The host now calls\n * this and carries no copy; the host that renders in an isolate agreed to exactly\n * that shape the same day.\n *\n * ⛔ Contract with the host, deliberately small:\n * - `content` the render payload (`site-content.json` / `__DATA__`), config included —\n * `config.services` and `config.base` are read from it.\n * - `route` the page to prefetch for; a `[slug]` template resolves through the same\n * matcher the SPA uses, so `/blog/post-1` finds `/blog/:slug`.\n * - `fetch` how to dispatch a request. The runtime composes the address; the host\n * decides how a site-relative one is reached (its origin, a binding).\n * ⛔ **Crossing an isolate boundary, this survives only as an RPC method\n * argument.** Through an entrypoint's `fetch(Request)` with a serialized\n * body it arrives `undefined` (measured by a host against a real\n * isolate loader, 2026-09-03) — and the fetcher then falls\n * back to `globalThis.fetch`, so the request leaves from the isolate,\n * outside whatever budget the host wrapped around it. `prefetchAndHydrate`\n * refuses a non-function for exactly this reason; this entry keeps the\n * permissive default because the build and browser lanes call it in-process.\n * - `prerender` whether a fetch is tried — `'always'` (default) tries every config; `'author'`\n * honours the author's `prerender: false`. ⛔ The default is `'always'` because this\n * entry has exactly one kind of caller: an isolate rendering per request, where the\n * flag means nothing and always prerendering is the product ([Diego, 2026-07-28 and\n * 2026-09-03]: \"`prerender: false` is not for the isolate\"). The build lane, which\n * bakes static artifacts and does honour the flag, uses its own executor\n * (`build/src/prerender.js`) and never calls this. `'author'` is the explicit opt-in\n * for a caller that bakes; omitting the option must not silently reproduce the\n * 2026-07-28 outcome — prefetch a no-op on a live-data template, page still 200.\n * - returns one entry per DECLARED config, `{ config, outcome, data, meta?, error? }`, keyed\n * downstream by `deriveCacheKey(config)`. `outcome` is `fetched`, `failed`\n * (transport or HTTP error, `error` says which) or `skipped` (the author\n * deferred it to the browser with `prerender: false`). `hydrateDataStore`\n * takes the list as-is and hydrates only `fetched` entries — a host reads the\n * outcomes to tell \"nothing was tried\" from \"everything tried failed\", which\n * is a different cache decision (measured by a host, 2026-09-03).\n *\n * It resolves nothing the host owns and models no host route layout: every address is\n * `{base}/…` from the payload, or the records service the host itself\n * published at `config.services.records`.\n */\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { deriveCacheKey } from '@uniweb/core/datastore'\nimport { routePatternToRegex, decodeRouteValue, splitPathCapture } from '@uniweb/core/route-match'\nimport { buildDetailConfig } from '@uniweb/core/detail-url'\nimport { resolveDefaultLocale } from '@uniweb/core/locale-config'\nimport { createDefaultFetcher } from './default-fetcher.js'\n\nconst isRefinement = (f) => f && typeof f === 'object' && f.refine === true\n\n/**\n * The page a route names — exact first, then the `[slug]` / `[...path]` templates, like\n * the SPA. Captured params are decoded the way `matchDynamicRoute` decodes them (a\n * catch-all per segment), so the values are what the site's query is bound against.\n */\nexport function findPageForRoute(content, route) {\n const pages = content?.pages || []\n const exact = pages.find((p) => p.route === route)\n if (exact) return { page: exact, params: {} }\n for (const page of pages) {\n if (!page.isDynamic || !page.route) continue\n const compiled = routePatternToRegex(page.route)\n const m = compiled?.regex ? compiled.regex.exec(route) : null\n if (m) {\n const params = {}\n ;(compiled.paramNames || []).forEach((n, i) => {\n const raw = m[i + 1]\n params[n] = n === compiled.catchAll ? raw.split('/').map(decodeRouteValue).join('/') : decodeRouteValue(raw)\n })\n return { page, params }\n }\n }\n return { page: null, params: {} }\n}\n\n/**\n * The route's variables and the delivery param for a matched template page — the same\n * binding the SPA makes in `Website._createDynamicPage`: `[slug]` binds the one capture\n * under the folder's own name; `[...path]` splits its capture into `path` / `dir` /\n * `slug` and delivers by `slug`, the record's handle.\n */\nfunction routeBinding(page, params) {\n const { catchAll } = routePatternToRegex(page.route)\n if (catchAll && params[catchAll] !== undefined) {\n const parts = splitPathCapture(params[catchAll])\n const paramName = page.paramName || 'slug'\n return { paramName, paramValue: parts.slug, variables: { ...params, ...parts } }\n }\n const paramName = page.paramName || Object.keys(params)[0]\n return { paramName, paramValue: params[paramName], variables: { ...params } }\n}\n\n/**\n * Every fetch config a page will need at render time, resolved once and de-duplicated by\n * cache key: the site-level fetch, the page's, its parent's, and each section's own\n * (including nested sections), each through `resolveFetchConfigs` — the same resolver the\n * entity store uses, so a host prefetches exactly what the render will ask for.\n *\n * @returns {Object[]} resolved fetch configs\n */\nexport function resolvePageFetchConfigs(content, route, { locale = null } = {}) {\n const { page, params } = findPageForRoute(content, route)\n if (!page) return []\n const pages = content?.pages || []\n const parent = page.parent ? pages.find((p) => p.route === page.parent) : null\n const binding = page.isDynamic && Object.keys(params).length ? routeBinding(page, params) : null\n const options = {\n locale,\n defaultLocale: resolveDefaultLocale(content?.config) ?? null,\n queries: content?.config?.queries ?? null,\n services: content?.config?.services ?? null,\n variables: binding?.variables ?? null,\n }\n const out = new Map()\n const add = (sources) => {\n for (const cfg of resolveFetchConfigs(sources, options).values()) {\n const key = deriveCacheKey(cfg)\n if (!out.has(key)) out.set(key, cfg)\n }\n }\n // The cascade a block sees: its own fetch (unless a refinement), page, parent, site.\n add([page.fetch ?? null, parent?.fetch ?? null, content?.config?.fetch ?? null])\n const walk = (sections) => {\n for (const s of sections || []) {\n if (s?.fetch && !isRefinement(s.fetch)) add([s.fetch, page.fetch ?? null, parent?.fetch ?? null, content?.config?.fetch ?? null])\n if (s?.subsections) walk(s.subsections)\n }\n }\n walk(page.sections)\n\n // ⭐ A template page is ABOUT one record, and the record is a fetch of its own.\n // The list the page inherits is what the entity store matches the route param\n // against; when that query has a per-record source (a live lane's record address,\n // a `deferred:` query's per-record file), the record itself comes from a second\n // request — which this helper never built, so a host prerendering a template page\n // got the BRIEF and the body arrived after hydration as a client fetch. The\n // detail config is built by the one rule the\n // entity store uses (`buildDetailConfig`), keyed by the route's param.\n if (binding && binding.paramValue !== undefined && page.parentSchema) {\n const listCfg = [...out.values()].find((cfg) => cfg.as === page.parentSchema && cfg.detail)\n const detailCfg = listCfg ? buildDetailConfig(listCfg, { paramName: binding.paramName, paramValue: String(binding.paramValue) }) : null\n if (detailCfg) {\n const key = deriveCacheKey(detailCfg)\n if (!out.has(key)) out.set(key, detailCfg)\n }\n }\n return [...out.values()]\n}\n\n/**\n * Execute resolved fetch configs through the runtime's default fetcher.\n *\n * @param {Object[]} configs resolved configs (from `resolvePageFetchConfigs` or the host's own\n * call to `resolveFetchConfigs`)\n * @param {Object} opts\n * @param {Object} opts.content the payload — `config.base`, `config.services`\n * @param {Function} [opts.fetch] the transport; defaults to the global `fetch`\n * @param {boolean} [opts.dev]\n * @returns {Promise<Array<{ config: Object, outcome: 'fetched'|'failed'|'skipped', data: any, error?: string }>>}\n */\nexport async function executeFetchConfigs(configs, { content, fetch = null, dev = false, prerender = 'always' } = {}) {\n if (prerender !== 'author' && prerender !== 'always') {\n throw new Error(`executeFetchConfigs: prerender must be 'author' or 'always', got ${JSON.stringify(prerender)}`)\n }\n const fetcher = createDefaultFetcher({\n basePath: content?.config?.base || '',\n dev,\n fetch,\n })\n const ctx = { website: null }\n // Dispatched together, not one after another: the records service batches the\n // requests issued in one tick into one POST, and a page's configs are\n // independent of each other. Order is preserved in the result.\n return Promise.all((configs || []).filter(Boolean).map(async (config) => {\n if (prerender === 'author' && config.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 hydrated.\n return { config, outcome: 'skipped', data: null }\n }\n const result = await fetcher.resolve(config, ctx)\n if (result?.error) return { config, outcome: 'failed', data: null, error: result.error }\n return { config, outcome: 'fetched', data: result?.data ?? null, ...(result?.meta ? { meta: result.meta } : {}) }\n }))\n}\n\n/** Resolve and execute in one call: what a host passes the isolate as `fetchedData`. */\nexport async function prefetchPageData({ content, route, locale = null, fetch = null, dev = false, prerender = 'always' }) {\n const configs = resolvePageFetchConfigs(content, route, { locale })\n return executeFetchConfigs(configs, { content, fetch, dev, prerender })\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 rendering per request — `resolvePage` → `renderPage` →\n * `injectPageContent`, and it wrote the route lookup three times in three files\n * before `resolvePage` was exported at 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 hydration.** The two lanes differ REALLY here, not incidentally: a\n * build initializes once and hydrates every collection up front, an isolate\n * initializes per locale and prefetches per route. Folding either in would fit\n * one caller and lie to the other. `initPrerender` / `initPrerenderForLocale` /\n * `prefetchPageData` / `hydrateDataStore` stay separate exports, and\n * `prefetchAndHydrate` below is the isolate's two-step, not a general one.\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 { resolvePage, renderPage, classifyRenderError, injectPageContent } from './ssr-renderer.js'\nimport { hydrateDataStore } from './wire-foundation.js'\nimport { prefetchPageData } from './prefetch.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\n * `prefetchPageData`'s per-entry outcome.\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.\n * @param {Object} [options]\n * @param {Object} [options.inject] - extra options forwarded to `injectPageContent`\n * @returns {{ outcome: 'rendered'|'notFound'|'failed', html: string|null,\n * page: Object|null, error: {type: string, message: string}|null }}\n */\n function render(target, { inject = {} } = {}) {\n const page = typeof target === 'string' ? resolvePage(website, target) : target\n\n // ⛔ Not an error: nothing matched, which is a genuine 404 and the caller's to\n // turn into one — a build skips it, an isolate serves its 404 page with a 404\n // status. Returning `failed` here would make those indistinguishable.\n if (!page) return { outcome: 'notFound', html: null, page: null, error: null }\n\n let result\n try {\n result = 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: 'failed', html: null, page, error: classifyRenderError(err) }\n }\n\n if (result.error) return { outcome: '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 })\n return { outcome: '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 * ⛔ The build lane does NOT call this: it hydrates every collection once, before\n * its page loop, from its own executor that honours the author's `prerender:` flag.\n * That difference is why this is a named isolate helper and not a step inside\n * `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 HERE, unlike on `prefetchPageData`, and this is the\n // one place the difference matters.\n //\n // A function does NOT survive every isolate boundary. Measured by a host against\n // a real isolate loader, 2026-09-03: passed through an\n // entrypoint's `fetch(Request)` with a JSON body the transport arrives\n // **`undefined`**; passed as an argument to an RPC method it arrives as a callable\n // function and the isolate invokes it. Only the RPC shape carries it.\n //\n // ⚠️ And `undefined` is not where it stops, which is the part their measurement\n // could not see from outside our code. `createDefaultFetcher` resolves the\n // transport as `fetchImpl || globalThis.fetch`, so a transport that failed to cross\n // silently becomes THE ISOLATE'S OWN NETWORK — outside the host's timeout, byte\n // budget and site-relative address resolution. With an absolute address it does not\n // even fail: the request goes out from the wrong place and comes back\n // `outcome: 'fetched'`. A wiring mistake wearing a success.\n //\n // ⇒ So the entry whose only caller crosses that boundary demands a real function\n // rather than defaulting. A Node or browser caller that genuinely wants the global\n // passes `fetch: globalThis.fetch` — one word, and it says so.\n if (typeof fetch !== 'function') {\n throw new Error(\n 'prefetchAndHydrate: `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 const fetched = await prefetchPageData({ content, route, locale, fetch, dev, 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 * ## ⛔ `limit` IS DROPPED, and it is the one place a corpus must diverge\n *\n * A saved query's `limit` is the LIST PAGE's presentation: `limit: 20` means the\n * page shows twenty. **Its detail pages still exist for every record matching\n * `scope` + `where`** — so a corpus that honoured `limit` would index twenty and\n * miss every page beyond them, which is worse than indexing nothing because the\n * gap is invisible.\n *\n * ⚠️ **This shipped wrong in 0.17.0 and was found by the consumer, not by us**\n * (2026-09-06): the config was passed through unchanged, `limit` crossed as the\n * question's own, and the corpus was capped. The claim that this surface met\n * \"the population its detail pages can reach\" was made *\"read charitably\"* — a\n * phrase doing work that one `sed` would have done better.\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'\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`\n * @param {AbortSignal} [options.signal]\n * @param {string[]} [options.only] - restrict to these query names\n * @param {'brief'|'full'} [options.depth='brief'] - what to ask for. `brief` is\n * what a list shows; **`full` is what an index wants** — a brief index cannot\n * 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 is bounded by `maxPages`.\n * @param {number} [options.maxPages] - the caller's own bound on the walk. The\n * default is a bound, not a target; a caller that knows its per-request budget\n * passes its own.\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]` carries\n * `{ depth, pages, partial?, bound? }`.\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 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 // `limit` is the list page's, never the corpus's — see the header.\n const { limit, ...population } = cfg\n const asked = { ...population, 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":["paramName","resolveDefaultLocale","deriveCacheKey","fetch"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/runtime",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.5",
|
|
4
4
|
"description": "Minimal runtime for loading Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=20.19"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@uniweb/core": "^0.24.
|
|
39
|
+
"@uniweb/core": "^0.24.4",
|
|
40
40
|
"@uniweb/theming": "^0.1.15"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"esbuild": "^0.21.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.27.0",
|
|
45
45
|
"vite": "^7.3.1",
|
|
46
46
|
"vitest": "^4.1.7",
|
|
47
|
-
"@uniweb/build": "0.
|
|
47
|
+
"@uniweb/build": "0.44.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": "^19.0.0",
|
package/src/wire-foundation.js
CHANGED
|
@@ -397,7 +397,8 @@ export function wireTracker(uniweb, { basePath = '', loadScripts = null } = {})
|
|
|
397
397
|
// and the other reading takes every site on it dark with every gate saying
|
|
398
398
|
// yes. `?? null` rather than `?? []` is the whole of that guard.
|
|
399
399
|
// ⛔ Each tier is read from ITS OWN key, not from the merged `options`. The
|
|
400
|
-
// merge exists so a site can override a host's `consent
|
|
400
|
+
// merge exists so a site's own option can override a host's — `consent`, say;
|
|
401
|
+
// the address is not an option, and comes from `resolveService` above. These
|
|
401
402
|
// two are not overrides of each other but answers to different questions, and
|
|
402
403
|
// reading either off the merge would let one tier answer the other's — a site
|
|
403
404
|
// writing `events:` would widen past what the host stores, silently.
|