
Obsidian
Documents
Source: packages/laikacms/src/impl/documents-obsidian
A DocumentsRepository implementation backed by an Obsidian vault. Each markdown note is a document keyed by its vault-relative path; published vs. unpublished state is read from frontmatter (the publish property, matching the Obsidian Publish convention) rather than from separate directories.
Pair it with a StorageRepository pointed at the vault root — typically a FileSystemStorageRepository configured with the markdown serializer.
Usage
import { ObsidianDocumentsRepository } from 'laikacms/documents/obsidian';
import { markdownSerializer } from 'laikacms/serializers/markdown';
import { FileSystemStorageRepository } from 'laikacms/storage/fs';
const storage = new FileSystemStorageRepository(
'/path/to/vault',
{ md: markdownSerializer },
'md',
);
const repo = new ObsidianDocumentsRepository(storage);Custom frontmatter properties
const repo = new ObsidianDocumentsRepository(storage, {
publishProperty: 'published', // read/write 'published' instead of 'publish'
statusProperty: 'stage', // read/write 'stage' instead of 'status'
defaultStatus: 'idea', // status when 'stage' is absent; default 'draft'
});Constructor
new ObsidianDocumentsRepository(
storageRepository: StorageRepository,
options?: ObsidianDocumentsRepositoryOptions,
)| Parameter | Type | Description |
|---|---|---|
storageRepository | StorageRepository | Underlying storage, typically FileSystemStorageRepository + markdownSerializer. |
options | ObsidianDocumentsRepositoryOptions | Optional. See table below. |
Options
| Option | Type | Default | Description |
|---|---|---|---|
publishProperty | string | 'publish' | Frontmatter property that marks a note as published. A note is a published Document when this property is strictly true; otherwise it is an Unpublished draft. |
statusProperty | string | 'status' | Frontmatter property that records the editorial status of a draft (draft, pending_review, …). |
defaultStatus | string | 'draft' | Status reported for a draft note that has no explicit statusProperty value. |
Frontmatter conventions
Published note
---
publish: true
language: en
title: My Note
---publish: true— required; anything other than stricttrueis treated as unpublished. Also preserved in the document'scontent.language— optional BCP 47 tag (e.g.en,nl). Omitted or empty resolves to'und'(undetermined).- All frontmatter keys — including
publish,language, andstatus— are passed through as documentcontent. The implementation readspublishandlanguageto derive document state and language, but does not strip them from the content map.
Draft / unpublished note
---
publish: false
status: pending_review
language: en
title: My Draft
---publish: false(or absent / any non-truevalue) — note is anUnpublisheddraft. Also preserved in the document'scontent.status— editorial state. Common values:draft,pending_review,published(thoughpublishedis normally expressed viapublish: true). Missing value falls back todefaultStatus. Also preserved in the document'scontent.language— optional BCP 47 tag. Also preserved in the document'scontent.
When a note is published via publish() the publish property is set to true and the status property is removed. When it is unpublished via unpublish() the reverse happens. Note that publish, language, and status are accessible both as top-level Document fields and within the content object.
Behaviour notes
- Keys. A document's key is its vault-relative path without the file extension (e.g.
blog/2024-hello-world). Keys are case-sensitive and use/as the path separator regardless of OS. - Listing reads full objects. Because published state is stored in frontmatter,
listRecordsandlistRecordSummariesmust read full file content — unlike storage implementations that use separate directories,listAtomSummariesalone cannot distinguish published notes from drafts. - Language. A note without a
languagefrontmatter key is returned withlanguage: 'und'. Settinglanguageto'und'on create/update removes the key from the frontmatter. - Capabilities. Pagination capabilities are delegated to the underlying
StorageRepository.
Limitations
Obsidian is a local markdown editor with no built-in version history. The revision API surface is therefore unsupported:
| Method | Behaviour |
|---|---|
getRevision | Always throws BadRequestError |
createRevision | Always throws BadRequestError |
listRevisions | Always returns an empty stream (total: 0) |
No built-in DocumentsRepository with revision history exists yet. If you need file-level history, pair a DocumentsRepository that delegates to a storage backend with change tracking — for example GithubStorageRepository from @laikacms/github (a StorageRepository) preserves each write as a GitHub commit, giving you a per-file audit trail at the storage layer.
Assets
Source: packages/laikacms/src/impl/assets-obsidian
Obsidian-vault-backed implementation of AssetsRepository.
Runtime: Node.js / Bun only. This module imports
node:fs,node:path, andnode:stream. It cannot run on Cloudflare Workers or other edge runtimes. Uselaikacms/assets/r2for edge-compatible asset storage.
Usage
import { ObsidianAssetsRepository } from 'laikacms/assets/obsidian';
// minimal — vault path only
const assets = new ObsidianAssetsRepository('/path/to/your/obsidian-vault');
// with options
const assets = new ObsidianAssetsRepository('/path/to/your/obsidian-vault', {
attachmentsDirectory: 'attachments',
documentExtensions: ['md', 'canvas'],
ignore: ['.obsidian', '.git'],
createUrl: key => `https://cdn.example.com/${key}`,
});Constructor
new ObsidianAssetsRepository(vaultPath: string, options?: ObsidianAssetsRepositoryOptions)| Parameter | Type | Description |
|---|---|---|
vaultPath | string | Absolute path to the Obsidian vault. |
options | ObsidianAssetsRepositoryOptions | Optional configuration (see below). |
Options
| Option | Type | Default | Description |
|---|---|---|---|
attachmentsDirectory | string | '' (vault root) | Subdirectory treated as the asset root. Asset keys are resolved relative to it. |
documentExtensions | string[] | ['md'] | File extensions (without the leading dot) excluded from listings because they belong to the documents layer. |
ignore | string[] | ['.obsidian', '.trash', '.git', '.DS_Store', 'Thumbs.db'] | Directory / file basenames skipped while listing. |
createUrl | (key: string) => string | Returns the key unchanged | Builds a serving URL for an asset key. Supply this to point at a static host or CDN. |
createVariations | (key: string) => Record<string, AssetVariation> | Returns {} | Returns named variations (thumbnails, WebP versions, etc.) for an asset key. See Image variations below. |
Every non-markdown file in the vault (images, PDFs, audio, …) is exposed as an Asset keyed by its vault-relative path. The implementation is read-oriented — for write-heavy workloads prefer laikacms/assets/r2 or another object-storage backend.
Filtering
ObsidianAssetsRepository supports a named filter[search] query parameter when listing resources via the assets-api:
GET /resources?filter[search]=logoThis performs a case-insensitive substring match on the resource key — the example above returns every asset whose key contains "logo" (e.g. attachments/logo.png, images/logo-dark.svg).
Sending an undeclared filter name (anything other than search) returns 400 Bad Request. Inspect GET /capabilities (attributes.filtering.filters) to see the current list of supported filter names at runtime.
Image variations
createVariations lets you expose pre-generated responsive variants (thumbnails, WebP copies, size tiers) for each asset. The callback receives the asset key and returns a named map of AssetVariation objects; these are served via GET /resources/:key?include=variations on the assets-api.
import { ObsidianAssetsRepository } from 'laikacms/assets/obsidian';
const assets = new ObsidianAssetsRepository('/path/to/vault', {
createUrl: key => `https://cdn.example.com/${key}`,
createVariations: key => ({
thumb: {
variant: 'thumb',
url: `https://cdn.example.com/thumb/${key}`,
width: 200,
height: 200,
},
webp: {
variant: 'webp',
url: `https://cdn.example.com/webp/${key.replace(/\.[^.]+$/, '.webp')}`,
mimeType: 'image/webp',
},
}),
});Return {} (or omit the option) for assets that have no variations.
Limitations
Obsidian vaults store no per-file custom metadata or cache headers, so updateAsset is unsupported:
| Method | Behaviour |
|---|---|
updateAsset | Always throws BadRequestError. Use createAsset to replace the file instead. |