// XML/HTML escaping and tag emission. The character table is `@ultimat3/core`'s `escapeHtml`; this // file adds only what XML needs on top of it — the characters XML cannot carry at all — because a // sitemap, a feed and a
tag all fail the same way on an unescaped ampersand. import { escapeHtml } from '@ultimat3/core'; /** * The characters XML 1.0 excludes from `Char`: the C0 controls other than tab, LF and CR, and the * two non-characters at the end of the BMP. * * They are dropped rather than escaped because XML 1.0 offers no way to write one — `` is * illegal for exactly the same reason the raw byte is, so an emitter's only total move is to omit * it. And it has to happen HERE, in the escaper every element, attribute and CDATA section goes * through: one such byte in one `FeedItem` title makes the whole document not well-formed, and a * reader answers that with "invalid XML" rather than with the 49 items it could have parsed. A * scraped title, a paste out of a word processor and a `\x00` a `text` column stored without * complaint all produce one. */ // The same exemption `@ultimat3/core`'s `error-render.ts` and `@ultimat3/schema`'s `errors.ts` // take, for the same reason. // biome-ignore lint/suspicious/noControlCharactersInRegex: naming them is the point. const ILLEGAL_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]/g; /** Never a surrogate range: an astral character is a legal PAIR, and half of one is worse. */ const legal = (value: string): string => value.replace(ILLEGAL_XML, ''); /** * Element text and attribute values alike: the XML-illegal characters dropped, then core's one * character table. `'` is a numeric reference, so the same output is well-formed XML and valid * HTML — which is why the `` tags and `