src / wikiSources.ts
import * as path from "node:path";
import * as fs from "node:fs/promises";
import {
WikiError,
WikiLayout,
backupFile,
ensureWikiInitialized,
pathExists,
sourceFileName,
sourceMetaPath,
sourcePath,
} from "./wikiPaths";
import { appendLog, listSourceEntries, refreshIndex } from "./wikiCore";
export async function listSources(
layout: WikiLayout,
filter?: string,
): Promise<{ sources: Array<{ name: string; origin: string | null }> }> {
await ensureWikiInitialized(layout);
const all = await listSourceEntries(layout);
const filtered = filter
? all.filter((s) => s.name.toLowerCase().includes(filter.toLowerCase()))
: all;
return { sources: filtered };
}
export async function readSource(
layout: WikiLayout,
name: string,
maxBytes: number,
): Promise<{
name: string;
path: string;
size: number;
origin: string | null;
content: string;
}> {
await ensureWikiInitialized(layout);
const file = sourceFileName(name);
const abs = sourcePath(layout, name);
const stat = await fs.stat(abs).catch(() => null);
if (!stat || !stat.isFile()) {
throw new WikiError(`Source "${file}" does not exist.`);
}
if (stat.size > maxBytes) {
throw new WikiError(
`Source is ${stat.size} bytes, larger than the configured cap. Increase the cap to read it.`,
);
}
const content = await fs.readFile(abs, "utf-8");
let origin: string | null = null;
try {
const meta = JSON.parse(await fs.readFile(sourceMetaPath(layout, name), "utf-8"));
if (typeof meta?.origin === "string") origin = meta.origin;
} catch {
/* no meta */
}
return { name: file, path: abs, size: stat.size, origin, content };
}
export async function saveSource(
layout: WikiLayout,
args: { name: string; content: string; origin?: string },
): Promise<{
name: string;
path: string;
bytes: number;
created: boolean;
backup_path: string | null;
meta_written: boolean;
index_rebuilt: boolean;
}> {
await ensureWikiInitialized(layout);
const file = sourceFileName(args.name);
const abs = sourcePath(layout, args.name);
await fs.mkdir(path.dirname(abs), { recursive: true });
const existed = await pathExists(abs);
let backupPath: string | null = null;
if (existed) {
backupPath = await backupFile(layout, abs, "sources", file);
}
await fs.writeFile(abs, args.content, "utf-8");
let metaWritten = false;
if (args.origin?.trim()) {
const meta = {
origin: args.origin.trim(),
saved_at: new Date().toISOString(),
};
await fs.writeFile(sourceMetaPath(layout, args.name), JSON.stringify(meta, null, 2) + "\n", "utf-8");
metaWritten = true;
}
const idx = await refreshIndex(layout);
await appendLog(
layout,
existed ? "source-update" : "source-save",
file,
args.origin?.trim() ? `origin: ${args.origin.trim()}` : "no origin given",
);
return {
name: file,
path: abs,
bytes: Buffer.byteLength(args.content, "utf-8"),
created: !existed,
backup_path: backupPath,
meta_written: metaWritten,
index_rebuilt: idx.rebuilt,
};
}