┌─ TS ───────────────────────────────────────────────────────────────────────┐
│ // Directory registers: a dir.yml at any directory inside the kb tree │
│ // declares what its subtree is - personal notes, project docs, or │
│ // vendor reference copies. Resolution is nearest-ancestor; unmarked │
│ // content is notes, the natural resting state of a wiki. │
│ import { existsSync, readFileSync, readdirSync } from "node:fs"; │
│ import { join } from "node:path"; │
│ │
│ export type Register = "notes" | "docs" | "reference"; │
│ │
│ export const DEFAULT_REGISTER: Register = "notes"; │
│ │
│ export interface DirManifest { │
│ register: Register; │
│ /** Display name for the subtree, e.g. a project name. */ │
│ title?: string; │
│ } │
│ │
│ export interface RegisterInfo { │
│ register: Register; │
│ /** Manifest directory that decided the register; null when default. */ │
│ root: string | null; │
│ title: string | null; │
│ } │
│ │
│ const REGISTERS: Register[] = ["notes", "docs", "reference"]; │
│ │
│ // dir.yml is two known keys - a tiny parser beats a yaml dependency │
│ function parseManifest(text: string, at: string): DirManifest { │
│ const field = (key: string): string | null => { │
│ const m = text.match(new RegExp(`^${key}:\\s*["']?(.+?)["']?\\s*$`, "m")); │
│ return m ? m[1] : null; │
│ }; │
│ const register = field("register") as Register | null; │
│ if (!register || !REGISTERS.includes(register)) { │
│ throw new Error(`[kb] ${at}: register must be one of ${REGISTERS.join(", ")}`) │
│ ; │
│ } │
│ return { register, title: field("title") ?? undefined }; │
│ } │
│ │
│ /** Every dir.yml under the kb tree, keyed by directory id ("" root). */ │
│ export function readManifests(contentDir = "src/content/kb"): Map<string, DirManif │
│ est> { │
│ const out = new Map<string, DirManifest>(); │
│ if (!existsSync(contentDir)) return out; │
│ const walk = (dir: string, prefix: string) => { │
│ const manifest = join(dir, "dir.yml"); │
│ if (existsSync(manifest)) { │
│ out.set(prefix, parseManifest(readFileSync(manifest, "utf8"), manifest)); │
│ } │
│ for (const entry of readdirSync(dir, { withFileTypes: true })) { │
│ if (entry.isDirectory()) { │
│ walk(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.nam │
│ e); │
│ } │
│ } │
│ }; │
│ walk(contentDir, ""); │
│ return out; │
│ } │
│ │
│ /** Register of a page id, from the nearest ancestor manifest. */ │
│ export function registerOf(id: string, manifests: Map<string, DirManifest>): Regis │
│ terInfo { │
│ const segments = id.split("/").slice(0, -1); │
│ for (let i = segments.length; i >= 0; i--) { │
│ const dir = segments.slice(0, i).join("/"); │
│ const m = manifests.get(dir); │
│ if (m) return { register: m.register, root: dir, title: m.title ?? null }; │
│ } │
│ return { register: DEFAULT_REGISTER, root: null, title: null }; │
│ } │
│ │
│ /** Display label for a register, as shown in chrome and search. */ │
│ export const registerLabel = (r: Register): string => │
│ r === "notes" ? "NOTE" : r === "docs" ? "DOC" : "REF"; │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ TS ─────────────────────────────────┐
│ // Directory registers: a dir.yml at any dir │
│ ectory inside the kb tree │
│ // declares what its subtree is - personal n │
│ otes, project docs, or │
│ // vendor reference copies. Resolution is ne │
│ arest-ancestor; unmarked │
│ // content is notes, the natural resting sta │
│ te of a wiki. │
│ import { existsSync, readFileSync, readdirSy │
│ nc } from "node:fs"; │
│ import { join } from "node:path"; │
│ │
│ export type Register = "notes" | "docs" | "r │
│ eference"; │
│ │
│ export const DEFAULT_REGISTER: Register = "n │
│ otes"; │
│ │
│ export interface DirManifest { │
│ register: Register; │
│ /** Display name for the subtree, e.g. a p │
│ roject name. */ │
│ title?: string; │
│ } │
│ │
│ export interface RegisterInfo { │
│ register: Register; │
│ /** Manifest directory that decided the re │
│ gister; null when default. */ │
│ root: string | null; │
│ title: string | null; │
│ } │
│ │
│ const REGISTERS: Register[] = ["notes", "doc │
│ s", "reference"]; │
│ │
│ // dir.yml is two known keys - a tiny parser │
│ beats a yaml dependency │
│ function parseManifest(text: string, at: str │
│ ing): DirManifest { │
│ const field = (key: string): string | null │
│ => { │
│ const m = text.match(new RegExp(`^${key} │
│ :\\s*["']?(.+?)["']?\\s*$`, "m")); │
│ return m ? m[1] : null; │
│ }; │
│ const register = field("register") as Regi │
│ ster | null; │
│ if (!register || !REGISTERS.includes(regis │
│ ter)) { │
│ throw new Error(`[kb] ${at}: register mu │
│ st be one of ${REGISTERS.join(", ")}`); │
│ } │
│ return { register, title: field("title") ? │
│ ? undefined }; │
│ } │
│ │
│ /** Every dir.yml under the kb tree, keyed b │
│ y directory id ("" root). */ │
│ export function readManifests(contentDir = " │
│ src/content/kb"): Map<string, DirManifest> { │
│ const out = new Map<string, DirManifest>() │
│ ; │
│ if (!existsSync(contentDir)) return out; │
│ const walk = (dir: string, prefix: string) │
│ => { │
│ const manifest = join(dir, "dir.yml"); │
│ if (existsSync(manifest)) { │
│ out.set(prefix, parseManifest(readFile │
│ Sync(manifest, "utf8"), manifest)); │
│ } │
│ for (const entry of readdirSync(dir, { w │
│ ithFileTypes: true })) { │
│ if (entry.isDirectory()) { │
│ walk(join(dir, entry.name), prefix ? │
│ `${prefix}/${entry.name}` : entry.name); │
│ } │
│ } │
│ }; │
│ walk(contentDir, ""); │
│ return out; │
│ } │
│ │
│ /** Register of a page id, from the nearest │
│ ancestor manifest. */ │
│ export function registerOf(id: string, manif │
│ ests: Map<string, DirManifest>): RegisterInf │
│ o { │
│ const segments = id.split("/").slice(0, -1 │
│ ); │
│ for (let i = segments.length; i >= 0; i--) │
│ { │
│ const dir = segments.slice(0, i).join("/ │
│ "); │
│ const m = manifests.get(dir); │
│ if (m) return { register: m.register, ro │
│ ot: dir, title: m.title ?? null }; │
│ } │
│ return { register: DEFAULT_REGISTER, root: │
│ null, title: null }; │
│ } │
│ │
│ /** Display label for a register, as shown i │
│ n chrome and search. */ │
│ export const registerLabel = (r: Register): │
│ string => │
│ r === "notes" ? "NOTE" : r === "docs" ? "D │
│ OC" : "REF"; │
└──────────────────────────────────────────────┘