Delivery (@glw907/cairn-cms/delivery)
This subpath is the public read model for a SvelteKit site. It carries the catch-all route loader,
the public route-data types, and the feed, sitemap, and robots responders. Import it from a
+server.ts or a +page.server.ts in the reader-facing site. The matching head
component, CairnHead, lives one level down at /delivery/head.
import { createPublicRoutes } from '@glw907/cairn-cms/delivery';
The TypeScript types in src/lib are the source of truth, and the export-coverage gate checks every
name here against them.
The re-exported /delivery/data surface
/delivery re-exports the entire /delivery/data surface: the index builders,
the feed, sitemap, and robots builders and responders, the SEO head builder, and the small pure
helpers. Those symbols are documented on the delivery-data reference and are not
repeated here. This page covers only the names /delivery adds on top of that surface: the
createPublicRoutes loader factory and its route-data types.
A SvelteKit site usually imports the shared symbols through this barrel. The feed.xml, feed.json,
sitemap.xml, and robots.txt showcase servers all reach rssResponse, sitemapResponse, and
robotsResponse through @glw907/cairn-cms/delivery.
createPublicRoutes
Stability tier: Scaffold API.
function createPublicRoutes(deps: PublicRoutesDeps): {
entryLoad: (event: { url: URL }) => Promise<EntryData>;
entries: () => { path: string }[];
};
Build the public route loader for a site’s unified index. Pass the
PublicRoutesDeps: the built site resolver, the render function, the origin, and
the SEO defaults. The returned object carries entryLoad, the one loader the catch-all route calls,
and entries, the prerender enumerator. entryLoad resolves one entry by request path and folds in
the rendered html, the SEO head, and the hero; it throws error(404) on a miss.
The showcase wires entryLoad and entries into its [...path] catch-all server. The
+page.server.ts calls entryLoad and the +page.svelte renders the entry directly.
import type { PageServerLoad, EntryGenerator } from './$types';
import { createPublicRoutes } from '@glw907/cairn-cms/delivery';
import { site, ORIGIN, SITE_DESCRIPTION } from '$lib/content';
import { cairn, siteConfig } from '$lib/cairn.config';
export const prerender = true;
const routes = createPublicRoutes({
site,
render: cairn.rendering.render,
origin: ORIGIN,
siteName: siteConfig.siteName,
description: SITE_DESCRIPTION,
defaultImage: ORIGIN + '/og/default.png',
feeds: { rss: ORIGIN + '/feed.xml', json: ORIGIN + '/feed.json' },
});
export const entries: EntryGenerator = () => routes.entries();
export const load: PageServerLoad = async ({ url }) => {
// entryLoad resolves one entry by request path and throws error(404) on a miss. The returned
// payload carries the rendered html, the SEO head, and the hero.
return routes.entryLoad({ url });
};
Route-data types
The shapes the public loaders return and consume. A template reads the loaded data; a server passes the deps.
PublicRoutesDeps
Stability tier: Extension API.
interface PublicRoutesDeps {
site: SiteResolver;
render: SiteRender;
origin: string;
siteName: string;
description: string;
feeds?: { rss?: string; json?: string };
defaultImage?: string;
resolveMedia?: MediaResolve;
}
The injected dependencies for the public loaders. render turns an entry’s markdown into html,
origin and feeds build the absolute URLs in the head, and description and defaultImage are the
site-wide fallbacks for an entry that declares none. resolveMedia resolves a frontmatter media:
hero reference to its delivery path; the site builds it from its committed media.json exactly as it
builds the body resolver, and when it is absent no heroImage projection is derived.
EntryData
Stability tier: Extension API.
interface EntryData {
concept: string;
entry: ContentEntry;
html: string;
canonicalUrl: string;
seo: SeoMeta;
newer?: ContentSummary;
older?: ContentSummary;
heroImage?: { url: string; absoluteUrl?: string; alt: string; caption?: string };
}
One entry’s data: the detail entry, its rendered html, its canonical URL, the SEO head, and the
adjacent entries for prev and next links. entryLoad returns this shape. heroImage is a derived
projection of the frontmatter image field, resolved through resolveMedia: url is the
root-relative path for an <img> and absoluteUrl the origin-anchored form for the og:image. The
canonical token is left untouched, so entry.frontmatter.image.src stays the media: token, and
heroImage is undefined when no hero is set, media is off, or the reference does not resolve.
CairnHead
Stability tier: Extension API.
import { CairnHead } from '@glw907/cairn-cms/delivery/head';
<CairnHead seo={SeoMeta} title={string | false} titleTemplate={(title: string) => string} />
Render a page’s SEO head from a SeoMeta object into <svelte:head>: a title,
meta tags, link tags, and one escaped JSON-LD script. The title renders from seo.title by default;
title={false} lets the site own the <title>, and a string overrides it. titleTemplate carries the
site’s own title-suffix convention (for example (t) => ${t} · 907.life\) and applies to seo.title
only when title is left undefined, so an explicit title or title={false} still wins. The
component carries no CSS, so it pulls in no admin styles. The showcase mounts it from the seo field
the catch-all loader returns.
<script lang="ts">
import type { PageData } from './$types';
import { CairnHead } from '@glw907/cairn-cms/delivery/head';
let { data }: { data: PageData } = $props();
</script>
<CairnHead seo={data.seo} />
<article>
<h1>{data.entry.title}</h1>
{@html data.html}
</article>
CairnHead imports from @glw907/cairn-cms/delivery/head, the component-free split that keeps the
data surface node-safe. A .svelte component would pull Svelte into the module graph, so the
plain-data builders live on /delivery/data and the one component lives on its
own /delivery/head entry. A plain-Node tool can import the builders without ever resolving a
component.