{"version":3,"file":"escape-DFTE7ZJc.mjs","names":[],"sources":["../src/utils/escape.ts"],"sourcesContent":["// XML 1.0 cell-string escaping. Mirrors openpyxl/openpyxl/utils/escape.py.\n//\n// Excel emits cell strings with control characters and other illegal\n// XML 1.0 codepoints encoded as `_xHHHH_` (uppercase hex). The\n// underscore itself is a literal in normal text but a sequence opener\n// in escape position; an existing `_xHHHH_` in the input is therefore\n// re-escaped to `_x005F_xHHHH_` so it round-trips losslessly.\n\n// Escape every C0 control character (U+0000 through U+001F) plus the\n// surrogate range U+D800–U+DFFF. NUL is invalid in XML 1.0 entirely.\n// This range deliberately covers `\\t` (U+0009), `\\n` (U+000A) and `\\r`\n// (U+000D) even though XML 1.0 considers them legal whitespace — XML\n// parsers normalise CRLF / lone CR to LF on read, so a cell string\n// containing `\\r` would silently lose its CR without the `_x000D_`\n// encoding. openpyxl escapes the same `\\x01-\\x19` range; we add NUL\n// to keep the writer well-formed when callers feed in binary data.\nconst ILLEGAL_RE =\n  // biome-ignore lint/suspicious/noControlCharactersInRegex: by design — these are the codepoints we replace\n  /[\\x00-\\x1F\\ud800-\\udfff]/g;\nconst ESCAPED_PATTERN_RE = /(_)(x[0-9A-Fa-f]{4}_)/g;\n\nconst toHex4 = (n: number): string => n.toString(16).toUpperCase().padStart(4, '0');\n\n/**\n * Escape a string for safe storage in an OOXML cell. Already-escaped\n * sequences (`_xHHHH_`) are protected by escaping their leading\n * underscore; illegal codepoints are replaced with their `_xHHHH_`\n * representation.\n */\nexport function escapeCellString(s: string): string {\n  // Re-escape any existing `_xHHHH_` so it round-trips; the underscore\n  // becomes `_x005F_` and the rest of the sequence is left as-is.\n  const protectedString = s.replace(ESCAPED_PATTERN_RE, '_x005F_$2');\n  return protectedString.replace(ILLEGAL_RE, (ch) => `_x${toHex4(ch.charCodeAt(0))}_`);\n}\n\nconst UNESCAPE_RE = /_x([0-9A-Fa-f]{4})_/g;\n\n/**\n * Inverse of {@link escapeCellString}. Looking from left to right\n * we replace any `_xHHHH_` sequence with the corresponding code unit;\n * the protected `_x005F_` becomes a literal underscore which the\n * subsequent replacements skip safely (replace's regex is non-overlapping).\n */\nexport function unescapeCellString(s: string): string {\n  return s.replace(UNESCAPE_RE, (_full, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)));\n}\n\n// ---- XML escape helpers ----------------------------------------------------\n//\n// One canonical implementation for text-node and attribute escaping. The\n// previous codebase carried three near-identical copies (one each in save.ts,\n// xml/serializer.ts, xml/stream-writer.ts); they disagreed about `>`-in-attribute\n// handling and would have drifted further apart over time. Keeping them in a\n// single place also makes future fixes (e.g. surrogate-pair scrubbing) land\n// once rather than three times.\n\n/**\n * Escape a string for safe placement in an XML text node. Replaces the three\n * codepoints that would otherwise terminate the text region or open a markup\n * sequence (`&`, `<`, `>`); `\"` and `'` are not legal markup terminators\n * inside text and stay verbatim.\n */\nexport function escapeXmlText(s: string): string {\n  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\n/**\n * Escape a string for safe placement inside a `\"`-quoted XML attribute.\n * Handles `&`, `<`, `>` and the `\"` that would otherwise close the value.\n *\n * Note: this deliberately does NOT escape `\\r` / `\\n` / `\\t` to numeric\n * character references. XML 1.0 attribute-value normalisation would\n * collapse them to spaces in theory, but the parser used on the read side\n * (fast-xml-parser) does not decode numeric character references, so a\n * write-then-read round-trip would surface the literal `&#9;` instead of\n * recovering the original tab. Leaving the whitespace bytes literal keeps\n * the round-trip stable and matches what Excel itself emits.\n */\nexport function escapeXmlAttr(s: string): string {\n  return s\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;');\n}\n"],"mappings":";AAgBA,MAAM,aAEJ;AACF,MAAM,qBAAqB;AAE3B,MAAM,UAAU,MAAsB,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,GAAG,GAAG;;;;;;;AAQlF,SAAgB,iBAAiB,GAAmB;CAIlD,OADwB,EAAE,QAAQ,oBAAoB,WACjC,CAAC,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO,GAAG,WAAW,CAAC,CAAC,EAAE,EAAE;AACrF;AAEA,MAAM,cAAc;;;;;;;AAQpB,SAAgB,mBAAmB,GAAmB;CACpD,OAAO,EAAE,QAAQ,cAAc,OAAO,QAAgB,OAAO,aAAa,OAAO,SAAS,KAAK,EAAE,CAAC,CAAC;AACrG;;;;;;;AAiBA,SAAgB,cAAc,GAAmB;CAC/C,OAAO,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM;AAC5E;;;;;;;;;;;;;AAcA,SAAgB,cAAc,GAAmB;CAC/C,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ;AAC3B"}