Content types
The core types describing content, the manifest, sources, the cache, the database, parsers, and the Content instance.
Every export is re-exported from the main comark-content entry point:
import type {
ComarkContent,
ContentOptions,
ContentHooks,
ContentFile,
ContentListFile,
ContentFileMeta,
Manifest,
NavigationItem,
Source,
Database,
RelationalDatabase,
CacheArtifact,
CacheOptions,
CacheStrategy,
CacheArtifactOptions,
Cache,
SearchOptions,
SearchResult,
Logger,
OnErrorMode,
} from 'comark-content'Every read API is built on two types: ContentFile (the full file, with parsed nodes) and ContentListFile (the same shape minus nodes).
ContentFile<TData, TMeta>
The full parsed file: the shape content.get() returns. Built around three fields: path, data (the frontmatter / parsed data), and meta (Content- and plugin-provided metadata), plus the parsed nodes.
interface ContentFile<TData = Record<string, any>, TMeta = Record<string, unknown>> {
path: string // '/posts/hello'
data: TData // the file's frontmatter / parsed data
meta: TMeta & ContentFileMeta // kind, type, key, source, extension, stem, partial (+ plugin fields)
nodes: MarkdownDocument['nodes'] // the parsed AST
}ContentFileMeta
The metadata every Content file carries, regardless of which API returned it. Plugins and parsers extend it through ContentFile's TMeta parameter.
interface ContentFileMeta {
kind: 'document' | 'media' // whether it's a document or a media file
type: string // mime type, e.g. 'text/markdown'
key: string // 'default/posts/hello.md'
source: string // the source the file belongs to
extension: string // '.md' (with the leading dot)
stem: string // 'posts/hello' (path without extension)
partial: boolean // true when only frontmatter was parsed (body skipped)
}ContentListFile<TData>
The lightweight representation: ContentFile without nodes. This is what content.list(), content.query() and content.stat() return, and what the manifest stores. Fetch the full file (including parsed nodes) with content.get().
type ContentListFile<TData = Record<string, any>> = Omit<ContentFile<TData>, 'nodes'>Manifest
The in-memory index of every entry by path. Built from frontmatter on content.init(), it backs list() and navigation(), and is serialised into an artifact by content.cache.manifest(). See the manifest concept.
interface Manifest {
sources: string[] // source names loaded into the index
items: Record<string, ContentListFile> // every entry, keyed by path
time: number // build timestamp (ms)
schemas?: Record<string, JsonSchema> // per-source `data` JSON Schema
}NavigationItem
The recursive shape produced by content.navigation().
interface NavigationItem {
title: string
description?: string
path: string
stem?: string
children?: NavigationItem[]
page?: false
[key: string]: unknown
}Source
The boundary that loads raw content. Implement this to build a custom source.
interface Source {
prefix?: string
keys: () => Promise<string[]> | string[]
getItem: (key: string) => Promise<string> | string
getItemRaw: (key: string) => Promise<unknown> | unknown
watch?: (cb: (event: 'update' | 'remove', key: string) => void) => () => void
}FSSourceOptions
The optional second argument of the fs source. It extends unstorage's fs driver options (everything except base, which fs takes as its first argument) and adds the source-level fields below.
interface FSSourceOptions extends Omit<FSStorageOptions, 'base'> {
prefix?: string
exclude?: string[]
schema?: JsonSchema
}See Filesystem options for what each field does.
GithubSource
The argument of the github source. It extends unstorage's github driver options (repo, branch, ttl, token, apiURL, cdnURL) and adds path plus the source-level fields below.
interface GithubSource extends Omit<GithubOptions, 'dir'> {
path?: string
prefix?: string
exclude?: string[]
schema?: JsonSchema
}See GitHub options for what each field does.
Database
The storage contract for parsed files. It is passed to the SQL-aware plugins (sqlQuery, sqliteFullTextSearch).
interface Database {
upsert: (source: string, data: ContentFile) => void | Promise<void>
delete: (source: string, key: string) => void | Promise<void>
get: (source: string, key: string) => ContentFile | Promise<ContentFile>
list: (source: string) => ContentFile[] | Promise<ContentFile[]>
clear: (source: string) => void | Promise<void>
}RelationalDatabase
Extends Database with the raw SQL methods the query and full-text search plugins actually call.
interface RelationalDatabase extends Database {
first: <T>(query: string, params?: unknown[]) => Promise<T | undefined>
all: <T>(query: string, params?: unknown[]) => Promise<T[]>
execute: <T>(query: string, params?: unknown[]) => Promise<T>
}CacheArtifact
Serialised representation of a source's entries or the manifest, produced by content.cache.manifest() / content.cache.snapshot() and consumed by the cache.loadManifest / cache.loadSnapshot loaders.
interface CacheArtifact {
name: string // 'manifest' or a source name
data: string // JSON, optionally gzipped + base64
checksum: string // SHA-256 of `data`
size: number // bytes
}CacheOptions
The cache object passed to comarkContent(). Configures the driver, freshness, and snapshot loaders. See Caching.
interface CacheOptions {
driver?: Driver // unstorage driver; defaults to in-memory
ttl?: number // ms before an entry is stale; omit to never expire
strategy?: CacheStrategy // how a stale entry is served; defaults to 'swr'
loadManifest?: () => Promise<CacheArtifact | null | undefined> | CacheArtifact | null | undefined
loadSnapshot?: (source: string) => Promise<CacheArtifact | null | undefined> | CacheArtifact | null | undefined
}CacheStrategy
How a stale entry is served: see strategy.
type CacheStrategy = 'swr' | 'none'CacheArtifactOptions
Options for producing a CacheArtifact via content.cache.manifest() / content.cache.snapshot().
interface CacheArtifactOptions {
compress?: boolean // gzip + base64 the payload; defaults to true
fresh?: boolean // re-read the source (true) or build from cache (false)
}Cache
The resolved cache exposed as content.cache. Backed by an unstorage driver, it stores ContentFile entries keyed <source>:<path> and the manifest. See Refreshing on demand.
interface Cache {
get<T = ContentFile>(key: string): Promise<T | null>
set<T = ContentFile>(key: string, value: T): Promise<void>
keys(prefix?: string): Promise<string[]>
refresh(source: string): Promise<ContentFile[]> // re-read a source, reconcile the manifest
invalidate(key: string): Promise<void> // drop one entry
expire(key: string): Promise<void> // mark one entry stale (swr refreshes on next read)
manifest(opts?: CacheArtifactOptions): Promise<CacheArtifact | null> // fresh: true (default) inits first; fresh: false reads the persisted index
snapshot(source: string, opts?: CacheArtifactOptions): Promise<CacheArtifact | null>
}ContentHooks
Events you can subscribe to via content.hooks.hook(name, callback).
interface ContentHooks {
'watch:file:update': (sourceName: string, key: string, file: ContentFile) => void
'watch:file:remove': (sourceName: string, key: string) => void
'file:parsed': (ctx: FileParsedContext) => void | Promise<void>
'typegen:field': (ctx: TypegenFieldContext) => void | Promise<void>
'file:upsert': (sourceName: string, key: string, file: ContentFile) => void
'file:remove': (sourceName: string, key: string) => void
'file:bulk:load': (sourceName: string, items: ContentFile[]) => void
}See FileParsedContext for the file:parsed payload and TypegenFieldContext for typegen:field. The markdown-fields, schema-validation, and references plugins are reference implementations.
SearchOptions
Options accepted by content.search(), added by the sqliteFullTextSearch plugin.
interface SearchOptions {
limit?: number
fields?: ('title' | 'content')[]
minTermLength?: number
weights?: { title?: number; content?: number; heading?: boolean }
snippet?: { columns?: ('title' | 'content')[]; around?: number; tag?: string }
}SearchResult
A single result row returned by content.search().
interface SearchResult {
source: string
id: string
title: string
titles: string[]
level: number
content: string
rank: number
snippets?: { title?: string; content?: string }
}Parser
Registered via addParser to turn a raw file into a parsed result. ParserContext describes the file; ParserResult is what the parser returns.
interface ParserContext {
filepath: string
extension: string
partial: boolean
read: () => string | Promise<string>
}
type ParserResult = {
kind?: 'media' | 'document'
type?: string
data?: ContentFile['data']
meta?: Record<string, any>
nodes?: ContentFile['nodes']
partial?: boolean
}
type Parser = (ctx: ParserContext) => ParserResult | null | Promise<ParserResult | null>ContentOptions
The options object passed to comarkContent(). See Options.
interface ContentOptions {
source?: Source
sources?: Record<string, Source>
cache?: CacheOptions | false
plugins?: ReadonlyArray<ContentPlugin<any>>
markdown?: ParserOptions
basePath?: string // default '/api/content'
baseURL?: string
logger?: Logger | false // default console-backed; false silences output
onError?: OnErrorMode // default 'warn'
}Logger
The minimal logger interface used across the Content instance pipeline. Provide one via ContentOptions.logger to redirect output, or pass false to silence it. The active logger is exposed as content.logger and in the plugin and parser contexts.
interface Logger {
debug: (...args: unknown[]) => void
info: (...args: unknown[]) => void
warn: (...args: unknown[]) => void
error: (...args: unknown[]) => void
}OnErrorMode
How the pipeline reacts when a file fails to parse. Set the default via ContentOptions.onError; the built-in json, yaml, and markdown-fields plugins accept a per-plugin override.
type OnErrorMode =
| 'throw' // surface the error and abort
| 'warn' // log via the logger and drop the file (default)
| 'ignore' // silently drop the fileComarkContent
The Content instance returned by comarkContent(); plugin methods are mixed in on top. Every member is documented on the comarkContent page.
interface ComarkContent {
options: ContentOptions
status: 'created' | 'initializing' | 'initialized-partial' | 'initialized-full'
cache: Cache
logger: Logger
hooks: Hookable<ContentHooks>
init(opts?: { partial?: boolean; ignoreCache?: boolean }): Promise<void>
loadSnapshot(sourceName: string): Promise<void>
list: ContentList
stat(key: string): ContentListFile | undefined
getSource(name: string): Source | undefined
get: ComarkGet
navigation(sources?: string[]): Promise<NavigationItem[]>
readonly manifest: Manifest
handler(request: Request): Promise<Response>
update(sourceName: string, file: ContentFile): Promise<void>
remove(sourceName: string, key: string): Promise<void>
watch(): Promise<() => Promise<void>>
}ContentList
The typed signature of content.list(): known source names narrow each item's data.
interface ContentList {
<K extends keyof ContentRegistry>(sources: K[]): Promise<ContentListFile<SourceData<K>>[]>
<Data = RegistryData>(sources?: string[]): Promise<ContentListFile<Data>[]>
}Registry and query types
Generated and advanced types powering path/source autocompletion and the sqlQuery builder. ContentRegistry and ContentPaths are empty by default and augmented by generateSourceTypes().
| Type | Description |
|---|---|
ContentRegistry | Map of source name to { data, row }; augmented by generated types. Empty by default. |
ContentPaths | Map of known document path to data type; augmented by generated types. Empty by default. |
RegistryRow<K> | The query-row type for source K. |
SourceData<K> | The data (frontmatter) type for source K. |
RegistryData | Union of every source's data type (the fallback for get/list). |
PathData<P> | The data type for a known path P. |
Ref<K> | Branded path string pointing at source K — emitted by typegen for reference fields. |
QueryRowBase | Base columns present on every query row. |
QueryRow | A flattened query row (path, meta.*, data.* columns). |
SourceQueryBuilder | The chainable builder returned by content.query(). |
SqlQueryMethods, ComarkQuery | The typed query() method the sqlQuery plugin adds. |
ComarkGet | The typed signature of content.get(). |
Option and method types
Per-feature option and method bags, each documented in full on its feature page.
| Type | Used by |
|---|---|
SqliteOptions | sqlite() |
SqliteWasmOptions | sqliteWasm() |
ContentRow | the SQLite __contents table row shape |
IndexSearchOptions | the full-text search index builder |
JsonSchema | a source's data schema (the source schema option) |