@document0/core
The core package handles everything related to reading your content from disk, building data structures for your UI, serving search and LLM endpoints, and processing OpenAPI specs.
DocsSource
The main entry point. Scans a directory for .md and .mdx files and produces typed PageData objects. All results are cached on the instance — subsequent calls return instantly.
import { DocsSource } from "@document0/core";
const source = new DocsSource({
rootDir: "./content/docs", // required: absolute or relative path
baseUrl: "/docs", // default: "/docs"
extensions: [".md", ".mdx"], // default
});Methods
getPages()
Returns all pages as a flat array of PageData. Cached after first call.
const pages = await source.getPages();
// PageData[]getPageTree()
Returns a nested TreeNode[] for sidebar rendering. Respects _meta.json ordering. Cached after first call.
const tree = await source.getPageTree();
// TreeNode[]getPage(slug)
O(1) lookup of a single page by its slug string (e.g. "guides/installation").
const page = await source.getPage("installation");
// PageData | undefinedgetPageByUrl(url)
O(1) lookup of a single page by its full URL (e.g. "/docs/guides/installation").
const page = await source.getPageByUrl("/docs/installation");
// PageData | undefinedinvalidate()
Clears all cached data (pages, tree, search index, lookup maps). Call this when content files change to force a reload.
source.invalidate();Content watching (dev)
Next.js: use @document0/next-dev (withDocument0 + one content-stamp import) so the dev bundler invalidates your DocsSource module when content/ changes (Fumadocs-style). See the Next.js guide.
Other runtimes: watchDocsSource from @document0/core/watch (Node-only; separate entry so chokidar is not pulled into unrelated graphs).
PageData
interface PageData {
slug: string; // e.g. "guides/installation"
slugs: string[]; // e.g. ["guides", "installation"]
url: string; // e.g. "/docs/guides/installation"
filePath: string; // absolute path to the .mdx file
content: string; // raw markdown body (frontmatter stripped)
frontmatter: {
title: string;
description?: string;
icon?: string;
full?: boolean;
[key: string]: unknown;
};
}Page tree
TreeNode
type TreeNode = PageNode | FolderNode | SeparatorNode;
interface PageNode {
type: "page";
name: string;
url: string;
slug: string;
icon?: string;
}
interface FolderNode {
type: "folder";
name: string;
icon?: string;
defaultOpen?: boolean;
index?: PageNode; // index.mdx inside this folder
children: TreeNode[];
}
interface SeparatorNode {
type: "separator";
name: string; // label, or empty string for an unlabelled divider
}_meta.json
Place a _meta.json file in any content directory to control ordering and labels.
{
"title": "Guides",
"pages": [
"index",
"--- Getting Started",
"installation",
"---",
"advanced"
],
"defaultOpen": true,
"icon": "📖"
}- Strings starting with
"--- "become labelled separators "---"alone becomes an unlabelled divider- Pages not listed are appended alphabetically
Navigation utilities
getBreadcrumbs
Returns the breadcrumb trail from the root to the current page.
import { getBreadcrumbs } from "@document0/core";
const crumbs = getBreadcrumbs(tree, "/docs/guides/installation");
// BreadcrumbItem[]interface BreadcrumbItem {
name: string;
url?: string; // undefined for the current (last) item
}getPageNeighbours
Returns the previous and next pages in document order.
import { getPageNeighbours } from "@document0/core";
const { previous, next } = getPageNeighbours(tree, "/docs/installation");
// { previous: PageNode | null, next: PageNode | null }isActiveOrAncestor
Returns true if a tree node is the active page or an ancestor of it, useful for expanding folders in a sidebar.
import { isActiveOrAncestor } from "@document0/core";
const open = isActiveOrAncestor(folderNode, currentUrl);Search
createSearchRoute
Creates an API route handler backed by Orama with full-text search, fuzzy matching, and relevance ranking. The search index is cached per DocsSource instance and cleared on invalidate().
import { createSearchRoute } from "@document0/core/search";
// app/internal/search/route.ts
export const { GET } = createSearchRoute(source);The returned GET handler accepts a ?q= query parameter and responds with SearchResult[].
interface SearchResult {
title: string;
description?: string;
url: string;
score: number;
}llms.txt
Generate llms.txt files so LLMs and AI tools can ingest your documentation.
createLlmsTxtRoute
Serves a concise index of all pages as llms.txt.
import { createLlmsTxtRoute } from "@document0/core/llms";
// app/llms.txt/route.ts
export const { GET } = createLlmsTxtRoute(source, {
title: "My Docs",
description: "Documentation for my project",
baseUrl: "https://docs.example.com",
});createLlmsFullTxtRoute
Serves the complete content of every page concatenated into a single text file.
import { createLlmsFullTxtRoute } from "@document0/core/llms";
// app/llms-full.txt/route.ts
export const { GET } = createLlmsFullTxtRoute(source, {
title: "My Docs",
description: "Documentation for my project",
baseUrl: "https://docs.example.com",
});createMdxPageRoute
Serves raw markdown content for a single page by slug.
import { createMdxPageRoute } from "@document0/core/llms";
// app/api/page/[...slug]/route.ts
export const { GET } = createMdxPageRoute(source);OpenAPI
createOpenAPISource
Parses an OpenAPI 3.x spec and generates OpenAPIPageData for each operation.
import { createOpenAPISource } from "@document0/core/openapi";
const operations = createOpenAPISource(specObject, { baseUrl: "/docs/api" });buildOpenAPITree
Builds a TreeNode[] from OpenAPI operations, grouped by tag.
import { buildOpenAPITree } from "@document0/core/openapi";
const tree = buildOpenAPITree(operations);buildOpenAPISearchIndex
Returns SearchResult[] from OpenAPI operations for use with search.
import { buildOpenAPISearchIndex } from "@document0/core/openapi";
const results = buildOpenAPISearchIndex(operations);