Skip to content

API

@ogygia/content

Collections, sources, the site layer and shells.

On this page

This page is generated from the package’s own .d.ts files on every build, so the signatures, descriptions and examples below always match the source.

import { … } from '@ogygia/content';

Exports blocks · build_docs · content · create_search · date_of · dated · defineSource · dimensions · enrich · folder · get_shell_context · glob · href_of · is_dimensioned · json · links · mapRaw · markdown · mountBase · numbered · orama_engine · order_of · outline · parseSchema · pick · roving · search · site · split_sections · strip_order_prefix · strip_prose · title_case · toRawSource · BlogList · BlogPost · BlogShell · BottomBar · CodeChrome · Doc · Frame · Link · OnThisPage · Pager · Search · SearchPage · Sheet · Sidebar · SiteSlot · Tab · TabGroup · ThemeToggle · fields · ContentHandle · Convention · Dimensioned · Outline · RovingOptions · Site · WithRemotes · Axis · BaseOption · BlockNode · BlockRegistry · BlockSchedule · BlockSource · BlogPostRef · ChangeFields · Check · CheckContext · Collection · ContentEntry · ContentMode · ContentOptions · ContentRef · ContentRelations · Coordinate · Crumb · DimensionsSpec · EmitHandler · EmitOptions · Entry · EntryParts · Fallback · Finding · FolderOptions · Format · GetRemote · GlobMap · GroupMeta · GroupSpec · Heading · LinkOptions · LinkRef · LinkSpec · ListRemote · LlmsEmitOptions · MarkdownMeta · MetaDecoration · NavGroup · NavItem · NavLeaf · NavLink · NavRef · NavTree · NumberedOptions · OutlineNode · OutlineOptions · OutlineSpec · OutlineThunk · PageFields · PageView · PostFields · PrevNext · RawEmit · RawRecord · RawSource · ReadContext · RefEntry · Resolved · ResolvedBlockNode · SchemaLike · SearchBrain · SearchClient · SearchClientOptions · SearchDoc · SearchEngine · SearchHit · SearchIndex · SearchOptions · Selection · Severity · ShellContext · SiteData · SiteMeta · SiteOptions · Source · SourceChanges · SourceEntry · SourceRef · Switcher · SwitcherAxis · TrailScope

blocks

function

Build a blocks source. The registry is resolved into regions AT MINT (blocks.resolve), so an entry's body is a clean tree of regions — the registry never rides along. data carries any meta frontmatter the page declared. The body renders through an internal recomposer, the same one the no-collection recipe reproduces in ten lines.

function blocks(input: GlobMap | RawSource<unknown>, registry: BlockRegistry, opts?: {
  id?: (key: string) => string;
  schedule?: BlockSchedule;
}): Source;

build_docs

function

Build every searchable document for the outline's leaves (optionally scoped to collections).

function build_docs(ol: Outline, only?: Collection[]): Promise<SearchDoc[]>;

content

function

function content<Meta = Record<string, never>, Schema extends SchemaInput | undefined = undefined, Data extends Record<string, unknown> = Schema extends SchemaInput ? LayeredData<Schema> extends Record<string, unknown> ? LayeredData<Schema> : Record<string, unknown> : Record<string, unknown>>(opts: {
  loader: Source<Meta>;
  schema?: Schema;
  filter?: (entry: ContentRef<Data, Meta>, ctx: ReadContext) => boolean;
  relations?: (self: ContentHandle<Data, Meta>) => ContentRelations;
}): ContentHandle<Data, Meta>;

function

Create site.search. The index builds on first query (single-flight), from ALL leaves (or the scoped subset — each distinct scope gets its own memoized index). Rebuilds when any contributing collection's catalog version changes (live sources).

function create_search(ol: Outline, engine: SearchEngine): SearchBrain;

date_of

function

Read the date prefix off one segment as ISO YYYY-MM-DD (whatever the authored format), or null when the segment isn't date-prefixed / the date is impossible. Exported so an app can recover the date for DISPLAY from filePath — the convention strips it from the slug.

function date_of(segment: string, format?: string): string | null;

dated

function

The dated convention: date-prefixed segments order siblings chronologically (order = days since epoch, so refs() come back oldest→newest; a blog index reverses). The date is stripped from the slug — URLs stay /blog/release, not /blog/2026-08-13-release — and recoverable for display via date_of on the entry's filePath. Undated segments (directories, one-off pages) pass through unordered. Verification is deliberately loose: only an UNPARSEABLE date-looking prefix is an error — mixing dated posts with undated pages is normal for a blog.

function dated(opts?: DatedOptions): Convention;

defineSource

function

Compose a raw source + a format into a finished Source. Threads init/live/groups.

function defineSource<V, Meta = Record<string, never>>(raw: RawSource<V>, format: Format<V, Meta>, extra?: {
  init?: () => Promise<void>;
  groups?: () => Promise<Map<string, GroupMeta>>;
}): Source<Meta>;

dimensions

function

Mint a dimensioned outline — the i18n/versioning wrapper site() consumes like any outline.

function dimensions(spec: DimensionsSpec): Dimensioned;

enrich

function

Enrich every entry's meta with derived facts — SOURCE middleware, the read-side sibling of mapRaw. The enricher sees the finished ref (id, data, meta, filePath) and returns extra meta merged over it, on refs() and get() alike. Loader-agnostic: git_meta() / reading_time() style values work over folder() and a CMS the same way (a CMS that already has the fact simply ships it in data and skips the enricher).

content({ loader: enrich(folder(map), reading_time()) })

function enrich<Meta, Extra extends Record<string, unknown>>(src: Source<Meta>, fn: (ref: SourceRef<Meta>) => Extra | Promise<Extra>): Source<Meta & Extra>;

folder

function

function folder<Meta = Record<string, never>>(map: GlobMap, opts?: FolderOptions<Meta>): Source<Meta>;

get_shell_context

function

function get_shell_context(): ShellContext | undefined;

glob

function

Wrap an import.meta.glob(...) map as a raw source: ids are the (prefix-stripped, extension-less) keys, get(id) loads ONE module, refs() loads all. Lazy globs load a module only when read — so get(id) on a big local collection touches one file, not the whole set.

function glob<V = unknown>(globMap: GlobMap, opts?: {
  id?: (key: string) => string;
}): RawSource<V>;

href_of

function

Compose the mount base and a slug into an href. Links bypass this.

function href_of(base: string, slug: string): string;

is_dimensioned

function

Narrow an unknown to a Dimensioned outline.

function is_dimensioned(x: unknown): x is Dimensioned;

json

function

JSON modules — a plain object, or { default: object }. Data-only (no body).

function json(input: Input<unknown>, opts?: BuilderOpts): Source;

function

The link audit as a check value. Validates each page's in-prose markdown links (meta.links, collected by the markdown format) against the site's own address space — missing pages, missing anchors, and stale redirect links. A blocks/CMS corpus that collects no meta.links is warned about once (dev), then passes (any format can fill meta.links to opt in).

site({ outline: docs, checks: [links()] }) site({ outline: docs, checks: [links({ anchors: false, redirected: 'error' })] })

mapRaw

function

Transform every raw record's value through fn — raw-source middleware for building custom loaders (e.g. a CMS adapter that maps its JSON shape to what blocks()/markdown() expect). Threads init/live/groups through unchanged.

function mapRaw<A, B>(src: RawSource<A>, fn: (value: A) => B): RawSource<B>;

markdown

function

.svx / .md content compiled by the markdown pipeline — body is the component, meta has headings.

function markdown(input: Input<unknown>, opts?: BuilderOpts): Source<MarkdownMeta>;

mountBase

function

Derive the mount prefix by subtraction: the catch-all's url.pathname minus its matched slug is the group's base. Call it once in the layout — the one place guaranteed to hold the request. Root mount → ''; a (docs) group at /docs → '/docs'. Composes with paths.base (already in the pathname). No global config, no registry.

function mountBase(url: URL | string | {
  pathname: string;
}, slug: string): string;

numbered

function

The BLESSED convention: NN- prefixes order siblings, stripped from slugs, labels title-cased. Its verify makes the convention self-checking wherever it is actually in use — a directory with NO prefixed children is simply not ordered (fine); one prefixed child means ALL siblings commit. A directory's +meta.json { "ordered": false } exempts it entirely.

function numbered(opts?: NumberedOptions): Convention;

orama_engine

function

The default engine — Orama over the section documents, title/heading boosted.

function orama_engine(): SearchEngine;

order_of

function

Read a leading NN- prefix as a number; missing prefix sorts last. 00-start → 0.

function order_of(segment: string): number;

outline

function

Weave any number of collections + an arrangement into one derived Outline.

function outline(spec: OutlineSpec, opts?: OutlineOptions): Outline;

parseSchema

function

function parseSchema(schema: SchemaLike | undefined, data: unknown, label: string): Promise<Record<string, unknown>>;

pick

function

pick(collection, ...patterns) — a subset in pattern order (exact ids or */** globs).

function pick(collection: Collection, ...patterns: string[]): Selection;

roving

function

Attach roving-tabindex behavior to container. The entry tab stop is the CURRENT item (or the first if none); arrows/Home/End move focus and carry the single tabindex="0" with it.

function roving(opts: RovingOptions): (container: HTMLElement) => (() => void) | undefined;

function

The headless search handle for bespoke chrome — const s = search({ base: '/docs' }), then s.query(q). One argument, convention-first. (The <Search> brick uses this internally; inside a <Shell> it needs no arguments at all.)

function search(opts?: SearchClientOptions): SearchClient;

site

function

Mint the site brains. site({ outline }) is the only required key; a bare collection auto-arranges.

function site(opts: SiteOptions): Site;

split_sections

function

Split a page's source into { heading, text } chunks aligned to its collected headings.

function split_sections(source: string, headings: Heading[]): {
  heading: Heading | null;
  text: string;
}[];

strip_order_prefix

function

Strip a leading NN- ordering prefix from one path segment. 00-start → start.

function strip_order_prefix(segment: string): string;

strip_prose

function

Light markdown/svx strip for index text: fences, tags, inline markers. Not a parser — good enough for tokenization.

function strip_prose(src: string): string;

title_case

function

data-state → Data State. The default label when no +meta.json overrides it.

function title_case(slug: string): string;

toRawSource

function

Turn a source builder's input (a glob map, or a raw source you wrote) into a raw source.

function toRawSource<V>(input: GlobMap | RawSource<V>, opts?: {
  id?: (key: string) => string;
}): RawSource<V>;

BlogList

component

import BlogList from '@ogygia/content';

BlogPost

component

import BlogPost from '@ogygia/content';

BlogShell

component

import BlogShell from '@ogygia/content';

BottomBar

component

import BottomBar from '@ogygia/content';

CodeChrome

component

import CodeChrome from '@ogygia/content';

Doc

component

import Doc from '@ogygia/content';

Frame

component

import Frame from '@ogygia/content';

component

OnThisPage

component

import OnThisPage from '@ogygia/content';

Pager

component

import Pager from '@ogygia/content';

Search

component

import Search from '@ogygia/content';

SearchPage

component

import SearchPage from '@ogygia/content';

Sheet

component

import Sheet from '@ogygia/content';

component

SiteSlot

component

import SiteSlot from '@ogygia/content';

Tab

component

import Tab from '@ogygia/content';

TabGroup

component

import TabGroup from '@ogygia/content';

ThemeToggle

component

import ThemeToggle from '@ogygia/content';

fields

const

The blessed schema family. page is the universal base; post / change are pre-layered genre stacks (base + genre extras) consumed by content({ schema }), which merges array layers left→right.

const fields: {
  page: typeof page;
  post: [typeof page, typeof post_only];
  change: [typeof page, typeof change_only];
}

ContentHandle

interface

The browser-safe handle content() returns (read paths + graph; remotes come from withRemotes()).

interface ContentHandle<Data extends Record<string, unknown> = Record<string, unknown>, Meta = Record<string, never>> {/*…*/}

ContentHandle.refs

The corpus as metadata — all visible REFS (each with rel / backlinks), never bodies. ctx threads into the collection filter (preview, roles); default {} = the public projection.

refs(ctx?: ReadContext): Promise<ContentRef<Data, Meta>[]>;

ContentHandle.get

Resolve one entry to { id, data, meta, body, source, rel, backlinks } for rendering. body is an inline <Region>. Unknown / filtered-out id → null (the caller decides the 404).

get(id: string, ctx?: ReadContext): Promise<Entry<Data, Meta> | null>;

ContentHandle.groups

Directory/section decoration the source exposes (folder() from +meta.json, a CMS from folders).

groups(): Promise<Map<string, GroupMeta>>;

Convention

interface

The pluggable filename convention. folder() uses the blessed numbered instance unless you hand it your own — pass a partial to keep the blessed behavior for the pieces you don't override.

interface Convention {/*…*/}

Convention.segment

One raw source segment → its structural meaning. Blessed: 02-data-state → slug + order 2.

segment(raw: string): {
    slug: string;
    order: number;
  };

Convention.label

Default label for a clean slug (a +meta.json label still overrides).

label(slug: string): string;

Convention.verify

Verify one directory's SIBLING segments (raw, as authored). Runs once per directory during the folder scan; returned strings become named BUILD errors. dir is the clean path ('' = root); meta is the directory's own +meta.json, so { "ordered": false } can exempt a directory.

verify(dir: string, segments: string[], meta: MetaDecoration | undefined): string[];

Dimensioned

interface

An Outline with the coordinate extras site() surfaces (switcher, fallback, coordinate).

interface Dimensioned extends Outline {/*…*/}

Dimensioned.axes

axes: Record<string, Axis>;

Dimensioned.tree

The nav tree for coord (default coordinate when omitted), hrefs baked for base, read context ctx.

tree(base?: string, coord?: Coordinate, ctx?: ReadContext): Promise<NavTree>;

Dimensioned.coordinateOf

The coordinate encoded in a full slug (defaults filled for absent axes).

coordinateOf(slug: string): Coordinate;

Dimensioned.canonicalAddresses

The DEFAULT coordinate's bare addresses — the canonical set to index for search (so fallback pages under other coordinates don't show as duplicate hits).

canonicalAddresses(ctx?: ReadContext): Promise<string[]>;

Dimensioned.switcher

The switcher for the coordinate in slug, hrefs baked for base.

switcher(slug: string, base?: string, ctx?: ReadContext): Promise<Switcher>;

Dimensioned.fallbackOf

Which axis (if any) fell back resolving slug.

fallbackOf(slug: string, ctx?: ReadContext): Promise<Fallback>;

Outline

interface

The public Outline — collection-like over arrangement. Mostly consumed through pharos().

interface Outline {/*…*/}

Outline.tree

The full nav tree, hrefs resolved for base (default ''), in read context ctx (default public).

tree(base?: string, ctx?: ReadContext): Promise<NavTree>;

Outline.resolve

Resolve a slug to its collection + entry + placement, or null.

resolve(slug: string, ctx?: ReadContext): Promise<{
    record: Resolved;
    collection: Collection;
  } | null>;

Outline.addresses

Every leaf slug, in reading order — the prerender source (public projection by default).

addresses(ctx?: ReadContext): Promise<string[]>;

Outline.neighbors

Reading-order neighbors of a slug, as refs for base (default ''). scope: 'group' bounds prev/next to the top-level section.

neighbors(slug: string, base?: string, ctx?: ReadContext, scope?: TrailScope): Promise<{
    prev?: NavRef;
    next?: NavRef;
  }>;

Outline.slug_for

Map an entry id (within a collection) to its slug — for resolving graph relations.

slug_for(collection: Collection, entryId: string, ctx?: ReadContext): Promise<string | undefined>;

Outline.alias

Canonical slug for a declared old address (redirect history), or undefined.

alias(slug: string, ctx?: ReadContext): Promise<string | undefined>;

Outline.aliases

The whole redirect map: old slug → canonical slug.

aliases(ctx?: ReadContext): Promise<Map<string, string>>;

RovingOptions

interface

interface RovingOptions {/*…*/}

RovingOptions.selector

Which descendants are the roving items (e.g. .og-nav-link).

selector: string;

RovingOptions.orientation

Arrow axis. Nav rails are 'vertical' (Up/Down); a tab bar is 'horizontal' (Left/Right).

orientation?: "vertical" | "horizontal";

RovingOptions.loop

Wrap past the ends (default true).

loop?: boolean;

Site

interface

The site brains. All data-returning, all browser-safe.

interface Site {/*…*/}

Site.outline

The underlying outline, for tier-2 chrome that wants the raw tree/resolver.

outline: Outline;

Site.data

Site facts (title/description/origin), or undefined if none were declared. Shells read title.

data?: SiteData;

Site.components

The element-override component map (user overrides; SiteSlot adds the a → Link default). Shell provides this via context; tier-2 renders can provide it themselves.

components: Record<string, Component<Record<string, unknown>>>;

Site.load

SvelteKit load — the 404 guard, alias 308s, and (when enabled) the per-page link audit.

load: (event: LoadLike) => Promise<void>;

Site.entries

SvelteKit entries — every leaf slug PLUS declared old addresses (so redirect stubs bake).

entries: () => Promise<Array<{
    slug: string;
  }>>;

Site.nav

The sidebar tree, hrefs resolved for base (default ''). On a dimensions() site pass the current slug so the tree reflects that coordinate; ignored otherwise.

nav: (opts?: BaseOption & {
    slug?: string;
    context?: ReadContext;
  }) => Promise<NavTree>;

Site.switcher

On a dimensions() site: the version/locale switcher for the coordinate in slug (hrefs baked for base). null on a plain site. Serializable — the shell renders a <select>.

switcher: (slug: string, opts?: BaseOption & {
    context?: ReadContext;
  }) => Promise<Switcher | null>;

Site.meta

The whole SHELL bundle in one browser-safe call: { nav, switcher, data } for a slug. Feed it to <Shell {meta}> so the corpus stays server-only — this is what a meta remote returns.

meta: (opts?: BaseOption & {
    slug?: string;
    context?: ReadContext;
  }) => Promise<SiteMeta>;

Site.page

Everything one page position needs, or null for an unknown slug. Call in the page component. Pass context (e.g. { preview: true }) to see the same projection the load guard used.

page: <Data extends Record<string, unknown> = Record<string, unknown>, Meta = unknown>(slug: string, opts?: BaseOption & {
    context?: ReadContext;
  }) => Promise<PageView<Data, Meta> | null>;

Site.check

Run all checks over the whole corpus as plain data (never throws) — for vitest/CI, and for dynamic sites where the prerender crawler never runs.

check: (opts?: {
    base?: string;
    context?: ReadContext;
  }) => Promise<Finding[]>;

Site.search

Full-text search brain — lazy in-memory index over the collections' section documents. Query from a server load / remote; scope with { in: [collection] }. Server-side (or the worker over the emitted index); do not call over a glob collection in the browser.

search: SearchBrain;

Site.emit

Machine-facing serializations — each mints a GET handler for a +server.ts.

emit: {
    sitemap: (opts?: EmitOptions) => EmitHandler;
    llms: (opts?: LlmsEmitOptions) => EmitHandler;
    raw: (opts?: {
      frontmatter?: "keep" | "strip";
    }) => RawEmit;
    search: (opts?: EmitOptions) => EmitHandler;
    rss: (opts: RssEmitOptions) => EmitHandler;
  };

WithRemotes

interface

The server-side handle withRemotes() returns: the collection's read paths + its remotes.

interface WithRemotes<T extends Record<string, unknown>> {/*…*/}

WithRemotes.refs

refs(): Promise<ContentRef<T>[]>;

WithRemotes.get

get(id: string): Promise<Entry<T> | null>;

WithRemotes.list

Prerendered/query remote over the corpus refs (wire-safe metadata).

list<Out = {
    id: string;
    data: T;
  }>(options?: RefsOptions<T, Out>): ListRemote<Out>;

WithRemotes.live

live: {
    list<Out = {
      id: string;
      data: T;
    }>(options?: LiveRefsOptions<T, Out>): ListRemote<Out>;
    get<Out = {
      id: string;
      data: T;
    } | null>(options?: LiveGetOptions<T, Out>): GetRemote<Out>;
  };

Axis

type

One axis of the content matrix.

type Axis = {
  values: string[];
  default?: string;
  fallback?: boolean;
  label?: string;
};

BaseOption

type

Per-render href option — the mount prefix the outline is served under.

type BaseOption = {
  base?: string;
};

BlockNode

type

One node in a block tree: a type naming a registered block, its props, and nested children. A block's wake schedule is baked into its registry import (with { wake: 'load' }) or decided by a schedule resolver passed to <Blocks> — not carried on the node.

type BlockNode = {
  type: string;
  id?: string;
  props?: Record<string, unknown>;
  children?: BlockNode[];
};

BlockRegistry

type

Map from a block type name to the component it renders — a with { region: 'raw' } import.

type BlockRegistry = Record<string, unknown>;

BlockSchedule

type

The schedule resolver for a tree: (node.props) => { wake?, margin? }, forwarded to region().

type BlockSchedule = RegionSchedule<Record<string, unknown>>;

BlockSource

type

A block source: an array of nodes, a single node, or { blocks, meta } (frontmatter in meta).

type BlockSource = BlockNode | BlockNode[] | {
  blocks: BlockNode[];
  meta?: Record<string, unknown>;
};

BlogPostRef

type

A post as the blog INDEX lists it (<BlogList posts>): the display fields + its href. Map a collection's refs to this over the wire so the corpus stays server-side.

type BlogPostRef = {
  href: string;
  title: string;
  date: string;
  summary?: string;
  author?: string;
  tags?: string[];
};

ChangeFields

type

Extra fields a changelog entry carries beyond a page.

type ChangeFields = {
  version: string;
  date: string;
};

Check

type

A content check. Implement page (per-page, runs in load), site (whole-corpus), or both. A check with only page gets a default site that runs page over every address.

type Check = {
  name: string;
  page?: (slug: string, cx: CheckContext) => Finding[] | Promise<Finding[]>;
  site?: (cx: CheckContext) => Finding[] | Promise<Finding[]>;
};

CheckContext

type

What a check is handed: the address space + the current read context + the mount base.

type CheckContext = {
  outline: Outline;
  base: string;
  ctx: ReadContext;
};

Collection

type

A content collection, as the outline consumes it (the browser-safe content() handle).

type Collection = ContentHandle<Record<string, unknown>, unknown>;

ContentEntry

type

::: warning Deprecated Old name for ContentRef. A ref never carries a body; use Entry (from get()) for that. :::

type ContentEntry<Data = Record<string, unknown>, Meta = Record<string, never>> = ContentRef<Data, Meta>;

ContentMode

type

type ContentMode = "prerender" | "query";

ContentOptions

type

Options for a content collection (kept for reference; content() infers these in place).

type ContentOptions<Data extends Record<string, unknown> = Record<string, unknown>, Meta = Record<string, never>> = {
  loader: Source<Meta>;
  schema?: SchemaInput;
  filter?: (entry: ContentRef<Data, Meta>, ctx: ReadContext) => boolean;
  relations?: (self: ContentHandle<Data, Meta>) => ContentRelations;
};

ContentRef

type

A content REF — the shallow face of an entry, what refs() yields and the catalog holds: identity

  • validated data (+ source-derived meta, structural order, filePath, and graph fields). NO body, NO source text — those are heavy faces that live only on Entry, fetched by get(). "Refs are what a corpus admits to having; an entry is what one page pays for."
type ContentRef<Data = Record<string, unknown>, Meta = Record<string, never>> = {
  id: string;
  data: Data;
  meta: Meta;
  order?: number[];
  filePath?: string;
  rel?: Record<string, RefEntry | RefEntry[] | null>;
  backlinks?: RefEntry[];
};

ContentRelations

type

Declared relations: { name: collection }, or { get name() { return collection } } for a cycle. The frontmatter field named after each relation carries the target id(s): a string → one ref, a string[] → many.

type ContentRelations = Record<string, unknown>;

Coordinate

type

A point in the matrix — { version: 'v1', locale: 'fr' }.

type Coordinate = Record<string, string>;

Crumb

type

One breadcrumb step: a group label on the path from root to a leaf (href when it is a page).

type Crumb = {
  label: string;
  href?: string;
};

DimensionsSpec

type

type DimensionsSpec = {
  axes: Record<string, Axis>;
  resolve: (coord: Coordinate) => Outline | OutlineSpec | Promise<Outline | OutlineSpec>;
};

EmitHandler

type

A GET handler an emission mounts as. export const GET = site.emit.sitemap({ base }).

type EmitHandler = (event: EmitEvent) => Promise<Response>;

EmitOptions

type

Common emission options. origin overrides the request origin (needed for prerendered output).

type EmitOptions = {
  base?: string;
  origin?: string;
};

Entry

type

A resolved entry from get() — data, meta, body, and the graph fields fully populated. rel is {} and backlinks [] when the collection has no graph. body is an inline <Region>, rendered in the page's own SSR pass, so islands inside it hydrate normally.

type Entry<Data = Record<string, unknown>, Meta = Record<string, never>> = {
  id: string;
  data: Data;
  meta: Meta;
  body?: RegionValue;
  source?: () => Promise<string>;
  rel: Record<string, RefEntry | RefEntry[] | null>;
  backlinks: RefEntry[];
};

EntryParts

type

What a format computes from one raw record. body is already a region you render with <Region>.

type EntryParts<Meta = Record<string, never>> = {
  data: Record<string, unknown>;
  body?: RegionValue;
  meta?: Meta;
  source?: () => Promise<string>;
};

Fallback

type

What fell back for a page (which axis, from → to), or null when the page is native.

type Fallback = {
  axis: string;
  from: string;
  to: string;
} | null;

Finding

type

One check result — file-anchored where possible, so it reads in the build-error voice.

type Finding = {
  check: string;
  severity: Severity;
  message: string;
  slug?: string;
  file?: string;
  line?: number;
};

FolderOptions

type

type FolderOptions<Meta> = {
  page?: RegExp;
  meta?: RegExp | false;
  convention?: Convention;
  format?: (input: RawSource<unknown>) => Source<Meta>;
};

Format

type

Parse one raw value into entry parts (data + optional body + optional meta).

type Format<V, Meta = Record<string, never>> = (value: V, id: string) => EntryParts<Meta> | Promise<EntryParts<Meta>>;

GetRemote

type

type GetRemote<Out> = (id: string) => Promise<Out>;

GlobMap

type

import.meta.glob(...) map — eager values or lazy loaders.

type GlobMap = Record<string, unknown | (() => Promise<unknown>)>;

GroupMeta

type

Directory/section decoration a source may expose (groups()), keyed by clean group path.

type GroupMeta = {
  label?: string;
};

GroupSpec

type

An explicit group. base prefixes the slugs of entries beneath it.

type GroupSpec = {
  label: string;
  items: Collection | Selection | OutlineNode[] | OutlineThunk;
  base?: string;
  slug?: (id: string) => string;
  collapsed?: boolean;
  badge?: string;
};

Heading

type

A heading pulled from the markdown pass (h2–h4). Powers on-page TOCs; rides markdown meta.

type Heading = {
  depth: 2 | 3 | 4;
  id: string;
  text: string;
};

LinkOptions

type

Tuning for the links check — the old audit options, now on the value.

type LinkOptions = {
  anchors?: boolean;
  redirected?: "error" | "warn" | "ok";
  ignore?: (href: string) => boolean;
};

LinkRef

type

One markdown link collected during the compile pass (raw, unclassified). Rides markdown meta; ogygia's audit resolves these against the site's address space. line is approximate (relative to the post-frontmatter text).

type LinkRef = {
  href: string;
  text: string;
  line?: number;
};

LinkSpec

type

A plain link node — points anywhere; not backed by an entry.

type LinkSpec = {
  label: string;
  href: string;
};

ListRemote

type

Return types for the minted remotes (the callable shape consumers use; cast for extra methods).

type ListRemote<Out> = () => Promise<Out[]>;

LlmsEmitOptions

type

type LlmsEmitOptions = EmitOptions & {
  title?: string;
  description?: string;
};

MarkdownMeta

type

Meta the markdown source derives: h2–h4 headings (for a TOC) and every markdown link (for the ogygia link audit), both collected during compile.

type MarkdownMeta = {
  headings: Heading[];
  links: LinkRef[];
};

MetaDecoration

type

Directory decoration a +meta.json may carry. DELIBERATELY tiny — the blessed convention has one ordering channel (the NN- prefix) and one naming channel (this file). Order does not belong here (that would be a second way to say the same thing), and chrome behavior (collapsing, badges) belongs to the spec/escape hatch, not to content decoration.

type MetaDecoration = {
  label?: string;
  ordered?: boolean;
};

type

A group of nav items — a section (from convention) or an explicit outline group.

type

type

A leaf in the nav tree — one content entry.

type

A plain link — points anywhere, external or otherwise; not backed by an entry.

type

A shallow reference to one entry, ready to link: its address + display fields.

type

The serializable sidebar tree — what site.nav() returns and a sidebar brick renders.

NumberedOptions

type

type NumberedOptions = {
  pad?: number;
  contiguous?: boolean;
  duplicates?: "error" | "allow";
};

OutlineNode

type

What can sit inside a group's items, or at the top level of a spec.

type OutlineNode = Collection | Selection | GroupSpec | LinkSpec | OutlineThunk;

OutlineOptions

type

type OutlineOptions = {
  redirects?: (entry: Ent) => string[] | string | undefined;
};

OutlineSpec

type

A whole spec: a bare collection (degenerate case) or an ordered list of nodes.

type OutlineSpec = Collection | OutlineNode[];

OutlineThunk

type

type OutlineThunk = () => OutlineNode[] | OutlineNode | Promise<OutlineNode[] | OutlineNode>;

PageFields

type

What every ogygia surface reads off a page. title is required; the rest carry blessed defaults.

type PageFields = {
  title: string;
  summary: string;
  draft: boolean;
  badge?: string;
  redirect_from?: string | string[];
  related: string[];
};

PageView

type

Everything a single page position needs, as plain data. entry carries the inline body region; everything else is derived from the outline and the content graph.

type PageView<Data extends Record<string, unknown> = Record<string, unknown>, Meta = unknown> = {
  slug: string;
  href: string;
  entry: Entry<Data, Meta>;
  section: string;
  crumbs: Crumb[];
  headings: Heading[];
  trail: {
    prev?: NavRef;
    next?: NavRef;
    related: NavRef[];
    suggested: NavRef[];
  };
  coordinate?: Record<string, string>;
  fallback?: {
    axis: string;
    from: string;
    to: string;
  } | null;
};

PostFields

type

Extra fields a blog post carries beyond a page (a Blog shell reads these; the brains do not).

type PostFields = {
  date: string;
  author?: string;
  tags: string[];
};

PrevNext

type

How "keep reading" is chosen. 'graph' = content relations, order fallback.

type PrevNext = "graph" | "order" | false;

RawEmit

type

The raw-markdown emission: a GET handler + its prerender entries, for a [...slug].md/+server.ts.

type RawEmit = {
  GET: (event: {
    params: Record<string, string | undefined>;
  }) => Promise<Response>;
  entries: () => Promise<Array<{
    slug: string;
  }>>;
};

RawRecord

type

One raw record before parsing: a compiled .svx module, a JSON blob, an API result.

type RawRecord<V> = {
  id: string;
  value: V;
  order?: number[];
  filePath?: string;
};

RawSource

type

A raw source yields unparsed values; a Format turns each into EntryParts.

type RawSource<V> = {
  init?: () => Promise<void>;
  refs(query?: unknown): Promise<RawRecord<V>[]>;
  get(id: string): Promise<RawRecord<V> | null>;
  live?: () => SourceChanges;
  groups?: () => Promise<Map<string, GroupMeta>>;
};

ReadContext

type

A request context the site derives per read (preview, roles) and threads into collection filters.

type ReadContext = Record<string, unknown>;

RefEntry

type

A resolved relation target: a shallow reference to another entry — its id and validated data. Deliberately shallow (no body, no URL); build the link from ref.id, or collection.get(ref.id).

type RefEntry<Data = Record<string, unknown>> = {
  id: string;
  data: Data;
};

Resolved

type

Resolution of a slug back to its collection + entry id + placement in the tree.

type Resolved = {
  slug: string;
  entryId: string;
  collectionIndex: number;
  section: string;
  crumbs: Crumb[];
  filePath?: string;
};

ResolvedBlockNode

type

A resolved node: its type already turned into a region value, ready for <Blocks nodes={…}>. This is the wire-law shape — the registry (full of functions) is consumed here, so the tree that survives is data whose leaves are regions. Fed back into <Blocks nodes={…}>.

type ResolvedBlockNode = {
  of: RegionValue;
  children?: ResolvedBlockNode[];
};

SchemaLike

type

Any Standard Schema (valibot / zod / arktype) or a { parse } object.

type SchemaLike = {
  ["~standard"]?: {
    validate: (value: unknown) => StandardResult | Promise<StandardResult>;
  };
  parse?: (value: unknown) => unknown;
};

SearchBrain

type

type SearchBrain = (q: string, opts?: SearchOptions) => Promise<SearchHit[]>;

SearchClient

type

type SearchClient = {
  query(q: string): Promise<SearchHit[]>;
  ready: Promise<void>;
  destroy(): void;
};

SearchClientOptions

type

type SearchClientOptions = {
  base?: string;
  endpoint?: string;
  limit?: number;
};

SearchDoc

type

One searchable document — a page SECTION (split by heading), or the page lead. Mount-independent: stores slug + anchor, so href is computed per query with the caller's base.

type SearchDoc = {
  id: string;
  slug: string;
  anchor: string;
  title: string;
  section: string;
  heading: string;
  text: string;
};

SearchEngine

type

The engine adapter: build documents into a queryable index. Must run in node AND the browser.

type SearchEngine = {
  init?(): Promise<void>;
  build(docs: SearchDoc[]): Promise<SearchIndex>;
};

SearchHit

type

One ranked hit — plain data, ready to render as a link.

type SearchHit = {
  href: string;
  slug: string;
  title: string;
  section: string;
  heading: string;
  excerpt: string;
  score: number;
};

SearchIndex

type

type SearchIndex = {
  query(q: string, opts: {
    limit: number;
    base: string;
  }): Promise<SearchHit[]>;
};

SearchOptions

type

type SearchOptions = {
  limit?: number;
  in?: Collection[];
  base?: string;
};

Selection

type

A subset of one collection, in resolution order — produced by pick().

type Selection = {
  readonly [SELECTION]: true;
  readonly collection: Collection;
  readonly patterns: string[];
};

Severity

type

How loud a finding is: 'error' fails the build (load throws); 'warn' logs in dev.

type Severity = "error" | "warn";

ShellContext

type

type ShellContext = {
  site?: Site;
  base: string;
  components?: ComponentMap;
  title?: string;
};

SiteData

type

Site-level facts, surfaced as site.data and used as the default for every emission.

type SiteData = {
  title: string;
  description: string;
  origin: string;
};

SiteMeta

type

The SHELL bundle — everything <Shell> needs as plain data, so the corpus can stay server-only. site.meta(slug) returns it; expose it as a remote and pass the result to <Shell {meta}>.

type SiteMeta = {
  nav: NavTree;
  switcher: Switcher | null;
  data?: SiteData;
};

SiteOptions

type

The single-object argument to site(). outline is the only required key.

type SiteOptions = {
  outline: Outline | OutlineSpec;
  data?: SiteData;
  base?: string;
  context?: (event: {
    url: URL;
    request?: Request;
    cookies?: unknown;
  }) => ReadContext;
  prevNext?: PrevNext;
  trail?: TrailScope;
  checks?: Check[];
  components?: Record<string, Component<Record<string, unknown>>>;
  search?: {
    engine?: SearchEngine;
  };
  redirects?: (entry: ContentRef) => string[] | string | undefined;
};

Source

type

The source contract — the only thing content({ loader }) accepts.

type Source<Meta = Record<string, never>> = {
  init?: () => Promise<void>;
  refs(query?: unknown): Promise<SourceRef<Meta>[]>;
  get(id: string): Promise<SourceEntry<Meta> | null>;
  live?: () => SourceChanges;
  groups?: () => Promise<Map<string, GroupMeta>>;
};

SourceChanges

type

A live source's change signal. Yield to tell the collection to re-read:

  • yield anything (e.g. 1) → the collection re-lists the whole source (simple, default);
  • yield a string[] of ids → the collection reloads only those ids (get(id) each, missing ids are dropped) — incremental, for large collections where a full re-list is wasteful.
type SourceChanges = AsyncIterable<string[] | unknown>;

SourceEntry

type

One full entry a source yields from get() — a ref plus the two heavy faces. Never crosses a wire.

type SourceEntry<Meta = Record<string, never>> = SourceRef<Meta> & {
  body?: RegionValue;
  source?: () => Promise<string>;
};

SourceRef

type

A shallow reference a source yields from refs() — identity + data, plus optional derived meta and structural order. NEVER a body or source text; those live only on SourceEntry. Wire-safe.

type SourceRef<Meta = Record<string, never>> = {
  id: string;
  data: Record<string, unknown>;
  meta?: Meta;
  order?: number[];
  filePath?: string;
};

Switcher

type

type Switcher = SwitcherAxis[];

SwitcherAxis

type

One axis of the switcher: where you are on it, and where each value would take you.

type SwitcherAxis = {
  axis: string;
  label: string;
  current: string;
  options: Array<{
    value: string;
    href: string;
    current: boolean;
    missing: boolean;
  }>;
};

TrailScope

type

Reading-order policy for prev/next: the whole site, or bounded by the top-level section.

type TrailScope = "site" | "group";