const NAMED_ENTITIES = { ' ': ' ', '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", ''': "'", }; const MAX_DECODE_ITERATIONS = 5; function decodeEntitiesOnce(text) { let result = text.replace(/&#(\d+);/g, (match, code) => { const num = Number(code); return Number.isFinite(num) ? String.fromCodePoint(num) : match; }); result = result.replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => { const num = parseInt(hex, 16); return Number.isFinite(num) ? String.fromCodePoint(num) : match; }); for (const [entity, char] of Object.entries(NAMED_ENTITIES)) { result = result.split(entity).join(char); } return result; } function stripHtmlTags(text) { return text.replace(/<[^>]*>/g, ' '); } // Decodifica HTML/entities (inclusive escapado multiplas vezes, ate MAX_DECODE_ITERATIONS // para nunca travar em loop) e remove tags -- sem apagar informacao (datas, nomes, numeros, // codigos de erro, hostname, versao continuam intactos: nenhuma dessas regras mexe em texto // que nao seja literalmente uma entidade HTML ou uma tag). function normalizeGlpiText(rawText) { if (rawText === null || rawText === undefined) return ''; let text = String(rawText); for (let i = 0; i < MAX_DECODE_ITERATIONS; i += 1) { const decoded = decodeEntitiesOnce(text); if (decoded === text) break; text = decoded; } text = stripHtmlTags(text); text = text .split('\n') .map((line) => line.replace(/[ \t]+/g, ' ').trim()) .join('\n') .replace(/\n{3,}/g, '\n\n') .trim(); return text; } module.exports = { normalizeGlpiText, decodeEntitiesOnce, stripHtmlTags };