/* Normalize generated Markdown fragments before they enter public text artifacts. Do not parse or reformat arbitrary Markdown documents. Do not mutate source content records. RFC-0316: Added public text artifact Markdown hygiene helpers. */ function normalizeLine(line: string): string { return line .replace(/^- ---\s*$/, "---") .replace(/^- -\s+/, "- ") .replace(/^- (#{1,6})\s+/, "$1 ") .replace(/^- (\d+\.)\s+/, "$1 "); } export function normalizeGeneratedMarkdownText(text: string | undefined): string { if (!text) return ""; return text .replace(/\r\n?/g, "\n") .replace(//gi, "\n") .replace(/\b(20\d{2})\/(\d{1,2})\/(\d{1,2})\b/g, (_match, year, month, day) => { return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`; }) .split("\n") .map(normalizeLine) .join("\n") .replace(/[ \t]+\n/g, "\n") .trim(); } function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } export function canonicalizeGeneratedMarkdownText( text: string | undefined, options: { baseUrl?: string; defaultLanguage?: string } = {}, ): string { let normalized = normalizeGeneratedMarkdownText(text); const { baseUrl, defaultLanguage } = options; if (baseUrl && defaultLanguage) { const base = baseUrl.replace(/\/+$/, ""); normalized = normalized.replace( new RegExp(`${escapeRegExp(base)}/${escapeRegExp(defaultLanguage)}(?=/)`, "g"), base, ); } return normalized; } function looksLikeMarkdownBlock(text: string): boolean { return text.includes("\n") || /^(#{1,6}\s+|[-*]\s+|\d+\.\s+|---$|>\s+)/.test(text.trim()); } export function formatGeneratedMarkdownListItem(text: string): string[] { const normalized = normalizeGeneratedMarkdownText(text); if (!normalized) return []; if (looksLikeMarkdownBlock(normalized)) { return normalized.split("\n"); } return [`- ${normalized}`]; }