// A line-by-line port of src/preprocess.py, so the browser app sees text exactly // as the models did in training. scripts/web_parity.mjs checks it against the // Python output on every message in the project. // // Python's re module treats \w, \d and \b as Unicode-aware for str patterns; // JavaScript's are ASCII-only, so they are spelled out with Unicode properties. // Python's \s (str.isspace) also differs from JavaScript's: it includes U+001C-U+001F // and U+0085 but not U+FEFF, so whitespace is spelled out too. const W = "[\\p{L}\\p{N}_]"; // Python's \w for str const D = "\\p{Nd}"; // Python's \d for str const B = `(?:(?<=${W})(?!${W})|(?"; export const AMOUNT = ""; export const URL = ""; export const PLACEHOLDERS = [PHONE, AMOUNT, URL]; const URL_RE = new RegExp( `(?:https?://|www\\.)${NS}+` + `|${B}(?:wa\\.me|bit\\.ly|t\\.me|tinyurl\\.com|chat\\.whatsapp\\.com)/${NS}*`, "giu"); const PHONE_RE = new RegExp(`(?||/g; function undoLeet(token) { if (PLACEHOLDERS.includes(token) || !/[A-Za-z]/.test(token)) return token; return token.replace(LEET_RE, (m) => LEET[m]); } export function normalise(text) { const parts = text.split(PLACEHOLDER_RE); const holders = text.match(PLACEHOLDER_RE) || []; const cleaned = parts.map((part) => { part = part.split(" ").map(undoLeet).join(" "); return part.replace(SPACED_RE, (m) => m.replace(/[ .\-]+/g, "")); }); let out = cleaned[0]; holders.forEach((h, i) => { out += h + cleaned[i + 1]; }); return out.replace(SPACES_RE, " ").trim(); } export function preprocess(text, { doMask = true, defend = false } = {}) { text = String(text).normalize("NFC"); if (defend) { text = Array.from(text.replace(ZERO_WIDTH_RE, ""), (ch) => CONFUSABLES[ch] ?? ch).join(""); } text = text.replace(SPACES_RE, " ").trim(); if (doMask) text = mask(text); if (defend) text = normalise(text); return text; }