Initial commit

This commit is contained in:
mats committed 2026-10-19 16:04:17 +02:00
commit afed023152
8152 files changed
+2086461

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+28
View File
@@ -0,0 +1,28 @@
/**
* Replace the given code point with U+FFFD if it is NUL (0), a surrogate, or
* outside the valid Unicode range. Code points in the C1 controls range
* (128–159) are remapped to their Windows-1252 equivalents, following the
* HTML spec. All other code points are returned unchanged.
* @param codePoint Unicode code point to convert.
*/
export declare function replaceCodePoint(codePoint: number): number;
/**
* XML numeric character references are the referenced Unicode code point.
* Invalid values still become U+FFFD; the HTML Windows-1252 C1 remap is not
* applied.
* @see https://www.w3.org/TR/xml/#NT-CharRef
* @param codePoint Unicode code point to convert.
*/
export declare function replaceCodePointXML(codePoint: number): number;
/**
* Convert the code point of a decoded numeric entity to a string, replacing
* invalid values.
*
* Fast path for plain BMP code points: [1..0x7F] and [0xA0..0xD7FF] pass
* `replaceCodePoint` unchanged (no NUL, C1 remap, surrogate, or out-of-range
* handling) and fit a single charCode. 0xd760 = 0xD800 (the first surrogate)
* - 0xA0.
* @param codePoint Unicode code point to convert.
*/
export declare function codePointToString(codePoint: number): string;
//# sourceMappingURL=decode-codepoint.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode-codepoint.d.ts","sourceRoot":"","sources":["../src/decode-codepoint.ts"],"names":[],"mappings":"AAwBA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAU1D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAI3D"}
+61
View File
@@ -0,0 +1,61 @@
// Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134
/**
* C1 Unicode control character reference replacements (code points 128–159).
* Index i gives the replacement for code point 128+i; 0 means "no replacement".
*/
const c1 = [
8364, 0, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 0,
381, 0, 0, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250,
339, 0, 382, 376,
];
/**
* True for NUL, UTF-16 surrogates, and values past U+10FFFF.
* @param codePoint Unicode code point to check.
*/
function isInvalidCodePoint(codePoint) {
return (codePoint === 0 ||
(codePoint >= 0xd8_00 && codePoint <= 0xdf_ff) ||
codePoint > 0x10_ff_ff);
}
/**
* Replace the given code point with U+FFFD if it is NUL (0), a surrogate, or
* outside the valid Unicode range. Code points in the C1 controls range
* (128–159) are remapped to their Windows-1252 equivalents, following the
* HTML spec. All other code points are returned unchanged.
* @param codePoint Unicode code point to convert.
*/
export function replaceCodePoint(codePoint) {
if (isInvalidCodePoint(codePoint)) {
return 0xff_fd;
}
if (codePoint >= 128 && codePoint <= 159) {
return c1[codePoint - 128] || codePoint;
}
return codePoint;
}
/**
* XML numeric character references are the referenced Unicode code point.
* Invalid values still become U+FFFD; the HTML Windows-1252 C1 remap is not
* applied.
* @see https://www.w3.org/TR/xml/#NT-CharRef
* @param codePoint Unicode code point to convert.
*/
export function replaceCodePointXML(codePoint) {
return isInvalidCodePoint(codePoint) ? 0xff_fd : codePoint;
}
/**
* Convert the code point of a decoded numeric entity to a string, replacing
* invalid values.
*
* Fast path for plain BMP code points: [1..0x7F] and [0xA0..0xD7FF] pass
* `replaceCodePoint` unchanged (no NUL, C1 remap, surrogate, or out-of-range
* handling) and fit a single charCode. 0xd760 = 0xD800 (the first surrogate)
* - 0xA0.
* @param codePoint Unicode code point to convert.
*/
export function codePointToString(codePoint) {
return (codePoint - 1) >>> 0 < 0x7f || (codePoint - 0xa0) >>> 0 < 0xd7_60
? String.fromCharCode(codePoint)
: String.fromCodePoint(replaceCodePoint(codePoint));
}
//# sourceMappingURL=decode-codepoint.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode-codepoint.js","sourceRoot":"","sources":["../src/decode-codepoint.ts"],"names":[],"mappings":"AAAA,qHAAqH;AAErH;;;GAGG;AACH,MAAM,EAAE,GAAa;IACjB,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACxE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI;IACzE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG;CACnB,CAAC;AAEF;;;GAGG;AACH,SAAS,kBAAkB,CAAC,SAAiB;IACzC,OAAO,CACH,SAAS,KAAK,CAAC;QACf,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,CAAC;QAC9C,SAAS,GAAG,UAAU,CACzB,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAC9C,IAAI,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,IAAI,SAAS,IAAI,GAAG,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;QACvC,OAAO,EAAE,CAAC,SAAS,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACjD,OAAO,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IAC/C,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO;QACrE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC;QAChC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AAC5D,CAAC"}
+227
View File
@@ -0,0 +1,227 @@
/**
* Decoding mode for named entities.
*/
export declare enum DecodingMode {
/** Entities in text nodes that can end with any character. */
Legacy = 0,
/** Only allow entities terminated with a semicolon. */
Strict = 1,
/** Entities in attributes have limitations on ending characters. */
Attribute = 2
}
/**
* Producers for character reference errors as defined in the HTML spec.
*/
export interface EntityErrorProducer {
missingSemicolonAfterCharacterReference(): void;
absenceOfDigitsInNumericCharacterReference(consumedCharacters: number): void;
/**
* Validate the accumulated numeric value, before Unicode replacement.
* Values beyond the JavaScript number range are positive infinity.
*/
validateNumericCharacterReference(code: number): void;
}
/**
* Token decoder with support of writing partial entities.
*/
export declare class EntityDecoder {
/** The predefined HTML or XML decode tree. */
private readonly decodeTree;
/**
* The function that is called when a codepoint is decoded.
*
* For named entities that decode to multiple code points, this will
* be called multiple times, with the second codepoint, and the same
* `consumed` value.
* @param codepoint The decoded codepoint.
* @param consumed The number of characters consumed by the decoder.
*/
private readonly emitCodePoint;
/** An object that is used to produce errors. */
private readonly errors?;
/** The current state of the decoder. */
private state;
/** Characters that were consumed while parsing an entity. */
private consumed;
/**
* The result of the entity.
*
* For named entities: the trie index of the best legacy match so far
* (0 = none). For numeric entities: the accumulated code point.
*/
private result;
/** The current index in the decode tree. */
private treeIndex;
/**
* Characters consumed since the last recorded legacy match, plus one.
* Invariant at the top of the `stateNamedEntity` loop: `excess` equals
* the number of unrecorded consumed characters + 1.
*/
private excess;
/** The mode in which the decoder is operating. */
private decodeMode;
/** The number of characters that have been consumed in the current run. */
private runConsumed;
constructor(
/** The predefined HTML or XML decode tree. */
decodeTree: Uint16Array,
/**
* The function that is called when a codepoint is decoded.
*
* For named entities that decode to multiple code points, this will
* be called multiple times, with the second codepoint, and the same
* `consumed` value.
* @param codepoint The decoded codepoint.
* @param consumed The number of characters consumed by the decoder.
*/
emitCodePoint: (cp: number, consumed: number) => void,
/** An object that is used to produce errors. */
errors?: EntityErrorProducer | undefined);
/**
* Resets the instance to make it reusable.
* @param decodeMode Entity decoding mode to use.
*/
startEntity(decodeMode: DecodingMode): void;
/**
* Write an entity to the decoder. This can be called multiple times with partial entities.
* If the entity is incomplete, the decoder will return -1.
*
* Mirrors the non-streaming `decodeWithTrie`, but with the ability to stop decoding if the
* entity is incomplete, and resume when the next string is written.
* @param input The string containing the entity (or a continuation of the entity).
* @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
*/
write(input: string, offset: number): number;
/**
* Switches between the numeric decimal and hexadecimal states.
*
* Equivalent to the `Numeric character reference state` in the HTML spec.
* @param input The string containing the entity (or a continuation of the entity).
* @param offset The current offset.
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
*/
private stateNumericStart;
/**
* Parses a hexadecimal numeric entity.
*
* Equivalent to the `Hexademical character reference state` in the HTML
* spec. Digit parsing matches the hex loop in `parseNumericEntity`.
* The accumulated value is preserved for numeric validation callbacks.
* @param input The string containing the entity (or a continuation of the entity).
* @param offset The current offset.
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
*/
private stateNumericHex;
/**
* Parses a decimal numeric entity.
*
* Equivalent to the `Decimal character reference state` in the HTML
* spec. Digit parsing matches the decimal loop in `parseNumericEntity`.
* The accumulated value is preserved for numeric validation callbacks.
* @param input The string containing the entity (or a continuation of the entity).
* @param offset The current offset.
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
*/
private stateNumericDecimal;
/**
* Validate and emit a numeric entity.
*
* Implements the logic from the `Hexademical character reference start
* state` and `Numeric character reference end state` in the HTML spec.
* @param lastCp The last code point of the entity. Used to see if the
* entity was terminated with a semicolon.
* @param expectedLength The minimum number of characters that should be
* consumed. Used to validate that at least one digit
* was consumed.
* @returns The number of characters that were consumed.
*/
private emitNumericEntity;
/**
* Flush locally-tracked walk state back to the fields, then emit the
* recorded legacy match or reject (cold path — at most once per
* entity). Called after failed navigation (leaf node, branch miss, or
* compact-run mismatch). In attribute mode, reject if no legacy was
* recorded at the current node, if we descended past it, or if the
* pending input character is an invalid attribute terminator.
* @param consumed Locally-tracked consumed count.
* @param excess Locally-tracked excess count.
* @param char Pending input character (may be the mismatching char).
* @param valueLength Value length at the current trie node.
*/
private flushAndEmitLegacyOrReject;
/**
* Parses a named entity.
*
* Equivalent to the `Named character reference state` in the HTML spec.
* @param input The string containing the entity (or a continuation of the entity).
* @param offset The current offset.
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
*/
private stateNamedEntity;
/**
* Emit a named entity that was not terminated with a semicolon.
* @returns The number of characters consumed.
*/
private emitNotTerminatedNamedEntity;
/**
* Emit a named entity.
* @param result The index of the entity in the decode tree.
* @param valueLength Encoded value length (header plus any value words).
* @param consumed The number of characters consumed.
* @returns The number of characters consumed.
*/
private emitNamedEntityData;
/**
* Signal to the parser that the end of the input was reached.
*
* Remaining data will be emitted and relevant errors will be produced.
* @returns The number of characters consumed.
*/
end(): number;
}
/**
* Determines the branch of the current node that is taken given the current
* character. This function is used to traverse the trie.
*
* See `BinTrieFlags` for the branch-data layouts handled here.
* @param decodeTree The trie.
* @param current The current node's header word.
* @param nodeIndex Index of the node's first branch-data word (the header
* plus any value words have been skipped by the caller).
* @param char The current character.
* @returns The index of the next node, or -1 if no branch is taken.
*/
export declare function determineBranch(decodeTree: Uint16Array, current: number, nodeIndex: number, char: number): number;
/**
* Decodes an HTML string.
* @param htmlString The string to decode.
* @param mode The decoding mode.
* @returns The decoded string.
*/
export declare function decodeHTML(htmlString: string, mode?: DecodingMode): string;
/**
* Decodes an HTML string in an attribute.
* @param htmlAttribute The string to decode.
* @returns The decoded string.
*/
export declare function decodeHTMLAttribute(htmlAttribute: string): string;
/**
* Decodes an HTML string, requiring all entities to be terminated by a semicolon.
* @param htmlString The string to decode.
* @returns The decoded string.
*/
export declare function decodeHTMLStrict(htmlString: string): string;
/**
* Decodes an XML string, requiring all entities to be terminated by a semicolon.
*
* Uses a hand-coded fast path for the 5 XML named entities (amp, lt, gt,
* quot, apos) plus numeric entities, bypassing the trie entirely.
* @param xmlString The string to decode.
* @returns The decoded string.
*/
export declare function decodeXML(xmlString: string): string;
export { replaceCodePoint, replaceCodePointXML, } from "./decode-codepoint.js";
export { htmlDecodeTree } from "./generated/decode-data-html.js";
export { xmlDecodeTree } from "./generated/decode-data-xml.js";
//# sourceMappingURL=decode.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../src/decode.ts"],"names":[],"mappings":"AAiGA;;GAEG;AACH,oBAAY,YAAY;IACpB,8DAA8D;IAC9D,MAAM,IAAI;IACV,uDAAuD;IACvD,MAAM,IAAI;IACV,oEAAoE;IACpE,SAAS,IAAI;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,uCAAuC,IAAI,IAAI,CAAC;IAChD,0CAA0C,CACtC,kBAAkB,EAAE,MAAM,GAC3B,IAAI,CAAC;IACR;;;OAGG;IACH,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzD;AAED;;GAEG;AACH,qBAAa,aAAa;IA6BlB,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAzC5B,wCAAwC;IACxC,OAAO,CAAC,KAAK,CAA0C;IACvD,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAK;IACrB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAK;IAEnB,4CAA4C;IAC5C,OAAO,CAAC,SAAS,CAAK;IACtB;;;;OAIG;IAEH,OAAO,CAAC,MAAM,CAAK;IACnB,kDAAkD;IAClD,OAAO,CAAC,UAAU,CAAuB;IACzC,2EAA2E;IAE3E,OAAO,CAAC,WAAW,CAAK;;IAGpB,8CAA8C;IAC7B,UAAU,EAAE,WAAW;IACxC;;;;;;;;OAQG;IACc,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI;IACtE,gDAAgD;IAC/B,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS;IAG7D;;;OAGG;IACH,WAAW,CAAC,UAAU,EAAE,YAAY,GAAG,IAAI;IAU3C;;;;;;;;;OASG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IA+B5C;;;;;;;OAOG;IAEH,OAAO,CAAC,iBAAiB;IAezB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IA2BvB;;;;;;;;;OASG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,iBAAiB;IAkCzB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,0BAA0B;IAiBlC;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAwOxB;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAWpC;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;;;;OAKG;IACH,GAAG,IAAI,MAAM;CA6BhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC3B,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACb,MAAM,CAiDR;AA4aD;;;;;GAKG;AACH,wBAAgB,UAAU,CACtB,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,YAAkC,GACzC,MAAM,CAMR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA4GnD;AAED,OAAO,EACH,gBAAgB,EAChB,mBAAmB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC"}
+1132
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode.js","sourceRoot":"","sources":["../src/decode.ts"],"names":[],"mappings":"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 truncated
+26
View File
@@ -0,0 +1,26 @@
/**
* Encodes all characters in the input using HTML entities. This includes
* characters that are valid ASCII characters in HTML documents, such as `#`.
*
* To get a more compact output, consider using the `encodeNonAsciiHTML`
* function, which will only encode characters that are not valid in HTML
* documents, as well as non-ASCII characters.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export declare function encodeHTML(input: string): string;
/**
* Encodes all non-ASCII characters, as well as characters not valid in HTML
* documents using HTML entities. This function will not encode characters that
* are valid in HTML documents, such as `#`.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export declare function encodeNonAsciiHTML(input: string): string;
/** @internal Exported for tests; not re-exported from the package entry. */
export declare const INLINE_SCAN_WINDOW = 16;
//# sourceMappingURL=encode.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"AAmKA;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AACD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAwBD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,KAAK,CAAC"}
+295
View File
@@ -0,0 +1,295 @@
import { XML_BITSET_VALUE, xmlEncodeRegex } from "./escape.js";
import htmlTrieData from "./generated/encode-html.js";
/**
* Flat lookup table for ASCII entity values (code points 0–127).
*
* Built once at startup from the full trie. For ASCII characters that need
* encoding, a direct `asciiEntities[charCode]` array index is much faster
* than `Map.get(charCode)` because it avoids hashing and bucket lookup.
*
* A few ASCII characters also start multi-code-point entities
* (e.g. `<` + U+20D2 → `&nvlt;`). For those, the table holds the branch
* node itself (children plus the single-char value), so the ASCII path
* never consults `htmlTrie`.
*/
const asciiEntities = /* #__PURE__ */ Array.from({ length: 128 }, () => null);
const htmlTrie = (() => {
/**
* Parse a compact encode trie string into a Map keyed by code point.
*
* The serialized format (produced by `scripts/write-encode-map.ts`) stores
* entries in ascending code-point order with delta encoding:
*
* [gap]name;[{children}]
*
* - `gap` is a base-10 integer giving `currentKey - previousKey - 1`.
* The very first entry stores the absolute key. A gap of 0 is omitted.
* - `name;` is the entity name (without `&` prefix) terminated by `;`.
* Because gaps use only digits `[0-9]` and entity names always start with
* a letter `[A-Za-z]`, no additional delimiter is needed.
* - `{…}` is an optional children block using the same scheme recursively.
* Children represent the second code unit of multi-character entities
* (e.g. `<` + U+20D2 → `&nvlt;`).
*/
const trie = new Map();
const data = htmlTrieData;
let cursor = 0;
let lastKey = -1;
function readGap() {
let value = 0;
let ch;
while ((ch = data.charCodeAt(cursor)) >= 48 /* '0' */ &&
ch <= 57 /* '9' */) {
value = value * 10 + ch - 48;
cursor++;
}
return value;
}
function readEntity() {
const semi = data.indexOf(";", cursor);
const entity = `&${data.substring(cursor, semi)};`;
cursor = semi + 1;
return entity;
}
const astralEntries = [];
while (cursor < data.length) {
lastKey += readGap() + 1;
const entityValue = data.charCodeAt(cursor) === 123 /* '{' */ ? null : readEntity();
if (data.charCodeAt(cursor) === 123 /* '{' */) {
cursor++; // Skip '{'
const next = new Map();
let childKey = -1;
while (data.charCodeAt(cursor) !== 125 /* '}' */) {
childKey += readGap() + 1;
next.set(childKey, readEntity());
}
const branch = { value: entityValue, next };
trie.set(lastKey, branch);
cursor++; // Skip '}'
// ASCII fast path holds the branch node itself (children + value).
if (lastKey < 0x80) {
asciiEntities[lastKey] = branch;
}
}
else if (lastKey < 0x80) {
asciiEntities[lastKey] = entityValue;
}
else if (lastKey > 0xff_ff) {
astralEntries.push([lastKey, entityValue]);
}
else {
trie.set(lastKey, entityValue);
}
}
/*
* Batch-insert astral entries as surrogate-pair trie nodes.
* Entries are sorted by code point, so same-high-surrogate groups
* are contiguous — no intermediate grouping Map needed.
*/
let astralIndex = 0;
while (astralIndex < astralEntries.length) {
const hi = 0xd8_00 | ((astralEntries[astralIndex][0] - 0x1_00_00) >> 10);
const children = [];
while (astralIndex < astralEntries.length &&
(0xd8_00 | ((astralEntries[astralIndex][0] - 0x1_00_00) >> 10)) ===
hi) {
const lo = 0xdc_00 |
((astralEntries[astralIndex][0] - 0x1_00_00) & 0x3_ff);
children.push([lo, astralEntries[astralIndex][1]]);
astralIndex++;
}
trie.set(hi, { value: null, next: new Map(children) });
}
return trie;
})();
/**
* Bitset covering ASCII code points 0–127. Each of the four 32-bit words
* covers a 32-code-point range. A set bit means "this character needs
* encoding" when used with `encodeHTML`.
*/
const HTML_BITSET = /* #__PURE__ */ new Uint32Array([
0x16_00, // 09 (\t), 0A (\n), 0C (\f)
0xfc_00_ff_fe, // 21-2D (!-.), 2E (.), 2F (/), 3A-3F (:;<=>?)
0xf8_00_00_01, // 40 (@), 5B-5F ([\]^_)
0x38_00_00_01, // 60 (`), 7B-7D ({|})
]);
const XML_BITSET = /* #__PURE__ */ new Uint32Array([0, XML_BITSET_VALUE, 0, 0]);
/*
* Regex equivalent of `HTML_BITSET` (plus all non-ASCII code units, lone
* surrogates included — no `u` flag). Must stay in sync with the bitset:
* the scan uses the regex to find candidates and the bitset to re-check
* adjacent characters. The XML equivalent is `xmlEncodeRegex`, shared with
* `escape.ts`.
*/
// eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes -- the `\u{...}` form requires the `u` flag, which we deliberately omit so lone surrogates match by code unit
const HTML_ENCODE_RE = /[\t\n\f!-/:-@[-`{-}\u0080-\uFFFF]/g;
const numericReference = (cp) => `&#x${cp.toString(16)};`;
/**
* Encodes all characters in the input using HTML entities. This includes
* characters that are valid ASCII characters in HTML documents, such as `#`.
*
* To get a more compact output, consider using the `encodeNonAsciiHTML`
* function, which will only encode characters that are not valid in HTML
* documents, as well as non-ASCII characters.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeHTML(input) {
return encodeHTMLTrieRe(HTML_BITSET, HTML_ENCODE_RE, input);
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in HTML
* documents using HTML entities. This function will not encode characters that
* are valid in HTML documents, such as `#`.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeNonAsciiHTML(input) {
return encodeHTMLTrieRe(XML_BITSET, xmlEncodeRegex, input);
}
/**
* Whether `code` (a UTF-16 code unit) must be encoded: any non-ASCII unit, or
* an ASCII unit flagged in `bitset`.
* @param bitset Bitset of ASCII characters to encode.
* @param code Code unit to test.
*/
function isEncodable(bitset, code) {
return code >= 0x80 || ((bitset[code >>> 5] >>> code) & 1) === 1;
}
/*
* The inline scan beats the regex jump only for short gaps (measured
* break-even is ~7 characters; below it the per-call regex overhead
* dominates, above it the regex skips clean spans faster in native code).
* So we scan at most `INLINE_SCAN_WINDOW` characters inline before deferring
* to the regex. Any gap long enough to exhaust the window switches to
* long-gap mode, which skips the inline scan entirely; we stay in that mode
* while total gaps are at least `LONG_GAP_THRESHOLD` characters long and
* drop back to the inline scan as soon as a shorter gap appears. Gaps are
* always measured from where they start, so every window-exhausting gap
* enters long-gap mode and no gap length pays both scans repeatedly.
*/
/** @internal Exported for tests; not re-exported from the package entry. */
export const INLINE_SCAN_WINDOW = 16;
const LONG_GAP_THRESHOLD = 8;
function encodeHTMLTrieRe(bitset, re, input) {
const { length } = input;
let out;
let last = 0; // Start of the next untouched slice.
let index = 0;
let wasLongGap = false; // The previous gap was long; skip the inline scan.
while (index < length) {
let char = input.charCodeAt(index);
/*
* Find the next encodable character (one matching `bitset`, or any
* non-ASCII unit). Gaps between entities in dense text are only a
* few characters, so scan a short window inline first; the regex —
* which matches exactly the same characters and skips clean spans
* in native code — runs for longer gaps. Gap lengths cluster, so
* after a window-exhausting gap the window is skipped until a gap
* shorter than LONG_GAP_THRESHOLD reappears. Once located, the
* character is captured in `char` and control falls through to the
* encode logic below instead of re-testing it on the next loop
* iteration.
*/
if (!isEncodable(bitset, char)) {
const gapStart = index;
let next = index + 1;
let wasFound = false;
if (!wasLongGap) {
const bound = Math.min(index + INLINE_SCAN_WINDOW, length);
while (next < bound &&
!isEncodable(bitset, (char = input.charCodeAt(next)))) {
next++;
}
if (next < bound) {
// `char` already holds the encodable unit at `next`.
index = next;
wasFound = true;
}
else if (next >= length) {
break;
}
}
if (!wasFound) {
/*
* Every match is a single code unit, so `test` pins it at
* `lastIndex - 1` without allocating a match object.
*/
re.lastIndex = next;
if (!re.test(input))
break;
index = re.lastIndex - 1;
/*
* Reaching the regex from the inline scan means the gap
* exhausted the window, so it is long by definition; once
* in long-gap mode, stay until a gap shorter than the
* threshold appears.
*/
wasLongGap =
!wasLongGap || index - gapStart >= LONG_GAP_THRESHOLD;
char = input.charCodeAt(index);
}
}
// Lazy-init: copy the prefix before the first character that needs encoding.
if (out == null)
out = input.substring(0, index);
else if (last !== index)
out += input.substring(last, index);
if (char < 0x80) {
const node = asciiEntities[char];
if (typeof node === "object" && node !== null) {
// Multi-code-point entity first (e.g. < + U+20D2 → &nvlt;).
if (index + 1 < length) {
const value = node.next.get(input.charCodeAt(index + 1));
if (value != null) {
out += value;
last = index += 2;
continue;
}
}
out += node.value ?? numericReference(char);
}
else {
out += node ?? numericReference(char);
}
}
else {
// Non-ASCII: full trie lookup with multi-char entity support.
let node = htmlTrie.get(char);
if (typeof node === "object") {
if (index + 1 < length) {
const value = node.next.get(input.charCodeAt(index + 1));
if (value != null) {
out += value;
last = index += 2;
continue;
}
}
node = node.value;
}
if (node == null) {
// No named entity exists; emit a hexadecimal numeric reference.
const cp = input.codePointAt(index);
out += numericReference(cp);
// Astral code points consume two UTF-16 code units.
if (cp !== char)
index++;
}
else {
out += node;
}
}
last = index += 1;
}
// If nothing needed encoding, return the original string (avoids allocation).
if (out == null)
return input;
if (last < length)
out += input.substr(last);
return out;
}
//# sourceMappingURL=encode.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"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 truncated
+60
View File
@@ -0,0 +1,60 @@
/**
* Read a code point at a given index.
* @param input String to read the code point from.
* @param index Current read position in the input string.
* @returns The code point at `index`, or `undefined` if `index` is out of range.
* @deprecated Use `String.prototype.codePointAt` directly instead; this export
* will be removed in the next major.
*/
export declare const getCodePoint: (input: string, index: number) => number;
/**
* Bitset for ASCII characters that need to be escaped in XML.
*/
export declare const XML_BITSET_VALUE = 1342177476;
/**
* Matches exactly the characters `encodeXML` escapes: the five XML special
* characters plus every non-ASCII code unit (lone surrogates included — no
* `u` flag). Kept in sync with `XML_BITSET_VALUE`.
*
* Shared with `encodeNonAsciiHTML` in `encode.ts`. Because the regex is
* stateful (`g` flag), every call site must set `lastIndex` before use.
*/
export declare const xmlEncodeRegex: RegExp;
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using XML entities.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export declare function encodeXML(input: string): string;
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using numeric hexadecimal reference (eg. `&#xfc;`).
*
* Have a look at `escapeUTF8` if you want a more concise output at the expense
* of reduced transportability.
* @param data String to escape.
*/
export declare const escape: typeof encodeXML;
/**
* Encodes all characters not valid in XML documents using XML entities.
*
* Note that the output will be character-set dependent.
* @param data String to escape.
*/
export declare function escapeUTF8(data: string): string;
/**
* Encodes all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export declare function escapeAttribute(data: string): string;
/**
* Encodes all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export declare function escapeText(data: string): string;
//# sourceMappingURL=escape.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAkBA;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAGvB,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,gBAAgB,aAAgB,CAAC;AAE9C;;;;;;;GAOG;AAEH,eAAO,MAAM,cAAc,EAAE,MAAgC,CAAC;AAe9D;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmD/C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,EAAE,OAAO,SAAqB,CAAC;AA2BlD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAID;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
+172
View File
@@ -0,0 +1,172 @@
/**
* Get the named reference for an XML special character or U+00A0.
* @param char Code unit matched by one of the escape regexes.
*/
function getEscape(char) {
return char === 34
? "&quot;"
: char === 38
? "&amp;"
: char === 39
? "&apos;"
: char === 60
? "&lt;"
: char === 62
? "&gt;"
: "&nbsp;";
}
/**
* Read a code point at a given index.
* @param input String to read the code point from.
* @param index Current read position in the input string.
* @returns The code point at `index`, or `undefined` if `index` is out of range.
* @deprecated Use `String.prototype.codePointAt` directly instead; this export
* will be removed in the next major.
*/
export const getCodePoint = (input, index) => input.codePointAt(index);
/**
* Bitset for ASCII characters that need to be escaped in XML.
*/
export const XML_BITSET_VALUE = 0x50_00_00_c4; // 32..63 -> 34 ("),38 (&),39 ('),60 (<),62 (>)
/**
* Matches exactly the characters `encodeXML` escapes: the five XML special
* characters plus every non-ASCII code unit (lone surrogates included — no
* `u` flag). Kept in sync with `XML_BITSET_VALUE`.
*
* Shared with `encodeNonAsciiHTML` in `encode.ts`. Because the regex is
* stateful (`g` flag), every call site must set `lastIndex` before use.
*/
// eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes -- the `\u{...}` form requires the `u` flag, which we deliberately omit so lone surrogates match by code unit
export const xmlEncodeRegex = /["&'<>\u0080-\uFFFF]/g;
/**
* Whether `code` (a UTF-16 code unit) is escaped by {@link encodeXML}: a
* non-ASCII unit, or one of the five XML specials flagged in
* `XML_BITSET_VALUE` (which is only meaningful for code units 32-63).
* @param code Code unit to test.
*/
function isXmlEscapable(code) {
return (code >= 0x80 ||
(code >= 32 && code < 64 && ((XML_BITSET_VALUE >>> code) & 1) === 1));
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using XML entities.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeXML(input) {
const { length } = input;
let out;
let last = 0;
let index = 0;
while (index < length) {
const char = input.charCodeAt(index);
/*
* Find the next character to escape: scan a short window inline
* (escapable characters cluster in markup-heavy input), then fall
* back to the regex, which skips clean spans in native code.
*/
if (!isXmlEscapable(char)) {
const bound = Math.min(index + 32, length);
let next = index + 1;
while (next < bound && !isXmlEscapable(input.charCodeAt(next))) {
next++;
}
if (next < bound) {
index = next;
continue;
}
if (next >= length)
break;
xmlEncodeRegex.lastIndex = next;
if (!xmlEncodeRegex.test(input))
break;
index = xmlEncodeRegex.lastIndex - 1;
continue;
}
if (out === undefined)
out = input.substring(0, index);
else if (last !== index)
out += input.substring(last, index);
if (char < 64) {
// Known replacement
out += getEscape(char);
last = index += 1;
continue;
}
// Non-ASCII: encode as numeric entity (handle surrogate pair)
const cp = input.codePointAt(index);
out += `&#x${cp.toString(16)};`;
if (cp !== char)
index++; // Skip trailing surrogate
last = index += 1;
}
if (out === undefined)
return input;
if (last < length)
out += input.substr(last);
return out;
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using numeric hexadecimal reference (eg. `&#xfc;`).
*
* Have a look at `escapeUTF8` if you want a more concise output at the expense
* of reduced transportability.
* @param data String to escape.
*/
export const escape = encodeXML;
/**
* Escape `data` using `re`, mapping each matched character to its entity.
* Every match is one UTF-16 code unit, so its index is `lastIndex - 1`.
* @param re Global regex matching exactly the characters to escape
* (`"`, `&`, `'`, `<`, `>`, `\u00A0` at most).
* @param data String to escape.
*/
function escapeWithRegex(re, data) {
re.lastIndex = 0;
if (!re.test(data))
return data;
let out = "";
let last = 0;
do {
const index = re.lastIndex - 1;
if (last !== index)
out += data.substring(last, index);
const char = data.charCodeAt(index);
out += getEscape(char);
last = index + 1;
} while (re.test(data));
return out + data.substring(last);
}
const xmlEscapeRegex = /["&'<>]/g;
/**
* Encodes all characters not valid in XML documents using XML entities.
*
* Note that the output will be character-set dependent.
* @param data String to escape.
*/
export function escapeUTF8(data) {
return escapeWithRegex(xmlEscapeRegex, data);
}
const attributeEscapeRegex = /["&\u{A0}]/gu;
/**
* Encodes all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export function escapeAttribute(data) {
return escapeWithRegex(attributeEscapeRegex, data);
}
const textEscapeRegex = /[&<>\u{A0}]/gu;
/**
* Encodes all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export function escapeText(data) {
return escapeWithRegex(textEscapeRegex, data);
}
//# sourceMappingURL=escape.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
/** Packed HTML decode trie data. */
export declare const htmlDecodeTree: Uint16Array;
//# sourceMappingURL=decode-data-html.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"decode-data-html.d.ts","sourceRoot":"","sources":["../../src/generated/decode-data-html.ts"],"names":[],"mappings":"AAGA,oCAAoC;AACpC,eAAO,MAAM,cAAc,EAAE,WAO5B,CAAC"}
+5
View File
@@ -0,0 +1,5 @@
// Generated using scripts/write-decode-map.ts
import { decodeTrieDict } from "../internal/decode-shared.js";
/** Packed HTML decode trie data. */
export const htmlDecodeTree = /* #__PURE__ */ decodeTrieDict("!}.&u%}'&}*'~!6*)%&,~!J~!J~%L~y<~!R,~~%Lu~~#GD~~#|)1#%}^%}2%+#.##%##%}&%##%'#%##&%#%#'%#&#%#&#'#%%#&#%##%#)%''%&%#%#'%#%%#%%}%%%#%#&(23#%%#&-%0%('1#(##%#'##+%'*.:1}#%#6-+(%'%%#%%%}#L'2351&('%}&/N'(0(/*-%(%%}#'+&T%7.2}#&%&#%#36/5##%&%%#&#%%#))2%%##%&&'0~!#*+&'%1~!%).'3q?&%'1~!.##%6(~!+%%%(Gw'rT~!E#<nA%#jZ~!H%(~!42##~!*31&~!G%U~#)5~#`3~!J~!Z~%]~%Y~%C~!q~!u~#kz~%#~!6'~!D~!U~!?~#T~!c%~!G#'~%7|~!G~!J~!G&~#pb~(Df}#%}*&}#%##%##%##&#-}&'#'&%#.++}%mI,#,@&(}*%}*'%&##&#%##%}&0}#.},U},%}+%}&%}#%##&}B%(}(%}+%)})%##%#&}&%##%&}<%}>%#%&}*%}(%}9%}/%})%}*%}*%}?&}&%}3%}&*#%})%#%#)}#&#-#+*%E%%'%'#%}#*V##&##I}#&&##%&%#&&Qf%%))w/0+&%#(#.%-''''++++7}>%4'',##1,#%#&%##&#'##&#*#9)%&%}#*}%,#+P(%A&%#'&##wSD',9E00#y#@}(+}&%&>~!#~!X}#*}(&&}(&}(,%}%&#+&}#&}I%#%}%)#(},'%#*}4%%#%}(''}#/##(##),%-##%%)#&}(.}&%#&}%%}*&#%},&&}&%}#%*'#%})%}D&}&%}-&}6&#&}-,%}#%})-(~+`~,=?~I9'9%~!,#%})%})%}@%}?%}(~!?~#<~#pP~#BG~#=1#%K+~#?#~%;)~#A~#mF1~#A'~'X%'~#lR~#N~'N~#r~#m#-~#i'?%#'%~#B%##%,%#~#_%#0%~#]732~,w~2+#:&#%&'0%&>%}#>##F+)#%&&#(+_}4&}-%}(&}@&}O7Fdf0@+/v4}&WU##&/0#&'('B#%}.%}'+#%}#%%&#&%#%##+#&#)#6#'#.},%}c%},%#%##%&#&%#&~#>'*-.%##%##%}#%%}%'~#)D1}#%*&~#_%%'(~#S2%'.}#~#=##*'*-%}&'%'##&&~'E%.#&~#M4}%%##&'%#~#O1##%&#'+~#<B%##%%'%+~#;#@%}#&%#&&%#(~#H1}'%'##&&~#?A}&'~#D#%32}'&&&&~#[}'(#%}'~#;C})&}%%#%~#=&%,3}%'(#%%~#^'#&&)#%'~#Y%-~#d-%'~#^%%&#&&&}#~#b~2t*&'~&(~&@~0%~e~3}%*''0})&}+~!9##-}#%-hD*)1fC#%/&/fB#40~!+#)*4~!+~!K'&:~!/*7~!.#~!H~!L':~%x&~!H#~!*~%1~!I#~!+A~#p'~!F~~#-#~,,(~.Z~!V~%;'B'mq-W~!N~%I%#&&#&}#%},%%}'%}+X#%}#&}(%}'%}<%}#%}%%'}'%}:~![)9@~%>~#UA%-%##&~!C%~!-.9:~!1~!-^2/:a~!y,D*J#-5)/4~%23,~#G~!L1~!0X3`~!2+~!!0-~&E~!W~!o,>Y&]~%cZx_&~#O*9#A#'#+I'%#)~!0B*-5A+-((F&*M#)(-7-5+'-3a5Vi~!Y~!?+[)%3),ERHm~!+:D,VG.+)?fB%%*(%)'(#&80%1'8`K8?`+'Z#&O&'H5#*9)A%%5&3))0%39+.*7#()&&*=4@**L)<'_&*+..;(#*+)./&0#3)%')-8(4ixD(&.}%,('aI:,)%,k2231T)I'#/-W7,/'Q#.'Y24+h')37</31&83##&0#),H(?'&?/1##%#&&#%''-%&&&#(&''&#.-'%#%%(,')*'&#&#'##%(%(#%('#&##%%%%('%#%#%%#%#&%##h>w+v<ayvyvcg.uuhKr}g/v|g>u9i[~>g5uI~=RvdwEg;v/g;uk!!TTSx]@RT!U!#!@VBRUU!'UTe-d0c`e&gSdicedFcrdTaqb.kYcAohdYd@a3e+d}dMdtd.aJ#bqcK`dle/e.e'dwdPdodddjbEb}ogd^ofdpduc6j?l%d{drdqc)d7bacOdQ%T#Y)X.sR[yH>6Vyv3[xwLu>vo'!*.[yBacahoj>6Rew3[xqdZa#!a&#^(X-[yG>6Vyu3[xvg3sEr|g.u/Ri9db0T#^(Xa)!-[y;>6Vylg4wKs{JwNZt3@3r=c4Z([xlg;wKt!cpq's@v7A'*a(a+!-a#[y<3Dt?3Dt'>6Vym3[xmg9rxsNJwLZt4~?r?db1T#`-!(Xa,!0[yS>6Vz%NuQs.g4wKtnJwNZtS@3r>c4Z([y%g;wKtrdga8!a(!#&T*Y-Xa#!a0<or[yc3Dtq>6Vz43[y3JwNZtf@3s!Ju}!%Dti:pm3c_%X#tjB5pkd6q!r]u?voC'*-a.a2!0a&a+[yI3DtI3Ds~3DtH>6Vyw3[xx;:s#~<5pKJwNZtE@3r~d`a)!a2T#a.(!+U.X1[yT3Dt`3Dtv>6Vz&3[y&g9rxwzcxstPu.<rAJwLZtT~?r@dZa%!a.&^*Za(/Reu[ya>6Vz23[y1g3sEr}wkg{NuQRg{ci(U#5@b`~,cg#U(2WnH5wugcRh7dX#T(Y,a'Ta!!a,[yZ<]mj>6Vz,3[y+Pv#5ReZKu+=,%!H}7ABwkaS?Rh:BcW(X#<]mrj:ubv/ARekdg%!(!a.*Ta(Y.X1!#sP>Rl*Dt6[y>>6Vyo3Wf*jOvuumvuRgRJuq*!:9<B@bX~3jVv&v@s@5Re[d/rQt{uAvo&a&a*)a2!,0Wf!3Dt0=Bs'>6Re}3[xy~<5s%JwJZt1~Gs)c;&!#2sJkNuXvzq7rxu,Re8dka4!a8(aEZ+a@Y.X1Xa)[yd=Bs(3DtP>6Vz53[y4cX#X&Re:avRe9~<5s&JwJZtQ~Gs*i^rzvdRg+Jv{%!2sbB@bX}kdga,!Za?&^*T1/!a'Dt+[y6>6Vyf3Wf%g/u;s4hGu6?Rh-JvZ,!c%#&RoX54Rivj7uyvf8RgTKvZB%*!2sGh<vu5Rgq<=C::9bb~#dZ#T&Ta6Y.X*Dt>[y93Wf)coZ(T,6VyifluvRgC@95@B@bX~/hFu34cC#T,k/unq8w8Q5RkUklwQuzunq8w8Q5Rk8d/rJu?v8w9)-&!a0a;a&aIWejg3sEr/h1s<DtDJvyZqY5aws3Jvy!&Wei~Hr1:au5@Bag>23E~5c:Z&bX};kKv?w&unuVu5Rjc;>bs)#~@:Rh.=ay<a]C;b`}Vd6s/t{uAvoaxa()!a,a7%-a#a2Dt,[yF2Wo[>6Vyt3[xuNuPRi&NuPwpi#RoWh?vf8Ri%Jv]!%Ri:KvxD!.'2WeAjZu`q9rxu,Re7woeAg-unLq(qA_/*2Wg_g3u5q^9:4E}/jTrxrzv=Wkkd~0UX#^^Xa-a1a5T&a=U1a'*aEa]!a*aPaA-adok[y54Rn>;:p3~Dp5g9rpsFNvZqjg3uJp4~<5p0Pw;5qlJwNZt*@3p1Pw:5p/Ou!5p2JvG'!6Vye=<qnJvh_[xhg3v,Rh3kOwOw-sDuev/Re^dha[a%!%!a+#Ta7)-5TaCaO!aka!a)sf[yb2>Rl!9ARiq5E}Qg=ucRkBE|oJrJ_@Wk~@Wk{JrJ_@Wk|@WkyJrJ_@Wk}@WkzJvO_[y2g-vMRmiKuYC!)&>Ri;>Ri<@3RkNc](X#@9Rk=g5vuRmhKvDB!+'=]meg3u4Rmgd)#Y'Vz3CARmfd`a+!%T'!+#Ta1Ta6TaM-sTDt9[yA9sYd'%Y#s[[xpj:ueunaXRgEjRq,v-vuqdd2'`#6Rev<32@5>:2<E}5xIo9a*X#Y(;5RePJvD_g>vyRgNj8w)v8<wggs:RgXiZt|vjx,hSq3ah!-(~@:Ro/Ou!5RhWj^v(pyw8unRhUdx-UY#^Ua.a3a70!)%UX1TaDa)'omRiRRhE[y:3Dsz=Br,>6Vyj3[xkg6ruwjcqsrPw;5r*Ku]D'Zt-@3r(~?r.i[vwv]dU1a--U#`a4(g/vsRhPOu!5RhLj:rmu9Wo!~@:wdh@g/vsRiTjXuvvNr}:RhBj^v(pyw8unRn]dz1UYa'a+^Y(!aETZalaRY.Ta?a4[yDJw1!#qLsW>6Vyrfzq-pLflpwRe|Js>%!Dt@3Dt&Jvy_[xs~HrnjMuwpsw'RecKu+D#'!t<~Grl~?rjg5u-x,gwp{ah!-(~@:Rg~Ou!5Rh'jXuvvNr}:Rh#cW#X/c;&!#2sLi[v7u7RgpJv)(!iLrxu,Re6j7v@s@5Se[e7d`aW!Za(a`T.a#!a3!&aDa-!9)Dt_=6s+3[x~~DR|h~DS6avhGun5RkZj3w)v-]mkKunB!&*]kb97R|i<ARk<c:Z(6Vy}Juh'!wziMRoS:F|vkLuauJv5vtvQRh1d='T+Y#VyO~DR|jcF#T'7R|g97R|kJv3'!ay<Rj,Jvh&!:ReXcsa6*a+#a#_aIRf9aLRf?c,Z&Rf5Rf7c.Z&Rf;Rf>cQ#%T'p-Rf8Rf=ct#%'(*!,p,Rf4p+Rf6Rf:Rf<d~'Ua%U*^UYa(!a,-!#a4YaTalaEX0a8a<Weo3Dt/3Dsx=Br93Wen~Dr;~<5p<JwNZt2@3p=Pw:5p;Ou!5r3c7&!#:p>3Ds}KvGB)_6Vyk2sM=<r7x'eovA(!hFu1ARf}cV#X&@r5j6rvwQa^Rf3c=Za'wkghJv__g;unRggA53B9=b^}%j6uduo5Jq;!(hIv%2Re`Ou4ARe_e%a#^^^Xa&!a*a2!&a6YaP!*ad!#a:aE/5Rn?[y@>6Vyp;:pE~DrY~<5pBJwNZt8@3pCh=rt3rWPw:5pAJup_[xoNuPpF9c!#'45pD5ARn)d8#X'X*3@rU72s]h>v<<sSjJpqvewOJq/(!hNw'5ReBk0s2u3w/w'5ReE5@Jq.!a+JQ!&WeU23d(#Y&RjG5]jBk!u7w&u0udARLine truncated
//# sourceMappingURL=decode-data-html.js.map
@@ -0,0 +1 @@
{"version":3,"file":"decode-data-html.js","sourceRoot":"","sources":["../../src/generated/decode-data-html.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,oCAAoC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAgB,eAAe,CAAC,cAAc,CACrE,kmkBAAkmkB,EAClmkB,MAAM,EACN,IAAI,EACJ,EAAE,EACF,EAAE,EACF,EAAE,CACL,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
/** Packed XML decode trie data. */
export declare const xmlDecodeTree: Uint16Array;
//# sourceMappingURL=decode-data-xml.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"decode-data-xml.d.ts","sourceRoot":"","sources":["../../src/generated/decode-data-xml.ts"],"names":[],"mappings":"AAEA,mCAAmC;AACnC,eAAO,MAAM,aAAa,EAAE,WAG1B,CAAC"}
+7
View File
@@ -0,0 +1,7 @@
// Generated using scripts/write-decode-map.ts
/** Packed XML decode trie data. */
export const xmlDecodeTree = /* #__PURE__ */ new Uint16Array([
512, 26_465, 29_036, 7, 0, 2, 4, 116, 24_638, 116, 24_636, 8693, 29_807,
24_610, 621, 1, 0, 0, 3, 112, 24_614, 111, 115, 24_615,
]);
//# sourceMappingURL=decode-data-xml.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode-data-xml.js","sourceRoot":"","sources":["../../src/generated/decode-data-xml.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,mCAAmC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAgB,eAAe,CAAC,IAAI,WAAW,CAAC;IACtE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IACvE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM;CACzD,CAAC,CAAC"}
+3
View File
@@ -0,0 +1,3 @@
declare const _default: "9Tab;NewLine;22excl;quot;num;dollar;percnt;amp;apos;lpar;rpar;ast;plus;comma;1period;sol;10colon;semi;lt;{8402nvlt;}equals;{8421bne;}gt;{8402nvgt;}quest;commat;26lbrack;bsol;rbrack;Hat;lowbar;DiacriticalGrave;5{106fjlig;}20lbrace;verbar;rbrace;34nbsp;iexcl;cent;pound;curren;yen;brvbar;sect;die;copy;ordf;laquo;not;shy;circledR;macr;deg;PlusMinus;sup2;sup3;acute;micro;para;centerdot;cedil;sup1;ordm;raquo;frac14;frac12;frac34;iquest;Agrave;Aacute;Acirc;Atilde;Auml;angst;AElig;Ccedil;Egrave;Eacute;Ecirc;Euml;Igrave;Iacute;Icirc;Iuml;ETH;Ntilde;Ograve;Oacute;Ocirc;Otilde;Ouml;times;Oslash;Ugrave;Uacute;Ucirc;Uuml;Yacute;THORN;szlig;agrave;aacute;acirc;atilde;auml;aring;aelig;ccedil;egrave;eacute;ecirc;euml;igrave;iacute;icirc;iuml;eth;ntilde;ograve;oacute;ocirc;otilde;ouml;div;oslash;ugrave;uacute;ucirc;uuml;yacute;thorn;yuml;Amacr;amacr;Abreve;abreve;Aogon;aogon;Cacute;cacute;Ccirc;ccirc;Cdot;cdot;Ccaron;ccaron;Dcaron;dcaron;Dstrok;dstrok;Emacr;emacr;2Edot;edot;Eogon;eogon;Ecaron;ecaron;Gcirc;gcirc;Gbreve;gbreve;Gdot;gdot;Gcedil;1Hcirc;hcirc;Hstrok;hstrok;Itilde;itilde;Imacr;imacr;2Iogon;iogon;Idot;imath;IJlig;ijlig;Jcirc;jcirc;Kcedil;kcedil;kgreen;Lacute;lacute;Lcedil;lcedil;Lcaron;lcaron;Lmidot;lmidot;Lstrok;lstrok;Nacute;nacute;Ncedil;ncedil;Ncaron;ncaron;napos;ENG;eng;Omacr;omacr;2Odblac;odblac;OElig;oelig;Racute;racute;Rcedil;rcedil;Rcaron;rcaron;Sacute;sacute;Scirc;scirc;Scedil;scedil;Scaron;scaron;Tcedil;tcedil;Tcaron;tcaron;Tstrok;tstrok;Utilde;utilde;Umacr;umacr;Ubreve;ubreve;Uring;uring;Udblac;udblac;Uogon;uogon;Wcirc;wcirc;Ycirc;ycirc;Yuml;Zacute;zacute;Zdot;zdot;Zcaron;zcaron;19fnof;34imped;63gacute;65jmath;142circ;caron;16breve;DiacriticalDot;ring;ogon;DiacriticalTilde;dblac;51DownBreve;127Alpha;Beta;Gamma;Delta;Epsilon;Zeta;Eta;Theta;Iota;Kappa;Lambda;Mu;Nu;Xi;Omicron;Pi;Rho;1Sigma;Tau;Upsilon;Phi;Chi;Psi;ohm;7alpha;beta;gamma;delta;epsi;zeta;eta;theta;iota;kappa;lambda;mu;nu;xi;omicron;pi;rho;sigmaf;sigma;tau;upsi;phi;chi;psi;omega;7thetasym;Upsi;2phiv;piv;5Gammad;digamma;18kappav;rhov;3epsiv;backepsilon;10IOcy;DJcy;GJcy;Jukcy;DScy;Iukcy;YIcy;Jsercy;LJcy;NJcy;TSHcy;KJcy;1Ubrcy;DZcy;Acy;Bcy;Vcy;Gcy;Dcy;IEcy;ZHcy;Zcy;Icy;Jcy;Kcy;Lcy;Mcy;Ncy;Ocy;Pcy;Rcy;Scy;Tcy;Ucy;Fcy;KHcy;TScy;CHcy;SHcy;SHCHcy;HARDcy;Ycy;SOFTcy;Ecy;YUcy;YAcy;acy;bcy;vcy;gcy;dcy;iecy;zhcy;zcy;icy;jcy;kcy;lcy;mcy;ncy;ocy;pcy;rcy;scy;tcy;ucy;fcy;khcy;tscy;chcy;shcy;shchcy;hardcy;ycy;softcy;ecy;yucy;yacy;1iocy;djcy;gjcy;jukcy;dscy;iukcy;yicy;jsercy;ljcy;njcy;tshcy;kjcy;1ubrcy;dzcy;7074ensp;emsp;emsp13;emsp14;1numsp;puncsp;ThinSpace;hairsp;NegativeMediumSpace;zwnj;zwj;lrm;rlm;dash;2ndash;mdash;horbar;Verbar;1lsquo;CloseCurlyQuote;lsquor;1ldquo;CloseCurlyDoubleQuote;bdquo;1dagger;Dagger;bull;2nldr;hellip;9permil;pertenk;prime;Prime;tprime;backprime;3lsaquo;rsaquo;3oline;2caret;1hybull;frasl;10bsemi;7qprime;7MediumSpace;{8202ThickSpace;}NoBreak;af;InvisibleTimes;ic;72euro;46tdot;DotDot;37complexes;2incare;4gscr;hamilt;Hfr;Hopf;planckh;hbar;imagline;Ifr;lagran;ell;1naturals;numero;copysr;weierp;Popf;Qopf;realine;real;reals;rx;3trade;1integers;2mho;zeetrf;iiota;2bernou;Cayleys;1escr;Escr;Fouriertrf;1Mellintrf;order;alefsym;beth;gimel;daleth;12CapitalDifferentialD;dd;ee;ii;10frac13;frac23;frac15;frac25;frac35;frac45;frac16;frac56;frac18;frac38;frac58;frac78;49larr;ShortUpArrow;rarr;darr;harr;updownarrow;nwarr;nearr;LowerRightArrow;LowerLeftArrow;nlarr;nrarr;1rarrw;{824nrarrw;}Larr;Uarr;Rarr;Darr;larrtl;rarrtl;LeftTeeArrow;mapstoup;map;DownTeeArrow;1hookleftarrow;hookrightarrow;larrlp;looparrowright;harrw;nharr;1lsh;rsh;ldsh;rdsh;1crarr;cularr;curarr;2circlearrowleft;circlearrowright;leftharpoonup;DownLeftVector;RightUpVector;LeftUpVector;rharu;DownRightVector;dharr;dharl;RightArrowLeftArrow;udarr;LeftArrowRightArrow;leftleftarrows;upuparrows;rightrightarrows;ddarr;leftrightharpoons;Equilibrium;nlArr;nhArr;nrArr;DoubleLeftArrow;DoubleUpArrow;DoubleRightArrow;dArr;DoubleLeftRightArrow;DoubleUpDownArrow;nwArr;neArr;seArr;swArr;lAarr;rAarr;1zigrarr;6larrb;rarrb;15DownArrowUpArrow;7loarr;roarr;hoarr;forall;comp;part;{824npart;}exist;nexist;empty;1Del;Element;NotElement;1ni;notni;2prod;coprod;sum;minus;MinusPlus;dotplus;1Backslash;lowast;compfn;1radic;2prop;infin;angrt;ang;{8402nang;}angmsd;angsph;mid;nmid;DoubleVerticalBar;NotDoubleVerticalBar;and;or;cap;{65024caps;}cup;{65024cups;}int;Int;iiint;conint;Conint;Cconint;cwint;ClockwiseContourIntegral;awconint;there4;becaus;ratio;Colon;dotminus;1mDDot;homtht;sim;{8402nvsim;}backsim;{817race;}ac;{819acE;}acd;VerticalTilde;NotTilde;eqsim;{824nesim;}sime;NotTildeEqual;cong;simne;ncong;ap;nap;ape;apid;{824napid;}backcong;asympeq;{8402nvap;}bump;{824nbump;}bumpe;{824nbumpe;}doteq;{824nedot;}doteqdot;efDot;erDot;Assign;ecolon;ecir;circeq;1wedgeq;veeeq;1triangleq;2equest;ne;Congruent;{8421bnequiv;}nequiv;1le;{8402nvle;}ge;{8402nvge;}lE;{824nlE;}gE;{824ngE;}lnE;{65024lvertneqq;}gnE;{65024gvertneqq;}ll;{824nLtv;7577nLt;}gg;{824nGtv;7577nGt;}between;NotCupCap;nless;ngt;nle;nge;lesssiLine truncated
export default _default;
//# sourceMappingURL=encode-html.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"encode-html.d.ts","sourceRoot":"","sources":["../../src/generated/encode-html.ts"],"names":[],"mappings":"wBAEe,89VAA89V;AAA7+V,wBAA8+V"}
+3
View File
@@ -0,0 +1,3 @@
// Generated using scripts/write-encode-map.ts
export default "9Tab;NewLine;22excl;quot;num;dollar;percnt;amp;apos;lpar;rpar;ast;plus;comma;1period;sol;10colon;semi;lt;{8402nvlt;}equals;{8421bne;}gt;{8402nvgt;}quest;commat;26lbrack;bsol;rbrack;Hat;lowbar;DiacriticalGrave;5{106fjlig;}20lbrace;verbar;rbrace;34nbsp;iexcl;cent;pound;curren;yen;brvbar;sect;die;copy;ordf;laquo;not;shy;circledR;macr;deg;PlusMinus;sup2;sup3;acute;micro;para;centerdot;cedil;sup1;ordm;raquo;frac14;frac12;frac34;iquest;Agrave;Aacute;Acirc;Atilde;Auml;angst;AElig;Ccedil;Egrave;Eacute;Ecirc;Euml;Igrave;Iacute;Icirc;Iuml;ETH;Ntilde;Ograve;Oacute;Ocirc;Otilde;Ouml;times;Oslash;Ugrave;Uacute;Ucirc;Uuml;Yacute;THORN;szlig;agrave;aacute;acirc;atilde;auml;aring;aelig;ccedil;egrave;eacute;ecirc;euml;igrave;iacute;icirc;iuml;eth;ntilde;ograve;oacute;ocirc;otilde;ouml;div;oslash;ugrave;uacute;ucirc;uuml;yacute;thorn;yuml;Amacr;amacr;Abreve;abreve;Aogon;aogon;Cacute;cacute;Ccirc;ccirc;Cdot;cdot;Ccaron;ccaron;Dcaron;dcaron;Dstrok;dstrok;Emacr;emacr;2Edot;edot;Eogon;eogon;Ecaron;ecaron;Gcirc;gcirc;Gbreve;gbreve;Gdot;gdot;Gcedil;1Hcirc;hcirc;Hstrok;hstrok;Itilde;itilde;Imacr;imacr;2Iogon;iogon;Idot;imath;IJlig;ijlig;Jcirc;jcirc;Kcedil;kcedil;kgreen;Lacute;lacute;Lcedil;lcedil;Lcaron;lcaron;Lmidot;lmidot;Lstrok;lstrok;Nacute;nacute;Ncedil;ncedil;Ncaron;ncaron;napos;ENG;eng;Omacr;omacr;2Odblac;odblac;OElig;oelig;Racute;racute;Rcedil;rcedil;Rcaron;rcaron;Sacute;sacute;Scirc;scirc;Scedil;scedil;Scaron;scaron;Tcedil;tcedil;Tcaron;tcaron;Tstrok;tstrok;Utilde;utilde;Umacr;umacr;Ubreve;ubreve;Uring;uring;Udblac;udblac;Uogon;uogon;Wcirc;wcirc;Ycirc;ycirc;Yuml;Zacute;zacute;Zdot;zdot;Zcaron;zcaron;19fnof;34imped;63gacute;65jmath;142circ;caron;16breve;DiacriticalDot;ring;ogon;DiacriticalTilde;dblac;51DownBreve;127Alpha;Beta;Gamma;Delta;Epsilon;Zeta;Eta;Theta;Iota;Kappa;Lambda;Mu;Nu;Xi;Omicron;Pi;Rho;1Sigma;Tau;Upsilon;Phi;Chi;Psi;ohm;7alpha;beta;gamma;delta;epsi;zeta;eta;theta;iota;kappa;lambda;mu;nu;xi;omicron;pi;rho;sigmaf;sigma;tau;upsi;phi;chi;psi;omega;7thetasym;Upsi;2phiv;piv;5Gammad;digamma;18kappav;rhov;3epsiv;backepsilon;10IOcy;DJcy;GJcy;Jukcy;DScy;Iukcy;YIcy;Jsercy;LJcy;NJcy;TSHcy;KJcy;1Ubrcy;DZcy;Acy;Bcy;Vcy;Gcy;Dcy;IEcy;ZHcy;Zcy;Icy;Jcy;Kcy;Lcy;Mcy;Ncy;Ocy;Pcy;Rcy;Scy;Tcy;Ucy;Fcy;KHcy;TScy;CHcy;SHcy;SHCHcy;HARDcy;Ycy;SOFTcy;Ecy;YUcy;YAcy;acy;bcy;vcy;gcy;dcy;iecy;zhcy;zcy;icy;jcy;kcy;lcy;mcy;ncy;ocy;pcy;rcy;scy;tcy;ucy;fcy;khcy;tscy;chcy;shcy;shchcy;hardcy;ycy;softcy;ecy;yucy;yacy;1iocy;djcy;gjcy;jukcy;dscy;iukcy;yicy;jsercy;ljcy;njcy;tshcy;kjcy;1ubrcy;dzcy;7074ensp;emsp;emsp13;emsp14;1numsp;puncsp;ThinSpace;hairsp;NegativeMediumSpace;zwnj;zwj;lrm;rlm;dash;2ndash;mdash;horbar;Verbar;1lsquo;CloseCurlyQuote;lsquor;1ldquo;CloseCurlyDoubleQuote;bdquo;1dagger;Dagger;bull;2nldr;hellip;9permil;pertenk;prime;Prime;tprime;backprime;3lsaquo;rsaquo;3oline;2caret;1hybull;frasl;10bsemi;7qprime;7MediumSpace;{8202ThickSpace;}NoBreak;af;InvisibleTimes;ic;72euro;46tdot;DotDot;37complexes;2incare;4gscr;hamilt;Hfr;Hopf;planckh;hbar;imagline;Ifr;lagran;ell;1naturals;numero;copysr;weierp;Popf;Qopf;realine;real;reals;rx;3trade;1integers;2mho;zeetrf;iiota;2bernou;Cayleys;1escr;Escr;Fouriertrf;1Mellintrf;order;alefsym;beth;gimel;daleth;12CapitalDifferentialD;dd;ee;ii;10frac13;frac23;frac15;frac25;frac35;frac45;frac16;frac56;frac18;frac38;frac58;frac78;49larr;ShortUpArrow;rarr;darr;harr;updownarrow;nwarr;nearr;LowerRightArrow;LowerLeftArrow;nlarr;nrarr;1rarrw;{824nrarrw;}Larr;Uarr;Rarr;Darr;larrtl;rarrtl;LeftTeeArrow;mapstoup;map;DownTeeArrow;1hookleftarrow;hookrightarrow;larrlp;looparrowright;harrw;nharr;1lsh;rsh;ldsh;rdsh;1crarr;cularr;curarr;2circlearrowleft;circlearrowright;leftharpoonup;DownLeftVector;RightUpVector;LeftUpVector;rharu;DownRightVector;dharr;dharl;RightArrowLeftArrow;udarr;LeftArrowRightArrow;leftleftarrows;upuparrows;rightrightarrows;ddarr;leftrightharpoons;Equilibrium;nlArr;nhArr;nrArr;DoubleLeftArrow;DoubleUpArrow;DoubleRightArrow;dArr;DoubleLeftRightArrow;DoubleUpDownArrow;nwArr;neArr;seArr;swArr;lAarr;rAarr;1zigrarr;6larrb;rarrb;15DownArrowUpArrow;7loarr;roarr;hoarr;forall;comp;part;{824npart;}exist;nexist;empty;1Del;Element;NotElement;1ni;notni;2prod;coprod;sum;minus;MinusPlus;dotplus;1Backslash;lowast;compfn;1radic;2prop;infin;angrt;ang;{8402nang;}angmsd;angsph;mid;nmid;DoubleVerticalBar;NotDoubleVerticalBar;and;or;cap;{65024caps;}cup;{65024cups;}int;Int;iiint;conint;Conint;Cconint;cwint;ClockwiseContourIntegral;awconint;there4;becaus;ratio;Colon;dotminus;1mDDot;homtht;sim;{8402nvsim;}backsim;{817race;}ac;{819acE;}acd;VerticalTilde;NotTilde;eqsim;{824nesim;}sime;NotTildeEqual;cong;simne;ncong;ap;nap;ape;apid;{824napid;}backcong;asympeq;{8402nvap;}bump;{824nbump;}bumpe;{824nbumpe;}doteq;{824nedot;}doteqdot;efDot;erDot;Assign;ecolon;ecir;circeq;1wedgeq;veeeq;1triangleq;2equest;ne;Congruent;{8421bnequiv;}nequiv;1le;{8402nvle;}ge;{8402nvge;}lE;{824nlE;}gE;{824ngE;}lnE;{65024lvertneqq;}gnE;{65024gvertneqq;}ll;{824nLtv;7577nLt;}gg;{824nGtv;7577nGt;}between;NotCupCap;nless;ngt;nle;nge;lesssim;GreaterLine truncated
//# sourceMappingURL=encode-html.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"encode-html.js","sourceRoot":"","sources":["../../src/generated/encode-html.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,eAAe,89VAA89V,CAAC"}
+89
View File
@@ -0,0 +1,89 @@
import { type DecodingMode } from "./decode.js";
/** The level of entities to support. */
export declare enum EntityLevel {
/** Support only XML entities. */
XML = 0,
/** Support HTML entities, which are a superset of XML entities. */
HTML = 1
}
/**
* Encoding strategy used by `encode`.
*/
export declare enum EncodingMode {
/**
* The output is UTF-8 encoded. Only characters that need escaping within
* XML will be escaped.
*/
UTF8 = 0,
/**
* The output consists only of ASCII characters. Characters that need
* escaping within HTML, and characters that aren't ASCII characters will
* be escaped.
*/
ASCII = 1,
/**
* Encode all characters that have an equivalent entity, as well as all
* characters that are not ASCII characters.
*/
Extensive = 2,
/**
* Encode all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
Attribute = 3,
/**
* Encode all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
Text = 4
}
/**
* Options for `decode`.
*/
export interface DecodingOptions {
/**
* The level of entities to support.
* @default {@link EntityLevel.XML}
*/
level?: EntityLevel;
/**
* Decoding mode. If `Legacy`, will support legacy entities not terminated
* with a semicolon (`;`).
*
* Always `Strict` for XML. For HTML, set this to
* {@link DecodingMode.Attribute} if you are parsing an attribute value.
* @default {@link DecodingMode.Legacy}
*/
mode?: DecodingMode | undefined;
}
/**
* Decodes a string with entities.
* @param input String to decode.
* @param options Decoding options.
*/
export declare function decode(input: string, options?: DecodingOptions | EntityLevel): string;
/**
* Options for `encode`.
*/
export interface EncodingOptions {
/**
* The level of entities to support.
* @default {@link EntityLevel.XML}
*/
level?: EntityLevel;
/**
* Output format.
* @default {@link EncodingMode.Extensive}
*/
mode?: EncodingMode;
}
/**
* Encodes a string with entities.
* @param input String to encode.
* @param options Encoding options.
*/
export declare function encode(input: string, options?: EncodingOptions | EntityLevel): string;
export { DecodingMode, decodeHTML, decodeHTMLAttribute, decodeHTMLStrict, decodeXML, decodeXML as decodeXMLStrict, EntityDecoder, } from "./decode.js";
export { encodeHTML, encodeNonAsciiHTML, } from "./encode.js";
export { encodeXML, escape, escapeAttribute, escapeText, escapeUTF8, } from "./escape.js";
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAyB,MAAM,aAAa,CAAC;AASvE,wCAAwC;AACxC,oBAAY,WAAW;IACnB,iCAAiC;IACjC,GAAG,IAAI;IACP,mEAAmE;IACnE,IAAI,IAAI;CACX;AAED;;GAEG;AACH,oBAAY,YAAY;IACpB;;;OAGG;IACH,IAAI,IAAA;IACJ;;;;OAIG;IACH,KAAK,IAAA;IACL;;;OAGG;IACH,SAAS,IAAA;IACT;;;OAGG;IACH,SAAS,IAAA;IACT;;;OAGG;IACH,IAAI,IAAA;CACP;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAe,GAAG,WAA6B,GACzD,MAAM,CASR;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAe,GAAG,WAA6B,GACzD,MAAM,CAyBR;AAED,OAAO,EACH,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,SAAS,IAAI,eAAe,EAC5B,aAAa,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACH,UAAU,EACV,kBAAkB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACH,SAAS,EACT,MAAM,EACN,eAAe,EACf,UAAU,EACV,UAAU,GACb,MAAM,aAAa,CAAC"}
+87
View File
@@ -0,0 +1,87 @@
import { decodeHTML, decodeXML } from "./decode.js";
import { encodeHTML, encodeNonAsciiHTML } from "./encode.js";
import { encodeXML, escapeAttribute, escapeText, escapeUTF8, } from "./escape.js";
/** The level of entities to support. */
export var EntityLevel;
(function (EntityLevel) {
/** Support only XML entities. */
EntityLevel[EntityLevel["XML"] = 0] = "XML";
/** Support HTML entities, which are a superset of XML entities. */
EntityLevel[EntityLevel["HTML"] = 1] = "HTML";
})(EntityLevel || (EntityLevel = {}));
/**
* Encoding strategy used by `encode`.
*/
export var EncodingMode;
(function (EncodingMode) {
/**
* The output is UTF-8 encoded. Only characters that need escaping within
* XML will be escaped.
*/
EncodingMode[EncodingMode["UTF8"] = 0] = "UTF8";
/**
* The output consists only of ASCII characters. Characters that need
* escaping within HTML, and characters that aren't ASCII characters will
* be escaped.
*/
EncodingMode[EncodingMode["ASCII"] = 1] = "ASCII";
/**
* Encode all characters that have an equivalent entity, as well as all
* characters that are not ASCII characters.
*/
EncodingMode[EncodingMode["Extensive"] = 2] = "Extensive";
/**
* Encode all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
EncodingMode[EncodingMode["Attribute"] = 3] = "Attribute";
/**
* Encode all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
EncodingMode[EncodingMode["Text"] = 4] = "Text";
})(EncodingMode || (EncodingMode = {}));
/**
* Decodes a string with entities.
* @param input String to decode.
* @param options Decoding options.
*/
export function decode(input, options = EntityLevel.XML) {
const level = typeof options === "number" ? options : options.level;
if (level === EntityLevel.HTML) {
const mode = typeof options === "object" ? options.mode : undefined;
return decodeHTML(input, mode);
}
return decodeXML(input);
}
/**
* Encodes a string with entities.
* @param input String to encode.
* @param options Encoding options.
*/
export function encode(input, options = EntityLevel.XML) {
const { mode = EncodingMode.Extensive, level = EntityLevel.XML } = typeof options === "number" ? { level: options } : options;
switch (mode) {
case EncodingMode.UTF8: {
return escapeUTF8(input);
}
case EncodingMode.Attribute: {
return escapeAttribute(input);
}
case EncodingMode.Text: {
return escapeText(input);
}
case EncodingMode.ASCII: {
return (level === EntityLevel.HTML ? encodeNonAsciiHTML : encodeXML)(input);
}
// biome-ignore lint/complexity/noUselessSwitchCase: we get an error for the switch not being exhaustive
case EncodingMode.Extensive:
default: {
return (level === EntityLevel.HTML ? encodeHTML : encodeXML)(input);
}
}
}
export { DecodingMode, decodeHTML, decodeHTMLAttribute, decodeHTMLStrict, decodeXML, decodeXML as decodeXMLStrict, EntityDecoder, } from "./decode.js";
export { encodeHTML, encodeNonAsciiHTML, } from "./encode.js";
export { encodeXML, escape, escapeAttribute, escapeText, escapeUTF8, } from "./escape.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
+48
View File
@@ -0,0 +1,48 @@
/**
* Bit flags & masks for the binary trie encoding used for entity decoding.
*
* The trie is a flat `Uint16Array`. Every node starts with one header word:
*
* 15..14 VALUE_LENGTH Number of words the value occupies, +1.
* 0 = no value; 1 = value inline in bits 12..0;
* 2/3 = value in the 1/2 words after the header.
* 13 FLAG13 If VALUE_LENGTH > 0: semicolon required ("strict"
* entity; `;` is never stored as a branch).
* If VALUE_LENGTH == 0: this node is a compact run.
* 12..7 BRANCH_LENGTH Number of branches (or run length for runs).
* 6..0 JUMP_TABLE Jump-table offset / single-branch char / first
* run char (see below).
*
* Branch data follows the header and any value words. Its shape is selected
* by (JUMP_TABLE, BRANCH_LENGTH) in the header:
*
* Single branch JUMP_TABLE = the only child's char, BRANCH_LENGTH = 0.
* No branch words; the child node follows immediately.
* Jump table JUMP_TABLE = first covered char (> 0), BRANCH_LENGTH =
* table length. One word per covered char: 0 = no branch,
* otherwise the child's offset from the END of the table,
* +1 (so 0 stays the no-branch sentinel).
* Dictionary JUMP_TABLE = 0, BRANCH_LENGTH = number of branches.
* ceil(n/2) words of sorted keys packed two per word
* (low byte first), then n pointer words storing the
* child's offset from the END of the branch data.
* Compact run VALUE_LENGTH = 0, FLAG13 set. BRANCH_LENGTH = run
* length (3..63), JUMP_TABLE = first char; remaining run
* chars packed two per word after the header. The target
* node follows the packed words immediately.
*
* Pointers are end-relative (rather than relative to the pointer's own
* position) because that makes the common "child encoded right after the
* branch data" case a small constant, which compresses far better. Offsets
* to already-encoded (shared) nodes wrap via uint16 modulo arithmetic; the
* decoder masks navigation results with `& 0xff_ff` to match.
*/
export declare const enum BinTrieFlags {
VALUE_LENGTH = 49152,
FLAG13 = 8192,
BRANCH_LENGTH = 8064,
JUMP_TABLE = 127,
/** Bits 12..0: the inline value of a VALUE_LENGTH = 1 header word. */
VALUE_MASK = 8191
}
//# sourceMappingURL=bin-trie-flags.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"bin-trie-flags.d.ts","sourceRoot":"","sources":["../../src/internal/bin-trie-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,0BAAkB,YAAY;IAC1B,YAAY,QAAwB;IACpC,MAAM,OAAwB;IAC9B,aAAa,OAAwB;IACrC,UAAU,MAAwB;IAClC,sEAAsE;IACtE,UAAU,OAAwB;CACrC"}
+49
View File
@@ -0,0 +1,49 @@
/**
* Bit flags & masks for the binary trie encoding used for entity decoding.
*
* The trie is a flat `Uint16Array`. Every node starts with one header word:
*
* 15..14 VALUE_LENGTH Number of words the value occupies, +1.
* 0 = no value; 1 = value inline in bits 12..0;
* 2/3 = value in the 1/2 words after the header.
* 13 FLAG13 If VALUE_LENGTH > 0: semicolon required ("strict"
* entity; `;` is never stored as a branch).
* If VALUE_LENGTH == 0: this node is a compact run.
* 12..7 BRANCH_LENGTH Number of branches (or run length for runs).
* 6..0 JUMP_TABLE Jump-table offset / single-branch char / first
* run char (see below).
*
* Branch data follows the header and any value words. Its shape is selected
* by (JUMP_TABLE, BRANCH_LENGTH) in the header:
*
* Single branch JUMP_TABLE = the only child's char, BRANCH_LENGTH = 0.
* No branch words; the child node follows immediately.
* Jump table JUMP_TABLE = first covered char (> 0), BRANCH_LENGTH =
* table length. One word per covered char: 0 = no branch,
* otherwise the child's offset from the END of the table,
* +1 (so 0 stays the no-branch sentinel).
* Dictionary JUMP_TABLE = 0, BRANCH_LENGTH = number of branches.
* ceil(n/2) words of sorted keys packed two per word
* (low byte first), then n pointer words storing the
* child's offset from the END of the branch data.
* Compact run VALUE_LENGTH = 0, FLAG13 set. BRANCH_LENGTH = run
* length (3..63), JUMP_TABLE = first char; remaining run
* chars packed two per word after the header. The target
* node follows the packed words immediately.
*
* Pointers are end-relative (rather than relative to the pointer's own
* position) because that makes the common "child encoded right after the
* branch data" case a small constant, which compresses far better. Offsets
* to already-encoded (shared) nodes wrap via uint16 modulo arithmetic; the
* decoder masks navigation results with `& 0xff_ff` to match.
*/
export var BinTrieFlags;
(function (BinTrieFlags) {
BinTrieFlags[BinTrieFlags["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH";
BinTrieFlags[BinTrieFlags["FLAG13"] = 8192] = "FLAG13";
BinTrieFlags[BinTrieFlags["BRANCH_LENGTH"] = 8064] = "BRANCH_LENGTH";
BinTrieFlags[BinTrieFlags["JUMP_TABLE"] = 127] = "JUMP_TABLE";
/** Bits 12..0: the inline value of a VALUE_LENGTH = 1 header word. */
BinTrieFlags[BinTrieFlags["VALUE_MASK"] = 8191] = "VALUE_MASK";
})(BinTrieFlags || (BinTrieFlags = {}));
//# sourceMappingURL=bin-trie-flags.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"bin-trie-flags.js","sourceRoot":"","sources":["../../src/internal/bin-trie-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAN,IAAkB,YAOjB;AAPD,WAAkB,YAAY;IAC1B,mEAAoC,CAAA;IACpC,sDAA8B,CAAA;IAC9B,oEAAqC,CAAA;IACrC,6DAAkC,CAAA;IAClC,sEAAsE;IACtE,8DAAkC,CAAA;AACtC,CAAC,EAPiB,YAAY,KAAZ,YAAY,QAO7B"}
+38
View File
@@ -0,0 +1,38 @@
/**
* Decode a dictionary-encoded trie string back into its Uint16Array.
*
* Stream layout (consumed in this order):
* 1. dict1 atoms — `dict1AtomCount` uint16 values, delta+RLE encoded.
* 2. dict2 atoms — `atomCount - dict1AtomCount` values, delta+RLE.
* 3. dict2 ngrams — `ngramCount - (dictSize - dict1AtomCount)` entries,
* each a pair of slot codes that resolve to earlier slots.
* 4. dict1 ngrams — `dictSize - dict1AtomCount` entries, same shape.
* 5. data — slot codes, each expanding to one or more uint16 values.
*
* Codes use a 91-char base (printable ASCII minus `"`, `$`, `\`):
* - char1 < dictSize → 1-char code, slot = char1
* - char1 ≥ dictSize → 2-char code, slot = dictSize + (char1 - dictSize)*91 + char2
*
* Slot index → token kind:
* [0, A) dict1 atoms (1-char codes)
* [A, dictSize) dict1 ngrams (1-char codes)
* [dictSize, dictSize+D) dict2 atoms (2-char codes)
* [dictSize+D, end) dict2 ngrams (2-char codes)
*
* Both atom dicts decode before any ngram, and dict2 ngrams decode before
* dict1 ngrams. So every ngram entry references slots whose contents are
* already filled — no forward references to handle.
*
* This runs on library import. Flat typed arrays store each slot as either
* a plain value (`single`, covering every atom) or a range in a shared
* `pool` (ngrams).
* @param input Packed trie string.
* @param resultLength Expected number of uint16 values in the output.
* @param atomCount Total number of distinct uint16 values in the trie.
* @param dict1AtomCount Atoms in the 1-char range (`A` above).
* @param ngramCount Total number of ngram entries (dict1 + dict2).
* @param dictSize Number of 1-char code slots; the rest of `BASE - dictSize`
* first-byte values are 2-char codes.
*/
export declare function decodeTrieDict(input: string, resultLength: number, atomCount: number, dict1AtomCount: number, ngramCount: number, dictSize: number): Uint16Array;
//# sourceMappingURL=decode-shared.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode-shared.d.ts","sourceRoot":"","sources":["../../src/internal/decode-shared.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACjB,WAAW,CAqJb"}
+195
View File
@@ -0,0 +1,195 @@
/*
* Inverse of the encoder's SAFE alphabet (0x21..0x7E minus 0x22, 0x24, 0x5C),
* precomputed once at module load. Entries for excluded chars stay 0 but
* are never read.
*/
const BASE91_INVERSE = /* #__PURE__ */ (() => {
const table = new Uint8Array(127);
let code = 0;
for (let char = 0x21; char <= 0x7e; char++) {
if (char !== 0x22 && char !== 0x24 && char !== 0x5c) {
table[char] = code++;
}
}
return table;
})();
/**
* Decode a dictionary-encoded trie string back into its Uint16Array.
*
* Stream layout (consumed in this order):
* 1. dict1 atoms — `dict1AtomCount` uint16 values, delta+RLE encoded.
* 2. dict2 atoms — `atomCount - dict1AtomCount` values, delta+RLE.
* 3. dict2 ngrams — `ngramCount - (dictSize - dict1AtomCount)` entries,
* each a pair of slot codes that resolve to earlier slots.
* 4. dict1 ngrams — `dictSize - dict1AtomCount` entries, same shape.
* 5. data — slot codes, each expanding to one or more uint16 values.
*
* Codes use a 91-char base (printable ASCII minus `"`, `$`, `\`):
* - char1 < dictSize → 1-char code, slot = char1
* - char1 ≥ dictSize → 2-char code, slot = dictSize + (char1 - dictSize)*91 + char2
*
* Slot index → token kind:
* [0, A) dict1 atoms (1-char codes)
* [A, dictSize) dict1 ngrams (1-char codes)
* [dictSize, dictSize+D) dict2 atoms (2-char codes)
* [dictSize+D, end) dict2 ngrams (2-char codes)
*
* Both atom dicts decode before any ngram, and dict2 ngrams decode before
* dict1 ngrams. So every ngram entry references slots whose contents are
* already filled — no forward references to handle.
*
* This runs on library import. Flat typed arrays store each slot as either
* a plain value (`single`, covering every atom) or a range in a shared
* `pool` (ngrams).
* @param input Packed trie string.
* @param resultLength Expected number of uint16 values in the output.
* @param atomCount Total number of distinct uint16 values in the trie.
* @param dict1AtomCount Atoms in the 1-char range (`A` above).
* @param ngramCount Total number of ngram entries (dict1 + dict2).
* @param dictSize Number of 1-char code slots; the rest of `BASE - dictSize`
* first-byte values are 2-char codes.
*/
export function decodeTrieDict(input, resultLength, atomCount, dict1AtomCount, ngramCount, dictSize) {
const base = 91;
const inputLength = input.length;
// For 2-char codes, slot = char1 * base - twoCharBias + char2.
const twoCharBias = dictSize * (base - 1);
let pos = 0;
/** Read one slot code at `pos` and return its slot index, advancing pos. */
const readSlotCode = () => {
const c1 = BASE91_INVERSE[input.charCodeAt(pos++)];
return c1 < dictSize
? c1
: c1 * base - twoCharBias + BASE91_INVERSE[input.charCodeAt(pos++)];
};
const dict2AtomCount = atomCount - dict1AtomCount;
const slotCount = atomCount + ngramCount;
/*
* Per-slot contents: atoms (always a single value) live directly in
* `single`; ngram slots hold -1 there and expand to
* `pool[start[slot] .. start[slot] + length[slot])`.
*/
const single = new Int32Array(slotCount);
single.fill(-1, dict1AtomCount, dictSize);
single.fill(-1, dictSize + dict2AtomCount, slotCount);
const start = new Int32Array(slotCount);
const length = new Int32Array(slotCount);
/**
* Decode `count` ascending uint16 values from a delta+RLE stream into
* `single[off..off+count)`.
*
* code < 89 → delta = code
* code == 89 → run-length: next char encodes runLength-2; emit `runLength` consecutive +1 values
* code == 90, next < 90 → escape: delta = 89 + next * BASE + after-next
* code == 90, next == 90 → double-escape: extra char for very large deltas
* @param count
* @param off
*/
function decodeDelta(count, off) {
let previous = 0;
let slot = off;
const end = off + count;
while (slot < end) {
const code = BASE91_INVERSE[input.charCodeAt(pos++)];
if (code < 89) {
previous += code;
single[slot++] = previous;
}
else if (code === 89) {
let runLength = BASE91_INVERSE[input.charCodeAt(pos++)] + 2;
while (runLength--)
single[slot++] = ++previous;
}
else {
const next = BASE91_INVERSE[input.charCodeAt(pos++)];
previous +=
89 +
// eslint-disable-next-line unicorn/prefer-minimal-ternary -- branches read a different number of side-effecting input bytes
(next < 90
? next * base + BASE91_INVERSE[input.charCodeAt(pos++)]
: BASE91_INVERSE[input.charCodeAt(pos++)] * 8281 +
BASE91_INVERSE[input.charCodeAt(pos++)] * base +
BASE91_INVERSE[input.charCodeAt(pos++)]);
single[slot++] = previous;
}
}
}
// Streams 1 & 2: atoms decoded into their slot ranges.
decodeDelta(dict1AtomCount, 0);
decodeDelta(dict2AtomCount, dictSize);
/*
* Streams 3 & 4 are read in two passes: first collect every ngram's two
* references and derive its expanded length (each ref resolves to an earlier
* slot, so lengths are already known), which sizes the shared pool.
* Pool ranges are handed out in decode order, so the second pass fills
* the pool contiguously with a single write cursor.
*/
const references = new Int32Array(ngramCount * 2);
let poolSize = 0;
let ngramIndex = 0;
/**
* Read `count` ngram entries (each = 2 slot-code references) for the slots
* starting at `startSlot`, recording references and assigning pool ranges.
* @param count
* @param startSlot
*/
function readNgramReferences(count, startSlot) {
for (let index = 0; index < count; index++) {
const slot = startSlot + index;
const a = readSlotCode();
const b = readSlotCode();
references[ngramIndex * 2] = a;
references[ngramIndex * 2 + 1] = b;
ngramIndex += 1;
start[slot] = poolSize;
const entryLength = (single[a] < 0 ? length[a] : 1) +
(single[b] < 0 ? length[b] : 1);
length[slot] = entryLength;
poolSize += entryLength;
}
}
readNgramReferences(ngramCount - dictSize + dict1AtomCount, dictSize + dict2AtomCount);
readNgramReferences(dictSize - dict1AtomCount, dict1AtomCount);
// Second pass: concatenate each ngram's two halves into the pool.
const pool = new Uint16Array(poolSize);
let write = 0;
for (let index = 0; index < ngramIndex; index++) {
for (let half = 0; half < 2; half++) {
const source = references[index * 2 + half];
const value = single[source];
if (value < 0) {
let read = start[source];
const readEnd = read + length[source];
while (read < readEnd)
pool[write++] = pool[read++];
}
else {
pool[write++] = value;
}
}
}
// Stream 5: data. Each code expands to its slot's stored values.
const out = new Uint16Array(resultLength);
let outIndex = 0;
while (pos < inputLength) {
let slot = BASE91_INVERSE[input.charCodeAt(pos++)];
if (slot >= dictSize) {
slot =
slot * base -
twoCharBias +
BASE91_INVERSE[input.charCodeAt(pos++)];
}
const value = single[slot];
if (value < 0) {
let read = start[slot];
const readEnd = read + length[slot];
while (read < readEnd)
out[outIndex++] = pool[read++];
}
else {
out[outIndex++] = value;
}
}
return out;
}
//# sourceMappingURL=decode-shared.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"decode-shared.js","sourceRoot":"","sources":["../../src/internal/decode-shared.ts"],"names":[],"mappings":"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 truncated
+83
View File
@@ -0,0 +1,83 @@
{
"name": "entities",
"version": "8.1.0",
"description": "Encode & decode XML and HTML entities with ease & speed",
"keywords": [
"html entities",
"entity decoder",
"entity encoding",
"html decoding",
"html encoding",
"xml decoding",
"xml encoding"
],
"repository": {
"type": "git",
"url": "https://github.com/fb55/entities.git"
},
"funding": "https://github.com/fb55/entities?sponsor=1",
"license": "BSD-2-Clause",
"author": "Felix Boehm <me@feedic.com>",
"sideEffects": false,
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./decode": {
"types": "./dist/decode.d.ts",
"default": "./dist/decode.js"
},
"./escape": {
"types": "./dist/escape.d.ts",
"default": "./dist/escape.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist",
"src",
"!**/*.spec.ts"
],
"scripts": {
"benchmark": "node --import=tsx scripts/benchmark.ts",
"build": "tsc",
"build:docs": "typedoc --hideGenerator src/index.ts",
"build:encode-trie": "node --import=tsx scripts/write-encode-map.ts",
"build:trie": "node --import=tsx scripts/write-decode-map.ts",
"format": "npm run format:es && npm run format:biome",
"format:biome": "biome check --fix .",
"format:es": "npm run lint:es -- --fix",
"lint": "npm run lint:es && npm run lint:ts && npm run lint:biome",
"lint:biome": "biome check .",
"lint:es": "eslint .",
"lint:ts": "tsc --noEmit -p tsconfig.eslint.json",
"prepublishOnly": "npm run build",
"test": "npm run test:vi && npm run lint",
"test:vi": "vitest run"
},
"devDependencies": {
"@biomejs/biome": "^2.5.12",
"@eslint/compat": "^2.1.1",
"@feedic/eslint-config": "^0.5.0",
"@types/he": "^1.2.3",
"@types/node": "^26.4.1",
"eslint": "^10.10.0",
"eslint-config-biome": "^2.1.3",
"globals": "^17.12.0",
"he": "^1.2.0",
"html-entities": "^2.6.0",
"parse-entities": "^4.0.2",
"tinybench": "^6.1.5",
"tsx": "^4.23.13",
"typedoc": "^0.28.20",
"typescript": "^6.0.3",
"typescript-eslint": "^8.69.0",
"vitest": "^5.0.0"
},
"engines": {
"node": ">=20.19.0"
}
}
+131
View File
@@ -0,0 +1,131 @@
# entities [![NPM version](https://img.shields.io/npm/v/entities.svg)](https://npmjs.org/package/entities) [![Downloads](https://img.shields.io/npm/dm/entities.svg)](https://npmjs.org/package/entities) [![Node.js CI](https://github.com/fb55/entities/actions/workflows/nodejs-test.yml/badge.svg)](https://github.com/fb55/entities/actions/workflows/nodejs-test.yml)
Encode & decode HTML & XML entities with ease & speed.
## Features
- 😇 Tried and true: `entities` is used by many popular libraries; eg.
[`htmlparser2`](https://github.com/fb55/htmlparser2), the official
[AWS SDK](https://github.com/aws/aws-sdk-js-v3) and
[`commonmark`](https://github.com/commonmark/commonmark.js) use it to process
HTML entities.
- ⚡️ Fast: `entities` is the fastest library for decoding HTML entities (as of
September 2025); see [performance](#performance).
- 🎛 Configurable: Get an output tailored for your needs. You are fine with
UTF8? That'll save you some bytes. Prefer to only have ASCII characters? We
can do that as well!
## How to…
### …install `entities`
npm install entities
### …use `entities`
```javascript
import * as entities from "entities";
// Encoding
entities.escapeUTF8("&#38; ü"); // "&amp;#38; ü"
entities.encodeXML("&#38; ü"); // "&amp;#38; &#xfc;"
entities.encodeHTML("&#38; ü"); // "&amp;&num;38&semi; &uuml;"
// Decoding
entities.decodeXML("asdf &amp; &#xFF; &#xFC; &apos;"); // "asdf & ÿ ü '"
entities.decodeHTML("asdf &amp; &yuml; &uuml; &apos;"); // "asdf & ÿ ü '"
```
## Performance
Benchmarked in September 2025 with Node v24.6.0 on Apple M2 using `tinybench`.
Higher ops/s is better; `avg (μs)` is the mean time per operation.
See `scripts/benchmark.ts` to reproduce.
### Decoding
| Library | Version | ops/s | avg (μs) | ±% | slower |
| -------------- | ------- | --------- | -------- | ---- | ------ |
| entities | 7.0.0 | 5,838,416 | 175.57 | 0.06 | — |
| html-entities | 2.6.0 | 2,919,637 | 347.77 | 0.33 | 50.0% |
| he | 1.2.0 | 2,318,438 | 446.48 | 0.70 | 60.3% |
| parse-entities | 4.0.2 | 852,855 | 1,199.51 | 0.36 | 85.4% |
### Encoding
| Library | Version | ops/s | avg (μs) | ±% | slower |
| -------------- | ------- | --------- | -------- | ---- | ------ |
| entities | 7.0.0 | 2,770,115 | 368.09 | 0.11 | — |
| html-entities | 2.6.0 | 1,491,963 | 679.96 | 0.58 | 46.2% |
| he | 1.2.0 | 481,278 | 2,118.25 | 0.61 | 82.6% |
### Escaping
| Library | Version | ops/s | avg (μs) | ±% | slower |
| -------------- | ------- | --------- | -------- | ---- | ------ |
| entities | 7.0.0 | 4,616,468 | 223.84 | 0.17 | — |
| he | 1.2.0 | 3,659,301 | 280.76 | 0.58 | 20.7% |
| html-entities | 2.6.0 | 3,555,301 | 296.63 | 0.84 | 23.0% |
Note: Micro-benchmarks may vary across machines and Node versions.
---
## FAQ
> What methods should I actually use to encode my documents?
If your target supports UTF-8, the `escapeUTF8` method is going to be your best
choice. Otherwise, use either `encodeHTML` or `encodeXML` based on whether
you're dealing with an HTML or an XML document.
You can have a look at the options for the `encode` and `decode` methods to see
everything you can configure.
> When should I use strict decoding?
When strict decoding, entities not terminated with a semicolon will be ignored.
This is helpful for decoding entities in legacy environments.
> Why should I use `entities` instead of alternative modules?
As of September 2025, `entities` is faster than other modules. Still, this is
not a differentiated space and other modules can catch up.
**More importantly**, you might already have `entities` in your dependency graph
(as a dependency of eg. `cheerio`, or `htmlparser2`), and including it directly
might not even increase your bundle size. The same is true for other entity
libraries, so have a look through your `node_modules` directory!
> Does `entities` support tree shaking?
Yes! Note that for best results, you should not use the `encode` and `decode`
functions, as they wrap around a number of other functions, all of which will
remain in the bundle. Instead, use the functions that you need directly.
---
## Acknowledgements
This library wouldn't be possible without the work of these individuals. Thanks
to
- [@mathiasbynens](https://github.com/mathiasbynens) for his explanations about
character encodings, and his library `he`, which was one of the inspirations
for `entities`
- [@inikulin](https://github.com/inikulin) for his work on optimized tries for
decoding HTML entities for the `parse5` project
- [@mdevils](https://github.com/mdevils) for taking on the challenge of
producing a quick entity library with his `html-entities` library. `entities`
would be quite a bit slower if there wasn't any competition. Right now
`entities` is on top, but we'll see how long that lasts!
---
License: BSD-2-Clause
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security). Tidelift will
coordinate the fix and disclosure.
+69
View File
@@ -0,0 +1,69 @@
// Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134
/**
* C1 Unicode control character reference replacements (code points 128–159).
* Index i gives the replacement for code point 128+i; 0 means "no replacement".
*/
const c1: number[] = [
8364, 0, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 0,
381, 0, 0, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250,
339, 0, 382, 376,
];
/**
* True for NUL, UTF-16 surrogates, and values past U+10FFFF.
* @param codePoint Unicode code point to check.
*/
function isInvalidCodePoint(codePoint: number): boolean {
return (
codePoint === 0 ||
(codePoint >= 0xd8_00 && codePoint <= 0xdf_ff) ||
codePoint > 0x10_ff_ff
);
}
/**
* Replace the given code point with U+FFFD if it is NUL (0), a surrogate, or
* outside the valid Unicode range. Code points in the C1 controls range
* (128–159) are remapped to their Windows-1252 equivalents, following the
* HTML spec. All other code points are returned unchanged.
* @param codePoint Unicode code point to convert.
*/
export function replaceCodePoint(codePoint: number): number {
if (isInvalidCodePoint(codePoint)) {
return 0xff_fd;
}
if (codePoint >= 128 && codePoint <= 159) {
return c1[codePoint - 128] || codePoint;
}
return codePoint;
}
/**
* XML numeric character references are the referenced Unicode code point.
* Invalid values still become U+FFFD; the HTML Windows-1252 C1 remap is not
* applied.
* @see https://www.w3.org/TR/xml/#NT-CharRef
* @param codePoint Unicode code point to convert.
*/
export function replaceCodePointXML(codePoint: number): number {
return isInvalidCodePoint(codePoint) ? 0xff_fd : codePoint;
}
/**
* Convert the code point of a decoded numeric entity to a string, replacing
* invalid values.
*
* Fast path for plain BMP code points: [1..0x7F] and [0xA0..0xD7FF] pass
* `replaceCodePoint` unchanged (no NUL, C1 remap, surrogate, or out-of-range
* handling) and fit a single charCode. 0xd760 = 0xD800 (the first surrogate)
* - 0xA0.
* @param codePoint Unicode code point to convert.
*/
export function codePointToString(codePoint: number): string {
return (codePoint - 1) >>> 0 < 0x7f || (codePoint - 0xa0) >>> 0 < 0xd7_60
? String.fromCharCode(codePoint)
: String.fromCodePoint(replaceCodePoint(codePoint));
}
+1368
View File
File diff suppressed because it is too large. Load diff
+337
View File
@@ -0,0 +1,337 @@
import { XML_BITSET_VALUE, xmlEncodeRegex } from "./escape.js";
import htmlTrieData from "./generated/encode-html.js";
/**
* A node inside the encoding trie used by `encode.ts`.
*
* There are two physical shapes to minimize allocations and lookup cost:
*
* 1. Leaf node (string)
* - A plain string (already in the form `"&name;"`).
* - Represents a terminal match with no children.
*
* 2. Branch / value node (object)
*/
type EncodeTrieNode =
| string
| { value: string | null; next: Map<number, EncodeTrieNode> };
/**
* Flat lookup table for ASCII entity values (code points 0–127).
*
* Built once at startup from the full trie. For ASCII characters that need
* encoding, a direct `asciiEntities[charCode]` array index is much faster
* than `Map.get(charCode)` because it avoids hashing and bucket lookup.
*
* A few ASCII characters also start multi-code-point entities
* (e.g. `<` + U+20D2 → `&nvlt;`). For those, the table holds the branch
* node itself (children plus the single-char value), so the ASCII path
* never consults `htmlTrie`.
*/
const asciiEntities: (EncodeTrieNode | null)[] = /* #__PURE__ */ Array.from(
{ length: 128 },
() => null,
);
const htmlTrie: Map<number, EncodeTrieNode> = (() => {
/**
* Parse a compact encode trie string into a Map keyed by code point.
*
* The serialized format (produced by `scripts/write-encode-map.ts`) stores
* entries in ascending code-point order with delta encoding:
*
* [gap]name;[{children}]
*
* - `gap` is a base-10 integer giving `currentKey - previousKey - 1`.
* The very first entry stores the absolute key. A gap of 0 is omitted.
* - `name;` is the entity name (without `&` prefix) terminated by `;`.
* Because gaps use only digits `[0-9]` and entity names always start with
* a letter `[A-Za-z]`, no additional delimiter is needed.
* - `{…}` is an optional children block using the same scheme recursively.
* Children represent the second code unit of multi-character entities
* (e.g. `<` + U+20D2 → `&nvlt;`).
*/
const trie = new Map<number, EncodeTrieNode>();
const data = htmlTrieData;
let cursor = 0;
let lastKey = -1;
function readGap(): number {
let value = 0;
let ch: number;
while (
(ch = data.charCodeAt(cursor)) >= 48 /* '0' */ &&
ch <= 57 /* '9' */
) {
value = value * 10 + ch - 48;
cursor++;
}
return value;
}
function readEntity(): string {
const semi = data.indexOf(";", cursor);
const entity = `&${data.substring(cursor, semi)};`;
cursor = semi + 1;
return entity;
}
const astralEntries: [number, string][] = [];
while (cursor < data.length) {
lastKey += readGap() + 1;
const entityValue =
data.charCodeAt(cursor) === 123 /* '{' */ ? null : readEntity();
if (data.charCodeAt(cursor) === 123 /* '{' */) {
cursor++; // Skip '{'
const next = new Map<number, EncodeTrieNode>();
let childKey = -1;
while (data.charCodeAt(cursor) !== 125 /* '}' */) {
childKey += readGap() + 1;
next.set(childKey, readEntity());
}
const branch = { value: entityValue, next };
trie.set(lastKey, branch);
cursor++; // Skip '}'
// ASCII fast path holds the branch node itself (children + value).
if (lastKey < 0x80) {
asciiEntities[lastKey] = branch;
}
} else if (lastKey < 0x80) {
asciiEntities[lastKey] = entityValue;
} else if (lastKey > 0xff_ff) {
astralEntries.push([lastKey, entityValue!]);
} else {
trie.set(lastKey, entityValue!);
}
}
/*
* Batch-insert astral entries as surrogate-pair trie nodes.
* Entries are sorted by code point, so same-high-surrogate groups
* are contiguous — no intermediate grouping Map needed.
*/
let astralIndex = 0;
while (astralIndex < astralEntries.length) {
const hi =
0xd8_00 | ((astralEntries[astralIndex][0] - 0x1_00_00) >> 10);
const children: [number, string][] = [];
while (
astralIndex < astralEntries.length &&
(0xd8_00 | ((astralEntries[astralIndex][0] - 0x1_00_00) >> 10)) ===
hi
) {
const lo =
0xdc_00 |
((astralEntries[astralIndex][0] - 0x1_00_00) & 0x3_ff);
children.push([lo, astralEntries[astralIndex][1]]);
astralIndex++;
}
trie.set(hi, { value: null, next: new Map(children) });
}
return trie;
})();
/**
* Bitset covering ASCII code points 0–127. Each of the four 32-bit words
* covers a 32-code-point range. A set bit means "this character needs
* encoding" when used with `encodeHTML`.
*/
const HTML_BITSET = /* #__PURE__ */ new Uint32Array([
0x16_00, // 09 (\t), 0A (\n), 0C (\f)
0xfc_00_ff_fe, // 21-2D (!-.), 2E (.), 2F (/), 3A-3F (:;<=>?)
0xf8_00_00_01, // 40 (@), 5B-5F ([\]^_)
0x38_00_00_01, // 60 (`), 7B-7D ({|})
]);
const XML_BITSET = /* #__PURE__ */ new Uint32Array([0, XML_BITSET_VALUE, 0, 0]);
/*
* Regex equivalent of `HTML_BITSET` (plus all non-ASCII code units, lone
* surrogates included — no `u` flag). Must stay in sync with the bitset:
* the scan uses the regex to find candidates and the bitset to re-check
* adjacent characters. The XML equivalent is `xmlEncodeRegex`, shared with
* `escape.ts`.
*/
// eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes -- the `\u{...}` form requires the `u` flag, which we deliberately omit so lone surrogates match by code unit
const HTML_ENCODE_RE = /[\t\n\f!-/:-@[-`{-}\u0080-\uFFFF]/g;
const numericReference = (cp: number) => `&#x${cp.toString(16)};`;
/**
* Encodes all characters in the input using HTML entities. This includes
* characters that are valid ASCII characters in HTML documents, such as `#`.
*
* To get a more compact output, consider using the `encodeNonAsciiHTML`
* function, which will only encode characters that are not valid in HTML
* documents, as well as non-ASCII characters.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeHTML(input: string): string {
return encodeHTMLTrieRe(HTML_BITSET, HTML_ENCODE_RE, input);
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in HTML
* documents using HTML entities. This function will not encode characters that
* are valid in HTML documents, such as `#`.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeNonAsciiHTML(input: string): string {
return encodeHTMLTrieRe(XML_BITSET, xmlEncodeRegex, input);
}
/**
* Whether `code` (a UTF-16 code unit) must be encoded: any non-ASCII unit, or
* an ASCII unit flagged in `bitset`.
* @param bitset Bitset of ASCII characters to encode.
* @param code Code unit to test.
*/
function isEncodable(bitset: Uint32Array, code: number): boolean {
return code >= 0x80 || ((bitset[code >>> 5] >>> code) & 1) === 1;
}
/*
* The inline scan beats the regex jump only for short gaps (measured
* break-even is ~7 characters; below it the per-call regex overhead
* dominates, above it the regex skips clean spans faster in native code).
* So we scan at most `INLINE_SCAN_WINDOW` characters inline before deferring
* to the regex. Any gap long enough to exhaust the window switches to
* long-gap mode, which skips the inline scan entirely; we stay in that mode
* while total gaps are at least `LONG_GAP_THRESHOLD` characters long and
* drop back to the inline scan as soon as a shorter gap appears. Gaps are
* always measured from where they start, so every window-exhausting gap
* enters long-gap mode and no gap length pays both scans repeatedly.
*/
/** @internal Exported for tests; not re-exported from the package entry. */
export const INLINE_SCAN_WINDOW = 16;
const LONG_GAP_THRESHOLD = 8;
function encodeHTMLTrieRe(
bitset: Uint32Array,
re: RegExp,
input: string,
): string {
const { length } = input;
let out: string | undefined;
let last = 0; // Start of the next untouched slice.
let index = 0;
let wasLongGap = false; // The previous gap was long; skip the inline scan.
while (index < length) {
let char = input.charCodeAt(index);
/*
* Find the next encodable character (one matching `bitset`, or any
* non-ASCII unit). Gaps between entities in dense text are only a
* few characters, so scan a short window inline first; the regex —
* which matches exactly the same characters and skips clean spans
* in native code — runs for longer gaps. Gap lengths cluster, so
* after a window-exhausting gap the window is skipped until a gap
* shorter than LONG_GAP_THRESHOLD reappears. Once located, the
* character is captured in `char` and control falls through to the
* encode logic below instead of re-testing it on the next loop
* iteration.
*/
if (!isEncodable(bitset, char)) {
const gapStart = index;
let next = index + 1;
let wasFound = false;
if (!wasLongGap) {
const bound = Math.min(index + INLINE_SCAN_WINDOW, length);
while (
next < bound &&
!isEncodable(bitset, (char = input.charCodeAt(next)))
) {
next++;
}
if (next < bound) {
// `char` already holds the encodable unit at `next`.
index = next;
wasFound = true;
} else if (next >= length) {
break;
}
}
if (!wasFound) {
/*
* Every match is a single code unit, so `test` pins it at
* `lastIndex - 1` without allocating a match object.
*/
re.lastIndex = next;
if (!re.test(input)) break;
index = re.lastIndex - 1;
/*
* Reaching the regex from the inline scan means the gap
* exhausted the window, so it is long by definition; once
* in long-gap mode, stay until a gap shorter than the
* threshold appears.
*/
wasLongGap =
!wasLongGap || index - gapStart >= LONG_GAP_THRESHOLD;
char = input.charCodeAt(index);
}
}
// Lazy-init: copy the prefix before the first character that needs encoding.
if (out == null) out = input.substring(0, index);
else if (last !== index) out += input.substring(last, index);
if (char < 0x80) {
const node = asciiEntities[char];
if (typeof node === "object" && node !== null) {
// Multi-code-point entity first (e.g. < + U+20D2 → &nvlt;).
if (index + 1 < length) {
const value = node.next.get(input.charCodeAt(index + 1));
if (value != null) {
out += value;
last = index += 2;
continue;
}
}
out += node.value ?? numericReference(char);
} else {
out += node ?? numericReference(char);
}
} else {
// Non-ASCII: full trie lookup with multi-char entity support.
let node: EncodeTrieNode | undefined | null = htmlTrie.get(char);
if (typeof node === "object") {
if (index + 1 < length) {
const value = node.next.get(input.charCodeAt(index + 1));
if (value != null) {
out += value;
last = index += 2;
continue;
}
}
node = node.value;
}
if (node == null) {
// No named entity exists; emit a hexadecimal numeric reference.
const cp = input.codePointAt(index)!;
out += numericReference(cp);
// Astral code points consume two UTF-16 code units.
if (cp !== char) index++;
} else {
out += node;
}
}
last = index += 1;
}
// If nothing needed encoding, return the original string (avoids allocation).
if (out == null) return input;
if (last < length) out += input.substr(last);
return out;
}
+187
View File
@@ -0,0 +1,187 @@
/**
* Get the named reference for an XML special character or U+00A0.
* @param char Code unit matched by one of the escape regexes.
*/
function getEscape(char: number): string {
return char === 34
? "&quot;"
: char === 38
? "&amp;"
: char === 39
? "&apos;"
: char === 60
? "&lt;"
: char === 62
? "&gt;"
: "&nbsp;";
}
/**
* Read a code point at a given index.
* @param input String to read the code point from.
* @param index Current read position in the input string.
* @returns The code point at `index`, or `undefined` if `index` is out of range.
* @deprecated Use `String.prototype.codePointAt` directly instead; this export
* will be removed in the next major.
*/
export const getCodePoint: (input: string, index: number) => number = (
input: string,
index: number,
): number => input.codePointAt(index)!;
/**
* Bitset for ASCII characters that need to be escaped in XML.
*/
export const XML_BITSET_VALUE = 0x50_00_00_c4; // 32..63 -> 34 ("),38 (&),39 ('),60 (<),62 (>)
/**
* Matches exactly the characters `encodeXML` escapes: the five XML special
* characters plus every non-ASCII code unit (lone surrogates included — no
* `u` flag). Kept in sync with `XML_BITSET_VALUE`.
*
* Shared with `encodeNonAsciiHTML` in `encode.ts`. Because the regex is
* stateful (`g` flag), every call site must set `lastIndex` before use.
*/
// eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes -- the `\u{...}` form requires the `u` flag, which we deliberately omit so lone surrogates match by code unit
export const xmlEncodeRegex: RegExp = /["&'<>\u0080-\uFFFF]/g;
/**
* Whether `code` (a UTF-16 code unit) is escaped by {@link encodeXML}: a
* non-ASCII unit, or one of the five XML specials flagged in
* `XML_BITSET_VALUE` (which is only meaningful for code units 32-63).
* @param code Code unit to test.
*/
function isXmlEscapable(code: number): boolean {
return (
code >= 0x80 ||
(code >= 32 && code < 64 && ((XML_BITSET_VALUE >>> code) & 1) === 1)
);
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using XML entities.
*
* If a character has no equivalent entity, a numeric hexadecimal reference
* (eg. `&#xfc;`) will be used.
* @param input Input string to encode.
*/
export function encodeXML(input: string): string {
const { length } = input;
let out: string | undefined;
let last = 0;
let index = 0;
while (index < length) {
const char = input.charCodeAt(index);
/*
* Find the next character to escape: scan a short window inline
* (escapable characters cluster in markup-heavy input), then fall
* back to the regex, which skips clean spans in native code.
*/
if (!isXmlEscapable(char)) {
const bound = Math.min(index + 32, length);
let next = index + 1;
while (next < bound && !isXmlEscapable(input.charCodeAt(next))) {
next++;
}
if (next < bound) {
index = next;
continue;
}
if (next >= length) break;
xmlEncodeRegex.lastIndex = next;
if (!xmlEncodeRegex.test(input)) break;
index = xmlEncodeRegex.lastIndex - 1;
continue;
}
if (out === undefined) out = input.substring(0, index);
else if (last !== index) out += input.substring(last, index);
if (char < 64) {
// Known replacement
out += getEscape(char);
last = index += 1;
continue;
}
// Non-ASCII: encode as numeric entity (handle surrogate pair)
const cp = input.codePointAt(index)!;
out += `&#x${cp.toString(16)};`;
if (cp !== char) index++; // Skip trailing surrogate
last = index += 1;
}
if (out === undefined) return input;
if (last < length) out += input.substr(last);
return out;
}
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using numeric hexadecimal reference (eg. `&#xfc;`).
*
* Have a look at `escapeUTF8` if you want a more concise output at the expense
* of reduced transportability.
* @param data String to escape.
*/
export const escape: typeof encodeXML = encodeXML;
/**
* Escape `data` using `re`, mapping each matched character to its entity.
* Every match is one UTF-16 code unit, so its index is `lastIndex - 1`.
* @param re Global regex matching exactly the characters to escape
* (`"`, `&`, `'`, `<`, `>`, `\u00A0` at most).
* @param data String to escape.
*/
function escapeWithRegex(re: RegExp, data: string): string {
re.lastIndex = 0;
if (!re.test(data)) return data;
let out = "";
let last = 0;
do {
const index = re.lastIndex - 1;
if (last !== index) out += data.substring(last, index);
const char = data.charCodeAt(index);
out += getEscape(char);
last = index + 1;
} while (re.test(data));
return out + data.substring(last);
}
const xmlEscapeRegex = /["&'<>]/g;
/**
* Encodes all characters not valid in XML documents using XML entities.
*
* Note that the output will be character-set dependent.
* @param data String to escape.
*/
export function escapeUTF8(data: string): string {
return escapeWithRegex(xmlEscapeRegex, data);
}
const attributeEscapeRegex = /["&\u{A0}]/gu;
/**
* Encodes all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export function escapeAttribute(data: string): string {
return escapeWithRegex(attributeEscapeRegex, data);
}
const textEscapeRegex = /[&<>\u{A0}]/gu;
/**
* Encodes all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
* @param data String to escape.
*/
export function escapeText(data: string): string {
return escapeWithRegex(textEscapeRegex, data);
}
+12
View File
@@ -0,0 +1,12 @@
// Generated using scripts/write-decode-map.ts
import { decodeTrieDict } from "../internal/decode-shared.js";
/** Packed HTML decode trie data. */
export const htmlDecodeTree: Uint16Array = /* #__PURE__ */ decodeTrieDict(
"!}.&u%}'&}*'~!6*)%&,~!J~!J~%L~y<~!R,~~%Lu~~#GD~~#|)1#%}^%}2%+#.##%##%}&%##%'#%##&%#%#'%#&#%#&#'#%%#&#%##%#)%''%&%#%#'%#%%#%%}%%%#%#&(23#%%#&-%0%('1#(##%#'##+%'*.:1}#%#6-+(%'%%#%%%}#L'2351&('%}&/N'(0(/*-%(%%}#'+&T%7.2}#&%&#%#36/5##%&%%#&#%%#))2%%##%&&'0~!#*+&'%1~!%).'3q?&%'1~!.##%6(~!+%%%(Gw'rT~!E#<nA%#jZ~!H%(~!42##~!*31&~!G%U~#)5~#`3~!J~!Z~%]~%Y~%C~!q~!u~#kz~%#~!6'~!D~!U~!?~#T~!c%~!G#'~%7|~!G~!J~!G&~#pb~(Df}#%}*&}#%##%##%##&#-}&'#'&%#.++}%mI,#,@&(}*%}*'%&##&#%##%}&0}#.},U},%}+%}&%}#%##&}B%(}(%}+%)})%##%#&}&%##%&}<%}>%#%&}*%}(%}9%}/%})%}*%}*%}?&}&%}3%}&*#%})%#%#)}#&#-#+*%E%%'%'#%}#*V##&##I}#&&##%&%#&&Qf%%))w/0+&%#(#.%-''''++++7}>%4'',##1,#%#&%##&#'##&#*#9)%&%}#*}%,#+P(%A&%#'&##wSD',9E00#y#@}(+}&%&>~!#~!X}#*}(&&}(&}(,%}%&#+&}#&}I%#%}%)#(},'%#*}4%%#%}(''}#/##(##),%-##%%)#&}(.}&%#&}%%}*&#%},&&}&%}#%*'#%})%}D&}&%}-&}6&#&}-,%}#%})-(~+`~,=?~I9'9%~!,#%})%})%}@%}?%}(~!?~#<~#pP~#BG~#=1#%K+~#?#~%;)~#A~#mF1~#A'~'X%'~#lR~#N~'N~#r~#m#-~#i'?%#'%~#B%##%,%#~#_%#0%~#]732~,w~2+#:&#%&'0%&>%}#>##F+)#%&&#(+_}4&}-%}(&}@&}O7Fdf0@+/v4}&WU##&/0#&'('B#%}.%}'+#%}#%%&#&%#%##+#&#)#6#'#.},%}c%},%#%##%&#&%#&~#>'*-.%##%##%}#%%}%'~#)D1}#%*&~#_%%'(~#S2%'.}#~#=##*'*-%}&'%'##&&~'E%.#&~#M4}%%##&'%#~#O1##%&#'+~#<B%##%%'%+~#;#@%}#&%#&&%#(~#H1}'%'##&&~#?A}&'~#D#%32}'&&&&~#[}'(#%}'~#;C})&}%%#%~#=&%,3}%'(#%%~#^'#&&)#%'~#Y%-~#d-%'~#^%%&#&&&}#~#b~2t*&'~&(~&@~0%~e~3}%*''0})&}+~!9##-}#%-hD*)1fC#%/&/fB#40~!+#)*4~!+~!K'&:~!/*7~!.#~!H~!L':~%x&~!H#~!*~%1~!I#~!+A~#p'~!F~~#-#~,,(~.Z~!V~%;'B'mq-W~!N~%I%#&&#&}#%},%%}'%}+X#%}#&}(%}'%}<%}#%}%%'}'%}:~![)9@~%>~#UA%-%##&~!C%~!-.9:~!1~!-^2/:a~!y,D*J#-5)/4~%23,~#G~!L1~!0X3`~!2+~!!0-~&E~!W~!o,>Y&]~%cZx_&~#O*9#A#'#+I'%#)~!0B*-5A+-((F&*M#)(-7-5+'-3a5Vi~!Y~!?+[)%3),ERHm~!+:D,VG.+)?fB%%*(%)'(#&80%1'8`K8?`+'Z#&O&'H5#*9)A%%5&3))0%39+.*7#()&&*=4@**L)<'_&*+..;(#*+)./&0#3)%')-8(4ixD(&.}%,('aI:,)%,k2231T)I'#/-W7,/'Q#.'Y24+h')37</31&83##&0#),H(?'&?/1##%#&&#%''-%&&&#(&''&#.-'%#%%(,')*'&#&#'##%(%(#%('#&##%%%%('%#%#%%#%#&%##h>w+v<ayvyvcg.uuhKr}g/v|g>u9i[~>g5uI~=RvdwEg;v/g;uk!!TTSx]@RT!U!#!@VBRUU!'UTe-d0c`e&gSdicedFcrdTaqb.kYcAohdYd@a3e+d}dMdtd.aJ#bqcK`dle/e.e'dwdPdodddjbEb}ogd^ofdpduc6j?l%d{drdqc)d7bacOdQ%T#Y)X.sR[yH>6Vyv3[xwLu>vo'!*.[yBacahoj>6Rew3[xqdZa#!a&#^(X-[yG>6Vyu3[xvg3sEr|g.u/Ri9db0T#^(Xa)!-[y;>6Vylg4wKs{JwNZt3@3r=c4Z([xlg;wKt!cpq's@v7A'*a(a+!-a#[y<3Dt?3Dt'>6Vym3[xmg9rxsNJwLZt4~?r?db1T#`-!(Xa,!0[yS>6Vz%NuQs.g4wKtnJwNZtS@3r>c4Z([y%g;wKtrdga8!a(!#&T*Y-Xa#!a0<or[yc3Dtq>6Vz43[y3JwNZtf@3s!Ju}!%Dti:pm3c_%X#tjB5pkd6q!r]u?voC'*-a.a2!0a&a+[yI3DtI3Ds~3DtH>6Vyw3[xx;:s#~<5pKJwNZtE@3r~d`a)!a2T#a.(!+U.X1[yT3Dt`3Dtv>6Vz&3[y&g9rxwzcxstPu.<rAJwLZtT~?r@dZa%!a.&^*Za(/Reu[ya>6Vz23[y1g3sEr}wkg{NuQRg{ci(U#5@b`~,cg#U(2WnH5wugcRh7dX#T(Y,a'Ta!!a,[yZ<]mj>6Vz,3[y+Pv#5ReZKu+=,%!H}7ABwkaS?Rh:BcW(X#<]mrj:ubv/ARekdg%!(!a.*Ta(Y.X1!#sP>Rl*Dt6[y>>6Vyo3Wf*jOvuumvuRgRJuq*!:9<B@bX~3jVv&v@s@5Re[d/rQt{uAvo&a&a*)a2!,0Wf!3Dt0=Bs'>6Re}3[xy~<5s%JwJZt1~Gs)c;&!#2sJkNuXvzq7rxu,Re8dka4!a8(aEZ+a@Y.X1Xa)[yd=Bs(3DtP>6Vz53[y4cX#X&Re:avRe9~<5s&JwJZtQ~Gs*i^rzvdRg+Jv{%!2sbB@bX}kdga,!Za?&^*T1/!a'Dt+[y6>6Vyf3Wf%g/u;s4hGu6?Rh-JvZ,!c%#&RoX54Rivj7uyvf8RgTKvZB%*!2sGh<vu5Rgq<=C::9bb~#dZ#T&Ta6Y.X*Dt>[y93Wf)coZ(T,6VyifluvRgC@95@B@bX~/hFu34cC#T,k/unq8w8Q5RkUklwQuzunq8w8Q5Rk8d/rJu?v8w9)-&!a0a;a&aIWejg3sEr/h1s<DtDJvyZqY5aws3Jvy!&Wei~Hr1:au5@Bag>23E~5c:Z&bX};kKv?w&unuVu5Rjc;>bs)#~@:Rh.=ay<a]C;b`}Vd6s/t{uAvoaxa()!a,a7%-a#a2Dt,[yF2Wo[>6Vyt3[xuNuPRi&NuPwpi#RoWh?vf8Ri%Jv]!%Ri:KvxD!.'2WeAjZu`q9rxu,Re7woeAg-unLq(qA_/*2Wg_g3u5q^9:4E}/jTrxrzv=Wkkd~0UX#^^Xa-a1a5T&a=U1a'*aEa]!a*aPaA-adok[y54Rn>;:p3~Dp5g9rpsFNvZqjg3uJp4~<5p0Pw;5qlJwNZt*@3p1Pw:5p/Ou!5p2JvG'!6Vye=<qnJvh_[xhg3v,Rh3kOwOw-sDuev/Re^dha[a%!%!a+#Ta7)-5TaCaO!aka!a)sf[yb2>Rl!9ARiq5E}Qg=ucRkBE|oJrJ_@Wk~@Wk{JrJ_@Wk|@WkyJrJ_@Wk}@WkzJvO_[y2g-vMRmiKuYC!)&>Ri;>Ri<@3RkNc](X#@9Rk=g5vuRmhKvDB!+'=]meg3u4Rmgd)#Y'Vz3CARmfd`a+!%T'!+#Ta1Ta6TaM-sTDt9[yA9sYd'%Y#s[[xpj:ueunaXRgEjRq,v-vuqdd2'`#6Rev<32@5>:2<E}5xIo9a*X#Y(;5RePJvD_g>vyRgNj8w)v8<wggs:RgXiZt|vjx,hSq3ah!-(~@:Ro/Ou!5RhWj^v(pyw8unRhUdx-UY#^Ua.a3a70!)%UX1TaDa)'omRiRRhE[y:3Dsz=Br,>6Vyj3[xkg6ruwjcqsrPw;5r*Ku]D'Zt-@3r(~?r.i[vwv]dU1a--U#`a4(g/vsRhPOu!5RhLj:rmu9Wo!~@:wdh@g/vsRiTjXuvvNr}:RhBj^v(pyw8unRn]dz1UYa'a+^Y(!aETZalaRY.Ta?a4[yDJw1!#qLsW>6Vyrfzq-pLflpwRe|Js>%!Dt@3Dt&Jvy_[xs~HrnjMuwpsw'RecKu+D#'!t<~Grl~?rjg5u-x,gwp{ah!-(~@:Rg~Ou!5Rh'jXuvvNr}:Rh#cW#X/c;&!#2sLi[v7u7RgpJv)(!iLrxu,Re6j7v@s@5Se[e7d`aW!Za(a`T.a#!a3!&aDa-!9)Dt_=6s+3[x~~DR|h~DS6avhGun5RkZj3w)v-]mkKunB!&*]kb97R|i<ARk<c:Z(6Vy}Juh'!wziMRoS:F|vkLuauJv5vtvQRh1d='T+Y#VyO~DR|jcF#T'7R|g97R|kJv3'!ay<Rj,Jvh&!:ReXcsa6*a+#a#_aIRf9aLRf?c,Z&Rf5Rf7c.Z&Rf;Rf>cQ#%T'p-Rf8Rf=ct#%'(*!,p,Rf4p+Rf6Rf:Rf<d~'Ua%U*^UYa(!a,-!#a4YaTalaEX0a8a<Weo3Dt/3Dsx=Br93Wen~Dr;~<5p<JwNZt2@3p=Pw:5p;Ou!5r3c7&!#:p>3Ds}KvGB)_6Vyk2sM=<r7x'eovA(!hFu1ARf}cV#X&@r5j6rvwQa^Rf3c=Za'wkghJv__g;unRggA53B9=b^}%j6uduo5Jq;!(hIv%2Re`Ou4ARe_e%a#^^^Xa&!a*a2!&a6YaP!*ad!#a:aE/5Rn?[y@>6Vyp;:pE~DrY~<5pBJwNZt8@3pCh=rt3rWPw:5pAJup_[xoNuPpF9c!#'45pD5ARn)d8#X'X*3@rU72s]h>v<<sSjJpqvewOJq/(!hNw'5ReBk0s2u3w/w'5ReE5@Jq.!a+JQ!&WeU23d(#Y&RjG5]jBk!u7w&u0udARjEe#+^^^Ub#!a2/a`Z(agT1!a-a;|@TaG!aS[yV=Re~fow'RguNuPRe?bLine truncated
13_494,
2713,
49,
25,
61,
);
+7
View File
@@ -0,0 +1,7 @@
// Generated using scripts/write-decode-map.ts
/** Packed XML decode trie data. */
export const xmlDecodeTree: Uint16Array = /* #__PURE__ */ new Uint16Array([
512, 26_465, 29_036, 7, 0, 2, 4, 116, 24_638, 116, 24_636, 8693, 29_807,
24_610, 621, 1, 0, 0, 3, 112, 24_614, 111, 115, 24_615,
]);
+3
View File
@@ -0,0 +1,3 @@
// Generated using scripts/write-encode-map.ts
export default "9Tab;NewLine;22excl;quot;num;dollar;percnt;amp;apos;lpar;rpar;ast;plus;comma;1period;sol;10colon;semi;lt;{8402nvlt;}equals;{8421bne;}gt;{8402nvgt;}quest;commat;26lbrack;bsol;rbrack;Hat;lowbar;DiacriticalGrave;5{106fjlig;}20lbrace;verbar;rbrace;34nbsp;iexcl;cent;pound;curren;yen;brvbar;sect;die;copy;ordf;laquo;not;shy;circledR;macr;deg;PlusMinus;sup2;sup3;acute;micro;para;centerdot;cedil;sup1;ordm;raquo;frac14;frac12;frac34;iquest;Agrave;Aacute;Acirc;Atilde;Auml;angst;AElig;Ccedil;Egrave;Eacute;Ecirc;Euml;Igrave;Iacute;Icirc;Iuml;ETH;Ntilde;Ograve;Oacute;Ocirc;Otilde;Ouml;times;Oslash;Ugrave;Uacute;Ucirc;Uuml;Yacute;THORN;szlig;agrave;aacute;acirc;atilde;auml;aring;aelig;ccedil;egrave;eacute;ecirc;euml;igrave;iacute;icirc;iuml;eth;ntilde;ograve;oacute;ocirc;otilde;ouml;div;oslash;ugrave;uacute;ucirc;uuml;yacute;thorn;yuml;Amacr;amacr;Abreve;abreve;Aogon;aogon;Cacute;cacute;Ccirc;ccirc;Cdot;cdot;Ccaron;ccaron;Dcaron;dcaron;Dstrok;dstrok;Emacr;emacr;2Edot;edot;Eogon;eogon;Ecaron;ecaron;Gcirc;gcirc;Gbreve;gbreve;Gdot;gdot;Gcedil;1Hcirc;hcirc;Hstrok;hstrok;Itilde;itilde;Imacr;imacr;2Iogon;iogon;Idot;imath;IJlig;ijlig;Jcirc;jcirc;Kcedil;kcedil;kgreen;Lacute;lacute;Lcedil;lcedil;Lcaron;lcaron;Lmidot;lmidot;Lstrok;lstrok;Nacute;nacute;Ncedil;ncedil;Ncaron;ncaron;napos;ENG;eng;Omacr;omacr;2Odblac;odblac;OElig;oelig;Racute;racute;Rcedil;rcedil;Rcaron;rcaron;Sacute;sacute;Scirc;scirc;Scedil;scedil;Scaron;scaron;Tcedil;tcedil;Tcaron;tcaron;Tstrok;tstrok;Utilde;utilde;Umacr;umacr;Ubreve;ubreve;Uring;uring;Udblac;udblac;Uogon;uogon;Wcirc;wcirc;Ycirc;ycirc;Yuml;Zacute;zacute;Zdot;zdot;Zcaron;zcaron;19fnof;34imped;63gacute;65jmath;142circ;caron;16breve;DiacriticalDot;ring;ogon;DiacriticalTilde;dblac;51DownBreve;127Alpha;Beta;Gamma;Delta;Epsilon;Zeta;Eta;Theta;Iota;Kappa;Lambda;Mu;Nu;Xi;Omicron;Pi;Rho;1Sigma;Tau;Upsilon;Phi;Chi;Psi;ohm;7alpha;beta;gamma;delta;epsi;zeta;eta;theta;iota;kappa;lambda;mu;nu;xi;omicron;pi;rho;sigmaf;sigma;tau;upsi;phi;chi;psi;omega;7thetasym;Upsi;2phiv;piv;5Gammad;digamma;18kappav;rhov;3epsiv;backepsilon;10IOcy;DJcy;GJcy;Jukcy;DScy;Iukcy;YIcy;Jsercy;LJcy;NJcy;TSHcy;KJcy;1Ubrcy;DZcy;Acy;Bcy;Vcy;Gcy;Dcy;IEcy;ZHcy;Zcy;Icy;Jcy;Kcy;Lcy;Mcy;Ncy;Ocy;Pcy;Rcy;Scy;Tcy;Ucy;Fcy;KHcy;TScy;CHcy;SHcy;SHCHcy;HARDcy;Ycy;SOFTcy;Ecy;YUcy;YAcy;acy;bcy;vcy;gcy;dcy;iecy;zhcy;zcy;icy;jcy;kcy;lcy;mcy;ncy;ocy;pcy;rcy;scy;tcy;ucy;fcy;khcy;tscy;chcy;shcy;shchcy;hardcy;ycy;softcy;ecy;yucy;yacy;1iocy;djcy;gjcy;jukcy;dscy;iukcy;yicy;jsercy;ljcy;njcy;tshcy;kjcy;1ubrcy;dzcy;7074ensp;emsp;emsp13;emsp14;1numsp;puncsp;ThinSpace;hairsp;NegativeMediumSpace;zwnj;zwj;lrm;rlm;dash;2ndash;mdash;horbar;Verbar;1lsquo;CloseCurlyQuote;lsquor;1ldquo;CloseCurlyDoubleQuote;bdquo;1dagger;Dagger;bull;2nldr;hellip;9permil;pertenk;prime;Prime;tprime;backprime;3lsaquo;rsaquo;3oline;2caret;1hybull;frasl;10bsemi;7qprime;7MediumSpace;{8202ThickSpace;}NoBreak;af;InvisibleTimes;ic;72euro;46tdot;DotDot;37complexes;2incare;4gscr;hamilt;Hfr;Hopf;planckh;hbar;imagline;Ifr;lagran;ell;1naturals;numero;copysr;weierp;Popf;Qopf;realine;real;reals;rx;3trade;1integers;2mho;zeetrf;iiota;2bernou;Cayleys;1escr;Escr;Fouriertrf;1Mellintrf;order;alefsym;beth;gimel;daleth;12CapitalDifferentialD;dd;ee;ii;10frac13;frac23;frac15;frac25;frac35;frac45;frac16;frac56;frac18;frac38;frac58;frac78;49larr;ShortUpArrow;rarr;darr;harr;updownarrow;nwarr;nearr;LowerRightArrow;LowerLeftArrow;nlarr;nrarr;1rarrw;{824nrarrw;}Larr;Uarr;Rarr;Darr;larrtl;rarrtl;LeftTeeArrow;mapstoup;map;DownTeeArrow;1hookleftarrow;hookrightarrow;larrlp;looparrowright;harrw;nharr;1lsh;rsh;ldsh;rdsh;1crarr;cularr;curarr;2circlearrowleft;circlearrowright;leftharpoonup;DownLeftVector;RightUpVector;LeftUpVector;rharu;DownRightVector;dharr;dharl;RightArrowLeftArrow;udarr;LeftArrowRightArrow;leftleftarrows;upuparrows;rightrightarrows;ddarr;leftrightharpoons;Equilibrium;nlArr;nhArr;nrArr;DoubleLeftArrow;DoubleUpArrow;DoubleRightArrow;dArr;DoubleLeftRightArrow;DoubleUpDownArrow;nwArr;neArr;seArr;swArr;lAarr;rAarr;1zigrarr;6larrb;rarrb;15DownArrowUpArrow;7loarr;roarr;hoarr;forall;comp;part;{824npart;}exist;nexist;empty;1Del;Element;NotElement;1ni;notni;2prod;coprod;sum;minus;MinusPlus;dotplus;1Backslash;lowast;compfn;1radic;2prop;infin;angrt;ang;{8402nang;}angmsd;angsph;mid;nmid;DoubleVerticalBar;NotDoubleVerticalBar;and;or;cap;{65024caps;}cup;{65024cups;}int;Int;iiint;conint;Conint;Cconint;cwint;ClockwiseContourIntegral;awconint;there4;becaus;ratio;Colon;dotminus;1mDDot;homtht;sim;{8402nvsim;}backsim;{817race;}ac;{819acE;}acd;VerticalTilde;NotTilde;eqsim;{824nesim;}sime;NotTildeEqual;cong;simne;ncong;ap;nap;ape;apid;{824napid;}backcong;asympeq;{8402nvap;}bump;{824nbump;}bumpe;{824nbumpe;}doteq;{824nedot;}doteqdot;efDot;erDot;Assign;ecolon;ecir;circeq;1wedgeq;veeeq;1triangleq;2equest;ne;Congruent;{8421bnequiv;}nequiv;1le;{8402nvle;}ge;{8402nvge;}lE;{824nlE;}gE;{824ngE;}lnE;{65024lvertneqq;}gnE;{65024gvertneqq;}ll;{824nLtv;7577nLt;}gg;{824nGtv;7577nGt;}between;NotCupCap;nless;ngt;nle;nge;lesssim;GreaterLine truncated
+160
View File
@@ -0,0 +1,160 @@
import { type DecodingMode, decodeHTML, decodeXML } from "./decode.js";
import { encodeHTML, encodeNonAsciiHTML } from "./encode.js";
import {
encodeXML,
escapeAttribute,
escapeText,
escapeUTF8,
} from "./escape.js";
/** The level of entities to support. */
export enum EntityLevel {
/** Support only XML entities. */
XML = 0,
/** Support HTML entities, which are a superset of XML entities. */
HTML = 1,
}
/**
* Encoding strategy used by `encode`.
*/
export enum EncodingMode {
/**
* The output is UTF-8 encoded. Only characters that need escaping within
* XML will be escaped.
*/
UTF8,
/**
* The output consists only of ASCII characters. Characters that need
* escaping within HTML, and characters that aren't ASCII characters will
* be escaped.
*/
ASCII,
/**
* Encode all characters that have an equivalent entity, as well as all
* characters that are not ASCII characters.
*/
Extensive,
/**
* Encode all characters that have to be escaped in HTML attributes,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
Attribute,
/**
* Encode all characters that have to be escaped in HTML text,
* following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
*/
Text,
}
/**
* Options for `decode`.
*/
export interface DecodingOptions {
/**
* The level of entities to support.
* @default {@link EntityLevel.XML}
*/
level?: EntityLevel;
/**
* Decoding mode. If `Legacy`, will support legacy entities not terminated
* with a semicolon (`;`).
*
* Always `Strict` for XML. For HTML, set this to
* {@link DecodingMode.Attribute} if you are parsing an attribute value.
* @default {@link DecodingMode.Legacy}
*/
mode?: DecodingMode | undefined;
}
/**
* Decodes a string with entities.
* @param input String to decode.
* @param options Decoding options.
*/
export function decode(
input: string,
options: DecodingOptions | EntityLevel = EntityLevel.XML,
): string {
const level = typeof options === "number" ? options : options.level;
if (level === EntityLevel.HTML) {
const mode = typeof options === "object" ? options.mode : undefined;
return decodeHTML(input, mode);
}
return decodeXML(input);
}
/**
* Options for `encode`.
*/
export interface EncodingOptions {
/**
* The level of entities to support.
* @default {@link EntityLevel.XML}
*/
level?: EntityLevel;
/**
* Output format.
* @default {@link EncodingMode.Extensive}
*/
mode?: EncodingMode;
}
/**
* Encodes a string with entities.
* @param input String to encode.
* @param options Encoding options.
*/
export function encode(
input: string,
options: EncodingOptions | EntityLevel = EntityLevel.XML,
): string {
const { mode = EncodingMode.Extensive, level = EntityLevel.XML } =
typeof options === "number" ? { level: options } : options;
switch (mode) {
case EncodingMode.UTF8: {
return escapeUTF8(input);
}
case EncodingMode.Attribute: {
return escapeAttribute(input);
}
case EncodingMode.Text: {
return escapeText(input);
}
case EncodingMode.ASCII: {
return (
level === EntityLevel.HTML ? encodeNonAsciiHTML : encodeXML
)(input);
}
// biome-ignore lint/complexity/noUselessSwitchCase: we get an error for the switch not being exhaustive
case EncodingMode.Extensive:
default: {
return (level === EntityLevel.HTML ? encodeHTML : encodeXML)(input);
}
}
}
export {
DecodingMode,
decodeHTML,
decodeHTMLAttribute,
decodeHTMLStrict,
decodeXML,
decodeXML as decodeXMLStrict,
EntityDecoder,
} from "./decode.js";
export {
encodeHTML,
encodeNonAsciiHTML,
} from "./encode.js";
export {
encodeXML,
escape,
escapeAttribute,
escapeText,
escapeUTF8,
} from "./escape.js";
+47
View File
@@ -0,0 +1,47 @@
/**
* Bit flags & masks for the binary trie encoding used for entity decoding.
*
* The trie is a flat `Uint16Array`. Every node starts with one header word:
*
* 15..14 VALUE_LENGTH Number of words the value occupies, +1.
* 0 = no value; 1 = value inline in bits 12..0;
* 2/3 = value in the 1/2 words after the header.
* 13 FLAG13 If VALUE_LENGTH > 0: semicolon required ("strict"
* entity; `;` is never stored as a branch).
* If VALUE_LENGTH == 0: this node is a compact run.
* 12..7 BRANCH_LENGTH Number of branches (or run length for runs).
* 6..0 JUMP_TABLE Jump-table offset / single-branch char / first
* run char (see below).
*
* Branch data follows the header and any value words. Its shape is selected
* by (JUMP_TABLE, BRANCH_LENGTH) in the header:
*
* Single branch JUMP_TABLE = the only child's char, BRANCH_LENGTH = 0.
* No branch words; the child node follows immediately.
* Jump table JUMP_TABLE = first covered char (> 0), BRANCH_LENGTH =
* table length. One word per covered char: 0 = no branch,
* otherwise the child's offset from the END of the table,
* +1 (so 0 stays the no-branch sentinel).
* Dictionary JUMP_TABLE = 0, BRANCH_LENGTH = number of branches.
* ceil(n/2) words of sorted keys packed two per word
* (low byte first), then n pointer words storing the
* child's offset from the END of the branch data.
* Compact run VALUE_LENGTH = 0, FLAG13 set. BRANCH_LENGTH = run
* length (3..63), JUMP_TABLE = first char; remaining run
* chars packed two per word after the header. The target
* node follows the packed words immediately.
*
* Pointers are end-relative (rather than relative to the pointer's own
* position) because that makes the common "child encoded right after the
* branch data" case a small constant, which compresses far better. Offsets
* to already-encoded (shared) nodes wrap via uint16 modulo arithmetic; the
* decoder masks navigation results with `& 0xff_ff` to match.
*/
export const enum BinTrieFlags {
VALUE_LENGTH = 0b1100_0000_0000_0000,
FLAG13 = 0b0010_0000_0000_0000,
BRANCH_LENGTH = 0b0001_1111_1000_0000,
JUMP_TABLE = 0b0000_0000_0111_1111,
/** Bits 12..0: the inline value of a VALUE_LENGTH = 1 header word. */
VALUE_MASK = 0b0001_1111_1111_1111,
}
+209
View File
@@ -0,0 +1,209 @@
/*
* Inverse of the encoder's SAFE alphabet (0x21..0x7E minus 0x22, 0x24, 0x5C),
* precomputed once at module load. Entries for excluded chars stay 0 but
* are never read.
*/
const BASE91_INVERSE = /* #__PURE__ */ (() => {
const table = new Uint8Array(127);
let code = 0;
for (let char = 0x21; char <= 0x7e; char++) {
if (char !== 0x22 && char !== 0x24 && char !== 0x5c) {
table[char] = code++;
}
}
return table;
})();
/**
* Decode a dictionary-encoded trie string back into its Uint16Array.
*
* Stream layout (consumed in this order):
* 1. dict1 atoms — `dict1AtomCount` uint16 values, delta+RLE encoded.
* 2. dict2 atoms — `atomCount - dict1AtomCount` values, delta+RLE.
* 3. dict2 ngrams — `ngramCount - (dictSize - dict1AtomCount)` entries,
* each a pair of slot codes that resolve to earlier slots.
* 4. dict1 ngrams — `dictSize - dict1AtomCount` entries, same shape.
* 5. data — slot codes, each expanding to one or more uint16 values.
*
* Codes use a 91-char base (printable ASCII minus `"`, `$`, `\`):
* - char1 < dictSize → 1-char code, slot = char1
* - char1 ≥ dictSize → 2-char code, slot = dictSize + (char1 - dictSize)*91 + char2
*
* Slot index → token kind:
* [0, A) dict1 atoms (1-char codes)
* [A, dictSize) dict1 ngrams (1-char codes)
* [dictSize, dictSize+D) dict2 atoms (2-char codes)
* [dictSize+D, end) dict2 ngrams (2-char codes)
*
* Both atom dicts decode before any ngram, and dict2 ngrams decode before
* dict1 ngrams. So every ngram entry references slots whose contents are
* already filled — no forward references to handle.
*
* This runs on library import. Flat typed arrays store each slot as either
* a plain value (`single`, covering every atom) or a range in a shared
* `pool` (ngrams).
* @param input Packed trie string.
* @param resultLength Expected number of uint16 values in the output.
* @param atomCount Total number of distinct uint16 values in the trie.
* @param dict1AtomCount Atoms in the 1-char range (`A` above).
* @param ngramCount Total number of ngram entries (dict1 + dict2).
* @param dictSize Number of 1-char code slots; the rest of `BASE - dictSize`
* first-byte values are 2-char codes.
*/
export function decodeTrieDict(
input: string,
resultLength: number,
atomCount: number,
dict1AtomCount: number,
ngramCount: number,
dictSize: number,
): Uint16Array {
const base = 91;
const inputLength = input.length;
// For 2-char codes, slot = char1 * base - twoCharBias + char2.
const twoCharBias = dictSize * (base - 1);
let pos = 0;
/** Read one slot code at `pos` and return its slot index, advancing pos. */
const readSlotCode = (): number => {
const c1 = BASE91_INVERSE[input.charCodeAt(pos++)];
return c1 < dictSize
? c1
: c1 * base - twoCharBias + BASE91_INVERSE[input.charCodeAt(pos++)];
};
const dict2AtomCount = atomCount - dict1AtomCount;
const slotCount = atomCount + ngramCount;
/*
* Per-slot contents: atoms (always a single value) live directly in
* `single`; ngram slots hold -1 there and expand to
* `pool[start[slot] .. start[slot] + length[slot])`.
*/
const single = new Int32Array(slotCount);
single.fill(-1, dict1AtomCount, dictSize);
single.fill(-1, dictSize + dict2AtomCount, slotCount);
const start = new Int32Array(slotCount);
const length = new Int32Array(slotCount);
/**
* Decode `count` ascending uint16 values from a delta+RLE stream into
* `single[off..off+count)`.
*
* code < 89 → delta = code
* code == 89 → run-length: next char encodes runLength-2; emit `runLength` consecutive +1 values
* code == 90, next < 90 → escape: delta = 89 + next * BASE + after-next
* code == 90, next == 90 → double-escape: extra char for very large deltas
* @param count
* @param off
*/
function decodeDelta(count: number, off: number): void {
let previous = 0;
let slot = off;
const end = off + count;
while (slot < end) {
const code = BASE91_INVERSE[input.charCodeAt(pos++)];
if (code < 89) {
previous += code;
single[slot++] = previous;
} else if (code === 89) {
let runLength = BASE91_INVERSE[input.charCodeAt(pos++)] + 2;
while (runLength--) single[slot++] = ++previous;
} else {
const next = BASE91_INVERSE[input.charCodeAt(pos++)];
previous +=
89 +
// eslint-disable-next-line unicorn/prefer-minimal-ternary -- branches read a different number of side-effecting input bytes
(next < 90
? next * base + BASE91_INVERSE[input.charCodeAt(pos++)]
: BASE91_INVERSE[input.charCodeAt(pos++)] * 8281 +
BASE91_INVERSE[input.charCodeAt(pos++)] * base +
BASE91_INVERSE[input.charCodeAt(pos++)]);
single[slot++] = previous;
}
}
}
// Streams 1 & 2: atoms decoded into their slot ranges.
decodeDelta(dict1AtomCount, 0);
decodeDelta(dict2AtomCount, dictSize);
/*
* Streams 3 & 4 are read in two passes: first collect every ngram's two
* references and derive its expanded length (each ref resolves to an earlier
* slot, so lengths are already known), which sizes the shared pool.
* Pool ranges are handed out in decode order, so the second pass fills
* the pool contiguously with a single write cursor.
*/
const references = new Int32Array(ngramCount * 2);
let poolSize = 0;
let ngramIndex = 0;
/**
* Read `count` ngram entries (each = 2 slot-code references) for the slots
* starting at `startSlot`, recording references and assigning pool ranges.
* @param count
* @param startSlot
*/
function readNgramReferences(count: number, startSlot: number): void {
for (let index = 0; index < count; index++) {
const slot = startSlot + index;
const a = readSlotCode();
const b = readSlotCode();
references[ngramIndex * 2] = a;
references[ngramIndex * 2 + 1] = b;
ngramIndex += 1;
start[slot] = poolSize;
const entryLength =
(single[a] < 0 ? length[a] : 1) +
(single[b] < 0 ? length[b] : 1);
length[slot] = entryLength;
poolSize += entryLength;
}
}
readNgramReferences(
ngramCount - dictSize + dict1AtomCount,
dictSize + dict2AtomCount,
);
readNgramReferences(dictSize - dict1AtomCount, dict1AtomCount);
// Second pass: concatenate each ngram's two halves into the pool.
const pool = new Uint16Array(poolSize);
let write = 0;
for (let index = 0; index < ngramIndex; index++) {
for (let half = 0; half < 2; half++) {
const source = references[index * 2 + half];
const value = single[source];
if (value < 0) {
let read = start[source];
const readEnd = read + length[source];
while (read < readEnd) pool[write++] = pool[read++];
} else {
pool[write++] = value;
}
}
}
// Stream 5: data. Each code expands to its slot's stored values.
const out = new Uint16Array(resultLength);
let outIndex = 0;
while (pos < inputLength) {
let slot = BASE91_INVERSE[input.charCodeAt(pos++)];
if (slot >= dictSize) {
slot =
slot * base -
twoCharBias +
BASE91_INVERSE[input.charCodeAt(pos++)];
}
const value = single[slot];
if (value < 0) {
let read = start[slot];
const readEnd = read + length[slot];
while (read < readEnd) out[outIndex++] = pool[read++];
} else {
out[outIndex++] = value;
}
}
return out;
}