โ– humdrum codex / soft
2.1 KB raw
 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) };
  },
};