@wrnexus/content
Content collections, validation, querying, and publishing workflows.
Install the package
After WorkRoot approves private registry access, install the release-aligned package:
bun add @wrnexus/content@0.8.7Request preview access. Never put registry tokens in source control.
Typed content collections for Markdown/MDX-like documents and remote CMS records. Collections validate frontmatter through any { parse(input) } schema, render escaped HTML, and expose draft preview, versions, references, headings, search indexes, pagination, RSS and sitemaps.
const posts = defineCollection({
name: "posts",
schema: PostSchema,
loader: localContentLoader("content/posts"),
previewToken: process.env.CONTENT_PREVIEW_TOKEN,
});
const published = await posts.load();
const preview = await posts.load({ previewToken: request.headers.get("x-preview-token") ?? "" });
Remote systems implement CmsAdapter, use cmsContentLoader, or return JSON records through remoteContentLoader. Markdown HTML is escaped by default; raw executable HTML is never trusted.
Complete TypeScript API
Generated from the exact installed package declarations.
type MdxComponent = (props: Record<string, string>, children: string) => string;
/** Execute explicitly registered MDX components without evaluating arbitrary JavaScript. */
declare function renderMdxComponents(source: string, components: Record<string, MdxComponent>): string;
interface SyntaxLanguageBundle {
highlight(source: string): string;
}
declare function createIncrementalHighlighter(loaders: Record<string, () => SyntaxLanguageBundle | Promise<SyntaxLanguageBundle>>): {
languages: () => string[];
highlight(language: string, source: string): Promise<string>;
render(html: string): Promise<string>;
};
interface VendorAdapterOptions {
fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
token?: string;
map?: (entry: any) => {
id: string;
content: string;
source?: string;
};
}
declare function contentfulAdapter(space: string, environment?: string, options?: VendorAdapterOptions): {
list(collection: string): Promise<any>;
};
declare function sanityAdapter(project: string, dataset: string, options?: VendorAdapterOptions & {
apiVersion?: string;
}): {
list(collection: string): Promise<any>;
};
declare function strapiAdapter(baseUrl: string, options?: VendorAdapterOptions): {
list(collection: string): Promise<any>;
};
interface ContentSchema<T> {
parse(input: unknown): T;
}
interface ContentEntry<T = Record<string, unknown>> {
id: string;
slug: string;
collection: string;
data: T;
body: string;
html: string;
excerpt: string;
headings: ContentHeading[];
draft: boolean;
version?: string;
source: string;
}
interface ContentHeading {
depth: number;
text: string;
slug: string;
}
interface ContentLoaderResult {
id: string;
source: string;
content: string;
}
interface ContentLoader {
load(): ContentLoaderResult[] | Promise<ContentLoaderResult[]>;
}
interface ContentCollectionOptions<T> {
name: string;
schema: ContentSchema<T>;
loader: ContentLoader;
includeDrafts?: boolean;
previewToken?: string;
references?: Record<string, ContentCollection<unknown>>;
}
interface ContentCollection<T> {
name: string;
load(options?: {
drafts?: boolean;
previewToken?: string;
version?: string;
}): Promise<ContentEntry<T>[]>;
get(id: string, options?: {
drafts?: boolean;
previewToken?: string;
version?: string;
}): Promise<ContentEntry<T> | null>;
}
declare function parseFrontmatter(source: string): {
data: Record<string, unknown>;
body: string;
};
declare function renderMarkdown(source: string): {
html: string;
headings: ContentHeading[];
excerpt: string;
};
declare function localContentLoader(directory: string): ContentLoader;
declare function remoteContentLoader(url: string, options?: {
fetch?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
headers?: HeadersInit;
}): ContentLoader;
declare function defineCollection<T>(options: ContentCollectionOptions<T>): ContentCollection<T>;
declare function resolveContentReference(collections: Record<string, ContentCollection<unknown>>, reference: string): Promise<ContentEntry<unknown> | null>;
declare function paginateContent<T>(entries: T[], page?: number, pageSize?: number): {
items: T[];
page: number;
pageSize: number;
total: number;
totalPages: number;
hasNext: boolean;
hasPrevious: boolean;
};
declare function createSearchIndex(entries: ContentEntry[]): {
id: string;
slug: string;
title: string;
text: string;
}[];
declare function searchContent(index: ReturnType<typeof createSearchIndex>, query: string): {
id: string;
slug: string;
title: string;
text: string;
}[];
declare function contentSitemap(entries: ContentEntry[], baseUrl: string): string;
declare function contentRss(entries: ContentEntry[], options: {
title: string;
baseUrl: string;
description?: string;
}): string;
interface CmsAdapter {
list(collection: string): Promise<Array<{
id: string;
content: string;
source?: string;
}>>;
}
declare function cmsContentLoader(adapter: CmsAdapter, collection: string): ContentLoader;
export { type CmsAdapter, type ContentCollection, type ContentCollectionOptions, type ContentEntry, type ContentHeading, type ContentLoader, type ContentLoaderResult, type ContentSchema, type MdxComponent, type SyntaxLanguageBundle, type VendorAdapterOptions, cmsContentLoader, contentRss, contentSitemap, contentfulAdapter, createIncrementalHighlighter, createSearchIndex, defineCollection, localContentLoader, paginateContent, parseFrontmatter, remoteContentLoader, renderMarkdown, renderMdxComponents, resolveContentReference, sanityAdapter, searchContent, strapiAdapter };
Examples
Copy-ready examples from the installed package documentation.
preview, versions, references, headings, search indexes, pagination, RSS and sitemaps.
const posts = defineCollection({
name: "posts",
schema: PostSchema,
loader: localContentLoader("content/posts"),
previewToken: process.env.CONTENT_PREVIEW_TOKEN,
});
const published = await posts.load();
const preview = await posts.load({ previewToken: request.headers.get("x-preview-token") ?? "" });Install @wrnexus/content
bun add @wrnexus/content