@elytracms/next 0.0.101 → 0.0.103
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/index.js +2 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components.ts","../src/canvas-renderer.tsx","../src/source.ts","../src/snapshot.ts","../src/metadata.ts","../src/route-core.ts","../src/cache.ts","../src/route.tsx","../src/sitemap.ts","../src/revalidate-core.ts","../src/revalidate.ts","../src/ingest-core.ts","../src/ingest.ts","../src/image.tsx","../src/full-bleed.ts","../src/host-env.ts","../src/host-content.ts","../src/host-globals.ts","../src/preview-core.ts","../src/preview.ts","../src/host-kit.ts","../src/index.ts"],"names":["record","SEO_SUBFIELDS","items","documentBodyCompositionNodes","cacheTags","jsx","Fragment","outcome","window","INGEST_MAX_BODY_BYTES","registerComponents","str","draftMode","redirect"],"mappings":"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","file":"index.js","sourcesContent":["import type { ComponentType } from 'react'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations } from '@elytracms/core/runtime-renderer'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\n\n/**\n * One host-repo component: the manifest the builder edits against plus the\n * real React implementation that renders it (vision AD-2 — project components\n * are real code in the host repo, never generated).\n *\n * This is the STRICT shape — the one a hand-authored component module should\n * export (`export const heroComponent: HostComponent = …`), so the compiler\n * checks the implementation where it is written. It is assignable to the\n * config's {@link HostComponentEntry}, which is what {@link defineHostComponents}\n * accepts, so both hand-built lists and the config's own list register through\n * the one signature (EC-541).\n */\nexport interface HostComponent {\n manifest: ComponentManifest\n implementation: ComponentType<Record<string, unknown>>\n}\n\n/**\n * The registered component surface a host app hands to `<CanvasRenderer />`\n * and the catch-all route helper: a manifest registry plus the implementation\n * map. Registration problems — duplicate ids, invalid ids, role claims, and\n * `invalid-implementation` for an entry whose `implementation` is not a React\n * component — are structured issues in `registry.issues`, the ONE record every\n * surface reads (EC-591; never thrown — an unknown component renders the EC-015\n * fallback). The studio registers the same config list through the same core\n * step, so it shows exactly these issues.\n */\nexport interface HostComponents {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * Component registration for the embedded runtime (EC-144, EC-591): the host\n * registers EXACTLY the list it is given — the config's `components` — through\n * core's one registration step `registerComponents`, the same step the studio\n * registry and the preview harness call. The engine adds nothing: there are no\n * base primitives and no override layer (EC-591 deleted `includeBasePrimitives`).\n * The components Elytra ships are an opt-in package listed in the config like any\n * other (`components: [...elytraComponents, ...siteComponents]`).\n *\n * One precedence rule: a duplicate id is refused WHOLE (`duplicate-id`, the first\n * entry kept), so a manifest never ships with another entry's implementation.\n * To replace a shipped component, list yours under the id and leave the shipped\n * entry out; to swap only the Image implementation for `next/image`, wrap the\n * list in {@link withNextImage}.\n *\n * Accepts the CONFIG's entry shape (`implementation: unknown`) rather than the\n * strict {@link HostComponent}: the config declares implementations opaquely,\n * and EC-539 made `createElytraHost({ ...site })` the contract, so a\n * compile-time check at this seam was already gone for the recommended path.\n * The check is RUNTIME instead (EC-541): a value that is not a component is\n * recorded as an `invalid-implementation` issue naming the component id and\n * the received kind, and is NOT put into `implementations` — so the renderer\n * takes its missing-component path (a visible EC-015 fallback) instead of\n * crashing the server render on `createElement` (\"Element type is invalid\")\n * or, for a string, silently rendering an HTML tag. The manifest still\n * registers, so the entry shows as broken rather than vanishing.\n *\n * One consequence of the `unknown` parameter: an INLINE\n * `implementation: (props) => …` literal no longer gets `props` contextually\n * typed (it is `noImplicitAny` under strict). Annotate the parameter, or\n * author the entry as a {@link HostComponent} — which is the documented shape\n * anyway. (An overload pair was tried and does not restore it: TypeScript\n * resolves the context-sensitive literal against the wider overload.)\n */\nexport function defineHostComponents(components: readonly HostComponentEntry[] = []): HostComponents {\n const { registry, implementations } = registerComponents(components)\n return { registry, implementations }\n}\n","import type { ReactNode } from 'react'\nimport type { ContentQueryError, ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { isBlockSequenceValueShape } from '@elytracms/core/rich-text'\nimport {\n createRenderContext,\n documentBodyCompositionNodes,\n documentBodyItems,\n renderDocumentBodyItems,\n serializeListingQuery,\n} from '@elytracms/core/runtime-renderer'\nimport type {\n DocumentBodyItem,\n ListingQuery,\n RelationTarget,\n RenderAsset,\n ResolveLink,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { HostComponents } from './components'\n\n/**\n * `<CanvasRenderer />` (EC-144, vision AD-1): an async React Server Component\n * that renders a resolved page (from `resolvePage`, EC-143) through\n * `@elytracms/core/runtime-renderer` with the host app's component registry. EC-015\n * fallback behavior is identical to the builder: a missing component, invalid\n * prop, or missing required slot renders a visible fallback — a page never\n * crashes and never silently omits a node.\n */\nexport interface CanvasRendererProps {\n /** The `resolvePage` result for the current URL. */\n result: ResolvePageResult\n /** Host component surface from `defineHostComponents`. */\n components: HostComponents\n /**\n * Delivery-shaped assets the page may reference (EC-195, from the render\n * outcome). An `asset`-typed Image prop resolves through these to a url +\n * intrinsic dimensions; an unknown id degrades to the visible fallback.\n */\n assets?: readonly ResolvedAsset[]\n /**\n * Populated composition-node `relation` props, keyed `collection:id` (EC-254,\n * from the render outcome). A block's `relation` prop (e.g. a ProductGrid's\n * picked products) resolves through these to the populated target; an unknown\n * key degrades to the block's own empty state. Absent → relation props pass\n * through unresolved (raw ref), the same degradation as a missing asset list.\n */\n relations?: Record<string, RelationTarget>\n /**\n * Documents each `listing` block resolved to, keyed by serialized query (EC-255,\n * from the render outcome). A ProductGrid keyed to a picked `category` reads its\n * products through this; an unknown key (or absent map) degrades to the block's\n * own empty state — the same degradation as a missing relation. A query that\n * could not run is baked as its errors (EC-581 6a) and shown above the block.\n */\n listings?: Record<string, BakedListing>\n /**\n * URL paths for the documents this page's rich-text props LINK to, keyed\n * `collection:id` (EC-291, from the render outcome). A self-typed internal link\n * (`attrs.ref = {type:'reference',…}`) resolves through this to a real `href`; an\n * unknown key (or absent map) degrades to the renderer's visible `unresolved-ref`\n * issue (EC-290), never a silent `#`.\n */\n links?: Record<string, string>\n /**\n * The resolved document's collection-declared delivery presentation (EC-499,\n * from the render outcome): which field(s) hold the body. Absent → the EC-458\n * shape picker selects the body by VALUE, exactly as before.\n */\n presentation?: DeliveryPresentation\n}\n\n/**\n * A collection's `presentation` declaration (EC-499), resolved for delivery: the\n * body field list normalized from `string | string[]`. Plain data — it rides the\n * (serializable) render outcome and the RSC boundary like the baked\n * assets/relations/links do.\n *\n * EC-524: this renderer applies no CHROME. It carried a `wrapper` component id\n * briefly (never released); a detail wrapper is a page template, templates are\n * code, and the host now supplies one from its own route file via\n * `createCanvasRoute`'s `wrap` option. What stays here is content selection —\n * which is exactly what makes a canvas body and a rich-text body render\n * agnostically through the one entry point.\n */\nexport interface DeliveryPresentation {\n /** Field names whose values form the body, in render order. */\n body?: readonly string[]\n}\n\n/**\n * One listing's baked result (EC-255; EC-581 6a): the documents its query matched,\n * or the structured errors `listDocuments` refused it with. Plain data — it rides\n * the serializable render outcome and the RSC boundary like the other bakes.\n */\nexport type BakedListing = RelationTarget[] | { errors: readonly ContentQueryError[] }\n\n/** Build the renderer's relation resolver from the baked, populated targets (EC-254). */\nfunction relationResolver(relations: Record<string, RelationTarget>): ResolveRelation {\n return (ref) => relations[`${ref.collection}:${ref.id}`] ?? null\n}\n\n/** Build the renderer's link resolver from the baked, doc-keyed URL paths (EC-291). */\nfunction linkResolver(links: Record<string, string>): ResolveLink {\n return (ref) => links[`${ref.collection}:${ref.id}`] ?? null\n}\n\n/**\n * Build the renderer's listing resolver from the baked, query-keyed documents\n * (EC-255). A refused query resolves to nothing (the block's own empty state); the\n * listing decorator below shows why.\n */\nfunction listingResolver(listings: Record<string, BakedListing>): ResolveListing {\n return (query) => {\n const baked = listings[serializeListingQuery(query)]\n return Array.isArray(baked) ? baked : null\n }\n}\n\n/**\n * Why a baked listing query cannot run (EC-581 6a): `listDocuments` refused it and the\n * host baked the errors. The render context (`createRenderContext`, EC-592) judges the\n * declaration itself, asks this for the query and shows the notice — the same two\n * judgments the studio canvas reads, so a listing that cannot run is never an empty\n * grid on one surface and a notice on the other.\n */\nfunction bakedListingFailure(listings: Record<string, BakedListing>): (query: ListingQuery) => string[] | null {\n return (query) => {\n const baked = listings[serializeListingQuery(query)]\n return baked && !Array.isArray(baked) ? baked.errors.map((error) => error.message) : null\n }\n}\n\n/**\n * Build the renderer's asset resolver from the baked delivery assets (EC-195). EC-587\n * (ASSETS D11): the baked `ResolvedAsset` IS the render asset — handed on as built by\n * `resolvedAsset(record, url, at)`, never re-picked field by field (the copy dropped `id`\n * and `mimeType`, so a prop got a different object than a document field).\n */\nfunction assetResolver(assets: readonly ResolvedAsset[]): (id: string) => RenderAsset | null {\n const byId = new Map(assets.map((asset) => [asset.id, asset]))\n return (id) => byId.get(id) ?? null\n}\n\n/** Visible fallback when the renderer is handed a non-`ok` resolution. */\nfunction UnresolvedPageFallback(props: { status: string }): ReactNode {\n return (\n <div data-ec-fallback=\"unresolved-page\" data-ec-status={props.status}>\n {`This URL did not resolve to a page (status: ${props.status}).`}\n </div>\n )\n}\n\n/** Visible fallback when an `ok` resolution carries no renderable document. */\nfunction MissingDocumentFallback(): ReactNode {\n return (\n <div data-ec-fallback=\"missing-document\">\n {`Route resolved but carried no page document to render.`}\n </div>\n )\n}\n\n/**\n * Render a resolved page server-side. Async so hosts can compose it like any\n * other RSC; resolution itself is synchronous (the content was prefetched by\n * the `ContentSource`).\n */\nexport async function CanvasRenderer(props: CanvasRendererProps): Promise<ReactNode> {\n const { result, components } = props\n\n if (result.status !== 'ok') {\n // The route helper answers redirect/notFound before rendering; direct\n // users of <CanvasRenderer /> still get a visible state, never a crash.\n return <UnresolvedPageFallback status={result.status} />\n }\n\n // EC-592: THE render context — the studio canvas builds the same one over its live\n // session; here the source is the baked render outcome and the surface a server render\n // (`mode: 'delivery'` unwraps the client error boundary; a listing that cannot run\n // says so above its block, EC-581 6a).\n const ctx = createRenderContext({\n components,\n mode: 'delivery',\n source: {\n resolveAsset: assetResolver(props.assets ?? []),\n // EC-254: wire relation population only when the host baked the populated\n // targets, so a host that did not opt in keeps the raw-ref passthrough.\n ...(props.relations ? { resolveRelation: relationResolver(props.relations) } : {}),\n // EC-255: wire the listing resolver only when the host baked the query results,\n // so a host that did not opt in keeps listing props empty (the block's own state).\n ...(props.listings\n ? { resolveListing: listingResolver(props.listings), listingFailure: bakedListingFailure(props.listings) }\n : {}),\n // EC-291: wire the rich-text link resolver only when the host baked the link map,\n // so a host that did not opt in degrades a reference link to the visible\n // `unresolved-ref` issue (EC-290) rather than a wrong href.\n ...(props.links ? { resolveLink: linkResolver(props.links) } : {}),\n },\n })\n\n const document = result.document\n\n // EC-187/EC-287: a routed document delivers its body directly — there is no\n // graph or layout indirection; shared chrome is composed in the host's\n // `app/layout.tsx`, and per-collection detail chrome in the host's own route\n // (`createCanvasRoute`'s `wrap`, EC-524). EC-499: WHICH field is the body, and\n // what shape it holds, is a declaration (`presentation.body`) with the EC-458\n // value-shape picker as the fallback — and one entry point renders a\n // composition body and a rich-text body alike. With no document, a visible\n // fallback.\n if (document) {\n return <>{renderDocumentBodyItems(bodyItemsOf(document, props.presentation), ctx)}</>\n }\n\n return <MissingDocumentFallback />\n}\n\n/**\n * The ordered body items of a resolved document (EC-499): the values of the\n * collection's declared `presentation.body` fields concatenated in declaration\n * order, or — when the collection declares nothing — the EC-458 value-shape\n * pick, unchanged. A declared field the document has no value for contributes\n * nothing (an empty body is legitimate); a declared field holding something that\n * is neither composition nor rich text contributes a visible fallback.\n */\nexport function bodyItemsOf(\n document: Record<string, unknown> | null | undefined,\n presentation?: DeliveryPresentation,\n): DocumentBodyItem[] {\n if (!document) return []\n const fields = presentation?.body ?? []\n if (fields.length > 0) return fields.flatMap((name) => documentBodyItems(document[name]))\n return documentBodyItems(compositionOf(document))\n}\n\n/**\n * Select the delivery composition from a resolved document (EC-187) — the\n * FALLBACK, used only when the collection declares no `presentation.body`\n * (EC-499). Kept exactly as it was so every undeclared collection renders\n * byte-for-byte what it rendered before: declaration beats shape-guessing, but\n * shape-guessing keeps working where nothing was declared.\n *\n * Its known limit is why the declaration exists: it can only ever select a\n * COMPOSITION, so a document whose body is a `richText` field was unrenderable\n * through the catch-all and needed a hand-written Next route.\n *\n * The\n * composition field can be named anything the host's collection schema chooses:\n * the conventional `body` (a `page`) or `content` (a routed content collection\n * like a `post`), but ALSO a host-specific name such as a production site's\n * `pageBuilder` (migrated faithfully from Sanity, EC-263). So the field is chosen\n * by VALUE — a composition is a `ComponentNode` or an array of them — NEVER by\n * name: a `body ?? content` name heuristic both picks a post's rich-text `body`\n * string over its `content` composition AND misses a `pageBuilder` entirely,\n * rendering nothing.\n *\n * `body` then `content` keep priority (so a content document's unrelated rich-text\n * `body` never shadows the conventional `content` composition); any other\n * composition-valued field is the generalized fallback.\n */\nfunction compositionOf(document: Record<string, unknown>): unknown {\n const body = document['body']\n // EC-547: a v2 value qualifies for the shape-pick only when it CARRIES\n // component roots — a prose-only sequence is renderable through a declared\n // `presentation.body`, but the undeclared fallback keeps its EC-458 promise\n // byte-identical: a document's prose `body` never shadows its conventional\n // `content` composition.\n if (isCompositionishSequence(body) || Array.isArray(body) || isComponentNodeValue(body)) return body\n const content = document['content']\n if (isCompositionishSequence(content) || Array.isArray(content) || isComponentNodeValue(content)) return content\n // Generalized (EC-263): the first field whose value is a composition — a node or\n // a non-empty array of nodes. `body`/`content` already took priority above.\n for (const value of Object.values(document)) {\n if (isCompositionishSequence(value)) return value\n if (isComponentNodeValue(value)) return value\n if (Array.isArray(value) && value.length > 0 && value.every(isComponentNodeValue)) return value\n }\n return undefined\n}\n\n/**\n * The routed document's COMPOSITION nodes as a `ComponentNode[]` (EC-205): the\n * same body selection the renderer uses, normalized to an array so a delivery\n * host can walk it (e.g. to page-scope baked assets, or to read the first\n * block's `presentation.fullBleed` hint). Non-node values are dropped.\n * `null`/no-document yields an empty array.\n *\n * EC-499: takes the collection's declared presentation when there is one, and\n * still returns COMPOSITION nodes only — a rich-text body yields `[]`, which is\n * the right answer for both of its readers (nothing to full-bleed; nothing that\n * `collectReferencedAssetIds` could walk). The rich-text half of a body is baked\n * separately in `route-core`.\n */\nexport function compositionNodesOf(\n document: Record<string, unknown> | null | undefined,\n presentation?: DeliveryPresentation,\n): ComponentNode[] {\n return documentBodyCompositionNodes(bodyItemsOf(document, presentation))\n}\n\n/** A v2 sequence that carries at least one component root (the shape-pick bar). */\nfunction isCompositionishSequence(value: unknown): boolean {\n return (\n isBlockSequenceValueShape(value) &&\n value.content.some(\n (node) =>\n typeof node === 'object' &&\n node !== null &&\n (node as { type?: unknown }).type === 'elytra/component',\n )\n )\n}\n\n/** Structural `ComponentNode` guard — a resolved field value is opaque at the type level. */\nfunction isComponentNodeValue(value: unknown): boolean {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { id?: unknown }).id === 'string' &&\n typeof (value as { componentId?: unknown }).componentId === 'string'\n )\n}\n","import type { CollectionDef, Locale, LocaleConfig, Perspective, RouteRecord } from '@elytracms/core/cms-core'\nimport { cacheTags } from '@elytracms/core/content'\nimport type { CacheTags, ContentClient, ResolvedAsset } from '@elytracms/core/content'\n\n/**\n * The content seam of the embedded runtime (EC-144, vision AD-1/AD-4): the\n * catch-all route helper consumes this interface, so where the content comes\n * from is swappable. EC-144 ships the in-memory snapshot bridge\n * (`createContentSnapshot` over a `PersistenceAdapter`); EC-145/146 plug a\n * Convex-backed source into the exact same seam.\n *\n * Accessors are synchronous over a prefetched `ContentLookup` (vision AD-4);\n * any fetching happens when the source is loaded, before clients are minted.\n *\n * EC-574: the `CanvasDataSource` shape and the `sources` member (the data-binding\n * subsystem's per-section query definitions, EC-166) are gone with that subsystem.\n */\nexport interface ContentSource {\n /** The project's locale configuration (default + supported set, EC-018). */\n readonly locales: LocaleConfig\n /**\n * Mint a typed accessor client (EC-143) bound to a perspective. The locale\n * defaults to the project default; the route helper overrides it per\n * request from the URL's locale prefix.\n */\n client(init: { perspective: Perspective; locale?: Locale }): ContentClient\n /**\n * The route records the source's clients resolve against (EC-171, additive).\n * Lets enumerating consumers — the sitemap helper — walk the same route\n * table `resolvePage` uses, instead of keeping a second list. Optional so\n * existing custom sources stay valid; without it the sitemap helper needs\n * explicit routes.\n */\n readonly routes?: readonly RouteRecord[]\n /**\n * The project's collection definitions (EC-499, additive) — config-owned\n * (AD-11), already prefetched by every source that reads a schema. The route\n * helper reads the resolved document's collection from here to honour its\n * declared `presentation` (which field(s) hold the delivery body, and the\n * optional detail wrapper). Optional so existing custom sources stay valid;\n * without it every collection is treated as undeclared and the EC-458 shape\n * picker decides, exactly as before.\n */\n readonly collections?: readonly CollectionDef[]\n /**\n * The project's delivery-shaped assets (EC-195, additive). Lets the route\n * helper bake the assets a page may reference into the (serializable) render\n * outcome, so the renderer can resolve an `asset`-typed Image prop to a usable\n * url + intrinsic dimensions — and a cached page keeps its images during a\n * backend outage. Optional so existing custom sources stay valid; without it,\n * asset-typed props fall back to their raw value (the renderer's visible\n * degradation), never a crash.\n *\n * EC-632: resolved in `locale` (absent → the source's default locale) — a localized\n * asset-collection field (alt, …) carries its value in that locale, with the default\n * locale as its fallback. The route helper passes the request's locale, so a page bakes\n * its assets in the locale it renders in.\n */\n assets?(locale?: Locale): readonly ResolvedAsset[]\n}\n\n/**\n * Merge accessor cache tags into one deterministic set (sorted, deduplicated)\n * — used by the route helper to combine the `resolvePage` tags with tags\n * added by the host's payload factory (e.g. a `listDocuments` call). The\n * merged set is what the EC-146 `onTags` hook receives.\n */\nexport function mergeCacheTags(first: CacheTags, ...rest: readonly CacheTags[]): CacheTags {\n const all = [first, ...rest]\n return cacheTags({\n docs: all.flatMap((t) => t.docs),\n collections: all.flatMap((t) => t.collections),\n routes: all.flatMap((t) => t.routes),\n assets: all.flatMap((t) => t.assets),\n locale: first.locale,\n })\n}\n","import { documentKey, parseLocaleConfig, readLiveRedirects } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfigInput,\n Perspective,\n RedirectRecord,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport {\n assetMetaDriftIssue,\n assetWithoutUrlIssue,\n createContentClient,\n createContentContext,\n isDocumentHistory,\n resolvedAsset,\n} from '@elytracms/core/content'\nimport type { ContentClient, ContentDocumentSource, ContentLookup } from '@elytracms/core/content'\nimport { assetMetaDrift, assetMetaLens, assetReadLocale, redirectRecordKind } from '@elytracms/core/persistence'\nimport type { AssetRecord, PersistenceAdapter } from '@elytracms/core/persistence'\nimport type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport type { ContentSource } from './source'\n\n/**\n * Bridge from a `PersistenceAdapter` to the synchronous `ContentLookup` the\n * accessors (EC-143) read through: prefetch the project's collections,\n * documents, assets, and graphs ONCE (async), then serve every accessor call\n * synchronously from memory. This is how `apps/example-site` reads the seeded\n * in-memory fixture project; a Convex-backed `ContentSource` (EC-145/146)\n * replaces this bridge without touching the route helper.\n *\n * The assembly half is exposed separately as {@link createStaticContentSource}\n * (EC-156): a host that already holds the prefetched data as plain values —\n * e.g. a delivery-snapshot payload fetched over Convex's public HTTP query\n * API — assembles the exact same `ContentSource` without faking an adapter.\n */\n\nexport interface ContentSnapshotOptions {\n /**\n * The project's collection definitions — **required** (EC-583). The snapshot's\n * lookup resolves localized fields and a collection's declared `presentation`\n * through them, and the persistence contract holds no schema any more (this used to\n * read `adapter.cms.findSchema`, which in a seeded host was the engine sample's JSON\n * projection unless the host re-saved its own). Hand it the config's collections —\n * the same ones the studio and the seed read.\n */\n collections: readonly CollectionDef[]\n /** Route records served by `resolvePage` (EC-019). */\n routes?: readonly RouteRecord[]\n /** Redirect records served by `resolvePage` (EC-019/020). */\n redirects?: readonly RedirectRecord[]\n /**\n * Locale configuration — **required**, never defaulted (EC-530). The default\n * locale is the storage key every localized value is written under, so a\n * fabricated `en` is a data migration to undo, not a config edit: content\n * authored under one locale and served under another, silently (EC-512\n * measured exactly that). EC-490 made a config declaring no locales an error\n * and EC-519 collapsed the two declarations into one; this was the last place\n * in the chain still inventing the value, and it sits in the package that\n * resolves the URLs. Hand it the project's `LocaleConfig` — the one the\n * config declares. Parsed here (EC-580): a `default` outside `locales` throws.\n */\n locales: LocaleConfigInput\n}\n\nexport interface ContentSnapshot extends ContentSource {\n readonly projectId: string\n}\n\n/**\n * Plain prefetched data for {@link createStaticContentSource} (EC-156): the\n * same inputs `createContentSnapshot` reads from a `PersistenceAdapter`, as\n * already-loaded values. Everything content-bearing is optional — an absent\n * piece simply resolves to \"not visible\", with the runtime's explicit\n * fallbacks (never a crash).\n */\nexport interface StaticContentData {\n projectId: string\n /** Locale configuration — **required**, never defaulted (see EC-530 above); parsed (EC-580). */\n locales: LocaleConfigInput\n /** Route records served by `resolvePage` (EC-019). */\n routes?: readonly RouteRecord[]\n /** Redirect records served by `resolvePage` (EC-019/020). */\n redirects?: readonly RedirectRecord[]\n /**\n * The project's collection definitions (every host path hands the resolved config's). They\n * are also the asset metadata's lens (EC-659): the asset collection's `localized` fields\n * are the only ones a stored per-locale value is read for. ABSENT means no schema — no\n * field is localized, so every stored translation is drift: never applied (the shared\n * value is served), each one named on `sourceIssues`.\n */\n collections?: readonly CollectionDef[]\n /**\n * Stored document sources. Either a bare `CmsDocument` (gated by its own\n * `state`) or a `DocumentHistory` carrying the live draft + the pinned\n * published snapshot (EC-224). Perspective gating happens in the pure\n * accessors (`selectPerspective`, EC-142): a `published` client sees a\n * history's pinned snapshot (or nothing), and never a bare `draft` row.\n */\n documents?: readonly ContentDocumentSource[]\n assets?: readonly AssetRecord[]\n /**\n * Resolve a serve URL for assets without a stored `url` (`undefined` when there is\n * none). EC-633: no default — an asset with no URL is no resolved asset (the ONE\n * constructor, `resolvedAsset`, decides it): it renders the canvas's empty state, its\n * field resolves to `null` with an `asset-without-url` issue, and this source names it\n * on `sourceIssues`. The placeholder this used to default to (`asset://unresolved/…`)\n * rendered a broken image on the site.\n */\n assetUrl?: (asset: AssetRecord) => string | undefined\n /**\n * EC-585 — the stored rows the host could not read (`parseDeliveredDocuments`), handed\n * to every client of this source as its `sourceIssues` (and in the results they\n * concern). A malformed row is not delivered, and never silently.\n */\n sourceIssues?: readonly CmsValidationIssue[]\n}\n\n/**\n * Assemble a `ContentSource` from plain prefetched data — the pure half of\n * {@link createContentSnapshot}, shared with hosts that fetched the data\n * themselves (e.g. the EC-156 live delivery snapshot). Accessor semantics are\n * byte-identical across both paths: the same `ContentLookup` feeds the same\n * pure `@elytracms/core/content` client.\n */\nexport function createStaticContentSource(data: StaticContentData): ContentSnapshot {\n // EC-580: THE delivery parse of the declared locale config into the branded\n // `LocaleConfig` every reader below holds — every source (empty, seeded, live)\n // is assembled here, so a `default` outside `locales` cannot reach a client. It\n // throws: a host builds its empty source at boot, so that is where it fails.\n const locales = parseLocaleConfig(data.locales)\n const routes = data.routes ?? []\n const redirects = data.redirects ?? []\n // EC-633: a record's serve URL — its stored `url`, else the host's lookup, else none.\n const assetUrl = (asset: AssetRecord): string | undefined => asset.url ?? data.assetUrl?.(asset)\n\n const collections = new Map<string, CollectionDef>()\n for (const collection of data.collections ?? []) collections.set(collection.id, collection)\n\n const documents = new Map<string, ContentDocumentSource>()\n const byCollection = new Map<string, ContentDocumentSource[]>()\n for (const source of data.documents ?? []) {\n const doc = isDocumentHistory(source) ? source.draft : source\n documents.set(documentKey(doc), source)\n const list = byCollection.get(doc.collection) ?? []\n list.push(source)\n byCollection.set(doc.collection, list)\n }\n\n const assets = new Map<string, AssetRecord>()\n for (const record of data.assets ?? []) assets.set(record.id, record)\n // EC-659: the asset collection's lens — every asset this source resolves (document fields,\n // the baked assets) reads its stored translations through it. No collections → no schema →\n // nothing localized (see `StaticContentData.collections`).\n const metaLens = assetMetaLens(data.collections ?? [])\n // EC-633: every record this source cannot serve a file for is named once, on the source\n // (the EC-585/EC-628 channel every client carries) — the site shows its empty state, and\n // this says why. EC-659: so is every stored translation the lens does not read (once per\n // value) — the shared value is served, the residue named, the data kept.\n const sourceIssues: CmsValidationIssue[] = [\n ...(data.sourceIssues ?? []),\n ...[...assets.values()].filter((record) => !assetUrl(record)).map((record) => assetWithoutUrlIssue(record.id)),\n ...[...assets.values()].flatMap((record) =>\n assetMetaDrift(record, metaLens).map((drift) => assetMetaDriftIssue(record.id, drift)),\n ),\n ]\n\n const lookupFor = (_perspective: Perspective): ContentLookup => ({\n collection: (id) => collections.get(id),\n document: (ref: DocumentRef) => documents.get(documentKey(ref)),\n documents: (collectionId) => byCollection.get(collectionId) ?? [],\n asset: (id) => assets.get(id),\n assetUrl,\n })\n\n const client = (init: { perspective: Perspective; locale?: Locale }): ContentClient =>\n createContentClient({\n lookup: lookupFor(init.perspective),\n context: createContentContext({\n locale: init.locale ?? locales.default,\n perspective: init.perspective,\n locales,\n assetMetaLens: metaLens,\n }),\n routes,\n redirects,\n ...(sourceIssues.length > 0 ? { sourceIssues } : {}),\n })\n\n return {\n projectId: data.projectId,\n locales,\n client,\n routes,\n // EC-195: the delivery-shaped assets, so the renderer can resolve an\n // `asset`-typed Image prop (an asset id) to a usable url + intrinsic\n // dimensions. Baked into the (serializable) render outcome by the route\n // helper, so a cached page keeps its images during a backend outage.\n // EC-632: in the requested locale (the route helper passes the request's), so a\n // localized alt is baked in the locale the page renders in.\n // EC-633: a record with no URL is no resolved asset — not baked, so the page renders\n // the empty state the canvas renders for it (and `sourceIssues` names it).\n assets: (locale?: Locale) =>\n [...assets.values()].flatMap((record) => {\n const asset = resolvedAsset(record, assetUrl(record), assetReadLocale(locales, metaLens, locale))\n return asset ? [asset] : []\n }),\n // EC-499: the schema the route helper reads a collection's declared\n // `presentation` from (which field is the delivery body, which component\n // wraps it). Already prefetched for the lookup above — exposing it costs\n // nothing and keeps the declaration reachable without a second wiring seam.\n ...(data.collections ? { collections: data.collections } : {}),\n }\n}\n\n/**\n * Prefetch a project's content from a `PersistenceAdapter` into an in-memory\n * `ContentSource`. The snapshot is a point-in-time view: load once per\n * request (or memoize across requests for fixture-backed sites) — accessors\n * never reach back to the adapter.\n */\nexport async function createContentSnapshot(\n adapter: PersistenceAdapter,\n projectId: string,\n options: ContentSnapshotOptions,\n): Promise<ContentSnapshot> {\n // --- Prefetch (the only async part) ------------------------------------\n const documentRecords = await adapter.cms.listDocuments(projectId)\n const versionRecords = await adapter.cms.listAllDocumentVersions(projectId)\n const assetRecords = await adapter.assets.listAssets(projectId)\n // EC-633 follow-on: the SERVE URL of a record with no stored `url`, resolved here, by the\n // blob adapter's contract — its live lookup (`resolveUrl`: `undefined` when the blob is\n // gone), or, for a backend without one, its synchronous stable `url` (\"backends with\n // synchronous stable URLs can rely on url\"). Never the synchronous LOCATOR of a backend\n // that has a lookup (`convex://storage/…`, `memory://…` — no browser loads it): no serve\n // URL → no resolved asset, named on `sourceIssues` (the static source's one answer).\n const blobs = adapter.blobs\n const servedUrls = new Map<string, string | undefined>()\n await Promise.all(\n assetRecords\n .filter((record) => !record.url)\n .map(async (record) => {\n const url = blobs.resolveUrl\n ? await blobs.resolveUrl(record.storageKey).catch(() => undefined)\n : blobs.url(record.storageKey)\n servedUrls.set(record.id, url || undefined)\n }),\n )\n // Redirects are editorial DATA (EC-303): read the ENABLED ones from the adapter's\n // redirect record kind (EC-616; projected to the routing `RedirectRecord`), like documents/assets\n // — NOT from config. An explicit `options.redirects` still overrides (fixtures/tests).\n const redirectRecords = await adapter.records(redirectRecordKind).list(projectId)\n // THE live rule — the delivery snapshot and the studio's URL map read it too (EC-627); a stored\n // redirect it cannot read is withheld AND named on this source (EC-628).\n const { redirects, issues: redirectIssues } = readLiveRedirects(redirectRecords)\n\n // Hybrid sources (EC-224): a `publishedVersion` pointer yields a\n // `DocumentHistory` (draft = the live row, published = the pinned snapshot);\n // an unpinned record stays a bare `CmsDocument` gated by its `state`.\n // Backward-compatible — pre-pointer data resolves exactly as before.\n const versionsByKey = new Map<string, Map<number, CmsDocument>>()\n for (const record of versionRecords) {\n const key = `${record.collection}:${record.docId}`\n let byVersion = versionsByKey.get(key)\n if (!byVersion) {\n byVersion = new Map()\n versionsByKey.set(key, byVersion)\n }\n byVersion.set(record.version, record.snapshot)\n }\n const documents: ContentDocumentSource[] = documentRecords.map((record) => {\n if (record.publishedVersion === undefined) return record.document\n // A dangling pin under-serves to `null` for `published` — never the draft.\n const published = versionsByKey\n .get(`${record.document.collection}:${record.document.id}`)\n ?.get(record.publishedVersion)\n return { draft: record.document, published, versions: [] }\n })\n\n // --- Assemble via the shared pure path ----------------------------------\n return createStaticContentSource({\n projectId,\n // EC-628: only for the stored redirects this source serves (an explicit override replaces them).\n ...(options.redirects === undefined && redirectIssues.length > 0 ? { sourceIssues: redirectIssues } : {}),\n locales: options.locales,\n ...(options.routes ? { routes: options.routes } : {}),\n redirects: options.redirects ? [...options.redirects] : redirects,\n collections: options.collections,\n documents,\n assets: assetRecords,\n assetUrl: (asset) => servedUrls.get(asset.id),\n })\n}\n","import type { Metadata, ResolvedMetadata } from 'next'\nimport { DEFAULT_SEO_FIELD, SEO_SUBFIELDS } from '@elytracms/core/cms-core'\nimport type { ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\n\n/**\n * A resolved document → Next.js `Metadata` (EC-171, reshaped by EC-187, and by EC-590 —\n * EC-565 Round 3 \"3g, extensibility\"): **`seoMetadata(doc, ctx)`**, the kit's pure,\n * exported SEO reader. The route's `generateMetadata` calls it (the default), so a\n * project WRAPS it (`{ ...seoMetadata(doc, ctx), other }` for its own sub-fields),\n * REPLACES it, or ignores it.\n *\n * What it reads — never a dictated field name:\n * - the document's **SEO object**: the field under its collection's SEO ROLE\n * (`seoField`, default a field named `seo`; the engine's `seoFields()` declares it).\n * Its sub-field names are the contract ({@link SEO_SUBFIELDS}); a sub-field the object\n * omits emits nothing, a sub-field the project added is theirs to read.\n * - the document's **display title**: the ONE `resolveDocumentTitle` over its TITLE ROLE\n * (`titleField`, default `title` → `name`) — the resolve result carries it\n * (`result.title`), so a function `titleField` titles the page too.\n *\n * Mapping:\n * - `title`: `seo.title` → the display title.\n * - `description`: `seo.description`.\n * - `openGraph`: title (`seo.ogTitle` → the effective title), description\n * (`seo.ogDescription` → `seo.description`), image (`seo.image`) — and `locale` from the\n * page's resolved locale when, and only when, the code carries a territory (EC-531,\n * EC-610). The site-wide half of Open Graph (`type`, `siteName`, the site image) is the\n * LAYOUT's; the canvas route merges this object over the layout's\n * (`mergeMetadataOverParent`), so a page states only what is its own. Site-level SEO\n * defaults live on the project's own settings document and are read by its layout —\n * this reader needs none (EC-590 (b): nothing here ever read a settings document).\n * - `robots`: `seo.noindex` → `{ index: false }`.\n * - `alternates.canonical`: `seo.canonical` (EC-228), verbatim.\n *\n * Image resolution (EC-202/EC-266): `seo.image` is an asset field, delivered as a resolved\n * asset object (`{ id, url, … }`) → its `url`; a string is a passthrough URL or a raw asset\n * id resolved against the page's baked `assets`; anything unresolvable is dropped — an\n * `og:image` must carry a real URL, never an internal id.\n *\n * `hreflang` alternates are intentionally NOT derived here (a per-page translation map is\n * the sitemap's — EC-517).\n */\nexport interface SeoMetadataContext {\n /** The SEO role field name — `result.seoField`. Default: the conventional `seo`. */\n seoField?: string | null\n /** The display title — `result.title` (the title role, resolved). */\n title?: string | null\n /** The page's resolved locale (`result.tags.locale`) — for `og:locale`. */\n locale?: string\n /** The page-scoped baked assets (a raw asset-id `image` resolves against them). */\n assets?: readonly ResolvedAsset[]\n}\n\n/** The {@link SeoMetadataContext} of a resolve result — the default route's one line. */\nexport function seoContextOf(\n result: ResolvePageResult,\n assets: readonly ResolvedAsset[] = [],\n): SeoMetadataContext {\n return {\n seoField: result.seoField ?? null,\n title: result.title ?? null,\n locale: result.tags.locale,\n assets,\n }\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Resolve the SEO object's `image` (an `asset` field, `seoFields()`) to a URL: a\n * delivery-resolved asset OBJECT → its `url` (EC-266); a raw asset-id string → resolved\n * against the baked assets; anything unresolvable → dropped (EC-202). EC-587 / 2h: an\n * asset value never holds a URL, so a URL-shaped string is not passed through.\n */\nfunction resolveSeoImage(value: unknown, assets: readonly ResolvedAsset[]): string | null {\n if (value && typeof value === 'object') {\n const url = (value as { url?: unknown }).url\n return typeof url === 'string' && url.length > 0 ? url : null\n }\n if (typeof value !== 'string' || value.length === 0) return null\n return assets.find((asset) => asset.id === value)?.url ?? null\n}\n\n/**\n * EC-531, narrowed by EC-610 (owner ruling 3, 2026-10-23): `og:locale` ONLY in the\n * shape Open Graph specifies — `language_TERRITORY` with an underscore (`de_DE`),\n * where BCP 47 writes `de-DE`. The value is the page's RESOLVED locale\n * (`result.tags.locale`, the same fact the router used to pick the content). A\n * bare language code (`de`) is outside that format, so the kit emits nothing for\n * it and the layout's own value stands through the merge: inventing a territory\n * (`de` → `de_DE`) would be wrong exactly where it matters (Austria, the UK,\n * Brazil, Latin America), and a wrong valid value looks deliberate.\n */\nexport function openGraphLocale(locale: string): string | null {\n return locale.includes('-') ? locale.replace(/-/g, '_') : null\n}\n\n/** The document's SEO object (the field under its SEO role), or an empty record. */\nexport function seoObjectOf(\n doc: Record<string, unknown> | null | undefined,\n seoField: string | null | undefined = DEFAULT_SEO_FIELD,\n): Record<string, unknown> {\n if (!doc || seoField === null) return {}\n const value = doc[seoField ?? DEFAULT_SEO_FIELD]\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}\n}\n\n/** The kit's SEO reader — see the module docs. Pure: data in, `Metadata` out. */\nexport function seoMetadata(\n doc: Record<string, unknown> | null | undefined,\n ctx: SeoMetadataContext = {},\n): Metadata {\n const seo = seoObjectOf(doc, ctx.seoField)\n const assets = ctx.assets ?? []\n\n const seoTitle = str(seo[SEO_SUBFIELDS.title])\n const description = str(seo[SEO_SUBFIELDS.description])\n const ogTitle = str(seo[SEO_SUBFIELDS.ogTitle])\n const ogDescription = str(seo[SEO_SUBFIELDS.ogDescription])\n const image = resolveSeoImage(seo[SEO_SUBFIELDS.image], assets)\n const noindex = seo[SEO_SUBFIELDS.noindex] === true\n const canonical = str(seo[SEO_SUBFIELDS.canonical])\n\n // The display title: the title ROLE, resolved by the content client (`result.title`);\n // without it, the role's default names.\n const docTitle = ctx.title !== undefined ? str(ctx.title) : (str(doc?.['title']) ?? str(doc?.['name']))\n const title = seoTitle ?? docTitle\n\n const metadata: Metadata = {}\n if (title !== null) metadata.title = title\n if (description) metadata.description = description\n\n if (doc) {\n const effectiveOgTitle = ogTitle ?? title\n const effectiveOgDescription = ogDescription ?? description\n const ogLocale = ctx.locale !== undefined ? openGraphLocale(ctx.locale) : null\n metadata.openGraph = {\n ...(effectiveOgTitle !== null ? { title: effectiveOgTitle } : {}),\n ...(effectiveOgDescription ? { description: effectiveOgDescription } : {}),\n ...(image ? { images: [{ url: image }] } : {}),\n ...(ogLocale !== null ? { locale: ogLocale } : {}),\n }\n }\n\n if (noindex) metadata.robots = { index: false }\n\n // EC-228: a page-declared canonical URL. Passed through verbatim (absolute or\n // root-relative); Next resolves it against `metadataBase` when relative.\n if (canonical) metadata.alternates = { canonical }\n\n return metadata\n}\n\n/**\n * EC-610 (owner ruling 1, 2026-10-13): **the site provides the default tags, and\n * the page's tags are merged on top** — a value the page states overrides the\n * site's, a value the page leaves empty falls back to the site's.\n *\n * Next merges metadata SHALLOWLY (read: `generate-metadata.md`, \"Merging\"): a\n * page segment's `openGraph` object REPLACES the layout's whole `openGraph`, so a\n * site-wide `type`, `siteName`, site image or `locale` declared once in the root\n * layout never reached a canvas page, and both real sites hand-rolled around it\n * differently (a page hook on one, a post bypass on the other). This is the one\n * merge, applied by the canvas route to whatever builds the page's metadata (the\n * kit's default or the host's `metadata` option): every object-valued key the\n * page sets (`openGraph`, `twitter`, `robots`, `alternates`, …) is merged one\n * level deep over the parent's RESOLVED value; a key the page does not set is left\n * to Next, which inherits it from the parent as before; a scalar or array the\n * page sets wins outright.\n *\n * The parent's resolved shapes spread back cleanly: Next hands `generateMetadata`\n * a `ResolvedMetadata` whose URLs are already strings, whose `images` are\n * descriptor objects and whose titles are `{ absolute, template }` — all of which\n * `Metadata` accepts (Next's own \"extend rather than replace\" example spreads\n * `(await parent).openGraph?.images`).\n */\nexport function mergeMetadataOverParent(\n parent: ResolvedMetadata | null | undefined,\n page: Metadata,\n): Metadata {\n if (!parent) return page\n const merged: Record<string, unknown> = { ...page }\n for (const [key, pageValue] of Object.entries(page)) {\n const parentValue = (parent as Record<string, unknown>)[key]\n if (isPlainObject(pageValue) && isPlainObject(parentValue)) {\n merged[key] = { ...parentValue, ...pageValue }\n }\n }\n return merged as Metadata\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { cacheTags } from '@elytracms/core/content'\nimport { splitLocalePath } from './locale-url'\nimport type {\n CacheTags,\n ContentClient,\n ListDocumentsQuery,\n ResolvedAsset,\n ResolvedDocument,\n ResolvePageResult,\n} from '@elytracms/core/content'\nimport type { Clock } from '@elytracms/core/determinism'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { propFieldLookupOf } from '@elytracms/core/component-registry'\nimport {\n collectReferencedAssetIds,\n collectReferencedListings,\n collectReferencedRelations,\n collectReferencedRichTextLinks,\n collectRichTextRefsInValue,\n documentBodyCompositionNodes,\n documentBodyRichTextValues,\n listingRequestOf,\n richTextEmbedsInValue,\n} from '@elytracms/core/runtime-renderer'\nimport type { ListingQuery, RelationTarget, ResolveRelation } from '@elytracms/core/runtime-renderer'\nimport { deliverCompositionProps, presentationBodyFields, SEO_SUBFIELDS } from '@elytracms/core/cms-core'\nimport type { CmsValidationIssue, Locale, Perspective } from '@elytracms/core/cms-core'\nimport { seoObjectOf } from './metadata'\nimport { mergeCacheTags } from './source'\nimport type { ContentSource } from './source'\nimport { bodyItemsOf } from './canvas-renderer'\nimport type { BakedListing, DeliveryPresentation } from './canvas-renderer'\n\n/**\n * The pure half of the catch-all route helper (EC-144): URL → outcome, with\n * no Next.js imports — so redirects, 404s, locale handling, asset/relation/\n * listing/link baking, and tag merging are all unit-testable as plain\n * functions. `route.tsx` maps\n * each outcome onto `redirect()` / `permanentRedirect()` / `notFound()` /\n * `<CanvasRenderer />`.\n */\n\n/** What the route helper tells a host callback about the request it is serving. */\nexport interface CanvasRequestInfo {\n /** The raw request path (including any locale prefix). */\n url: string\n perspective: Perspective\n}\n\n/**\n * What a host's `wrap` function is told about the request it is wrapping\n * (EC-524 — see `CanvasRouteOptions.wrap`).\n *\n * Every field is something the route already had in hand at the wrap point:\n * nothing is gathered for this seam, and nothing was invented for it.\n */\nexport interface CanvasWrapContext extends CanvasRequestInfo {\n /**\n * The collection of the document this URL resolved to — **the branch key**.\n * `undefined` when the resolution carried no document, so a host branching on\n * a collection id naturally wraps nothing there (which is what the removed\n * `presentation.wrapper` declaration did too: it only applied with a document).\n */\n collection?: string\n /**\n * The resolved document. Verbatim the `document` prop the removed wrapper\n * component received, so a wrapper ported to this seam needs no other source\n * for its field-level chrome (the title, a byline, a date).\n */\n document: ResolvedDocument | null\n /**\n * The whole resolution — the same value the `metadata` callback already\n * receives, so this is the established host-callback argument on this route\n * rather than a new surface. It carries `dynamicParams`, `route`,\n * `localeFallback` and `issues` for a wrapper that needs more than the\n * document (EC-499 recorded wanting exactly that and could not have it across\n * a serializable declaration).\n */\n result: ResolvePageResult\n /**\n * The request's locale — the one the page resolved and renders in (EC-720). A NAME for\n * `result.tags.locale`, the value the route split from the URL once and that the page,\n * its metadata, listings, assets and beacon already read: never a second derivation, so\n * a wrapper's chrome strings and the content it wraps cannot disagree. Hand it to\n * `elytra.loadGlobals({ locale })` for chrome the wrapper reads itself.\n */\n locale: Locale\n}\n\n/**\n * PURE core (EC-524): build the wrap context from a render outcome's resolution\n * and the request info. Extracted from `route.tsx` so the derivation — above all\n * \"no document ⇒ no collection ⇒ a collection-branching host wraps nothing\" — is\n * testable without Next's request APIs.\n */\nexport function canvasWrapContext(\n result: ResolvePageResult,\n info: CanvasRequestInfo,\n): CanvasWrapContext {\n const collection = result.document?.collection\n return {\n ...info,\n ...(collection !== undefined ? { collection } : {}),\n document: result.document,\n result,\n locale: result.tags.locale,\n }\n}\n\n/**\n * EC-618 — what the route's `analytics` slot is told about the page it renders on: the\n * resolved document (the content model, not the URL — slug changes and locale paths then\n * attribute correctly), the locale it rendered in, the request path and the status. Plain\n * serializable values, so the slot may be a client component.\n */\nexport interface CanvasBeaconProps {\n /** The request path as served (including any locale prefix). */\n path: string\n /** 200: the route renders the slot only on a rendered page (a 404 never reaches it). */\n status: 200\n /** The locale the page rendered in. */\n locale: string\n /** The resolved document's collection and id — absent when the URL resolved to no document. */\n collection?: string\n documentId?: string\n}\n\n/** PURE core (EC-618): the slot's props from a render outcome's resolution and the request info. */\nexport function canvasBeaconProps(result: ResolvePageResult, info: CanvasRequestInfo): CanvasBeaconProps {\n const document = result.document\n return {\n path: info.url,\n status: 200,\n locale: result.tags.locale,\n ...(document ? { collection: document.collection, documentId: document.id } : {}),\n }\n}\n\nexport type CanvasPageOutcome =\n | { kind: 'redirect'; target: string; permanent: boolean; tags: CacheTags }\n | { kind: 'not-found'; tags: CacheTags }\n | {\n kind: 'render'\n result: ResolvePageResult\n /**\n * The delivery-shaped assets the page may reference (EC-195). Baked into\n * the outcome (serializable) so the renderer resolves `asset`-typed Image\n * props to a url + intrinsic dimensions, and a cached page keeps its\n * images during a backend outage.\n */\n assets: readonly ResolvedAsset[]\n /**\n * The composition-node `relation` props this page references, populated to\n * depth-1 delivery shape and keyed `collection:id` (EC-254). Baked into the\n * outcome (serializable) so `<CanvasRenderer />` rebuilds\n * `RenderContext.resolveRelation` from it inside the RSC — a function (the\n * content client) cannot ride the data cache or cross the RSC boundary, so\n * the data is baked exactly as the page-scoped `assets` list is.\n */\n relations: Record<string, RelationTarget>\n /**\n * The documents each `listing` block on this page resolved to (EC-255),\n * keyed by serialized query. Baked into the outcome (serializable) so\n * `<CanvasRenderer />` rebuilds `RenderContext.resolveListing` from it inside\n * the RSC — like `relations`, because the content client that runs the query\n * cannot ride the data cache or cross the RSC boundary. A listing depends on\n * collection MEMBERSHIP (a product joining/leaving the category), so its\n * invalidation rides the per-query `listDocuments` tags merged into `tags`.\n *\n * EC-581 6a: a query `listDocuments` refused (a non-filterable field, an\n * operand of the wrong type, a bad limit) is baked as `{ errors }` — the\n * structured `ContentQueryError`s — and `<CanvasRenderer />` shows them above\n * the block (`data-ec-fallback=\"invalid-listing\"`), never an empty grid\n * indistinguishable from \"no matching content\".\n */\n listings: Record<string, BakedListing>\n /**\n * EC-581 6a: whether resolving this page READ THE CLOCK — a listing on it\n * binds an operand to `now` (e.g. \"events whose date is still ahead\"). Such a\n * page's content changes as time passes, not only on publish: a cached entry\n * of it is correct for the instant it was resolved and goes stale when a\n * listed document crosses its `now` boundary, which no publish tag covers. A\n * cache layer must bound that entry's life (the delivery cache — EC-515 —\n * reads this). `false` for every page with no `now`-bound listing.\n */\n readsNow: boolean\n /**\n * The public URL path of every document a rich-text prop on this page LINKS\n * to (EC-291), keyed `collection:id`. Baked into the outcome (serializable) so\n * `<CanvasRenderer />` rebuilds `RenderContext.resolveLink` from it inside the\n * RSC — like `relations`, because the content client that derives the URL\n * (`resolveLink` → `urlForDocument`) cannot ride the data cache or cross the\n * RSC boundary. A link depends on the target's IDENTITY (its slug/parent →\n * path), so its invalidation rides the per-link doc tags merged into `tags`:\n * renaming, re-parenting, or first-publishing a linked document re-bakes the\n * embedding page's href.\n */\n links: Record<string, string>\n /**\n * The resolved document's collection-declared delivery presentation\n * (EC-499): which field(s) are the render body. Plain, serializable data —\n * handed to `<CanvasRenderer />` so ONE catch-all renders every\n * collection's body, whatever its shape. Absent when the collection\n * declares nothing (or the source exposes no collections), and the EC-458\n * value-shape picker then decides exactly as before.\n */\n presentation?: DeliveryPresentation\n tags: CacheTags\n }\n\n/**\n * The outcome a page renders (EC-722): what `route.load(url)` returns once the route has\n * answered a redirect or a not-found itself — a caller of it never branches on `kind`.\n */\nexport type CanvasRenderOutcome = Extract<CanvasPageOutcome, { kind: 'render' }>\n\n/** Build the request path from a Next catch-all `slug` param. */\nexport function pathFromSlug(slug: readonly string[] | undefined): string {\n return '/' + (slug ?? []).join('/')\n}\n\n/**\n * Derive the request locale from a leading URL segment (`/de/about` → locale\n * `de`, path `/about`). EC-486 moved the implementation to `./locale-url`, so\n * this direction and the sitemap's inverse are one definition rather than two\n * that can drift; re-exported here because it is a published API surface.\n */\nexport { splitLocalePath }\n\nexport interface ResolveCanvasPageOptions {\n /**\n * Component registry (EC-205): when provided, the baked {@link CanvasPageOutcome}\n * `assets` are PAGE-SCOPED to exactly the asset ids this page's composition +\n * layout resolve through `resolveAsset` — not the whole project asset list,\n * which otherwise rides in every page's cache entry + RSC payload. Omitted →\n * all source assets (back-compat). Scoping mirrors the renderer's asset\n * resolution exactly, so no referenced image is ever dropped.\n */\n registry?: ComponentRegistry\n /**\n * The clock a `{ now: true }` listing operand reads (EC-581 6a) — an ISO-8601\n * UTC instant, read at most once per resolution so every listing on the page\n * compares against the SAME instant. Defaults to the system clock; tests and\n * fixtures inject a fixed one. Taken as a FUNCTION, read during resolution, so a\n * `now` value is never an argument a cache keys on.\n */\n clock?: Clock\n}\n\n/** The real clock, used only at this boundary when the caller injects none. */\nconst systemClock: Clock = () => new Date().toISOString()\n\n/**\n * Page-scope the baked assets to those the page actually resolves (EC-205): the\n * static `asset`-typed props of its composition + selected layout tree. Document\n * asset FIELDS resolve into the document separately and do not use this list, so\n * they do not widen it.\n */\nfunction scopeAssetsToPage(\n allAssets: readonly ResolvedAsset[],\n registry: ComponentRegistry,\n result: ResolvePageResult,\n body: PageBody,\n resolveRelation: ResolveRelation,\n): readonly ResolvedAsset[] {\n if (allAssets.length === 0) return allAssets\n const ids = new Set<string>()\n // EC-270: pass `resolveRelation` so the collector follows a Reusable block's\n // `element` pick into the referenced document and page-scopes the asset ids\n // inside its transcluded `content` — otherwise a transcluded image is baked\n // nowhere and renders empty on the host (the relation bake already transcludes).\n const seen = new Set<string>()\n for (const node of body.nodes) {\n collectReferencedAssetIds(node, registry, ids, resolveRelation, seen)\n }\n // EC-499: a rich-text BODY (not just a component's rich-text prop) pins assets\n // too — an inline image carries `attrs.ref = { type:'asset', id }`. Its\n // component-node siblings are already in `body.nodes`; this collects the pins\n // that sit directly in the prose, so the host bakes them instead of the page\n // rendering an unresolved-ref placeholder where the image should be.\n for (const value of body.richTexts) {\n collectRichTextRefsInValue(value, { onAsset: (id) => ids.add(id) })\n }\n // EC-202: the OG image is an asset the page uses (for its `og:image` meta), so\n // it joins the page-scoped set — (EC-227) tagged for invalidation when that\n // asset record is edited. EC-266: the SEO object's `image` (an asset field, EC-590) is delivered\n // as a ResolvedAsset OBJECT ({ id, url }); a string is a raw id (or a passthrough\n // URL, which has no asset id to tag). Take the id from whichever shape.\n const ogImage = seoObjectOf(result.document, result.seoField)[SEO_SUBFIELDS.image]\n const ogImageId =\n typeof ogImage === 'string'\n ? ogImage\n : ogImage && typeof ogImage === 'object' && typeof (ogImage as { id?: unknown }).id === 'string'\n ? (ogImage as { id: string }).id\n : null\n if (ogImageId) ids.add(ogImageId)\n return ids.size === 0 ? [] : allAssets.filter((asset) => ids.has(asset.id))\n}\n\n/**\n * One request's resolved delivery body (EC-499), split into what each baker\n * needs: the composition NODES to walk (EC-254 relation scan, EC-205 asset\n * scoping, EC-255 listings, EC-291 links) and the rich-text REGIONS whose own\n * refs must be baked alongside them.\n */\ninterface PageBody {\n /**\n * Every composition node the page renders: the body's own component nodes AND\n * the components EMBEDDED in its rich-text regions (EC-189/EC-434 — the\n * ambient embed renderer renders those on delivery, so their relations,\n * listings and assets have to be baked or they render empty).\n */\n nodes: ComponentNode[]\n /** The body's rich-text regions, for the pinned-asset / internal-link bake. */\n richTexts: unknown[]\n}\n\n/** Split a resolved document's declared body into the bakers' two views (EC-499). */\nfunction pageBodyOf(result: ResolvePageResult, presentation?: DeliveryPresentation): PageBody {\n const items = bodyItemsOf(result.document, presentation)\n const richTexts = documentBodyRichTextValues(items)\n return {\n nodes: [\n ...documentBodyCompositionNodes(items),\n ...richTexts.flatMap((value) => richTextEmbedsInValue(value)),\n ],\n richTexts,\n }\n}\n\n/**\n * The collection-declared delivery presentation for the document this URL\n * resolved to (EC-499), normalized for the render outcome — `undefined` when the\n * source exposes no collections, the collection is unknown, or it declares\n * nothing (the EC-458 shape picker then decides, unchanged).\n *\n * EC-524: body fields only. The detail wrapper this used to carry alongside them\n * is host code now, applied by `createCanvasRoute`'s `wrap` around the whole\n * renderer, so nothing chrome-shaped crosses the outcome.\n */\nfunction presentationFor(\n source: ContentSource,\n result: ResolvePageResult,\n): DeliveryPresentation | undefined {\n const collectionId = result.document?.collection\n if (!collectionId || !source.collections) return undefined\n const collection = source.collections.find((candidate) => candidate.id === collectionId)\n if (!collection?.presentation) return undefined\n const body = presentationBodyFields(collection)\n return body.length > 0 ? { body: [...body] } : undefined\n}\n\n/**\n * EC-662 — THE delivery projection over the page's component props, run ONCE here, before\n * any baker and the renderer (Codex round 3, F02 residual + F03 diagnostic). Every\n * composition value the resolved document delivers (its body — the converged sequence or\n * a legacy node list — whichever fields hold one) has the props of every node in it\n * projected at every depth (`deliverCompositionProps`): a tolerated stored form becomes\n * its canonical twin, a value the prop's rule rejects or a reference to a collection the\n * prop does not target is withheld — and each of those carries its located issue\n * (`[field, 'content', i, 'props', name, …]`) onto the result's `issues`, where a page's\n * findings already are. The bakers below walk, and `<CanvasRenderer />` renders, the\n * PROJECTED document; the renderer's own pass over it is the identity (the projection\n * delivers what it is given), so the page is judged once and renders what was judged.\n *\n * Without a registry no prop can be judged (no manifest says what a prop is): the props are\n * delivered as stored and a `props-not-judged` warning says so — never silence.\n */\nfunction projectPageProps(result: ResolvePageResult, registry: ComponentRegistry | undefined): ResolvePageResult {\n const document = result.document\n if (!document) return result\n const lookupManifest = registry ? propFieldLookupOf((id) => registry.getManifest(id)) : undefined\n const issues: CmsValidationIssue[] = []\n let projected: Record<string, unknown> | undefined\n for (const [name, value] of Object.entries(document)) {\n const delivered = deliverCompositionProps(\n value,\n { path: [name], collectionId: document.collection, documentId: document.id },\n issues,\n lookupManifest,\n )\n if (delivered !== value) (projected ??= { ...document })[name] = delivered\n }\n if (!projected && issues.length === 0) return result\n return {\n ...result,\n ...(projected ? { document: projected as ResolvedDocument } : {}),\n issues: [...result.issues, ...issues],\n }\n}\n\n/**\n * Populate every composition-node `relation` prop the page references to depth-1,\n * keyed `collection:id` (EC-254). Walks the page composition + layout (and, through\n * `collectReferencedRelations`, transcluded reusable content) and resolves each ref\n * through the request's client. Empty when no registry is supplied (the collector\n * needs manifests to know which props are relations) — relation props then pass\n * through raw, the graceful degradation a host without a registry already gets.\n *\n * `docKeys` carries the `collection:id` of every referenced pick (resolved or not),\n * so the caller cache-tags the page by them (EC-146): editing/publishing a picked\n * document — including a previously-unpublished one that should now appear — drops\n * exactly the pages that embed it. A fixed pick depends on the document's identity,\n * not on its collection's membership, so no coarse collection tag is added.\n */\nfunction populatePageRelations(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n): { relations: Record<string, RelationTarget>; docKeys: string[] } {\n if (!registry) return { relations: {}, docKeys: [] }\n const refKeys = new Set<string>()\n const relations = collectReferencedRelations(\n body.nodes,\n registry,\n (ref) => client.resolveRelation(ref),\n {},\n refKeys,\n )\n return { relations, docKeys: [...refKeys] }\n}\n\n/**\n * Resolve every `listing` block on the page to its matching documents (EC-255),\n * keyed by serialized query for `<CanvasRenderer />` to rebuild `resolveListing`.\n * Runs each listing's query through the request's client (so perspective + locale\n * match the rest of the resolution — published-only on a live page, per AD-5), and\n * returns the per-query `listDocuments` `CacheTags` so the caller folds them into\n * the page's tag set: those carry both the COLLECTION tag (membership — a product\n * joining/leaving the category re-renders the grid) and the listed documents' own\n * tags (a member's content edit re-renders it). Empty when no registry is supplied\n * (the collector needs manifests to know which blocks declare a listing).\n *\n * EC-581 6a: each query executes through `listingRequestOf` — the same phase the\n * studio canvas runs — with `clock` read at most once for the whole page\n * (`readsNow` reports whether it was). A query `listDocuments` refuses is baked as\n * its structured errors, so the page shows them instead of a silent empty grid.\n */\nfunction populatePageListings(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n clock: Clock,\n): { listings: Record<string, BakedListing>; tags: CacheTags[]; readsNow: boolean } {\n if (!registry) return { listings: {}, tags: [], readsNow: false }\n const tags: CacheTags[] = []\n let instant: string | undefined\n const now = (): string => (instant ??= clock())\n const listings = collectReferencedListings<BakedListing>(\n body.nodes,\n registry,\n (query: ListingQuery) => {\n const listed = client.listDocuments(query.collection, listDocumentsQueryOf(query, now))\n tags.push(listed.tags)\n return listed.ok ? (listed.documents as RelationTarget[]) : { errors: listed.errors }\n },\n // EC-255: reach listings nested inside transcluded reusable content (the same\n // depth-1 populate the relation bake uses), so a grid inside a reusable bakes —\n // and its membership tag is folded in — instead of rendering empty on the host.\n (ref) => client.resolveRelation(ref),\n )\n return { listings, tags, readsNow: instant !== undefined }\n}\n\n/**\n * A listing query as `listDocuments` input (EC-581 6a). The operands are JSON\n * bound from authored props and `now`; the cast hands them to `listDocuments`,\n * whose `validateWhere`/`validateLimit` judge them — the one validator — and\n * return a structured error for anything that is not a valid operand.\n */\nfunction listDocumentsQueryOf(query: ListingQuery, now: () => string): ListDocumentsQuery {\n const request = listingRequestOf(query, now)\n return {\n where: request.where as ListDocumentsQuery['where'],\n ...(request.sort ? { sort: request.sort } : {}),\n ...(request.limit !== undefined ? { limit: request.limit as number } : {}),\n }\n}\n\n/**\n * Resolve every internal rich-text LINK on the page to its target document's URL\n * path (EC-291), keyed `collection:id` for `<CanvasRenderer />` to rebuild\n * `resolveLink`. Collects the referenced doc refs from the page's rich-text props\n * (transclusion-aware) and derives each URL through the request's client\n * (`resolveLink` → `urlForDocument`), so the path matches what the same routes serve.\n * A ref that resolves to no live URL (unpublished target / no addressing route) is\n * simply omitted — the renderer then shows the EC-290 `unresolved-ref` issue.\n *\n * `docKeys` carries EVERY referenced target (resolved or not), so the caller cache-\n * tags the page by them (EC-146): publishing or renaming a linked document — including\n * one not yet published when the page was baked — drops exactly the embedding pages.\n * Empty when no registry is supplied (the collector needs no manifest, but the bake\n * stays consistent with its relation/listing siblings, which do).\n */\nfunction populatePageLinks(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n): { links: Record<string, string>; docKeys: string[] } {\n if (!registry) return { links: {}, docKeys: [] }\n const refs = collectReferencedRichTextLinks(\n body.nodes,\n registry,\n (ref) => client.resolveRelation(ref),\n )\n // EC-499: a rich-text BODY carries link marks directly, not only inside a\n // component's rich-text prop — collect those too, so an internal link in a\n // post's prose resolves to a real href instead of the `unresolved-ref` fallback.\n for (const value of body.richTexts) {\n collectRichTextRefsInValue(value, {\n onLink: (ref) => {\n refs[`${ref.collection}:${ref.id}`] = ref\n },\n })\n }\n const links: Record<string, string> = {}\n for (const [key, ref] of Object.entries(refs)) {\n const url = client.resolveLink(ref)\n if (url !== null) links[key] = url\n }\n return { links, docKeys: Object.keys(refs) }\n}\n\n/** One request, read once (EC-720): its locale, that locale's client, and the page resolution. */\nexport interface CanvasRequestPage {\n /** The request's locale — split from the URL's leading segment, else the project default. */\n locale: Locale\n /** The content client of that locale, in the request's perspective. */\n client: ContentClient\n /** The URL's resolution in that locale (`resolved.tags.locale` is the same `locale`). */\n resolved: ResolvePageResult\n}\n\n/**\n * THE request split (EC-720): one URL → its locale, the client of that locale and the\n * page resolution. Every request-time reader of a page goes through it — the catch-all's\n * {@link resolveCanvasPage} and the kit's `pageOpensFullBleed` — so the locale a page\n * resolves in is derived in one place, and every reader downstream reads that one value\n * (`resolved.tags.locale`) rather than splitting the URL again.\n */\nexport function resolveRequestPage(\n source: ContentSource,\n url: string,\n perspective: Perspective,\n): CanvasRequestPage {\n const { locale, path } = splitLocalePath(url, source.locales)\n const client = source.client({ perspective, locale })\n return { locale, client, resolved: client.resolvePage(path) }\n}\n\n/**\n * Resolve one request URL against a content source: split the locale, run\n * `resolvePage` in the requested perspective, assemble binding payloads, and\n * merge every accessor's `CacheTags` into one deterministic set. Pure — all\n * data was prefetched by the source.\n */\nexport async function resolveCanvasPage(\n source: ContentSource,\n url: string,\n perspective: Perspective,\n options: ResolveCanvasPageOptions = {},\n): Promise<CanvasPageOutcome> {\n const { locale, client, resolved } = resolveRequestPage(source, url, perspective)\n\n if (resolved.status === 'redirect' && resolved.redirect) {\n return {\n kind: 'redirect',\n target: resolved.redirect.target,\n permanent: resolved.redirect.permanent,\n tags: resolved.tags,\n }\n }\n\n if (resolved.status !== 'ok') {\n return { kind: 'not-found', tags: resolved.tags }\n }\n\n // EC-662: the page's component props, projected once — what every baker below walks and\n // the renderer renders, with their located issues on the result.\n const result = projectPageProps(resolved, options.registry)\n\n // EC-499: read the collection's declared delivery presentation ONCE, and split\n // the resolved body by it — every baker below and the renderer then agree on\n // exactly which fields are the body and which of its items is which kind.\n const presentation = presentationFor(source, result)\n const body = pageBodyOf(result, presentation)\n\n // EC-632: the assets in the request's locale — a localized alt bakes in the page's locale.\n const allAssets = source.assets?.(locale) ?? []\n // EC-205: page-scope the baked assets when a registry is supplied (the route\n // helper passes it); else keep the full list (back-compat).\n const bakedAssets = options.registry\n ? scopeAssetsToPage(allAssets, options.registry, result, body, (ref) =>\n client.resolveRelation(ref),\n )\n : allAssets\n // EC-227: tag the cache entry with every asset it baked, so editing an asset\n // record (alt/dimensions/re-upload) drops exactly the pages carrying it. The\n // locale is irrelevant for asset tags (an asset record is locale-agnostic) —\n // `cacheTags` just needs one; `result.tags.locale` is the page's.\n const assetTags = cacheTags({\n assets: bakedAssets.map((asset) => asset.id),\n locale: result.tags.locale,\n })\n // EC-254: populate the relation props of the nodes inside the page's blocks\n // (e.g. a ProductGrid's picked products), keyed for `<CanvasRenderer />` to\n // rebuild `resolveRelation`. Reuses the request's client, so perspective +\n // locale match the rest of the resolution.\n const { relations, docKeys: relationDocKeys } = populatePageRelations(\n body,\n client,\n options.registry,\n )\n // EC-146: tag the page by every picked document so editing/publishing one drops\n // the embedding page (the locale is the page's; doc tags are locale-agnostic).\n const relationTags = cacheTags({ docs: relationDocKeys, locale: result.tags.locale })\n // EC-255: resolve every listing block's query (e.g. a ProductGrid keyed to a\n // category) and bake the result keyed by serialized query. The per-query\n // `listDocuments` tags (collection membership + listed docs) join the merged set,\n // so adding/removing a member or editing one invalidates the embedding page.\n const {\n listings,\n tags: listingTags,\n readsNow,\n } = populatePageListings(body, client, options.registry, options.clock ?? systemClock)\n // EC-291: resolve every internal rich-text link to its target's URL path and bake\n // it for `<CanvasRenderer />` to rebuild `resolveLink`. Tag the page by each linked\n // document so renaming/re-parenting/first-publishing a target re-bakes the href.\n const { links, docKeys: linkDocKeys } = populatePageLinks(body, client, options.registry)\n const linkTags = cacheTags({ docs: linkDocKeys, locale: result.tags.locale })\n return {\n kind: 'render',\n result,\n assets: bakedAssets,\n relations,\n listings,\n readsNow,\n links,\n ...(presentation ? { presentation } : {}),\n tags: mergeCacheTags(\n result.tags,\n ...listingTags,\n assetTags,\n relationTags,\n linkTags,\n ),\n }\n}\n","import type { CacheTags } from '@elytracms/core/content'\nimport type { RevalidationEvent } from '@elytracms/core/revalidation'\n\n/**\n * Cache-tag pipeline (EC-146, vision AD-5) — the pure half.\n *\n * This module defines (a) the **canonical Next.js tag string format** mapping\n * the accessor-level `CacheTags` (EC-143) onto `cacheTag` / `revalidateTag` keys,\n * (b) the **tag derivation for a revalidation event** ({@link revalidationTags},\n * EC-579 — the receiver's half of the one emitter), and (c) the **pure half of a\n * cached resolution**: its key ({@link canvasCacheKey}) and the `cacheTag` calls that\n * declare its tags ({@link entryTagCalls}). No Next.js imports — everything here is\n * unit-testable.\n *\n * **Caching is the host's (EC-620).** The engine's part is exactly three things: the\n * tags on everything the accessors return, the invalidation signal (the Convex\n * trigger's event → the kit receiver → `revalidateTag` for every derived tag), and a\n * default in the scaffold. Whether a page caches, under which scope, for how long and\n * with which Next flag is the host's: the kit holds no `'use cache'` scope and no\n * `cacheLife`. The host's own `'use cache'` function declares a result's tags through\n * the canvas route's `applyCacheTags` (`route.tsx`), which makes the\n * {@link entryTagCalls} calls with the host's one scope.\n *\n * This is the ONE tag module: the entries are tagged here and the invalidations\n * are derived here, from the same formatters and the same scope. Before EC-579 the\n * format was mirrored twice more (the studio's browser dispatcher and an inline\n * literal in the Convex emitter), and each emitter brought its own scope.\n *\n * ## Tag format\n *\n * - `doc:<collection>:<id>` — one per document whose content was actually in\n * the response (fetch-time tagging: includes depth-1 populated relation\n * targets, and documents inside filtered/dynamic lists *after* the filter\n * ran).\n * - `collection:<name>:<locale>` — the coarse membership tag: every response\n * that read from a collection carries it, so create/delete/newly-matching\n * documents sweep cached lists without read-set tracking.\n * - `route:<pattern>:<locale>` — the resolved URL path plus every hop of a\n * redirect chain.\n * - `asset:<id>` — one per asset whose delivery shape the page *baked*\n * (page-scoped images + `seoOgImage`, EC-227); editing that asset record\n * (alt/dimensions/re-upload) drops exactly the pages that baked it. Not\n * locale-scoped — an asset record is locale-agnostic.\n * - `project` — the whole-project sweep tag attached to **every** cached\n * entry; derived for the events whose effect no narrower tag names (a redirect\n * change, an asset edit — see {@link revalidationTags}).\n *\n * ## Project scope\n *\n * Every tag is prefixed `p:<projectId>:` when a scope is configured (e.g.\n * `p:prj_aurora:doc:post:post-1`). The cost is a few bytes per tag and it\n * keeps multi-project hosts (one Next app serving several builder projects)\n * from cross-invalidating, so the prefix is **on whenever the host knows its\n * project id** — decision: include it. Since EC-579 the scope has ONE owner: the\n * host. The webhook carries an event, not tags, and the receiver derives the\n * tags with the same scope its cache entries are written under (the delivery\n * kit hands both the one `contentScope`), so emitter and host can no longer\n * disagree about it.\n */\n\n/** Apply the optional multi-project scope prefix to one tag. */\nexport function scopeCacheTag(tag: string, scope?: string): string {\n return scope === undefined || scope.length === 0 ? tag : `p:${scope}:${tag}`\n}\n\n/**\n * Tag for one document identity. `docKey` is the EC-143 `documentKey` string\n * (`<collection>:<id>`), exactly as found in `CacheTags.docs`.\n */\nexport function docCacheTag(docKey: string, scope?: string): string {\n return scopeCacheTag(`doc:${docKey}`, scope)\n}\n\n/** The coarse membership tag of one collection in one locale. */\nexport function collectionCacheTag(collection: string, locale: string, scope?: string): string {\n return scopeCacheTag(`collection:${collection}:${locale}`, scope)\n}\n\n/** Tag for one resolved route path (or redirect-chain hop) in one locale. */\nexport function routeCacheTag(pattern: string, locale: string, scope?: string): string {\n return scopeCacheTag(`route:${pattern}:${locale}`, scope)\n}\n\n/**\n * Tag for one baked asset (EC-227). `assetId` is the bare asset id, as found in\n * `CacheTags.assets`. Not locale-scoped: an asset record is one row shared\n * across locales, so an edit invalidates the baking pages in every locale.\n */\nexport function assetCacheTag(assetId: string, scope?: string): string {\n return scopeCacheTag(`asset:${assetId}`, scope)\n}\n\n/**\n * The whole-project sweep tag. Attached to every cached page entry; derived for\n * the events no narrower tag covers ({@link revalidationTags}).\n */\nexport function projectSweepTag(scope?: string): string {\n return scopeCacheTag('project', scope)\n}\n\n/**\n * Map an accessor-level `CacheTags` (what one request actually read) onto the\n * full, deterministic (sorted, deduplicated) set of Next tag strings for the\n * cache entry — including the project sweep tag.\n */\nexport function nextCacheTags(tags: CacheTags, scope?: string): string[] {\n const all = new Set<string>()\n all.add(projectSweepTag(scope))\n for (const docKey of tags.docs) all.add(docCacheTag(docKey, scope))\n for (const collection of tags.collections) {\n all.add(collectionCacheTag(collection, tags.locale, scope))\n }\n for (const route of tags.routes) all.add(routeCacheTag(route, tags.locale, scope))\n for (const assetId of tags.assets) all.add(assetCacheTag(assetId, scope))\n return [...all].sort()\n}\n\n// --- Revalidation event → tags (EC-579) ----------------------------------------\n\n/** The receiver's own project — what its cached entries are tagged with. */\nexport interface RevalidationProject {\n /** The tag scope of this host's cache entries (`p:<scope>:…`), if any. */\n scope?: string | undefined\n /** Every locale this host serves — one `collection:` tag per locale. */\n locales: readonly string[]\n}\n\n/**\n * The tags one revalidation event invalidates on this host — sorted, deduplicated,\n * scoped with the receiver's OWN scope, so the emitter never needs to know it.\n *\n * - `publish` / `unpublish` / `delete` → `doc:<collection>:<id>` (its detail page and\n * every cached list that contained it) + `collection:<collection>:<locale>` for\n * EVERY locale this host serves (list membership may have changed, and a URL\n * cached as not-found for a document that now exists carries it too). **No\n * sweep** — a publish drops exactly the entries that read the document or its\n * collection (EC-515);\n * - `redirects` → the sweep (routing can change any URL; see the `redirects` trigger\n * in the studio's `convex/triggers.ts` for why a targeted `route:` tag is wrong);\n * - `asset` → `asset:<id>` + the sweep (a page reading the asset through a document\n * asset FIELD carries no `asset:` tag — EC-566 D16, EC-589);\n * - `probe` → nothing (the doctor's no-op ping).\n *\n * Why the precise tags are enough (EC-515 option D, kept by EC-620): a cached\n * resolution declares its tags with `cacheTag` from INSIDE the cache function that\n * loaded it (the host's, through `applyCacheTags`), and Next decides an entry's\n * staleness from the tags it was STORED with — so a `doc:` tag drops the entries that\n * read the document, on a self-hosted host as on any other. Until EC-515 the read\n * named only the baseline sweep (`unstable_cache` judged the reading wrapper's tags),\n * every precise tag was inert on a self-hosted host, and every publish carried the\n * sweep to compensate (wave-0091). Measured both ways in EC-515's harness, and again\n * with the host-owned scope in EC-620's.\n */\nexport function revalidationTags(event: RevalidationEvent, project: RevalidationProject): string[] {\n const { scope } = project\n const tags = new Set<string>()\n switch (event.kind) {\n case 'publish':\n case 'unpublish':\n case 'delete':\n tags.add(docCacheTag(`${event.collection}:${event.id}`, scope))\n for (const locale of project.locales) tags.add(collectionCacheTag(event.collection, locale, scope))\n break\n case 'redirects':\n tags.add(projectSweepTag(scope))\n break\n case 'asset':\n tags.add(assetCacheTag(event.id, scope))\n tags.add(projectSweepTag(scope))\n break\n case 'probe':\n break\n }\n return [...tags].sort()\n}\n\n\n// --- A cached resolution: its key and its tag calls (EC-515, EC-620) -------------\n\ndeclare const canvasCacheKeyBrand: unique symbol\n\n/**\n * What identifies one published canvas resolution — the argument the canvas route\n * hands the host's cache function (`CanvasRouteOptions.published`), which therefore\n * IS that entry's key: Next keys a `'use cache'` entry by (build id, function id,\n * serialized arguments), so whatever identifies an entry has to be an argument, and\n * nothing else may be. It encodes three facts ({@link parseCanvasCacheKey}):\n *\n * - `scope` — the tag scope (the project id the studio publishes under; `''` if none).\n * - `mode` — which content world the loader reads (the delivery kit's `contentMode`:\n * `live` / `fallback` / `empty`; `''` for a host driving the route directly with\n * one world).\n * - `url` — the request path, locale prefix included.\n *\n * **An opaque string, not an object — measured (EC-620).** Next wraps every\n * `'use cache'` function in React's per-request `cache()`, which memoizes by argument\n * IDENTITY. `Page` and `generateMetadata` each build the key for the same URL; as two\n * equal objects they missed that memo and a cold URL ran the loader twice (page and\n * metadata filling the entry concurrently), as one string they share the in-flight\n * miss — one load, exactly as the kit-owned entry did. A string is also trivially\n * stable for Next's own key serialization. The brand keeps a hand-built string out.\n *\n * The mode is in the key because an entry can outlive the world that wrote it (a\n * shared or remote cache handler, a host whose env changed under a warm cache) and a\n * hit never asks the loader whether it still applies. Before EC-543 the key was\n * `(scope, url)` only, and since `ELYTRA_PROJECT_ID` *is* the config slug, a live host\n * and an empty one keyed the same URL identically — measured on 0.0.85: a warm cache\n * written in live mode kept answering `200` with the live world's bytes after both env\n * vars were blanked and the host itself reported `contentMode: 'empty'`. Keying on the\n * mode makes that cross-read structurally impossible rather than something to detect —\n * a world change is a cold miss, never a stale hit. Since EC-620 the cache function is\n * the host's, so the property holds as long as the host keys on this value — which\n * it does by passing it through unchanged (the canvas route refuses a key of another\n * world).\n */\nexport type CanvasCacheKey = string & { readonly [canvasCacheKeyBrand]: true }\n\n/** What a {@link CanvasCacheKey} is built from (absent scope/mode = `''`). */\nexport interface CanvasCacheKeyInput {\n scope?: string | undefined\n mode?: string | undefined\n url: string\n}\n\n/** The three facts a {@link CanvasCacheKey} encodes. */\nexport interface CanvasCacheKeyParts {\n scope: string\n mode: string\n url: string\n}\n\n/**\n * Build the key of one published resolution — see {@link CanvasCacheKey}. The\n * encoding is JSON of `[scope, mode, url]`: injective (no separator a URL could\n * contain can make two worlds collide) and readable in a log.\n */\nexport function canvasCacheKey({ scope, mode, url }: CanvasCacheKeyInput): CanvasCacheKey {\n return JSON.stringify([scope ?? '', mode ?? '', url]) as CanvasCacheKey\n}\n\n/**\n * Decode a {@link CanvasCacheKey}; `undefined` for any string {@link canvasCacheKey}\n * did not produce (the canvas route refuses such a key loudly).\n */\nexport function parseCanvasCacheKey(key: string): CanvasCacheKeyParts | undefined {\n let parsed: unknown\n try {\n parsed = JSON.parse(key)\n } catch {\n return undefined\n }\n if (!Array.isArray(parsed) || parsed.length !== 3 || !parsed.every((part) => typeof part === 'string')) {\n return undefined\n }\n const [scope, mode, url] = parsed as [string, string, string]\n return { scope, mode, url }\n}\n\n/**\n * Next's per-call tag cap: `cacheTag()` validates each call's tags and stops at\n * `NEXT_CACHE_TAG_MAX_ITEMS` (128), dropping the rest with only a console warning\n * (`patch-fetch.js` `validateTags`). The cap is per CALL — the entry accumulates every\n * call's tags (`cache-tag.js`).\n */\nexport const NEXT_CACHE_TAG_CALL_LIMIT = 128\n\n/**\n * The `cacheTag()` calls that declare one result's tags on a cache entry: the project\n * sweep FIRST and ALONE, then the read set in chunks under\n * {@link NEXT_CACHE_TAG_CALL_LIMIT}. The canvas route's `applyCacheTags` makes exactly\n * these calls.\n *\n * {@link nextCacheTags} is sorted, and `p:<scope>:project` sorts after every\n * `collection:` and `doc:` tag — handed to one `cacheTag` call, a page that read more\n * than ~125 documents would lose the sweep to the per-call cap, silently, and with it\n * every redirect and asset invalidation (measured, EC-515). Its own first call makes\n * that impossible; the chunking keeps every other tag too.\n */\nexport function entryTagCalls(tags: CacheTags, scope?: string): string[][] {\n const sweep = projectSweepTag(scope)\n const rest = nextCacheTags(tags, scope).filter((tag) => tag !== sweep)\n const calls: string[][] = [[sweep]]\n for (let start = 0; start < rest.length; start += NEXT_CACHE_TAG_CALL_LIMIT) {\n calls.push(rest.slice(start, start + NEXT_CACHE_TAG_CALL_LIMIT))\n }\n return calls\n}\n","import type { Metadata, ResolvingMetadata } from 'next'\nimport { cacheTag } from 'next/cache'\nimport { draftMode } from 'next/headers'\nimport { notFound, permanentRedirect, redirect } from 'next/navigation'\nimport type { ComponentType, ReactNode } from 'react'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport type { CacheTags, ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\nimport { canvasCacheKey, entryTagCalls, parseCanvasCacheKey } from './cache'\nimport type { CanvasCacheKey } from './cache'\nimport { CanvasRenderer } from './canvas-renderer'\nimport { mergeMetadataOverParent, seoContextOf, seoMetadata } from './metadata'\nimport type { HostComponents } from './components'\nimport { canvasBeaconProps, canvasWrapContext, pathFromSlug, resolveCanvasPage } from './route-core'\nimport type {\n CanvasBeaconProps,\n CanvasPageOutcome,\n CanvasRenderOutcome,\n CanvasRequestInfo,\n CanvasWrapContext,\n} from './route-core'\nimport type { ContentSource } from './source'\n\n/**\n * Catch-all route helper (EC-144): the factory a host app calls once in\n * `app/[[...slug]]/page.tsx` to wire `resolvePage` → `<CanvasRenderer />`,\n * with redirects answered by Next's `redirect()`/`permanentRedirect()` (per\n * the route's `permanent` flag), unknown URLs answered by `notFound()` (the\n * host's 404 renders), and the perspective chosen by Next `draftMode()` —\n * draft when enabled (via the token-gated preview route), published\n * otherwise.\n *\n * **Caching is the host's (EC-620).** The route resolves and renders with no cache\n * scope of its own — it holds no `'use cache'` function and sets no cache life, so a\n * host needs no Next flag for it. A host that caches gives the route its own\n * `'use cache'` function as {@link CanvasRouteOptions.published}: the route hands it\n * the request's {@link CanvasCacheKey}; the function calls {@link CanvasRoute.resolve},\n * declares the result's tags with {@link CanvasRoute.applyCacheTags} (the fetch-time\n * tag set of what the response actually contained) and picks its own `cacheLife`.\n * Publishing in the studio then invalidates exactly the affected entries through the\n * signed `/api/revalidate` webhook — the receiver derives the same scoped tag strings\n * — on Vercel and on a self-hosted `next start` alike; while the content backend is\n * unreachable a cached entry keeps serving, because a hit never invokes `loadContent`.\n * The scaffold's page ships exactly that as its default (tag-only, no time limit).\n * Without `published`, every request resolves fresh — dynamic, uncached, and up to\n * date by construction.\n *\n * ```tsx\n * import { cacheLife } from 'next/cache'\n * import type { CanvasCacheKey, CanvasPageOutcome } from '@elytracms/next'\n *\n * const route = elytra.canvasRoute({ published: cachedPage })\n *\n * async function cachedPage(key: CanvasCacheKey): Promise<CanvasPageOutcome> {\n * 'use cache'\n * const outcome = await route.resolve(key)\n * route.applyCacheTags(outcome) // a publish drops exactly the pages that read it\n * cacheLife({ revalidate: Infinity, expire: Infinity }) // tag-only — your choice\n * return outcome\n * }\n *\n * export default route.Page\n * export const generateMetadata = route.generateMetadata\n * ```\n *\n * Whatever the page chooses, three properties stay in the route, outside any cache\n * scope: draft mode never reaches `published` (it is per-editor and request-time);\n * `notFound()` / `redirect()` answer from the outcome, so a 404 stays HTTP 404 and a\n * redirect 307/308; and `Page` and `generateMetadata` call `published` with the SAME\n * key, so they read one entry.\n */\n\n// EC-524: both context shapes and the wrap-context derivation live in the PURE\n// half (`route-core.ts`) — no Next request APIs are involved in either, and the\n// derivation earns a unit test there. Re-exported here because this module is\n// the published API surface hosts import from.\nexport type { CanvasBeaconProps, CanvasRenderOutcome, CanvasRequestInfo, CanvasWrapContext } from './route-core'\n\n/**\n * What the route helper tells the content loader about the request it is\n * loading for (EC-156). Loaders that serve both perspectives from one\n * prefetched source (the fixture snapshot) can ignore it; a live loader uses\n * it to fetch the draft-perspective snapshot only for draft-mode requests —\n * it must NOT call request APIs like `draftMode()` itself, because on a cached\n * published path the loader runs inside the host's `'use cache'` function, where\n * request APIs are unavailable.\n */\nexport interface CanvasContentRequest {\n perspective: Perspective\n}\n\nexport interface CanvasRouteOptions {\n /**\n * Async content loader — the seam EC-145/146 plug Convex into. For the\n * fixture-backed example this memoizes a `createContentSnapshot` call.\n * Invoked per request; cache/memoize inside the loader as appropriate.\n * Behind a host cache function ({@link CanvasRouteOptions.published}) it runs\n * only on a cache miss — cache hits never touch the content backend.\n * Receives the request's perspective (see {@link CanvasContentRequest});\n * zero-arg loaders remain valid.\n */\n loadContent: (request: CanvasContentRequest) => Promise<ContentSource>\n /** Host component surface from `defineHostComponents`. */\n components: HostComponents\n /**\n * The tag scope of everything this route resolves (`p:<scope>:…`) — what\n * {@link CanvasRoute.applyCacheTags} scopes with and what every\n * {@link CanvasCacheKey} carries. It must be the SAME scope the revalidate receiver\n * derives its tags with (`createRevalidateRoute`'s `scope`); the delivery kit hands\n * its one `contentScope` to both (EC-579) — a disagreement would drop nothing,\n * silently.\n */\n scope?: string\n /**\n * Which content world `loadContent` reads — the delivery kit passes its\n * `contentMode` (`'live' | 'fallback' | 'empty'`). It rides in every\n * {@link CanvasCacheKey}, so an entry written in one world can never be served in\n * another (EC-543). A host driving this helper directly with one world may omit it.\n */\n mode?: string\n /**\n * How a PUBLISHED request gets its outcome — the host's cache seam (EC-620). Pass\n * your own `'use cache'` function: it receives the request's {@link CanvasCacheKey}\n * (keep that its only argument — it is the entry's key), calls\n * {@link CanvasRoute.resolve} with it, declares the result's tags with\n * {@link CanvasRoute.applyCacheTags}, picks its `cacheLife`, and returns the\n * outcome (module example above; the scaffold's page ships it as the default).\n *\n * Omit it and every published request resolves fresh — dynamic and uncached. Draft\n * mode never calls it. `Page` and `generateMetadata` call it with the same key.\n */\n published?: (key: CanvasCacheKey) => Promise<CanvasPageOutcome>\n /**\n * EC-146 hook: receives the merged `CacheTags` of everything this request\n * read (the `resolvePage` tags plus the baked relations/listings/links). On a\n * cache hit (through a host {@link CanvasRouteOptions.published} function) these\n * are the stored tags of the run that wrote the entry.\n */\n onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void\n /** Override the generated `<head>` metadata for a resolved page. */\n metadata?: (result: ResolvePageResult, info: CanvasRequestInfo) => Metadata\n /**\n * Wrap the rendered page in the host's own chrome (EC-524) — a detail\n * template, a per-collection layout, a draft ribbon. Receives the rendered\n * canvas as `children` and returns whatever should stand in its place:\n *\n * ```tsx\n * elytra.canvasRoute({\n * wrap: (children, ctx) =>\n * ctx.collection === 'post' ? <PostDetail document={ctx.document}>{children}</PostDetail> : children,\n * })\n * ```\n *\n * **This is deliberately host code, not config** (owner ruling 2026-08-01):\n * *\"this sounds very much like page-templates … templates belong in code, so\n * the wrapper is something the developer has to care about … the page-content\n * (Canvas or Rich-Text) should render agnostically.\"* A collection's\n * `presentation.body` still names WHICH field is the content — that is content\n * selection, and it is what lets one catch-all render a canvas page and a\n * rich-text post through the same call. What surrounds that content is the\n * developer's implementation decision, branched here in their own file.\n *\n * It replaced a `presentation.wrapper` component-id STRING resolved from the\n * canvas registry, which forced a delivery-only component to register as a\n * pseudo-block (EC-514). A function beats that on every axis that matters: it\n * cannot dangle, the compiler checks it, and it can branch on anything in\n * {@link CanvasWrapContext} rather than on collection identity alone.\n *\n * Applied to the `render` outcome only — a redirect or a 404 never reaches it.\n * Returning `children` unchanged is the no-op, and omitting `wrap` entirely is\n * the default.\n */\n wrap?: (children: ReactNode, ctx: CanvasWrapContext) => ReactNode\n /**\n * The ANALYTICS slot (EC-618): a component the route renders inside every `render`\n * outcome, AFTER the page (and outside `wrap`, so a wrapper cannot drop it), with the\n * resolved document, the locale, the path and the status ({@link CanvasBeaconProps}).\n * Published perspective only — a draft preview is an editor at work, not a visit.\n *\n * The engine supplies the slot and nothing else: its value is the beacon of the\n * analytics addon, `@elytracms/analytics/next` — **unpublished, pending counsel's\n * verdict on its consent-free claim** (EC-618) — or a host's own component. Absent, the\n * route renders exactly what it rendered before, and sends nothing.\n *\n * A not-found page never reaches this slot (`notFound()` renders the host's\n * `not-found.tsx`), so a 404 beacon lives in that file.\n */\n analytics?: ComponentType<CanvasBeaconProps>\n}\n\nexport interface CanvasRouteProps {\n params: Promise<{ slug?: string[] }>\n}\n\nexport interface CanvasRoute {\n /** The page component: `export default route.Page`. */\n Page: (props: CanvasRouteProps) => Promise<ReactNode>\n /**\n * Next metadata hook: `export const generateMetadata = route.generateMetadata`.\n * Next passes the parent segment's resolving metadata as the second argument;\n * the route merges the page's tags over it (EC-610). A caller that invokes this\n * by hand (the scaffold's `app/page.tsx`) forwards its own `parent`.\n *\n * `parent` is REQUIRED, not optional (EC-614): Next's webpack build validates a\n * page's `generateMetadata` with `SecondArg<…> extends ResolvingMetadata`, and an\n * optional parameter reads as `ResolvingMetadata | undefined`, which fails that\n * constraint — a consumer re-exporting this hook could not `next build --webpack`\n * on 0.0.88 (the Turbopack validator is lenient, which is how the gate missed it).\n */\n generateMetadata: (props: CanvasRouteProps, parent: ResolvingMetadata) => Promise<Metadata>\n /**\n * Resolve one PUBLISHED request — with no cache scope of its own (EC-620). Call it\n * from the `'use cache'` function you pass as {@link CanvasRouteOptions.published},\n * with the key the route handed that function. A key of another scope or content\n * mode (a cache function shared with a route of another world) is refused loudly:\n * the entry would be keyed under one world and resolved in another (EC-543).\n *\n * The outcome carries everything a cache decision needs: `tags` (what the\n * resolution read — hand them to {@link applyCacheTags}) and, on a `render`\n * outcome, `readsNow` (a listing on the page compared against the clock, so the\n * page changes as time passes — bound that entry's life).\n */\n resolve: (key: CanvasCacheKey) => Promise<CanvasPageOutcome>\n /**\n * Declare a result's tags on the CURRENT `'use cache'` entry, scoped with this\n * route's scope — exactly the strings the revalidate receiver revalidates (EC-620).\n * Any accessor result works (`{ tags }`): a resolution from {@link resolve}, or a\n * `listDocuments` result in a route of your own.\n *\n * It makes the {@link entryTagCalls} calls: the project sweep first and alone, the\n * rest in chunks of at most 128. Next keeps 128 tags per `cacheTag()` call and drops\n * the rest with only a warning, and the sweep sorts last — one `cacheTag(...tags)`\n * call loses it on a page that read ~125 documents, and with it every redirect and\n * asset invalidation (measured, EC-515/EC-620).\n *\n * Callable ONLY inside a `'use cache'` function: anywhere else Next's own `cacheTag`\n * throws (E819 during a request or a prerender; E886 when neither `cacheComponents`\n * nor `experimental.useCache` is enabled) — a loud error, never a silent no-op.\n */\n applyCacheTags: (result: { tags: CacheTags }) => void\n /**\n * EC-722 — resolve one URL for a route of YOUR OWN and settle its status, before any\n * `<Suspense>` boundary: `const outcome = await route.load('/blog/' + slug)`.\n *\n * It does what {@link Page} does before rendering, and nothing else: reads the request's\n * perspective, resolves a published request through your\n * {@link CanvasRouteOptions.published} cache function with the SAME key `Page` hands it\n * (one entry; a draft resolves fresh), and answers a redirect with\n * `redirect()`/`permanentRedirect()` and a missing document with `notFound()` itself —\n * so what comes back is always a page to render.\n *\n * **Call it outside every `<Suspense>` boundary** (the route's page, not a suspended\n * child). Inside one, the response status is already committed when the call settles:\n * measured, a hand-written route that resolved and called `notFound()` inside\n * `<Suspense>` answered a missing document with HTTP 200 (EC-567 V11). Outside, under\n * Cache Components, it is legal because a published request is a cache read through\n * `published` — the reason the catch-all page can stand outside a boundary too. Without\n * `published`, a live-mode read outside `<Suspense>` fails `next build` under Cache\n * Components (the same rule `Page` lives by); without Cache Components it is a plain\n * dynamic read.\n *\n * Your route's own `generateMetadata` can delegate to {@link generateMetadata} with the\n * catch-all slug of the same URL; `load` fires no `onTags` (the route's `Page` does).\n */\n load: (url: string) => Promise<CanvasRenderOutcome>\n /**\n * `export const generateStaticParams = route.generateStaticParams` — needed in the\n * catch-all page file only when the HOST enables `cacheComponents` (that flag's\n * rule, not the kit's; measured on Next 16.2.10, EC-515): without it, awaiting the\n * catch-all's `params` fails `next build` (\"Uncached data was accessed outside of\n * <Suspense>\"). Wrapping the page in `<Suspense>` (or a root `loading.tsx`) instead\n * also builds, but answers every 404 and every redirect with HTTP 200 — the status\n * is committed before the content resolves. Declaring params keeps the page\n * blocking, so `notFound()` is a real 404 and a redirect a real 308/307. A host\n * without `cacheComponents` needs none of it.\n *\n * It names ONE param set: {@link CANVAS_VALIDATION_SLUG}, a sample that Next\n * prerenders to validate the route and that resolves to not-found without\n * reading content (the build never touches the backend for it). It fits both\n * catch-all shapes (`[[...slug]]` and the scaffold's required `[...slug]`, which\n * rejects an empty slug), and it is never empty (Next rejects an empty list).\n * Every real URL renders on its first request and is cached from then on.\n */\n generateStaticParams: () => Promise<Array<{ slug: string[] }>>\n}\n\n/**\n * The catch-all's build-time validation sample (see\n * {@link CanvasRoute.generateStaticParams}). The path is reserved: the canvas\n * route answers it with not-found and never resolves it against content.\n */\nexport const CANVAS_VALIDATION_SLUG: readonly string[] = ['_elytra']\nconst CANVAS_VALIDATION_PATH = pathFromSlug([...CANVAS_VALIDATION_SLUG])\n\n/**\n * Default metadata (EC-171): the page's canonical SEO fields — title,\n * description, OG tags, robots noindex — with document-title / page-name\n * fallbacks. `export const generateMetadata = route.generateMetadata` is the\n * one line a host needs; a custom `metadata` option replaces this builder.\n * Either way the result is merged over the layout's resolved metadata (EC-610).\n */\nfunction defaultMetadata(result: ResolvePageResult, assets: readonly ResolvedAsset[]): Metadata {\n return seoMetadata(result.document, seoContextOf(result, assets))\n}\n\n/**\n * THE edge mapping of Next's draft mode onto the delivery perspective (EC-595 binding\n * point 1): draft mode reads the draft variant, everything else the published snapshot.\n * The kit's other request-time read (`pageOpensFullBleed`) imports this one, and the\n * package exports it for a host's own server components, so the mapping is declared\n * once. A request API: never call it inside a cache scope.\n */\nexport async function requestPerspective(): Promise<Perspective> {\n const { isEnabled } = await draftMode()\n return isEnabled ? DRAFT_PERSPECTIVE : 'published'\n}\n\n/**\n * Create the canvas route for `app/[[...slug]]/page.tsx` — `{ Page, generateMetadata,\n * generateStaticParams }` to export, plus `resolve` / `applyCacheTags` for the host's\n * own cache function ({@link CanvasRouteOptions.published}) and `load` for a route of the\n * host's own (EC-722 — {@link CanvasRoute.load}).\n */\nexport function createCanvasRoute(options: CanvasRouteOptions): CanvasRoute {\n // A `published` key that is PRESENT but undefined is the hoisting trap, measured\n // (EC-620): Next's `'use cache'` transform compiles `async function cachedPage() {\n // 'use cache' … }` into a `var` assigned in place, so a cache function declared BELOW\n // the `canvasRoute({ published: cachedPage })` call is still `undefined` when the call\n // runs — and the page would serve uncached forever, silently (under\n // `cacheComponents`, as runtime 500s). Omitting the key is the way to opt out.\n if (Object.hasOwn(options, 'published') && options.published === undefined) {\n throw new Error(\n 'Elytra canvas route: `published` was passed but is undefined. If it names your cache function ' +\n '(the one carrying the cache directive), declare that function ABOVE the canvasRoute() call — ' +\n 'Next compiles a cached function into a variable assigned where it is written, so it is not ' +\n 'hoisted like a plain function. To render without a cache, omit `published` entirely.',\n )\n }\n\n const resolveFresh = async (url: string, perspective: Perspective): Promise<CanvasPageOutcome> => {\n const source = await options.loadContent({ perspective })\n return resolveCanvasPage(source, url, perspective, {\n // EC-205: page-scope the baked assets to what the page actually uses.\n registry: options.components.registry,\n })\n }\n\n // The world this route resolves in: what every key it hands out carries and what\n // every key it accepts must carry.\n const scope = options.scope ?? ''\n const mode = options.mode ?? ''\n const keyFor = (url: string): CanvasCacheKey => canvasCacheKey({ scope, mode, url })\n\n const resolve = async (key: CanvasCacheKey): Promise<CanvasPageOutcome> => {\n const parts = parseCanvasCacheKey(key)\n if (!parts) {\n throw new Error(\n `Elytra canvas route: resolve() was handed ${JSON.stringify(key)}, which is not a key this route ` +\n `produced. Pass your cache function's argument through unchanged — it is the entry's key.`,\n )\n }\n if (parts.scope !== scope || parts.mode !== mode) {\n throw new Error(\n `Elytra canvas route: resolve() was handed a cache key of scope \"${parts.scope}\" and content mode ` +\n `\"${parts.mode}\", but this route resolves in scope \"${scope}\" and mode \"${mode}\". The entry would ` +\n `be keyed under one world and filled from another (EC-543). Pass each route's \\`published\\` ` +\n `function the key that route hands it, and call that same route's resolve().`,\n )\n }\n return resolveFresh(parts.url, 'published')\n }\n\n const applyCacheTags = (result: { tags: CacheTags }): void => {\n for (const tags of entryTagCalls(result.tags, scope)) cacheTag(...tags)\n }\n\n /**\n * Resolve one request — through the host's cache function when it gave one.\n * Shared by `Page`, `load` and `generateMetadata`, so all call it with the same key\n * and read (and on a miss, write) one entry.\n */\n const loadOutcome = async (url: string, perspective: Perspective): Promise<CanvasPageOutcome> => {\n // Draft mode never reaches the host's cache: draft content is per-editor,\n // changes on every keystroke, and must never be persisted into (or served\n // from) the shared published-data cache. (`draftMode()` is a request API and\n // could not run inside a cache scope anyway; Next would not persist a\n // `'use cache'` entry under draft mode either.)\n if (perspective !== 'published' || !options.published) return resolveFresh(url, perspective)\n return options.published(keyFor(url))\n }\n\n /**\n * THE status decision (EC-722), read by `Page` and `load` alike: a redirect and a\n * not-found are answered here — Next's `redirect()` / `notFound()` throw — so only a\n * page to render comes back. Outside every `<Suspense>` boundary that sets the status.\n */\n const settle = (outcome: CanvasPageOutcome): CanvasRenderOutcome => {\n switch (outcome.kind) {\n case 'redirect':\n // 301 vs 302 per the redirect record's `permanent` flag (EC-019/020).\n return outcome.permanent ? permanentRedirect(outcome.target) : redirect(outcome.target)\n case 'not-found':\n return notFound()\n case 'render':\n return outcome\n }\n }\n\n const load = async (url: string): Promise<CanvasRenderOutcome> => {\n // The reserved validation sample is answered as on the catch-all: never read.\n if (url === CANVAS_VALIDATION_PATH) return notFound()\n const perspective = await requestPerspective()\n return settle(await loadOutcome(url, perspective))\n }\n\n const Page = async (props: CanvasRouteProps): Promise<ReactNode> => {\n const { slug } = await props.params\n const url = pathFromSlug(slug)\n if (url === CANVAS_VALIDATION_PATH) return notFound()\n const perspective = await requestPerspective()\n\n const loaded = await loadOutcome(url, perspective)\n options.onTags?.(loaded.tags, { url, perspective })\n const outcome = settle(loaded)\n\n const rendered = (\n <CanvasRenderer\n result={outcome.result}\n components={options.components}\n assets={outcome.assets}\n relations={outcome.relations}\n listings={outcome.listings}\n links={outcome.links}\n // EC-499: the resolved collection's declared body, so this ONE\n // catch-all renders a composition page and a rich-text post detail\n // alike — no per-collection Next file route needed.\n presentation={outcome.presentation}\n />\n )\n // EC-524: the host's own chrome goes AROUND the renderer, never inside\n // it. `children` is the (async) canvas element — React composes it in\n // place, so a wrapper may be a server or a client component.\n const page = options.wrap ? options.wrap(rendered, canvasWrapContext(outcome.result, { url, perspective })) : rendered\n // EC-618: the analytics slot — after the page, outside `wrap`, published only.\n const Beacon = options.analytics\n if (!Beacon || perspective !== 'published') return page\n return (\n <>\n {page}\n <Beacon {...canvasBeaconProps(outcome.result, { url, perspective })} />\n </>\n )\n }\n\n const pageMetadata = async (props: CanvasRouteProps): Promise<Metadata> => {\n const { slug } = await props.params\n const url = pathFromSlug(slug)\n if (url === CANVAS_VALIDATION_PATH) return {}\n const perspective = await requestPerspective()\n\n // Through the host's cache: shares `Page`'s entry (same function, same key), so\n // metadata is a cache read, not a second resolution. Uncached: resolve without\n // the payload factory — metadata only needs the resolution itself, and side\n // effects (addTags/onTags) must fire exactly once per request, in Page.\n if (options.published && perspective === 'published') {\n const outcome = await loadOutcome(url, perspective)\n if (outcome.kind !== 'render') return {}\n return options.metadata\n ? options.metadata(outcome.result, { url, perspective })\n : defaultMetadata(outcome.result, outcome.assets)\n }\n\n const source = await options.loadContent({ perspective })\n const outcome = await resolveCanvasPage(source, url, perspective)\n if (outcome.kind !== 'render') return {}\n return options.metadata\n ? options.metadata(outcome.result, { url, perspective })\n : defaultMetadata(outcome.result, outcome.assets)\n }\n\n // EC-610: the page's tags over the site's. `parent` is the layout chain's\n // resolved metadata, which Next otherwise merges only one key deep — a\n // page-level `openGraph` would erase the layout's `type`, `siteName`, site\n // image and `locale` on every canvas page.\n const generateMetadata = async (\n props: CanvasRouteProps,\n parent: ResolvingMetadata,\n ): Promise<Metadata> => {\n const [page, resolvedParent] = await Promise.all([pageMetadata(props), parent])\n return mergeMetadataOverParent(resolvedParent, page)\n }\n\n const generateStaticParams = async (): Promise<Array<{ slug: string[] }>> => [\n { slug: [...CANVAS_VALIDATION_SLUG] },\n ]\n\n return { Page, generateMetadata, resolve, applyCacheTags, load, generateStaticParams }\n}\n","import type { MetadataRoute } from 'next'\nimport { SEO_SUBFIELDS, splitPath } from '@elytracms/core/cms-core'\nimport type { Locale, RouteRecord } from '@elytracms/core/cms-core'\nimport { COMPOSITE_KEY_SEPARATOR } from '@elytracms/core/project-graph'\nimport type { ContentClient } from '@elytracms/core/content'\nimport { localeUrlPath } from './locale-url'\nimport { seoObjectOf } from './metadata'\nimport type { ContentSource } from './source'\n\n/**\n * `sitemap.xml` from published routes (EC-171, reshaped by EC-590): the URL of every\n * published document of every collection a route names, per locale — Next's\n * `app/sitemap.ts` convention. Documents marked `noindex` in their SEO object, and URLs\n * that do not resolve, are omitted.\n *\n * **The router enumerates, no host provider (EC-590; EC-565 Round 3, 3e).** A route record\n * names the collection it serves (`document.collection`), so the router's `urlsFor` —\n * the same reverse direction the studio's live link and every rendered link read —\n * yields one URL per published document: template routes (`/blog/:slug`), hierarchy\n * mounts (`/:path*`, composed through the parent and slug roles) and literal-id routes\n * alike. The former `SitemapParamsProvider` existed because \"a route record does not\n * declare which collection feeds its params\" — false for every document-bound route\n * (F6c RESTATED). A document-less static route (`/contact`, no `document`) is listed by\n * its pattern.\n *\n * **Enumeration is a candidate list, not the output (EC-486).** Every candidate is put\n * back through `resolvePage`, and a path the resolver refuses is dropped — so the sitemap\n * cannot claim a URL the delivery host would 404. What was enumerated and then refused is\n * reported in {@link CanvasSitemapResult.skipped}, never dropped in silence. `lastModified`\n * is omitted — the delivery shape carries no per-page timestamps today.\n *\n * Host usage (`app/sitemap.ts`):\n *\n * ```ts\n * import { createCanvasSitemap } from '@elytracms/next'\n * import { elytra } from '../lib/elytra'\n *\n * export default createCanvasSitemap({ loadContent: elytra.loadContent, baseUrl: elytra.frontendUrl! })\n * ```\n */\n\nexport interface CanvasSitemapOptions {\n /** Absolute site origin, e.g. `https://example.com` (no trailing slash needed). */\n baseUrl: string\n}\n\nexport interface SitemapSkippedRoute {\n routeId: string\n pattern: string\n reason: string\n}\n\nexport interface CanvasSitemapResult {\n entries: MetadataRoute.Sitemap\n /** Routes whose candidates could not be published — explicit, never silent. */\n skipped: SitemapSkippedRoute[]\n}\n\n/** The origin half; the locale ⇄ path half is the router's (`localeUrlPath`, EC-486). */\nfunction originOf(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, '')\n}\n\n/** A candidate URL: what `resolvePage` is asked, what a crawler requests, and its group. */\ninterface Candidate {\n route: RouteRecord\n locale: Locale\n routePath: string\n path: string\n addressable: boolean\n /** Locale-invariant identity for `hreflang` grouping (EC-517). */\n group: string\n}\n\n/**\n * Enumerate the sitemap entries of one content source (published perspective). Pure over\n * the prefetched source — synchronous accessors, deterministic ordering (sorted by URL).\n */\nexport function canvasSitemapEntries(\n source: ContentSource,\n options: CanvasSitemapOptions,\n): CanvasSitemapResult {\n const routes = source.routes ?? []\n const locales = source.locales\n const skipped: SitemapSkippedRoute[] = []\n const skip = (route: RouteRecord, reason: string): void => {\n if (!skipped.some((s) => s.routeId === route.id && s.reason === reason)) {\n skipped.push({ routeId: route.id, pattern: route.pattern, reason })\n }\n }\n\n const clients = new Map<Locale, ContentClient>()\n const clientFor = (locale: Locale): ContentClient => {\n let client = clients.get(locale)\n if (!client) {\n client = source.client({ perspective: 'published', locale })\n clients.set(locale, client)\n }\n return client\n }\n\n // --- Candidates -------------------------------------------------------------------\n const candidates: Candidate[] = []\n // Every collection a route names, in the locales its routes serve (an agnostic route\n // serves every locale; locale-scoped routes only theirs).\n const collectionLocales = new Map<string, Set<Locale>>()\n for (const route of routes) {\n if (!route.document) continue\n const set = collectionLocales.get(route.document.collection) ?? new Set<Locale>()\n for (const locale of route.locale ? [route.locale] : locales.locales) set.add(locale)\n collectionLocales.set(route.document.collection, set)\n }\n for (const [collection, routeLocales] of collectionLocales) {\n for (const locale of locales.locales) {\n if (!routeLocales.has(locale)) continue\n for (const entry of clientFor(locale).urlsFor(collection)) {\n candidates.push({\n route: entry.route,\n locale,\n routePath: entry.routePath,\n path: entry.path,\n addressable: entry.addressable,\n // EC-517: group by the DOCUMENT — the locale-invariant fact — scoped by route.\n group: `${entry.route.id}${COMPOSITE_KEY_SEPARATOR}doc${COMPOSITE_KEY_SEPARATOR}${collection}${COMPOSITE_KEY_SEPARATOR}${entry.id}`,\n })\n }\n }\n }\n // A document-less static route is its own URL.\n for (const route of routes) {\n if (route.document || splitPath(route.pattern).some((segment) => segment.startsWith(':'))) continue\n for (const locale of route.locale ? [route.locale] : locales.locales) {\n const { urlPath, addressable } = localeUrlPath(route.pattern, locale, locales)\n candidates.push({\n route,\n locale,\n routePath: route.pattern,\n path: urlPath,\n addressable,\n group: `${route.id}${COMPOSITE_KEY_SEPARATOR}path${COMPOSITE_KEY_SEPARATOR}${route.pattern}`,\n })\n }\n }\n\n // --- The gate: the resolver serves it, it is indexable, a crawler can reach it ---------\n const byUrl = new Map<string, { group: string; locale: Locale }>()\n const byGroup = new Map<string, Map<Locale, string>>()\n // EC-486: per (route, locale), how many enumerated paths the RESOLVER refused — a mount\n // that composed paths but serves none is a dead declaration and says so.\n const tally = new Map<string, { route: RouteRecord; locale: Locale; total: number; unresolved: number }>()\n\n for (const candidate of candidates) {\n const key = `${candidate.route.id}${COMPOSITE_KEY_SEPARATOR}${candidate.locale}`\n const count = tally.get(key) ?? { route: candidate.route, locale: candidate.locale, total: 0, unresolved: 0 }\n count.total += 1\n tally.set(key, count)\n\n const result = clientFor(candidate.locale).resolvePage(candidate.routePath, candidate.locale)\n if (result.status !== 'ok') {\n // A `redirect` is a deliberate authoring act, not a dead route.\n if (result.status !== 'redirect') count.unresolved += 1\n continue\n }\n if (seoObjectOf(result.document, result.seoField)[SEO_SUBFIELDS.noindex] === true) continue\n // EC-486: the URL a crawler will actually request. For a path whose first segment is\n // another locale's code the host derives a different locale/path pair from it, so no\n // URL addresses this page — publishing one would submit a guaranteed 404.\n if (!candidate.addressable) {\n skip(\n candidate.route,\n `\"${candidate.routePath}\" (locale \"${candidate.locale}\") has no addressable URL — its first segment is read as a locale prefix`,\n )\n continue\n }\n const absolute = originOf(options.baseUrl) + candidate.path\n if (!byUrl.has(absolute)) byUrl.set(absolute, { group: candidate.group, locale: candidate.locale })\n const perLocale = byGroup.get(candidate.group) ?? new Map<Locale, string>()\n if (!perLocale.has(candidate.locale)) perLocale.set(candidate.locale, absolute)\n byGroup.set(candidate.group, perLocale)\n }\n\n for (const { route, locale, total, unresolved } of tally.values()) {\n if (total > 0 && unresolved === total && route.document && splitPath(route.pattern).at(-1)?.endsWith('*')) {\n skip(\n route,\n `hierarchy mount over \"${route.document.collection}\" composed ${total} path(s) in locale \"${locale}\", none of which the resolver serves`,\n )\n }\n }\n\n const entries: MetadataRoute.Sitemap = []\n for (const [url, { group }] of byUrl) {\n const perLocale = byGroup.get(group)\n // A single-locale group is a page that exists in one language; `hreflang` between\n // one URL and itself says nothing, so it stays off.\n const languages = perLocale && perLocale.size > 1 ? Object.fromEntries(perLocale) : undefined\n entries.push({ url, ...(languages ? { alternates: { languages } } : {}) })\n }\n entries.sort((a, b) => (a.url < b.url ? -1 : a.url > b.url ? 1 : 0))\n\n return { entries, skipped }\n}\n\nexport interface CreateCanvasSitemapOptions extends CanvasSitemapOptions {\n /** The same content loader the catch-all route uses (published perspective). */\n loadContent: (request: { perspective: 'published' }) => Promise<ContentSource>\n}\n\n/**\n * The `app/sitemap.ts` one-liner: `export default createCanvasSitemap({ … })`.\n * Loads the published content source and returns the enumerated entries\n * (skipped routes are dropped here — use {@link canvasSitemapEntries} to\n * inspect them).\n */\nexport function createCanvasSitemap(\n options: CreateCanvasSitemapOptions,\n): () => Promise<MetadataRoute.Sitemap> {\n return async () => {\n const source = await options.loadContent({ perspective: 'published' })\n return canvasSitemapEntries(source, options).entries\n }\n}\n","import { readRevalidationBody, verifyRevalidationSignature } from '@elytracms/core/revalidation'\nimport type { RevalidationEvent } from '@elytracms/core/revalidation'\n\n/**\n * Pure request logic of the revalidation webhook receiver (EC-146), kept free of\n * Next.js imports so it is unit-testable. `revalidate.ts` wires it into a route\n * handler with `revalidateTag`.\n *\n * The wire — event schema, signer, header — is NOT declared here: it is the one\n * module `@elytracms/core/revalidation`, shared with the emitter (the studio's\n * Convex write chokepoint) and the doctor probe (EC-579). This file only decides\n * the HTTP answer: auth before parsing, then the event.\n */\n\nexport type RevalidateEvaluation =\n | { ok: true; event: RevalidationEvent }\n | { ok: false; status: 400 | 401; message: string }\n\n/**\n * Evaluate one webhook request: verify the HMAC first (auth before parsing), then\n * read the event.\n *\n * - unconfigured (empty) secret → 401 for every request: the endpoint stays closed\n * until a secret is deliberately configured (mirrors EC-144 preview);\n * - missing/malformed/mismatched signature → 401 (constant-time verify);\n * - unparseable or schema-invalid body → 400, nothing revalidated; a body in the\n * pre-EC-579 tag-list shape gets its own named 400 (an older Elytra emitter —\n * deploy the matching studio functions), because a version skew between the\n * Convex deployment and this host is the one way a correctly signed body can\n * arrive in the wrong shape.\n */\nexport async function evaluateRevalidateRequest(\n rawBody: string,\n signature: string | null | undefined,\n secret: string | undefined,\n): Promise<RevalidateEvaluation> {\n if (secret === undefined || secret.length === 0) {\n return { ok: false, status: 401, message: 'Revalidation webhook is not configured.' }\n }\n if (typeof signature !== 'string' || signature.length === 0) {\n return { ok: false, status: 401, message: 'Missing or malformed webhook signature.' }\n }\n if (!(await verifyRevalidationSignature(rawBody, signature, secret))) {\n return { ok: false, status: 401, message: 'Invalid webhook signature.' }\n }\n const read = readRevalidationBody(rawBody)\n if (!read.ok) return { ok: false, status: 400, message: read.message }\n return { ok: true, event: read.event }\n}\n","import { revalidateTag } from 'next/cache'\nimport { REVALIDATE_SIGNATURE_HEADER } from '@elytracms/core/revalidation'\nimport { revalidationTags } from './cache'\nimport type { RevalidationProject } from './cache'\nimport { evaluateRevalidateRequest } from './revalidate-core'\n\n/**\n * Revalidation webhook route handler (EC-146): the receiving end of the\n * publish → live-in-seconds pipeline. The studio's Convex write chokepoint — the ONE\n * emitter (EC-579) — POSTs a signed EVENT (`{ kind, collection?, id? }`, the wire in\n * `@elytracms/core/revalidation`); this handler verifies the HMAC over the raw body,\n * derives the tags from the event with this host's OWN scope and locales\n * (`revalidationTags`, `cache.ts`) and calls `revalidateTag` for each, dropping the\n * cached entries whose fetch-time tag sets contained them.\n *\n * Server-to-server only: the emitter runs in Convex, so there is no CORS and no\n * `OPTIONS` preflight (the studio's browser dispatcher that needed them is deleted).\n * Server-only by construction — consumed from an App Router route handler file,\n * never from client bundles.\n */\n\nexport interface RevalidateRouteOptions extends RevalidationProject {\n /**\n * Shared HMAC secret. An empty/undefined secret rejects every request with\n * 401 — the endpoint stays closed until deliberately configured.\n */\n secret: string | undefined\n}\n\nexport interface RevalidateRouteHandlers {\n POST(request: Request): Promise<Response>\n}\n\n/**\n * Create the webhook route handler for `app/api/revalidate/route.ts`. Through the\n * delivery kit it is `export const { POST } = elytra.revalidateRoute()`, which hands\n * it the kit's own scope and locales — the ones its cached entries carry. Wiring it\n * by hand, pass the SAME scope your canvas route caches under:\n *\n * ```ts\n * export const { POST } = createRevalidateRoute({\n * secret: process.env.REVALIDATE_SECRET,\n * scope: process.env.ELYTRA_PROJECT_ID,\n * locales: ['en', 'de'],\n * })\n * ```\n *\n * Unsigned/invalid requests are rejected with 401 (constant-time HMAC verify, see\n * `evaluateRevalidateRequest`), signed-but-unreadable ones with 400; a valid event\n * revalidates its derived tags and answers `{ revalidated: tags }` (`[]` for the\n * doctor's `probe`).\n */\nexport function createRevalidateRoute(options: RevalidateRouteOptions): RevalidateRouteHandlers {\n const project: RevalidationProject = { scope: options.scope, locales: [...options.locales] }\n return {\n async POST(request: Request): Promise<Response> {\n const rawBody = await request.text()\n const signature = request.headers.get(REVALIDATE_SIGNATURE_HEADER)\n const evaluation = await evaluateRevalidateRequest(rawBody, signature, options.secret)\n if (!evaluation.ok) {\n return Response.json({ error: evaluation.message }, { status: evaluation.status })\n }\n const tags = revalidationTags(evaluation.event, project)\n // EC-515: `{ expire: 0 }`, NOT `'max'`. Next 16 requires a cache-life\n // profile as the second argument, and EC-219 reached for `'max'` on the\n // claim that it \"preserves the legacy drop-by-tag semantics\". **Measured\n // false on Next 16.2.10** — `'max'` is the *opposite* of a drop:\n //\n // `revalidateTag(tag, profile)` passes `{ expire: <profile>.expire }`\n // down to the cache handler. The `max` profile is `expire: 60*60*24*365`\n // (`server/config-shared.js`), so the default self-hosted handler stamps\n // `{ stale: now, expired: now + 365 days }` on the tag\n // (`FileSystemCache.revalidateTag`; the `'use cache'` handler stamps the\n // same way). `areTagsExpired` requires `expired <= now` — a year in the\n // future never qualifies — so the entry is only marked STALE: the next\n // visitor is served the old page while a background refresh runs. Under\n // the pre-EC-515 `unstable_cache` probe/bail two-pass that refresh could\n // never complete, so the stale value was served forever (measured).\n //\n // `{ expire: 0 }` stamps `expired: now`, which `areTagsExpired` treats as\n // an immediate hard drop — byte-for-byte the behaviour of the deprecated\n // one-argument form: the request after a publish is fresh. `CacheLifeConfig`\n // is a public part of the `revalidateTag` signature, so this is not a cast\n // or a back door. `updateTag` is still not an option — Server-Action-only,\n // throws here.\n //\n // Measured (self-hosted `next start`, Next 16.2.10): with `{ expire: 0 }`\n // every event's entries are fresh on the next request — the EC-515 case\n // table, `'use cache'` entries, Turbopack and webpack.\n for (const tag of tags) revalidateTag(tag, { expire: 0 })\n return Response.json({ revalidated: tags })\n },\n }\n}\n","import { INGEST_MAX_BODY_BYTES, readIngestPayload } from '@elytracms/core/ingest'\nimport type { IngestPayload } from '@elytracms/core/ingest'\n\n/**\n * EC-617 — the pure decision core of the PUBLIC INGEST route (`createIngestRoute`, the\n * Next wrapper in `ingest.ts`): no Next imports, no network, an injected clock — the\n * house \"pure core + thin wrapper\" shape of `evaluateRevalidateRequest` /\n * `createRevalidateRoute`.\n *\n * The route is a site's one public write path (ruling EC-565 § 6c). It runs, in order:\n *\n * 1. **method** — `POST` only (405);\n * 2. **configured** — the server-held `INGEST_SECRET` and the receiver URL are set (503\n * otherwise: the route stays closed until deliberately configured, never a no-op);\n * 3. **same-origin** — a browser request from another origin (`Origin` / `Sec-Fetch-Site`)\n * is refused (403). The route emits no CORS headers and reads no cookie;\n * 4. **rate limit** — a fixed window per CLIENT KEY (429, `Retry-After`);\n * 5. **size** — the raw body capped at `maxBodyBytes` (413), checked before parsing;\n * 6. **shape** — JSON in the ingest envelope (400, with the issues);\n * 7. **bot gate** — the host's own check, an injected seam (403, `{ gate: 'bot' }`): the\n * engine supplies the slot, never the vendor;\n * 8. **enrichment** (EC-618) — an optional seam an ADDON fills (the analytics addon derives\n * a country and a visitor's sketch coordinates here, where the request is): it returns the\n * payload to forward, which is re-validated against the envelope and the receiver's cap\n * (500 — the site's own fault — when it throws or returns something invalid). Absent, the\n * route is exactly what it was;\n *\n * then the wrapper signs the payload with the server-held secret and forwards it to the\n * one Convex receiver, relaying its answer. Only the PAYLOAD is forwarded — never a request\n * header — so whatever an enrichment reads from the request (the client address, the user\n * agent, a geo header) stays in this request's memory; only what it derives travels.\n */\n\n/** The answer the core gives the wrapper. */\nexport type IngestEvaluation =\n | { ok: true; payload: IngestPayload }\n | { ok: false; status: 400 | 403 | 405 | 413 | 429 | 500 | 503; body: Record<string, unknown>; headers?: Record<string, string> }\n\n/** What the host's bot gate answers (a Turnstile / hCaptcha / honeypot check the HOST implements). */\nexport type BotGateVerdict = { allow: true } | { allow: false; reason: string }\n\n/**\n * The bot-gate seam: called with the request and the parsed payload, AFTER the cheap\n * checks (so a flood never reaches a paid verification API). Absent → no gate.\n */\nexport type IngestBotGate = (input: { request: Request; payload: IngestPayload }) => BotGateVerdict | Promise<BotGateVerdict>\n\n/**\n * EC-618 — the enrichment seam: called with the request and the parsed payload LAST (after\n * the bot gate), it returns the payload the route signs and forwards. The engine supplies\n * the slot, never the code: an addon fills it (`@elytracms/analytics/next`'s\n * `analyticsEnrichment`), and a host may compose its own. Whatever it reads from the\n * request is held for this request only — the route forwards the returned payload and\n * nothing else. Absent → no enrichment, the payload forwarded as parsed.\n */\nexport type IngestEnrich = (input: { request: Request; payload: IngestPayload }) => IngestPayload | Promise<IngestPayload>\n\n/**\n * The fixed-window limiter — the AI route's pattern (EC-602), keyed per client. Holds,\n * in memory only, one `{ windowStart, count }` per key seen in the current window; expired\n * windows are dropped as the clock moves, so the map is bounded by the clients of one\n * window. Each server instance holds its own windows (a serverless platform runs several),\n * so the limit is per instance — the same documented limitation as the AI route.\n */\nexport interface FixedWindowLimiter {\n /** Count one request for `key`; `retryAfterSeconds` when it is over the limit. */\n hit(key: string): { allowed: true } | { allowed: false; retryAfterSeconds: number }\n /** How many keys the limiter currently holds (for the bound test). */\n size(): number\n}\n\nexport function createFixedWindowLimiter(options: {\n limit: number\n windowMs?: number\n now?: () => number\n}): FixedWindowLimiter {\n const windowMs = options.windowMs ?? 60_000\n const now = options.now ?? Date.now\n const windows = new Map<string, { windowStart: number; count: number }>()\n return {\n hit(key) {\n const at = now()\n // Drop every expired window first — the map never outlives the current window's clients.\n for (const [held, window] of windows) if (at - window.windowStart >= windowMs) windows.delete(held)\n const window = windows.get(key)\n if (!window) {\n windows.set(key, { windowStart: at, count: 1 })\n return { allowed: true }\n }\n if (window.count >= options.limit) {\n return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil((window.windowStart + windowMs - at) / 1000)) }\n }\n window.count += 1\n return { allowed: true }\n },\n size: () => windows.size,\n }\n}\n\n/**\n * The DEFAULT client key: the first hop of `x-forwarded-for` (what Vercel and most proxies\n * set), else `x-real-ip`, else one shared `unknown` bucket. An IP address is personal\n * data: it is held ONLY as a key of the in-memory window above, for at most one window,\n * and is never stored, logged or forwarded to the receiver. A host that wants a different\n * key (a hashed one, a session id) passes `clientKey`.\n */\nexport function defaultIngestClientKey(request: Request): string {\n const forwarded = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim()\n if (forwarded) return forwarded\n const real = request.headers.get('x-real-ip')?.trim()\n return real || 'unknown'\n}\n\n/** Is this a cross-origin BROWSER request? (A request with no browser headers is not judged here.) */\nexport function isCrossOrigin(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'none') return true\n const origin = request.headers.get('origin')\n if (!origin) return false\n try {\n return new URL(origin).origin !== new URL(request.url).origin\n } catch {\n return true\n }\n}\n\nexport interface IngestEvaluationInput {\n request: Request\n /** The raw body, read by the wrapper up to `maxBodyBytes + 1` bytes (`undefined` when over). */\n rawBody: string | undefined\n configured: boolean\n limiter: FixedWindowLimiter\n clientKey: (request: Request) => string\n botGate?: IngestBotGate\n enrich?: IngestEnrich\n}\n\n/** Evaluate one request (see the module header for the order). */\nexport async function evaluateIngestRequest(input: IngestEvaluationInput): Promise<IngestEvaluation> {\n const { request } = input\n if (request.method !== 'POST') {\n return { ok: false, status: 405, body: { error: 'Only POST is accepted.' }, headers: { allow: 'POST' } }\n }\n if (!input.configured) {\n return { ok: false, status: 503, body: { error: 'Ingest is not configured on this site.' } }\n }\n if (isCrossOrigin(request)) {\n return { ok: false, status: 403, body: { error: 'Cross-origin requests are not accepted.' } }\n }\n const limited = input.limiter.hit(input.clientKey(request))\n if (!limited.allowed) {\n return {\n ok: false,\n status: 429,\n body: { error: 'Too many requests — try again shortly.' },\n headers: { 'retry-after': String(limited.retryAfterSeconds) },\n }\n }\n if (input.rawBody === undefined) {\n return { ok: false, status: 413, body: { error: 'The payload is too large.' } }\n }\n const read = readIngestPayload(input.rawBody)\n if (!read.ok) {\n return { ok: false, status: 400, body: { error: read.message, ...(read.issues ? { issues: read.issues } : {}) } }\n }\n if (input.botGate) {\n const verdict = await input.botGate({ request, payload: read.value })\n if (!verdict.allow) {\n return { ok: false, status: 403, body: { error: 'Refused by the bot gate.', gate: 'bot', reason: verdict.reason } }\n }\n }\n if (!input.enrich) return { ok: true, payload: read.value }\n return enrichPayload(input.enrich, request, read.value)\n}\n\n/**\n * Run the enrichment and hold its result to the same rules as a visitor's payload: the\n * envelope, and the receiver's body cap. A failure is the SITE's (its own enrichment), so it\n * answers 500 naming the enrichment — never a visitor-facing 400.\n */\nasync function enrichPayload(enrich: IngestEnrich, request: Request, payload: IngestPayload): Promise<IngestEvaluation> {\n let enriched: IngestPayload\n try {\n enriched = await enrich({ request, payload })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return { ok: false, status: 500, body: { error: `The site's ingest enrichment failed: ${message}` } }\n }\n const body = JSON.stringify(enriched ?? null)\n const reread = readIngestPayload(body)\n if (!reread.ok) {\n return {\n ok: false,\n status: 500,\n body: { error: \"The site's ingest enrichment returned an invalid payload.\", ...(reread.issues ? { issues: reread.issues } : {}) },\n }\n }\n if (new TextEncoder().encode(body).byteLength > INGEST_MAX_BODY_BYTES) {\n return { ok: false, status: 500, body: { error: \"The site's ingest enrichment grew the payload past the receiver's cap.\" } }\n }\n return { ok: true, payload: reread.value }\n}\n\n/**\n * Read a request body as text, at most `maxBytes` (counted in UTF-8 bytes). `undefined`\n * when it is longer — the over-size body is never buffered whole. A declared\n * `Content-Length` over the cap is refused before a byte is read.\n */\nexport async function readCappedBody(request: Request, maxBytes: number = INGEST_MAX_BODY_BYTES): Promise<string | undefined> {\n const declared = Number(request.headers.get('content-length'))\n if (Number.isFinite(declared) && declared > maxBytes) return undefined\n if (!request.body) return ''\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n total += value.byteLength\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined)\n return undefined\n }\n chunks.push(value)\n }\n const bytes = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(bytes)\n}\n","import { INGEST_MAX_BODY_BYTES, INGEST_RECEIVER_PATH, signedIngestRequest } from '@elytracms/core/ingest'\nimport {\n createFixedWindowLimiter,\n defaultIngestClientKey,\n evaluateIngestRequest,\n readCappedBody,\n} from './ingest-core'\nimport type { IngestBotGate, IngestEnrich } from './ingest-core'\n\n/**\n * EC-617 — the PUBLIC INGEST route: a site's one write path into its project (form\n * submissions, analytics events, upload URLs), mounted SAME-ORIGIN in the site's own\n * App Router:\n *\n * ```ts\n * // frontend/app/api/ingest/route.ts — through the delivery kit\n * export const { POST } = elytra.ingestRoute()\n * ```\n *\n * The decisions are the pure core's (`evaluateIngestRequest`: method, configured,\n * same-origin, rate limit, size, shape, the host's bot gate); this wrapper then SIGNS the\n * payload with the server-held `INGEST_SECRET` (it never reaches a browser) and forwards it\n * to the deployment's ONE receiver (`<deployment>.convex.site/elytra/ingest`), relaying the\n * receiver's answer. A receiver that rejects the signature is a SITE misconfiguration\n * (the two secrets differ), answered 502 with that sentence — never a 401 a visitor could\n * read as their fault.\n *\n * Uploads leave same-origin by design: an `upload` payload asks the receiver for ONE\n * short-lived storage URL (the request rides this route's gate, limiter and bot gate), and\n * the browser then sends the bytes straight to that URL; the submission that follows claims\n * the stored file, which is checked against what the upload request declared.\n */\n\nexport interface IngestRouteOptions {\n /** The server-held HMAC secret (= the deployment's `ELYTRA_INGEST_SECRET`). Unset → 503. */\n secret: string | undefined\n /** The deployment's HTTP origin (`https://<name>.convex.site`); the receiver path is appended. Unset → 503. */\n siteUrl: string | undefined\n /** Cap on the raw JSON body, ≤ the receiver's `INGEST_MAX_BODY_BYTES` (default). */\n maxBodyBytes?: number\n /** Requests per client key per minute (default 30). */\n maxRequestsPerMinute?: number\n /** The rate limiter's key for a request (default: the client IP, in memory only — see `defaultIngestClientKey`). */\n clientKey?: (request: Request) => string\n /** The host's bot check (Turnstile, hCaptcha, a honeypot …). The engine supplies the slot, never the vendor. */\n botGate?: IngestBotGate\n /**\n * EC-618 — an addon's enrichment, run last: it returns the payload to sign and forward\n * (the analytics addon adds a country and the visitor's sketch coordinates, derived from\n * the request here and never forwarded raw). Absent → the payload is forwarded as parsed.\n */\n enrich?: IngestEnrich\n /** Injected clock (tests). */\n now?: () => number\n /** Injected transport (tests). */\n fetchImpl?: typeof fetch\n}\n\nexport interface IngestRouteHandlers {\n POST(request: Request): Promise<Response>\n}\n\nconst NO_STORE = { 'cache-control': 'no-store' }\n\n/** Create the ingest route handler (see the module header). */\nexport function createIngestRoute(options: IngestRouteOptions): IngestRouteHandlers {\n const maxBodyBytes = options.maxBodyBytes ?? INGEST_MAX_BODY_BYTES\n if (maxBodyBytes > INGEST_MAX_BODY_BYTES || maxBodyBytes <= 0) {\n // A larger body would only fail at the receiver — refuse the configuration at boot.\n throw new Error(`createIngestRoute: maxBodyBytes must be between 1 and ${INGEST_MAX_BODY_BYTES} (the receiver's cap).`)\n }\n const limiter = createFixedWindowLimiter({\n limit: options.maxRequestsPerMinute ?? 30,\n ...(options.now ? { now: options.now } : {}),\n })\n const clientKey = options.clientKey ?? defaultIngestClientKey\n const receiver = options.siteUrl ? `${options.siteUrl.replace(/\\/+$/, '')}${INGEST_RECEIVER_PATH}` : undefined\n const fetchImpl = options.fetchImpl ?? fetch\n\n return {\n async POST(request: Request): Promise<Response> {\n const rawBody = request.method === 'POST' ? await readCappedBody(request, maxBodyBytes) : ''\n const evaluation = await evaluateIngestRequest({\n request,\n rawBody,\n configured: options.secret !== undefined && options.secret.length > 0 && receiver !== undefined,\n limiter,\n clientKey,\n ...(options.botGate ? { botGate: options.botGate } : {}),\n ...(options.enrich ? { enrich: options.enrich } : {}),\n })\n if (!evaluation.ok) {\n return Response.json(evaluation.body, { status: evaluation.status, headers: { ...NO_STORE, ...evaluation.headers } })\n }\n const signed = await signedIngestRequest(evaluation.payload, options.secret as string)\n let answer: Response\n try {\n answer = await fetchImpl(receiver as string, { method: 'POST', headers: signed.headers, body: signed.body })\n } catch {\n return Response.json({ error: 'The ingest receiver could not be reached.' }, { status: 502, headers: NO_STORE })\n }\n if (answer.status === 401) {\n return Response.json(\n { error: \"The ingest receiver refused this site's signature — INGEST_SECRET must equal the deployment's ELYTRA_INGEST_SECRET.\" },\n { status: 502, headers: NO_STORE },\n )\n }\n const text = await answer.text()\n return new Response(text, {\n status: answer.status,\n headers: { ...NO_STORE, 'content-type': answer.headers.get('content-type') ?? 'application/json' },\n })\n },\n }\n}\n","import NextImage from 'next/image'\nimport type { ImageLoader } from 'next/image'\nimport type { ReactNode } from 'react'\nimport type { ResolvedAsset } from '@elytracms/core/content'\nimport { normalizeImageSource } from '@elytracms/components'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\n// TYPE-ONLY by design: importing `./host-image` at runtime would bundle a second\n// singleton instance into the server entry (see host-image.ts module docs).\nimport type { HostImageRenderer } from './host-image'\n\n/**\n * Image delivery for the embedded runtime (EC-152).\n *\n * ## The honest v1 design\n *\n * Stored asset bytes are served from plain blob-storage URLs (Convex file\n * storage serve URLs in production). Those URLs do **not** transform — there\n * is no `?w=`/`?q=` resizing service behind them, and no image CDN build-out\n * in v1. The standard, no-CDN path is therefore `next/image` with its\n * **default loader**: the host app's own Next.js optimizer fetches the remote\n * asset URL, resizes/re-encodes it, and serves the variants same-origin from\n * `/_next/image`. That is exactly what {@link ElytraImage} does.\n *\n * For that to work the host app must allowlist the asset origin in\n * `next.config.mjs`:\n *\n * ```js\n * const nextConfig = {\n * images: {\n * remotePatterns: [\n * // Convex file storage serve URLs:\n * { protocol: 'https', hostname: '*.convex.cloud' },\n * ],\n * },\n * }\n * ```\n *\n * {@link createAssetImageLoader} exists for the day the asset URLs sit behind\n * a CDN that *does* honor width/quality query params — it appends them and\n * nothing more. Pointing it at raw Convex serve URLs would silently serve\n * full-size bytes while pretending to resize, so it is **not** the default.\n */\n\n/**\n * The slice of the {@link ResolvedAsset} delivery shape an image needs:\n * the resolvable URL, intrinsic dimensions for layout stability, and alt\n * text. Structurally satisfied by every `ResolvedAsset`.\n */\nexport type ElytraImageAsset = Pick<\n ResolvedAsset,\n 'url' | 'width' | 'height' | 'alt' | 'focalPoint'\n> & {\n /**\n * LQIP blur-placeholder data URI (EC-420) → `next/image` `blurDataURL`. Optional\n * so existing call sites are unaffected; when present, the optimized image blurs\n * up while loading. `null`/absent = today's behaviour (no placeholder).\n */\n lqip?: string | null\n}\n\nexport interface ElytraImageProps {\n /** The resolved asset (delivery shape). `null` renders nothing. */\n asset: ElytraImageAsset | null | undefined\n /** `sizes` hint forwarded to `next/image` for responsive variants. */\n sizes?: string\n /** Preload hint forwarded to `next/image` (above-the-fold images). */\n priority?: boolean\n /** Quality (1–100) forwarded to `next/image`; Next defaults to 75. */\n quality?: number\n className?: string\n /**\n * Loading strategy forwarded to `next/image` (EC-428). `'eager'` opts an\n * image out of lazy-loading without the preload `priority` implies. Next throws\n * on `priority` + `loading='lazy'` together, so {@link ElytraImage} drops `lazy`\n * when `priority` is set (priority wins) — delivery never crashes.\n */\n loading?: 'eager' | 'lazy'\n /**\n * Custom `next/image` loader (e.g. from {@link createAssetImageLoader})\n * when assets sit behind a transforming CDN. Omit for the v1 default:\n * Next's own optimizer.\n */\n loader?: ImageLoader\n}\n\n/** LQIP raster long edge (EC-420). MUST equal studio `LQIP_MAX_EDGE` (thumbnail.ts). */\nconst LQIP_MAX_EDGE = 24\n\n/**\n * Recompute the EC-420 LQIP raster's pixel dimensions from the asset's intrinsic\n * size — the SAME `fitLongEdge` math the generator uses\n * (`packages/studio/src/lib/assets/thumbnail.ts:64-67`), duplicated here (3 lines)\n * because `packages/next` must NOT import studio internals (EC-428 AD-3). Fits the\n * long edge to {@link LQIP_MAX_EDGE}, rounds, floors each edge to ≥1px, and NEVER\n * upscales — so a ≤24px source keeps its own dims (the case the aspect-ratio\n * approximation gets wrong). Deterministic from `width`/`height`, non-null on the\n * NextImage path (the only place the wrap runs).\n */\nfunction fitLqipLongEdge(width: number, height: number): { width: number; height: number } {\n const scale = Math.min(1, LQIP_MAX_EDGE / Math.max(width, height))\n return { width: Math.max(1, Math.round(width * scale)), height: Math.max(1, Math.round(height * scale)) }\n}\n\n/**\n * Pre-wrap a raster LQIP data URI in an SVG that reproduces Next's OWN\n * static-import blur strength (EC-428 AD-2, ported from a consumer's reference implementation).\n *\n * Next's `getImageBlurSvg` only scales its blur (`blurWidth * 40` viewBox) for\n * **static imports** that carry `blurWidth`/`blurHeight`; a runtime `blurDataURL`\n * falls back to `stdDeviation=20` over the image's INTRINSIC-size viewBox — on a\n * large hero that is effectively no blur, so the ≤24px LQIP shows raw pixel blocks.\n * There is no public API to pass `blurWidth`/`blurHeight` for runtime URLs, so we\n * reproduce the scaled path ourselves: `viewBox = lqipDims * 40`, Next's identical\n * alpha-safe filter chain (feGaussianBlur → alpha-clamp feColorMatrix → feFlood/\n * feComposite `out` → composite over SourceGraphic → feGaussianBlur), and an\n * `<image preserveAspectRatio='none'>` over the original LQIP.\n *\n * Base64-encoded (`data:image/svg+xml;base64,`), NEVER percent-encoding: Next nests\n * this whole URI as its outer SVG's `<image href>`, and the browser percent-decodes\n * the outer data URI exactly once — percent-encoded nested `%xx` would corrupt. The\n * SVG string is pure ASCII (the inner `href` is itself a base64 data URI), so `btoa`\n * (a global in every runtime this targets — browser, Node ≥16, edge) is safe.\n */\nexport function scaledLqipBlur(lqip: string, width: number, height: number): string {\n const { width: lqipW, height: lqipH } = fitLqipLongEdge(width, height)\n const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${lqipW * 40} ${lqipH * 40}'><filter id='b' color-interpolation-filters='sRGB'><feGaussianBlur stdDeviation='20'/><feColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/><feFlood x='0' y='0' width='100%' height='100%'/><feComposite operator='out' in='s'/><feComposite in2='SourceGraphic'/><feGaussianBlur stdDeviation='20'/></filter><image width='100%' height='100%' x='0' y='0' preserveAspectRatio='none' style='filter:url(#b)' href='${lqip}'/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\n/**\n * Render a resolved asset through `next/image` (EC-152).\n *\n * - With known intrinsic dimensions the optimizer serves resized variants\n * and the reserved width/height prevent layout shift.\n * - Without dimensions `next/image` cannot render (it requires `width` +\n * `height` or `fill`), so the component degrades to a plain `<img>` —\n * un-optimized but visible, never a crash. Records written by the EC-151\n * upload flow always carry detected dimensions.\n * - A `null`/empty asset renders nothing (missing assets surface as\n * structured `unknown-asset` validation issues upstream, not here).\n */\nexport function ElytraImage(props: ElytraImageProps): ReactNode {\n const { asset, sizes, priority, quality, className, loader, loading } = props\n if (!asset || asset.url.length === 0) return null\n const alt = asset.alt ?? ''\n // EC-230: a focal point becomes CSS `object-position`, so an art-directed image\n // keeps its subject in frame when a layout crops it (`object-fit: cover`).\n // Inert under the default fit — harmless to set, correct once a layout crops.\n const objectPosition = asset.focalPoint\n ? `${asset.focalPoint.x * 100}% ${asset.focalPoint.y * 100}%`\n : undefined\n if (asset.width === null || asset.height === null) {\n // Honest fallback: next/image requires intrinsic dimensions; a\n // dimensionless record degrades to an unoptimized <img> instead of\n // crashing the page. No placeholder here — a plain <img> can't consume a\n // blurDataURL — and the wrap needs non-null dims, so it stays below this guard.\n return (\n <img\n data-ec-image=\"fallback\"\n src={asset.url}\n alt={alt}\n className={className}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n }\n // EC-420 (AD-2): a resolved LQIP data URI becomes a next/image blur-up\n // placeholder — the image blurs while the optimized bytes stream in.\n // EC-428 (AD-2/AD-3): but Next only scales its blur for static imports; a runtime\n // blurDataURL under-blurs a large image to raw pixel blocks — so pre-wrap the\n // raster LQIP in a viewBox-scaled SVG using the recomputed LQIP dims. AD-4: an\n // lqip that is ALREADY an SVG (a future engine that pre-blurs, or a consumer's\n // interim wrapper) passes through untouched. Absent lqip = pre-EC-420 behaviour.\n const blurDataURL =\n asset.lqip == null\n ? null\n : asset.lqip.startsWith('data:image/svg')\n ? asset.lqip\n : scaledLqipBlur(asset.lqip, asset.width, asset.height)\n const blur = blurDataURL ? { placeholder: 'blur' as const, blurDataURL } : {}\n // EC-428 (AD-5): forward `loading`, but Next throws when `priority` +\n // `loading='lazy'` arrive together (get-img-props.js). Priority wins; drop the\n // conflicting `lazy` so a priority image never crashes delivery.\n const loadingProp = priority && loading === 'lazy' ? undefined : loading\n return (\n <NextImage\n data-ec-image=\"next\"\n src={asset.url}\n alt={alt}\n width={asset.width}\n height={asset.height}\n {...(sizes !== undefined ? { sizes } : {})}\n {...(priority !== undefined ? { priority } : {})}\n {...(loadingProp !== undefined ? { loading: loadingProp } : {})}\n {...(quality !== undefined ? { quality } : {})}\n {...(className !== undefined ? { className } : {})}\n {...(loader !== undefined ? { loader } : {})}\n {...blur}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n}\n\nexport interface AssetImageLoaderOptions {\n /** Query param name for the requested width. Default `\"w\"`. */\n widthParam?: string\n /** Query param name for the requested quality. Default `\"q\"`. */\n qualityParam?: string\n /** Quality used when `next/image` passes none. Default `75`. */\n defaultQuality?: number\n}\n\n/**\n * A `next/image` loader for asset URLs served through a transforming CDN:\n * it appends width/quality query params to the asset URL and returns it.\n *\n * Be honest about what this does: it only *requests* a transform. Plain\n * Convex file-storage serve URLs ignore these params and return the original\n * bytes, so this loader is only correct once the asset origin actually\n * resizes (e.g. an image CDN in front of storage). Until then, use the\n * default `next/image` loader (see module docs) — that is the v1 path.\n */\nexport function createAssetImageLoader(options: AssetImageLoaderOptions = {}): ImageLoader {\n const widthParam = options.widthParam ?? 'w'\n const qualityParam = options.qualityParam ?? 'q'\n const defaultQuality = options.defaultQuality ?? 75\n return ({ src, width, quality }) => {\n const separator = src.includes('?') ? '&' : '?'\n return `${src}${separator}${widthParam}=${width}&${qualityParam}=${quality ?? defaultQuality}`\n }\n}\n\n/**\n * Per-deployment image-delivery options for {@link withNextImage} (EC-271).\n * The default is the host's own Next.js optimizer (Vercel) — zero config. A host\n * on a different CDN supplies its own `loader` (a plain function) to route variants\n * through that CDN instead, without touching component code.\n */\nexport interface NextImageOptions {\n /**\n * Custom `next/image` loader — the override seam for non-Vercel hosting. Omit\n * for the Vercel default (Next's own `/_next/image` optimizer). Supply a\n * function to delegate resizing to another CDN:\n *\n * - a CDN that honors `?w=&q=` query params → {@link createAssetImageLoader};\n * - Cloudflare Images / Imgix / a bespoke transform → your own `ImageLoader`\n * (e.g. rewrite to `/cdn-cgi/image/width=<w>,quality=<q>/<src>`).\n *\n * The loader receives `{ src, width, quality }` and returns the variant URL.\n */\n loader?: ImageLoader\n /** Default responsive `sizes` hint when a block doesn't set one. */\n sizes?: string\n /** Default quality (1–100) when a block doesn't set one (Next defaults to 75). */\n quality?: number\n}\n\n/**\n * The image-role implementation for Next.js host apps, rendered optimized through\n * {@link ElytraImage}. It reads the image role's contract (`ROLE_CONTRACTS`) — the\n * `src` asset prop and the literal `url` prop (EC-636: an asset never holds a URL, an\n * external image is `url`) — plus `alt`/`width`/`height`/`priority` as the shipped\n * `@elytracms/components` Image declares them, through the one reader\n * `normalizeImageSource`. The renderer resolves an `asset`-typed `src` (EC-195) to\n * `{ url, width, height, alt }` before this runs, so an uploaded image gets intrinsic\n * dimensions (→ no layout shift, sized variants); an external `url` has none unless\n * `width`/`height` are set, so it renders as the plain `<img>` fallback.\n * `priority` opts an above-the-fold image out of lazy-loading.\n */\nfunction createNextImage(options: NextImageOptions) {\n return function ElytraNextImage(props: Record<string, unknown>): ReactNode {\n const { url, width, height, alt, focalPoint, lqip } = normalizeImageSource(props)\n if (url.length === 0) return null\n return (\n <ElytraImage\n asset={{ url, width: width ?? null, height: height ?? null, alt, focalPoint, lqip }}\n {...(props.priority === true ? { priority: true } : {})}\n {...(options.loader !== undefined ? { loader: options.loader } : {})}\n {...(options.sizes !== undefined ? { sizes: options.sizes } : {})}\n {...(options.quality !== undefined ? { quality: options.quality } : {})}\n />\n )\n }\n}\n\n/**\n * Batteries included, opted into (EC-591): return the component list with the\n * implementation of the component that fills the `image` role swapped for\n * `next/image` delivery ({@link ElytraImage}) — the manifest is kept, so the\n * studio and the host still register the same component. Nothing is added to the\n * list and nothing is registered by the engine: no image-role component in the\n * list → the list comes back unchanged.\n *\n * Which entry fills the role is decided by core's one registration rule (the first\n * valid claimant — `ComponentRegistry.roleComponent('image')`), so the swap can\n * never land on a different entry than the one the registry resolves.\n *\n * It registers the list BEFORE `createElytraHost` resolves the entry through the config\n * gate, so it must not be the first place a broken list is read (EC-678): the\n * registration never throws — a malformed manifest is a located finding, and the gate\n * reads the same registration — so a broken list comes back from here unchanged and the\n * host's boot throws the gate's LOCATED error (`invalid-config-shape at\n * components.N.manifest.props.p: …`), the same one `elytra doctor` `config-valid` and the\n * studio report. (Measured on `6310b7b4`: a prop that was not a field-def threw here,\n * unlocated, before the gate ran.) Use it on\n * the DELIVERY side only (the studio's canvas runs on Vite, where `next/image`\n * does not exist) — the scaffold's host seam file does it in one line:\n *\n * ```ts\n * export const elytra = createElytraHost({ ...site, components: withNextImage(site.components) })\n * ```\n *\n * Pass {@link NextImageOptions} to delegate image delivery to another CDN\n * (EC-271) — the v1 default is Vercel, with room to grow:\n *\n * ```ts\n * withNextImage(site.components, { loader: createAssetImageLoader() }) // ?w=&q= CDN\n * withNextImage(site.components, { loader: cloudflareImageLoader }) // Cloudflare Images\n * ```\n */\nexport function withNextImage(\n components: readonly HostComponentEntry[] | undefined,\n options: NextImageOptions = {},\n): HostComponentEntry[] {\n const list = [...(components ?? [])]\n const image = registerComponents(list).registry.roleComponent('image')\n if (!image) return list\n const implementation = createNextImage(options)\n // Read null-safely: a list element that is not an entry is the gate's to refuse, located.\n return list.map((entry) =>\n (entry as HostComponentEntry | null | undefined)?.manifest === image ? { manifest: entry.manifest, implementation } : entry,\n )\n}\n\n/** Per-deployment options for {@link elytraHostImage} (the EC-271 loader seam). */\nexport interface ElytraHostImageOptions {\n /** Custom `next/image` loader for non-Vercel CDNs (see {@link NextImageOptions}). */\n loader?: ImageLoader\n /** Default quality (1–100) when a call site doesn't set one (Next defaults to 75). */\n quality?: number\n}\n\n/**\n * The `next/image`-backed {@link HostImageRenderer} (EC-368 image defaults): the\n * renderer a Next host registers on the `@elytracms/next/host-image` bridge so\n * DUAL-RENDERED project components (which can't import `next/image` themselves)\n * deliver optimized images — resized/modern-format variants, intrinsic\n * dimensions (no layout shift), focal-point `object-position`. A dimensionless\n * record degrades to a plain `<img>` (honest, never a crash), exactly like\n * {@link ElytraImage}.\n *\n * ```ts\n * import { elytraHostImage } from '@elytracms/next'\n * import { setHostImage } from '@elytracms/next/host-image'\n *\n * setHostImage(elytraHostImage()) // once, at host boot (never in the studio)\n * ```\n */\nexport function elytraHostImage(options: ElytraHostImageOptions = {}): HostImageRenderer {\n // EC-428 (AD-5): forward `loading` — before this the renderer destructured\n // only asset/alt/className/priority/sizes, so a non-priority eager image silently\n // fell back to lazy. ElytraImage resolves the priority + loading='lazy' conflict.\n // EC-587: an omitted `alt` is the asset's own (the one reader's value, already on\n // the resolved asset); a caller's explicit alt — e.g. '' for a decorative image — wins.\n return ({ asset, alt, className, priority, sizes, loading }) => (\n <ElytraImage\n asset={{ ...asset, alt: alt ?? asset.alt }}\n {...(className !== undefined ? { className } : {})}\n {...(priority ? { priority: true } : {})}\n {...(sizes !== undefined ? { sizes } : {})}\n {...(loading !== undefined ? { loading } : {})}\n {...(options.loader !== undefined ? { loader: options.loader } : {})}\n {...(options.quality !== undefined ? { quality: options.quality } : {})}\n />\n )\n}\n","import type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { compositionNodesOf } from './canvas-renderer'\nimport type { DeliveryPresentation } from './canvas-renderer'\n\n/**\n * Manifest-driven header clearance (EC-368): does this page OPEN full-bleed?\n *\n * A host with a floating header (occupying no flow space) must pad a page's\n * content down by the header height — unless the page's FIRST canvas block is a\n * structural hero that deliberately bleeds to the viewport top and renders\n * under the header. Which components bleed is declared on their OWN manifest\n * (`presentation: { fullBleed: true }`, `@elytracms/core/component-registry`),\n * so the host derives the decision from the registry instead of keeping a\n * parallel hand-maintained component-id set that silently drifts when a new\n * hero ships.\n *\n * Pure over the delivery-shaped document: the composition field is found by\n * VALUE (the same `compositionNodesOf` selection the renderer uses), so hosts\n * with `body`, `content`, or bespoke composition field names all work. Absent\n * document, empty composition, unknown component, or an undeclared hint all\n * answer `false` — the safe default is clearance.\n *\n * EC-499: pass the collection's resolved `presentation` (from the render\n * outcome) so this reads the SAME body the renderer will render when the\n * collection declares one — otherwise the value-shape pick could name a\n * different field than the declaration does. A body whose first item is prose\n * rather than a block answers `false`, which is right: rich text opens no hero.\n *\n * ```tsx\n * const outcome = client.resolvePage(path)\n * const fullBleed = firstBlockFullBleed(outcome.document, components.registry)\n * return fullBleed ? page : <div className=\"pt-[var(--header-clearance)]\">{page}</div>\n * ```\n *\n * The host kit wires the resolution half for you: `elytra.pageOpensFullBleed(slug)`.\n */\nexport function firstBlockFullBleed(\n document: Record<string, unknown> | null | undefined,\n registry: ComponentRegistry,\n presentation?: DeliveryPresentation,\n): boolean {\n const first = compositionNodesOf(document, presentation)[0]\n if (!first) return false\n return registry.getManifest(first.componentId)?.presentation?.fullBleed === true\n}\n","/**\n * Env resolution for the delivery host kit (EC-368). ONE documented name per\n * concern — every kit surface reads the same resolved bag, so the two-\n * disconnected-secrets class of bug (a preview route gating on one env var\n * while the draft loader reads another) is structurally impossible: there is\n * exactly one `DRAFT_PREVIEW_TOKEN`, consumed by BOTH the preview-enable route\n * and the draft snapshot fetch.\n *\n * | Env var | Concern |\n * | --------------------- | ---------------------------------------------------- |\n * | `ELYTRA_CONVEX_URL` | Convex deployment URL — presence (with the project |\n * | | id) switches the loader to LIVE mode |\n * | `ELYTRA_PROJECT_ID` | The project the studio publishes under (= tag scope) |\n * | `DRAFT_PREVIEW_TOKEN` | The ONE draft secret: gates the preview-enable route |\n * | | AND authenticates the draft snapshot fetch (must |\n * | | equal the Convex deployment's own env var) |\n * | `REVALIDATE_SECRET` | Instant-publish webhook HMAC secret (must equal the |\n * | | deployment's `CANVAS_REVALIDATE_SECRET`) |\n * | `INGEST_SECRET` | Public-ingest HMAC secret (EC-617; must equal the |\n * | | deployment's `ELYTRA_INGEST_SECRET`) |\n *\n * EC-579 retired `STUDIO_ORIGIN` (the CORS origin of the studio's deleted browser\n * dispatcher): the one emitter is the Convex deployment, server to server.\n */\n\n/** The kit's documented env names (single source of truth, also used in docs/tests). */\nexport const ELYTRA_HOST_ENV_KEYS = {\n convexUrl: 'ELYTRA_CONVEX_URL',\n projectId: 'ELYTRA_PROJECT_ID',\n draftToken: 'DRAFT_PREVIEW_TOKEN',\n revalidateSecret: 'REVALIDATE_SECRET',\n ingestSecret: 'INGEST_SECRET',\n} as const\n\nexport interface ElytraHostEnv {\n convexUrl?: string\n projectId?: string\n draftToken?: string\n revalidateSecret?: string\n ingestSecret?: string\n}\n\nfunction cleaned(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed !== undefined && trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Resolve the kit's env bag from a raw env record (`process.env` by default at\n * the factory). Values are trimmed; empty strings resolve to `undefined` — an\n * env var set to `\"\"` must behave exactly like an unset one (endpoints stay\n * closed, live mode stays off), never like a configured empty secret.\n */\nexport function resolveHostEnv(env: Record<string, string | undefined>): ElytraHostEnv {\n const resolved: ElytraHostEnv = {}\n for (const [key, name] of Object.entries(ELYTRA_HOST_ENV_KEYS)) {\n const value = cleaned(env[name])\n if (value !== undefined) resolved[key as keyof ElytraHostEnv] = value\n }\n return resolved\n}\n","import { DRAFT_VARIANT, variantOf } from '@elytracms/core/cms-core'\nimport type { CollectionDef, LocaleConfigInput, Perspective, RouteRecord } from '@elytracms/core/cms-core'\nimport { parseDeliveredDocuments, parseDeliveredRedirects } from '@elytracms/core/content'\nimport type { ContentDocumentSource } from '@elytracms/core/content'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { createStaticContentSource } from './snapshot'\nimport type { ContentSource } from './source'\n\n/**\n * The LIVE half of the delivery host kit (EC-368): fetch the delivery snapshot\n * from a Convex deployment (`convex/delivery.ts` in the builder app) over\n * Convex's public HTTP query API — a plain `fetch` to\n * `POST {deployment}/api/query`, no Convex npm dependency — and assemble it\n * through the same `createStaticContentSource` path the fixture snapshot uses,\n * so accessor and rendering semantics are byte-identical across seeded, empty,\n * and live modes. This engine-izes the ~300-line `live-content.ts` both real\n * consumers hand-copied (EC-369 finding #5); the fetch + Zod-validation logic\n * now lives in exactly one place.\n *\n * Perspective handling: published requests fetch the PUBLIC `delivery:snapshot`\n * (published documents only — the standing live-is-published-only invariant).\n * Draft-mode requests fetch `delivery:draftSnapshot`, gated by the deployment's\n * `DRAFT_PREVIEW_TOKEN` — the same single secret that gates the kit's\n * preview-enable route (see `host-env.ts`).\n *\n * Routes: the config's route records (AD-3/AD-11, EC-207), exactly as resolved —\n * the backend never carries route data, and the engine derives none (EC-590: the\n * implicit \"`/` → the lowest-id `page`\" home fallback is gone with every other\n * engine-dictated name; a project declares its `/`, the scaffold ships one).\n */\n\nexport interface LiveContentOptions {\n /** Convex deployment URL, e.g. `https://your-deployment-123.convex.cloud`. */\n convexUrl: string\n projectId: string\n /**\n * Project locale config — config-owned (AD-11), never read from the snapshot.\n * The declared shape; parsed where the source is assembled (EC-580).\n */\n locales: LocaleConfigInput\n /** CMS collection schema — config-owned (AD-11); drives depth-1 relation population. */\n collections: readonly CollectionDef[]\n /** Route records — config-owned (AD-3/AD-11), the resolved project's. */\n routes: readonly RouteRecord[]\n /** `DRAFT_PREVIEW_TOKEN` — required for draft-perspective loads. */\n draftToken?: string\n /** Fetch implementation override (tests, custom agents). Defaults to global fetch. */\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * The `delivery.ts` payload shape (validated field-by-field below). A\n * deployment IS one environment (EC-179); the snapshot carries content only\n * (EC-287) — documents, assets, editorial redirects (EC-303). (A `sources`\n * strand from an older deployment is ignored — it belonged to the deleted\n * data-binding subsystem, EC-574.)\n */\ninterface DeliverySnapshotPayload {\n projectId: string\n redirects?: unknown[]\n /**\n * EC-628 — the stored redirects the deployment withheld because it could not read them\n * (`invalid-redirect` issues from the one live rule). Absent from a deployment older than\n * EC-628, which serves such rows in `redirects` instead (and this host names them there).\n */\n redirectIssues?: unknown[]\n documents: unknown[]\n /**\n * EC-670 — the stored documents the deployment withheld because it could not unfold them\n * (`invalid-stored-document` issues: a folded value whose payload does not parse). Absent\n * from a deployment older than EC-670, whose snapshot rejected altogether instead.\n */\n documentIssues?: unknown[]\n assets: unknown[]\n}\n\n/**\n * Run one query through Convex's public HTTP API. Response envelope (verified\n * against live deployments): `{ status: 'success', value }` or\n * `{ status: 'error', errorMessage }`. `cache: 'no-store'` keeps Next's fetch\n * cache out of the loop — the route helper's tagged data cache is the only\n * cache layer, so tag revalidation always reaches the deployment.\n */\nasync function runConvexQuery(\n fetchImpl: typeof globalThis.fetch,\n convexUrl: string,\n path: string,\n args: Record<string, unknown>,\n): Promise<unknown> {\n const response = await fetchImpl(`${convexUrl.replace(/\\/$/, '')}/api/query`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ path, args, format: 'json' }),\n cache: 'no-store',\n })\n const body = (await response.json().catch(() => undefined)) as\n | { status?: string; value?: unknown; errorMessage?: string; message?: string }\n | undefined\n if (!response.ok || body?.status !== 'success') {\n throw new Error(\n `Convex query \"${path}\" failed (${response.status}): ${\n body?.errorMessage ?? body?.message ?? 'no error message'\n }`,\n )\n }\n return body.value\n}\n\n/**\n * Assemble a fetched delivery-snapshot payload into a `ContentSource` — the\n * pure half of {@link loadLiveContent}, exported so the parsing/assembly rules\n * are directly testable against fixture payloads (no network).\n */\nexport function assembleLiveSource(\n payload: DeliverySnapshotPayload,\n options: Pick<LiveContentOptions, 'locales' | 'collections' | 'routes'>,\n perspective: Perspective,\n): ContentSource {\n // Locales/collections/routes are config-owned (AD-11): from the host repo's\n // own `cms/`, passed in via options — never from the snapshot.\n const locales = options.locales\n // EC-585 (Codex F12): THE delivery parse — a stored row that is not a canonical document\n // is not delivered, and is named as a visible `invalid-stored-document` issue on the\n // source's clients (it used to vanish with zero issues). EC-670 (Codex F19): so is every\n // row the deployment withheld upstream (`documentIssues` — a folded value that would not\n // decode), carried beside the rows it did serve.\n const { documents, issues: documentIssues } = parseDeliveredDocuments(\n payload.documents,\n payload.documentIssues ?? [],\n )\n // EC-628 (Codex F04): the redirect strand gets the same — a delivered redirect this host\n // cannot read, and every redirect the deployment withheld, is an `invalid-redirect` issue.\n const delivered = parseDeliveredRedirects(payload.redirects ?? [], payload.redirectIssues ?? [])\n const sourceIssues = [...documentIssues, ...delivered.issues]\n\n // The config's routes, as resolved — no engine-derived home route (EC-590).\n const routes = [...options.routes]\n\n // EC-224 B-1: the published snapshot ships each document as its PINNED\n // published content (delivery resolved the version pointer server-side). Wrap\n // those rows as a `DocumentHistory` so the pure published gate\n // (`selectPerspective`) serves `.published` directly.\n //\n // TRUST CONTRACT: the wrap treats whatever the published fetch returned AS\n // published and deliberately does NOT re-gate — it cannot, the wire carries\n // no version pointer. Correctness rests ENTIRELY on `delivery:snapshot`\n // shipping published-only content (pinned version snapshots, never a draft —\n // see the builder's `convex/delivery.ts`). Guard the invariant in delivery,\n // not here. Draft-fetch rows stay bare: the draft perspective's\n // `selectPerspective` serves them as-is (a bare row is the draft variant row).\n const documentSources: ContentDocumentSource[] =\n perspective === 'published'\n ? documents.map((doc) => ({ draft: doc, published: doc, versions: [] }))\n : documents\n\n return createStaticContentSource({\n projectId: payload.projectId,\n locales,\n routes,\n // Redirects are editorial DATA (EC-303): the ENABLED ones ship in the\n // snapshot, so they change dynamically (no redeploy). Native next.config\n // redirects run before this.\n redirects: delivered.redirects,\n collections: [...options.collections],\n documents: documentSources,\n assets: payload.assets as AssetRecord[],\n ...(sourceIssues.length > 0 ? { sourceIssues } : {}),\n })\n}\n\n/** Fetch the live delivery snapshot and assemble the `ContentSource` for one request. */\nexport async function loadLiveContent(\n options: LiveContentOptions,\n perspective: Perspective,\n): Promise<ContentSource> {\n const fetchImpl = options.fetch ?? globalThis.fetch\n const args: Record<string, unknown> = { projectId: options.projectId }\n\n let payload: DeliverySnapshotPayload\n const variant = variantOf(perspective)\n if (variant !== undefined) {\n // EC-595: a perspective may name any variant; the deployment serves exactly one\n // variant snapshot, the draft's. A release perspective (v1.x) is refused here,\n // visibly, rather than read from the draft snapshot as if it were the draft.\n if (variant !== DRAFT_VARIANT) {\n throw new Error(\n `Elytra live content: no delivery snapshot serves the \"${variant}\" variant — the deployment ` +\n `serves the published snapshot and the \"${DRAFT_VARIANT}\" variant's only.`,\n )\n }\n if (!options.draftToken) {\n // Explicit, visible failure: draft mode without a configured token must\n // never silently fall back to published content.\n throw new Error(\n 'Draft preview requested but DRAFT_PREVIEW_TOKEN is not configured on the host.',\n )\n }\n payload = (await runConvexQuery(fetchImpl, options.convexUrl, 'delivery:draftSnapshot', {\n ...args,\n token: options.draftToken,\n })) as DeliverySnapshotPayload\n } else {\n payload = (await runConvexQuery(\n fetchImpl,\n options.convexUrl,\n 'delivery:snapshot',\n args,\n )) as DeliverySnapshotPayload\n }\n\n return assembleLiveSource(payload, options, perspective)\n}\n","import { validateLocale } from '@elytracms/core/cms-core'\nimport type { DocumentRef, Locale, Perspective } from '@elytracms/core/cms-core'\nimport type { ContentClient, ResolvedDocument } from '@elytracms/core/content'\nimport type { ContentSource } from './source'\n\n/**\n * Site-chrome globals for delivery hosts (EC-368, from EC-369 finding #5): the\n * layout, header, and footer are NOT rendered through the canvas, so they read\n * the `settings` singleton + `navigation` documents straight from the\n * `ContentSource` and pre-resolve everything — keeping the chrome components\n * pure presentational, with no `ContentClient` or `@elytracms/*` knowledge.\n * Both real consumers hand-authored this ~200-line layer independently; the\n * MECHANISM now lives here, while the per-project projection (which settings\n * fields exist, what the chrome looks like) stays consumer-owned:\n *\n * - **settings**: the singleton collection's lone document (EC-274) — no\n * document id is ever named.\n * - **navigation by slot**: menus are addressed by their EC-284 alias slot\n * (`main`, `footer`), never a document id — delivery shape carries `alias`\n * since EC-364.\n * - **link resolution**: link-object values (`linkType: href | page | file`)\n * resolve through `client.resolveLink` — the same `urlForDocument` authority\n * the router serves, so a nav link to a nested page carries its FULL composed\n * path (`/krippe/tagesablauf`, EC-218/EC-360), and `home → '/'` needs no\n * special-casing.\n * - **asset resolution**: asset field values (stored ids) resolve to serve URLs\n * through the source's delivery-shaped asset list.\n *\n * Everything degrades explicitly: a missing settings document is `null`, a\n * missing slot is `null`/`[]`, an unresolvable link drops out (never a silent\n * `#`), an unknown asset id is `undefined` — the host renders sensible empties\n * for the empty-source build and surfaces gaps honestly.\n */\n\nexport interface GlobalsConventions {\n /** The settings singleton's collection id. Default `'settings'`. */\n settingsCollection?: string\n /** The navigation documents' collection id. Default `'navigation'`. */\n navigationCollection?: string\n /**\n * Field names probed (in order) for a navigation document's entry list.\n * Default `['items', 'links']` — the two shapes the shipped consumers and the\n * starter template use.\n */\n navigationItemsFields?: readonly string[]\n}\n\n/** A fully-resolved link: the composed href plus the new-tab flag. */\nexport interface ResolvedLink {\n href: string\n openInNewTab: boolean\n}\n\n/** A leaf navigation link (children of a dropdown / footer column). */\nexport interface ResolvedNavLink extends ResolvedLink {\n label: string\n /** Optional subline shown under the label (wide header dropdowns). */\n description?: string\n}\n\n/** A top-level navigation entry; `href` is absent for pure group headings. */\nexport interface ResolvedNavEntry {\n label: string\n href?: string\n openInNewTab: boolean\n description?: string\n children: ResolvedNavLink[]\n}\n\n/** The resolved chrome-globals surface `loadGlobals` yields (EC-368). */\nexport interface SiteGlobals {\n /** The settings singleton, delivery-shaped, or `null` (empty build / not published). */\n settings: ResolvedDocument | null\n /** The navigation document filling a slot alias (EC-284/EC-364), or `null`. */\n navigation(slot: string): ResolvedDocument | null\n /**\n * A slot's entry list, fully resolved: labels, composed hrefs (nested paths\n * included), new-tab flags, sublines, one level of children. Entries whose\n * link cannot resolve to an href are kept only when they carry children (a\n * group heading); leaf entries without a resolvable href drop out.\n */\n navigationEntries(slot: string): ResolvedNavEntry[]\n /** Resolve a link-object field value to an href + new-tab flag, or `null`. */\n resolveLink(value: unknown): ResolvedLink | null\n /** Resolve an asset field value (stored id / URL / `{ url }`) to a URL, or `undefined`. */\n assetUrl(value: unknown): string | undefined\n /** The accessor client behind the globals — for bespoke projections. */\n client: ContentClient\n /** The underlying content source (assets(), locales, routes). */\n source: ContentSource\n}\n\nexport interface ResolveGlobalsOptions {\n /** Perspective of the chrome read. Default `'published'` (live is published-only). */\n perspective?: Perspective\n /**\n * The request's locale (EC-720). Default: the source's default locale. A locale the\n * project does not declare throws a located `unknown-locale` error — read silently, it\n * would serve every field from the default locale's fallback and look like a translation\n * gap rather than a wrong argument.\n */\n locale?: Locale\n conventions?: GlobalsConventions\n}\n\nconst DEFAULT_ITEMS_FIELDS = ['items', 'links'] as const\n\n/**\n * EC-720: the chrome is read in a locale the project DECLARES, or not at all. The content\n * client takes any code and answers an undeclared one from the default locale's fallback,\n * field by field — a wrong argument that would look like a translation gap on the page.\n * The project's locales are the source's own (`source.locales`, parsed from the config\n * the studio authors in), so the one rule is read here, where both chrome readers\n * (`resolveGlobals`, `elytra.loadGlobals`) pass.\n */\nfunction assertDeclaredLocale(source: ContentSource, locale: Locale): void {\n const [issue] = validateLocale(source.locales, locale)\n if (!issue) return\n throw new Error(\n `Elytra chrome globals: ${issue.code} at ${issue.path.join('.')} — the locale \"${locale}\" is not one of ` +\n `this project's locales (it declares ${source.locales.locales.join(', ')}). Pass the locale the page ` +\n `renders in — resolveLocale({ pathname }, locales) splits it from the URL exactly as the catch-all ` +\n `does — or omit it for the default locale.`,\n )\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction items(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value) ? value.map(record) : []\n}\n\n/**\n * Resolve the chrome globals over an already-loaded `ContentSource` — the pure\n * core of the host kit's `loadGlobals` (directly testable over fixture\n * sources).\n */\nexport function resolveGlobals(\n source: ContentSource,\n options: ResolveGlobalsOptions = {},\n): SiteGlobals {\n const perspective = options.perspective ?? 'published'\n const conventions = options.conventions ?? {}\n const settingsCollection = conventions.settingsCollection ?? 'settings'\n const navigationCollection = conventions.navigationCollection ?? 'navigation'\n const itemsFields = conventions.navigationItemsFields ?? DEFAULT_ITEMS_FIELDS\n\n if (options.locale !== undefined) assertDeclaredLocale(source, options.locale)\n const client = source.client({ perspective, ...(options.locale !== undefined ? { locale: options.locale } : {}) })\n const assets = source.assets?.(options.locale) ?? []\n const assetById = new Map(assets.map((asset) => [asset.id, asset] as const))\n\n const listDocs = (collection: string): ResolvedDocument[] => {\n const result = client.listDocuments(collection)\n return result.ok ? result.documents : []\n }\n\n // EC-274: `settings` is a singleton — the lone document of its collection.\n const settings = listDocs(settingsCollection)[0] ?? null\n\n // EC-284/EC-364: menus are addressed by slot alias through the content client\n // (delivery shape carries `alias`), never by document id.\n const navDocs = listDocs(navigationCollection)\n const navigation = (slot: string): ResolvedDocument | null =>\n navDocs.find((doc) => (doc as Record<string, unknown>).alias === slot) ?? null\n\n /**\n * Asset field value → serve URL: a delivered asset object's `url`, or a raw asset id\n * resolved against the baked assets. EC-587 / 2h: an asset VALUE never holds a URL,\n * so a URL-shaped string is not passed through (the validator names it as an\n * `invalid-field-value`); an external image is a literal `src` on a component.\n */\n const assetUrl = (value: unknown): string | undefined => {\n if (typeof value === 'string') return assetById.get(value)?.url\n const url = record(value).url\n return typeof url === 'string' && url.length > 0 ? url : undefined\n }\n\n /** A raw or populated relation value's `{ collection, id }` identity, if present. */\n const refOf = (value: unknown): DocumentRef | null => {\n const ref = record(value)\n return typeof ref.collection === 'string' && typeof ref.id === 'string'\n ? { collection: ref.collection, id: ref.id }\n : null\n }\n\n /** Resolve a link-object value (`linkType: href | page | file`) to an href. */\n const resolveLink = (value: unknown): ResolvedLink | null => {\n const link = record(value)\n const openInNewTab = link.openInNewTab === true\n const href = str(link.href) || str(link.url)\n const linkType = str(link.linkType) || (href ? 'href' : '')\n\n if (linkType === 'href' || linkType === 'external') {\n return href ? { href, openInNewTab } : null\n }\n if (linkType === 'file') {\n const url = assetUrl(link.file)\n return url ? { href: url, openInNewTab } : null\n }\n if (linkType === 'page') {\n const page = link.page\n // The reverse-router authority (EC-291/EC-360): the SAME `urlForDocument`\n // the routes serve — composed nested paths, `home → '/'`, locale prefix.\n const ref = refOf(page)\n if (ref) {\n const path = client.resolveLink(ref)\n if (path !== null) return { href: path, openInNewTab }\n }\n // A populated relation's derived `path` (the same router, EC-360); else explicit\n // null. EC-590 (IDENTITY #12): no `'/' + slug` guess — for a document no route\n // addresses it was a silent 404, where the same target in rich text is a visible\n // `unresolved-ref`. The engine dictates no URL shape.\n const shaped = record(page)\n if (typeof shaped.path === 'string' && shaped.path.startsWith('/')) {\n return { href: shaped.path, openInNewTab }\n }\n return null\n }\n return null\n }\n\n const toNavLink = (entry: Record<string, unknown>): ResolvedNavLink | null => {\n const label = str(entry.label)\n // Structured shape: the link object lives under `link`; flat starter shape:\n // the entry itself carries `href` (resolveLink's href fallback picks it up).\n const resolved = resolveLink('link' in entry ? entry.link : entry)\n if (!label || !resolved) return null\n const description = str(entry.description)\n return { label, ...resolved, ...(description ? { description } : {}) }\n }\n\n const toNavEntry = (entry: Record<string, unknown>): ResolvedNavEntry | null => {\n const label = str(entry.label)\n if (!label) return null\n const resolved = resolveLink('link' in entry ? entry.link : entry)\n const description = str(entry.description)\n const children = items(entry.children)\n .map(toNavLink)\n .filter((child): child is ResolvedNavLink => child !== null)\n // A leaf entry needs a resolvable target; an entry with children stands as a\n // group heading even without one (footer columns, pure dropdowns).\n if (!resolved && children.length === 0) return null\n return {\n label,\n ...(resolved ? { href: resolved.href } : {}),\n openInNewTab: resolved?.openInNewTab ?? false,\n ...(description ? { description } : {}),\n children,\n }\n }\n\n const navigationEntries = (slot: string): ResolvedNavEntry[] => {\n const doc = navigation(slot)\n if (!doc) return []\n const values = doc as Record<string, unknown>\n const field = itemsFields.find((name) => Array.isArray(values[name]))\n if (!field) return []\n return items(values[field])\n .map(toNavEntry)\n .filter((entry): entry is ResolvedNavEntry => entry !== null)\n }\n\n return {\n settings,\n navigation,\n navigationEntries,\n resolveLink,\n assetUrl,\n client,\n source,\n }\n}\n","/**\n * Pure token/target logic of the draft-preview routes (EC-144), kept free of\n * Next.js imports so it is unit-testable. `preview.ts` wires it into route\n * handlers with `draftMode()`.\n */\n\nexport type PreviewEvaluation =\n | { ok: true; redirectTo: string }\n | { ok: false; status: 400 | 401; message: string }\n\n/**\n * The fixed origin redirect targets are resolved against. `.invalid` is reserved\n * (RFC 6761), so it never names a real host; `https:` is a special scheme, so the\n * parser applies the same normalisation a browser applies when it resolves a\n * `Location` header against the site (`\\`→`/`, tab/LF/CR stripped, dot segments\n * removed).\n */\nconst RESOLUTION_ORIGIN = 'https://redirect-target.invalid'\n\n/** Resolve `value` the way a browser would, keeping it only if it stays on the origin. */\nfunction resolveOnOrigin(value: string): URL | null {\n let resolved: URL\n try {\n resolved = new URL(value, RESOLUTION_ORIGIN)\n } catch {\n return null\n }\n return resolved.origin === RESOLUTION_ORIGIN ? resolved : null\n}\n\n/** C0 controls (U+0000–U+001F) and DEL (U+007F): never part of a legitimate target. */\nfunction hasControlCharacter(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code <= 0x1f || code === 0x7f) return true\n }\n return false\n}\n\n/**\n * The ONE same-site check for a preview redirect target, shared by the enable and\n * disable routes, so the preview endpoints can never be used as an open redirect.\n * `null` means rejected; an empty value yields `fallback`.\n *\n * EC-594: the parser a browser uses decides, not a blocklist of shapes (the old\n * blocklist caught `//host` and backslashes — EC-304 B-3 — but not `/<TAB>/host`,\n * which the parser collapses to `//host`):\n *\n * - only a path-absolute reference (`/…`) is accepted — a relative one would be\n * resolved against the route's own path by a browser, so it has no fixed meaning;\n * - any C0 control or DEL is rejected outright;\n * - the value is resolved against a fixed origin and must stay on it;\n * - what is returned is the parser's normalised `pathname + search + hash`, never\n * the raw input — and that returned target must itself stay on the origin, since\n * dot-segment removal can leave a pathname starting with `//` (`/.//host`,\n * `/..//host`, `/%2e//host`), which as a `Location` is scheme-relative.\n *\n * A scheme-relative or absolute value naming the resolution origin itself resolves\n * on it and yields its path — harmless, because only that path is returned.\n */\nexport function safeRedirectTarget(\n value: string | null | undefined,\n fallback = '/',\n): string | null {\n if (value === null || value === undefined || value === '') return fallback\n if (!value.startsWith('/') || hasControlCharacter(value)) return null\n const resolved = resolveOnOrigin(value)\n if (resolved === null) return null\n const target = resolved.pathname + resolved.search + resolved.hash\n return resolveOnOrigin(target) === null ? null : target\n}\n\n/**\n * Evaluate a preview-enable request. Drafts are token-gated (EC-144\n * acceptance: without the token, drafts are never reachable):\n *\n * - an unconfigured (empty) expected token rejects every request — preview\n * cannot be accidentally left open;\n * - a missing or mismatched `token` query param is a 401;\n * - an off-site `redirect` target is a 400.\n */\nexport function evaluatePreviewRequest(\n requestUrl: string | URL,\n expectedToken: string | undefined,\n): PreviewEvaluation {\n if (expectedToken === undefined || expectedToken.length === 0) {\n return { ok: false, status: 401, message: 'Draft preview is not configured.' }\n }\n\n let url: URL\n try {\n url = typeof requestUrl === 'string' ? new URL(requestUrl, 'http://localhost') : requestUrl\n } catch {\n return { ok: false, status: 400, message: 'Malformed preview request URL.' }\n }\n\n const token = url.searchParams.get('token')\n if (token !== expectedToken) {\n return { ok: false, status: 401, message: 'Invalid preview token.' }\n }\n\n const redirectTo = safeRedirectTarget(url.searchParams.get('redirect'))\n if (redirectTo === null) {\n return { ok: false, status: 400, message: 'Preview redirect target must be a same-site path.' }\n }\n\n return { ok: true, redirectTo }\n}\n\n/**\n * The redirect target of a preview-disable request. Disabling needs no token (it\n * only ever reduces visibility), so anyone can reach the route — its `redirect`\n * param goes through the same {@link safeRedirectTarget} as the enable route. An\n * off-site or unreadable target falls back to the site root: leaving draft mode\n * always succeeds.\n */\nexport function previewDisableTarget(requestUrl: string | URL): string {\n let url: URL\n try {\n url = typeof requestUrl === 'string' ? new URL(requestUrl, 'http://localhost') : requestUrl\n } catch {\n return '/'\n }\n return safeRedirectTarget(url.searchParams.get('redirect')) ?? '/'\n}\n","import { draftMode } from 'next/headers'\nimport { redirect } from 'next/navigation'\nimport { evaluatePreviewRequest, previewDisableTarget } from './preview-core'\n\n/**\n * Draft preview route handlers (EC-144): Next.js `draftMode()` integration.\n * Server-only by construction — these factories are consumed from App Router\n * route handler files, never from client bundles.\n */\n\nexport interface PreviewRouteOptions {\n /**\n * The shared secret gating draft preview. An empty/undefined token means\n * every request is rejected with 401 — drafts stay unreachable until a\n * token is configured.\n */\n token: string | undefined\n}\n\n/** The shape an `app/api/.../route.ts` file re-exports. */\nexport interface PreviewRouteHandlers {\n GET(request: Request): Promise<Response>\n}\n\n/**\n * Create the preview-enable route handler for\n * `app/api/preview/route.ts`:\n *\n * ```ts\n * export const { GET } = createPreviewRoute({ token: process.env.PREVIEW_TOKEN })\n * ```\n *\n * `GET /api/preview?token=...&redirect=/some/path` checks the token, enables\n * Next draft mode (the catch-all helper then renders the `draft`\n * perspective), and redirects to the target path. Wrong/missing token → 401.\n */\nexport function createPreviewRoute(options: PreviewRouteOptions): PreviewRouteHandlers {\n return {\n async GET(request: Request): Promise<Response> {\n const evaluation = evaluatePreviewRequest(request.url, options.token)\n if (!evaluation.ok) {\n return new Response(evaluation.message, { status: evaluation.status })\n }\n const draft = await draftMode()\n draft.enable()\n redirect(evaluation.redirectTo)\n },\n }\n}\n\n/**\n * Create the preview-disable route handler (e.g.\n * `app/api/preview/disable/route.ts`). Disabling needs no token — it only\n * ever reduces visibility back to the published perspective. The `redirect`\n * target passes the same same-site check as the enable route; an off-site one\n * redirects to `/` instead.\n */\nexport function createPreviewDisableRoute(): PreviewRouteHandlers {\n return {\n async GET(request: Request): Promise<Response> {\n const draft = await draftMode()\n draft.disable()\n redirect(previewDisableTarget(request.url))\n },\n }\n}\n","import type { ReactNode } from 'react'\nimport { createRouter } from '@elytracms/core/cms-core'\nimport type {\n CmsValidationIssue,\n CollectionDef,\n Locale,\n LocaleConfigInput,\n Perspective,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport type { Metadata } from 'next'\nimport { defineHostComponents } from './components'\nimport type { HostComponents } from './components'\nimport { projectFrontendUrl, resolveProject } from '@elytracms/core/studio-core'\nimport type {\n HostComponentEntry,\n ResolvedProject,\n WorkspaceProjectEntry,\n} from '@elytracms/core/studio-core'\nimport { firstBlockFullBleed } from './full-bleed'\nimport { ELYTRA_HOST_ENV_KEYS, resolveHostEnv } from './host-env'\nimport type { ElytraHostEnv } from './host-env'\nimport { loadLiveContent } from './host-content'\nimport { resolveGlobals } from './host-globals'\nimport type { GlobalsConventions, SiteGlobals } from './host-globals'\nimport { createPreviewDisableRoute, createPreviewRoute } from './preview'\nimport type { PreviewRouteHandlers } from './preview'\nimport { createRevalidateRoute } from './revalidate'\nimport type { RevalidateRouteHandlers, RevalidateRouteOptions } from './revalidate'\nimport { createIngestRoute } from './ingest'\nimport type { IngestRouteHandlers, IngestRouteOptions } from './ingest'\nimport { convexSiteUrl } from '@elytracms/core/ingest'\nimport { createCanvasRoute, requestPerspective } from './route'\nimport type {\n CanvasContentRequest,\n CanvasRequestInfo,\n CanvasRoute,\n CanvasRouteOptions,\n CanvasWrapContext,\n} from './route'\nimport { pathFromSlug, resolveRequestPage } from './route-core'\nimport { createStaticContentSource } from './snapshot'\nimport type { ContentSource } from './source'\nimport type { CacheTags, ResolvePageResult } from '@elytracms/core/content'\n\n/**\n * The delivery host kit (EC-368): ONE factory over (env, project config) that\n * yields every engine-shaped delivery surface a Next.js host mounts — content\n * loading (live snapshot / build-safe empty / token-gated draft), the catch-all\n * canvas route, the instant-publish revalidate webhook, draft-mode plumbing,\n * chrome globals, and the full-bleed layout hint. A consumer wires env + ~5\n * lines per surface; no engine logic is ever copied into a host repo again\n * (EC-369 findings #4/#5/#6/#11).\n *\n * ```ts\n * // frontend/lib/elytra.ts — the ONE seam file\n * import { createElytraHost } from '@elytracms/next'\n * // Read the project out of the repo's own config — `elytra.config.ts` is the\n * // single contract surface, and importing it costs a delivery bundle nothing\n * // (EC-532). Any other module layout works just as well; this is an example,\n * // not a required path.\n * import { site } from '../../elytra.config'\n *\n * export const elytra = createElytraHost({ ...site })\n * ```\n *\n * ```ts\n * // app/[[...slug]]/page.tsx\n * const route = elytra.canvasRoute()\n * export default route.Page\n * export const generateMetadata = route.generateMetadata\n * export const generateStaticParams = route.generateStaticParams // only if YOU enable cacheComponents\n * // (to cache: pass your own 'use cache' function as `published` — see `route.tsx`, EC-620)\n *\n * // app/api/revalidate/route.ts\n * export const { POST } = elytra.revalidateRoute()\n *\n * // app/api/draft/route.ts — GET /api/draft?token=…&redirect=/path\n * export const { GET } = elytra.previewRoute()\n *\n * // app/api/draft/disable/route.ts\n * export const { GET } = elytra.previewDisableRoute()\n * ```\n *\n * Trust boundaries are unchanged: live surfaces read the PUBLISHED snapshot;\n * draft is reachable only through explicit Next draft mode behind the ONE\n * `DRAFT_PREVIEW_TOKEN` (which also authenticates the draft snapshot fetch —\n * see `host-env.ts`); the revalidate webhook stays HMAC-signed. The consumer\n * keeps ownership of project config (AD-11), host components, form endpoints,\n * and anything genuinely bespoke.\n */\n\n/**\n * EC-581 (ratified 2b): the options are generic over the config's `collections`, so a\n * project declared with `defineCollection` keeps its literal types through the host and\n * `InferDocument<typeof host, 'post'>` (`@elytracms/core/cms-core`) is a delivered post.\n */\nexport interface ElytraHostOptions<\n Collections extends readonly CollectionDef[] = readonly CollectionDef[],\n> extends Omit<WorkspaceProjectEntry, 'slug' | 'locales' | 'collections' | 'routes' | 'components' | 'frontendUrl'> {\n /**\n * The project's display name (the entry's own `name` — `{ ...site }` carries it).\n * EC-583: the options ARE the config's project entry plus the host-only keys below,\n * and the kit resolves them through the one resolver the studio boots through.\n */\n name: string\n /**\n * The project slug (config-as-code identity, mirrors `elytra.config.ts`).\n * Used as the content scope when no live backend is configured.\n */\n slug: string\n /**\n * Locale config — config-owned (AD-11), never read from the snapshot.\n *\n * **Import it, do not restate it** (EC-519). This is the *same*\n * {@link LocaleConfigInput} `elytra.config.ts` takes, so a project declares its\n * locales once and hands that one binding to both mounts. It stays a host\n * option because the engine cannot know where a repo keeps the value — but it\n * is no longer an option you have to *retype*: since EC-532 the delivery\n * bundle can import `elytra.config.ts` itself for zero engine modules\n * (measured), so `createElytraHost({ ...site })` over a project exported from\n * the config is the shortest correct answer.\n *\n * This used to be `{ default, locales }` against a config spelling the same\n * fact `locales` + `defaultLocale`, and in our own demo the two drifted — the\n * host said `['en']`, the config said `['en','de']`, and every `/de` URL 404'd\n * silently for weeks (EC-512).\n *\n * The DECLARED shape (EC-580): the kit parses it into the engine's `LocaleConfig`\n * where every content source is assembled (`createStaticContentSource` — the\n * empty source is built at boot), so a `default` outside `locales` stops the host\n * with a located message instead of serving — the same rule the studio's\n * workspace gate applies to the same value.\n */\n locales: LocaleConfigInput\n /** CMS collection schema — config-owned (AD-11). */\n collections: Collections\n /** Routes as code (AD-3/AD-11): records, or the `(collections) => records` derive. */\n routes: readonly RouteRecord[] | ((collections: CollectionDef[]) => RouteRecord[])\n /**\n * The repo's page-builder components (manifest + implementation pairs — the\n * SAME list the studio composes with, usually `site.components` via the\n * `{ ...site }` spread). The host registers exactly this list (EC-591): the\n * engine adds no components of its own — the shipped ones are the opt-in\n * `@elytracms/components` package, listed in the config. For `next/image`\n * delivery of the image-role component, pass the list through\n * `withNextImage(site.components)` (one line, opt-in).\n *\n * Typed as the CONFIG's `HostComponentEntry` (`implementation: unknown`), not\n * the stricter {@link HostComponent}, so that `createElytraHost({ ...site })`\n * — the shape EC-532 makes the contract — compiles however the consumer typed\n * their project entry. It previously demanded `ComponentType`, which meant the\n * recommended spread worked only when the entry's implementation types\n * survived inference: `apps/demo/elytra.config.ts` gets away with it via\n * `satisfies`, and anyone using the engine's own published `HostComponentEntry`\n * / `WorkspaceProjectEntry` annotation did not. Both production consumers hit\n * exactly that and each carried one hand-restated `components` line (EC-539).\n *\n * What the widening costs, and where it is paid (EC-541): a list passed HERE\n * gets no compile-time check that each `implementation` is really a\n * component. The check is RUNTIME instead — `defineHostComponents` takes the\n * same `HostComponentEntry` shape (no cast, one signature) and tests every\n * implementation on registration; a value that is not a component becomes an\n * `invalid-implementation` issue in `components.registry.issues` and is withheld from\n * the implementation map, so its nodes render the visible missing-component\n * fallback rather than crashing the server render. Authoring a component\n * module as the strict {@link HostComponent} keeps the compile-time check at\n * the place the implementation is written.\n */\n components?: readonly HostComponentEntry[]\n /**\n * The public base URL of the delivery site (the config's project-level\n * `frontendUrl`). The handle's {@link ElytraHost.frontendUrl} is the RESOLVED one —\n * the bound environment's own URL when it declares one (EC-583).\n */\n frontendUrl?: string\n /**\n * Env bag; defaults to `process.env`. Documented names (see `host-env.ts`):\n * `ELYTRA_CONVEX_URL`, `ELYTRA_PROJECT_ID`, `DRAFT_PREVIEW_TOKEN`,\n * `REVALIDATE_SECRET`.\n */\n env?: Record<string, string | undefined>\n /**\n * The no-backend content fallback, used whenever `ELYTRA_CONVEX_URL` +\n * `ELYTRA_PROJECT_ID` are not both set. Default: a build-safe EMPTY source —\n * consumer CI builds green with no backend, and every URL is a clean 404\n * until content exists — in every cache state, not only a cold one, because\n * the content mode is part of the route's data-cache key (see\n * {@link ElytraContentMode}). The starter template overrides this with its\n * seeded in-memory world.\n */\n fallbackContent?: (request: CanvasContentRequest) => Promise<ContentSource>\n /** Chrome-globals conventions (settings/navigation collection ids, items fields). */\n globals?: GlobalsConventions\n /** Fetch implementation override for the live snapshot loader (tests, proxies). */\n fetch?: typeof globalThis.fetch\n}\n\n/** Pass-through canvas-route options the kit does not own. */\nexport interface HostCanvasRouteOptions {\n /** Tag observation hook (see `CanvasRouteOptions.onTags`). */\n onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void\n /** Metadata override (see `CanvasRouteOptions.metadata`). */\n metadata?: (result: ResolvePageResult, info: CanvasRequestInfo) => Metadata\n /**\n * Host-owned chrome around the rendered page (EC-524) — the seam a per-\n * collection detail template lives on. Branch on `ctx.collection` in your own\n * route file (see `CanvasRouteOptions.wrap`).\n */\n wrap?: (children: ReactNode, ctx: CanvasWrapContext) => ReactNode\n /**\n * The host's cache function for published requests (EC-620 — see\n * `CanvasRouteOptions.published`): your own `'use cache'` function that calls\n * `route.resolve(key)`, `route.applyCacheTags(outcome)` and picks its `cacheLife`.\n * Omit it and the route resolves every request fresh (dynamic, uncached).\n *\n * The route it builds is always scoped to the kit's {@link ElytraHost.contentScope}\n * with the kit's {@link ElytraHost.contentMode} in every cache key (EC-543), and\n * there is no override (EC-579): the kit's revalidate receiver derives its tags with\n * that same `contentScope`, so one value scopes both the entries and their\n * invalidation — a disagreement would drop nothing, silently.\n */\n published?: CanvasRouteOptions['published']\n /**\n * EC-618 — the analytics slot (see `CanvasRouteOptions.analytics`): its value is an\n * addon's beacon (`@elytracms/analytics/next`, unpublished pending counsel). Absent →\n * nothing renders and nothing is sent.\n */\n analytics?: CanvasRouteOptions['analytics']\n}\n\nexport interface LoadGlobalsOptions {\n /** Perspective of the chrome read. Default `'published'` (live is published-only). */\n perspective?: Perspective\n /**\n * The request's locale (EC-720) — the chrome's settings, menus and link paths are read in\n * it. Default: the project's default locale, which is every page's locale on a\n * single-locale site. On a multi-locale site pass the locale the page renders in: from a\n * layout in the catch-all segment, `resolveLocale({ pathname: pathFromSlug(slug) },\n * locales)` — the SAME split the catch-all resolves the page with — or a `wrap`\n * function's `ctx.locale`. A locale the project does not declare is an error, never a\n * silent read of the default.\n */\n locale?: Locale\n}\n\n/**\n * Which world {@link ElytraHost.loadContent} reads from.\n *\n * - `live` — the configured Convex deployment (published; token-gated draft).\n * - `fallback` — the host's own `fallbackContent` world (the scaffold's seeded\n * starter, the demo's in-memory fixture). A legitimate, supported mode.\n * - `empty` — no backend and no fallback: this project's structure with no\n * content, so every URL is a clean 404. Correct for a CI build; almost never\n * what a deployed site intends.\n *\n * The mode was decided from env long before EC-527 and readable by **nobody**,\n * so a site serving the fallback looked identical from the outside to one\n * reading the real deployment. Measured on the demo: `/` returned `200` while\n * all four of EC-512's published fixtures 404'd — an agent following AGENTS.md\n * §5 gets no signal that the thing under test was never exercised. Declared\n * once, read here, so a host can *say* which world it is in instead of\n * re-deriving it from `env` and drifting.\n *\n * **The mode is also part of the route's cache-entry key** (EC-543; since EC-515\n * an argument of the cached function). An entry can outlive the world that wrote\n * it and a hit never runs the loader, so until 0.0.85 the \"clean 404\" above held\n * only on a COLD cache: measured, a `next start` with both env vars blanked and a\n * warm `.next/cache` from a live run answered `200` with the live world's bytes\n * on every cached URL while reporting `empty` — the wrong-world failure EC-527\n * removed, one layer down. Keyed on the mode (`canvasCacheKey`, `cache.ts`), a\n * world change is a cold miss per URL, never a stale hit, in either direction.\n */\nexport type ElytraContentMode = 'live' | 'fallback' | 'empty'\n\n/** The delivery host handle {@link createElytraHost} yields. */\nexport interface ElytraHost<\n Collections extends readonly CollectionDef[] = readonly CollectionDef[],\n> {\n /**\n * The config's collections, as passed (EC-581) — typed by their literals when the\n * project declares them with `defineCollection`, so `InferDocument<typeof host, 'post'>`\n * types a delivered post with no generation step.\n */\n readonly collections: Collections\n /**\n * The cache-tag scope: `ELYTRA_PROJECT_ID` in live mode, else the config slug.\n * The ONE scope of this host (EC-579): its cache entries are written under it and\n * its revalidate receiver derives every invalidation tag with it.\n */\n contentScope: string\n /**\n * Which world {@link loadContent} reads from — see {@link ElytraContentMode}.\n * Read it to announce the mode (a boot line, a dev-only banner) rather than\n * re-deriving it from {@link env}, which is the drift this field exists to\n * prevent.\n */\n contentMode: ElytraContentMode\n /** The resolved env bag (documented names, trimmed, empty = unset). */\n env: ElytraHostEnv\n /**\n * The ONE resolved project (EC-583) — the options resolved through\n * `resolveProject`, the same rules the studio boots through: composed, defaulted,\n * validation slots normalized, routes materialized, every boot rule applied. The\n * kit reads its locales, collections, routes and frontend URL from here.\n */\n project: ResolvedProject\n /**\n * The project's route issue set at boot (EC-590 — the ONE router's: route conflicts,\n * mount conflicts, reachability). Surfaced on the console at build and in dev; the\n * editorial redirects (data) are judged per snapshot by the content client's\n * `routerIssues` and in the studio.\n */\n routeIssues: readonly CmsValidationIssue[]\n /** Materialized route records (the resolved project's; a derive ran once, at factory time). */\n routes: RouteRecord[]\n /** The registered component surface — exactly the `components` option's list (EC-591). */\n components: HostComponents\n /**\n * The public base URL of the delivery site, when configured — the resolved project's\n * (`projectFrontendUrl`): the bound environment's own `frontendUrl`, else the\n * project-level one. The same answer the studio's live links read (EC-583).\n */\n frontendUrl?: string\n /**\n * The content loader (the seam every kit surface reads through): live\n * delivery snapshot when env is configured — published, or the token-gated\n * draft for draft-mode requests — else the fallback source.\n *\n * An UNCACHED read: the kit caches nothing itself (EC-620 — caching is the host's).\n * Under `cacheComponents`, live-mode I/O in a layout or page outside `<Suspense>` fails\n * `next build` — call it inside a `<Suspense>`'d component (route handlers and\n * `sitemap.ts` just render dynamically).\n */\n loadContent(request: CanvasContentRequest): Promise<ContentSource>\n /**\n * Load + resolve the site-chrome globals (settings singleton, navigation by\n * slot alias, link/asset resolution — see `host-globals.ts`). One content\n * load per call; published perspective and the project's default locale by\n * default — pass the request's `locale` on a multi-locale site (EC-720).\n *\n * An UNCACHED read, like {@link loadContent}: under `cacheComponents`, render the\n * chrome that awaits it inside `<Suspense>` — and keep the canvas page OUTSIDE\n * every boundary, or its 404s and redirects answer HTTP 200 (measured, EC-515).\n */\n loadGlobals(options?: LoadGlobalsOptions): Promise<SiteGlobals>\n /** The catch-all canvas route pair for `app/[[...slug]]/page.tsx`. */\n canvasRoute(options?: HostCanvasRouteOptions): CanvasRoute\n /**\n * The instant-publish webhook handler for `app/api/revalidate/route.ts`, wired to\n * `REVALIDATE_SECRET` and to this host's own project — {@link contentScope} and the\n * config locales — which it derives the tags of every event with (EC-579). Unset\n * secret = endpoint closed (401), never a default secret. Only the secret can be\n * overridden: the scope and locales are the ones this kit's cache entries carry.\n */\n revalidateRoute(overrides?: Partial<Pick<RevalidateRouteOptions, 'secret'>>): RevalidateRouteHandlers\n /**\n * The draft-mode ENABLE route for e.g. `app/api/draft/route.ts` —\n * `GET /api/draft?token=…&redirect=/path`, gated by the ONE\n * `DRAFT_PREVIEW_TOKEN` (the same secret the draft snapshot fetch sends, so\n * the two can never disconnect). Unset token = drafts unreachable.\n */\n previewRoute(): PreviewRouteHandlers\n /** The draft-mode DISABLE route (no token — it only reduces visibility). */\n previewDisableRoute(): PreviewRouteHandlers\n /**\n * EC-617 — the PUBLIC INGEST route for `app/api/ingest/route.ts`: the site's one write path\n * (form submissions, analytics events, upload URLs), signed with `INGEST_SECRET` and\n * forwarded to this deployment's receiver (its HTTP origin derived from\n * `ELYTRA_CONVEX_URL`; pass `siteUrl` for a self-hosted backend). Unset secret or no\n * backend = 503, closed. The rate limit, the client key and the bot gate are the host's to\n * tune — the engine supplies the slots, never a vendor.\n */\n ingestRoute(options?: Partial<Omit<IngestRouteOptions, 'secret'>>): IngestRouteHandlers\n /**\n * Does the page for this catch-all `slug` OPEN full-bleed? Manifest-driven\n * (`presentation.fullBleed`, EC-368) and draft-mode aware — the host lays out\n * header clearance from data instead of a hand-kept component-id set. An\n * uncached read (see {@link loadContent}).\n */\n pageOpensFullBleed(slug: readonly string[] | undefined): Promise<boolean>\n}\n\n/**\n * Resolve the host's project entry through the ONE resolver (EC-583) — the rules the\n * studio boots through, so the delivery site refuses exactly what the studio refuses\n * (measured before this: an entry with a reserved field name or a rule its type cannot\n * keep stopped the studio and was served by the host). Throws a located message: a\n * host builds at boot, so that is where an invalid config fails, never per request.\n */\nfunction resolveHostProject(entry: Record<string, unknown>): ResolvedProject {\n const result = resolveProject(entry)\n if (result.ok) return result.project\n const lines = result.issues\n .filter((issue) => issue.severity === 'error')\n .map((issue) => {\n const at = issue.path.length > 0 ? ` at ${issue.path.join('.')}` : ''\n return ` • ${issue.code}${at}: ${issue.message}`\n })\n throw new Error(\n `Elytra delivery host: the project config \"${String(entry['slug'])}\" is invalid — the studio refuses it too:\\n${lines.join('\\n')}`,\n )\n}\n\n/**\n * Print the boot-time route issues once — during `next build` and in dev, never on every\n * production request (EC-590, 3h: \"surfaced at build and in dev\"). An `error` is a route\n * the site cannot serve as declared; a `warning` a precedence worth knowing.\n */\nfunction surfaceRouteIssues(slug: string, issues: readonly CmsValidationIssue[]): void {\n if (issues.length === 0) return\n const building = process.env['NEXT_PHASE'] === 'phase-production-build'\n if (!building && process.env['NODE_ENV'] === 'production') return\n const lines = issues.map((issue) => ` • ${issue.severity} ${issue.code}: ${issue.message}`)\n const report = `[elytra] project \"${slug}\": ${issues.length} route issue(s) —\\n${lines.join('\\n')}`\n if (issues.some((issue) => issue.severity === 'error')) console.error(report)\n else console.warn(report)\n}\n\n/** Create the delivery host handle — see the module docs for the full wiring. */\nexport function createElytraHost<const Collections extends readonly CollectionDef[]>(\n options: ElytraHostOptions<Collections>,\n): ElytraHost<Collections> {\n // The host-only keys stay here; everything else IS the project entry (EC-583).\n const { env: envOption, fallbackContent, globals, fetch: fetchOption, ...entry } = options\n const project = resolveHostProject(entry)\n const env = resolveHostEnv(envOption ?? process.env)\n const routes = project.routes\n // EC-590: the ONE router's issue set over the resolved routes — read by the doctor and\n // the studio too, and surfaced HERE at build and in dev (the host showed none before).\n const routeIssues = createRouter(project).issues\n surfaceRouteIssues(project.slug, routeIssues)\n const live = env.convexUrl !== undefined && env.projectId !== undefined\n\n // EC-527: half-configured is never deliberate. Someone set one of the pair\n // meaning to connect, and the site would otherwise serve the fallback world\n // while looking completely healthy — the exact silent-wrong-world failure this\n // ticket exists to remove, except in production. An explicit gap beats a\n // connected-shaped no-op; `revalidateRoute` already makes that same call about\n // its secret. Both unset stays silent: no-backend is a supported mode.\n if (!live && (env.convexUrl !== undefined || env.projectId !== undefined)) {\n const [present, missing] =\n env.convexUrl !== undefined\n ? [ELYTRA_HOST_ENV_KEYS.convexUrl, ELYTRA_HOST_ENV_KEYS.projectId]\n : [ELYTRA_HOST_ENV_KEYS.projectId, ELYTRA_HOST_ENV_KEYS.convexUrl]\n throw new Error(\n `Elytra delivery host: ${present} is set but ${missing} is not — live content needs both. ` +\n `Left as it is, this site would serve fallback content and look healthy. ` +\n `Set ${missing} to go live, or unset ${present} to run the no-backend fallback on purpose.`,\n )\n }\n\n const contentMode: ElytraContentMode = live\n ? 'live'\n : fallbackContent\n ? 'fallback'\n : 'empty'\n const contentScope = live ? (env.projectId as string) : project.slug\n\n // EC-591: exactly the given list — no engine-registered components, no default\n // image swap (that is the opt-in `withNextImage`). EC-539: the option is the\n // CONFIG's entry shape (`implementation: unknown`) so the recommended\n // `{ ...site }` spread compiles; EC-541: registration takes that shape too and\n // checks each implementation at runtime — no narrowing cast here.\n const components = defineHostComponents(project.components ?? [])\n\n // The build-safe default fallback: an EMPTY source over the config's own\n // structure. Constructed once — it is fully static.\n const emptySource: ContentSource = createStaticContentSource({\n projectId: contentScope,\n locales: project.locales,\n routes,\n // Redirects are editorial DATA (EC-303) — the no-backend build has none.\n redirects: [],\n collections: project.collections,\n documents: [],\n assets: [],\n })\n\n const loadContent = (request: CanvasContentRequest): Promise<ContentSource> => {\n if (live) {\n return loadLiveContent(\n {\n convexUrl: env.convexUrl as string,\n projectId: env.projectId as string,\n locales: project.locales,\n collections: project.collections,\n routes,\n ...(env.draftToken ? { draftToken: env.draftToken } : {}),\n ...(fetchOption ? { fetch: fetchOption } : {}),\n },\n request.perspective,\n )\n }\n if (fallbackContent) return fallbackContent(request)\n return Promise.resolve(emptySource)\n }\n\n const loadGlobals = async (globalsOptions: LoadGlobalsOptions = {}): Promise<SiteGlobals> => {\n const perspective = globalsOptions.perspective ?? 'published'\n const source = await loadContent({ perspective })\n return resolveGlobals(source, {\n perspective,\n // EC-720: the request's locale, judged against the project's locales by the core.\n ...(globalsOptions.locale !== undefined ? { locale: globalsOptions.locale } : {}),\n ...(globals ? { conventions: globals } : {}),\n })\n }\n\n const canvasRoute = (routeOptions: HostCanvasRouteOptions = {}): CanvasRoute =>\n createCanvasRoute({\n ...routeOptions,\n loadContent,\n components,\n // EC-620: the route caches nothing itself; these reach the host's own cache\n // function. Tag scope = `contentScope`, the SAME value `revalidateRoute` derives\n // its invalidation tags with (EC-579) — so `route.applyCacheTags` writes exactly\n // the strings the receiver revalidates. EC-543: the mode rides in every cache key\n // the route hands out, so no world ever reads another's entries. Spread last:\n // neither can be overridden.\n scope: contentScope,\n mode: contentMode,\n })\n\n const revalidateRoute = (\n overrides: Partial<Pick<RevalidateRouteOptions, 'secret'>> = {},\n ): RevalidateRouteHandlers =>\n createRevalidateRoute({\n // An unset secret keeps the endpoint CLOSED (401) — the kit never ships a\n // default secret (an auth-shaped no-op is worse than an explicit gap).\n secret: 'secret' in overrides ? overrides.secret : env.revalidateSecret,\n // EC-579: the receiver's OWN project — the scope its cache entries are written\n // under (`canvasRoute` above) and every locale it serves. The emitter sends only\n // the event, so this is the one place the scope and locales are read for it.\n scope: contentScope,\n locales: project.locales.locales,\n })\n\n // ONE secret by construction: the preview gate and the draft snapshot fetch\n // both read env.draftToken — they cannot disconnect (EC-369 finding #4).\n const previewRoute = (): PreviewRouteHandlers => createPreviewRoute({ token: env.draftToken })\n const previewDisableRoute = (): PreviewRouteHandlers => createPreviewDisableRoute()\n // EC-617: one secret name (`INGEST_SECRET`), the receiver on this deployment's HTTP origin.\n const ingestRoute = (overrides: Partial<Omit<IngestRouteOptions, 'secret'>> = {}): IngestRouteHandlers =>\n createIngestRoute({\n ...overrides,\n secret: env.ingestSecret,\n siteUrl: 'siteUrl' in overrides ? overrides.siteUrl : convexSiteUrl(env.convexUrl),\n })\n\n const pageOpensFullBleed = async (slug: readonly string[] | undefined): Promise<boolean> => {\n const perspective = await requestPerspective()\n const source = await loadContent({ perspective })\n // EC-720: the catch-all's own request split — this hint reads the page of the locale\n // the page renders in, derived where the route derives it.\n const { resolved } = resolveRequestPage(source, pathFromSlug(slug), perspective)\n return firstBlockFullBleed(resolved.document, components.registry)\n }\n\n const frontendUrl = projectFrontendUrl(project)\n\n return {\n routeIssues,\n collections: options.collections,\n contentScope,\n contentMode,\n env,\n project,\n routes,\n components,\n ...(frontendUrl !== undefined ? { frontendUrl } : {}),\n loadContent,\n loadGlobals,\n canvasRoute,\n revalidateRoute,\n previewRoute,\n previewDisableRoute,\n ingestRoute,\n pageOpensFullBleed,\n }\n}\n","/**\n * @elytracms/next — the embedded runtime (EC-144, vision AD-1): render\n * builder-managed pages inside the user's own Next.js App Router repo with\n * the user's own components. Drop `createCanvasRoute` into a catch-all\n * route, register components with `defineHostComponents`, done.\n *\n * Nothing here imports builder-only code (no studio, no operations, no\n * TanStack) — only the runtime packages: content accessors, the component\n * registry, and the runtime renderer.\n */\nexport const PACKAGE = '@elytracms/next'\n\n// Component registration — exactly the config's list (EC-591: the engine registers none).\nexport { defineHostComponents } from './components'\nexport type { HostComponent, HostComponents } from './components'\n\n// The server component. EC-499: `bodyItemsOf` is the body selection it renders\n// (declared `presentation.body`, else the EC-458 value-shape pick) — exported so\n// a host that renders a document body itself splits it the same way delivery does.\nexport { CanvasRenderer, bodyItemsOf } from './canvas-renderer'\nexport type { CanvasRendererProps, DeliveryPresentation } from './canvas-renderer'\n// EC-581 6a: one listing's baked result on the render outcome — documents, or why it could not run.\nexport type { BakedListing } from './canvas-renderer'\n\n// EC-574: the binding payload surface (`createBindingResolver`, `BindingPayloads`,\n// `SourcePayloadError`, `defaultBindingPayloads`, the route `payloads` option) and\n// the EC-166 source materialization (`materializeSourcePayloads`,\n// `collectBindingSourceIds`, `CanvasDataSource`) went with the data-binding\n// subsystem. Props are static values; dynamic data lives in the component (AD-12).\n\n// Content source seam + cache-tag merging (EC-145/146 plug in here).\nexport { mergeCacheTags } from './source'\nexport type { ContentSource } from './source'\n\n// PersistenceAdapter → ContentLookup snapshot bridge, plus the plain-data\n// assembler for hosts that prefetched the content themselves (EC-156).\nexport { createContentSnapshot, createStaticContentSource } from './snapshot'\nexport type { ContentSnapshot, ContentSnapshotOptions, StaticContentData } from './snapshot'\n\n// Catch-all route helper (pure core + Next wiring).\nexport { canvasBeaconProps, pathFromSlug, resolveCanvasPage, splitLocalePath } from './route-core'\n// The locale ⇄ URL mapping, both directions from one definition (EC-486), and\n// the request-side capability over it (EC-531): `resolveLocale` answers which\n// locale a request is for, `textDirection` gives the `dir` a locale renders under.\n// A multi-locale host renders `<html lang dir>` from these; a single-locale host\n// needs neither (its `locales.default` is correct by definition).\nexport { localeUrlPath, resolveLocale, textDirection } from './locale-url'\nexport type { LocaleRequest, LocaleStrategy, LocaleUrlPath } from './locale-url'\nexport type { CanvasPageOutcome, CanvasRenderOutcome, ResolveCanvasPageOptions } from './route-core'\n// EC-515: `CANVAS_VALIDATION_SLUG` is the catch-all's reserved build-time\n// validation sample (`route.generateStaticParams`, needed when the host enables\n// cacheComponents).\n// EC-595: `requestPerspective` is THE draft-mode → perspective mapping the route uses,\n// exported so a host's own server component reads the request's perspective the same\n// way instead of re-deriving it.\nexport { CANVAS_VALIDATION_SLUG, createCanvasRoute, requestPerspective } from './route'\n// EC-620: the key the route hands the host's own `'use cache'` function (`published`).\nexport type { CanvasCacheKey } from './cache'\nexport type {\n // EC-618: what the canvas route's `analytics` slot is told (the slot's value is an addon's beacon).\n CanvasBeaconProps,\n CanvasContentRequest,\n CanvasRequestInfo,\n CanvasRoute,\n CanvasRouteOptions,\n CanvasRouteProps,\n // EC-524: what a host's `wrap` function is told — the collection to branch on,\n // the resolved document, and the whole resolution.\n CanvasWrapContext,\n} from './route'\n\n// Per-page SEO → Next metadata, and sitemap.xml from published routes (EC-171).\nexport {\n mergeMetadataOverParent,\n openGraphLocale,\n seoContextOf,\n seoMetadata,\n seoObjectOf,\n} from './metadata'\nexport type { SeoMetadataContext } from './metadata'\nexport { canvasSitemapEntries, createCanvasSitemap } from './sitemap'\nexport type {\n CanvasSitemapOptions,\n CanvasSitemapResult,\n CreateCanvasSitemapOptions,\n SitemapSkippedRoute,\n} from './sitemap'\n\n// Cache tags & instant publish (EC-146): tag format, the event → tags derivation,\n// and the signed revalidation webhook receiver. The cache scope is the HOST's\n// (EC-620): the kit holds no `'use cache'` function — a host's own one declares a\n// result's tags through `route.applyCacheTags`. EC-579: the wire itself (event\n// schema, signer, header) is `@elytracms/core/revalidation`, re-exported so a host\n// that signs its own event imports one package.\nexport {\n collectionCacheTag,\n docCacheTag,\n nextCacheTags,\n projectSweepTag,\n revalidationTags,\n routeCacheTag,\n scopeCacheTag,\n} from './cache'\nexport type { RevalidationProject } from './cache'\nexport {\n REVALIDATE_SIGNATURE_HEADER,\n revalidationEventSchema,\n signRevalidationBody,\n signedRevalidationRequest,\n} from '@elytracms/core/revalidation'\nexport type { RevalidationEvent, RevalidationEventKind } from '@elytracms/core/revalidation'\nexport { evaluateRevalidateRequest } from './revalidate-core'\nexport type { RevalidateEvaluation } from './revalidate-core'\nexport { createRevalidateRoute } from './revalidate'\nexport type { RevalidateRouteHandlers, RevalidateRouteOptions } from './revalidate'\n// EC-617: the public ingest route — the pure core (method, same-origin, limiter, size, shape,\n// the host's bot gate) and the Next wrapper that signs and forwards to the one receiver.\nexport {\n createFixedWindowLimiter,\n defaultIngestClientKey,\n evaluateIngestRequest,\n isCrossOrigin,\n readCappedBody,\n} from './ingest-core'\nexport type { BotGateVerdict, FixedWindowLimiter, IngestBotGate, IngestEnrich, IngestEvaluation } from './ingest-core'\nexport { createIngestRoute } from './ingest'\nexport type { IngestRouteHandlers, IngestRouteOptions } from './ingest'\n\n// Asset-pipeline image delivery (EC-147) + the host-image renderer (EC-368):\n// `withNextImage` (EC-591) swaps the image-role component's implementation for\n// next/image — opt-in, one line in the host seam file; `elytraHostImage` is the\n// next/image-backed renderer a host registers on the `@elytracms/next/host-image`\n// bridge (the bridge itself is its OWN subpath — importing it from here would\n// bundle a second singleton instance).\nexport { ElytraImage, createAssetImageLoader, elytraHostImage, withNextImage } from './image'\nexport type {\n AssetImageLoaderOptions,\n ElytraHostImageOptions,\n ElytraImageAsset,\n ElytraImageProps,\n NextImageOptions,\n} from './image'\n\n// The delivery host kit (EC-368): one factory over (env, project config)\n// yielding every engine-shaped host surface — content loading, the catch-all\n// route, revalidate, draft-mode plumbing, chrome globals, layout hints.\nexport { createElytraHost } from './host-kit'\nexport type {\n ElytraContentMode,\n ElytraHost,\n ElytraHostOptions,\n HostCanvasRouteOptions,\n LoadGlobalsOptions,\n} from './host-kit'\nexport { ELYTRA_HOST_ENV_KEYS, resolveHostEnv } from './host-env'\nexport type { ElytraHostEnv } from './host-env'\nexport { assembleLiveSource, loadLiveContent } from './host-content'\nexport type { LiveContentOptions } from './host-content'\nexport { resolveGlobals } from './host-globals'\nexport type {\n GlobalsConventions,\n ResolveGlobalsOptions,\n ResolvedLink,\n ResolvedNavEntry,\n ResolvedNavLink,\n SiteGlobals,\n} from './host-globals'\nexport { firstBlockFullBleed } from './full-bleed'\n\n// Draft preview (token-gated draftMode integration).\nexport { evaluatePreviewRequest, safeRedirectTarget } from './preview-core'\nexport type { PreviewEvaluation } from './preview-core'\nexport { createPreviewDisableRoute, createPreviewRoute } from './preview'\nexport type { PreviewRouteHandlers, PreviewRouteOptions } from './preview'\n\n// Re-exports so host apps can author manifests, declare their config, and shape\n// content WITHOUT depending on the internal packages (or on zod) directly — a\n// consuming repo installs ONLY `@elytracms/next` for delivery (EC-193).\nexport { defineComponent } from '@elytracms/core/component-registry'\n// EC-190: component props are field-defs (`PropField`), not the retired `PropSpec`.\nexport type { ComponentManifest, PropField, SlotSpec } from '@elytracms/core/component-registry'\nexport type { ComponentImplementations, RenderAsset } from '@elytracms/core/runtime-renderer'\nexport { z } from 'zod'\n\n// Config + content shapes a host declares (collections, locales, routes,\n// documents) — the public surface of the structure that lives in the repo\n// (AD-11). Re-exported here so the host imports one package (EC-193).\nexport {\n documentSchema,\n routeRecordSchema,\n redirectRecordSchema,\n localeConfigSchema,\n parseLocaleConfig,\n} from '@elytracms/core/cms-core'\nexport type {\n CollectionDef,\n CmsDocument,\n FieldDef,\n LocaleConfig,\n LocaleConfigInput,\n Locale,\n RouteRecord,\n RedirectRecord,\n} from '@elytracms/core/cms-core'\n// Composition node shape (a page document's `body` is a ComponentNode list, per AD-12).\nexport type { ComponentNode } from '@elytracms/core/project-graph'\n// The delivery perspective — `'published'` or one variant row, `{ variant }` (EC-595\n// binding point 1) — with its draft constant and the one accessor a host callback\n// (`wrap`, `metadata`, `onTags`, `fallbackContent`) reads it through.\nexport { DRAFT_PERSPECTIVE, variantOf } from '@elytracms/core/cms-core'\nexport type { Perspective } from '@elytracms/core/cms-core'\n// The delivery shape of an asset (resolved url + metadata).\nexport type { AssetRecord } from '@elytracms/core/persistence'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components.ts","../src/canvas-renderer.tsx","../src/source.ts","../src/snapshot.ts","../src/metadata.ts","../src/route-core.ts","../src/cache.ts","../src/route.tsx","../src/sitemap.ts","../src/revalidate-core.ts","../src/revalidate.ts","../src/ingest-core.ts","../src/ingest.ts","../src/image.tsx","../src/full-bleed.ts","../src/host-env.ts","../src/host-content.ts","../src/host-globals.ts","../src/preview-core.ts","../src/preview.ts","../src/host-kit.ts","../src/index.ts"],"names":["record","SEO_SUBFIELDS","items","documentBodyCompositionNodes","cacheTags","jsx","Fragment","outcome","window","INGEST_MAX_BODY_BYTES","registerComponents","str","draftMode","redirect"],"mappings":"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","file":"index.js","sourcesContent":["import type { ComponentType } from 'react'\nimport type { ComponentManifest, ComponentRegistry } from '@elytracms/core/component-registry'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { ComponentImplementations } from '@elytracms/core/runtime-renderer'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\n\n/**\n * One host-repo component: the manifest the builder edits against plus the\n * real React implementation that renders it (vision AD-2 — project components\n * are real code in the host repo, never generated).\n *\n * This is the STRICT shape — the one a hand-authored component module should\n * export (`export const heroComponent: HostComponent = …`), so the compiler\n * checks the implementation where it is written. It is assignable to the\n * config's {@link HostComponentEntry}, which is what {@link defineHostComponents}\n * accepts, so both hand-built lists and the config's own list register through\n * the one signature (EC-541).\n */\nexport interface HostComponent {\n manifest: ComponentManifest\n implementation: ComponentType<Record<string, unknown>>\n}\n\n/**\n * The registered component surface a host app hands to `<CanvasRenderer />`\n * and the catch-all route helper: a manifest registry plus the implementation\n * map. Registration problems — duplicate ids, invalid ids, role claims, and\n * `invalid-implementation` for an entry whose `implementation` is not a React\n * component — are structured issues in `registry.issues`, the ONE record every\n * surface reads (EC-591; never thrown — an unknown component renders the EC-015\n * fallback). The studio registers the same config list through the same core\n * step, so it shows exactly these issues.\n */\nexport interface HostComponents {\n registry: ComponentRegistry\n implementations: ComponentImplementations\n}\n\n/**\n * Component registration for the embedded runtime (EC-144, EC-591): the host\n * registers EXACTLY the list it is given — the config's `components` — through\n * core's one registration step `registerComponents`, the same step the studio\n * registry and the preview harness call. The engine adds nothing: there are no\n * base primitives and no override layer (EC-591 deleted `includeBasePrimitives`).\n * The components Elytra ships are an opt-in package listed in the config like any\n * other (`components: [...elytraComponents, ...siteComponents]`).\n *\n * One precedence rule: a duplicate id is refused WHOLE (`duplicate-id`, the first\n * entry kept), so a manifest never ships with another entry's implementation.\n * To replace a shipped component, list yours under the id and leave the shipped\n * entry out; to swap only the Image implementation for `next/image`, wrap the\n * list in {@link withNextImage}.\n *\n * Accepts the CONFIG's entry shape (`implementation: unknown`) rather than the\n * strict {@link HostComponent}: the config declares implementations opaquely,\n * and EC-539 made `createElytraHost({ ...site })` the contract, so a\n * compile-time check at this seam was already gone for the recommended path.\n * The check is RUNTIME instead (EC-541): a value that is not a component is\n * recorded as an `invalid-implementation` issue naming the component id and\n * the received kind, and is NOT put into `implementations` — so the renderer\n * takes its missing-component path (a visible EC-015 fallback) instead of\n * crashing the server render on `createElement` (\"Element type is invalid\")\n * or, for a string, silently rendering an HTML tag. The manifest still\n * registers, so the entry shows as broken rather than vanishing.\n *\n * One consequence of the `unknown` parameter: an INLINE\n * `implementation: (props) => …` literal no longer gets `props` contextually\n * typed (it is `noImplicitAny` under strict). Annotate the parameter, or\n * author the entry as a {@link HostComponent} — which is the documented shape\n * anyway. (An overload pair was tried and does not restore it: TypeScript\n * resolves the context-sensitive literal against the wider overload.)\n */\nexport function defineHostComponents(components: readonly HostComponentEntry[] = []): HostComponents {\n const { registry, implementations } = registerComponents(components)\n return { registry, implementations }\n}\n","import type { ReactNode } from 'react'\nimport type { ContentQueryError, ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { isBlockSequenceValueShape } from '@elytracms/core/rich-text'\nimport {\n createRenderContext,\n documentBodyCompositionNodes,\n documentBodyItems,\n renderDocumentBodyItems,\n serializeListingQuery,\n} from '@elytracms/core/runtime-renderer'\nimport type {\n DocumentBodyItem,\n ListingQuery,\n RelationTarget,\n RenderAsset,\n ResolveLink,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { HostComponents } from './components'\n\n/**\n * `<CanvasRenderer />` (EC-144, vision AD-1): an async React Server Component\n * that renders a resolved page (from `resolvePage`, EC-143) through\n * `@elytracms/core/runtime-renderer` with the host app's component registry. EC-015\n * fallback behavior is identical to the builder: a missing component, invalid\n * prop, or missing required slot renders a visible fallback — a page never\n * crashes and never silently omits a node.\n */\nexport interface CanvasRendererProps {\n /** The `resolvePage` result for the current URL. */\n result: ResolvePageResult\n /** Host component surface from `defineHostComponents`. */\n components: HostComponents\n /**\n * Delivery-shaped assets the page may reference (EC-195, from the render\n * outcome). An `asset`-typed Image prop resolves through these to a url +\n * intrinsic dimensions; an unknown id degrades to the visible fallback.\n */\n assets?: readonly ResolvedAsset[]\n /**\n * Populated composition-node `relation` props, keyed `collection:id` (EC-254,\n * from the render outcome). A block's `relation` prop (e.g. a ProductGrid's\n * picked products) resolves through these to the populated target; an unknown\n * key degrades to the block's own empty state. Absent → relation props pass\n * through unresolved (raw ref), the same degradation as a missing asset list.\n */\n relations?: Record<string, RelationTarget>\n /**\n * Documents each `listing` block resolved to, keyed by serialized query (EC-255,\n * from the render outcome). A ProductGrid keyed to a picked `category` reads its\n * products through this; an unknown key (or absent map) degrades to the block's\n * own empty state — the same degradation as a missing relation. A query that\n * could not run is baked as its errors (EC-581 6a) and shown above the block.\n */\n listings?: Record<string, BakedListing>\n /**\n * URL paths for the documents this page's rich-text props LINK to, keyed\n * `collection:id` (EC-291, from the render outcome). A self-typed internal link\n * (`attrs.ref = {type:'reference',…}`) resolves through this to a real `href`; an\n * unknown key (or absent map) degrades to the renderer's visible `unresolved-ref`\n * issue (EC-290), never a silent `#`.\n */\n links?: Record<string, string>\n /**\n * The resolved document's collection-declared delivery presentation (EC-499,\n * from the render outcome): which field(s) hold the body. Absent → the EC-458\n * shape picker selects the body by VALUE, exactly as before.\n */\n presentation?: DeliveryPresentation\n}\n\n/**\n * A collection's `presentation` declaration (EC-499), resolved for delivery: the\n * body field list normalized from `string | string[]`. Plain data — it rides the\n * (serializable) render outcome and the RSC boundary like the baked\n * assets/relations/links do.\n *\n * EC-524: this renderer applies no CHROME. It carried a `wrapper` component id\n * briefly (never released); a detail wrapper is a page template, templates are\n * code, and the host now supplies one from its own route file via\n * `createCanvasRoute`'s `wrap` option. What stays here is content selection —\n * which is exactly what makes a canvas body and a rich-text body render\n * agnostically through the one entry point.\n */\nexport interface DeliveryPresentation {\n /** Field names whose values form the body, in render order. */\n body?: readonly string[]\n}\n\n/**\n * One listing's baked result (EC-255; EC-581 6a): the documents its query matched,\n * or the structured errors `listDocuments` refused it with. Plain data — it rides\n * the serializable render outcome and the RSC boundary like the other bakes.\n */\nexport type BakedListing = RelationTarget[] | { errors: readonly ContentQueryError[] }\n\n/** Build the renderer's relation resolver from the baked, populated targets (EC-254). */\nfunction relationResolver(relations: Record<string, RelationTarget>): ResolveRelation {\n return (ref) => relations[`${ref.collection}:${ref.id}`] ?? null\n}\n\n/** Build the renderer's link resolver from the baked, doc-keyed URL paths (EC-291). */\nfunction linkResolver(links: Record<string, string>): ResolveLink {\n return (ref) => links[`${ref.collection}:${ref.id}`] ?? null\n}\n\n/**\n * Build the renderer's listing resolver from the baked, query-keyed documents\n * (EC-255). A refused query resolves to nothing (the block's own empty state); the\n * listing decorator below shows why.\n */\nfunction listingResolver(listings: Record<string, BakedListing>): ResolveListing {\n return (query) => {\n const baked = listings[serializeListingQuery(query)]\n return Array.isArray(baked) ? baked : null\n }\n}\n\n/**\n * Why a baked listing query cannot run (EC-581 6a): `listDocuments` refused it and the\n * host baked the errors. The render context (`createRenderContext`, EC-592) judges the\n * declaration itself, asks this for the query and shows the notice — the same two\n * judgments the studio canvas reads, so a listing that cannot run is never an empty\n * grid on one surface and a notice on the other.\n */\nfunction bakedListingFailure(listings: Record<string, BakedListing>): (query: ListingQuery) => string[] | null {\n return (query) => {\n const baked = listings[serializeListingQuery(query)]\n return baked && !Array.isArray(baked) ? baked.errors.map((error) => error.message) : null\n }\n}\n\n/**\n * Build the renderer's asset resolver from the baked delivery assets (EC-195). EC-587\n * (ASSETS D11): the baked `ResolvedAsset` IS the render asset — handed on as built by\n * `resolvedAsset(record, url, at)`, never re-picked field by field (the copy dropped `id`\n * and `mimeType`, so a prop got a different object than a document field).\n */\nfunction assetResolver(assets: readonly ResolvedAsset[]): (id: string) => RenderAsset | null {\n const byId = new Map(assets.map((asset) => [asset.id, asset]))\n return (id) => byId.get(id) ?? null\n}\n\n/** Visible fallback when the renderer is handed a non-`ok` resolution. */\nfunction UnresolvedPageFallback(props: { status: string }): ReactNode {\n return (\n <div data-ec-fallback=\"unresolved-page\" data-ec-status={props.status}>\n {`This URL did not resolve to a page (status: ${props.status}).`}\n </div>\n )\n}\n\n/** Visible fallback when an `ok` resolution carries no renderable document. */\nfunction MissingDocumentFallback(): ReactNode {\n return (\n <div data-ec-fallback=\"missing-document\">\n {`Route resolved but carried no page document to render.`}\n </div>\n )\n}\n\n/**\n * Render a resolved page server-side. Async so hosts can compose it like any\n * other RSC; resolution itself is synchronous (the content was prefetched by\n * the `ContentSource`).\n */\nexport async function CanvasRenderer(props: CanvasRendererProps): Promise<ReactNode> {\n const { result, components } = props\n\n if (result.status !== 'ok') {\n // The route helper answers redirect/notFound before rendering; direct\n // users of <CanvasRenderer /> still get a visible state, never a crash.\n return <UnresolvedPageFallback status={result.status} />\n }\n\n // EC-592: THE render context — the studio canvas builds the same one over its live\n // session; here the source is the baked render outcome and the surface a server render\n // (`mode: 'delivery'` unwraps the client error boundary; a listing that cannot run\n // says so above its block, EC-581 6a).\n const ctx = createRenderContext({\n components,\n mode: 'delivery',\n source: {\n resolveAsset: assetResolver(props.assets ?? []),\n // EC-254: wire relation population only when the host baked the populated\n // targets, so a host that did not opt in keeps the raw-ref passthrough.\n ...(props.relations ? { resolveRelation: relationResolver(props.relations) } : {}),\n // EC-255: wire the listing resolver only when the host baked the query results,\n // so a host that did not opt in keeps listing props empty (the block's own state).\n ...(props.listings\n ? { resolveListing: listingResolver(props.listings), listingFailure: bakedListingFailure(props.listings) }\n : {}),\n // EC-291: wire the rich-text link resolver only when the host baked the link map,\n // so a host that did not opt in degrades a reference link to the visible\n // `unresolved-ref` issue (EC-290) rather than a wrong href.\n ...(props.links ? { resolveLink: linkResolver(props.links) } : {}),\n },\n })\n\n const document = result.document\n\n // EC-187/EC-287: a routed document delivers its body directly — there is no\n // graph or layout indirection; shared chrome is composed in the host's\n // `app/layout.tsx`, and per-collection detail chrome in the host's own route\n // (`createCanvasRoute`'s `wrap`, EC-524). EC-499: WHICH field is the body, and\n // what shape it holds, is a declaration (`presentation.body`) with the EC-458\n // value-shape picker as the fallback — and one entry point renders a\n // composition body and a rich-text body alike. With no document, a visible\n // fallback.\n if (document) {\n return <>{renderDocumentBodyItems(bodyItemsOf(document, props.presentation), ctx)}</>\n }\n\n return <MissingDocumentFallback />\n}\n\n/**\n * The ordered body items of a resolved document (EC-499): the values of the\n * collection's declared `presentation.body` fields concatenated in declaration\n * order, or — when the collection declares nothing — the EC-458 value-shape\n * pick, unchanged. A declared field the document has no value for contributes\n * nothing (an empty body is legitimate); a declared field holding something that\n * is neither composition nor rich text contributes a visible fallback.\n */\nexport function bodyItemsOf(\n document: Record<string, unknown> | null | undefined,\n presentation?: DeliveryPresentation,\n): DocumentBodyItem[] {\n if (!document) return []\n const fields = presentation?.body ?? []\n if (fields.length > 0) return fields.flatMap((name) => documentBodyItems(document[name]))\n return documentBodyItems(compositionOf(document))\n}\n\n/**\n * Select the delivery composition from a resolved document (EC-187) — the\n * FALLBACK, used only when the collection declares no `presentation.body`\n * (EC-499). Kept exactly as it was so every undeclared collection renders\n * byte-for-byte what it rendered before: declaration beats shape-guessing, but\n * shape-guessing keeps working where nothing was declared.\n *\n * Its known limit is why the declaration exists: it can only ever select a\n * COMPOSITION, so a document whose body is a `richText` field was unrenderable\n * through the catch-all and needed a hand-written Next route.\n *\n * The\n * composition field can be named anything the host's collection schema chooses:\n * the conventional `body` (a `page`) or `content` (a routed content collection\n * like a `post`), but ALSO a host-specific name such as a production site's\n * `pageBuilder` (migrated faithfully from Sanity, EC-263). So the field is chosen\n * by VALUE — a composition is a v2 sequence carrying a component root — NEVER by\n * name: a `body ?? content` name heuristic both picks a post's rich-text `body`\n * string over its `content` composition AND misses a `pageBuilder` entirely,\n * rendering nothing.\n *\n * `body` then `content` keep priority (so a content document's unrelated rich-text\n * `body` never shadows the conventional `content` composition); any other\n * composition-valued field is the generalized fallback.\n */\nfunction compositionOf(document: Record<string, unknown>): unknown {\n const body = document['body']\n // EC-547: a v2 value qualifies for the shape-pick only when it CARRIES\n // component roots — a prose-only sequence is renderable through a declared\n // `presentation.body`, but the undeclared fallback keeps its EC-458 promise\n // byte-identical: a document's prose `body` never shadows its conventional\n // `content` composition.\n if (isCompositionishSequence(body)) return body\n const content = document['content']\n if (isCompositionishSequence(content)) return content\n // Generalized (EC-263): the first field whose value is a composition.\n // `body`/`content` already took priority above. (EC-708: a pre-0.0.84 bare node or node\n // list is no composition — the shape pick reads the v2 sequence only.)\n for (const value of Object.values(document)) {\n if (isCompositionishSequence(value)) return value\n }\n return undefined\n}\n\n/**\n * The routed document's COMPOSITION nodes as a `ComponentNode[]` (EC-205): the\n * same body selection the renderer uses, normalized to an array so a delivery\n * host can walk it (e.g. to page-scope baked assets, or to read the first\n * block's `presentation.fullBleed` hint). Non-node values are dropped.\n * `null`/no-document yields an empty array.\n *\n * EC-499: takes the collection's declared presentation when there is one, and\n * still returns COMPOSITION nodes only — a rich-text body yields `[]`, which is\n * the right answer for both of its readers (nothing to full-bleed; nothing that\n * `collectReferencedAssetIds` could walk). The rich-text half of a body is baked\n * separately in `route-core`.\n */\nexport function compositionNodesOf(\n document: Record<string, unknown> | null | undefined,\n presentation?: DeliveryPresentation,\n): ComponentNode[] {\n return documentBodyCompositionNodes(bodyItemsOf(document, presentation))\n}\n\n/** A v2 sequence that carries at least one component root (the shape-pick bar). */\nfunction isCompositionishSequence(value: unknown): boolean {\n return (\n isBlockSequenceValueShape(value) &&\n value.content.some(\n (node) =>\n typeof node === 'object' &&\n node !== null &&\n (node as { type?: unknown }).type === 'elytra/component',\n )\n )\n}\n\n","import type { CollectionDef, Locale, LocaleConfig, Perspective, RouteRecord } from '@elytracms/core/cms-core'\nimport { cacheTags } from '@elytracms/core/content'\nimport type { CacheTags, ContentClient, ResolvedAsset } from '@elytracms/core/content'\n\n/**\n * The content seam of the embedded runtime (EC-144, vision AD-1/AD-4): the\n * catch-all route helper consumes this interface, so where the content comes\n * from is swappable. EC-144 ships the in-memory snapshot bridge\n * (`createContentSnapshot` over a `PersistenceAdapter`); EC-145/146 plug a\n * Convex-backed source into the exact same seam.\n *\n * Accessors are synchronous over a prefetched `ContentLookup` (vision AD-4);\n * any fetching happens when the source is loaded, before clients are minted.\n *\n * EC-574: the `CanvasDataSource` shape and the `sources` member (the data-binding\n * subsystem's per-section query definitions, EC-166) are gone with that subsystem.\n */\nexport interface ContentSource {\n /** The project's locale configuration (default + supported set, EC-018). */\n readonly locales: LocaleConfig\n /**\n * Mint a typed accessor client (EC-143) bound to a perspective. The locale\n * defaults to the project default; the route helper overrides it per\n * request from the URL's locale prefix.\n */\n client(init: { perspective: Perspective; locale?: Locale }): ContentClient\n /**\n * The route records the source's clients resolve against (EC-171, additive).\n * Lets enumerating consumers — the sitemap helper — walk the same route\n * table `resolvePage` uses, instead of keeping a second list. Optional so\n * existing custom sources stay valid; without it the sitemap helper needs\n * explicit routes.\n */\n readonly routes?: readonly RouteRecord[]\n /**\n * The project's collection definitions (EC-499, additive) — config-owned\n * (AD-11), already prefetched by every source that reads a schema. The route\n * helper reads the resolved document's collection from here to honour its\n * declared `presentation` (which field(s) hold the delivery body, and the\n * optional detail wrapper). Optional so existing custom sources stay valid;\n * without it every collection is treated as undeclared and the EC-458 shape\n * picker decides, exactly as before.\n */\n readonly collections?: readonly CollectionDef[]\n /**\n * The project's delivery-shaped assets (EC-195, additive). Lets the route\n * helper bake the assets a page may reference into the (serializable) render\n * outcome, so the renderer can resolve an `asset`-typed Image prop to a usable\n * url + intrinsic dimensions — and a cached page keeps its images during a\n * backend outage. Optional so existing custom sources stay valid; without it,\n * asset-typed props fall back to their raw value (the renderer's visible\n * degradation), never a crash.\n *\n * EC-632: resolved in `locale` (absent → the source's default locale) — a localized\n * asset-collection field (alt, …) carries its value in that locale, with the default\n * locale as its fallback. The route helper passes the request's locale, so a page bakes\n * its assets in the locale it renders in.\n */\n assets?(locale?: Locale): readonly ResolvedAsset[]\n}\n\n/**\n * Merge accessor cache tags into one deterministic set (sorted, deduplicated)\n * — used by the route helper to combine the `resolvePage` tags with tags\n * added by the host's payload factory (e.g. a `listDocuments` call). The\n * merged set is what the EC-146 `onTags` hook receives.\n */\nexport function mergeCacheTags(first: CacheTags, ...rest: readonly CacheTags[]): CacheTags {\n const all = [first, ...rest]\n return cacheTags({\n docs: all.flatMap((t) => t.docs),\n collections: all.flatMap((t) => t.collections),\n routes: all.flatMap((t) => t.routes),\n assets: all.flatMap((t) => t.assets),\n locale: first.locale,\n })\n}\n","import { documentKey, parseLocaleConfig, readLiveRedirects } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfigInput,\n Perspective,\n RedirectRecord,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport {\n assetMetaDriftIssue,\n assetWithoutUrlIssue,\n createContentClient,\n createContentContext,\n isDocumentHistory,\n resolvedAsset,\n} from '@elytracms/core/content'\nimport type { ContentClient, ContentDocumentSource, ContentLookup } from '@elytracms/core/content'\nimport { assetMetaDrift, assetMetaLens, assetReadLocale, redirectRecordKind } from '@elytracms/core/persistence'\nimport type { AssetRecord, PersistenceAdapter } from '@elytracms/core/persistence'\nimport type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport type { ContentSource } from './source'\n\n/**\n * Bridge from a `PersistenceAdapter` to the synchronous `ContentLookup` the\n * accessors (EC-143) read through: prefetch the project's collections,\n * documents, assets, and graphs ONCE (async), then serve every accessor call\n * synchronously from memory. This is how `apps/example-site` reads the seeded\n * in-memory fixture project; a Convex-backed `ContentSource` (EC-145/146)\n * replaces this bridge without touching the route helper.\n *\n * The assembly half is exposed separately as {@link createStaticContentSource}\n * (EC-156): a host that already holds the prefetched data as plain values —\n * e.g. a delivery-snapshot payload fetched over Convex's public HTTP query\n * API — assembles the exact same `ContentSource` without faking an adapter.\n */\n\nexport interface ContentSnapshotOptions {\n /**\n * The project's collection definitions — **required** (EC-583). The snapshot's\n * lookup resolves localized fields and a collection's declared `presentation`\n * through them, and the persistence contract holds no schema any more (this used to\n * read `adapter.cms.findSchema`, which in a seeded host was the engine sample's JSON\n * projection unless the host re-saved its own). Hand it the config's collections —\n * the same ones the studio and the seed read.\n */\n collections: readonly CollectionDef[]\n /** Route records served by `resolvePage` (EC-019). */\n routes?: readonly RouteRecord[]\n /** Redirect records served by `resolvePage` (EC-019/020). */\n redirects?: readonly RedirectRecord[]\n /**\n * Locale configuration — **required**, never defaulted (EC-530). The default\n * locale is the storage key every localized value is written under, so a\n * fabricated `en` is a data migration to undo, not a config edit: content\n * authored under one locale and served under another, silently (EC-512\n * measured exactly that). EC-490 made a config declaring no locales an error\n * and EC-519 collapsed the two declarations into one; this was the last place\n * in the chain still inventing the value, and it sits in the package that\n * resolves the URLs. Hand it the project's `LocaleConfig` — the one the\n * config declares. Parsed here (EC-580): a `default` outside `locales` throws.\n */\n locales: LocaleConfigInput\n}\n\nexport interface ContentSnapshot extends ContentSource {\n readonly projectId: string\n}\n\n/**\n * Plain prefetched data for {@link createStaticContentSource} (EC-156): the\n * same inputs `createContentSnapshot` reads from a `PersistenceAdapter`, as\n * already-loaded values. Everything content-bearing is optional — an absent\n * piece simply resolves to \"not visible\", with the runtime's explicit\n * fallbacks (never a crash).\n */\nexport interface StaticContentData {\n projectId: string\n /** Locale configuration — **required**, never defaulted (see EC-530 above); parsed (EC-580). */\n locales: LocaleConfigInput\n /** Route records served by `resolvePage` (EC-019). */\n routes?: readonly RouteRecord[]\n /** Redirect records served by `resolvePage` (EC-019/020). */\n redirects?: readonly RedirectRecord[]\n /**\n * The project's collection definitions (every host path hands the resolved config's). They\n * are also the asset metadata's lens (EC-659): the asset collection's `localized` fields\n * are the only ones a stored per-locale value is read for. ABSENT means no schema — no\n * field is localized, so every stored translation is drift: never applied (the shared\n * value is served), each one named on `sourceIssues`.\n */\n collections?: readonly CollectionDef[]\n /**\n * Stored document sources. Either a bare `CmsDocument` (gated by its own\n * `state`) or a `DocumentHistory` carrying the live draft + the pinned\n * published snapshot (EC-224). Perspective gating happens in the pure\n * accessors (`selectPerspective`, EC-142): a `published` client sees a\n * history's pinned snapshot (or nothing), and never a bare `draft` row.\n */\n documents?: readonly ContentDocumentSource[]\n assets?: readonly AssetRecord[]\n /**\n * Resolve a serve URL for assets without a stored `url` (`undefined` when there is\n * none). EC-633: no default — an asset with no URL is no resolved asset (the ONE\n * constructor, `resolvedAsset`, decides it): it renders the canvas's empty state, its\n * field resolves to `null` with an `asset-without-url` issue, and this source names it\n * on `sourceIssues`. The placeholder this used to default to (`asset://unresolved/…`)\n * rendered a broken image on the site.\n */\n assetUrl?: (asset: AssetRecord) => string | undefined\n /**\n * EC-585 — the stored rows the host could not read (`parseDeliveredDocuments`), handed\n * to every client of this source as its `sourceIssues` (and in the results they\n * concern). A malformed row is not delivered, and never silently.\n */\n sourceIssues?: readonly CmsValidationIssue[]\n}\n\n/**\n * Assemble a `ContentSource` from plain prefetched data — the pure half of\n * {@link createContentSnapshot}, shared with hosts that fetched the data\n * themselves (e.g. the EC-156 live delivery snapshot). Accessor semantics are\n * byte-identical across both paths: the same `ContentLookup` feeds the same\n * pure `@elytracms/core/content` client.\n */\nexport function createStaticContentSource(data: StaticContentData): ContentSnapshot {\n // EC-580: THE delivery parse of the declared locale config into the branded\n // `LocaleConfig` every reader below holds — every source (empty, seeded, live)\n // is assembled here, so a `default` outside `locales` cannot reach a client. It\n // throws: a host builds its empty source at boot, so that is where it fails.\n const locales = parseLocaleConfig(data.locales)\n const routes = data.routes ?? []\n const redirects = data.redirects ?? []\n // EC-633: a record's serve URL — its stored `url`, else the host's lookup, else none.\n const assetUrl = (asset: AssetRecord): string | undefined => asset.url ?? data.assetUrl?.(asset)\n\n const collections = new Map<string, CollectionDef>()\n for (const collection of data.collections ?? []) collections.set(collection.id, collection)\n\n const documents = new Map<string, ContentDocumentSource>()\n const byCollection = new Map<string, ContentDocumentSource[]>()\n for (const source of data.documents ?? []) {\n const doc = isDocumentHistory(source) ? source.draft : source\n documents.set(documentKey(doc), source)\n const list = byCollection.get(doc.collection) ?? []\n list.push(source)\n byCollection.set(doc.collection, list)\n }\n\n const assets = new Map<string, AssetRecord>()\n for (const record of data.assets ?? []) assets.set(record.id, record)\n // EC-659: the asset collection's lens — every asset this source resolves (document fields,\n // the baked assets) reads its stored translations through it. No collections → no schema →\n // nothing localized (see `StaticContentData.collections`).\n const metaLens = assetMetaLens(data.collections ?? [])\n // EC-633: every record this source cannot serve a file for is named once, on the source\n // (the EC-585/EC-628 channel every client carries) — the site shows its empty state, and\n // this says why. EC-659: so is every stored translation the lens does not read (once per\n // value) — the shared value is served, the residue named, the data kept.\n const sourceIssues: CmsValidationIssue[] = [\n ...(data.sourceIssues ?? []),\n ...[...assets.values()].filter((record) => !assetUrl(record)).map((record) => assetWithoutUrlIssue(record.id)),\n ...[...assets.values()].flatMap((record) =>\n assetMetaDrift(record, metaLens).map((drift) => assetMetaDriftIssue(record.id, drift)),\n ),\n ]\n\n const lookupFor = (_perspective: Perspective): ContentLookup => ({\n collection: (id) => collections.get(id),\n document: (ref: DocumentRef) => documents.get(documentKey(ref)),\n documents: (collectionId) => byCollection.get(collectionId) ?? [],\n asset: (id) => assets.get(id),\n assetUrl,\n })\n\n const client = (init: { perspective: Perspective; locale?: Locale }): ContentClient =>\n createContentClient({\n lookup: lookupFor(init.perspective),\n context: createContentContext({\n locale: init.locale ?? locales.default,\n perspective: init.perspective,\n locales,\n assetMetaLens: metaLens,\n }),\n routes,\n redirects,\n ...(sourceIssues.length > 0 ? { sourceIssues } : {}),\n })\n\n return {\n projectId: data.projectId,\n locales,\n client,\n routes,\n // EC-195: the delivery-shaped assets, so the renderer can resolve an\n // `asset`-typed Image prop (an asset id) to a usable url + intrinsic\n // dimensions. Baked into the (serializable) render outcome by the route\n // helper, so a cached page keeps its images during a backend outage.\n // EC-632: in the requested locale (the route helper passes the request's), so a\n // localized alt is baked in the locale the page renders in.\n // EC-633: a record with no URL is no resolved asset — not baked, so the page renders\n // the empty state the canvas renders for it (and `sourceIssues` names it).\n assets: (locale?: Locale) =>\n [...assets.values()].flatMap((record) => {\n const asset = resolvedAsset(record, assetUrl(record), assetReadLocale(locales, metaLens, locale))\n return asset ? [asset] : []\n }),\n // EC-499: the schema the route helper reads a collection's declared\n // `presentation` from (which field is the delivery body, which component\n // wraps it). Already prefetched for the lookup above — exposing it costs\n // nothing and keeps the declaration reachable without a second wiring seam.\n ...(data.collections ? { collections: data.collections } : {}),\n }\n}\n\n/**\n * Prefetch a project's content from a `PersistenceAdapter` into an in-memory\n * `ContentSource`. The snapshot is a point-in-time view: load once per\n * request (or memoize across requests for fixture-backed sites) — accessors\n * never reach back to the adapter.\n */\nexport async function createContentSnapshot(\n adapter: PersistenceAdapter,\n projectId: string,\n options: ContentSnapshotOptions,\n): Promise<ContentSnapshot> {\n // --- Prefetch (the only async part) ------------------------------------\n const documentRecords = await adapter.cms.listDocuments(projectId)\n const versionRecords = await adapter.cms.listAllDocumentVersions(projectId)\n const assetRecords = await adapter.assets.listAssets(projectId)\n // EC-633 follow-on: the SERVE URL of a record with no stored `url`, resolved here, by the\n // blob adapter's contract — its live lookup (`resolveUrl`: `undefined` when the blob is\n // gone), or, for a backend without one, its synchronous stable `url` (\"backends with\n // synchronous stable URLs can rely on url\"). Never the synchronous LOCATOR of a backend\n // that has a lookup (`convex://storage/…`, `memory://…` — no browser loads it): no serve\n // URL → no resolved asset, named on `sourceIssues` (the static source's one answer).\n const blobs = adapter.blobs\n const servedUrls = new Map<string, string | undefined>()\n await Promise.all(\n assetRecords\n .filter((record) => !record.url)\n .map(async (record) => {\n const url = blobs.resolveUrl\n ? await blobs.resolveUrl(record.storageKey).catch(() => undefined)\n : blobs.url(record.storageKey)\n servedUrls.set(record.id, url || undefined)\n }),\n )\n // Redirects are editorial DATA (EC-303): read the ENABLED ones from the adapter's\n // redirect record kind (EC-616; projected to the routing `RedirectRecord`), like documents/assets\n // — NOT from config. An explicit `options.redirects` still overrides (fixtures/tests).\n const redirectRecords = await adapter.records(redirectRecordKind).list(projectId)\n // THE live rule — the delivery snapshot and the studio's URL map read it too (EC-627); a stored\n // redirect it cannot read is withheld AND named on this source (EC-628).\n const { redirects, issues: redirectIssues } = readLiveRedirects(redirectRecords)\n\n // Hybrid sources (EC-224): a `publishedVersion` pointer yields a\n // `DocumentHistory` (draft = the live row, published = the pinned snapshot);\n // an unpinned record stays a bare `CmsDocument` gated by its `state`.\n // Backward-compatible — pre-pointer data resolves exactly as before.\n const versionsByKey = new Map<string, Map<number, CmsDocument>>()\n for (const record of versionRecords) {\n const key = `${record.collection}:${record.docId}`\n let byVersion = versionsByKey.get(key)\n if (!byVersion) {\n byVersion = new Map()\n versionsByKey.set(key, byVersion)\n }\n byVersion.set(record.version, record.snapshot)\n }\n const documents: ContentDocumentSource[] = documentRecords.map((record) => {\n if (record.publishedVersion === undefined) return record.document\n // A dangling pin under-serves to `null` for `published` — never the draft.\n const published = versionsByKey\n .get(`${record.document.collection}:${record.document.id}`)\n ?.get(record.publishedVersion)\n return { draft: record.document, published, versions: [] }\n })\n\n // --- Assemble via the shared pure path ----------------------------------\n return createStaticContentSource({\n projectId,\n // EC-628: only for the stored redirects this source serves (an explicit override replaces them).\n ...(options.redirects === undefined && redirectIssues.length > 0 ? { sourceIssues: redirectIssues } : {}),\n locales: options.locales,\n ...(options.routes ? { routes: options.routes } : {}),\n redirects: options.redirects ? [...options.redirects] : redirects,\n collections: options.collections,\n documents,\n assets: assetRecords,\n assetUrl: (asset) => servedUrls.get(asset.id),\n })\n}\n","import type { Metadata, ResolvedMetadata } from 'next'\nimport { DEFAULT_SEO_FIELD, SEO_SUBFIELDS } from '@elytracms/core/cms-core'\nimport type { ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\n\n/**\n * A resolved document → Next.js `Metadata` (EC-171, reshaped by EC-187, and by EC-590 —\n * EC-565 Round 3 \"3g, extensibility\"): **`seoMetadata(doc, ctx)`**, the kit's pure,\n * exported SEO reader. The route's `generateMetadata` calls it (the default), so a\n * project WRAPS it (`{ ...seoMetadata(doc, ctx), other }` for its own sub-fields),\n * REPLACES it, or ignores it.\n *\n * What it reads — never a dictated field name:\n * - the document's **SEO object**: the field under its collection's SEO ROLE\n * (`seoField`, default a field named `seo`; the engine's `seoFields()` declares it).\n * Its sub-field names are the contract ({@link SEO_SUBFIELDS}); a sub-field the object\n * omits emits nothing, a sub-field the project added is theirs to read.\n * - the document's **display title**: the ONE `resolveDocumentTitle` over its TITLE ROLE\n * (`titleField`, default `title` → `name`) — the resolve result carries it\n * (`result.title`), so a function `titleField` titles the page too.\n *\n * Mapping:\n * - `title`: `seo.title` → the display title.\n * - `description`: `seo.description`.\n * - `openGraph`: title (`seo.ogTitle` → the effective title), description\n * (`seo.ogDescription` → `seo.description`), image (`seo.image`) — and `locale` from the\n * page's resolved locale when, and only when, the code carries a territory (EC-531,\n * EC-610). The site-wide half of Open Graph (`type`, `siteName`, the site image) is the\n * LAYOUT's; the canvas route merges this object over the layout's\n * (`mergeMetadataOverParent`), so a page states only what is its own. Site-level SEO\n * defaults live on the project's own settings document and are read by its layout —\n * this reader needs none (EC-590 (b): nothing here ever read a settings document).\n * - `robots`: `seo.noindex` → `{ index: false }`.\n * - `alternates.canonical`: `seo.canonical` (EC-228), verbatim.\n *\n * Image resolution (EC-202/EC-266): `seo.image` is an asset field, delivered as a resolved\n * asset object (`{ id, url, … }`) → its `url`; a string is a passthrough URL or a raw asset\n * id resolved against the page's baked `assets`; anything unresolvable is dropped — an\n * `og:image` must carry a real URL, never an internal id.\n *\n * `hreflang` alternates are intentionally NOT derived here (a per-page translation map is\n * the sitemap's — EC-517).\n */\nexport interface SeoMetadataContext {\n /** The SEO role field name — `result.seoField`. Default: the conventional `seo`. */\n seoField?: string | null\n /** The display title — `result.title` (the title role, resolved). */\n title?: string | null\n /** The page's resolved locale (`result.tags.locale`) — for `og:locale`. */\n locale?: string\n /** The page-scoped baked assets (a raw asset-id `image` resolves against them). */\n assets?: readonly ResolvedAsset[]\n}\n\n/** The {@link SeoMetadataContext} of a resolve result — the default route's one line. */\nexport function seoContextOf(\n result: ResolvePageResult,\n assets: readonly ResolvedAsset[] = [],\n): SeoMetadataContext {\n return {\n seoField: result.seoField ?? null,\n title: result.title ?? null,\n locale: result.tags.locale,\n assets,\n }\n}\n\nfunction str(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null\n}\n\n/**\n * Resolve the SEO object's `image` (an `asset` field, `seoFields()`) to a URL: a\n * delivery-resolved asset OBJECT → its `url` (EC-266); a raw asset-id string → resolved\n * against the baked assets; anything unresolvable → dropped (EC-202). EC-587 / 2h: an\n * asset value never holds a URL, so a URL-shaped string is not passed through.\n */\nfunction resolveSeoImage(value: unknown, assets: readonly ResolvedAsset[]): string | null {\n if (value && typeof value === 'object') {\n const url = (value as { url?: unknown }).url\n return typeof url === 'string' && url.length > 0 ? url : null\n }\n if (typeof value !== 'string' || value.length === 0) return null\n return assets.find((asset) => asset.id === value)?.url ?? null\n}\n\n/**\n * EC-531, narrowed by EC-610 (owner ruling 3, 2026-10-23): `og:locale` ONLY in the\n * shape Open Graph specifies — `language_TERRITORY` with an underscore (`de_DE`),\n * where BCP 47 writes `de-DE`. The value is the page's RESOLVED locale\n * (`result.tags.locale`, the same fact the router used to pick the content). A\n * bare language code (`de`) is outside that format, so the kit emits nothing for\n * it and the layout's own value stands through the merge: inventing a territory\n * (`de` → `de_DE`) would be wrong exactly where it matters (Austria, the UK,\n * Brazil, Latin America), and a wrong valid value looks deliberate.\n */\nexport function openGraphLocale(locale: string): string | null {\n return locale.includes('-') ? locale.replace(/-/g, '_') : null\n}\n\n/** The document's SEO object (the field under its SEO role), or an empty record. */\nexport function seoObjectOf(\n doc: Record<string, unknown> | null | undefined,\n seoField: string | null | undefined = DEFAULT_SEO_FIELD,\n): Record<string, unknown> {\n if (!doc || seoField === null) return {}\n const value = doc[seoField ?? DEFAULT_SEO_FIELD]\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}\n}\n\n/** The kit's SEO reader — see the module docs. Pure: data in, `Metadata` out. */\nexport function seoMetadata(\n doc: Record<string, unknown> | null | undefined,\n ctx: SeoMetadataContext = {},\n): Metadata {\n const seo = seoObjectOf(doc, ctx.seoField)\n const assets = ctx.assets ?? []\n\n const seoTitle = str(seo[SEO_SUBFIELDS.title])\n const description = str(seo[SEO_SUBFIELDS.description])\n const ogTitle = str(seo[SEO_SUBFIELDS.ogTitle])\n const ogDescription = str(seo[SEO_SUBFIELDS.ogDescription])\n const image = resolveSeoImage(seo[SEO_SUBFIELDS.image], assets)\n const noindex = seo[SEO_SUBFIELDS.noindex] === true\n const canonical = str(seo[SEO_SUBFIELDS.canonical])\n\n // The display title: the title ROLE, resolved by the content client (`result.title`);\n // without it, the role's default names.\n const docTitle = ctx.title !== undefined ? str(ctx.title) : (str(doc?.['title']) ?? str(doc?.['name']))\n const title = seoTitle ?? docTitle\n\n const metadata: Metadata = {}\n if (title !== null) metadata.title = title\n if (description) metadata.description = description\n\n if (doc) {\n const effectiveOgTitle = ogTitle ?? title\n const effectiveOgDescription = ogDescription ?? description\n const ogLocale = ctx.locale !== undefined ? openGraphLocale(ctx.locale) : null\n metadata.openGraph = {\n ...(effectiveOgTitle !== null ? { title: effectiveOgTitle } : {}),\n ...(effectiveOgDescription ? { description: effectiveOgDescription } : {}),\n ...(image ? { images: [{ url: image }] } : {}),\n ...(ogLocale !== null ? { locale: ogLocale } : {}),\n }\n }\n\n if (noindex) metadata.robots = { index: false }\n\n // EC-228: a page-declared canonical URL. Passed through verbatim (absolute or\n // root-relative); Next resolves it against `metadataBase` when relative.\n if (canonical) metadata.alternates = { canonical }\n\n return metadata\n}\n\n/**\n * EC-610 (owner ruling 1, 2026-10-13): **the site provides the default tags, and\n * the page's tags are merged on top** — a value the page states overrides the\n * site's, a value the page leaves empty falls back to the site's.\n *\n * Next merges metadata SHALLOWLY (read: `generate-metadata.md`, \"Merging\"): a\n * page segment's `openGraph` object REPLACES the layout's whole `openGraph`, so a\n * site-wide `type`, `siteName`, site image or `locale` declared once in the root\n * layout never reached a canvas page, and both real sites hand-rolled around it\n * differently (a page hook on one, a post bypass on the other). This is the one\n * merge, applied by the canvas route to whatever builds the page's metadata (the\n * kit's default or the host's `metadata` option): every object-valued key the\n * page sets (`openGraph`, `twitter`, `robots`, `alternates`, …) is merged one\n * level deep over the parent's RESOLVED value; a key the page does not set is left\n * to Next, which inherits it from the parent as before; a scalar or array the\n * page sets wins outright.\n *\n * The parent's resolved shapes spread back cleanly: Next hands `generateMetadata`\n * a `ResolvedMetadata` whose URLs are already strings, whose `images` are\n * descriptor objects and whose titles are `{ absolute, template }` — all of which\n * `Metadata` accepts (Next's own \"extend rather than replace\" example spreads\n * `(await parent).openGraph?.images`).\n */\nexport function mergeMetadataOverParent(\n parent: ResolvedMetadata | null | undefined,\n page: Metadata,\n): Metadata {\n if (!parent) return page\n const merged: Record<string, unknown> = { ...page }\n for (const [key, pageValue] of Object.entries(page)) {\n const parentValue = (parent as Record<string, unknown>)[key]\n if (isPlainObject(pageValue) && isPlainObject(parentValue)) {\n merged[key] = { ...parentValue, ...pageValue }\n }\n }\n return merged as Metadata\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { cacheTags } from '@elytracms/core/content'\nimport { splitLocalePath } from './locale-url'\nimport type {\n CacheTags,\n ContentClient,\n ListDocumentsQuery,\n ResolvedAsset,\n ResolvedDocument,\n ResolvePageResult,\n} from '@elytracms/core/content'\nimport type { Clock } from '@elytracms/core/determinism'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { propFieldLookupOf } from '@elytracms/core/component-registry'\nimport {\n collectReferencedAssetIds,\n collectReferencedListings,\n collectReferencedRelations,\n collectReferencedRichTextLinks,\n collectRichTextRefsInValue,\n documentBodyCompositionNodes,\n documentBodyRichTextValues,\n listingRequestOf,\n richTextEmbedsInValue,\n} from '@elytracms/core/runtime-renderer'\nimport type { ListingQuery, RelationTarget, ResolveRelation } from '@elytracms/core/runtime-renderer'\nimport { deliverCompositionProps, presentationBodyFields, SEO_SUBFIELDS } from '@elytracms/core/cms-core'\nimport type { CmsValidationIssue, Locale, Perspective } from '@elytracms/core/cms-core'\nimport { seoObjectOf } from './metadata'\nimport { mergeCacheTags } from './source'\nimport type { ContentSource } from './source'\nimport { bodyItemsOf } from './canvas-renderer'\nimport type { BakedListing, DeliveryPresentation } from './canvas-renderer'\n\n/**\n * The pure half of the catch-all route helper (EC-144): URL → outcome, with\n * no Next.js imports — so redirects, 404s, locale handling, asset/relation/\n * listing/link baking, and tag merging are all unit-testable as plain\n * functions. `route.tsx` maps\n * each outcome onto `redirect()` / `permanentRedirect()` / `notFound()` /\n * `<CanvasRenderer />`.\n */\n\n/** What the route helper tells a host callback about the request it is serving. */\nexport interface CanvasRequestInfo {\n /** The raw request path (including any locale prefix). */\n url: string\n perspective: Perspective\n}\n\n/**\n * What a host's `wrap` function is told about the request it is wrapping\n * (EC-524 — see `CanvasRouteOptions.wrap`).\n *\n * Every field is something the route already had in hand at the wrap point:\n * nothing is gathered for this seam, and nothing was invented for it.\n */\nexport interface CanvasWrapContext extends CanvasRequestInfo {\n /**\n * The collection of the document this URL resolved to — **the branch key**.\n * `undefined` when the resolution carried no document, so a host branching on\n * a collection id naturally wraps nothing there (which is what the removed\n * `presentation.wrapper` declaration did too: it only applied with a document).\n */\n collection?: string\n /**\n * The resolved document. Verbatim the `document` prop the removed wrapper\n * component received, so a wrapper ported to this seam needs no other source\n * for its field-level chrome (the title, a byline, a date).\n */\n document: ResolvedDocument | null\n /**\n * The whole resolution — the same value the `metadata` callback already\n * receives, so this is the established host-callback argument on this route\n * rather than a new surface. It carries `dynamicParams`, `route`,\n * `localeFallback` and `issues` for a wrapper that needs more than the\n * document (EC-499 recorded wanting exactly that and could not have it across\n * a serializable declaration).\n */\n result: ResolvePageResult\n /**\n * The request's locale — the one the page resolved and renders in (EC-720). A NAME for\n * `result.tags.locale`, the value the route split from the URL once and that the page,\n * its metadata, listings, assets and beacon already read: never a second derivation, so\n * a wrapper's chrome strings and the content it wraps cannot disagree. Hand it to\n * `elytra.loadGlobals({ locale })` for chrome the wrapper reads itself.\n */\n locale: Locale\n}\n\n/**\n * PURE core (EC-524): build the wrap context from a render outcome's resolution\n * and the request info. Extracted from `route.tsx` so the derivation — above all\n * \"no document ⇒ no collection ⇒ a collection-branching host wraps nothing\" — is\n * testable without Next's request APIs.\n */\nexport function canvasWrapContext(\n result: ResolvePageResult,\n info: CanvasRequestInfo,\n): CanvasWrapContext {\n const collection = result.document?.collection\n return {\n ...info,\n ...(collection !== undefined ? { collection } : {}),\n document: result.document,\n result,\n locale: result.tags.locale,\n }\n}\n\n/**\n * EC-618 — what the route's `analytics` slot is told about the page it renders on: the\n * resolved document (the content model, not the URL — slug changes and locale paths then\n * attribute correctly), the locale it rendered in, the request path and the status. Plain\n * serializable values, so the slot may be a client component.\n */\nexport interface CanvasBeaconProps {\n /** The request path as served (including any locale prefix). */\n path: string\n /** 200: the route renders the slot only on a rendered page (a 404 never reaches it). */\n status: 200\n /** The locale the page rendered in. */\n locale: string\n /** The resolved document's collection and id — absent when the URL resolved to no document. */\n collection?: string\n documentId?: string\n}\n\n/** PURE core (EC-618): the slot's props from a render outcome's resolution and the request info. */\nexport function canvasBeaconProps(result: ResolvePageResult, info: CanvasRequestInfo): CanvasBeaconProps {\n const document = result.document\n return {\n path: info.url,\n status: 200,\n locale: result.tags.locale,\n ...(document ? { collection: document.collection, documentId: document.id } : {}),\n }\n}\n\nexport type CanvasPageOutcome =\n | { kind: 'redirect'; target: string; permanent: boolean; tags: CacheTags }\n | { kind: 'not-found'; tags: CacheTags }\n | {\n kind: 'render'\n result: ResolvePageResult\n /**\n * The delivery-shaped assets the page may reference (EC-195). Baked into\n * the outcome (serializable) so the renderer resolves `asset`-typed Image\n * props to a url + intrinsic dimensions, and a cached page keeps its\n * images during a backend outage.\n */\n assets: readonly ResolvedAsset[]\n /**\n * The composition-node `relation` props this page references, populated to\n * depth-1 delivery shape and keyed `collection:id` (EC-254). Baked into the\n * outcome (serializable) so `<CanvasRenderer />` rebuilds\n * `RenderContext.resolveRelation` from it inside the RSC — a function (the\n * content client) cannot ride the data cache or cross the RSC boundary, so\n * the data is baked exactly as the page-scoped `assets` list is.\n */\n relations: Record<string, RelationTarget>\n /**\n * The documents each `listing` block on this page resolved to (EC-255),\n * keyed by serialized query. Baked into the outcome (serializable) so\n * `<CanvasRenderer />` rebuilds `RenderContext.resolveListing` from it inside\n * the RSC — like `relations`, because the content client that runs the query\n * cannot ride the data cache or cross the RSC boundary. A listing depends on\n * collection MEMBERSHIP (a product joining/leaving the category), so its\n * invalidation rides the per-query `listDocuments` tags merged into `tags`.\n *\n * EC-581 6a: a query `listDocuments` refused (a non-filterable field, an\n * operand of the wrong type, a bad limit) is baked as `{ errors }` — the\n * structured `ContentQueryError`s — and `<CanvasRenderer />` shows them above\n * the block (`data-ec-fallback=\"invalid-listing\"`), never an empty grid\n * indistinguishable from \"no matching content\".\n */\n listings: Record<string, BakedListing>\n /**\n * EC-581 6a: whether resolving this page READ THE CLOCK — a listing on it\n * binds an operand to `now` (e.g. \"events whose date is still ahead\"). Such a\n * page's content changes as time passes, not only on publish: a cached entry\n * of it is correct for the instant it was resolved and goes stale when a\n * listed document crosses its `now` boundary, which no publish tag covers. A\n * cache layer must bound that entry's life (the delivery cache — EC-515 —\n * reads this). `false` for every page with no `now`-bound listing.\n */\n readsNow: boolean\n /**\n * The public URL path of every document a rich-text prop on this page LINKS\n * to (EC-291), keyed `collection:id`. Baked into the outcome (serializable) so\n * `<CanvasRenderer />` rebuilds `RenderContext.resolveLink` from it inside the\n * RSC — like `relations`, because the content client that derives the URL\n * (`resolveLink` → `urlForDocument`) cannot ride the data cache or cross the\n * RSC boundary. A link depends on the target's IDENTITY (its slug/parent →\n * path), so its invalidation rides the per-link doc tags merged into `tags`:\n * renaming, re-parenting, or first-publishing a linked document re-bakes the\n * embedding page's href.\n */\n links: Record<string, string>\n /**\n * The resolved document's collection-declared delivery presentation\n * (EC-499): which field(s) are the render body. Plain, serializable data —\n * handed to `<CanvasRenderer />` so ONE catch-all renders every\n * collection's body, whatever its shape. Absent when the collection\n * declares nothing (or the source exposes no collections), and the EC-458\n * value-shape picker then decides exactly as before.\n */\n presentation?: DeliveryPresentation\n tags: CacheTags\n }\n\n/**\n * The outcome a page renders (EC-722): what `route.load(url)` returns once the route has\n * answered a redirect or a not-found itself — a caller of it never branches on `kind`.\n */\nexport type CanvasRenderOutcome = Extract<CanvasPageOutcome, { kind: 'render' }>\n\n/** Build the request path from a Next catch-all `slug` param. */\nexport function pathFromSlug(slug: readonly string[] | undefined): string {\n return '/' + (slug ?? []).join('/')\n}\n\n/**\n * Derive the request locale from a leading URL segment (`/de/about` → locale\n * `de`, path `/about`). EC-486 moved the implementation to `./locale-url`, so\n * this direction and the sitemap's inverse are one definition rather than two\n * that can drift; re-exported here because it is a published API surface.\n */\nexport { splitLocalePath }\n\nexport interface ResolveCanvasPageOptions {\n /**\n * Component registry (EC-205): when provided, the baked {@link CanvasPageOutcome}\n * `assets` are PAGE-SCOPED to exactly the asset ids this page's composition +\n * layout resolve through `resolveAsset` — not the whole project asset list,\n * which otherwise rides in every page's cache entry + RSC payload. Omitted →\n * all source assets (back-compat). Scoping mirrors the renderer's asset\n * resolution exactly, so no referenced image is ever dropped.\n */\n registry?: ComponentRegistry\n /**\n * The clock a `{ now: true }` listing operand reads (EC-581 6a) — an ISO-8601\n * UTC instant, read at most once per resolution so every listing on the page\n * compares against the SAME instant. Defaults to the system clock; tests and\n * fixtures inject a fixed one. Taken as a FUNCTION, read during resolution, so a\n * `now` value is never an argument a cache keys on.\n */\n clock?: Clock\n}\n\n/** The real clock, used only at this boundary when the caller injects none. */\nconst systemClock: Clock = () => new Date().toISOString()\n\n/**\n * Page-scope the baked assets to those the page actually resolves (EC-205): the\n * static `asset`-typed props of its composition + selected layout tree. Document\n * asset FIELDS resolve into the document separately and do not use this list, so\n * they do not widen it.\n */\nfunction scopeAssetsToPage(\n allAssets: readonly ResolvedAsset[],\n registry: ComponentRegistry,\n result: ResolvePageResult,\n body: PageBody,\n resolveRelation: ResolveRelation,\n): readonly ResolvedAsset[] {\n if (allAssets.length === 0) return allAssets\n const ids = new Set<string>()\n // EC-270: pass `resolveRelation` so the collector follows a Reusable block's\n // `element` pick into the referenced document and page-scopes the asset ids\n // inside its transcluded `content` — otherwise a transcluded image is baked\n // nowhere and renders empty on the host (the relation bake already transcludes).\n const seen = new Set<string>()\n for (const node of body.nodes) {\n collectReferencedAssetIds(node, registry, ids, resolveRelation, seen)\n }\n // EC-499: a rich-text BODY (not just a component's rich-text prop) pins assets\n // too — an inline image carries `attrs.ref = { type:'asset', id }`. Its\n // component-node siblings are already in `body.nodes`; this collects the pins\n // that sit directly in the prose, so the host bakes them instead of the page\n // rendering an unresolved-ref placeholder where the image should be.\n for (const value of body.richTexts) {\n collectRichTextRefsInValue(value, { onAsset: (id) => ids.add(id) })\n }\n // EC-202: the OG image is an asset the page uses (for its `og:image` meta), so\n // it joins the page-scoped set — (EC-227) tagged for invalidation when that\n // asset record is edited. EC-266: the SEO object's `image` (an asset field, EC-590) is delivered\n // as a ResolvedAsset OBJECT ({ id, url }); a string is a raw id (or a passthrough\n // URL, which has no asset id to tag). Take the id from whichever shape.\n const ogImage = seoObjectOf(result.document, result.seoField)[SEO_SUBFIELDS.image]\n const ogImageId =\n typeof ogImage === 'string'\n ? ogImage\n : ogImage && typeof ogImage === 'object' && typeof (ogImage as { id?: unknown }).id === 'string'\n ? (ogImage as { id: string }).id\n : null\n if (ogImageId) ids.add(ogImageId)\n return ids.size === 0 ? [] : allAssets.filter((asset) => ids.has(asset.id))\n}\n\n/**\n * One request's resolved delivery body (EC-499), split into what each baker\n * needs: the composition NODES to walk (EC-254 relation scan, EC-205 asset\n * scoping, EC-255 listings, EC-291 links) and the rich-text REGIONS whose own\n * refs must be baked alongside them.\n */\ninterface PageBody {\n /**\n * Every composition node the page renders: the body's own component nodes AND\n * the components EMBEDDED in its rich-text regions (EC-189/EC-434 — the\n * ambient embed renderer renders those on delivery, so their relations,\n * listings and assets have to be baked or they render empty).\n */\n nodes: ComponentNode[]\n /** The body's rich-text regions, for the pinned-asset / internal-link bake. */\n richTexts: unknown[]\n}\n\n/** Split a resolved document's declared body into the bakers' two views (EC-499). */\nfunction pageBodyOf(result: ResolvePageResult, presentation?: DeliveryPresentation): PageBody {\n const items = bodyItemsOf(result.document, presentation)\n const richTexts = documentBodyRichTextValues(items)\n return {\n nodes: [\n ...documentBodyCompositionNodes(items),\n ...richTexts.flatMap((value) => richTextEmbedsInValue(value)),\n ],\n richTexts,\n }\n}\n\n/**\n * The collection-declared delivery presentation for the document this URL\n * resolved to (EC-499), normalized for the render outcome — `undefined` when the\n * source exposes no collections, the collection is unknown, or it declares\n * nothing (the EC-458 shape picker then decides, unchanged).\n *\n * EC-524: body fields only. The detail wrapper this used to carry alongside them\n * is host code now, applied by `createCanvasRoute`'s `wrap` around the whole\n * renderer, so nothing chrome-shaped crosses the outcome.\n */\nfunction presentationFor(\n source: ContentSource,\n result: ResolvePageResult,\n): DeliveryPresentation | undefined {\n const collectionId = result.document?.collection\n if (!collectionId || !source.collections) return undefined\n const collection = source.collections.find((candidate) => candidate.id === collectionId)\n if (!collection?.presentation) return undefined\n const body = presentationBodyFields(collection)\n return body.length > 0 ? { body: [...body] } : undefined\n}\n\n/**\n * EC-662 — THE delivery projection over the page's component props, run ONCE here, before\n * any baker and the renderer (Codex round 3, F02 residual + F03 diagnostic). Every\n * composition value the resolved document delivers (its body — the converged sequence or\n * a legacy node list — whichever fields hold one) has the props of every node in it\n * projected at every depth (`deliverCompositionProps`): a tolerated stored form becomes\n * its canonical twin, a value the prop's rule rejects or a reference to a collection the\n * prop does not target is withheld — and each of those carries its located issue\n * (`[field, 'content', i, 'props', name, …]`) onto the result's `issues`, where a page's\n * findings already are. The bakers below walk, and `<CanvasRenderer />` renders, the\n * PROJECTED document; the renderer's own pass over it is the identity (the projection\n * delivers what it is given), so the page is judged once and renders what was judged.\n *\n * Without a registry no prop can be judged (no manifest says what a prop is): the props are\n * delivered as stored and a `props-not-judged` warning says so — never silence.\n */\nfunction projectPageProps(result: ResolvePageResult, registry: ComponentRegistry | undefined): ResolvePageResult {\n const document = result.document\n if (!document) return result\n const lookupManifest = registry ? propFieldLookupOf((id) => registry.getManifest(id)) : undefined\n const issues: CmsValidationIssue[] = []\n let projected: Record<string, unknown> | undefined\n for (const [name, value] of Object.entries(document)) {\n const delivered = deliverCompositionProps(\n value,\n { path: [name], collectionId: document.collection, documentId: document.id },\n issues,\n lookupManifest,\n )\n if (delivered !== value) (projected ??= { ...document })[name] = delivered\n }\n if (!projected && issues.length === 0) return result\n return {\n ...result,\n ...(projected ? { document: projected as ResolvedDocument } : {}),\n issues: [...result.issues, ...issues],\n }\n}\n\n/**\n * Populate every composition-node `relation` prop the page references to depth-1,\n * keyed `collection:id` (EC-254). Walks the page composition + layout (and, through\n * `collectReferencedRelations`, transcluded reusable content) and resolves each ref\n * through the request's client. Empty when no registry is supplied (the collector\n * needs manifests to know which props are relations) — relation props then pass\n * through raw, the graceful degradation a host without a registry already gets.\n *\n * `docKeys` carries the `collection:id` of every referenced pick (resolved or not),\n * so the caller cache-tags the page by them (EC-146): editing/publishing a picked\n * document — including a previously-unpublished one that should now appear — drops\n * exactly the pages that embed it. A fixed pick depends on the document's identity,\n * not on its collection's membership, so no coarse collection tag is added.\n */\nfunction populatePageRelations(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n): { relations: Record<string, RelationTarget>; docKeys: string[] } {\n if (!registry) return { relations: {}, docKeys: [] }\n const refKeys = new Set<string>()\n const relations = collectReferencedRelations(\n body.nodes,\n registry,\n (ref) => client.resolveRelation(ref),\n {},\n refKeys,\n )\n return { relations, docKeys: [...refKeys] }\n}\n\n/**\n * Resolve every `listing` block on the page to its matching documents (EC-255),\n * keyed by serialized query for `<CanvasRenderer />` to rebuild `resolveListing`.\n * Runs each listing's query through the request's client (so perspective + locale\n * match the rest of the resolution — published-only on a live page, per AD-5), and\n * returns the per-query `listDocuments` `CacheTags` so the caller folds them into\n * the page's tag set: those carry both the COLLECTION tag (membership — a product\n * joining/leaving the category re-renders the grid) and the listed documents' own\n * tags (a member's content edit re-renders it). Empty when no registry is supplied\n * (the collector needs manifests to know which blocks declare a listing).\n *\n * EC-581 6a: each query executes through `listingRequestOf` — the same phase the\n * studio canvas runs — with `clock` read at most once for the whole page\n * (`readsNow` reports whether it was). A query `listDocuments` refuses is baked as\n * its structured errors, so the page shows them instead of a silent empty grid.\n */\nfunction populatePageListings(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n clock: Clock,\n): { listings: Record<string, BakedListing>; tags: CacheTags[]; readsNow: boolean } {\n if (!registry) return { listings: {}, tags: [], readsNow: false }\n const tags: CacheTags[] = []\n let instant: string | undefined\n const now = (): string => (instant ??= clock())\n const listings = collectReferencedListings<BakedListing>(\n body.nodes,\n registry,\n (query: ListingQuery) => {\n const listed = client.listDocuments(query.collection, listDocumentsQueryOf(query, now))\n tags.push(listed.tags)\n return listed.ok ? (listed.documents as RelationTarget[]) : { errors: listed.errors }\n },\n // EC-255: reach listings nested inside transcluded reusable content (the same\n // depth-1 populate the relation bake uses), so a grid inside a reusable bakes —\n // and its membership tag is folded in — instead of rendering empty on the host.\n (ref) => client.resolveRelation(ref),\n )\n return { listings, tags, readsNow: instant !== undefined }\n}\n\n/**\n * A listing query as `listDocuments` input (EC-581 6a). The operands are JSON\n * bound from authored props and `now`; the cast hands them to `listDocuments`,\n * whose `validateWhere`/`validateLimit` judge them — the one validator — and\n * return a structured error for anything that is not a valid operand.\n */\nfunction listDocumentsQueryOf(query: ListingQuery, now: () => string): ListDocumentsQuery {\n const request = listingRequestOf(query, now)\n return {\n where: request.where as ListDocumentsQuery['where'],\n ...(request.sort ? { sort: request.sort } : {}),\n ...(request.limit !== undefined ? { limit: request.limit as number } : {}),\n }\n}\n\n/**\n * Resolve every internal rich-text LINK on the page to its target document's URL\n * path (EC-291), keyed `collection:id` for `<CanvasRenderer />` to rebuild\n * `resolveLink`. Collects the referenced doc refs from the page's rich-text props\n * (transclusion-aware) and derives each URL through the request's client\n * (`resolveLink` → `urlForDocument`), so the path matches what the same routes serve.\n * A ref that resolves to no live URL (unpublished target / no addressing route) is\n * simply omitted — the renderer then shows the EC-290 `unresolved-ref` issue.\n *\n * `docKeys` carries EVERY referenced target (resolved or not), so the caller cache-\n * tags the page by them (EC-146): publishing or renaming a linked document — including\n * one not yet published when the page was baked — drops exactly the embedding pages.\n * Empty when no registry is supplied (the collector needs no manifest, but the bake\n * stays consistent with its relation/listing siblings, which do).\n */\nfunction populatePageLinks(\n body: PageBody,\n client: ContentClient,\n registry: ComponentRegistry | undefined,\n): { links: Record<string, string>; docKeys: string[] } {\n if (!registry) return { links: {}, docKeys: [] }\n const refs = collectReferencedRichTextLinks(\n body.nodes,\n registry,\n (ref) => client.resolveRelation(ref),\n )\n // EC-499: a rich-text BODY carries link marks directly, not only inside a\n // component's rich-text prop — collect those too, so an internal link in a\n // post's prose resolves to a real href instead of the `unresolved-ref` fallback.\n for (const value of body.richTexts) {\n collectRichTextRefsInValue(value, {\n onLink: (ref) => {\n refs[`${ref.collection}:${ref.id}`] = ref\n },\n })\n }\n const links: Record<string, string> = {}\n for (const [key, ref] of Object.entries(refs)) {\n const url = client.resolveLink(ref)\n if (url !== null) links[key] = url\n }\n return { links, docKeys: Object.keys(refs) }\n}\n\n/** One request, read once (EC-720): its locale, that locale's client, and the page resolution. */\nexport interface CanvasRequestPage {\n /** The request's locale — split from the URL's leading segment, else the project default. */\n locale: Locale\n /** The content client of that locale, in the request's perspective. */\n client: ContentClient\n /** The URL's resolution in that locale (`resolved.tags.locale` is the same `locale`). */\n resolved: ResolvePageResult\n}\n\n/**\n * THE request split (EC-720): one URL → its locale, the client of that locale and the\n * page resolution. Every request-time reader of a page goes through it — the catch-all's\n * {@link resolveCanvasPage} and the kit's `pageOpensFullBleed` — so the locale a page\n * resolves in is derived in one place, and every reader downstream reads that one value\n * (`resolved.tags.locale`) rather than splitting the URL again.\n */\nexport function resolveRequestPage(\n source: ContentSource,\n url: string,\n perspective: Perspective,\n): CanvasRequestPage {\n const { locale, path } = splitLocalePath(url, source.locales)\n const client = source.client({ perspective, locale })\n return { locale, client, resolved: client.resolvePage(path) }\n}\n\n/**\n * Resolve one request URL against a content source: split the locale, run\n * `resolvePage` in the requested perspective, assemble binding payloads, and\n * merge every accessor's `CacheTags` into one deterministic set. Pure — all\n * data was prefetched by the source.\n */\nexport async function resolveCanvasPage(\n source: ContentSource,\n url: string,\n perspective: Perspective,\n options: ResolveCanvasPageOptions = {},\n): Promise<CanvasPageOutcome> {\n const { locale, client, resolved } = resolveRequestPage(source, url, perspective)\n\n if (resolved.status === 'redirect' && resolved.redirect) {\n return {\n kind: 'redirect',\n target: resolved.redirect.target,\n permanent: resolved.redirect.permanent,\n tags: resolved.tags,\n }\n }\n\n if (resolved.status !== 'ok') {\n return { kind: 'not-found', tags: resolved.tags }\n }\n\n // EC-662: the page's component props, projected once — what every baker below walks and\n // the renderer renders, with their located issues on the result.\n const result = projectPageProps(resolved, options.registry)\n\n // EC-499: read the collection's declared delivery presentation ONCE, and split\n // the resolved body by it — every baker below and the renderer then agree on\n // exactly which fields are the body and which of its items is which kind.\n const presentation = presentationFor(source, result)\n const body = pageBodyOf(result, presentation)\n\n // EC-632: the assets in the request's locale — a localized alt bakes in the page's locale.\n const allAssets = source.assets?.(locale) ?? []\n // EC-205: page-scope the baked assets when a registry is supplied (the route\n // helper passes it); else keep the full list (back-compat).\n const bakedAssets = options.registry\n ? scopeAssetsToPage(allAssets, options.registry, result, body, (ref) =>\n client.resolveRelation(ref),\n )\n : allAssets\n // EC-227: tag the cache entry with every asset it baked, so editing an asset\n // record (alt/dimensions/re-upload) drops exactly the pages carrying it. The\n // locale is irrelevant for asset tags (an asset record is locale-agnostic) —\n // `cacheTags` just needs one; `result.tags.locale` is the page's.\n const assetTags = cacheTags({\n assets: bakedAssets.map((asset) => asset.id),\n locale: result.tags.locale,\n })\n // EC-254: populate the relation props of the nodes inside the page's blocks\n // (e.g. a ProductGrid's picked products), keyed for `<CanvasRenderer />` to\n // rebuild `resolveRelation`. Reuses the request's client, so perspective +\n // locale match the rest of the resolution.\n const { relations, docKeys: relationDocKeys } = populatePageRelations(\n body,\n client,\n options.registry,\n )\n // EC-146: tag the page by every picked document so editing/publishing one drops\n // the embedding page (the locale is the page's; doc tags are locale-agnostic).\n const relationTags = cacheTags({ docs: relationDocKeys, locale: result.tags.locale })\n // EC-255: resolve every listing block's query (e.g. a ProductGrid keyed to a\n // category) and bake the result keyed by serialized query. The per-query\n // `listDocuments` tags (collection membership + listed docs) join the merged set,\n // so adding/removing a member or editing one invalidates the embedding page.\n const {\n listings,\n tags: listingTags,\n readsNow,\n } = populatePageListings(body, client, options.registry, options.clock ?? systemClock)\n // EC-291: resolve every internal rich-text link to its target's URL path and bake\n // it for `<CanvasRenderer />` to rebuild `resolveLink`. Tag the page by each linked\n // document so renaming/re-parenting/first-publishing a target re-bakes the href.\n const { links, docKeys: linkDocKeys } = populatePageLinks(body, client, options.registry)\n const linkTags = cacheTags({ docs: linkDocKeys, locale: result.tags.locale })\n return {\n kind: 'render',\n result,\n assets: bakedAssets,\n relations,\n listings,\n readsNow,\n links,\n ...(presentation ? { presentation } : {}),\n tags: mergeCacheTags(\n result.tags,\n ...listingTags,\n assetTags,\n relationTags,\n linkTags,\n ),\n }\n}\n","import type { CacheTags } from '@elytracms/core/content'\nimport type { RevalidationEvent } from '@elytracms/core/revalidation'\n\n/**\n * Cache-tag pipeline (EC-146, vision AD-5) — the pure half.\n *\n * This module defines (a) the **canonical Next.js tag string format** mapping\n * the accessor-level `CacheTags` (EC-143) onto `cacheTag` / `revalidateTag` keys,\n * (b) the **tag derivation for a revalidation event** ({@link revalidationTags},\n * EC-579 — the receiver's half of the one emitter), and (c) the **pure half of a\n * cached resolution**: its key ({@link canvasCacheKey}) and the `cacheTag` calls that\n * declare its tags ({@link entryTagCalls}). No Next.js imports — everything here is\n * unit-testable.\n *\n * **Caching is the host's (EC-620).** The engine's part is exactly three things: the\n * tags on everything the accessors return, the invalidation signal (the Convex\n * trigger's event → the kit receiver → `revalidateTag` for every derived tag), and a\n * default in the scaffold. Whether a page caches, under which scope, for how long and\n * with which Next flag is the host's: the kit holds no `'use cache'` scope and no\n * `cacheLife`. The host's own `'use cache'` function declares a result's tags through\n * the canvas route's `applyCacheTags` (`route.tsx`), which makes the\n * {@link entryTagCalls} calls with the host's one scope.\n *\n * This is the ONE tag module: the entries are tagged here and the invalidations\n * are derived here, from the same formatters and the same scope. Before EC-579 the\n * format was mirrored twice more (the studio's browser dispatcher and an inline\n * literal in the Convex emitter), and each emitter brought its own scope.\n *\n * ## Tag format\n *\n * - `doc:<collection>:<id>` — one per document whose content was actually in\n * the response (fetch-time tagging: includes depth-1 populated relation\n * targets, and documents inside filtered/dynamic lists *after* the filter\n * ran).\n * - `collection:<name>:<locale>` — the coarse membership tag: every response\n * that read from a collection carries it, so create/delete/newly-matching\n * documents sweep cached lists without read-set tracking.\n * - `route:<pattern>:<locale>` — the resolved URL path plus every hop of a\n * redirect chain.\n * - `asset:<id>` — one per asset whose delivery shape the page *baked*\n * (page-scoped images + `seoOgImage`, EC-227); editing that asset record\n * (alt/dimensions/re-upload) drops exactly the pages that baked it. Not\n * locale-scoped — an asset record is locale-agnostic.\n * - `project` — the whole-project sweep tag attached to **every** cached\n * entry; derived for the events whose effect no narrower tag names (a redirect\n * change, an asset edit — see {@link revalidationTags}).\n *\n * ## Project scope\n *\n * Every tag is prefixed `p:<projectId>:` when a scope is configured (e.g.\n * `p:prj_aurora:doc:post:post-1`). The cost is a few bytes per tag and it\n * keeps multi-project hosts (one Next app serving several builder projects)\n * from cross-invalidating, so the prefix is **on whenever the host knows its\n * project id** — decision: include it. Since EC-579 the scope has ONE owner: the\n * host. The webhook carries an event, not tags, and the receiver derives the\n * tags with the same scope its cache entries are written under (the delivery\n * kit hands both the one `contentScope`), so emitter and host can no longer\n * disagree about it.\n */\n\n/** Apply the optional multi-project scope prefix to one tag. */\nexport function scopeCacheTag(tag: string, scope?: string): string {\n return scope === undefined || scope.length === 0 ? tag : `p:${scope}:${tag}`\n}\n\n/**\n * Tag for one document identity. `docKey` is the EC-143 `documentKey` string\n * (`<collection>:<id>`), exactly as found in `CacheTags.docs`.\n */\nexport function docCacheTag(docKey: string, scope?: string): string {\n return scopeCacheTag(`doc:${docKey}`, scope)\n}\n\n/** The coarse membership tag of one collection in one locale. */\nexport function collectionCacheTag(collection: string, locale: string, scope?: string): string {\n return scopeCacheTag(`collection:${collection}:${locale}`, scope)\n}\n\n/** Tag for one resolved route path (or redirect-chain hop) in one locale. */\nexport function routeCacheTag(pattern: string, locale: string, scope?: string): string {\n return scopeCacheTag(`route:${pattern}:${locale}`, scope)\n}\n\n/**\n * Tag for one baked asset (EC-227). `assetId` is the bare asset id, as found in\n * `CacheTags.assets`. Not locale-scoped: an asset record is one row shared\n * across locales, so an edit invalidates the baking pages in every locale.\n */\nexport function assetCacheTag(assetId: string, scope?: string): string {\n return scopeCacheTag(`asset:${assetId}`, scope)\n}\n\n/**\n * The whole-project sweep tag. Attached to every cached page entry; derived for\n * the events no narrower tag covers ({@link revalidationTags}).\n */\nexport function projectSweepTag(scope?: string): string {\n return scopeCacheTag('project', scope)\n}\n\n/**\n * Map an accessor-level `CacheTags` (what one request actually read) onto the\n * full, deterministic (sorted, deduplicated) set of Next tag strings for the\n * cache entry — including the project sweep tag.\n */\nexport function nextCacheTags(tags: CacheTags, scope?: string): string[] {\n const all = new Set<string>()\n all.add(projectSweepTag(scope))\n for (const docKey of tags.docs) all.add(docCacheTag(docKey, scope))\n for (const collection of tags.collections) {\n all.add(collectionCacheTag(collection, tags.locale, scope))\n }\n for (const route of tags.routes) all.add(routeCacheTag(route, tags.locale, scope))\n for (const assetId of tags.assets) all.add(assetCacheTag(assetId, scope))\n return [...all].sort()\n}\n\n// --- Revalidation event → tags (EC-579) ----------------------------------------\n\n/** The receiver's own project — what its cached entries are tagged with. */\nexport interface RevalidationProject {\n /** The tag scope of this host's cache entries (`p:<scope>:…`), if any. */\n scope?: string | undefined\n /** Every locale this host serves — one `collection:` tag per locale. */\n locales: readonly string[]\n}\n\n/**\n * The tags one revalidation event invalidates on this host — sorted, deduplicated,\n * scoped with the receiver's OWN scope, so the emitter never needs to know it.\n *\n * - `publish` / `unpublish` / `delete` → `doc:<collection>:<id>` (its detail page and\n * every cached list that contained it) + `collection:<collection>:<locale>` for\n * EVERY locale this host serves (list membership may have changed, and a URL\n * cached as not-found for a document that now exists carries it too). **No\n * sweep** — a publish drops exactly the entries that read the document or its\n * collection (EC-515);\n * - `redirects` → the sweep (routing can change any URL; see the `redirects` trigger\n * in the studio's `convex/triggers.ts` for why a targeted `route:` tag is wrong);\n * - `asset` → `asset:<id>` + the sweep (a page reading the asset through a document\n * asset FIELD carries no `asset:` tag — EC-566 D16, EC-589);\n * - `probe` → nothing (the doctor's no-op ping).\n *\n * Why the precise tags are enough (EC-515 option D, kept by EC-620): a cached\n * resolution declares its tags with `cacheTag` from INSIDE the cache function that\n * loaded it (the host's, through `applyCacheTags`), and Next decides an entry's\n * staleness from the tags it was STORED with — so a `doc:` tag drops the entries that\n * read the document, on a self-hosted host as on any other. Until EC-515 the read\n * named only the baseline sweep (`unstable_cache` judged the reading wrapper's tags),\n * every precise tag was inert on a self-hosted host, and every publish carried the\n * sweep to compensate (wave-0091). Measured both ways in EC-515's harness, and again\n * with the host-owned scope in EC-620's.\n */\nexport function revalidationTags(event: RevalidationEvent, project: RevalidationProject): string[] {\n const { scope } = project\n const tags = new Set<string>()\n switch (event.kind) {\n case 'publish':\n case 'unpublish':\n case 'delete':\n tags.add(docCacheTag(`${event.collection}:${event.id}`, scope))\n for (const locale of project.locales) tags.add(collectionCacheTag(event.collection, locale, scope))\n break\n case 'redirects':\n tags.add(projectSweepTag(scope))\n break\n case 'asset':\n tags.add(assetCacheTag(event.id, scope))\n tags.add(projectSweepTag(scope))\n break\n case 'probe':\n break\n }\n return [...tags].sort()\n}\n\n\n// --- A cached resolution: its key and its tag calls (EC-515, EC-620) -------------\n\ndeclare const canvasCacheKeyBrand: unique symbol\n\n/**\n * What identifies one published canvas resolution — the argument the canvas route\n * hands the host's cache function (`CanvasRouteOptions.published`), which therefore\n * IS that entry's key: Next keys a `'use cache'` entry by (build id, function id,\n * serialized arguments), so whatever identifies an entry has to be an argument, and\n * nothing else may be. It encodes three facts ({@link parseCanvasCacheKey}):\n *\n * - `scope` — the tag scope (the project id the studio publishes under; `''` if none).\n * - `mode` — which content world the loader reads (the delivery kit's `contentMode`:\n * `live` / `fallback` / `empty`; `''` for a host driving the route directly with\n * one world).\n * - `url` — the request path, locale prefix included.\n *\n * **An opaque string, not an object — measured (EC-620).** Next wraps every\n * `'use cache'` function in React's per-request `cache()`, which memoizes by argument\n * IDENTITY. `Page` and `generateMetadata` each build the key for the same URL; as two\n * equal objects they missed that memo and a cold URL ran the loader twice (page and\n * metadata filling the entry concurrently), as one string they share the in-flight\n * miss — one load, exactly as the kit-owned entry did. A string is also trivially\n * stable for Next's own key serialization. The brand keeps a hand-built string out.\n *\n * The mode is in the key because an entry can outlive the world that wrote it (a\n * shared or remote cache handler, a host whose env changed under a warm cache) and a\n * hit never asks the loader whether it still applies. Before EC-543 the key was\n * `(scope, url)` only, and since `ELYTRA_PROJECT_ID` *is* the config slug, a live host\n * and an empty one keyed the same URL identically — measured on 0.0.85: a warm cache\n * written in live mode kept answering `200` with the live world's bytes after both env\n * vars were blanked and the host itself reported `contentMode: 'empty'`. Keying on the\n * mode makes that cross-read structurally impossible rather than something to detect —\n * a world change is a cold miss, never a stale hit. Since EC-620 the cache function is\n * the host's, so the property holds as long as the host keys on this value — which\n * it does by passing it through unchanged (the canvas route refuses a key of another\n * world).\n */\nexport type CanvasCacheKey = string & { readonly [canvasCacheKeyBrand]: true }\n\n/** What a {@link CanvasCacheKey} is built from (absent scope/mode = `''`). */\nexport interface CanvasCacheKeyInput {\n scope?: string | undefined\n mode?: string | undefined\n url: string\n}\n\n/** The three facts a {@link CanvasCacheKey} encodes. */\nexport interface CanvasCacheKeyParts {\n scope: string\n mode: string\n url: string\n}\n\n/**\n * Build the key of one published resolution — see {@link CanvasCacheKey}. The\n * encoding is JSON of `[scope, mode, url]`: injective (no separator a URL could\n * contain can make two worlds collide) and readable in a log.\n */\nexport function canvasCacheKey({ scope, mode, url }: CanvasCacheKeyInput): CanvasCacheKey {\n return JSON.stringify([scope ?? '', mode ?? '', url]) as CanvasCacheKey\n}\n\n/**\n * Decode a {@link CanvasCacheKey}; `undefined` for any string {@link canvasCacheKey}\n * did not produce (the canvas route refuses such a key loudly).\n */\nexport function parseCanvasCacheKey(key: string): CanvasCacheKeyParts | undefined {\n let parsed: unknown\n try {\n parsed = JSON.parse(key)\n } catch {\n return undefined\n }\n if (!Array.isArray(parsed) || parsed.length !== 3 || !parsed.every((part) => typeof part === 'string')) {\n return undefined\n }\n const [scope, mode, url] = parsed as [string, string, string]\n return { scope, mode, url }\n}\n\n/**\n * Next's per-call tag cap: `cacheTag()` validates each call's tags and stops at\n * `NEXT_CACHE_TAG_MAX_ITEMS` (128), dropping the rest with only a console warning\n * (`patch-fetch.js` `validateTags`). The cap is per CALL — the entry accumulates every\n * call's tags (`cache-tag.js`).\n */\nexport const NEXT_CACHE_TAG_CALL_LIMIT = 128\n\n/**\n * The `cacheTag()` calls that declare one result's tags on a cache entry: the project\n * sweep FIRST and ALONE, then the read set in chunks under\n * {@link NEXT_CACHE_TAG_CALL_LIMIT}. The canvas route's `applyCacheTags` makes exactly\n * these calls.\n *\n * {@link nextCacheTags} is sorted, and `p:<scope>:project` sorts after every\n * `collection:` and `doc:` tag — handed to one `cacheTag` call, a page that read more\n * than ~125 documents would lose the sweep to the per-call cap, silently, and with it\n * every redirect and asset invalidation (measured, EC-515). Its own first call makes\n * that impossible; the chunking keeps every other tag too.\n */\nexport function entryTagCalls(tags: CacheTags, scope?: string): string[][] {\n const sweep = projectSweepTag(scope)\n const rest = nextCacheTags(tags, scope).filter((tag) => tag !== sweep)\n const calls: string[][] = [[sweep]]\n for (let start = 0; start < rest.length; start += NEXT_CACHE_TAG_CALL_LIMIT) {\n calls.push(rest.slice(start, start + NEXT_CACHE_TAG_CALL_LIMIT))\n }\n return calls\n}\n","import type { Metadata, ResolvingMetadata } from 'next'\nimport { cacheTag } from 'next/cache'\nimport { draftMode } from 'next/headers'\nimport { notFound, permanentRedirect, redirect } from 'next/navigation'\nimport type { ComponentType, ReactNode } from 'react'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport type { CacheTags, ResolvedAsset, ResolvePageResult } from '@elytracms/core/content'\nimport { canvasCacheKey, entryTagCalls, parseCanvasCacheKey } from './cache'\nimport type { CanvasCacheKey } from './cache'\nimport { CanvasRenderer } from './canvas-renderer'\nimport { mergeMetadataOverParent, seoContextOf, seoMetadata } from './metadata'\nimport type { HostComponents } from './components'\nimport { canvasBeaconProps, canvasWrapContext, pathFromSlug, resolveCanvasPage } from './route-core'\nimport type {\n CanvasBeaconProps,\n CanvasPageOutcome,\n CanvasRenderOutcome,\n CanvasRequestInfo,\n CanvasWrapContext,\n} from './route-core'\nimport type { ContentSource } from './source'\n\n/**\n * Catch-all route helper (EC-144): the factory a host app calls once in\n * `app/[[...slug]]/page.tsx` to wire `resolvePage` → `<CanvasRenderer />`,\n * with redirects answered by Next's `redirect()`/`permanentRedirect()` (per\n * the route's `permanent` flag), unknown URLs answered by `notFound()` (the\n * host's 404 renders), and the perspective chosen by Next `draftMode()` —\n * draft when enabled (via the token-gated preview route), published\n * otherwise.\n *\n * **Caching is the host's (EC-620).** The route resolves and renders with no cache\n * scope of its own — it holds no `'use cache'` function and sets no cache life, so a\n * host needs no Next flag for it. A host that caches gives the route its own\n * `'use cache'` function as {@link CanvasRouteOptions.published}: the route hands it\n * the request's {@link CanvasCacheKey}; the function calls {@link CanvasRoute.resolve},\n * declares the result's tags with {@link CanvasRoute.applyCacheTags} (the fetch-time\n * tag set of what the response actually contained) and picks its own `cacheLife`.\n * Publishing in the studio then invalidates exactly the affected entries through the\n * signed `/api/revalidate` webhook — the receiver derives the same scoped tag strings\n * — on Vercel and on a self-hosted `next start` alike; while the content backend is\n * unreachable a cached entry keeps serving, because a hit never invokes `loadContent`.\n * The scaffold's page ships exactly that as its default (tag-only, no time limit).\n * Without `published`, every request resolves fresh — dynamic, uncached, and up to\n * date by construction.\n *\n * ```tsx\n * import { cacheLife } from 'next/cache'\n * import type { CanvasCacheKey, CanvasPageOutcome } from '@elytracms/next'\n *\n * const route = elytra.canvasRoute({ published: cachedPage })\n *\n * async function cachedPage(key: CanvasCacheKey): Promise<CanvasPageOutcome> {\n * 'use cache'\n * const outcome = await route.resolve(key)\n * route.applyCacheTags(outcome) // a publish drops exactly the pages that read it\n * cacheLife({ revalidate: Infinity, expire: Infinity }) // tag-only — your choice\n * return outcome\n * }\n *\n * export default route.Page\n * export const generateMetadata = route.generateMetadata\n * ```\n *\n * Whatever the page chooses, three properties stay in the route, outside any cache\n * scope: draft mode never reaches `published` (it is per-editor and request-time);\n * `notFound()` / `redirect()` answer from the outcome, so a 404 stays HTTP 404 and a\n * redirect 307/308; and `Page` and `generateMetadata` call `published` with the SAME\n * key, so they read one entry.\n */\n\n// EC-524: both context shapes and the wrap-context derivation live in the PURE\n// half (`route-core.ts`) — no Next request APIs are involved in either, and the\n// derivation earns a unit test there. Re-exported here because this module is\n// the published API surface hosts import from.\nexport type { CanvasBeaconProps, CanvasRenderOutcome, CanvasRequestInfo, CanvasWrapContext } from './route-core'\n\n/**\n * What the route helper tells the content loader about the request it is\n * loading for (EC-156). Loaders that serve both perspectives from one\n * prefetched source (the fixture snapshot) can ignore it; a live loader uses\n * it to fetch the draft-perspective snapshot only for draft-mode requests —\n * it must NOT call request APIs like `draftMode()` itself, because on a cached\n * published path the loader runs inside the host's `'use cache'` function, where\n * request APIs are unavailable.\n */\nexport interface CanvasContentRequest {\n perspective: Perspective\n}\n\nexport interface CanvasRouteOptions {\n /**\n * Async content loader — the seam EC-145/146 plug Convex into. For the\n * fixture-backed example this memoizes a `createContentSnapshot` call.\n * Invoked per request; cache/memoize inside the loader as appropriate.\n * Behind a host cache function ({@link CanvasRouteOptions.published}) it runs\n * only on a cache miss — cache hits never touch the content backend.\n * Receives the request's perspective (see {@link CanvasContentRequest});\n * zero-arg loaders remain valid.\n */\n loadContent: (request: CanvasContentRequest) => Promise<ContentSource>\n /** Host component surface from `defineHostComponents`. */\n components: HostComponents\n /**\n * The tag scope of everything this route resolves (`p:<scope>:…`) — what\n * {@link CanvasRoute.applyCacheTags} scopes with and what every\n * {@link CanvasCacheKey} carries. It must be the SAME scope the revalidate receiver\n * derives its tags with (`createRevalidateRoute`'s `scope`); the delivery kit hands\n * its one `contentScope` to both (EC-579) — a disagreement would drop nothing,\n * silently.\n */\n scope?: string\n /**\n * Which content world `loadContent` reads — the delivery kit passes its\n * `contentMode` (`'live' | 'fallback' | 'empty'`). It rides in every\n * {@link CanvasCacheKey}, so an entry written in one world can never be served in\n * another (EC-543). A host driving this helper directly with one world may omit it.\n */\n mode?: string\n /**\n * How a PUBLISHED request gets its outcome — the host's cache seam (EC-620). Pass\n * your own `'use cache'` function: it receives the request's {@link CanvasCacheKey}\n * (keep that its only argument — it is the entry's key), calls\n * {@link CanvasRoute.resolve} with it, declares the result's tags with\n * {@link CanvasRoute.applyCacheTags}, picks its `cacheLife`, and returns the\n * outcome (module example above; the scaffold's page ships it as the default).\n *\n * Omit it and every published request resolves fresh — dynamic and uncached. Draft\n * mode never calls it. `Page` and `generateMetadata` call it with the same key.\n */\n published?: (key: CanvasCacheKey) => Promise<CanvasPageOutcome>\n /**\n * EC-146 hook: receives the merged `CacheTags` of everything this request\n * read (the `resolvePage` tags plus the baked relations/listings/links). On a\n * cache hit (through a host {@link CanvasRouteOptions.published} function) these\n * are the stored tags of the run that wrote the entry.\n */\n onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void\n /** Override the generated `<head>` metadata for a resolved page. */\n metadata?: (result: ResolvePageResult, info: CanvasRequestInfo) => Metadata\n /**\n * Wrap the rendered page in the host's own chrome (EC-524) — a detail\n * template, a per-collection layout, a draft ribbon. Receives the rendered\n * canvas as `children` and returns whatever should stand in its place:\n *\n * ```tsx\n * elytra.canvasRoute({\n * wrap: (children, ctx) =>\n * ctx.collection === 'post' ? <PostDetail document={ctx.document}>{children}</PostDetail> : children,\n * })\n * ```\n *\n * **This is deliberately host code, not config** (owner ruling 2026-08-01):\n * *\"this sounds very much like page-templates … templates belong in code, so\n * the wrapper is something the developer has to care about … the page-content\n * (Canvas or Rich-Text) should render agnostically.\"* A collection's\n * `presentation.body` still names WHICH field is the content — that is content\n * selection, and it is what lets one catch-all render a canvas page and a\n * rich-text post through the same call. What surrounds that content is the\n * developer's implementation decision, branched here in their own file.\n *\n * It replaced a `presentation.wrapper` component-id STRING resolved from the\n * canvas registry, which forced a delivery-only component to register as a\n * pseudo-block (EC-514). A function beats that on every axis that matters: it\n * cannot dangle, the compiler checks it, and it can branch on anything in\n * {@link CanvasWrapContext} rather than on collection identity alone.\n *\n * Applied to the `render` outcome only — a redirect or a 404 never reaches it.\n * Returning `children` unchanged is the no-op, and omitting `wrap` entirely is\n * the default.\n */\n wrap?: (children: ReactNode, ctx: CanvasWrapContext) => ReactNode\n /**\n * The ANALYTICS slot (EC-618): a component the route renders inside every `render`\n * outcome, AFTER the page (and outside `wrap`, so a wrapper cannot drop it), with the\n * resolved document, the locale, the path and the status ({@link CanvasBeaconProps}).\n * Published perspective only — a draft preview is an editor at work, not a visit.\n *\n * The engine supplies the slot and nothing else: its value is the beacon of the\n * analytics addon, `@elytracms/analytics/next` — **unpublished, pending counsel's\n * verdict on its consent-free claim** (EC-618) — or a host's own component. Absent, the\n * route renders exactly what it rendered before, and sends nothing.\n *\n * A not-found page never reaches this slot (`notFound()` renders the host's\n * `not-found.tsx`), so a 404 beacon lives in that file.\n */\n analytics?: ComponentType<CanvasBeaconProps>\n}\n\nexport interface CanvasRouteProps {\n params: Promise<{ slug?: string[] }>\n}\n\nexport interface CanvasRoute {\n /** The page component: `export default route.Page`. */\n Page: (props: CanvasRouteProps) => Promise<ReactNode>\n /**\n * Next metadata hook: `export const generateMetadata = route.generateMetadata`.\n * Next passes the parent segment's resolving metadata as the second argument;\n * the route merges the page's tags over it (EC-610). A caller that invokes this\n * by hand (the scaffold's `app/page.tsx`) forwards its own `parent`.\n *\n * `parent` is REQUIRED, not optional (EC-614): Next's webpack build validates a\n * page's `generateMetadata` with `SecondArg<…> extends ResolvingMetadata`, and an\n * optional parameter reads as `ResolvingMetadata | undefined`, which fails that\n * constraint — a consumer re-exporting this hook could not `next build --webpack`\n * on 0.0.88 (the Turbopack validator is lenient, which is how the gate missed it).\n */\n generateMetadata: (props: CanvasRouteProps, parent: ResolvingMetadata) => Promise<Metadata>\n /**\n * Resolve one PUBLISHED request — with no cache scope of its own (EC-620). Call it\n * from the `'use cache'` function you pass as {@link CanvasRouteOptions.published},\n * with the key the route handed that function. A key of another scope or content\n * mode (a cache function shared with a route of another world) is refused loudly:\n * the entry would be keyed under one world and resolved in another (EC-543).\n *\n * The outcome carries everything a cache decision needs: `tags` (what the\n * resolution read — hand them to {@link applyCacheTags}) and, on a `render`\n * outcome, `readsNow` (a listing on the page compared against the clock, so the\n * page changes as time passes — bound that entry's life).\n */\n resolve: (key: CanvasCacheKey) => Promise<CanvasPageOutcome>\n /**\n * Declare a result's tags on the CURRENT `'use cache'` entry, scoped with this\n * route's scope — exactly the strings the revalidate receiver revalidates (EC-620).\n * Any accessor result works (`{ tags }`): a resolution from {@link resolve}, or a\n * `listDocuments` result in a route of your own.\n *\n * It makes the {@link entryTagCalls} calls: the project sweep first and alone, the\n * rest in chunks of at most 128. Next keeps 128 tags per `cacheTag()` call and drops\n * the rest with only a warning, and the sweep sorts last — one `cacheTag(...tags)`\n * call loses it on a page that read ~125 documents, and with it every redirect and\n * asset invalidation (measured, EC-515/EC-620).\n *\n * Callable ONLY inside a `'use cache'` function: anywhere else Next's own `cacheTag`\n * throws (E819 during a request or a prerender; E886 when neither `cacheComponents`\n * nor `experimental.useCache` is enabled) — a loud error, never a silent no-op.\n */\n applyCacheTags: (result: { tags: CacheTags }) => void\n /**\n * EC-722 — resolve one URL for a route of YOUR OWN and settle its status, before any\n * `<Suspense>` boundary: `const outcome = await route.load('/blog/' + slug)`.\n *\n * It does what {@link Page} does before rendering, and nothing else: reads the request's\n * perspective, resolves a published request through your\n * {@link CanvasRouteOptions.published} cache function with the SAME key `Page` hands it\n * (one entry; a draft resolves fresh), and answers a redirect with\n * `redirect()`/`permanentRedirect()` and a missing document with `notFound()` itself —\n * so what comes back is always a page to render.\n *\n * **Call it outside every `<Suspense>` boundary** (the route's page, not a suspended\n * child). Inside one, the response status is already committed when the call settles:\n * measured, a hand-written route that resolved and called `notFound()` inside\n * `<Suspense>` answered a missing document with HTTP 200 (EC-567 V11). Outside, under\n * Cache Components, it is legal because a published request is a cache read through\n * `published` — the reason the catch-all page can stand outside a boundary too. Without\n * `published`, a live-mode read outside `<Suspense>` fails `next build` under Cache\n * Components (the same rule `Page` lives by); without Cache Components it is a plain\n * dynamic read.\n *\n * Your route's own `generateMetadata` can delegate to {@link generateMetadata} with the\n * catch-all slug of the same URL; `load` fires no `onTags` (the route's `Page` does).\n */\n load: (url: string) => Promise<CanvasRenderOutcome>\n /**\n * `export const generateStaticParams = route.generateStaticParams` — needed in the\n * catch-all page file only when the HOST enables `cacheComponents` (that flag's\n * rule, not the kit's; measured on Next 16.2.10, EC-515): without it, awaiting the\n * catch-all's `params` fails `next build` (\"Uncached data was accessed outside of\n * <Suspense>\"). Wrapping the page in `<Suspense>` (or a root `loading.tsx`) instead\n * also builds, but answers every 404 and every redirect with HTTP 200 — the status\n * is committed before the content resolves. Declaring params keeps the page\n * blocking, so `notFound()` is a real 404 and a redirect a real 308/307. A host\n * without `cacheComponents` needs none of it.\n *\n * It names ONE param set: {@link CANVAS_VALIDATION_SLUG}, a sample that Next\n * prerenders to validate the route and that resolves to not-found without\n * reading content (the build never touches the backend for it). It fits both\n * catch-all shapes (`[[...slug]]` and the scaffold's required `[...slug]`, which\n * rejects an empty slug), and it is never empty (Next rejects an empty list).\n * Every real URL renders on its first request and is cached from then on.\n */\n generateStaticParams: () => Promise<Array<{ slug: string[] }>>\n}\n\n/**\n * The catch-all's build-time validation sample (see\n * {@link CanvasRoute.generateStaticParams}). The path is reserved: the canvas\n * route answers it with not-found and never resolves it against content.\n */\nexport const CANVAS_VALIDATION_SLUG: readonly string[] = ['_elytra']\nconst CANVAS_VALIDATION_PATH = pathFromSlug([...CANVAS_VALIDATION_SLUG])\n\n/**\n * Default metadata (EC-171): the page's canonical SEO fields — title,\n * description, OG tags, robots noindex — with document-title / page-name\n * fallbacks. `export const generateMetadata = route.generateMetadata` is the\n * one line a host needs; a custom `metadata` option replaces this builder.\n * Either way the result is merged over the layout's resolved metadata (EC-610).\n */\nfunction defaultMetadata(result: ResolvePageResult, assets: readonly ResolvedAsset[]): Metadata {\n return seoMetadata(result.document, seoContextOf(result, assets))\n}\n\n/**\n * THE edge mapping of Next's draft mode onto the delivery perspective (EC-595 binding\n * point 1): draft mode reads the draft variant, everything else the published snapshot.\n * The kit's other request-time read (`pageOpensFullBleed`) imports this one, and the\n * package exports it for a host's own server components, so the mapping is declared\n * once. A request API: never call it inside a cache scope.\n */\nexport async function requestPerspective(): Promise<Perspective> {\n const { isEnabled } = await draftMode()\n return isEnabled ? DRAFT_PERSPECTIVE : 'published'\n}\n\n/**\n * Create the canvas route for `app/[[...slug]]/page.tsx` — `{ Page, generateMetadata,\n * generateStaticParams }` to export, plus `resolve` / `applyCacheTags` for the host's\n * own cache function ({@link CanvasRouteOptions.published}) and `load` for a route of the\n * host's own (EC-722 — {@link CanvasRoute.load}).\n */\nexport function createCanvasRoute(options: CanvasRouteOptions): CanvasRoute {\n // A `published` key that is PRESENT but undefined is the hoisting trap, measured\n // (EC-620): Next's `'use cache'` transform compiles `async function cachedPage() {\n // 'use cache' … }` into a `var` assigned in place, so a cache function declared BELOW\n // the `canvasRoute({ published: cachedPage })` call is still `undefined` when the call\n // runs — and the page would serve uncached forever, silently (under\n // `cacheComponents`, as runtime 500s). Omitting the key is the way to opt out.\n if (Object.hasOwn(options, 'published') && options.published === undefined) {\n throw new Error(\n 'Elytra canvas route: `published` was passed but is undefined. If it names your cache function ' +\n '(the one carrying the cache directive), declare that function ABOVE the canvasRoute() call — ' +\n 'Next compiles a cached function into a variable assigned where it is written, so it is not ' +\n 'hoisted like a plain function. To render without a cache, omit `published` entirely.',\n )\n }\n\n const resolveFresh = async (url: string, perspective: Perspective): Promise<CanvasPageOutcome> => {\n const source = await options.loadContent({ perspective })\n return resolveCanvasPage(source, url, perspective, {\n // EC-205: page-scope the baked assets to what the page actually uses.\n registry: options.components.registry,\n })\n }\n\n // The world this route resolves in: what every key it hands out carries and what\n // every key it accepts must carry.\n const scope = options.scope ?? ''\n const mode = options.mode ?? ''\n const keyFor = (url: string): CanvasCacheKey => canvasCacheKey({ scope, mode, url })\n\n const resolve = async (key: CanvasCacheKey): Promise<CanvasPageOutcome> => {\n const parts = parseCanvasCacheKey(key)\n if (!parts) {\n throw new Error(\n `Elytra canvas route: resolve() was handed ${JSON.stringify(key)}, which is not a key this route ` +\n `produced. Pass your cache function's argument through unchanged — it is the entry's key.`,\n )\n }\n if (parts.scope !== scope || parts.mode !== mode) {\n throw new Error(\n `Elytra canvas route: resolve() was handed a cache key of scope \"${parts.scope}\" and content mode ` +\n `\"${parts.mode}\", but this route resolves in scope \"${scope}\" and mode \"${mode}\". The entry would ` +\n `be keyed under one world and filled from another (EC-543). Pass each route's \\`published\\` ` +\n `function the key that route hands it, and call that same route's resolve().`,\n )\n }\n return resolveFresh(parts.url, 'published')\n }\n\n const applyCacheTags = (result: { tags: CacheTags }): void => {\n for (const tags of entryTagCalls(result.tags, scope)) cacheTag(...tags)\n }\n\n /**\n * Resolve one request — through the host's cache function when it gave one.\n * Shared by `Page`, `load` and `generateMetadata`, so all call it with the same key\n * and read (and on a miss, write) one entry.\n */\n const loadOutcome = async (url: string, perspective: Perspective): Promise<CanvasPageOutcome> => {\n // Draft mode never reaches the host's cache: draft content is per-editor,\n // changes on every keystroke, and must never be persisted into (or served\n // from) the shared published-data cache. (`draftMode()` is a request API and\n // could not run inside a cache scope anyway; Next would not persist a\n // `'use cache'` entry under draft mode either.)\n if (perspective !== 'published' || !options.published) return resolveFresh(url, perspective)\n return options.published(keyFor(url))\n }\n\n /**\n * THE status decision (EC-722), read by `Page` and `load` alike: a redirect and a\n * not-found are answered here — Next's `redirect()` / `notFound()` throw — so only a\n * page to render comes back. Outside every `<Suspense>` boundary that sets the status.\n */\n const settle = (outcome: CanvasPageOutcome): CanvasRenderOutcome => {\n switch (outcome.kind) {\n case 'redirect':\n // 301 vs 302 per the redirect record's `permanent` flag (EC-019/020).\n return outcome.permanent ? permanentRedirect(outcome.target) : redirect(outcome.target)\n case 'not-found':\n return notFound()\n case 'render':\n return outcome\n }\n }\n\n const load = async (url: string): Promise<CanvasRenderOutcome> => {\n // The reserved validation sample is answered as on the catch-all: never read.\n if (url === CANVAS_VALIDATION_PATH) return notFound()\n const perspective = await requestPerspective()\n return settle(await loadOutcome(url, perspective))\n }\n\n const Page = async (props: CanvasRouteProps): Promise<ReactNode> => {\n const { slug } = await props.params\n const url = pathFromSlug(slug)\n if (url === CANVAS_VALIDATION_PATH) return notFound()\n const perspective = await requestPerspective()\n\n const loaded = await loadOutcome(url, perspective)\n options.onTags?.(loaded.tags, { url, perspective })\n const outcome = settle(loaded)\n\n const rendered = (\n <CanvasRenderer\n result={outcome.result}\n components={options.components}\n assets={outcome.assets}\n relations={outcome.relations}\n listings={outcome.listings}\n links={outcome.links}\n // EC-499: the resolved collection's declared body, so this ONE\n // catch-all renders a composition page and a rich-text post detail\n // alike — no per-collection Next file route needed.\n presentation={outcome.presentation}\n />\n )\n // EC-524: the host's own chrome goes AROUND the renderer, never inside\n // it. `children` is the (async) canvas element — React composes it in\n // place, so a wrapper may be a server or a client component.\n const page = options.wrap ? options.wrap(rendered, canvasWrapContext(outcome.result, { url, perspective })) : rendered\n // EC-618: the analytics slot — after the page, outside `wrap`, published only.\n const Beacon = options.analytics\n if (!Beacon || perspective !== 'published') return page\n return (\n <>\n {page}\n <Beacon {...canvasBeaconProps(outcome.result, { url, perspective })} />\n </>\n )\n }\n\n const pageMetadata = async (props: CanvasRouteProps): Promise<Metadata> => {\n const { slug } = await props.params\n const url = pathFromSlug(slug)\n if (url === CANVAS_VALIDATION_PATH) return {}\n const perspective = await requestPerspective()\n\n // Through the host's cache: shares `Page`'s entry (same function, same key), so\n // metadata is a cache read, not a second resolution. Uncached: resolve without\n // the payload factory — metadata only needs the resolution itself, and side\n // effects (addTags/onTags) must fire exactly once per request, in Page.\n if (options.published && perspective === 'published') {\n const outcome = await loadOutcome(url, perspective)\n if (outcome.kind !== 'render') return {}\n return options.metadata\n ? options.metadata(outcome.result, { url, perspective })\n : defaultMetadata(outcome.result, outcome.assets)\n }\n\n const source = await options.loadContent({ perspective })\n const outcome = await resolveCanvasPage(source, url, perspective)\n if (outcome.kind !== 'render') return {}\n return options.metadata\n ? options.metadata(outcome.result, { url, perspective })\n : defaultMetadata(outcome.result, outcome.assets)\n }\n\n // EC-610: the page's tags over the site's. `parent` is the layout chain's\n // resolved metadata, which Next otherwise merges only one key deep — a\n // page-level `openGraph` would erase the layout's `type`, `siteName`, site\n // image and `locale` on every canvas page.\n const generateMetadata = async (\n props: CanvasRouteProps,\n parent: ResolvingMetadata,\n ): Promise<Metadata> => {\n const [page, resolvedParent] = await Promise.all([pageMetadata(props), parent])\n return mergeMetadataOverParent(resolvedParent, page)\n }\n\n const generateStaticParams = async (): Promise<Array<{ slug: string[] }>> => [\n { slug: [...CANVAS_VALIDATION_SLUG] },\n ]\n\n return { Page, generateMetadata, resolve, applyCacheTags, load, generateStaticParams }\n}\n","import type { MetadataRoute } from 'next'\nimport { SEO_SUBFIELDS, splitPath } from '@elytracms/core/cms-core'\nimport type { Locale, RouteRecord } from '@elytracms/core/cms-core'\nimport { COMPOSITE_KEY_SEPARATOR } from '@elytracms/core/project-graph'\nimport type { ContentClient } from '@elytracms/core/content'\nimport { localeUrlPath } from './locale-url'\nimport { seoObjectOf } from './metadata'\nimport type { ContentSource } from './source'\n\n/**\n * `sitemap.xml` from published routes (EC-171, reshaped by EC-590): the URL of every\n * published document of every collection a route names, per locale — Next's\n * `app/sitemap.ts` convention. Documents marked `noindex` in their SEO object, and URLs\n * that do not resolve, are omitted.\n *\n * **The router enumerates, no host provider (EC-590; EC-565 Round 3, 3e).** A route record\n * names the collection it serves (`document.collection`), so the router's `urlsFor` —\n * the same reverse direction the studio's live link and every rendered link read —\n * yields one URL per published document: template routes (`/blog/:slug`), hierarchy\n * mounts (`/:path*`, composed through the parent and slug roles) and literal-id routes\n * alike. The former `SitemapParamsProvider` existed because \"a route record does not\n * declare which collection feeds its params\" — false for every document-bound route\n * (F6c RESTATED). A document-less static route (`/contact`, no `document`) is listed by\n * its pattern.\n *\n * **Enumeration is a candidate list, not the output (EC-486).** Every candidate is put\n * back through `resolvePage`, and a path the resolver refuses is dropped — so the sitemap\n * cannot claim a URL the delivery host would 404. What was enumerated and then refused is\n * reported in {@link CanvasSitemapResult.skipped}, never dropped in silence. `lastModified`\n * is omitted — the delivery shape carries no per-page timestamps today.\n *\n * Host usage (`app/sitemap.ts`):\n *\n * ```ts\n * import { createCanvasSitemap } from '@elytracms/next'\n * import { elytra } from '../lib/elytra'\n *\n * export default createCanvasSitemap({ loadContent: elytra.loadContent, baseUrl: elytra.frontendUrl! })\n * ```\n */\n\nexport interface CanvasSitemapOptions {\n /** Absolute site origin, e.g. `https://example.com` (no trailing slash needed). */\n baseUrl: string\n}\n\nexport interface SitemapSkippedRoute {\n routeId: string\n pattern: string\n reason: string\n}\n\nexport interface CanvasSitemapResult {\n entries: MetadataRoute.Sitemap\n /** Routes whose candidates could not be published — explicit, never silent. */\n skipped: SitemapSkippedRoute[]\n}\n\n/** The origin half; the locale ⇄ path half is the router's (`localeUrlPath`, EC-486). */\nfunction originOf(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, '')\n}\n\n/** A candidate URL: what `resolvePage` is asked, what a crawler requests, and its group. */\ninterface Candidate {\n route: RouteRecord\n locale: Locale\n routePath: string\n path: string\n addressable: boolean\n /** Locale-invariant identity for `hreflang` grouping (EC-517). */\n group: string\n}\n\n/**\n * Enumerate the sitemap entries of one content source (published perspective). Pure over\n * the prefetched source — synchronous accessors, deterministic ordering (sorted by URL).\n */\nexport function canvasSitemapEntries(\n source: ContentSource,\n options: CanvasSitemapOptions,\n): CanvasSitemapResult {\n const routes = source.routes ?? []\n const locales = source.locales\n const skipped: SitemapSkippedRoute[] = []\n const skip = (route: RouteRecord, reason: string): void => {\n if (!skipped.some((s) => s.routeId === route.id && s.reason === reason)) {\n skipped.push({ routeId: route.id, pattern: route.pattern, reason })\n }\n }\n\n const clients = new Map<Locale, ContentClient>()\n const clientFor = (locale: Locale): ContentClient => {\n let client = clients.get(locale)\n if (!client) {\n client = source.client({ perspective: 'published', locale })\n clients.set(locale, client)\n }\n return client\n }\n\n // --- Candidates -------------------------------------------------------------------\n const candidates: Candidate[] = []\n // Every collection a route names, in the locales its routes serve (an agnostic route\n // serves every locale; locale-scoped routes only theirs).\n const collectionLocales = new Map<string, Set<Locale>>()\n for (const route of routes) {\n if (!route.document) continue\n const set = collectionLocales.get(route.document.collection) ?? new Set<Locale>()\n for (const locale of route.locale ? [route.locale] : locales.locales) set.add(locale)\n collectionLocales.set(route.document.collection, set)\n }\n for (const [collection, routeLocales] of collectionLocales) {\n for (const locale of locales.locales) {\n if (!routeLocales.has(locale)) continue\n for (const entry of clientFor(locale).urlsFor(collection)) {\n candidates.push({\n route: entry.route,\n locale,\n routePath: entry.routePath,\n path: entry.path,\n addressable: entry.addressable,\n // EC-517: group by the DOCUMENT — the locale-invariant fact — scoped by route.\n group: `${entry.route.id}${COMPOSITE_KEY_SEPARATOR}doc${COMPOSITE_KEY_SEPARATOR}${collection}${COMPOSITE_KEY_SEPARATOR}${entry.id}`,\n })\n }\n }\n }\n // A document-less static route is its own URL.\n for (const route of routes) {\n if (route.document || splitPath(route.pattern).some((segment) => segment.startsWith(':'))) continue\n for (const locale of route.locale ? [route.locale] : locales.locales) {\n const { urlPath, addressable } = localeUrlPath(route.pattern, locale, locales)\n candidates.push({\n route,\n locale,\n routePath: route.pattern,\n path: urlPath,\n addressable,\n group: `${route.id}${COMPOSITE_KEY_SEPARATOR}path${COMPOSITE_KEY_SEPARATOR}${route.pattern}`,\n })\n }\n }\n\n // --- The gate: the resolver serves it, it is indexable, a crawler can reach it ---------\n const byUrl = new Map<string, { group: string; locale: Locale }>()\n const byGroup = new Map<string, Map<Locale, string>>()\n // EC-486: per (route, locale), how many enumerated paths the RESOLVER refused — a mount\n // that composed paths but serves none is a dead declaration and says so.\n const tally = new Map<string, { route: RouteRecord; locale: Locale; total: number; unresolved: number }>()\n\n for (const candidate of candidates) {\n const key = `${candidate.route.id}${COMPOSITE_KEY_SEPARATOR}${candidate.locale}`\n const count = tally.get(key) ?? { route: candidate.route, locale: candidate.locale, total: 0, unresolved: 0 }\n count.total += 1\n tally.set(key, count)\n\n const result = clientFor(candidate.locale).resolvePage(candidate.routePath, candidate.locale)\n if (result.status !== 'ok') {\n // A `redirect` is a deliberate authoring act, not a dead route.\n if (result.status !== 'redirect') count.unresolved += 1\n continue\n }\n if (seoObjectOf(result.document, result.seoField)[SEO_SUBFIELDS.noindex] === true) continue\n // EC-486: the URL a crawler will actually request. For a path whose first segment is\n // another locale's code the host derives a different locale/path pair from it, so no\n // URL addresses this page — publishing one would submit a guaranteed 404.\n if (!candidate.addressable) {\n skip(\n candidate.route,\n `\"${candidate.routePath}\" (locale \"${candidate.locale}\") has no addressable URL — its first segment is read as a locale prefix`,\n )\n continue\n }\n const absolute = originOf(options.baseUrl) + candidate.path\n if (!byUrl.has(absolute)) byUrl.set(absolute, { group: candidate.group, locale: candidate.locale })\n const perLocale = byGroup.get(candidate.group) ?? new Map<Locale, string>()\n if (!perLocale.has(candidate.locale)) perLocale.set(candidate.locale, absolute)\n byGroup.set(candidate.group, perLocale)\n }\n\n for (const { route, locale, total, unresolved } of tally.values()) {\n if (total > 0 && unresolved === total && route.document && splitPath(route.pattern).at(-1)?.endsWith('*')) {\n skip(\n route,\n `hierarchy mount over \"${route.document.collection}\" composed ${total} path(s) in locale \"${locale}\", none of which the resolver serves`,\n )\n }\n }\n\n const entries: MetadataRoute.Sitemap = []\n for (const [url, { group }] of byUrl) {\n const perLocale = byGroup.get(group)\n // A single-locale group is a page that exists in one language; `hreflang` between\n // one URL and itself says nothing, so it stays off.\n const languages = perLocale && perLocale.size > 1 ? Object.fromEntries(perLocale) : undefined\n entries.push({ url, ...(languages ? { alternates: { languages } } : {}) })\n }\n entries.sort((a, b) => (a.url < b.url ? -1 : a.url > b.url ? 1 : 0))\n\n return { entries, skipped }\n}\n\nexport interface CreateCanvasSitemapOptions extends CanvasSitemapOptions {\n /** The same content loader the catch-all route uses (published perspective). */\n loadContent: (request: { perspective: 'published' }) => Promise<ContentSource>\n}\n\n/**\n * The `app/sitemap.ts` one-liner: `export default createCanvasSitemap({ … })`.\n * Loads the published content source and returns the enumerated entries\n * (skipped routes are dropped here — use {@link canvasSitemapEntries} to\n * inspect them).\n */\nexport function createCanvasSitemap(\n options: CreateCanvasSitemapOptions,\n): () => Promise<MetadataRoute.Sitemap> {\n return async () => {\n const source = await options.loadContent({ perspective: 'published' })\n return canvasSitemapEntries(source, options).entries\n }\n}\n","import { readRevalidationBody, verifyRevalidationSignature } from '@elytracms/core/revalidation'\nimport type { RevalidationEvent } from '@elytracms/core/revalidation'\n\n/**\n * Pure request logic of the revalidation webhook receiver (EC-146), kept free of\n * Next.js imports so it is unit-testable. `revalidate.ts` wires it into a route\n * handler with `revalidateTag`.\n *\n * The wire — event schema, signer, header — is NOT declared here: it is the one\n * module `@elytracms/core/revalidation`, shared with the emitter (the studio's\n * Convex write chokepoint) and the doctor probe (EC-579). This file only decides\n * the HTTP answer: auth before parsing, then the event.\n */\n\nexport type RevalidateEvaluation =\n | { ok: true; event: RevalidationEvent }\n | { ok: false; status: 400 | 401; message: string }\n\n/**\n * Evaluate one webhook request: verify the HMAC first (auth before parsing), then\n * read the event.\n *\n * - unconfigured (empty) secret → 401 for every request: the endpoint stays closed\n * until a secret is deliberately configured (mirrors EC-144 preview);\n * - missing/malformed/mismatched signature → 401 (constant-time verify);\n * - unparseable or schema-invalid body → 400, nothing revalidated; a body in the\n * pre-EC-579 tag-list shape gets its own named 400 (an older Elytra emitter —\n * deploy the matching studio functions), because a version skew between the\n * Convex deployment and this host is the one way a correctly signed body can\n * arrive in the wrong shape.\n */\nexport async function evaluateRevalidateRequest(\n rawBody: string,\n signature: string | null | undefined,\n secret: string | undefined,\n): Promise<RevalidateEvaluation> {\n if (secret === undefined || secret.length === 0) {\n return { ok: false, status: 401, message: 'Revalidation webhook is not configured.' }\n }\n if (typeof signature !== 'string' || signature.length === 0) {\n return { ok: false, status: 401, message: 'Missing or malformed webhook signature.' }\n }\n if (!(await verifyRevalidationSignature(rawBody, signature, secret))) {\n return { ok: false, status: 401, message: 'Invalid webhook signature.' }\n }\n const read = readRevalidationBody(rawBody)\n if (!read.ok) return { ok: false, status: 400, message: read.message }\n return { ok: true, event: read.event }\n}\n","import { revalidateTag } from 'next/cache'\nimport { REVALIDATE_SIGNATURE_HEADER } from '@elytracms/core/revalidation'\nimport { revalidationTags } from './cache'\nimport type { RevalidationProject } from './cache'\nimport { evaluateRevalidateRequest } from './revalidate-core'\n\n/**\n * Revalidation webhook route handler (EC-146): the receiving end of the\n * publish → live-in-seconds pipeline. The studio's Convex write chokepoint — the ONE\n * emitter (EC-579) — POSTs a signed EVENT (`{ kind, collection?, id? }`, the wire in\n * `@elytracms/core/revalidation`); this handler verifies the HMAC over the raw body,\n * derives the tags from the event with this host's OWN scope and locales\n * (`revalidationTags`, `cache.ts`) and calls `revalidateTag` for each, dropping the\n * cached entries whose fetch-time tag sets contained them.\n *\n * Server-to-server only: the emitter runs in Convex, so there is no CORS and no\n * `OPTIONS` preflight (the studio's browser dispatcher that needed them is deleted).\n * Server-only by construction — consumed from an App Router route handler file,\n * never from client bundles.\n */\n\nexport interface RevalidateRouteOptions extends RevalidationProject {\n /**\n * Shared HMAC secret. An empty/undefined secret rejects every request with\n * 401 — the endpoint stays closed until deliberately configured.\n */\n secret: string | undefined\n}\n\nexport interface RevalidateRouteHandlers {\n POST(request: Request): Promise<Response>\n}\n\n/**\n * Create the webhook route handler for `app/api/revalidate/route.ts`. Through the\n * delivery kit it is `export const { POST } = elytra.revalidateRoute()`, which hands\n * it the kit's own scope and locales — the ones its cached entries carry. Wiring it\n * by hand, pass the SAME scope your canvas route caches under:\n *\n * ```ts\n * export const { POST } = createRevalidateRoute({\n * secret: process.env.REVALIDATE_SECRET,\n * scope: process.env.ELYTRA_PROJECT_ID,\n * locales: ['en', 'de'],\n * })\n * ```\n *\n * Unsigned/invalid requests are rejected with 401 (constant-time HMAC verify, see\n * `evaluateRevalidateRequest`), signed-but-unreadable ones with 400; a valid event\n * revalidates its derived tags and answers `{ revalidated: tags }` (`[]` for the\n * doctor's `probe`).\n */\nexport function createRevalidateRoute(options: RevalidateRouteOptions): RevalidateRouteHandlers {\n const project: RevalidationProject = { scope: options.scope, locales: [...options.locales] }\n return {\n async POST(request: Request): Promise<Response> {\n const rawBody = await request.text()\n const signature = request.headers.get(REVALIDATE_SIGNATURE_HEADER)\n const evaluation = await evaluateRevalidateRequest(rawBody, signature, options.secret)\n if (!evaluation.ok) {\n return Response.json({ error: evaluation.message }, { status: evaluation.status })\n }\n const tags = revalidationTags(evaluation.event, project)\n // EC-515: `{ expire: 0 }`, NOT `'max'`. Next 16 requires a cache-life\n // profile as the second argument, and EC-219 reached for `'max'` on the\n // claim that it \"preserves the legacy drop-by-tag semantics\". **Measured\n // false on Next 16.2.10** — `'max'` is the *opposite* of a drop:\n //\n // `revalidateTag(tag, profile)` passes `{ expire: <profile>.expire }`\n // down to the cache handler. The `max` profile is `expire: 60*60*24*365`\n // (`server/config-shared.js`), so the default self-hosted handler stamps\n // `{ stale: now, expired: now + 365 days }` on the tag\n // (`FileSystemCache.revalidateTag`; the `'use cache'` handler stamps the\n // same way). `areTagsExpired` requires `expired <= now` — a year in the\n // future never qualifies — so the entry is only marked STALE: the next\n // visitor is served the old page while a background refresh runs. Under\n // the pre-EC-515 `unstable_cache` probe/bail two-pass that refresh could\n // never complete, so the stale value was served forever (measured).\n //\n // `{ expire: 0 }` stamps `expired: now`, which `areTagsExpired` treats as\n // an immediate hard drop — byte-for-byte the behaviour of the deprecated\n // one-argument form: the request after a publish is fresh. `CacheLifeConfig`\n // is a public part of the `revalidateTag` signature, so this is not a cast\n // or a back door. `updateTag` is still not an option — Server-Action-only,\n // throws here.\n //\n // Measured (self-hosted `next start`, Next 16.2.10): with `{ expire: 0 }`\n // every event's entries are fresh on the next request — the EC-515 case\n // table, `'use cache'` entries, Turbopack and webpack.\n for (const tag of tags) revalidateTag(tag, { expire: 0 })\n return Response.json({ revalidated: tags })\n },\n }\n}\n","import { INGEST_MAX_BODY_BYTES, readIngestPayload } from '@elytracms/core/ingest'\nimport type { IngestPayload } from '@elytracms/core/ingest'\n\n/**\n * EC-617 — the pure decision core of the PUBLIC INGEST route (`createIngestRoute`, the\n * Next wrapper in `ingest.ts`): no Next imports, no network, an injected clock — the\n * house \"pure core + thin wrapper\" shape of `evaluateRevalidateRequest` /\n * `createRevalidateRoute`.\n *\n * The route is a site's one public write path (ruling EC-565 § 6c). It runs, in order:\n *\n * 1. **method** — `POST` only (405);\n * 2. **configured** — the server-held `INGEST_SECRET` and the receiver URL are set (503\n * otherwise: the route stays closed until deliberately configured, never a no-op);\n * 3. **same-origin** — a browser request from another origin (`Origin` / `Sec-Fetch-Site`)\n * is refused (403). The route emits no CORS headers and reads no cookie;\n * 4. **rate limit** — a fixed window per CLIENT KEY (429, `Retry-After`);\n * 5. **size** — the raw body capped at `maxBodyBytes` (413), checked before parsing;\n * 6. **shape** — JSON in the ingest envelope (400, with the issues);\n * 7. **bot gate** — the host's own check, an injected seam (403, `{ gate: 'bot' }`): the\n * engine supplies the slot, never the vendor;\n * 8. **enrichment** (EC-618) — an optional seam an ADDON fills (the analytics addon derives\n * a country and a visitor's sketch coordinates here, where the request is): it returns the\n * payload to forward, which is re-validated against the envelope and the receiver's cap\n * (500 — the site's own fault — when it throws or returns something invalid). Absent, the\n * route is exactly what it was;\n *\n * then the wrapper signs the payload with the server-held secret and forwards it to the\n * one Convex receiver, relaying its answer. Only the PAYLOAD is forwarded — never a request\n * header — so whatever an enrichment reads from the request (the client address, the user\n * agent, a geo header) stays in this request's memory; only what it derives travels.\n */\n\n/** The answer the core gives the wrapper. */\nexport type IngestEvaluation =\n | { ok: true; payload: IngestPayload }\n | { ok: false; status: 400 | 403 | 405 | 413 | 429 | 500 | 503; body: Record<string, unknown>; headers?: Record<string, string> }\n\n/** What the host's bot gate answers (a Turnstile / hCaptcha / honeypot check the HOST implements). */\nexport type BotGateVerdict = { allow: true } | { allow: false; reason: string }\n\n/**\n * The bot-gate seam: called with the request and the parsed payload, AFTER the cheap\n * checks (so a flood never reaches a paid verification API). Absent → no gate.\n */\nexport type IngestBotGate = (input: { request: Request; payload: IngestPayload }) => BotGateVerdict | Promise<BotGateVerdict>\n\n/**\n * EC-618 — the enrichment seam: called with the request and the parsed payload LAST (after\n * the bot gate), it returns the payload the route signs and forwards. The engine supplies\n * the slot, never the code: an addon fills it (`@elytracms/analytics/next`'s\n * `analyticsEnrichment`), and a host may compose its own. Whatever it reads from the\n * request is held for this request only — the route forwards the returned payload and\n * nothing else. Absent → no enrichment, the payload forwarded as parsed.\n */\nexport type IngestEnrich = (input: { request: Request; payload: IngestPayload }) => IngestPayload | Promise<IngestPayload>\n\n/**\n * The fixed-window limiter — the AI route's pattern (EC-602), keyed per client. Holds,\n * in memory only, one `{ windowStart, count }` per key seen in the current window; expired\n * windows are dropped as the clock moves, so the map is bounded by the clients of one\n * window. Each server instance holds its own windows (a serverless platform runs several),\n * so the limit is per instance — the same documented limitation as the AI route.\n */\nexport interface FixedWindowLimiter {\n /** Count one request for `key`; `retryAfterSeconds` when it is over the limit. */\n hit(key: string): { allowed: true } | { allowed: false; retryAfterSeconds: number }\n /** How many keys the limiter currently holds (for the bound test). */\n size(): number\n}\n\nexport function createFixedWindowLimiter(options: {\n limit: number\n windowMs?: number\n now?: () => number\n}): FixedWindowLimiter {\n const windowMs = options.windowMs ?? 60_000\n const now = options.now ?? Date.now\n const windows = new Map<string, { windowStart: number; count: number }>()\n return {\n hit(key) {\n const at = now()\n // Drop every expired window first — the map never outlives the current window's clients.\n for (const [held, window] of windows) if (at - window.windowStart >= windowMs) windows.delete(held)\n const window = windows.get(key)\n if (!window) {\n windows.set(key, { windowStart: at, count: 1 })\n return { allowed: true }\n }\n if (window.count >= options.limit) {\n return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil((window.windowStart + windowMs - at) / 1000)) }\n }\n window.count += 1\n return { allowed: true }\n },\n size: () => windows.size,\n }\n}\n\n/**\n * The DEFAULT client key: the first hop of `x-forwarded-for` (what Vercel and most proxies\n * set), else `x-real-ip`, else one shared `unknown` bucket. An IP address is personal\n * data: it is held ONLY as a key of the in-memory window above, for at most one window,\n * and is never stored, logged or forwarded to the receiver. A host that wants a different\n * key (a hashed one, a session id) passes `clientKey`.\n */\nexport function defaultIngestClientKey(request: Request): string {\n const forwarded = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim()\n if (forwarded) return forwarded\n const real = request.headers.get('x-real-ip')?.trim()\n return real || 'unknown'\n}\n\n/** Is this a cross-origin BROWSER request? (A request with no browser headers is not judged here.) */\nexport function isCrossOrigin(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'none') return true\n const origin = request.headers.get('origin')\n if (!origin) return false\n try {\n return new URL(origin).origin !== new URL(request.url).origin\n } catch {\n return true\n }\n}\n\nexport interface IngestEvaluationInput {\n request: Request\n /** The raw body, read by the wrapper up to `maxBodyBytes + 1` bytes (`undefined` when over). */\n rawBody: string | undefined\n configured: boolean\n limiter: FixedWindowLimiter\n clientKey: (request: Request) => string\n botGate?: IngestBotGate\n enrich?: IngestEnrich\n}\n\n/** Evaluate one request (see the module header for the order). */\nexport async function evaluateIngestRequest(input: IngestEvaluationInput): Promise<IngestEvaluation> {\n const { request } = input\n if (request.method !== 'POST') {\n return { ok: false, status: 405, body: { error: 'Only POST is accepted.' }, headers: { allow: 'POST' } }\n }\n if (!input.configured) {\n return { ok: false, status: 503, body: { error: 'Ingest is not configured on this site.' } }\n }\n if (isCrossOrigin(request)) {\n return { ok: false, status: 403, body: { error: 'Cross-origin requests are not accepted.' } }\n }\n const limited = input.limiter.hit(input.clientKey(request))\n if (!limited.allowed) {\n return {\n ok: false,\n status: 429,\n body: { error: 'Too many requests — try again shortly.' },\n headers: { 'retry-after': String(limited.retryAfterSeconds) },\n }\n }\n if (input.rawBody === undefined) {\n return { ok: false, status: 413, body: { error: 'The payload is too large.' } }\n }\n const read = readIngestPayload(input.rawBody)\n if (!read.ok) {\n return { ok: false, status: 400, body: { error: read.message, ...(read.issues ? { issues: read.issues } : {}) } }\n }\n if (input.botGate) {\n const verdict = await input.botGate({ request, payload: read.value })\n if (!verdict.allow) {\n return { ok: false, status: 403, body: { error: 'Refused by the bot gate.', gate: 'bot', reason: verdict.reason } }\n }\n }\n if (!input.enrich) return { ok: true, payload: read.value }\n return enrichPayload(input.enrich, request, read.value)\n}\n\n/**\n * Run the enrichment and hold its result to the same rules as a visitor's payload: the\n * envelope, and the receiver's body cap. A failure is the SITE's (its own enrichment), so it\n * answers 500 naming the enrichment — never a visitor-facing 400.\n */\nasync function enrichPayload(enrich: IngestEnrich, request: Request, payload: IngestPayload): Promise<IngestEvaluation> {\n let enriched: IngestPayload\n try {\n enriched = await enrich({ request, payload })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return { ok: false, status: 500, body: { error: `The site's ingest enrichment failed: ${message}` } }\n }\n const body = JSON.stringify(enriched ?? null)\n const reread = readIngestPayload(body)\n if (!reread.ok) {\n return {\n ok: false,\n status: 500,\n body: { error: \"The site's ingest enrichment returned an invalid payload.\", ...(reread.issues ? { issues: reread.issues } : {}) },\n }\n }\n if (new TextEncoder().encode(body).byteLength > INGEST_MAX_BODY_BYTES) {\n return { ok: false, status: 500, body: { error: \"The site's ingest enrichment grew the payload past the receiver's cap.\" } }\n }\n return { ok: true, payload: reread.value }\n}\n\n/**\n * Read a request body as text, at most `maxBytes` (counted in UTF-8 bytes). `undefined`\n * when it is longer — the over-size body is never buffered whole. A declared\n * `Content-Length` over the cap is refused before a byte is read.\n */\nexport async function readCappedBody(request: Request, maxBytes: number = INGEST_MAX_BODY_BYTES): Promise<string | undefined> {\n const declared = Number(request.headers.get('content-length'))\n if (Number.isFinite(declared) && declared > maxBytes) return undefined\n if (!request.body) return ''\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n total += value.byteLength\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined)\n return undefined\n }\n chunks.push(value)\n }\n const bytes = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(bytes)\n}\n","import { INGEST_MAX_BODY_BYTES, INGEST_RECEIVER_PATH, signedIngestRequest } from '@elytracms/core/ingest'\nimport {\n createFixedWindowLimiter,\n defaultIngestClientKey,\n evaluateIngestRequest,\n readCappedBody,\n} from './ingest-core'\nimport type { IngestBotGate, IngestEnrich } from './ingest-core'\n\n/**\n * EC-617 — the PUBLIC INGEST route: a site's one write path into its project (form\n * submissions, analytics events, upload URLs), mounted SAME-ORIGIN in the site's own\n * App Router:\n *\n * ```ts\n * // frontend/app/api/ingest/route.ts — through the delivery kit\n * export const { POST } = elytra.ingestRoute()\n * ```\n *\n * The decisions are the pure core's (`evaluateIngestRequest`: method, configured,\n * same-origin, rate limit, size, shape, the host's bot gate); this wrapper then SIGNS the\n * payload with the server-held `INGEST_SECRET` (it never reaches a browser) and forwards it\n * to the deployment's ONE receiver (`<deployment>.convex.site/elytra/ingest`), relaying the\n * receiver's answer. A receiver that rejects the signature is a SITE misconfiguration\n * (the two secrets differ), answered 502 with that sentence — never a 401 a visitor could\n * read as their fault.\n *\n * Uploads leave same-origin by design: an `upload` payload asks the receiver for ONE\n * short-lived storage URL (the request rides this route's gate, limiter and bot gate), and\n * the browser then sends the bytes straight to that URL; the submission that follows claims\n * the stored file, which is checked against what the upload request declared.\n */\n\nexport interface IngestRouteOptions {\n /** The server-held HMAC secret (= the deployment's `ELYTRA_INGEST_SECRET`). Unset → 503. */\n secret: string | undefined\n /** The deployment's HTTP origin (`https://<name>.convex.site`); the receiver path is appended. Unset → 503. */\n siteUrl: string | undefined\n /** Cap on the raw JSON body, ≤ the receiver's `INGEST_MAX_BODY_BYTES` (default). */\n maxBodyBytes?: number\n /** Requests per client key per minute (default 30). */\n maxRequestsPerMinute?: number\n /** The rate limiter's key for a request (default: the client IP, in memory only — see `defaultIngestClientKey`). */\n clientKey?: (request: Request) => string\n /** The host's bot check (Turnstile, hCaptcha, a honeypot …). The engine supplies the slot, never the vendor. */\n botGate?: IngestBotGate\n /**\n * EC-618 — an addon's enrichment, run last: it returns the payload to sign and forward\n * (the analytics addon adds a country and the visitor's sketch coordinates, derived from\n * the request here and never forwarded raw). Absent → the payload is forwarded as parsed.\n */\n enrich?: IngestEnrich\n /** Injected clock (tests). */\n now?: () => number\n /** Injected transport (tests). */\n fetchImpl?: typeof fetch\n}\n\nexport interface IngestRouteHandlers {\n POST(request: Request): Promise<Response>\n}\n\nconst NO_STORE = { 'cache-control': 'no-store' }\n\n/** Create the ingest route handler (see the module header). */\nexport function createIngestRoute(options: IngestRouteOptions): IngestRouteHandlers {\n const maxBodyBytes = options.maxBodyBytes ?? INGEST_MAX_BODY_BYTES\n if (maxBodyBytes > INGEST_MAX_BODY_BYTES || maxBodyBytes <= 0) {\n // A larger body would only fail at the receiver — refuse the configuration at boot.\n throw new Error(`createIngestRoute: maxBodyBytes must be between 1 and ${INGEST_MAX_BODY_BYTES} (the receiver's cap).`)\n }\n const limiter = createFixedWindowLimiter({\n limit: options.maxRequestsPerMinute ?? 30,\n ...(options.now ? { now: options.now } : {}),\n })\n const clientKey = options.clientKey ?? defaultIngestClientKey\n const receiver = options.siteUrl ? `${options.siteUrl.replace(/\\/+$/, '')}${INGEST_RECEIVER_PATH}` : undefined\n const fetchImpl = options.fetchImpl ?? fetch\n\n return {\n async POST(request: Request): Promise<Response> {\n const rawBody = request.method === 'POST' ? await readCappedBody(request, maxBodyBytes) : ''\n const evaluation = await evaluateIngestRequest({\n request,\n rawBody,\n configured: options.secret !== undefined && options.secret.length > 0 && receiver !== undefined,\n limiter,\n clientKey,\n ...(options.botGate ? { botGate: options.botGate } : {}),\n ...(options.enrich ? { enrich: options.enrich } : {}),\n })\n if (!evaluation.ok) {\n return Response.json(evaluation.body, { status: evaluation.status, headers: { ...NO_STORE, ...evaluation.headers } })\n }\n const signed = await signedIngestRequest(evaluation.payload, options.secret as string)\n let answer: Response\n try {\n answer = await fetchImpl(receiver as string, { method: 'POST', headers: signed.headers, body: signed.body })\n } catch {\n return Response.json({ error: 'The ingest receiver could not be reached.' }, { status: 502, headers: NO_STORE })\n }\n if (answer.status === 401) {\n return Response.json(\n { error: \"The ingest receiver refused this site's signature — INGEST_SECRET must equal the deployment's ELYTRA_INGEST_SECRET.\" },\n { status: 502, headers: NO_STORE },\n )\n }\n const text = await answer.text()\n return new Response(text, {\n status: answer.status,\n headers: { ...NO_STORE, 'content-type': answer.headers.get('content-type') ?? 'application/json' },\n })\n },\n }\n}\n","import NextImage from 'next/image'\nimport type { ImageLoader } from 'next/image'\nimport type { ReactNode } from 'react'\nimport type { ResolvedAsset } from '@elytracms/core/content'\nimport { normalizeImageSource } from '@elytracms/components'\nimport { registerComponents } from '@elytracms/core/runtime-renderer'\nimport type { HostComponentEntry } from '@elytracms/core/studio-core'\n// TYPE-ONLY by design: importing `./host-image` at runtime would bundle a second\n// singleton instance into the server entry (see host-image.ts module docs).\nimport type { HostImageRenderer } from './host-image'\n\n/**\n * Image delivery for the embedded runtime (EC-152).\n *\n * ## The honest v1 design\n *\n * Stored asset bytes are served from plain blob-storage URLs (Convex file\n * storage serve URLs in production). Those URLs do **not** transform — there\n * is no `?w=`/`?q=` resizing service behind them, and no image CDN build-out\n * in v1. The standard, no-CDN path is therefore `next/image` with its\n * **default loader**: the host app's own Next.js optimizer fetches the remote\n * asset URL, resizes/re-encodes it, and serves the variants same-origin from\n * `/_next/image`. That is exactly what {@link ElytraImage} does.\n *\n * For that to work the host app must allowlist the asset origin in\n * `next.config.mjs`:\n *\n * ```js\n * const nextConfig = {\n * images: {\n * remotePatterns: [\n * // Convex file storage serve URLs:\n * { protocol: 'https', hostname: '*.convex.cloud' },\n * ],\n * },\n * }\n * ```\n *\n * {@link createAssetImageLoader} exists for the day the asset URLs sit behind\n * a CDN that *does* honor width/quality query params — it appends them and\n * nothing more. Pointing it at raw Convex serve URLs would silently serve\n * full-size bytes while pretending to resize, so it is **not** the default.\n */\n\n/**\n * The slice of the {@link ResolvedAsset} delivery shape an image needs:\n * the resolvable URL, intrinsic dimensions for layout stability, and alt\n * text. Structurally satisfied by every `ResolvedAsset`.\n */\nexport type ElytraImageAsset = Pick<\n ResolvedAsset,\n 'url' | 'width' | 'height' | 'alt' | 'focalPoint'\n> & {\n /**\n * LQIP blur-placeholder data URI (EC-420) → `next/image` `blurDataURL`. Optional\n * so existing call sites are unaffected; when present, the optimized image blurs\n * up while loading. `null`/absent = today's behaviour (no placeholder).\n */\n lqip?: string | null\n}\n\nexport interface ElytraImageProps {\n /** The resolved asset (delivery shape). `null` renders nothing. */\n asset: ElytraImageAsset | null | undefined\n /** `sizes` hint forwarded to `next/image` for responsive variants. */\n sizes?: string\n /** Preload hint forwarded to `next/image` (above-the-fold images). */\n priority?: boolean\n /** Quality (1–100) forwarded to `next/image`; Next defaults to 75. */\n quality?: number\n className?: string\n /**\n * Loading strategy forwarded to `next/image` (EC-428). `'eager'` opts an\n * image out of lazy-loading without the preload `priority` implies. Next throws\n * on `priority` + `loading='lazy'` together, so {@link ElytraImage} drops `lazy`\n * when `priority` is set (priority wins) — delivery never crashes.\n */\n loading?: 'eager' | 'lazy'\n /**\n * Custom `next/image` loader (e.g. from {@link createAssetImageLoader})\n * when assets sit behind a transforming CDN. Omit for the v1 default:\n * Next's own optimizer.\n */\n loader?: ImageLoader\n}\n\n/** LQIP raster long edge (EC-420). MUST equal studio `LQIP_MAX_EDGE` (thumbnail.ts). */\nconst LQIP_MAX_EDGE = 24\n\n/**\n * Recompute the EC-420 LQIP raster's pixel dimensions from the asset's intrinsic\n * size — the SAME `fitLongEdge` math the generator uses\n * (`packages/studio/src/lib/assets/thumbnail.ts:64-67`), duplicated here (3 lines)\n * because `packages/next` must NOT import studio internals (EC-428 AD-3). Fits the\n * long edge to {@link LQIP_MAX_EDGE}, rounds, floors each edge to ≥1px, and NEVER\n * upscales — so a ≤24px source keeps its own dims (the case the aspect-ratio\n * approximation gets wrong). Deterministic from `width`/`height`, non-null on the\n * NextImage path (the only place the wrap runs).\n */\nfunction fitLqipLongEdge(width: number, height: number): { width: number; height: number } {\n const scale = Math.min(1, LQIP_MAX_EDGE / Math.max(width, height))\n return { width: Math.max(1, Math.round(width * scale)), height: Math.max(1, Math.round(height * scale)) }\n}\n\n/**\n * Pre-wrap a raster LQIP data URI in an SVG that reproduces Next's OWN\n * static-import blur strength (EC-428 AD-2, ported from a consumer's reference implementation).\n *\n * Next's `getImageBlurSvg` only scales its blur (`blurWidth * 40` viewBox) for\n * **static imports** that carry `blurWidth`/`blurHeight`; a runtime `blurDataURL`\n * falls back to `stdDeviation=20` over the image's INTRINSIC-size viewBox — on a\n * large hero that is effectively no blur, so the ≤24px LQIP shows raw pixel blocks.\n * There is no public API to pass `blurWidth`/`blurHeight` for runtime URLs, so we\n * reproduce the scaled path ourselves: `viewBox = lqipDims * 40`, Next's identical\n * alpha-safe filter chain (feGaussianBlur → alpha-clamp feColorMatrix → feFlood/\n * feComposite `out` → composite over SourceGraphic → feGaussianBlur), and an\n * `<image preserveAspectRatio='none'>` over the original LQIP.\n *\n * Base64-encoded (`data:image/svg+xml;base64,`), NEVER percent-encoding: Next nests\n * this whole URI as its outer SVG's `<image href>`, and the browser percent-decodes\n * the outer data URI exactly once — percent-encoded nested `%xx` would corrupt. The\n * SVG string is pure ASCII (the inner `href` is itself a base64 data URI), so `btoa`\n * (a global in every runtime this targets — browser, Node ≥16, edge) is safe.\n */\nexport function scaledLqipBlur(lqip: string, width: number, height: number): string {\n const { width: lqipW, height: lqipH } = fitLqipLongEdge(width, height)\n const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${lqipW * 40} ${lqipH * 40}'><filter id='b' color-interpolation-filters='sRGB'><feGaussianBlur stdDeviation='20'/><feColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/><feFlood x='0' y='0' width='100%' height='100%'/><feComposite operator='out' in='s'/><feComposite in2='SourceGraphic'/><feGaussianBlur stdDeviation='20'/></filter><image width='100%' height='100%' x='0' y='0' preserveAspectRatio='none' style='filter:url(#b)' href='${lqip}'/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\n/**\n * Render a resolved asset through `next/image` (EC-152).\n *\n * - With known intrinsic dimensions the optimizer serves resized variants\n * and the reserved width/height prevent layout shift.\n * - Without dimensions `next/image` cannot render (it requires `width` +\n * `height` or `fill`), so the component degrades to a plain `<img>` —\n * un-optimized but visible, never a crash. Records written by the EC-151\n * upload flow always carry detected dimensions.\n * - A `null`/empty asset renders nothing (missing assets surface as\n * structured `unknown-asset` validation issues upstream, not here).\n */\nexport function ElytraImage(props: ElytraImageProps): ReactNode {\n const { asset, sizes, priority, quality, className, loader, loading } = props\n if (!asset || asset.url.length === 0) return null\n const alt = asset.alt ?? ''\n // EC-230: a focal point becomes CSS `object-position`, so an art-directed image\n // keeps its subject in frame when a layout crops it (`object-fit: cover`).\n // Inert under the default fit — harmless to set, correct once a layout crops.\n const objectPosition = asset.focalPoint\n ? `${asset.focalPoint.x * 100}% ${asset.focalPoint.y * 100}%`\n : undefined\n if (asset.width === null || asset.height === null) {\n // Honest fallback: next/image requires intrinsic dimensions; a\n // dimensionless record degrades to an unoptimized <img> instead of\n // crashing the page. No placeholder here — a plain <img> can't consume a\n // blurDataURL — and the wrap needs non-null dims, so it stays below this guard.\n return (\n <img\n data-ec-image=\"fallback\"\n src={asset.url}\n alt={alt}\n className={className}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n }\n // EC-420 (AD-2): a resolved LQIP data URI becomes a next/image blur-up\n // placeholder — the image blurs while the optimized bytes stream in.\n // EC-428 (AD-2/AD-3): but Next only scales its blur for static imports; a runtime\n // blurDataURL under-blurs a large image to raw pixel blocks — so pre-wrap the\n // raster LQIP in a viewBox-scaled SVG using the recomputed LQIP dims. AD-4: an\n // lqip that is ALREADY an SVG (a future engine that pre-blurs, or a consumer's\n // interim wrapper) passes through untouched. Absent lqip = pre-EC-420 behaviour.\n const blurDataURL =\n asset.lqip == null\n ? null\n : asset.lqip.startsWith('data:image/svg')\n ? asset.lqip\n : scaledLqipBlur(asset.lqip, asset.width, asset.height)\n const blur = blurDataURL ? { placeholder: 'blur' as const, blurDataURL } : {}\n // EC-428 (AD-5): forward `loading`, but Next throws when `priority` +\n // `loading='lazy'` arrive together (get-img-props.js). Priority wins; drop the\n // conflicting `lazy` so a priority image never crashes delivery.\n const loadingProp = priority && loading === 'lazy' ? undefined : loading\n return (\n <NextImage\n data-ec-image=\"next\"\n src={asset.url}\n alt={alt}\n width={asset.width}\n height={asset.height}\n {...(sizes !== undefined ? { sizes } : {})}\n {...(priority !== undefined ? { priority } : {})}\n {...(loadingProp !== undefined ? { loading: loadingProp } : {})}\n {...(quality !== undefined ? { quality } : {})}\n {...(className !== undefined ? { className } : {})}\n {...(loader !== undefined ? { loader } : {})}\n {...blur}\n {...(objectPosition ? { style: { objectPosition } } : {})}\n />\n )\n}\n\nexport interface AssetImageLoaderOptions {\n /** Query param name for the requested width. Default `\"w\"`. */\n widthParam?: string\n /** Query param name for the requested quality. Default `\"q\"`. */\n qualityParam?: string\n /** Quality used when `next/image` passes none. Default `75`. */\n defaultQuality?: number\n}\n\n/**\n * A `next/image` loader for asset URLs served through a transforming CDN:\n * it appends width/quality query params to the asset URL and returns it.\n *\n * Be honest about what this does: it only *requests* a transform. Plain\n * Convex file-storage serve URLs ignore these params and return the original\n * bytes, so this loader is only correct once the asset origin actually\n * resizes (e.g. an image CDN in front of storage). Until then, use the\n * default `next/image` loader (see module docs) — that is the v1 path.\n */\nexport function createAssetImageLoader(options: AssetImageLoaderOptions = {}): ImageLoader {\n const widthParam = options.widthParam ?? 'w'\n const qualityParam = options.qualityParam ?? 'q'\n const defaultQuality = options.defaultQuality ?? 75\n return ({ src, width, quality }) => {\n const separator = src.includes('?') ? '&' : '?'\n return `${src}${separator}${widthParam}=${width}&${qualityParam}=${quality ?? defaultQuality}`\n }\n}\n\n/**\n * Per-deployment image-delivery options for {@link withNextImage} (EC-271).\n * The default is the host's own Next.js optimizer (Vercel) — zero config. A host\n * on a different CDN supplies its own `loader` (a plain function) to route variants\n * through that CDN instead, without touching component code.\n */\nexport interface NextImageOptions {\n /**\n * Custom `next/image` loader — the override seam for non-Vercel hosting. Omit\n * for the Vercel default (Next's own `/_next/image` optimizer). Supply a\n * function to delegate resizing to another CDN:\n *\n * - a CDN that honors `?w=&q=` query params → {@link createAssetImageLoader};\n * - Cloudflare Images / Imgix / a bespoke transform → your own `ImageLoader`\n * (e.g. rewrite to `/cdn-cgi/image/width=<w>,quality=<q>/<src>`).\n *\n * The loader receives `{ src, width, quality }` and returns the variant URL.\n */\n loader?: ImageLoader\n /** Default responsive `sizes` hint when a block doesn't set one. */\n sizes?: string\n /** Default quality (1–100) when a block doesn't set one (Next defaults to 75). */\n quality?: number\n}\n\n/**\n * The image-role implementation for Next.js host apps, rendered optimized through\n * {@link ElytraImage}. It reads the image role's contract (`ROLE_CONTRACTS`) — the\n * `src` asset prop and the literal `url` prop (EC-636: an asset never holds a URL, an\n * external image is `url`) — plus `alt`/`width`/`height`/`priority` as the shipped\n * `@elytracms/components` Image declares them, through the one reader\n * `normalizeImageSource`. The renderer resolves an `asset`-typed `src` (EC-195) to\n * `{ url, width, height, alt }` before this runs, so an uploaded image gets intrinsic\n * dimensions (→ no layout shift, sized variants); an external `url` has none unless\n * `width`/`height` are set, so it renders as the plain `<img>` fallback.\n * `priority` opts an above-the-fold image out of lazy-loading.\n */\nfunction createNextImage(options: NextImageOptions) {\n return function ElytraNextImage(props: Record<string, unknown>): ReactNode {\n const { url, width, height, alt, focalPoint, lqip } = normalizeImageSource(props)\n if (url.length === 0) return null\n return (\n <ElytraImage\n asset={{ url, width: width ?? null, height: height ?? null, alt, focalPoint, lqip }}\n {...(props.priority === true ? { priority: true } : {})}\n {...(options.loader !== undefined ? { loader: options.loader } : {})}\n {...(options.sizes !== undefined ? { sizes: options.sizes } : {})}\n {...(options.quality !== undefined ? { quality: options.quality } : {})}\n />\n )\n }\n}\n\n/**\n * Batteries included, opted into (EC-591): return the component list with the\n * implementation of the component that fills the `image` role swapped for\n * `next/image` delivery ({@link ElytraImage}) — the manifest is kept, so the\n * studio and the host still register the same component. Nothing is added to the\n * list and nothing is registered by the engine: no image-role component in the\n * list → the list comes back unchanged.\n *\n * Which entry fills the role is decided by core's one registration rule (the first\n * valid claimant — `ComponentRegistry.roleComponent('image')`), so the swap can\n * never land on a different entry than the one the registry resolves.\n *\n * It registers the list BEFORE `createElytraHost` resolves the entry through the config\n * gate, so it must not be the first place a broken list is read (EC-678): the\n * registration never throws — a malformed manifest is a located finding, and the gate\n * reads the same registration — so a broken list comes back from here unchanged and the\n * host's boot throws the gate's LOCATED error (`invalid-config-shape at\n * components.N.manifest.props.p: …`), the same one `elytra doctor` `config-valid` and the\n * studio report. (Measured on `6310b7b4`: a prop that was not a field-def threw here,\n * unlocated, before the gate ran.) Use it on\n * the DELIVERY side only (the studio's canvas runs on Vite, where `next/image`\n * does not exist) — the scaffold's host seam file does it in one line:\n *\n * ```ts\n * export const elytra = createElytraHost({ ...site, components: withNextImage(site.components) })\n * ```\n *\n * Pass {@link NextImageOptions} to delegate image delivery to another CDN\n * (EC-271) — the v1 default is Vercel, with room to grow:\n *\n * ```ts\n * withNextImage(site.components, { loader: createAssetImageLoader() }) // ?w=&q= CDN\n * withNextImage(site.components, { loader: cloudflareImageLoader }) // Cloudflare Images\n * ```\n */\nexport function withNextImage(\n components: readonly HostComponentEntry[] | undefined,\n options: NextImageOptions = {},\n): HostComponentEntry[] {\n const list = [...(components ?? [])]\n const image = registerComponents(list).registry.roleComponent('image')\n if (!image) return list\n const implementation = createNextImage(options)\n // Read null-safely: a list element that is not an entry is the gate's to refuse, located.\n return list.map((entry) =>\n (entry as HostComponentEntry | null | undefined)?.manifest === image ? { manifest: entry.manifest, implementation } : entry,\n )\n}\n\n/** Per-deployment options for {@link elytraHostImage} (the EC-271 loader seam). */\nexport interface ElytraHostImageOptions {\n /** Custom `next/image` loader for non-Vercel CDNs (see {@link NextImageOptions}). */\n loader?: ImageLoader\n /** Default quality (1–100) when a call site doesn't set one (Next defaults to 75). */\n quality?: number\n}\n\n/**\n * The `next/image`-backed {@link HostImageRenderer} (EC-368 image defaults): the\n * renderer a Next host registers on the `@elytracms/next/host-image` bridge so\n * DUAL-RENDERED project components (which can't import `next/image` themselves)\n * deliver optimized images — resized/modern-format variants, intrinsic\n * dimensions (no layout shift), focal-point `object-position`. A dimensionless\n * record degrades to a plain `<img>` (honest, never a crash), exactly like\n * {@link ElytraImage}.\n *\n * ```ts\n * import { elytraHostImage } from '@elytracms/next'\n * import { setHostImage } from '@elytracms/next/host-image'\n *\n * setHostImage(elytraHostImage()) // once, at host boot (never in the studio)\n * ```\n */\nexport function elytraHostImage(options: ElytraHostImageOptions = {}): HostImageRenderer {\n // EC-428 (AD-5): forward `loading` — before this the renderer destructured\n // only asset/alt/className/priority/sizes, so a non-priority eager image silently\n // fell back to lazy. ElytraImage resolves the priority + loading='lazy' conflict.\n // EC-587: an omitted `alt` is the asset's own (the one reader's value, already on\n // the resolved asset); a caller's explicit alt — e.g. '' for a decorative image — wins.\n return ({ asset, alt, className, priority, sizes, loading }) => (\n <ElytraImage\n asset={{ ...asset, alt: alt ?? asset.alt }}\n {...(className !== undefined ? { className } : {})}\n {...(priority ? { priority: true } : {})}\n {...(sizes !== undefined ? { sizes } : {})}\n {...(loading !== undefined ? { loading } : {})}\n {...(options.loader !== undefined ? { loader: options.loader } : {})}\n {...(options.quality !== undefined ? { quality: options.quality } : {})}\n />\n )\n}\n","import type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport { compositionNodesOf } from './canvas-renderer'\nimport type { DeliveryPresentation } from './canvas-renderer'\n\n/**\n * Manifest-driven header clearance (EC-368): does this page OPEN full-bleed?\n *\n * A host with a floating header (occupying no flow space) must pad a page's\n * content down by the header height — unless the page's FIRST canvas block is a\n * structural hero that deliberately bleeds to the viewport top and renders\n * under the header. Which components bleed is declared on their OWN manifest\n * (`presentation: { fullBleed: true }`, `@elytracms/core/component-registry`),\n * so the host derives the decision from the registry instead of keeping a\n * parallel hand-maintained component-id set that silently drifts when a new\n * hero ships.\n *\n * Pure over the delivery-shaped document: the composition field is found by\n * VALUE (the same `compositionNodesOf` selection the renderer uses), so hosts\n * with `body`, `content`, or bespoke composition field names all work. Absent\n * document, empty composition, unknown component, or an undeclared hint all\n * answer `false` — the safe default is clearance.\n *\n * EC-499: pass the collection's resolved `presentation` (from the render\n * outcome) so this reads the SAME body the renderer will render when the\n * collection declares one — otherwise the value-shape pick could name a\n * different field than the declaration does. A body whose first item is prose\n * rather than a block answers `false`, which is right: rich text opens no hero.\n *\n * ```tsx\n * const outcome = client.resolvePage(path)\n * const fullBleed = firstBlockFullBleed(outcome.document, components.registry)\n * return fullBleed ? page : <div className=\"pt-[var(--header-clearance)]\">{page}</div>\n * ```\n *\n * The host kit wires the resolution half for you: `elytra.pageOpensFullBleed(slug)`.\n */\nexport function firstBlockFullBleed(\n document: Record<string, unknown> | null | undefined,\n registry: ComponentRegistry,\n presentation?: DeliveryPresentation,\n): boolean {\n const first = compositionNodesOf(document, presentation)[0]\n if (!first) return false\n return registry.getManifest(first.componentId)?.presentation?.fullBleed === true\n}\n","/**\n * Env resolution for the delivery host kit (EC-368). ONE documented name per\n * concern — every kit surface reads the same resolved bag, so the two-\n * disconnected-secrets class of bug (a preview route gating on one env var\n * while the draft loader reads another) is structurally impossible: there is\n * exactly one `DRAFT_PREVIEW_TOKEN`, consumed by BOTH the preview-enable route\n * and the draft snapshot fetch.\n *\n * | Env var | Concern |\n * | --------------------- | ---------------------------------------------------- |\n * | `ELYTRA_CONVEX_URL` | Convex deployment URL — presence (with the project |\n * | | id) switches the loader to LIVE mode |\n * | `ELYTRA_PROJECT_ID` | The project the studio publishes under (= tag scope) |\n * | `DRAFT_PREVIEW_TOKEN` | The ONE draft secret: gates the preview-enable route |\n * | | AND authenticates the draft snapshot fetch (must |\n * | | equal the Convex deployment's own env var) |\n * | `REVALIDATE_SECRET` | Instant-publish webhook HMAC secret (must equal the |\n * | | deployment's `CANVAS_REVALIDATE_SECRET`) |\n * | `INGEST_SECRET` | Public-ingest HMAC secret (EC-617; must equal the |\n * | | deployment's `ELYTRA_INGEST_SECRET`) |\n *\n * EC-579 retired `STUDIO_ORIGIN` (the CORS origin of the studio's deleted browser\n * dispatcher): the one emitter is the Convex deployment, server to server.\n */\n\n/** The kit's documented env names (single source of truth, also used in docs/tests). */\nexport const ELYTRA_HOST_ENV_KEYS = {\n convexUrl: 'ELYTRA_CONVEX_URL',\n projectId: 'ELYTRA_PROJECT_ID',\n draftToken: 'DRAFT_PREVIEW_TOKEN',\n revalidateSecret: 'REVALIDATE_SECRET',\n ingestSecret: 'INGEST_SECRET',\n} as const\n\nexport interface ElytraHostEnv {\n convexUrl?: string\n projectId?: string\n draftToken?: string\n revalidateSecret?: string\n ingestSecret?: string\n}\n\nfunction cleaned(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed !== undefined && trimmed.length > 0 ? trimmed : undefined\n}\n\n/**\n * Resolve the kit's env bag from a raw env record (`process.env` by default at\n * the factory). Values are trimmed; empty strings resolve to `undefined` — an\n * env var set to `\"\"` must behave exactly like an unset one (endpoints stay\n * closed, live mode stays off), never like a configured empty secret.\n */\nexport function resolveHostEnv(env: Record<string, string | undefined>): ElytraHostEnv {\n const resolved: ElytraHostEnv = {}\n for (const [key, name] of Object.entries(ELYTRA_HOST_ENV_KEYS)) {\n const value = cleaned(env[name])\n if (value !== undefined) resolved[key as keyof ElytraHostEnv] = value\n }\n return resolved\n}\n","import { DRAFT_VARIANT, variantOf } from '@elytracms/core/cms-core'\nimport type { CollectionDef, LocaleConfigInput, Perspective, RouteRecord } from '@elytracms/core/cms-core'\nimport { parseDeliveredDocuments, parseDeliveredRedirects } from '@elytracms/core/content'\nimport type { ContentDocumentSource } from '@elytracms/core/content'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { createStaticContentSource } from './snapshot'\nimport type { ContentSource } from './source'\n\n/**\n * The LIVE half of the delivery host kit (EC-368): fetch the delivery snapshot\n * from a Convex deployment (`convex/delivery.ts` in the builder app) over\n * Convex's public HTTP query API — a plain `fetch` to\n * `POST {deployment}/api/query`, no Convex npm dependency — and assemble it\n * through the same `createStaticContentSource` path the fixture snapshot uses,\n * so accessor and rendering semantics are byte-identical across seeded, empty,\n * and live modes. This engine-izes the ~300-line `live-content.ts` both real\n * consumers hand-copied (EC-369 finding #5); the fetch + Zod-validation logic\n * now lives in exactly one place.\n *\n * Perspective handling: published requests fetch the PUBLIC `delivery:snapshot`\n * (published documents only — the standing live-is-published-only invariant).\n * Draft-mode requests fetch `delivery:draftSnapshot`, gated by the deployment's\n * `DRAFT_PREVIEW_TOKEN` — the same single secret that gates the kit's\n * preview-enable route (see `host-env.ts`).\n *\n * Routes: the config's route records (AD-3/AD-11, EC-207), exactly as resolved —\n * the backend never carries route data, and the engine derives none (EC-590: the\n * implicit \"`/` → the lowest-id `page`\" home fallback is gone with every other\n * engine-dictated name; a project declares its `/`, the scaffold ships one).\n */\n\nexport interface LiveContentOptions {\n /** Convex deployment URL, e.g. `https://your-deployment-123.convex.cloud`. */\n convexUrl: string\n projectId: string\n /**\n * Project locale config — config-owned (AD-11), never read from the snapshot.\n * The declared shape; parsed where the source is assembled (EC-580).\n */\n locales: LocaleConfigInput\n /** CMS collection schema — config-owned (AD-11); drives depth-1 relation population. */\n collections: readonly CollectionDef[]\n /** Route records — config-owned (AD-3/AD-11), the resolved project's. */\n routes: readonly RouteRecord[]\n /** `DRAFT_PREVIEW_TOKEN` — required for draft-perspective loads. */\n draftToken?: string\n /** Fetch implementation override (tests, custom agents). Defaults to global fetch. */\n fetch?: typeof globalThis.fetch\n}\n\n/**\n * The `delivery.ts` payload shape (validated field-by-field below). A\n * deployment IS one environment (EC-179); the snapshot carries content only\n * (EC-287) — documents, assets, editorial redirects (EC-303). (A `sources`\n * strand from an older deployment is ignored — it belonged to the deleted\n * data-binding subsystem, EC-574.)\n */\ninterface DeliverySnapshotPayload {\n projectId: string\n redirects?: unknown[]\n /**\n * EC-628 — the stored redirects the deployment withheld because it could not read them\n * (`invalid-redirect` issues from the one live rule). Absent from a deployment older than\n * EC-628, which serves such rows in `redirects` instead (and this host names them there).\n */\n redirectIssues?: unknown[]\n documents: unknown[]\n /**\n * EC-670 — the stored documents the deployment withheld because it could not unfold them\n * (`invalid-stored-document` issues: a folded value whose payload does not parse). Absent\n * from a deployment older than EC-670, whose snapshot rejected altogether instead.\n */\n documentIssues?: unknown[]\n assets: unknown[]\n}\n\n/**\n * Run one query through Convex's public HTTP API. Response envelope (verified\n * against live deployments): `{ status: 'success', value }` or\n * `{ status: 'error', errorMessage }`. `cache: 'no-store'` keeps Next's fetch\n * cache out of the loop — the route helper's tagged data cache is the only\n * cache layer, so tag revalidation always reaches the deployment.\n */\nasync function runConvexQuery(\n fetchImpl: typeof globalThis.fetch,\n convexUrl: string,\n path: string,\n args: Record<string, unknown>,\n): Promise<unknown> {\n const response = await fetchImpl(`${convexUrl.replace(/\\/$/, '')}/api/query`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ path, args, format: 'json' }),\n cache: 'no-store',\n })\n const body = (await response.json().catch(() => undefined)) as\n | { status?: string; value?: unknown; errorMessage?: string; message?: string }\n | undefined\n if (!response.ok || body?.status !== 'success') {\n throw new Error(\n `Convex query \"${path}\" failed (${response.status}): ${\n body?.errorMessage ?? body?.message ?? 'no error message'\n }`,\n )\n }\n return body.value\n}\n\n/**\n * Assemble a fetched delivery-snapshot payload into a `ContentSource` — the\n * pure half of {@link loadLiveContent}, exported so the parsing/assembly rules\n * are directly testable against fixture payloads (no network).\n */\nexport function assembleLiveSource(\n payload: DeliverySnapshotPayload,\n options: Pick<LiveContentOptions, 'locales' | 'collections' | 'routes'>,\n perspective: Perspective,\n): ContentSource {\n // Locales/collections/routes are config-owned (AD-11): from the host repo's\n // own `cms/`, passed in via options — never from the snapshot.\n const locales = options.locales\n // EC-585 (Codex F12): THE delivery parse — a stored row that is not a canonical document\n // is not delivered, and is named as a visible `invalid-stored-document` issue on the\n // source's clients (it used to vanish with zero issues). EC-670 (Codex F19): so is every\n // row the deployment withheld upstream (`documentIssues` — a folded value that would not\n // decode), carried beside the rows it did serve.\n const { documents, issues: documentIssues } = parseDeliveredDocuments(\n payload.documents,\n payload.documentIssues ?? [],\n )\n // EC-628 (Codex F04): the redirect strand gets the same — a delivered redirect this host\n // cannot read, and every redirect the deployment withheld, is an `invalid-redirect` issue.\n const delivered = parseDeliveredRedirects(payload.redirects ?? [], payload.redirectIssues ?? [])\n const sourceIssues = [...documentIssues, ...delivered.issues]\n\n // The config's routes, as resolved — no engine-derived home route (EC-590).\n const routes = [...options.routes]\n\n // EC-224 B-1: the published snapshot ships each document as its PINNED\n // published content (delivery resolved the version pointer server-side). Wrap\n // those rows as a `DocumentHistory` so the pure published gate\n // (`selectPerspective`) serves `.published` directly.\n //\n // TRUST CONTRACT: the wrap treats whatever the published fetch returned AS\n // published and deliberately does NOT re-gate — it cannot, the wire carries\n // no version pointer. Correctness rests ENTIRELY on `delivery:snapshot`\n // shipping published-only content (pinned version snapshots, never a draft —\n // see the builder's `convex/delivery.ts`). Guard the invariant in delivery,\n // not here. Draft-fetch rows stay bare: the draft perspective's\n // `selectPerspective` serves them as-is (a bare row is the draft variant row).\n const documentSources: ContentDocumentSource[] =\n perspective === 'published'\n ? documents.map((doc) => ({ draft: doc, published: doc, versions: [] }))\n : documents\n\n return createStaticContentSource({\n projectId: payload.projectId,\n locales,\n routes,\n // Redirects are editorial DATA (EC-303): the ENABLED ones ship in the\n // snapshot, so they change dynamically (no redeploy). Native next.config\n // redirects run before this.\n redirects: delivered.redirects,\n collections: [...options.collections],\n documents: documentSources,\n assets: payload.assets as AssetRecord[],\n ...(sourceIssues.length > 0 ? { sourceIssues } : {}),\n })\n}\n\n/** Fetch the live delivery snapshot and assemble the `ContentSource` for one request. */\nexport async function loadLiveContent(\n options: LiveContentOptions,\n perspective: Perspective,\n): Promise<ContentSource> {\n const fetchImpl = options.fetch ?? globalThis.fetch\n const args: Record<string, unknown> = { projectId: options.projectId }\n\n let payload: DeliverySnapshotPayload\n const variant = variantOf(perspective)\n if (variant !== undefined) {\n // EC-595: a perspective may name any variant; the deployment serves exactly one\n // variant snapshot, the draft's. A release perspective (v1.x) is refused here,\n // visibly, rather than read from the draft snapshot as if it were the draft.\n if (variant !== DRAFT_VARIANT) {\n throw new Error(\n `Elytra live content: no delivery snapshot serves the \"${variant}\" variant — the deployment ` +\n `serves the published snapshot and the \"${DRAFT_VARIANT}\" variant's only.`,\n )\n }\n if (!options.draftToken) {\n // Explicit, visible failure: draft mode without a configured token must\n // never silently fall back to published content.\n throw new Error(\n 'Draft preview requested but DRAFT_PREVIEW_TOKEN is not configured on the host.',\n )\n }\n payload = (await runConvexQuery(fetchImpl, options.convexUrl, 'delivery:draftSnapshot', {\n ...args,\n token: options.draftToken,\n })) as DeliverySnapshotPayload\n } else {\n payload = (await runConvexQuery(\n fetchImpl,\n options.convexUrl,\n 'delivery:snapshot',\n args,\n )) as DeliverySnapshotPayload\n }\n\n return assembleLiveSource(payload, options, perspective)\n}\n","import { validateLocale } from '@elytracms/core/cms-core'\nimport type { DocumentRef, Locale, Perspective } from '@elytracms/core/cms-core'\nimport type { ContentClient, ResolvedDocument } from '@elytracms/core/content'\nimport type { ContentSource } from './source'\n\n/**\n * Site-chrome globals for delivery hosts (EC-368, from EC-369 finding #5): the\n * layout, header, and footer are NOT rendered through the canvas, so they read\n * the `settings` singleton + `navigation` documents straight from the\n * `ContentSource` and pre-resolve everything — keeping the chrome components\n * pure presentational, with no `ContentClient` or `@elytracms/*` knowledge.\n * Both real consumers hand-authored this ~200-line layer independently; the\n * MECHANISM now lives here, while the per-project projection (which settings\n * fields exist, what the chrome looks like) stays consumer-owned:\n *\n * - **settings**: the singleton collection's lone document (EC-274) — no\n * document id is ever named.\n * - **navigation by slot**: menus are addressed by their EC-284 alias slot\n * (`main`, `footer`), never a document id — delivery shape carries `alias`\n * since EC-364.\n * - **link resolution**: link-object values (`linkType: href | page | file`)\n * resolve through `client.resolveLink` — the same `urlForDocument` authority\n * the router serves, so a nav link to a nested page carries its FULL composed\n * path (`/krippe/tagesablauf`, EC-218/EC-360), and `home → '/'` needs no\n * special-casing.\n * - **asset resolution**: asset field values (stored ids) resolve to serve URLs\n * through the source's delivery-shaped asset list.\n *\n * Everything degrades explicitly: a missing settings document is `null`, a\n * missing slot is `null`/`[]`, an unresolvable link drops out (never a silent\n * `#`), an unknown asset id is `undefined` — the host renders sensible empties\n * for the empty-source build and surfaces gaps honestly.\n */\n\nexport interface GlobalsConventions {\n /** The settings singleton's collection id. Default `'settings'`. */\n settingsCollection?: string\n /** The navigation documents' collection id. Default `'navigation'`. */\n navigationCollection?: string\n /**\n * Field names probed (in order) for a navigation document's entry list.\n * Default `['items', 'links']` — the two shapes the shipped consumers and the\n * starter template use.\n */\n navigationItemsFields?: readonly string[]\n}\n\n/** A fully-resolved link: the composed href plus the new-tab flag. */\nexport interface ResolvedLink {\n href: string\n openInNewTab: boolean\n}\n\n/** A leaf navigation link (children of a dropdown / footer column). */\nexport interface ResolvedNavLink extends ResolvedLink {\n label: string\n /** Optional subline shown under the label (wide header dropdowns). */\n description?: string\n}\n\n/** A top-level navigation entry; `href` is absent for pure group headings. */\nexport interface ResolvedNavEntry {\n label: string\n href?: string\n openInNewTab: boolean\n description?: string\n children: ResolvedNavLink[]\n}\n\n/** The resolved chrome-globals surface `loadGlobals` yields (EC-368). */\nexport interface SiteGlobals {\n /** The settings singleton, delivery-shaped, or `null` (empty build / not published). */\n settings: ResolvedDocument | null\n /** The navigation document filling a slot alias (EC-284/EC-364), or `null`. */\n navigation(slot: string): ResolvedDocument | null\n /**\n * A slot's entry list, fully resolved: labels, composed hrefs (nested paths\n * included), new-tab flags, sublines, one level of children. Entries whose\n * link cannot resolve to an href are kept only when they carry children (a\n * group heading); leaf entries without a resolvable href drop out.\n */\n navigationEntries(slot: string): ResolvedNavEntry[]\n /** Resolve a link-object field value to an href + new-tab flag, or `null`. */\n resolveLink(value: unknown): ResolvedLink | null\n /** Resolve an asset field value (stored id / URL / `{ url }`) to a URL, or `undefined`. */\n assetUrl(value: unknown): string | undefined\n /** The accessor client behind the globals — for bespoke projections. */\n client: ContentClient\n /** The underlying content source (assets(), locales, routes). */\n source: ContentSource\n}\n\nexport interface ResolveGlobalsOptions {\n /** Perspective of the chrome read. Default `'published'` (live is published-only). */\n perspective?: Perspective\n /**\n * The request's locale (EC-720). Default: the source's default locale. A locale the\n * project does not declare throws a located `unknown-locale` error — read silently, it\n * would serve every field from the default locale's fallback and look like a translation\n * gap rather than a wrong argument.\n */\n locale?: Locale\n conventions?: GlobalsConventions\n}\n\nconst DEFAULT_ITEMS_FIELDS = ['items', 'links'] as const\n\n/**\n * EC-720: the chrome is read in a locale the project DECLARES, or not at all. The content\n * client takes any code and answers an undeclared one from the default locale's fallback,\n * field by field — a wrong argument that would look like a translation gap on the page.\n * The project's locales are the source's own (`source.locales`, parsed from the config\n * the studio authors in), so the one rule is read here, where both chrome readers\n * (`resolveGlobals`, `elytra.loadGlobals`) pass.\n */\nfunction assertDeclaredLocale(source: ContentSource, locale: Locale): void {\n const [issue] = validateLocale(source.locales, locale)\n if (!issue) return\n throw new Error(\n `Elytra chrome globals: ${issue.code} at ${issue.path.join('.')} — the locale \"${locale}\" is not one of ` +\n `this project's locales (it declares ${source.locales.locales.join(', ')}). Pass the locale the page ` +\n `renders in — resolveLocale({ pathname }, locales) splits it from the URL exactly as the catch-all ` +\n `does — or omit it for the default locale.`,\n )\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction items(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value) ? value.map(record) : []\n}\n\n/**\n * Resolve the chrome globals over an already-loaded `ContentSource` — the pure\n * core of the host kit's `loadGlobals` (directly testable over fixture\n * sources).\n */\nexport function resolveGlobals(\n source: ContentSource,\n options: ResolveGlobalsOptions = {},\n): SiteGlobals {\n const perspective = options.perspective ?? 'published'\n const conventions = options.conventions ?? {}\n const settingsCollection = conventions.settingsCollection ?? 'settings'\n const navigationCollection = conventions.navigationCollection ?? 'navigation'\n const itemsFields = conventions.navigationItemsFields ?? DEFAULT_ITEMS_FIELDS\n\n if (options.locale !== undefined) assertDeclaredLocale(source, options.locale)\n const client = source.client({ perspective, ...(options.locale !== undefined ? { locale: options.locale } : {}) })\n const assets = source.assets?.(options.locale) ?? []\n const assetById = new Map(assets.map((asset) => [asset.id, asset] as const))\n\n const listDocs = (collection: string): ResolvedDocument[] => {\n const result = client.listDocuments(collection)\n return result.ok ? result.documents : []\n }\n\n // EC-274: `settings` is a singleton — the lone document of its collection.\n const settings = listDocs(settingsCollection)[0] ?? null\n\n // EC-284/EC-364: menus are addressed by slot alias through the content client\n // (delivery shape carries `alias`), never by document id.\n const navDocs = listDocs(navigationCollection)\n const navigation = (slot: string): ResolvedDocument | null =>\n navDocs.find((doc) => (doc as Record<string, unknown>).alias === slot) ?? null\n\n /**\n * Asset field value → serve URL: a delivered asset object's `url`, or a raw asset id\n * resolved against the baked assets. EC-587 / 2h: an asset VALUE never holds a URL,\n * so a URL-shaped string is not passed through (the validator names it as an\n * `invalid-field-value`); an external image is a literal `src` on a component.\n */\n const assetUrl = (value: unknown): string | undefined => {\n if (typeof value === 'string') return assetById.get(value)?.url\n const url = record(value).url\n return typeof url === 'string' && url.length > 0 ? url : undefined\n }\n\n /** A raw or populated relation value's `{ collection, id }` identity, if present. */\n const refOf = (value: unknown): DocumentRef | null => {\n const ref = record(value)\n return typeof ref.collection === 'string' && typeof ref.id === 'string'\n ? { collection: ref.collection, id: ref.id }\n : null\n }\n\n /** Resolve a link-object value (`linkType: href | page | file`) to an href. */\n const resolveLink = (value: unknown): ResolvedLink | null => {\n const link = record(value)\n const openInNewTab = link.openInNewTab === true\n const href = str(link.href) || str(link.url)\n const linkType = str(link.linkType) || (href ? 'href' : '')\n\n if (linkType === 'href' || linkType === 'external') {\n return href ? { href, openInNewTab } : null\n }\n if (linkType === 'file') {\n const url = assetUrl(link.file)\n return url ? { href: url, openInNewTab } : null\n }\n if (linkType === 'page') {\n const page = link.page\n // The reverse-router authority (EC-291/EC-360): the SAME `urlForDocument`\n // the routes serve — composed nested paths, `home → '/'`, locale prefix.\n const ref = refOf(page)\n if (ref) {\n const path = client.resolveLink(ref)\n if (path !== null) return { href: path, openInNewTab }\n }\n // A populated relation's derived `path` (the same router, EC-360); else explicit\n // null. EC-590 (IDENTITY #12): no `'/' + slug` guess — for a document no route\n // addresses it was a silent 404, where the same target in rich text is a visible\n // `unresolved-ref`. The engine dictates no URL shape.\n const shaped = record(page)\n if (typeof shaped.path === 'string' && shaped.path.startsWith('/')) {\n return { href: shaped.path, openInNewTab }\n }\n return null\n }\n return null\n }\n\n const toNavLink = (entry: Record<string, unknown>): ResolvedNavLink | null => {\n const label = str(entry.label)\n // Structured shape: the link object lives under `link`; flat starter shape:\n // the entry itself carries `href` (resolveLink's href fallback picks it up).\n const resolved = resolveLink('link' in entry ? entry.link : entry)\n if (!label || !resolved) return null\n const description = str(entry.description)\n return { label, ...resolved, ...(description ? { description } : {}) }\n }\n\n const toNavEntry = (entry: Record<string, unknown>): ResolvedNavEntry | null => {\n const label = str(entry.label)\n if (!label) return null\n const resolved = resolveLink('link' in entry ? entry.link : entry)\n const description = str(entry.description)\n const children = items(entry.children)\n .map(toNavLink)\n .filter((child): child is ResolvedNavLink => child !== null)\n // A leaf entry needs a resolvable target; an entry with children stands as a\n // group heading even without one (footer columns, pure dropdowns).\n if (!resolved && children.length === 0) return null\n return {\n label,\n ...(resolved ? { href: resolved.href } : {}),\n openInNewTab: resolved?.openInNewTab ?? false,\n ...(description ? { description } : {}),\n children,\n }\n }\n\n const navigationEntries = (slot: string): ResolvedNavEntry[] => {\n const doc = navigation(slot)\n if (!doc) return []\n const values = doc as Record<string, unknown>\n const field = itemsFields.find((name) => Array.isArray(values[name]))\n if (!field) return []\n return items(values[field])\n .map(toNavEntry)\n .filter((entry): entry is ResolvedNavEntry => entry !== null)\n }\n\n return {\n settings,\n navigation,\n navigationEntries,\n resolveLink,\n assetUrl,\n client,\n source,\n }\n}\n","/**\n * Pure token/target logic of the draft-preview routes (EC-144), kept free of\n * Next.js imports so it is unit-testable. `preview.ts` wires it into route\n * handlers with `draftMode()`.\n */\n\nexport type PreviewEvaluation =\n | { ok: true; redirectTo: string }\n | { ok: false; status: 400 | 401; message: string }\n\n/**\n * The fixed origin redirect targets are resolved against. `.invalid` is reserved\n * (RFC 6761), so it never names a real host; `https:` is a special scheme, so the\n * parser applies the same normalisation a browser applies when it resolves a\n * `Location` header against the site (`\\`→`/`, tab/LF/CR stripped, dot segments\n * removed).\n */\nconst RESOLUTION_ORIGIN = 'https://redirect-target.invalid'\n\n/** Resolve `value` the way a browser would, keeping it only if it stays on the origin. */\nfunction resolveOnOrigin(value: string): URL | null {\n let resolved: URL\n try {\n resolved = new URL(value, RESOLUTION_ORIGIN)\n } catch {\n return null\n }\n return resolved.origin === RESOLUTION_ORIGIN ? resolved : null\n}\n\n/** C0 controls (U+0000–U+001F) and DEL (U+007F): never part of a legitimate target. */\nfunction hasControlCharacter(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code <= 0x1f || code === 0x7f) return true\n }\n return false\n}\n\n/**\n * The ONE same-site check for a preview redirect target, shared by the enable and\n * disable routes, so the preview endpoints can never be used as an open redirect.\n * `null` means rejected; an empty value yields `fallback`.\n *\n * EC-594: the parser a browser uses decides, not a blocklist of shapes (the old\n * blocklist caught `//host` and backslashes — EC-304 B-3 — but not `/<TAB>/host`,\n * which the parser collapses to `//host`):\n *\n * - only a path-absolute reference (`/…`) is accepted — a relative one would be\n * resolved against the route's own path by a browser, so it has no fixed meaning;\n * - any C0 control or DEL is rejected outright;\n * - the value is resolved against a fixed origin and must stay on it;\n * - what is returned is the parser's normalised `pathname + search + hash`, never\n * the raw input — and that returned target must itself stay on the origin, since\n * dot-segment removal can leave a pathname starting with `//` (`/.//host`,\n * `/..//host`, `/%2e//host`), which as a `Location` is scheme-relative.\n *\n * A scheme-relative or absolute value naming the resolution origin itself resolves\n * on it and yields its path — harmless, because only that path is returned.\n */\nexport function safeRedirectTarget(\n value: string | null | undefined,\n fallback = '/',\n): string | null {\n if (value === null || value === undefined || value === '') return fallback\n if (!value.startsWith('/') || hasControlCharacter(value)) return null\n const resolved = resolveOnOrigin(value)\n if (resolved === null) return null\n const target = resolved.pathname + resolved.search + resolved.hash\n return resolveOnOrigin(target) === null ? null : target\n}\n\n/**\n * Evaluate a preview-enable request. Drafts are token-gated (EC-144\n * acceptance: without the token, drafts are never reachable):\n *\n * - an unconfigured (empty) expected token rejects every request — preview\n * cannot be accidentally left open;\n * - a missing or mismatched `token` query param is a 401;\n * - an off-site `redirect` target is a 400.\n */\nexport function evaluatePreviewRequest(\n requestUrl: string | URL,\n expectedToken: string | undefined,\n): PreviewEvaluation {\n if (expectedToken === undefined || expectedToken.length === 0) {\n return { ok: false, status: 401, message: 'Draft preview is not configured.' }\n }\n\n let url: URL\n try {\n url = typeof requestUrl === 'string' ? new URL(requestUrl, 'http://localhost') : requestUrl\n } catch {\n return { ok: false, status: 400, message: 'Malformed preview request URL.' }\n }\n\n const token = url.searchParams.get('token')\n if (token !== expectedToken) {\n return { ok: false, status: 401, message: 'Invalid preview token.' }\n }\n\n const redirectTo = safeRedirectTarget(url.searchParams.get('redirect'))\n if (redirectTo === null) {\n return { ok: false, status: 400, message: 'Preview redirect target must be a same-site path.' }\n }\n\n return { ok: true, redirectTo }\n}\n\n/**\n * The redirect target of a preview-disable request. Disabling needs no token (it\n * only ever reduces visibility), so anyone can reach the route — its `redirect`\n * param goes through the same {@link safeRedirectTarget} as the enable route. An\n * off-site or unreadable target falls back to the site root: leaving draft mode\n * always succeeds.\n */\nexport function previewDisableTarget(requestUrl: string | URL): string {\n let url: URL\n try {\n url = typeof requestUrl === 'string' ? new URL(requestUrl, 'http://localhost') : requestUrl\n } catch {\n return '/'\n }\n return safeRedirectTarget(url.searchParams.get('redirect')) ?? '/'\n}\n","import { draftMode } from 'next/headers'\nimport { redirect } from 'next/navigation'\nimport { evaluatePreviewRequest, previewDisableTarget } from './preview-core'\n\n/**\n * Draft preview route handlers (EC-144): Next.js `draftMode()` integration.\n * Server-only by construction — these factories are consumed from App Router\n * route handler files, never from client bundles.\n */\n\nexport interface PreviewRouteOptions {\n /**\n * The shared secret gating draft preview. An empty/undefined token means\n * every request is rejected with 401 — drafts stay unreachable until a\n * token is configured.\n */\n token: string | undefined\n}\n\n/** The shape an `app/api/.../route.ts` file re-exports. */\nexport interface PreviewRouteHandlers {\n GET(request: Request): Promise<Response>\n}\n\n/**\n * Create the preview-enable route handler for\n * `app/api/preview/route.ts`:\n *\n * ```ts\n * export const { GET } = createPreviewRoute({ token: process.env.PREVIEW_TOKEN })\n * ```\n *\n * `GET /api/preview?token=...&redirect=/some/path` checks the token, enables\n * Next draft mode (the catch-all helper then renders the `draft`\n * perspective), and redirects to the target path. Wrong/missing token → 401.\n */\nexport function createPreviewRoute(options: PreviewRouteOptions): PreviewRouteHandlers {\n return {\n async GET(request: Request): Promise<Response> {\n const evaluation = evaluatePreviewRequest(request.url, options.token)\n if (!evaluation.ok) {\n return new Response(evaluation.message, { status: evaluation.status })\n }\n const draft = await draftMode()\n draft.enable()\n redirect(evaluation.redirectTo)\n },\n }\n}\n\n/**\n * Create the preview-disable route handler (e.g.\n * `app/api/preview/disable/route.ts`). Disabling needs no token — it only\n * ever reduces visibility back to the published perspective. The `redirect`\n * target passes the same same-site check as the enable route; an off-site one\n * redirects to `/` instead.\n */\nexport function createPreviewDisableRoute(): PreviewRouteHandlers {\n return {\n async GET(request: Request): Promise<Response> {\n const draft = await draftMode()\n draft.disable()\n redirect(previewDisableTarget(request.url))\n },\n }\n}\n","import type { ReactNode } from 'react'\nimport { createRouter } from '@elytracms/core/cms-core'\nimport type {\n CmsValidationIssue,\n CollectionDef,\n Locale,\n LocaleConfigInput,\n Perspective,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport type { Metadata } from 'next'\nimport { defineHostComponents } from './components'\nimport type { HostComponents } from './components'\nimport { projectFrontendUrl, resolveProject } from '@elytracms/core/studio-core'\nimport type {\n HostComponentEntry,\n ResolvedProject,\n WorkspaceProjectEntry,\n} from '@elytracms/core/studio-core'\nimport { firstBlockFullBleed } from './full-bleed'\nimport { ELYTRA_HOST_ENV_KEYS, resolveHostEnv } from './host-env'\nimport type { ElytraHostEnv } from './host-env'\nimport { loadLiveContent } from './host-content'\nimport { resolveGlobals } from './host-globals'\nimport type { GlobalsConventions, SiteGlobals } from './host-globals'\nimport { createPreviewDisableRoute, createPreviewRoute } from './preview'\nimport type { PreviewRouteHandlers } from './preview'\nimport { createRevalidateRoute } from './revalidate'\nimport type { RevalidateRouteHandlers, RevalidateRouteOptions } from './revalidate'\nimport { createIngestRoute } from './ingest'\nimport type { IngestRouteHandlers, IngestRouteOptions } from './ingest'\nimport { convexSiteUrl } from '@elytracms/core/ingest'\nimport { createCanvasRoute, requestPerspective } from './route'\nimport type {\n CanvasContentRequest,\n CanvasRequestInfo,\n CanvasRoute,\n CanvasRouteOptions,\n CanvasWrapContext,\n} from './route'\nimport { pathFromSlug, resolveRequestPage } from './route-core'\nimport { createStaticContentSource } from './snapshot'\nimport type { ContentSource } from './source'\nimport type { CacheTags, ResolvePageResult } from '@elytracms/core/content'\n\n/**\n * The delivery host kit (EC-368): ONE factory over (env, project config) that\n * yields every engine-shaped delivery surface a Next.js host mounts — content\n * loading (live snapshot / build-safe empty / token-gated draft), the catch-all\n * canvas route, the instant-publish revalidate webhook, draft-mode plumbing,\n * chrome globals, and the full-bleed layout hint. A consumer wires env + ~5\n * lines per surface; no engine logic is ever copied into a host repo again\n * (EC-369 findings #4/#5/#6/#11).\n *\n * ```ts\n * // frontend/lib/elytra.ts — the ONE seam file\n * import { createElytraHost } from '@elytracms/next'\n * // Read the project out of the repo's own config — `elytra.config.ts` is the\n * // single contract surface, and importing it costs a delivery bundle nothing\n * // (EC-532). Any other module layout works just as well; this is an example,\n * // not a required path.\n * import { site } from '../../elytra.config'\n *\n * export const elytra = createElytraHost({ ...site })\n * ```\n *\n * ```ts\n * // app/[[...slug]]/page.tsx\n * const route = elytra.canvasRoute()\n * export default route.Page\n * export const generateMetadata = route.generateMetadata\n * export const generateStaticParams = route.generateStaticParams // only if YOU enable cacheComponents\n * // (to cache: pass your own 'use cache' function as `published` — see `route.tsx`, EC-620)\n *\n * // app/api/revalidate/route.ts\n * export const { POST } = elytra.revalidateRoute()\n *\n * // app/api/draft/route.ts — GET /api/draft?token=…&redirect=/path\n * export const { GET } = elytra.previewRoute()\n *\n * // app/api/draft/disable/route.ts\n * export const { GET } = elytra.previewDisableRoute()\n * ```\n *\n * Trust boundaries are unchanged: live surfaces read the PUBLISHED snapshot;\n * draft is reachable only through explicit Next draft mode behind the ONE\n * `DRAFT_PREVIEW_TOKEN` (which also authenticates the draft snapshot fetch —\n * see `host-env.ts`); the revalidate webhook stays HMAC-signed. The consumer\n * keeps ownership of project config (AD-11), host components, form endpoints,\n * and anything genuinely bespoke.\n */\n\n/**\n * EC-581 (ratified 2b): the options are generic over the config's `collections`, so a\n * project declared with `defineCollection` keeps its literal types through the host and\n * `InferDocument<typeof host, 'post'>` (`@elytracms/core/cms-core`) is a delivered post.\n */\nexport interface ElytraHostOptions<\n Collections extends readonly CollectionDef[] = readonly CollectionDef[],\n> extends Omit<WorkspaceProjectEntry, 'slug' | 'locales' | 'collections' | 'routes' | 'components' | 'frontendUrl'> {\n /**\n * The project's display name (the entry's own `name` — `{ ...site }` carries it).\n * EC-583: the options ARE the config's project entry plus the host-only keys below,\n * and the kit resolves them through the one resolver the studio boots through.\n */\n name: string\n /**\n * The project slug (config-as-code identity, mirrors `elytra.config.ts`).\n * Used as the content scope when no live backend is configured.\n */\n slug: string\n /**\n * Locale config — config-owned (AD-11), never read from the snapshot.\n *\n * **Import it, do not restate it** (EC-519). This is the *same*\n * {@link LocaleConfigInput} `elytra.config.ts` takes, so a project declares its\n * locales once and hands that one binding to both mounts. It stays a host\n * option because the engine cannot know where a repo keeps the value — but it\n * is no longer an option you have to *retype*: since EC-532 the delivery\n * bundle can import `elytra.config.ts` itself for zero engine modules\n * (measured), so `createElytraHost({ ...site })` over a project exported from\n * the config is the shortest correct answer.\n *\n * This used to be `{ default, locales }` against a config spelling the same\n * fact `locales` + `defaultLocale`, and in our own demo the two drifted — the\n * host said `['en']`, the config said `['en','de']`, and every `/de` URL 404'd\n * silently for weeks (EC-512).\n *\n * The DECLARED shape (EC-580): the kit parses it into the engine's `LocaleConfig`\n * where every content source is assembled (`createStaticContentSource` — the\n * empty source is built at boot), so a `default` outside `locales` stops the host\n * with a located message instead of serving — the same rule the studio's\n * workspace gate applies to the same value.\n */\n locales: LocaleConfigInput\n /** CMS collection schema — config-owned (AD-11). */\n collections: Collections\n /** Routes as code (AD-3/AD-11): records, or the `(collections) => records` derive. */\n routes: readonly RouteRecord[] | ((collections: CollectionDef[]) => RouteRecord[])\n /**\n * The repo's page-builder components (manifest + implementation pairs — the\n * SAME list the studio composes with, usually `site.components` via the\n * `{ ...site }` spread). The host registers exactly this list (EC-591): the\n * engine adds no components of its own — the shipped ones are the opt-in\n * `@elytracms/components` package, listed in the config. For `next/image`\n * delivery of the image-role component, pass the list through\n * `withNextImage(site.components)` (one line, opt-in).\n *\n * Typed as the CONFIG's `HostComponentEntry` (`implementation: unknown`), not\n * the stricter {@link HostComponent}, so that `createElytraHost({ ...site })`\n * — the shape EC-532 makes the contract — compiles however the consumer typed\n * their project entry. It previously demanded `ComponentType`, which meant the\n * recommended spread worked only when the entry's implementation types\n * survived inference: `apps/demo/elytra.config.ts` gets away with it via\n * `satisfies`, and anyone using the engine's own published `HostComponentEntry`\n * / `WorkspaceProjectEntry` annotation did not. Both production consumers hit\n * exactly that and each carried one hand-restated `components` line (EC-539).\n *\n * What the widening costs, and where it is paid (EC-541): a list passed HERE\n * gets no compile-time check that each `implementation` is really a\n * component. The check is RUNTIME instead — `defineHostComponents` takes the\n * same `HostComponentEntry` shape (no cast, one signature) and tests every\n * implementation on registration; a value that is not a component becomes an\n * `invalid-implementation` issue in `components.registry.issues` and is withheld from\n * the implementation map, so its nodes render the visible missing-component\n * fallback rather than crashing the server render. Authoring a component\n * module as the strict {@link HostComponent} keeps the compile-time check at\n * the place the implementation is written.\n */\n components?: readonly HostComponentEntry[]\n /**\n * The public base URL of the delivery site (the config's project-level\n * `frontendUrl`). The handle's {@link ElytraHost.frontendUrl} is the RESOLVED one —\n * the bound environment's own URL when it declares one (EC-583).\n */\n frontendUrl?: string\n /**\n * Env bag; defaults to `process.env`. Documented names (see `host-env.ts`):\n * `ELYTRA_CONVEX_URL`, `ELYTRA_PROJECT_ID`, `DRAFT_PREVIEW_TOKEN`,\n * `REVALIDATE_SECRET`.\n */\n env?: Record<string, string | undefined>\n /**\n * The no-backend content fallback, used whenever `ELYTRA_CONVEX_URL` +\n * `ELYTRA_PROJECT_ID` are not both set. Default: a build-safe EMPTY source —\n * consumer CI builds green with no backend, and every URL is a clean 404\n * until content exists — in every cache state, not only a cold one, because\n * the content mode is part of the route's data-cache key (see\n * {@link ElytraContentMode}). The starter template overrides this with its\n * seeded in-memory world.\n */\n fallbackContent?: (request: CanvasContentRequest) => Promise<ContentSource>\n /** Chrome-globals conventions (settings/navigation collection ids, items fields). */\n globals?: GlobalsConventions\n /** Fetch implementation override for the live snapshot loader (tests, proxies). */\n fetch?: typeof globalThis.fetch\n}\n\n/** Pass-through canvas-route options the kit does not own. */\nexport interface HostCanvasRouteOptions {\n /** Tag observation hook (see `CanvasRouteOptions.onTags`). */\n onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void\n /** Metadata override (see `CanvasRouteOptions.metadata`). */\n metadata?: (result: ResolvePageResult, info: CanvasRequestInfo) => Metadata\n /**\n * Host-owned chrome around the rendered page (EC-524) — the seam a per-\n * collection detail template lives on. Branch on `ctx.collection` in your own\n * route file (see `CanvasRouteOptions.wrap`).\n */\n wrap?: (children: ReactNode, ctx: CanvasWrapContext) => ReactNode\n /**\n * The host's cache function for published requests (EC-620 — see\n * `CanvasRouteOptions.published`): your own `'use cache'` function that calls\n * `route.resolve(key)`, `route.applyCacheTags(outcome)` and picks its `cacheLife`.\n * Omit it and the route resolves every request fresh (dynamic, uncached).\n *\n * The route it builds is always scoped to the kit's {@link ElytraHost.contentScope}\n * with the kit's {@link ElytraHost.contentMode} in every cache key (EC-543), and\n * there is no override (EC-579): the kit's revalidate receiver derives its tags with\n * that same `contentScope`, so one value scopes both the entries and their\n * invalidation — a disagreement would drop nothing, silently.\n */\n published?: CanvasRouteOptions['published']\n /**\n * EC-618 — the analytics slot (see `CanvasRouteOptions.analytics`): its value is an\n * addon's beacon (`@elytracms/analytics/next`, unpublished pending counsel). Absent →\n * nothing renders and nothing is sent.\n */\n analytics?: CanvasRouteOptions['analytics']\n}\n\nexport interface LoadGlobalsOptions {\n /** Perspective of the chrome read. Default `'published'` (live is published-only). */\n perspective?: Perspective\n /**\n * The request's locale (EC-720) — the chrome's settings, menus and link paths are read in\n * it. Default: the project's default locale, which is every page's locale on a\n * single-locale site. On a multi-locale site pass the locale the page renders in: from a\n * layout in the catch-all segment, `resolveLocale({ pathname: pathFromSlug(slug) },\n * locales)` — the SAME split the catch-all resolves the page with — or a `wrap`\n * function's `ctx.locale`. A locale the project does not declare is an error, never a\n * silent read of the default.\n */\n locale?: Locale\n}\n\n/**\n * Which world {@link ElytraHost.loadContent} reads from.\n *\n * - `live` — the configured Convex deployment (published; token-gated draft).\n * - `fallback` — the host's own `fallbackContent` world (the scaffold's seeded\n * starter, the demo's in-memory fixture). A legitimate, supported mode.\n * - `empty` — no backend and no fallback: this project's structure with no\n * content, so every URL is a clean 404. Correct for a CI build; almost never\n * what a deployed site intends.\n *\n * The mode was decided from env long before EC-527 and readable by **nobody**,\n * so a site serving the fallback looked identical from the outside to one\n * reading the real deployment. Measured on the demo: `/` returned `200` while\n * all four of EC-512's published fixtures 404'd — an agent following AGENTS.md\n * §5 gets no signal that the thing under test was never exercised. Declared\n * once, read here, so a host can *say* which world it is in instead of\n * re-deriving it from `env` and drifting.\n *\n * **The mode is also part of the route's cache-entry key** (EC-543; since EC-515\n * an argument of the cached function). An entry can outlive the world that wrote\n * it and a hit never runs the loader, so until 0.0.85 the \"clean 404\" above held\n * only on a COLD cache: measured, a `next start` with both env vars blanked and a\n * warm `.next/cache` from a live run answered `200` with the live world's bytes\n * on every cached URL while reporting `empty` — the wrong-world failure EC-527\n * removed, one layer down. Keyed on the mode (`canvasCacheKey`, `cache.ts`), a\n * world change is a cold miss per URL, never a stale hit, in either direction.\n */\nexport type ElytraContentMode = 'live' | 'fallback' | 'empty'\n\n/** The delivery host handle {@link createElytraHost} yields. */\nexport interface ElytraHost<\n Collections extends readonly CollectionDef[] = readonly CollectionDef[],\n> {\n /**\n * The config's collections, as passed (EC-581) — typed by their literals when the\n * project declares them with `defineCollection`, so `InferDocument<typeof host, 'post'>`\n * types a delivered post with no generation step.\n */\n readonly collections: Collections\n /**\n * The cache-tag scope: `ELYTRA_PROJECT_ID` in live mode, else the config slug.\n * The ONE scope of this host (EC-579): its cache entries are written under it and\n * its revalidate receiver derives every invalidation tag with it.\n */\n contentScope: string\n /**\n * Which world {@link loadContent} reads from — see {@link ElytraContentMode}.\n * Read it to announce the mode (a boot line, a dev-only banner) rather than\n * re-deriving it from {@link env}, which is the drift this field exists to\n * prevent.\n */\n contentMode: ElytraContentMode\n /** The resolved env bag (documented names, trimmed, empty = unset). */\n env: ElytraHostEnv\n /**\n * The ONE resolved project (EC-583) — the options resolved through\n * `resolveProject`, the same rules the studio boots through: composed, defaulted,\n * validation slots normalized, routes materialized, every boot rule applied. The\n * kit reads its locales, collections, routes and frontend URL from here.\n */\n project: ResolvedProject\n /**\n * The project's route issue set at boot (EC-590 — the ONE router's: route conflicts,\n * mount conflicts, reachability). Surfaced on the console at build and in dev; the\n * editorial redirects (data) are judged per snapshot by the content client's\n * `routerIssues` and in the studio.\n */\n routeIssues: readonly CmsValidationIssue[]\n /** Materialized route records (the resolved project's; a derive ran once, at factory time). */\n routes: RouteRecord[]\n /** The registered component surface — exactly the `components` option's list (EC-591). */\n components: HostComponents\n /**\n * The public base URL of the delivery site, when configured — the resolved project's\n * (`projectFrontendUrl`): the bound environment's own `frontendUrl`, else the\n * project-level one. The same answer the studio's live links read (EC-583).\n */\n frontendUrl?: string\n /**\n * The content loader (the seam every kit surface reads through): live\n * delivery snapshot when env is configured — published, or the token-gated\n * draft for draft-mode requests — else the fallback source.\n *\n * An UNCACHED read: the kit caches nothing itself (EC-620 — caching is the host's).\n * Under `cacheComponents`, live-mode I/O in a layout or page outside `<Suspense>` fails\n * `next build` — call it inside a `<Suspense>`'d component (route handlers and\n * `sitemap.ts` just render dynamically).\n */\n loadContent(request: CanvasContentRequest): Promise<ContentSource>\n /**\n * Load + resolve the site-chrome globals (settings singleton, navigation by\n * slot alias, link/asset resolution — see `host-globals.ts`). One content\n * load per call; published perspective and the project's default locale by\n * default — pass the request's `locale` on a multi-locale site (EC-720).\n *\n * An UNCACHED read, like {@link loadContent}: under `cacheComponents`, render the\n * chrome that awaits it inside `<Suspense>` — and keep the canvas page OUTSIDE\n * every boundary, or its 404s and redirects answer HTTP 200 (measured, EC-515).\n */\n loadGlobals(options?: LoadGlobalsOptions): Promise<SiteGlobals>\n /** The catch-all canvas route pair for `app/[[...slug]]/page.tsx`. */\n canvasRoute(options?: HostCanvasRouteOptions): CanvasRoute\n /**\n * The instant-publish webhook handler for `app/api/revalidate/route.ts`, wired to\n * `REVALIDATE_SECRET` and to this host's own project — {@link contentScope} and the\n * config locales — which it derives the tags of every event with (EC-579). Unset\n * secret = endpoint closed (401), never a default secret. Only the secret can be\n * overridden: the scope and locales are the ones this kit's cache entries carry.\n */\n revalidateRoute(overrides?: Partial<Pick<RevalidateRouteOptions, 'secret'>>): RevalidateRouteHandlers\n /**\n * The draft-mode ENABLE route for e.g. `app/api/draft/route.ts` —\n * `GET /api/draft?token=…&redirect=/path`, gated by the ONE\n * `DRAFT_PREVIEW_TOKEN` (the same secret the draft snapshot fetch sends, so\n * the two can never disconnect). Unset token = drafts unreachable.\n */\n previewRoute(): PreviewRouteHandlers\n /** The draft-mode DISABLE route (no token — it only reduces visibility). */\n previewDisableRoute(): PreviewRouteHandlers\n /**\n * EC-617 — the PUBLIC INGEST route for `app/api/ingest/route.ts`: the site's one write path\n * (form submissions, analytics events, upload URLs), signed with `INGEST_SECRET` and\n * forwarded to this deployment's receiver (its HTTP origin derived from\n * `ELYTRA_CONVEX_URL`; pass `siteUrl` for a self-hosted backend). Unset secret or no\n * backend = 503, closed. The rate limit, the client key and the bot gate are the host's to\n * tune — the engine supplies the slots, never a vendor.\n */\n ingestRoute(options?: Partial<Omit<IngestRouteOptions, 'secret'>>): IngestRouteHandlers\n /**\n * Does the page for this catch-all `slug` OPEN full-bleed? Manifest-driven\n * (`presentation.fullBleed`, EC-368) and draft-mode aware — the host lays out\n * header clearance from data instead of a hand-kept component-id set. An\n * uncached read (see {@link loadContent}).\n */\n pageOpensFullBleed(slug: readonly string[] | undefined): Promise<boolean>\n}\n\n/**\n * Resolve the host's project entry through the ONE resolver (EC-583) — the rules the\n * studio boots through, so the delivery site refuses exactly what the studio refuses\n * (measured before this: an entry with a reserved field name or a rule its type cannot\n * keep stopped the studio and was served by the host). Throws a located message: a\n * host builds at boot, so that is where an invalid config fails, never per request.\n */\nfunction resolveHostProject(entry: Record<string, unknown>): ResolvedProject {\n const result = resolveProject(entry)\n if (result.ok) return result.project\n const lines = result.issues\n .filter((issue) => issue.severity === 'error')\n .map((issue) => {\n const at = issue.path.length > 0 ? ` at ${issue.path.join('.')}` : ''\n return ` • ${issue.code}${at}: ${issue.message}`\n })\n throw new Error(\n `Elytra delivery host: the project config \"${String(entry['slug'])}\" is invalid — the studio refuses it too:\\n${lines.join('\\n')}`,\n )\n}\n\n/**\n * Print the boot-time route issues once — during `next build` and in dev, never on every\n * production request (EC-590, 3h: \"surfaced at build and in dev\"). An `error` is a route\n * the site cannot serve as declared; a `warning` a precedence worth knowing.\n */\nfunction surfaceRouteIssues(slug: string, issues: readonly CmsValidationIssue[]): void {\n if (issues.length === 0) return\n const building = process.env['NEXT_PHASE'] === 'phase-production-build'\n if (!building && process.env['NODE_ENV'] === 'production') return\n const lines = issues.map((issue) => ` • ${issue.severity} ${issue.code}: ${issue.message}`)\n const report = `[elytra] project \"${slug}\": ${issues.length} route issue(s) —\\n${lines.join('\\n')}`\n if (issues.some((issue) => issue.severity === 'error')) console.error(report)\n else console.warn(report)\n}\n\n/** Create the delivery host handle — see the module docs for the full wiring. */\nexport function createElytraHost<const Collections extends readonly CollectionDef[]>(\n options: ElytraHostOptions<Collections>,\n): ElytraHost<Collections> {\n // The host-only keys stay here; everything else IS the project entry (EC-583).\n const { env: envOption, fallbackContent, globals, fetch: fetchOption, ...entry } = options\n const project = resolveHostProject(entry)\n const env = resolveHostEnv(envOption ?? process.env)\n const routes = project.routes\n // EC-590: the ONE router's issue set over the resolved routes — read by the doctor and\n // the studio too, and surfaced HERE at build and in dev (the host showed none before).\n const routeIssues = createRouter(project).issues\n surfaceRouteIssues(project.slug, routeIssues)\n const live = env.convexUrl !== undefined && env.projectId !== undefined\n\n // EC-527: half-configured is never deliberate. Someone set one of the pair\n // meaning to connect, and the site would otherwise serve the fallback world\n // while looking completely healthy — the exact silent-wrong-world failure this\n // ticket exists to remove, except in production. An explicit gap beats a\n // connected-shaped no-op; `revalidateRoute` already makes that same call about\n // its secret. Both unset stays silent: no-backend is a supported mode.\n if (!live && (env.convexUrl !== undefined || env.projectId !== undefined)) {\n const [present, missing] =\n env.convexUrl !== undefined\n ? [ELYTRA_HOST_ENV_KEYS.convexUrl, ELYTRA_HOST_ENV_KEYS.projectId]\n : [ELYTRA_HOST_ENV_KEYS.projectId, ELYTRA_HOST_ENV_KEYS.convexUrl]\n throw new Error(\n `Elytra delivery host: ${present} is set but ${missing} is not — live content needs both. ` +\n `Left as it is, this site would serve fallback content and look healthy. ` +\n `Set ${missing} to go live, or unset ${present} to run the no-backend fallback on purpose.`,\n )\n }\n\n const contentMode: ElytraContentMode = live\n ? 'live'\n : fallbackContent\n ? 'fallback'\n : 'empty'\n const contentScope = live ? (env.projectId as string) : project.slug\n\n // EC-591: exactly the given list — no engine-registered components, no default\n // image swap (that is the opt-in `withNextImage`). EC-539: the option is the\n // CONFIG's entry shape (`implementation: unknown`) so the recommended\n // `{ ...site }` spread compiles; EC-541: registration takes that shape too and\n // checks each implementation at runtime — no narrowing cast here.\n const components = defineHostComponents(project.components ?? [])\n\n // The build-safe default fallback: an EMPTY source over the config's own\n // structure. Constructed once — it is fully static.\n const emptySource: ContentSource = createStaticContentSource({\n projectId: contentScope,\n locales: project.locales,\n routes,\n // Redirects are editorial DATA (EC-303) — the no-backend build has none.\n redirects: [],\n collections: project.collections,\n documents: [],\n assets: [],\n })\n\n const loadContent = (request: CanvasContentRequest): Promise<ContentSource> => {\n if (live) {\n return loadLiveContent(\n {\n convexUrl: env.convexUrl as string,\n projectId: env.projectId as string,\n locales: project.locales,\n collections: project.collections,\n routes,\n ...(env.draftToken ? { draftToken: env.draftToken } : {}),\n ...(fetchOption ? { fetch: fetchOption } : {}),\n },\n request.perspective,\n )\n }\n if (fallbackContent) return fallbackContent(request)\n return Promise.resolve(emptySource)\n }\n\n const loadGlobals = async (globalsOptions: LoadGlobalsOptions = {}): Promise<SiteGlobals> => {\n const perspective = globalsOptions.perspective ?? 'published'\n const source = await loadContent({ perspective })\n return resolveGlobals(source, {\n perspective,\n // EC-720: the request's locale, judged against the project's locales by the core.\n ...(globalsOptions.locale !== undefined ? { locale: globalsOptions.locale } : {}),\n ...(globals ? { conventions: globals } : {}),\n })\n }\n\n const canvasRoute = (routeOptions: HostCanvasRouteOptions = {}): CanvasRoute =>\n createCanvasRoute({\n ...routeOptions,\n loadContent,\n components,\n // EC-620: the route caches nothing itself; these reach the host's own cache\n // function. Tag scope = `contentScope`, the SAME value `revalidateRoute` derives\n // its invalidation tags with (EC-579) — so `route.applyCacheTags` writes exactly\n // the strings the receiver revalidates. EC-543: the mode rides in every cache key\n // the route hands out, so no world ever reads another's entries. Spread last:\n // neither can be overridden.\n scope: contentScope,\n mode: contentMode,\n })\n\n const revalidateRoute = (\n overrides: Partial<Pick<RevalidateRouteOptions, 'secret'>> = {},\n ): RevalidateRouteHandlers =>\n createRevalidateRoute({\n // An unset secret keeps the endpoint CLOSED (401) — the kit never ships a\n // default secret (an auth-shaped no-op is worse than an explicit gap).\n secret: 'secret' in overrides ? overrides.secret : env.revalidateSecret,\n // EC-579: the receiver's OWN project — the scope its cache entries are written\n // under (`canvasRoute` above) and every locale it serves. The emitter sends only\n // the event, so this is the one place the scope and locales are read for it.\n scope: contentScope,\n locales: project.locales.locales,\n })\n\n // ONE secret by construction: the preview gate and the draft snapshot fetch\n // both read env.draftToken — they cannot disconnect (EC-369 finding #4).\n const previewRoute = (): PreviewRouteHandlers => createPreviewRoute({ token: env.draftToken })\n const previewDisableRoute = (): PreviewRouteHandlers => createPreviewDisableRoute()\n // EC-617: one secret name (`INGEST_SECRET`), the receiver on this deployment's HTTP origin.\n const ingestRoute = (overrides: Partial<Omit<IngestRouteOptions, 'secret'>> = {}): IngestRouteHandlers =>\n createIngestRoute({\n ...overrides,\n secret: env.ingestSecret,\n siteUrl: 'siteUrl' in overrides ? overrides.siteUrl : convexSiteUrl(env.convexUrl),\n })\n\n const pageOpensFullBleed = async (slug: readonly string[] | undefined): Promise<boolean> => {\n const perspective = await requestPerspective()\n const source = await loadContent({ perspective })\n // EC-720: the catch-all's own request split — this hint reads the page of the locale\n // the page renders in, derived where the route derives it.\n const { resolved } = resolveRequestPage(source, pathFromSlug(slug), perspective)\n return firstBlockFullBleed(resolved.document, components.registry)\n }\n\n const frontendUrl = projectFrontendUrl(project)\n\n return {\n routeIssues,\n collections: options.collections,\n contentScope,\n contentMode,\n env,\n project,\n routes,\n components,\n ...(frontendUrl !== undefined ? { frontendUrl } : {}),\n loadContent,\n loadGlobals,\n canvasRoute,\n revalidateRoute,\n previewRoute,\n previewDisableRoute,\n ingestRoute,\n pageOpensFullBleed,\n }\n}\n","/**\n * @elytracms/next — the embedded runtime (EC-144, vision AD-1): render\n * builder-managed pages inside the user's own Next.js App Router repo with\n * the user's own components. Drop `createCanvasRoute` into a catch-all\n * route, register components with `defineHostComponents`, done.\n *\n * Nothing here imports builder-only code (no studio, no operations, no\n * TanStack) — only the runtime packages: content accessors, the component\n * registry, and the runtime renderer.\n */\nexport const PACKAGE = '@elytracms/next'\n\n// Component registration — exactly the config's list (EC-591: the engine registers none).\nexport { defineHostComponents } from './components'\nexport type { HostComponent, HostComponents } from './components'\n\n// The server component. EC-499: `bodyItemsOf` is the body selection it renders\n// (declared `presentation.body`, else the EC-458 value-shape pick) — exported so\n// a host that renders a document body itself splits it the same way delivery does.\nexport { CanvasRenderer, bodyItemsOf } from './canvas-renderer'\nexport type { CanvasRendererProps, DeliveryPresentation } from './canvas-renderer'\n// EC-581 6a: one listing's baked result on the render outcome — documents, or why it could not run.\nexport type { BakedListing } from './canvas-renderer'\n\n// EC-574: the binding payload surface (`createBindingResolver`, `BindingPayloads`,\n// `SourcePayloadError`, `defaultBindingPayloads`, the route `payloads` option) and\n// the EC-166 source materialization (`materializeSourcePayloads`,\n// `collectBindingSourceIds`, `CanvasDataSource`) went with the data-binding\n// subsystem. Props are static values; dynamic data lives in the component (AD-12).\n\n// Content source seam + cache-tag merging (EC-145/146 plug in here).\nexport { mergeCacheTags } from './source'\nexport type { ContentSource } from './source'\n\n// PersistenceAdapter → ContentLookup snapshot bridge, plus the plain-data\n// assembler for hosts that prefetched the content themselves (EC-156).\nexport { createContentSnapshot, createStaticContentSource } from './snapshot'\nexport type { ContentSnapshot, ContentSnapshotOptions, StaticContentData } from './snapshot'\n\n// Catch-all route helper (pure core + Next wiring).\nexport { canvasBeaconProps, pathFromSlug, resolveCanvasPage, splitLocalePath } from './route-core'\n// The locale ⇄ URL mapping, both directions from one definition (EC-486), and\n// the request-side capability over it (EC-531): `resolveLocale` answers which\n// locale a request is for, `textDirection` gives the `dir` a locale renders under.\n// A multi-locale host renders `<html lang dir>` from these; a single-locale host\n// needs neither (its `locales.default` is correct by definition).\nexport { localeUrlPath, resolveLocale, textDirection } from './locale-url'\nexport type { LocaleRequest, LocaleStrategy, LocaleUrlPath } from './locale-url'\nexport type { CanvasPageOutcome, CanvasRenderOutcome, ResolveCanvasPageOptions } from './route-core'\n// EC-515: `CANVAS_VALIDATION_SLUG` is the catch-all's reserved build-time\n// validation sample (`route.generateStaticParams`, needed when the host enables\n// cacheComponents).\n// EC-595: `requestPerspective` is THE draft-mode → perspective mapping the route uses,\n// exported so a host's own server component reads the request's perspective the same\n// way instead of re-deriving it.\nexport { CANVAS_VALIDATION_SLUG, createCanvasRoute, requestPerspective } from './route'\n// EC-620: the key the route hands the host's own `'use cache'` function (`published`).\nexport type { CanvasCacheKey } from './cache'\nexport type {\n // EC-618: what the canvas route's `analytics` slot is told (the slot's value is an addon's beacon).\n CanvasBeaconProps,\n CanvasContentRequest,\n CanvasRequestInfo,\n CanvasRoute,\n CanvasRouteOptions,\n CanvasRouteProps,\n // EC-524: what a host's `wrap` function is told — the collection to branch on,\n // the resolved document, and the whole resolution.\n CanvasWrapContext,\n} from './route'\n\n// Per-page SEO → Next metadata, and sitemap.xml from published routes (EC-171).\nexport {\n mergeMetadataOverParent,\n openGraphLocale,\n seoContextOf,\n seoMetadata,\n seoObjectOf,\n} from './metadata'\nexport type { SeoMetadataContext } from './metadata'\nexport { canvasSitemapEntries, createCanvasSitemap } from './sitemap'\nexport type {\n CanvasSitemapOptions,\n CanvasSitemapResult,\n CreateCanvasSitemapOptions,\n SitemapSkippedRoute,\n} from './sitemap'\n\n// Cache tags & instant publish (EC-146): tag format, the event → tags derivation,\n// and the signed revalidation webhook receiver. The cache scope is the HOST's\n// (EC-620): the kit holds no `'use cache'` function — a host's own one declares a\n// result's tags through `route.applyCacheTags`. EC-579: the wire itself (event\n// schema, signer, header) is `@elytracms/core/revalidation`, re-exported so a host\n// that signs its own event imports one package.\nexport {\n collectionCacheTag,\n docCacheTag,\n nextCacheTags,\n projectSweepTag,\n revalidationTags,\n routeCacheTag,\n scopeCacheTag,\n} from './cache'\nexport type { RevalidationProject } from './cache'\nexport {\n REVALIDATE_SIGNATURE_HEADER,\n revalidationEventSchema,\n signRevalidationBody,\n signedRevalidationRequest,\n} from '@elytracms/core/revalidation'\nexport type { RevalidationEvent, RevalidationEventKind } from '@elytracms/core/revalidation'\nexport { evaluateRevalidateRequest } from './revalidate-core'\nexport type { RevalidateEvaluation } from './revalidate-core'\nexport { createRevalidateRoute } from './revalidate'\nexport type { RevalidateRouteHandlers, RevalidateRouteOptions } from './revalidate'\n// EC-617: the public ingest route — the pure core (method, same-origin, limiter, size, shape,\n// the host's bot gate) and the Next wrapper that signs and forwards to the one receiver.\nexport {\n createFixedWindowLimiter,\n defaultIngestClientKey,\n evaluateIngestRequest,\n isCrossOrigin,\n readCappedBody,\n} from './ingest-core'\nexport type { BotGateVerdict, FixedWindowLimiter, IngestBotGate, IngestEnrich, IngestEvaluation } from './ingest-core'\nexport { createIngestRoute } from './ingest'\nexport type { IngestRouteHandlers, IngestRouteOptions } from './ingest'\n\n// Asset-pipeline image delivery (EC-147) + the host-image renderer (EC-368):\n// `withNextImage` (EC-591) swaps the image-role component's implementation for\n// next/image — opt-in, one line in the host seam file; `elytraHostImage` is the\n// next/image-backed renderer a host registers on the `@elytracms/next/host-image`\n// bridge (the bridge itself is its OWN subpath — importing it from here would\n// bundle a second singleton instance).\nexport { ElytraImage, createAssetImageLoader, elytraHostImage, withNextImage } from './image'\nexport type {\n AssetImageLoaderOptions,\n ElytraHostImageOptions,\n ElytraImageAsset,\n ElytraImageProps,\n NextImageOptions,\n} from './image'\n\n// The delivery host kit (EC-368): one factory over (env, project config)\n// yielding every engine-shaped host surface — content loading, the catch-all\n// route, revalidate, draft-mode plumbing, chrome globals, layout hints.\nexport { createElytraHost } from './host-kit'\nexport type {\n ElytraContentMode,\n ElytraHost,\n ElytraHostOptions,\n HostCanvasRouteOptions,\n LoadGlobalsOptions,\n} from './host-kit'\nexport { ELYTRA_HOST_ENV_KEYS, resolveHostEnv } from './host-env'\nexport type { ElytraHostEnv } from './host-env'\nexport { assembleLiveSource, loadLiveContent } from './host-content'\nexport type { LiveContentOptions } from './host-content'\nexport { resolveGlobals } from './host-globals'\nexport type {\n GlobalsConventions,\n ResolveGlobalsOptions,\n ResolvedLink,\n ResolvedNavEntry,\n ResolvedNavLink,\n SiteGlobals,\n} from './host-globals'\nexport { firstBlockFullBleed } from './full-bleed'\n\n// Draft preview (token-gated draftMode integration).\nexport { evaluatePreviewRequest, safeRedirectTarget } from './preview-core'\nexport type { PreviewEvaluation } from './preview-core'\nexport { createPreviewDisableRoute, createPreviewRoute } from './preview'\nexport type { PreviewRouteHandlers, PreviewRouteOptions } from './preview'\n\n// Re-exports so host apps can author manifests, declare their config, and shape\n// content WITHOUT depending on the internal packages (or on zod) directly — a\n// consuming repo installs ONLY `@elytracms/next` for delivery (EC-193).\nexport { defineComponent } from '@elytracms/core/component-registry'\n// EC-190: component props are field-defs (`PropField`), not the retired `PropSpec`.\nexport type { ComponentManifest, PropField, SlotSpec } from '@elytracms/core/component-registry'\nexport type { ComponentImplementations, RenderAsset } from '@elytracms/core/runtime-renderer'\nexport { z } from 'zod'\n\n// Config + content shapes a host declares (collections, locales, routes,\n// documents) — the public surface of the structure that lives in the repo\n// (AD-11). Re-exported here so the host imports one package (EC-193).\nexport {\n documentSchema,\n routeRecordSchema,\n redirectRecordSchema,\n localeConfigSchema,\n parseLocaleConfig,\n} from '@elytracms/core/cms-core'\nexport type {\n CollectionDef,\n CmsDocument,\n FieldDef,\n LocaleConfig,\n LocaleConfigInput,\n Locale,\n RouteRecord,\n RedirectRecord,\n} from '@elytracms/core/cms-core'\n// Composition node shape (a page document's `body` is a ComponentNode list, per AD-12).\nexport type { ComponentNode } from '@elytracms/core/project-graph'\n// The delivery perspective — `'published'` or one variant row, `{ variant }` (EC-595\n// binding point 1) — with its draft constant and the one accessor a host callback\n// (`wrap`, `metadata`, `onTags`, `fallbackContent`) reads it through.\nexport { DRAFT_PERSPECTIVE, variantOf } from '@elytracms/core/cms-core'\nexport type { Perspective } from '@elytracms/core/cms-core'\n// The delivery shape of an asset (resolved url + metadata).\nexport type { AssetRecord } from '@elytracms/core/persistence'\n"]}
|