export interface ConfigValidationIssue { code: string; message: string; path: string; } export function dedupeIssues(issues: ConfigValidationIssue[]): ConfigValidationIssue[] { const seen = new Set(); const result: ConfigValidationIssue[] = []; for (const item of issues) { const key = `${item.code}:${item.path}:${item.message}`; if (seen.has(key)) continue; seen.add(key); result.push(item); } return result; } export function formatConfigIssues(issues: ConfigValidationIssue[]): string { return issues.map(formatConfigIssue).join("\n"); } export function issue(code: string, path: string, message: string): ConfigValidationIssue { return { code, message, path }; } export function joinPath(base: string, key: string): string { if (base === "") return key; if (key.startsWith("[")) return `${base}${key}`; return `${base}.${key}`; } export function renderPath(path: string): string { return path === "" ? "配置文件" : path; } export function throwConfigIssues(issues: ConfigValidationIssue[]): never { throw new Error(formatConfigIssues(issues)); } function formatConfigIssue(i: ConfigValidationIssue): string { return `${renderPath(i.path)} ${i.message}`; }