1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
import { readdir, stat } from "node:fs/promises";
import { existsSync } from "node:fs";
import { join, relative, basename, sep } from "node:path";
import type { SourceModule } from "../registry";
import {
ObsidianConfig,
ObsidianPayload,
type ObsidianConfig as Config,
type ObsidianPayload as Payload,
} from "@/lib/schemas/sources/obsidian";
const SKIP_DIRS = new Set([".obsidian", ".trash", ".git", "node_modules"]);
function vaultPath(config: Config): string {
return config.vault || process.env.OBSIDIAN_VAULT || "";
}
export const obsidianModule: SourceModule<Config, Payload> = {
kind: "obsidian",
label: "Obsidian",
keyless: true,
defaultRefreshSeconds: 600,
configSchema: ObsidianConfig,
payloadSchema: ObsidianPayload,
isConfigured: (config) => {
const v = vaultPath(config);
return v.length > 0 && existsSync(v);
},
// Read-only: we only stat/list note files. Never write to the vault.
async fetch({ config }) {
const vault = vaultPath(config);
if (!vault || !existsSync(vault)) {
throw new Error("Vault path not found โ set it in Settings.");
}
const entries = await readdir(vault, { recursive: true, withFileTypes: true });
const mdFiles = entries.filter((e) => {
if (!e.isFile() || !e.name.endsWith(".md")) return false;
const parent = (e as { parentPath?: string; path?: string }).parentPath ??
(e as { path?: string }).path ?? vault;
const rel = relative(vault, join(parent, e.name));
return !rel.split(sep).some((seg) => SKIP_DIRS.has(seg));
});
const notes = await Promise.all(
mdFiles.map(async (e) => {
const parent = (e as { parentPath?: string; path?: string }).parentPath ??
(e as { path?: string }).path ?? vault;
const full = join(parent, e.name);
const s = await stat(full);
return {
title: basename(e.name, ".md"),
path: relative(vault, full),
modified: s.mtime.toISOString(),
};
}),
);
notes.sort((a, b) => Date.parse(b.modified) - Date.parse(a.modified));
return { vault, notes: notes.slice(0, config.limit) };
},
};
|