Skip to content

API

ogygia/content

Collections, sources, the site layer, shells.

On this page

Auto-generated from the package’s own .d.ts on every build — signatures, JSDoc, and examples straight from 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;

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-startstart.

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-stateData 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: SchemaLike & {
    readonly "~standard": {
      types?: {
        output: PageFields;
      };
    };
  };
  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';