src/normalization/text.ts

type TextReplacement = { pattern: RegExp; replacement: string; };

type NormalizeTextOptions = { maxLen?: number; minLen?: number; ellipsis?: boolean; replacements?: TextReplacement[]; trim?: boolean; };

export function normalizeWhitespaceText(value: unknown, options: NormalizeTextOptions = {}): string { const shouldTrim = options.trim !== false; let normalized = String(value || "");

const replacements = Array.isArray(options.replacements) ? options.replacements : []; for (const entry of replacements) { normalized = normalized.replace(entry.pattern, entry.replacement); }

normalized = normalized.replace(/\s+/g, " "); if (shouldTrim) normalized = normalized.trim();

const maxCandidate = Number(options.maxLen); if (!Number.isFinite(maxCandidate)) return normalized;

const maxLen = Math.max(0, Math.floor(maxCandidate)); const minLen = Math.max(0, Math.floor(Number(options.minLen) || 0)); const boundedMax = Math.max(minLen, maxLen);

if (normalized.length <= boundedMax) return normalized;

if (options.ellipsis) { return ${normalized.slice(0, Math.max(0, boundedMax - 1)).trimEnd()}…; }

return normalized.slice(0, boundedMax); }