// Безопасная сериализация YAML-скаляра. Общая для генераторов frontmatter и // style-settings: пользовательский ввод не должен ломать/тихо терять данные в YAML. // YAML plain-scalar indicator chars: значение, начинающееся с любого из них, // нужно квотить, иначе парсер прочитает его как seq/map/anchor/tag/comment/число. const LEADING_INDICATORS = new Set([ "-", "?", ":", ",", "[", "]", "{", "}", "#", "&", "*", "!", "|", ">", "'", '"', "%", "@", "`", ]); /** * True, когда значение сломает или коэрцитнёт тип, если вывести его как plain-scalar. * Покрывает: пустое, окружающие пробелы, ведущие индикаторы, "двоеточие+пробел", * inline-комментарий "#", и значения, выглядящие как число/булево/null/дата/время. */ export function yamlScalarNeedsQuotes(v: string): boolean { if (v === "") return true; if (v !== v.trim()) return true; if (LEADING_INDICATORS.has(v[0])) return true; if (v.includes(": ") || v.endsWith(":")) return true; if (v.includes(" #")) return true; if (/[\n\t]/.test(v)) return true; if (/^(true|false|yes|no|on|off|null|~)$/i.test(v)) return true; // bool / null if (/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/.test(v)) return true; // number if (/^0x[0-9a-fA-F]+$/.test(v)) return true; // hex int if (/^\d{1,2}:\d{2}(:\d{2})?$/.test(v)) return true; // time HH:MM(:SS) — YAML читает как base-60 if (/^\d{4}-\d{2}-\d{2}([Tt ].*)?$/.test(v)) return true; // date / datetime-looking return false; } /** Вывести значение как безопасный YAML-скаляр — в кавычках только когда нужно. */ export function yamlScalar(v: string): string { return yamlScalarNeedsQuotes(v) ? '"' + v.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"' : v; }