The manifest

The in-memory index built from your sources.

The manifest is the in-memory index Comark builds from your sources. It's what makes get, list, and navigation instant. It's built from the source (never shipped in your bundle or stored in the SQLite database), and when a cache driver is configured it's persisted there too, so a fresh instance restores it instead of re-reading the source.

Interface

The manifest is a map of path to a lightweight entry (ContentListFile), plus the list of source names and the per-source inferred schemas:

manifest.ts
interface Manifest {
  sources: string[]                    
  items: Record<string, ContentListFile>   
  schemas?: Record<string, JsonSchema>
}
FieldTypeDescription
sourcesstring[]The configured source names.
itemsRecord<string, ContentListFile>Every entry, keyed by its public path.
schemasRecord<string, JsonSchema>Inferred or explicit data schema per source. Optional.

sources

The names of every configured source, in declaration order: ['default'] for a single source, or your own keys for named sources. It's the set list() and navigation() walk when you don't scope them, and each name prefixes the key of its entries.

items

The index itself: a record from public path to a lightweight ContentListFile. This is what get(), list(), stat(), and navigation() read from. Each item carries meta and data (limited to the listingFields if defined), but no nodes. The parsed body is fetched by get() and stored in the cache, not here.

schemas

Optional. The data (frontmatter) JSON Schema for each source, either inferred from its content during load or supplied explicitly via the source's schema. It powers generated types and query columns, and is absent until something is inferred or declared.

Built lazily, once

You never build the manifest by hand. comarkContent() returns immediately and wait until the first read to trigger its initialization process (init()) which walks each source's keys() and reads metadata into the manifest:

init.ts
const content = comarkContent({ source: fs('./content') })

await content.list()   // first call runs init() for you

By default init() runs metadata-only. It means that it reads frontmatter to populate the manifest but skips parsing bodies. That's why list() and navigation() are cheap. get() parses a body on demand and caches it, it doesn't change the manifest.

When a cache is configured, init() first tries to restore the manifest from the cache and only walks the sources on a miss, persisting the rebuilt manifest afterwards. Later changes (update, remove, content.cache.refresh) re-persist it.

Watch and refresh

The manifest is an index built at init(). For most sources it won't notice new, renamed, or deleted files on its own, so you refresh on your own terms.

If your provider supports watching, you can use content.watch() to keep the manifest and cache in sync with file changes.

Else, you can detect changes manually and refresh the cache with content.cache.refresh(source).

refresh.ts
await content.cache.refresh('default')  // re-list the source, add/remove manifest entries

Independent of the database

The manifest is pure in-memory bookkeeping and has nothing to do with the optional SQLite database. get, list, and navigation work with no database at all. It only backs the SQL query and full-text search plugins, which index the same content separately.

The manifest is exposed read-only as content.manifest. Beyond the cache's automatic persistence, it can also be serialized as a portable artifact to ship a pre-built index to environments without a shared cache (a browser, a static deploy).