@uniweb/runtime 0.14.1 → 0.14.2

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 CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.js","sources":["../src/prepare-props.js","../../core/src/route-match.js","../../core/src/icon-corpus.js","../src/default-404.js","../src/wire-foundation.js","../src/area-wrappers.js","../src/appearance.js","../src/ssr-renderer.js","../../core/src/data-paths.js","../../core/src/substitute-placeholders.js","../../core/src/query-address.js","../../core/src/fetch-config.js","../../core/src/datastore.js","../../core/src/locale-config.js","../src/default-fetcher.js","../src/prefetch.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-066d.\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 * Dynamic route patterns — the ONE home for how `/blog/:id` matches a path.\n *\n * Why this module exists. The rule was implemented twice and the two copies\n * disagreed. `Website#_matchDynamicRoute` built the pattern with `:(\\w+)`;\n * `generate404Html` in `@uniweb/runtime`'s SSR renderer built it with\n * `:[^/]+` and allowed an optional trailing slash. For `:id` they agree, so\n * nothing failed — but for a param name carrying a non-word character\n * (`/blog/:post-id`) the first matched only `post` and left `-id` as a\n * literal, while the second consumed the whole name. Two answers to one\n * question, neither wrong on the routes anyone had tried.\n *\n * That is already bad inside one repo. It is worse across them: a host that\n * renders a page server-side has to decide *which* page a path names, and the\n * runtime then hydrates over that decision in the browser. If the two matchers\n * disagree by a single route, the server renders page A and hydration replaces\n * it with page B — silently, and only on the paths that have a pattern, which\n * are exactly the interesting ones. So this is a cross-boundary contract, not\n * an implementation detail, and it is exported rather than merely shared.\n *\n * Zero-dependency leaf, like `./data-paths.js` and `./locale-config.js`, so a\n * consumer that must not pull core's graph — an edge worker, a build step —\n * can import the subpath `@uniweb/core/route-match` directly.\n *\n * ## The syntax, in full\n *\n * `:param` is the only construct. There are deliberately **no** catch-alls\n * (`*`), **no** optional segments (`?`), and **no** regex constraints — a\n * pattern is not a regular expression, and regex metacharacters in a route are\n * escaped to literals before any substitution happens. Matching is anchored,\n * case-sensitive, and a param captures exactly one non-empty path segment.\n *\n * ## What this module does NOT decide\n *\n * Matching a pattern means *the route exists*. It says nothing about whether\n * the record behind it exists — that is a data question the caller answers\n * later, and a matched pattern with no backing record is a rendered\n * not-found page rather than a route miss. Anything deciding a 404 purely from\n * this module can only answer the first question.\n */\n\n/**\n * Characters allowed in a param NAME — word characters plus the hyphen, so a\n * `[post-id]` route folder round-trips.\n *\n * Deliberately not `[^/]+`: a greedy name would swallow a literal suffix in the\n * same segment, so `/files/:name.json` would capture `name.json` as the param\n * name and leave nothing to match the extension.\n */\nconst PARAM_NAME = '[A-Za-z0-9_-]+'\n\n/** Regex metacharacters that must survive as literals. `-` is not one of them. */\nconst REGEX_SPECIALS = /[.*+?^${}()|[\\]\\\\]/g\n\n/**\n * Normalize a route for comparison: collapse a trailing slash, treat an empty\n * route as the root.\n *\n * `/about/` and `/about` are the same route; `/` stays `/`.\n *\n * @param {string} route\n * @returns {string}\n */\nexport function normalizeRoute(route) {\n if (typeof route !== 'string' || route === '') return '/'\n return route === '/' ? '/' : route.replace(/\\/+$/, '') || '/'\n}\n\n/**\n * Whether a route is a dynamic template rather than a concrete path.\n *\n * @param {string} route\n * @returns {boolean}\n */\nexport function isDynamicRoute(route) {\n return typeof route === 'string' && route.includes(':')\n}\n\n/**\n * Compile a route pattern to an anchored regex plus its param names.\n *\n * Exported for callers that match one pattern against many paths and want to\n * compile once — an edge worker checking every request against a site's\n * patterns, for instance.\n *\n * @param {string} pattern - e.g. `/blog/:id`\n * @returns {{ regex: RegExp, paramNames: string[] }}\n */\nexport function routePatternToRegex(pattern) {\n const paramNames = []\n const source = normalizeRoute(pattern)\n // Escape first: a `.` in a route is a literal `.`, not \"any character\".\n .replace(REGEX_SPECIALS, '\\\\$&')\n // Then each `:name` becomes one non-empty segment capture.\n .replace(new RegExp(`:(${PARAM_NAME})`, 'g'), (_, name) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n\n return { regex: new RegExp(`^${source}$`), paramNames }\n}\n\n/**\n * Decode a value that arrived from a URL, falling back to the raw input.\n *\n * Guarded rather than bare, for two independent reasons:\n *\n * A `%` that is not an escape is legitimate content — `/100%-Guide` authored by\n * hand, or a value that has already been decoded once — and `decodeURIComponent`\n * throws `URIError` on those. Falling back to the input keeps such a route\n * matching exactly as well as it did before.\n *\n * And the input is attacker-controlled: `/blog/%zz` is a URL anyone can paste or\n * link. This module is called by hosts that resolve a path to a page *per\n * request*, where a throw out of the matcher is a visitor-triggerable 500 rather\n * than a client-side error. A malformed escape is not a reason to lose an\n * otherwise-good match, so the fallback is the raw capture rather than a miss —\n * a route miss would turn a typo'd escape into a 404 on a page that exists.\n *\n * @param {string} value\n * @returns {string}\n */\nexport function decodeRouteValue(value) {\n if (typeof value !== 'string' || !value.includes('%')) return value\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\n/**\n * Match a concrete path against a route pattern.\n *\n * ```js\n * matchDynamicRoute('/blog/:slug', '/blog/my-post') // → { params: { slug: 'my-post' } }\n * matchDynamicRoute('/blog/:slug', '/blog/a/b') // → null (a param is one segment)\n * matchDynamicRoute('/blog/:slug', '/blog/') // → null (a param is non-empty)\n * ```\n *\n * Captured values are decoded, so a path carries percent encoding and the param\n * does not. A malformed escape falls back to the raw capture rather than\n * throwing — see `decodeRouteValue`. This function does not throw.\n *\n * @param {string} pattern - Route pattern with `:param` placeholders\n * @param {string} path - Concrete path to match\n * @returns {{ params: Record<string,string> } | null}\n */\nexport function matchDynamicRoute(pattern, path) {\n const { regex, paramNames } = routePatternToRegex(pattern)\n const match = normalizeRoute(path).match(regex)\n if (!match) return null\n\n const params = {}\n paramNames.forEach((name, i) => {\n params[name] = decodeRouteValue(match[i + 1])\n })\n return { params }\n}\n\n/**\n * Strip a locale prefix from a route.\n *\n * Pages are stored with unprefixed routes — the locale is a URL concern, not\n * part of a page's identity — so a lookup has to remove it first. The default\n * locale carries no prefix, which is why it is a no-op there.\n *\n * `/fr` and `/fr/` both mean the locale's home page.\n *\n * @param {string} route\n * @param {string|null} activeLocale\n * @param {string|null} defaultLocale\n * @returns {string}\n */\nexport function stripLocalePrefix(route, activeLocale, defaultLocale) {\n if (typeof route !== 'string') return '/'\n if (!activeLocale || activeLocale === defaultLocale) return route\n\n const prefix = `/${activeLocale}`\n if (route === prefix || route === `${prefix}/`) return '/'\n if (route.startsWith(`${prefix}/`)) return route.slice(prefix.length)\n return route\n}\n","/**\n * The icon corpus — its default origin and its filename rule.\n *\n * ## Why this is one module and not five constants\n *\n * An icon referenced by `library` + `name` is **our own asset**, not the site's.\n * We publish the families, we document them, and `@uniweb/icons`'\n * `scripts/build-cdn.js` writes the files. So unlike a site asset — whose URL\n * pattern the HOST declares because the bytes are in the host's store — the\n * layout here is ours to name, and a default origin is the correct answer\n * rather than a guessed one.\n *\n * That makes this a **writer/reader pair**, which is the part that needs a\n * single definition:\n *\n * writer @uniweb/icons scripts/build-cdn.js emits cdn/{family}/{family}-{name}.svg\n * readers @uniweb/runtime setup.js browser resolution\n * @uniweb/runtime ssr-renderer.js prerender + Worker isolate prefetch\n * @uniweb/icons src/resolver.js local-then-CDN resolution\n *\n * Before 2026-08-17 the origin was spelled out in three of those and the\n * filename rule in all four. A writer and its readers drifting is the exact\n * defect `@uniweb/core/route-match` exists to prevent, and the one the runtime\n * channel's bridge-filename helper prevents by construction. Same treatment\n * here: one helper, no second spelling.\n *\n * ## ⛔ Keep this a LEAF — zero imports\n *\n * `ssr-renderer.js` is bundled into the SSR isolate that runs in a Cloudflare\n * Worker, so anything it reaches must import nothing: no `node:*`, no DOM, no\n * `@uniweb/core` root (which pulls semantic-parser and theming). That is the\n * same constraint `route-match` and `locale-config` carry, and the reason this\n * lives in core rather than in `@uniweb/icons` — a Worker cannot take a package\n * whose value is ~3,200 icon modules behind a dynamic import, and `@uniweb/runtime`\n * depends on core already.\n *\n * A host may override the ORIGIN — a mirror of this corpus is a legitimate\n * deployment choice, and on a hosted site the base comes from the payload the\n * host serves. It may not override the LAYOUT: a mirror mirrors. Re-deriving\n * filenames instead of copying them is what produced two incompatible spellings\n * of the same corpus once already.\n *\n * @module @uniweb/core/icon-corpus\n */\n\n/**\n * Where the framework publishes its own icon corpus.\n *\n * Not a fallback for a missing host address — it is the address of OUR artifact,\n * and it is what makes `![](lu-house)` work in a project with no backend at all.\n * A host that mirrors the corpus supplies its own origin on the payload.\n */\nexport const DEFAULT_ICON_BASE = 'https://uniweb.github.io/icons'\n\n/**\n * The corpus path for one icon, relative to any origin serving it.\n *\n * `{family}/{family}-{name}.svg` — the family repeats deliberately: the\n * directory groups, and the filename prefix keeps ids unique across families so\n * a name alone is never ambiguous.\n *\n * @param {string} family - short family code (`lu`, `hi2`, `fa6`)\n * @param {string} name - icon id within that family (`house`, `a-arrow-down`)\n * @returns {string} e.g. `lu/lu-house.svg`\n */\nexport function iconPath(family, name) {\n return `${family}/${family}-${name}.svg`\n}\n\n/**\n * The full URL for one icon against a serving origin.\n *\n * @param {string} family - short family code\n * @param {string} name - icon id within that family\n * @param {string} [base] - serving origin; defaults to the framework's own\n * @returns {string}\n */\nexport function iconUrl(family, name, base = DEFAULT_ICON_BASE) {\n return `${String(base).replace(/\\/+$/, '')}/${iconPath(family, name)}`\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\n * production canonical for multi-locale content (the Cloudflare Worker\n * SSR path consumes it directly). Build-time SSG pre-flattens to one\n * file per locale and so falls 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, Cloudflare\n * Worker SSR); the Cloudflare one was using the wrong shape, silently\n * killing prefetched-data reuse in production. This helper is the one\n * canonical 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 website.dataStore.set(deriveCacheKey(entry.config), { 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 Cloudflare 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 cloud SSR (unicloud).\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 { 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 * Convert hex/rgb color to rgba with opacity.\n * Mirrors withOpacity() in Background.jsx.\n */\nfunction withOpacity(color, opacity) {\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) {\n return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n }\n return color\n}\n\n/**\n * Resolve a URL against the site's base path.\n * Mirrors resolveUrl() in Background.jsx.\n */\nfunction resolveUrl(url) {\n if (!url || !url.startsWith('/')) return url\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n if (!basePath) return url\n if (url.startsWith(basePath + '/') || url === basePath) return url\n return basePath + url\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(${resolveUrl(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, Cloudflare Worker SSR) 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 * - Unicloud: (none needed — payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; unicloud 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\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 * Compiled-query paths — the ONE home for the URL and directory convention that\n * the build emits and every fetcher requests.\n *\n * ⚠️ `<name>` IS A QUERY'S NAME. `/data/<name>.json` is a named query's\n * MATERIALIZATION — the answer when no host declares a live lane — and never the\n * definition of anything. The records themselves live in `entities/{schema}/`,\n * and `records.yml` decides which of them are published.\n *\n * Why this module exists. The path `/data/<name>.json` was a bare string\n * literal in six places across three packages: the build wrote it\n * (the query processor), the build resolved the query shorthand to it\n * (`data-fetcher.js`), core injected the per-record default\n * (`fetch-config.js applyDeferredDetail`), core gated locale-prefixing on it\n * (`fetch-config.js localizeConfig`), the dev server matched it with a regex\n * (`build/src/site/plugin.js`), and kit's `useEntityDetail` requested it.\n *\n * They drifted. `useEntityDetail` was edited to `/_data/` on its own and\n * nothing else followed, so a public documented hook requested a URL that\n * nothing anywhere emitted or served — broken on every lane, silently,\n * because it has no call site in this workspace to fail. Emit and request\n * had passing tests the whole time; each pinned its own literal.\n *\n * So the invariant is structural, not documented: producers and consumers\n * read the same constant, and a test asserts they agree. Changing the\n * convention is then one edit here rather than a six-site sweep with a\n * silent-failure trap in it (see `localizeConfig` — a missed site there\n * degrades to default-locale content with a 200, not a 404).\n *\n * WHY THIS PATH IS NOT `_data`. The site's other reserved paths are\n * underscore-prefixed — `_search`, `_pages/`, `_importmap/` — and the\n * inconsistency invites a rename. It has been proposed and declined. Those\n * are machinery: an endpoint and bundler artifacts, which no visitor should\n * land on and which an underscore correctly marks as internal. Compiled\n * this JSON is the opposite — it is the site's own content, the same\n * records an agent that found the site through `llms.txt` may reasonably\n * fetch directly. `/data/articles.json` is a legitimate public address, and\n * `data` is a legitimate page route; neither collides with the other, since\n * pages emit `.html` and queries emit `.json`. Prefixing it would say\n * \"internal\" about something that is not.\n *\n * Zero-dependency leaf, like `./locale-config.js`, so a consumer that must\n * not pull core's graph (semantic-parser, theming) can import the subpath\n * `@uniweb/core/data-paths` directly.\n */\n\n/**\n * The directory segment, for filesystem joins and regex construction.\n * The build writes `<site>/public/<DATA_DIR>/` and copies it to\n * `<dist>/<DATA_DIR>/`.\n */\nexport const DATA_DIR = 'data'\n\n/**\n * The URL prefix every compiled-query request carries. Note the\n * trailing slash: `isDataUrl` is a prefix test, and without it `/database`\n * would match.\n */\nexport const DATA_URL_PREFIX = `/${DATA_DIR}/`\n\n/**\n * URL of a query's cascade payload — every record it returns, with\n * `deferred:` fields stripped when the query declares them.\n *\n * @param {string} name - The query name.\n * @returns {string} e.g. `/data/articles.json`\n */\nexport function queryDataUrl(name) {\n return `${DATA_URL_PREFIX}${name}.json`\n}\n\n/**\n * URL of one record's full payload — every field, including deferred ones.\n * Emitted per record only when the query declares `deferred:`.\n *\n * Takes either a concrete slug (kit's `useEntityDetail`, which holds a\n * record) or the literal placeholder `{slug}` (core's `applyDeferredDetail`,\n * which builds a pattern that `substitutePlaceholders` resolves later\n * against the dynamic-route param). Both are plain interpolation; this\n * function does not encode, matching the behavior of the call sites it\n * replaced.\n *\n * @param {string} query - The query name.\n * @param {string} slug - A record slug, or a `{param}` placeholder.\n * @returns {string} e.g. `/data/articles/design-tips.json`\n */\nexport function recordDataUrl(query, slug) {\n return `${DATA_URL_PREFIX}${query}/${slug}.json`\n}\n\n/**\n * The inverse of `queryDataUrl` — recover a query name from a fetch\n * path so a caller can look it up among the declared queries.\n *\n * Best-effort by design, and the caller decides what a miss means: a path\n * outside the compiled tree is returned with only its `.json`\n * suffix removed, which simply will not match any declared query and\n * lets the caller fall through to reading the file. Nested names round-trip\n * (`/data/archive/2024/posts.json` → `archive/2024/posts`).\n *\n * Lives here because it is the *same* convention read backwards. Left at a\n * call site it becomes a regex with the prefix baked in — which is exactly\n * how `validate-data.js` came to hold a sixth copy of it.\n *\n * @param {string} path - A fetch config's `path`.\n * @returns {string} The derived query name.\n */\nexport function queryNameFromUrl(path) {\n if (typeof path !== 'string') return ''\n // DATA_DIR is a plain identifier segment, so it needs no regex escaping.\n return path.replace(new RegExp(`^/?${DATA_DIR}/`), '').replace(/\\.json$/i, '')\n}\n\n/**\n * Whether a fetch config's `path` addresses compiled query data.\n *\n * Used to scope behavior that only makes sense for build-emitted files —\n * locale prefixing in particular, which must not touch a remote `url:`\n * source or an author-declared `detailUrl:`.\n *\n * @param {*} path - A fetch config's `path` field.\n * @returns {boolean}\n */\nexport function isDataUrl(path) {\n return typeof path === 'string' && path.startsWith(DATA_URL_PREFIX)\n}\n","/**\n * Substitute `{name}` placeholders in strings (or throughout an object tree)\n * using a flat context map. Used in two places:\n *\n * - URL templates for detail queries: `detail: '/articles/{slug}'` gets\n * `slug` resolved from the dynamic-route context. Encoding ON.\n *\n * - POST `body:` objects where a field carries a route-param reference:\n * `body: { variables: { slug: \"{slug}\" } }`. Encoding OFF — values go\n * into JSON as-is.\n *\n * Behavior:\n * - Matches `{name}` where `name` is `[A-Za-z_][A-Za-z0-9_]*`. This keeps\n * the substitution *strict* so literal `{` / `}` elsewhere (notably\n * GraphQL selection sets like `{ field }`) don't accidentally match.\n * A whitespace inside the braces disqualifies the match.\n * - Only keys actually present in `context` substitute. Unknown keys\n * pass through unchanged, preserving the literal `{name}`.\n * - Encoding uses `encodeURIComponent` when `encode: true`.\n * - Object/array recursion is structural; primitives other than strings\n * pass through. Returns a new object tree; input is not mutated.\n */\n\nconst PLACEHOLDER_RE = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g\n\n/**\n * @param {*} value - The tree (string, object, array, primitive) to walk.\n * @param {Record<string, string|number>} context - Name → value map. Missing\n * keys leave the placeholder literal in place.\n * @param {Object} [options]\n * @param {boolean} [options.encode=true] - When true, `encodeURIComponent` the\n * substituted value. Turn off for JSON-body substitution where the value\n * will be serialized by JSON.stringify.\n * @returns {*} New tree with substitutions applied.\n */\nexport function substitutePlaceholders(value, context, options = {}) {\n const { encode = true } = options\n\n if (typeof value === 'string') {\n return value.replace(PLACEHOLDER_RE, (literal, key) => {\n if (!(key in (context || {}))) return literal\n const raw = context[key]\n if (raw === undefined || raw === null) return literal\n return encode ? encodeURIComponent(String(raw)) : String(raw)\n })\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => substitutePlaceholders(item, context, options))\n }\n\n if (value && typeof value === 'object') {\n const result = {}\n for (const key of Object.keys(value)) {\n result[key] = substitutePlaceholders(value[key], context, options)\n }\n return result\n }\n\n return value\n}\n\nexport default substitutePlaceholders\n","/**\n * Resolve a query request to an address the fetcher can call.\n *\n * ## The one idea\n *\n * A site names a query; it never names where its records live. Where\n * it lives is a **deployment** fact, and the two possible answers have different\n * owners:\n *\n * 1. **A host that serves records live** declares a pair of URL *patterns*\n * at `config.records`. It owns every segment of them.\n * 2. **Nobody** — and the answer is the artifact the build itself emitted,\n * `/data/<name>.json`, which is not an address at all but a path in the\n * site's own URL space.\n *\n * Absence of (1) is therefore not an error and not a decline: it falls THROUGH\n * to (2). That is what makes a site with no backend the default rather than a\n * special case, and it is why this does not go through `resolveService` — a\n * service's absence means the site has no such feature and the caller draws\n * nothing, which is right for `submit` and wrong here, where the fallback is a\n * file the build knows it wrote.\n *\n * ## ⛔ Patterns, not a base — and the reason is a deleted function\n *\n * A base assumes the layout is \"root plus one segment\". A pattern assumes\n * nothing, so a host can carry a site id, a locale segment, a different root\n * for records than for the list, or none of those, and move any of it\n * without a framework release.\n *\n * This is the `config.assets.url` rule applied to records. That pattern exists\n * because the CLI once composed `{assetBase}dist/{id}/base.{ext}` — a backend's\n * path layout, inside a published CLI, on a release cadence the backend could\n * not move. It was deleted rather than parameterized. Composing a segment of\n * our own here would rebuild exactly that coupling, on a lane where the wrong\n * answer is *stale or missing content* rather than a visible 404.\n *\n * ⇒ Substituting `{path}` and `{param}` is the WHOLE of what this does.\n *\n * Zero-dependency beyond two sibling leaves, so the SSR pipeline and a Worker\n * isolate can both import it.\n */\n\nimport { substitutePlaceholders } from './substitute-placeholders.js'\n\n/**\n * The placeholder a list pattern must carry.\n *\n * ⛔ IT IS `{path}`, NOT `{query}`, AND THAT IS NOT COSMETIC. A *query* is\n * framework's own build concept — a named set our build compiles to one file. A host\n * serving records has no such thing: it has content organised somewhere, and what we\n * substitute is a **path** to it. Naming the slot for our file vocabulary put that\n * vocabulary into a string a HOST writes, which makes them reason in our shape.\n * See `kb/framework/architecture/backend-boundary.md` §2.\n */\nconst PATH_SLOT = '{path}'\n/** The placeholder a record pattern must carry to address a specific record. */\nconst PARAM_SLOT = '{param}'\n\nconst warnedPatterns = new Set()\n\nfunction warnOnce(key, message) {\n if (warnedPatterns.has(key)) return\n warnedPatterns.add(key)\n console.warn(`[query-address] ${message}`)\n}\n\n/** Test seam — reset the once-per-pattern memo so suites do not leak. */\nexport function _resetQueryAddressWarnings() {\n warnedPatterns.clear()\n}\n\n/**\n * Is this a usable lane declaration?\n *\n * A declaration present with no pattern is a host saying \"not for this site\" —\n * indistinguishable, for a caller, from no declaration at all. Both fall\n * through to the artifact.\n */\nfunction readPattern(lane, key) {\n if (!lane || typeof lane !== 'object' || Array.isArray(lane)) return null\n const pattern = lane[key]\n return typeof pattern === 'string' && pattern.length > 0 ? pattern : null\n}\n\n/**\n * The address for a whole query's records, or `null` to fall through to the artifact.\n *\n * ⚠️ A pattern that does not carry `{path}` is REFUSED rather than used.\n * Substituting nothing would yield one identical URL for every query on the\n * site — every schema reading the same records, with a 200 on each request. That\n * is the failure this check exists for; an unusable pattern must degrade to the\n * artifact, which is at least correct.\n *\n * @param {string} query - the query's authored name (the wiring key).\n * @param {Object|null} lane - `config.records`.\n * @returns {string|null} the address, or null when nothing usable is declared.\n */\nexport function resolveQueryAddress(query, lane) {\n if (typeof query !== 'string' || query.length === 0) return null\n const pattern = readPattern(lane, 'list')\n if (!pattern) return null\n if (!pattern.includes(PATH_SLOT)) {\n warnOnce(\n `list:${pattern}`,\n `config.records.list carries no ${PATH_SLOT} placeholder, so every ` +\n `query would resolve to the same address. Ignoring it and reading the ` +\n `compiled file instead.`\n )\n return null\n }\n return substitutePlaceholders(pattern, { path: query })\n}\n\n/**\n * The address pattern for ONE record of a query, with `{param}` left in\n * place for the dynamic-route substitution that happens later.\n *\n * Returning a pattern rather than a finished URL is deliberate: the route param\n * is not known here, and the framework already has one place that resolves it\n * (`buildDetailConfig` / `substitutePlaceholders` at fetch time). Resolving it\n * twice, in two places, is how the two copies drift.\n *\n * @param {string} query\n * @param {Object|null} lane - `config.records`.\n * @returns {string|null} a pattern still containing `{param}`, or null.\n */\nexport function resolveRecordAddressPattern(query, lane) {\n if (typeof query !== 'string' || query.length === 0) return null\n const pattern = readPattern(lane, 'record')\n if (!pattern) return null\n if (!pattern.includes(PARAM_SLOT)) {\n warnOnce(\n `record:${pattern}`,\n `config.records.record carries no ${PARAM_SLOT} placeholder, so every record ` +\n `would resolve to the same address. Ignoring it and reading the per-record ` +\n `file instead.`\n )\n return null\n }\n // Only `{path}` is substituted here — `{param}` survives for the\n // dynamic-route resolution that owns it.\n return substitutePlaceholders(pattern, { path: query })\n}\n","/**\n * Fetch-config resolution — the shared rule, in one place.\n *\n * \"Which fetch configs apply here?\" is a framework concept. Authors declare\n * `fetch:` at the section, page, folder and site levels; the framework decides\n * which declaration wins per schema, how a local data path is localized, and\n * when a query with deferred fields gets a detail pattern injected.\n *\n * Every host that renders a page needs that answer — the browser runtime, the\n * build-time prerenderer, and any server-side renderer. The rule had grown\n * more than one implementation, and they had diverged in both directions (each\n * carrying a level or a semantic the other lacked). This module is the single\n * definition they call.\n *\n * INTENTIONALLY A LEAF: safe to load anywhere — including environments with no\n * DOM, no filesystem, and a hard bundle-size ceiling. The rule that protects\n * that is **no transitive graph**: import nothing from the package root (which\n * pulls semantic-parser and theming) and nothing that itself imports. A\n * zero-dependency sibling leaf is admissible and `./data-paths.js` is the only\n * one taken — the path convention it holds has to be identical here and in the\n * build that emits the files, and a second copy of that string is precisely\n * the drift this module exists to prevent.\n *\n * WHAT THIS DOES NOT OWN: where the sources come from. A caller holding a live\n * object graph reads them off the graph; a caller holding a content document\n * reads them off the JSON. Both hand the same ordered array to\n * `resolveFetchConfigs`. That difference is real and stays with the caller.\n */\n\nimport { queryDataUrl, isDataUrl, recordDataUrl } from './data-paths.js'\nimport { resolveQueryAddress, resolveRecordAddressPattern } from './query-address.js'\n\n/**\n * Is this fetch declaration a per-instance *refinement* of an ancestor's\n * config rather than a new source of its own?\n *\n * The spelling is `refine: true`. Its earlier alias, `inherit: true`, was\n * accepted with a warning from April 2026 and removed on 2026-09-02: the build\n * refuses it with an error, and `EntityStore` refuses it in dev. This predicate\n * stays silent so it is safe in any environment.\n *\n * @param {Object} cfg - a fetch declaration\n * @returns {boolean}\n */\nexport function isFetchRefinement(cfg) {\n return cfg?.refine === true\n}\n\n/**\n * Localize a fetch config that reads a local data path.\n *\n * Non-default locales get `/{locale}` prefixed onto `/data/` paths so the\n * caller reads the translated JSON (`/fr/data/articles.json`). Configs with no\n * `path` (remote `url:` sources), or paths outside `/data/`, pass through\n * untouched — a remote endpoint's localization is the author's business.\n *\n * @param {Object} cfg\n * @param {string|null} locale - the locale being rendered\n * @param {string|null} defaultLocale - the site's default locale\n * @returns {Object} the original config, or a localized copy\n */\nfunction localizeConfig(cfg, locale, defaultLocale) {\n if (!cfg.path) return cfg\n if (!locale || locale === defaultLocale) return cfg\n if (!isDataUrl(cfg.path)) return cfg\n return { ...cfg, path: `/${locale}${cfg.path}` }\n}\n\n/**\n * Auto-inject `detail:` on a query ref whose query declares\n * `deferred:` fields.\n *\n * A deferred query ships a lean list payload, so the full record has to\n * come from somewhere else. Two patterns, picked by what the query\n * declares:\n *\n * - the query has `detailUrl:` → use it verbatim (a remote source);\n * - otherwise → `/data/<schema>/{slug}.json`, the per-record file emitted\n * alongside the lean list.\n *\n * Conventions carried from the original implementation:\n * - Per-record sources are keyed by `item.slug`, and the injected pattern\n * uses the `{slug}` placeholder. Substitution works when the dynamic\n * route's paramName is `slug` (the documented convention); a route using\n * another param name needs an explicit author-written `detail:`.\n * - Per-record files are not currently localized. A site needing localized\n * a deferred query writes its own `detail:` URL.\n *\n * An author-supplied `cfg.detail` always wins; this only fills the default.\n * With no `queries` map available the config passes through untouched —\n * deferred-detail injection is an enhancement, never a correctness\n * requirement, so a caller that does not have query metadata still gets\n * a usable config. That matters for hosts whose content projection may not\n * carry query metadata at all.\n *\n * @param {Object} cfg\n * @param {Object|null} queries - the site's `config.queries` map\n * @returns {Object} the original config, or a copy carrying `detail`\n */\nfunction applyDeferredDetail(cfg, queries, records) {\n if (cfg.detail !== undefined) return cfg\n\n // ⭐ A lane's record address is injected whenever the lane declares one —\n // NOT only for a `deferred:` query, and the difference is load-bearing.\n //\n // A live lane answers a list request at brief depth and a record request in\n // full, so a detail page that filtered the list would render the brief and\n // silently miss the body. And it cannot fall back to the rule below: the\n // `deferred:` declaration lives in `config.queries`, which a host's\n // projection is not obliged to carry — so on such a host that rule can never\n // fire, and this is the only way a detail page reaches a whole record.\n if (cfg.endpoint) {\n // ⛔ `cfg.query`, not `cfg.query ?? cfg.schema`. The `??` was unreachable:\n // `endpoint` is set in exactly one place (`resolveQuerySource`), which returns\n // early unless `cfg.query` is a non-empty string — so reaching here proves it.\n // It read as a tolerance for two producer shapes and was really a vestige of\n // the build lane not emitting `query`, which it now does.\n const recordPattern = resolveRecordAddressPattern(cfg.query, records)\n if (recordPattern) return { ...cfg, detail: recordPattern }\n }\n\n // ⛔ **`config.queries` is keyed by QUERY NAME, so look it up by the query.**\n // This read `cfg.schema` — the BINDING KEY, which merely defaults to the query\n // name. `fetch: { query: 'articles', schema: 'posts' }` is a supported, allow-\n // listed, unwarned form (`RECOGNIZED_FETCH_KEYS.query`), and under it the lookup\n // missed and a detail page silently rendered the brief without its body.\n // Measured 2026-09-01, control passing: `{query:'articles'}` resolved\n // `/data/articles/{slug}.json`; `{query:'articles',schema:'posts'}` resolved\n // nothing, from the same file.\n //\n // ⚖️ The `|| cfg.schema` is NOT the vestige deleted above. A source-shape fetch\n // (`{ path: … }`) has no query at all, and its schema — inferred from the path —\n // is the only key there is. Two shapes, two answers; the deleted one had one\n // shape and pretended otherwise.\n const queryName = cfg.query || bindingKey(cfg)\n if (!queryName || !queries) return cfg\n const collConfig = queries[queryName]\n if (!collConfig || typeof collConfig !== 'object') return cfg\n const deferred = Array.isArray(collConfig.deferred) ? collConfig.deferred : null\n if (!deferred || deferred.length === 0) return cfg\n const pattern = typeof collConfig.detailUrl === 'string'\n ? collConfig.detailUrl\n : recordDataUrl(queryName, '{slug}')\n return { ...cfg, detail: pattern }\n}\n\n/**\n * Resolve a query reference to something the fetcher can call.\n *\n * ⭐ ONE NAME END TO END. The author writes `query:` in queries.yml, the wire\n * carries `query`, and this reads `query`. It said `collection` on the wire for\n * a while, on the belief that the field was the backend's to name — measured\n * otherwise: `fetch` is a blob they carry, not one they model.\n *\n * The author names a query; this decides where its records live, and there are\n * exactly two answers:\n *\n * - a host declared a live lane (`config.records`) → an `endpoint`, final on\n * arrival, which the fetcher calls without composing anything further;\n * - nobody did → the `path` of the artifact the build emitted.\n *\n * ⭐ The second is not a fallback in the apologetic sense. It is the answer for\n * every site with no backend, which is the framework's default rather than a\n * degraded mode — so an absent lane is silent, not warned.\n *\n * ⭐ `query` OUTRANKS a `path` sitting beside it, which matters because the sync\n * producer emits both — `query` for a consumer that resolves it, `path` as the\n * artifact address for one that cannot. Resolving whenever `query` is present is\n * also what the build-time parser does (`parseFetchConfig` returns early on\n * `query`, ignoring any `path`), so the two agree rather than disagreeing on a\n * shape nobody hand-writes.\n */\nfunction resolveQuerySource(cfg, records) {\n if (typeof cfg.query !== 'string' || cfg.query.length === 0) return cfg\n\n const endpoint = resolveQueryAddress(cfg.query, records)\n if (endpoint) {\n // Drop the transitional `path`: two addresses on one request is an\n // ambiguity the fetcher would have to break by accident of field order.\n const { path, url, ...rest } = cfg\n return { ...rest, endpoint }\n }\n return { ...cfg, path: queryDataUrl(cfg.query) }\n}\n\n/**\n * The binding key of a fetch config — the `content.data.<key>` a component reads.\n *\n * ⭐ **`as` is the name.** It was called `schema` until 2026-09-02, which\n * collided with the MODEL REF of the same name on a `queries` declaration — one\n * word for two things, which is what let a binding-key override silently break\n * detail resolution.\n *\n * ⛔ **The `?? cfg.schema` alias that briefly rode alongside it is GONE**\n * (2026-09-02, ruled by Diego: *\"they are not in prod so I saw no point in it.\n * We need to move forward.\"*). It was removed in the same pass as frontend's and\n * hosting's, and every producer here now emits `as` alone.\n *\n * ⚠️ **The consequence, stated plainly: a payload synced before that carries\n * `schema` and resolves to NOTHING here.** No data, no error — this is the\n * silent class, and the remedy is a re-push, not a code change. If a\n * seed or a dev site renders a section empty, check what its stored payload\n * spells before looking anywhere else.\n *\n * ⭐ The one place `schema` is still read is `parseFetchConfig` in\n * `@uniweb/build`, and it is a different thing: normalizing an AUTHOR's older\n * spelling in a content file at the boundary, so that one name travels inside.\n *\n * @param {Object} cfg\n * @returns {string|undefined}\n */\nfunction bindingKey(cfg) {\n return cfg?.as\n}\n\n/**\n * Resolve the applicable fetch configs from an ordered list of sources.\n *\n * The rule: walk the sources in precedence order and take the FIRST match per\n * schema. Sources are the framework's cascade, most specific first — typically\n * section → page → parent page → site. A source may be a single config or an\n * array of them; arrays are walked in order.\n *\n * First-match-per-schema (rather than first-match-wins-outright) is what lets\n * a page needing two schemas inherit one from the site and declare the other\n * itself. Collapsing that to a single winner is a real behavior change, not a\n * simplification.\n *\n * @param {Array<Object|Array<Object>>} sources - ordered, most specific first.\n * Falsy entries are skipped, so callers can pass optional levels directly.\n * @param {Object} [options]\n * @param {string[]} [options.schemas] - restrict to these schema names.\n * Empty (the default) collects every schema found.\n * @param {string|null} [options.locale] - the locale being rendered\n * @param {string|null} [options.defaultLocale] - the site's default locale\n * @param {Object|null} [options.queries] - the site's `config.queries`\n * @param {Object|null} [options.records] - the site's `config.records`, a host's\n * live-records lane. Absent means the compiled artifact answers, which is\n * the whole of what a site with no backend needs.\n * @returns {Map<string, Object>} schema name → resolved config\n */\nexport function resolveFetchConfigs(sources, options = {}) {\n const {\n schemas = [],\n locale = null,\n defaultLocale = null,\n queries = null,\n records = null,\n } = options\n\n const configs = new Map()\n const collectAll = schemas.length === 0\n\n for (const source of sources) {\n if (!source) continue\n const configList = Array.isArray(source) ? source : [source]\n for (const cfg of configList) {\n const key = bindingKey(cfg)\n if (!key) continue\n if (configs.has(key)) continue\n if (!collectAll && !schemas.includes(key)) continue\n // Address first: localization and deferred-detail both key on `path`,\n // which a query ref does not have until this runs.\n const sourced = resolveQuerySource(cfg, records)\n const localized = localizeConfig(sourced, locale, defaultLocale)\n configs.set(key, applyDeferredDetail(localized, queries, records))\n }\n }\n\n return configs\n}\n","/**\n * DataStore\n *\n * Pure keyed cache with in-flight deduplication. Persists across SPA navigation.\n *\n * Owned by the Website; accessed only by the FetcherDispatcher (which computes\n * cache keys and runs fetchers) and by build-time / startup preload paths\n * (which write entries keyed by the default cache key so runtime cache probes\n * find them).\n *\n * No knowledge of fetchers, transports, or cascades. Keys are opaque strings.\n */\n\n/**\n * Default cache-key derivation for a request or fetch config.\n *\n * The framework's default URL fetcher and the build-time preload path both\n * use this key shape. Fetchers with state-dependent requests (e.g., a query\n * slug read from `page.state`) must declare their own `cacheKey(request)`\n * on the fetcher so reactive changes miss the cache and re-fetch.\n *\n * Fields that contribute to the key:\n * - path, url — what resource is being fetched\n * - schema — which entity type the response will be stored under\n * - transform — any per-fetch response unwrap; different transforms\n * of the same endpoint produce different cached data\n * - method (POST) — POST requests may share a URL with GET; don't collide\n * - body (POST) — two POSTs to the same URL with different bodies are\n * different queries; must cache distinctly\n *\n * Post-processing fields like `limit`, `sort`, `filter` are applied after\n * fetch and must not split the cache.\n *\n * @param {Object} request - Normalized request (or fetch config)\n * @returns {string} A stable JSON string usable as a cache-Map key\n */\nexport function deriveCacheKey(request) {\n // ⭐ `as` is the binding key — the name it has had since 2026-09-02, when the\n // compatibility alias for the older `schema` spelling was removed alongside\n // frontend's and hosting's.\n const { path, url, endpoint, transform } = request || {}\n const as = request?.as\n const method = request?.method && request.method.toUpperCase() !== 'GET'\n ? request.method.toUpperCase()\n : undefined\n const body = method === 'POST' ? request?.body : undefined\n // ⚠️ The field NAME is part of the hash, so renaming it moves every key ONCE.\n // In-memory stores repopulate; a consumer with a persistent cache takes one\n // cold pass. Chosen over hashing under the old name, which would have hidden\n // the rename inside the one function whose job is to be canonical.\n return JSON.stringify({ path, url, endpoint, as, transform, method, body })\n}\n\nexport default class DataStore {\n constructor() {\n // key → { data, meta? }\n this._cache = new Map()\n // key → { promise, signals: Set<AbortSignal> }\n this._inflight = new Map()\n // Notified on every successful `set()`.\n this._listeners = new Set()\n // Key-scoped listeners: key → Set<Function>\n this._keyedListeners = new Map()\n\n Object.seal(this)\n }\n\n /**\n * Subscribe to cache updates.\n *\n * Two forms:\n * - `subscribe(fn)` — fires after every successful `set()` (all keys).\n * - `subscribe(key, fn)` — fires only when `set(key, ...)` or `delete(key)` is called.\n *\n * The global form is useful for debugging / blanket observers. The keyed\n * form is what Layer-3 kit hooks (`useFetched`, `useCacheEntry`) use so\n * a cache write for one request doesn't wake up every subscriber.\n *\n * @param {string|Function} keyOrFn\n * @param {Function} [maybeFn]\n * @returns {Function} unsubscribe\n */\n subscribe(keyOrFn, maybeFn) {\n if (typeof keyOrFn === 'string' && typeof maybeFn === 'function') {\n const key = keyOrFn\n let set = this._keyedListeners.get(key)\n if (!set) {\n set = new Set()\n this._keyedListeners.set(key, set)\n }\n set.add(maybeFn)\n return () => {\n const s = this._keyedListeners.get(key)\n if (!s) return\n s.delete(maybeFn)\n if (s.size === 0) this._keyedListeners.delete(key)\n }\n }\n if (typeof keyOrFn === 'function') {\n this._listeners.add(keyOrFn)\n return () => this._listeners.delete(keyOrFn)\n }\n throw new TypeError('DataStore.subscribe: expected (fn) or (key, fn)')\n }\n\n /**\n * Cache presence check.\n *\n * @param {string} key\n * @returns {boolean}\n */\n has(key) {\n return this._cache.has(key)\n }\n\n /**\n * Cache lookup.\n *\n * @param {string} key\n * @returns {{ data: any, meta?: Object } | null}\n */\n get(key) {\n return this._cache.has(key) ? this._cache.get(key) : null\n }\n\n /**\n * Cache store. Fires listeners: first the global ones (all-writes), then\n * any subscribers registered for this specific key.\n *\n * @param {string} key\n * @param {{ data: any, meta?: Object }} entry\n */\n set(key, entry) {\n this._cache.set(key, entry)\n for (const fn of this._listeners) fn()\n const keyed = this._keyedListeners.get(key)\n if (keyed) {\n for (const fn of keyed) fn()\n }\n }\n\n /**\n * Drop one entry, and any in-flight record for the same key.\n *\n * Fires the key's subscribers (and the global ones) so an observer re-reads\n * and sees the absence, exactly as it would see a write. Exists for the case\n * `clear()` is too blunt for: one consumer's entries must leave memory — a\n * viewer's records at sign-out — while everything else stays warm.\n *\n * @param {string} key\n * @returns {boolean} true if an entry was removed\n */\n delete(key) {\n const had = this._cache.delete(key)\n this._inflight.delete(key)\n if (!had) return false\n for (const fn of this._listeners) fn()\n const keyed = this._keyedListeners.get(key)\n if (keyed) {\n for (const fn of keyed) fn()\n }\n return true\n }\n\n /**\n * In-flight fetch registry — used by the dispatcher to dedup concurrent\n * requests and collect abort signals so the underlying fetch is cancelled\n * only when every attached block aborts.\n *\n * @returns {Map<string, { promise: Promise, signals: Set<AbortSignal> }>}\n */\n get inflight() {\n return this._inflight\n }\n\n /**\n * Flush cache and in-flight map. Listeners are preserved so subscribers\n * that outlive the cache (kit hooks waiting on a key) aren't orphaned.\n */\n clear() {\n this._cache.clear()\n this._inflight.clear()\n }\n}\n","/**\n * Shared locale-config helpers — the ONE home for the language rules that\n * build, sync, runtime, and the CLI all apply to a site's config.\n *\n * Contract (\"Per-locale publish readiness\"):\n * - `languages` (site.yml) / `info.languages` (wire) — the DECLARED working\n * set. A plain, strongly-validated string list.\n * - `publishLanguages` (site.yml) / `info.publish_languages` (wire) — publish\n * intent. Publishable = intersection with declared; absent field = all\n * declared publishable; present-but-empty = nothing publishable; dangling\n * codes (listed but not declared) are benign — warn, ignore in the\n * intersection, round-trip verbatim.\n * - Effective default locale = `defaultLanguage || languages[0] || 'en'` —\n * one rule everywhere. (Historically half the call sites skipped the\n * `languages[0]` step; this module exists so that can't drift again.)\n */\n\n/**\n * Extract a locale code from a declared-language entry. The contract is\n * strings-only; legacy `{ code, label }` objects are tolerated on read\n * (they appeared in older configs and the runtime's buildLocalesList\n * accepted them) but are never produced. The `'*'` wildcard marker\n * (auto-discover from `locales/`) is not a locale code.\n *\n * @param {*} entry - Declared-language entry.\n * @returns {string|null} The locale code, or null when unusable.\n */\nfunction codeOf(entry) {\n if (typeof entry === 'string' && entry.trim() && entry.trim() !== '*') return entry.trim()\n if (entry && typeof entry === 'object' && typeof entry.code === 'string' && entry.code.trim()) {\n return entry.code.trim()\n }\n return null\n}\n\n/**\n * Whether `languages` uses the auto-discover wildcard (`'*'`, or an array\n * containing it). The declared set is then filesystem-derived and unknown\n * to these pure helpers.\n *\n * @param {*} value - The authored `languages` value.\n * @returns {boolean}\n */\n/**\n * Human-readable display names for the locales a site is likely to declare.\n *\n * This lives in core, not kit, because core is what BUILDS the locale objects a\n * foundation reads (`website.getLocales()`, `website.langs`). It used to live\n * only in kit, so core could not resolve a label and left the field absent for\n * a plain-string `languages:` entry — which meant the very form this module\n * tells authors to migrate TO produced worse labels than the legacy object form\n * it warns about. Measured 2026-08-23 on a site with 11 locales: dropping the\n * `label:` keys silently turned \"Français\" into \"fr\" in the switcher, because\n * the common foundation idiom is `locale.label || locale.code`.\n *\n * Kit re-exports this so `@uniweb/kit`'s `LOCALE_DISPLAY_NAMES` keeps working.\n */\nexport const LOCALE_DISPLAY_NAMES = {\n en: 'English',\n es: 'Español',\n fr: 'Français',\n de: 'Deutsch',\n it: 'Italiano',\n pt: 'Português',\n nl: 'Nederlands',\n pl: 'Polski',\n ru: 'Русский',\n ja: '日本語',\n ko: '한국어',\n zh: '中文',\n 'zh-CN': '简体中文',\n 'zh-TW': '繁體中文',\n ar: 'العربية',\n he: 'עברית',\n hi: 'हिन्दी',\n th: 'ไทย',\n vi: 'Tiếng Việt',\n tr: 'Türkçe',\n uk: 'Українська',\n cs: 'Čeština',\n el: 'Ελληνικά',\n hu: 'Magyar',\n ro: 'Română',\n sv: 'Svenska',\n da: 'Dansk',\n fi: 'Suomi',\n no: 'Norsk',\n id: 'Bahasa Indonesia',\n ms: 'Bahasa Melayu'\n}\n\n/**\n * Resolve a locale's display label.\n *\n * Priority: an explicitly configured `label` -> the display-name table -> the\n * code uppercased. Accepts either a bare code string or a `{ code, label? }`\n * object, so callers do not have to branch on which form the config used.\n *\n * @param {string|{code: string, label?: string}} entry\n * @returns {string} Display label, or '' when the entry carries no code.\n */\nexport function localeLabel(entry) {\n if (typeof entry === 'string') {\n return LOCALE_DISPLAY_NAMES[entry] || entry.toUpperCase()\n }\n if (!entry || typeof entry.code !== 'string' || !entry.code) return ''\n return entry.label || LOCALE_DISPLAY_NAMES[entry.code] || entry.code.toUpperCase()\n}\n\nexport function isWildcardLanguages(value) {\n return value === '*' || (Array.isArray(value) && value.includes('*'))\n}\n\n/**\n * Normalize a language list to validated string codes: invalid entries\n * dropped, duplicates deduped, order preserved.\n *\n * @param {*} value - The authored list (anything; non-arrays yield []).\n * @returns {string[]} Clean locale codes.\n */\nexport function normalizeLanguageList(value) {\n if (!Array.isArray(value)) return []\n const seen = new Set()\n const codes = []\n for (const entry of value) {\n const code = codeOf(entry)\n if (!code || seen.has(code)) continue\n seen.add(code)\n codes.push(code)\n }\n return codes\n}\n\n/**\n * The effective default locale: `defaultLanguage || languages[0] || 'en'`.\n * Works on authored site.yml, built site-content config, and served payload\n * config alike (all carry the same camelCase keys).\n *\n * @param {Object} [config] - Site config (`{ defaultLanguage?, languages? }`).\n * @returns {string} The effective default locale code.\n */\nexport function resolveDefaultLocale(config = {}) {\n if (typeof config?.defaultLanguage === 'string' && config.defaultLanguage.trim()) {\n return config.defaultLanguage.trim()\n }\n return normalizeLanguageList(config?.languages)[0] || 'en'\n}\n\n/**\n * The publishable set: `publishLanguages ∩ languages`, in declared order.\n *\n * - Absent `publishLanguages` → all declared publishable (`explicit: false`).\n * - Present (even empty) → the intersection (`explicit: true`); an authored\n * empty list means nothing publishable — NOT treated as absent.\n * - Dangling codes are returned for the caller to warn about; they are never\n * silently pruned from the authored/stored list (the verbatim round-trip is\n * what preserves publish intent across a remove + re-add in `languages`).\n *\n * With wildcard `languages` (`'*'`), the declared set is unknown here\n * (filesystem-derived), so the intersection with \"all\" is the publish list\n * itself and dangling codes cannot exist.\n *\n * @param {Object} [config] - Site config (`{ languages?, publishLanguages? }`).\n * @returns {{ publishable: string[], dangling: string[], explicit: boolean }}\n */\nexport function resolvePublishableLocales(config = {}) {\n const declared = normalizeLanguageList(config?.languages)\n const raw = config?.publishLanguages\n if (raw == null) return { publishable: declared, dangling: [], explicit: false }\n const listed = normalizeLanguageList(raw)\n if (isWildcardLanguages(config?.languages)) {\n return { publishable: listed, dangling: [], explicit: true }\n }\n const declaredSet = new Set(declared)\n const listedSet = new Set(listed)\n return {\n publishable: declared.filter((code) => listedSet.has(code)),\n dangling: listed.filter((code) => !declaredSet.has(code)),\n explicit: true\n }\n}\n\n/**\n * Validate a site's language configuration against the contract. Pure — the\n * caller decides how to surface results (build warnings, publish hard error).\n *\n * Errors (producers hard-error at build/deploy/push):\n * - `nothing-publishable` — an explicit publish list intersects declared to ∅.\n * - `default-not-publishable` — the effective default is excluded from the\n * publishable set.\n *\n * Warnings:\n * - `invalid-language-entry` / `invalid-publish-language-entry` — non-string\n * entries (dropped by normalization).\n * - `duplicate-language` — repeated codes (deduped).\n * - `dangling-publish-language` — listed but not declared (ignored at\n * publish, preserved in the file/wire).\n *\n * @param {Object} [config] - Site config.\n * @returns {{ errors: {code: string, message: string}[],\n * warnings: {code: string, message: string}[] }}\n */\nexport function validateLanguageConfig(config = {}) {\n const errors = []\n const warnings = []\n\n const inspectList = (value, field, entryCode) => {\n if (value == null) return\n if (value === '*') return // auto-discover wildcard (languages only)\n if (!Array.isArray(value)) {\n warnings.push({\n code: entryCode,\n message: `${field} must be a list of locale codes (got ${typeof value}) — ignored`\n })\n return\n }\n const seen = new Set()\n for (const entry of value) {\n if (entry === '*') continue // auto-discover wildcard marker, not a code\n const code = codeOf(entry)\n if (!code) {\n warnings.push({\n code: entryCode,\n message: `${field} entry ${JSON.stringify(entry)} is not a locale code string — dropped`\n })\n continue\n }\n if (typeof entry !== 'string') {\n warnings.push({\n code: entryCode,\n message: `${field} entry for '${code}' uses the legacy object form — use the plain string '${code}'`\n })\n }\n if (seen.has(code)) {\n warnings.push({ code: 'duplicate-language', message: `${field} lists '${code}' more than once — deduped` })\n }\n seen.add(code)\n }\n }\n\n inspectList(config?.languages, 'languages', 'invalid-language-entry')\n inspectList(config?.publishLanguages, 'publishLanguages', 'invalid-publish-language-entry')\n\n const { publishable, dangling, explicit } = resolvePublishableLocales(config)\n for (const code of dangling) {\n warnings.push({\n code: 'dangling-publish-language',\n message: `publishLanguages lists '${code}' but languages does not declare it — ignored at publish (kept in the file so a re-declared language keeps its publish intent)`\n })\n }\n\n if (explicit && publishable.length === 0) {\n errors.push({\n code: 'nothing-publishable',\n message: 'publishLanguages leaves no publishable language (empty list, or nothing it lists is declared) — a publishable default language is required'\n })\n } else if (explicit) {\n const defaultLocale = resolveDefaultLocale(config)\n if (!publishable.includes(defaultLocale)) {\n errors.push({\n code: 'default-not-publishable',\n message: `the default language '${defaultLocale}' is not in publishLanguages — the effective default must be publishable`\n })\n }\n }\n\n return { errors, warnings }\n}\n","/**\n * Runtime default fetcher.\n *\n * Used as the FetcherDispatcher's terminal fallback when no foundation\n * route and no foundation fallback match. Sites that declare no fetcher\n * at all — starter/docs/marketing templates hitting /data/*.json — ride\n * on this path with zero config.\n *\n * The fetcher recognizes a general-purpose vocabulary under `site.yml fetcher:`\n * so sites with a real backend don't need a foundation just to add a base URL\n * or static headers:\n *\n * fetcher:\n * baseUrl: https://api.example.com\n * headers:\n * X-Tenant: acme\n * Accept: application/vnd.example+json\n * envelope:\n * list: data.items\n * item: data.article\n * error: errors.0.message\n *\n * Per-fetch, the request may carry `method: 'POST'` + `body:` for backends\n * that take queries in a body (GraphQL, search endpoints). `{paramName}`\n * placeholders in body strings are substituted from `request.dynamicContext`\n * so template-page detail queries can reference route params.\n *\n * Every key is optional. When the config is empty, behavior is byte-for-byte\n * identical to a plain `fetch()` with JSON parsing.\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 fetcher; the\n * runtime installs this one automatically. A foundation that needs\n * auth / retry / response normalization declares a named transport\n * and composes `@uniweb/fetchers` middleware around its own `resolve()`.\n *\n * There is intentionally no \"reuse the default and wrap it\" path for\n * foundations — doing so would duplicate this code into every foundation\n * bundle. The subpath export exists specifically for preview-mode shells\n * that need to delegate *non-authenticated* requests to a default-fetcher\n * instance while intercepting authenticated ones via their own transport.\n *\n * Intentional omissions: credentials / secrets are NOT part of the vocabulary.\n * Any value the framework puts into the served HTML is public to the browser.\n * Sites needing private credentials use a deployment-layer proxy — the site\n * fetches a same-origin URL, and a layer in front (e.g. the Uniweb platform's\n * edge worker, or any custom backend) resolves the credential and forwards\n * upstream. Framework sees a plain URL; platform owns the secret.\n *\n * `headers:` IS supported because static per-site headers (tenant routing,\n * content-type negotiation, custom Accept values) aren't credentials and\n * aren't anything sites try to hide. Sites that accidentally put a secret\n * in `headers:` have the same problem they'd have hardcoding it in the URL:\n * it's public. That's not a framework feature gap; it's how browsers work.\n */\n\nimport {\n substitutePlaceholders,\n matchWhere,\n deriveCacheKey,\n resolveRequestStyle,\n resolveServiceUrl,\n} from '@uniweb/core'\n\n// The request style is the wire dialect operators are encoded in. One\n// ships — json-body, the framework's own — and `resolveRequestStyle` is\n// loud on any other name: it throws in dev and logs once in production.\n// Another dialect is a named transport, from the foundation or from an\n// extension the site selects; it is never a second built-in style.\n\n// Operators the default fetcher knows how to handle. When listed in\n// `config.supports`, they're shipped to the source as part of the\n// request; when not listed, they're applied as a JS fallback after\n// fetch. The cache key reflects which operators get pushed down — same\n// query against different `supports:` produces different cache entries.\nconst KNOWN_OPERATORS = new Set(['where', 'limit', 'sort'])\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 {Object} [options.config={}] - Site-level fetcher config from\n * `site.yml fetcher:`. Vocabulary recognized by the default fetcher:\n * `baseUrl`, `headers`, `envelope`, `supports`, `request.style`,\n * `request.rename`. Unknown keys are ignored (foundations may use the\n * same block for their own keys). Default behavior (empty config)\n * matches today's plain GET + JSON.\n * @param {boolean} [options.dev=false] - Enable dev-mode diagnostics: an\n * unknown request style throws; a rename entry for an operator the wire\n * does not carry warns.\n * @returns {{ resolve: (req: Object, ctx: Object) => Promise<{ data, error? }> }}\n */\nexport function createDefaultFetcher({ basePath = '', config = {}, dev = false, records = null, fetch: fetchImpl = null } = {}) {\n // The transport is injectable: a host executing fetches outside a browser (an SSR isolate)\n // decides how a site-relative address such as `/_records/members` is dispatched — through its\n // own origin or a service binding — and hands that in. Defaults to the global `fetch`, resolved\n // at call time so a test stub installed later is honoured.\n const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init)\n const pathPrefix = basePath && basePath !== '/' ? basePath.replace(/\\/$/, '') : ''\n\n const baseUrl = typeof config?.baseUrl === 'string'\n ? config.baseUrl.replace(/\\/$/, '')\n : ''\n\n // Static headers merged into every remote request. Local `/data/*.json`\n // requests are never decorated — they're just file reads under public/.\n const staticHeaders = buildStaticHeaders(config?.headers)\n\n // `supports:` declares which query operators (where, limit, sort) the\n // backend evaluates at the source. Operators in this list are shipped\n // in the request; operators not in this list are applied as a JS\n // fallback after the response arrives. Default: empty — the framework\n // default fetcher serving static files supports nothing natively.\n const supports = normalizeSupports(config?.supports)\n\n // Request style — the wire dialect operators are encoded in. Read from\n // `site.yml fetcher.request.style`; `null`/absent and `json-body` both\n // resolve to the one shipped style, and any other name is loud (see\n // `resolveRequestStyle`).\n const requestConfig = (config?.request && typeof config.request === 'object') ? config.request : {}\n const styleName = typeof requestConfig.style === 'string' ? requestConfig.style : null\n const style = resolveRequestStyle(styleName, { dev })\n\n // Operator-name renames applied on top of the style's wire names.\n // Shallow: only the operator keys (where / limit / sort) are rewritten.\n // Field names inside a where-object are untouched.\n const rename = normalizeRename(requestConfig.rename, style, { dev })\n\n // `envelope:` extends today's `transform:` to cover detail responses and\n // errors. Three dot-paths, all optional:\n // - envelope.list — applied on list responses. Per-fetch\n // `transform:` on the request wins (per-fetch overrides site-level).\n // - envelope.item — applied when request.dynamicContext is set\n // (the request is for a template-page item).\n // - envelope.error — extract error text from non-2xx response body.\n //\n // Priority (highest wins): per-fetch request.envelope > site-level\n // config.envelope > style.defaultEnvelope. json-body declares no\n // envelope; the slot is the encoder's to fill, and a site-level value\n // always wins over it.\n const siteEnvelope = (config?.envelope && typeof config.envelope === 'object')\n ? config.envelope\n : null\n const envelope = { ...(style.defaultEnvelope || {}), ...(siteEnvelope || {}) }\n\n // ⭐ The LIVE LANE's envelope is the backend's. `config.records` is stamped by the backend\n // that answers a records request, so where the array sits in ITS response is its to\n // declare: `records.envelope.records` — the KEY says what it holds, the VALUE is the JSON\n // key the array sits under (`{ records: \"entries\" }` ⇒ body.entries). That spelling is the\n // agreed one (2026-08-30: `collection` retired; ⛔ not `list`, which is a URL pattern on the\n // same stamp). It applies only to a request that resolved to that lane (`endpoint` set)\n // and wins over the site's own `fetcher.envelope`, which describes the author's backend.\n // Ruled 2026-09-03 [Diego]: the backend sets `config.records`; the fetch comes from the\n // runtime. Until this line the runtime resolved `list`/`record` off the stamp and ignored\n // its envelope.\n const stampedArrayKey = (records?.envelope && typeof records.envelope === 'object'\n && typeof records.envelope.records === 'string' && records.envelope.records.length)\n ? records.envelope.records\n : null\n const laneEnvelope = stampedArrayKey ? { list: stampedArrayKey } : null\n\n return {\n /**\n * Cache-key function. The default-fetcher's cache key includes only\n * the operators it pushes down (because they affect what the source\n * sees). Operators applied as runtime fallback operate on a shared\n * cached value and therefore must NOT split the cache.\n *\n * Example: with `supports: []`, two pages declaring different\n * `where:` clauses against the same path share one cache entry —\n * the file is fetched once and each page filters its own copy. With\n * `supports: [where]`, the same two pages fire two requests because\n * the predicate travels in the request.\n */\n cacheKey(request) {\n // Build a request projection that includes only operators the active\n // style will actually push for this request. deriveCacheKey already\n // covers the always-keyed fields.\n //\n // The key also carries the style name. With one shipped style it is\n // a constant segment, kept so that key shapes do not move.\n const base = deriveCacheKey(request)\n const projected = {}\n for (const op of supports) {\n if (!style.canPush.has(op)) continue\n if (request[op] !== undefined) projected[op] = request[op]\n }\n if (Object.keys(projected).length === 0 && style.name === 'json-body') {\n // Keep back-compat key shape when the ambient default pushes nothing.\n return base\n }\n return base + '::style=' + style.name + '::' + JSON.stringify(projected)\n },\n\n async resolve(request, ctx = {}) {\n if (!request) return { data: null }\n const { path, url, endpoint, 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 let isRemote\n if (endpoint) {\n // A host-declared collection lane, resolved upstream from the pattern\n // it published (`@uniweb/core/query-address`). FINAL ON ARRIVAL:\n //\n // - `baseUrl` is NOT joined. That knob points a site at ITS OWN\n // backend; prepending it to an address a host composed would\n // corrupt exactly the layout the pattern exists to let them own.\n // - the site `base` IS applied to a rooted address, the same rule\n // every other site-relative address follows — shared with\n // `resolveServiceUrl` rather than spelled a second time here.\n //\n // Remote semantics otherwise: this answers a QUERY, so operator\n // pushdown and static headers both apply, which is what separates it\n // from `path` (a static file that can neither filter nor sort).\n target = resolveServiceUrl(endpoint, pathPrefix)\n isRemote = true\n } else 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 isRemote = false\n } else if (url) {\n // Remote URL — baseUrl applies when url is relative (no scheme,\n // not protocol-relative). Absolute or protocol-relative pass through.\n target = isAbsoluteUrl(url) ? url : joinUrl(baseUrl, url)\n isRemote = true\n } else {\n return { data: [], error: 'No path, url or endpoint specified' }\n }\n\n const init = { signal: ctx.signal, method }\n const headers = {}\n\n // Static site-level headers go on remote requests only — we don't\n // decorate local file reads with tenant/content-type headers.\n if (isRemote && staticHeaders) Object.assign(headers, staticHeaders)\n\n // Push down supported query operators to the source via the active\n // request style. Pushdown only applies to remote URLs — local `path:`\n // reads are static files that can't filter or sort. Operators the\n // style didn't push get applied as a JS fallback after the response\n // (see the post-fetch block below).\n //\n // The style owns the wire format: json-body encodes GET pushdown as\n // `?_where=<JSON>&_limit=&_sort=` and POST pushdown as top-level keys\n // merged into an object body. It is the only shipped wire — another\n // dialect is a named transport.\n const pushCandidates = new Set()\n if (isRemote) {\n for (const op of KNOWN_OPERATORS) {\n if (\n supports.has(op) &&\n style.canPush.has(op) &&\n request[op] !== undefined &&\n request[op] !== null\n ) {\n pushCandidates.add(op)\n }\n }\n }\n\n const encoded = pushCandidates.size > 0\n ? style.encode(request, { method, pushCandidates, rename })\n : { queryParams: [], bodyMerge: null, pushed: new Set() }\n const pushedOperators = encoded.pushed\n\n if (encoded.queryParams.length > 0 && method === 'GET') {\n target = appendStyleQueryParams(target, encoded.queryParams)\n }\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 resolvedBody = (rawBody !== undefined && rawBody !== null && dc && dc.paramName)\n ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false })\n : rawBody\n\n // Compose the final body: author-supplied body merged with pushed\n // operators from the style. When no body and no pushdown, send\n // a body containing just the pushed operators if any exist.\n const finalBody = composePostBody(resolvedBody, encoded.bodyMerge)\n\n if (finalBody !== null) {\n // Default Content-Type to JSON unless the site's static headers\n // already set one (for application/graphql or form-urlencoded).\n if (!hasHeader(headers, 'Content-Type')) {\n headers['Content-Type'] = 'application/json'\n }\n init.body = typeof finalBody === 'string' ? finalBody : JSON.stringify(finalBody)\n }\n }\n\n if (Object.keys(headers).length) init.headers = headers\n\n try {\n const response = await doFetch(target, init)\n\n // Per-request envelope (set by object-form `detail:`) wins over\n // site-level envelope. This lets a detail query declare its own\n // item/collection/error paths independently of the collection.\n const requestEnvelope = (request.envelope && typeof request.envelope === 'object')\n ? request.envelope\n : null\n const effectiveEnvelope = requestEnvelope\n ?? (endpoint && laneEnvelope ? { ...envelope, ...laneEnvelope } : envelope)\n\n if (!response.ok) {\n // If `envelope.error` is configured, 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 (effectiveEnvelope.error) {\n try {\n const text = await response.text()\n const body = safeParseJSON(text)\n if (body !== undefined) {\n const candidate = getNestedValue(body, effectiveEnvelope.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 response envelope. Priority order, highest wins:\n // 1. Per-fetch `transform:` (existing, documented knob).\n // 2. Per-request `envelope.item` (detail) or `envelope.list`.\n // 3. Site-level `envelope.item` (detail) or `envelope.list`.\n const isDetailRequest = !!request.dynamicContext\n const effectiveTransform =\n transform\n || (isDetailRequest ? effectiveEnvelope.item : effectiveEnvelope.list)\n if (effectiveTransform && data !== null && data !== undefined) {\n data = getNestedValue(data, effectiveTransform)\n }\n\n // Apply runtime fallback for query operators not pushed down.\n // Only applies to array data (filtering/sorting/limiting a single\n // record doesn't make sense). For non-arrays, operators are\n // silently ignored — the source returned what it returned.\n data = applyFallbackOperators(data, request, pushedOperators)\n\n return { data: data ?? [] }\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 * Normalize the `request.rename` map. Returns null if nothing valid.\n * Dev-mode warns on operator names that don't exist in the style's\n * `canPush` set — a rename that targets an operator the style doesn't\n * push is silently dead config, and the warning surfaces the mistake.\n */\nfunction normalizeRename(raw, style, { dev }) {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null\n const out = {}\n for (const [op, wireName] of Object.entries(raw)) {\n if (typeof wireName !== 'string' || wireName.length === 0) continue\n if (dev && !style.canPush.has(op) && !warnedRenameTargets.has(op)) {\n warnedRenameTargets.add(op)\n console.warn(\n `[default-fetcher] request.rename: operator \"${op}\" is not pushed by ` +\n `style \"${style.name}\" — rename has no effect. Known operators for ` +\n `this style: ${[...style.canPush].join(', ') || '(none)'}.`,\n )\n }\n out[op] = wireName\n }\n return Object.keys(out).length ? out : null\n}\nconst warnedRenameTargets = new Set()\n\n/**\n * Normalize the supports declaration to a Set of known operators. Unknown\n * operator names are ignored with a one-time dev warning.\n */\nfunction normalizeSupports(raw) {\n const out = new Set()\n if (!Array.isArray(raw)) return out\n for (const op of raw) {\n if (typeof op !== 'string') continue\n if (KNOWN_OPERATORS.has(op)) out.add(op)\n else if (!warnedUnknownOperators.has(op)) {\n warnedUnknownOperators.add(op)\n console.warn(`[default-fetcher] supports: unknown operator \"${op}\" — ignored.`)\n }\n }\n return out\n}\nconst warnedUnknownOperators = new Set()\n\n/**\n * Append [key, value] pairs emitted by a style to a URL as query\n * parameters. Existing query string is preserved; values are URL-encoded.\n */\nfunction appendStyleQueryParams(url, pairs) {\n if (!pairs || pairs.length === 0) return url\n const params = pairs.map(\n ([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v),\n )\n const sep = url.includes('?') ? '&' : '?'\n return url + sep + params.join('&')\n}\n\n/**\n * Compose a POST body that includes the style's bodyMerge alongside the\n * author-supplied body. When neither exists, returns null (no body sent).\n *\n * If the author supplied a string body (typically GraphQL), we can't\n * merge — the string is sent as-is and the style's bodyMerge is dropped.\n * Sites with string POST bodies needing pushdown should write the\n * operators into the body themselves.\n */\nfunction composePostBody(authorBody, bodyMerge) {\n if (!bodyMerge) {\n return authorBody === undefined ? null : authorBody\n }\n if (typeof authorBody === 'string') {\n return authorBody\n }\n const base = (authorBody && typeof authorBody === 'object') ? authorBody : {}\n return { ...base, ...bodyMerge }\n}\n\n/**\n * Apply query operators that weren't pushed down to the source. The\n * source returned `data` unfiltered/unlimited/unsorted for those\n * operators; the runtime applies them now in JS.\n */\nfunction applyFallbackOperators(data, request, pushedOperators) {\n if (!Array.isArray(data)) return data\n let result = data\n\n if (request.where && !pushedOperators.has('where')) {\n result = matchWhere(request.where, result)\n }\n if (request.sort && !pushedOperators.has('sort')) {\n result = applySortFallback(result, request.sort)\n }\n if (typeof request.limit === 'number' && request.limit > 0 && !pushedOperators.has('limit')) {\n result = result.slice(0, request.limit)\n }\n return result\n}\n\n/**\n * Stable sort by an expression like \"date desc\" or \"order asc, title asc\".\n * Mirrors the build-time applySort behavior.\n */\nfunction applySortFallback(items, sortExpr) {\n const sorts = String(sortExpr).split(',').map((s) => {\n const [field, dir = 'asc'] = s.trim().split(/\\s+/)\n return { field, desc: dir.toLowerCase() === 'desc' }\n })\n return [...items].sort((a, b) => {\n for (const { field, desc } of sorts) {\n const av = getNestedValue(a, field) ?? ''\n const bv = getNestedValue(b, field) ?? ''\n if (av < bv) return desc ? 1 : -1\n if (av > bv) return desc ? -1 : 1\n }\n return 0\n })\n}\n\n/**\n * Build the static headers object from `site.yml fetcher.headers:`. Returns\n * null when none are configured so the caller can skip adding an empty\n * `headers` init option.\n */\nfunction buildStaticHeaders(headers) {\n if (!headers || typeof headers !== 'object' || Array.isArray(headers)) return null\n const out = {}\n for (const [k, v] of Object.entries(headers)) {\n if (v === null || v === undefined) continue\n out[k] = String(v)\n }\n return Object.keys(out).length ? out : null\n}\n\n/**\n * Case-insensitive header-key check. Lets a site write `Content-Type` or\n * `content-type` and still override the POST default correctly.\n */\nfunction hasHeader(headers, name) {\n const lower = name.toLowerCase()\n return Object.keys(headers).some((k) => k.toLowerCase() === lower)\n}\n\n/**\n * Is this URL absolute (has a scheme) or protocol-relative? Those two pass\n * through the default fetcher unchanged. Everything else is considered\n * relative and resolves against `config.baseUrl` (if set).\n */\nfunction isAbsoluteUrl(url) {\n if (typeof url !== 'string') return false\n if (url.startsWith('//')) return true // protocol-relative\n return /^[a-z][a-z0-9+.-]*:\\/\\//i.test(url) // scheme://…\n}\n\n/**\n * Join `baseUrl` with a relative `url`, avoiding double slashes. If `baseUrl`\n * is empty, the url is returned unchanged — even if relative — so sites that\n * don't set `baseUrl` behave exactly like they did before this capability\n * was added.\n */\nfunction joinUrl(baseUrl, url) {\n if (!baseUrl) return url\n if (url.startsWith('/')) return baseUrl + url\n return baseUrl + '/' + url\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 `config.records`; the fetch comes from the runtime.* The host now calls\n * this and carries no copy. Hosting agreed to exactly 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.records`, `config.fetcher`, `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 * - `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, 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 (hosting, 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 an endpoint the host itself published in `config.records`.\n */\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { deriveCacheKey } from '@uniweb/core/datastore'\nimport { routePatternToRegex } from '@uniweb/core/route-match'\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/** The page a route names — exact first, then the `[slug]` templates, like the SPA. */\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) return { page, params: Object.fromEntries((compiled.paramNames || []).map((n, i) => [n, m[i + 1]])) }\n }\n return { page: null, 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 } = 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 options = {\n locale,\n defaultLocale: resolveDefaultLocale(content?.config) ?? null,\n queries: content?.config?.queries ?? null,\n records: content?.config?.records ?? 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 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.fetcher`, `config.records`\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 config: content?.config?.fetcher ?? {},\n records: content?.config?.records ?? null,\n dev,\n fetch,\n })\n const ctx = { website: null }\n const out = []\n for (const config of configs || []) {\n if (!config) continue\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 out.push({ config, outcome: 'skipped', data: null })\n continue\n }\n const result = await fetcher.resolve(config, ctx)\n if (result?.error) out.push({ config, outcome: 'failed', data: null, error: result.error })\n else out.push({ config, outcome: 'fetched', data: result?.data ?? null })\n }\n return out\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"],"names":["resolveDefaultLocale","deriveCacheKey","substitutePlaceholders","fetch"],"mappings":"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"}
1
+ {"version":3,"file":"ssr.js","sources":["../src/prepare-props.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"],"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-066d.\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 * 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\n * production canonical for multi-locale content (the Cloudflare Worker\n * SSR path consumes it directly). Build-time SSG pre-flattens to one\n * file per locale and so falls 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, Cloudflare\n * Worker SSR); the Cloudflare one was using the wrong shape, silently\n * killing prefetched-data reuse in production. This helper is the one\n * canonical 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 website.dataStore.set(deriveCacheKey(entry.config), { 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 Cloudflare 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 cloud SSR (unicloud).\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 { 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 * Convert hex/rgb color to rgba with opacity.\n * Mirrors withOpacity() in Background.jsx.\n */\nfunction withOpacity(color, opacity) {\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) {\n return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`\n }\n }\n return color\n}\n\n/**\n * Resolve a URL against the site's base path.\n * Mirrors resolveUrl() in Background.jsx.\n */\nfunction resolveUrl(url) {\n if (!url || !url.startsWith('/')) return url\n const basePath = globalThis.uniweb?.activeWebsite?.basePath || ''\n if (!basePath) return url\n if (url.startsWith(basePath + '/') || url === basePath) return url\n return basePath + url\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(${resolveUrl(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, Cloudflare Worker SSR) 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 * - Unicloud: (none needed — payload is complete)\n *\n * NOTE: Does NOT clone content. Cloning is the consumer's responsibility\n * (build modifies content before init; unicloud 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\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 * route and no foundation fallback match. Sites that declare no fetcher\n * at all — starter/docs/marketing templates hitting /data/*.json — ride\n * on this path with zero config.\n *\n * The fetcher recognizes a general-purpose vocabulary under `site.yml fetcher:`\n * so sites with a real backend don't need a foundation just to add a base URL\n * or static headers:\n *\n * fetcher:\n * baseUrl: https://api.example.com\n * headers:\n * X-Tenant: acme\n * Accept: application/vnd.example+json\n * envelope:\n * list: data.items\n * item: data.article\n * error: errors.0.message\n *\n * Per-fetch, the request may carry `method: 'POST'` + `body:` for backends\n * that take queries in a body (GraphQL, search endpoints). `{paramName}`\n * placeholders in body strings are substituted from `request.dynamicContext`\n * so template-page detail queries can reference route params.\n *\n * Every key is optional. When the config is empty, behavior is byte-for-byte\n * identical to a plain `fetch()` with JSON parsing.\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 fetcher; the\n * runtime installs this one automatically. A foundation that needs\n * auth / retry / response normalization declares a named transport\n * and composes `@uniweb/fetchers` middleware around its own `resolve()`.\n *\n * There is intentionally no \"reuse the default and wrap it\" path for\n * foundations — doing so would duplicate this code into every foundation\n * bundle. The subpath export exists specifically for preview-mode shells\n * that need to delegate *non-authenticated* requests to a default-fetcher\n * instance while intercepting authenticated ones via their own transport.\n *\n * Intentional omissions: credentials / secrets are NOT part of the vocabulary.\n * Any value the framework puts into the served HTML is public to the browser.\n * Sites needing private credentials use a deployment-layer proxy — the site\n * fetches a same-origin URL, and a layer in front (e.g. the Uniweb platform's\n * edge worker, or any custom backend) resolves the credential and forwards\n * upstream. Framework sees a plain URL; platform owns the secret.\n *\n * `headers:` IS supported because static per-site headers (tenant routing,\n * content-type negotiation, custom Accept values) aren't credentials and\n * aren't anything sites try to hide. Sites that accidentally put a secret\n * in `headers:` have the same problem they'd have hardcoding it in the URL:\n * it's public. That's not a framework feature gap; it's how browsers work.\n */\n\nimport {\n substitutePlaceholders,\n matchWhere,\n deriveCacheKey,\n resolveRequestStyle,\n resolveServiceUrl,\n} from '@uniweb/core'\n\n// The request style is the wire dialect operators are encoded in. One\n// ships — json-body, the framework's own — and `resolveRequestStyle` is\n// loud on any other name: it throws in dev and logs once in production.\n// Another dialect is a named transport, from the foundation or from an\n// extension the site selects; it is never a second built-in style.\n\n// Operators the default fetcher knows how to handle. When listed in\n// `config.supports`, they're shipped to the source as part of the\n// request; when not listed, they're applied as a JS fallback after\n// fetch. The cache key reflects which operators get pushed down — same\n// query against different `supports:` produces different cache entries.\nconst KNOWN_OPERATORS = new Set(['where', 'limit', 'sort'])\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 {Object} [options.config={}] - Site-level fetcher config from\n * `site.yml fetcher:`. Vocabulary recognized by the default fetcher:\n * `baseUrl`, `headers`, `envelope`, `supports`, `request.style`,\n * `request.rename`. Unknown keys are ignored (foundations may use the\n * same block for their own keys). Default behavior (empty config)\n * matches today's plain GET + JSON.\n * @param {boolean} [options.dev=false] - Enable dev-mode diagnostics: an\n * unknown request style throws; a rename entry for an operator the wire\n * does not carry warns.\n * @returns {{ resolve: (req: Object, ctx: Object) => Promise<{ data, error? }> }}\n */\nexport function createDefaultFetcher({ basePath = '', config = {}, dev = false, records = null, fetch: fetchImpl = null } = {}) {\n // The transport is injectable: a host executing fetches outside a browser (an SSR isolate)\n // decides how a site-relative address such as `/_records/members` is dispatched — through its\n // own origin or a service binding — and hands that in. Defaults to the global `fetch`, resolved\n // at call time so a test stub installed later is honoured.\n const doFetch = (input, init) => (fetchImpl || globalThis.fetch)(input, init)\n const pathPrefix = basePath && basePath !== '/' ? basePath.replace(/\\/$/, '') : ''\n\n const baseUrl = typeof config?.baseUrl === 'string'\n ? config.baseUrl.replace(/\\/$/, '')\n : ''\n\n // Static headers merged into every remote request. Local `/data/*.json`\n // requests are never decorated — they're just file reads under public/.\n const staticHeaders = buildStaticHeaders(config?.headers)\n\n // `supports:` declares which query operators (where, limit, sort) the\n // backend evaluates at the source. Operators in this list are shipped\n // in the request; operators not in this list are applied as a JS\n // fallback after the response arrives. Default: empty — the framework\n // default fetcher serving static files supports nothing natively.\n const supports = normalizeSupports(config?.supports)\n\n // Request style — the wire dialect operators are encoded in. Read from\n // `site.yml fetcher.request.style`; `null`/absent and `json-body` both\n // resolve to the one shipped style, and any other name is loud (see\n // `resolveRequestStyle`).\n const requestConfig = (config?.request && typeof config.request === 'object') ? config.request : {}\n const styleName = typeof requestConfig.style === 'string' ? requestConfig.style : null\n const style = resolveRequestStyle(styleName, { dev })\n\n // Operator-name renames applied on top of the style's wire names.\n // Shallow: only the operator keys (where / limit / sort) are rewritten.\n // Field names inside a where-object are untouched.\n const rename = normalizeRename(requestConfig.rename, style, { dev })\n\n // `envelope:` extends today's `transform:` to cover detail responses and\n // errors. Three dot-paths, all optional:\n // - envelope.list — applied on list responses. Per-fetch\n // `transform:` on the request wins (per-fetch overrides site-level).\n // - envelope.item — applied when request.dynamicContext is set\n // (the request is for a template-page item).\n // - envelope.error — extract error text from non-2xx response body.\n //\n // Priority (highest wins): per-fetch request.envelope > site-level\n // config.envelope > style.defaultEnvelope. json-body declares no\n // envelope; the slot is the encoder's to fill, and a site-level value\n // always wins over it.\n const siteEnvelope = (config?.envelope && typeof config.envelope === 'object')\n ? config.envelope\n : null\n const envelope = { ...(style.defaultEnvelope || {}), ...(siteEnvelope || {}) }\n\n // ⭐ The LIVE LANE's envelope is the backend's. `config.records` is stamped by the backend\n // that answers a records request, so where the array sits in ITS response is its to\n // declare: `records.envelope.records` — the KEY says what it holds, the VALUE is the JSON\n // key the array sits under (`{ records: \"entries\" }` ⇒ body.entries). That spelling is the\n // agreed one (2026-08-30: `collection` retired; ⛔ not `list`, which is a URL pattern on the\n // same stamp). It applies only to a request that resolved to that lane (`endpoint` set)\n // and wins over the site's own `fetcher.envelope`, which describes the author's backend.\n // Ruled 2026-09-03 [Diego]: the backend sets `config.records`; the fetch comes from the\n // runtime. Until this line the runtime resolved `list`/`record` off the stamp and ignored\n // its envelope.\n const stampedArrayKey = (records?.envelope && typeof records.envelope === 'object'\n && typeof records.envelope.records === 'string' && records.envelope.records.length)\n ? records.envelope.records\n : null\n const laneEnvelope = stampedArrayKey ? { list: stampedArrayKey } : null\n\n return {\n /**\n * Cache-key function. The default-fetcher's cache key includes only\n * the operators it pushes down (because they affect what the source\n * sees). Operators applied as runtime fallback operate on a shared\n * cached value and therefore must NOT split the cache.\n *\n * Example: with `supports: []`, two pages declaring different\n * `where:` clauses against the same path share one cache entry —\n * the file is fetched once and each page filters its own copy. With\n * `supports: [where]`, the same two pages fire two requests because\n * the predicate travels in the request.\n */\n cacheKey(request) {\n // Build a request projection that includes only operators the active\n // style will actually push for this request. deriveCacheKey already\n // covers the always-keyed fields.\n //\n // The key also carries the style name. With one shipped style it is\n // a constant segment, kept so that key shapes do not move.\n const base = deriveCacheKey(request)\n const projected = {}\n for (const op of supports) {\n if (!style.canPush.has(op)) continue\n if (request[op] !== undefined) projected[op] = request[op]\n }\n if (Object.keys(projected).length === 0 && style.name === 'json-body') {\n // Keep back-compat key shape when the ambient default pushes nothing.\n return base\n }\n return base + '::style=' + style.name + '::' + JSON.stringify(projected)\n },\n\n async resolve(request, ctx = {}) {\n if (!request) return { data: null }\n const { path, url, endpoint, 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 let isRemote\n if (endpoint) {\n // A host-declared collection lane, resolved upstream from the pattern\n // it published (`@uniweb/core/query-address`). FINAL ON ARRIVAL:\n //\n // - `baseUrl` is NOT joined. That knob points a site at ITS OWN\n // backend; prepending it to an address a host composed would\n // corrupt exactly the layout the pattern exists to let them own.\n // - the site `base` IS applied to a rooted address, the same rule\n // every other site-relative address follows — shared with\n // `resolveServiceUrl` rather than spelled a second time here.\n //\n // Remote semantics otherwise: this answers a QUERY, so operator\n // pushdown and static headers both apply, which is what separates it\n // from `path` (a static file that can neither filter nor sort).\n target = resolveServiceUrl(endpoint, pathPrefix)\n isRemote = true\n } else 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 isRemote = false\n } else if (url) {\n // Remote URL — baseUrl applies when url is relative (no scheme,\n // not protocol-relative). Absolute or protocol-relative pass through.\n target = isAbsoluteUrl(url) ? url : joinUrl(baseUrl, url)\n isRemote = true\n } else {\n return { data: [], error: 'No path, url or endpoint specified' }\n }\n\n const init = { signal: ctx.signal, method }\n const headers = {}\n\n // Static site-level headers go on remote requests only — we don't\n // decorate local file reads with tenant/content-type headers.\n if (isRemote && staticHeaders) Object.assign(headers, staticHeaders)\n\n // Push down supported query operators to the source via the active\n // request style. Pushdown only applies to remote URLs — local `path:`\n // reads are static files that can't filter or sort. Operators the\n // style didn't push get applied as a JS fallback after the response\n // (see the post-fetch block below).\n //\n // The style owns the wire format: json-body encodes GET pushdown as\n // `?_where=<JSON>&_limit=&_sort=` and POST pushdown as top-level keys\n // merged into an object body. It is the only shipped wire — another\n // dialect is a named transport.\n const pushCandidates = new Set()\n if (isRemote) {\n for (const op of KNOWN_OPERATORS) {\n if (\n supports.has(op) &&\n style.canPush.has(op) &&\n request[op] !== undefined &&\n request[op] !== null\n ) {\n pushCandidates.add(op)\n }\n }\n }\n\n const encoded = pushCandidates.size > 0\n ? style.encode(request, { method, pushCandidates, rename })\n : { queryParams: [], bodyMerge: null, pushed: new Set() }\n const pushedOperators = encoded.pushed\n\n if (encoded.queryParams.length > 0 && method === 'GET') {\n target = appendStyleQueryParams(target, encoded.queryParams)\n }\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 resolvedBody = (rawBody !== undefined && rawBody !== null && dc && dc.paramName)\n ? substitutePlaceholders(rawBody, { [dc.paramName]: dc.paramValue }, { encode: false })\n : rawBody\n\n // Compose the final body: author-supplied body merged with pushed\n // operators from the style. When no body and no pushdown, send\n // a body containing just the pushed operators if any exist.\n const finalBody = composePostBody(resolvedBody, encoded.bodyMerge)\n\n if (finalBody !== null) {\n // Default Content-Type to JSON unless the site's static headers\n // already set one (for application/graphql or form-urlencoded).\n if (!hasHeader(headers, 'Content-Type')) {\n headers['Content-Type'] = 'application/json'\n }\n init.body = typeof finalBody === 'string' ? finalBody : JSON.stringify(finalBody)\n }\n }\n\n if (Object.keys(headers).length) init.headers = headers\n\n try {\n const response = await doFetch(target, init)\n\n // Per-request envelope (set by object-form `detail:`) wins over\n // site-level envelope. This lets a detail query declare its own\n // item/collection/error paths independently of the collection.\n const requestEnvelope = (request.envelope && typeof request.envelope === 'object')\n ? request.envelope\n : null\n const effectiveEnvelope = requestEnvelope\n ?? (endpoint && laneEnvelope ? { ...envelope, ...laneEnvelope } : envelope)\n\n if (!response.ok) {\n // If `envelope.error` is configured, 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 (effectiveEnvelope.error) {\n try {\n const text = await response.text()\n const body = safeParseJSON(text)\n if (body !== undefined) {\n const candidate = getNestedValue(body, effectiveEnvelope.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 response envelope. Priority order, highest wins:\n // 1. Per-fetch `transform:` (existing, documented knob).\n // 2. Per-request `envelope.item` (detail) or `envelope.list`.\n // 3. Site-level `envelope.item` (detail) or `envelope.list`.\n const isDetailRequest = !!request.dynamicContext\n const effectiveTransform =\n transform\n || (isDetailRequest ? effectiveEnvelope.item : effectiveEnvelope.list)\n if (effectiveTransform && data !== null && data !== undefined) {\n data = getNestedValue(data, effectiveTransform)\n }\n\n // Apply runtime fallback for query operators not pushed down.\n // Only applies to array data (filtering/sorting/limiting a single\n // record doesn't make sense). For non-arrays, operators are\n // silently ignored — the source returned what it returned.\n data = applyFallbackOperators(data, request, pushedOperators)\n\n return { data: data ?? [] }\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 * Normalize the `request.rename` map. Returns null if nothing valid.\n * Dev-mode warns on operator names that don't exist in the style's\n * `canPush` set — a rename that targets an operator the style doesn't\n * push is silently dead config, and the warning surfaces the mistake.\n */\nfunction normalizeRename(raw, style, { dev }) {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null\n const out = {}\n for (const [op, wireName] of Object.entries(raw)) {\n if (typeof wireName !== 'string' || wireName.length === 0) continue\n if (dev && !style.canPush.has(op) && !warnedRenameTargets.has(op)) {\n warnedRenameTargets.add(op)\n console.warn(\n `[default-fetcher] request.rename: operator \"${op}\" is not pushed by ` +\n `style \"${style.name}\" — rename has no effect. Known operators for ` +\n `this style: ${[...style.canPush].join(', ') || '(none)'}.`,\n )\n }\n out[op] = wireName\n }\n return Object.keys(out).length ? out : null\n}\nconst warnedRenameTargets = new Set()\n\n/**\n * Normalize the supports declaration to a Set of known operators. Unknown\n * operator names are ignored with a one-time dev warning.\n */\nfunction normalizeSupports(raw) {\n const out = new Set()\n if (!Array.isArray(raw)) return out\n for (const op of raw) {\n if (typeof op !== 'string') continue\n if (KNOWN_OPERATORS.has(op)) out.add(op)\n else if (!warnedUnknownOperators.has(op)) {\n warnedUnknownOperators.add(op)\n console.warn(`[default-fetcher] supports: unknown operator \"${op}\" — ignored.`)\n }\n }\n return out\n}\nconst warnedUnknownOperators = new Set()\n\n/**\n * Append [key, value] pairs emitted by a style to a URL as query\n * parameters. Existing query string is preserved; values are URL-encoded.\n */\nfunction appendStyleQueryParams(url, pairs) {\n if (!pairs || pairs.length === 0) return url\n const params = pairs.map(\n ([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v),\n )\n const sep = url.includes('?') ? '&' : '?'\n return url + sep + params.join('&')\n}\n\n/**\n * Compose a POST body that includes the style's bodyMerge alongside the\n * author-supplied body. When neither exists, returns null (no body sent).\n *\n * If the author supplied a string body (typically GraphQL), we can't\n * merge — the string is sent as-is and the style's bodyMerge is dropped.\n * Sites with string POST bodies needing pushdown should write the\n * operators into the body themselves.\n */\nfunction composePostBody(authorBody, bodyMerge) {\n if (!bodyMerge) {\n return authorBody === undefined ? null : authorBody\n }\n if (typeof authorBody === 'string') {\n return authorBody\n }\n const base = (authorBody && typeof authorBody === 'object') ? authorBody : {}\n return { ...base, ...bodyMerge }\n}\n\n/**\n * Apply query operators that weren't pushed down to the source. The\n * source returned `data` unfiltered/unlimited/unsorted for those\n * operators; the runtime applies them now in JS.\n */\nfunction applyFallbackOperators(data, request, pushedOperators) {\n if (!Array.isArray(data)) return data\n let result = data\n\n if (request.where && !pushedOperators.has('where')) {\n result = matchWhere(request.where, result)\n }\n if (request.sort && !pushedOperators.has('sort')) {\n result = applySortFallback(result, request.sort)\n }\n if (typeof request.limit === 'number' && request.limit > 0 && !pushedOperators.has('limit')) {\n result = result.slice(0, request.limit)\n }\n return result\n}\n\n/**\n * Stable sort by an expression like \"date desc\" or \"order asc, title asc\".\n * Mirrors the build-time applySort behavior.\n */\nfunction applySortFallback(items, sortExpr) {\n const sorts = String(sortExpr).split(',').map((s) => {\n const [field, dir = 'asc'] = s.trim().split(/\\s+/)\n return { field, desc: dir.toLowerCase() === 'desc' }\n })\n return [...items].sort((a, b) => {\n for (const { field, desc } of sorts) {\n const av = getNestedValue(a, field) ?? ''\n const bv = getNestedValue(b, field) ?? ''\n if (av < bv) return desc ? 1 : -1\n if (av > bv) return desc ? -1 : 1\n }\n return 0\n })\n}\n\n/**\n * Build the static headers object from `site.yml fetcher.headers:`. Returns\n * null when none are configured so the caller can skip adding an empty\n * `headers` init option.\n */\nfunction buildStaticHeaders(headers) {\n if (!headers || typeof headers !== 'object' || Array.isArray(headers)) return null\n const out = {}\n for (const [k, v] of Object.entries(headers)) {\n if (v === null || v === undefined) continue\n out[k] = String(v)\n }\n return Object.keys(out).length ? out : null\n}\n\n/**\n * Case-insensitive header-key check. Lets a site write `Content-Type` or\n * `content-type` and still override the POST default correctly.\n */\nfunction hasHeader(headers, name) {\n const lower = name.toLowerCase()\n return Object.keys(headers).some((k) => k.toLowerCase() === lower)\n}\n\n/**\n * Is this URL absolute (has a scheme) or protocol-relative? Those two pass\n * through the default fetcher unchanged. Everything else is considered\n * relative and resolves against `config.baseUrl` (if set).\n */\nfunction isAbsoluteUrl(url) {\n if (typeof url !== 'string') return false\n if (url.startsWith('//')) return true // protocol-relative\n return /^[a-z][a-z0-9+.-]*:\\/\\//i.test(url) // scheme://…\n}\n\n/**\n * Join `baseUrl` with a relative `url`, avoiding double slashes. If `baseUrl`\n * is empty, the url is returned unchanged — even if relative — so sites that\n * don't set `baseUrl` behave exactly like they did before this capability\n * was added.\n */\nfunction joinUrl(baseUrl, url) {\n if (!baseUrl) return url\n if (url.startsWith('/')) return baseUrl + url\n return baseUrl + '/' + url\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 `config.records`; the fetch comes from the runtime.* The host now calls\n * this and carries no copy. Hosting agreed to exactly 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.records`, `config.fetcher`, `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` (hosting, measured under `wrangler dev`\n * against a real Worker 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, 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 (hosting, 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 an endpoint the host itself published in `config.records`.\n */\nimport { resolveFetchConfigs } from '@uniweb/core/fetch-config'\nimport { deriveCacheKey } from '@uniweb/core/datastore'\nimport { routePatternToRegex } from '@uniweb/core/route-match'\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/** The page a route names — exact first, then the `[slug]` templates, like the SPA. */\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) return { page, params: Object.fromEntries((compiled.paramNames || []).map((n, i) => [n, m[i + 1]])) }\n }\n return { page: null, 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 } = 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 options = {\n locale,\n defaultLocale: resolveDefaultLocale(content?.config) ?? null,\n queries: content?.config?.queries ?? null,\n records: content?.config?.records ?? 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 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.fetcher`, `config.records`\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 config: content?.config?.fetcher ?? {},\n records: content?.config?.records ?? null,\n dev,\n fetch,\n })\n const ctx = { website: null }\n const out = []\n for (const config of configs || []) {\n if (!config) continue\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 out.push({ config, outcome: 'skipped', data: null })\n continue\n }\n const result = await fetcher.resolve(config, ctx)\n if (result?.error) out.push({ config, outcome: 'failed', data: null, error: result.error })\n else out.push({ config, outcome: 'fetched', data: result?.data ?? null })\n }\n return out\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 (hosting drew this line themselves,\n * 2026-09-03; `framework/CLAUDE.md` § *Serve locations are read, never constructed*).\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 hosting under\n // `wrangler dev` against a real Worker 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"],"names":["resolveDefaultLocale","deriveCacheKey","fetch"],"mappings":"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"}