// horst — settings. // // Parses the `horst` block from Pi settings. export interface HorstSettings { os: Record; hosts: Record; preferredShell?: string; } export const EMPTY_HORST_SETTINGS: HorstSettings = { os: {}, hosts: {} }; function horstOsKey(platform: string): string | undefined { if (platform === "linux") return "lnx"; if (platform === "darwin") return "mac"; if (platform === "win32") return "win32"; return undefined; } function isObject(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function pickMap(value: unknown, at: string): Record { if (value === undefined) return {}; if (!isObject(value)) throw new Error(`${at} must be an object`); const map: Record = {}; for (const [key, entry] of Object.entries(value)) { const tools = isObject(entry) ? entry["tools"] : undefined; if (!Array.isArray(tools)) throw new Error( `${at}.${key}.tools must be an array of +name/-name modifiers; use ["+name", "-name"] instead of { add, remove }`, ); if ( !tools.every( (tool) => typeof tool === "string" && (tool.startsWith("+") || tool.startsWith("-")) && tool.length > 1 && tool === tool.trim() && tool.slice(1) === tool.slice(1).trim() && !tool.includes("*"), ) ) throw new Error( `${at}.${key}.tools must contain only +name/-name modifiers with non-empty exact tool names, not patterns`, ); map[key.toLowerCase()] = tools; } return map; } /** Validate the `horst` block; throws on malformed values. */ export function parseHorst(raw: unknown): HorstSettings { if (!isObject(raw)) throw new Error("expected an object"); const { preferredShell } = raw; if ( preferredShell !== undefined && (typeof preferredShell !== "string" || !preferredShell.trim()) ) throw new Error("preferredShell must be a non-empty string"); return { os: pickMap(raw["os"], "os"), hosts: pickMap(raw["hosts"], "hosts"), ...(preferredShell ? { preferredShell: preferredShell.trim() } : {}), }; } /** Apply OS modifiers before matching host modifiers, in list order. */ export function resolveHorstTools( settings: HorstSettings, platform: string, hostname: string, ): string[] | undefined { const osTools = settings.os[horstOsKey(platform) ?? ""]; const hostTools = settings.hosts[hostname.toLowerCase()]; if (!osTools && !hostTools) return undefined; return [...(osTools ?? []), ...(hostTools ?? [])]; }